@kithinji/pod 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 (98) hide show
  1. package/build.js +22 -0
  2. package/dist/main.js +4464 -0
  3. package/dist/main.js.map +7 -0
  4. package/dist/types/add/component/component.d.ts +5 -0
  5. package/dist/types/add/component/component.d.ts.map +1 -0
  6. package/dist/types/add/component/index.d.ts +2 -0
  7. package/dist/types/add/component/index.d.ts.map +1 -0
  8. package/dist/types/add/index.d.ts +4 -0
  9. package/dist/types/add/index.d.ts.map +1 -0
  10. package/dist/types/add/module/index.d.ts +2 -0
  11. package/dist/types/add/module/index.d.ts.map +1 -0
  12. package/dist/types/add/module/module.d.ts +3 -0
  13. package/dist/types/add/module/module.d.ts.map +1 -0
  14. package/dist/types/add/new/index.d.ts +2 -0
  15. package/dist/types/add/new/index.d.ts.map +1 -0
  16. package/dist/types/config/config.d.ts +18 -0
  17. package/dist/types/config/config.d.ts.map +1 -0
  18. package/dist/types/config/index.d.ts +2 -0
  19. package/dist/types/config/index.d.ts.map +1 -0
  20. package/dist/types/dev/index.d.ts +2 -0
  21. package/dist/types/dev/index.d.ts.map +1 -0
  22. package/dist/types/dev/project.d.ts +9 -0
  23. package/dist/types/dev/project.d.ts.map +1 -0
  24. package/dist/types/dev/server.d.ts +2 -0
  25. package/dist/types/dev/server.d.ts.map +1 -0
  26. package/dist/types/docker/docker.d.ts +2 -0
  27. package/dist/types/docker/docker.d.ts.map +1 -0
  28. package/dist/types/docker/index.d.ts +2 -0
  29. package/dist/types/docker/index.d.ts.map +1 -0
  30. package/dist/types/macros/expand_macros.d.ts +48 -0
  31. package/dist/types/macros/expand_macros.d.ts.map +1 -0
  32. package/dist/types/macros/index.d.ts +3 -0
  33. package/dist/types/macros/index.d.ts.map +1 -0
  34. package/dist/types/macros/macro_executer.d.ts +12 -0
  35. package/dist/types/macros/macro_executer.d.ts.map +1 -0
  36. package/dist/types/main.d.ts +13 -0
  37. package/dist/types/main.d.ts.map +1 -0
  38. package/dist/types/plugins/analyzers/graph.d.ts +25 -0
  39. package/dist/types/plugins/analyzers/graph.d.ts.map +1 -0
  40. package/dist/types/plugins/css/index.d.ts +7 -0
  41. package/dist/types/plugins/css/index.d.ts.map +1 -0
  42. package/dist/types/plugins/generators/generate_controller.d.ts +2 -0
  43. package/dist/types/plugins/generators/generate_controller.d.ts.map +1 -0
  44. package/dist/types/plugins/generators/generate_rsc.d.ts +2 -0
  45. package/dist/types/plugins/generators/generate_rsc.d.ts.map +1 -0
  46. package/dist/types/plugins/generators/generate_server_component.d.ts +2 -0
  47. package/dist/types/plugins/generators/generate_server_component.d.ts.map +1 -0
  48. package/dist/types/plugins/generators/tsx_server_stub.d.ts +2 -0
  49. package/dist/types/plugins/generators/tsx_server_stub.d.ts.map +1 -0
  50. package/dist/types/plugins/index.d.ts +4 -0
  51. package/dist/types/plugins/index.d.ts.map +1 -0
  52. package/dist/types/plugins/my.d.ts +10 -0
  53. package/dist/types/plugins/my.d.ts.map +1 -0
  54. package/dist/types/plugins/transformers/j2d.d.ts +11 -0
  55. package/dist/types/plugins/transformers/j2d.d.ts.map +1 -0
  56. package/dist/types/store/index.d.ts +2 -0
  57. package/dist/types/store/index.d.ts.map +1 -0
  58. package/dist/types/store/store.d.ts +14 -0
  59. package/dist/types/store/store.d.ts.map +1 -0
  60. package/dist/types/utils/cases.d.ts +4 -0
  61. package/dist/types/utils/cases.d.ts.map +1 -0
  62. package/dist/types/utils/create.d.ts +12 -0
  63. package/dist/types/utils/create.d.ts.map +1 -0
  64. package/dist/types/utils/index.d.ts +3 -0
  65. package/dist/types/utils/index.d.ts.map +1 -0
  66. package/package.json +44 -0
  67. package/src/add/component/component.ts +496 -0
  68. package/src/add/component/index.ts +1 -0
  69. package/src/add/index.ts +3 -0
  70. package/src/add/module/index.ts +1 -0
  71. package/src/add/module/module.ts +521 -0
  72. package/src/add/new/index.ts +135 -0
  73. package/src/config/config.ts +141 -0
  74. package/src/config/index.ts +1 -0
  75. package/src/dev/index.ts +1 -0
  76. package/src/dev/project.ts +45 -0
  77. package/src/dev/server.ts +190 -0
  78. package/src/docker/docker.ts +452 -0
  79. package/src/docker/index.ts +1 -0
  80. package/src/macros/expand_macros.ts +791 -0
  81. package/src/macros/index.ts +2 -0
  82. package/src/macros/macro_executer.ts +189 -0
  83. package/src/main.ts +95 -0
  84. package/src/plugins/analyzers/graph.ts +291 -0
  85. package/src/plugins/css/index.ts +25 -0
  86. package/src/plugins/generators/generate_controller.ts +308 -0
  87. package/src/plugins/generators/generate_rsc.ts +274 -0
  88. package/src/plugins/generators/generate_server_component.ts +279 -0
  89. package/src/plugins/generators/tsx_server_stub.ts +295 -0
  90. package/src/plugins/index.ts +3 -0
  91. package/src/plugins/my.ts +274 -0
  92. package/src/plugins/transformers/j2d.ts +1014 -0
  93. package/src/store/index.ts +1 -0
  94. package/src/store/store.ts +44 -0
  95. package/src/utils/cases.ts +15 -0
  96. package/src/utils/create.ts +26 -0
  97. package/src/utils/index.ts +2 -0
  98. package/tsconfig.json +27 -0
