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.
Files changed (210) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +29 -41
  3. package/api/generated/bench-latest.json +43 -21
  4. package/api/generated/lsp-index.json +1 -1
  5. package/api/manifests/roblox-target.manifest.json +5 -5
  6. package/api/normalized/roblox-target.profile.json +8729 -267
  7. package/api/roblox/luau-builtins.d.luau +185 -0
  8. package/api/roblox/surface.json +1 -0
  9. package/api/roblox-api.lock.json +11 -2
  10. package/bin/cluaupp.js +38 -0
  11. package/editors/vscode/README.md +1 -1
  12. package/editors/vscode/package.json +50 -15
  13. package/editors/vscode/snippets/flare.json +1 -1
  14. package/editors/vscode/snippets/hive.json +1 -1
  15. package/editors/vscode/syntaxes/clpp.tmLanguage.json +211 -0
  16. package/examples/README.md +9 -9
  17. package/examples/game/cluaupp.config.json +2 -2
  18. package/examples/game/default.project.json +4 -15
  19. package/examples/game/out/ReplicatedStorage/Shared/GameInfo.luau +8 -0
  20. package/examples/game/out/ReplicatedStorage/Shared/GameInfo.luau.map.json +13 -0
  21. package/examples/game/out/ServerScriptService/Leaderstats.server.luau +26 -0
  22. package/examples/game/out/ServerScriptService/Leaderstats.server.luau.map.json +13 -0
  23. package/examples/game/out/ServerScriptService/Main.server.luau +15 -0
  24. package/examples/game/out/ServerScriptService/Main.server.luau.map.json +13 -0
  25. package/examples/game/out/StarterPlayer/StarterPlayerScripts/Main.client.luau +10 -0
  26. package/examples/game/out/StarterPlayer/StarterPlayerScripts/Main.client.luau.map.json +13 -0
  27. package/examples/game/src/ReplicatedStorage/Shared/GameInfo.clp +1 -0
  28. package/examples/game/src/ServerScriptService/Leaderstats.server.clp +18 -0
  29. package/examples/game/src/ServerScriptService/Main.server.clp +9 -0
  30. package/examples/game/src/StarterPlayer/StarterPlayerScripts/Main.client.clp +4 -0
  31. package/generated/api/build-profile.js +1 -1
  32. package/generated/api/loader.js +2 -2
  33. package/generated/api/surface/build.js +328 -0
  34. package/generated/api/surface/luau-defs.js +595 -0
  35. package/generated/api/surface/model.js +8 -0
  36. package/generated/api-cli.js +2 -2
  37. package/generated/cli.js +18 -17
  38. package/generated/clpp/contract.js +6 -3
  39. package/generated/clpp/handshake.js +36 -0
  40. package/generated/clpp/modules.js +20 -9
  41. package/generated/clpp/runner.js +62 -31
  42. package/generated/compile.js +18 -1
  43. package/generated/compiler/ast.js +3 -0
  44. package/generated/compiler/checker/calls.js +999 -0
  45. package/generated/compiler/checker/checker.js +1215 -0
  46. package/generated/compiler/checker/exprs.js +791 -0
  47. package/generated/compiler/checker/flow.js +168 -0
  48. package/generated/compiler/checker/members.js +763 -0
  49. package/generated/compiler/checker/statements.js +792 -0
  50. package/generated/compiler/checker/symbols.js +90 -0
  51. package/generated/compiler/checker/typeops.js +348 -0
  52. package/generated/compiler/checker/yields.js +115 -0
  53. package/generated/compiler/diagnostics.js +138 -0
  54. package/generated/compiler/emit/emitter.js +2356 -0
  55. package/generated/compiler/emit/luau-types.js +137 -0
  56. package/generated/compiler/emit/names.js +130 -0
  57. package/generated/compiler/emit/runtime.js +432 -0
  58. package/generated/compiler/lexer.js +403 -0
  59. package/generated/compiler/parser.js +1881 -0
  60. package/generated/compiler/project.js +332 -0
  61. package/generated/compiler/roblox/api.js +388 -0
  62. package/generated/compiler/roblox/context.js +100 -0
  63. package/generated/compiler/source.js +59 -0
  64. package/generated/compiler/stdlib.js +232 -0
  65. package/generated/compiler/types.js +250 -0
  66. package/generated/intellisense.js +1 -15
  67. package/generated/lsp.js +1 -1
  68. package/generated/target/build-cache.js +4 -4
  69. package/generated/target/datamodel-generate.js +12 -0
  70. package/generated/target/gen-rojo-tree.js +15 -0
  71. package/generated/target/source-map.js +61 -8
  72. package/generated/target/studio-bridge.js +1 -0
  73. package/generated/utils/project.js +104 -31
  74. package/include/README.md +5 -0
  75. package/native/bin/.gitkeep +1 -0
  76. package/package.json +17 -6
  77. package/plugins/CluauppNav/Plugin.luau +28 -4
  78. package/rust/cluaupp/Cargo.lock +795 -0
  79. package/rust/cluaupp/Cargo.toml +17 -0
  80. package/rust/cluaupp/src/main.rs +717 -0
  81. package/scripts/postinstall-native.js +56 -0
  82. package/skills/README.md +3 -3
  83. package/skills/clpp-hive-flare/SKILL.md +30 -26
  84. package/skills/clpp-language/SKILL.md +67 -74
  85. package/skills/clpp-language/compiler.md +24 -36
  86. package/skills/clpp-style/SKILL.md +31 -44
  87. package/src/api/build-profile.ts +1 -1
  88. package/src/api/loader.ts +2 -2
  89. package/src/api/surface/build.ts +374 -0
  90. package/src/api/surface/luau-defs.ts +635 -0
  91. package/src/api/surface/model.ts +96 -0
  92. package/src/api-cli.ts +2 -2
  93. package/src/cli.ts +19 -18
  94. package/src/clpp/contract.ts +20 -3
  95. package/src/clpp/handshake.ts +25 -0
  96. package/src/clpp/modules.ts +17 -10
  97. package/src/clpp/runner.ts +52 -34
  98. package/src/compile.ts +20 -2
  99. package/src/compiler/ast.ts +336 -0
  100. package/src/compiler/checker/calls.ts +1025 -0
  101. package/src/compiler/checker/checker.ts +1352 -0
  102. package/src/compiler/checker/exprs.ts +805 -0
  103. package/src/compiler/checker/flow.ts +174 -0
  104. package/src/compiler/checker/members.ts +793 -0
  105. package/src/compiler/checker/statements.ts +802 -0
  106. package/src/compiler/checker/symbols.ts +219 -0
  107. package/src/compiler/checker/typeops.ts +355 -0
  108. package/src/compiler/checker/yields.ts +120 -0
  109. package/src/compiler/diagnostics.ts +163 -0
  110. package/src/compiler/emit/emitter.ts +2486 -0
  111. package/src/compiler/emit/luau-types.ts +143 -0
  112. package/src/compiler/emit/names.ts +130 -0
  113. package/src/compiler/emit/runtime.ts +438 -0
  114. package/src/compiler/lexer.ts +444 -0
  115. package/src/compiler/parser.ts +1943 -0
  116. package/src/compiler/project.ts +354 -0
  117. package/src/compiler/roblox/api.ts +408 -0
  118. package/src/compiler/roblox/context.ts +108 -0
  119. package/src/compiler/source.ts +67 -0
  120. package/src/compiler/stdlib.ts +256 -0
  121. package/src/compiler/types.ts +313 -0
  122. package/src/intellisense.ts +1 -13
  123. package/src/lsp.ts +1 -1
  124. package/src/target/build-cache.ts +5 -8
  125. package/src/target/datamodel-generate.ts +12 -1
  126. package/src/target/gen-rojo-tree.ts +15 -0
  127. package/src/target/source-map.ts +65 -27
  128. package/src/target/studio-bridge.ts +3 -0
  129. package/src/types.ts +3 -0
  130. package/src/utils/project.ts +99 -37
  131. package/templates/README.md +7 -0
  132. package/templates/{game → game-modern}/cluaupp.config.json +2 -2
  133. package/templates/{game → game-modern}/package.json +1 -2
  134. package/templates/game-modern/rokit.toml +2 -0
  135. package/templates/game-modern/src/ReplicatedStorage/Shared/GameInfo.clp +1 -0
  136. package/templates/game-modern/src/ServerScriptService/Handlers/Guard.server.clp +4 -0
  137. package/templates/game-modern/src/ServerScriptService/Main.server.clp +8 -0
  138. package/templates/game-modern/src/StarterPlayer/StarterPlayerScripts/Main.client.clp +4 -0
  139. package/examples/game/out/ReplicatedStorage/Shared/Constants/Datas/TemplateData.luau +0 -29
  140. package/examples/game/out/ReplicatedStorage/Shared/Constants/Datas/TemplateData.luau.map.json +0 -13
  141. package/examples/game/out/ReplicatedStorage/Shared/Net/Net.luau +0 -83
  142. package/examples/game/out/ReplicatedStorage/Shared/Net/Net.luau.map.json +0 -13
  143. package/examples/game/out/ServerScriptService/Boot/DataBoot.server.luau +0 -18
  144. package/examples/game/out/ServerScriptService/Boot/DataBoot.server.luau.map.json +0 -13
  145. package/examples/game/out/ServerScriptService/Configuration/ServerConfig.luau +0 -14
  146. package/examples/game/out/ServerScriptService/Configuration/ServerConfig.luau.map.json +0 -13
  147. package/examples/game/out/ServerScriptService/Handlers/Guard.server.luau +0 -15
  148. package/examples/game/out/ServerScriptService/Handlers/Guard.server.luau.map.json +0 -13
  149. package/examples/game/out/ServerScriptService/Handlers/PlayerHandler.server.luau +0 -50
  150. package/examples/game/out/ServerScriptService/Handlers/PlayerHandler.server.luau.map.json +0 -13
  151. package/examples/game/out/StarterPlayer/StarterCharacterScripts/Character/CharacterReady.client.luau +0 -12
  152. package/examples/game/out/StarterPlayer/StarterCharacterScripts/Character/CharacterReady.client.luau.map.json +0 -13
  153. package/examples/game/out/StarterPlayer/StarterPlayerScripts/Controllers/DataController.client.luau +0 -16
  154. package/examples/game/out/StarterPlayer/StarterPlayerScripts/Controllers/DataController.client.luau.map.json +0 -13
  155. package/examples/game/src/Client/Controllers/DataController.client.clpp +0 -7
  156. package/examples/game/src/Client/Main.client.clpp +0 -4
  157. package/examples/game/src/Declarations/Net.flare +0 -5
  158. package/examples/game/src/Include/GameTypes.clh +0 -40
  159. package/examples/game/src/Modules/Core/Guard.server.clpp +0 -7
  160. package/examples/game/src/Modules/Core/PlayerHandler.server.clpp +0 -23
  161. package/examples/game/src/Server/Boot/DataBoot.server.clpp +0 -13
  162. package/examples/game/src/Server/Main.server.clpp +0 -4
  163. package/templates/game/.clangd +0 -25
  164. package/templates/game/.vscode/extensions.json +0 -8
  165. package/templates/game/.vscode/settings.json +0 -17
  166. package/templates/game/Packages/.gitkeep +0 -0
  167. package/templates/game/compile_flags.txt +0 -6
  168. package/templates/game/default.project.json +0 -41
  169. package/templates/game/include/clpp/datatypes.clh +0 -575
  170. package/templates/game/include/clpp/generated/enums.clh +0 -4920
  171. package/templates/game/include/clpp/generated/instances.clh +0 -87182
  172. package/templates/game/include/clpp/libs/arrayindexer.clh +0 -12
  173. package/templates/game/include/clpp/libs/axiom.clh +0 -154
  174. package/templates/game/include/clpp/libs/bloom.clh +0 -58
  175. package/templates/game/include/clpp/libs/coil.clh +0 -37
  176. package/templates/game/include/clpp/libs/crest.clh +0 -22
  177. package/templates/game/include/clpp/libs/echo.clh +0 -52
  178. package/templates/game/include/clpp/libs/ember.clh +0 -17
  179. package/templates/game/include/clpp/libs/flare.clh +0 -65
  180. package/templates/game/include/clpp/libs/gleam.clh +0 -111
  181. package/templates/game/include/clpp/libs/guide.clh +0 -42
  182. package/templates/game/include/clpp/libs/helm.clh +0 -31
  183. package/templates/game/include/clpp/libs/hive.clh +0 -20
  184. package/templates/game/include/clpp/libs/keep.clh +0 -158
  185. package/templates/game/include/clpp/libs/lens.clh +0 -24
  186. package/templates/game/include/clpp/libs/mint.clh +0 -28
  187. package/templates/game/include/clpp/libs/net.clh +0 -31
  188. package/templates/game/include/clpp/libs/occlude.clh +0 -15
  189. package/templates/game/include/clpp/libs/pin.clh +0 -28
  190. package/templates/game/include/clpp/libs/promise.clh +0 -24
  191. package/templates/game/include/clpp/libs/roster.clh +0 -123
  192. package/templates/game/include/clpp/libs/shift.clh +0 -22
  193. package/templates/game/include/clpp/libs/spark.clh +0 -28
  194. package/templates/game/include/clpp/libs/stage.clh +0 -23
  195. package/templates/game/include/clpp/libs/sweep.clh +0 -23
  196. package/templates/game/include/clpp/libs/trace.clh +0 -18
  197. package/templates/game/include/clpp/libs/ward.clh +0 -19
  198. package/templates/game/include/clpp/libs.clh +0 -31
  199. package/templates/game/include/clpp/roblox.clh +0 -41
  200. package/templates/game/rokit.toml +0 -5
  201. package/templates/game/src/Client/Controllers/DataController.client.clpp +0 -7
  202. package/templates/game/src/Client/Main.client.clpp +0 -4
  203. package/templates/game/src/Declarations/Net.flare +0 -5
  204. package/templates/game/src/Include/GameTypes.clh +0 -40
  205. package/templates/game/src/Modules/Core/Guard.server.clpp +0 -7
  206. package/templates/game/src/Modules/Core/PlayerHandler.server.clpp +0 -24
  207. package/templates/game/src/Server/Boot/DataBoot.server.clpp +0 -13
  208. package/templates/game/src/Server/Main.server.clpp +0 -4
  209. package/templates/game/stylua.toml +0 -9
  210. package/templates/game/wally.toml +0 -12
