svelte-docinfo 0.1.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 (97) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +349 -0
  3. package/dist/analyze-core.d.ts +1386 -0
  4. package/dist/analyze-core.d.ts.map +1 -0
  5. package/dist/analyze-core.js +324 -0
  6. package/dist/analyze.d.ts +137 -0
  7. package/dist/analyze.d.ts.map +1 -0
  8. package/dist/analyze.js +148 -0
  9. package/dist/cli.d.ts +102 -0
  10. package/dist/cli.d.ts.map +1 -0
  11. package/dist/cli.js +206 -0
  12. package/dist/concurrency.d.ts +38 -0
  13. package/dist/concurrency.d.ts.map +1 -0
  14. package/dist/concurrency.js +57 -0
  15. package/dist/declaration-build.d.ts +164 -0
  16. package/dist/declaration-build.d.ts.map +1 -0
  17. package/dist/declaration-build.js +15 -0
  18. package/dist/declaration-helpers.d.ts +169 -0
  19. package/dist/declaration-helpers.d.ts.map +1 -0
  20. package/dist/declaration-helpers.js +239 -0
  21. package/dist/dep-resolver.d.ts +166 -0
  22. package/dist/dep-resolver.d.ts.map +1 -0
  23. package/dist/dep-resolver.js +144 -0
  24. package/dist/diagnostics.d.ts +650 -0
  25. package/dist/diagnostics.d.ts.map +1 -0
  26. package/dist/diagnostics.js +377 -0
  27. package/dist/discovery.d.ts +115 -0
  28. package/dist/discovery.d.ts.map +1 -0
  29. package/dist/discovery.js +126 -0
  30. package/dist/exports.d.ts +105 -0
  31. package/dist/exports.d.ts.map +1 -0
  32. package/dist/exports.js +274 -0
  33. package/dist/files.d.ts +77 -0
  34. package/dist/files.d.ts.map +1 -0
  35. package/dist/files.js +95 -0
  36. package/dist/index.d.ts +32 -0
  37. package/dist/index.d.ts.map +1 -0
  38. package/dist/index.js +30 -0
  39. package/dist/log.d.ts +32 -0
  40. package/dist/log.d.ts.map +1 -0
  41. package/dist/log.js +11 -0
  42. package/dist/logo.d.ts +8 -0
  43. package/dist/logo.d.ts.map +1 -0
  44. package/dist/logo.js +9 -0
  45. package/dist/main.d.ts +11 -0
  46. package/dist/main.d.ts.map +1 -0
  47. package/dist/main.js +18 -0
  48. package/dist/paths.d.ts +35 -0
  49. package/dist/paths.d.ts.map +1 -0
  50. package/dist/paths.js +34 -0
  51. package/dist/postprocess.d.ts +145 -0
  52. package/dist/postprocess.d.ts.map +1 -0
  53. package/dist/postprocess.js +305 -0
  54. package/dist/session.d.ts +248 -0
  55. package/dist/session.d.ts.map +1 -0
  56. package/dist/session.js +496 -0
  57. package/dist/source-config.d.ts +330 -0
  58. package/dist/source-config.d.ts.map +1 -0
  59. package/dist/source-config.js +356 -0
  60. package/dist/source.d.ts +126 -0
  61. package/dist/source.d.ts.map +1 -0
  62. package/dist/source.js +68 -0
  63. package/dist/svelte.d.ts +179 -0
  64. package/dist/svelte.d.ts.map +1 -0
  65. package/dist/svelte.js +898 -0
  66. package/dist/tsdoc.d.ts +145 -0
  67. package/dist/tsdoc.d.ts.map +1 -0
  68. package/dist/tsdoc.js +262 -0
  69. package/dist/types.d.ts +3381 -0
  70. package/dist/types.d.ts.map +1 -0
  71. package/dist/types.js +588 -0
  72. package/dist/typescript-exports.d.ts +91 -0
  73. package/dist/typescript-exports.d.ts.map +1 -0
  74. package/dist/typescript-exports.js +656 -0
  75. package/dist/typescript-extract-class.d.ts +29 -0
  76. package/dist/typescript-extract-class.d.ts.map +1 -0
  77. package/dist/typescript-extract-class.js +251 -0
  78. package/dist/typescript-extract-function.d.ts +47 -0
  79. package/dist/typescript-extract-function.d.ts.map +1 -0
  80. package/dist/typescript-extract-function.js +108 -0
  81. package/dist/typescript-extract-shared.d.ts +166 -0
  82. package/dist/typescript-extract-shared.d.ts.map +1 -0
  83. package/dist/typescript-extract-shared.js +528 -0
  84. package/dist/typescript-extract-type-properties.d.ts +34 -0
  85. package/dist/typescript-extract-type-properties.d.ts.map +1 -0
  86. package/dist/typescript-extract-type-properties.js +241 -0
  87. package/dist/typescript-extract-type.d.ts +48 -0
  88. package/dist/typescript-extract-type.d.ts.map +1 -0
  89. package/dist/typescript-extract-type.js +238 -0
  90. package/dist/typescript-program.d.ts +211 -0
  91. package/dist/typescript-program.d.ts.map +1 -0
  92. package/dist/typescript-program.js +290 -0
  93. package/dist/vite.d.ts +116 -0
  94. package/dist/vite.d.ts.map +1 -0
  95. package/dist/vite.js +418 -0
  96. package/package.json +131 -0
  97. package/virtual-svelte-docinfo.d.ts +49 -0
