cluaupp 0.2.2 → 1.0.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.
Files changed (129) hide show
  1. package/CHANGELOG.md +14 -75
  2. package/README.md +42 -88
  3. package/docs/README.md +25 -21
  4. package/docs/architecture.md +13 -76
  5. package/docs/cli.md +12 -64
  6. package/docs/comparison.md +6 -24
  7. package/docs/config.md +6 -7
  8. package/docs/cpp-advanced.md +6 -3
  9. package/docs/cpp-types.md +2 -0
  10. package/docs/examples/combat.md +19 -227
  11. package/docs/examples/hud.md +13 -81
  12. package/docs/examples/index.md +9 -16
  13. package/docs/examples/leaderstats.md +44 -115
  14. package/docs/getting-started.md +20 -135
  15. package/docs/intellisense.md +5 -42
  16. package/docs/intro.md +16 -15
  17. package/docs/libraries/dataservice.md +4 -1
  18. package/docs/libraries/index.md +2 -1
  19. package/docs/libraries/more.md +3 -1
  20. package/docs/libraries/ui.md +76 -0
  21. package/docs/migration.md +38 -0
  22. package/docs/oop/file-tags.md +12 -36
  23. package/docs/oop/index.md +18 -11
  24. package/docs/roblox-api.md +4 -56
  25. package/docs/syntax.md +54 -179
  26. package/editors/vscode/extension.js +1 -1
  27. package/editors/vscode/package.json +4 -4
  28. package/examples/README.md +5 -7
  29. package/examples/game/.vscode/extensions.json +2 -2
  30. package/examples/game/.vscode/settings.json +6 -23
  31. package/examples/game/src/client/hud.client.clpp +54 -0
  32. package/examples/game/src/client/init.client.clpp +6 -0
  33. package/examples/game/src/server/combat.server.clpp +85 -0
  34. package/examples/game/src/server/leaderstats.server.clpp +28 -0
  35. package/examples/game/src/shared/PlayerData.clh +20 -0
  36. package/examples/game/src/shared/config.clp +6 -0
  37. package/examples/game/src/shared/features.clp +88 -0
  38. package/generated/api.js +15 -1
  39. package/generated/cli.js +40 -24
  40. package/generated/clpp/contract.js +5 -0
  41. package/generated/clpp/index.js +27 -0
  42. package/generated/clpp/paths.js +121 -0
  43. package/generated/clpp/postprocess.js +78 -0
  44. package/generated/clpp/runner.js +102 -0
  45. package/generated/compile.js +26 -138
  46. package/generated/emit.js +79 -3
  47. package/generated/intellisense.js +83 -207
  48. package/generated/lex.js +3 -0
  49. package/generated/libs.js +1 -1
  50. package/generated/lsp.js +1 -8
  51. package/generated/parse.js +119 -24
  52. package/generated/preprocess.js +8 -179
  53. package/generated/system-understander.js +1 -1
  54. package/generated/transpile.js +3 -50
  55. package/generated/utils/project.js +13 -28
  56. package/include/{cluaupp/datatypes.hpp → clpp/datatypes.clh} +5 -1
  57. package/include/{cluaupp/generated/instances.hpp → clpp/generated/instances.clh} +1 -1
  58. package/include/{cluaupp/libs/chrono.hpp → clpp/libs/chrono.clh} +9 -9
  59. package/include/{cluaupp/libs/cmdr.hpp → clpp/libs/cmdr.clh} +4 -4
  60. package/include/{cluaupp/libs/dataservice.hpp → clpp/libs/dataservice.clh} +8 -15
  61. package/include/{cluaupp/libs/display.hpp → clpp/libs/display.clh} +1 -2
  62. package/include/{cluaupp/libs/ezvisual.hpp → clpp/libs/ezvisual.clh} +1 -3
  63. package/include/{cluaupp/libs/formatnumber.hpp → clpp/libs/formatnumber.clh} +2 -3
  64. package/include/{cluaupp/libs/fusion.hpp → clpp/libs/fusion.clh} +5 -5
  65. package/include/{cluaupp/libs/iris.hpp → clpp/libs/iris.clh} +2 -2
  66. package/include/{cluaupp/libs/janitor.hpp → clpp/libs/janitor.clh} +1 -2
  67. package/include/{cluaupp/libs/module3d.hpp → clpp/libs/module3d.clh} +2 -4
  68. package/include/{cluaupp/libs/net.hpp → clpp/libs/net.clh} +2 -4
  69. package/include/{cluaupp/libs/promise.hpp → clpp/libs/promise.clh} +1 -4
  70. package/include/{cluaupp/libs/spring.hpp → clpp/libs/spring.clh} +1 -2
  71. package/include/{cluaupp/libs/statemachine.hpp → clpp/libs/statemachine.clh} +2 -3
  72. package/include/{cluaupp/libs/stickybillboard.hpp → clpp/libs/stickybillboard.clh} +1 -2
  73. package/include/{cluaupp/libs/topbarplus.hpp → clpp/libs/topbarplus.clh} +3 -4
  74. package/include/clpp/libs.clh +24 -0
  75. package/include/clpp/roblox.clh +31 -0
  76. package/package.json +20 -16
  77. package/src/api.ts +15 -1
  78. package/src/cli.ts +39 -25
  79. package/src/clpp/contract.ts +39 -0
  80. package/src/clpp/index.ts +18 -0
  81. package/src/clpp/paths.ts +117 -0
  82. package/src/clpp/postprocess.ts +93 -0
  83. package/src/clpp/runner.ts +102 -0
  84. package/src/compile.ts +26 -149
  85. package/src/intellisense.ts +82 -211
  86. package/src/libs.ts +1 -1
  87. package/src/lsp.ts +1 -5
  88. package/src/preprocess.ts +8 -180
  89. package/src/system-understander.ts +1 -1
  90. package/src/transpile.ts +9 -52
  91. package/src/utils/project.ts +12 -28
  92. package/templates/game/.vscode/extensions.json +2 -2
  93. package/templates/game/.vscode/settings.json +6 -23
  94. package/templates/game/src/client/init.client.clpp +6 -0
  95. package/templates/game/src/server/leaderstats.server.clpp +29 -0
  96. package/templates/game/src/shared/config.clp +6 -0
  97. package/examples/game/.clangd +0 -25
  98. package/examples/game/.vscode/c_cpp_properties.json +0 -27
  99. package/examples/game/compile_flags.txt +0 -6
  100. package/examples/game/src/client/hud.client.cpp +0 -12
  101. package/examples/game/src/client/init.client.cpp +0 -6
  102. package/examples/game/src/server/combat.server.cpp +0 -23
  103. package/examples/game/src/server/leaderstats.server.cpp +0 -29
  104. package/examples/game/src/shared/config.cpp +0 -5
  105. package/examples/game/src/shared/config.h +0 -5
  106. package/include/cluaupp/libs.hpp +0 -25
  107. package/include/cluaupp/roblox.hpp +0 -47
  108. package/src/architecture.ts +0 -500
  109. package/src/ast.ts +0 -37
  110. package/src/emit.ts +0 -531
  111. package/src/emitter/luau-codegen.ts +0 -187
  112. package/src/emitter/translators.ts +0 -168
  113. package/src/headers.ts +0 -233
  114. package/src/layout.ts +0 -114
  115. package/src/lex.ts +0 -155
  116. package/src/parse.ts +0 -630
  117. package/src/parser/collector.ts +0 -148
  118. package/src/parser/index.ts +0 -27
  119. package/src/tree-sitter-cpp.d.ts +0 -4
  120. package/src/understand.ts +0 -322
  121. package/templates/game/.vscode/c_cpp_properties.json +0 -27
  122. package/templates/game/src/client/init.client.cpp +0 -6
  123. package/templates/game/src/server/leaderstats.server.cpp +0 -29
  124. package/templates/game/src/shared/config.cpp +0 -5
  125. package/templates/game/src/shared/config.h +0 -5
  126. /package/include/{cluaupp/generated/enums.hpp → clpp/generated/enums.clh} +0 -0
  127. /package/include/{cluaupp/libs/math.hpp → clpp/libs/math.clh} +0 -0
  128. /package/include/{cluaupp/libs/twinkle.hpp → clpp/libs/twinkle.clh} +0 -0
  129. /package/include/{cluaupp/libs/vfx.hpp → clpp/libs/vfx.clh} +0 -0
