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
@@ -0,0 +1,117 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+
4
+ export const SOURCE_EXTS = [".clpp", ".clp", ".clh"];
5
+
6
+ export function isSourceFile(fileName: string): boolean {
7
+ return SOURCE_EXTS.includes(path.extname(fileName).toLowerCase());
8
+ }
9
+
10
+ export function isEngineStub(filePath: string): boolean {
11
+ const normalized = filePath.replace(/\\/g, "/").toLowerCase();
12
+ return (
13
+ normalized.includes("/include/cluaupp/") ||
14
+ normalized.includes("/include/clpp/") ||
15
+ normalized.includes("/include/cluau/") ||
16
+ normalized.includes("/stdlib/") ||
17
+ normalized.endsWith("/roblox.hpp") ||
18
+ normalized.endsWith("/roblox.h") ||
19
+ normalized.endsWith("/roblox.clh")
20
+ );
21
+ }
22
+
23
+ export function isHeaderFile(fileName: string): boolean {
24
+ return path.extname(fileName).toLowerCase() === ".clh";
25
+ }
26
+
27
+ export function isImplFile(fileName: string): boolean {
28
+ const ext = path.extname(fileName).toLowerCase();
29
+ return ext === ".clp" || ext === ".clpp";
30
+ }
31
+
32
+ export function toLuauPath(filePath: string): string {
33
+ return String(filePath).replace(/\.(clpp|clp|clh)$/i, ".luau");
34
+ }
35
+
36
+ export function fileStem(fileName: string): string {
37
+ let name = path.basename(fileName);
38
+ const lower = name.toLowerCase();
39
+ for (const ext of [".clpp", ".clp", ".clh"]) {
40
+ if (lower.endsWith(ext)) {
41
+ name = name.slice(0, -ext.length);
42
+ break;
43
+ }
44
+ }
45
+ const tagged = name.toLowerCase();
46
+ for (const tag of [".legacy.server", ".legacy.client", ".legacy", ".server", ".client", ".plugin"]) {
47
+ if (tagged.endsWith(tag)) {
48
+ name = name.slice(0, -tag.length);
49
+ break;
50
+ }
51
+ }
52
+ return name;
53
+ }
54
+
55
+ export function scriptKind(fileName: string): "server" | "client" | "plugin" | "legacy" | "module" | "header" {
56
+ const name = path.basename(fileName).toLowerCase();
57
+ if (name.endsWith(".clh")) {
58
+ return "header";
59
+ }
60
+ if (name.includes(".legacy.")) {
61
+ return "legacy";
62
+ }
63
+ if (name.includes(".server.")) {
64
+ return "server";
65
+ }
66
+ if (name.includes(".client.")) {
67
+ return "client";
68
+ }
69
+ if (name.includes(".plugin.")) {
70
+ return "plugin";
71
+ }
72
+ return "module";
73
+ }
74
+
75
+ export function emitKind(fileName: string): "flat" | "module" | "legacy" {
76
+ const kind = scriptKind(fileName);
77
+ if (kind === "legacy") {
78
+ return "legacy";
79
+ }
80
+ if (kind === "module" || kind === "header") {
81
+ return "module";
82
+ }
83
+ return "flat";
84
+ }
85
+
86
+ export function isTaggedScript(fileName: string): boolean {
87
+ const kind = scriptKind(fileName);
88
+ return kind === "server" || kind === "client" || kind === "plugin" || kind === "legacy";
89
+ }
90
+
91
+ export function siblingHeader(implPath: string | null | undefined): string | null {
92
+ if (!implPath || isHeaderFile(implPath)) {
93
+ return null;
94
+ }
95
+ const dir = path.dirname(implPath);
96
+ const stem = fileStem(implPath);
97
+ const candidate = path.join(dir, `${stem}.clh`);
98
+ if (fs.existsSync(candidate)) {
99
+ return candidate;
100
+ }
101
+ return null;
102
+ }
103
+
104
+ export function collectSources(dir: string, files: string[] = []): string[] {
105
+ if (!fs.existsSync(dir)) {
106
+ return files;
107
+ }
108
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
109
+ const full = path.join(dir, entry.name);
110
+ if (entry.isDirectory()) {
111
+ collectSources(full, files);
112
+ } else if (isSourceFile(entry.name)) {
113
+ files.push(full);
114
+ }
115
+ }
116
+ return files;
117
+ }
@@ -0,0 +1,93 @@
1
+ import { insertPreamble, MODULES, requireCluauppLib } from "../libs.js";
2
+ import type { CompileArtifact } from "./contract.js";
3
+ import { isTaggedScript } from "./paths.js";
4
+
5
+ export type PostprocessOptions = {
6
+ relativeName: string;
7
+ outName: string;
8
+ srcDir?: string;
9
+ rootDir?: string;
10
+ strict?: boolean;
11
+ skipInit?: boolean;
12
+ };
13
+
14
+ function insertAfterHeader(luau: string, block: string): string {
15
+ const match = String(luau).match(/^(?:--[^\n]*\n)+/);
16
+ if (!match) {
17
+ return `${block}\n${luau}`;
18
+ }
19
+ const insertAt = match[0].length;
20
+ const rest = luau.slice(insertAt).replace(/^\n*/, "\n");
21
+ return `${luau.slice(0, insertAt)}\n${block}\n${rest}`;
22
+ }
23
+
24
+ function rewriteClppLibs(luau: string): string {
25
+ return String(luau).replace(/require\(\s*ClppLibs\.([A-Za-z0-9_]+)\s*\)/g, (_all, name: string) => {
26
+ return requireCluauppLib(name);
27
+ });
28
+ }
29
+
30
+ function ensureReplicatedStorage(luau: string): string {
31
+ if (!luau.includes("CluauppLibs")) {
32
+ return luau;
33
+ }
34
+ if (/GetService\(\s*"ReplicatedStorage"\s*\)/.test(luau)) {
35
+ return luau;
36
+ }
37
+ return insertAfterHeader(luau, 'const ReplicatedStorage = game:GetService("ReplicatedStorage")');
38
+ }
39
+
40
+ function stripInitCall(luau: string): string {
41
+ return String(luau).replace(/\ninit\(\)\s*\n?$/m, "\n");
42
+ }
43
+
44
+ function ensureStrict(luau: string, strict?: boolean): string {
45
+ if (!strict || /^\s*--!strict\b/m.test(luau)) {
46
+ return luau;
47
+ }
48
+ if (luau.startsWith("--")) {
49
+ return `--!strict\n${luau}`;
50
+ }
51
+ return `--!strict\n${luau}`;
52
+ }
53
+
54
+ function librarySpecs(names: string[] | undefined) {
55
+ const specs: Array<{ file: string; bind: string }> = [];
56
+ const seen = new Set<string>();
57
+ for (const name of names || []) {
58
+ const spec = (MODULES as Record<string, { file: string; bind: string }>)[name];
59
+ if (!spec || seen.has(spec.bind)) {
60
+ continue;
61
+ }
62
+ seen.add(spec.bind);
63
+ specs.push(spec);
64
+ }
65
+ return specs;
66
+ }
67
+
68
+ function missingLibraries(luau: string, names: string[] | undefined) {
69
+ return librarySpecs(names).filter((spec) => {
70
+ return !luau.includes(`CluauppLibs.${spec.file}`) && !new RegExp(`\\bconst ${spec.bind}\\b`).test(luau);
71
+ });
72
+ }
73
+
74
+ export function shouldSkipInit(fileName: string, hasSiblingHeader: boolean): boolean {
75
+ if (!hasSiblingHeader) {
76
+ return false;
77
+ }
78
+ return !isTaggedScript(fileName);
79
+ }
80
+
81
+ export function clppLuauToGame(artifact: CompileArtifact, options: PostprocessOptions): string {
82
+ let luau = rewriteClppLibs(artifact.luau || "");
83
+ luau = ensureReplicatedStorage(luau);
84
+ if (options.skipInit) {
85
+ luau = stripInitCall(luau);
86
+ }
87
+ luau = ensureStrict(luau, options.strict);
88
+ const missing = missingLibraries(luau, artifact.libraries);
89
+ if (missing.length > 0) {
90
+ luau = insertPreamble(luau, [], missing, options.outName);
91
+ }
92
+ return luau;
93
+ }
@@ -0,0 +1,102 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { CLPP_INSTALL_HINT, type CompileArtifact, type CompileRequest, type LanguageManifest } from "./contract.js";
3
+
4
+ const MISSING_CLPP = `cluaupp: clpp not found. ${CLPP_INSTALL_HINT}`;
5
+
6
+ function whichClpp(): string | null {
7
+ if (process.env.CLPP_PATH) {
8
+ return process.env.CLPP_PATH;
9
+ }
10
+ const finder = process.platform === "win32" ? "where" : "which";
11
+ const found = spawnSync(finder, ["clpp"], { encoding: "utf8", windowsHide: true });
12
+ if (found.status !== 0) {
13
+ return null;
14
+ }
15
+ const line = String(found.stdout || "")
16
+ .split(/\r?\n/)
17
+ .map((item) => item.trim())
18
+ .find((item) => item.length > 0);
19
+ return line || "clpp";
20
+ }
21
+
22
+ export function hasClpp(): boolean {
23
+ return Boolean(whichClpp());
24
+ }
25
+
26
+ export function resolveClppBinary(): string {
27
+ const bin = whichClpp();
28
+ if (!bin) {
29
+ throw new Error(MISSING_CLPP);
30
+ }
31
+ return bin;
32
+ }
33
+
34
+ function parseArtifact(stdout: string, fileName: string, fallbackError: string): CompileArtifact {
35
+ const text = String(stdout || "").trim();
36
+ try {
37
+ const parsed = JSON.parse(text) as CompileArtifact;
38
+ if (parsed && typeof parsed === "object") {
39
+ return parsed;
40
+ }
41
+ } catch {
42
+ // fall through
43
+ }
44
+ return {
45
+ ok: false,
46
+ luau: "",
47
+ fileName,
48
+ outputHint: "",
49
+ scriptKind: null,
50
+ isScript: false,
51
+ isHeader: false,
52
+ rojoClass: "ModuleScript",
53
+ libraries: [],
54
+ error: fallbackError || "clpp returned invalid JSON",
55
+ };
56
+ }
57
+
58
+ export function compileViaClpp(request: CompileRequest): CompileArtifact {
59
+ const bin = resolveClppBinary();
60
+ const payload = JSON.stringify({
61
+ source: request.source,
62
+ fileName: request.fileName,
63
+ strict: request.strict,
64
+ });
65
+ const result = spawnSync(bin, ["api", "compile"], {
66
+ input: payload,
67
+ encoding: "utf8",
68
+ cwd: request.cwd,
69
+ maxBuffer: 16 * 1024 * 1024,
70
+ windowsHide: true,
71
+ });
72
+ const stderr = String(result.stderr || "").trim();
73
+ const artifact = parseArtifact(result.stdout, request.fileName, stderr || result.error?.message || "clpp api compile failed");
74
+ if (!artifact.ok) {
75
+ throw new Error(artifact.error || stderr || `clpp failed (${result.status})`);
76
+ }
77
+ return artifact;
78
+ }
79
+
80
+ export function clppManifest(): LanguageManifest {
81
+ const bin = resolveClppBinary();
82
+ const result = spawnSync(bin, ["api", "manifest"], {
83
+ encoding: "utf8",
84
+ windowsHide: true,
85
+ });
86
+ if (result.status !== 0) {
87
+ throw new Error(String(result.stderr || result.error?.message || "clpp api manifest failed"));
88
+ }
89
+ return JSON.parse(result.stdout) as LanguageManifest;
90
+ }
91
+
92
+ export function clppVersion(): string | null {
93
+ const bin = whichClpp();
94
+ if (!bin) {
95
+ return null;
96
+ }
97
+ const result = spawnSync(bin, ["--version"], { encoding: "utf8", windowsHide: true });
98
+ if (result.status !== 0) {
99
+ return null;
100
+ }
101
+ return String(result.stdout || "").trim() || null;
102
+ }
package/src/compile.ts CHANGED
@@ -1,160 +1,37 @@
1
- import fs from "node:fs";
2
1
  import path from "node:path";
