svelte-docinfo 0.4.1 → 0.5.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.
@@ -32,9 +32,56 @@ const posixifyArray = (arr) => {
32
32
  }
33
33
  return changed ?? arr;
34
34
  };
35
+ /**
36
+ * Build the `(module, name)` → declaration lookup used for `aliasOf`-chain
37
+ * walking. Key format: `` `${modulePath}\n${name}` `` — `\n` can't appear in
38
+ * either part.
39
+ */
40
+ const buildDeclarationIndex = (modules) => {
41
+ const byIdentity = new Map();
42
+ for (const mod of modules) {
43
+ for (const declaration of mod.declarations) {
44
+ byIdentity.set(`${mod.path}\n${declaration.name}`, declaration);
45
+ }
46
+ }
47
+ return byIdentity;
48
+ };
49
+ /**
50
+ * Resolve a declaration to its canonical identity. A same-name re-export of
51
+ * an intermediate rename produces an alias pointing at another alias, so a
52
+ * single hop isn't enough — follow the chain through declarations present in
53
+ * the index. The terminal is the declaration *object* when it's in the set
54
+ * (object identity keeps two same-`(module, name)` declarations distinct in
55
+ * pathological inputs), else the dangling `(module, name)` key (two aliases
56
+ * of the same absent canonical still resolve to one identity). The visited
57
+ * set guards malformed cyclic input.
58
+ */
59
+ const resolveCanonicalIdentity = (byIdentity, modulePath, declaration) => {
60
+ let current = declaration;
61
+ const visited = new Set([`${modulePath}\n${declaration.name}`]);
62
+ while (current.aliasOf) {
63
+ const nextKey = `${current.aliasOf.module}\n${current.aliasOf.name}`;
64
+ if (visited.has(nextKey))
65
+ return nextKey;
66
+ visited.add(nextKey);
67
+ const next = byIdentity.get(nextKey);
68
+ if (!next)
69
+ return nextKey;
70
+ current = next;
71
+ }
72
+ return current;
73
+ };
35
74
  /**
36
75
  * Find duplicate declaration names across modules.
37
76
  *
77
+ * A duplicate is two *different things* sharing a name in the flat namespace.
78
+ * Occurrences are compared by canonical identity — `aliasOf` chains are
79
+ * resolved first, so an alias and its canonical (or two aliases of the same
80
+ * canonical) are one thing, not a collision. Documenting a same-name re-export
81
+ * (which synthesizes an alias) or re-exporting a component under its own name
82
+ * (`export {default as Foo} from './Foo.svelte'`) therefore doesn't flag.
83
+ * When a name does flag, all occurrences are reported, aliases included.
84
+ *
38
85
  * Callers can decide how to handle duplicates (throw, warn, ignore).
39
86
  *
40
87
  * @returns `Map` of declaration names to their `DuplicateDeclaration` occurrences (only includes duplicates)
@@ -54,10 +101,13 @@ const posixifyArray = (arr) => {
54
101
  * ```
55
102
  */