package/generated/api.js CHANGED
@@ -43,9 +43,23 @@ function luauType(name) {
43
43
  if (!name) {
44
44
  return null;
45
45
  }
46
- const cleaned = String(name).replace(/\*+$/, "").replace(/^const\s+/, "").replace(/^Enum::/, "Enum.");
46
+ const cleaned = String(name)
47
+ .replace(/\*+$/, "")
48
+ .replace(/^const\s+/, "")
49
+ .replace(/^Enum::/, "Enum.")
50
+ .replace(/\s+/g, " ")
51
+ .trim();
47
52
  if (cleaned in LUAU_TYPES) {
48
53
  return LUAU_TYPES[cleaned];
49
54
  }
55
+ const generic = cleaned.match(/^(?:std::)?(?:vector|array|span|LuaArray)\s*<\s*([^>]+)\s*>$/);
56
+ if (generic) {
57
+ const inner = luauType(generic[1]) || generic[1];
58
+ return `{${inner}}`;
59
+ }
60
+ const optional = cleaned.match(/^(?:std::)?optional\s*<\s*([^>]+)\s*>$/);
61
+ if (optional) {
62
+ return `${luauType(optional[1]) || optional[1]}?`;
63
+ }
50
64
  return cleaned;
51
65
  }
package/generated/cli.js CHANGED
@@ -3,7 +3,7 @@ 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.program = exports.init = exports.watch = void 0;
6
+ exports.hasClpp = exports.program = exports.init = exports.watch = void 0;
7
7
  exports.build = build;
