c-next 0.2.11 → 0.2.13

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 (75) hide show
  1. package/README.md +80 -649
  2. package/dist/index.js +8273 -6357
  3. package/dist/index.js.map +4 -4
  4. package/grammar/C.g4 +17 -4
  5. package/package.json +3 -3
  6. package/src/__tests__/index.test.ts +1 -1
  7. package/src/cli/CleanCommand.ts +8 -12
  8. package/src/cli/Cli.ts +29 -6
  9. package/src/cli/Runner.ts +42 -62
  10. package/src/cli/__tests__/CleanCommand.test.ts +10 -10
  11. package/src/cli/__tests__/Cli.test.ts +59 -7
  12. package/src/cli/__tests__/ConfigPrinter.test.ts +12 -12
  13. package/src/cli/__tests__/PathNormalizer.test.ts +5 -5
  14. package/src/cli/__tests__/Runner.test.ts +108 -82
  15. package/src/cli/serve/ServeCommand.ts +1 -1
  16. package/src/cli/types/ICliConfig.ts +2 -2
  17. package/src/lib/parseWithSymbols.ts +21 -21
  18. package/src/transpiler/Transpiler.ts +99 -46
  19. package/src/transpiler/__tests__/DualCodePaths.test.ts +29 -29
  20. package/src/transpiler/__tests__/Transpiler.coverage.test.ts +244 -72
  21. package/src/transpiler/__tests__/Transpiler.test.ts +32 -72
  22. package/src/transpiler/__tests__/determineProjectRoot.test.ts +30 -28
  23. package/src/transpiler/__tests__/needsConditionalPreprocessing.test.ts +1 -1
  24. package/src/transpiler/data/CNextMarkerDetector.ts +34 -0
  25. package/src/transpiler/data/CppEntryPointScanner.ts +174 -0
  26. package/src/transpiler/data/FileDiscovery.ts +2 -105
  27. package/src/transpiler/data/InputExpansion.ts +37 -81
  28. package/src/transpiler/data/__tests__/CNextMarkerDetector.test.ts +62 -0
  29. package/src/transpiler/data/__tests__/CppEntryPointScanner.test.ts +239 -0
  30. package/src/transpiler/data/__tests__/FileDiscovery.test.ts +45 -191
  31. package/src/transpiler/data/__tests__/InputExpansion.test.ts +36 -204
  32. package/src/transpiler/logic/analysis/InitializationAnalyzer.ts +2 -2
  33. package/src/transpiler/logic/analysis/PassByValueAnalyzer.ts +4 -5
  34. package/src/transpiler/logic/parser/c/grammar/C.interp +33 -3
  35. package/src/transpiler/logic/parser/c/grammar/C.tokens +237 -207
  36. package/src/transpiler/logic/parser/c/grammar/CLexer.interp +48 -3
  37. package/src/transpiler/logic/parser/c/grammar/CLexer.tokens +237 -207
  38. package/src/transpiler/logic/parser/c/grammar/CLexer.ts +702 -611
  39. package/src/transpiler/logic/parser/c/grammar/CParser.ts +1221 -1107
  40. package/src/transpiler/logic/symbols/SymbolTable.ts +147 -73
  41. package/src/transpiler/logic/symbols/__tests__/SymbolTable.test.ts +157 -14
  42. package/src/transpiler/logic/symbols/c/__tests__/CResolver.integration.test.ts +3 -3
  43. package/src/transpiler/logic/symbols/c/collectors/StructCollector.ts +7 -37
  44. package/src/transpiler/logic/symbols/cnext/__tests__/TSymbolInfoAdapter.test.ts +6 -6
  45. package/src/transpiler/logic/symbols/cnext/adapters/TSymbolInfoAdapter.ts +28 -27
  46. package/src/transpiler/logic/symbols/cnext/index.ts +4 -4
  47. package/src/transpiler/logic/symbols/cnext/utils/SymbolNameUtils.ts +5 -5
  48. package/src/transpiler/output/codegen/CodeGenerator.ts +16 -1
  49. package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +15 -0
  50. package/src/transpiler/output/codegen/__tests__/ExpressionWalker.test.ts +3 -3
  51. package/src/transpiler/output/codegen/__tests__/RequireInclude.test.ts +14 -14
  52. package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +2 -2
  53. package/src/transpiler/output/codegen/helpers/FunctionContextManager.ts +15 -3
  54. package/src/transpiler/output/codegen/helpers/ParameterInputAdapter.ts +14 -6
  55. package/src/transpiler/output/codegen/helpers/__tests__/ParameterInputAdapter.test.ts +1 -0
  56. package/src/transpiler/output/codegen/types/IFunctionContextCallbacks.ts +2 -0
  57. package/src/transpiler/output/codegen/utils/QualifiedNameGenerator.ts +7 -7
  58. package/src/transpiler/output/codegen/utils/__tests__/QualifiedNameGenerator.test.ts +3 -3
  59. package/src/transpiler/output/headers/BaseHeaderGenerator.ts +10 -1
  60. package/src/transpiler/output/headers/HeaderGenerator.ts +3 -0
  61. package/src/transpiler/output/headers/HeaderGeneratorUtils.ts +6 -2
  62. package/src/transpiler/output/headers/__tests__/HeaderGeneratorUtils.test.ts +16 -0
  63. package/src/transpiler/output/headers/adapters/HeaderSymbolAdapter.ts +19 -19
  64. package/src/transpiler/output/headers/adapters/__tests__/HeaderSymbolAdapter.test.ts +5 -5
  65. package/src/transpiler/state/SymbolRegistry.ts +10 -12
  66. package/src/transpiler/state/__tests__/SymbolRegistry.test.ts +11 -13
  67. package/src/transpiler/types/ICachedFileEntry.ts +4 -0
  68. package/src/transpiler/types/IPipelineFile.ts +3 -0
  69. package/src/transpiler/types/ITranspilerConfig.ts +2 -2
  70. package/src/transpiler/types/symbols/IScopeSymbol.ts +1 -1
  71. package/src/utils/FunctionUtils.ts +3 -3
  72. package/src/utils/__tests__/FunctionUtils.test.ts +6 -4
  73. package/src/utils/cache/CacheManager.ts +28 -15
  74. package/src/utils/cache/__tests__/CacheManager.test.ts +6 -4
  75. package/src/transpiler/data/types/IDiscoveryOptions.ts +0 -15