@@ -3,23 +3,76 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.remapSourceLines = remapSourceLines;
7
+ exports.embedSourceMap = embedSourceMap;
6
8
  exports.writeSourceMap = writeSourceMap;
7
- /** Minimal source map: CL++ path ↔ emitted Luau (1:1 file mapping for v1). */
9
+ /** Compiler coordinates are retained only for lines that survive host transformations. */
8
10
  const node_fs_1 = __importDefault(require("node:fs"));
9
11
  const node_path_1 = __importDefault(require("node:path"));
12
+ /** LCS alignment: inserted headers and removed lines never become fake source locations. */
13
+ function remapSourceLines(original, generated, mappings = []) {
14
+ const a = original.split(/\r?\n/);
15
+ const b = generated.split(/\r?\n/);
16
+ if (original === generated)
17
+ return mappings.map(m => ({ ...m }));
18
+ // Avoid unbounded memory for generated bundles. An absent map is safer than a wrong one.
19
+ if (a.length * b.length > 4_000_000)
20
+ return [];
21
+ const widths = b.length + 1;
22
+ const dp = new Uint32Array((a.length + 1) * widths);
23
+ for (let i = a.length - 1; i >= 0; i--)
24
+ for (let j = b.length - 1; j >= 0; j--) {
25
+ dp[i * widths + j] = a[i].trim() === b[j].trim() ? 1 + dp[(i + 1) * widths + j + 1] : Math.max(dp[(i + 1) * widths + j], dp[i * widths + j + 1]);
26
+ }
27
+ const translated = new Map();
28
+ let i = 0, j = 0;
29
+ while (i < a.length && j < b.length) {
30
+ if (a[i].trim() === b[j].trim()) {
31
+ translated.set(i + 1, j + 1);
32
+ i++;
33
+ j++;
34
+ }
35
+ else if (dp[(i + 1) * widths + j] >= dp[i * widths + j + 1])
36
+ i++;
37
+ else
38
+ j++;
39
+ }
40
+ return mappings.flatMap(m => {
41
+ const line = translated.get(m.luauLine);
42
+ if (!line || !Number.isInteger(m.clppLine) || m.clppLine < 1)
43
+ return [];
44
+ // Formatting can move columns; do not invent a column in that case.
45
+ const exact = a[m.luauLine - 1] === b[line - 1];
46
+ const { luauColumn, ...rest } = m;
47
+ return [{ ...rest, luauLine: line, ...(exact && luauColumn ? { luauColumn } : {}) }];
48
+ });
49
+ }
50
+ function embedSourceMap(luau, lines) {
51
+ if (!lines.length)
52
+ return { contents: luau, lines: [] };
53
+ const shifted = lines.map(m => ({ ...m, luauLine: m.luauLine + 1 }));
54
+ // Preserve Luau directives at the start of the file.
55
+ const parts = luau.split(/\r?\n/);
56
+ let index = 0;
57
+ while (index < parts.length && parts[index].startsWith("--!"))
58
+ index++;
59
+ const adjusted = shifted.map((m, i) => ({ ...m, luauLine: lines[i].luauLine > index ? m.luauLine : lines[i].luauLine }));
60
+ parts.splice(index, 0, "-- cluaupp-map: " + JSON.stringify(adjusted));
61
+ return { contents: parts.join("\n"), lines: adjusted };
62
+ }
10
63
  function writeSourceMap(opts) {
64
+ const lines = opts.lines || [];
65
+ const source = opts.sourceClppRel.replace(/\\/g, "/");
11
66
  const map = {
12
67
  version: 1,
13
68
  file: node_path_1.default.basename(opts.outLuauPath),
14
69
  sourceRoot: opts.projectRoot.replace(/\\/g, "/"),
15
- sources: [opts.sourceClppRel.replace(/\\/g, "/")],
16
- mappings: "AAAA", // identity stub — refine when clpp emits VLQ maps
17
- cluaupp: {
18
- source: opts.sourceClppRel.replace(/\\/g, "/"),
19
- generated: node_path_1.default.relative(opts.projectRoot, opts.outLuauPath).replace(/\\/g, "/"),
20
- },
70
+ sources: [...new Set([source, ...lines.map(m => m.file)])],
71
+ cluaupp: { source, generated: node_path_1.default.relative(opts.projectRoot, opts.outLuauPath).replace(/\\/g, "/"), lines },
21
72
  };
22
73
  const mapPath = opts.outLuauPath + ".map.json";
23
- node_fs_1.default.writeFileSync(mapPath, JSON.stringify(map, null, "\t") + "\n", "utf8");
74
+ const contents = JSON.stringify(map, null, "\t") + "\n";
75
+ if (!node_fs_1.default.existsSync(mapPath) || node_fs_1.default.readFileSync(mapPath, "utf8") !== contents)
76
+ node_fs_1.default.writeFileSync(mapPath, contents, "utf8");
24
77
  return mapPath;
25
78
  }
