cluaupp 1.5.2 → 2.0.0-beta.2
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/CHANGELOG.md +8 -0
- package/README.md +29 -41
- package/api/generated/bench-latest.json +43 -21
- package/api/generated/lsp-index.json +1 -1
- package/api/manifests/roblox-target.manifest.json +5 -5
- package/api/normalized/roblox-target.profile.json +8729 -267
- package/api/roblox/luau-builtins.d.luau +185 -0
- package/api/roblox/surface.json +1 -0
- package/api/roblox-api.lock.json +11 -2
- package/bin/cluaupp.js +38 -0
- package/editors/vscode/README.md +1 -1
- package/editors/vscode/package.json +50 -15
- package/editors/vscode/snippets/flare.json +1 -1
- package/editors/vscode/snippets/hive.json +1 -1
- package/editors/vscode/syntaxes/clpp.tmLanguage.json +211 -0
- package/examples/README.md +9 -9
- package/examples/game/cluaupp.config.json +2 -2
- package/examples/game/default.project.json +4 -15
- package/examples/game/out/ReplicatedStorage/Shared/GameInfo.luau +8 -0
- package/examples/game/out/ReplicatedStorage/Shared/GameInfo.luau.map.json +13 -0
- package/examples/game/out/ServerScriptService/Leaderstats.server.luau +26 -0
- package/examples/game/out/ServerScriptService/Leaderstats.server.luau.map.json +13 -0
- package/examples/game/out/ServerScriptService/Main.server.luau +15 -0
- package/examples/game/out/ServerScriptService/Main.server.luau.map.json +13 -0
- package/examples/game/out/StarterPlayer/StarterPlayerScripts/Main.client.luau +10 -0
- package/examples/game/out/StarterPlayer/StarterPlayerScripts/Main.client.luau.map.json +13 -0
- package/examples/game/src/ReplicatedStorage/Shared/GameInfo.clp +1 -0
- package/examples/game/src/ServerScriptService/Leaderstats.server.clp +18 -0
- package/examples/game/src/ServerScriptService/Main.server.clp +9 -0
- package/examples/game/src/StarterPlayer/StarterPlayerScripts/Main.client.clp +4 -0
- package/generated/api/build-profile.js +1 -1
- package/generated/api/loader.js +2 -2
- package/generated/api/surface/build.js +328 -0
- package/generated/api/surface/luau-defs.js +595 -0
- package/generated/api/surface/model.js +8 -0
- package/generated/api-cli.js +2 -2
- package/generated/cli.js +18 -17
- package/generated/clpp/contract.js +6 -3
- package/generated/clpp/handshake.js +36 -0
- package/generated/clpp/modules.js +20 -9
- package/generated/clpp/runner.js +62 -31
- package/generated/compile.js +18 -1
- package/generated/compiler/ast.js +3 -0
- package/generated/compiler/checker/calls.js +999 -0
- package/generated/compiler/checker/checker.js +1215 -0
- package/generated/compiler/checker/exprs.js +791 -0
- package/generated/compiler/checker/flow.js +168 -0
- package/generated/compiler/checker/members.js +763 -0
- package/generated/compiler/checker/statements.js +792 -0
- package/generated/compiler/checker/symbols.js +90 -0
- package/generated/compiler/checker/typeops.js +348 -0
- package/generated/compiler/checker/yields.js +115 -0
- package/generated/compiler/diagnostics.js +138 -0
- package/generated/compiler/emit/emitter.js +2356 -0
- package/generated/compiler/emit/luau-types.js +137 -0
- package/generated/compiler/emit/names.js +130 -0
- package/generated/compiler/emit/runtime.js +432 -0
- package/generated/compiler/lexer.js +403 -0
- package/generated/compiler/parser.js +1881 -0
- package/generated/compiler/project.js +332 -0
- package/generated/compiler/roblox/api.js +388 -0
- package/generated/compiler/roblox/context.js +100 -0
- package/generated/compiler/source.js +59 -0
- package/generated/compiler/stdlib.js +232 -0
- package/generated/compiler/types.js +250 -0
- package/generated/intellisense.js +1 -15
- package/generated/lsp.js +1 -1
- package/generated/target/build-cache.js +4 -4
- package/generated/target/datamodel-generate.js +12 -0
- package/generated/target/gen-rojo-tree.js +15 -0
- package/generated/target/source-map.js +61 -8
- package/generated/target/studio-bridge.js +1 -0
- package/generated/utils/project.js +104 -31
- package/include/README.md +5 -0
- package/native/bin/.gitkeep +1 -0
- package/package.json +17 -6
- package/plugins/CluauppNav/Plugin.luau +28 -4
- package/rust/cluaupp/Cargo.lock +795 -0
- package/rust/cluaupp/Cargo.toml +17 -0
- package/rust/cluaupp/src/main.rs +717 -0
- package/scripts/postinstall-native.js +56 -0
- package/skills/README.md +3 -3
- package/skills/clpp-hive-flare/SKILL.md +30 -26
- package/skills/clpp-language/SKILL.md +67 -74
- package/skills/clpp-language/compiler.md +24 -36
- package/skills/clpp-style/SKILL.md +31 -44
- package/src/api/build-profile.ts +1 -1
- package/src/api/loader.ts +2 -2
- package/src/api/surface/build.ts +374 -0
- package/src/api/surface/luau-defs.ts +635 -0
- package/src/api/surface/model.ts +96 -0
- package/src/api-cli.ts +2 -2
- package/src/cli.ts +19 -18
- package/src/clpp/contract.ts +20 -3
- package/src/clpp/handshake.ts +25 -0
- package/src/clpp/modules.ts +17 -10
- package/src/clpp/runner.ts +52 -34
- package/src/compile.ts +20 -2
- package/src/compiler/ast.ts +336 -0
- package/src/compiler/checker/calls.ts +1025 -0
- package/src/compiler/checker/checker.ts +1352 -0
- package/src/compiler/checker/exprs.ts +805 -0
- package/src/compiler/checker/flow.ts +174 -0
- package/src/compiler/checker/members.ts +793 -0
- package/src/compiler/checker/statements.ts +802 -0
- package/src/compiler/checker/symbols.ts +219 -0
- package/src/compiler/checker/typeops.ts +355 -0
- package/src/compiler/checker/yields.ts +120 -0
- package/src/compiler/diagnostics.ts +163 -0
- package/src/compiler/emit/emitter.ts +2486 -0
- package/src/compiler/emit/luau-types.ts +143 -0
- package/src/compiler/emit/names.ts +130 -0
- package/src/compiler/emit/runtime.ts +438 -0
- package/src/compiler/lexer.ts +444 -0
- package/src/compiler/parser.ts +1943 -0
- package/src/compiler/project.ts +354 -0
- package/src/compiler/roblox/api.ts +408 -0
- package/src/compiler/roblox/context.ts +108 -0
- package/src/compiler/source.ts +67 -0
- package/src/compiler/stdlib.ts +256 -0
- package/src/compiler/types.ts +313 -0
- package/src/intellisense.ts +1 -13
- package/src/lsp.ts +1 -1
- package/src/target/build-cache.ts +5 -8
- package/src/target/datamodel-generate.ts +12 -1
- package/src/target/gen-rojo-tree.ts +15 -0
- package/src/target/source-map.ts +65 -27
- package/src/target/studio-bridge.ts +3 -0
- package/src/types.ts +3 -0
- package/src/utils/project.ts +99 -37
- package/templates/README.md +7 -0
- package/templates/{game → game-modern}/cluaupp.config.json +2 -2
- package/templates/{game → game-modern}/package.json +1 -2
- package/templates/game-modern/rokit.toml +2 -0
- package/templates/game-modern/src/ReplicatedStorage/Shared/GameInfo.clp +1 -0
- package/templates/game-modern/src/ServerScriptService/Handlers/Guard.server.clp +4 -0
- package/templates/game-modern/src/ServerScriptService/Main.server.clp +8 -0
- package/templates/game-modern/src/StarterPlayer/StarterPlayerScripts/Main.client.clp +4 -0
- package/examples/game/out/ReplicatedStorage/Shared/Constants/Datas/TemplateData.luau +0 -29
- package/examples/game/out/ReplicatedStorage/Shared/Constants/Datas/TemplateData.luau.map.json +0 -13
- package/examples/game/out/ReplicatedStorage/Shared/Net/Net.luau +0 -83
- package/examples/game/out/ReplicatedStorage/Shared/Net/Net.luau.map.json +0 -13
- package/examples/game/out/ServerScriptService/Boot/DataBoot.server.luau +0 -18
- package/examples/game/out/ServerScriptService/Boot/DataBoot.server.luau.map.json +0 -13
- package/examples/game/out/ServerScriptService/Configuration/ServerConfig.luau +0 -14
- package/examples/game/out/ServerScriptService/Configuration/ServerConfig.luau.map.json +0 -13
- package/examples/game/out/ServerScriptService/Handlers/Guard.server.luau +0 -15
- package/examples/game/out/ServerScriptService/Handlers/Guard.server.luau.map.json +0 -13
- package/examples/game/out/ServerScriptService/Handlers/PlayerHandler.server.luau +0 -50
- package/examples/game/out/ServerScriptService/Handlers/PlayerHandler.server.luau.map.json +0 -13
- package/examples/game/out/StarterPlayer/StarterCharacterScripts/Character/CharacterReady.client.luau +0 -12
- package/examples/game/out/StarterPlayer/StarterCharacterScripts/Character/CharacterReady.client.luau.map.json +0 -13
- package/examples/game/out/StarterPlayer/StarterPlayerScripts/Controllers/DataController.client.luau +0 -16
- package/examples/game/out/StarterPlayer/StarterPlayerScripts/Controllers/DataController.client.luau.map.json +0 -13
- package/examples/game/src/Client/Controllers/DataController.client.clpp +0 -7
- package/examples/game/src/Client/Main.client.clpp +0 -4
- package/examples/game/src/Declarations/Net.flare +0 -5
- package/examples/game/src/Include/GameTypes.clh +0 -40
- package/examples/game/src/Modules/Core/Guard.server.clpp +0 -7
- package/examples/game/src/Modules/Core/PlayerHandler.server.clpp +0 -23
- package/examples/game/src/Server/Boot/DataBoot.server.clpp +0 -13
- package/examples/game/src/Server/Main.server.clpp +0 -4
- package/templates/game/.clangd +0 -25
- package/templates/game/.vscode/extensions.json +0 -8
- package/templates/game/.vscode/settings.json +0 -17
- package/templates/game/Packages/.gitkeep +0 -0
- package/templates/game/compile_flags.txt +0 -6
- package/templates/game/default.project.json +0 -41
- package/templates/game/include/clpp/datatypes.clh +0 -575
- package/templates/game/include/clpp/generated/enums.clh +0 -4920
- package/templates/game/include/clpp/generated/instances.clh +0 -87182
- package/templates/game/include/clpp/libs/arrayindexer.clh +0 -12
- package/templates/game/include/clpp/libs/axiom.clh +0 -154
- package/templates/game/include/clpp/libs/bloom.clh +0 -58
- package/templates/game/include/clpp/libs/coil.clh +0 -37
- package/templates/game/include/clpp/libs/crest.clh +0 -22
- package/templates/game/include/clpp/libs/echo.clh +0 -52
- package/templates/game/include/clpp/libs/ember.clh +0 -17
- package/templates/game/include/clpp/libs/flare.clh +0 -65
- package/templates/game/include/clpp/libs/gleam.clh +0 -111
- package/templates/game/include/clpp/libs/guide.clh +0 -42
- package/templates/game/include/clpp/libs/helm.clh +0 -31
- package/templates/game/include/clpp/libs/hive.clh +0 -20
- package/templates/game/include/clpp/libs/keep.clh +0 -158
- package/templates/game/include/clpp/libs/lens.clh +0 -24
- package/templates/game/include/clpp/libs/mint.clh +0 -28
- package/templates/game/include/clpp/libs/net.clh +0 -31
- package/templates/game/include/clpp/libs/occlude.clh +0 -15
- package/templates/game/include/clpp/libs/pin.clh +0 -28
- package/templates/game/include/clpp/libs/promise.clh +0 -24
- package/templates/game/include/clpp/libs/roster.clh +0 -123
- package/templates/game/include/clpp/libs/shift.clh +0 -22
- package/templates/game/include/clpp/libs/spark.clh +0 -28
- package/templates/game/include/clpp/libs/stage.clh +0 -23
- package/templates/game/include/clpp/libs/sweep.clh +0 -23
- package/templates/game/include/clpp/libs/trace.clh +0 -18
- package/templates/game/include/clpp/libs/ward.clh +0 -19
- package/templates/game/include/clpp/libs.clh +0 -31
- package/templates/game/include/clpp/roblox.clh +0 -41
- package/templates/game/rokit.toml +0 -5
- package/templates/game/src/Client/Controllers/DataController.client.clpp +0 -7
- package/templates/game/src/Client/Main.client.clpp +0 -4
- package/templates/game/src/Declarations/Net.flare +0 -5
- package/templates/game/src/Include/GameTypes.clh +0 -40
- package/templates/game/src/Modules/Core/Guard.server.clpp +0 -7
- package/templates/game/src/Modules/Core/PlayerHandler.server.clpp +0 -24
- package/templates/game/src/Server/Boot/DataBoot.server.clpp +0 -13
- package/templates/game/src/Server/Main.server.clpp +0 -4
- package/templates/game/stylua.toml +0 -9
- package/templates/game/wally.toml +0 -12
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Roblox surface: the typed Roblox API the CL++ checker validates against.
|
|
3
|
+
* Built from luau-lsp's Roblox definitions (types, nullability, overloads, operators) merged with
|
|
4
|
+
* the Roblox API dump profile (security, read-only, creatable, services, int vs float).
|
|
5
|
+
* Stored as `api/roblox/surface.json` and loaded once per process.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export type LType =
|
|
9
|
+
| { k: "name"; name: string; args?: LType[] }
|
|
10
|
+
| { k: "opt"; t: LType }
|
|
11
|
+
| { k: "union"; ts: LType[] }
|
|
12
|
+
| { k: "inter"; ts: LType[] }
|
|
13
|
+
| { k: "array"; t: LType }
|
|
14
|
+
| { k: "map"; key: LType; value: LType }
|
|
15
|
+
| { k: "table"; fields: Record<string, LType> }
|
|
16
|
+
| { k: "fn"; params: LParam[]; variadic?: LType; returns: LType[]; returnsVariadic?: LType; generics?: string[] }
|
|
17
|
+
| { k: "lit"; value: string | boolean }
|
|
18
|
+
| { k: "pack"; name: string };
|
|
19
|
+
|
|
20
|
+
export interface LParam {
|
|
21
|
+
name?: string;
|
|
22
|
+
t: LType;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type LFn = Extract<LType, { k: "fn" }>;
|
|
26
|
+
|
|
27
|
+
export interface SurfaceMember {
|
|
28
|
+
kind: "property" | "method" | "event" | "callback";
|
|
29
|
+
/** Property type, or the function type of a callback. */
|
|
30
|
+
type?: LType;
|
|
31
|
+
/** Method overloads (without `self`). */
|
|
32
|
+
overloads?: LFn[];
|
|
33
|
+
/** Event argument types. */
|
|
34
|
+
args?: LType[];
|
|
35
|
+
readonly?: boolean;
|
|
36
|
+
/** `true`, or the replacement to suggest. */
|
|
37
|
+
deprecated?: string | true;
|
|
38
|
+
/** Read / call security when not `None`. */
|
|
39
|
+
security?: string;
|
|
40
|
+
writeSecurity?: string;
|
|
41
|
+
yields?: boolean;
|
|
42
|
+
notScriptable?: boolean;
|
|
43
|
+
/** Instance-valued property (may be nil even when the definitions say otherwise). */
|
|
44
|
+
notReplicated?: boolean;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface SurfaceClass {
|
|
48
|
+
name: string;
|
|
49
|
+
superclass: string | null;
|
|
50
|
+
members: Record<string, SurfaceMember>;
|
|
51
|
+
creatable: boolean;
|
|
52
|
+
service: boolean;
|
|
53
|
+
abstract?: boolean;
|
|
54
|
+
deprecated?: string | true;
|
|
55
|
+
notCreatable?: boolean;
|
|
56
|
+
tags?: string[];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface SurfaceStatic {
|
|
60
|
+
kind: "function" | "value";
|
|
61
|
+
overloads?: LFn[];
|
|
62
|
+
type?: LType;
|
|
63
|
+
deprecated?: string | true;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface SurfaceDatatype {
|
|
67
|
+
name: string;
|
|
68
|
+
members: Record<string, SurfaceMember>;
|
|
69
|
+
/** Metamethods: `__add`, `__sub`, `__mul`, `__div`, `__idiv`, `__mod`, `__unm`, `__eq`, `__lt`, `__le`. */
|
|
70
|
+
operators: Record<string, LFn[]>;
|
|
71
|
+
statics: Record<string, SurfaceStatic>;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface SurfaceEnum {
|
|
75
|
+
name: string;
|
|
76
|
+
items: string[];
|
|
77
|
+
deprecated?: string[];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface SurfaceGlobal {
|
|
81
|
+
type?: LType;
|
|
82
|
+
overloads?: LFn[];
|
|
83
|
+
deprecated?: string | true;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface RobloxSurface {
|
|
87
|
+
schema: 1;
|
|
88
|
+
generatedAt: string;
|
|
89
|
+
sources: Record<string, string>;
|
|
90
|
+
classes: Record<string, SurfaceClass>;
|
|
91
|
+
datatypes: Record<string, SurfaceDatatype>;
|
|
92
|
+
enums: Record<string, SurfaceEnum>;
|
|
93
|
+
globals: Record<string, SurfaceGlobal>;
|
|
94
|
+
libraries: Record<string, Record<string, SurfaceStatic>>;
|
|
95
|
+
aliases: Record<string, LType>;
|
|
96
|
+
}
|
package/src/api-cli.ts
CHANGED
|
@@ -55,7 +55,7 @@ export function registerApiCommands(program: import("commander").Command): void
|
|
|
55
55
|
.command("generate")
|
|
56
56
|
.description("Build RobloxTargetProfile and regenerate CL++ / Luau projections")
|
|
57
57
|
.option("--dump <path>", "Mini-API-Dump.json path")
|
|
58
|
-
.option("--skip-headers", "Skip
|
|
58
|
+
.option("--skip-headers", "Skip legacy .clh API projections")
|
|
59
59
|
.option("--skip-luau", "Skip .d.luau generation")
|
|
60
60
|
.option("--docs", "No-op retained for CLI compat (Creator Docs are not mirrored)")
|
|
61
61
|
.action((opts: { dump?: string; skipHeaders?: boolean; skipLuau?: boolean; docs?: boolean }) => {
|
|
@@ -235,7 +235,7 @@ export function registerTargetCommands(program: import("commander").Command): vo
|
|
|
235
235
|
|
|
236
236
|
target
|
|
237
237
|
.command("datamodel")
|
|
238
|
-
.description("Build Typed DataModel profile
|
|
238
|
+
.description("Build Typed DataModel profile (legacy projects also get .clh)")
|
|
239
239
|
.argument("[folder]", "game root with default.project.json", ".")
|
|
240
240
|
.option("-r, --rojo <path>", "Rojo project json")
|
|
241
241
|
.action((folder: string, options: { rojo?: string }) => {
|
package/src/cli.ts
CHANGED
|
@@ -4,8 +4,7 @@ import path from "node:path";
|
|
|
4
4
|
import { pkg } from "./package-info.js";
|
|
5
5
|
import { start as startLsp } from "./lsp.js";
|
|
6
6
|
import { collectSources, toLuauPath } from "./clpp/paths.js";
|
|
7
|
-
import {
|
|
8
|
-
import { clppInstalls, clppManifest, clppVersion, hasClpp, resolveClppBinary } from "./clpp/runner.js";
|
|
7
|
+
import { clppManifest, hasClpp } from "./clpp/runner.js";
|
|
9
8
|
import { installEditorSupport, syncEditorSupport } from "./intellisense.js";
|
|
10
9
|
import { RojoMapper } from "./utils/rojo-mapper.js";
|
|
11
10
|
import { ProcessOrchestrator } from "./utils/process-orchestrator.js";
|
|
@@ -103,7 +102,8 @@ program
|
|
|
103
102
|
|
|
104
103
|
program
|
|
105
104
|
.command("lsp")
|
|
106
|
-
.description("
|
|
105
|
+
.description("Schema language server over stdio")
|
|
106
|
+
.option("--stdio", "Use standard input/output (default)")
|
|
107
107
|
.argument("[folder]", "project folder", ".")
|
|
108
108
|
.action((folder: string) => {
|
|
109
109
|
startLsp({ projectRoot: path.resolve(process.cwd(), folder) });
|
|
@@ -112,7 +112,7 @@ program
|
|
|
112
112
|
program
|
|
113
113
|
.command("intellisense")
|
|
114
114
|
.alias("intelisense")
|
|
115
|
-
.description("Install schema
|
|
115
|
+
.description("Install schema editor support and sync .vscode")
|
|
116
116
|
.argument("[folder]", "project folder", ".")
|
|
117
117
|
.action(async (folder: string) => {
|
|
118
118
|
try {
|
|
@@ -120,7 +120,6 @@ program
|
|
|
120
120
|
syncEditorSupport(root, loadConfig(root));
|
|
121
121
|
await installEditorSupport();
|
|
122
122
|
console.log("cluaupp: editor associations written in", root);
|
|
123
|
-
console.log("cluaupp:", CLPP_INSTALL_HINT);
|
|
124
123
|
} catch (err) {
|
|
125
124
|
console.error(err instanceof Error ? err.message : err);
|
|
126
125
|
process.exit(1);
|
|
@@ -129,20 +128,22 @@ program
|
|
|
129
128
|
|
|
130
129
|
program
|
|
131
130
|
.command("language")
|
|
132
|
-
.description("
|
|
133
|
-
.
|
|
131
|
+
.description("Describe the Roblox .clp target; --legacy probes external clpp")
|
|
132
|
+
.option("--legacy", "Read the manifest of the compatible legacy compiler")
|
|
133
|
+
.action((options: { legacy?: boolean }) => {
|
|
134
134
|
try {
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
135
|
+
const manifest = options.legacy ? clppManifest() : {
|
|
136
|
+
name: "Cluaupp Roblox target",
|
|
137
|
+
version: pkg.version,
|
|
138
|
+
status: "experimental",
|
|
139
|
+
sourceExtensions: [".clp"],
|
|
140
|
+
fileTags: { server: ".server.clp", client: ".client.clp", plugin: ".plugin.clp" },
|
|
141
|
+
importSyntax: "link",
|
|
142
|
+
headers: false,
|
|
143
|
+
builtinLinks: ["@clpp.roblox", "@clpp.libs.<Library>"],
|
|
144
|
+
legacy: { sourceExtensions: [".clpp", ".clh"], command: "cluaupp language --legacy" },
|
|
145
|
+
};
|
|
146
|
+
console.log(JSON.stringify(manifest, null, "\t"));
|
|
146
147
|
} catch (err) {
|
|
147
148
|
console.error(err instanceof Error ? err.message : err);
|
|
148
149
|
process.exit(1);
|
package/src/clpp/contract.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/** Legacy adapter policy for CL++ 0.8.x. Requires an upstream compiler implementing the handshake. */
|
|
2
2
|
|
|
3
3
|
export interface CompileRequest {
|
|
4
4
|
source: string;
|
|
@@ -21,6 +21,10 @@ export interface CompileRequest {
|
|
|
21
21
|
|
|
22
22
|
export interface CompileDiagnostic {
|
|
23
23
|
message: string;
|
|
24
|
+
code?: string;
|
|
25
|
+
help?: string;
|
|
26
|
+
endLine?: number;
|
|
27
|
+
endColumn?: number;
|
|
24
28
|
/** 1-based */
|
|
25
29
|
line: number;
|
|
26
30
|
/** 1-based */
|
|
@@ -36,6 +40,14 @@ export interface ModuleRequireEntry {
|
|
|
36
40
|
toFile?: string;
|
|
37
41
|
}
|
|
38
42
|
|
|
43
|
+
export interface SourceMapLine {
|
|
44
|
+
luauLine: number;
|
|
45
|
+
clppLine: number;
|
|
46
|
+
file: string;
|
|
47
|
+
luauColumn?: number;
|
|
48
|
+
clppColumn?: number;
|
|
49
|
+
}
|
|
50
|
+
|
|
39
51
|
export interface CompileArtifact {
|
|
40
52
|
ok: boolean;
|
|
41
53
|
luau: string;
|
|
@@ -48,7 +60,7 @@ export interface CompileArtifact {
|
|
|
48
60
|
libraries: string[];
|
|
49
61
|
error?: string;
|
|
50
62
|
diagnostics?: CompileDiagnostic[];
|
|
51
|
-
sourceMap?:
|
|
63
|
+
sourceMap?: SourceMapLine[];
|
|
52
64
|
/** Named `import` / quoted include requires from CL++ (RFC 0003). */
|
|
53
65
|
requires?: ModuleRequireEntry[];
|
|
54
66
|
/** Selective @native candidates (RFC 0011). Not auto-applied as blanket. */
|
|
@@ -62,6 +74,8 @@ export interface CompileArtifact {
|
|
|
62
74
|
}
|
|
63
75
|
|
|
64
76
|
export interface LanguageManifest {
|
|
77
|
+
/** Explicit host protocol handshake; a VM manifest is not compatible. */
|
|
78
|
+
contractVersion: number;
|
|
65
79
|
name: "CL++";
|
|
66
80
|
id: "clpp";
|
|
67
81
|
version: string;
|
|
@@ -77,6 +91,9 @@ export interface LanguageManifest {
|
|
|
77
91
|
}
|
|
78
92
|
|
|
79
93
|
export const MIN_CLPP_VERSION = "0.8.0";
|
|
94
|
+
export const MAX_CLPP_VERSION_EXCLUSIVE = "0.9.0";
|
|
95
|
+
export const CLPP_VERSION_RANGE = ">=0.8.0 <0.9.0";
|
|
96
|
+
export const CLPP_CONTRACT_VERSION = 1;
|
|
80
97
|
|
|
81
98
|
export const CLPP_INSTALL_HINT =
|
|
82
|
-
"Install
|
|
99
|
+
"Install the legacy Luau compiler >=0.8.0 <0.9.0 with contractVersion 1 (the CL++ VM is incompatible) (`clpp` from https://github.com/KartzRbx/CLPP/releases). Put it on PATH. Prefer `import { Name } from \"./x.clh\"` for language modules; angle `#include <clpp/…>` stays for host prelude. Override with CLPP or CLPP_PATH.";
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { CLPP_CONTRACT_VERSION, CLPP_VERSION_RANGE, MIN_CLPP_VERSION, MAX_CLPP_VERSION_EXCLUSIVE, type LanguageManifest } from "./contract.js";
|
|
2
|
+
|
|
3
|
+
export function parseCompilerVersion(text: string): number[] | null {
|
|
4
|
+
const m = /^(?:clpp\s+|CL\+\+\s+)?(\d+)\.(\d+)\.(\d+)(?:-[\w.-]+)?(?:\+[\w.-]+)?$/i.exec(text.trim());
|
|
5
|
+
return m ? m.slice(1, 4).map(Number) : null;
|
|
6
|
+
}
|
|
7
|
+
function compare(a: number[], b: number[]): number {
|
|
8
|
+
return a[0] - b[0] || a[1] - b[1] || a[2] - b[2];
|
|
9
|
+
}
|
|
10
|
+
export function acceptsCompilerVersion(text: string): boolean {
|
|
11
|
+
const v = parseCompilerVersion(text);
|
|
12
|
+
return !!v && compare(v, parseCompilerVersion(MIN_CLPP_VERSION)!) >= 0 && compare(v, parseCompilerVersion(MAX_CLPP_VERSION_EXCLUSIVE)!) < 0;
|
|
13
|
+
}
|
|
14
|
+
export function validateCompilerHandshake(version: string, value: unknown): LanguageManifest {
|
|
15
|
+
const fail = (reason: string): never => {throw new Error(`cluaupp: incompatible clpp (${version || "unknown"}): ${reason}. Require Luau compiler ${CLPP_VERSION_RANGE}, contractVersion ${CLPP_CONTRACT_VERSION}. The clpp game VM cannot emit Roblox Luau. Set CLPP or CLPP_PATH to the Luau compiler.`);};
|
|
16
|
+
if (!acceptsCompilerVersion(version)) fail("unsupported version");
|
|
17
|
+
if (!value || typeof value !== "object") fail("missing api manifest");
|
|
18
|
+
const m = value as LanguageManifest;
|
|
19
|
+
if (m.id !== "clpp" || m.name !== "CL++") fail("wrong compiler identity");
|
|
20
|
+
if (m.contractVersion !== CLPP_CONTRACT_VERSION) fail("missing or unsupported contractVersion");
|
|
21
|
+
if (!acceptsCompilerVersion(m.version) || compare(parseCompilerVersion(version)!, parseCompilerVersion(m.version)!) !== 0) fail("binary and manifest versions disagree");
|
|
22
|
+
if (!Array.isArray(m.extensions) || ![".clpp", ".clp", ".clh"].every(x => m.extensions.includes(x))) fail("missing source extensions");
|
|
23
|
+
if (!Array.isArray(m.tags) || ![["*.server.clpp", "server", "Script"], ["*.client.clpp", "client", "LocalScript"], ["*.clh", "header", "ModuleScript"]].every(([pattern, kind, cls]) => m.tags.some(t => t.pattern === pattern && t.scriptKind === kind && t.rojoClass === cls))) fail("missing Roblox file tags");
|
|
24
|
+
return m;
|
|
25
|
+
}
|
package/src/clpp/modules.ts
CHANGED
|
@@ -150,7 +150,7 @@ export function resolveLanguageDeps(source: string, fromFile: string, srcDir?: s
|
|
|
150
150
|
}
|
|
151
151
|
|
|
152
152
|
/**
|
|
153
|
-
* Fingerprint source +
|
|
153
|
+
* Fingerprint source + transitive language deps so import/include edits invalidate incremental cache.
|
|
154
154
|
*/
|
|
155
155
|
export function fingerprintSourceWithDeps(
|
|
156
156
|
source: string,
|
|
@@ -159,15 +159,22 @@ export function fingerprintSourceWithDeps(
|
|
|
159
159
|
srcDir: string | undefined,
|
|
160
160
|
hashText: (text: string) => string,
|
|
161
161
|
): string {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
162
|
+
const parts = [configKey, source];
|
|
163
|
+
const seen = new Set<string>([path.resolve(fromFile)]);
|
|
164
|
+
const visit = (text: string, file: string) => {
|
|
165
|
+
for (const ref of collectModuleRefs(text)) {
|
|
166
|
+
if (ref.angled) continue;
|
|
167
|
+
const dep = resolveModuleFile(ref.modulePath, file, srcDir);
|
|
168
|
+
if (!dep) {parts.push("\n//missing:" + file + ":" + ref.modulePath); continue;}
|
|
169
|
+
const key = path.resolve(dep);
|
|
170
|
+
if (seen.has(key)) continue;
|
|
171
|
+
seen.add(key);
|
|
172
|
+
const contents = fs.readFileSync(key, "utf8");
|
|
173
|
+
parts.push("\n//dep:" + key.replace(/\\/g, "/") + "\n", contents);
|
|
174
|
+
visit(contents, key);
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
visit(source, fromFile);
|
|
171
178
|
return hashText(parts.join(""));
|
|
172
179
|
}
|
|
173
180
|
|
package/src/clpp/runner.ts
CHANGED
|
@@ -4,47 +4,66 @@ import os from "node:os";
|
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import {
|
|
6
6
|
CLPP_INSTALL_HINT,
|
|
7
|
-
MIN_CLPP_VERSION,
|
|
8
7
|
type CompileArtifact,
|
|
9
8
|
type CompileDiagnostic,
|
|
10
9
|
type CompileRequest,
|
|
11
10
|
type LanguageManifest,
|
|
12
11
|
} from "./contract.js";
|
|
13
12
|
|
|
13
|
+
import { acceptsCompilerVersion, validateCompilerHandshake } from "./handshake.js";
|
|
14
|
+
|
|
14
15
|
const MISSING_CLPP = `cluaupp: clpp not found. ${CLPP_INSTALL_HINT}`;
|
|
15
16
|
|
|
16
17
|
type Semver = { major: number; minor: number; patch: number };
|
|
17
18
|
|
|
18
19
|
let cachedBin: string | null | undefined;
|
|
20
|
+
let cachedSelectionKey = "";
|
|
21
|
+
const contractSupport = new Map<string, LanguageManifest>();
|
|
22
|
+
|
|
23
|
+
function binaryKey(bin: string): string {
|
|
24
|
+
const stat = fs.statSync(bin);
|
|
25
|
+
return JSON.stringify([bin, stat.size, stat.mtimeMs]);
|
|
26
|
+
}
|
|
19
27
|
|
|
20
28
|
function parseSemver(text: string): Semver | null {
|
|
21
|
-
const match = String(text).match(/(\d+)\.(\d+)
|
|
29
|
+
const match = String(text).match(/(\d+)\.(\d+)(?:\.(\d+))?/);
|
|
22
30
|
if (!match) {
|
|
23
31
|
return null;
|
|
24
32
|
}
|
|
25
|
-
return { major: Number(match[1]), minor: Number(match[2]), patch: Number(match[3]) };
|
|
33
|
+
return { major: Number(match[1]), minor: Number(match[2]), patch: Number(match[3] || 0) };
|
|
26
34
|
}
|
|
27
35
|
|
|
28
36
|
function cmpSemver(a: Semver, b: Semver): number {
|
|
29
37
|
return a.major - b.major || a.minor - b.minor || a.patch - b.patch;
|
|
30
38
|
}
|
|
31
39
|
|
|
32
|
-
function minClpp(): Semver {
|
|
33
|
-
return parseSemver(MIN_CLPP_VERSION) || { major: 0, minor: 8, patch: 0 };
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function tooOldMessage(bin: string, version: string | null): string {
|
|
37
|
-
return `cluaupp: clpp ${version || "unknown"} is too old (${bin}). Need CL++ ${MIN_CLPP_VERSION}+. Install https://github.com/KartzRbx/CLPP/releases or set CLPP / CLPP_PATH.`;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
40
|
function probeVersion(bin: string): string | null {
|
|
41
|
-
const result = spawnSync(bin, ["--version"], { encoding: "utf8", windowsHide: true });
|
|
41
|
+
const result = spawnSync(bin, ["--version"], { encoding: "utf8", windowsHide: true, timeout: 10_000 });
|
|
42
42
|
if (result.status !== 0) {
|
|
43
43
|
return null;
|
|
44
44
|
}
|
|
45
45
|
return String(result.stdout || result.stderr || "").trim() || null;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
function handshake(bin: string): LanguageManifest {
|
|
49
|
+
const key = binaryKey(bin);
|
|
50
|
+
const cached = contractSupport.get(key);
|
|
51
|
+
if (cached) return cached;
|
|
52
|
+
const version = probeVersion(bin) || "";
|
|
53
|
+
let manifest: unknown;
|
|
54
|
+
// Reject the VM before asking it to compile or interpret any input.
|
|
55
|
+
if (acceptsCompilerVersion(version)) {
|
|
56
|
+
const result = spawnSync(bin, ["api", "manifest"], { encoding: "utf8", windowsHide: true, timeout: 10_000 });
|
|
57
|
+
if (result.status === 0) {try {manifest = JSON.parse(String(result.stdout || ""));} catch {}}
|
|
58
|
+
}
|
|
59
|
+
const validated = validateCompilerHandshake(version, manifest);
|
|
60
|
+
contractSupport.set(key, validated);
|
|
61
|
+
return validated;
|
|
62
|
+
}
|
|
63
|
+
function supportsLuauContract(bin: string): boolean {
|
|
64
|
+
try {handshake(bin); return true;} catch {return false;}
|
|
65
|
+
}
|
|
66
|
+
|
|
48
67
|
function pushCandidate(out: string[], seen: Set<string>, item: string | undefined): void {
|
|
49
68
|
const value = String(item || "").trim();
|
|
50
69
|
if (!value) {
|
|
@@ -71,9 +90,11 @@ function listClppCandidates(): string[] {
|
|
|
71
90
|
const override: string[] = [];
|
|
72
91
|
pushCandidate(override, seen, process.env.CLPP);
|
|
73
92
|
pushCandidate(override, seen, process.env.CLPP_PATH);
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
93
|
+
if (override.length > 0) {
|
|
94
|
+
for (const bin of override) {
|
|
95
|
+
if (!existsBin(bin)) throw new Error(`cluaupp: explicit CLPP/CLPP_PATH does not exist: ${bin}`);
|
|
96
|
+
}
|
|
97
|
+
return override;
|
|
77
98
|
}
|
|
78
99
|
|
|
79
100
|
const out: string[] = [];
|
|
@@ -106,7 +127,7 @@ function pickClppBinary(): string | null {
|
|
|
106
127
|
if (!fallback) {
|
|
107
128
|
fallback = bin;
|
|
108
129
|
}
|
|
109
|
-
if (!parsed) {
|
|
130
|
+
if (!parsed || !supportsLuauContract(bin)) {
|
|
110
131
|
continue;
|
|
111
132
|
}
|
|
112
133
|
if (!best || cmpSemver(parsed, best.parsed) > 0) {
|
|
@@ -117,9 +138,11 @@ function pickClppBinary(): string | null {
|
|
|
117
138
|
}
|
|
118
139
|
|
|
119
140
|
function whichClpp(): string | null {
|
|
120
|
-
|
|
141
|
+
const selectionKey = JSON.stringify([process.env.CLPP, process.env.CLPP_PATH, process.env.PATH]);
|
|
142
|
+
if (cachedBin !== undefined && selectionKey === cachedSelectionKey) {
|
|
121
143
|
return cachedBin;
|
|
122
144
|
}
|
|
145
|
+
cachedSelectionKey = selectionKey;
|
|
123
146
|
cachedBin = pickClppBinary();
|
|
124
147
|
return cachedBin;
|
|
125
148
|
}
|
|
@@ -133,11 +156,7 @@ export function resolveClppBinary(): string {
|
|
|
133
156
|
if (!bin) {
|
|
134
157
|
throw new Error(MISSING_CLPP);
|
|
135
158
|
}
|
|
136
|
-
|
|
137
|
-
const parsed = parseSemver(version || "");
|
|
138
|
-
if (!parsed || cmpSemver(parsed, minClpp()) < 0) {
|
|
139
|
-
throw new Error(tooOldMessage(bin, version));
|
|
140
|
-
}
|
|
159
|
+
handshake(bin);
|
|
141
160
|
return bin;
|
|
142
161
|
}
|
|
143
162
|
|
|
@@ -145,7 +164,7 @@ function parseArtifact(stdout: string, fileName: string, fallbackError: string):
|
|
|
145
164
|
const text = String(stdout || "").trim();
|
|
146
165
|
try {
|
|
147
166
|
const parsed = JSON.parse(text) as CompileArtifact;
|
|
148
|
-
if (parsed && typeof parsed === "object") {
|
|
167
|
+
if (parsed && typeof parsed === "object" && typeof parsed.ok === "boolean" && typeof parsed.luau === "string" && typeof parsed.fileName === "string") {
|
|
149
168
|
return parsed;
|
|
150
169
|
}
|
|
151
170
|
} catch {
|
|
@@ -171,7 +190,7 @@ export function formatClppFailure(artifact: CompileArtifact, fallback: string):
|
|
|
171
190
|
return diags
|
|
172
191
|
.map((d: CompileDiagnostic) => {
|
|
173
192
|
const where = `${artifact.fileName || "clpp"}:${d.line}:${d.column}`;
|
|
174
|
-
return `${where}: ${d.message}`;
|
|
193
|
+
return `${where}: ${d.severity}${d.code ? " [" + d.code + "]" : ""}: ${d.message}${d.help ? "\n " + d.help : ""}`;
|
|
175
194
|
})
|
|
176
195
|
.join("\n");
|
|
177
196
|
}
|
|
@@ -200,7 +219,7 @@ export function compileViaClpp(request: CompileRequest): CompileArtifact {
|
|
|
200
219
|
});
|
|
201
220
|
const stderr = String(result.stderr || "").trim();
|
|
202
221
|
const artifact = parseArtifact(result.stdout, request.fileName, stderr || result.error?.message || "clpp api compile failed");
|
|
203
|
-
if (!artifact.ok) {
|
|
222
|
+
if (result.status !== 0 || result.error || !artifact.ok) {
|
|
204
223
|
throw new Error(formatClppFailure(artifact, stderr || result.error?.message || `clpp failed (${result.status})`));
|
|
205
224
|
}
|
|
206
225
|
return artifact;
|
|
@@ -232,7 +251,7 @@ export function compileViaClppAsync(request: CompileRequest): Promise<CompileArt
|
|
|
232
251
|
child.on("error", reject);
|
|
233
252
|
child.on("close", (code) => {
|
|
234
253
|
const artifact = parseArtifact(stdout, request.fileName, stderr.trim() || `clpp api compile failed (${code})`);
|
|
235
|
-
if (!artifact.ok) {
|
|
254
|
+
if (code !== 0 || !artifact.ok) {
|
|
236
255
|
reject(new Error(formatClppFailure(artifact, stderr.trim() || `clpp failed (${code})`)));
|
|
237
256
|
return;
|
|
238
257
|
}
|
|
@@ -256,14 +275,7 @@ export function compileViaClppAsync(request: CompileRequest): Promise<CompileArt
|
|
|
256
275
|
|
|
257
276
|
export function clppManifest(): LanguageManifest {
|
|
258
277
|
const bin = resolveClppBinary();
|
|
259
|
-
|
|
260
|
-
encoding: "utf8",
|
|
261
|
-
windowsHide: true,
|
|
262
|
-
});
|
|
263
|
-
if (result.status !== 0) {
|
|
264
|
-
throw new Error(String(result.stderr || result.error?.message || "clpp api manifest failed"));
|
|
265
|
-
}
|
|
266
|
-
return JSON.parse(result.stdout) as LanguageManifest;
|
|
278
|
+
return handshake(bin);
|
|
267
279
|
}
|
|
268
280
|
|
|
269
281
|
export function clppVersion(): string | null {
|
|
@@ -282,3 +294,9 @@ export function clppInstalls(): Array<{ bin: string; version: string | null; sel
|
|
|
282
294
|
selected: bin === selected,
|
|
283
295
|
}));
|
|
284
296
|
}
|
|
297
|
+
|
|
298
|
+
/** Include the actual binary and handshake in incremental fingerprints. */
|
|
299
|
+
export function clppCacheIdentity(): string {
|
|
300
|
+
const bin = resolveClppBinary();
|
|
301
|
+
return JSON.stringify([binaryKey(bin), handshake(bin)]);
|
|
302
|
+
}
|
package/src/compile.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { compileViaClpp, compileViaClppAsync } from "./clpp/runner.js";
|
|
|
5
5
|
import { clppLuauToGame, shouldSkipInit } from "./clpp/postprocess.js";
|
|
6
6
|
import { emitKind, siblingHeader, toLuauPath } from "./clpp/paths.js";
|
|
7
7
|
import type { CompileArtifact } from "./clpp/contract.js";
|
|
8
|
-
import { projectRoot } from "./package-info.js";
|
|
8
|
+
import { pkg, projectRoot } from "./package-info.js";
|
|
9
9
|
import { runContextFromFileName } from "./target/roblox.js";
|
|
10
10
|
import { capabilityProfilePath, writeCapabilityProfile, loadCapabilityProfile } from "./target/capability-profile.js";
|
|
11
11
|
import { assertContextSafe, listContextWarnings, formatContextViolations } from "./target/context-check.js";
|
|
@@ -15,6 +15,8 @@ import { writeDatamodelArtifacts } from "./target/datamodel-generate.js";
|
|
|
15
15
|
import { assertParallelSafe } from "./target/parallel-check.js";
|
|
16
16
|
import { assertAuthoritySafe, loadPlatformPolicy } from "./target/authority-check.js";
|
|
17
17
|
import { assertGetServiceSafe } from "./target/get-service.js";
|
|
18
|
+
import { compileSource as compileRobloxClp } from "./compiler/project.js";
|
|
19
|
+
import { formatDiagnostic } from "./compiler/diagnostics.js";
|
|
18
20
|
|
|
19
21
|
function defaultTargetProfilePath(): string | undefined {
|
|
20
22
|
const cached = path.join(projectRoot, "api", "normalized", "roblox-target.profile.json");
|
|
@@ -130,7 +132,7 @@ function finishArtifact(
|
|
|
130
132
|
return {
|
|
131
133
|
kind: emitKind(relative),
|
|
132
134
|
plan: { kind: emitKind(relative), strict: options.strict === true },
|
|
133
|
-
files: [{ name: outName, contents: luau }],
|
|
135
|
+
files: [{ name: outName, contents: luau, sourceMap: artifact.sourceMap, generatedSource: artifact.luau }],
|
|
134
136
|
stale: [],
|
|
135
137
|
};
|
|
136
138
|
}
|
|
@@ -140,6 +142,9 @@ export function compileSource(source: string, fileName?: string, options: Compil
|
|
|
140
142
|
}
|
|
141
143
|
|
|
142
144
|
export function compileService(source: string, fileName?: string, options: CompileOptions = {}): CompileServiceResult {
|
|
145
|
+
if (/\.clp$/i.test(options.relativeName || fileName || "")) {
|
|
146
|
+
return compileModernSource(source, fileName, options);
|
|
147
|
+
}
|
|
143
148
|
const args = compileArgs(source, fileName, options);
|
|
144
149
|
enforcePlatform(source, args.relative, args);
|
|
145
150
|
const artifact = compileViaClpp(args.request);
|
|
@@ -147,8 +152,21 @@ export function compileService(source: string, fileName?: string, options: Compi
|
|
|
147
152
|
}
|
|
148
153
|
|
|
149
154
|
export async function compileServiceAsync(source: string, fileName?: string, options: CompileOptions = {}): Promise<CompileServiceResult> {
|
|
155
|
+
if (/\.clp$/i.test(options.relativeName || fileName || "")) {
|
|
156
|
+
return compileModernSource(source, fileName, options);
|
|
157
|
+
}
|
|
150
158
|
const args = compileArgs(source, fileName, options);
|
|
151
159
|
enforcePlatform(source, args.relative, args);
|
|
152
160
|
const artifact = await compileViaClppAsync(args.request);
|
|
153
161
|
return finishArtifact(artifact, args.relative, args.outName, args.options, args.skipInit, source);
|
|
154
162
|
}
|
|
163
|
+
|
|
164
|
+
function compileModernSource(source: string, fileName: string | undefined, options: CompileOptions): CompileServiceResult {
|
|
165
|
+
const relative = (options.relativeName || fileName || "input.clp").replace(/\\/g, "/");
|
|
166
|
+
const srcDir = options.srcDir || (options.filePath ? path.dirname(path.resolve(options.filePath)) : process.cwd());
|
|
167
|
+
const result = compileRobloxClp(source, relative, { srcDir, strict: options.strict === true, version: pkg.version });
|
|
168
|
+
if (result.luau === null) {
|
|
169
|
+
throw new Error(result.diagnostics.filter((d) => d.severity === "error").map((d) => formatDiagnostic(d, result.source)).join("\n") || `${relative}: Luau emission failed`);
|
|
170
|
+
}
|
|
171
|
+
return { kind: emitKind(relative), files: [{ name: options.outName || toLuauPath(relative), contents: result.luau, sourceMap: result.sourceMap, generatedSource: result.luau }], stale: [] };
|
|
172
|
+
}
|