@ugo-studio/jspp 0.3.0 → 0.3.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 (127) hide show
  1. package/LICENSE +25 -25
  2. package/README.md +20 -12
  3. package/dist/cli/args.js +22 -0
  4. package/dist/cli/compiler.js +53 -0
  5. package/dist/cli/index.js +43 -107
  6. package/dist/cli/pch.js +71 -0
  7. package/dist/cli/runner.js +23 -0
  8. package/dist/cli/spinner.js +27 -11
  9. package/dist/cli/transpiler.js +20 -0
  10. package/dist/cli/utils.js +59 -0
  11. package/dist/cli/wasm.js +70 -0
  12. package/dist/index.js +17 -6
  13. package/dist/{analysis → interpreter/analysis}/scope.js +38 -3
  14. package/dist/{analysis → interpreter/analysis}/typeAnalyzer.js +563 -28
  15. package/dist/{core → interpreter/core}/codegen/class-handlers.js +1 -1
  16. package/dist/{core → interpreter/core}/codegen/control-flow-handlers.js +12 -11
  17. package/dist/{core → interpreter/core}/codegen/declaration-handlers.js +28 -9
  18. package/dist/{core → interpreter/core}/codegen/destructuring-handlers.js +9 -4
  19. package/dist/{core → interpreter/core}/codegen/expression-handlers.js +82 -88
  20. package/dist/{core → interpreter/core}/codegen/function-handlers.js +159 -46
  21. package/dist/{core → interpreter/core}/codegen/helpers.js +170 -25
  22. package/dist/interpreter/core/codegen/index.js +156 -0
  23. package/dist/{core → interpreter/core}/codegen/literal-handlers.js +9 -0
  24. package/dist/{core → interpreter/core}/codegen/statement-handlers.js +47 -7
  25. package/package.json +6 -4
  26. package/scripts/precompile-headers.ts +293 -50
  27. package/scripts/setup-compiler.ts +63 -63
  28. package/scripts/setup-emsdk.ts +114 -0
  29. package/src/prelude/any_value.cpp +888 -0
  30. package/src/prelude/any_value.hpp +29 -24
  31. package/src/prelude/{exception_helpers.hpp → exception.cpp} +53 -53
  32. package/src/prelude/exception.hpp +27 -27
  33. package/src/prelude/iterator_instantiations.hpp +10 -0
  34. package/src/prelude/{index.hpp → jspp.hpp} +13 -17
  35. package/src/prelude/library/array.cpp +191 -0
  36. package/src/prelude/library/array.hpp +5 -178
  37. package/src/prelude/library/boolean.cpp +30 -0
  38. package/src/prelude/library/boolean.hpp +14 -0
  39. package/src/prelude/library/console.cpp +125 -0
  40. package/src/prelude/library/console.hpp +9 -97
  41. package/src/prelude/library/error.cpp +100 -0
  42. package/src/prelude/library/error.hpp +8 -108
  43. package/src/prelude/library/function.cpp +69 -0
  44. package/src/prelude/library/function.hpp +6 -5
  45. package/src/prelude/library/global.cpp +98 -0
  46. package/src/prelude/library/global.hpp +12 -28
  47. package/src/prelude/library/global_usings.hpp +15 -0
  48. package/src/prelude/library/math.cpp +261 -0
  49. package/src/prelude/library/math.hpp +8 -288
  50. package/src/prelude/library/object.cpp +379 -0
  51. package/src/prelude/library/object.hpp +5 -267
  52. package/src/prelude/library/performance.cpp +21 -0
  53. package/src/prelude/library/performance.hpp +5 -20
  54. package/src/prelude/library/process.cpp +38 -0
  55. package/src/prelude/library/process.hpp +3 -31
  56. package/src/prelude/library/promise.cpp +131 -0
  57. package/src/prelude/library/promise.hpp +5 -116
  58. package/src/prelude/library/symbol.cpp +56 -0
  59. package/src/prelude/library/symbol.hpp +5 -46
  60. package/src/prelude/library/timer.cpp +88 -0
  61. package/src/prelude/library/timer.hpp +11 -87
  62. package/src/prelude/runtime.cpp +19 -0
  63. package/src/prelude/types.hpp +26 -20
  64. package/src/prelude/utils/access.hpp +123 -32
  65. package/src/prelude/utils/assignment_operators.hpp +119 -99
  66. package/src/prelude/utils/log_any_value/array.hpp +61 -40
  67. package/src/prelude/utils/log_any_value/function.hpp +39 -39
  68. package/src/prelude/utils/log_any_value/log_any_value.hpp +1 -1
  69. package/src/prelude/utils/log_any_value/object.hpp +60 -3
  70. package/src/prelude/utils/log_any_value/primitives.hpp +1 -1
  71. package/src/prelude/utils/operators.hpp +109 -94
  72. package/src/prelude/utils/operators_native.hpp +349 -0
  73. package/src/prelude/utils/well_known_symbols.hpp +24 -24
  74. package/src/prelude/values/array.cpp +1399 -0
  75. package/src/prelude/values/array.hpp +4 -0
  76. package/src/prelude/values/async_iterator.cpp +251 -0
  77. package/src/prelude/values/async_iterator.hpp +60 -32
  78. package/src/prelude/values/boolean.cpp +64 -0
  79. package/src/prelude/values/function.cpp +262 -0
  80. package/src/prelude/values/function.hpp +10 -30
  81. package/src/prelude/values/iterator.cpp +309 -0
  82. package/src/prelude/values/iterator.hpp +33 -64
  83. package/src/prelude/values/number.cpp +221 -0
  84. package/src/prelude/values/object.cpp +200 -0
  85. package/src/prelude/values/object.hpp +4 -0
  86. package/src/prelude/values/promise.cpp +479 -0
  87. package/src/prelude/values/promise.hpp +9 -2
  88. package/src/prelude/values/prototypes/array.hpp +46 -1348
  89. package/src/prelude/values/prototypes/async_iterator.hpp +19 -61
  90. package/src/prelude/values/prototypes/boolean.hpp +24 -0
  91. package/src/prelude/values/prototypes/function.hpp +7 -46
  92. package/src/prelude/values/prototypes/iterator.hpp +15 -191
  93. package/src/prelude/values/prototypes/number.hpp +30 -210
  94. package/src/prelude/values/prototypes/object.hpp +7 -23
  95. package/src/prelude/values/prototypes/promise.hpp +8 -186
  96. package/src/prelude/values/prototypes/string.hpp +28 -553
  97. package/src/prelude/values/prototypes/symbol.hpp +9 -70
  98. package/src/prelude/values/shape.hpp +52 -52
  99. package/src/prelude/values/string.cpp +485 -0
  100. package/src/prelude/values/symbol.cpp +89 -0
  101. package/src/prelude/values/symbol.hpp +101 -101
  102. package/dist/cli/file-utils.js +0 -20
  103. package/dist/cli-utils/args.js +0 -59
  104. package/dist/cli-utils/colors.js +0 -9
  105. package/dist/cli-utils/file-utils.js +0 -20
  106. package/dist/cli-utils/spinner.js +0 -55
  107. package/dist/cli.js +0 -153
  108. package/dist/core/codegen/index.js +0 -86
  109. package/src/prelude/any_value_access.hpp +0 -170
  110. package/src/prelude/any_value_defines.hpp +0 -190
  111. package/src/prelude/any_value_helpers.hpp +0 -374
  112. package/src/prelude/utils/operators_primitive.hpp +0 -337
  113. package/src/prelude/values/helpers/array.hpp +0 -199
  114. package/src/prelude/values/helpers/async_iterator.hpp +0 -275
  115. package/src/prelude/values/helpers/function.hpp +0 -109
  116. package/src/prelude/values/helpers/iterator.hpp +0 -145
  117. package/src/prelude/values/helpers/object.hpp +0 -104
  118. package/src/prelude/values/helpers/promise.hpp +0 -254
  119. package/src/prelude/values/helpers/string.hpp +0 -37
  120. package/src/prelude/values/helpers/symbol.hpp +0 -21
  121. /package/dist/{ast → interpreter/ast}/symbols.js +0 -0
  122. /package/dist/{ast → interpreter/ast}/types.js +0 -0
  123. /package/dist/{core → interpreter/core}/codegen/visitor.js +0 -0
  124. /package/dist/{core → interpreter/core}/constants.js +0 -0
  125. /package/dist/{core → interpreter/core}/error.js +0 -0
  126. /package/dist/{core → interpreter/core}/parser.js +0 -0
  127. /package/dist/{core → interpreter/core}/traverser.js +0 -0