@@ -80,6 +80,7 @@ function startStudioBridge(opts = {}) {
80
80
  type: data.type || "open",
81
81
  path: rel || undefined,
82
82
  line: data.line,
83
+ column: data.column,
83
84
  instancePath: data.instancePath,
84
85
  message: data.message,
85
86
  });
@@ -13,6 +13,7 @@ exports.watch = watch;
13
13
  const node_fs_1 = __importDefault(require("node:fs"));
14
14
  const node_path_1 = __importDefault(require("node:path"));
15
15
  const package_info_js_1 = require("../package-info.js");
16
+ const runner_js_1 = require("../clpp/runner.js");
16
17
  const postprocess_js_1 = require("../clpp/postprocess.js");
17
18
  const paths_js_1 = require("../clpp/paths.js");
18
19
  const index_js_1 = require("../flare/index.js");
@@ -33,6 +34,8 @@ const datamodel_js_1 = require("../target/datamodel.js");
33
34
  const gen_rojo_tree_js_1 = require("../target/gen-rojo-tree.js");
34
35
  const build_cache_js_1 = require("../target/build-cache.js");
35
36
  const modules_js_1 = require("../clpp/modules.js");
37
+ const project_js_1 = require("../compiler/project.js");
38
+ const diagnostics_js_1 = require("../compiler/diagnostics.js");
36
39
  function readProjectConfig(file) {
37
40
  const raw = JSON.parse(node_fs_1.default.readFileSync(file, "utf8"));
38
41
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
@@ -322,6 +325,10 @@ function compileOptions(root, config, file, rel) {
322
325
  outDir: config.outDir,
323
326
  };
324
327
  }
