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
@@ -0,0 +1,330 @@
1
+ /**
2
+ * Source configuration, path extraction, and file filtering.
3
+ *
4
+ * Provides `ModuleSourceOptions` configuration management and functions
5
+ * that operate on source files using those options: path extraction,
6
+ * source detection, dependency filtering, and file collection.
7
+ *
8
+ * @see `source.ts` for pure file type predicates (`isTypescript`, `isSvelte`, etc.)
9
+ * @see `analyze.ts` for consumers (`analyze`, `analyzeFromFiles`)
10
+ *
11
+ * @module
12
+ */
13
+ import { type AnalyzerType, type SourceFileInfo } from './source.js';
14
+ /**
15
+ * Configuration for module source detection and path extraction.
16
+ *
17
+ * Uses proper path semantics with `projectRoot` as the base for all path operations.
18
+ * Paths are matched using `startsWith` rather than substring search, which correctly
19
+ * handles nested directories without special heuristics.
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * const options = createSourceOptions(process.cwd(), {
24
+ * sourcePaths: ['src/lib', 'src/routes'],
25
+ * sourceRoot: 'src',
26
+ * });
27
+ * ```
28
+ */
29
+ export interface ModuleSourceOptions {
30
+ /**
31
+ * Path to the project root directory.
32
+ *
33
+ * All `sourcePaths` are relative to this. Typically `process.cwd()`.
34
+ * Normalized (resolved to absolute, posixified to forward slashes,
35
+ * trailing slash stripped) by `normalizeSourceOptions`, which returns a
36
+ * new options object. Internal callers (`isSource`, `extractPath`) assume
37
+ * this is already POSIX form — construct via `createSourceOptions` /
38
+ * `normalizeSourceOptions` rather than building `ModuleSourceOptions`
39
+ * literals by hand on Windows.
40
+ *
41
+ * @example
42
+ * ```ts
43
+ * '/home/user/my-project'
44
+ * ```
45
+ */
46
+ projectRoot: string;
47
+ /**
48
+ * Source directory paths to include, relative to `projectRoot`.
49
+ *
50
+ * Normalized (leading/trailing slashes stripped) by `normalizeSourceOptions`,
51
+ * which returns a new options object.
52
+ *
53
+ * @example
54
+ * ```ts
55
+ * ['src/lib'] // single source directory
56
+ * ```
57
+ * @example
58
+ * ```ts
59
+ * ['src/lib', 'src/routes'] // multiple directories
60
+ * ```
61
+ */
62
+ sourcePaths: Array<string>;
63
+ /**
64
+ * Source root for extracting relative module paths, relative to `projectRoot`.
65
+ *
66
+ * Normalized (leading/trailing slashes stripped; `'.'` collapsed to `''`)
67
+ * by `normalizeSourceOptions`, which returns a new options object.
68
+ *
69
+ * When omitted:
70
+ * - Single `sourcePath`: defaults to that path
71
+ * - Multiple `sourcePaths`: auto-derived as the longest common directory prefix
72
+ * (or `''` when paths share no common prefix — produces project-relative module paths)
73
+ *
74
+ * @example
75
+ * ```ts
76
+ * 'src/lib' // module paths like 'foo.ts', 'utils/bar.ts'
77
+ * ```
78
+ * @example
79
+ * ```ts
80
+ * 'src' // module paths like 'lib/foo.ts', 'routes/page.svelte'
81
+ * ```
82
+ * @example
83
+ * ```ts
84
+ * '' // or '.': module paths stay project-relative, e.g. 'src/lib/foo.ts'
85
+ * ```
86
+ */
87
+ sourceRoot?: string;
88
+ /**
89
+ * Glob patterns to exclude from analysis, relative to `projectRoot`.
90
+ *
91
+ * Applied at both stages of the pipeline:
92
+ * - **Discovery time** by `globFiles`/`discoverFromExports`, preventing matched files from being loaded.
93
+ * - **Analysis time** by `isSource()` against `relative(projectRoot, absolutePath)`,
94
+ * catching files that enter through TypeScript import resolution
95
+ * (e.g., a source file imports a test helper).
96
+ *
97
+ * `analyzeFromFiles` accepts a top-level `exclude` shortcut that merges into this field.
98
+ *
99
+ * Compiled to a matcher once per options object via picomatch and cached by reference;
100
+ * mutating this array post-`isSource`-call has no effect — pass through
101
+ * `normalizeSourceOptions` (which returns a fresh object) or otherwise build a
102
+ * new options object to apply changes.
103
+ *
104
+ * @default `['**\/*.test.ts', '**\/*.spec.ts']`
105
+ */
106
+ exclude: Array<string>;
107
+ /**
108
+ * Determine which analyzer to use for a file path.
109
+ *
110
+ * Called for files in source directories. Return an `AnalyzerType` or `null` to skip:
111
+ * - `'typescript'` — TypeScript/JS files analyzed via TypeScript compiler API
112
+ * - `'svelte'` — Svelte components analyzed via svelte2tsx + TypeScript compiler API
113
+ * - `'css'` — CSS files included as modules with no declarations
114
+ * - `'json'` — JSON files included as modules with no declarations
115
+ * - `null` — skip the file
116
+ *
117
+ * @default Uses file extension: `.svelte` → svelte, `.ts`/`.js` → typescript, `.css` → css, `.json` → json
118
+ *
119
+ * @example
120
+ * ```ts
121
+ * // Add MDsveX support
122
+ * getAnalyzerType: (path) => {
123
+ * if (path.endsWith('.svelte') || path.endsWith('.svx')) return 'svelte';
124
+ * if (path.endsWith('.ts') || path.endsWith('.js')) return 'typescript';
125
+ * if (path.endsWith('.css')) return 'css';
126
+ * if (path.endsWith('.json')) return 'json';
127
+ * return null;
128
+ * }
129
+ * ```
130
+ *
131
+ * @example
132
+ * ```ts
133
+ * // Include .d.ts files
134
+ * getAnalyzerType: (path) => {
135
+ * if (path.endsWith('.svelte')) return 'svelte';
136
+ * if (path.endsWith('.ts') || path.endsWith('.d.ts') || path.endsWith('.js')) return 'typescript';
137
+ * if (path.endsWith('.css')) return 'css';
138
+ * if (path.endsWith('.json')) return 'json';
139
+ * return null;
140
+ * }
141
+ * ```
142
+ */
143
+ getAnalyzerType: (path: string) => AnalyzerType | null;
144
+ }
145
+ /**
146
+ * Default source options preset (without `projectRoot`).
147
+ *
148
+ * Use with `createSourceOptions` to build complete options.
149
+ * Contains all `ModuleSourceOptions` fields except `projectRoot`,
150
+ * which is provided separately as the first argument to `createSourceOptions`.
151
+ */
152
+ export type SourceOptionsDefaults = Omit<ModuleSourceOptions, 'projectRoot'>;
153
+ /**
154
+ * Default partial options for standard SvelteKit library structure.
155
+ *
156
+ * Does not include `projectRoot` — use `createSourceOptions` to create
157
+ * complete options with your project root.
158
+ *
159
+ * `exclude` is the single source of truth for filtering: globs applied at both
160
+ * discovery time (by `globFiles`/`discoverFromExports`) and analysis time
161
+ * (by `isSource()` against project-root-relative paths).
162
+ *
163
+ * @see `createSourceOptions` for the typical way to build complete options
164
+ */
165
+ export declare const DEFAULT_SOURCE_OPTIONS: SourceOptionsDefaults;
166
+ /**
167
+ * Create complete, normalized, validated source options from project root and optional overrides.
168
+ *
169
+ * Merges `overrides` with `DEFAULT_SOURCE_OPTIONS`, then normalizes via
170
+ * `normalizeSourceOptions` — so the returned object always has an absolute
171
+ * `projectRoot`, slash-stripped path entries, and an explicit `sourceRoot`
172
+ * (auto-derived for multi-path layouts). Throws on validation failure.
173
+ *
174
+ * @param projectRoot - path to project root (typically `process.cwd()`); resolved to absolute
175
+ * @param overrides - optional overrides for default options
176
+ * @throws Error if validation fails (empty `sourcePaths`, or `sourceRoot` not a prefix of all `sourcePaths`)
177
+ *
178
+ * @example
179
+ * ```ts
180
+ * // Standard SvelteKit library
181
+ * const options = createSourceOptions(process.cwd());
182
+ * ```
183
+ *
184
+ * @example
185
+ * ```ts
186
+ * // Multiple source directories
187
+ * const options = createSourceOptions(process.cwd(), {
188
+ * sourcePaths: ['src/lib', 'src/routes'],
189
+ * sourceRoot: 'src',
190
+ * });
191
+ * ```
192
+ *
193
+ * @example
194
+ * ```ts
195
+ * // Custom exclusions
196
+ * const options = createSourceOptions(process.cwd(), {
197
+ * exclude: ['**\/*.test.ts', '**\/*.internal.ts'],
198
+ * });
199
+ * ```
200
+ */
201
+ export declare const createSourceOptions: (projectRoot: string, overrides?: Partial<SourceOptionsDefaults>) => ModuleSourceOptions;
202
+ /**
203
+ * Normalize and validate `ModuleSourceOptions`, returning a new options object.
204
+ *
205
+ * Normalization:
206
+ * - `projectRoot` resolved to absolute via `path.resolve` (relative paths resolve against cwd)
207
+ * - Trailing slash stripped from `projectRoot`
208
+ * - Leading/trailing slashes stripped from `sourcePaths` entries
209
+ * - Leading/trailing slashes stripped from `sourceRoot` (if provided)
210
+ * - `sourceRoot` of `'.'` is normalized to `''` (project root sentinel)
211
+ *
212
+ * Validation (after normalization):
213
+ * 1. `sourcePaths` has at least one entry
214
+ * 2. `sourceRoot` (if provided and non-empty) is a prefix of all `sourcePaths`
215
+ *
216
+ * When `sourceRoot` is omitted and multiple `sourcePaths` are provided,
217
+ * `sourceRoot` is auto-derived as their longest common path prefix. If the
218
+ * paths share no common prefix, the derived root is `''` and `extractPath`
219
+ * produces project-relative module paths.
220
+ *
221
+ * Returns a fresh object — the input is not mutated. Re-normalization produces
222
+ * a fresh identity, which naturally invalidates the `excludeMatcherCache`
223
+ * (keyed by options-object identity) without a separate "rebuild a fresh
224
+ * object" rule.
225
+ *
226
+ * @returns a new `ModuleSourceOptions` with normalized fields
227
+ * @throws Error if validation fails
228
+ *
229
+ * @example
230
+ * ```ts
231
+ * // Normalization: slashes stripped, relative projectRoot resolved
232
+ * const normalized = normalizeSourceOptions({projectRoot: '.', sourcePaths: ['/src/lib/'], ...});
233
+ * // normalized.projectRoot is now absolute, normalized.sourcePaths is ['src/lib']
234
+ * ```
235
+ */
236
+ export declare const normalizeSourceOptions: (options: ModuleSourceOptions) => ModuleSourceOptions;
237
+ /**
238
+ * Get the effective `sourceRoot` from options.
239
+ *
240
+ * Returns `sourceRoot` if provided, otherwise:
241
+ * - Single `sourcePath`: returns that path
242
+ * - Multiple `sourcePaths`: derives the longest common directory prefix
243
+ *
244
+ * @returns the effective source root path
245
+ */
246
+ export declare const getSourceRoot: (options: ModuleSourceOptions) => string;
247
+ /**
248
+ * Extract module path relative to source root from absolute source ID.
249
+ *
250
+ * Uses proper path semantics: strips `projectRoot/sourceRoot/` prefix.
251
+ *
252
+ * @param sourceId - absolute path to the source file
253
+ * @param options - module source options for path extraction
254
+ *
255
+ * @example
256
+ * ```ts
257
+ * const options = createSourceOptions('/home/user/project');
258
+ * extractPath('/home/user/project/src/lib/foo.ts', options) // => 'foo.ts'
259
+ * extractPath('/home/user/project/src/lib/nested/bar.svelte', options) // => 'nested/bar.svelte'
260
+ * ```
261
+ *
262
+ * @example
263
+ * ```ts
264
+ * const options = createSourceOptions('/home/user/project', {
265
+ * sourcePaths: ['src/lib', 'src/routes'],
266
+ * sourceRoot: 'src',
267
+ * });
268
+ * extractPath('/home/user/project/src/lib/foo.ts', options) // => 'lib/foo.ts'
269
+ * extractPath('/home/user/project/src/routes/page.svelte', options) // => 'routes/page.svelte'
270
+ * ```
271
+ */
272
+ export declare const extractPath: (sourceId: string, options: ModuleSourceOptions) => string;
273
+ /**
274
+ * Check if a path is an analyzable source file.
275
+ *
276
+ * Combines all filtering: source directory paths, exclude globs, and analyzer
277
+ * availability. This is the single check for whether a file should be
278
+ * included in library analysis.
279
+ *
280
+ * Uses proper path semantics with `startsWith` matching against
281
+ * `projectRoot/sourcePath/`. No heuristics needed — nested directories are
282
+ * correctly excluded by the prefix check.
283
+ *
284
+ * Order is sourceDir-then-exclude: the prefix check guarantees the path lives
285
+ * under `projectRoot` before relativization, so `relative()` always produces
286
+ * a clean glob-shaped string for the matcher. Files outside `projectRoot`
287
+ * (rare; would require monorepo path mapping) short-circuit at the prefix
288
+ * check before reaching the matcher.
289
+ *
290
+ * @param path - full absolute path to check
291
+ * @param options - module source options for filtering
292
+ * @returns true if the path is an analyzable source file
293
+ *
294
+ * @example
295
+ * ```ts
296
+ * const options = createSourceOptions('/home/user/project');
297
+ * isSource('/home/user/project/src/lib/foo.ts', options) // => true
298
+ * isSource('/home/user/project/src/lib/styles.css', options) // => true
299
+ * isSource('/home/user/project/src/lib/data.json', options) // => true
300
+ * isSource('/home/user/project/src/lib/foo.test.ts', options) // => false (excluded)
301
+ * isSource('/home/user/project/src/fixtures/mini/src/lib/bar.ts', options) // => false (wrong prefix)
302
+ * ```
303
+ */
304
+ export declare const isSource: (path: string, options: ModuleSourceOptions) => boolean;
305
+ /**
306
+ * Extract dependencies and dependents for a module from source file info.
307
+ *
308
+ * Filters to only include source modules (excludes external packages, node_modules, tests).
309
+ * Returns sorted arrays of module paths (relative to `sourceRoot`) for deterministic output.
310
+ *
311
+ * Native paths in `sourceFile.dependencies`/`dependents` are accepted —
312
+ * `isSource` and `extractPath` posixify their inputs, so direct callers with
313
+ * hand-built input need not pre-normalize.
314
+ *
315
+ * Accepts `SourceFileInfo` plus an optional `dependents` field — the public
316
+ * input type carries only `dependencies` (caller-supplied opt-in), while
317
+ * `dependents` is computed downstream by `computeDependents` and flows through
318
+ * as an enriched shape.
319
+ *
320
+ * @param sourceFile - the source file info to extract dependencies from
321
+ * @param options - module source options for filtering and path extraction
322
+ * @returns sorted arrays of module paths (relative to `sourceRoot`) for dependencies and dependents
323
+ */
324
+ export declare const extractDependencies: (sourceFile: SourceFileInfo & {
325
+ dependents?: ReadonlyArray<string>;
326
+ }, options: ModuleSourceOptions) => {
327
+ dependencies: Array<string>;
328
+ dependents: Array<string>;
329
+ };
330
+ //# sourceMappingURL=source-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"source-config.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/source-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH,OAAO,EAAC,KAAK,YAAY,EAAE,KAAK,cAAc,EAAqB,MAAM,aAAa,CAAC;AAGvF;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,mBAAmB;IACnC;;;;;;;;;;;;;;;OAeG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB;;;;;;;;;;;;;;OAcG;IACH,WAAW,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC3B;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;;;;;;;;;;;;OAiBG;IACH,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACvB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAmCG;IACH,eAAe,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,YAAY,GAAG,IAAI,CAAC;CACvD;AAED;;;;;;GAMG;AACH,MAAM,MAAM,qBAAqB,GAAG,IAAI,CAAC,mBAAmB,EAAE,aAAa,CAAC,CAAC;AAE7E;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,sBAAsB,EAAE,qBAIpC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,eAAO,MAAM,mBAAmB,GAC/B,aAAa,MAAM,EACnB,YAAY,OAAO,CAAC,qBAAqB,CAAC,KACxC,mBAKA,CAAC;AAEJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,eAAO,MAAM,sBAAsB,GAAI,SAAS,mBAAmB,KAAG,mBAmDrE,CAAC;AA+BF;;;;;;;;GAQG;AACH,eAAO,MAAM,aAAa,GAAI,SAAS,mBAAmB,KAAG,MAQ5D,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,WAAW,GAAI,UAAU,MAAM,EAAE,SAAS,mBAAmB,KAAG,MAgB5E,CAAC;AAwBF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,eAAO,MAAM,QAAQ,GAAI,MAAM,MAAM,EAAE,SAAS,mBAAmB,KAAG,OAwBrE,CAAC;AAEF;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,mBAAmB,GAC/B,YAAY,cAAc,GAAG;IAAC,UAAU,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;CAAC,EACjE,SAAS,mBAAmB,KAC1B;IAAC,YAAY,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAAC,UAAU,EAAE,KAAK,CAAC,MAAM,CAAC,CAAA;CA2BzD,CAAC"}
@@ -0,0 +1,356 @@
1
+ /**
2
+ * Source configuration, path extraction, and file filtering.
3
+ *
4
+ * Provides `ModuleSourceOptions` configuration management and functions
5
+ * that operate on source files using those options: path extraction,
6
+ * source detection, dependency filtering, and file collection.
7
+ *
8
+ * @see `source.ts` for pure file type predicates (`isTypescript`, `isSvelte`, etc.)
9
+ * @see `analyze.ts` for consumers (`analyze`, `analyzeFromFiles`)
10
+ *
11
+ * @module
12
+ */
13
+ import { resolve, relative } from 'node:path';
14
+ import picomatch from 'picomatch';
15
+ import { getDefaultAnalyzer } from './source.js';
16
+ import { toPosixPath } from './paths.js';
17
+ /**
18
+ * Default partial options for standard SvelteKit library structure.
19
+ *
20
+ * Does not include `projectRoot` — use `createSourceOptions` to create
21
+ * complete options with your project root.
22
+ *
23
+ * `exclude` is the single source of truth for filtering: globs applied at both
24
+ * discovery time (by `globFiles`/`discoverFromExports`) and analysis time
25
+ * (by `isSource()` against project-root-relative paths).
26
+ *
27
+ * @see `createSourceOptions` for the typical way to build complete options
28
+ */
29
+ export const DEFAULT_SOURCE_OPTIONS = {
30
+ sourcePaths: ['src/lib'],
31
+ exclude: ['**/*.test.ts', '**/*.spec.ts'],
32
+ getAnalyzerType: getDefaultAnalyzer,
33
+ };
34
+ /**
35
+ * Create complete, normalized, validated source options from project root and optional overrides.
36
+ *
37
+ * Merges `overrides` with `DEFAULT_SOURCE_OPTIONS`, then normalizes via
38
+ * `normalizeSourceOptions` — so the returned object always has an absolute
39
+ * `projectRoot`, slash-stripped path entries, and an explicit `sourceRoot`
40
+ * (auto-derived for multi-path layouts). Throws on validation failure.
41
+ *
42
+ * @param projectRoot - path to project root (typically `process.cwd()`); resolved to absolute
43
+ * @param overrides - optional overrides for default options
44
+ * @throws Error if validation fails (empty `sourcePaths`, or `sourceRoot` not a prefix of all `sourcePaths`)
45
+ *
46
+ * @example
47
+ * ```ts
48
+ * // Standard SvelteKit library
49
+ * const options = createSourceOptions(process.cwd());
50
+ * ```
51
+ *
52
+ * @example
53
+ * ```ts
54
+ * // Multiple source directories
55
+ * const options = createSourceOptions(process.cwd(), {
56
+ * sourcePaths: ['src/lib', 'src/routes'],
57
+ * sourceRoot: 'src',
58
+ * });
59
+ * ```
60
+ *
61
+ * @example
62
+ * ```ts
63
+ * // Custom exclusions
64
+ * const options = createSourceOptions(process.cwd(), {
65
+ * exclude: ['**\/*.test.ts', '**\/*.internal.ts'],
66
+ * });
67
+ * ```
68
+ */
69
+ export const createSourceOptions = (projectRoot, overrides) => normalizeSourceOptions({
70
+ projectRoot,
71
+ ...DEFAULT_SOURCE_OPTIONS,
72
+ ...overrides,
73
+ });
74
+ /**
75
+ * Normalize and validate `ModuleSourceOptions`, returning a new options object.
76
+ *
77
+ * Normalization:
78
+ * - `projectRoot` resolved to absolute via `path.resolve` (relative paths resolve against cwd)
79
+ * - Trailing slash stripped from `projectRoot`
80
+ * - Leading/trailing slashes stripped from `sourcePaths` entries
81
+ * - Leading/trailing slashes stripped from `sourceRoot` (if provided)
82
+ * - `sourceRoot` of `'.'` is normalized to `''` (project root sentinel)
83
+ *
84
+ * Validation (after normalization):
85
+ * 1. `sourcePaths` has at least one entry
86
+ * 2. `sourceRoot` (if provided and non-empty) is a prefix of all `sourcePaths`
87
+ *
88
+ * When `sourceRoot` is omitted and multiple `sourcePaths` are provided,
89
+ * `sourceRoot` is auto-derived as their longest common path prefix. If the
90
+ * paths share no common prefix, the derived root is `''` and `extractPath`
91
+ * produces project-relative module paths.
92
+ *
93
+ * Returns a fresh object — the input is not mutated. Re-normalization produces
94
+ * a fresh identity, which naturally invalidates the `excludeMatcherCache`
95
+ * (keyed by options-object identity) without a separate "rebuild a fresh
96
+ * object" rule.
97
+ *
98
+ * @returns a new `ModuleSourceOptions` with normalized fields
99
+ * @throws Error if validation fails
100
+ *
101
+ * @example
102
+ * ```ts
103
+ * // Normalization: slashes stripped, relative projectRoot resolved
104
+ * const normalized = normalizeSourceOptions({projectRoot: '.', sourcePaths: ['/src/lib/'], ...});
105
+ * // normalized.projectRoot is now absolute, normalized.sourcePaths is ['src/lib']
106
+ * ```
107
+ */
108
+ export const normalizeSourceOptions = (options) => {
109
+ // Normalize projectRoot: resolve to absolute, posixify, strip trailing slash.
110
+ // Posixify after `resolve` because `path.resolve` returns native separators
111
+ // on Windows; everything internal compares against forward-slash form.
112
+ let projectRoot = toPosixPath(resolve(options.projectRoot));
113
+ if (projectRoot.length > 1 && projectRoot.endsWith('/')) {
114
+ projectRoot = projectRoot.slice(0, -1);
115
+ }
116
+ // Normalize sourcePaths: posixify (in case a Windows user supplies
117
+ // `'src\\lib'`), then strip leading/trailing slashes.
118
+ const sourcePaths = options.sourcePaths.map((p) => stripSlashes(toPosixPath(p)));
119
+ // Validate sourcePaths non-empty
120
+ if (sourcePaths.length === 0) {
121
+ throw new Error('ModuleSourceOptions.sourcePaths must have at least one entry. ' +
122
+ "For SvelteKit projects, the default is ['src/lib']; for plain TS, try ['src'].");
123
+ }
124
+ // Normalize sourceRoot: posixify, strip leading/trailing slashes.
125
+ // `.` is treated as the project root sentinel — equivalent to an empty string,
126
+ // which `extractPath` interprets as "strip projectRoot only" (module paths
127
+ // stay project-relative). `.` is the ergonomic CLI form; `''` is the
128
+ // shell-quoting form. Both reach the same effective state.
129
+ let sourceRoot = options.sourceRoot === undefined ? undefined : stripSlashes(toPosixPath(options.sourceRoot));
130
+ if (sourceRoot === '.')
131
+ sourceRoot = '';
132
+ // Auto-derive sourceRoot for multiple sourcePaths when not explicitly provided.
133
+ // Empty common prefix is a valid result — `extractPath` will produce
134
+ // project-relative module paths (e.g., `lib/foo.ts`, `routes/page.svelte`)
135
+ // instead of stripping a common directory prefix.
136
+ if (sourceRoot === undefined && sourcePaths.length > 1) {
137
+ sourceRoot = deriveCommonPrefix(sourcePaths);
138
+ }
139
+ // Validate sourceRoot prefix constraint (if sourceRoot is set — either explicit or auto-derived)
140
+ if (sourceRoot !== undefined && sourceRoot !== '') {
141
+ for (const sourcePath of sourcePaths) {
142
+ if (sourcePath !== sourceRoot && !sourcePath.startsWith(sourceRoot + '/')) {
143
+ throw new Error(`sourcePaths entry "${sourcePath}" must start with sourceRoot "${sourceRoot}". ` +
144
+ `extractPath uses sourceRoot to compute module paths.`);
145
+ }
146
+ }
147
+ }
148
+ return { ...options, projectRoot, sourcePaths, sourceRoot };
149
+ };
150
+ /** Strip all leading and trailing forward slashes from a path segment. */
151
+ const stripSlashes = (p) => {
152
+ let result = p;
153
+ while (result.startsWith('/'))
154
+ result = result.slice(1);
155
+ while (result.endsWith('/'))
156
+ result = result.slice(0, -1);
157
+ return result;
158
+ };
159
+ /**
160
+ * Derive the longest common directory prefix from an array of paths.
161
+ *
162
+ * @returns common prefix (e.g., `['src/lib', 'src/routes']` → `'src'`),
163
+ * or empty string if paths share no common prefix
164
+ */
165
+ const deriveCommonPrefix = (paths) => {
166
+ if (paths.length === 0)
167
+ return '';
168
+ const segments = paths[0].split('/');
169
+ let common = '';
170
+ for (const segment of segments) {
171
+ const candidate = common ? common + '/' + segment : segment;
172
+ if (paths.every((p) => p === candidate || p.startsWith(candidate + '/'))) {
173
+ common = candidate;
174
+ }
175
+ else {
176
+ break;
177
+ }
178
+ }
179
+ return common;
180
+ };
181
+ /**
182
+ * Get the effective `sourceRoot` from options.
183
+ *
184
+ * Returns `sourceRoot` if provided, otherwise:
185
+ * - Single `sourcePath`: returns that path
186
+ * - Multiple `sourcePaths`: derives the longest common directory prefix
187
+ *
188
+ * @returns the effective source root path
189
+ */
190
+ export const getSourceRoot = (options) => {
191
+ if (options.sourceRoot !== undefined) {
192
+ return options.sourceRoot;
193
+ }
194
+ if (options.sourcePaths.length === 1) {
195
+ return options.sourcePaths[0];
196
+ }
197
+ return deriveCommonPrefix(options.sourcePaths);
198
+ };
199
+ /**
200
+ * Extract module path relative to source root from absolute source ID.
201
+ *
202
+ * Uses proper path semantics: strips `projectRoot/sourceRoot/` prefix.
203
+ *
204
+ * @param sourceId - absolute path to the source file
205
+ * @param options - module source options for path extraction
206
+ *
207
+ * @example
208
+ * ```ts
209
+ * const options = createSourceOptions('/home/user/project');
210
+ * extractPath('/home/user/project/src/lib/foo.ts', options) // => 'foo.ts'
211
+ * extractPath('/home/user/project/src/lib/nested/bar.svelte', options) // => 'nested/bar.svelte'
212
+ * ```
213
+ *
214
+ * @example
215
+ * ```ts
216
+ * const options = createSourceOptions('/home/user/project', {
217
+ * sourcePaths: ['src/lib', 'src/routes'],
218
+ * sourceRoot: 'src',
219
+ * });
220
+ * extractPath('/home/user/project/src/lib/foo.ts', options) // => 'lib/foo.ts'
221
+ * extractPath('/home/user/project/src/routes/page.svelte', options) // => 'routes/page.svelte'
222
+ * ```
223
+ */
224
+ export const extractPath = (sourceId, options) => {
225
+ // Posixify input so the prefix slice works regardless of which separator
226
+ // the caller's path system uses. `options.projectRoot` is already POSIX.
227
+ const posixId = toPosixPath(sourceId);
228
+ const effectiveRoot = getSourceRoot(options);
229
+ // Build the full prefix: projectRoot + '/' + sourceRoot + '/'
230
+ // When sourceRoot is empty, prefix is just projectRoot + '/'
231
+ const prefix = effectiveRoot
232
+ ? options.projectRoot + '/' + effectiveRoot + '/'
233
+ : options.projectRoot + '/';
234
+ if (posixId.startsWith(prefix)) {
235
+ return posixId.slice(prefix.length);
236
+ }
237
+ // Fallback: return full path if prefix doesn't match (shouldn't happen with valid inputs)
238
+ return posixId;
239
+ };
240
+ /**
241
+ * Compiled glob-matcher cache for `options.exclude`, keyed by options object identity.
242
+ *
243
+ * Picomatch compilation is non-trivial; caching avoids re-parsing the same patterns
244
+ * on every `isSource()` call. Mutating `options.exclude` post-cache-population has
245
+ * no effect — callers that need to change the exclude list must build a fresh
246
+ * options object (different identity). `normalizeSourceOptions` returns a new
247
+ * object, so re-running it through normalize naturally produces a fresh identity
248
+ * and the cache populates fresh on first access.
249
+ */
250
+ const excludeMatcherCache = new WeakMap();
251
+ const getExcludeMatcher = (options) => {
252
+ let matcher = excludeMatcherCache.get(options);
253
+ if (!matcher) {
254
+ matcher = options.exclude.length === 0 ? () => false : picomatch(options.exclude);
255
+ excludeMatcherCache.set(options, matcher);
256
+ }
257
+ return matcher;
258
+ };
259
+ /**
260
+ * Check if a path is an analyzable source file.
261
+ *
262
+ * Combines all filtering: source directory paths, exclude globs, and analyzer
263
+ * availability. This is the single check for whether a file should be
264
+ * included in library analysis.
265
+ *
266
+ * Uses proper path semantics with `startsWith` matching against
267
+ * `projectRoot/sourcePath/`. No heuristics needed — nested directories are
268
+ * correctly excluded by the prefix check.
269
+ *
270
+ * Order is sourceDir-then-exclude: the prefix check guarantees the path lives
271
+ * under `projectRoot` before relativization, so `relative()` always produces
272
+ * a clean glob-shaped string for the matcher. Files outside `projectRoot`
273
+ * (rare; would require monorepo path mapping) short-circuit at the prefix
274
+ * check before reaching the matcher.
275
+ *
276
+ * @param path - full absolute path to check
277
+ * @param options - module source options for filtering
278
+ * @returns true if the path is an analyzable source file
279
+ *
280
+ * @example
281
+ * ```ts
282
+ * const options = createSourceOptions('/home/user/project');
283
+ * isSource('/home/user/project/src/lib/foo.ts', options) // => true
284
+ * isSource('/home/user/project/src/lib/styles.css', options) // => true
285
+ * isSource('/home/user/project/src/lib/data.json', options) // => true
286
+ * isSource('/home/user/project/src/lib/foo.test.ts', options) // => false (excluded)
287
+ * isSource('/home/user/project/src/fixtures/mini/src/lib/bar.ts', options) // => false (wrong prefix)
288
+ * ```
289
+ */
290
+ export const isSource = (path, options) => {
291
+ // Posixify input — callers may pass native paths (Windows backslashes).
292
+ // `options.projectRoot` and `sourcePaths` are already POSIX after
293
+ // `normalizeSourceOptions`.
294
+ const posixPath = toPosixPath(path);
295
+ // Source-dir prefix check first — guarantees the path lives under projectRoot
296
+ // before we relativize for glob matching. Out-of-root files (which can't be
297
+ // sources anyway) short-circuit here without reaching the matcher.
298
+ const inSourceDir = options.sourcePaths.some((sourcePath) => {
299
+ const fullPrefix = options.projectRoot + '/' + sourcePath + '/';
300
+ return posixPath.startsWith(fullPrefix);
301
+ });
302
+ if (!inSourceDir)
303
+ return false;
304
+ // Glob-match the project-root-relative path against `exclude`. `relative`
305
+ // returns native separators on Windows; posixify so the matcher sees the
306
+ // same forward-slash form the user wrote their globs in.
307
+ const relPath = toPosixPath(relative(options.projectRoot, posixPath));
308
+ if (getExcludeMatcher(options)(relPath))
309
+ return false;
310
+ // Check if file type is analyzable. Pass the posixified form so analyzer
311
+ // callbacks see the canonical id, not a native-separator variant.
312
+ return options.getAnalyzerType(posixPath) !== null;
313
+ };
314
+ /**
315
+ * Extract dependencies and dependents for a module from source file info.
316
+ *
317
+ * Filters to only include source modules (excludes external packages, node_modules, tests).
318
+ * Returns sorted arrays of module paths (relative to `sourceRoot`) for deterministic output.
319
+ *
320
+ * Native paths in `sourceFile.dependencies`/`dependents` are accepted —
321
+ * `isSource` and `extractPath` posixify their inputs, so direct callers with
322
+ * hand-built input need not pre-normalize.
323
+ *
324
+ * Accepts `SourceFileInfo` plus an optional `dependents` field — the public
325
+ * input type carries only `dependencies` (caller-supplied opt-in), while
326
+ * `dependents` is computed downstream by `computeDependents` and flows through
327
+ * as an enriched shape.
328
+ *
329
+ * @param sourceFile - the source file info to extract dependencies from
330
+ * @param options - module source options for filtering and path extraction
331
+ * @returns sorted arrays of module paths (relative to `sourceRoot`) for dependencies and dependents
332
+ */
333
+ export const extractDependencies = (sourceFile, options) => {
334
+ const dependencies = [];
335
+ const dependents = [];
336
+ // Extract dependencies (files this module imports) if provided
337
+ if (sourceFile.dependencies) {
338
+ for (const depId of sourceFile.dependencies) {
339
+ if (isSource(depId, options)) {
340
+ dependencies.push(extractPath(depId, options));
341
+ }
342
+ }
343
+ }
344
+ // Extract dependents (files that import this module) if provided
345
+ if (sourceFile.dependents) {
346
+ for (const dependentId of sourceFile.dependents) {
347
+ if (isSource(dependentId, options)) {
348
+ dependents.push(extractPath(dependentId, options));
349
+ }
350
+ }
351
+ }
352
+ // Sort for deterministic output
353
+ dependencies.sort();
354
+ dependents.sort();
355
+ return { dependencies, dependents };
356
+ };