8
8
  exports.dispatch = dispatch;
9
9
  const commander_1 = require("commander");
@@ -11,7 +11,10 @@ const promises_1 = __importDefault(require("node:fs/promises"));
11
11
  const node_path_1 = __importDefault(require("node:path"));
12
12
  const package_info_js_1 = require("./package-info.js");
13
13
  const lsp_js_1 = require("./lsp.js");
14
- const preprocess_js_1 = require("./preprocess.js");
14
+ const paths_js_1 = require("./clpp/paths.js");
15
+ const contract_js_1 = require("./clpp/contract.js");
16
+ const runner_js_1 = require("./clpp/runner.js");
17
+ Object.defineProperty(exports, "hasClpp", { enumerable: true, get: function () { return runner_js_1.hasClpp; } });
15
18
  const intellisense_js_1 = require("./intellisense.js");
16
19
  const rojo_mapper_js_1 = require("./utils/rojo-mapper.js");
17
20
  const process_orchestrator_js_1 = require("./utils/process-orchestrator.js");
@@ -23,7 +26,7 @@ const program = new commander_1.Command();
23
26
  exports.program = program;
24
27
  program
25
28
  .name("cluaupp")
26
- .description("Definitive transpiler from C++ subset to structured Luau")
29
+ .description("Cluaupp toolchain — compile CL++ to Luau, Rojo, and CluauppLibs")
27
30
  .version(package_info_js_1.pkg.version, "-v, --version", "print version")
28
31
  .showHelpAfterError()
29
32
  .action(() => {
@@ -44,14 +47,14 @@ program
44
47
  });
45
48
  program
46
49
  .command("build")
47
- .description("Transpila um arquivo, diretório ou projeto C++ para Luau")
50
+ .description("Compile a CL++ file, directory, or project to Luau")
48
51
  .argument("[folder]", "project folder", ".")
