Skip to content

Frames and Composer Markers

Frames are Luau files that contain composer markers, which are build-time macros to replace chunks of code with build data.


Built-in markers

Marker Replaced with Type
__COMPOSER.Insert(__COMPOSER.build) Full compiled source code string
__COMPOSER.Insert(__COMPOSER.genDate) ISO 8601 build date (quoted) "YYYY-MM-DD"
__COMPOSER.Insert(__COMPOSER.cfg) Build config name (quoted) "Release"
__COMPOSER.Insert(__COMPOSER.vers) Version string (quoted) "v1.2.3"

Writing a frame

A frame is a plain .luau file. Use markers as expressions — they are replaced with raw Luau values (strings are already quoted with %q).

pipeline/frames/release.luau
--!nolint
--!nocheck
--!native
--!optimize 2

--[[
    Generated by ProCMP — do not edit manually.
]]

_P = {
    genDate = __COMPOSER.Insert(__COMPOSER.genDate),
    cfg     = __COMPOSER.Insert(__COMPOSER.cfg),
    vers    = __COMPOSER.Insert(__COMPOSER.vers),
}

__COMPOSER.Insert(__COMPOSER.build)

After composition, __COMPOSER.Insert(__COMPOSER.vers) becomes "v1.2.3".


Runtime access

The _P table is available at runtime in your built script:

if _P.cfg == "Debug" then
    warn("[DEBUG BUILD]")
end

print("Version:", _P.vers)
print("Built:", _P.genDate)

You can rename _P to anything. If you use luau-lsp, add type definitions to silence warnings:

.globals/pcmp.d.luau
declare __COMPOSER: {
    Insert: (text: string) -> string,
    genDate: string,
    build:   string,
    cfg:     string,
    vers:    string,
}

declare _P: { [any]: any }

Multiple frames

You can have different frames for different configs. The frame field is per-config in .pcmp.json:

{
    "buildConfigs": [
        {
            "name": "Debug",
            "frame": "pipeline/frames/debug.luau",
            ...
        },
        {
            "name": "Release",
            "frame": "pipeline/frames/release.luau",
            ...
        }
    ]
}

A common pattern is a debug frame with no optimisation flags and extra logging headers, and a release frame with --!native --!optimize 2 and a clean header.