3
- import { parse } from "./parse.js";
4
- import { emit } from "./emit.js";
5
- import { preprocess, isHeaderFile, toLuauPath, siblingImplementation, siblingHeader, implOutName } from "./preprocess.js";
6
- import { collectLibraries, insertPreamble } from "./libs.js";
7
- import { planOutput } from "./architecture.js";
8
- import { emitHeaderModule, primaryHeaderName } from "./headers.js";
9
- import type { AstNode, AstProgram, ModuleInclude, PreprocessOptions } from "./ast.js";
10
2
  import type { CompileOptions, CompileServiceResult } from "./types.js";
11
-
12
- function shouldAttachRequires(name: string, contents: string): boolean {
13
- const file = String(name || "").replace(/\\/g, "/");
14
- if (/\.json$/i.test(file) || /\.meta\.json$/i.test(file)) {
15
- return false;
16
- }
17
- const trimmed = String(contents || "").trim();
18
- if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
19
- return false;
20
- }
21
- return true;
22
- }
23
-
24
- function attachRequires(contents: string, source: string, ast: AstProgram, options: PreprocessOptions, outName: string): string {
25
- if (!shouldAttachRequires(outName, contents)) {
26
- return contents;
27
- }
28
- const libraries = String(contents).includes("CluauppLibs") ? [] : collectLibraries(source, ast);
29
- return insertPreamble(contents, options.moduleIncludes || [], libraries, outName);
30
- }
31
-
32
- function posixRel(from: string, file: string): string {
33
- return path.relative(from, file).replace(/\\/g, "/");
34
- }
35
-
36
- function declKey(decl: AstNode): string {
37
- return `${decl.owner || ""}::${decl.name}`;
38
- }
39
-
40
- function mergeSiblingImpl(ast: AstProgram, implPath: string, options: PreprocessOptions): void {
41
- const implOptions: PreprocessOptions = {
42
- ...options,
43
- moduleIncludes: [],
44
- seen: new Set(),
45
- pragmaResolved: false,
46
- };
47
- const prepared = preprocess(fs.readFileSync(implPath, "utf8"), implPath, implOptions);
48
- const implAst = parse(prepared, path.basename(implPath)) as AstProgram;
49
- if (!Array.isArray(ast.body)) {
50
- ast.body = [];
51
- }
52
- const existing = new Set(
53
- (ast.body || [])
54
- .filter((decl) => decl && (decl.type === "function" || decl.type === "proto") && decl.name)
55
- .map(declKey),
56
- );
57
- for (const decl of implAst.body || []) {
58
- if ((decl.type !== "function" && decl.type !== "proto") || !decl.name) {
59
- continue;
60
- }
61
- const key = declKey(decl);
62
- if (existing.has(key)) {
63
- continue;
64
- }
65
- existing.add(key);
66
- ast.body.push({
67
- type: "proto",
68
- name: decl.name,
69
- owner: decl.owner,
70
- returnType: decl.returnType,
71
- params: decl.params,
72
- });
73
- }
74
- }
75
-
76
- function wireHeaderImpl(ast: AstProgram, fileName: string, options: PreprocessOptions): void {
77
- const headerPath = options.filePath || null;
78
- const impl = headerPath ? siblingImplementation(headerPath) : null;
79
- if (!impl) {
80
- return;
81
- }
82
- mergeSiblingImpl(ast, impl, options);
83
- const typeName = primaryHeaderName(ast, options.relativeName || fileName);
84
- const implRel = options.srcDir ? posixRel(options.srcDir, impl) : path.basename(impl).replace(/\\/g, "/");
85
- const implOut = implOutName(implRel);
86
- options.headerImplName = `${typeName}Module`;
87
- options.moduleIncludes = options.moduleIncludes || [];
88
- if (!options.moduleIncludes.some((spec: ModuleInclude) => spec.name === options.headerImplName)) {
89
- options.moduleIncludes.push({
90
- name: options.headerImplName,
91
- outRel: implOut,
92
- exports: { consts: [], structs: [] },
93
- });
94
- }
95
- }
96
-
97
- function compileHeader(source: string, fileName: string, options: PreprocessOptions, ast: AstProgram): CompileServiceResult {
98
- wireHeaderImpl(ast, fileName, options);
99
- const rel = (options.relativeName || fileName || "header.h").replace(/\\/g, "/");
100
- const outName = toLuauPath(rel);
101
- const luau = attachRequires(emitHeaderModule(ast, options), source, ast, options, outName);
102
- return {
103
- kind: "module",
104
- plan: { kind: "module", strict: options.strict === true },
105
- files: [{ name: outName, contents: luau }],
106
- stale: [],
107
- };
108
- }
3
+ import { compileViaClpp } from "./clpp/runner.js";
4
+ import { clppLuauToGame, shouldSkipInit } from "./clpp/postprocess.js";
5
+ import { emitKind, siblingHeader, toLuauPath } from "./clpp/paths.js";
109
6
 
