@isentinel/weld 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/weld.js +17 -0
- package/dist/cli.d.mts +10 -0
- package/dist/cli.d.mts.map +1 -0
- package/dist/cli.mjs +1613 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/index.d.mts +45 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +7 -0
- package/dist/index.mjs.map +1 -0
- package/dist/schema-Cp_hoJ0u.mjs +34 -0
- package/dist/schema-Cp_hoJ0u.mjs.map +1 -0
- package/loaders/luau-raw.d.mts +34 -0
- package/loaders/luau-raw.mjs +25 -0
- package/package.json +102 -0
package/dist/cli.mjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.mjs","names":["loadConfig","c12LoadConfig","loadConfig"],"sources":["../src/codegen/bundle-generator.ts","../src/codegen/transforms/rename-locals.ts","../src/codegen/transforms/regex-utils.ts","../src/codegen/transforms/rewrite-internal-imports.ts","../src/codegen/transforms/strip-boilerplate.ts","../src/codegen/transforms/validate-no-script-refs.ts","../src/codegen/defaults.ts","../src/codegen/module-id.ts","../src/codegen/pipeline.ts","../src/utils/path.ts","../src/codegen/sourcemap-emitter.ts","../src/declarations/declaration-bundler.ts","../src/environment.ts","../src/extraction/extract.luau","../src/extraction/schema.ts","../src/extraction/ndjson-parser.ts","../src/extraction/lute-spawner.ts","../src/graph/chunk-assigner.ts","../src/graph/dependency-graph.ts","../src/resolution/ts-import-parser.ts","../src/resolution/module-resolver.ts","../src/resolution/path-require-resolver.ts","../src/resolution/rbxpath-reverse-mapper.ts","../src/bundler.ts","../src/config/loader.ts","../src/utils/logger.ts","../src/cli.ts"],"sourcesContent":["/** A module within a chunk. */\nexport interface BundleChunkModule {\n\t/** Short module ID (e.g. \"a\", \"b\"). */\n\tid: string;\n\t/**\n\t * Original (pre-transform) source of the module. Used for `sourcesContent`\n\t * in emitted source maps. Optional; sourcemap entries are skipped when\n\t * absent.\n\t */\n\toriginalContent?: string;\n\t/** Rewritten source — internal TS.import calls replaced. */\n\tsource: string;\n\t/**\n\t * Original filesystem path of the module. Used to attribute output lines\n\t * to the original file in emitted source maps. Optional; sourcemap entries\n\t * are skipped when absent.\n\t */\n\tsourcePath?: string;\n\t/** Total locals in scope, injected as a debug comment when present. */\n\ttotalLocals?: number;\n}\n\n/** A chunk of flattened modules. */\nexport interface BundleChunk {\n\t/** Chunk index. */\n\tid: number;\n\t/** Modules in this chunk with renamed+rewritten source. */\n\tmodules: Array<BundleChunkModule>;\n}\n\n/** Information about a source-mapped output line. */\nexport interface BundleLineMapping {\n\t/** Original content of the source file (for sourcesContent). */\n\tcontent: string;\n\t/** Output line in the bundle (1-based). */\n\tgeneratedLine: number;\n\t/** Original source line (1-based). */\n\toriginalLine: number;\n\t/** Filesystem path of the source. */\n\tsourcePath: string;\n}\n\n/** Information needed to source-map the entry source. */\ninterface EntrySourceInfo {\n\t/** Original (pre-transform) entry source. */\n\toriginalContent: string;\n\t/** Filesystem path of the entry source. */\n\tsourcePath: string;\n}\n\n/** Options for bundle generation. */\ninterface GenerateBundleOptions {\n\t/** Information needed to emit sourcemap entries for the entry source. */\n\tentryInfo?: EntrySourceInfo;\n\t/**\n\t * Whether to emit the `local TS = _G[script]` boilerplate at the top of\n\t * the bundle. Defaults to `true`. Set to `false` for path-mode bundles\n\t * that do not use the roblox-ts runtime.\n\t */\n\tincludeTsGlobal?: boolean;\n\t/**\n\t * Optional callback invoked once per source-mapped output line. When\n\t * provided, modules and entry info that include `sourcePath` and\n\t * `originalContent` produce mappings; lines without provenance are\n\t * silently skipped.\n\t */\n\tonMapping?: (mapping: BundleLineMapping) => void;\n}\n\n/**\n * Generate the bundled Luau output.\n *\n * @param modules - Non-entry modules in topological order (deps first).\n * @param entrySource - Rewritten entry point source (inlined at top level).\n * @returns The complete bundled Luau source.\n */\nexport function generateBundle(\n\tmodules: ReadonlyArray<BundleChunkModule>,\n\tentrySource: string,\n): string {\n\treturn generateBundleWithOptions({ entrySource, modules, options: {} });\n}\n\n/**\n * Generate bundled output with flattened chunks.\n *\n * @param chunks - Chunks in topological order.\n * @param entrySource - Rewritten entry point source (inlined at top level).\n * @param options - Optional bundle generation options.\n * @returns The complete bundled Luau source.\n */\n// eslint-disable-next-line better-max-params/better-max-params -- options is an optional opt-in\nexport function generateChunkedBundle(\n\tchunks: ReadonlyArray<BundleChunk>,\n\tentrySource: string,\n\toptions: GenerateBundleOptions = {},\n): string {\n\tconst { entryInfo, onMapping } = options;\n\tconst lines = bundlePrelude(options);\n\n\tfor (const chunk of chunks) {\n\t\tif (chunk.modules.length === 0) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tappendWrappedChunk({ chunk, lines, onMapping });\n\t}\n\n\tconst trimmedEntry = entrySource.replace(/\\n$/, \"\");\n\tconst entryStartLine = lines.length + 1;\n\tlines.push(trimmedEntry, \"\");\n\tif (entryInfo !== undefined && onMapping !== undefined) {\n\t\temitLineMappings({\n\t\t\tcontent: entryInfo.originalContent,\n\t\t\tlineCount: countLines(trimmedEntry),\n\t\t\tonMapping,\n\t\t\tsourcePath: entryInfo.sourcePath,\n\t\t\tstartLine: entryStartLine,\n\t\t});\n\t}\n\n\treturn lines.join(\"\\n\");\n}\n\nfunction bundlePrelude(options: GenerateBundleOptions): Array<string> {\n\tconst lines: Array<string> = [];\n\tif (options.includeTsGlobal !== false) {\n\t\tlines.push(\"local TS = _G[script]\", \"\");\n\t}\n\n\tlines.push(\"local __WELD_MODULES = {\", \"\\tcache = {} :: any,\", \"}\", \"\");\n\treturn lines;\n}\n\n/**\n * Build the do-wrapped block for a single module.\n *\n * @param module_ - The module to wrap.\n * @returns Lines for the module block (without the outer do/end).\n */\nfunction buildModuleBlock(module_: BundleChunkModule): Array<string> {\n\tconst lines: Array<string> = [\"\\tdo\", \"\\t\\tlocal function __modImpl()\"];\n\n\tif (module_.totalLocals !== undefined) {\n\t\tlines.push(`\\t\\t\\t-- Variables in scope: ${String(module_.totalLocals)}`);\n\t}\n\n\tfor (const sourceLine of module_.source.split(\"\\n\")) {\n\t\tlines.push(sourceLine === \"\" ? \"\" : `\\t\\t\\t${sourceLine}`);\n\t}\n\n\tlines.push(\n\t\t\"\\t\\tend\",\n\t\t`\\t\\tfunction __WELD_MODULES.${module_.id}(): typeof(__modImpl())`,\n\t\t`\\t\\t\\tlocal v = __WELD_MODULES.cache.${module_.id}`,\n\t\t\"\\t\\t\\tif not v then\",\n\t\t\"\\t\\t\\t\\tv = { c = __modImpl() }\",\n\t\t`\\t\\t\\t\\t__WELD_MODULES.cache.${module_.id} = v`,\n\t\t\"\\t\\t\\tend\",\n\t\t\"\\t\\t\\treturn v.c\",\n\t\t\"\\t\\tend\",\n\t\t\"\\tend\",\n\t);\n\n\treturn lines;\n}\n\nfunction generateBundleWithOptions(input: {\n\tentrySource: string;\n\tmodules: ReadonlyArray<BundleChunkModule>;\n\toptions: GenerateBundleOptions;\n}): string {\n\tconst lines = bundlePrelude(input.options);\n\n\tif (input.modules.length > 0) {\n\t\tlines.push(\"do\");\n\t\tfor (const module_ of input.modules) {\n\t\t\tlines.push(...buildModuleBlock(module_));\n\t\t}\n\n\t\tlines.push(\"end\", \"\");\n\t}\n\n\tlines.push(input.entrySource.replace(/\\n$/, \"\"), \"\");\n\n\treturn lines.join(\"\\n\");\n}\n\nfunction countLines(source: string): number {\n\treturn source.split(\"\\n\").length;\n}\n\nfunction emitLineMappings(input: {\n\tcontent: string;\n\tlineCount: number;\n\tonMapping: NonNullable<GenerateBundleOptions[\"onMapping\"]>;\n\tsourcePath: string;\n\tstartLine: number;\n}): void {\n\tconst { content, lineCount, onMapping, sourcePath, startLine } = input;\n\tfor (let index = 0; index < lineCount; index++) {\n\t\tonMapping({\n\t\t\tcontent,\n\t\t\tgeneratedLine: startLine + index,\n\t\t\toriginalLine: index + 1,\n\t\t\tsourcePath,\n\t\t});\n\t}\n}\n\n/**\n * Sum totalLocals across all modules in a chunk. Returns 0 if no module has\n * totalLocals set (debug mode off).\n *\n * @param chunk - The chunk to sum.\n * @returns Total locals across the chunk's modules.\n */\nfunction sumChunkLocals(chunk: BundleChunk): number {\n\tlet total = 0;\n\tfor (const module_ of chunk.modules) {\n\t\ttotal += module_.totalLocals ?? 0;\n\t}\n\n\treturn total;\n}\n\nfunction appendChunkModuleBody(input: {\n\tlines: Array<string>;\n\tmodule_: BundleChunkModule;\n\tonMapping?: GenerateBundleOptions[\"onMapping\"];\n}): void {\n\tconst { lines, module_, onMapping } = input;\n\tif (module_.totalLocals !== undefined) {\n\t\tlines.push(`\\t-- [${module_.id}] Variables in scope: ${String(module_.totalLocals)}`);\n\t}\n\n\tconst moduleStartLine = lines.length + 1;\n\tconst moduleLines = module_.source.split(\"\\n\");\n\tfor (const sourceLine of moduleLines) {\n\t\tlines.push(`\\t${sourceLine}`);\n\t}\n\n\tif (\n\t\tonMapping !== undefined &&\n\t\tmodule_.sourcePath !== undefined &&\n\t\tmodule_.originalContent !== undefined\n\t) {\n\t\temitLineMappings({\n\t\t\tcontent: module_.originalContent,\n\t\t\tlineCount: moduleLines.length,\n\t\t\tonMapping,\n\t\t\tsourcePath: module_.sourcePath,\n\t\t\tstartLine: moduleStartLine,\n\t\t});\n\t}\n}\n\n/**\n * Append a chunk wrapped in a do-block to isolate its locals.\n *\n * @param input - Output lines array, the chunk to append, and optional source-map callback.\n */\nfunction appendWrappedChunk(input: {\n\tchunk: BundleChunk;\n\tlines: Array<string>;\n\tonMapping?: GenerateBundleOptions[\"onMapping\"];\n}): void {\n\tconst { chunk, lines, onMapping } = input;\n\tconst chunkTotal = sumChunkLocals(chunk);\n\tif (chunkTotal > 0) {\n\t\tlines.push(\n\t\t\t`-- Chunk ${String(chunk.id)}: ${String(chunkTotal)} locals (${String(chunk.modules.length)} modules)`,\n\t\t);\n\t}\n\n\tlines.push(\"do\");\n\n\tfor (const module_ of chunk.modules) {\n\t\tappendChunkModuleBody({ lines, module_, onMapping });\n\t}\n\n\tfor (const module_ of chunk.modules) {\n\t\tlines.push(`\\tfunction __WELD_MODULES.${module_.id}() return _${module_.id} end`);\n\t}\n\n\tlines.push(\"end\", \"\");\n}\n","import type { LocalBinding, Transform } from \"./types.ts\";\n\n/**\n * Build a byte-offset-to-char-offset mapping for a source string.\n * Lute reports byte offsets but JS string operations use char offsets.\n * They differ when the source contains multibyte UTF-8 characters.\n *\n * @param source - The source string.\n * @returns A function that converts byte offsets to char offsets.\n */\nfunction buildByteToCharMap(source: string): (byteOffset: number) => number {\n\tconst encoder = new TextEncoder();\n\tconst bytes = encoder.encode(source);\n\n\t// Build a map from byte offset → char offset\n\tconst byteToChar = new Int32Array(bytes.length + 1);\n\tlet charIndex = 0;\n\tlet byteIndex = 0;\n\n\tfor (const codePoint of source) {\n\t\tconst charBytes = encoder.encode(codePoint).length;\n\t\tfor (let offset = 0; offset < charBytes; offset++) {\n\t\t\tbyteToChar[byteIndex + offset] = charIndex;\n\t\t}\n\n\t\tbyteIndex += charBytes;\n\t\t// Advance by UTF-16 code unit count (2 for astral/surrogate pairs)\n\t\tcharIndex += codePoint.length;\n\t}\n\n\tbyteToChar[byteIndex] = charIndex;\n\n\t// eslint-disable-next-line ts/no-non-null-assertion -- byteOffset always within source bounds\n\treturn (byteOffset: number): number => byteToChar[byteOffset]!;\n}\n\n/**\n * Apply all local renames to the source, working in descending offset order.\n *\n * @param source - Original source.\n * @param options - Rename configuration.\n * @returns Source with renamed locals.\n */\nfunction applyRenames(\n\tsource: string,\n\toptions: {\n\t\tlocals: ReadonlyArray<LocalBinding>;\n\t\tprefix: string;\n\t\ttoChar: (byteOffset: number) => number;\n\t},\n): string {\n\tconst { locals, prefix, toChar } = options;\n\tconst allOccurrences: Array<{ end: number; name: string; start: number }> = [];\n\n\tfor (const binding of locals) {\n\t\tfor (const loc of binding.locations) {\n\t\t\tallOccurrences.push({\n\t\t\t\tname: binding.name,\n\t\t\t\tend: toChar(loc.end),\n\t\t\t\tstart: toChar(loc.start),\n\t\t\t});\n\t\t}\n\t}\n\n\tallOccurrences.sort((first, second) => second.start - first.start);\n\n\tlet result = source;\n\tfor (const occurrence of allOccurrences) {\n\t\tconst replacement = `${prefix}_${occurrence.name}`;\n\t\tresult = result.slice(0, occurrence.start) + replacement + result.slice(occurrence.end);\n\t}\n\n\treturn result;\n}\n\n/**\n * Rewrite the trailing top-level return to an export capture assignment.\n *\n * @param source - Source with renames applied.\n * @param prefix - Module prefix for the export capture variable.\n * @returns The rewritten source.\n */\nfunction rewriteReturn(source: string, prefix: string): string {\n\tlet result = source;\n\tconst lastReturnIndex = result.lastIndexOf(\"\\nreturn \");\n\tif (lastReturnIndex !== -1) {\n\t\tconst before = result.slice(0, lastReturnIndex + 1);\n\t\tconst after = result.slice(lastReturnIndex + \"\\nreturn \".length);\n\t\tresult = `${before}local _${prefix} = ${after}`;\n\t} else if (result.startsWith(\"return \")) {\n\t\tresult = `local _${prefix} = ${result.slice(\"return \".length)}`;\n\t}\n\n\treturn result;\n}\n\n/** Prefix top-level local variables and rewrite trailing return to export capture. */\nexport const renameLocals: Transform = {\n\tname: \"rename-locals\",\n\tapply(source, context) {\n\t\tconst toChar = buildByteToCharMap(source);\n\t\tconst renamed = applyRenames(source, {\n\t\t\tlocals: context.locals,\n\t\t\tprefix: context.moduleId,\n\t\t\ttoChar,\n\t\t});\n\n\t\treturn rewriteReturn(renamed, context.moduleId);\n\t},\n};\n","/**\n * Escape special regex characters in a string.\n *\n * @param text - The string to escape.\n * @returns The escaped string safe for use in a regex.\n */\nexport function escapeRegex(text: string): string {\n\treturn text.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n","import { escapeRegex } from \"./regex-utils.ts\";\nimport type { ResolvedImport, Transform } from \"./types.ts\";\n\n/**\n * Build a regex pattern that matches the call site for a resolved import.\n *\n * @param resolved - The resolved import metadata, including kind and args.\n * @returns A RegExp matching the corresponding source call.\n */\nfunction buildImportPattern(resolved: ResolvedImport): RegExp {\n\tif (resolved.kind === \"require\") {\n\t\t/* c8 ignore next -- args[0] is always present for resolved require kinds (filtered upstream) */\n\t\tconst argument = escapeRegex(resolved.args[0] ?? \"\");\n\t\treturn new RegExp(`require\\\\(\\\\s*${argument}\\\\s*\\\\)`);\n\t}\n\n\tconst joined = resolved.args.map((argument) => escapeRegex(argument)).join(\",\\\\s*\");\n\treturn new RegExp(`TS\\\\.import\\\\(${joined}\\\\)`);\n}\n\n/**\n * Replace all `TS.import()` calls with module references.\n *\n * @param source - Source with TS boilerplate already stripped.\n * @param resolvedImports - Resolved imports to apply.\n * @returns The rewritten source and count of successful replacements.\n */\nfunction rewriteImports(\n\tsource: string,\n\tresolvedImports: ReadonlyArray<ResolvedImport>,\n): { rewriteCount: number; source: string } {\n\tlet result = source;\n\tlet rewriteCount = 0;\n\n\tfor (const resolved of resolvedImports) {\n\t\tconst pattern = buildImportPattern(resolved);\n\t\tconst replacement =\n\t\t\tresolved.intraChunk === true\n\t\t\t\t? `_${resolved.moduleId}`\n\t\t\t\t: `__WELD_MODULES.${resolved.moduleId}()`;\n\t\tconst rewritten = result.replace(pattern, replacement);\n\n\t\tif (rewritten !== result) {\n\t\t\trewriteCount++;\n\t\t}\n\n\t\tresult = rewritten;\n\t}\n\n\treturn { rewriteCount, source: result };\n}\n\n/** Replace `TS.import()` calls with `__WELD_MODULES` references or direct local access. */\nexport const rewriteInternalImports: Transform = {\n\tname: \"rewrite-internal-imports\",\n\tapply(source, context) {\n\t\tconst { rewriteCount, source: rewritten } = rewriteImports(source, context.resolvedImports);\n\n\t\tif (rewriteCount !== context.resolvedImports.length) {\n\t\t\tthrow new Error(\n\t\t\t\t`rewrite count mismatch: expected ${String(context.resolvedImports.length)}, got ${String(rewriteCount)}`,\n\t\t\t);\n\t\t}\n\n\t\treturn rewritten;\n\t},\n};\n","import type { Transform } from \"./types.ts\";\n\nconst TS_BOILERPLATE_PATTERN = /local\\s+TS\\s*=\\s*_G\\[script]\\n?/;\n\n/** Strip the `local TS = _G[script]` boilerplate line. */\nexport const stripBoilerplate: Transform = {\n\tname: \"strip-boilerplate\",\n\tapply(source) {\n\t\treturn source.replace(TS_BOILERPLATE_PATTERN, \"\");\n\t},\n};\n","import type { Transform } from \"./types.ts\";\n\n/**\n * Matches all Luau comments in left-to-right order:\n * - Block comments with any `=` delimiter count: `--[=*[ ... ]=*]`\n * - Single-line comments: `--` to end of line.\n *\n * Order matters: block pattern is tried first at each `--` position.\n * If `--[[` appears inside a `-- single-line comment`, the single-line\n * pattern matches first (consuming to EOL), preventing a false block open.\n */\nconst COMMENT_PATTERN = /--\\[(=*)\\[[\\s\\S]*?]\\1]|--[^\\n]*/g;\n\n/**\n * Matches only the external passthrough shape\n * `TS.import(script, TS.getModule(<args>)[.sub.path]*)`, tolerating the\n * dotted subpath chain roblox-ts emits for nested module paths like\n * `TS.getModule(script, \"@rbxts\", \"vide\").src`. Internal calls like\n * `TS.import(script, script.Parent, \"dep\")` are intentionally *not* matched\n * so a leftover internal import that was not rewritten still fails validation.\n */\nconst TS_IMPORT_PATTERN = /TS\\.import\\(\\s*script\\s*,\\s*TS\\.getModule\\([^()]*\\)(?:\\.\\w+)*\\s*\\)/g;\n\nfunction stripValidatedRegions(source: string): string {\n\treturn source.replace(COMMENT_PATTERN, \"\").replace(TS_IMPORT_PATTERN, \"\");\n}\n\n/** Validate that non-entry modules contain no remaining `script` references. */\nexport const validateNoScriptRefs: Transform = {\n\tname: \"validate-no-script-refs\",\n\tapply(source) {\n\t\tif (/\\bscript\\b/.test(stripValidatedRegions(source))) {\n\t\t\tthrow new Error(\"non-boilerplate script reference found\");\n\t\t}\n\n\t\treturn source;\n\t},\n};\n","import { renameLocals } from \"./transforms/rename-locals.ts\";\nimport { rewriteInternalImports } from \"./transforms/rewrite-internal-imports.ts\";\nimport { stripBoilerplate } from \"./transforms/strip-boilerplate.ts\";\nimport type { Transform } from \"./transforms/types.ts\";\nimport { validateNoScriptRefs } from \"./transforms/validate-no-script-refs.ts\";\n\n/** Pipeline for non-entry modules: rename locals, strip boilerplate, rewrite imports, validate. */\nexport const MODULE_PIPELINE: ReadonlyArray<Transform> = [\n\trenameLocals,\n\tstripBoilerplate,\n\trewriteInternalImports,\n\tvalidateNoScriptRefs,\n];\n\n/** Pipeline for entry point modules: no rename (locals stay global), no validate (script refs are legal). */\nexport const ENTRY_PIPELINE: ReadonlyArray<Transform> = [stripBoilerplate, rewriteInternalImports];\n\n/**\n * Path-mode non-entry pipeline. Skips validateNoScriptRefs because handwritten\n * Luau modules legitimately reference `script`. Skips stripBoilerplate because\n * there is no `local TS = _G[script]` line to remove.\n */\nexport const PATH_MODULE_PIPELINE: ReadonlyArray<Transform> = [\n\trenameLocals,\n\trewriteInternalImports,\n];\n\n/** Path-mode entry pipeline. */\nexport const PATH_ENTRY_PIPELINE: ReadonlyArray<Transform> = [rewriteInternalImports];\n","const ALPHABET_SIZE = 26;\n/**\n * 'a'.charCodeAt(0).\n */\nconst CHAR_CODE_A = 97;\n\n/**\n * Generate a short alphabetic ID from a zero-based index using base-26\n * encoding with lowercase letters.\n *\n * @param index - Zero-based module index.\n * @returns A lowercase alphabetic identifier.\n */\nexport function generateId(index: number): string {\n\tlet result = \"\";\n\tlet remaining = index;\n\n\tdo {\n\t\tresult = String.fromCharCode(CHAR_CODE_A + (remaining % ALPHABET_SIZE)) + result;\n\t\tremaining = Math.floor(remaining / ALPHABET_SIZE) - 1;\n\t} while (remaining >= 0);\n\n\treturn result;\n}\n\n/**\n * Assign short IDs to modules in the given order.\n *\n * @param sortedFilePaths - File paths in deterministic sort order.\n * @returns A map from file path to short alphabetic ID.\n */\nexport function assignModuleIds(sortedFilePaths: ReadonlyArray<string>): Map<string, string> {\n\tconst result = new Map<string, string>();\n\n\tfor (const [index, filePath] of sortedFilePaths.entries()) {\n\t\tresult.set(filePath, generateId(index));\n\t}\n\n\treturn result;\n}\n","import type { Transform, TransformContext } from \"./transforms/types.ts\";\n\n/** Options for {@link runPipeline}. */\ninterface PipelineOptions {\n\t/** Shared context for all transforms. */\n\treadonly context: TransformContext;\n\t/** Ordered transforms to apply. */\n\treadonly transforms: ReadonlyArray<Transform>;\n}\n\n/**\n * Run a sequence of transforms over source code.\n *\n * @param source - Input source code.\n * @param options - Pipeline configuration.\n * @returns The final transformed source.\n */\nexport function runPipeline(source: string, options: PipelineOptions): string {\n\tconst { context, transforms } = options;\n\treturn transforms.reduce((current, transform) => transform.apply(current, context), source);\n}\n","/**\n * Convert Windows backslash paths to POSIX forward slashes.\n *\n * @param value - Path to normalize.\n * @returns The path with forward slashes.\n */\nexport function toPosix(value: string): string {\n\treturn value.replaceAll(\"\\\\\", \"/\");\n}\n\nexport const LUAU_EXTENSIONS = [\".luau\", \".lua\"] as const;\n\n/**\n * Strip Luau file extensions (.luau, .lua) from a path. Returns the path\n * unchanged if it does not end with a known extension.\n *\n * @param filePath - Path to strip extension from.\n * @returns The path without the extension, or unchanged if no known extension.\n */\nexport function stripExtension(filePath: string): string {\n\tfor (const extension of LUAU_EXTENSIONS) {\n\t\tif (filePath.endsWith(extension)) {\n\t\t\treturn filePath.slice(0, -extension.length);\n\t\t}\n\t}\n\n\treturn filePath;\n}\n","import { addMapping, GenMapping, setSourceContent, toEncodedMap } from \"@jridgewell/gen-mapping\";\n\nimport path from \"node:path\";\n\nimport { toPosix } from \"../utils/path.ts\";\n\n/** A single line-to-line mapping from generated output to original source. */\ninterface SourceMapEntry {\n\t/** Original source content for embedding in `sourcesContent`. */\n\treadonly content: string;\n\t/** Output line in the bundle (1-based). */\n\treadonly generatedLine: number;\n\t/** Original source line (1-based). */\n\treadonly originalLine: number;\n\t/** Absolute filesystem path of the source file. */\n\treadonly sourcePath: string;\n}\n\n/** Build inputs for {@link emitSourceMap}. */\ninterface EmitSourceMapInput {\n\t/** Mappings from output lines to source positions. */\n\treadonly entries: ReadonlyArray<SourceMapEntry>;\n\t/** Path to the bundled output file (used for the `file` field). */\n\treadonly outputPath: string;\n\t/** Directory the sourcemap will be written to (sources are made relative to this). */\n\treadonly sourcemapDirectory: string;\n}\n\n/** Source Map V3 JSON shape (subset relevant to our emission). */\ninterface SourceMapV3 {\n\t/** Generated file the sourcemap describes (basename of the bundled output). */\n\treadonly file: string;\n\t/** VLQ-encoded mappings string. */\n\treadonly mappings: string;\n\t/** Source files referenced by the mappings, relative to the sourcemap directory. */\n\treadonly sources: ReadonlyArray<string>;\n\t/** Inline contents for each source, parallel to `sources`. */\n\treadonly sourcesContent: ReadonlyArray<null | string>;\n\t/** Source map specification version. Always `3`. */\n\treadonly version: 3;\n}\n\n/**\n * Emit a Source Map V3 JSON object from a list of line-to-line mappings.\n *\n * Sources are emitted as paths relative to {@link EmitSourceMapInput.sourcemapDirectory}\n * with forward slashes. Line numbers are 1-based for both ends.\n *\n * @param input - The mappings, output path, and sourcemap directory.\n * @returns A Source Map V3 JSON object.\n */\nexport function emitSourceMap(input: EmitSourceMapInput): SourceMapV3 {\n\tconst map = new GenMapping({ file: path.basename(input.outputPath) });\n\tconst relativeBySource = new Map<string, string>();\n\n\tfor (const entry of input.entries) {\n\t\tlet relativeSource = relativeBySource.get(entry.sourcePath);\n\t\tif (relativeSource === undefined) {\n\t\t\trelativeSource = toPosix(path.relative(input.sourcemapDirectory, entry.sourcePath));\n\t\t\trelativeBySource.set(entry.sourcePath, relativeSource);\n\t\t\tsetSourceContent(map, relativeSource, entry.content);\n\t\t}\n\n\t\taddMapping(map, {\n\t\t\tgenerated: { column: 0, line: entry.generatedLine },\n\t\t\toriginal: { column: 0, line: entry.originalLine },\n\t\t\tsource: relativeSource,\n\t\t});\n\t}\n\n\treturn toEncodedMap(map) as SourceMapV3;\n}\n","import { generateDtsBundle } from \"dts-bundle-generator\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\n\nimport { toPosix } from \"../utils/path.ts\";\n\n/** Result of declaration bundling. */\nexport interface DeclarationBundleResult {\n\t/** Absolute path of the output .d.ts file. */\n\toutputPath: string;\n}\n\n/** Configuration for declaration bundling. */\nexport interface DeclarationBundleConfig {\n\t/** Absolute path to the entry .luau file. */\n\tentryLuauPath: string;\n\t/** Package names whose types should be imported rather than inlined. */\n\timportedLibraries?: ReadonlyArray<string>;\n\t/** Absolute path for the output .d.ts file. */\n\toutputPath: string;\n}\n\n/**\n * Map a .luau file path to its co-located .d.ts declaration file path.\n *\n * For `init.luau`, tries `init.d.ts` first, then falls back to `index.d.ts`\n * (roblox-ts renames `index.ts` → `init.luau`, but tsc emits `index.d.ts`).\n *\n * @param luauPath - Absolute posix-normalized path to a .luau file.\n * @returns Absolute path to the corresponding .d.ts file.\n */\nexport function luauToDtsPath(luauPath: string): string {\n\tconst direct = toPosix(luauPath.replace(/\\.luau$/, \".d.ts\"));\n\n\tif (fs.existsSync(direct)) {\n\t\treturn direct;\n\t}\n\n\tconst checked = [direct];\n\n\tif (path.basename(luauPath) === \"init.luau\") {\n\t\tconst indexPath = toPosix(path.join(path.dirname(luauPath), \"index.d.ts\"));\n\t\tchecked.push(indexPath);\n\t\tif (fs.existsSync(indexPath)) {\n\t\t\treturn indexPath;\n\t\t}\n\t}\n\n\tthrow new Error(`No declaration file found for ${luauPath} (checked: ${checked.join(\", \")})`);\n}\n\n/**\n * Bundle .d.ts declaration files for a Luau entry module.\n *\n * Maps the entry .luau to its co-located .d.ts, then uses dts-bundle-generator\n * to produce a single merged declaration file.\n *\n * @param config - Declaration bundle configuration.\n * @returns The bundle result with output path.\n */\nexport function bundleDeclarations(config: DeclarationBundleConfig): DeclarationBundleResult {\n\tconst entryDtsPath = luauToDtsPath(config.entryLuauPath);\n\n\tconst libraries =\n\t\tconfig.importedLibraries && config.importedLibraries.length > 0\n\t\t\t? { importedLibraries: [...config.importedLibraries] }\n\t\t\t: undefined;\n\n\tconst results = generateDtsBundle(\n\t\t[{ filePath: entryDtsPath, libraries, output: { noBanner: true } }],\n\t\t{},\n\t);\n\n\t// eslint-disable-next-line ts/no-non-null-assertion -- generateDtsBundle returns one result per entry\n\tconst bundled = results[0]!;\n\n\tfs.mkdirSync(path.dirname(config.outputPath), { recursive: true });\n\tfs.writeFileSync(config.outputPath, bundled);\n\n\treturn { outputPath: config.outputPath };\n}\n","import { createEnv } from \"@t3-oss/env-core\";\n\nimport { type } from \"arktype\";\nimport process from \"node:process\";\n\n/**\n * Validated environment variables for the bundler.\n *\n * Uses `createEnv` from `@t3-oss/env-core` with arktype as the Standard Schema\n * validator. Validation runs eagerly at module load time to fail fast on\n * invalid configuration.\n */\nexport const environment: Readonly<{ WELD_DEBUG?: string }> = createEnv({\n\temptyStringAsUndefined: true,\n\truntimeEnv: process.env,\n\tserver: {\n\t\tWELD_DEBUG: type(\"string | undefined\"),\n\t},\n});\n\nconst FALSY_VALUES: ReadonlySet<string> = new Set([\"0\", \"false\"]);\n\n/**\n * Whether debug mode is enabled via the WELD_DEBUG environment variable.\n *\n * @returns True when WELD_DEBUG is set to a truthy value.\n */\nexport function isDebug(): boolean {\n\tconst value = process.env[\"WELD_DEBUG\"];\n\tif (value === undefined || value === \"\") {\n\t\treturn false;\n\t}\n\n\treturn !FALSY_VALUES.has(value);\n}\n","export default \"--- Weld extraction script: discovers Luau files, parses each with Lute's\\n--- syntax.parse(), walks the full CST to find require()/TS.import() calls\\n--- and count local variables per scope. Outputs one NDJSON line per file\\n--- to stdout, followed by a __TIMING__-prefixed timing summary.\\n---\\n--- Usage: lute run extract.luau -- <luau-root>\\n---\\n--- CST shape (Lute returns full CST, not stripped AST):\\n--- - Values/variables/arguments are wrapped: {node: <actual>}\\n--- - Names are token objects: {text: \\\"name\\\"}\\n--- - Strings: {tag: \\\"string\\\", token: {text: \\\"value\\\"}}\\n--- - Locals: {tag: \\\"local\\\", local: {name: {text: \\\"x\\\"}}, token: {text: \\\"x\\\"}}\\n--- - Globals: {tag: \\\"global\\\", name: {text: \\\"script\\\"}}\\n--- - Index chains: {tag: \\\"indexname\\\", expression: <base>, index: {text: \\\"Parent\\\"}}\\n--- - Calls: {tag: \\\"call\\\", func: <expr>, arguments: [{node: <arg>}, ...]}\\n\\nlocal fs = require(\\\"@std/fs\\\")\\nlocal json = require(\\\"@std/json\\\")\\nlocal process = require(\\\"@std/process\\\")\\nlocal syntax = require(\\\"@std/syntax\\\")\\n\\n--------------------------------------------------------------------------------\\n-- Argument parsing\\n--------------------------------------------------------------------------------\\n\\nlocal args = process.args\\nlocal pastSeparator = false\\nlocal userArgs: { string } = {}\\nfor _, arg in args do\\n\\tif pastSeparator then\\n\\t\\ttable.insert(userArgs, arg)\\n\\telseif arg == \\\"--\\\" then\\n\\t\\tpastSeparator = true\\n\\tend\\nend\\n\\nlocal luauRoot = userArgs[1]\\nif not luauRoot then\\n\\terror(\\\"Usage: lute run extract.luau -- <luau-root>\\\")\\nend\\n\\nluauRoot = string.gsub(luauRoot, \\\"\\\\\\\\\\\", \\\"/\\\")\\nif string.sub(luauRoot, -1) == \\\"/\\\" then\\n\\tluauRoot = string.sub(luauRoot, 1, -2)\\nend\\n\\n--------------------------------------------------------------------------------\\n-- File discovery\\n--------------------------------------------------------------------------------\\n\\nlocal function discoverFiles(directory: string, relativeTo: string, results: { string })\\n\\tlocal ok, entries = pcall(fs.listDirectory, directory)\\n\\tif not ok then\\n\\t\\treturn\\n\\tend\\n\\n\\tfor _, entry in entries do\\n\\t\\tlocal fullPath = directory .. \\\"/\\\" .. entry.name\\n\\t\\tif entry.type == \\\"dir\\\" then\\n\\t\\t\\t-- Skip node_modules, coverage dirs, and dot directories\\n\\t\\t\\tif entry.name == \\\"node_modules\\\" or entry.name == \\\".jest-roblox\\\" then\\n\\t\\t\\t\\tcontinue\\n\\t\\t\\tend\\n\\n\\t\\t\\tif string.sub(entry.name, 1, 1) == \\\".\\\" then\\n\\t\\t\\t\\tcontinue\\n\\t\\t\\tend\\n\\n\\t\\t\\tdiscoverFiles(fullPath, relativeTo, results)\\n\\t\\telseif\\n\\t\\t\\tentry.type == \\\"file\\\"\\n\\t\\t\\tand (string.sub(entry.name, -5) == \\\".luau\\\" or string.sub(entry.name, -4) == \\\".lua\\\")\\n\\t\\tthen\\n\\t\\t\\tlocal relative = string.sub(fullPath, #relativeTo + 2)\\n\\t\\t\\ttable.insert(results, relative)\\n\\t\\tend\\n\\tend\\nend\\n\\n--------------------------------------------------------------------------------\\n-- CST helpers\\n--------------------------------------------------------------------------------\\n\\ntype RequireInfo = {\\n\\tkind: \\\"require\\\" | \\\"ts_import\\\" | \\\"ts_import_external\\\",\\n\\targs: { string },\\n}\\n\\ntype SourceLocation = {\\n\\tstart: number,\\n\\t[\\\"end\\\"]: number,\\n}\\n\\ntype TopLevelLocal = {\\n\\tname: string,\\n\\tlocations: { SourceLocation },\\n}\\n\\n--- Build a table mapping 1-based line numbers to 0-based byte offsets of each\\n--- line's start position. Used to convert CST line/column positions to byte\\n--- offsets.\\nlocal function buildLineOffsets(source: string): { number }\\n\\tlocal offsets: { number } = { 0 } -- line 1 starts at byte 0\\n\\tlocal pos = 1\\n\\twhile pos <= #source do\\n\\t\\tlocal nl = string.find(source, \\\"\\\\n\\\", pos, true)\\n\\t\\tif not nl then\\n\\t\\t\\tbreak\\n\\t\\tend\\n\\t\\t-- Next line starts at the byte after the newline\\n\\t\\ttable.insert(offsets, nl) -- 0-based offset: nl is 1-based pos of \\\\n, next char is at nl (0-based)\\n\\t\\tpos = nl + 1\\n\\tend\\n\\treturn offsets\\nend\\n\\n--- Convert a CST location (1-based line/column) to a 0-based byte offset range.\\nlocal function locationToByteRange(location: any, lineOffsets: { number }): SourceLocation\\n\\tlocal beginLine = location.beginLine :: number\\n\\tlocal beginCol = location.beginColumn :: number\\n\\tlocal endLine = location.endLine :: number\\n\\tlocal endCol = location.endColumn :: number\\n\\tlocal startOffset = lineOffsets[beginLine] + beginCol - 1\\n\\tlocal endOffset = lineOffsets[endLine] + endCol - 1\\n\\treturn { start = startOffset, [\\\"end\\\"] = endOffset }\\nend\\n\\n--- Create a unique key for a declaration binding based on its location.\\nlocal function bindingKey(location: any): string\\n\\treturn `{location.beginLine}:{location.beginColumn}`\\nend\\n\\n--- Unwrap a CST node wrapper ({node: X} -> X).\\nlocal function unwrap(wrapper: any): any\\n\\tif type(wrapper) == \\\"table\\\" and wrapper.node ~= nil then\\n\\t\\treturn wrapper.node\\n\\tend\\n\\n\\treturn wrapper\\nend\\n\\n--- Get the text name from a CST name/token reference.\\nlocal function getName(node: any): string?\\n\\tif type(node) ~= \\\"table\\\" then\\n\\t\\treturn nil\\n\\tend\\n\\n\\tif node.text ~= nil then\\n\\t\\treturn node.text :: string\\n\\tend\\n\\n\\tif node.name ~= nil and type(node.name) == \\\"table\\\" then\\n\\t\\treturn (node.name :: any).text\\n\\tend\\n\\n\\treturn nil\\nend\\n\\n--- Reconstruct a path expression into a readable string.\\nlocal function exprToString(node: any): string?\\n\\tif type(node) ~= \\\"table\\\" then\\n\\t\\treturn nil\\n\\tend\\n\\n\\tif node.tag == \\\"string\\\" then\\n\\t\\tlocal text: string? = if node.token then (node.token :: any).text else node.text\\n\\t\\tif text then\\n\\t\\t\\treturn `\\\"{text}\\\"`\\n\\t\\tend\\n\\n\\t\\treturn nil\\n\\tend\\n\\n\\tif node.tag == \\\"global\\\" then\\n\\t\\treturn getName(node)\\n\\tend\\n\\n\\tif node.tag == \\\"local\\\" then\\n\\t\\tif node.token then\\n\\t\\t\\treturn getName(node.token)\\n\\t\\tend\\n\\n\\t\\tif node[\\\"local\\\"] then\\n\\t\\t\\treturn getName(node[\\\"local\\\"])\\n\\t\\tend\\n\\n\\t\\treturn nil\\n\\tend\\n\\n\\tif node.tag == \\\"indexname\\\" then\\n\\t\\tlocal base = exprToString(node.expression)\\n\\t\\tlocal index = getName(node.index)\\n\\t\\tif base and index then\\n\\t\\t\\treturn `{base}.{index}`\\n\\t\\tend\\n\\n\\t\\treturn index\\n\\tend\\n\\n\\treturn nil\\nend\\n\\n--- Extract all arguments from a CST call's argument list as readable strings.\\nlocal function extractCallArgs(arguments: any): { string }\\n\\tlocal result: { string } = {}\\n\\tif type(arguments) ~= \\\"table\\\" then\\n\\t\\treturn result\\n\\tend\\n\\n\\tfor _, argWrapper in arguments do\\n\\t\\tlocal arg = unwrap(argWrapper)\\n\\t\\tif type(arg) == \\\"table\\\" then\\n\\t\\t\\tlocal repr = exprToString(arg)\\n\\t\\t\\tif repr then\\n\\t\\t\\t\\ttable.insert(result, repr)\\n\\t\\t\\tend\\n\\t\\tend\\n\\tend\\n\\n\\treturn result\\nend\\n\\n--- Check if a CST node is a TS.getModule(...) call.\\nlocal function isGetModuleCall(node: any): boolean\\n\\tif type(node) ~= \\\"table\\\" or node.tag ~= \\\"call\\\" then\\n\\t\\treturn false\\n\\tend\\n\\n\\tlocal func = node.func\\n\\tif not func or func.tag ~= \\\"indexname\\\" then\\n\\t\\treturn false\\n\\tend\\n\\n\\tlocal baseName = exprToString(func.expression)\\n\\tlocal indexName = getName(func.index)\\n\\treturn baseName == \\\"TS\\\" and indexName == \\\"getModule\\\"\\nend\\n\\n--- Extract TS.getModule arguments and any sub-path chain from a CST node.\\n--- For `TS.getModule(script, \\\"@rbxts\\\", \\\"vide\\\").src`, returns {\\\"@rbxts\\\", \\\"vide\\\", \\\"src\\\"}.\\n--- Returns nil if the node does not contain a TS.getModule call.\\nlocal function extractGetModuleArgs(node: any): { string }?\\n\\t-- Walk down indexname chain to find the base call\\n\\tlocal subPathSegments: { string } = {}\\n\\tlocal current = node\\n\\n\\twhile type(current) == \\\"table\\\" and current.tag == \\\"indexname\\\" do\\n\\t\\tlocal indexName = getName(current.index)\\n\\t\\tif indexName then\\n\\t\\t\\ttable.insert(subPathSegments, 1, indexName)\\n\\t\\tend\\n\\t\\tcurrent = current.expression\\n\\tend\\n\\n\\t-- Base must be a TS.getModule(...) call\\n\\tif not isGetModuleCall(current) then\\n\\t\\treturn nil\\n\\tend\\n\\n\\t-- Extract string literal args from getModule (skip first arg: script)\\n\\tlocal result: { string } = {}\\n\\tif current.arguments and type(current.arguments) == \\\"table\\\" then\\n\\t\\tlocal skipFirst = true\\n\\t\\tfor _, argWrapper in current.arguments do\\n\\t\\t\\tif skipFirst then\\n\\t\\t\\t\\tskipFirst = false\\n\\t\\t\\t\\tcontinue\\n\\t\\t\\tend\\n\\t\\t\\tlocal arg = unwrap(argWrapper)\\n\\t\\t\\tif type(arg) == \\\"table\\\" and arg.tag == \\\"string\\\" then\\n\\t\\t\\t\\tlocal text: string? = if arg.token then (arg.token :: any).text else arg.text\\n\\t\\t\\t\\tif text then\\n\\t\\t\\t\\t\\ttable.insert(result, text)\\n\\t\\t\\t\\tend\\n\\t\\t\\tend\\n\\t\\tend\\n\\tend\\n\\n\\t-- Append sub-path segments\\n\\tfor _, segment in subPathSegments do\\n\\t\\ttable.insert(result, segment)\\n\\tend\\n\\n\\treturn result\\nend\\n\\n--- Check if a CST call node is require() or TS.import() and record it.\\nlocal function checkCall(callNode: any, requires: { RequireInfo })\\n\\tif type(callNode) ~= \\\"table\\\" or callNode.tag ~= \\\"call\\\" then\\n\\t\\treturn\\n\\tend\\n\\n\\tlocal func = callNode.func\\n\\tif not func then\\n\\t\\treturn\\n\\tend\\n\\n\\t-- require(...) -- global or local\\n\\tif func.tag == \\\"global\\\" or func.tag == \\\"local\\\" then\\n\\t\\tlocal funcName = exprToString(func)\\n\\t\\tif funcName == \\\"require\\\" then\\n\\t\\t\\tlocal info: RequireInfo = {\\n\\t\\t\\t\\tkind = \\\"require\\\",\\n\\t\\t\\t\\targs = extractCallArgs(callNode.arguments),\\n\\t\\t\\t}\\n\\t\\t\\ttable.insert(requires, info)\\n\\t\\t\\treturn\\n\\t\\tend\\n\\tend\\n\\n\\t-- TS.import(...) -- indexname where base is \\\"TS\\\" and index is \\\"import\\\"\\n\\tif func.tag == \\\"indexname\\\" then\\n\\t\\tlocal baseName = exprToString(func.expression)\\n\\t\\tlocal indexName = getName(func.index)\\n\\n\\t\\tif baseName == \\\"TS\\\" and indexName == \\\"import\\\" then\\n\\t\\t\\t-- Check if any argument contains TS.getModule (external dep)\\n\\t\\t\\tlocal externalArgs: { string }? = nil\\n\\t\\t\\tif callNode.arguments and type(callNode.arguments) == \\\"table\\\" then\\n\\t\\t\\t\\tfor _, argWrapper in callNode.arguments do\\n\\t\\t\\t\\t\\tlocal arg = unwrap(argWrapper)\\n\\t\\t\\t\\t\\tlocal moduleArgs = extractGetModuleArgs(arg)\\n\\t\\t\\t\\t\\tif moduleArgs then\\n\\t\\t\\t\\t\\t\\texternalArgs = moduleArgs\\n\\t\\t\\t\\t\\t\\tbreak\\n\\t\\t\\t\\t\\tend\\n\\t\\t\\t\\tend\\n\\t\\t\\tend\\n\\n\\t\\t\\tif externalArgs then\\n\\t\\t\\t\\tlocal info: RequireInfo = {\\n\\t\\t\\t\\t\\tkind = \\\"ts_import_external\\\",\\n\\t\\t\\t\\t\\targs = externalArgs,\\n\\t\\t\\t\\t}\\n\\t\\t\\t\\ttable.insert(requires, info)\\n\\t\\t\\telse\\n\\t\\t\\t\\tlocal info: RequireInfo = {\\n\\t\\t\\t\\t\\tkind = \\\"ts_import\\\",\\n\\t\\t\\t\\t\\targs = extractCallArgs(callNode.arguments),\\n\\t\\t\\t\\t}\\n\\t\\t\\t\\ttable.insert(requires, info)\\n\\t\\t\\tend\\n\\t\\tend\\n\\tend\\nend\\n\\n--- Table for tracking top-level local references during expression walking.\\n--- Maps binding key (line:col) to the TopLevelLocal entry.\\ntype TopLevelTracker = {\\n\\t--- Map from binding key to TopLevelLocal entry.\\n\\tbindings: { [string]: TopLevelLocal },\\n\\t--- Precomputed line-start byte offsets.\\n\\tlineOffsets: { number },\\n}\\n\\n--- Forward declaration for mutual recursion (walkExpression uses walkBlock)\\nlocal walkBlock: (block: any, requires: { RequireInfo }, tracker: TopLevelTracker?, rootBlock: boolean?) -> (number, number)\\n\\n--------------------------------------------------------------------------------\\n-- Full expression walker\\n--------------------------------------------------------------------------------\\n\\n--- Walk an expression recursively, checking for calls at every position.\\n--- This is the key difference from the spike: we walk ALL expression positions.\\n--- When tracker is provided, also records byte offsets for top-level local refs.\\nlocal function walkExpression(expr: any, requires: { RequireInfo }, tracker: TopLevelTracker?)\\n\\tif type(expr) ~= \\\"table\\\" then\\n\\t\\treturn\\n\\tend\\n\\n\\t-- Track local references when a tracker is active\\n\\tif tracker and expr.tag == \\\"local\\\" and expr.kind == \\\"expr\\\" then\\n\\t\\tlocal binding = expr[\\\"local\\\"]\\n\\t\\tif type(binding) == \\\"table\\\" and binding.location then\\n\\t\\t\\tlocal key = bindingKey(binding.location)\\n\\t\\t\\tlocal entry = tracker.bindings[key]\\n\\t\\t\\tif entry then\\n\\t\\t\\t\\tlocal range = locationToByteRange(expr.location, tracker.lineOffsets)\\n\\t\\t\\t\\ttable.insert(entry.locations, range)\\n\\t\\t\\tend\\n\\t\\tend\\n\\tend\\n\\n\\t-- Always check if this expression itself is a call\\n\\tcheckCall(expr, requires)\\n\\n\\t-- Recurse into sub-expressions based on tag\\n\\tlocal tag = expr.tag\\n\\n\\tif tag == \\\"call\\\" then\\n\\t\\t-- Walk the function expression (could be a nested call)\\n\\t\\tif expr.func then\\n\\t\\t\\twalkExpression(expr.func, requires, tracker)\\n\\t\\tend\\n\\n\\t\\t-- Walk each argument (nested calls in arguments)\\n\\t\\tif expr.arguments and type(expr.arguments) == \\\"table\\\" then\\n\\t\\t\\tfor _, argWrapper in expr.arguments do\\n\\t\\t\\t\\twalkExpression(unwrap(argWrapper), requires, tracker)\\n\\t\\t\\tend\\n\\t\\tend\\n\\telseif tag == \\\"indexname\\\" or tag == \\\"index\\\" then\\n\\t\\tif expr.expression then\\n\\t\\t\\twalkExpression(expr.expression, requires, tracker)\\n\\t\\tend\\n\\n\\t\\tif tag == \\\"index\\\" and expr.index then\\n\\t\\t\\twalkExpression(unwrap(expr.index), requires, tracker)\\n\\t\\tend\\n\\telseif tag == \\\"binary\\\" then\\n\\t\\tif expr.lhsOperand then\\n\\t\\t\\twalkExpression(unwrap(expr.lhsOperand), requires, tracker)\\n\\t\\tend\\n\\n\\t\\tif expr.rhsOperand then\\n\\t\\t\\twalkExpression(unwrap(expr.rhsOperand), requires, tracker)\\n\\t\\tend\\n\\telseif tag == \\\"unary\\\" then\\n\\t\\tif expr.operand then\\n\\t\\t\\twalkExpression(unwrap(expr.operand), requires, tracker)\\n\\t\\tend\\n\\telseif tag == \\\"group\\\" then\\n\\t\\tif expr.expression then\\n\\t\\t\\twalkExpression(unwrap(expr.expression), requires, tracker)\\n\\t\\tend\\n\\telseif tag == \\\"table\\\" then\\n\\t\\tif expr.entries and type(expr.entries) == \\\"table\\\" then\\n\\t\\t\\tfor _, entry in expr.entries do\\n\\t\\t\\t\\tlocal e = unwrap(entry)\\n\\t\\t\\t\\tif type(e) == \\\"table\\\" then\\n\\t\\t\\t\\t\\t-- Table entry value\\n\\t\\t\\t\\t\\tif e.value then\\n\\t\\t\\t\\t\\t\\twalkExpression(unwrap(e.value), requires, tracker)\\n\\t\\t\\t\\t\\tend\\n\\n\\t\\t\\t\\t\\t-- Table entry key (indexexpr)\\n\\t\\t\\t\\t\\tif e.key then\\n\\t\\t\\t\\t\\t\\twalkExpression(unwrap(e.key), requires, tracker)\\n\\t\\t\\t\\t\\tend\\n\\t\\t\\t\\tend\\n\\t\\t\\tend\\n\\t\\tend\\n\\telseif tag == \\\"ifelse\\\" then\\n\\t\\t-- Ternary: if cond then a else b\\n\\t\\tif expr.condition then\\n\\t\\t\\twalkExpression(unwrap(expr.condition), requires, tracker)\\n\\t\\tend\\n\\n\\t\\tif expr.thenExpr then\\n\\t\\t\\twalkExpression(unwrap(expr.thenExpr), requires, tracker)\\n\\t\\tend\\n\\n\\t\\tif expr.elseExpr then\\n\\t\\t\\twalkExpression(unwrap(expr.elseExpr), requires, tracker)\\n\\t\\tend\\n\\n\\t\\t-- elseif chains in if-expressions\\n\\t\\tif expr.elseifs and type(expr.elseifs) == \\\"table\\\" then\\n\\t\\t\\tfor _, elseifEntry in expr.elseifs do\\n\\t\\t\\t\\tif type(elseifEntry) == \\\"table\\\" then\\n\\t\\t\\t\\t\\tif elseifEntry.condition then\\n\\t\\t\\t\\t\\t\\twalkExpression(unwrap(elseifEntry.condition), requires, tracker)\\n\\t\\t\\t\\t\\tend\\n\\n\\t\\t\\t\\t\\tif elseifEntry.thenExpr then\\n\\t\\t\\t\\t\\t\\twalkExpression(unwrap(elseifEntry.thenExpr), requires, tracker)\\n\\t\\t\\t\\t\\tend\\n\\t\\t\\t\\tend\\n\\t\\t\\tend\\n\\t\\tend\\n\\telseif tag == \\\"function\\\" then\\n\\t\\t-- Anonymous function expression: walk the body for require calls\\n\\t\\t-- but mark as non-root so locals inside aren't tracked as top-level.\\n\\t\\tif expr.body and type(expr.body) == \\\"table\\\" and expr.body.statements then\\n\\t\\t\\twalkBlock(expr.body, requires, tracker, false)\\n\\t\\tend\\n\\telseif tag == \\\"cast\\\" then\\n\\t\\tif expr.operand then\\n\\t\\t\\twalkExpression(unwrap(expr.operand), requires, tracker)\\n\\t\\tend\\n\\tend\\nend\\n\\n--------------------------------------------------------------------------------\\n-- Statement/block walker\\n--------------------------------------------------------------------------------\\n\\n--- Count local declarations in a CST statement.\\nlocal function countLocals(stat: any): number\\n\\tif stat.tag == \\\"local\\\" then\\n\\t\\tif stat.variables and type(stat.variables) == \\\"table\\\" then\\n\\t\\t\\treturn #stat.variables\\n\\t\\tend\\n\\n\\t\\treturn 1\\n\\telseif stat.tag == \\\"localfunction\\\" then\\n\\t\\treturn 1\\n\\tend\\n\\n\\treturn 0\\nend\\n\\n--- Register a top-level local declaration in the tracker.\\nlocal function registerTopLevelLocal(\\n\\ttracker: TopLevelTracker,\\n\\tname: string,\\n\\tdeclLocation: any\\n)\\n\\tlocal key = bindingKey(declLocation)\\n\\tlocal range = locationToByteRange(declLocation, tracker.lineOffsets)\\n\\tlocal entry: TopLevelLocal = {\\n\\t\\tname = name,\\n\\t\\tlocations = { range },\\n\\t}\\n\\ttracker.bindings[key] = entry\\nend\\n\\n--- Walk a block's statements, extracting requires and counting locals.\\n--- Uses iterative stack-based traversal for the block level to avoid stack limits.\\n--- When tracker is provided, records top-level local declarations from the root\\n--- block (first stack entry only) and all references throughout the tree.\\nwalkBlock = function(block: any, requires: { RequireInfo }, tracker: TopLevelTracker?, rootBlock: boolean?): (number, number)\\n\\tlocal maxLocals = 0\\n\\tlocal totalLocals = 0\\n\\tlocal isInitialRoot = if rootBlock == nil then true else rootBlock\\n\\n\\ttype StackEntry = { statements: any, locals: number, isRoot: boolean }\\n\\tlocal stack: { StackEntry } = {}\\n\\ttable.insert(stack, { statements = block.statements or {}, locals = 0, isRoot = isInitialRoot })\\n\\n\\twhile #stack > 0 do\\n\\t\\tlocal entry = table.remove(stack, #stack) :: StackEntry\\n\\t\\tlocal statements = entry.statements\\n\\t\\tlocal scopeLocals = entry.locals\\n\\t\\tlocal isRoot = entry.isRoot\\n\\n\\t\\tif type(statements) ~= \\\"table\\\" then\\n\\t\\t\\tcontinue\\n\\t\\tend\\n\\n\\t\\tfor _, stat in statements do\\n\\t\\t\\tif type(stat) ~= \\\"table\\\" then\\n\\t\\t\\t\\tcontinue\\n\\t\\t\\tend\\n\\n\\t\\t\\tlocal localCount = countLocals(stat)\\n\\t\\t\\tscopeLocals += localCount\\n\\t\\t\\ttotalLocals += localCount\\n\\n\\t\\t\\t-- Register top-level locals when tracking (root block only)\\n\\t\\t\\tif tracker and isRoot then\\n\\t\\t\\t\\tif stat.tag == \\\"local\\\" and stat.variables then\\n\\t\\t\\t\\t\\tfor _, varWrapper in stat.variables do\\n\\t\\t\\t\\t\\t\\tlocal v = unwrap(varWrapper)\\n\\t\\t\\t\\t\\t\\tif type(v) == \\\"table\\\" and v.name and v.location then\\n\\t\\t\\t\\t\\t\\t\\tlocal varName = getName(v.name)\\n\\t\\t\\t\\t\\t\\t\\tif varName then\\n\\t\\t\\t\\t\\t\\t\\t\\tregisterTopLevelLocal(tracker, varName, v.location)\\n\\t\\t\\t\\t\\t\\t\\tend\\n\\t\\t\\t\\t\\t\\tend\\n\\t\\t\\t\\t\\tend\\n\\t\\t\\t\\telseif stat.tag == \\\"localfunction\\\" and stat.name then\\n\\t\\t\\t\\t\\tlocal funcName = getName(stat.name.name or stat.name)\\n\\t\\t\\t\\t\\tlocal funcLoc = stat.name.location\\n\\t\\t\\t\\t\\tif funcName and funcLoc then\\n\\t\\t\\t\\t\\t\\tregisterTopLevelLocal(tracker, funcName, funcLoc)\\n\\t\\t\\t\\t\\tend\\n\\t\\t\\t\\tend\\n\\t\\t\\tend\\n\\n\\t\\t\\t-- Walk values in local/assign statements\\n\\t\\t\\tif (stat.tag == \\\"local\\\" or stat.tag == \\\"assign\\\") and stat.values then\\n\\t\\t\\t\\tfor _, valueWrapper in stat.values do\\n\\t\\t\\t\\t\\twalkExpression(unwrap(valueWrapper), requires, tracker)\\n\\t\\t\\t\\tend\\n\\t\\t\\tend\\n\\n\\t\\t\\t-- Walk assignment targets (for destructuring/indexing)\\n\\t\\t\\tif stat.tag == \\\"assign\\\" and stat.variables then\\n\\t\\t\\t\\tfor _, varWrapper in stat.variables do\\n\\t\\t\\t\\t\\twalkExpression(unwrap(varWrapper), requires, tracker)\\n\\t\\t\\t\\tend\\n\\t\\t\\tend\\n\\n\\t\\t\\t-- Expression statements (bare calls)\\n\\t\\t\\tif stat.tag == \\\"expression\\\" and stat.expression then\\n\\t\\t\\t\\twalkExpression(stat.expression, requires, tracker)\\n\\t\\t\\tend\\n\\n\\t\\t\\t-- Return statements (CST uses \\\"expressions\\\" not \\\"values\\\")\\n\\t\\t\\tif stat.tag == \\\"return\\\" and stat.expressions then\\n\\t\\t\\t\\tfor _, valueWrapper in stat.expressions do\\n\\t\\t\\t\\t\\twalkExpression(unwrap(valueWrapper), requires, tracker)\\n\\t\\t\\t\\tend\\n\\t\\t\\tend\\n\\n\\t\\t\\t-- For-in: walk the expression list\\n\\t\\t\\tif stat.tag == \\\"forin\\\" and stat.values then\\n\\t\\t\\t\\tfor _, valueWrapper in stat.values do\\n\\t\\t\\t\\t\\twalkExpression(unwrap(valueWrapper), requires, tracker)\\n\\t\\t\\t\\tend\\n\\t\\t\\tend\\n\\n\\t\\t\\t-- Numeric for: walk start/end/step\\n\\t\\t\\tif stat.tag == \\\"for\\\" then\\n\\t\\t\\t\\tif stat.from then\\n\\t\\t\\t\\t\\twalkExpression(unwrap(stat.from), requires, tracker)\\n\\t\\t\\t\\tend\\n\\n\\t\\t\\t\\tif stat.to then\\n\\t\\t\\t\\t\\twalkExpression(unwrap(stat.to), requires, tracker)\\n\\t\\t\\t\\tend\\n\\n\\t\\t\\t\\tif stat.step then\\n\\t\\t\\t\\t\\twalkExpression(unwrap(stat.step), requires, tracker)\\n\\t\\t\\t\\tend\\n\\t\\t\\tend\\n\\n\\t\\t\\t-- While/repeat: walk condition\\n\\t\\t\\tif stat.tag == \\\"while\\\" and stat.condition then\\n\\t\\t\\t\\twalkExpression(unwrap(stat.condition), requires, tracker)\\n\\t\\t\\tend\\n\\n\\t\\t\\tif stat.tag == \\\"repeat\\\" and stat.condition then\\n\\t\\t\\t\\twalkExpression(unwrap(stat.condition), requires, tracker)\\n\\t\\t\\tend\\n\\n\\t\\t\\t-- If statement conditions\\n\\t\\t\\tif stat.tag == \\\"conditional\\\" and stat.condition then\\n\\t\\t\\t\\twalkExpression(unwrap(stat.condition), requires, tracker)\\n\\t\\t\\tend\\n\\n\\t\\t\\t-- Non-local function statements: track name references.\\n\\t\\t\\t-- Handles both `function foo(...)` (direct local) and\\n\\t\\t\\t-- `function Foo.bar(...)` / `function Foo:baz(...)` (indexname\\n\\t\\t\\t-- where the base expression is a local reference).\\n\\t\\t\\tif stat.tag == \\\"function\\\" and stat.name and tracker then\\n\\t\\t\\t\\tlocal nameNode = stat.name\\n\\t\\t\\t\\t-- Direct local: `function foo(...)`\\n\\t\\t\\t\\tif nameNode.tag == \\\"local\\\" or (nameNode.token and nameNode[\\\"local\\\"]) then\\n\\t\\t\\t\\t\\tlocal nameToken = nameNode.token :: any\\n\\t\\t\\t\\t\\tlocal nameLocal = nameNode[\\\"local\\\"] :: any\\n\\t\\t\\t\\t\\tif nameToken and nameLocal and type(nameLocal) == \\\"table\\\" then\\n\\t\\t\\t\\t\\t\\tlocal declLoc = nameLocal.location\\n\\t\\t\\t\\t\\t\\tif nameToken.location and declLoc then\\n\\t\\t\\t\\t\\t\\t\\tlocal key = bindingKey(declLoc)\\n\\t\\t\\t\\t\\t\\t\\tlocal existing = tracker.bindings[key]\\n\\t\\t\\t\\t\\t\\t\\tif existing then\\n\\t\\t\\t\\t\\t\\t\\t\\tlocal range = locationToByteRange(nameToken.location, tracker.lineOffsets)\\n\\t\\t\\t\\t\\t\\t\\t\\ttable.insert(existing.locations, range)\\n\\t\\t\\t\\t\\t\\t\\tend\\n\\t\\t\\t\\t\\t\\tend\\n\\t\\t\\t\\t\\tend\\n\\t\\t\\t\\tend\\n\\t\\t\\t\\t-- Indexname: `function Foo.bar(...)` — walk the base expression\\n\\t\\t\\t\\tif nameNode.tag == \\\"indexname\\\" and nameNode.expression then\\n\\t\\t\\t\\t\\twalkExpression(nameNode.expression, requires, tracker)\\n\\t\\t\\t\\tend\\n\\t\\t\\tend\\n\\n\\t\\t\\t-- Push nested blocks onto the stack (never root)\\n\\t\\t\\tif stat.body and type(stat.body) == \\\"table\\\" and stat.body.statements then\\n\\t\\t\\t\\ttable.insert(stack, { statements = stat.body.statements, locals = 0, isRoot = false })\\n\\t\\t\\tend\\n\\n\\t\\t\\tif stat.thenBlock and type(stat.thenBlock) == \\\"table\\\" and stat.thenBlock.statements then\\n\\t\\t\\t\\ttable.insert(stack, { statements = stat.thenBlock.statements, locals = 0, isRoot = false })\\n\\t\\t\\tend\\n\\n\\t\\t\\tif stat.elseBlock and type(stat.elseBlock) == \\\"table\\\" and stat.elseBlock.statements then\\n\\t\\t\\t\\ttable.insert(stack, { statements = stat.elseBlock.statements, locals = 0, isRoot = false })\\n\\t\\t\\tend\\n\\n\\t\\t\\tif stat.elseifs and type(stat.elseifs) == \\\"table\\\" then\\n\\t\\t\\t\\tfor _, elseif_ in stat.elseifs do\\n\\t\\t\\t\\t\\tif type(elseif_) == \\\"table\\\" then\\n\\t\\t\\t\\t\\t\\t-- Walk elseif condition\\n\\t\\t\\t\\t\\t\\tif elseif_.condition then\\n\\t\\t\\t\\t\\t\\t\\twalkExpression(unwrap(elseif_.condition), requires, tracker)\\n\\t\\t\\t\\t\\t\\tend\\n\\n\\t\\t\\t\\t\\t\\tlocal thenBlock = elseif_.thenBlock\\n\\t\\t\\t\\t\\t\\tif type(thenBlock) == \\\"table\\\" and thenBlock.statements then\\n\\t\\t\\t\\t\\t\\t\\ttable.insert(stack, { statements = thenBlock.statements, locals = 0, isRoot = false })\\n\\t\\t\\t\\t\\t\\tend\\n\\t\\t\\t\\t\\tend\\n\\t\\t\\t\\tend\\n\\t\\t\\tend\\n\\n\\t\\t\\t-- Function bodies (localfunction and function statements)\\n\\t\\t\\tif stat.func and type(stat.func) == \\\"table\\\" then\\n\\t\\t\\t\\tlocal funcBody = stat.func.body\\n\\t\\t\\t\\tif type(funcBody) == \\\"table\\\" and funcBody.statements then\\n\\t\\t\\t\\t\\ttable.insert(stack, { statements = funcBody.statements, locals = 0, isRoot = false })\\n\\t\\t\\t\\tend\\n\\t\\t\\tend\\n\\t\\tend\\n\\n\\t\\tif scopeLocals > maxLocals then\\n\\t\\t\\tmaxLocals = scopeLocals\\n\\t\\tend\\n\\tend\\n\\n\\treturn maxLocals, totalLocals\\nend\\n\\n--------------------------------------------------------------------------------\\n-- Per-file analysis\\n--------------------------------------------------------------------------------\\n\\nlocal function analyzeSource(source: string, filePath: string): (any, number, number)\\n\\tlocal parseStart = os.clock()\\n\\tlocal parseResult = syntax.parse(source)\\n\\tlocal parseTime = os.clock() - parseStart\\n\\n\\tlocal requires: { RequireInfo } = {}\\n\\n\\t-- Build tracker for top-level local collection\\n\\tlocal lineOffsets = buildLineOffsets(source)\\n\\tlocal tracker: TopLevelTracker = {\\n\\t\\tbindings = {},\\n\\t\\tlineOffsets = lineOffsets,\\n\\t}\\n\\n\\tlocal walkStart = os.clock()\\n\\tlocal maxLocals, totalLocals = walkBlock(parseResult.root, requires, tracker)\\n\\tlocal walkTime = os.clock() - walkStart\\n\\n\\t-- Serialize requires for JSON\\n\\tlocal reqs: json.Array = {} :: any\\n\\tfor _, req in requires do\\n\\t\\ttable.insert(reqs :: any, {\\n\\t\\t\\tkind = req.kind,\\n\\t\\t\\targs = req.args :: json.Array,\\n\\t\\t})\\n\\tend\\n\\n\\t-- Serialize topLevelLocals for JSON\\n\\tlocal topLocals: json.Array = {} :: any\\n\\tfor _, entry in tracker.bindings do\\n\\t\\tlocal locs: json.Array = {} :: any\\n\\t\\tfor _, loc in entry.locations do\\n\\t\\t\\ttable.insert(locs :: any, {\\n\\t\\t\\t\\tstart = loc.start,\\n\\t\\t\\t\\t[\\\"end\\\"] = loc[\\\"end\\\"],\\n\\t\\t\\t})\\n\\t\\tend\\n\\t\\ttable.insert(topLocals :: any, {\\n\\t\\t\\tname = entry.name,\\n\\t\\t\\tlocations = locs,\\n\\t\\t})\\n\\tend\\n\\n\\tlocal analysis = {\\n\\t\\tpath = filePath,\\n\\t\\trequires = reqs,\\n\\t\\tmaxLocalsInScope = maxLocals,\\n\\t\\ttotalLocals = totalLocals,\\n\\t\\ttopLevelLocals = topLocals,\\n\\t}\\n\\n\\treturn analysis, parseTime, walkTime\\nend\\n\\n--------------------------------------------------------------------------------\\n-- Main\\n--------------------------------------------------------------------------------\\n\\n-- Discover files\\nlocal discoverStart = os.clock()\\nlocal files: { string } = {}\\ndiscoverFiles(luauRoot, luauRoot, files)\\nlocal discoverTime = os.clock() - discoverStart\\n\\n-- Parse + extract, stream NDJSON\\nlocal totalStart = os.clock()\\nlocal totalParseMs = 0\\nlocal totalWalkMs = 0\\n\\nfor _, relativePath in files do\\n\\tlocal fullPath = luauRoot .. \\\"/\\\" .. relativePath\\n\\tlocal source = fs.readFileToString(fullPath)\\n\\tlocal analysis, parseTime, walkTime = analyzeSource(source, relativePath)\\n\\n\\ttotalParseMs += parseTime * 1000\\n\\ttotalWalkMs += walkTime * 1000\\n\\n\\tprint(json.serialize(analysis))\\nend\\n\\nlocal totalTime = os.clock() - totalStart\\n\\n-- Final line: timing summary prefixed so Node can distinguish it\\nprint(\\\"__TIMING__\\\" .. json.serialize({\\n\\tfileCount = #files,\\n\\tdiscoverMs = discoverTime * 1000,\\n\\tparseMs = totalParseMs,\\n\\twalkMs = totalWalkMs,\\n\\twallMs = totalTime * 1000,\\n}))\\n\";","import { type, type Type } from \"arktype\";\n\n/** A single require/import entry extracted from a Luau file. */\nexport interface RequireInfo {\n\t/** Raw argument strings from the call expression. */\n\targs: Array<string>;\n\t/** Whether this was a `require()`, `TS.import()`, or external `TS.import(TS.getModule(...))` call. */\n\tkind: \"require\" | \"ts_import\" | \"ts_import_external\";\n}\n\n/** Schema for a single require/import entry extracted from a Luau file. */\nexport const requireInfoSchema: Type<RequireInfo> = type({\n\targs: \"string[]\",\n\tkind: \"'require' | 'ts_import' | 'ts_import_external'\",\n}).as<RequireInfo>();\n\n/** Byte offset range within a source file. */\nexport interface SourceLocation {\n\t/** End byte offset (exclusive). */\n\tend: number;\n\t/** Start byte offset (inclusive). */\n\tstart: number;\n}\n\n/** A top-level local variable and all locations where it appears. */\nexport interface TopLevelLocal {\n\t/** The variable name. */\n\tname: string;\n\t/** Every occurrence of this variable (declaration + references). */\n\tlocations: Array<SourceLocation>;\n}\n\n/** Schema for a top-level local binding with source locations. */\nconst topLevelLocalSchema: Type<TopLevelLocal> = type({\n\tname: \"string\",\n\tlocations: type({ end: \"number\", start: \"number\" }).array(),\n}).as<TopLevelLocal>();\n\n/** Per-file extraction data from the Lute analysis. */\nexport interface FileAnalysis {\n\t/** Maximum number of local variables in any single scope. */\n\tmaxLocalsInScope: number;\n\t/** Posix-normalized file path relative to the Luau root. */\n\tpath: string;\n\t/** Module source with locals renamed using a prefix. Only present when rename prefix is provided. */\n\trenamedSource?: string;\n\t/** All require/import calls found in this file. */\n\trequires: Array<RequireInfo>;\n\t/** Top-level local declarations with byte offset locations for renaming. */\n\ttopLevelLocals?: Array<TopLevelLocal>;\n\t/** Total number of local variable declarations across all scopes. */\n\ttotalLocals: number;\n}\n\n/** Schema for per-file extraction data from the Lute analysis. */\nexport const fileAnalysisSchema: Type<FileAnalysis> = type({\n\t\"maxLocalsInScope\": \"number\",\n\t\"path\": \"string\",\n\t\"renamedSource?\": \"string\",\n\t\"requires\": requireInfoSchema.array(),\n\t\"topLevelLocals?\": topLevelLocalSchema.array(),\n\t\"totalLocals\": \"number\",\n}).as<FileAnalysis>();\n\n/** Timing summary emitted as the final NDJSON line. */\nexport interface TimingSummary {\n\t/** Time spent discovering files in milliseconds. */\n\tdiscoverMs: number;\n\t/** Number of files processed. */\n\tfileCount: number;\n\t/** Time spent parsing files in milliseconds. */\n\tparseMs: number;\n\t/** Time spent walking ASTs in milliseconds. */\n\twalkMs: number;\n\t/** Total wall-clock time in milliseconds. */\n\twallMs: number;\n}\n\n/** Schema for the timing summary emitted as the final NDJSON line. */\nexport const timingSummarySchema: Type<TimingSummary> = type({\n\tdiscoverMs: \"number\",\n\tfileCount: \"number\",\n\tparseMs: \"number\",\n\twalkMs: \"number\",\n\twallMs: \"number\",\n}).as<TimingSummary>();\n","import { type } from \"arktype\";\n\nimport {\n\ttype FileAnalysis,\n\tfileAnalysisSchema,\n\ttype TimingSummary,\n\ttimingSummarySchema,\n} from \"./schema.ts\";\n\nconst TIMING_PREFIX = \"__TIMING__\";\n\n/** Result of parsing NDJSON output from the Lute extraction script. */\nexport interface ExtractionResult {\n\t/** Per-file analysis data. */\n\tanalyses: Array<FileAnalysis>;\n\t/** Optional timing summary from the final line. */\n\ttiming?: TimingSummary;\n}\n\n/**\n * Parse NDJSON stdout from the Lute extraction process.\n *\n * @param stdout - Raw stdout string with one JSON object per line.\n * @returns Parsed extraction result with file analyses and optional timing.\n */\nexport function parseNdjsonOutput(stdout: string): ExtractionResult {\n\tconst lines = stdout.split(\"\\n\");\n\tconst analyses: Array<FileAnalysis> = [];\n\tlet timing: TimingSummary | undefined;\n\n\tfor (const line of lines) {\n\t\tconst trimmed = line.trim();\n\t\tif (trimmed === \"\") {\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (trimmed.startsWith(TIMING_PREFIX)) {\n\t\t\tconst json = trimmed.slice(TIMING_PREFIX.length);\n\t\t\tconst parsed = safeParse(json, trimmed);\n\t\t\tconst result = timingSummarySchema(parsed);\n\t\t\tif (result instanceof type.errors) {\n\t\t\t\tthrow new Error(`Invalid timing data on line: ${trimmed}\\n${result.summary}`);\n\t\t\t}\n\n\t\t\ttiming = result;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst parsed = safeParse(trimmed, trimmed);\n\t\tconst result = fileAnalysisSchema(parsed);\n\t\tif (result instanceof type.errors) {\n\t\t\tthrow new Error(`Invalid file analysis on line: ${trimmed}\\n${result.summary}`);\n\t\t}\n\n\t\tanalyses.push(result);\n\t}\n\n\treturn { analyses, timing };\n}\n\n/**\n * Parse a JSON string, throwing a descriptive error on failure.\n *\n * @param json - JSON string to parse.\n * @param originalLine - Original line for error context.\n * @returns The parsed value.\n */\nfunction safeParse(json: string, originalLine: string): unknown {\n\ttry {\n\t\treturn JSON.parse(json) as unknown;\n\t} catch {\n\t\tthrow new Error(`Invalid JSON on line: ${originalLine}`);\n\t}\n}\n","import { spawnLute, writeTemporaryLuauScript } from \"@isentinel/luau-ast\";\n\nimport extractLuauSource from \"./extract.luau\";\nimport type { ExtractionResult } from \"./ndjson-parser.ts\";\nimport { parseNdjsonOutput } from \"./ndjson-parser.ts\";\n\n/**\n * Spawn the Lute extraction process and return parsed results.\n *\n * @param luauRoot - Root directory containing Luau source files.\n * @param scriptPath - Optional override path to the extract.luau script.\n * @returns Parsed extraction result with file analyses and optional timing.\n */\nexport function spawnLuteExtractor(luauRoot: string, scriptPath?: string): ExtractionResult {\n\tconst resolvedScript = scriptPath ?? writeTemporaryLuauScript(extractLuauSource, \"extract\");\n\n\tconst stdout = spawnLute({\n\t\targs: [luauRoot],\n\t\tmaxBuffer: 10 * 1024 * 1024,\n\t\tscriptPath: resolvedScript,\n\t\ttimeout: 30_000,\n\t});\n\n\treturn parseNdjsonOutput(stdout);\n}\n","import type { DependencyGraph } from \"./dependency-graph.ts\";\n\n/** Default soft cap for locals per chunk. */\nconst DEFAULT_SOFT_CAP = 150;\n\n/** Default hard cap for locals per chunk. */\nconst DEFAULT_HARD_CAP = 180;\n\n/** Fan-in threshold: modules imported by this many or more get penalized. */\nconst HIGH_FAN_IN_THRESHOLD = 3;\n\n/** Modules with fewer locals than this get a merge bonus. */\nconst SMALL_MODULE_THRESHOLD = 10;\n\n/** Modules with more locals than this get a merge penalty. */\nconst LARGE_MODULE_THRESHOLD = 50;\n\n/** A chunk of modules that will share a scope in the bundled output. */\nexport interface Chunk {\n\t/** Unique chunk index. */\n\tid: number;\n\t/** Module file paths in dependency order (deps first). */\n\tmodules: Array<string>;\n\t/** Total estimated locals in this chunk. */\n\ttotalLocals: number;\n}\n\n/** Result of chunk assignment. */\nexport interface ChunkAssignment {\n\t/** Ordered chunks (dependency order between chunks). */\n\tchunks: Array<Chunk>;\n\t/** Map from module file path to chunk ID. */\n\tmoduleToChunk: ReadonlyMap<string, number>;\n}\n\n/** Options for {@link assignChunks}. */\ninterface ChunkAssignerOptions {\n\t/** The dependency graph. */\n\tgraph: DependencyGraph;\n\t/** Hard cap for locals per chunk (default 180). */\n\thardCap?: number;\n\t/** Map from file path to top-level local count (not totalLocals). */\n\tlocalCounts: ReadonlyMap<string, number>;\n\t/** Soft cap for locals per chunk (default 150). */\n\tsoftCap?: number;\n}\n\n/**\n * Compute merge score for a module against the current chunk.\n *\n * @param modulePath - Path of the module to score.\n * @param moduleLocals - Number of locals in the module.\n * @param fanIn - Fan-in count for the module.\n * @param chunkModules - Set of module paths already in the chunk.\n * @param graph - The dependency graph.\n * @returns Numeric merge score (positive = prefer merge, negative = prefer separate).\n */\ninterface MergeScoreInput {\n\tchunkModules: ReadonlySet<string>;\n\tfanIn: number;\n\tgraph: DependencyGraph;\n\tmoduleLocals: number;\n\tmodulePath: string;\n}\n\n/** Mutable state for the chunk packing loop. */\ninterface PackState {\n\tchunks: Array<Chunk>;\n\tcurrentChunk: Chunk | undefined;\n\tcurrentChunkModules: Set<string>;\n\tmoduleToChunk: Map<string, number>;\n}\n\n/** Input for the packing loop. */\ninterface PackInput {\n\tfanIn: ReadonlyMap<string, number>;\n\tgraph: DependencyGraph;\n\thardCap: number;\n\tlocalCounts: ReadonlyMap<string, number>;\n\tmodules: ReadonlyArray<string>;\n\tsoftCap: number;\n}\n\n/** Input for packing a single module. */\ninterface PackOneInput {\n\tfanIn: ReadonlyMap<string, number>;\n\tgraph: DependencyGraph;\n\thardCap: number;\n\tlocalCounts: ReadonlyMap<string, number>;\n\tmodulePath: string;\n\tsoftCap: number;\n}\n\n/** Options for creating a new chunk. */\ninterface CreateChunkOptions {\n\tchunkId: number;\n\tmoduleLocals: number;\n\tmodulePath: string;\n}\n\n/** Options for starting a new chunk in the pack state. */\ninterface StartChunkOptions {\n\tmoduleLocals: number;\n\tmodulePath: string;\n}\n\n/**\n * Assign modules to chunks using greedy DFS packing with merge scoring.\n * Walks the dependency graph in post-order, greedily merging modules into\n * chunks based on fan-in, module size, and local variable budgets.\n *\n * @param options - Chunk assignment configuration.\n * @returns The chunk assignment with ordered chunks and module mapping.\n */\nexport function assignChunks(options: ChunkAssignerOptions): ChunkAssignment {\n\tconst { graph, localCounts } = options;\n\tconst softCap = options.softCap ?? DEFAULT_SOFT_CAP;\n\tconst hardCap = options.hardCap ?? DEFAULT_HARD_CAP;\n\n\tconst fanIn = computeFanIn(graph);\n\tconst dfsOrder = dfsOrderDfs(graph);\n\tconst nonEntryModules = dfsOrder.filter((module_) => module_ !== graph.entryPoint);\n\n\tconst state = packModules({\n\t\tfanIn,\n\t\tgraph,\n\t\thardCap,\n\t\tlocalCounts,\n\t\tmodules: nonEntryModules,\n\t\tsoftCap,\n\t});\n\n\t// Entry point always gets its own chunk (last)\n\t// eslint-disable-next-line ts/no-non-null-assertion -- entry always in localCounts\n\tconst entryLocals = localCounts.get(graph.entryPoint)!;\n\tconst entryChunk = createChunk({\n\t\tchunkId: state.chunks.length,\n\t\tmoduleLocals: entryLocals,\n\t\tmodulePath: graph.entryPoint,\n\t});\n\tstate.chunks.push(entryChunk);\n\tstate.moduleToChunk.set(graph.entryPoint, entryChunk.id);\n\n\treturn { chunks: state.chunks, moduleToChunk: state.moduleToChunk };\n}\n\n/**\n * Create a new chunk with the given module.\n *\n * @param options - Chunk creation options.\n * @returns The new chunk.\n */\nfunction createChunk(options: CreateChunkOptions): Chunk {\n\treturn {\n\t\tid: options.chunkId,\n\t\tmodules: [options.modulePath],\n\t\ttotalLocals: options.moduleLocals + 1,\n\t};\n}\n\n/**\n * Start a new chunk in the pack state.\n *\n * @param state - Mutable pack state.\n * @param options - Module to start the chunk with.\n */\nfunction startChunk(state: PackState, options: StartChunkOptions): void {\n\tconst chunk = createChunk({ chunkId: state.chunks.length, ...options });\n\tstate.currentChunk = chunk;\n\tstate.currentChunkModules = new Set([options.modulePath]);\n\tstate.chunks.push(chunk);\n\tstate.moduleToChunk.set(options.modulePath, chunk.id);\n}\n\nfunction computeDependencyBonus({\n\tchunkModules,\n\tgraph,\n\tmodulePath,\n}: {\n\tchunkModules: ReadonlySet<string>;\n\tgraph: DependencyGraph;\n\tmodulePath: string;\n}): number {\n\t// eslint-disable-next-line ts/no-non-null-assertion -- all scored modules exist in graph\n\tconst node = graph.nodes.get(modulePath)!;\n\n\tfor (const edge of node.imports) {\n\t\tif (chunkModules.has(edge.resolvedPath)) {\n\t\t\treturn 1;\n\t\t}\n\t}\n\n\treturn 0;\n}\n\nfunction computeMergeScore(input: MergeScoreInput): number {\n\tconst { chunkModules, fanIn, graph, moduleLocals, modulePath } = input;\n\tlet score = 0;\n\n\t// Single parent (only one importer) → positive\n\tif (fanIn === 1) {\n\t\tscore += 2;\n\t}\n\n\t// Small module (< 10 locals) → positive\n\tif (moduleLocals < SMALL_MODULE_THRESHOLD) {\n\t\tscore += 1;\n\t}\n\n\tscore += computeDependencyBonus({ chunkModules, graph, modulePath });\n\n\t// High fan-in (3+ importers) → negative\n\tif (fanIn >= HIGH_FAN_IN_THRESHOLD) {\n\t\tscore -= 2;\n\t}\n\n\t// Large module (> 50 locals) → negative\n\tif (moduleLocals > LARGE_MODULE_THRESHOLD) {\n\t\tscore -= 1;\n\t}\n\n\treturn score;\n}\n\n/**\n * Try to pack one module into the current chunk or start a new one.\n *\n * @param state - Mutable pack state.\n * @param input - Module packing parameters.\n */\nfunction packOneModule(state: PackState, input: PackOneInput): void {\n\tconst { fanIn, graph, hardCap, localCounts, modulePath, softCap } = input;\n\t// eslint-disable-next-line ts/no-non-null-assertion -- all modules have local counts from extraction\n\tconst moduleLocals = localCounts.get(modulePath)!;\n\n\tif (state.currentChunk === undefined) {\n\t\tstartChunk(state, { moduleLocals, modulePath });\n\t\treturn;\n\t}\n\n\tconst estimated = state.currentChunk.totalLocals + moduleLocals + 1;\n\tconst score = computeMergeScore({\n\t\tchunkModules: state.currentChunkModules,\n\t\t// eslint-disable-next-line ts/no-non-null-assertion -- all modules have computed fan-in\n\t\tfanIn: fanIn.get(modulePath)!,\n\t\tgraph,\n\t\tmoduleLocals,\n\t\tmodulePath,\n\t});\n\n\tconst shouldMerge = (estimated <= softCap && score >= 0) || (estimated <= hardCap && score > 0);\n\n\tif (shouldMerge) {\n\t\tstate.currentChunk.modules.push(modulePath);\n\t\tstate.currentChunk.totalLocals = estimated;\n\t\tstate.currentChunkModules.add(modulePath);\n\t\tstate.moduleToChunk.set(modulePath, state.currentChunk.id);\n\t} else {\n\t\tstartChunk(state, { moduleLocals, modulePath });\n\t}\n}\n\n/**\n * Pack modules into chunks using merge scoring.\n *\n * @param input - Packing configuration.\n * @returns Mutable pack state with chunks and mappings.\n */\nfunction packModules(input: PackInput): PackState {\n\tconst { fanIn, graph, hardCap, localCounts, modules, softCap } = input;\n\tconst state: PackState = {\n\t\tchunks: [],\n\t\tcurrentChunk: undefined,\n\t\tcurrentChunkModules: new Set(),\n\t\tmoduleToChunk: new Map(),\n\t};\n\n\tfor (const modulePath of modules) {\n\t\tpackOneModule(state, { fanIn, graph, hardCap, localCounts, modulePath, softCap });\n\t}\n\n\treturn state;\n}\n\n/**\n * Compute fan-in for each module (how many other modules import it).\n *\n * @param graph - The dependency graph.\n * @returns Map from file path to fan-in count.\n */\nfunction computeFanIn(graph: DependencyGraph): ReadonlyMap<string, number> {\n\tconst fanIn = new Map<string, number>();\n\tfor (const path of graph.nodes.keys()) {\n\t\tfanIn.set(path, 0);\n\t}\n\n\tfor (const node of graph.nodes.values()) {\n\t\tfor (const edge of node.imports) {\n\t\t\t// eslint-disable-next-line ts/no-non-null-assertion -- all nodes initialized to 0 above\n\t\t\tfanIn.set(edge.resolvedPath, fanIn.get(edge.resolvedPath)! + 1);\n\t\t}\n\t}\n\n\treturn fanIn;\n}\n\n/**\n * Walk the graph in dfsOrder DFS from the entry point.\n * Visits leaves first, entry last.\n *\n * @param graph - The dependency graph.\n * @returns Module paths in dfsOrder.\n */\nfunction dfsOrderDfs(graph: DependencyGraph): Array<string> {\n\tconst visited = new Set<string>();\n\tconst order: Array<string> = [];\n\n\tfunction visit(path: string): void {\n\t\tif (visited.has(path)) {\n\t\t\treturn;\n\t\t}\n\n\t\tvisited.add(path);\n\t\t// eslint-disable-next-line ts/no-non-null-assertion -- DFS only visits paths in graph.nodes\n\t\tconst node = graph.nodes.get(path)!;\n\t\tfor (const edge of node.imports) {\n\t\t\tvisit(edge.resolvedPath);\n\t\t}\n\n\t\torder.push(path);\n\t}\n\n\tvisit(graph.entryPoint);\n\n\treturn order;\n}\n","import type { FileAnalysis } from \"../extraction/schema.ts\";\nimport type { PathRequireResolution } from \"../resolution/path-require-resolver.ts\";\n\n/** Metadata for a single import call extracted from a module. */\nexport interface ImportEdge {\n\t/** Raw TS.import args from extraction. */\n\targs: ReadonlyArray<string>;\n\t/** The kind of import this edge represents. */\n\tkind: \"require\" | \"ts_import\";\n\t/** Resolved filesystem path of the target module. */\n\tresolvedPath: string;\n}\n\n/** A vertex in the module dependency graph. */\nexport interface ModuleNode {\n\t/** Filesystem path (posix-normalized). */\n\tfilePath: string;\n\t/** Resolved imports from this module. */\n\timports: Array<ImportEdge>;\n}\n\n/** Resolved dependency graph with topological ordering. */\nexport interface DependencyGraph {\n\t/** Filesystem path of the entry module. */\n\tentryPoint: string;\n\t/** All reachable modules keyed by file path. */\n\tnodes: ReadonlyMap<string, ModuleNode>;\n\t/** Topologically sorted file paths (deps before dependents, entry last). */\n\tsorted: ReadonlyArray<string>;\n}\n\n/** Configuration for {@link buildDependencyGraph}. */\ninterface BuildGraphOptions {\n\t/** All file analyses from Lute extraction. */\n\tanalyses: ReadonlyArray<FileAnalysis>;\n\t/** Filesystem path of the entry module. */\n\tentryFilePath: string;\n\t/**\n\t * Resolves a `TS.import(...)` call to a target path. Required when sources\n\t * contain TS.import calls; may be omitted in path-only bundles.\n\t */\n\tresolve?: (fromFilePath: string, tsImportArgs: ReadonlyArray<string>) => string;\n\t/**\n\t * Resolves a path-style `require(...)` call. When omitted, plain `require()`\n\t * calls cause an error (legacy behavior for ts-import-only bundles).\n\t */\n\tresolveRequire?: (fromFilePath: string, requireString: string) => PathRequireResolution;\n}\n\n/** Shared state for DFS traversal with cycle detection. */\ninterface DfsState {\n\t/** Indexed analyses for O(1) lookup. */\n\tanalysisByPath: ReadonlyMap<string, FileAnalysis>;\n\t/** Accumulated module nodes. */\n\tnodes: Map<string, ModuleNode>;\n\t/** Paths currently on the DFS stack (for cycle detection). */\n\tonStack: Set<string>;\n\t/** Resolver function for TS.import calls. */\n\tresolve: BuildGraphOptions[\"resolve\"];\n\t/** Resolver function for plain require() calls (path mode). */\n\tresolveRequire: BuildGraphOptions[\"resolveRequire\"];\n\t/** Fully visited paths. */\n\tvisited: Set<string>;\n}\n\n/**\n * Build a directed dependency graph from file analyses and module resolution.\n * Discovers reachable modules via DFS, resolves TS.import calls, and produces\n * a topologically sorted ordering.\n *\n * @param options - Graph building configuration.\n * @returns The dependency graph with topologically sorted modules.\n */\nexport function buildDependencyGraph(options: BuildGraphOptions): DependencyGraph {\n\tconst { analyses, entryFilePath, resolve, resolveRequire } = options;\n\tconst analysisByPath = indexAnalyses(analyses);\n\tconst nodes = discoverReachableModules({\n\t\tanalysisByPath,\n\t\tentryFilePath,\n\t\tresolve,\n\t\tresolveRequire,\n\t});\n\tconst sorted = topologicalSort(nodes);\n\n\treturn { entryPoint: entryFilePath, nodes, sorted };\n}\n\n/**\n * Index file analyses by path for O(1) lookup.\n *\n * @param analyses - Array of file analyses to index.\n * @returns Map from file path to its analysis.\n */\nfunction indexAnalyses(analyses: ReadonlyArray<FileAnalysis>): Map<string, FileAnalysis> {\n\tconst map = new Map<string, FileAnalysis>();\n\tfor (const entry of analyses) {\n\t\tmap.set(entry.path, entry);\n\t}\n\n\treturn map;\n}\n\n/* c8 ignore start -- only true-branch path is reached in practice; defensive */\nfunction stripQuotes(text: string): string {\n\tif (text.length >= 2 && text.startsWith('\"') && text.endsWith('\"')) {\n\t\treturn text.slice(1, -1);\n\t}\n\n\treturn text;\n}\n/* c8 ignore stop */\n\nfunction resolvePlainRequire(\n\trequest: FileAnalysis[\"requires\"][number],\n\toptions: { filePath: string; resolveRequire: BuildGraphOptions[\"resolveRequire\"] },\n): ImportEdge | undefined {\n\tif (request.args.length === 0) {\n\t\treturn undefined;\n\t}\n\n\tif (options.resolveRequire === undefined) {\n\t\tthrow new Error(`plain require() not supported in bundled modules: ${options.filePath}`);\n\t}\n\n\t// eslint-disable-next-line ts/no-non-null-assertion -- args.length guarded above\n\tconst requireString = stripQuotes(request.args[0]!);\n\tconst result = options.resolveRequire(options.filePath, requireString);\n\tif (result.kind === \"passthrough\") {\n\t\treturn undefined;\n\t}\n\n\treturn { args: request.args, kind: \"require\", resolvedPath: result.path };\n}\n\n/**\n * Resolve a single require/import request into an optional ImportEdge.\n *\n * @param request - The extracted require/import metadata.\n * @param options - File path and resolver functions.\n * @returns The resolved edge, or `undefined` for passthrough/external imports.\n */\nfunction resolveSingleImport(\n\trequest: FileAnalysis[\"requires\"][number],\n\toptions: {\n\t\tfilePath: string;\n\t\tresolve: BuildGraphOptions[\"resolve\"];\n\t\tresolveRequire: BuildGraphOptions[\"resolveRequire\"];\n\t},\n): ImportEdge | undefined {\n\tif (request.kind === \"ts_import_external\") {\n\t\treturn undefined;\n\t}\n\n\tif (request.kind === \"require\") {\n\t\treturn resolvePlainRequire(request, options);\n\t}\n\n\t/* c8 ignore next 5 -- defensive: ts_import without resolve is a misconfiguration the dispatch path normally rules out */\n\tif (options.resolve === undefined) {\n\t\tthrow new Error(\n\t\t\t`TS.import resolution invoked without a resolve callback: ${options.filePath}`,\n\t\t);\n\t}\n\n\tconst resolvedPath = options.resolve(options.filePath, request.args);\n\treturn { args: request.args, kind: \"ts_import\", resolvedPath };\n}\n\n/**\n * Resolve every require/import in a file's analysis into import edges.\n *\n * @param options - File analysis and resolver functions.\n * @returns Array of resolved import edges.\n */\nfunction resolveImports(options: {\n\tfileAnalysis: FileAnalysis;\n\tfilePath: string;\n\tresolve: BuildGraphOptions[\"resolve\"];\n\tresolveRequire: BuildGraphOptions[\"resolveRequire\"];\n}): Array<ImportEdge> {\n\tconst imports: Array<ImportEdge> = [];\n\tfor (const request of options.fileAnalysis.requires) {\n\t\tconst edge = resolveSingleImport(request, options);\n\t\tif (edge !== undefined) {\n\t\t\timports.push(edge);\n\t\t}\n\t}\n\n\treturn imports;\n}\n\n/**\n * DFS visit a single node: resolve imports, detect cycles, recurse.\n *\n * @param state - Shared DFS traversal state.\n * @param filePath - Path of the module to visit.\n */\nfunction dfsVisit(state: DfsState, filePath: string): void {\n\tif (state.visited.has(filePath)) {\n\t\treturn;\n\t}\n\n\tif (state.onStack.has(filePath)) {\n\t\tconst cycle = [...state.onStack, filePath];\n\t\tconst start = cycle.indexOf(filePath);\n\t\tthrow new Error(`Circular dependency detected: ${cycle.slice(start).join(\" -> \")}`);\n\t}\n\n\tconst fileAnalysis = state.analysisByPath.get(filePath);\n\tif (!fileAnalysis) {\n\t\tthrow new Error(`No analysis found for module: ${filePath}`);\n\t}\n\n\tstate.onStack.add(filePath);\n\n\tconst imports = resolveImports({\n\t\tfileAnalysis,\n\t\tfilePath,\n\t\tresolve: state.resolve,\n\t\tresolveRequire: state.resolveRequire,\n\t});\n\tfor (const edge of imports) {\n\t\tdfsVisit(state, edge.resolvedPath);\n\t}\n\n\tstate.onStack.delete(filePath);\n\tstate.visited.add(filePath);\n\tstate.nodes.set(filePath, { filePath, imports });\n}\n\n/**\n * DFS from the entry point to discover all reachable modules,\n * detect cycles, and build the adjacency list.\n *\n * @param options - Entry path, indexed analyses, and resolver function.\n * @returns Map of reachable module nodes.\n */\nfunction discoverReachableModules(options: {\n\tanalysisByPath: ReadonlyMap<string, FileAnalysis>;\n\tentryFilePath: string;\n\tresolve: BuildGraphOptions[\"resolve\"];\n\tresolveRequire: BuildGraphOptions[\"resolveRequire\"];\n}): Map<string, ModuleNode> {\n\tconst state: DfsState = {\n\t\tanalysisByPath: options.analysisByPath,\n\t\tnodes: new Map(),\n\t\tonStack: new Set(),\n\t\tresolve: options.resolve,\n\t\tresolveRequire: options.resolveRequire,\n\t\tvisited: new Set(),\n\t};\n\n\tdfsVisit(state, options.entryFilePath);\n\n\treturn state.nodes;\n}\n\n/**\n * Compute in-degree for each node based on import edges.\n *\n * @param nodes - Adjacency list of reachable modules.\n * @returns Map from file path to its in-degree count.\n */\nfunction computeInDegrees(nodes: ReadonlyMap<string, ModuleNode>): Map<string, number> {\n\tconst inDegree = new Map<string, number>();\n\tfor (const path of nodes.keys()) {\n\t\tinDegree.set(path, 0);\n\t}\n\n\tfor (const node of nodes.values()) {\n\t\tfor (const edge of node.imports) {\n\t\t\t// eslint-disable-next-line ts/no-non-null-assertion -- DFS ensures all edge targets are initialized\n\t\t\tinDegree.set(edge.resolvedPath, inDegree.get(edge.resolvedPath)! + 1);\n\t\t}\n\t}\n\n\treturn inDegree;\n}\n\n/**\n * Drain the Kahn queue: pop zero-in-degree nodes, decrement neighbors.\n *\n * @param options - Kahn's algorithm state.\n * @returns Nodes in dependents-first order.\n */\nfunction drainKahnQueue(options: {\n\tinDegree: Map<string, number>;\n\tnodes: ReadonlyMap<string, ModuleNode>;\n\tqueue: Array<string>;\n}): Array<string> {\n\tconst { inDegree, nodes, queue } = options;\n\tconst sorted: Array<string> = [];\n\n\twhile (queue.length > 0) {\n\t\t// eslint-disable-next-line ts/no-non-null-assertion -- length checked above\n\t\tconst path = queue.shift()!;\n\t\tsorted.push(path);\n\n\t\t// eslint-disable-next-line ts/no-non-null-assertion -- DFS ensures all edge targets exist in nodes\n\t\tconst node = nodes.get(path)!;\n\t\tfor (const edge of node.imports) {\n\t\t\t// eslint-disable-next-line ts/no-non-null-assertion -- DFS ensures all edge targets are initialized\n\t\t\tconst updated = inDegree.get(edge.resolvedPath)! - 1;\n\t\t\tinDegree.set(edge.resolvedPath, updated);\n\t\t\tif (updated === 0) {\n\t\t\t\tqueue.push(edge.resolvedPath);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn sorted;\n}\n\n/**\n * Topologically sort module nodes using Kahn's algorithm.\n * Returns dependencies before dependents (leaf modules first, entry last).\n *\n * @param nodes - Adjacency list of reachable modules.\n * @returns Topologically sorted file paths with dependencies first.\n */\nfunction topologicalSort(nodes: ReadonlyMap<string, ModuleNode>): Array<string> {\n\tconst inDegree = computeInDegrees(nodes);\n\n\tconst queue: Array<string> = [];\n\tfor (const [path, degree] of inDegree) {\n\t\tif (degree === 0) {\n\t\t\tqueue.push(path);\n\t\t}\n\t}\n\n\tconst sorted = drainKahnQueue({ inDegree, nodes, queue });\n\n\t// Kahn's produces dependents-first; reverse for deps-first ordering.\n\treturn sorted.reverse();\n}\n","/** Result of parsing a TS.import call's argument list. */\ninterface ParsedTsImport {\n\t/** Number of `.Parent` traversals in the base path. */\n\tparentCount: number;\n\t/** Unquoted string path segments following the base path. */\n\tsegments: Array<string>;\n}\n\nconst PARENT_PATTERN = /\\.Parent/g;\n\n/**\n * Parse the argument list of a `TS.import` call into a structured result.\n *\n * @param args - Raw argument strings extracted from the Luau AST.\n * @returns The parsed import with parent count and path segments.\n */\nexport function parseTsImport(args: Array<string>): ParsedTsImport {\n\tif (args.length < 3) {\n\t\tthrow new Error(\n\t\t\t`TS.import requires at least 3 arguments (script, base, segment), got ${String(args.length)}`,\n\t\t);\n\t}\n\n\t// eslint-disable-next-line ts/no-non-null-assertion -- length checked above\n\tconst basePath = args[1]!;\n\tconst rawSegments = args.slice(2);\n\n\tconst parentCount = basePath.match(PARENT_PATTERN)?.length ?? 0;\n\tconst segments = rawSegments.map((segment) => segment.replace(/^\"/, \"\").replace(/\"$/, \"\"));\n\n\treturn { parentCount, segments };\n}\n","import type { ReverseMapper } from \"./rbxpath-reverse-mapper.ts\";\nimport { parseTsImport } from \"./ts-import-parser.ts\";\n\n/** Resolves TS.import calls to filesystem paths. */\ninterface ModuleResolver {\n\t/**\n\t * Resolve a TS.import call to a filesystem path.\n\t *\n\t * @param fromFilePath - The absolute path of the calling file.\n\t * @param tsImportArgs - The raw argument strings from the TS.import call.\n\t * @returns The resolved filesystem path.\n\t */\n\tresolve(fromFilePath: string, tsImportArgs: ReadonlyArray<string>): string;\n}\n\n/**\n * Create a module resolver that wires TS.import parsing, Rojo path\n * resolution, and reverse mapping to resolve imports to filesystem paths.\n *\n * @param getRbxPath - Function that maps a file path to its RbxPath.\n * @param reverseMapper - Reverse mapper for looking up filesystem paths.\n * @returns A ModuleResolver instance.\n */\nexport function createModuleResolver(\n\tgetRbxPath: (filePath: string) => ReadonlyArray<string> | undefined,\n\treverseMapper: ReverseMapper,\n): ModuleResolver {\n\treturn {\n\t\tresolve(fromFilePath: string, tsImportArgs: ReadonlyArray<string>): string {\n\t\t\tconst callerRbxPath = getRbxPath(fromFilePath);\n\t\t\tif (callerRbxPath === undefined) {\n\t\t\t\tthrow new Error(`Cannot resolve RbxPath for caller: ${fromFilePath}`);\n\t\t\t}\n\n\t\t\tconst { parentCount, segments } = parseTsImport([...tsImportArgs]);\n\n\t\t\tif (parentCount > callerRbxPath.length) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Parent traversal count (${String(parentCount)}) exceeds caller RbxPath depth (${String(callerRbxPath.length)}) for: ${fromFilePath}`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst basePath = callerRbxPath.slice(0, callerRbxPath.length - parentCount);\n\t\t\tconst targetRbxPath = [...basePath, ...segments];\n\n\t\t\tconst resolved = reverseMapper.resolve(targetRbxPath);\n\t\t\tif (resolved === undefined) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Cannot resolve target module: ${targetRbxPath.join(\".\")} (from ${fromFilePath})`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn resolved;\n\t\t},\n\t};\n}\n","// cspell:words basenames bundleable\nimport fs from \"node:fs\";\nimport path from \"node:path\";\n\nimport { LUAU_EXTENSIONS, toPosix } from \"../utils/path.ts\";\n\nconst PATH_SEPARATOR = \"/\";\nconst PASSTHROUGH_PREFIXES: ReadonlySet<string> = new Set([\"@rbx\", \"@std\"]);\nconst INIT_BASENAMES: ReadonlySet<string> = new Set([\"init.lua\", \"init.luau\"]);\n\n/**\n * Result of resolving a path-style `require(...)` call.\n *\n * - `resolved`: the call should be replaced with a reference to the bundled\n * module at `path`.\n * - `passthrough`: the call should be left untouched in the emitted output\n * (Lute stdlib alias, package alias, etc.).\n */\nexport type PathRequireResolution = { kind: \"passthrough\" } | { kind: \"resolved\"; path: string };\n\n/** Resolves path-style `require(...)` calls to filesystem paths at build time. */\ntype PathRequireResolver = (fromFilePath: string, requireString: string) => PathRequireResolution;\n\ninterface WalkState {\n\tcurrentDirectory: string;\n\tinLeadingDotZone: boolean;\n}\n\n/**\n * Build a resolver that mirrors Roblox's runtime string-require semantics on\n * the filesystem: `./` and `../` resolve relative to the caller, `@self/x`\n * resolves to a sibling of an init module, and `@std/*` / `@rbx/*` are\n * passed through (left unchanged in emitted output).\n *\n * @returns A function that resolves a single `require(...)` string.\n */\nexport function createPathRequireResolver(): PathRequireResolver {\n\tconst resolutionCache = new Map<string, string | undefined>();\n\treturn function resolve(fromFilePath, requireString) {\n\t\tconst parts = requireString.split(PATH_SEPARATOR);\n\t\t// eslint-disable-next-line ts/no-non-null-assertion -- String.split always returns at least one element\n\t\tconst firstPart = parts[0]!;\n\n\t\tif (PASSTHROUGH_PREFIXES.has(firstPart)) {\n\t\t\treturn { kind: \"passthrough\" };\n\t\t}\n\n\t\tvalidateFirstPart(fromFilePath, firstPart);\n\t\tconst targetWithoutExtension = walkParts(fromFilePath, parts);\n\t\tconst resolved = resolveFileWithExtension(targetWithoutExtension, resolutionCache);\n\t\tif (resolved === undefined) {\n\t\t\tthrow new Error(\n\t\t\t\t`cannot resolve require(\"${requireString}\") from ${fromFilePath}: no .luau or .lua file at ${targetWithoutExtension}`,\n\t\t\t);\n\t\t}\n\n\t\treturn { kind: \"resolved\", path: resolved };\n\t};\n}\n\nfunction validateFirstPart(fromFilePath: string, firstPart: string): void {\n\tif (firstPart === \"\") {\n\t\tthrow new Error(`paths beginning with '/' are not supported (from ${fromFilePath})`);\n\t}\n\n\tif (firstPart === \"@game\") {\n\t\tthrow new Error(`@game-prefixed requires are not bundleable (from ${fromFilePath})`);\n\t}\n\n\tif (firstPart === \"@self\" && !INIT_BASENAMES.has(path.basename(fromFilePath))) {\n\t\tthrow new Error(\n\t\t\t`@self requires are only valid from init.luau / init.lua (from ${fromFilePath})`,\n\t\t);\n\t}\n\n\tif (\n\t\tfirstPart !== \".\" &&\n\t\tfirstPart !== \"..\" &&\n\t\tfirstPart !== \"@self\" &&\n\t\t!firstPart.startsWith(\"@\")\n\t) {\n\t\tthrow new Error(\n\t\t\t`bare require(\"${firstPart}\") is not supported; use a relative path (from ${fromFilePath})`,\n\t\t);\n\t}\n}\n\nfunction ensureLeadingZone(state: WalkState, part: string): void {\n\tif (!state.inLeadingDotZone) {\n\t\tthrow new Error(`'${part}' is only allowed at the beginning of a require path`);\n\t}\n}\n\nfunction processPart(state: WalkState, part: string): void {\n\tif (part === \"\") {\n\t\treturn;\n\t}\n\n\tif (part === \".\" || part === \"@self\") {\n\t\tensureLeadingZone(state, part);\n\t\tstate.inLeadingDotZone = false;\n\t\treturn;\n\t}\n\n\tif (part === \"..\") {\n\t\tensureLeadingZone(state, part);\n\t\tstate.currentDirectory = toPosix(path.dirname(state.currentDirectory));\n\t\treturn;\n\t}\n\n\tstate.currentDirectory = toPosix(path.join(state.currentDirectory, part));\n\tstate.inLeadingDotZone = false;\n}\n\nfunction walkParts(fromFilePath: string, parts: ReadonlyArray<string>): string {\n\tconst state: WalkState = {\n\t\tcurrentDirectory: toPosix(path.dirname(fromFilePath)),\n\t\tinLeadingDotZone: true,\n\t};\n\n\tfor (const part of parts) {\n\t\tprocessPart(state, part);\n\t}\n\n\treturn state.currentDirectory;\n}\n\nfunction resolveFileWithExtension(\n\ttargetWithoutExtension: string,\n\tcache: Map<string, string | undefined>,\n): string | undefined {\n\tconst cached = cache.get(targetWithoutExtension);\n\tif (cached !== undefined || cache.has(targetWithoutExtension)) {\n\t\treturn cached;\n\t}\n\n\tfor (const extension of LUAU_EXTENSIONS) {\n\t\tconst candidate = targetWithoutExtension + extension;\n\t\tif (fs.existsSync(candidate)) {\n\t\t\tcache.set(targetWithoutExtension, candidate);\n\t\t\treturn candidate;\n\t\t}\n\t}\n\n\tcache.set(targetWithoutExtension, undefined);\n\treturn undefined;\n}\n","/** Look up the filesystem path for a given RbxPath. */\nexport interface ReverseMapper {\n\t/** Look up the filesystem path for a given RbxPath. */\n\tresolve(rbxPath: ReadonlyArray<string>): string | undefined;\n}\n\n/**\n * Serialize an RbxPath array into a string suitable for use as a Map key.\n *\n * @param rbxPath - The RbxPath segments to serialize.\n * @returns The serialized string key.\n */\nexport function serializeRbxPath(rbxPath: ReadonlyArray<string>): string {\n\treturn rbxPath.join(\"\\0\");\n}\n\n/**\n * Build a reverse map from RbxPath to filesystem path.\n * Calls `getRbxPath` on each discovered file.\n *\n * @param discoveredFiles - Absolute paths of all discovered files.\n * @param getRbxPath - Function that maps a file path to its RbxPath.\n * @returns A ReverseMapper for looking up filesystem paths by RbxPath.\n */\nexport function createReverseMapper(\n\tdiscoveredFiles: ReadonlyArray<string>,\n\tgetRbxPath: (absolutePath: string) => ReadonlyArray<string> | undefined,\n): ReverseMapper {\n\tconst map = new Map<string, string>();\n\n\tfor (const filePath of discoveredFiles) {\n\t\tconst rbxPath = getRbxPath(filePath);\n\t\tif (rbxPath === undefined) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tmap.set(serializeRbxPath(rbxPath), filePath);\n\t}\n\n\treturn {\n\t\tresolve(rbxPath: ReadonlyArray<string>): string | undefined {\n\t\t\treturn map.get(serializeRbxPath(rbxPath));\n\t\t},\n\t};\n}\n","import type { RojoProject } from \"@isentinel/rojo-utils\";\nimport { loadRojoProject, mapFsPathToDataModel } from \"@isentinel/rojo-utils\";\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\n\nimport type { BundleChunk, BundleLineMapping } from \"./codegen/bundle-generator.ts\";\nimport { generateChunkedBundle } from \"./codegen/bundle-generator.ts\";\nimport {\n\tENTRY_PIPELINE,\n\tMODULE_PIPELINE,\n\tPATH_ENTRY_PIPELINE,\n\tPATH_MODULE_PIPELINE,\n} from \"./codegen/defaults.ts\";\nimport { assignModuleIds } from \"./codegen/module-id.ts\";\nimport { runPipeline } from \"./codegen/pipeline.ts\";\nimport { emitSourceMap } from \"./codegen/sourcemap-emitter.ts\";\nimport type { ResolvedImport, Transform } from \"./codegen/transforms/types.ts\";\nimport type { RequireMode } from \"./config/schema.ts\";\nimport { bundleDeclarations } from \"./declarations/declaration-bundler.ts\";\nimport { isDebug } from \"./environment.ts\";\nimport { spawnLuteExtractor } from \"./extraction/lute-spawner.ts\";\nimport type { FileAnalysis, TopLevelLocal } from \"./extraction/schema.ts\";\nimport type { ChunkAssignment } from \"./graph/chunk-assigner.ts\";\nimport { assignChunks } from \"./graph/chunk-assigner.ts\";\nimport type { DependencyGraph } from \"./graph/dependency-graph.ts\";\nimport { buildDependencyGraph } from \"./graph/dependency-graph.ts\";\nimport { createModuleResolver } from \"./resolution/module-resolver.ts\";\nimport { createPathRequireResolver } from \"./resolution/path-require-resolver.ts\";\nimport { createReverseMapper } from \"./resolution/rbxpath-reverse-mapper.ts\";\nimport { toPosix } from \"./utils/path.ts\";\n\nconst LOCALS_WARNING_THRESHOLD = 200;\n\n/** Configuration for the bundle operation. */\nexport interface BundleConfig {\n\t/** Whether to also produce a bundled .d.ts declaration file. */\n\tdeclaration?: boolean;\n\t/** Path to the entry .luau file. */\n\tentry: string;\n\t/** Package names whose types should be imported rather than inlined in declarations. */\n\texternal?: ReadonlyArray<string>;\n\t/**\n\t * Directory Lute walks for source files. Defaults to the entry's parent\n\t * directory. Set explicitly when path-mode requires (`require(\"../x\")`)\n\t * traverse outside the entry's subtree.\n\t */\n\tluauRoot?: string;\n\t/** Path for the bundled output file. */\n\toutput: string;\n\t/**\n\t * Path to the Rojo project.json file. Required when `requireMode` is\n\t * `\"ts-import\"` (the default). Ignored in `\"path\"` mode where requires are\n\t * resolved relative to the calling file's directory.\n\t */\n\tproject?: string;\n\t/**\n\t * How to resolve require/import calls. `\"ts-import\"` (default) handles\n\t * `TS.import(...)` calls via Rojo project paths. `\"path\"` resolves plain\n\t * `require(\"./x\")` calls relative to the calling file's directory.\n\t */\n\trequireMode?: RequireMode;\n}\n\n/** Result of a successful bundle operation. */\nexport interface BundleResult {\n\t/** Path to the bundled .d.ts file, if declarations were generated. */\n\tdeclarationOutputPath?: string;\n\t/** Number of modules in the bundle (including entry). */\n\tmoduleCount: number;\n\t/** Absolute path of the output file. */\n\toutputPath: string;\n\t/**\n\t * Absolute path of the emitted Source Map V3 sidecar. Present only when\n\t * `requireMode` is `\"path\"` and the bundle has at least one source-mapped\n\t * line.\n\t */\n\tsourceMapPath?: string;\n\t/** Timing breakdown in milliseconds. */\n\ttiming: {\n\t\tcodegenMs: number;\n\t\t/** Time spent bundling declarations, if enabled. */\n\t\tdeclarationMs?: number;\n\t\textractionMs: number;\n\t\tresolutionMs: number;\n\t\ttotalMs: number;\n\t};\n\t/** Warning messages. */\n\twarnings: Array<string>;\n}\n\ninterface CodegenOptions {\n\tanalyses: ReadonlyArray<FileAnalysis>;\n\tentryPath: string;\n\tgraph: DependencyGraph;\n\trequireMode: RequireMode;\n}\n\ninterface AnalysisLookups {\n\tlocalCounts: ReadonlyMap<string, number>;\n\ttopLevelLocals: ReadonlyMap<string, ReadonlyArray<TopLevelLocal>>;\n}\n\ninterface ModuleProcessInput {\n\tchunkAssignment: ChunkAssignment;\n\tgraph: DependencyGraph;\n\tmoduleId: string;\n\tmoduleIds: ReadonlyMap<string, string>;\n\tmodulePath: string;\n\tpipeline: ReadonlyArray<Transform>;\n\ttopLevelLocals: ReadonlyArray<TopLevelLocal>;\n\ttotalLocals: number;\n}\n\ninterface PhaseTimings {\n\tcodegenMs: number;\n\tdeclarationMs?: number;\n\textractionMs: number;\n\tresolutionMs: number;\n}\n\ninterface BundlePhases {\n\tdeclarationOutputPath?: string;\n\tgraph: DependencyGraph;\n\trawAnalyses: ReadonlyArray<FileAnalysis>;\n\tsourceMapPath?: string;\n\ttiming: PhaseTimings;\n}\n\n/**\n * Bundle a Luau entry point and its dependencies into a single file.\n *\n * @param config - Bundle configuration.\n * @returns The bundle result with output path, module count, timing, and warnings.\n */\nexport function bundle(config: BundleConfig): BundleResult {\n\tconst outputPath = toPosix(path.resolve(config.output));\n\tif (config.declaration === true && !outputPath.endsWith(\".luau\")) {\n\t\tthrow new Error(`--declaration requires output path ending in .luau, got: ${outputPath}`);\n\t}\n\n\tconst totalStart = performance.now();\n\tconst entryPath = toPosix(path.resolve(config.entry));\n\tconst phases = runBundlePhases({ config, entryPath, outputPath });\n\treturn {\n\t\tdeclarationOutputPath: phases.declarationOutputPath,\n\t\tmoduleCount: phases.graph.sorted.length,\n\t\toutputPath,\n\t\tsourceMapPath: phases.sourceMapPath,\n\t\ttiming: { ...phases.timing, totalMs: performance.now() - totalStart },\n\t\twarnings: collectLocalsWarnings(phases.rawAnalyses),\n\t};\n}\n\nfunction writeSourceMapIfRequested(\n\tconfig: BundleConfig,\n\tcontext: { entries: ReadonlyArray<BundleLineMapping>; outputPath: string },\n): string | undefined {\n\tif (config.requireMode !== \"path\" || context.entries.length === 0) {\n\t\treturn undefined;\n\t}\n\n\tconst sourceMapPath = `${context.outputPath}.map`;\n\tconst map = emitSourceMap({\n\t\tentries: context.entries,\n\t\toutputPath: context.outputPath,\n\t\tsourcemapDirectory: toPosix(path.dirname(context.outputPath)),\n\t});\n\tfs.writeFileSync(sourceMapPath, JSON.stringify(map));\n\treturn sourceMapPath;\n}\n\nfunction emitDeclarations(\n\tconfig: BundleConfig,\n\toptions: { entryPath: string; outputPath: string },\n): { declarationMs?: number; declarationOutputPath?: string } {\n\tif (config.declaration !== true) {\n\t\treturn {};\n\t}\n\n\tconst start = performance.now();\n\tconst dtsOutputPath = options.outputPath\n\t\t.replace(/init\\.luau$/, \"index.d.ts\")\n\t\t.replace(/\\.luau$/, \".d.ts\");\n\tconst result = bundleDeclarations({\n\t\tentryLuauPath: options.entryPath,\n\t\timportedLibraries: config.external,\n\t\toutputPath: dtsOutputPath,\n\t});\n\treturn { declarationMs: performance.now() - start, declarationOutputPath: result.outputPath };\n}\n\nfunction writeBundleArtifacts(input: {\n\tcodegen: { output: string; sourceMapEntries: ReadonlyArray<BundleLineMapping> };\n\tconfig: BundleConfig;\n\tentryPath: string;\n\toutputPath: string;\n}): { declarationMs?: number; declarationOutputPath?: string; sourceMapPath?: string } {\n\tconst { codegen, config, entryPath, outputPath } = input;\n\tfs.mkdirSync(path.dirname(outputPath), { recursive: true });\n\tfs.writeFileSync(outputPath, codegen.output);\n\tconst sourceMapPath = writeSourceMapIfRequested(config, {\n\t\tentries: codegen.sourceMapEntries,\n\t\toutputPath,\n\t});\n\tconst declarations = emitDeclarations(config, { entryPath, outputPath });\n\treturn { ...declarations, sourceMapPath };\n}\n\nfunction toAbsoluteAnalyses(\n\tanalyses: ReadonlyArray<FileAnalysis>,\n\tluauRoot: string,\n): Array<FileAnalysis> {\n\treturn analyses.map((a) => ({ ...a, path: toPosix(path.join(luauRoot, a.path)) }));\n}\n\nfunction extractAndValidate(options: { entryPath: string; luauRoot?: string }): {\n\tabsoluteAnalyses: Array<FileAnalysis>;\n\textractionMs: number;\n\trawAnalyses: ReadonlyArray<FileAnalysis>;\n} {\n\tconst { entryPath } = options;\n\tconst luauRoot =\n\t\toptions.luauRoot !== undefined && options.luauRoot !== \"\"\n\t\t\t? toPosix(path.resolve(options.luauRoot))\n\t\t\t: path.dirname(entryPath);\n\tconst extractionStart = performance.now();\n\tconst extraction = spawnLuteExtractor(luauRoot);\n\tconst extractionMs = performance.now() - extractionStart;\n\tconst entryRelativePath = toPosix(path.relative(luauRoot, entryPath));\n\tif (!extraction.analyses.some((analysis) => analysis.path === entryRelativePath)) {\n\t\tthrow new Error(`Entry file not found in extraction data: ${entryPath}`);\n\t}\n\n\treturn {\n\t\tabsoluteAnalyses: toAbsoluteAnalyses(extraction.analyses, luauRoot),\n\t\textractionMs,\n\t\trawAnalyses: extraction.analyses,\n\t};\n}\n\nfunction buildPathModeGraph(\n\tanalyses: ReadonlyArray<FileAnalysis>,\n\tentryPath: string,\n): DependencyGraph {\n\treturn buildDependencyGraph({\n\t\tanalyses,\n\t\tentryFilePath: entryPath,\n\t\tresolveRequire: createPathRequireResolver(),\n\t});\n}\n\nfunction createRbxPathResolver(\n\tproject: RojoProject,\n\tprojectDirectory: string,\n): (filePath: string) => ReadonlyArray<string> | undefined {\n\treturn (filePath: string): ReadonlyArray<string> | undefined => {\n\t\tconst posixPath = toPosix(filePath);\n\t\tconst relativePath = toPosix(path.relative(projectDirectory, posixPath));\n\t\tconst dataModelPath = mapFsPathToDataModel(relativePath, project.tree);\n\t\tif (dataModelPath === undefined) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tif (dataModelPath === \"\") {\n\t\t\treturn [\"game\"];\n\t\t}\n\n\t\treturn [\"game\", ...dataModelPath.split(\"/\")];\n\t};\n}\n\nfunction buildTsImportGraph(options: {\n\tanalyses: ReadonlyArray<FileAnalysis>;\n\tconfig: BundleConfig;\n\tentryPath: string;\n}): DependencyGraph {\n\tconst { analyses, config, entryPath } = options;\n\tif (config.project === undefined || config.project === \"\") {\n\t\tthrow new Error(\"project is required when requireMode is 'ts-import'\");\n\t}\n\n\tconst project = loadRojoProject(config.project);\n\tconst projectDirectory = toPosix(path.dirname(path.resolve(config.project)));\n\tconst getRbxPath = createRbxPathResolver(project, projectDirectory);\n\tconst discoveredFiles = analyses.map((a) => a.path);\n\tconst reverseMapper = createReverseMapper(discoveredFiles, getRbxPath);\n\tconst resolver = createModuleResolver(getRbxPath, reverseMapper);\n\treturn buildDependencyGraph({\n\t\tanalyses,\n\t\tentryFilePath: entryPath,\n\t\tresolve: (from, args) => resolver.resolve(from, args),\n\t});\n}\n\nfunction resolveModules(\n\tconfig: BundleConfig,\n\tanalyses: ReadonlyArray<FileAnalysis>,\n\tentryPath: string,\n): DependencyGraph {\n\tif (config.requireMode === \"path\") {\n\t\treturn buildPathModeGraph(analyses, entryPath);\n\t}\n\n\treturn buildTsImportGraph({ analyses, config, entryPath });\n}\n\nfunction buildAnalysisLookups(analyses: ReadonlyArray<FileAnalysis>): AnalysisLookups {\n\tconst localCounts = new Map<string, number>();\n\tconst topLevelLocals = new Map<string, ReadonlyArray<TopLevelLocal>>();\n\tfor (const analysis of analyses) {\n\t\tconst locals = analysis.topLevelLocals ?? [];\n\t\tconst nonBoilerplate = locals.filter((local) => local.name !== \"TS\");\n\t\tlocalCounts.set(analysis.path, nonBoilerplate.length);\n\t\ttopLevelLocals.set(analysis.path, nonBoilerplate);\n\t}\n\n\treturn { localCounts, topLevelLocals };\n}\n\nfunction buildResolvedImports(\n\tgraph: DependencyGraph,\n\toptions: {\n\t\tchunkMapping: ReadonlyMap<string, number>;\n\t\tfilePath: string;\n\t\tmoduleIds: ReadonlyMap<string, string>;\n\t},\n): Array<ResolvedImport> {\n\tconst { chunkMapping, filePath, moduleIds } = options;\n\t// eslint-disable-next-line ts/no-non-null-assertion -- filePath always exists in graph nodes\n\tconst node = graph.nodes.get(filePath)!;\n\tconst sourceChunk = chunkMapping.get(filePath);\n\treturn node.imports.map((edge) => {\n\t\tconst targetChunk = chunkMapping.get(edge.resolvedPath);\n\t\tconst intraChunk =\n\t\t\tsourceChunk !== undefined && targetChunk !== undefined && sourceChunk === targetChunk;\n\t\treturn {\n\t\t\targs: [...edge.args],\n\t\t\tintraChunk: intraChunk || undefined,\n\t\t\tkind: edge.kind,\n\t\t\t// eslint-disable-next-line ts/no-non-null-assertion -- all non-entry deps have assigned IDs\n\t\t\tmoduleId: moduleIds.get(edge.resolvedPath)!,\n\t\t};\n\t});\n}\n\nfunction renameAndRewriteWithRaw(input: ModuleProcessInput, rawSource: string): string {\n\tconst { chunkAssignment, graph, moduleId, moduleIds, modulePath, pipeline, topLevelLocals } =\n\t\tinput;\n\tconst resolvedImports = buildResolvedImports(graph, {\n\t\tchunkMapping: chunkAssignment.moduleToChunk,\n\t\tfilePath: modulePath,\n\t\tmoduleIds,\n\t});\n\treturn runPipeline(rawSource, {\n\t\tcontext: {\n\t\t\tisEntryPoint: false,\n\t\t\tlocals: topLevelLocals,\n\t\t\tmoduleId,\n\t\t\tresolvedImports,\n\t\t},\n\t\ttransforms: pipeline,\n\t});\n}\n\nfunction processChunkedModule(input: ModuleProcessInput): {\n\tid: string;\n\toriginalContent?: string;\n\tsource: string;\n\tsourcePath?: string;\n\ttotalLocals?: number;\n} {\n\tconst rawSource = fs.readFileSync(input.modulePath, \"utf-8\");\n\treturn {\n\t\tid: input.moduleId,\n\t\toriginalContent: rawSource,\n\t\tsource: renameAndRewriteWithRaw(input, rawSource),\n\t\tsourcePath: input.modulePath,\n\t\ttotalLocals: isDebug() ? input.totalLocals : undefined,\n\t};\n}\n\nfunction buildBundleChunks(options: {\n\tchunkAssignment: ChunkAssignment;\n\tgraph: DependencyGraph;\n\tlookups: AnalysisLookups;\n\tmoduleIds: ReadonlyMap<string, string>;\n\tpipeline: ReadonlyArray<Transform>;\n}): Array<BundleChunk> {\n\tconst { chunkAssignment, graph, lookups, moduleIds, pipeline } = options;\n\treturn chunkAssignment.chunks\n\t\t.filter((chunk) => !chunk.modules.includes(graph.entryPoint))\n\t\t.map((chunk) => {\n\t\t\tconst modules = chunk.modules.map((modulePath) => {\n\t\t\t\t// eslint-disable-next-line ts/no-non-null-assertion -- moduleIds built from same nonEntryPaths\n\t\t\t\tconst moduleId = moduleIds.get(modulePath)!;\n\t\t\t\treturn processChunkedModule({\n\t\t\t\t\tchunkAssignment,\n\t\t\t\t\tgraph,\n\t\t\t\t\tmoduleId,\n\t\t\t\t\tmoduleIds,\n\t\t\t\t\tmodulePath,\n\t\t\t\t\tpipeline,\n\t\t\t\t\t// eslint-disable-next-line ts/no-non-null-assertion -- all modules have lookups\n\t\t\t\t\ttopLevelLocals: lookups.topLevelLocals.get(modulePath)!,\n\t\t\t\t\t// eslint-disable-next-line ts/no-non-null-assertion -- all modules have lookups\n\t\t\t\t\ttotalLocals: lookups.localCounts.get(modulePath)!,\n\t\t\t\t});\n\t\t\t});\n\t\t\treturn { id: chunk.id, modules };\n\t\t});\n}\n\nfunction rewriteEntrySource(options: {\n\tchunkAssignment: ChunkAssignment;\n\tentryPath: string;\n\tgraph: DependencyGraph;\n\tmoduleIds: ReadonlyMap<string, string>;\n\tpipeline: ReadonlyArray<Transform>;\n}): { originalContent: string; rewritten: string } {\n\tconst { chunkAssignment, entryPath, graph, moduleIds, pipeline } = options;\n\tconst entrySource = fs.readFileSync(entryPath, \"utf-8\");\n\tconst entryImports = buildResolvedImports(graph, {\n\t\tchunkMapping: chunkAssignment.moduleToChunk,\n\t\tfilePath: entryPath,\n\t\tmoduleIds,\n\t});\n\tconst rewritten = runPipeline(entrySource, {\n\t\tcontext: {\n\t\t\tisEntryPoint: true,\n\t\t\tlocals: [],\n\t\t\tmoduleId: \"\",\n\t\t\tresolvedImports: entryImports,\n\t\t},\n\t\ttransforms: pipeline,\n\t});\n\treturn { originalContent: entrySource, rewritten };\n}\n\nfunction pickPipelines(requireMode: RequireMode): {\n\tentry: ReadonlyArray<Transform>;\n\tmodule: ReadonlyArray<Transform>;\n} {\n\tif (requireMode === \"path\") {\n\t\treturn { entry: PATH_ENTRY_PIPELINE, module: PATH_MODULE_PIPELINE };\n\t}\n\n\treturn { entry: ENTRY_PIPELINE, module: MODULE_PIPELINE };\n}\n\nfunction assembleBundle(input: {\n\tchunks: ReadonlyArray<BundleChunk>;\n\tentry: { originalContent: string; rewritten: string };\n\tentryPath: string;\n\trequireMode: RequireMode;\n}): { output: string; sourceMapEntries: Array<BundleLineMapping> } {\n\tconst sourceMapEntries: Array<BundleLineMapping> = [];\n\tconst output = generateChunkedBundle(input.chunks, input.entry.rewritten, {\n\t\tentryInfo: { originalContent: input.entry.originalContent, sourcePath: input.entryPath },\n\t\tincludeTsGlobal: input.requireMode !== \"path\",\n\t\tonMapping: (mapping) => {\n\t\t\tsourceMapEntries.push(mapping);\n\t\t},\n\t});\n\treturn { output, sourceMapEntries };\n}\n\nfunction generateOutput(options: CodegenOptions): {\n\toutput: string;\n\tsourceMapEntries: Array<BundleLineMapping>;\n} {\n\tconst { analyses, entryPath, graph, requireMode } = options;\n\tconst moduleIds = assignModuleIds(graph.sorted.filter((mp) => mp !== entryPath));\n\tconst lookups = buildAnalysisLookups(analyses);\n\tconst chunkAssignment = assignChunks({ graph, localCounts: lookups.localCounts });\n\tconst pipelines = pickPipelines(requireMode);\n\tconst chunks = buildBundleChunks({\n\t\tchunkAssignment,\n\t\tgraph,\n\t\tlookups,\n\t\tmoduleIds,\n\t\tpipeline: pipelines.module,\n\t});\n\tconst entry = rewriteEntrySource({\n\t\tchunkAssignment,\n\t\tentryPath,\n\t\tgraph,\n\t\tmoduleIds,\n\t\tpipeline: pipelines.entry,\n\t});\n\treturn assembleBundle({ chunks, entry, entryPath, requireMode });\n}\n\nfunction runCodegen(input: {\n\tanalyses: ReadonlyArray<FileAnalysis>;\n\tconfig: BundleConfig;\n\tentryPath: string;\n\tgraph: DependencyGraph;\n}): { codegen: { output: string; sourceMapEntries: Array<BundleLineMapping> }; codegenMs: number } {\n\tconst start = performance.now();\n\tconst codegen = generateOutput({\n\t\tanalyses: input.analyses,\n\t\tentryPath: input.entryPath,\n\t\tgraph: input.graph,\n\t\trequireMode: input.config.requireMode ?? \"ts-import\",\n\t});\n\treturn { codegen, codegenMs: performance.now() - start };\n}\n\nfunction runBundlePhases(input: {\n\tconfig: BundleConfig;\n\tentryPath: string;\n\toutputPath: string;\n}): BundlePhases {\n\tconst { config, entryPath, outputPath } = input;\n\tconst { absoluteAnalyses, extractionMs, rawAnalyses } = extractAndValidate({\n\t\tentryPath,\n\t\tluauRoot: config.luauRoot,\n\t});\n\tconst resolutionStart = performance.now();\n\tconst graph = resolveModules(config, absoluteAnalyses, entryPath);\n\tconst resolutionMs = performance.now() - resolutionStart;\n\tconst { codegen, codegenMs } = runCodegen({\n\t\tanalyses: absoluteAnalyses,\n\t\tconfig,\n\t\tentryPath,\n\t\tgraph,\n\t});\n\tconst written = writeBundleArtifacts({ codegen, config, entryPath, outputPath });\n\treturn {\n\t\tdeclarationOutputPath: written.declarationOutputPath,\n\t\tgraph,\n\t\trawAnalyses,\n\t\tsourceMapPath: written.sourceMapPath,\n\t\ttiming: { codegenMs, declarationMs: written.declarationMs, extractionMs, resolutionMs },\n\t};\n}\n\nfunction collectLocalsWarnings(analyses: ReadonlyArray<FileAnalysis>): Array<string> {\n\tconst warnings: Array<string> = [];\n\tfor (const analysis of analyses) {\n\t\tif (analysis.totalLocals > LOCALS_WARNING_THRESHOLD) {\n\t\t\twarnings.push(\n\t\t\t\t`${analysis.path}: ${String(analysis.totalLocals)} locals exceeds ${String(LOCALS_WARNING_THRESHOLD)} threshold`,\n\t\t\t);\n\t\t}\n\t}\n\n\treturn warnings;\n}\n","import { loadConfig as c12LoadConfig } from \"c12\";\nimport { defuFn } from \"defu\";\nimport process from \"node:process\";\n\nimport type { WeldConfig } from \"./schema.ts\";\nimport { validateConfig } from \"./schema.ts\";\n\n/**\n * Load and validate a weld config file using c12 discovery.\n *\n * @param configPath - Explicit path to a config file, or undefined for auto-discovery.\n * @param cwd - Working directory for config discovery.\n * @returns The validated config.\n * @rejects When the config file is not found, invalid, or extends resolution fails.\n */\nexport async function loadConfig(\n\tconfigPath?: string,\n\tcwd: string = process.cwd(),\n): Promise<WeldConfig> {\n\tconst { config, extendWarnings } = await loadRawConfig(configPath, cwd);\n\n\tif (extendWarnings.length > 0) {\n\t\tconst extendsPath = extendWarnings[0]?.match(/`([^`]+)`/)?.[1];\n\t\tthrow new Error(\n\t\t\t`Failed to resolve extends: \"${String(extendsPath)}\". If the file exists, try adding the file extension (e.g. \".ts\").`,\n\t\t);\n\t}\n\n\treturn validateConfig(resolveFunctionValues(config));\n}\n\nfunction merger(...sources: Array<null | undefined | WeldConfig>): WeldConfig {\n\treturn defuFn(...(sources.filter(Boolean) as [WeldConfig, ...Array<WeldConfig>]));\n}\n\nfunction interceptExtendWarnings(\n\toriginalWarn: typeof console.warn,\n\textendWarnings: Array<string>,\n): typeof console.warn {\n\treturn (...args: Array<unknown>) => {\n\t\tconst message = args.join(\" \");\n\t\tif (message.includes(\"Cannot extend config\")) {\n\t\t\textendWarnings.push(message);\n\t\t\treturn;\n\t\t}\n\n\t\toriginalWarn.apply(console, args);\n\t};\n}\n\nfunction isNotFoundError(err: unknown): boolean {\n\treturn err instanceof Error && err.message.includes(\"cannot be resolved\");\n}\n\nasync function loadRawConfig(\n\tconfigPath: string | undefined,\n\tcwd: string,\n): Promise<{ config: WeldConfig; extendWarnings: Array<string> }> {\n\tconst extendWarnings: Array<string> = [];\n\tconst originalWarn = console.warn;\n\n\ttry {\n\t\tconsole.warn = interceptExtendWarnings(originalWarn, extendWarnings);\n\n\t\tconst result = await c12LoadConfig<WeldConfig>({\n\t\t\tname: \"weld\",\n\t\t\tconfigFile: configPath,\n\t\t\tconfigFileRequired: configPath !== undefined,\n\t\t\tcwd,\n\t\t\tdotenv: false,\n\t\t\tglobalRc: false,\n\t\t\tmerger,\n\t\t\tomit$Keys: true,\n\t\t\tpackageJson: false,\n\t\t\trcFile: false,\n\t\t});\n\n\t\treturn { config: result.config, extendWarnings };\n\t} catch (err) {\n\t\tif (configPath !== undefined && isNotFoundError(err)) {\n\t\t\tthrow new Error(`Config file not found: ${configPath}`, { cause: err });\n\t\t}\n\n\t\tthrow err;\n\t} finally {\n\t\tconsole.warn = originalWarn;\n\t}\n}\n\nfunction isMergerFunction(value: unknown): value is (defaults: undefined) => unknown {\n\treturn typeof value === \"function\";\n}\n\nfunction resolveFunctionValues(config: WeldConfig): WeldConfig {\n\tconst resolved: Record<string, unknown> = {};\n\n\tfor (const [key, value] of Object.entries(config)) {\n\t\tresolved[key] = isMergerFunction(value) ? value(undefined) : value;\n\t}\n\n\treturn resolved as WeldConfig;\n}\n","import process from \"node:process\";\nimport color from \"tinyrainbow\";\n\nimport packageJson from \"../../package.json\" with { type: \"json\" };\n\n/**\n * Information about a completed bundle operation.\n */\nexport interface BundleResultInfo {\n\t/** Filesystem path where the declaration bundle was written. */\n\tdeclarationOutputPath?: string;\n\t/** Number of modules included in the bundle. */\n\tmoduleCount: number;\n\t/** Filesystem path where the bundle was written. */\n\toutputPath: string;\n\t/** Timing breakdown of the bundle phases in milliseconds. */\n\ttiming: {\n\t\tcodegenMs: number;\n\t\t/** Time spent bundling declarations, if enabled. */\n\t\tdeclarationMs?: number;\n\t\textractionMs: number;\n\t\tresolutionMs: number;\n\t\ttotalMs: number;\n\t};\n\t/** Warning messages produced during bundling. */\n\twarnings: Array<string>;\n}\n\nconst LABEL_WIDTH = 12;\n\n/**\n * Format a duration in milliseconds for display. Values below 1000 are shown\n * as whole milliseconds (e.g. \"456ms\"). Values at or above 1000 are shown as\n * seconds with one decimal place (e.g. \"1.2s\").\n *\n * @param ms - Duration in milliseconds.\n * @returns Formatted time string.\n */\nexport function formatTime(ms: number): string {\n\tif (ms >= 1000) {\n\t\treturn `${(ms / 1000).toFixed(1)}s`;\n\t}\n\n\treturn `${Math.round(ms)}ms`;\n}\n\n/**\n * Log the weld banner header to stderr.\n */\nexport function logHeader(): void {\n\tprocess.stderr.write(color.bold(`⚡ weld v${packageJson.version}\\n`));\n}\n\n/**\n * Log a warning message to stderr with a yellow warning symbol.\n *\n * @param message - Warning text to display.\n */\nexport function logWarning(message: string): void {\n\tprocess.stderr.write(`${color.yellow(\"⚠\")} ${message}\\n`);\n}\n\n/**\n * Log an error with formatted output to stderr. Accepts both string messages\n * and Error objects.\n *\n * @param error - The error to display.\n */\nexport function logError(error: unknown): void {\n\tconst message = error instanceof Error ? error.message : String(error);\n\tprocess.stderr.write(`${color.red(\"✖\")} ${message}\\n`);\n}\n\n/**\n * Log a successful bundle result to stderr, including module count, output\n * path, and a timing breakdown.\n *\n * @param result - Bundle result information.\n */\nexport function logResult(result: BundleResultInfo): void {\n\tconst { declarationOutputPath, moduleCount, outputPath, timing } = result;\n\n\tconst lines: Array<[label: string, ms: number]> = [\n\t\t[\"Extraction\", timing.extractionMs],\n\t\t[\"Resolution\", timing.resolutionMs],\n\t\t[\"Codegen\", timing.codegenMs],\n\t];\n\n\tif (timing.declarationMs !== undefined) {\n\t\tlines.push([\"Declaration\", timing.declarationMs]);\n\t}\n\n\tconst timeValues = [...lines.map(([, ms]) => formatTime(ms)), formatTime(timing.totalMs)];\n\tconst maxTimeWidth = Math.max(...timeValues.map((value) => value.length));\n\n\tprocess.stderr.write(\n\t\t`${color.green(\"✓\")} Bundled ${String(moduleCount)} modules → ${outputPath}\\n`,\n\t);\n\n\tif (declarationOutputPath !== undefined) {\n\t\tprocess.stderr.write(`${color.green(\"✓\")} Declaration → ${declarationOutputPath}\\n`);\n\t}\n\n\tprocess.stderr.write(\"\\n\");\n\n\tfor (const [label, ms] of lines) {\n\t\tconst formattedTime = formatTime(ms).padStart(maxTimeWidth);\n\t\tprocess.stderr.write(` ${label.padEnd(LABEL_WIDTH)} ${formattedTime}\\n`);\n\t}\n\n\tprocess.stderr.write(color.dim(` ${\"─\".repeat(LABEL_WIDTH + 1 + maxTimeWidth)}\\n`));\n\n\tconst totalFormatted = formatTime(timing.totalMs).padStart(maxTimeWidth);\n\tprocess.stderr.write(` ${\"Total\".padEnd(LABEL_WIDTH)} ${totalFormatted}\\n`);\n}\n","import process from \"node:process\";\nimport { parseArgs } from \"node:util\";\n\nimport { bundle } from \"./bundler.ts\";\nimport type { BundleConfig } from \"./bundler.ts\";\nimport { loadConfig } from \"./config/loader.ts\";\nimport type { RequireMode, WeldConfig } from \"./config/schema.ts\";\nimport { logError, logResult, logWarning } from \"./utils/logger.ts\";\n\n/** Parsed CLI options for the bundle command. */\ninterface CliOptions {\n\t/** Path to config file. */\n\tconfig?: string;\n\t/** Whether to produce a bundled .d.ts declaration file. */\n\tdeclaration?: boolean;\n\t/** Path to the entry .luau file. */\n\tentry?: string;\n\t/** Package names whose types should be imported rather than inlined. */\n\texternal: Array<string>;\n\t/** Directory Lute walks for source files. */\n\tluauRoot?: string;\n\t/** Path for the bundled output file. */\n\toutput?: string;\n\t/** Path to the Rojo project.json file. */\n\tproject?: string;\n\t/** How to resolve require/import calls. */\n\trequireMode?: RequireMode;\n}\n\n/**\n * Entry point for the weld bundler CLI.\n *\n * @param args - CLI arguments to parse.\n */\nexport async function main(args: Array<string> = process.argv.slice(2)): Promise<void> {\n\ttry {\n\t\tconst cliOptions = parseBundleArgs(args);\n\t\tconst fileConfig = await loadConfig(cliOptions.config);\n\t\tconst config = mergeConfig(fileConfig, cliOptions);\n\n\t\tconst result = bundle(config);\n\t\tlogResult(result);\n\n\t\tfor (const warning of result.warnings) {\n\t\t\tlogWarning(warning);\n\t\t}\n\n\t\tprocess.exitCode = 0;\n\t} catch (err: unknown) {\n\t\tlogError(err);\n\t\tprocess.exitCode = 1;\n\t}\n}\n\nfunction mergeConfig(fileConfig: WeldConfig, cli: CliOptions): BundleConfig {\n\tconst entry = cli.entry ?? fileConfig.entry;\n\tconst project = cli.project ?? fileConfig.project;\n\tconst output = cli.output ?? fileConfig.output;\n\tconst luauRoot = cli.luauRoot ?? fileConfig.luauRoot;\n\tconst requireMode = cli.requireMode ?? fileConfig.requireMode ?? \"ts-import\";\n\n\tif (entry === undefined || entry === \"\") {\n\t\tthrow new Error(\"--entry is required (via CLI or config file)\");\n\t}\n\n\tif (requireMode === \"ts-import\" && (project === undefined || project === \"\")) {\n\t\tthrow new Error(\"--project is required (via CLI or config file)\");\n\t}\n\n\tif (output === undefined || output === \"\") {\n\t\tthrow new Error(\"--output is required (via CLI or config file)\");\n\t}\n\n\tconst external = cli.external.length > 0 ? cli.external : fileConfig.external;\n\tconst declaration = cli.declaration ?? fileConfig.declaration ?? false;\n\tconst resolvedProject = requireMode === \"path\" ? undefined : project;\n\n\treturn {\n\t\tdeclaration,\n\t\tentry,\n\t\texternal,\n\t\tluauRoot,\n\t\toutput,\n\t\tproject: resolvedProject,\n\t\trequireMode,\n\t};\n}\n\nfunction parseRequireMode(raw: string | undefined): RequireMode | undefined {\n\tif (raw === undefined) {\n\t\treturn undefined;\n\t}\n\n\tif (raw !== \"ts-import\" && raw !== \"path\") {\n\t\tthrow new Error(`--require-mode must be 'ts-import' or 'path' (got: ${raw})`);\n\t}\n\n\treturn raw;\n}\n\n/**\n * Parse CLI arguments into bundle options.\n *\n * @param args - Raw CLI argument strings.\n * @returns Parsed CLI options.\n */\nfunction parseBundleArgs(args: Array<string>): CliOptions {\n\tconst { values } = parseArgs({\n\t\targs,\n\t\toptions: {\n\t\t\t\"config\": { short: \"c\", type: \"string\" },\n\t\t\t\"declaration\": { short: \"d\", type: \"boolean\" },\n\t\t\t\"entry\": { short: \"e\", type: \"string\" },\n\t\t\t\"external\": { multiple: true, type: \"string\" },\n\t\t\t\"luau-root\": { type: \"string\" },\n\t\t\t\"output\": { short: \"o\", type: \"string\" },\n\t\t\t\"project\": { short: \"p\", type: \"string\" },\n\t\t\t\"require-mode\": { short: \"r\", type: \"string\" },\n\t\t},\n\t\tstrict: true,\n\t});\n\n\treturn {\n\t\tconfig: values.config,\n\t\tdeclaration: values.declaration,\n\t\tentry: values.entry,\n\t\texternal: values.external ?? [],\n\t\tluauRoot: values[\"luau-root\"],\n\t\toutput: values.output,\n\t\tproject: values.project,\n\t\trequireMode: parseRequireMode(values[\"require-mode\"]),\n\t};\n}\n"],"mappings":"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"}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
//#region src/config/schema.d.ts
|
|
2
|
+
/** How weld resolves require/import calls. */
|
|
3
|
+
type RequireMode = "path" | "ts-import";
|
|
4
|
+
/** Configuration for the weld bundler. */
|
|
5
|
+
interface WeldConfig {
|
|
6
|
+
/** Whether to produce a bundled .d.ts declaration file. */
|
|
7
|
+
declaration?: boolean;
|
|
8
|
+
/** Path to the entry .luau file. */
|
|
9
|
+
entry?: string;
|
|
10
|
+
/** Configs to inherit from. */
|
|
11
|
+
extends?: Array<string> | string;
|
|
12
|
+
/** Package names whose types should be imported rather than inlined in declarations. */
|
|
13
|
+
external?: Array<string>;
|
|
14
|
+
/**
|
|
15
|
+
* Directory Lute walks for source files. Defaults to the entry's parent
|
|
16
|
+
* directory. Set explicitly when `require("../x")` traversal needs files
|
|
17
|
+
* outside that subtree.
|
|
18
|
+
*/
|
|
19
|
+
luauRoot?: string;
|
|
20
|
+
/** Path for the bundled output file. */
|
|
21
|
+
output?: string;
|
|
22
|
+
/** Path to the Rojo project.json file. */
|
|
23
|
+
project?: string;
|
|
24
|
+
/**
|
|
25
|
+
* How to resolve require/import calls. `"ts-import"` (default) handles
|
|
26
|
+
* `TS.import(...)` calls via Rojo project paths. `"path"` resolves plain
|
|
27
|
+
* `require("./x")` calls relative to the calling file's directory.
|
|
28
|
+
*/
|
|
29
|
+
requireMode?: RequireMode;
|
|
30
|
+
}
|
|
31
|
+
/** Input type for defineConfig — allows function values for defu merging. */
|
|
32
|
+
interface WeldConfigInput extends Omit<WeldConfig, "external"> {
|
|
33
|
+
/** Package names whose types should be imported rather than inlined in declarations. */
|
|
34
|
+
external?: Mergeable<Array<string>>;
|
|
35
|
+
}
|
|
36
|
+
type MergerFunction<T> = (defaults: T) => T;
|
|
37
|
+
type Mergeable<T> = MergerFunction<T> | T;
|
|
38
|
+
/** Arktype schema for runtime validation of weld config. */
|
|
39
|
+
declare const defineConfig: (input: WeldConfigInput) => WeldConfigInput;
|
|
40
|
+
//#endregion
|
|
41
|
+
//#region src/index.d.ts
|
|
42
|
+
declare const VERSION: string;
|
|
43
|
+
//#endregion
|
|
44
|
+
export { VERSION, type WeldConfig, type WeldConfigInput, defineConfig };
|
|
45
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/config/schema.ts","../src/index.ts"],"mappings":";;KAIY,WAAA;AAAZ;AAAA,UAGiB,UAAA;;EAEhB,WAAA;EALW;EAOX,KAAA;EAJgB;EAMhB,OAAA,GAAU,KAAA;;EAEV,QAAA,GAAW,KAAA;;;;;;EAMX,QAAA;;EAEA,MAAA;;EAEA,OAAA;;;;;;EAMA,WAAA,GAAc,WAAA;AAAA;;UAME,eAAA,SAAwB,IAAA,CAAK,UAAA;;EAE7C,QAAA,GAAW,SAAA,CAAU,KAAA;AAAA;AAAA,KAGjB,cAAA,OAAqB,QAAA,EAAU,CAAA,KAAM,CAAA;AAAA,KAErC,SAAA,MAAe,cAAA,CAAe,CAAA,IAAK,CAAA;;cA8B3B,YAAA,GAAe,KAAA,EAAO,eAAA,KAAoB,eAAA;;;cCrE1C,OAAA"}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["packageJson.version"],"sources":["../src/index.ts"],"sourcesContent":["import packageJson from \"../package.json\" with { type: \"json\" };\n\nexport { defineConfig } from \"./config/schema.ts\";\nexport type { WeldConfig, WeldConfigInput } from \"./config/schema.ts\";\n\nexport const VERSION: string = packageJson.version;\n"],"mappings":";;AAKA,MAAa,UAAkBA"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type } from "arktype";
|
|
2
|
+
import { createDefineConfig } from "c12";
|
|
3
|
+
//#region package.json
|
|
4
|
+
var version = "0.1.0";
|
|
5
|
+
//#endregion
|
|
6
|
+
//#region src/config/schema.ts
|
|
7
|
+
/** Arktype schema for runtime validation of weld config. */
|
|
8
|
+
const configSchema = type({
|
|
9
|
+
"+": "reject",
|
|
10
|
+
"declaration?": "boolean",
|
|
11
|
+
"entry?": "string",
|
|
12
|
+
"extends?": type("string").or(type("string[]")),
|
|
13
|
+
"external?": "string[]",
|
|
14
|
+
"luauRoot?": "string",
|
|
15
|
+
"output?": "string",
|
|
16
|
+
"project?": "string",
|
|
17
|
+
"requireMode?": "'ts-import' | 'path'"
|
|
18
|
+
}).as();
|
|
19
|
+
/**
|
|
20
|
+
* Validate a raw config object against the weld config schema.
|
|
21
|
+
*
|
|
22
|
+
* @param raw - The raw config object to validate.
|
|
23
|
+
* @returns The validated config.
|
|
24
|
+
*/
|
|
25
|
+
function validateConfig(raw) {
|
|
26
|
+
const result = configSchema(raw);
|
|
27
|
+
if (result instanceof type.errors) throw new Error(`Invalid config: ${result.summary}`);
|
|
28
|
+
return result;
|
|
29
|
+
}
|
|
30
|
+
const defineConfig = createDefineConfig();
|
|
31
|
+
//#endregion
|
|
32
|
+
export { validateConfig as n, version as r, defineConfig as t };
|
|
33
|
+
|
|
34
|
+
//# sourceMappingURL=schema-Cp_hoJ0u.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-Cp_hoJ0u.mjs","names":[],"sources":["../package.json","../src/config/schema.ts"],"sourcesContent":["","import { type, type Type } from \"arktype\";\nimport { createDefineConfig } from \"c12\";\n\n/** How weld resolves require/import calls. */\nexport type RequireMode = \"path\" | \"ts-import\";\n\n/** Configuration for the weld bundler. */\nexport interface WeldConfig {\n\t/** Whether to produce a bundled .d.ts declaration file. */\n\tdeclaration?: boolean;\n\t/** Path to the entry .luau file. */\n\tentry?: string;\n\t/** Configs to inherit from. */\n\textends?: Array<string> | string;\n\t/** Package names whose types should be imported rather than inlined in declarations. */\n\texternal?: Array<string>;\n\t/**\n\t * Directory Lute walks for source files. Defaults to the entry's parent\n\t * directory. Set explicitly when `require(\"../x\")` traversal needs files\n\t * outside that subtree.\n\t */\n\tluauRoot?: string;\n\t/** Path for the bundled output file. */\n\toutput?: string;\n\t/** Path to the Rojo project.json file. */\n\tproject?: string;\n\t/**\n\t * How to resolve require/import calls. `\"ts-import\"` (default) handles\n\t * `TS.import(...)` calls via Rojo project paths. `\"path\"` resolves plain\n\t * `require(\"./x\")` calls relative to the calling file's directory.\n\t */\n\trequireMode?: RequireMode;\n}\n\n// cspell:ignore defu\n\n/** Input type for defineConfig — allows function values for defu merging. */\nexport interface WeldConfigInput extends Omit<WeldConfig, \"external\"> {\n\t/** Package names whose types should be imported rather than inlined in declarations. */\n\texternal?: Mergeable<Array<string>>;\n}\n\ntype MergerFunction<T> = (defaults: T) => T;\n\ntype Mergeable<T> = MergerFunction<T> | T;\n\n/** Arktype schema for runtime validation of weld config. */\nexport const configSchema: Type<WeldConfig> = type({\n\t\"+\": \"reject\",\n\t\"declaration?\": \"boolean\",\n\t\"entry?\": \"string\",\n\t\"extends?\": type(\"string\").or(type(\"string[]\")),\n\t\"external?\": \"string[]\",\n\t\"luauRoot?\": \"string\",\n\t\"output?\": \"string\",\n\t\"project?\": \"string\",\n\t\"requireMode?\": \"'ts-import' | 'path'\",\n}).as<WeldConfig>();\n\n/**\n * Validate a raw config object against the weld config schema.\n *\n * @param raw - The raw config object to validate.\n * @returns The validated config.\n */\nexport function validateConfig(raw: unknown): WeldConfig {\n\tconst result = configSchema(raw);\n\tif (result instanceof type.errors) {\n\t\tthrow new Error(`Invalid config: ${result.summary}`);\n\t}\n\n\treturn result;\n}\n\nexport const defineConfig: (input: WeldConfigInput) => WeldConfigInput =\n\tcreateDefineConfig<WeldConfigInput>();\n"],"mappings":";;;;;;;AC+CA,MAAa,eAAiC,KAAK;CAClD,KAAK;CACL,gBAAgB;CAChB,UAAU;CACV,YAAY,KAAK,SAAS,CAAC,GAAG,KAAK,WAAW,CAAC;CAC/C,aAAa;CACb,aAAa;CACb,WAAW;CACX,YAAY;CACZ,gBAAgB;CAChB,CAAC,CAAC,IAAgB;;;;;;;AAQnB,SAAgB,eAAe,KAA0B;CACxD,MAAM,SAAS,aAAa,IAAI;AAChC,KAAI,kBAAkB,KAAK,OAC1B,OAAM,IAAI,MAAM,mBAAmB,OAAO,UAAU;AAGrD,QAAO;;AAGR,MAAa,eACZ,oBAAqC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
interface ResolveContext {
|
|
2
|
+
conditions?: Array<string>;
|
|
3
|
+
importAttributes?: Record<string, string>;
|
|
4
|
+
parentURL?: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
interface ResolveResult {
|
|
8
|
+
format?: string;
|
|
9
|
+
url: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
type NextResolve = (specifier: string, context: ResolveContext) => Promise<ResolveResult>;
|
|
13
|
+
|
|
14
|
+
interface LoadContext {
|
|
15
|
+
conditions?: Array<string>;
|
|
16
|
+
format?: string;
|
|
17
|
+
importAttributes?: Record<string, string>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface LoadResult {
|
|
21
|
+
format: string;
|
|
22
|
+
shortCircuit?: boolean;
|
|
23
|
+
source: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
type NextLoad = (url: string, context: LoadContext) => Promise<LoadResult>;
|
|
27
|
+
|
|
28
|
+
export function resolve(
|
|
29
|
+
specifier: string,
|
|
30
|
+
context: ResolveContext,
|
|
31
|
+
nextResolve: NextResolve,
|
|
32
|
+
): Promise<ResolveResult>;
|
|
33
|
+
|
|
34
|
+
export function load(url: string, context: LoadContext, nextLoad: NextLoad): Promise<LoadResult>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
|
|
4
|
+
export async function resolve(specifier, context, nextResolve) {
|
|
5
|
+
const resolved = await nextResolve(specifier, context);
|
|
6
|
+
|
|
7
|
+
if (resolved.url.endsWith(".luau") || resolved.url.endsWith(".lua")) {
|
|
8
|
+
return { ...resolved, format: "luau-raw" };
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return resolved;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export async function load(url, context, nextLoad) {
|
|
15
|
+
if (context.format === "luau-raw") {
|
|
16
|
+
const content = await readFile(fileURLToPath(url), "utf-8");
|
|
17
|
+
return {
|
|
18
|
+
format: "module",
|
|
19
|
+
shortCircuit: true,
|
|
20
|
+
source: `export default ${JSON.stringify(content)};`,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return nextLoad(url, context);
|
|
25
|
+
}
|