@@ -21,10 +21,7 @@ describe("Transpiler", () => {
21
21
  // because it doesn't rely on FileDiscovery for the main code path
22
22
 
23
23
  it("transpiles source string without file I/O", async () => {
24
- const transpiler = new Transpiler(
25
- { inputs: [], noCache: true },
26
- mockFs,
27
- );
24
+ const transpiler = new Transpiler({ input: "", noCache: true }, mockFs);
28
25
 
29
26
  const result = (
30
27
  await transpiler.transpile({
@@ -39,10 +36,7 @@ describe("Transpiler", () => {
39
36
  });
40
37
 
41
38
  it("returns parse errors for invalid source", async () => {
42
- const transpiler = new Transpiler(
43
- { inputs: [], noCache: true },
44
- mockFs,
45
- );
39
+ const transpiler = new Transpiler({ input: "", noCache: true }, mockFs);
46
40
 
47
41
  const result = await transpiler.transpile({
48
42
  kind: "source",
@@ -54,10 +48,7 @@ describe("Transpiler", () => {
54
48
  });
55
49
 
56
50
  it("generates header code for exported functions", async () => {
57
- const transpiler = new Transpiler(
58
- { inputs: [], noCache: true },
59
- mockFs,
60
- );
51
+ const transpiler = new Transpiler({ input: "", noCache: true }, mockFs);
61
52
 
62
53
  const result = (
63
54
  await transpiler.transpile({
@@ -76,10 +67,7 @@ describe("Transpiler", () => {
76
67
  });
77
68
 
78
69
  it("returns undefined headerCode when no exports", async () => {
79
- const transpiler = new Transpiler(
80
- { inputs: [], noCache: true },
81
- mockFs,
82
- );
70
+ const transpiler = new Transpiler({ input: "", noCache: true }, mockFs);
83
71
 
84
72
  const result = (
85
73
  await transpiler.transpile({
@@ -94,7 +82,7 @@ describe("Transpiler", () => {
94
82
 
95
83
  it("respects parseOnly mode", async () => {
96
84
  const transpiler = new Transpiler(
97
- { inputs: [], parseOnly: true, noCache: true },
85
+ { input: "", parseOnly: true, noCache: true },
98
86
  mockFs,
99
87
  );
100
88
 
@@ -110,10 +98,7 @@ describe("Transpiler", () => {
110
98
  });
111
99
 
112
100
  it("handles code generation errors gracefully", async () => {
113
- const transpiler = new Transpiler(
114
- { inputs: [], noCache: true },
115
- mockFs,
116
- );
101
+ const transpiler = new Transpiler({ input: "", noCache: true }, mockFs);
117
102
 
118
103
  // This should parse but might have semantic issues
119
104
  const result = (
@@ -128,10 +113,7 @@ describe("Transpiler", () => {
128
113
  });
129
114
 
130
115
  it("reports narrowing error at the correct line", async () => {
131
- const transpiler = new Transpiler(
132
- { inputs: [], noCache: true },
133
- mockFs,
134
- );
116
+ const transpiler = new Transpiler({ input: "", noCache: true }, mockFs);
135
117
 
136
118
  const result = (
137
119
  await transpiler.transpile({
@@ -148,10 +130,7 @@ describe("Transpiler", () => {
148
130
  });
149
131
 
150
132
  it("defaults to line 1 for errors without location info", async () => {
151
- const transpiler = new Transpiler(
152
- { inputs: [], noCache: true },
153
- mockFs,
154
- );
133
+ const transpiler = new Transpiler({ input: "", noCache: true }, mockFs);
155
134
 
156
135
  // Ternary with bare variable produces error without line prefix
157
136
  const result = (
@@ -168,10 +147,7 @@ describe("Transpiler", () => {
168
147
  });
169
148
 
170
149
  it("transpiles various C-Next types correctly", async () => {
171
- const transpiler = new Transpiler(
172
- { inputs: [], noCache: true },
173
- mockFs,
174
- );
150
+ const transpiler = new Transpiler({ input: "", noCache: true }, mockFs);
175
151
 
176
152
  const result = (
177
153
  await transpiler.transpile({
@@ -201,10 +177,7 @@ describe("Transpiler", () => {
201
177
  });
202
178
 
203
179
  it("handles assignment operator correctly", async () => {
204
- const transpiler = new Transpiler(
205
- { inputs: [], noCache: true },
206
- mockFs,
207
- );
180
+ const transpiler = new Transpiler({ input: "", noCache: true }, mockFs);
208
181
 
209
182
  const result = (
210
183
  await transpiler.transpile({
@@ -223,10 +196,7 @@ describe("Transpiler", () => {
223
196
  });
224
197
 
225
198
  it("handles equality operator correctly", async () => {
226
- const transpiler = new Transpiler(
227
- { inputs: [], noCache: true },
228
- mockFs,
229
- );
199
+ const transpiler = new Transpiler({ input: "", noCache: true }, mockFs);
230
200
 
231
201
  const result = (
232
202
  await transpiler.transpile({
@@ -244,10 +214,7 @@ describe("Transpiler", () => {
244
214
  });
245
215
 
246
216
  it("generates struct definitions", async () => {
247
- const transpiler = new Transpiler(
248
- { inputs: [], noCache: true },
249
- mockFs,
250
- );
217
+ const transpiler = new Transpiler({ input: "", noCache: true }, mockFs);
251
218
 
252
219
  const result = (
253
220
  await transpiler.transpile({
@@ -268,10 +235,7 @@ describe("Transpiler", () => {
268
235
  });
269
236
 
270
237
  it("generates enum definitions", async () => {
271
- const transpiler = new Transpiler(
272
- { inputs: [], noCache: true },
273
- mockFs,
274
- );
238
+ const transpiler = new Transpiler({ input: "", noCache: true }, mockFs);
275
239
 
276
240
  const result = (
277
241
  await transpiler.transpile({
@@ -294,10 +258,7 @@ describe("Transpiler", () => {
294
258
  });
295
259
 
296
260
  it("handles scope definitions", async () => {
297
- const transpiler = new Transpiler(
298
- { inputs: [], noCache: true },
299
- mockFs,
300
- );
261
+ const transpiler = new Transpiler({ input: "", noCache: true }, mockFs);
301
262
 
302
263
  const result = (
303
264
  await transpiler.transpile({
@@ -326,7 +287,7 @@ describe("Transpiler", () => {
326
287
 
327
288
  const transpiler = new Transpiler(
328
289
  {
329
- inputs: ["/project/src/main.cnx"],
290
+ input: "/project/src/main.cnx",
330
291
  outDir: "/project/build",
331
292
  noCache: true,
332
293
  },
@@ -349,7 +310,7 @@ describe("Transpiler", () => {
349
310
 
350
311
  const transpiler = new Transpiler(
351
312
  {
352
- inputs: ["/project/src/main.cnx"],
313
+ input: "/project/src/main.cnx",
353
314
  outDir: "/project/build",
354
315
  noCache: true,
355
316
  },
@@ -367,7 +328,7 @@ describe("Transpiler", () => {
367
328
 
368
329
  const transpiler = new Transpiler(
369
330
  {
370
- inputs: ["/project/src/main.cnx"],
331
+ input: "/project/src/main.cnx",
371
332
  outDir: "/project/build",
372
333
  headerOutDir: "/project/include",
373
334
  noCache: true,
@@ -395,7 +356,7 @@ describe("Transpiler", () => {
395
356
 
396
357
  const transpiler = new Transpiler(
397
358
  {
398
- inputs: ["/project/src/lib.cnx"],
359
+ input: "/project/src/lib.cnx",
399
360
  outDir: "/project/build",
400
361
  noCache: true,
401
362
  },
@@ -412,10 +373,10 @@ describe("Transpiler", () => {
412
373
  expect(hFile?.content).toContain("Math_add");
413
374
  });
414
375
 
415
- it("returns error for non-existent input", async () => {
376
+ it("returns no files for non-existent input", async () => {
416
377
  const transpiler = new Transpiler(
417
378
  {
418
- inputs: ["/nonexistent/file.cnx"],
379
+ input: "/nonexistent/file.cnx",
419
380
  noCache: true,
420
381
  },
421
382
  mockFs,
@@ -423,8 +384,7 @@ describe("Transpiler", () => {
423
384
 
424
385
  const result = await transpiler.transpile({ kind: "files" });
425
386
 
426
- expect(result.success).toBe(false);
427
- expect(result.errors[0].message).toContain("Input not found");
387
+ expect(result.filesProcessed).toBe(0);
428
388
  });
429
389
 
430
390
  it("returns warning when no C-Next files found", async () => {
@@ -432,7 +392,7 @@ describe("Transpiler", () => {
432
392
 
433
393
  const transpiler = new Transpiler(
434
394
  {
435
- inputs: ["/project/empty"],
395
+ input: "/project/empty",
436
396
  noCache: true,
437
397
  },
438
398
  mockFs,
@@ -448,7 +408,7 @@ describe("Transpiler", () => {
448
408
 
449
409
  const transpiler = new Transpiler(
450
410
  {
451
- inputs: ["/project/src/invalid.cnx"],
411
+ input: "/project/src/invalid.cnx",
452
412
  noCache: true,
453
413
  },
454
414
  mockFs,
@@ -480,7 +440,7 @@ describe("Transpiler", () => {
480
440
  writeFileSync(testFile, "u32 getValue() { return 42; }");
481
441
 
482
442
  const transpiler = new Transpiler({
483
- inputs: [testFile],
443
+ input: testFile,
484
444
  outDir: testDir,
485
445
  noCache: true,
486
446
  });
@@ -497,7 +457,7 @@ describe("Transpiler", () => {
497
457
  writeFileSync(testFile, "void foo( { }");
498
458
 
499
459
  const transpiler = new Transpiler({
500
- inputs: [testFile],
460
+ input: testFile,
501
461
  noCache: true,
502
462
  });
503
463
 
@@ -513,7 +473,7 @@ describe("Transpiler", () => {
513
473
  writeFileSync(testFile, "void foo() {\n @@@invalid\n}");
514
474
 
515
475
  const transpiler = new Transpiler({
516
- inputs: [testFile],
476
+ input: testFile,
517
477
  noCache: true,
518
478
  });
519
479
 
@@ -530,7 +490,7 @@ describe("Transpiler", () => {
530
490
  writeFileSync(testFile, "@@@ $$$ %%%");
531
491
 
532
492
  const transpiler = new Transpiler({
533
- inputs: [testFile],
493
+ input: testFile,
534
494
  noCache: true,
535
495
  });
536
496
 
@@ -546,7 +506,7 @@ describe("Transpiler", () => {
546
506
  writeFileSync(testFile, "void main() { }");
547
507
 
548
508
  const transpiler = new Transpiler({
549
- inputs: [testFile],
509
+ input: testFile,
550
510
  outDir: outputDir,
551
511
  noCache: true,
552
512
  });
@@ -569,7 +529,7 @@ describe("Transpiler", () => {
569
529
  );
570
530
 
571
531
  const transpiler = new Transpiler({
572
- inputs: [testFile],
532
+ input: testFile,
573
533
  outDir: testDir,
574
534
  noCache: true,
575
535
  });
@@ -592,7 +552,7 @@ describe("Transpiler", () => {
592
552
  });
593
553
 
594
554
  it("transpile({ kind: 'source' }) returns ITranspilerResult with files[]", async () => {
595
- const transpiler = new Transpiler({ inputs: [], noCache: true }, mockFs);
555
+ const transpiler = new Transpiler({ input: "", noCache: true }, mockFs);
596
556
 
597
557
  const result = await transpiler.transpile({
598
558
  kind: "source",
@@ -604,7 +564,7 @@ describe("Transpiler", () => {
604
564
  });
605
565
 
606
566
  it("transpile({ kind: 'source' }) returns header in files[0].headerCode", async () => {
607
- const transpiler = new Transpiler({ inputs: [], noCache: true }, mockFs);
567
+ const transpiler = new Transpiler({ input: "", noCache: true }, mockFs);
608
568
 
609
569
  const result = await transpiler.transpile({
610
570
  kind: "source",
@@ -619,7 +579,7 @@ describe("Transpiler", () => {
619
579
  });
620
580
 
621
581
  it("transpile({ kind: 'source' }) returns errors in result", async () => {
622
- const transpiler = new Transpiler({ inputs: [], noCache: true }, mockFs);
582
+ const transpiler = new Transpiler({ input: "", noCache: true }, mockFs);
623
583
 
624
584
  const result = await transpiler.transpile({
625
585
  kind: "source",
@@ -42,7 +42,7 @@ describe("Transpiler.determineProjectRoot", () => {
42
42
  describe("no inputs", () => {
43
43
  it("returns undefined when inputs array is empty", () => {
44
44
  const transpiler = new Transpiler({
45
- inputs: [],
45
+ input: "",
46
46
  });
47
47
 
48
48
  expect(getProjectRoot(transpiler)).toBeUndefined();
@@ -50,7 +50,7 @@ describe("Transpiler.determineProjectRoot", () => {
50
50
 
51
51
  it("disables caching when inputs array is empty", () => {
52
52
  const transpiler = new Transpiler({
53
- inputs: [],
53
+ input: "",
54
54
  });
55
55
 
56
56
  expect(hasCacheManager(transpiler)).toBe(false);
@@ -66,7 +66,7 @@ describe("Transpiler.determineProjectRoot", () => {
66
66
  writeFileSync(join(projectDir, "main.cnx"), "void main() {}");
67
67
 
68
68
  const transpiler = new Transpiler({
69
- inputs: [join(projectDir, "main.cnx")],
69
+ input: join(projectDir, "main.cnx"),
70
70
  noCache: true, // Disable cache to avoid side effects
71
71
  });
72
72
 
@@ -82,7 +82,7 @@ describe("Transpiler.determineProjectRoot", () => {
82
82
  writeFileSync(join(srcDir, "main.cnx"), "void main() {}");
83
83
 
84
84
  const transpiler = new Transpiler({
85
- inputs: [join(srcDir, "main.cnx")],
85
+ input: join(srcDir, "main.cnx"),
86
86
  noCache: true,
87
87
  });
88
88
 
@@ -97,7 +97,7 @@ describe("Transpiler.determineProjectRoot", () => {
97
97
  writeFileSync(join(srcDir, "main.cnx"), "void main() {}");
98
98
 
99
99
  const transpiler = new Transpiler({
100
- inputs: [join(srcDir, "main.cnx")],
100
+ input: join(srcDir, "main.cnx"),
101
101
  noCache: true,
102
102
  });
103
103
 
@@ -112,7 +112,7 @@ describe("Transpiler.determineProjectRoot", () => {
112
112
  writeFileSync(join(srcDir, "main.cnx"), "void main() {}");
113
113
 
114
114
  const transpiler = new Transpiler({
115
- inputs: [join(srcDir, "main.cnx")],
115
+ input: join(srcDir, "main.cnx"),
116
116
  noCache: true,
117
117
  });
118
118
 
@@ -127,7 +127,7 @@ describe("Transpiler.determineProjectRoot", () => {
127
127
  writeFileSync(join(srcDir, "main.cnx"), "void main() {}");
128
128
 
129
129
  const transpiler = new Transpiler({
130
- inputs: [join(srcDir, "main.cnx")],
130
+ input: join(srcDir, "main.cnx"),
131
131
  noCache: true,
132
132
  });
133
133
 
@@ -142,7 +142,7 @@ describe("Transpiler.determineProjectRoot", () => {
142
142
  writeFileSync(join(deepDir, "helper.cnx"), "void helper() {}");
143
143
 
144
144
  const transpiler = new Transpiler({
145
- inputs: [join(deepDir, "helper.cnx")],
145
+ input: join(deepDir, "helper.cnx"),
146
146
  noCache: true,
147
147
  });
148
148
 
@@ -159,7 +159,7 @@ describe("Transpiler.determineProjectRoot", () => {
159
159
  // Note: newfile.cnx does NOT exist
160
160
 
161
161
  const transpiler = new Transpiler({
162
- inputs: [join(srcDir, "newfile.cnx")],
162
+ input: join(srcDir, "newfile.cnx"),
163
163
  noCache: true,
164
164
  });
165
165
 
@@ -173,7 +173,7 @@ describe("Transpiler.determineProjectRoot", () => {
173
173
  // Note: the nested directories and file do NOT exist
174
174
 
175
175
  const transpiler = new Transpiler({
176
- inputs: [join(projectDir, "src", "deep", "nested", "file.cnx")],
176
+ input: join(projectDir, "src", "deep", "nested", "file.cnx"),
177
177
  noCache: true,
178
178
  });
179
179
 
@@ -181,28 +181,30 @@ describe("Transpiler.determineProjectRoot", () => {
181
181
  });
182
182
  });
183
183
 
184
- describe("input is a directory", () => {
185
- it("uses directory directly when input is a directory with marker", () => {
184
+ describe("input file in project root", () => {
185
+ it("finds marker in same directory as input file", () => {
186
186
  const projectDir = join(testDir, "project");
187
187
  mkdirSync(projectDir, { recursive: true });
188
188
  writeFileSync(join(projectDir, "cnext.config.json"), "{}");
189
+ writeFileSync(join(projectDir, "main.cnx"), "void main() {}");
189
190
 
190
191
  const transpiler = new Transpiler({
191
- inputs: [projectDir],
192
+ input: join(projectDir, "main.cnx"),
192
193
  noCache: true,
193
194
  });
194
195
 
195
196
  expect(getProjectRoot(transpiler)).toBe(projectDir);
196
197
  });
197
198
 
198
- it("finds marker in parent when input is a subdirectory", () => {
199
+ it("finds marker in parent when input file is in subdirectory", () => {
199
200
  const projectDir = join(testDir, "project");
200
201
  const srcDir = join(projectDir, "src");
201
202
  mkdirSync(srcDir, { recursive: true });
202
203
  writeFileSync(join(projectDir, "cnext.config.json"), "{}");
204
+ writeFileSync(join(srcDir, "main.cnx"), "void main() {}");
203
205
 
204
206
  const transpiler = new Transpiler({
205
- inputs: [srcDir],
207
+ input: join(srcDir, "main.cnx"),
206
208
  noCache: true,
207
209
  });
208
210
 
@@ -220,7 +222,7 @@ describe("Transpiler.determineProjectRoot", () => {
220
222
  writeFileSync(join(projectDir, "main.cnx"), "void main() {}");
221
223
 
222
224
  const transpiler = new Transpiler({
223
- inputs: [join(projectDir, "main.cnx")],
225
+ input: join(projectDir, "main.cnx"),
224
226
  noCache: true,
225
227
  });
226
228
 
@@ -238,7 +240,7 @@ describe("Transpiler.determineProjectRoot", () => {
238
240
  writeFileSync(join(innerDir, "main.cnx"), "void main() {}");
239
241
 
240
242
  const transpiler = new Transpiler({
241
- inputs: [join(innerDir, "main.cnx")],
243
+ input: join(innerDir, "main.cnx"),
242
244
  noCache: true,
243
245
  });
244
246
 
@@ -257,7 +259,7 @@ describe("Transpiler.determineProjectRoot", () => {
257
259
  writeFileSync(join(isolatedDir, "orphan.cnx"), "void orphan() {}");
258
260
 
259
261
  const transpiler = new Transpiler({
260
- inputs: [join(isolatedDir, "orphan.cnx")],
262
+ input: join(isolatedDir, "orphan.cnx"),
261
263
  noCache: true,
262
264
  });
263
265
 
@@ -280,7 +282,7 @@ describe("Transpiler.determineProjectRoot", () => {
280
282
  // This is hard to test in practice because we're inside the c-next repo
281
283
  // We can at least verify the noCache flag works
282
284
  const transpiler = new Transpiler({
283
- inputs: [join(testDir, "some.cnx")],
285
+ input: join(testDir, "some.cnx"),
284
286
  noCache: true,
285
287
  });
286
288
 
@@ -296,7 +298,7 @@ describe("Transpiler.determineProjectRoot", () => {
296
298
  writeFileSync(join(projectDir, "main.cnx"), "void main() {}");
297
299
 
298
300
  const transpiler = new Transpiler({
299
- inputs: [join(projectDir, "main.cnx")],
301
+ input: join(projectDir, "main.cnx"),
300
302
  noCache: true,
301
303
  });
302
304
 
@@ -313,7 +315,7 @@ describe("Transpiler.determineProjectRoot", () => {
313
315
  writeFileSync(join(projectDir, "main.cnx"), "void main() {}");
314
316
 
315
317
  const transpiler = new Transpiler({
316
- inputs: [join(projectDir, "main.cnx")],
318
+ input: join(projectDir, "main.cnx"),
317
319
  noCache: false,
318
320
  });
319
321
 
@@ -333,7 +335,7 @@ describe("Transpiler.determineProjectRoot", () => {
333
335
  const relativePath = join("test-project-root-tmp", "project", "main.cnx");
334
336
 
335
337
  const transpiler = new Transpiler({
336
- inputs: [relativePath],
338
+ input: relativePath,
337
339
  noCache: true,
338
340
  });
339
341
 
@@ -352,7 +354,7 @@ describe("Transpiler.determineProjectRoot", () => {
352
354
  writeFileSync(join(srcDir, "main.cnx"), "void main() {}");
353
355
 
354
356
  const transpiler = new Transpiler({
355
- inputs: [join(srcDir, "main.cnx")],
357
+ input: join(srcDir, "main.cnx"),
356
358
  noCache: true,
357
359
  });
358
360
 
@@ -367,7 +369,7 @@ describe("Transpiler.determineProjectRoot", () => {
367
369
  writeFileSync(join(projectDir, "main.cnx"), "void main() {}");
368
370
 
369
371
  const transpiler = new Transpiler({
370
- inputs: [join(projectDir, "main.cnx")],
372
+ input: join(projectDir, "main.cnx"),
371
373
  noCache: true,
372
374
  });
373
375
 
@@ -385,7 +387,7 @@ describe("Transpiler.determineProjectRoot", () => {
385
387
  writeFileSync(join(project2, "b.cnx"), "void b() {}");
386
388
 
387
389
  const transpiler = new Transpiler({
388
- inputs: [join(project1, "a.cnx"), join(project2, "b.cnx")],
390
+ input: join(project1, "a.cnx"),
389
391
  noCache: true,
390
392
  });
391
393
 
@@ -400,7 +402,7 @@ describe("Transpiler.determineProjectRoot", () => {
400
402
  writeFileSync(join(projectDir, "main.cnx"), "void main() {}");
401
403
 
402
404
  const transpiler = new Transpiler({
403
- inputs: [join(projectDir, "main.cnx")],
405
+ input: join(projectDir, "main.cnx"),
404
406
  noCache: true,
405
407
  });
406
408
 
@@ -417,7 +419,7 @@ describe("Transpiler.determineProjectRoot", () => {
417
419
  writeFileSync(join(srcDir, "main.cnx"), "void main() {}");
418
420
 
419
421
  const transpiler = new Transpiler({
420
- inputs: [join(srcDir, "main.cnx")],
422
+ input: join(srcDir, "main.cnx"),
421
423
  noCache: false,
422
424
  });
423
425
 
@@ -436,7 +438,7 @@ describe("Transpiler.determineProjectRoot", () => {
436
438
  writeFileSync(join(projectDir, "main.cnx"), "void main() {}");
437
439
 
438
440
  const transpiler = new Transpiler({
439
- inputs: [join(projectDir, "main.cnx")],
441
+ input: join(projectDir, "main.cnx"),
440
442
  noCache: true,
441
443
  });
442
444
 
@@ -30,7 +30,7 @@ describe("needsConditionalPreprocessing", () => {
30
30
 
31
31
  beforeEach(() => {
32
32
  transpiler = new TestableTranspiler(
33
- { inputs: [], noCache: true },
33
+ { input: "", noCache: true },
34
34
  new MockFileSystem(),
35
35
  );
36
36
  });
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Detects C-Next generation markers in header files.
3
+ *
4
+ * Used by the C/C++ entry point feature to discover which headers
5
+ * were generated from .cnx source files.
6
+ */
7
+ class CNextMarkerDetector {
8
+ /**
9
+ * Regex to extract source path from generation marker.
10
+ * Matches: "Generated by C-Next Transpiler from: <path>"
11
+ */
12
+ private static readonly SOURCE_PATH_REGEX =
13
+ /Generated by C-Next Transpiler from:\s*(\S+)/;
14
+
15
+ /**
16
+ * Check if content contains any C-Next generation marker.
17
+ */
18
+ static isCNextGenerated(content: string): boolean {
19
+ return content.includes("Generated by C-Next Transpiler");
20
+ }
21
+
22
+ /**
23
+ * Extract the source .cnx path from a C-Next generation marker.
24
+ *
25
+ * @param content - File content (typically first 500 chars is sufficient)
26
+ * @returns The source path (e.g., "led.cnx") or null if no marker found
27
+ */
28
+ static extractSourcePath(content: string): string | null {
29
+ const match = this.SOURCE_PATH_REGEX.exec(content);
30
+ return match ? match[1] : null;
31
+ }
32
+ }
33
+
34
+ export default CNextMarkerDetector;