@@ -1,7 +1,82 @@
1
- import { spawnSync } from "child_process";
1
+ import { spawn } from "child_process";
2
2
  import fs from "fs/promises";
3
3
  import path from "path";
4
4
 
5
+ const COLORS = {
6
+ reset: "\x1b[0m",
7
+ cyan: "\x1b[36m",
8
+ green: "\x1b[32m",
9
+ yellow: "\x1b[33m",
10
+ red: "\x1b[31m",
11
+ dim: "\x1b[2m",
12
+ bold: "\x1b[1m",
13
+ };
14
+
15
+ export class Spinner {
16
+ private frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
17
+ private interval: any = null;
18
+ private frameIndex = 0;
19
+ public text: string;
20
+
21
+ constructor(text: string) {
22
+ this.text = text;
23
+ }
24
+
25
+ start() {
26
+ process.stdout.write("\x1b[?25l"); // Hide cursor
27
+ this.frameIndex = 0;
28
+ this.render();
29
+ this.interval = setInterval(() => {
30
+ this.frameIndex = (this.frameIndex + 1) % this.frames.length;
31
+ this.render();
32
+ }, 80);
33
+ }
34
+
35
+ update(text: string) {
36
+ this.text = text;
37
+ this.render();
38
+ }
39
+
40
+ stop(symbol: string = "", color: string = COLORS.reset) {
41
+ if (this.interval) {
42
+ clearInterval(this.interval);
43
+ this.interval = null;
44
+ }
45
+ this.clearLine();
46
+ process.stdout.write(
47
+ `${color}${symbol} ${COLORS.reset} ${this.text}\n`,
48
+ );
49
+ process.stdout.write("\x1b[?25h"); // Show cursor
50
+ }
51
+
52
+ succeed(text?: string) {
53
+ if (text) this.text = text;
54
+ this.stop("✔", COLORS.green);
55
+ }
56
+
57
+ fail(text?: string) {
58
+ if (text) this.text = text;
59
+ this.stop("✖", COLORS.red);
60
+ }
61
+
62
+ info(text?: string) {
63
+ if (text) this.text = text;
64
+ this.stop("ℹ", COLORS.cyan);
65
+ }
66
+
67
+ private render() {
68
+ this.clearLine();
69
+ const frame = this.frames[this.frameIndex];
70
+ process.stdout.write(
71
+ `${COLORS.cyan}${frame} ${COLORS.reset} ${this.text}`,
72
+ );
73
+ }
74
+
75
+ private clearLine() {
76
+ process.stdout.write("\r\x1b[K");
77
+ }
78
+ }
79
+
5
80
  const PRELUDE_DIR = path.resolve(process.cwd(), "src", "prelude");