49
- .option("-i, --input <path>", "Arquivo ou diretório C++ de entrada")
50
- .option("-o, --output <path>", "Arquivo ou diretório Luau de saída")
51
- .option("-r, --rojo <path>", "Caminho para o default.project.json do Rojo", "./default.project.json")
52
- .option("--strict", "Emitir --!strict")
53
- .option("--format", "Rodar StyLua no output")
54
- .option("--analyze", "Rodar luau-analyze no output")
52
+ .option("-i, --input <path>", "CL++ file or directory")
53
+ .option("-o, --output <path>", "Luau file or directory")
54
+ .option("-r, --rojo <path>", "Path to Rojo default.project.json", "./default.project.json")
55
+ .option("--strict", "Emit --!strict")
56
+ .option("--format", "Run StyLua on output")
57
+ .option("--analyze", "Run luau-analyze on output")
55
58
  .action(async (folder, options) => {
56
59
  if (options.input) {
57
60
  await buildInput(options);
@@ -73,8 +76,8 @@ program
73
76
  .command("watch")
74
77
  .description("rebuild on save")
75
78
  .argument("[folder]", "project folder", ".")
76
- .option("-r, --rojo <path>", "Caminho para o default.project.json do Rojo", "./default.project.json")
77
- .option("--format", "Rodar StyLua no output")
79
+ .option("-r, --rojo <path>", "Path to Rojo default.project.json", "./default.project.json")
80
+ .option("--format", "Run StyLua on output")
78
81
  .action((folder, options) => {
79
82
  try {
80
83
  (0, project_js_1.watch)(node_path_1.default.resolve(process.cwd(), folder), {
@@ -89,7 +92,7 @@ program
89
92
  });
90
93
  program
91
94
  .command("lsp")
92
- .description("Cluaupp subset diagnostics over stdio (JSON-RPC). C++ completion is clangd.")
95
+ .description("Language server stdio (JSON-RPC). Use `clpp install` for CL++ IntelliSense.")
93
96
  .argument("[folder]", "project folder", ".")
94
97
  .action((folder) => {
95
98
  (0, lsp_js_1.start)({ projectRoot: node_path_1.default.resolve(process.cwd(), folder) });
@@ -97,18 +100,32 @@ program
97
100
  program
98
101
  .command("intellisense")
99
102
  .alias("intelisense")
100
- .description("install LLVM clangd and write compile_commands.json")
103
+ .description("Point the editor at CL++ (`clpp install`)")
101
104
  .argument("[folder]", "project folder", ".")
102
105
  .action(async (folder) => {
103
106
  try {
104
107
  const root = node_path_1.default.resolve(process.cwd(), folder);
105
108
  (0, intellisense_js_1.syncEditorSupport)(root, (0, project_js_1.loadConfig)(root));
106
- const installed = await (0, intellisense_js_1.installEditorSupport)();
107
- console.log("cluaupp: compile_commands.json, .clangd, and .vscode updated in", root);
108
- console.log("cluaupp: C++ completion is clangd (include/cluaupp/roblox.hpp)");
109
- if (installed.clangd || installed.llvm) {
110
- console.log("cluaupp: reload Cursor (Ctrl+Shift+P → Developer: Reload Window)");
109
+ await (0, intellisense_js_1.installEditorSupport)();
110
+ console.log("cluaupp: editor associations written in", root);
111
+ console.log("cluaupp:", contract_js_1.CLPP_INSTALL_HINT);
112
+ }
113
+ catch (err) {
114
+ console.error(err instanceof Error ? err.message : err);
115
+ process.exit(1);
116
+ }
117
+ });
118
+ program
119
+ .command("language")
120
+ .description("CL++ manifest (`clpp api manifest`)")
121
+ .action(() => {
122
+ try {
123
+ (0, runner_js_1.resolveClppBinary)();
124
+ const version = (0, runner_js_1.clppVersion)();
125
+ if (version) {
126
+ console.log("clpp", version);
111
127
  }
128
+ console.log(JSON.stringify((0, runner_js_1.clppManifest)(), null, "\t"));
112
129
  }
113
130
  catch (err) {
114
131
  console.error(err instanceof Error ? err.message : err);
@@ -129,14 +146,13 @@ async function buildInput(options) {
129
146
  console.error(`[Erro] O caminho de entrada especificado não existe: ${inputPath}`);
130
147
  process.exit(1);
131
148
  }
132
- console.log("🏁 Inicializando Pipeline do Compilador Cluaupp V2...");
133
149
  const mapper = new rojo_mapper_js_1.RojoMapper(node_path_1.default.resolve(options.rojo), node_path_1.default.dirname(inputPath));
134
150
  await mapper.load();
135
151
  const inputs = (await promises_1.default.stat(inputPath)).isDirectory()
136
- ? (0, project_js_1.collectCpp)(inputPath)
152
+ ? (0, paths_js_1.collectSources)(inputPath)
137
153
  : [inputPath];
138
154
  if (inputs.length === 0) {
139
- console.error("no .cpp/.h/.hpp files in", inputPath);
155
+ console.error("no .clpp/.clp/.clh files in", inputPath);
140
156
  process.exit(1);
141
157
  }
142
158
  const outputIsFile = /\.luau$/i.test(outputPath);
@@ -154,7 +170,7 @@ async function buildInput(options) {
154
170
  srcDir: node_path_1.default.dirname(file),
155
171
  includeDirs: [node_path_1.default.dirname(file)],
156
172
  }, mapper);
157
- const dest = outputIsFile ? outputPath : node_path_1.default.join(outputPath, node_path_1.default.basename((0, preprocess_js_1.toLuauPath)(rel)));
173
+ const dest = outputIsFile ? outputPath : node_path_1.default.join(outputPath, node_path_1.default.basename((0, paths_js_1.toLuauPath)(rel)));
158
174
  await promises_1.default.mkdir(node_path_1.default.dirname(dest), { recursive: true });
159
175
  const luau = compiled.files[0]?.contents ?? "";
160
176
  await promises_1.default.writeFile(dest, luau, "utf8");
@@ -167,7 +183,7 @@ async function buildInput(options) {
167
183
  console.log("\nRelatório de Análise Estática do Luau:\n", analysisReport);
168
184
  }
169
185
  }
170
- console.log(`Transpilação concluída! ${file} → ${dest}`);
186
+ console.log(`Compiled ${file} → ${dest}`);
171
187
  }
172
188
  }
173
189
  function build(root, options = {}) {
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ /** Stable CL++ ↔ Cluaupp contract. Cluaupp invokes the `clpp` binary. */
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.CLPP_INSTALL_HINT = void 0;
5
+ exports.CLPP_INSTALL_HINT = "Install CL++ and put `clpp` on PATH: https://github.com/KartzRbx/CLPP (`cargo install --path .` then `clpp install`). Override with CLPP_PATH.";
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toLuauPath = exports.siblingHeader = exports.scriptKind = exports.isTaggedScript = exports.isSourceFile = exports.isImplFile = exports.isHeaderFile = exports.isEngineStub = exports.fileStem = exports.emitKind = exports.collectSources = exports.SOURCE_EXTS = exports.shouldSkipInit = exports.clppLuauToGame = exports.resolveClppBinary = exports.hasClpp = exports.clppVersion = exports.clppManifest = exports.compileViaClpp = exports.CLPP_INSTALL_HINT = void 0;
4
+ var contract_js_1 = require("./contract.js");
5
+ Object.defineProperty(exports, "CLPP_INSTALL_HINT", { enumerable: true, get: function () { return contract_js_1.CLPP_INSTALL_HINT; } });
6
+ var runner_js_1 = require("./runner.js");
7
+ Object.defineProperty(exports, "compileViaClpp", { enumerable: true, get: function () { return runner_js_1.compileViaClpp; } });
8
+ Object.defineProperty(exports, "clppManifest", { enumerable: true, get: function () { return runner_js_1.clppManifest; } });
9
+ Object.defineProperty(exports, "clppVersion", { enumerable: true, get: function () { return runner_js_1.clppVersion; } });
10
+ Object.defineProperty(exports, "hasClpp", { enumerable: true, get: function () { return runner_js_1.hasClpp; } });
11
+ Object.defineProperty(exports, "resolveClppBinary", { enumerable: true, get: function () { return runner_js_1.resolveClppBinary; } });
12
+ var postprocess_js_1 = require("./postprocess.js");
13
+ Object.defineProperty(exports, "clppLuauToGame", { enumerable: true, get: function () { return postprocess_js_1.clppLuauToGame; } });
14
+ Object.defineProperty(exports, "shouldSkipInit", { enumerable: true, get: function () { return postprocess_js_1.shouldSkipInit; } });
15
+ var paths_js_1 = require("./paths.js");
16
+ Object.defineProperty(exports, "SOURCE_EXTS", { enumerable: true, get: function () { return paths_js_1.SOURCE_EXTS; } });
17
+ Object.defineProperty(exports, "collectSources", { enumerable: true, get: function () { return paths_js_1.collectSources; } });
18
+ Object.defineProperty(exports, "emitKind", { enumerable: true, get: function () { return paths_js_1.emitKind; } });
19
+ Object.defineProperty(exports, "fileStem", { enumerable: true, get: function () { return paths_js_1.fileStem; } });
20
+ Object.defineProperty(exports, "isEngineStub", { enumerable: true, get: function () { return paths_js_1.isEngineStub; } });
21
+ Object.defineProperty(exports, "isHeaderFile", { enumerable: true, get: function () { return paths_js_1.isHeaderFile; } });
22
+ Object.defineProperty(exports, "isImplFile", { enumerable: true, get: function () { return paths_js_1.isImplFile; } });
23
+ Object.defineProperty(exports, "isSourceFile", { enumerable: true, get: function () { return paths_js_1.isSourceFile; } });
24
+ Object.defineProperty(exports, "isTaggedScript", { enumerable: true, get: function () { return paths_js_1.isTaggedScript; } });
25
+ Object.defineProperty(exports, "scriptKind", { enumerable: true, get: function () { return paths_js_1.scriptKind; } });
26
+ Object.defineProperty(exports, "siblingHeader", { enumerable: true, get: function () { return paths_js_1.siblingHeader; } });
27
+ Object.defineProperty(exports, "toLuauPath", { enumerable: true, get: function () { return paths_js_1.toLuauPath; } });
@@ -0,0 +1,121 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.SOURCE_EXTS = void 0;
7
+ exports.isSourceFile = isSourceFile;
8
+ exports.isEngineStub = isEngineStub;
9
+ exports.isHeaderFile = isHeaderFile;
10
+ exports.isImplFile = isImplFile;
11
+ exports.toLuauPath = toLuauPath;
12
+ exports.fileStem = fileStem;
13
+ exports.scriptKind = scriptKind;
14
+ exports.emitKind = emitKind;
15
+ exports.isTaggedScript = isTaggedScript;
16
+ exports.siblingHeader = siblingHeader;
17
+ exports.collectSources = collectSources;
18
+ const node_fs_1 = __importDefault(require("node:fs"));
19
+ const node_path_1 = __importDefault(require("node:path"));
20
+ exports.SOURCE_EXTS = [".clpp", ".clp", ".clh"];
21
+ function isSourceFile(fileName) {
22
+ return exports.SOURCE_EXTS.includes(node_path_1.default.extname(fileName).toLowerCase());
23
+ }
24
+ function isEngineStub(filePath) {
25
+ const normalized = filePath.replace(/\\/g, "/").toLowerCase();
26
+ return (normalized.includes("/include/cluaupp/") ||
27
+ normalized.includes("/include/clpp/") ||
28
+ normalized.includes("/include/cluau/") ||
29
+ normalized.includes("/stdlib/") ||
30
+ normalized.endsWith("/roblox.hpp") ||
31
+ normalized.endsWith("/roblox.h") ||
32
+ normalized.endsWith("/roblox.clh"));
33
+ }
34
+ function isHeaderFile(fileName) {
35
+ return node_path_1.default.extname(fileName).toLowerCase() === ".clh";
36
+ }
37
+ function isImplFile(fileName) {
38
+ const ext = node_path_1.default.extname(fileName).toLowerCase();
39
+ return ext === ".clp" || ext === ".clpp";
40
+ }
41
+ function toLuauPath(filePath) {
42
+ return String(filePath).replace(/\.(clpp|clp|clh)$/i, ".luau");
43
+ }
44
+ function fileStem(fileName) {
45
+ let name = node_path_1.default.basename(fileName);
46
+ const lower = name.toLowerCase();
47
+ for (const ext of [".clpp", ".clp", ".clh"]) {
48
+ if (lower.endsWith(ext)) {
49
+ name = name.slice(0, -ext.length);
50
+ break;
51
+ }
52
+ }
53
+ const tagged = name.toLowerCase();
54
+ for (const tag of [".legacy.server", ".legacy.client", ".legacy", ".server", ".client", ".plugin"]) {
55
+ if (tagged.endsWith(tag)) {
56
+ name = name.slice(0, -tag.length);
57
+ break;
58
+ }
59
+ }
60
+ return name;
61
+ }
62
+ function scriptKind(fileName) {
63
+ const name = node_path_1.default.basename(fileName).toLowerCase();
64
+ if (name.endsWith(".clh")) {
65
+ return "header";
66
+ }
67
+ if (name.includes(".legacy.")) {
68
+ return "legacy";
69
+ }
70
+ if (name.includes(".server.")) {
71
+ return "server";
72
+ }
73
+ if (name.includes(".client.")) {
74
+ return "client";
75
+ }
76
+ if (name.includes(".plugin.")) {
77
+ return "plugin";
78
+ }
79
+ return "module";
80
+ }
81
+ function emitKind(fileName) {
82
+ const kind = scriptKind(fileName);
83
+ if (kind === "legacy") {
84
+ return "legacy";
85
+ }
86
+ if (kind === "module" || kind === "header") {
87
+ return "module";
88
+ }
89
+ return "flat";
90
+ }
91
+ function isTaggedScript(fileName) {
92
+ const kind = scriptKind(fileName);
93
+ return kind === "server" || kind === "client" || kind === "plugin" || kind === "legacy";
94
+ }
95
+ function siblingHeader(implPath) {
96
+ if (!implPath || isHeaderFile(implPath)) {
97
+ return null;
98
+ }
99
+ const dir = node_path_1.default.dirname(implPath);
100
+ const stem = fileStem(implPath);
101
+ const candidate = node_path_1.default.join(dir, `${stem}.clh`);
102
+ if (node_fs_1.default.existsSync(candidate)) {
103
+ return candidate;
104
+ }
105
+ return null;
106
+ }
107
+ function collectSources(dir, files = []) {
108
+ if (!node_fs_1.default.existsSync(dir)) {
109
+ return files;
110
+ }
111
+ for (const entry of node_fs_1.default.readdirSync(dir, { withFileTypes: true })) {
112
+ const full = node_path_1.default.join(dir, entry.name);
113
+ if (entry.isDirectory()) {
114
+ collectSources(full, files);
115
+ }
116
+ else if (isSourceFile(entry.name)) {
117
+ files.push(full);
118
+ }
119
+ }
120
+ return files;
121
+ }
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.shouldSkipInit = shouldSkipInit;
4
+ exports.clppLuauToGame = clppLuauToGame;
5
+ const libs_js_1 = require("../libs.js");
6
+ const paths_js_1 = require("./paths.js");
7
+ function insertAfterHeader(luau, block) {
8
+ const match = String(luau).match(/^(?:--[^\n]*\n)+/);
9
+ if (!match) {
10
+ return `${block}\n${luau}`;
11
+ }
12
+ const insertAt = match[0].length;
13
+ const rest = luau.slice(insertAt).replace(/^\n*/, "\n");
14
+ return `${luau.slice(0, insertAt)}\n${block}\n${rest}`;
15
+ }
16
+ function rewriteClppLibs(luau) {
17
+ return String(luau).replace(/require\(\s*ClppLibs\.([A-Za-z0-9_]+)\s*\)/g, (_all, name) => {
18
+ return (0, libs_js_1.requireCluauppLib)(name);
19
+ });
20
+ }
21
+ function ensureReplicatedStorage(luau) {
22
+ if (!luau.includes("CluauppLibs")) {
23
+ return luau;
24
+ }
25
+ if (/GetService\(\s*"ReplicatedStorage"\s*\)/.test(luau)) {
26
+ return luau;
27
+ }
28
+ return insertAfterHeader(luau, 'const ReplicatedStorage = game:GetService("ReplicatedStorage")');
29
+ }
30
+ function stripInitCall(luau) {
31
+ return String(luau).replace(/\ninit\(\)\s*\n?$/m, "\n");
32
+ }
33
+ function ensureStrict(luau, strict) {
34
+ if (!strict || /^\s*--!strict\b/m.test(luau)) {
35
+ return luau;
36
+ }
37
+ if (luau.startsWith("--")) {
38
+ return `--!strict\n${luau}`;
39
+ }
40
+ return `--!strict\n${luau}`;
41
+ }
42
+ function librarySpecs(names) {
43
+ const specs = [];
44
+ const seen = new Set();
45
+ for (const name of names || []) {
46
+ const spec = libs_js_1.MODULES[name];
47
+ if (!spec || seen.has(spec.bind)) {
48
+ continue;
49
+ }
50
+ seen.add(spec.bind);
51
+ specs.push(spec);
52
+ }
53
+ return specs;
54
+ }
55
+ function missingLibraries(luau, names) {
56
+ return librarySpecs(names).filter((spec) => {
57
+ return !luau.includes(`CluauppLibs.${spec.file}`) && !new RegExp(`\\bconst ${spec.bind}\\b`).test(luau);
58
+ });
59
+ }
60
+ function shouldSkipInit(fileName, hasSiblingHeader) {
61
+ if (!hasSiblingHeader) {
62
+ return false;
63
+ }
64
+ return !(0, paths_js_1.isTaggedScript)(fileName);
65
+ }
66
+ function clppLuauToGame(artifact, options) {
67
+ let luau = rewriteClppLibs(artifact.luau || "");
68
+ luau = ensureReplicatedStorage(luau);
69
+ if (options.skipInit) {
70
+ luau = stripInitCall(luau);
71
+ }
72
+ luau = ensureStrict(luau, options.strict);
73
+ const missing = missingLibraries(luau, artifact.libraries);
74
+ if (missing.length > 0) {
75
+ luau = (0, libs_js_1.insertPreamble)(luau, [], missing, options.outName);
76
+ }
77
+ return luau;
78
+ }
@@ -0,0 +1,102 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.hasClpp = hasClpp;
4
+ exports.resolveClppBinary = resolveClppBinary;
5
+ exports.compileViaClpp = compileViaClpp;
6
+ exports.clppManifest = clppManifest;
7
+ exports.clppVersion = clppVersion;
8
+ const node_child_process_1 = require("node:child_process");
9
+ const contract_js_1 = require("./contract.js");
10
+ const MISSING_CLPP = `cluaupp: clpp not found. ${contract_js_1.CLPP_INSTALL_HINT}`;
11
+ function whichClpp() {
12
+ if (process.env.CLPP_PATH) {
13
+ return process.env.CLPP_PATH;
14
+ }
15
+ const finder = process.platform === "win32" ? "where" : "which";
16
+ const found = (0, node_child_process_1.spawnSync)(finder, ["clpp"], { encoding: "utf8", windowsHide: true });
17
+ if (found.status !== 0) {
18
+ return null;
19
+ }
20
+ const line = String(found.stdout || "")
21
+ .split(/\r?\n/)
22
+ .map((item) => item.trim())
23
+ .find((item) => item.length > 0);
24
+ return line || "clpp";
25
+ }
26
+ function hasClpp() {
27
+ return Boolean(whichClpp());
28
+ }
29
+ function resolveClppBinary() {
30
+ const bin = whichClpp();
31
+ if (!bin) {
32
+ throw new Error(MISSING_CLPP);
33
+ }
34
+ return bin;
35
+ }
36
+ function parseArtifact(stdout, fileName, fallbackError) {
37
+ const text = String(stdout || "").trim();
38
+ try {
39
+ const parsed = JSON.parse(text);
40
+ if (parsed && typeof parsed === "object") {
41
+ return parsed;
42
+ }
43
+ }
44
+ catch {
45
+ // fall through
46
+ }
47
+ return {
48
+ ok: false,
49
+ luau: "",
50
+ fileName,
51
+ outputHint: "",
52
+ scriptKind: null,
53
+ isScript: false,
54
+ isHeader: false,
55
+ rojoClass: "ModuleScript",
56
+ libraries: [],
57
+ error: fallbackError || "clpp returned invalid JSON",
58
+ };
59
+ }
60
+ function compileViaClpp(request) {
61
+ const bin = resolveClppBinary();
62
+ const payload = JSON.stringify({
63
+ source: request.source,
64
+ fileName: request.fileName,
65
+ strict: request.strict,
66
+ });
67
+ const result = (0, node_child_process_1.spawnSync)(bin, ["api", "compile"], {
68
+ input: payload,
69
+ encoding: "utf8",
70
+ cwd: request.cwd,
71
+ maxBuffer: 16 * 1024 * 1024,
72
+ windowsHide: true,
73
+ });
74
+ const stderr = String(result.stderr || "").trim();
75
+ const artifact = parseArtifact(result.stdout, request.fileName, stderr || result.error?.message || "clpp api compile failed");
76
+ if (!artifact.ok) {
77
+ throw new Error(artifact.error || stderr || `clpp failed (${result.status})`);
78
+ }
79
+ return artifact;
80
+ }
81
+ function clppManifest() {
82
+ const bin = resolveClppBinary();
83
+ const result = (0, node_child_process_1.spawnSync)(bin, ["api", "manifest"], {
84
+ encoding: "utf8",
85
+ windowsHide: true,
86
+ });
87
+ if (result.status !== 0) {
88
+ throw new Error(String(result.stderr || result.error?.message || "clpp api manifest failed"));
89
+ }
90
+ return JSON.parse(result.stdout);
91
+ }
92
+ function clppVersion() {
93
+ const bin = whichClpp();
94
+ if (!bin) {
95
+ return null;
96
+ }
97
+ const result = (0, node_child_process_1.spawnSync)(bin, ["--version"], { encoding: "utf8", windowsHide: true });
98
+ if (result.status !== 0) {
99
+ return null;
100
+ }
101
+ return String(result.stdout || "").trim() || null;
102
+ }