package/dist/log.js ADDED
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Minimal logger interface threaded through analysis functions.
3
+ *
4
+ * Separate from `diagnostics.ts` because logging and diagnostics are not the
5
+ * same concern: diagnostics are structured, schema-validated records of
6
+ * analysis-time failures; logs are unstructured progress/info messages the
7
+ * caller chooses how to display.
8
+ *
9
+ * @module
10
+ */
11
+ export {};
package/dist/logo.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ export declare const logo_svelte_docinfo: {
2
+ label: string;
3
+ fill: string;
4
+ paths: {
5
+ d: string;
6
+ }[];
7
+ };
8
+ //# sourceMappingURL=logo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logo.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/logo.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,mBAAmB;;;;;;CAQb,CAAC"}
package/dist/logo.js ADDED
@@ -0,0 +1,9 @@
1
+ export const logo_svelte_docinfo = {
2
+ label: 'a cyan scroll with curling ends',
3
+ fill: '#1ec3d2',
4
+ paths: [
5
+ {
6
+ d: 'M 83,80.28 c -9.75,-34.41 2.18,-52.33 -3.76,-70 -2.31,-7.66 -48.25,-12 -70.25,-6 -12,6 -10,24 4,24 6,0 5.51,-13.67 6,-18 0,0 -1.43,59.52 5.13,80.21 1.2,3.8 2.91,5.28 4,5.92 3.93,2.31 63.48,3.74 68.01,0.54 1.18,-0.84 3.11,-5.38 2.63,-8.75 -0.42,-2.94 -3.27,-5.78 -5.28,-6.26 -1.74,-0.41 -0.62,-0.79 -5.37,-0.59 C 77.2,81.96 71.49,85.44 47.57,84.25 41.58,84.09 11.04,79.77 64.12,56.73 51.2,46.21 46.99,49.36 37.77,43.13 37.39,42.87 55,45.85 66.37,56.98 -10.84,93.94 82.99,80.29 82.99,80.29 Z',
7
+ },
8
+ ],
9
+ };
package/dist/main.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * CLI entry point for svelte-docinfo.
4
+ *
5
+ * This file is the bin script entry point. The actual CLI implementation
6
+ * is in `cli.ts` which exports `runCli()` for testing.
7
+ *
8
+ * @module
9
+ */
10
+ export {};
11
+ //# sourceMappingURL=main.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/main.ts"],"names":[],"mappings":";AACA;;;;;;;GAOG"}
package/dist/main.js ADDED
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * CLI entry point for svelte-docinfo.
4
+ *
5
+ * This file is the bin script entry point. The actual CLI implementation
6
+ * is in `cli.ts` which exports `runCli()` for testing.
7
+ *
8
+ * @module
9
+ */
10
+ import { runCli } from './cli.js';
11
+ try {
12
+ const exitCode = await runCli();
13
+ process.exit(exitCode);
14
+ }
15
+ catch (error) {
16
+ console.error('Fatal error:', error);
17
+ process.exit(2);
18
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Path normalization chokepoint.
3
+ *
4
+ * Internal contract: every path stored, compared, or used as a Map/Set key
5
+ * inside this library is in POSIX form (forward slashes). The library accepts
6
+ * native paths (backslash on Windows, forward slash elsewhere) at every public
7
+ * API boundary and at every `node:path` call site whose result flows into
8
+ * storage or comparison.
9
+ *
10
+ * `toPosixPath` is the only normalization primitive — anything that needs to
11
+ * convert a native path to the internal form goes through it. The function is
12
+ * idempotent: forward-slash inputs round-trip unchanged, so calls outside
13
+ * Windows are effectively a no-op.
14
+ *
15
+ * Drive-letter case (`C:` vs `c:`) and Windows long-path prefixes (`\\?\`) are
16
+ * deliberately out of scope — they don't arise from the path sources this
17
+ * library consumes (tsconfig roots, glob results, caller-supplied ids).
18
+ *
19
+ * @module
20
+ */
21
+ /**
22
+ * Normalize a path to POSIX form (forward slashes).
23
+ *
24
+ * Replaces every backslash with a forward slash. Idempotent: forward-slash
25
+ * input returns unchanged. Empty string returns empty string.
26
+ *
27
+ * @example
28
+ * ```ts
29
+ * toPosixPath('C:\\proj\\src\\lib\\foo.ts') // => 'C:/proj/src/lib/foo.ts'
30
+ * toPosixPath('/home/user/proj/foo.ts') // => '/home/user/proj/foo.ts' (unchanged)
31
+ * toPosixPath('') // => ''
32
+ * ```
33
+ */
34
+ export declare const toPosixPath: (p: string) => string;
35
+ //# sourceMappingURL=paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/paths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,WAAW,GAAI,GAAG,MAAM,KAAG,MAAwD,CAAC"}
package/dist/paths.js ADDED
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Path normalization chokepoint.
3
+ *
4
+ * Internal contract: every path stored, compared, or used as a Map/Set key
5
+ * inside this library is in POSIX form (forward slashes). The library accepts
6
+ * native paths (backslash on Windows, forward slash elsewhere) at every public
7
+ * API boundary and at every `node:path` call site whose result flows into
8
+ * storage or comparison.
9
+ *
10
+ * `toPosixPath` is the only normalization primitive — anything that needs to
11
+ * convert a native path to the internal form goes through it. The function is
12
+ * idempotent: forward-slash inputs round-trip unchanged, so calls outside
13
+ * Windows are effectively a no-op.
14
+ *
15
+ * Drive-letter case (`C:` vs `c:`) and Windows long-path prefixes (`\\?\`) are
16
+ * deliberately out of scope — they don't arise from the path sources this
17
+ * library consumes (tsconfig roots, glob results, caller-supplied ids).
18
+ *
19
+ * @module
20
+ */
21
+ /**
22
+ * Normalize a path to POSIX form (forward slashes).
23
+ *
24
+ * Replaces every backslash with a forward slash. Idempotent: forward-slash
25
+ * input returns unchanged. Empty string returns empty string.
26
+ *
27
+ * @example
28
+ * ```ts
29
+ * toPosixPath('C:\\proj\\src\\lib\\foo.ts') // => 'C:/proj/src/lib/foo.ts'
30
+ * toPosixPath('/home/user/proj/foo.ts') // => '/home/user/proj/foo.ts' (unchanged)
31
+ * toPosixPath('') // => ''
32
+ * ```
33
+ */
34
+ export const toPosixPath = (p) => (p.includes('\\') ? p.replace(/\\/g, '/') : p);
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Post-processing for analyzed library metadata.
3
+ *
4
+ * These functions transform analysis results after module-level analysis is complete:
5
+ *
6
+ * 1. **Validation** — `findDuplicates` checks flat namespace constraints
7
+ * 2. **Transformation** — `mergeReExports` resolves re-export relationships,
8
+ * `computeDependents` builds bidirectional dependency graphs
9
+ * 3. **Output** — `sortModules` prepares deterministic output
10
+ *
11
+ * @see `analyze.ts` for the main analysis entry point
12
+ *
13
+ * @module
14
+ */
15
+ import type { DeclarationJson, ModuleJson } from './types.js';
16
+ import type { ReExportInfo } from './declaration-build.js';
17
+ import type { SourceFileInfo } from './source.js';
18
+ /**
19
+ * A duplicate declaration with its full metadata and module path.
20
+ */
21
+ export interface DuplicateDeclaration {
22
+ /** The full declaration metadata. */
23
+ declaration: DeclarationJson;
24
+ /** Module path where this declaration is defined. */
25
+ module: string;
26
+ }
27
+ /**
28
+ * Find duplicate declaration names across modules.
29
+ *
30
+ * Callers can decide how to handle duplicates (throw, warn, ignore).
31
+ *
32
+ * @returns `Map` of declaration names to their `DuplicateDeclaration` occurrences (only includes duplicates)
33
+ *
34
+ * @example
35
+ * ```ts
36
+ * const duplicates = findDuplicates(modules);
37
+ * if (duplicates.size > 0) {
38
+ * for (const [name, occurrences] of duplicates) {
39
+ * console.error(`"${name}" found in:`);
40
+ * for (const {declaration, module} of occurrences) {
41
+ * console.error(` - ${module}:${declaration.sourceLine} (${declaration.kind})`);
42
+ * }
43
+ * }
44
+ * throw new Error(`Found ${duplicates.size} duplicate declaration names`);
45
+ * }
46
+ * ```
47
+ */
48
+ export declare const findDuplicates: (modules: Array<ModuleJson>) => Map<string, Array<DuplicateDeclaration>>;
49
+ /**
50
+ * Sort modules alphabetically by path for deterministic output and cleaner diffs.
51
+ *
52
+ * @param modules - the modules to sort
53
+ * @returns a new sorted array (does not mutate input)
54
+ */
55
+ export declare const sortModules: (modules: Array<ModuleJson>) => Array<ModuleJson>;
56
+ /**
57
+ * A collected re-export with its source module context.
58
+ *
59
+ * Used during the two-phase re-export resolution:
60
+ * 1. Phase 1: Collect re-exports from each module during analysis
61
+ * 2. Phase 2: Group by original module and merge into `alsoExportedFrom`
62
+ */
63
+ export interface ReExportEntry {
64
+ /** The module that re-exports the declaration. */
65
+ reExportingModule: string;
66
+ /** The re-export info (name and original module). */
67
+ reExport: ReExportInfo;
68
+ }
69
+ /**
70
+ * Build `alsoExportedFrom` arrays from collected re-export data.
71
+ *
72
+ * This function resolves the two-phase re-export problem:
73
+ *
74
+ * **Problem**: When module A re-exports from module B, we discover this while
75
+ * analyzing A, but need to update B's declarations. However, B may already be
76
+ * processed or may be processed later.
77
+ *
78
+ * **Solution**: Collect all re-exports in phase 1, then merge them in phase 2
79
+ * after all modules are analyzed.
80
+ *
81
+ * Component-only fields on renamed component aliases (`props`,
82
+ * `acceptsChildren`, etc.) are populated separately by `resolveComponentAliases`
83
+ * — call it after this function. They split because they touch disjoint fields
84
+ * and have different inputs.
85
+ *
86
+ * @param modules - the modules array with all modules (will be mutated)
87
+ * @param collectedReExports - array of re-exports collected during phase 1
88
+ * @mutates modules - unions new re-exporters into `declaration.alsoExportedFrom`
89
+ * (deduped + sorted), so a second call with the same inputs is a no-op
90
+ *
91
+ * @example
92
+ * ```ts
93
+ * // helpers.ts exports: foo, bar
94
+ * // index.ts does: export {foo, bar} from './files.js'
95
+ * //
96
+ * // After processing:
97
+ * // - helpers.ts foo declaration gets: alsoExportedFrom: ['index.ts']
98
+ * // - helpers.ts bar declaration gets: alsoExportedFrom: ['index.ts']
99
+ * ```
100
+ */
101
+ export declare const mergeReExports: (modules: Array<ModuleJson>, collectedReExports: Array<ReExportEntry>) => void;
102
+ /**
103
+ * Copy props/acceptsChildren/lang/etc. from canonical component declarations
104
+ * onto synthesized component-aliased declarations.
105
+ *
106
+ * Renamed Svelte component re-exports (`export {default as Foo} from './X.svelte'`)
107
+ * are emitted as `kind: 'component'` placeholders by `analyzeExports`, with `aliasOf`
108
+ * pointing at the canonical. The canonical's component-specific fields are only
109
+ * available after `analyzeSvelteModule` synthesizes the canonical declaration, so
110
+ * the copy happens here in phase 2 once all modules are analyzed.
111
+ *
112
+ * Call this *after* `mergeReExports` — both walk the same modules array but
113
+ * read/write disjoint fields, so order between them only matters for clarity.
114
+ *
115
+ * @mutates modules - fills component-only fields on aliased component declarations
116
+ */
117
+ export declare const resolveComponentAliases: (modules: Array<ModuleJson>) => void;
118
+ /**
119
+ * Compute bidirectional dependencies from source files.
120
+ *
121
+ * This function ensures that if file A has file B in its `dependencies`,
122
+ * then file B will have file A in its `dependents`. This provides consistent
123
+ * output regardless of whether callers provide one-directional or bidirectional
124
+ * dependency information.
125
+ *
126
+ * Returns new `SourceFileInfo` objects when computed dependents exist or when
127
+ * paths needed posixification; otherwise the original input objects flow
128
+ * through `===`-equal (fast path for session callers, who already pass POSIX
129
+ * paths and may have no inferable dependents for a given file).
130
+ *
131
+ * @param files - source files with optional dependency information
132
+ * @returns new array with bidirectional dependencies computed
133
+ *
134
+ * @example
135
+ * ```ts
136
+ * // Input: Calculator.svelte has dependencies: [math.ts]
137
+ * // Output: Calculator.svelte has dependencies: [math.ts]
138
+ * // math.ts has dependents: [Calculator.svelte]
139
+ * const filesWithBidirectional = computeDependents(files);
140
+ * ```
141
+ */
142
+ export declare const computeDependents: (files: ReadonlyArray<SourceFileInfo>) => Array<SourceFileInfo & {
143
+ dependents?: ReadonlyArray<string>;
144
+ }>;
145
+ //# sourceMappingURL=postprocess.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postprocess.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/postprocess.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAA2B,eAAe,EAAE,UAAU,EAAC,MAAM,YAAY,CAAC;AACtF,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,wBAAwB,CAAC;AACzD,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,aAAa,CAAC;AAuBhD;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACpC,qCAAqC;IACrC,WAAW,EAAE,eAAe,CAAC;IAC7B,qDAAqD;IACrD,MAAM,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,cAAc,GAC1B,SAAS,KAAK,CAAC,UAAU,CAAC,KACxB,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,oBAAoB,CAAC,CA4BzC,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,WAAW,GAAI,SAAS,KAAK,CAAC,UAAU,CAAC,KAAG,KAAK,CAAC,UAAU,CAExE,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC7B,kDAAkD;IAClD,iBAAiB,EAAE,MAAM,CAAC;IAC1B,qDAAqD;IACrD,QAAQ,EAAE,YAAY,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,eAAO,MAAM,cAAc,GAC1B,SAAS,KAAK,CAAC,UAAU,CAAC,EAC1B,oBAAoB,KAAK,CAAC,aAAa,CAAC,KACtC,IAkCF,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,uBAAuB,GAAI,SAAS,KAAK,CAAC,UAAU,CAAC,KAAG,IAoDpE,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,iBAAiB,GAC7B,OAAO,aAAa,CAAC,cAAc,CAAC,KAClC,KAAK,CAAC,cAAc,GAAG;IAAC,UAAU,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;CAAC,CAwD7D,CAAC"}
@@ -0,0 +1,305 @@
1
+ /**
2
+ * Post-processing for analyzed library metadata.
3
+ *
4
+ * These functions transform analysis results after module-level analysis is complete:
5
+ *
6
+ * 1. **Validation** — `findDuplicates` checks flat namespace constraints
7
+ * 2. **Transformation** — `mergeReExports` resolves re-export relationships,
8
+ * `computeDependents` builds bidirectional dependency graphs
9
+ * 3. **Output** — `sortModules` prepares deterministic output
10
+ *
11
+ * @see `analyze.ts` for the main analysis entry point
12
+ *
13
+ * @module
14
+ */
15
+ import { toPosixPath } from './paths.js';
16
+ /**
17
+ * Posixify every entry in `arr`. Returns the same array reference when no
18
+ * entry needed normalization, so identity-equality short-circuits in
19
+ * `computeDependents`'s no-rewrite branch.
20
+ */
21
+ const posixifyArray = (arr) => {
22
+ if (!arr)
23
+ return arr;
24
+ let changed = null;
25
+ for (let i = 0; i < arr.length; i++) {
26
+ const p = toPosixPath(arr[i]);
27
+ if (p !== arr[i]) {
28
+ if (!changed)
29
+ changed = arr.slice();
30
+ changed[i] = p;
31
+ }
32
+ }
33
+ return changed ?? arr;
34
+ };
35
+ /**
36
+ * Find duplicate declaration names across modules.
37
+ *
38
+ * Callers can decide how to handle duplicates (throw, warn, ignore).
39
+ *
40
+ * @returns `Map` of declaration names to their `DuplicateDeclaration` occurrences (only includes duplicates)
41
+ *
42
+ * @example
43
+ * ```ts
44
+ * const duplicates = findDuplicates(modules);
45
+ * if (duplicates.size > 0) {
46
+ * for (const [name, occurrences] of duplicates) {
47
+ * console.error(`"${name}" found in:`);
48
+ * for (const {declaration, module} of occurrences) {
49
+ * console.error(` - ${module}:${declaration.sourceLine} (${declaration.kind})`);
50
+ * }
51
+ * }
52
+ * throw new Error(`Found ${duplicates.size} duplicate declaration names`);
53
+ * }
54
+ * ```
55
+ */
56
+ export const findDuplicates = (modules) => {
57
+ const allOccurrences = new Map();
58
+ // Collect declaration names. The default slot is module-scoped per the JS
59
+ // spec — every module can have its own `'default'` and they don't collide
60
+ // — so skip `name === 'default'` entries from this flat-namespace check.
61
+ for (const mod of modules) {
62
+ for (const declaration of mod.declarations) {
63
+ if (declaration.name === 'default')
64
+ continue;
65
+ if (!allOccurrences.has(declaration.name)) {
66
+ allOccurrences.set(declaration.name, []);
67
+ }
68
+ allOccurrences.get(declaration.name).push({
69
+ declaration,
70
+ module: mod.path,
71
+ });
72
+ }
73
+ }
74
+ // Filter to only duplicates
75
+ const duplicates = new Map();
76
+ for (const [name, occurrences] of allOccurrences) {
77
+ if (occurrences.length > 1) {
78
+ duplicates.set(name, occurrences);
79
+ }
80
+ }
81
+ return duplicates;
82
+ };
83
+ /**
84
+ * Sort modules alphabetically by path for deterministic output and cleaner diffs.
85
+ *
86
+ * @param modules - the modules to sort
87
+ * @returns a new sorted array (does not mutate input)
88
+ */
89
+ export const sortModules = (modules) => {
90
+ return modules.slice().sort((a, b) => a.path.localeCompare(b.path));
91
+ };
92
+ /**
93
+ * Build `alsoExportedFrom` arrays from collected re-export data.
94
+ *
95
+ * This function resolves the two-phase re-export problem:
96
+ *
97
+ * **Problem**: When module A re-exports from module B, we discover this while
98
+ * analyzing A, but need to update B's declarations. However, B may already be
99
+ * processed or may be processed later.
100
+ *
101
+ * **Solution**: Collect all re-exports in phase 1, then merge them in phase 2
102
+ * after all modules are analyzed.
103
+ *
104
+ * Component-only fields on renamed component aliases (`props`,
105
+ * `acceptsChildren`, etc.) are populated separately by `resolveComponentAliases`
106
+ * — call it after this function. They split because they touch disjoint fields
107
+ * and have different inputs.
108
+ *
109
+ * @param modules - the modules array with all modules (will be mutated)
110
+ * @param collectedReExports - array of re-exports collected during phase 1
111
+ * @mutates modules - unions new re-exporters into `declaration.alsoExportedFrom`
112
+ * (deduped + sorted), so a second call with the same inputs is a no-op
113
+ *
114
+ * @example
115
+ * ```ts
116
+ * // helpers.ts exports: foo, bar
117
+ * // index.ts does: export {foo, bar} from './files.js'
118
+ * //
119
+ * // After processing:
120
+ * // - helpers.ts foo declaration gets: alsoExportedFrom: ['index.ts']
121
+ * // - helpers.ts bar declaration gets: alsoExportedFrom: ['index.ts']
122
+ * ```
123
+ */
124
+ export const mergeReExports = (modules, collectedReExports) => {
125
+ // Group re-exports by `(originalModule, name)`. The default slot keys as
126
+ // `'default'` like any other name — each module owns its own default slot,
127
+ // so the per-module map prevents cross-module collisions naturally.
128
+ const reExportMap = new Map();
129
+ for (const { reExportingModule, reExport } of collectedReExports) {
130
+ const { name, originalModule } = reExport;
131
+ if (!reExportMap.has(originalModule)) {
132
+ reExportMap.set(originalModule, new Map());
133
+ }
134
+ const moduleMap = reExportMap.get(originalModule);
135
+ if (!moduleMap.has(name)) {
136
+ moduleMap.set(name, []);
137
+ }
138
+ moduleMap.get(name).push(reExportingModule);
139
+ }
140
+ // Merge into original declarations
141
+ for (const mod of modules) {
142
+ const moduleReExports = reExportMap.get(mod.path);
143
+ if (!moduleReExports)
144
+ continue;
145
+ for (const declaration of mod.declarations) {
146
+ const reExporters = moduleReExports.get(declaration.name);
147
+ if (reExporters?.length) {
148
+ // Union with existing entries, dedupe, sort — keeps the function idempotent
149
+ // when re-run on already-merged modules
150
+ const merged = new Set(declaration.alsoExportedFrom);
151
+ for (const reExporter of reExporters)
152
+ merged.add(reExporter);
153
+ declaration.alsoExportedFrom = Array.from(merged).sort();
154
+ }
155
+ }
156
+ }
157
+ };
158
+ /**
159
+ * Copy props/acceptsChildren/lang/etc. from canonical component declarations
160
+ * onto synthesized component-aliased declarations.
161
+ *
162
+ * Renamed Svelte component re-exports (`export {default as Foo} from './X.svelte'`)
163
+ * are emitted as `kind: 'component'` placeholders by `analyzeExports`, with `aliasOf`
164
+ * pointing at the canonical. The canonical's component-specific fields are only
165
+ * available after `analyzeSvelteModule` synthesizes the canonical declaration, so
166
+ * the copy happens here in phase 2 once all modules are analyzed.
167
+ *
168
+ * Call this *after* `mergeReExports` — both walk the same modules array but
169
+ * read/write disjoint fields, so order between them only matters for clarity.
170
+ *
171
+ * @mutates modules - fills component-only fields on aliased component declarations
172
+ */
173
+ export const resolveComponentAliases = (modules) => {
174
+ // Build {modulePath → {componentName → ComponentDeclarationJson}} for canonical lookups
175
+ const canonicalByModule = new Map();
176
+ for (const mod of modules) {
177
+ for (const decl of mod.declarations) {
178
+ if (decl.kind !== 'component' || decl.aliasOf)
179
+ continue;
180
+ let perModule = canonicalByModule.get(mod.path);
181
+ if (!perModule) {
182
+ perModule = new Map();
183
+ canonicalByModule.set(mod.path, perModule);
184
+ }
185
+ perModule.set(decl.name, decl);
186
+ }
187
+ }
188
+ for (const mod of modules) {
189
+ for (const decl of mod.declarations) {
190
+ if (decl.kind !== 'component' || !decl.aliasOf)
191
+ continue;
192
+ const canonical = canonicalByModule.get(decl.aliasOf.module)?.get(decl.aliasOf.name);
193
+ if (!canonical)
194
+ continue;
195
+ decl.props = canonical.props;
196
+ decl.acceptsChildren = canonical.acceptsChildren;
197
+ decl.intersects = canonical.intersects;
198
+ decl.genericParams = canonical.genericParams;
199
+ if (canonical.lang !== undefined)
200
+ decl.lang = canonical.lang;
201
+ if (canonical.docComment !== undefined && decl.docComment === undefined) {
202
+ decl.docComment = canonical.docComment;
203
+ }
204
+ if (canonical.typeSignature !== undefined && decl.typeSignature === undefined) {
205
+ decl.typeSignature = canonical.typeSignature;
206
+ }
207
+ if (canonical.examples.length > 0 && decl.examples.length === 0) {
208
+ decl.examples = canonical.examples;
209
+ }
210
+ if (canonical.seeAlso.length > 0 && decl.seeAlso.length === 0) {
211
+ decl.seeAlso = canonical.seeAlso;
212
+ }
213
+ if (canonical.throws.length > 0 && decl.throws.length === 0) {
214
+ decl.throws = canonical.throws;
215
+ }
216
+ if (canonical.deprecatedMessage !== undefined && decl.deprecatedMessage === undefined) {
217
+ decl.deprecatedMessage = canonical.deprecatedMessage;
218
+ }
219
+ if (canonical.since !== undefined && decl.since === undefined) {
220
+ decl.since = canonical.since;
221
+ }
222
+ if (canonical.mutates !== undefined && decl.mutates === undefined) {
223
+ decl.mutates = canonical.mutates;
224
+ }
225
+ if (canonical.partial)
226
+ decl.partial = true;
227
+ }
228
+ }
229
+ };
230
+ /**
231
+ * Compute bidirectional dependencies from source files.
232
+ *
233
+ * This function ensures that if file A has file B in its `dependencies`,
234
+ * then file B will have file A in its `dependents`. This provides consistent
235
+ * output regardless of whether callers provide one-directional or bidirectional
236
+ * dependency information.
237
+ *
238
+ * Returns new `SourceFileInfo` objects when computed dependents exist or when
239
+ * paths needed posixification; otherwise the original input objects flow
240
+ * through `===`-equal (fast path for session callers, who already pass POSIX
241
+ * paths and may have no inferable dependents for a given file).
242
+ *
243
+ * @param files - source files with optional dependency information
244
+ * @returns new array with bidirectional dependencies computed
245
+ *
246
+ * @example
247
+ * ```ts
248
+ * // Input: Calculator.svelte has dependencies: [math.ts]
249
+ * // Output: Calculator.svelte has dependencies: [math.ts]
250
+ * // math.ts has dependents: [Calculator.svelte]
251
+ * const filesWithBidirectional = computeDependents(files);
252
+ * ```
253
+ */
254
+ export const computeDependents = (files) => {
255
+ // Posixify ids and dependency lists at the boundary so a power user
256
+ // supplying a hand-built `SourceFileInfo[]` with mixed-shape paths still
257
+ // gets correct lookup behavior. Identity-preserving when no normalization
258
+ // is needed (session callers, who already pass POSIX): both arrays and the
259
+ // outer `SourceFileInfo` flow through `===`-equal.
260
+ const posixFiles = files.map((file) => {
261
+ const posixId = toPosixPath(file.id);
262
+ const posixDeps = posixifyArray(file.dependencies);
263
+ if (posixId === file.id && posixDeps === file.dependencies) {
264
+ return file;
265
+ }
266
+ return {
267
+ ...file,
268
+ id: posixId,
269
+ dependencies: posixDeps,
270
+ };
271
+ });
272
+ // Build a map of file id -> dependents (computed from dependencies)
273
+ const computedDependents = new Map();
274
+ // Initialize all files in the map
275
+ for (const file of posixFiles) {
276
+ computedDependents.set(file.id, new Set());
277
+ }
278
+ // Compute dependents from dependencies
279
+ for (const file of posixFiles) {
280
+ if (!file.dependencies)
281
+ continue;
282
+ for (const depId of file.dependencies) {
283
+ // Only add if the dependency is in our file set
284
+ if (computedDependents.has(depId)) {
285
+ computedDependents.get(depId).add(file.id);
286
+ }
287
+ }
288
+ }
289
+ // Attach computed dependents to each file. Dependents are derived from
290
+ // forward edges in the owned set, not from caller input — the public
291
+ // `SourceFileInfo` carries no `dependents` field.
292
+ return posixFiles.map((file) => {
293
+ const computed = computedDependents.get(file.id);
294
+ if (!computed || computed.size === 0) {
295
+ // No computed dependents, return as-is
296
+ return file;
297
+ }
298
+ // Sort for deterministic output
299
+ const dependents = Array.from(computed).sort();
300
+ return {
301
+ ...file,
302
+ dependents,
303
+ };
304
+ });
305
+ };