6
81
  const PRECOMPILED_HEADER_BASE_DIR = path.resolve(
7
82
  process.cwd(),
@@ -11,11 +86,24 @@ const PRECOMPILED_HEADER_BASE_DIR = path.resolve(
11
86
  const MODES = [
12
87
  {
13
88
  name: "debug",
14
- flags: ["-O0"],
89
+ flags: ["-Og"],
90
+ linkerFlags: [],
91
+ compiler: "g++",
92
+ archiver: "ar",
15
93
  },
16
94
  {
17
95
  name: "release",
18
96
  flags: ["-O3", "-DNDEBUG"],
97
+ linkerFlags: [],
98
+ compiler: "g++",
99
+ archiver: "ar",
100
+ },
101
+ {
102
+ name: "wasm",
103
+ flags: ["-O3", "-DNDEBUG"],
104
+ linkerFlags: ["-sASYNCIFY", "-sALLOW_MEMORY_GROWTH=1"],
105
+ compiler: "em++",
106
+ archiver: "emar",
19
107
  },
20
108
  ];
21
109
 
@@ -24,15 +112,19 @@ if (process.platform === "win32") {
24
112
  MODES[1].flags.push("-Wa,-mbig-obj");
25
113
  }
26
114
 
27
- async function getLatestMtime(dirPath: string): Promise<number> {
115
+ async function getLatestMtime(
116
+ dirPath: string,
117
+ filter?: (name: string) => boolean,
118
+ ): Promise<number> {
28
119
  let maxMtime = 0;
29
120
  const entries = await fs.readdir(dirPath, { withFileTypes: true });
30
121
  for (const entry of entries) {
31
122
  const fullPath = path.join(dirPath, entry.name);
32
123
  if (entry.isDirectory()) {
33
- const nestedMtime = await getLatestMtime(fullPath);
124
+ const nestedMtime = await getLatestMtime(fullPath, filter);
34
125
  if (nestedMtime > maxMtime) maxMtime = nestedMtime;
35
126
  } else {
127
+ if (filter && !filter(entry.name)) continue;
36
128
  const stats = await fs.stat(fullPath);
37
129
  if (stats.mtimeMs > maxMtime) maxMtime = stats.mtimeMs;
38
130
  }
@@ -40,53 +132,91 @@ async function getLatestMtime(dirPath: string): Promise<number> {
40
132
  return maxMtime;
41
133
  }
42
134
 
135
+ async function findCppFiles(dir: string): Promise<string[]> {
136
+ const entries = await fs.readdir(dir, { withFileTypes: true });
137
+ const files = await Promise.all(entries.map((entry) => {
138
+ const res = path.resolve(dir, entry.name);
139
+ if (entry.isDirectory()) {
140
+ return findCppFiles(res);
141
+ } else {
142
+ return res.endsWith(".cpp") ? [res] : [];
143
+ }
144
+ }));
145
+ return Array.prototype.concat(...files);
146
+ }
147
+
148
+ async function runCommand(cmd: string, args: string[]): Promise<boolean> {
149
+ // console.log(`${COLORS.dim}> ${cmd} ${args.join(" ")}${COLORS.reset}`);
150
+ return new Promise((resolve) => {
151
+ const proc = spawn(cmd, args, {
152
+ stdio: "inherit",
153
+ shell: process.platform === "win32",
154
+ });
155
+ proc.on("close", (code) => resolve(code === 0));
156
+ });
157
+ }
158
+
43
159
  async function precompileHeaders() {
44
160
  const force = process.argv.includes("--force");
161
+ const jsppCliIsParent = process.argv.includes("--jspp-cli-is-parent");
162
+ const silent = process.argv.includes("--silent");
163
+
164
+ const modeArgIdx = process.argv.indexOf("--mode");
165
+ const targetMode = modeArgIdx !== -1
166
+ ? process.argv[modeArgIdx + 1]
167
+ : undefined;
168
+
169
+ if (!jsppCliIsParent && !silent) {
170
+ console.log(
171
+ `${COLORS.bold}${COLORS.cyan}JSPP: Precompiling headers and runtime...${COLORS.reset}\n`,
172
+ );
173
+ }
174
+
45
175
  try {
46
176
  await fs.mkdir(PRECOMPILED_HEADER_BASE_DIR, { recursive: true });
47
- const sourceMtime = await getLatestMtime(PRELUDE_DIR);
177
+
178
+ const latestHeaderMtime = await getLatestMtime(
179
+ PRELUDE_DIR,
180
+ (name) => name.endsWith(".hpp") || name.endsWith(".h"),
181
+ );
48
182
 
49
183
  for (const mode of MODES) {
184
+ if (targetMode && mode.name !== targetMode) continue;
185
+
50
186
  const modeDir = path.join(PRECOMPILED_HEADER_BASE_DIR, mode.name);
51
- const headerPath = path.join(modeDir, "index.hpp");
52
- const gchPath = path.join(modeDir, "index.hpp.gch");
187
+ const headerPath = path.join(modeDir, "jspp.hpp");
188
+ const gchPath = path.join(modeDir, "jspp.hpp.gch");
189
+
190
+ const modeLabel = `[${mode.name.toUpperCase()}]`;
191
+ const spinner = new Spinner(`${modeLabel} Checking headers...`);
192
+ if (!silent) spinner.start();
53
193
 
54
- if (!force) {
194
+ await fs.mkdir(modeDir, { recursive: true });
195
+
196
+ let gchRebuilt = false;
197
+ let shouldBuildGch = force;
198
+
199
+ if (!shouldBuildGch) {
55
200
  try {
56
201
  const gchStats = await fs.stat(gchPath);
57
- if (gchStats.mtimeMs >= sourceMtime) {
58
- console.log(
59
- `[${mode.name.toUpperCase()}] Headers are up-to-date. Skipping.`,
60
- );
61
- continue;
202
+ if (latestHeaderMtime > gchStats.mtimeMs) {
203
+ shouldBuildGch = true;
62
204
  }
63
205
  } catch (e) {
64
- // PCH doesn't exist, proceed to compile
206
+ shouldBuildGch = true;
65
207
  }
66
208
  }
67
209
 
68
- console.log(`\n[${mode.name.toUpperCase()}] Setting up...`);
69
- await fs.mkdir(modeDir, { recursive: true });
70
-
71
- // Copy index.hpp
72
- await fs.copyFile(path.join(PRELUDE_DIR, "index.hpp"), headerPath);
73
-
74
- // Remove existing gch if it exists
75
- if (
76
- await fs.stat(gchPath).then(
77
- () => true,
78
- () => false,
79
- )
80
- ) {
81
- await fs.unlink(gchPath);
82
- }
83
-
84
- console.log(`[${mode.name.toUpperCase()}] Compiling header...`);
85
- const tempGchPath = `${gchPath}.tmp`;
210
+ if (shouldBuildGch) {
211
+ if (silent) spinner.start();
212
+ spinner.update(`${modeLabel} Compiling header...`);
213
+ await fs.copyFile(
214
+ path.join(PRELUDE_DIR, "jspp.hpp"),
215
+ headerPath,
216
+ );
86
217
 
87
- const compile = spawnSync(
88
- "g++",
89
- [
218
+ const tempGchPath = `${gchPath}.tmp`;
219
+ const success = await runCommand(mode.compiler, [
90
220
  "-x",
91
221
  "c++-header",
92
222
  "-std=c++23",
@@ -95,31 +225,144 @@ async function precompileHeaders() {
95
225
  "-o",
96
226
  tempGchPath,
97
227
  "-I",
228
+ modeDir,
229
+ "-I",
98
230
  PRELUDE_DIR,
99
- ],
100
- {
101
- stdio: "inherit",
102
- },
231
+ ]);
232
+
233
+ if (!success) {
234
+ spinner.fail(`${modeLabel} Failed to precompile headers.`);
235
+ process.exit(1);
236
+ }
237
+
238
+ await fs.rename(tempGchPath, gchPath);
239
+ gchRebuilt = true;
240
+ spinner.succeed(`${modeLabel} PCH Success.`);
241
+ } else {
242
+ if (!silent) {
243
+ spinner.succeed(`${modeLabel} Headers are up-to-date.`);
244
+ }
245
+ }
246
+
247
+ // --- Incremental Compilation of .cpp files ---
248
+ const cppFiles = await findCppFiles(PRELUDE_DIR);
249
+ const objFiles: string[] = [];
250
+ let anyObjRebuilt = false;
251
+
252
+ // We need gchPath to exist for next check, if it doesn't we probably have a problem or it's a first run
253
+ let gchMtime = 0;
254
+ try {
255
+ gchMtime = (await fs.stat(gchPath)).mtimeMs;
256
+ } catch (e) {}
257
+
258
+ const libSpinner = new Spinner(
259
+ `${modeLabel} Checking runtime library...`,
103
260
  );
261
+ if (!silent) libSpinner.start();
104
262
 
105
- if (compile.status !== 0) {
263
+ for (let idx = 0; idx < cppFiles.length; idx++) {
264
+ const cppFile = cppFiles[idx];
265
+ const relativePath = path.relative(PRELUDE_DIR, cppFile);
266
+ const objFile = path.join(
267
+ modeDir,
268
+ relativePath.replace(/\.cpp$/, ".o"),
269
+ );
270
+ await fs.mkdir(path.dirname(objFile), { recursive: true });
271
+ objFiles.push(objFile);
272
+
273
+ let shouldCompile = force || gchRebuilt;
274
+ if (!shouldCompile) {
275
+ try {
276
+ const objStats = await fs.stat(objFile);
277
+ const cppStats = await fs.stat(cppFile);
278
+ if (
279
+ cppStats.mtimeMs > objStats.mtimeMs ||
280
+ gchMtime > objStats.mtimeMs
281
+ ) {
282
+ shouldCompile = true;
283
+ }
284
+ } catch (e) {
285
+ shouldCompile = true;
286
+ }
287
+ }
288
+
289
+ if (shouldCompile) {
290
+ if (silent && !libSpinner["interval"]) libSpinner.start();
291
+ libSpinner.update(
292
+ `${modeLabel} Compiling ${relativePath} ${COLORS.dim}[${
293
+ idx + 1
294
+ }/${cppFiles.length}]${COLORS.reset}`,
295
+ );
296
+ const success = await runCommand(mode.compiler, [
297
+ "-c",
298
+ "-std=c++23",
299
+ ...mode.flags,
300
+ cppFile,
301
+ "-o",
302
+ objFile,
303
+ "-I",
304
+ modeDir,
305
+ "-I",
306
+ PRELUDE_DIR,
307
+ ]);
308
+
309
+ if (!success) {
310
+ libSpinner.fail(
311
+ `${modeLabel} Failed to compile ${relativePath}`,
312
+ );
313
+ process.exit(1);
314
+ }
315
+ anyObjRebuilt = true;
316
+ }
317
+ }
318
+
319
+ const libPath = path.join(modeDir, "libjspp.a");
320
+ let shouldArchive = anyObjRebuilt;
321
+ if (!shouldArchive) {
106
322
  try {
107
- await fs.unlink(tempGchPath);
323
+ await fs.access(libPath);
108
324
  } catch (e) {
109
- // Ignore if temp file doesn't exist
325
+ shouldArchive = true;
110
326
  }
111
- console.error(
112
- `[${mode.name.toUpperCase()}] Failed to precompile headers.`,
113
- );
114
- process.exit(1);
115
327
  }
116
328
 
117
- // Atomically replace the old GCH with the new one
118
- await fs.rename(tempGchPath, gchPath);
119
- console.log(`[${mode.name.toUpperCase()}] Success.`);
329
+ if (shouldArchive) {
330
+ if (silent && !libSpinner["interval"]) libSpinner.start();
331
+ libSpinner.update(`${modeLabel} Updating runtime library...`);
332
+ const tempLibPath = `${libPath}.tmp`;
333
+
334
+ const success = await runCommand(mode.archiver, [
335
+ "rcs",
336
+ tempLibPath,
337
+ ...objFiles,
338
+ ]);
339
+
340
+ if (!success) {
341
+ libSpinner.fail(
342
+ `${modeLabel} Failed to create static library.`,
343
+ );
344
+ process.exit(1);
345
+ }
346
+
347
+ await fs.rename(tempLibPath, libPath);
348
+ libSpinner.succeed(`${modeLabel} Runtime Library Success.`);
349
+ } else {
350
+ if (!silent) {
351
+ libSpinner.succeed(
352
+ `${modeLabel} Runtime library is up-to-date.`,
353
+ );
354
+ } else {
355
+ libSpinner.stop();
356
+ }
357
+ }
358
+ }
359
+ if (!jsppCliIsParent && !silent) {
360
+ console.log(
361
+ `\n${COLORS.bold}${COLORS.green}JSPP: Environment ready.${COLORS.reset}\n`,
362
+ );
120
363
  }
121
364
  } catch (error: any) {
122
- console.error(`Error: ${error.message}`);
365
+ console.error(`${COLORS.red}Error: ${error.message}${COLORS.reset}`);
123
366
  process.exit(1);
124
367
  }
125
368
  }
@@ -1,63 +1,63 @@
1
- import { spawnSync } from "child_process";
2
- import { platform } from "os";
3
-
4
- const COLORS = {
5
- reset: "\x1b[0m",
6
- green: "\x1b[32m",
7
- yellow: "\x1b[33m",
8
- red: "\x1b[31m",
9
- cyan: "\x1b[36m",
10
- bold: "\x1b[1m",
11
- };
12
-
13
- function checkGpp(): boolean {
14
- try {
15
- const result = spawnSync("g++", ["--version"], { encoding: "utf8" });
16
- return result.status === 0;
17
- } catch (e) {
18
- return false;
19
- }
20
- }
21
-
22
- function setup() {
23
- console.log(`${COLORS.bold}${COLORS.cyan}JSPP: Checking for C++ compiler...${COLORS.reset}`);
24
-
25
- if (checkGpp()) {
26
- console.log(`${COLORS.green}✔ g++ found.${COLORS.reset}`);
27
- return;
28
- }
29
-
30
- console.log(`${COLORS.yellow}⚠ g++ (GCC) not found in PATH. JSPP requires a C++23 compatible compiler.${COLORS.reset}`);
31
-
32
- const os = platform();
33
-
34
- if (os === "win32") {
35
- console.log(`\n${COLORS.bold}To install GCC on Windows:${COLORS.reset}`);
36
- console.log(`1. Install MinGW-w64 via MSYS2: ${COLORS.cyan}https://www.msys2.org/${COLORS.reset}`);
37
- console.log(`2. Or use winget: ${COLORS.cyan}winget install MSYS2.MSYS2${COLORS.reset}`);
38
- console.log(` Then run: ${COLORS.bold}pacman -S mingw-w64-x86_64-gcc${COLORS.reset}`);
39
- } else if (os === "linux") {
40
- console.log(`\n${COLORS.bold}To install GCC on Linux (Ubuntu/Debian):${COLORS.reset}`);
41
- console.log(`${COLORS.cyan}sudo apt update && sudo apt install g++-14 -y${COLORS.reset}`);
42
- console.log(`\nAttempting to install now (may require password)...`);
43
-
44
- // Try to install automatically on Linux if apt is found
45
- try {
46
- const install = spawnSync("sudo", ["apt-get", "install", "-y", "g++-14"], { stdio: "inherit" });
47
- if (install.status === 0) {
48
- console.log(`${COLORS.green}✔ g++-14 installed successfully.${COLORS.reset}`);
49
- // Try to set up symlink if needed or just inform user
50
- return;
51
- }
52
- } catch (e) {
53
- console.error(`${COLORS.red}Automatic installation failed. Please install manually.${COLORS.reset}`);
54
- }
55
- } else if (os === "darwin") {
56
- console.log(`\n${COLORS.bold}To install GCC on macOS:${COLORS.reset}`);
57
- console.log(`${COLORS.cyan}brew install gcc${COLORS.reset}`);
58
- }
59
-
60
- console.log(`\n${COLORS.bold}After installation, please ensure 'g++' is in your PATH and restart your terminal.${COLORS.reset}\n`);
61
- }
62
-
63
- setup();
1
+ import { spawnSync } from "child_process";
2
+ import { platform } from "os";
3
+
4
+ const COLORS = {
5
+ reset: "\x1b[0m",
6
+ green: "\x1b[32m",
7
+ yellow: "\x1b[33m",
8
+ red: "\x1b[31m",
9
+ cyan: "\x1b[36m",
10
+ bold: "\x1b[1m",
11
+ };
12
+
13
+ function checkGpp(): boolean {
14
+ try {
15
+ const result = spawnSync("g++", ["--version"], { encoding: "utf8" });
16
+ return result.status === 0;
17
+ } catch (e) {
18
+ return false;
19
+ }
20
+ }
21
+
22
+ function setup() {
23
+ console.log(`${COLORS.bold}${COLORS.cyan}JSPP: Checking for C++ compiler...${COLORS.reset}`);
24
+
25
+ if (checkGpp()) {
26
+ console.log(`${COLORS.green}✔ g++ found.${COLORS.reset}`);
27
+ return;
28
+ }
29
+
30
+ console.log(`${COLORS.yellow}⚠ g++ (GCC) not found in PATH. JSPP requires a C++23 compatible compiler.${COLORS.reset}`);
31
+
32
+ const os = platform();
33
+
34
+ if (os === "win32") {
35
+ console.log(`\n${COLORS.bold}To install GCC on Windows:${COLORS.reset}`);
36
+ console.log(`1. Install MinGW-w64 via MSYS2: ${COLORS.cyan}https://www.msys2.org/${COLORS.reset}`);
37
+ console.log(`2. Or use winget: ${COLORS.cyan}winget install MSYS2.MSYS2${COLORS.reset}`);
38
+ console.log(` Then run: ${COLORS.bold}pacman -S mingw-w64-x86_64-gcc${COLORS.reset}`);
39
+ } else if (os === "linux") {
40
+ console.log(`\n${COLORS.bold}To install GCC on Linux (Ubuntu/Debian):${COLORS.reset}`);
41
+ console.log(`${COLORS.cyan}sudo apt update && sudo apt install g++-14 -y${COLORS.reset}`);
42
+ console.log(`\nAttempting to install now (may require password)...`);
43
+
44
+ // Try to install automatically on Linux if apt is found
45
+ try {
46
+ const install = spawnSync("sudo", ["apt-get", "install", "-y", "g++-14"], { stdio: "inherit" });
47
+ if (install.status === 0) {
48
+ console.log(`${COLORS.green}✔ g++-14 installed successfully.${COLORS.reset}`);
49
+ // Try to set up symlink if needed or just inform user
50
+ return;
51
+ }
52
+ } catch (e) {
53
+ console.error(`${COLORS.red}Automatic installation failed. Please install manually.${COLORS.reset}`);
54
+ }
55
+ } else if (os === "darwin") {
56
+ console.log(`\n${COLORS.bold}To install GCC on macOS:${COLORS.reset}`);
57
+ console.log(`${COLORS.cyan}brew install gcc${COLORS.reset}`);
58
+ }
59
+
60
+ console.log(`\n${COLORS.bold}After installation, please ensure 'g++' is in your PATH and restart your terminal.${COLORS.reset}\n`);
61
+ }
62
+
63
+ setup();
@@ -0,0 +1,114 @@
1
+ import { spawnSync } from "child_process";
2
+ import fs from "fs";
3
+ import { platform } from "os";
4
+ import path from "path";
5
+
6
+ const COLORS = {
7
+ reset: "\x1b[0m",
8
+ green: "\x1b[32m",
9
+ yellow: "\x1b[33m",
10
+ red: "\x1b[31m",
11
+ cyan: "\x1b[36m",
12
+ bold: "\x1b[1m",
13
+ };
14
+
15
+ const EMSDK_DIR = path.resolve(process.cwd(), ".emsdk");
16
+ const isWin = platform() === "win32";
17
+ const emsdkCmd = isWin ? "emsdk.bat" : "./emsdk";
18
+
19
+ function checkEmcc(): boolean {
20
+ try {
21
+ const result = spawnSync("emcc", ["--version"], { encoding: "utf8" });
22
+ return result.status === 0;
23
+ } catch (e) {
24
+ return false;
25
+ }
26
+ }
27
+
28
+ function runCommand(
29
+ cmd: string,
30
+ args: string[],
31
+ cwd: string = process.cwd(),
32
+ ): boolean {
33
+ console.log(`${COLORS.cyan}> ${cmd} ${args.join(" ")}${COLORS.reset}`);
34
+ const result = spawnSync(cmd, args, {
35
+ cwd,
36
+ stdio: "inherit",
37
+ shell: isWin,
38
+ });
39
+ return result.status === 0;
40
+ }
41
+
42
+ async function setup() {
43
+ console.log(
44
+ `${COLORS.bold}${COLORS.cyan}JSPP: Setting up Emscripten SDK for Wasm support...${COLORS.reset}`,
45
+ );
46
+
47
+ if (checkEmcc()) {
48
+ console.log(
49
+ `${COLORS.green}✔ Emscripten (emcc) found in PATH.${COLORS.reset}`,
50
+ );
51
+ return;
52
+ }
53
+
54
+ if (fs.existsSync(path.join(EMSDK_DIR, emsdkCmd))) {
55
+ console.log(
56
+ `${COLORS.yellow}ℹ Local EMSDK found in .emsdk directory.${COLORS.reset}`,
57
+ );
58
+ } else {
59
+ console.log(
60
+ `${COLORS.yellow}ℹ EMSDK not found. Cloning from GitHub...${COLORS.reset}`,
61
+ );
62
+ if (
63
+ !runCommand("git", [
64
+ "clone",
65
+ "https://github.com/emscripten-core/emsdk.git",
66
+ ".emsdk",
67
+ ])
68
+ ) {
69
+ console.error(
70
+ `${COLORS.red}Error: Failed to clone EMSDK repository. Make sure 'git' is installed.${COLORS.reset}`,
71
+ );
72
+ return;
73
+ }
74
+ }
75
+
76
+ console.log(
77
+ `${COLORS.cyan}Installing latest Emscripten SDK...${COLORS.reset}`,
78
+ );
79
+ if (!runCommand(emsdkCmd, ["install", "latest"], EMSDK_DIR)) {
80
+ console.error(
81
+ `${COLORS.red}Error: Failed to install Emscripten SDK.${COLORS.reset}`,
82
+ );
83
+ return;
84
+ }
85
+
86
+ console.log(
87
+ `${COLORS.cyan}Activating latest Emscripten SDK...${COLORS.reset}`,
88
+ );
89
+ if (
90
+ !runCommand(emsdkCmd, ["activate", "latest", "--permanent"], EMSDK_DIR)
91
+ ) {
92
+ console.error(
93
+ `${COLORS.red}Error: Failed to activate Emscripten SDK.${COLORS.reset}`,
94
+ );
95
+ return;
96
+ }
97
+
98
+ console.log(
99
+ `\n${COLORS.green}✔ Emscripten SDK setup complete.${COLORS.reset}`,
100
+ );
101
+ console.log(
102
+ `${COLORS.yellow}Note: To use emcc in your current terminal, you may need to run:${COLORS.reset}`,
103
+ );
104
+ if (isWin) {
105
+ console.log(`${COLORS.bold} .emsdk\\emsdk_env.bat${COLORS.reset}`);
106
+ } else {
107
+ console.log(
108
+ `${COLORS.bold} source .emsdk/emsdk_env.sh${COLORS.reset}`,
109
+ );
110
+ }
111
+ console.log("");
112
+ }
113
+
114
+ setup();