@tsonic/frontend 0.0.1

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 (145) hide show
  1. package/package.json +53 -0
  2. package/src/dependency-graph.ts +18 -0
  3. package/src/dotnet-metadata.ts +121 -0
  4. package/src/graph/builder.ts +81 -0
  5. package/src/graph/circular.ts +58 -0
  6. package/src/graph/extraction/exports.ts +55 -0
  7. package/src/graph/extraction/imports.ts +81 -0
  8. package/src/graph/extraction/index.ts +7 -0
  9. package/src/graph/extraction/orchestrator.ts +99 -0
  10. package/src/graph/extraction.ts +10 -0
  11. package/src/graph/helpers.ts +51 -0
  12. package/src/graph/index.ts +17 -0
  13. package/src/graph/types.ts +13 -0
  14. package/src/index.ts +80 -0
  15. package/src/ir/binding-resolution.test.ts +585 -0
  16. package/src/ir/builder/exports.ts +78 -0
  17. package/src/ir/builder/helpers.ts +27 -0
  18. package/src/ir/builder/imports.ts +153 -0
  19. package/src/ir/builder/index.ts +10 -0
  20. package/src/ir/builder/orchestrator.ts +178 -0
  21. package/src/ir/builder/statements.ts +55 -0
  22. package/src/ir/builder/types.ts +8 -0
  23. package/src/ir/builder/validation.ts +129 -0
  24. package/src/ir/builder.test.ts +581 -0
  25. package/src/ir/builder.ts +14 -0
  26. package/src/ir/converters/expressions/access.ts +99 -0
  27. package/src/ir/converters/expressions/calls.ts +137 -0
  28. package/src/ir/converters/expressions/collections.ts +84 -0
  29. package/src/ir/converters/expressions/functions.ts +62 -0
  30. package/src/ir/converters/expressions/helpers.ts +264 -0
  31. package/src/ir/converters/expressions/index.ts +43 -0
  32. package/src/ir/converters/expressions/literals.ts +22 -0
  33. package/src/ir/converters/expressions/operators.ts +147 -0
  34. package/src/ir/converters/expressions/other.ts +60 -0
  35. package/src/ir/converters/statements/control/blocks.ts +22 -0
  36. package/src/ir/converters/statements/control/conditionals.ts +67 -0
  37. package/src/ir/converters/statements/control/exceptions.ts +43 -0
  38. package/src/ir/converters/statements/control/index.ts +17 -0
  39. package/src/ir/converters/statements/control/loops.ts +99 -0
  40. package/src/ir/converters/statements/control.ts +17 -0
  41. package/src/ir/converters/statements/declarations/classes/constructors.ts +120 -0
  42. package/src/ir/converters/statements/declarations/classes/index.ts +12 -0
  43. package/src/ir/converters/statements/declarations/classes/methods.ts +61 -0
  44. package/src/ir/converters/statements/declarations/classes/orchestrator.ts +166 -0
  45. package/src/ir/converters/statements/declarations/classes/override-detection.ts +116 -0
  46. package/src/ir/converters/statements/declarations/classes/properties.ts +63 -0
  47. package/src/ir/converters/statements/declarations/classes.ts +6 -0
  48. package/src/ir/converters/statements/declarations/enums.ts +29 -0
  49. package/src/ir/converters/statements/declarations/functions.ts +39 -0
  50. package/src/ir/converters/statements/declarations/index.ts +14 -0
  51. package/src/ir/converters/statements/declarations/interfaces.ts +131 -0
  52. package/src/ir/converters/statements/declarations/registry.ts +45 -0
  53. package/src/ir/converters/statements/declarations/type-aliases.ts +25 -0
  54. package/src/ir/converters/statements/declarations/variables.ts +60 -0
  55. package/src/ir/converters/statements/declarations.ts +16 -0
  56. package/src/ir/converters/statements/helpers.ts +174 -0
  57. package/src/ir/converters/statements/index.ts +40 -0
  58. package/src/ir/expression-converter.ts +207 -0
  59. package/src/ir/generic-validator.ts +100 -0
  60. package/src/ir/hierarchical-bindings-e2e.test.ts +163 -0
  61. package/src/ir/index.ts +6 -0
  62. package/src/ir/statement-converter.ts +128 -0
  63. package/src/ir/type-converter/arrays.ts +20 -0
  64. package/src/ir/type-converter/converter.ts +10 -0
  65. package/src/ir/type-converter/functions.ts +22 -0
  66. package/src/ir/type-converter/index.ts +11 -0
  67. package/src/ir/type-converter/inference.ts +122 -0
  68. package/src/ir/type-converter/literals.ts +40 -0
  69. package/src/ir/type-converter/objects.ts +107 -0
  70. package/src/ir/type-converter/orchestrator.ts +85 -0
  71. package/src/ir/type-converter/patterns.ts +73 -0
  72. package/src/ir/type-converter/primitives.ts +57 -0
  73. package/src/ir/type-converter/references.ts +64 -0
  74. package/src/ir/type-converter/unions-intersections.ts +34 -0
  75. package/src/ir/type-converter.ts +13 -0
  76. package/src/ir/types/expressions.ts +215 -0
  77. package/src/ir/types/guards.ts +39 -0
  78. package/src/ir/types/helpers.ts +135 -0
  79. package/src/ir/types/index.ts +108 -0
  80. package/src/ir/types/ir-types.ts +96 -0
  81. package/src/ir/types/module.ts +57 -0
  82. package/src/ir/types/statements.ts +238 -0
  83. package/src/ir/types.ts +97 -0
  84. package/src/metadata/bindings-loader.test.ts +144 -0
  85. package/src/metadata/bindings-loader.ts +357 -0
  86. package/src/metadata/index.ts +15 -0
  87. package/src/metadata/library-loader.ts +153 -0
  88. package/src/metadata/loader.test.ts +156 -0
  89. package/src/metadata/loader.ts +382 -0
  90. package/src/program/bindings.test.ts +512 -0
  91. package/src/program/bindings.ts +253 -0
  92. package/src/program/config.ts +30 -0
  93. package/src/program/creation.ts +249 -0
  94. package/src/program/dependency-graph.ts +245 -0
  95. package/src/program/diagnostics.ts +103 -0
  96. package/src/program/index.ts +19 -0
  97. package/src/program/metadata.ts +68 -0
  98. package/src/program/queries.ts +18 -0
  99. package/src/program/types.ts +38 -0
  100. package/src/program.ts +13 -0
  101. package/src/resolver/dotnet-import-resolver.ts +226 -0
  102. package/src/resolver/import-resolution.ts +177 -0
  103. package/src/resolver/index.ts +18 -0
  104. package/src/resolver/namespace.test.ts +86 -0
  105. package/src/resolver/namespace.ts +42 -0
  106. package/src/resolver/naming.ts +38 -0
  107. package/src/resolver/path-resolution.ts +22 -0
  108. package/src/resolver/types.ts +15 -0
  109. package/src/resolver.test.ts +155 -0
  110. package/src/resolver.ts +14 -0
  111. package/src/symbol-table/builder.ts +114 -0
  112. package/src/symbol-table/creation.ts +42 -0
  113. package/src/symbol-table/helpers.ts +18 -0
  114. package/src/symbol-table/index.ts +13 -0
  115. package/src/symbol-table/queries.ts +42 -0
  116. package/src/symbol-table/types.ts +28 -0
  117. package/src/symbol-table.ts +14 -0
  118. package/src/types/bindings.ts +172 -0
  119. package/src/types/diagnostic.test.ts +164 -0
  120. package/src/types/diagnostic.ts +153 -0
  121. package/src/types/explicit-views.test.ts +113 -0
  122. package/src/types/explicit-views.ts +218 -0
  123. package/src/types/metadata.ts +229 -0
  124. package/src/types/module.ts +99 -0
  125. package/src/types/nested-types.test.ts +194 -0
  126. package/src/types/nested-types.ts +215 -0
  127. package/src/types/parameter-modifiers.ts +173 -0
  128. package/src/types/ref-parameters.test.ts +192 -0
  129. package/src/types/ref-parameters.ts +268 -0
  130. package/src/types/result.test.ts +157 -0
  131. package/src/types/result.ts +48 -0
  132. package/src/types/support-types.test.ts +81 -0
  133. package/src/types/support-types.ts +288 -0
  134. package/src/types/test-harness.ts +180 -0
  135. package/src/validation/exports.ts +98 -0
  136. package/src/validation/features.ts +89 -0
  137. package/src/validation/generics.ts +40 -0
  138. package/src/validation/helpers.ts +31 -0
  139. package/src/validation/imports.ts +97 -0
  140. package/src/validation/index.ts +11 -0
  141. package/src/validation/orchestrator.ts +51 -0
  142. package/src/validation/static-safety.ts +267 -0
  143. package/src/validator.test.ts +468 -0
  144. package/src/validator.ts +15 -0
  145. package/tsconfig.json +13 -0
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@tsonic/frontend",
3
+ "version": "0.0.1",
4
+ "description": "TypeScript parser and IR builder for Tsonic compiler",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "default": "./dist/index.js"
12
+ },
13
+ "./types/metadata.js": {
14
+ "types": "./dist/types/metadata.d.ts",
15
+ "default": "./dist/types/metadata.js"
16
+ },
17
+ "./types/bindings.js": {
18
+ "types": "./dist/types/bindings.d.ts",
19
+ "default": "./dist/types/bindings.js"
20
+ },
21
+ "./types/nested-types.js": {
22
+ "types": "./dist/types/nested-types.d.ts",
23
+ "default": "./dist/types/nested-types.js"
24
+ },
25
+ "./types/explicit-views.js": {
26
+ "types": "./dist/types/explicit-views.d.ts",
27
+ "default": "./dist/types/explicit-views.js"
28
+ },
29
+ "./metadata/index.js": {
30
+ "types": "./dist/metadata/index.d.ts",
31
+ "default": "./dist/metadata/index.js"
32
+ }
33
+ },
34
+ "scripts": {
35
+ "build": "tsc -b",
36
+ "clean": "rm -rf dist *.tsbuildinfo",
37
+ "test": "mocha 'dist/**/*.test.js'",
38
+ "test:watch": "mocha 'dist/**/*.test.js' --watch"
39
+ },
40
+ "dependencies": {
41
+ "typescript": "5.9.2"
42
+ },
43
+ "devDependencies": {
44
+ "@types/chai": "5.2.2",
45
+ "@types/mocha": "10.0.10",
46
+ "@types/node": "20.14.0",
47
+ "chai": "5.3.3",
48
+ "mocha": "11.7.2"
49
+ },
50
+ "engines": {
51
+ "node": ">=18.0.0"
52
+ }
53
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Module dependency graph builder
3
+ * Main dispatcher - re-exports from graph/ subdirectory
4
+ */
5
+
6
+ export type { DependencyAnalysis } from "./graph/types.js";
7
+ export { buildDependencyGraph } from "./graph/builder.js";
8
+ export {
9
+ extractModuleInfo,
10
+ extractImport,
11
+ extractExport,
12
+ } from "./graph/extraction.js";
13
+ export { checkCircularDependencies } from "./graph/circular.js";
14
+ export {
15
+ isTopLevelCode,
16
+ hasExecutableInitializer,
17
+ hasExportModifier,
18
+ } from "./graph/helpers.js";
@@ -0,0 +1,121 @@
1
+ /**
2
+ * .NET Metadata Loader - Reads .metadata.json files to determine override behavior
3
+ */
4
+
5
+ /**
6
+ * Metadata for a single .NET type member (method, property, etc.)
7
+ */
8
+ export type DotnetMemberMetadata = {
9
+ readonly kind: "method" | "property" | "field" | "event";
10
+ readonly virtual?: boolean; // True if method is virtual/abstract
11
+ readonly sealed?: boolean; // True if method is sealed (cannot override)
12
+ readonly abstract?: boolean; // True if method is abstract
13
+ };
14
+
15
+ /**
16
+ * Metadata for a complete .NET type (class, interface, struct, etc.)
17
+ */
18
+ export type DotnetTypeMetadata = {
19
+ readonly kind: "class" | "interface" | "struct" | "enum";
20
+ readonly members: Readonly<Record<string, DotnetMemberMetadata>>;
21
+ };
22
+
23
+ /**
24
+ * Complete metadata file structure
25
+ */
26
+ export type DotnetMetadataFile = {
27
+ readonly types: Readonly<Record<string, DotnetTypeMetadata>>;
28
+ };
29
+
30
+ /**
31
+ * Registry of all loaded .NET metadata
32
+ * Maps fully-qualified type names to their metadata
33
+ */
34
+ export class DotnetMetadataRegistry {
35
+ private readonly metadata = new Map<string, DotnetTypeMetadata>();
36
+
37
+ /**
38
+ * Load a metadata file and add its types to the registry
39
+ */
40
+ loadMetadataFile(_filePath: string, content: DotnetMetadataFile): void {
41
+ for (const [typeName, typeMetadata] of Object.entries(content.types)) {
42
+ this.metadata.set(typeName, typeMetadata);
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Look up metadata for a .NET type by fully-qualified name
48
+ */
49
+ getTypeMetadata(qualifiedName: string): DotnetTypeMetadata | undefined {
50
+ return this.metadata.get(qualifiedName);
51
+ }
52
+
53
+ /**
54
+ * Look up metadata for a specific member of a .NET type
55
+ * @param qualifiedTypeName Fully-qualified type name (e.g., "System.IO.StringWriter")
56
+ * @param memberSignature Member signature (e.g., "ToString()" or "Write(string)")
57
+ */
58
+ getMemberMetadata(
59
+ qualifiedTypeName: string,
60
+ memberSignature: string
61
+ ): DotnetMemberMetadata | undefined {
62
+ const typeMetadata = this.metadata.get(qualifiedTypeName);
63
+ if (!typeMetadata) {
64
+ return undefined;
65
+ }
66
+ return typeMetadata.members[memberSignature];
67
+ }
68
+
69
+ /**
70
+ * Check if a member is virtual (can be overridden)
71
+ */
72
+ isVirtualMember(qualifiedTypeName: string, memberSignature: string): boolean {
73
+ const memberMetadata = this.getMemberMetadata(
74
+ qualifiedTypeName,
75
+ memberSignature
76
+ );
77
+ return memberMetadata?.virtual === true;
78
+ }
79
+
80
+ /**
81
+ * Check if a member is sealed (cannot be overridden)
82
+ */
83
+ isSealedMember(qualifiedTypeName: string, memberSignature: string): boolean {
84
+ const memberMetadata = this.getMemberMetadata(
85
+ qualifiedTypeName,
86
+ memberSignature
87
+ );
88
+ return memberMetadata?.sealed === true;
89
+ }
90
+
91
+ /**
92
+ * Get all loaded type names
93
+ */
94
+ getAllTypeNames(): readonly string[] {
95
+ return Array.from(this.metadata.keys());
96
+ }
97
+
98
+ /**
99
+ * Clear all loaded metadata
100
+ */
101
+ clear(): void {
102
+ this.metadata.clear();
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Build a method signature string for metadata lookup
108
+ * Format: "MethodName(type1,type2,...)"
109
+ * @param methodName The method name
110
+ * @param parameterTypes Array of parameter type names (e.g., ["string", "number"])
111
+ * @returns Signature string for metadata lookup
112
+ */
113
+ export const buildMethodSignature = (
114
+ methodName: string,
115
+ parameterTypes: readonly string[]
116
+ ): string => {
117
+ if (parameterTypes.length === 0) {
118
+ return `${methodName}()`;
119
+ }
120
+ return `${methodName}(${parameterTypes.join(",")})`;
121
+ };
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Dependency graph builder - Main orchestrator
3
+ */
4
+
5
+ import * as path from "node:path";
6
+ import { TsonicProgram } from "../program.js";
7
+ import { createModuleGraph, Import } from "../types/module.js";
8
+ import {
9
+ addDiagnostic,
10
+ createDiagnosticsCollector,
11
+ } from "../types/diagnostic.js";
12
+ import {
13
+ createSymbolTable,
14
+ addSymbol,
15
+ buildSymbolTable,
16
+ } from "../symbol-table.js";
17
+ import { DependencyAnalysis } from "./types.js";
18
+ import { extractModuleInfo } from "./extraction.js";
19
+ import { checkCircularDependencies } from "./circular.js";
20
+
21
+ /**
22
+ * Build a complete dependency graph for a Tsonic program
23
+ */
24
+ export const buildDependencyGraph = (
25
+ program: TsonicProgram,
26
+ entryPoints: readonly string[]
27
+ ): DependencyAnalysis => {
28
+ const modules = new Map();
29
+ const dependencies = new Map();
30
+ const dependents = new Map();
31
+ let symbolTable = createSymbolTable();
32
+ let diagnostics = createDiagnosticsCollector();
33
+
34
+ // Process all source files
35
+ program.sourceFiles.forEach((sourceFile) => {
36
+ const moduleInfo = extractModuleInfo(sourceFile, program);
37
+ modules.set(sourceFile.fileName, moduleInfo);
38
+
39
+ // Build symbol table
40
+ const symbols = buildSymbolTable(sourceFile, program.checker);
41
+ symbols.forEach((symbol) => {
42
+ symbolTable = addSymbol(symbolTable, symbol);
43
+ });
44
+ });
45
+
46
+ // Build dependency relationships
47
+ modules.forEach((module, modulePath) => {
48
+ const deps: string[] = [];
49
+
50
+ module.imports.forEach((imp: Import) => {
51
+ if (imp.resolvedPath) {
52
+ deps.push(imp.resolvedPath);
53
+
54
+ // Add to dependents map
55
+ const currentDependents = dependents.get(imp.resolvedPath) ?? [];
56
+ dependents.set(imp.resolvedPath, [...currentDependents, modulePath]);
57
+ }
58
+ });
59
+
60
+ dependencies.set(modulePath, deps);
61
+ });
62
+
63
+ // Check for circular dependencies
64
+ const circularCheck = checkCircularDependencies(dependencies);
65
+ if (!circularCheck.ok) {
66
+ diagnostics = addDiagnostic(diagnostics, circularCheck.error);
67
+ }
68
+
69
+ const graph = createModuleGraph(
70
+ modules,
71
+ dependencies,
72
+ dependents,
73
+ entryPoints.map((ep) => path.resolve(ep))
74
+ );
75
+
76
+ return {
77
+ graph,
78
+ symbolTable,
79
+ diagnostics,
80
+ };
81
+ };
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Circular dependency detection
3
+ */
4
+
5
+ import * as path from "node:path";
6
+ import { Diagnostic, createDiagnostic } from "../types/diagnostic.js";
7
+ import { Result, ok, error } from "../types/result.js";
8
+
9
+ /**
10
+ * Check for circular dependencies using depth-first search
11
+ */
12
+ export const checkCircularDependencies = (
13
+ dependencies: ReadonlyMap<string, readonly string[]>
14
+ ): Result<void, Diagnostic> => {
15
+ const visited = new Set<string>();
16
+ const stack = new Set<string>();
17
+
18
+ const visit = (module: string, path: string[]): string[] | null => {
19
+ if (stack.has(module)) {
20
+ return [...path, module]; // Found cycle
21
+ }
22
+
23
+ if (visited.has(module)) {
24
+ return null; // Already checked
25
+ }
26
+
27
+ visited.add(module);
28
+ stack.add(module);
29
+
30
+ const deps = dependencies.get(module) ?? [];
31
+ for (const dep of deps) {
32
+ const cycle = visit(dep, [...path, module]);
33
+ if (cycle) {
34
+ return cycle;
35
+ }
36
+ }
37
+
38
+ stack.delete(module);
39
+ return null;
40
+ };
41
+
42
+ for (const [module] of dependencies) {
43
+ const cycle = visit(module, []);
44
+ if (cycle) {
45
+ return error(
46
+ createDiagnostic(
47
+ "TSN1002",
48
+ "error",
49
+ `Circular dependency detected: ${cycle.map((m) => path.basename(m)).join(" → ")}`,
50
+ undefined,
51
+ "Break the circular dependency by refactoring shared code"
52
+ )
53
+ );
54
+ }
55
+ }
56
+
57
+ return ok(undefined);
58
+ };
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Export information extraction
3
+ */
4
+
5
+ import * as ts from "typescript";
6
+ import { Export } from "../../types/module.js";
7
+
8
+ /**
9
+ * Extract export information from an export declaration
10
+ */
11
+ export const extractExport = (node: ts.ExportDeclaration): Export | null => {
12
+ if (
13
+ !node.moduleSpecifier &&
14
+ node.exportClause &&
15
+ ts.isNamedExports(node.exportClause)
16
+ ) {
17
+ // export { a, b as c } - named exports without re-export
18
+ const elements = Array.from(node.exportClause.elements);
19
+ if (elements.length > 0 && elements[0]) {
20
+ const spec = elements[0];
21
+ return {
22
+ kind: "named",
23
+ name: spec.name.text,
24
+ localName: (spec.propertyName ?? spec.name).text,
25
+ };
26
+ }
27
+ return null;
28
+ }
29
+
30
+ if (node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier)) {
31
+ // export { ... } from "..." - re-exports
32
+ const fromModule = node.moduleSpecifier.text;
33
+ const exportedNames: { readonly name: string; readonly alias?: string }[] =
34
+ [];
35
+
36
+ if (node.exportClause) {
37
+ if (ts.isNamedExports(node.exportClause)) {
38
+ Array.from(node.exportClause.elements).forEach((spec) => {
39
+ exportedNames.push({
40
+ name: (spec.propertyName ?? spec.name).text,
41
+ alias: spec.propertyName ? spec.name.text : undefined,
42
+ });
43
+ });
44
+ }
45
+ }
46
+
47
+ return {
48
+ kind: "reexport",
49
+ fromModule,
50
+ exports: exportedNames,
51
+ };
52
+ }
53
+
54
+ return null;
55
+ };
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Import information extraction
3
+ */
4
+
5
+ import * as ts from "typescript";
6
+ import { TsonicProgram } from "../../program.js";
7
+ import { Import } from "../../types/module.js";
8
+ import { resolveImport } from "../../resolver.js";
9
+
10
+ /**
11
+ * Extract import information from an import declaration
12
+ */
13
+ export const extractImport = (
14
+ node: ts.ImportDeclaration,
15
+ sourceFile: ts.SourceFile,
16
+ program: TsonicProgram
17
+ ): Import | null => {
18
+ if (!ts.isStringLiteral(node.moduleSpecifier)) {
19
+ return null;
20
+ }
21
+
22
+ const specifier = node.moduleSpecifier.text;
23
+ const result = resolveImport(
24
+ specifier,
25
+ sourceFile.fileName,
26
+ program.options.sourceRoot,
27
+ program.dotnetResolver
28
+ );
29
+
30
+ const importedNames: { readonly name: string; readonly alias?: string }[] =
31
+ [];
32
+
33
+ if (node.importClause) {
34
+ // Default import
35
+ if (node.importClause.name) {
36
+ importedNames.push({
37
+ name: "default",
38
+ alias: node.importClause.name.text,
39
+ });
40
+ }
41
+
42
+ // Named imports
43
+ if (node.importClause.namedBindings) {
44
+ if (ts.isNamespaceImport(node.importClause.namedBindings)) {
45
+ // import * as ns from "..."
46
+ importedNames.push({
47
+ name: "*",
48
+ alias: node.importClause.namedBindings.name.text,
49
+ });
50
+ } else if (ts.isNamedImports(node.importClause.namedBindings)) {
51
+ // import { a, b as c } from "..."
52
+ node.importClause.namedBindings.elements.forEach((spec) => {
53
+ importedNames.push({
54
+ name: (spec.propertyName ?? spec.name).text,
55
+ alias: spec.propertyName ? spec.name.text : undefined,
56
+ });
57
+ });
58
+ }
59
+ }
60
+ }
61
+
62
+ if (result.ok) {
63
+ return {
64
+ kind: result.value.isLocal
65
+ ? "local"
66
+ : result.value.isDotNet
67
+ ? "dotnet"
68
+ : "node_module",
69
+ specifier,
70
+ resolvedPath: result.value.resolvedPath || undefined,
71
+ namespace: result.value.resolvedNamespace,
72
+ importedNames,
73
+ };
74
+ }
75
+
76
+ return {
77
+ kind: "local",
78
+ specifier,
79
+ importedNames,
80
+ };
81
+ };
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Module extraction - Public API
3
+ */
4
+
5
+ export { extractModuleInfo } from "./orchestrator.js";
6
+ export { extractImport } from "./imports.js";
7
+ export { extractExport } from "./exports.js";
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Module information extraction orchestrator
3
+ */
4
+
5
+ import * as ts from "typescript";
6
+ import { TsonicProgram } from "../../program.js";
7
+ import { ModuleInfo, Import, Export } from "../../types/module.js";
8
+ import { getNamespaceFromPath, getClassNameFromPath } from "../../resolver.js";
9
+ import { hasExportModifier, isTopLevelCode } from "../helpers.js";
10
+ import { extractImport } from "./imports.js";
11
+ import { extractExport } from "./exports.js";
12
+
13
+ /**
14
+ * Extract module information from a TypeScript source file
15
+ */
16
+ export const extractModuleInfo = (
17
+ sourceFile: ts.SourceFile,
18
+ program: TsonicProgram
19
+ ): ModuleInfo => {
20
+ const imports: Import[] = [];
21
+ const exports: Export[] = [];
22
+ let hasTopLevelCode = false;
23
+
24
+ const visitor = (node: ts.Node): void => {
25
+ // Extract imports
26
+ if (ts.isImportDeclaration(node)) {
27
+ const imp = extractImport(node, sourceFile, program);
28
+ if (imp) {
29
+ imports.push(imp);
30
+ }
31
+ }
32
+
33
+ // Extract exports
34
+ if (ts.isExportDeclaration(node)) {
35
+ const exp = extractExport(node);
36
+ if (exp) {
37
+ exports.push(exp);
38
+ }
39
+ }
40
+
41
+ if (ts.isExportAssignment(node)) {
42
+ exports.push({
43
+ kind: "default",
44
+ localName: node.expression.getText(),
45
+ });
46
+ }
47
+
48
+ // Check for exported declarations
49
+ if (hasExportModifier(node)) {
50
+ if (ts.isVariableStatement(node)) {
51
+ node.declarationList.declarations.forEach((decl) => {
52
+ if (ts.isIdentifier(decl.name)) {
53
+ exports.push({
54
+ kind: "named",
55
+ name: decl.name.text,
56
+ localName: decl.name.text,
57
+ });
58
+ }
59
+ });
60
+ } else if (
61
+ (ts.isFunctionDeclaration(node) || ts.isClassDeclaration(node)) &&
62
+ node.name
63
+ ) {
64
+ exports.push({
65
+ kind: "named",
66
+ name: node.name.text,
67
+ localName: node.name.text,
68
+ });
69
+ }
70
+ }
71
+
72
+ // Check for top-level code (non-declaration statements)
73
+ if (isTopLevelCode(node)) {
74
+ hasTopLevelCode = true;
75
+ }
76
+
77
+ ts.forEachChild(node, visitor);
78
+ };
79
+
80
+ visitor(sourceFile);
81
+
82
+ const namespace = getNamespaceFromPath(
83
+ sourceFile.fileName,
84
+ program.options.sourceRoot,
85
+ program.options.rootNamespace
86
+ );
87
+
88
+ const className = getClassNameFromPath(sourceFile.fileName);
89
+
90
+ return {
91
+ filePath: sourceFile.fileName,
92
+ sourceText: sourceFile.getFullText(),
93
+ imports,
94
+ exports,
95
+ hasTopLevelCode,
96
+ namespace,
97
+ className,
98
+ };
99
+ };
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Module information extraction from TypeScript source files
3
+ * Main dispatcher - re-exports from extraction/ subdirectory
4
+ */
5
+
6
+ export {
7
+ extractModuleInfo,
8
+ extractImport,
9
+ extractExport,
10
+ } from "./extraction/index.js";
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Helper functions for dependency graph analysis
3
+ */
4
+
5
+ import * as ts from "typescript";
6
+
7
+ /**
8
+ * Check if a node is top-level executable code (not just declarations)
9
+ */
10
+ export const isTopLevelCode = (node: ts.Node): boolean => {
11
+ // Check if this is a top-level statement that's not a declaration
12
+ if (node.parent && ts.isSourceFile(node.parent)) {
13
+ return (
14
+ !ts.isModuleDeclaration(node) &&
15
+ !ts.isImportDeclaration(node) &&
16
+ !ts.isExportDeclaration(node) &&
17
+ !ts.isExportAssignment(node) &&
18
+ !ts.isTypeAliasDeclaration(node) &&
19
+ !ts.isInterfaceDeclaration(node) &&
20
+ !(ts.isVariableStatement(node) && !hasExecutableInitializer(node)) &&
21
+ !ts.isFunctionDeclaration(node) &&
22
+ !ts.isClassDeclaration(node) &&
23
+ !ts.isEnumDeclaration(node)
24
+ );
25
+ }
26
+ return false;
27
+ };
28
+
29
+ /**
30
+ * Check if a variable statement has an initializer that executes code
31
+ */
32
+ export const hasExecutableInitializer = (
33
+ node: ts.VariableStatement
34
+ ): boolean => {
35
+ return node.declarationList.declarations.some(
36
+ (decl) => decl.initializer && !ts.isLiteralExpression(decl.initializer)
37
+ );
38
+ };
39
+
40
+ /**
41
+ * Check if a node has the export modifier
42
+ */
43
+ export const hasExportModifier = (node: ts.Node): boolean => {
44
+ if (!ts.canHaveModifiers(node)) {
45
+ return false;
46
+ }
47
+ const modifiers = ts.getModifiers(node);
48
+ return (
49
+ modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) ?? false
50
+ );
51
+ };
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Dependency graph builder - Public API
3
+ */
4
+
5
+ export type { DependencyAnalysis } from "./types.js";
6
+ export { buildDependencyGraph } from "./builder.js";
7
+ export {
8
+ extractModuleInfo,
9
+ extractImport,
10
+ extractExport,
11
+ } from "./extraction.js";
12
+ export { checkCircularDependencies } from "./circular.js";
13
+ export {
14
+ isTopLevelCode,
15
+ hasExecutableInitializer,
16
+ hasExportModifier,
17
+ } from "./helpers.js";
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Dependency graph type definitions
3
+ */
4
+
5
+ import { ModuleGraph } from "../types/module.js";
6
+ import { DiagnosticsCollector } from "../types/diagnostic.js";
7
+ import { SymbolTable } from "../symbol-table.js";
8
+
9
+ export type DependencyAnalysis = {
10
+ readonly graph: ModuleGraph;
11
+ readonly symbolTable: SymbolTable;
12
+ readonly diagnostics: DiagnosticsCollector;
13
+ };