110
7
  export function compileSource(source: string, fileName?: string, options: CompileOptions = {}): string {
111
- const opts = options as PreprocessOptions;
112
- opts.moduleIncludes = opts.moduleIncludes || [];
113
- const prepared = preprocess(source, opts.filePath || null, opts);
114
- const ast = parse(prepared, fileName || "input.cpp");
115
- if (isHeaderFile(fileName || "") || isHeaderFile(opts.filePath || "")) {
116
- return compileHeader(source, fileName || "header.h", opts, ast).files[0].contents;
117
- }
118
- const luau = emit(ast, opts);
119
- return attachRequires(luau, source, ast, opts, opts.outName || fileName || "input.cpp");
8
+ return compileService(source, fileName, options).files[0]?.contents ?? "";
120
9
  }
121
10
 
122
11
  export function compileService(source: string, fileName?: string, options: CompileOptions = {}): CompileServiceResult {
123
- const opts = options as PreprocessOptions;
124
- opts.moduleIncludes = opts.moduleIncludes || [];
125
- const prepared = preprocess(source, opts.filePath || null, opts);
126
- const ast = parse(prepared, fileName || "input.cpp");
127
- if (isHeaderFile(fileName || "") || isHeaderFile(opts.filePath || "") || isHeaderFile(opts.relativeName || "")) {
128
- return compileHeader(source, fileName || "header.h", opts, ast);
129
- }
130
- const implHeader = opts.filePath ? siblingHeader(opts.filePath) : null;
131
- if (implHeader) {
132
- opts.skipInit = true;
133
- }
134
- const planned = planOutput(ast, fileName || "input.cpp", {
135
- ...opts,
136
- relativeName: opts.relativeName || fileName,
137
- source: prepared,
138
- siblingHeader: implHeader,
12
+ const relative = (options.relativeName || fileName || "input.clpp").replace(/\\/g, "/");
13
+ const outName = options.outName || toLuauPath(relative);
14
+ const header = options.filePath ? siblingHeader(options.filePath) : null;
15
+ const skipInit = options.skipInit === true || shouldSkipInit(relative, Boolean(header));
16
+ const diskPath = options.filePath ? path.resolve(options.filePath) : null;
17
+ const artifact = compileViaClpp({
18
+ source,
19
+ fileName: diskPath ? diskPath.replace(/\\/g, "/") : relative,
20
+ strict: options.strict,
21
+ cwd: diskPath ? path.dirname(diskPath) : options.srcDir,
22
+ });
23
+ const luau = clppLuauToGame(artifact, {
24
+ relativeName: relative,
25
+ outName,
26
+ srcDir: options.srcDir,
27
+ rootDir: options.rootDir,
28
+ strict: options.strict,
29
+ skipInit,
139
30
  });
140
- if (planned.files) {
141
- return {
142
- ...planned,
143
- files: planned.files.map((file: { name: string; contents: string }) => ({
144
- ...file,
145
- contents: attachRequires(file.contents, source, ast, opts, file.name),
146
- })),
147
- };
148
- }
149
- const luau = attachRequires(emit(ast, opts), source, ast, opts, planned.outName || opts.outName || fileName || "input.cpp");
150
- const name =
151
- planned.outName ||
152
- opts.outName ||
153
- String(fileName || "input.cpp").replace(/\.(cpp|cc|cxx|c|h|hpp|hh)$/i, ".luau");
154
31
  return {
155
- kind: planned.kind || "flat",
156
- plan: planned.plan,
157
- files: [{ name, contents: luau }],
158
- stale: planned.stale || [],
32
+ kind: emitKind(relative),
33
+ plan: { kind: emitKind(relative), strict: options.strict === true },
34
+ files: [{ name: outName, contents: luau }],
35
+ stale: [],
159
36
  };
160
37
  }