328
+ function needsLegacyHeaders(srcDir) {
329
+ const sources = (0, paths_js_1.collectSources)(srcDir);
330
+ return sources.some(file => /\.clpp$/i.test(file)) || !sources.some(file => /\.clp$/i.test(file));
331
+ }
325
332
  function compileSchemaJobs(root, config, srcDir) {
326
333
  const jobs = [];
327
334
  const errors = [];
@@ -336,7 +343,8 @@ function compileSchemaJobs(root, config, srcDir) {
336
343
  if (!(0, safe_paths_js_1.isInside)(node_path_1.default.resolve(root, config.rootDir), headerDest) && !(0, safe_paths_js_1.isInside)(srcDir, headerDest)) {
337
344
  throw new Error(`cluaupp schema: skip unsafe header ${emit.headerRel}`);
338
345
  }
339
- writeTextIfChanged(headerDest, emit.header);
346
+ if (needsLegacyHeaders(srcDir))
347
+ writeTextIfChanged(headerDest, emit.header);
340
348
  jobs.push({
341
349
  rel,
342
350
  files: [{ name: emit.luauRel, contents: emit.luau }],
@@ -364,7 +372,8 @@ function compileFlareJobs(root, config, srcDir) {
364
372
  if (!(0, safe_paths_js_1.isInside)(node_path_1.default.resolve(root, config.rootDir), headerDest) && !(0, safe_paths_js_1.isInside)(srcDir, headerDest)) {
365
373
  throw new Error(`cluaupp flare: skip unsafe header ${emit.headerRel}`);
366
374
  }
367
- writeTextIfChanged(headerDest, emit.header);
375
+ if (needsLegacyHeaders(srcDir))
376
+ writeTextIfChanged(headerDest, emit.header);
368
377
  jobs.push({
369
378
  rel,
370
379
  files: [{ name: emit.luauRel, contents: emit.luau }],
@@ -377,25 +386,61 @@ function compileFlareJobs(root, config, srcDir) {
377
386
  }
378
387
  return { jobs, errors };
379
388
  }
380
- function configFingerprint(config) {
389
+ function configFingerprint(root, config, rojoPath) {
390
+ const inputs = [node_path_1.default.resolve(root, rojoPath || "default.project.json"), node_path_1.default.join(root, "cluaupp.platform.json"), node_path_1.default.join(root, "cluaupp.profile.json"), node_path_1.default.join(package_info_js_1.projectRoot, "api", "normalized", "roblox-target.profile.json"), node_path_1.default.join(package_info_js_1.projectRoot, "api", "generated", "capability-profile.json"), node_path_1.default.join(package_info_js_1.projectRoot, "api", "roblox-api.lock.json")];
381
391
  return JSON.stringify({
382
392
  strict: config.strict === true,
383
393
  architecture: config.architecture === true,
384
394
  rootDir: config.rootDir,
385
395
  outDir: config.outDir,
386
396
  v: package_info_js_1.pkg.version,
397
+ rules: config.rules,
398
+ components: config.components,
399
+ inputs: inputs.map(file => [file, node_fs_1.default.existsSync(file) ? (0, build_cache_js_1.hashText)(node_fs_1.default.readFileSync(file, "utf8")) : null]),
387
400
  });
388
401
  }
402
+ function compileClpJobs(root, config, srcDir) {
403
+ const compiler = new project_js_1.ProjectCompiler({ root, srcDir, strict: config.strict === true, version: package_info_js_1.pkg.version });
404
+ const jobs = [];
405
+ const errors = [];
406
+ for (const result of compiler.compileAll()) {
407
+ if (result.luau === null) {
408
+ const diagnostics = result.diagnostics.filter((d) => d.severity === "error");
409
+ errors.push({
410
+ rel: result.rel,
411
+ prefixes: sourcePrefixes(result.rel),
412
+ message: diagnostics.map((d) => (0, diagnostics_js_1.formatDiagnostic)(d, result.source)).join("\n") || `${result.rel}: Luau emission failed`,
413
+ });
414
+ continue;
415
+ }
416
+ jobs.push({ rel: result.rel, files: [{ name: result.outRel, contents: result.luau, sourceMap: result.sourceMap, generatedSource: result.luau }], stale: [] });
417
+ }
418
+ return { jobs, errors };
419
+ }
389
420
  function compileProject(root, config, mapper, options = {}) {
390
421
  const srcDir = node_path_1.default.join(root, config.rootDir);
391
422
  const schema = compileSchemaJobs(root, config, srcDir);
392
423
  const files = (0, paths_js_1.collectSources)(srcDir);
393
- const jobs = [...schema.jobs];
394
- const errors = [...schema.errors];
424
+ const legacyFiles = files.filter((file) => !file.toLowerCase().endsWith(".clp"));
425
+ const clp = compileClpJobs(root, config, srcDir);
426
+ const jobs = [...schema.jobs, ...clp.jobs];
427
+ const errors = [...schema.errors, ...clp.errors];
395
428
  const incremental = options.incremental !== false;
396
- const cfgKey = configFingerprint(config);
429
+ let cfgKey = configFingerprint(root, config, options.rojo);
430
+ if (legacyFiles.length) {
431
+ try {
432
+ cfgKey += (0, runner_js_1.clppCacheIdentity)();
433
+ }
434
+ catch (err) {
435
+ for (const file of legacyFiles) {
436
+ const rel = posixRel(srcDir, file);
437
+ errors.push({ rel, prefixes: sourcePrefixes(rel), message: err.message });
438
+ }
439
+ return { files, jobs, errors };
440
+ }
441
+ }
397
442
  let cacheHits = 0;
398
- for (const file of files) {
443
+ for (const file of legacyFiles) {
399
444
  const source = node_fs_1.default.readFileSync(file, "utf8");
400
445
  const rel = posixRel(srcDir, file);
401
446
  const sourceHash = (0, modules_js_1.fingerprintSourceWithDeps)(source, file, cfgKey, srcDir, build_cache_js_1.hashText);
@@ -424,7 +469,7 @@ function compileProject(root, config, mapper, options = {}) {
424
469
  }
425
470
  }
426
471
  if (cacheHits > 0) {
427
- console.log(`cluaupp: incremental cache hit ${cacheHits}/${files.length}`);
472
+ console.log(`cluaupp: incremental cache hit ${cacheHits}/${legacyFiles.length}`);
428
473
  }
429
474
  return { files, jobs, errors };
430
475
  }
@@ -432,13 +477,27 @@ async function compileProjectAsync(root, config, mapper, options = {}) {
432
477
  const srcDir = node_path_1.default.join(root, config.rootDir);
433
478
  const schema = compileSchemaJobs(root, config, srcDir);
434
479
  const files = (0, paths_js_1.collectSources)(srcDir);
435
- const jobs = [...schema.jobs];
436
- const errors = [...schema.errors];
480
+ const legacyFiles = files.filter((file) => !file.toLowerCase().endsWith(".clp"));
481
+ const clp = compileClpJobs(root, config, srcDir);
482
+ const jobs = [...schema.jobs, ...clp.jobs];
483
+ const errors = [...schema.errors, ...clp.errors];
437
484
  const incremental = options.incremental !== false;
438
- const cfgKey = configFingerprint(config);
485
+ let cfgKey = configFingerprint(root, config, options.rojo);
486
+ if (legacyFiles.length) {
487
+ try {
488
+ cfgKey += (0, runner_js_1.clppCacheIdentity)();
489
+ }
490
+ catch (err) {
491
+ for (const file of legacyFiles) {
492
+ const rel = posixRel(srcDir, file);
493
+ errors.push({ rel, prefixes: sourcePrefixes(rel), message: err.message });
494
+ }
495
+ return { files, jobs, errors };
496
+ }
497
+ }
439
498
  const concurrency = options.jobs ?? (0, build_cache_js_1.defaultJobCount)();
440
499
  let cacheHits = 0;
441
- const compiled = await (0, build_cache_js_1.mapPool)(files, concurrency, async (file) => {
500
+ const compiled = await (0, build_cache_js_1.mapPool)(legacyFiles, concurrency, async (file) => {
442
501
  const source = node_fs_1.default.readFileSync(file, "utf8");
443
502
  const rel = posixRel(srcDir, file);
444
503
  const sourceHash = (0, modules_js_1.fingerprintSourceWithDeps)(source, file, cfgKey, srcDir, build_cache_js_1.hashText);
@@ -495,9 +554,9 @@ async function compileProjectAsync(root, config, mapper, options = {}) {
495
554
  }
496
555
  }
497
556
  if (cacheHits > 0) {
498
- console.log(`cluaupp: incremental cache hit ${cacheHits}/${files.length} (jobs=${concurrency})`);
557
+ console.log(`cluaupp: incremental cache hit ${cacheHits}/${legacyFiles.length} (jobs=${concurrency})`);
499
558
  }
500
- else if (files.length > 1 && concurrency > 1) {
559
+ else if (legacyFiles.length > 1 && concurrency > 1) {
501
560
  console.log(`cluaupp: parallel compile jobs=${concurrency}`);
502
561
  }
503
562
  return { files, jobs, errors };
@@ -536,7 +595,19 @@ function writeJobs(root, config, jobs, format) {
536
595
  if (/\.luau$/i.test(dest)) {
537
596
  const srcFile = node_path_1.default.join(root, config.rootDir, job.rel);
538
597
  const clppSource = node_fs_1.default.existsSync(srcFile) ? node_fs_1.default.readFileSync(srcFile, "utf8") : undefined;
539
- contents = (0, luau_stamp_js_1.stampLuauFromClpp)((0, postprocess_js_1.rewriteClppEmit)(contents), job.rel, clppSource);
598
+ contents = (0, luau_stamp_js_1.stampLuauFromClpp)(/\.clp$/i.test(job.rel) ? contents : (0, postprocess_js_1.rewriteClppEmit)(contents), posixRel(root, srcFile), clppSource);
599
+ }
600
+ let lines = (artifact.sourceMap || []).map(m => ({ ...m, file: node_path_1.default.isAbsolute(m.file) ? posixRel(root, m.file) : node_path_1.default.posix.join(config.rootDir.replace(/\\/g, "/"), m.file) }));
601
+ if (format && /\.luau$/i.test(dest)) {
602
+ writeTextIfChanged(dest, contents);
603
+ process_orchestrator_js_1.ProcessOrchestrator.formatWithStyLuaSync(dest);
604
+ contents = node_fs_1.default.readFileSync(dest, "utf8");
605
+ }
606
+ if (/\.luau$/i.test(dest)) {
607
+ lines = (0, source_map_js_1.remapSourceLines)(artifact.generatedSource || artifact.contents, contents, lines);
608
+ const embedded = (0, source_map_js_1.embedSourceMap)(contents, lines);
609
+ contents = embedded.contents;
610
+ lines = embedded.lines;
540
611
  }
541
612
  const changed = writeTextIfChanged(dest, contents);
542
613
  written.add(resolveKey(dest));
@@ -546,14 +617,12 @@ function writeJobs(root, config, jobs, format) {
546
617
  if (/\.luau$/i.test(dest)) {
547
618
  const mapPath = (0, source_map_js_1.writeSourceMap)({
548
619
  outLuauPath: dest,
549
- sourceClppRel: job.rel,
620
+ sourceClppRel: node_path_1.default.posix.join(config.rootDir.replace(/\\/g, "/"), job.rel),
550
621
  projectRoot: root,
622
+ lines,
551
623
  });
552
624
  written.add(resolveKey(mapPath));
553
625
  }
554
- if (format && /\.luau$/i.test(dest)) {
555
- process_orchestrator_js_1.ProcessOrchestrator.formatWithStyLuaSync(dest);
556
- }
557
626
  }
558
627
  for (const stale of job.stale || []) {
559
628
  const dest = outDest(root, config, stale);
@@ -568,7 +637,10 @@ function writeJobs(root, config, jobs, format) {
568
637
  }
569
638
  function ensureVendor(root) {
570
639
  copyRuntime(root);
571
- copyHeaders(root);
640
+ const config = loadConfig(root);
641
+ if ((0, paths_js_1.collectSources)(node_path_1.default.join(root, config.rootDir)).some(file => /\.clpp$/i.test(file))) {
642
+ copyHeaders(root);
643
+ }
572
644
  }
573
645
  function finishBuild(root, config, compiled, options) {
574
646
  const exitOnError = options.exitOnError !== false;
@@ -697,10 +769,15 @@ async function buildAsync(root, options = {}) {
697
769
  }
698
770
  async function init(dest) {
699
771
  const target = (0, safe_paths_js_1.assertInitDest)(dest);
700
- const template = node_path_1.default.join(__dirname, "..", "..", "templates", "game");
701
- const include = node_path_1.default.join(__dirname, "..", "..", "include");
772
+ if (node_fs_1.default.existsSync(node_path_1.default.join(target, "cluaupp.config.json")) || node_fs_1.default.existsSync(node_path_1.default.join(target, "src"))) {
773
+ throw new Error("cluaupp init: project already exists; choose a new folder. Existing .clpp/.clh sources require migration, not another init.");
774
+ }
775
+ const template = node_path_1.default.join(__dirname, "..", "..", "templates", "game-modern");
702
776
  copyDir(template, target);
703
- copyDir(include, node_path_1.default.join(target, "include"));
777
+ const packageFile = node_path_1.default.join(target, "package.json");
778
+ const projectPackage = JSON.parse(node_fs_1.default.readFileSync(packageFile, "utf8"));
779
+ projectPackage.devDependencies = { ...projectPackage.devDependencies, cluaupp: package_info_js_1.pkg.version };
780
+ node_fs_1.default.writeFileSync(packageFile, JSON.stringify(projectPackage, null, "\t") + "\n");
704
781
  const skills = node_path_1.default.join(__dirname, "..", "..", "skills");
705
782
  if (node_fs_1.default.existsSync(skills)) {
706
783
  copyDir(skills, node_path_1.default.join(target, ".cursor", "skills"));
@@ -709,17 +786,13 @@ async function init(dest) {
709
786
  const initConfig = loadConfig(target);
710
787
  syncRojoTree(target, initConfig);
711
788
  (0, intellisense_js_1.syncEditorSupport)(target);
712
- try {
713
- (0, intellisense_js_1.installEditorExtension)();
714
- }
715
- catch {
716
- // editor homes may be missing in CI
717
- }
789
+ // CL++ 0.10 owns its editor extension; project init only writes associations.
718
790
  console.log("Cluaupp ready in", target);
719
- console.log(" clpp install (CL++ highlighting + IntelliSense)");
791
+ console.log(" CL++ 0.10 editor extension provides syntax highlighting; Cluaupp compiles Roblox .clp files");
720
792
  console.log(" cluaupp intellisense (schema .flare .hive .mint .bloom .helm .shift .axiom)");
721
793
  console.log(" rokit install");
722
- console.log(" cluaupp build");
794
+ console.log(" npm install");
795
+ console.log(" npm run build");
723
796
  console.log(" rojo serve");
724
797
  }
725
798
  function pidAlive(pid) {
@@ -0,0 +1,5 @@
1
+ # Legacy CL++ headers
2
+
3
+ The .clh files in this directory use the legacy .clpp compiler syntax. They are preserved for the explicit legacy adapter and API projection tests. They are not modules for modern .clp programs, and modern cluaupp init and build do not copy them into a project.
4
+
5
+ Modern Roblox code uses link @clpp.roblox; and libraries use link @clpp.libs.Ward as Ward; (replace Ward with the runtime library). The API types come from the registry. Changing .clh to .clp does not migrate the syntax. Generated schema bindings still need modern type projections; legacy headers are not advertised as modern bindings.
@@ -0,0 +1 @@
1
+
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "cluaupp",
3
- "version": "1.5.2",
4
- "description": "Cluaupp toolchain — compile CL++ to Luau with Rojo and CluauppLibs.",
3
+ "version": "2.0.0-beta.2",
4
+ "description": "Roblox target for CL++: compile .clp to Luau with Rojo and CluauppLibs.",
5
5
  "author": "KartzDev",
6
6
  "license": "MIT",
7
7
  "bin": {
@@ -17,6 +17,8 @@
17
17
  "bin",
18
18
  "src",
19
19
  "generated",
20
+ "rust",
21
+ "native",
20
22
  "include",
21
23
  "templates",
22
24
  "examples",
@@ -27,6 +29,7 @@
27
29
  "data",
28
30
  "plugins",
29
31
  "scripts/postinstall-editor.js",
32
+ "scripts/postinstall-native.js",
30
33
  "README.md",
31
34
  "LICENSE",
32
35
  "CHANGELOG.md",
@@ -40,11 +43,14 @@
40
43
  "cluau": "node bin/cluaupp.js",
41
44
  "build": "tsc",
42
45
  "build:cli": "tsc",
46
+ "build:native": "cargo build --release --manifest-path rust/cluaupp/Cargo.toml",
43
47
  "dev:cli": "tsx src/cli.ts",
44
48
  "prepare": "tsc",
45
- "postinstall": "node scripts/postinstall-editor.js",
46
- "test": "node test/security.test.js && node test/understander.test.js && node test/out.test.js && node test/types.test.js && node test/modules.test.js && node test/modules-import.test.js && node test/gen-rojo-tree.test.js && node test/get-service.test.js && node test/clpp.test.js && node test/flare.test.js && node test/native.test.js && node test/postprocess.test.js && node test/api.test.js && node test/api-reuse.test.js && node test/context-safety.test.js && node test/datamodel.test.js && node test/platform-analyzers.test.js && node test/platform-extras.test.js && node test/generator.test.js && node test/studio/smoke.test.js",
49
+ "postinstall": "node scripts/postinstall-native.js && node scripts/postinstall-editor.js",
50
+ "test": "node test/cli-modern.test.js && node test/handshake.test.js && node test/source-map.test.js && node test/cache-invalidation.test.js && node test/package.test.js && node test/security.test.js && node test/understander.test.js && node test/modern-clp.test.js && node test/out.test.js && node test/types.test.js && node test/modules.test.js && node test/modules-import.test.js && node test/gen-rojo-tree.test.js && node test/get-service.test.js && node test/clpp.test.js && node test/flare.test.js && node test/native.test.js && node test/postprocess.test.js && node test/api.test.js && node test/api-reuse.test.js && node test/context-safety.test.js && node test/datamodel.test.js && node test/platform-analyzers.test.js && node test/platform-extras.test.js && node test/generator.test.js && node test/studio/smoke.test.js",
47
51
  "test:clpp-contract": "node test/clpp.test.js",
52
+ "test:modern": "node test/modern-clp.test.js",
53
+ "bench": "node scripts/bench-site.cjs",
48
54
  "generate-api": "node scripts/generate-api.js",
49
55
  "vendor-libs": "node scripts/vendor-libs.js",
50
56
  "build:libs": "node scripts/sync-packages.js",
@@ -52,10 +58,15 @@
52
58
  "site:dev": "npm run dev -w @cluaupp/docs",
53
59
  "dev": "node scripts/dev.js",
54
60
  "stop": "node scripts/stop.js",
55
- "prepublishOnly": "tsc && npm test"
61
+ "prepublishOnly": "npm run setup:runtime && tsc && node generated/cli.js api generate && node generated/cli.js api verify && npm test && npm run test:runtime && npm run test:release",
62
+ "api:surface": "tsx scripts/build-surface.ts",
63
+ "test:handshake": "node test/handshake.test.js",
64
+ "test:runtime": "node scripts/test-runtime.cjs",
65
+ "test:release": "node scripts/test-release.cjs",
66
+ "setup:runtime": "node scripts/setup-runtime.cjs"
56
67
  },
57
68
  "engines": {
58
- "node": ">=18"
69
+ "node": ">=22.12.0"
59
70
  },
60
71
  "keywords": [
61
72
  "luau",
@@ -91,6 +91,26 @@ local function findSourceComment(source: string): string?
91
91
  return nil
92
92
  end
93
93
 
94
+ -- Only exact compiler locations are used. Headers/helpers stay unmapped.
95
+ local function mappedLocation(source: string, generatedLine: number): (string?, number?, number?)
96
+ for line in string.gmatch(source, "[^\r\n]+") do
97
+ local raw = string.match(line, "^%-%-%s*cluaupp%-map:%s*(.+)$")
98
+ if raw then
99
+ local ok, entries = pcall(function() return HttpService:JSONDecode(raw) end)
100
+ if ok and type(entries) == "table" then
101
+ for _, entry in entries do
102
+ if entry.luauLine == generatedLine and type(entry.file) == "string" and type(entry.clppLine) == "number" then
103
+ return entry.file, entry.clppLine, entry.clppColumn
104
+ end
105
+ end
106
+ end
107
+ return nil, nil, nil
108
+ end
109
+ if not string.match(line, "^%-%-") then break end
110
+ end
111
+ return nil, nil, nil
112
+ end
113
+
94
114
  local function currentClppPath(): (string?, LuaSourceContainer?)
95
115
  local sel = Selection:Get()
96
116
  local scriptInst = sel[1]
@@ -186,15 +206,19 @@ ScriptContext.Error:Connect(function(message, stack, scriptInst)
186
206
  local ok, source = pcall(function()
187
207
  return (scriptInst :: LuaSourceContainer).Source
188
208
  end)
189
- local clpp = ok and typeof(source) == "string" and findSourceComment(source) or nil
190
- local line = tonumber(string.match(stack or "", ":(%d+)")) or 1
191
- local text = string.format("[error] %s\n luau: %s\n clpp: %s:%d", tostring(message), scriptInst:GetFullName(), tostring(clpp or "?"), line)
209
+ local line = tonumber(string.match(tostring(message), ":(%d+):")) or tonumber(string.match(stack or "", ":(%d+)"))
210
+ local clpp, sourceLine, sourceColumn
211
+ if ok and typeof(source) == "string" and line then
212
+ clpp, sourceLine, sourceColumn = mappedLocation(source, line)
213
+ end
214
+ local text = string.format("[error] %s\n luau: %s\n clpp: %s:%d", tostring(message), scriptInst:GetFullName(), tostring(clpp or "(unmapped)"), sourceLine or 0)
192
215
  appendLog(text, Color3.fromRGB(255, 120, 120))
193
216
  if clpp then
194
217
  postBridge({
195
218
  type = "error",
196
219
  path = clpp,
197
- line = line,
220
+ line = sourceLine,
221
+ column = sourceColumn,
198
222
  message = tostring(message),
199
223
  instancePath = scriptInst:GetFullName(),
200
224
  })