@@ -0,0 +1,791 @@
1
+ import ts from "typescript";
2
+ import path from "path";
3
+ import Module from "node:module";
4
+ import { macroExecuter } from "./macro_executer";
5
+ import { Store } from "@/store";
6
+
7
+ export interface MacroContext {
8
+ node: ts.CallExpression;
9
+ sourceFile: ts.SourceFile;
10
+ ts: typeof ts;
11
+ factory: ts.NodeFactory;
12
+ store: Store;
13
+ graph: MacroDependencyGraph;
14
+ get program(): ts.Program;
15
+ get checker(): ts.TypeChecker;
16
+ error(msg: string): never;
17
+ resolveNodeValue(node: ts.Node): any;
18
+ resolveIdentifier(identifier: ts.Identifier): ts.Node;
19
+ }
20
+
21
+ interface MacroNode {
22
+ key: string;
23
+ variableName: string;
24
+ node: ts.CallExpression;
25
+ sourceFile: ts.SourceFile;
26
+ filePath: string;
27
+ dependencies: Set<string>;
28
+ astResult?: ts.Node;
29
+ computed: boolean;
30
+ }
31
+
32
+ class MacroDependencyGraph {
33
+ private nodes = new Map<string, MacroNode>();
34
+ private projectRoot: string;
35
+ private typeChecker?: ts.TypeChecker;
36
+
37
+ constructor(projectRoot: string) {
38
+ this.projectRoot = projectRoot;
39
+ }
40
+
41
+ setTypeChecker(checker: ts.TypeChecker) {
42
+ this.typeChecker = checker;
43
+ }
44
+
45
+ createKey(sourceFile: ts.SourceFile, variableName: string): string {
46
+ const relativePath = path.relative(this.projectRoot, sourceFile.fileName);
47
+ const normalized = relativePath.replace(/\\/g, "/");
48
+ return `${normalized}:${variableName}`;
49
+ }
50
+
51
+ addNode(
52
+ key: string,
53
+ variableName: string,
54
+ node: ts.CallExpression,
55
+ sourceFile: ts.SourceFile
56
+ ) {
57
+ if (!this.nodes.has(key)) {
58
+ this.nodes.set(key, {
59
+ key,
60
+ variableName,
61
+ node,
62
+ sourceFile,
63
+ filePath: sourceFile.fileName,
64
+ dependencies: new Set(),
65
+ astResult: undefined,
66
+ computed: false,
67
+ });
68
+ }
69
+ }
70
+
71
+ getNode(key: string): MacroNode | undefined {
72
+ return this.nodes.get(key);
73
+ }
74
+
75
+ addDependency(fromKey: string, toKey: string) {
76
+ const node = this.nodes.get(fromKey);
77
+ if (node) {
78
+ node.dependencies.add(toKey);
79
+ }
80
+ }
81
+
82
+ setResult(key: string, astResult: ts.Node) {
83
+ const node = this.nodes.get(key);
84
+ if (node) {
85
+ node.computed = true;
86
+ node.astResult = astResult;
87
+ }
88
+ }
89
+
90
+ getResult(key: string): ts.Node | undefined {
91
+ return this.nodes.get(key)?.astResult;
92
+ }
93
+
94
+ isComputed(key: string): boolean {
95
+ return this.nodes.get(key)?.computed ?? false;
96
+ }
97
+
98
+ topologicalSort(): string[] {
99
+ const visited = new Set<string>();
100
+ const inProgress = new Set<string>();
101
+ const sorted: string[] = [];
102
+
103
+ const visit = (key: string, path: string[] = []) => {
104
+ if (visited.has(key)) return;
105
+
106
+ if (inProgress.has(key)) {
107
+ const cycle = [...path, key].join(" -> ");
108
+ throw new Error(`Circular macro dependency detected: ${cycle}`);
109
+ }
110
+
111
+ const node = this.nodes.get(key);
112
+ if (!node) return;
113
+
114
+ inProgress.add(key);
115
+
116
+ for (const depKey of node.dependencies) {
117
+ visit(depKey, [...path, key]);
118
+ }
119
+
120
+ inProgress.delete(key);
121
+ visited.add(key);
122
+ sorted.push(key);
123
+ };
124
+
125
+ for (const key of this.nodes.keys()) {
126
+ visit(key);
127
+ }
128
+
129
+ return sorted;
130
+ }
131
+
132
+ clear() {
133
+ this.nodes.clear();
134
+ }
135
+
136
+ getNodesForFile(filePath: string): MacroNode[] {
137
+ return Array.from(this.nodes.values()).filter(
138
+ (node) => node.filePath === filePath
139
+ );
140
+ }
141
+ }
142
+
143
+ let globalGraph: MacroDependencyGraph | null = null;
144
+
145
+ export function getGlobalMacroGraph(projectRoot: string): MacroDependencyGraph {
146
+ if (!globalGraph) {
147
+ globalGraph = new MacroDependencyGraph(projectRoot);
148
+ }
149
+ return globalGraph;
150
+ }
151
+
152
+ export function resetGlobalMacroGraph() {
153
+ globalGraph = null;
154
+ }
155
+
156
+ function resolveImportSpecifier(
157
+ importPath: string,
158
+ fromFile: string,
159
+ compilerOptions: ts.CompilerOptions
160
+ ): string | undefined {
161
+ const resolved = ts.resolveModuleName(
162
+ importPath,
163
+ fromFile,
164
+ compilerOptions,
165
+ ts.sys
166
+ );
167
+
168
+ if (resolved.resolvedModule?.resolvedFileName) {
169
+ return resolved.resolvedModule.resolvedFileName;
170
+ }
171
+
172
+ try {
173
+ const requireFromFile = Module.createRequire(fromFile);
174
+ return requireFromFile.resolve(importPath);
175
+ } catch (e) {
176
+ return undefined;
177
+ }
178
+ }
179
+
180
+ function resolveImportFullPath(
181
+ symbolName: string,
182
+ sourceFile: ts.SourceFile,
183
+ compilerOptions: ts.CompilerOptions
184
+ ): { importPath: string; resolvedPath: string | undefined } | undefined {
185
+ let importPath: string | undefined;
186
+
187
+ sourceFile.forEachChild((node) => {
188
+ if (!ts.isImportDeclaration(node) || !node.importClause) return;
189
+
190
+ const { namedBindings, name } = node.importClause;
191
+
192
+ if (name && name.text === symbolName) {
193
+ importPath = (node.moduleSpecifier as ts.StringLiteral).text;
194
+ }
195
+
196
+ if (namedBindings && ts.isNamedImports(namedBindings)) {
197
+ for (const specifier of namedBindings.elements) {
198
+ const importedName = specifier.name.text;
199
+ if (importedName === symbolName) {
200
+ importPath = (node.moduleSpecifier as ts.StringLiteral).text;
201
+ }
202
+ }
203
+ }
204
+
205
+ if (namedBindings && ts.isNamespaceImport(namedBindings)) {
206
+ if (symbolName.startsWith(namedBindings.name.text + ".")) {
207
+ importPath = (node.moduleSpecifier as ts.StringLiteral).text;
208
+ }
209
+ }
210
+ });
211
+
212
+ if (!importPath) return undefined;
213
+
214
+ const resolvedPath = resolveImportSpecifier(
215
+ importPath,
216
+ sourceFile.fileName,
217
+ compilerOptions
218
+ );
219
+
220
+ return {
221
+ importPath: importPath.startsWith(".") ? resolvedPath! : importPath,
222
+ resolvedPath,
223
+ };
224
+ }
225
+
226
+ function isNpmPackage(importPath: string): boolean {
227
+ return (
228
+ !importPath.startsWith(".") &&
229
+ !importPath.startsWith("/") &&
230
+ !path.isAbsolute(importPath)
231
+ );
232
+ }
233
+
234
+ function findVariableDeclarationInFile(
235
+ variableName: string,
236
+ sourceFile: ts.SourceFile
237
+ ): ts.VariableDeclaration | undefined {
238
+ let found: ts.VariableDeclaration | undefined;
239
+
240
+ function visit(node: ts.Node) {
241
+ if (found) return;
242
+
243
+ if (ts.isVariableDeclaration(node)) {
244
+ if (ts.isIdentifier(node.name) && node.name.text === variableName) {
245
+ found = node;
246
+ return;
247
+ }
248
+ }
249
+
250
+ ts.forEachChild(node, visit);
251
+ }
252
+
253
+ visit(sourceFile);
254
+ return found;
255
+ }
256
+
257
+ export function extractValueFromNode(node: ts.Node): any | undefined {
258
+ if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) {
259
+ return node.text;
260
+ }
261
+ if (ts.isNumericLiteral(node)) return Number(node.text);
262
+ if (node.kind === ts.SyntaxKind.TrueKeyword) return true;
263
+ if (node.kind === ts.SyntaxKind.FalseKeyword) return false;
264
+ if (node.kind === ts.SyntaxKind.NullKeyword) return null;
265
+ if (node.kind === ts.SyntaxKind.UndefinedKeyword) return undefined;
266
+
267
+ if (ts.isTemplateExpression(node)) {
268
+ let result = node.head.text;
269
+ for (const span of node.templateSpans) {
270
+ const exprValue = extractValueFromNode(span.expression);
271
+ result += String(exprValue) + span.literal.text;
272
+ }
273
+ return result;
274
+ }
275
+
276
+ if (ts.isObjectLiteralExpression(node)) {
277
+ const obj: any = {};
278
+ for (const prop of node.properties) {
279
+ if (ts.isPropertyAssignment(prop)) {
280
+ const key = ts.isIdentifier(prop.name)
281
+ ? prop.name.text
282
+ : ts.isStringLiteral(prop.name)
283
+ ? prop.name.text
284
+ : ts.isNumericLiteral(prop.name)
285
+ ? prop.name.text
286
+ : ts.isComputedPropertyName(prop.name)
287
+ ? extractValueFromNode(prop.name.expression)
288
+ : undefined;
289
+
290
+ if (key !== undefined) {
291
+ obj[key] = extractValueFromNode(prop.initializer);
292
+ }
293
+ } else if (ts.isShorthandPropertyAssignment(prop)) {
294
+ obj[prop.name.text] = prop.name.text;
295
+ } else if (ts.isSpreadAssignment(prop)) {
296
+ const spread = extractValueFromNode(prop.expression);
297
+ if (typeof spread === "object" && spread !== null) {
298
+ Object.assign(obj, spread);
299
+ }
300
+ }
301
+ }
302
+ return obj;
303
+ }
304
+
305
+ if (ts.isArrayLiteralExpression(node)) {
306
+ return node.elements
307
+ .map((el) => {
308
+ if (ts.isSpreadElement(el)) {
309
+ const spread = extractValueFromNode(el.expression);
310
+ return Array.isArray(spread) ? spread : [spread];
311
+ }
312
+ return extractValueFromNode(el);
313
+ })
314
+ .flat();
315
+ }
316
+
317
+ if (ts.isPrefixUnaryExpression(node)) {
318
+ const operand = extractValueFromNode(node.operand);
319
+ switch (node.operator) {
320
+ case ts.SyntaxKind.MinusToken:
321
+ return -operand;
322
+ case ts.SyntaxKind.PlusToken:
323
+ return +operand;
324
+ case ts.SyntaxKind.ExclamationToken:
325
+ return !operand;
326
+ case ts.SyntaxKind.TildeToken:
327
+ return ~operand;
328
+ }
329
+ }
330
+
331
+ if (ts.isBinaryExpression(node)) {
332
+ const left = extractValueFromNode(node.left);
333
+ const right = extractValueFromNode(node.right);
334
+
335
+ switch (node.operatorToken.kind) {
336
+ case ts.SyntaxKind.PlusToken:
337
+ return left + right;
338
+ case ts.SyntaxKind.MinusToken:
339
+ return left - right;
340
+ case ts.SyntaxKind.AsteriskToken:
341
+ return left * right;
342
+ case ts.SyntaxKind.SlashToken:
343
+ return left / right;
344
+ case ts.SyntaxKind.PercentToken:
345
+ return left % right;
346
+ case ts.SyntaxKind.AsteriskAsteriskToken:
347
+ return left ** right;
348
+ case ts.SyntaxKind.EqualsEqualsToken:
349
+ return left == right;
350
+ case ts.SyntaxKind.EqualsEqualsEqualsToken:
351
+ return left === right;
352
+ case ts.SyntaxKind.ExclamationEqualsToken:
353
+ return left != right;
354
+ case ts.SyntaxKind.ExclamationEqualsEqualsToken:
355
+ return left !== right;
356
+ case ts.SyntaxKind.LessThanToken:
357
+ return left < right;
358
+ case ts.SyntaxKind.LessThanEqualsToken:
359
+ return left <= right;
360
+ case ts.SyntaxKind.GreaterThanToken:
361
+ return left > right;
362
+ case ts.SyntaxKind.GreaterThanEqualsToken:
363
+ return left >= right;
364
+ case ts.SyntaxKind.AmpersandAmpersandToken:
365
+ return left && right;
366
+ case ts.SyntaxKind.BarBarToken:
367
+ return left || right;
368
+ case ts.SyntaxKind.QuestionQuestionToken:
369
+ return left ?? right;
370
+ }
371
+ }
372
+
373
+ if (ts.isParenthesizedExpression(node)) {
374
+ return extractValueFromNode(node.expression);
375
+ }
376
+
377
+ if (ts.isConditionalExpression(node)) {
378
+ const condition = extractValueFromNode(node.condition);
379
+ return condition
380
+ ? extractValueFromNode(node.whenTrue)
381
+ : extractValueFromNode(node.whenFalse);
382
+ }
383
+
384
+ if (ts.isPropertyAccessExpression(node)) {
385
+ const obj = extractValueFromNode(node.expression);
386
+ const propName = node.name.text;
387
+ return obj?.[propName];
388
+ }
389
+
390
+ if (ts.isElementAccessExpression(node)) {
391
+ const obj = extractValueFromNode(node.expression);
392
+ const index = extractValueFromNode(node.argumentExpression);
393
+ return obj?.[index];
394
+ }
395
+
396
+ return undefined;
397
+ }
398
+
399
+ function createNodeResolver(
400
+ graph: MacroDependencyGraph,
401
+ currentFileKey: string,
402
+ sourceFile: ts.SourceFile,
403
+ compilerOptions: ts.CompilerOptions
404
+ ) {
405
+ const trackedDependencies: string[] = [];
406
+
407
+ function resolveIdentifierToNode(identifier: ts.Identifier): ts.Node {
408
+ const name = identifier.text;
409
+
410
+ // Look for local variable declaration
411
+ const declaration = findVariableDeclarationInFile(name, sourceFile);
412
+
413
+ if (declaration && declaration.initializer) {
414
+ const varStatement = declaration.parent.parent as ts.VariableStatement;
415
+ const isConst = varStatement.declarationList.flags & ts.NodeFlags.Const;
416
+
417
+ if (!isConst) {
418
+ throw new Error(
419
+ `Macro argument '${name}' must be a const variable. let/var are not allowed.`
420
+ );
421
+ }
422
+
423
+ // Check if it's a macro call
424
+ if (ts.isCallExpression(declaration.initializer)) {
425
+ const expr = declaration.initializer.expression;
426
+
427
+ if (ts.isIdentifier(expr) && expr.text.endsWith("$")) {
428
+ const depKey = graph.createKey(sourceFile, name);
429
+ trackedDependencies.push(depKey);
430
+
431
+ const result = graph.getResult(depKey);
432
+ if (result !== undefined) {
433
+ return result;
434
+ }
435
+
436
+ throw new Error(
437
+ `Macro dependency '${name}' has not been computed yet. This should not happen.`
438
+ );
439
+ }
440
+ }
441
+
442
+ return declaration.initializer;
443
+ }
444
+
445
+ // Try to resolve from imports
446
+ const resolved = resolveImportFullPath(name, sourceFile, compilerOptions);
447
+
448
+ if (resolved) {
449
+ if (isNpmPackage(resolved.importPath)) {
450
+ throw new Error(
451
+ `Cannot resolve identifier '${name}' from npm package '${resolved.importPath}'. ` +
452
+ `Macro arguments from npm packages must be constants that can be evaluated at compile time.`
453
+ );
454
+ }
455
+
456
+ if (!resolved.resolvedPath) {
457
+ throw new Error(
458
+ `Could not resolve import path: ${resolved.importPath}`
459
+ );
460
+ }
461
+
462
+ const importedSource = ts.sys.readFile(resolved.resolvedPath);
463
+ if (!importedSource) {
464
+ throw new Error(
465
+ `Could not read imported file: ${resolved.resolvedPath}`
466
+ );
467
+ }
468
+
469
+ const importedSourceFile = ts.createSourceFile(
470
+ resolved.resolvedPath,
471
+ importedSource,
472
+ ts.ScriptTarget.Latest,
473
+ true
474
+ );
475
+
476
+ const importedDecl = findVariableDeclarationInFile(
477
+ name,
478
+ importedSourceFile
479
+ );
480
+
481
+ if (importedDecl && importedDecl.initializer) {
482
+ // Check if it's a macro call
483
+ if (ts.isCallExpression(importedDecl.initializer)) {
484
+ const expr = importedDecl.initializer.expression;
485
+ if (ts.isIdentifier(expr) && expr.text.endsWith("$")) {
486
+ const depKey = graph.createKey(importedSourceFile, name);
487
+ trackedDependencies.push(depKey);
488
+
489
+ const result = graph.getResult(depKey);
490
+ if (result !== undefined) {
491
+ return result;
492
+ }
493
+
494
+ throw new Error(
495
+ `Cross-file macro dependency '${name}' from '${resolved.resolvedPath}' needs to be computed first.`
496
+ );
497
+ }
498
+ }
499
+
500
+ return importedDecl.initializer;
501
+ }
502
+ }
503
+
504
+ throw new Error(
505
+ `Could not resolve identifier '${name}'. Make sure it's a const variable or imported constant.`
506
+ );
507
+ }
508
+
509
+ function resolveNodeValue(node: ts.Node): any {
510
+ if (ts.isIdentifier(node)) {
511
+ const resolvedNode = resolveIdentifierToNode(node);
512
+ return extractValueFromNode(resolvedNode);
513
+ }
514
+ return extractValueFromNode(node);
515
+ }
516
+
517
+ return {
518
+ resolveIdentifierToNode,
519
+ resolveNodeValue,
520
+ getTrackedDependencies: () => trackedDependencies,
521
+ };
522
+ }
523
+
524
+ export async function expandMacros(
525
+ source: string,
526
+ filePath: string,
527
+ projectRoot: string = process.cwd()
528
+ ): Promise<string> {
529
+ if (!source.includes("$(") && !source.includes("$`")) {
530
+ return source;
531
+ }
532
+
533
+ const compilerOptions: ts.CompilerOptions = {
534
+ module: ts.ModuleKind.ESNext,
535
+ target: ts.ScriptTarget.Latest,
536
+ moduleResolution: ts.ModuleResolutionKind.NodeNext,
537
+ };
538
+
539
+ const sourceFile = ts.createSourceFile(
540
+ filePath,
541
+ source,
542
+ ts.ScriptTarget.Latest,
543
+ true,
544
+ filePath.endsWith(".tsx") ? ts.ScriptKind.TSX : ts.ScriptKind.TS
545
+ );
546
+
547
+ const graph = getGlobalMacroGraph(projectRoot);
548
+
549
+ const getProgram = () =>
550
+ ts.createProgram([filePath], {
551
+ target: ts.ScriptTarget.ESNext,
552
+ module: ts.ModuleKind.ESNext,
553
+ });
554
+
555
+ const getTypeChecker = () => getProgram()?.getTypeChecker();
556
+
557
+ // Discover all macro calls
558
+ function discoverMacros(node: ts.Node) {
559
+ if (ts.isVariableDeclaration(node) && node.initializer) {
560
+ if (
561
+ ts.isCallExpression(node.initializer) &&
562
+ ts.isIdentifier(node.initializer.expression) &&
563
+ node.initializer.expression.text.endsWith("$")
564
+ ) {
565
+ if (ts.isIdentifier(node.name)) {
566
+ const variableName = node.name.text;
567
+ const key = graph.createKey(sourceFile, variableName);
568
+ graph.addNode(key, variableName, node.initializer, sourceFile);
569
+ }
570
+ }
571
+ }
572
+ ts.forEachChild(node, discoverMacros);
573
+ }
574
+
575
+ discoverMacros(sourceFile);
576
+
577
+ // Build dependency graph
578
+ const fileNodes = graph.getNodesForFile(filePath);
579
+
580
+ for (const macroNode of fileNodes) {
581
+ if (graph.isComputed(macroNode.key)) continue;
582
+
583
+ const resolver = createNodeResolver(
584
+ graph,
585
+ macroNode.key,
586
+ macroNode.sourceFile,
587
+ compilerOptions
588
+ );
589
+
590
+ try {
591
+ // Walk through arguments to discover dependencies
592
+ for (const arg of macroNode.node.arguments) {
593
+ function visitForDeps(n: ts.Node) {
594
+ if (ts.isIdentifier(n)) {
595
+ try {
596
+ resolver.resolveIdentifierToNode(n);
597
+ } catch (e) {
598
+ // Ignore resolution errors during dependency discovery
599
+ }
600
+ }
601
+ ts.forEachChild(n, visitForDeps);
602
+ }
603
+ visitForDeps(arg);
604
+ }
605
+
606
+ const deps = resolver.getTrackedDependencies();
607
+ for (const dep of deps) {
608
+ graph.addDependency(macroNode.key, dep);
609
+ }
610
+ } catch (e) {
611
+ // Ignore errors during dependency discovery
612
+ }
613
+ }
614
+
615
+ // Execute macros in topological order
616
+ const sortedKeys = graph.topologicalSort();
617
+
618
+ for (const key of sortedKeys) {
619
+ const macroNode = graph.getNode(key);
620
+ if (!macroNode || graph.isComputed(key)) continue;
621
+
622
+ const node = macroNode.node;
623
+ const name = (node.expression as ts.Identifier).text;
624
+
625
+ const resolved = resolveImportFullPath(
626
+ name,
627
+ macroNode.sourceFile,
628
+ compilerOptions
629
+ );
630
+
631
+ if (!resolved) {
632
+ throw new Error(`Could not resolve macro import for '${name}'`);
633
+ }
634
+
635
+ const macro = macroExecuter().getMacro(resolved.importPath, name);
636
+
637
+ if (!macro) {
638
+ throw new Error(`Could not get macro '${name}' for key '${key}'`);
639
+ }
640
+
641
+ const resolver = createNodeResolver(
642
+ graph,
643
+ key,
644
+ macroNode.sourceFile,
645
+ compilerOptions
646
+ );
647
+
648
+ const macroContext: MacroContext = {
649
+ node,
650
+ sourceFile: macroNode.sourceFile,
651
+ ts,
652
+ store: Store.getInstance(),
653
+ factory: ts.factory,
654
+ graph,
655
+ get program() {
656
+ return getProgram();
657
+ },
658
+ get checker() {
659
+ return getTypeChecker();
660
+ },
661
+ error: (msg: string) => {
662
+ throw new Error(msg);
663
+ },
664
+ resolveNodeValue: resolver.resolveNodeValue,
665
+ resolveIdentifier: resolver.resolveIdentifierToNode,
666
+ };
667
+
668
+ try {
669
+ // Pass AST nodes directly to macro
670
+ const result = macro(...node.arguments, macroContext);
671
+
672
+ if (!result || typeof result !== "object" || !("kind" in result)) {
673
+ throw new Error(`Macro '${name}' must return a TypeScript AST node`);
674
+ }
675
+
676
+ graph.setResult(key, result);
677
+ } catch (e: any) {
678
+ console.error(`Macro '${name}' execution failed: ${e?.message ?? e}`);
679
+ throw e;
680
+ }
681
+ }
682
+
683
+ // Transform the AST
684
+ const transformer: ts.TransformerFactory<ts.SourceFile> = (context) => {
685
+ const visit = (node: ts.Node): ts.Node => {
686
+ if (ts.isVariableDeclaration(node) && node.initializer) {
687
+ if (
688
+ ts.isCallExpression(node.initializer) &&
689
+ ts.isIdentifier(node.initializer.expression) &&
690
+ node.initializer.expression.text.endsWith("$") &&
691
+ ts.isIdentifier(node.name)
692
+ ) {
693
+ const key = graph.createKey(sourceFile, node.name.text);
694
+ const macroNode = graph.getNode(key);
695
+
696
+ if (macroNode && graph.isComputed(key)) {
697
+ const result = graph.getResult(key)!;
698
+
699
+ return context.factory.updateVariableDeclaration(
700
+ node,
701
+ node.name,
702
+ node.exclamationToken,
703
+ node.type,
704
+ result as any
705
+ );
706
+ }
707
+ }
708
+ }
709
+
710
+ if (ts.isCallExpression(node) && ts.isIdentifier(node.expression)) {
711
+ const name = node.expression.text;
712
+
713
+ if (name.endsWith("$")) {
714
+ const resolved = resolveImportFullPath(
715
+ name,
716
+ sourceFile,
717
+ compilerOptions
718
+ );
719
+
720
+ if (resolved) {
721
+ const macro = macroExecuter().getMacro(resolved.importPath, name);
722
+
723
+ if (macro) {
724
+ const tempKey = `${graph.createKey(sourceFile, "__temp__")}:${
725
+ node.pos
726
+ }`;
727
+ const resolver = createNodeResolver(
728
+ graph,
729
+ tempKey,
730
+ sourceFile,
731
+ compilerOptions
732
+ );
733
+
734
+ const macroContext: MacroContext = {
735
+ node,
736
+ sourceFile,
737
+ ts,
738
+ graph,
739
+ store: Store.getInstance(),
740
+ factory: context.factory,
741
+ get program() {
742
+ return getProgram();
743
+ },
744
+ get checker() {
745
+ return getTypeChecker();
746
+ },
747
+ error: (msg: string) => {
748
+ throw new Error(msg);
749
+ },
750
+ resolveNodeValue: resolver.resolveNodeValue,
751
+ resolveIdentifier: resolver.resolveIdentifierToNode,
752
+ };
753
+
754
+ try {
755
+ // Pass AST nodes directly to macro
756
+ const result = macro(...node.arguments, macroContext);
757
+
758
+ if (
759
+ !result ||
760
+ typeof result !== "object" ||
761
+ !("kind" in result)
762
+ ) {
763
+ throw new Error(
764
+ `Macro '${name}' must return a TypeScript AST node`
765
+ );
766
+ }
767
+
768
+ return result;
769
+ } catch (e: any) {
770
+ console.log(
771
+ `Macro '${name}' execution failed: ${e?.message ?? e}`
772
+ );
773
+ return node;
774
+ }
775
+ }
776
+ }
777
+ }
778
+ }
779
+
780
+ return ts.visitEachChild(node, visit, context);
781
+ };
782
+
783
+ return (sf) => ts.visitNode(sf, visit) as ts.SourceFile;
784
+ };
785
+
786
+ const result = ts.transform(sourceFile, [transformer]);
787
+ const output = ts.createPrinter().printFile(result.transformed[0]);
788
+ result.dispose();
789
+
790
+ return output;
791
+ }