56
103
  export const findDuplicates = (modules) => {
104
+ const byIdentity = buildDeclarationIndex(modules);
105
+ const resolveCanonical = (modulePath, declaration) => resolveCanonicalIdentity(byIdentity, modulePath, declaration);
106
+ // Collect declaration names with their canonical identities. The default
107
+ // slot is module-scoped per the JS spec — every module can have its own
108
+ // `'default'` and they don't collide — so skip `name === 'default'`
109
+ // entries from this flat-namespace check.
57
110
  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
111
  for (const mod of modules) {
62
112
  for (const declaration of mod.declarations) {
63
113
  if (declaration.name === 'default')
@@ -68,74 +118,90 @@ export const findDuplicates = (modules) => {
68
118
  allOccurrences.get(declaration.name).push({
69
119
  declaration,
70
120
  module: mod.path,
121
+ canonical: resolveCanonical(mod.path, declaration),
71
122
  });
72
123
  }
73
124
  }
74
- // Filter to only duplicates
125
+ // A name is duplicated only when its occurrences span >1 canonical identity
75
126
  const duplicates = new Map();
76
127
  for (const [name, occurrences] of allOccurrences) {
77
- if (occurrences.length > 1) {
78
- duplicates.set(name, occurrences);
128
+ const identities = new Set(occurrences.map((o) => o.canonical));
129
+ if (identities.size > 1) {
130
+ duplicates.set(name, occurrences.map(({ declaration, module }) => ({ declaration, module })));
79
131
  }
80
132
  }
81
133
  return duplicates;
82
134
  };
135
+ /**
136
+ * Code-unit string comparator for deterministic output ordering.
137
+ *
138
+ * Unlike `localeCompare` (host-locale/ICU-dependent, so byte-identical input
139
+ * can serialize in different orders on different machines), code-unit order is
140
+ * environment-independent — and it matches the default `Array.prototype.sort`
141
+ * used for `alsoExportedFrom`, `dependencies`, and `dependents`.
142
+ */
143
+ export const compareStrings = (a, b) => (a < b ? -1 : a > b ? 1 : 0);
83
144
  /**
84
145
  * Sort modules alphabetically by path for deterministic output and cleaner diffs.
85
146
  *
147
+ * Code-unit order (`compareStrings`) so the output is environment-independent.
148
+ *
86
149
  * @param modules - the modules to sort
87
150
  * @returns a new sorted array (does not mutate input)
88
151
  */
89
152
  export const sortModules = (modules) => {
90
- return modules.slice().sort((a, b) => a.path.localeCompare(b.path));
153
+ return modules.slice().sort((a, b) => compareStrings(a.path, b.path));
91
154
  };
92
155
  /**
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.
156
+ * Build `alsoExportedFrom` arrays from the modules' forward re-export edges.
100
157
  *
101
- * **Solution**: Collect all re-exports in phase 1, then merge them in phase 2
102
- * after all modules are analyzed.
158
+ * Each module carries its same-name re-export edges as `ModuleJson.reExports`
159
+ * (collected in phase 1); this phase-2 pass inverts them onto the canonical
160
+ * declarations so both directions of the same fact are queryable. Edges whose
161
+ * canonical module or declaration is absent from `modules` are skipped — the
162
+ * forward entry remains without a back-link (see `ReExportJson` for the
163
+ * presence caveats).
103
164
  *
104
165
  * Component-only fields on renamed component aliases (`props`,
105
166
  * `acceptsChildren`, etc.) are populated separately by `resolveComponentAliases`
106
167
  * — call it after this function. They split because they touch disjoint fields
107
168
  * and have different inputs.
108
169
  *
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`
170
+ * @param modules - the modules array with all modules (will be mutated).
171
+ * Must be parsed `ModuleJson`s — wire JSON strips empty arrays, so run
172
+ * raw JSON through `AnalyzeResultJson.parse` first or `reExports` may be
173
+ * `undefined`
174
+ * @mutates modules - unions re-exporters into `declaration.alsoExportedFrom`
112
175
  * (deduped + sorted), so a second call with the same inputs is a no-op
113
176
  *
114
177
  * @example
115
178
  * ```ts
116
179
  * // helpers.ts exports: foo, bar
117
- * // index.ts does: export {foo, bar} from './files.js'
180
+ * // index.ts does: export {foo, bar} from './helpers.js'
181
+ * // (so index.ts's ModuleJson carries reExports:
182
+ * // [{name: 'foo', module: 'helpers.ts'}, {name: 'bar', module: 'helpers.ts'}])
118
183
  * //
119
184
  * // After processing:
120
185
  * // - helpers.ts foo declaration gets: alsoExportedFrom: ['index.ts']
121
186
  * // - helpers.ts bar declaration gets: alsoExportedFrom: ['index.ts']
122
187
  * ```
123
188
  */
124
- export const mergeReExports = (modules, collectedReExports) => {
125
- // Group re-exports by `(originalModule, name)`. The default slot keys as
189
+ export const mergeReExports = (modules) => {
190
+ // Group edges by `(canonical module, name)`. The default slot keys as
126
191
  // `'default'` like any other name — each module owns its own default slot,
127
192
  // so the per-module map prevents cross-module collisions naturally.
128
193
  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, []);
194
+ for (const mod of modules) {
195
+ for (const { name, module: originalModule } of mod.reExports) {
196
+ if (!reExportMap.has(originalModule)) {
197
+ reExportMap.set(originalModule, new Map());
198
+ }
199
+ const moduleMap = reExportMap.get(originalModule);
200
+ if (!moduleMap.has(name)) {
201
+ moduleMap.set(name, []);
202
+ }
203
+ moduleMap.get(name).push(mod.path);
137
204
  }
138
- moduleMap.get(name).push(reExportingModule);
139
205
  }
140
206
  // Merge into original declarations
141
207
  for (const mod of modules) {
@@ -303,3 +369,185 @@ export const computeDependents = (files) => {
303
369
  };
304
370
  });
305
371
  };
372
+ /**
373
+ * Resolve a module's full export surface from the analyzed model, applying
374
+ * ES module semantics to star exports.
375
+ *
376
+ * Combines the module's own `declarations` (including synthesized aliases),
377
+ * `reExports` edges, `externalReExports`, and transitively-resolved
378
+ * `starExports` into one deduped, name-sorted list. The ES rules applied to
379
+ * star projection:
380
+ *
381
+ * - explicit exports (declarations, edges, externals) shadow star-projected
382
+ * names
383
+ * - a name projected by two stars that resolve to *different* canonicals is
384
+ * ambiguous and excluded (same canonical through a diamond is included
385
+ * once)
386
+ * - `default` is never star-projected — including canonical Svelte component
387
+ * declarations, which represent their file's default export. (Caveat: a
388
+ * star-projected re-export edge whose canonical is a component is treated
389
+ * as a default-slot re-export and skipped; a `<script module>` const
390
+ * sharing the component's exact name would be skipped with it.)
391
+ *
392
+ * A Position-3 alias and its `reExports` edge are the same fact — the
393
+ * declaration entry wins, inheriting the edge's `typeOnly`.
394
+ *
395
+ * Cyclic star graphs terminate by contributing nothing along the back-edge
396
+ * (an approximation of ES fixpoint resolution — fine in practice). Surfaces
397
+ * resolved inside a cycle are path-relative and not memoized, so sibling
398
+ * star paths resolve independently rather than inheriting them. Star
399
+ * targets missing from `modules` are reported in `unresolvedStarExports`
400
+ * rather than guessed at; `externalStarExports` aggregates external star
401
+ * specifiers reachable from the module, whose names are unknowable.
402
+ *
403
+ * @param modules - the analyzed modules (parsed `ModuleJson`s — run wire
404
+ * JSON through `AnalyzeResultJson.parse` first)
405
+ * @param path - the module to resolve, as a `ModuleJson.path` value
406
+ * @returns the resolved surface, or `null` when `path` isn't in `modules`
407
+ */
408
+ export const resolveExportSurface = (modules, path) => {
409
+ const byPath = new Map(modules.map((m) => [m.path, m]));
410
+ if (!byPath.has(path))
411
+ return null;
412
+ const byIdentity = buildDeclarationIndex(modules);
413
+ const cache = new Map();
414
+ const visiting = new Set();
415
+ const emptySurface = () => ({
416
+ entries: new Map(),
417
+ unresolvedStars: new Set(),
418
+ externalStars: new Set(),
419
+ });
420
+ // Only surfaces resolved without hitting a cycle back-edge are memoized.
421
+ // A surface computed while one of its star ancestors is mid-resolution is
422
+ // relative to that path — it can carry names a full resolution would
423
+ // exclude as ambiguous (the back-edge contributed nothing to weigh them
424
+ // against). Caching it would leak the path-relative answer to sibling
425
+ // star paths; tainted surfaces are instead recomputed per consumer.
426
+ // Recomputation is bounded by `visiting` and only occurs inside cyclic
427
+ // clusters, which are pathological to begin with.
428
+ const resolveModule = (modulePath) => {
429
+ const cached = cache.get(modulePath);
430
+ if (cached)
431
+ return { surface: cached, tainted: false };
432
+ // Cycle break: a star back-edge contributes nothing
433
+ if (visiting.has(modulePath))
434
+ return { surface: emptySurface(), tainted: true };
435
+ visiting.add(modulePath);
436
+ const mod = byPath.get(modulePath);
437
+ const surface = emptySurface();
438
+ let tainted = false;
439
+ const { entries } = surface;
440
+ // 1. Own declarations — including synthesized aliases and `default`
441
+ const edgesByName = new Map(mod.reExports.map((e) => [e.name, e]));
442
+ for (const declaration of mod.declarations) {
443
+ // A Position-3 alias duplicates its edge; carry the edge's typeOnly
444
+ const matchingEdge = declaration.aliasOf &&
445
+ edgesByName.get(declaration.name)?.module === declaration.aliasOf.module
446
+ ? edgesByName.get(declaration.name)
447
+ : undefined;
448
+ entries.set(declaration.name, {
449
+ entry: {
450
+ name: declaration.name,
451
+ via: 'declaration',
452
+ module: modulePath,
453
+ declaration,
454
+ ...(matchingEdge?.typeOnly ? { typeOnly: true } : {}),
455
+ },
456
+ identity: resolveCanonicalIdentity(byIdentity, modulePath, declaration),
457
+ });
458
+ }
459
+ // 2. Same-name re-export edges not already covered by a declaration.
460
+ // Edges can collide on `name` (Svelte default re-keying — see
461
+ // `ReExportJson`); the surface is keyed by name, so the first edge in
462
+ // sort order wins and the collider is dropped
463
+ for (const edge of mod.reExports) {
464
+ if (entries.has(edge.name))
465
+ continue;
466
+ const canonical = byIdentity.get(`${edge.module}\n${edge.name}`);
467
+ entries.set(edge.name, {
468
+ entry: {
469
+ name: edge.name,
470
+ via: 'reExport',
471
+ module: edge.module,
472
+ ...(canonical ? { declaration: canonical } : {}),
473
+ ...(edge.typeOnly ? { typeOnly: true } : {}),
474
+ },
475
+ identity: canonical
476
+ ? resolveCanonicalIdentity(byIdentity, edge.module, canonical)
477
+ : `${edge.module}\n${edge.name}`,
478
+ });
479
+ }
480
+ // 3. Direct external re-exports
481
+ for (const external of mod.externalReExports) {
482
+ if (entries.has(external.name))
483
+ continue;
484
+ entries.set(external.name, {
485
+ entry: {
486
+ name: external.name,
487
+ via: 'external',
488
+ specifier: external.specifier,
489
+ ...(external.originalName !== undefined ? { originalName: external.originalName } : {}),
490
+ ...(external.typeOnly ? { typeOnly: true } : {}),
491
+ },
492
+ identity: `ext\n${external.specifier}\n${external.originalName ?? external.name}`,
493
+ });
494
+ }
495
+ // 4. External stars — names unknowable, surface incompleteness recorded
496
+ for (const specifier of mod.externalStarExports) {
497
+ surface.externalStars.add(specifier);
498
+ }
499
+ // 5. Star projection, with ES shadowing and ambiguity rules
500
+ const excluded = new Set();
501
+ for (const target of mod.starExports) {
502
+ if (!byPath.has(target)) {
503
+ surface.unresolvedStars.add(target);
504
+ continue;
505
+ }
506
+ const resolved = resolveModule(target);
507
+ if (resolved.tainted)
508
+ tainted = true;
509
+ const sub = resolved.surface;
510
+ for (const star of sub.unresolvedStars)
511
+ surface.unresolvedStars.add(star);
512
+ for (const star of sub.externalStars)
513
+ surface.externalStars.add(star);
514
+ for (const { entry, identity } of sub.entries.values()) {
515
+ // `default` never projects — nor do canonical Svelte components,
516
+ // which represent their file's default export (whether reached
517
+ // as a declaration or through a re-export edge)
518
+ if (entry.name === 'default')
519
+ continue;
520
+ if (entry.declaration?.kind === 'component' && !entry.declaration.aliasOf)
521
+ continue;
522
+ if (excluded.has(entry.name))
523
+ continue;
524
+ const existing = entries.get(entry.name);
525
+ if (existing) {
526
+ // Explicit exports shadow; identical canonicals (diamond) merge
527
+ if (existing.entry.via !== 'star' || existing.identity === identity)
528
+ continue;
529
+ // Ambiguous between two stars — excluded per ES semantics
530
+ entries.delete(entry.name);
531
+ excluded.add(entry.name);
532
+ continue;
533
+ }
534
+ entries.set(entry.name, {
535
+ entry: { ...entry, via: 'star', starFrom: target },
536
+ identity,
537
+ });
538
+ }
539
+ }
540
+ visiting.delete(modulePath);
541
+ if (!tainted)
542
+ cache.set(modulePath, surface);
543
+ return { surface, tainted };
544
+ };
545
+ const resolved = resolveModule(path).surface;
546
+ return {
547
+ entries: Array.from(resolved.entries.values())
548
+ .map(({ entry }) => entry)
549
+ .sort((a, b) => compareStrings(a.name, b.name)),
550
+ unresolvedStarExports: Array.from(resolved.unresolvedStars).sort(),
551
+ externalStarExports: Array.from(resolved.externalStars).sort(),
552
+ };
553
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"svelte.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/svelte.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAC,QAAQ,EAAsB,MAAM,2BAA2B,CAAC;AAGxE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE5B,OAAO,KAAK,EAA2C,kBAAkB,EAAC,MAAM,YAAY,CAAC;AAC7F,OAAO,KAAK,EAGX,cAAc,EACd,MAAM,wBAAwB,CAAC;AAKhC,OAAO,EAAC,KAAK,cAAc,EAA0C,MAAM,aAAa,CAAC;AACzF,OAAO,EAAC,KAAK,mBAAmB,EAAsB,MAAM,oBAAoB,CAAC;AACjF,OAAO,EAAC,KAAK,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAGjD,0FAA0F;AAC1F,KAAK,SAAS,GAAG,YAAY,CAAC,OAAO,QAAQ,CAAC,CAAC;AAE/C;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IACjC,gEAAgE;IAChE,WAAW,EAAE,MAAM,CAAC;IACpB,iDAAiD;IACjD,OAAO,EAAE,MAAM,CAAC;IAChB,2EAA2E;IAC3E,SAAS,EAAE,SAAS,GAAG,IAAI,CAAC;IAC5B,sGAAsG;IACtG,IAAI,EAAE,IAAI,GAAG,SAAS,CAAC;CACvB;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,eAAe;IAC/B,OAAO,EAAE,iBAAiB,GAAG,SAAS,CAAC;IACvC,WAAW,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;CAC/B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,qBAAqB,GAAI,YAAY,cAAc,KAAG,eAoDlE,CAAC;AAkHF;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,GAAI,cAAc,MAAM,KAAG,MAAM,GAAG,SAcpE,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,wBAAwB,GAAI,cAAc,MAAM,KAAG,MAAM,GAAG,SA0BxE,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,0BAA0B,GAAI,eAAe,MAAM,KAAG,MAAM,GAAG,SAU3E,CAAC;AA0PF;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,GAAI,YAAY,MAAM,KAAG,OAExD,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,wBAAwB,GACpC,aAAa,EAAE,CAAC,IAAI,EACpB,SAAS,EAAE,CAAC,WAAW,KACrB,KAAK,CAAC,kBAAkB,CAqB1B,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,mBAAmB,GAAI,YAAY,MAAM,KAAG,OAKxD,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,8BAA8B,GAAI,YAAY,KAAK,CAAC,kBAAkB,CAAC,KAAG,MAGtF,CAAC;AA8NF;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,mBAAmB,GAC/B,YAAY,cAAc,GAAG;IAAC,UAAU,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;CAAC,EACjE,YAAY,MAAM,EAClB,SAAS,EAAE,CAAC,WAAW,EACvB,SAAS,mBAAmB,EAC5B,aAAa,KAAK,CAAC,UAAU,CAAC,EAC9B,SAAS,EAAE,CAAC,OAAO,EACnB,aAAa,iBAAiB,KAC5B,cAAc,GAAG,SAuNnB,CAAC"}
1
+ {"version":3,"file":"svelte.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/svelte.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAC,QAAQ,EAAsB,MAAM,2BAA2B,CAAC;AAGxE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE5B,OAAO,KAAK,EAA2C,kBAAkB,EAAC,MAAM,YAAY,CAAC;AAC7F,OAAO,KAAK,EAGX,cAAc,EACd,MAAM,wBAAwB,CAAC;AAKhC,OAAO,EAAC,KAAK,cAAc,EAA0C,MAAM,aAAa,CAAC;AACzF,OAAO,EAAC,KAAK,mBAAmB,EAAsB,MAAM,oBAAoB,CAAC;AACjF,OAAO,EAAC,KAAK,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAGjD,0FAA0F;AAC1F,KAAK,SAAS,GAAG,YAAY,CAAC,OAAO,QAAQ,CAAC,CAAC;AAE/C;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IACjC,gEAAgE;IAChE,WAAW,EAAE,MAAM,CAAC;IACpB,iDAAiD;IACjD,OAAO,EAAE,MAAM,CAAC;IAChB,2EAA2E;IAC3E,SAAS,EAAE,SAAS,GAAG,IAAI,CAAC;IAC5B,sGAAsG;IACtG,IAAI,EAAE,IAAI,GAAG,SAAS,CAAC;CACvB;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,eAAe;IAC/B,OAAO,EAAE,iBAAiB,GAAG,SAAS,CAAC;IACvC,WAAW,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;CAC/B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,qBAAqB,GAAI,YAAY,cAAc,KAAG,eAoDlE,CAAC;AAkHF;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,GAAI,cAAc,MAAM,KAAG,MAAM,GAAG,SAcpE,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,wBAAwB,GAAI,cAAc,MAAM,KAAG,MAAM,GAAG,SA0BxE,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,0BAA0B,GAAI,eAAe,MAAM,KAAG,MAAM,GAAG,SAU3E,CAAC;AA0PF;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,GAAI,YAAY,MAAM,KAAG,OAExD,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,wBAAwB,GACpC,aAAa,EAAE,CAAC,IAAI,EACpB,SAAS,EAAE,CAAC,WAAW,KACrB,KAAK,CAAC,kBAAkB,CAqB1B,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,mBAAmB,GAAI,YAAY,MAAM,KAAG,OAKxD,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,8BAA8B,GAAI,YAAY,KAAK,CAAC,kBAAkB,CAAC,KAAG,MAGtF,CAAC;AA8NF;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,mBAAmB,GAC/B,YAAY,cAAc,GAAG;IAAC,UAAU,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;CAAC,EACjE,YAAY,MAAM,EAClB,SAAS,EAAE,CAAC,WAAW,EACvB,SAAS,mBAAmB,EAC5B,aAAa,KAAK,CAAC,UAAU,CAAC,EAC9B,SAAS,EAAE,CAAC,OAAO,EACnB,aAAa,iBAAiB,KAC5B,cAAc,GAAG,SAoQnB,CAAC"}
package/dist/svelte.js CHANGED
@@ -728,7 +728,7 @@ export const analyzeSvelteModule = (sourceFile, modulePath, checker, options, di
728
728
  return undefined;
729
729
  }
730
730
  // 1. Use analyzeExports for full checker-backed analysis (same as .ts files)
731
- const { declarations: rawDeclarations, reExports, starExports, moduleComment: scriptModuleComment, } = analyzeExports(virtualTsSource, checker, options, diagnostics);
731
+ const { declarations: rawDeclarations, reExports, starExports, externalReExports, externalStarExports, moduleComment: scriptModuleComment, } = analyzeExports(virtualTsSource, checker, options, diagnostics);
732
732
  // 2. Filter internal svelte2tsx symbols and the default export (generated component class),
733
733
  // and reclassify exported snippets from 'function' to 'snippet'
734
734
  const moduleDeclarations = [];
@@ -761,9 +761,24 @@ export const analyzeSvelteModule = (sourceFile, modulePath, checker, options, di
761
761
  }
762
762
  // 2b. Remap source lines for module-level exports using source map.
763
763
  // analyzeExports extracts sourceLine from virtual file positions — remap to original .svelte.
764
- if (virtualFile.sourceMap && moduleDeclarations.length > 0) {
764
+ if (virtualFile.sourceMap) {
765
765
  // Build name→node map from virtual source statements
766
766
  const nodesByName = new Map();
767
+ // Export-statement bindings (specifiers + `* as ns`), kept separate:
768
+ // `const foo = 1; export {foo}` would otherwise collide — the
769
+ // declaration's line should point at the const, the synthesized
770
+ // alias's at the specifier.
771
+ const exportNodesByName = new Map();
772
+ // Virtual line → export binding node, for re-export edges. Edges
773
+ // can't be matched by name (Svelte default-slot re-keying renames
774
+ // them), but every edge's virtual sourceLine is some binding's line.
775
+ const exportNodesByLine = new Map();
776
+ const addExportNode = (name, node) => {
777
+ exportNodesByName.set(name, node);
778
+ const line = virtualTsSource.getLineAndCharacterOfPosition(node.getStart(virtualTsSource)).line + 1;
779
+ if (!exportNodesByLine.has(line))
780
+ exportNodesByLine.set(line, node);
781
+ };
767
782
  for (const stmt of virtualTsSource.statements) {
768
783
  if (ts.isVariableStatement(stmt)) {
769
784
  for (const decl of stmt.declarationList.declarations) {
@@ -787,11 +802,26 @@ export const analyzeSvelteModule = (sourceFile, modulePath, checker, options, di
787
802
  else if (ts.isEnumDeclaration(stmt)) {
788
803
  nodesByName.set(stmt.name.text, stmt);
789
804
  }
805
+ else if (ts.isExportDeclaration(stmt)) {
806
+ if (stmt.exportClause && ts.isNamedExports(stmt.exportClause)) {
807
+ for (const spec of stmt.exportClause.elements) {
808
+ addExportNode(spec.name.text, spec);
809
+ }
810
+ }
811
+ else if (stmt.exportClause && ts.isNamespaceExport(stmt.exportClause)) {
812
+ addExportNode(stmt.exportClause.name.text, stmt.exportClause);
813
+ }
814
+ }
790
815
  }
791
816
  for (const d of moduleDeclarations) {
792
817
  if (d.declaration.name === undefined)
793
818
  continue;
794
- const node = nodesByName.get(d.declaration.name);
819
+ // Synthesized aliases and namespace declarations point at their
820
+ // export statement; everything else at its value declaration
821
+ const fromExportStatement = d.declaration.aliasOf !== undefined || d.declaration.kind === 'namespace';
822
+ const node = fromExportStatement
823
+ ? exportNodesByName.get(d.declaration.name)
824
+ : nodesByName.get(d.declaration.name);
795
825
  if (node) {
796
826
  const mapped = mapVirtualPosition(node, virtualTsSource, virtualFile.sourceMap);
797
827
  if (mapped.line !== undefined) {
@@ -799,6 +829,18 @@ export const analyzeSvelteModule = (sourceFile, modulePath, checker, options, di
799
829
  }
800
830
  }
801
831
  }
832
+ // Remap edge lines by virtual line (name-independent — see above)
833
+ for (const edge of [...reExports, ...externalReExports]) {
834
+ if (edge.sourceLine === undefined)
835
+ continue;
836
+ const node = exportNodesByLine.get(edge.sourceLine);
837
+ if (node) {
838
+ const mapped = mapVirtualPosition(node, virtualTsSource, virtualFile.sourceMap);
839
+ if (mapped.line !== undefined) {
840
+ edge.sourceLine = mapped.line;
841
+ }
842
+ }
843
+ }
802
844
  }
803
845
  // 3. Synthesize component declaration
804
846
  const componentName = getComponentName(modulePath);
@@ -894,5 +936,7 @@ export const analyzeSvelteModule = (sourceFile, modulePath, checker, options, di
894
936
  dependents,
895
937
  starExports,
896
938
  reExports,
939
+ externalReExports,
940
+ externalStarExports,
897
941
  };
898
942
  };
package/dist/types.d.ts CHANGED
@@ -2393,6 +2393,96 @@ export declare const DeclarationJson: z.ZodDiscriminatedUnion<[
2393
2393
  ], 'kind'>;
2394
2394
  export type DeclarationJson = z.infer<typeof DeclarationJson>;
2395
2395
  export type DeclarationJsonInput = z.input<typeof DeclarationJson>;
2396
+ /**
2397
+ * A same-name re-export edge, from the re-exporting module's side.
2398
+ *
2399
+ * Each entry on `ModuleJson.reExports` records that the module's source
2400
+ * contains `export {name} from ...` where the canonical declaration lives in
2401
+ * `module`. This is the forward view of the same fact that
2402
+ * `alsoExportedFrom` records on the canonical declaration — consumers
2403
+ * resolve the canonical by `(module, name)`, the same lookup contract as
2404
+ * `aliasOf`.
2405
+ *
2406
+ * Same-name re-exports of analyzed source only. Renamed re-exports appear as
2407
+ * declarations with `aliasOf` in the re-exporting module; star exports live
2408
+ * in `ModuleJson.starExports`; external re-exports in
2409
+ * `ModuleJson.externalReExports` / `externalStarExports`. Use
2410
+ * `resolveExportSurface` (in `postprocess.ts`) to combine all of these into
2411
+ * a module's full export surface — it handles the name dedup (a documented
2412
+ * same-name re-export appears both here and as a synthesized alias
2413
+ * declaration) and the ES star semantics (explicit exports shadow
2414
+ * star-projected ones, names ambiguous between stars are excluded,
2415
+ * `default` doesn't project).
2416
+ *
2417
+ * `module` points at the *canonical* module (multi-hop chains are resolved),
2418
+ * not the immediate specifier in the export statement. For Svelte default-slot
2419
+ * re-exports (`export {default} from './X.svelte'`), `name` is the component
2420
+ * name derived from the filename (`'X'`), matching the canonical declaration's
2421
+ * name — the same exception documented on `aliasOf`. Because of that
2422
+ * re-keying, two entries in one module can share a `name` (a re-keyed
2423
+ * component colliding with a same-name re-export from another module);
2424
+ * `(module, name)` pairs remain unique (exact duplicates are deduped at
2425
+ * construction).
2426
+ *
2427
+ * `@nodocs` on the export statement suppresses the entry (and the
2428
+ * `alsoExportedFrom` back-link). When the canonical declaration itself is
2429
+ * `@nodocs`, or the canonical module isn't part of the analyzed set, the
2430
+ * entry still exists but has no matching back-link — the same presence
2431
+ * caveat as `aliasOf.module` and `starExports`.
2432
+ */
2433
+ export declare const ReExportJson: z.ZodObject<{
2434
+ /** Exported name — the canonical declaration's name in `module`. */
2435
+ name: z.ZodString;
2436
+ /** Module path (relative to `sourceRoot`) where the canonical declaration lives. */
2437
+ module: z.ZodString;
2438
+ /**
2439
+ * Whether the statement (`export type {A} from ...`) or specifier
2440
+ * (`export {type A} from ...`) is type-only — the name is erased at
2441
+ * runtime and importable only via `import type`.
2442
+ */
2443
+ typeOnly: z.ZodDefault<z.ZodBoolean>;
2444
+ /**
2445
+ * 1-based line of the export specifier in this module's source. When
2446
+ * identical `(name, module)` edges are deduped (Svelte default-slot
2447
+ * re-keying), the smallest line is kept.
2448
+ */
2449
+ sourceLine: z.ZodOptional<z.ZodNumber>;
2450
+ }, z.core.$strict>;
2451
+ export type ReExportJson = z.infer<typeof ReExportJson>;
2452
+ export type ReExportJsonInput = z.input<typeof ReExportJson>;
2453
+ /**
2454
+ * A re-export whose immediate target is outside the analyzed source set —
2455
+ * `export {x} from 'pkg'`, `export {x as y} from 'pkg'`, or
2456
+ * `export * as ns from 'pkg'`.
2457
+ *
2458
+ * `specifier` is the module specifier as written (usually a package name);
2459
+ * there is no canonical declaration to resolve, so these entries are flat
2460
+ * facts about the statement rather than edges into the module graph.
2461
+ *
2462
+ * Only statements that *directly* reference the external specifier are
2463
+ * captured. Forms that stay silent: `import {x} from 'pkg'; export {x};`
2464
+ * (import-then-export), re-export chains that reach a package through
2465
+ * another source module (that module owns the entry), and specifiers the
2466
+ * checker can't resolve. Statement-level `@nodocs` suppresses the entry.
2467
+ */
2468
+ export declare const ExternalReExportJson: z.ZodObject<{
2469
+ /** Public exported name from this module. */
2470
+ name: z.ZodString;
2471
+ /** Module specifier as written in the statement (e.g. `'pkg'`). */
2472
+ specifier: z.ZodString;
2473
+ /**
2474
+ * The name inside the external module when renamed
2475
+ * (`export {x as y} from 'pkg'` → `'x'`). Omitted for same-name
2476
+ * re-exports and namespace form (`export * as ns`).
2477
+ */
2478
+ originalName: z.ZodOptional<z.ZodString>;
2479
+ /** Whether the statement or specifier is type-only — see `ReExportJson.typeOnly`. */
2480
+ typeOnly: z.ZodDefault<z.ZodBoolean>;
2481
+ /** 1-based line of the export specifier in this module's source. */
2482
+ sourceLine: z.ZodOptional<z.ZodNumber>;
2483
+ }, z.core.$strict>;
2484
+ export type ExternalReExportJson = z.infer<typeof ExternalReExportJson>;
2485
+ export type ExternalReExportJsonInput = z.input<typeof ExternalReExportJson>;
2396
2486
  /**
2397
2487
  * Metadata for a source module — the top-level container in the data model.
2398
2488
  *
@@ -4114,9 +4204,63 @@ export declare const ModuleJson: z.ZodObject<{
4114
4204
  dependents: z.ZodDefault<z.ZodArray<z.ZodString>>;
4115
4205
  /**
4116
4206
  * Modules fully re-exported via `export * from './module'`.
4117
- * Paths are relative to `sourceRoot`.
4207
+ * Paths are relative to `sourceRoot`. Statement-level `@nodocs`
4208
+ * suppresses the entry, like the other re-export encodings.
4118
4209
  */
4119
4210
  starExports: z.ZodDefault<z.ZodArray<z.ZodString>>;
4211
+ /**
4212
+ * Same-name re-exports in this module's source, sorted by `name` then
4213
+ * `module` (names can collide — see `ReExportJson`). The forward view of
4214
+ * `alsoExportedFrom` — see `ReExportJson` for the full contract (canonical
4215
+ * resolution, Svelte default-slot naming, `@nodocs` suppression, presence
4216
+ * caveats).
4217
+ */
4218
+ reExports: z.ZodDefault<z.ZodArray<z.ZodObject<{
4219
+ /** Exported name — the canonical declaration's name in `module`. */
4220
+ name: z.ZodString;
4221
+ /** Module path (relative to `sourceRoot`) where the canonical declaration lives. */
4222
+ module: z.ZodString;
4223
+ /**
4224
+ * Whether the statement (`export type {A} from ...`) or specifier
4225
+ * (`export {type A} from ...`) is type-only — the name is erased at
4226
+ * runtime and importable only via `import type`.
4227
+ */
4228
+ typeOnly: z.ZodDefault<z.ZodBoolean>;
4229
+ /**
4230
+ * 1-based line of the export specifier in this module's source. When
4231
+ * identical `(name, module)` edges are deduped (Svelte default-slot
4232
+ * re-keying), the smallest line is kept.
4233
+ */
4234
+ sourceLine: z.ZodOptional<z.ZodNumber>;
4235
+ }, z.core.$strict>>>;
4236
+ /**
4237
+ * Re-exports whose immediate target is an external package, sorted by
4238
+ * `name` then `specifier`. See `ExternalReExportJson` for which forms
4239
+ * are captured.
4240
+ */
4241
+ externalReExports: z.ZodDefault<z.ZodArray<z.ZodObject<{
4242
+ /** Public exported name from this module. */
4243
+ name: z.ZodString;
4244
+ /** Module specifier as written in the statement (e.g. `'pkg'`). */
4245
+ specifier: z.ZodString;
4246
+ /**
4247
+ * The name inside the external module when renamed
4248
+ * (`export {x as y} from 'pkg'` → `'x'`). Omitted for same-name
4249
+ * re-exports and namespace form (`export * as ns`).
4250
+ */
4251
+ originalName: z.ZodOptional<z.ZodString>;
4252
+ /** Whether the statement or specifier is type-only — see `ReExportJson.typeOnly`. */
4253
+ typeOnly: z.ZodDefault<z.ZodBoolean>;
4254
+ /** 1-based line of the export specifier in this module's source. */
4255
+ sourceLine: z.ZodOptional<z.ZodNumber>;
4256
+ }, z.core.$strict>>>;
4257
+ /**
4258
+ * External modules fully re-exported via `export * from 'pkg'`, as
4259
+ * written (statement order). The projected names are unknown — the
4260
+ * package isn't analyzed. Statement-level `@nodocs` suppresses the
4261
+ * entry; unresolvable specifiers are skipped.
4262
+ */
4263
+ externalStarExports: z.ZodDefault<z.ZodArray<z.ZodString>>;
4120
4264
  /**
4121
4265
  * Whether the module is a placeholder for a file that couldn't be analyzed.
4122
4266
  *