svelte-docinfo 0.2.0 → 0.3.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.
package/README.md CHANGED
@@ -37,15 +37,14 @@ but instead of AST-only inspection it uses the TypeScript compiler API for riche
37
37
  and also analyzes TypeScript modules.
38
38
  The docs [compare](https://svelte-docinfo.fuz.dev/docs/introduction#Compared-to-sveld) them.
39
39
 
40
- This is an early release --
41
- there are gaps to be filled and design choices to make,
42
- but for Svelte 5 it's mostly complete and used in production websites.
43
- Please open issues for bugs
40
+ The library is mostly complete for Svelte 5 and used in production websites,
41
+ but you may find gaps and flaws -- please open issues for bugs
44
42
  and [discussions](https://github.com/fuzdev/svelte-docinfo/discussions)
45
43
  for everything else!
46
44
 
47
- **AI disclosure:** the code and docs beyond the intro were written by Claude Code with uneven human guidance.
48
- The first release took about 5 months of intermittent work and 500 manual commits.
45
+ **AI disclosure:** the code and docs beyond the intro
46
+ were mostly written by Claude Code with uneven human guidance.
47
+ The first release took 5 months of intermittent work and ~500 manual commits.
49
48
 
50
49
  ## Quick start
51
50
 
@@ -95,7 +94,8 @@ Three ways to integrate `svelte-docinfo`:
95
94
  import svelteDocinfo from 'svelte-docinfo/vite.js';
96
95
 
97
96
  export default defineConfig({plugins: [sveltekit(), svelteDocinfo()]});
98
- // then anywhere in your app: import {modules} from 'virtual:svelte-docinfo';
97
+ // then anywhere in your app:
98
+ // import {modules} from 'virtual:svelte-docinfo';
99
99
  ```
100
100
 
101
101
  2. [CLI](#cli) - quick analysis from the command line, useful for ad-hoc inspection and shell pipelines:
@@ -174,6 +174,8 @@ All three produce the same JSON shape:
174
174
  }
175
175
  ```
176
176
 
177
+ For many more worked examples — source input paired with the exact JSON `svelte-docinfo` emits — browse the [test fixtures](https://github.com/fuzdev/svelte-docinfo/tree/main/src/test/fixtures), where each case sits beside its `expected.json`.
178
+
177
179
  ## Vite plugin
178
180
 
179
181
  1. Add the plugin to `vite.config.ts`:
@@ -336,7 +338,8 @@ Known gaps: standalone `namespace Foo {}` declarations and decorators are not ye
336
338
  was this project's main inspiration.
337
339
 
338
340
  Extracted from [fuz_ui](https://github.com/fuzdev/fuz_ui),
339
- which has example components using the data for docs websites.
341
+ which has example components using the data for docs websites like
342
+ [`DeclarationDetail`](https://github.com/fuzdev/fuz_ui/blob/main/src/lib/DeclarationDetail.svelte).
340
343
 
341
344
  `svelte-docinfo` is more featureful than it would be
342
345
  otherwise thanks to LLM assistance, mostly Claude Code.
@@ -2,8 +2,8 @@
2
2
  * Pure two-phase analysis orchestrator.
3
3
  *
4
4
  * Extracted from `analyze.ts` to break what would otherwise be a circular
5
- * import: `session.ts → analyze.ts (for the orchestrator) → session.ts (for
6
- * the wrappers)`. With the orchestrator here, both `session.ts` and
5
+ * import: `session.ts` → `analyze.ts` (for the orchestrator) → `session.ts`
6
+ * (for the wrappers). With the orchestrator here, both `session.ts` and
7
7
  * `analyze.ts` import downward into this module without depending on each
8
8
  * other.
9
9
  *
@@ -2,8 +2,8 @@
2
2
  * Pure two-phase analysis orchestrator.
3
3
  *
4
4
  * Extracted from `analyze.ts` to break what would otherwise be a circular
5
- * import: `session.ts → analyze.ts (for the orchestrator) → session.ts (for
6
- * the wrappers)`. With the orchestrator here, both `session.ts` and
5
+ * import: `session.ts` → `analyze.ts` (for the orchestrator) → `session.ts`
6
+ * (for the wrappers). With the orchestrator here, both `session.ts` and
7
7
  * `analyze.ts` import downward into this module without depending on each
8
8
  * other.
9
9
  *
@@ -1 +1 @@
1
- {"version":3,"file":"exports.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/exports.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAMH,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAMjD;;GAEG;AACH,MAAM,WAAW,WAAW;IAC3B,sDAAsD;IACtD,SAAS,EAAE,MAAM,CAAC;IAClB,uDAAuD;IACvD,SAAS,EAAE,OAAO,CAAC;IACnB,2GAA2G;IAC3G,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACvC,qCAAqC;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2EAA2E;IAC3E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sDAAsD;IACtD,OAAO,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CACxB;AAED;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACtC;;;;OAIG;IACH,KAAK,EAAE,KAAK,CAAC,cAAc,CAAC,GAAG,IAAI,CAAC;IACpC,oEAAoE;IACpE,WAAW,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;CAC/B;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC7B,iCAAiC;IACjC,OAAO,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC;IAC5B,qDAAqD;IACrD,UAAU,EAAE,OAAO,CAAC;CACpB;AASD;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,GAAU,aAAa,MAAM,KAAG,OAAO,CAAC,aAAa,CA+BpF,CAAC;AAiCF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,eAAe,GAC3B,UAAU,MAAM,EAChB,WAAW,MAAM,EACjB,SAAS;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAC,KAC3C,MAAM,GAAG,IAwCX,CAAC;AAkBF;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,mBAAmB,GAC/B,SAAS,uBAAuB,KAC9B,OAAO,CAAC,sBAAsB,CA0DhC,CAAC"}
1
+ {"version":3,"file":"exports.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/exports.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAMH,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAMjD;;GAEG;AACH,MAAM,WAAW,WAAW;IAC3B,sDAAsD;IACtD,SAAS,EAAE,MAAM,CAAC;IAClB,uDAAuD;IACvD,SAAS,EAAE,OAAO,CAAC;IACnB,2GAA2G;IAC3G,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACvC,qCAAqC;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2EAA2E;IAC3E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sDAAsD;IACtD,OAAO,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CACxB;AAED;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACtC;;;;OAIG;IACH,KAAK,EAAE,KAAK,CAAC,cAAc,CAAC,GAAG,IAAI,CAAC;IACpC,oEAAoE;IACpE,WAAW,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;CAC/B;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC7B,iCAAiC;IACjC,OAAO,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC;IAC5B,qDAAqD;IACrD,UAAU,EAAE,OAAO,CAAC;CACpB;AASD;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,GAAU,aAAa,MAAM,KAAG,OAAO,CAAC,aAAa,CA+BpF,CAAC;AAoCF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,eAAe,GAC3B,UAAU,MAAM,EAChB,WAAW,MAAM,EACjB,SAAS;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAC,KAC3C,MAAM,GAAG,IAwCX,CAAC;AAkBF;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,mBAAmB,GAC/B,SAAS,uBAAuB,KAC9B,OAAO,CAAC,sBAAsB,CA0DhC,CAAC"}
package/dist/exports.js CHANGED
@@ -71,7 +71,10 @@ const flattenConditions = (value, prefix) => {
71
71
  }
72
72
  // Object = conditions map (possibly nested)
73
73
  if (typeof value === 'object' && !Array.isArray(value)) {
74
- const result = {};
74
+ // Null-prototype map: condition keys come from package.json `exports` (external
75
+ // input) and are read back by key in `selectCondition` (`key in conditions`,
76
+ // `conditions[key]`); avoids prototype keys leaking into membership/lookup.
77
+ const result = Object.create(null);
75
78
  for (const [key, nested] of Object.entries(value)) {
76
79
  const flattened = flattenConditions(nested, key);
77
80
  if (flattened) {
package/dist/svelte.d.ts CHANGED
@@ -15,7 +15,7 @@
15
15
  * There is no Svelte 4 compatibility layer.
16
16
  *
17
17
  * @see `typescript-exports.ts` for `analyzeExports`, `extractModuleComment`
18
- * @see `typescript-extract-shared.ts` for `parseGenericParam`, `filterIntersectionProperties`
18
+ * @see `typescript-extract-shared.ts` for `parseGenericParam`, `filterExternalProperties`
19
19
  * @see `typescript-program.ts` for `IsExternalFile`, `createIsExternalFile`
20
20
  * @see `tsdoc.ts` for `parseComment`, `applyToDeclaration`
21
21
  * @see `source.ts` for `SourceFileInfo`, `getComponentName`
package/dist/svelte.js CHANGED
@@ -15,7 +15,7 @@
15
15
  * There is no Svelte 4 compatibility layer.
16
16
  *
17
17
  * @see `typescript-exports.ts` for `analyzeExports`, `extractModuleComment`
18
- * @see `typescript-extract-shared.ts` for `parseGenericParam`, `filterIntersectionProperties`
18
+ * @see `typescript-extract-shared.ts` for `parseGenericParam`, `filterExternalProperties`
19
19
  * @see `typescript-program.ts` for `IsExternalFile`, `createIsExternalFile`
20
20
  * @see `tsdoc.ts` for `parseComment`, `applyToDeclaration`
21
21
  * @see `source.ts` for `SourceFileInfo`, `getComponentName`
@@ -28,7 +28,7 @@ import { svelte2tsx } from 'svelte2tsx';
28
28
  import ts from 'typescript';
29
29
  import { parseComment, applyToDeclaration, hasModuleTag } from './tsdoc.js';
30
30
  import { createIsExternalFile } from './typescript-program.js';
31
- import { parseGenericParam, filterIntersectionProperties } from './typescript-extract-shared.js';
31
+ import { parseGenericParam, filterExternalProperties } from './typescript-extract-shared.js';
32
32
  import { extractModuleComment, analyzeExports } from './typescript-exports.js';
33
33
  import { getComponentName, SVELTE_VIRTUAL_SUFFIX } from './source.js';
34
34
  import { extractDependencies } from './source-config.js';
@@ -638,14 +638,14 @@ const extractPropsViaChecker = (virtualSource, checker, componentName, filePath,
638
638
  // Detect `acceptsChildren` via type inference: `children` must resolve to a
639
639
  // `Snippet<...>` type. Checking the symbol name alone (the previous approach)
640
640
  // misreports a non-Snippet `children` (e.g. `children: string`) as accepting
641
- // children. The lookup runs on the unfiltered intersection so inherited
641
+ // children. The lookup runs on the unfiltered props type so inherited
642
642
  // `children` from `SvelteHTMLElements`/`DOMAttributes` (declared as `Snippet`)
643
643
  // is honored even when its declaration lives in node_modules.
644
- const allProperties = propsType.getProperties();
645
644
  const acceptsChildren = detectChildrenSnippet(propsType, propsTypeNode, checker, diagnostics, componentName, filePath);
646
- // For intersection types, filter out properties from external sources (node_modules)
647
- const intersectionResult = filterIntersectionProperties(propsType, propsTypeNode, checker, isExternalFile);
648
- const properties = intersectionResult?.properties ?? allProperties;
645
+ // Drop properties contributed by external types (node_modules / svelte's
646
+ // element-attribute bags like `SvelteHTMLElements['li']`); those external
647
+ // types are summarized in `intersects` rather than enumerated as props.
648
+ const { properties, externalTypes } = filterExternalProperties(propsType, propsTypeNode, checker, isExternalFile);
649
649
  const props = [];
650
650
  for (const prop of properties) {
651
651
  const propDecl = prop.valueDeclaration || prop.declarations?.[0];
@@ -693,7 +693,7 @@ const extractPropsViaChecker = (virtualSource, checker, componentName, filePath,
693
693
  const propSourceFile = propDecl?.getSourceFile();
694
694
  props.push(assemblePropInfo(prop.name, typeString, optional, propDecl, propSourceFile, propsDefaults, bindableProps, snippetParams));
695
695
  }
696
- return { props, intersectionTypes: intersectionResult?.intersectionTypes, acceptsChildren };
696
+ return { props, externalTypes, acceptsChildren };
697
697
  };
698
698
  /**
699
699
  * Analyze a Svelte module using checker-backed analysis.
@@ -813,15 +813,15 @@ export const analyzeSvelteModule = (sourceFile, modulePath, checker, options, di
813
813
  const isExternalFile = createIsExternalFile(options);
814
814
  // Extract props via checker (resolves imported types)
815
815
  const { bindableProps, propsDefaults } = extractPropsMetadata(virtualTsSource);
816
- const { props, intersectionTypes, acceptsChildren: propsAcceptsChildren, } = extractPropsViaChecker(virtualTsSource, checker, componentName, modulePath, virtualFile.sourceMap, diagnostics, propsDefaults, bindableProps, isExternalFile);
816
+ const { props, externalTypes, acceptsChildren: propsAcceptsChildren, } = extractPropsViaChecker(virtualTsSource, checker, componentName, modulePath, virtualFile.sourceMap, diagnostics, propsDefaults, bindableProps, isExternalFile);
817
817
  if (props.length > 0) {
818
818
  componentDecl.props = props;
819
819
  }
820
- if (intersectionTypes?.length) {
821
- componentDecl.intersects = intersectionTypes;
820
+ if (externalTypes?.length) {
821
+ componentDecl.intersects = externalTypes;
822
822
  }
823
823
  // Determine acceptsChildren via two paths:
824
- // Path A: children found in props type (from extractPropsViaChecker, before intersection filtering)
824
+ // Path A: children found in props type (from extractPropsViaChecker, before external-property filtering)
825
825
  // Path B: implicit children usage in template (for components without $props() children declaration)
826
826
  let acceptsChildren = propsAcceptsChildren;
827
827
  if (!acceptsChildren) {
@@ -1 +1 @@
1
- {"version":3,"file":"tsdoc.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/tsdoc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAEH,OAAO,EAAE,MAAM,YAAY,CAAC;AAE5B,OAAO,KAAK,EAAC,oBAAoB,EAAE,eAAe,EAAC,MAAM,wBAAwB,CAAC;AAElF;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IAClC,gDAAgD;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,gDAAgD;IAChD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,oCAAoC;IACpC,MAAM,CAAC,EAAE,KAAK,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAC,CAAC,CAAC;IACrD,qCAAqC;IACrC,QAAQ,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACzB,8CAA8C;IAC9C,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,sCAAsC;IACtC,OAAO,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACxB,yCAAyC;IACzC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yCAAyC;IACzC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uFAAuF;IACvF,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,iEAAiE;IACjE,MAAM,CAAC,EAAE,OAAO,CAAC;CACjB;AAYD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,YAAY,GACxB,MAAM,EAAE,CAAC,IAAI,EACb,YAAY,EAAE,CAAC,UAAU,KACvB,kBAAkB,GAAG,SAoGvB,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,kBAAkB,GAC9B,aAAa,oBAAoB,GAAG,eAAe,EACnD,OAAO,kBAAkB,GAAG,SAAS,KACnC,IAgCF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,GAAI,MAAM,EAAE,CAAC,IAAI,KAAG,OACsB,CAAC;AAEpE;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,YAAY,GAAI,aAAa,MAAM,KAAG,MAAM,GAAG,SAW3D,CAAC"}
1
+ {"version":3,"file":"tsdoc.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/tsdoc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAEH,OAAO,EAAE,MAAM,YAAY,CAAC;AAE5B,OAAO,KAAK,EAAC,oBAAoB,EAAE,eAAe,EAAC,MAAM,wBAAwB,CAAC;AAElF;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IAClC,gDAAgD;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,gDAAgD;IAChD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,oCAAoC;IACpC,MAAM,CAAC,EAAE,KAAK,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAC,CAAC,CAAC;IACrD,qCAAqC;IACrC,QAAQ,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACzB,8CAA8C;IAC9C,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,sCAAsC;IACtC,OAAO,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACxB,yCAAyC;IACzC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yCAAyC;IACzC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uFAAuF;IACvF,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,iEAAiE;IACjE,MAAM,CAAC,EAAE,OAAO,CAAC;CACjB;AAYD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,YAAY,GACxB,MAAM,EAAE,CAAC,IAAI,EACb,YAAY,EAAE,CAAC,UAAU,KACvB,kBAAkB,GAAG,SA4GvB,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,kBAAkB,GAC9B,aAAa,oBAAoB,GAAG,eAAe,EACnD,OAAO,kBAAkB,GAAG,SAAS,KACnC,IAgCF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,GAAI,MAAM,EAAE,CAAC,IAAI,KAAG,OACsB,CAAC;AAEpE;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,YAAY,GAAI,aAAa,MAAM,KAAG,MAAM,GAAG,SAW3D,CAAC"}
package/dist/tsdoc.js CHANGED
@@ -85,7 +85,12 @@ export const parseComment = (node, sourceFile) => {
85
85
  if (tsdocComments.length === 0)
86
86
  return undefined;
87
87
  let fullText = '';
88
- const params = {};
88
+ // Null-prototype map: keys are parameter names parsed from source. Without it,
89
+ // a parameter named after an `Object.prototype` key (`constructor`, `toString`,
90
+ // `__proto__`, …) with no matching `@param` tag would read the inherited
91
+ // prototype value on lookup (`tsdocParams?.[param.name]`) instead of `undefined`,
92
+ // and writing such a key (`@param __proto__`) would pollute the prototype.
93
+ const params = Object.create(null);
89
94
  let returns;
90
95
  let throws;
91
96
  let examples;
@@ -161,7 +166,10 @@ export const parseComment = (node, sourceFile) => {
161
166
  if (match) {
162
167
  const paramName = match[1];
163
168
  const description = match[2].trim();
164
- (mutates ??= {})[paramName] = description;
169
+ // Null-prototype map: `paramName` comes from source (`\w+` matches
170
+ // `__proto__`, `constructor`, …); a plain object would let such a key
171
+ // pollute the prototype on write and read back later by key.
172
+ (mutates ??= Object.create(null))[paramName] = description;
165
173
  }
166
174
  }
167
175
  else if (tagName === 'nodocs') {
@@ -86,19 +86,23 @@ export declare const isExternalIntersectionBranch: (branchNode: ts.TypeNode, che
86
86
  */
87
87
  export declare const resolveIntersectionTypeNode: (type: ts.Type, typeNode: ts.Node) => ts.IntersectionTypeNode | undefined;
88
88
  /**
89
- * Filter intersection type properties to exclude those from external sources,
90
- * and extract type names for branches whose properties are all external.
91
- *
92
- * Returns `null` for non-intersection types (caller should use unfiltered properties).
93
- * For intersection types, returns filtered properties and external type names.
94
- *
95
- * Per-property filtering works through utility types (Partial, Pick, Readonly)
96
- * because TypeScript preserves original declaration sources on mapped type properties.
89
+ * Partition a type's properties into local (kept) and external (dropped), and
90
+ * collect the external type references that contributed the dropped ones.
91
+ *
92
+ * Applies to any composition shape — intersection, union, bare reference,
93
+ * indexed-access — not only intersections. Membership is decided per property
94
+ * by declaration origin (`isExternalProperty`), which is structure-agnostic:
95
+ * TypeScript preserves original declaration sources on derived properties, so
96
+ * the test gives the right answer through utility-type wrappers (Partial, Pick,
97
+ * `OmitStrict`) too. A property with no declarations (synthesized) is treated as
98
+ * local and kept. The external-type labels for `intersects` come from an AST
99
+ * walk (`collectExternalTypeRefs`) — the authoritative source for the
100
+ * `&`/`|`/index-access shape inference would otherwise erase.
97
101
  */
98
- export declare const filterIntersectionProperties: (type: ts.Type, typeNode: ts.Node, checker: ts.TypeChecker, isExternalFile: IsExternalFile) => {
102
+ export declare const filterExternalProperties: (type: ts.Type, typeNode: ts.Node, checker: ts.TypeChecker, isExternalFile: IsExternalFile) => {
99
103
  properties: Array<ts.Symbol>;
100
- intersectionTypes: Array<string>;
101
- } | null;
104
+ externalTypes: Array<string>;
105
+ };
102
106
  /**
103
107
  * Detect a Svelte 5 reactivity rune from a variable or property initializer.
104
108
  *
@@ -1 +1 @@
1
- {"version":3,"file":"typescript-extract-shared.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/typescript-extract-shared.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"typescript-extract-shared.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/typescript-extract-shared.ts"],"names":[],"mappings":"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"}
@@ -103,7 +103,10 @@ export const extractSignatureParameters = (sig, checker, tsdocParams) => {
103
103
  const prefix = param.name + '.';
104
104
  for (const [key, value] of Object.entries(tsdocParams)) {
105
105
  if (key.startsWith(prefix)) {
106
- (propertyDescriptions ??= {})[key.slice(prefix.length)] = value;
106
+ // Null-prototype map: the sliced sub-path is source-derived and is
107
+ // emitted as `propertyDescriptions`; a `@param obj.__proto__` key on a
108
+ // plain object would pollute the prototype on write.
109
+ (propertyDescriptions ??= Object.create(null))[key.slice(prefix.length)] = value;
107
110
  }
108
111
  }
109
112
  }
@@ -347,38 +350,92 @@ export const resolveIntersectionTypeNode = (type, typeNode) => {
347
350
  return undefined;
348
351
  };
349
352
  /**
350
- * Filter intersection type properties to exclude those from external sources,
351
- * and extract type names for branches whose properties are all external.
353
+ * Determine whether a type-reference / indexed-access node names a type whose
354
+ * properties all come from external files (e.g. `SvelteHTMLElements['li']`,
355
+ * `HTMLAttributes<HTMLDivElement>`). Such a node is an external attribute "bag"
356
+ * that should be summarized in `intersects` rather than enumerated as members.
352
357
  *
353
- * Returns `null` for non-intersection types (caller should use unfiltered properties).
354
- * For intersection types, returns filtered properties and external type names.
358
+ * Mirrors the per-property origin test used for membership: external only when
359
+ * the node has at least one property and every property is external. A
360
+ * zero-property branch (e.g. a pure index signature) is not surfaced — there is
361
+ * no named member to attribute to it.
362
+ */
363
+ const isExternalTypeRefNode = (node, checker, isExternalFile) => {
364
+ const props = checker.getTypeAtLocation(node).getProperties();
365
+ return props.length > 0 && props.every((p) => isExternalProperty(p, isExternalFile));
366
+ };
367
+ /**
368
+ * Walk a written type node and collect, in source order, the verbatim text of
369
+ * every external type reference it composes.
370
+ *
371
+ * Structure is read from the AST rather than the inferred type because
372
+ * inference erases it: `(A | B) & C` normalizes to a union and `X['k']`
373
+ * flattens to a property bag, both losing the `&`/`|`/index-access shape the
374
+ * author wrote. Composition nodes (intersection, union, parenthesized) recurse;
375
+ * leaf references (`TypeReference`, `IndexedAccessType`) are tested with
376
+ * `isExternalTypeRefNode` and, when external, emitted via `getText()`. Inline
377
+ * object literals and other local shapes contribute no entry.
378
+ *
379
+ * @mutates out - appends each external reference's source text
380
+ */
381
+ const collectExternalTypeRefs = (node, checker, isExternalFile, out) => {
382
+ if (ts.isParenthesizedTypeNode(node)) {
383
+ collectExternalTypeRefs(node.type, checker, isExternalFile, out);
384
+ }
385
+ else if (ts.isIntersectionTypeNode(node) || ts.isUnionTypeNode(node)) {
386
+ for (const branch of node.types)
387
+ collectExternalTypeRefs(branch, checker, isExternalFile, out);
388
+ }
389
+ else if (ts.isTypeReferenceNode(node) || ts.isIndexedAccessTypeNode(node)) {
390
+ if (isExternalTypeRefNode(node, checker, isExternalFile))
391
+ out.push(node.getText());
392
+ }
393
+ };
394
+ /**
395
+ * Resolve a props/type-alias annotation node to the written type node whose
396
+ * structure drives `intersects` extraction.
397
+ *
398
+ * The svelte2tsx props annotation is a reference to a generated `$$ComponentProps`
399
+ * alias; unwrap one level of *local* type-alias reference so the underlying
400
+ * composition (`SvelteHTMLElements['li']`, `(A | B) & C`, …) is visible. External
401
+ * alias references are left intact so they read as a single named bag rather than
402
+ * leaking their node_modules-internal definition. Type-alias callers pass the
403
+ * written node directly, so for them this is a no-op.
404
+ */
405
+ const resolveAnnotationTypeNode = (typeNode, checker, isExternalFile) => {
406
+ if (ts.isTypeReferenceNode(typeNode)) {
407
+ const aliasDecl = checker
408
+ .getSymbolAtLocation(typeNode.typeName)
409
+ ?.getDeclarations()
410
+ ?.find(ts.isTypeAliasDeclaration);
411
+ if (aliasDecl && !isExternalFile(aliasDecl.getSourceFile()))
412
+ return aliasDecl.type;
413
+ }
414
+ return ts.isTypeNode(typeNode) ? typeNode : undefined;
415
+ };
416
+ /**
417
+ * Partition a type's properties into local (kept) and external (dropped), and
418
+ * collect the external type references that contributed the dropped ones.
355
419
  *
356
- * Per-property filtering works through utility types (Partial, Pick, Readonly)
357
- * because TypeScript preserves original declaration sources on mapped type properties.
420
+ * Applies to any composition shape — intersection, union, bare reference,
421
+ * indexed-access — not only intersections. Membership is decided per property
422
+ * by declaration origin (`isExternalProperty`), which is structure-agnostic:
423
+ * TypeScript preserves original declaration sources on derived properties, so
424
+ * the test gives the right answer through utility-type wrappers (Partial, Pick,
425
+ * `OmitStrict`) too. A property with no declarations (synthesized) is treated as
426
+ * local and kept. The external-type labels for `intersects` come from an AST
427
+ * walk (`collectExternalTypeRefs`) — the authoritative source for the
428
+ * `&`/`|`/index-access shape inference would otherwise erase.
358
429
  */
359
- export const filterIntersectionProperties = (type, typeNode, checker, isExternalFile) => {
360
- if (!type.isIntersection())
361
- return null;
430
+ export const filterExternalProperties = (type, typeNode, checker, isExternalFile) => {
362
431
  const properties = type
363
432
  .getProperties()
364
433
  .filter((prop) => !isExternalProperty(prop, isExternalFile));
365
- // Resolve through type aliases (e.g., svelte2tsx $$ComponentProps) to find
366
- // the IntersectionTypeNode for `intersects` extraction.
367
- const intersectionNode = resolveIntersectionTypeNode(type, typeNode);
368
- const intersectionTypes = [];
369
- if (intersectionNode) {
370
- for (const branch of intersectionNode.types) {
371
- if (ts.isTypeReferenceNode(branch) || ts.isIndexedAccessTypeNode(branch)) {
372
- const branchType = checker.getTypeAtLocation(branch);
373
- const branchProps = branchType.getProperties();
374
- if (branchProps.length > 0 &&
375
- branchProps.every((p) => isExternalProperty(p, isExternalFile))) {
376
- intersectionTypes.push(branch.getText());
377
- }
378
- }
379
- }
380
- }
381
- return { properties, intersectionTypes };
434
+ const externalTypes = [];
435
+ const annotation = resolveAnnotationTypeNode(typeNode, checker, isExternalFile);
436
+ if (annotation)
437
+ collectExternalTypeRefs(annotation, checker, isExternalFile, externalTypes);
438
+ return { properties, externalTypes };
382
439
  };
383
440
  /**
384
441
  * Detect a Svelte 5 reactivity rune from a variable or property initializer.
@@ -1 +1 @@
1
- {"version":3,"file":"typescript-extract-type-properties.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/typescript-extract-type-properties.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,MAAM,YAAY,CAAC;AAG5B,OAAO,KAAK,EAAC,oBAAoB,EAAkB,MAAM,wBAAwB,CAAC;AAClF,OAAO,EAAC,KAAK,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAEjD,OAAO,EAAC,KAAK,cAAc,EAAC,MAAM,yBAAyB,CAAC;AA+J5D;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,0BAA0B,GACtC,MAAM,EAAE,CAAC,oBAAoB,EAC7B,UAAU,EAAE,CAAC,IAAI,EACjB,SAAS,EAAE,CAAC,WAAW,EACvB,aAAa,oBAAoB,EACjC,aAAa,KAAK,CAAC,UAAU,CAAC,EAC9B,gBAAgB,cAAc,KAC5B,IA2IF,CAAC"}
1
+ {"version":3,"file":"typescript-extract-type-properties.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/typescript-extract-type-properties.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,MAAM,YAAY,CAAC;AAG5B,OAAO,KAAK,EAAC,oBAAoB,EAAkB,MAAM,wBAAwB,CAAC;AAClF,OAAO,EAAC,KAAK,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAEjD,OAAO,EAAC,KAAK,cAAc,EAAC,MAAM,yBAAyB,CAAC;AA+J5D;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,0BAA0B,GACtC,MAAM,EAAE,CAAC,oBAAoB,EAC7B,UAAU,EAAE,CAAC,IAAI,EACjB,SAAS,EAAE,CAAC,WAAW,EACvB,aAAa,oBAAoB,EACjC,aAAa,KAAK,CAAC,UAAU,CAAC,EAC9B,gBAAgB,cAAc,KAC5B,IA8IF,CAAC"}
@@ -18,7 +18,7 @@ import ts from 'typescript';
18
18
  import {} from './diagnostics.js';
19
19
  import { parseComment, applyToDeclaration } from './tsdoc.js';
20
20
  import {} from './typescript-program.js';
21
- import { emitCallOrConstructSignature, filterIntersectionProperties, getNodeLocation, isExternalIntersectionBranch, populateCallableMember, resolveIntersectionTypeNode, } from './typescript-extract-shared.js';
21
+ import { emitCallOrConstructSignature, filterExternalProperties, getNodeLocation, isExternalIntersectionBranch, populateCallableMember, resolveIntersectionTypeNode, } from './typescript-extract-shared.js';
22
22
  /**
23
23
  * Check whether a resolved type has properties worth extracting for documentation.
24
24
  *
@@ -160,11 +160,15 @@ const isReadonlyProperty = (prop, mappedReadonly) => {
160
160
  export const extractTypeAliasProperties = (node, nodeType, checker, declaration, diagnostics, isExternalFile) => {
161
161
  if (!hasExtractableProperties(nodeType))
162
162
  return;
163
- // For intersection types, filter out properties from external sources (node_modules)
164
- // and extract type names for the `intersects` field
165
- const intersectionResult = filterIntersectionProperties(nodeType, node.type, checker, isExternalFile);
166
- if (intersectionResult?.intersectionTypes.length) {
167
- declaration.intersects = intersectionResult.intersectionTypes;
163
+ // Drop properties contributed by external types (node_modules / declaration
164
+ // files) and surface those external types in the `intersects` field. Applies
165
+ // to the property-bearing shapes that pass `hasExtractableProperties` above —
166
+ // intersections, bare references, indexed-access. Unions are gated out here
167
+ // (the Svelte prop path calls `filterExternalProperties` directly, so unions
168
+ // still surface `intersects` there, just not for plain type aliases).
169
+ const { properties: filteredProperties, externalTypes } = filterExternalProperties(nodeType, node.type, checker, isExternalFile);
170
+ if (externalTypes.length) {
171
+ declaration.intersects = externalTypes;
168
172
  }
169
173
  // Detect mapped-type-level readonly (e.g., Readonly<T>, { readonly [K in ...]: ... })
170
174
  let mappedReadonly = false;
@@ -177,9 +181,8 @@ export const extractTypeAliasProperties = (node, nodeType, checker, declaration,
177
181
  mappedReadonly = true;
178
182
  }
179
183
  }
180
- // Extract named properties (filtered for intersections, full for other types)
181
- const properties = intersectionResult?.properties ?? nodeType.getProperties();
182
- for (const prop of properties) {
184
+ // Extract named properties (external contributions already filtered out)
185
+ for (const prop of filteredProperties) {
183
186
  // Skip internal TypeScript symbols
184
187
  if (prop.getName().startsWith('__@'))
185
188
  continue;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svelte-docinfo",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "static analysis for TypeScript and Svelte",
5
5
  "glyph": "📜",
6
6
  "logo": "logo.svg",
@@ -58,11 +58,12 @@
58
58
  }
59
59
  },
60
60
  "devDependencies": {
61
+ "@fuzdev/blake3_wasm": "^0.1.1",
61
62
  "@fuzdev/fuz_code": "^0.45.1",
62
- "@fuzdev/fuz_css": "^0.60.0",
63
- "@fuzdev/fuz_ui": "^0.195.1",
64
- "@fuzdev/fuz_util": "^0.62.0",
65
- "@fuzdev/gro": "^0.199.1",
63
+ "@fuzdev/fuz_css": "^0.61.1",
64
+ "@fuzdev/fuz_ui": "^0.197.0",
65
+ "@fuzdev/fuz_util": "^0.63.1",
66
+ "@fuzdev/gro": "^0.200.0",
66
67
  "@ryanatkn/eslint-config": "^0.12.1",
67
68
  "@sveltejs/acorn-typescript": "^1.0.9",
68
69
  "@sveltejs/adapter-static": "^3.0.10",
@@ -74,14 +75,14 @@
74
75
  "@types/picomatch": "^4.0.2",
75
76
  "@webref/css": "^8.2.0",
76
77
  "eslint": "^9.39.1",
77
- "eslint-plugin-svelte": "^3.13.1",
78
+ "eslint-plugin-svelte": "^3.19.0",
78
79
  "esm-env": "^1.2.2",
79
80
  "magic-string": "^0.30.21",
80
81
  "prettier": "^3.7.4",
81
82
  "prettier-plugin-svelte": "^3.4.1",
82
- "svelte": "^5.55.10",
83
+ "svelte": "^5.56.0",
83
84
  "svelte-check": "^4.3.5",
84
- "svelte2tsx": "^0.7.52",
85
+ "svelte2tsx": "^0.7.55",
85
86
  "tslib": "^2.8.1",
86
87
  "typescript-eslint": "^8.48.1",
87
88
  "vite": "^7.3.1",