@fuzdev/fuz_ui 0.175.0 → 0.176.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/README.md +1 -1
  2. package/dist/Alert.svelte +2 -0
  3. package/dist/Alert.svelte.d.ts.map +1 -1
  4. package/dist/ApiModule.svelte +5 -6
  5. package/dist/ApiModule.svelte.d.ts.map +1 -1
  6. package/dist/DeclarationDetail.svelte +2 -1
  7. package/dist/DeclarationDetail.svelte.d.ts.map +1 -1
  8. package/dist/Details.svelte +2 -0
  9. package/dist/Details.svelte.d.ts.map +1 -1
  10. package/dist/Themed.svelte +2 -0
  11. package/dist/Themed.svelte.d.ts.map +1 -1
  12. package/dist/analysis_context.d.ts +195 -0
  13. package/dist/analysis_context.d.ts.map +1 -0
  14. package/dist/analysis_context.js +134 -0
  15. package/dist/library_analysis.d.ts +112 -0
  16. package/dist/library_analysis.d.ts.map +1 -0
  17. package/dist/library_analysis.js +106 -0
  18. package/dist/library_gen.d.ts +88 -5
  19. package/dist/library_gen.d.ts.map +1 -1
  20. package/dist/library_gen.js +163 -69
  21. package/dist/library_gen_helpers.d.ts +81 -72
  22. package/dist/library_gen_helpers.d.ts.map +1 -1
  23. package/dist/library_gen_helpers.js +115 -156
  24. package/dist/library_gen_output.d.ts +34 -0
  25. package/dist/library_gen_output.d.ts.map +1 -0
  26. package/dist/library_gen_output.js +40 -0
  27. package/dist/mdz.d.ts +3 -0
  28. package/dist/mdz.d.ts.map +1 -1
  29. package/dist/mdz.js +12 -3
  30. package/dist/module_helpers.d.ts +246 -24
  31. package/dist/module_helpers.d.ts.map +1 -1
  32. package/dist/module_helpers.js +250 -42
  33. package/dist/svelte_helpers.d.ts +65 -10
  34. package/dist/svelte_helpers.d.ts.map +1 -1
  35. package/dist/svelte_helpers.js +171 -49
  36. package/dist/ts_helpers.d.ts +132 -61
  37. package/dist/ts_helpers.d.ts.map +1 -1
  38. package/dist/ts_helpers.js +423 -282
  39. package/dist/tsdoc_helpers.d.ts +11 -0
  40. package/dist/tsdoc_helpers.d.ts.map +1 -1
  41. package/dist/tsdoc_helpers.js +22 -47
  42. package/dist/tsdoc_mdz.d.ts +36 -0
  43. package/dist/tsdoc_mdz.d.ts.map +1 -0
  44. package/dist/tsdoc_mdz.js +56 -0
  45. package/dist/vite_plugin_library_well_known.js +5 -5
  46. package/package.json +11 -6
  47. package/src/lib/analysis_context.ts +250 -0
  48. package/src/lib/library_analysis.ts +168 -0
  49. package/src/lib/library_gen.ts +247 -84
  50. package/src/lib/library_gen_helpers.ts +148 -215
  51. package/src/lib/library_gen_output.ts +63 -0
  52. package/src/lib/mdz.ts +13 -4
  53. package/src/lib/module_helpers.ts +392 -47
  54. package/src/lib/svelte_helpers.ts +291 -55
  55. package/src/lib/ts_helpers.ts +538 -338
  56. package/src/lib/tsdoc_helpers.ts +24 -49
  57. package/src/lib/tsdoc_mdz.ts +62 -0
  58. package/src/lib/vite_plugin_library_well_known.ts +5 -5
@@ -5,33 +5,228 @@
5
5
  * and import relationships in the package generation system.
6
6
  *
7
7
  * All functions are prefixed with `module_` for clarity.
8
+ *
9
+ * @module
10
+ */
11
+ /**
12
+ * Analyzer type for source files.
13
+ *
14
+ * - `'typescript'` - TypeScript/JavaScript files analyzed via TypeScript Compiler API
15
+ * - `'svelte'` - Svelte components analyzed via svelte2tsx + TypeScript Compiler API
16
+ */
17
+ export type AnalyzerType = 'typescript' | 'svelte';
18
+ /**
19
+ * File information for source analysis.
20
+ *
21
+ * Can be constructed from Gro's Disknode or from plain file system access.
22
+ * This abstraction enables non-Gro usage while keeping Gro support via adapter.
23
+ *
24
+ * Note: `content` is required to keep analysis functions pure (no hidden I/O).
25
+ * Callers are responsible for reading file content before analysis.
8
26
  */
27
+ export interface SourceFileInfo {
28
+ /** Absolute path to the file. */
29
+ id: string;
30
+ /** File content (required - analysis functions don't read from disk). */
31
+ content: string;
32
+ /**
33
+ * Absolute file paths of modules this file imports (optional).
34
+ * Only include resolved local imports, not node_modules.
35
+ * Order should be declaration order in source for deterministic output.
36
+ */
37
+ dependencies?: ReadonlyArray<string>;
38
+ /**
39
+ * Absolute file paths of modules that import this file (optional).
40
+ * Only include resolved local imports, not node_modules.
41
+ */
42
+ dependents?: ReadonlyArray<string>;
43
+ }
9
44
  /**
10
- * Configuration for module source detection.
45
+ * Configuration for module source detection and path extraction.
11
46
  *
12
- * Allows customizing which paths are considered source modules,
13
- * useful for projects with non-standard directory structures.
47
+ * Uses proper path semantics with `project_root` as the base for all path operations.
48
+ * Paths are matched using `startsWith` rather than substring search, which correctly
49
+ * handles nested directories without special heuristics.
50
+ *
51
+ * @example
52
+ * const options = module_create_source_options(process.cwd(), {
53
+ * source_paths: ['src/lib', 'src/routes'],
54
+ * source_root: 'src',
55
+ * });
14
56
  */
15
57
  export interface ModuleSourceOptions {
16
- /** Source directory paths to include. @default ['/src/lib/'] */
17
- source_paths?: Array<string>;
18
- /** File extensions to analyze. @default ['.ts', '.js', '.svelte'] */
19
- extensions?: Array<string>;
20
- /** Patterns to exclude (matched against full path). @default [/\.test\.ts$/] */
21
- exclude_patterns?: Array<RegExp>;
58
+ /**
59
+ * Absolute path to the project root directory.
60
+ *
61
+ * All `source_paths` are relative to this. Typically `process.cwd()` when
62
+ * running from the project root via Gro, Vite, or other build tools.
63
+ *
64
+ * @example '/home/user/my-project'
65
+ */
66
+ project_root: string;
67
+ /**
68
+ * Source directory paths to include, relative to `project_root`.
69
+ *
70
+ * Paths should not have leading or trailing slashes - they are added
71
+ * internally for correct matching.
72
+ *
73
+ * @example ['src/lib'] - single source directory
74
+ * @example ['src/lib', 'src/routes'] - multiple directories
75
+ */
76
+ source_paths: Array<string>;
77
+ /**
78
+ * Source root for extracting relative module paths, relative to `project_root`.
79
+ *
80
+ * When omitted:
81
+ * - Single `source_path`: defaults to that path
82
+ * - Multiple `source_paths`: required (no auto-derivation)
83
+ *
84
+ * @example 'src/lib' - module paths like 'foo.ts', 'utils/bar.ts'
85
+ * @example 'src' - module paths like 'lib/foo.ts', 'routes/page.svelte'
86
+ */
87
+ source_root?: string;
88
+ /** Patterns to exclude (matched against full path). */
89
+ exclude_patterns: Array<RegExp>;
90
+ /**
91
+ * Determine which analyzer to use for a file path.
92
+ *
93
+ * Called for files in source directories. Return `'typescript'`, `'svelte'`,
94
+ * or `null` to skip the file. This is the single source of truth for which
95
+ * files are analyzable and how to analyze them.
96
+ *
97
+ * @default Uses file extension: `.svelte` → svelte, `.ts`/`.js` → typescript
98
+ *
99
+ * @example
100
+ * // Add MDsveX support
101
+ * get_analyzer: (path) => {
102
+ * if (path.endsWith('.svelte') || path.endsWith('.svx')) return 'svelte';
103
+ * if (path.endsWith('.ts') || path.endsWith('.js')) return 'typescript';
104
+ * return null;
105
+ * }
106
+ *
107
+ * @example
108
+ * // Include .d.ts files
109
+ * get_analyzer: (path) => {
110
+ * if (path.endsWith('.svelte')) return 'svelte';
111
+ * if (path.endsWith('.ts') || path.endsWith('.d.ts') || path.endsWith('.js')) return 'typescript';
112
+ * return null;
113
+ * }
114
+ */
115
+ get_analyzer: (path: string) => AnalyzerType | null;
22
116
  }
23
117
  /**
24
- * Default options for module source detection.
118
+ * Default analyzer resolver based on file extension.
119
+ *
120
+ * - `.svelte` → `'svelte'`
121
+ * - `.ts`, `.js` → `'typescript'`
122
+ * - Other extensions → `null` (skip)
123
+ */
124
+ export declare const module_get_analyzer_default: (path: string) => AnalyzerType | null;
125
+ /**
126
+ * Partial source options without `project_root`.
127
+ *
128
+ * Use with `module_create_source_options` to build complete options.
129
+ */
130
+ export type ModuleSourcePartial = Omit<ModuleSourceOptions, 'project_root'>;
131
+ /**
132
+ * Default partial options for standard SvelteKit library structure.
133
+ *
134
+ * Does not include `project_root` - use `module_create_source_options()` to create
135
+ * complete options with your project root.
136
+ */
137
+ export declare const MODULE_SOURCE_PARTIAL: ModuleSourcePartial;
138
+ /**
139
+ * Create complete source options from project root and optional overrides.
140
+ *
141
+ * @param project_root Absolute path to project root (typically `process.cwd()`)
142
+ * @param overrides Optional overrides for default options
143
+ *
144
+ * @example
145
+ * // Standard SvelteKit library
146
+ * const options = module_create_source_options(process.cwd());
147
+ *
148
+ * @example
149
+ * // Multiple source directories
150
+ * const options = module_create_source_options(process.cwd(), {
151
+ * source_paths: ['src/lib', 'src/routes'],
152
+ * source_root: 'src',
153
+ * });
154
+ *
155
+ * @example
156
+ * // Custom exclusions
157
+ * const options = module_create_source_options(process.cwd(), {
158
+ * exclude_patterns: [/\.test\.ts$/, /\.internal\.ts$/],
159
+ * });
160
+ */
161
+ export declare const module_create_source_options: (project_root: string, overrides?: Partial<ModuleSourcePartial>) => ModuleSourceOptions;
162
+ /**
163
+ * Validate `ModuleSourceOptions` format and consistency.
164
+ *
165
+ * Checks:
166
+ * 1. `project_root` is an absolute path (starts with `/`)
167
+ * 2. `source_paths` entries don't have leading/trailing slashes
168
+ * 3. `source_root` (if provided) doesn't have leading/trailing slashes
169
+ * 4. Multiple `source_paths` require explicit `source_root`
170
+ * 5. `source_root` is a prefix of all `source_paths`
171
+ *
172
+ * @throws Error if validation fails
173
+ *
174
+ * @example
175
+ * // Valid - single source path (source_root auto-derived)
176
+ * module_validate_source_options({
177
+ * project_root: '/home/user/project',
178
+ * source_paths: ['src/lib'],
179
+ * ...
180
+ * });
181
+ *
182
+ * @example
183
+ * // Valid - multiple source paths with explicit source_root
184
+ * module_validate_source_options({
185
+ * project_root: '/home/user/project',
186
+ * source_paths: ['src/lib', 'src/routes'],
187
+ * source_root: 'src',
188
+ * ...
189
+ * });
190
+ *
191
+ * @example
192
+ * // Invalid - multiple source paths without source_root
193
+ * module_validate_source_options({
194
+ * project_root: '/home/user/project',
195
+ * source_paths: ['src/lib', 'src/routes'], // throws
196
+ * ...
197
+ * });
198
+ */
199
+ export declare const module_validate_source_options: (options: ModuleSourceOptions) => void;
200
+ /**
201
+ * Get the effective source_root from options.
202
+ *
203
+ * Returns `source_root` if provided, otherwise returns `source_paths[0]` for single-path configs.
204
+ *
205
+ * @throws Error if source_root is required but not provided (multiple source_paths)
25
206
  */
26
- export declare const MODULE_SOURCE_DEFAULTS: Required<ModuleSourceOptions>;
207
+ export declare const module_get_source_root: (options: ModuleSourceOptions) => string;
27
208
  /**
28
- * Extract module path relative to src/lib from absolute source ID.
209
+ * Extract module path relative to source root from absolute source ID.
210
+ *
211
+ * Uses proper path semantics: strips `project_root/source_root/` prefix.
212
+ *
213
+ * @param source_id Absolute path to the source file
214
+ * @param options Module source options for path extraction
215
+ *
216
+ * @example
217
+ * const options = module_create_source_options('/home/user/project');
218
+ * module_extract_path('/home/user/project/src/lib/foo.ts', options) // => 'foo.ts'
219
+ * module_extract_path('/home/user/project/src/lib/nested/bar.svelte', options) // => 'nested/bar.svelte'
29
220
  *
30
221
  * @example
31
- * module_extract_path('/home/user/project/src/lib/foo.ts') // => 'foo.ts'
32
- * module_extract_path('/home/user/project/src/lib/nested/bar.svelte') // => 'nested/bar.svelte'
222
+ * const options = module_create_source_options('/home/user/project', {
223
+ * source_paths: ['src/lib', 'src/routes'],
224
+ * source_root: 'src',
225
+ * });
226
+ * module_extract_path('/home/user/project/src/lib/foo.ts', options) // => 'lib/foo.ts'
227
+ * module_extract_path('/home/user/project/src/routes/page.svelte', options) // => 'routes/page.svelte'
33
228
  */
34
- export declare const module_extract_path: (source_id: string) => string;
229
+ export declare const module_extract_path: (source_id: string, options: ModuleSourceOptions) => string;
35
230
  /**
36
231
  * Extract component name from a Svelte module path.
37
232
  *
@@ -47,23 +242,50 @@ export declare const module_get_component_name: (module_path: string) => string;
47
242
  * module_get_key('foo.ts') // => './foo.ts'
48
243
  */
49
244
  export declare const module_get_key: (module_path: string) => string;
245
+ /**
246
+ * Check if a path is a TypeScript or JavaScript file.
247
+ *
248
+ * Includes both `.ts` and `.js` files since JS files are valid in TS projects.
249
+ * Excludes `.d.ts` declaration files - use a custom `get_analyzer` to include them.
250
+ */
50
251
  export declare const module_is_typescript: (path: string) => boolean;
51
252
  export declare const module_is_svelte: (path: string) => boolean;
52
253
  export declare const module_is_css: (path: string) => boolean;
53
254
  export declare const module_is_json: (path: string) => boolean;
54
255
  export declare const module_is_test: (path: string) => boolean;
55
256
  /**
56
- * Check if a path matches source criteria.
257
+ * Check if a path is an analyzable source file.
57
258
  *
58
- * Checks source directory paths, file extensions, and exclusion patterns.
59
- * Uses defaults if no options provided.
259
+ * Combines all filtering: exclusion patterns, source directory paths,
260
+ * and analyzer availability. This is the single check for whether a
261
+ * file should be included in library analysis.
262
+ *
263
+ * Uses proper path semantics with `startsWith` matching against
264
+ * `project_root/source_path/`. No heuristics needed - nested directories
265
+ * are correctly excluded by the prefix check.
266
+ *
267
+ * @param path Full absolute path to check
268
+ * @param options Module source options for filtering
269
+ * @returns True if the path is an analyzable source file
270
+ *
271
+ * @example
272
+ * const options = module_create_source_options('/home/user/project');
273
+ * module_is_source('/home/user/project/src/lib/foo.ts', options) // => true
274
+ * module_is_source('/home/user/project/src/lib/foo.test.ts', options) // => false (excluded)
275
+ * module_is_source('/home/user/project/src/fixtures/mini/src/lib/bar.ts', options) // => false (wrong prefix)
276
+ */
277
+ export declare const module_is_source: (path: string, options: ModuleSourceOptions) => boolean;
278
+ /**
279
+ * Extract dependencies and dependents for a module from source file info.
60
280
  *
61
- * Rejects nested repo paths by ensuring the first `/src/` leads to the source directory
62
- * (e.g. rejects `/src/fixtures/repos/foo/src/lib/index.ts` because `/src/fixtures/` `/src/lib/`).
281
+ * Filters to only include source modules (excludes external packages, node_modules, tests).
282
+ * Returns sorted arrays of module paths (relative to source_root) for deterministic output.
63
283
  *
64
- * @param path Full path to check
65
- * @param options Configuration options (uses defaults if not provided)
66
- * @returns True if the path matches all criteria
284
+ * @param source_file The source file info to extract dependencies from
285
+ * @param options Module source options for filtering and path extraction
67
286
  */
68
- export declare const module_matches_source: (path: string, options?: ModuleSourceOptions) => boolean;
287
+ export declare const module_extract_dependencies: (source_file: SourceFileInfo, options: ModuleSourceOptions) => {
288
+ dependencies: Array<string>;
289
+ dependents: Array<string>;
290
+ };
69
291
  //# sourceMappingURL=module_helpers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"module_helpers.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/module_helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;;;GAKG;AACH,MAAM,WAAW,mBAAmB;IACnC,gEAAgE;IAChE,YAAY,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC7B,qEAAqE;IACrE,UAAU,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC3B,gFAAgF;IAChF,gBAAgB,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CACjC;AAED;;GAEG;AACH,eAAO,MAAM,sBAAsB,EAAE,QAAQ,CAAC,mBAAmB,CAIhE,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,GAAI,WAAW,MAAM,KAAG,MAGvD,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,yBAAyB,GAAI,aAAa,MAAM,KAAG,MACN,CAAC;AAE3D;;;;;GAKG;AACH,eAAO,MAAM,cAAc,GAAI,aAAa,MAAM,KAAG,MAA4B,CAAC;AAElF,eAAO,MAAM,oBAAoB,GAAI,MAAM,MAAM,KAAG,OACP,CAAC;AAE9C,eAAO,MAAM,gBAAgB,GAAI,MAAM,MAAM,KAAG,OAAmC,CAAC;AAEpF,eAAO,MAAM,aAAa,GAAI,MAAM,MAAM,KAAG,OAAgC,CAAC;AAE9E,eAAO,MAAM,cAAc,GAAI,MAAM,MAAM,KAAG,OAAiC,CAAC;AAEhF,eAAO,MAAM,cAAc,GAAI,MAAM,MAAM,KAAG,OAAoC,CAAC;AAEnF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,qBAAqB,GAAI,MAAM,MAAM,EAAE,UAAU,mBAAmB,KAAG,OAyBnF,CAAC"}
1
+ {"version":3,"file":"module_helpers.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/module_helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG,YAAY,GAAG,QAAQ,CAAC;AAEnD;;;;;;;;GAQG;AACH,MAAM,WAAW,cAAc;IAC9B,iCAAiC;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,yEAAyE;IACzE,OAAO,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,YAAY,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACrC;;;OAGG;IACH,UAAU,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CACnC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,mBAAmB;IACnC;;;;;;;OAOG;IACH,YAAY,EAAE,MAAM,CAAC;IACrB;;;;;;;;OAQG;IACH,YAAY,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC5B;;;;;;;;;OASG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uDAAuD;IACvD,gBAAgB,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAChC;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,YAAY,GAAG,IAAI,CAAC;CACpD;AAED;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,GAAI,MAAM,MAAM,KAAG,YAAY,GAAG,IAIzE,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,mBAAmB,GAAG,IAAI,CAAC,mBAAmB,EAAE,cAAc,CAAC,CAAC;AAE5E;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,EAAE,mBAInC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,4BAA4B,GACxC,cAAc,MAAM,EACpB,YAAY,OAAO,CAAC,mBAAmB,CAAC,KACtC,mBAID,CAAC;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,eAAO,MAAM,8BAA8B,GAAI,SAAS,mBAAmB,KAAG,IAqE7E,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,GAAI,SAAS,mBAAmB,KAAG,MAWrE,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,mBAAmB,GAAI,WAAW,MAAM,EAAE,SAAS,mBAAmB,KAAG,MAUrF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,yBAAyB,GAAI,aAAa,MAAM,KAAG,MACN,CAAC;AAE3D;;;;;GAKG;AACH,eAAO,MAAM,cAAc,GAAI,aAAa,MAAM,KAAG,MAA4B,CAAC;AAElF;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,GAAI,MAAM,MAAM,KAAG,OACsB,CAAC;AAE3E,eAAO,MAAM,gBAAgB,GAAI,MAAM,MAAM,KAAG,OAAmC,CAAC;AAEpF,eAAO,MAAM,aAAa,GAAI,MAAM,MAAM,KAAG,OAAgC,CAAC;AAE9E,eAAO,MAAM,cAAc,GAAI,MAAM,MAAM,KAAG,OAAiC,CAAC;AAEhF,eAAO,MAAM,cAAc,GAAI,MAAM,MAAM,KAAG,OAAoC,CAAC;AAEnF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,gBAAgB,GAAI,MAAM,MAAM,EAAE,SAAS,mBAAmB,KAAG,OAe7E,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,2BAA2B,GACvC,aAAa,cAAc,EAC3B,SAAS,mBAAmB,KAC1B;IAAC,YAAY,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAAC,UAAU,EAAE,KAAK,CAAC,MAAM,CAAC,CAAA;CA2BzD,CAAC"}
@@ -5,25 +5,197 @@
5
5
  * and import relationships in the package generation system.
6
6
  *
7
7
  * All functions are prefixed with `module_` for clarity.
8
+ *
9
+ * @module
8
10
  */
9
11
  /**
10
- * Default options for module source detection.
12
+ * Default analyzer resolver based on file extension.
13
+ *
14
+ * - `.svelte` → `'svelte'`
15
+ * - `.ts`, `.js` → `'typescript'`
16
+ * - Other extensions → `null` (skip)
11
17
  */
12
- export const MODULE_SOURCE_DEFAULTS = {
13
- source_paths: ['/src/lib/'],
14
- extensions: ['.ts', '.js', '.svelte'],
18
+ export const module_get_analyzer_default = (path) => {
19
+ if (module_is_svelte(path))
20
+ return 'svelte';
21
+ if (module_is_typescript(path))
22
+ return 'typescript';
23
+ return null;
24
+ };
25
+ /**
26
+ * Default partial options for standard SvelteKit library structure.
27
+ *
28
+ * Does not include `project_root` - use `module_create_source_options()` to create
29
+ * complete options with your project root.
30
+ */
31
+ export const MODULE_SOURCE_PARTIAL = {
32
+ source_paths: ['src/lib'],
15
33
  exclude_patterns: [/\.test\.ts$/],
34
+ get_analyzer: module_get_analyzer_default,
35
+ };
36
+ /**
37
+ * Create complete source options from project root and optional overrides.
38
+ *
39
+ * @param project_root Absolute path to project root (typically `process.cwd()`)
40
+ * @param overrides Optional overrides for default options
41
+ *
42
+ * @example
43
+ * // Standard SvelteKit library
44
+ * const options = module_create_source_options(process.cwd());
45
+ *
46
+ * @example
47
+ * // Multiple source directories
48
+ * const options = module_create_source_options(process.cwd(), {
49
+ * source_paths: ['src/lib', 'src/routes'],
50
+ * source_root: 'src',
51
+ * });
52
+ *
53
+ * @example
54
+ * // Custom exclusions
55
+ * const options = module_create_source_options(process.cwd(), {
56
+ * exclude_patterns: [/\.test\.ts$/, /\.internal\.ts$/],
57
+ * });
58
+ */
59
+ export const module_create_source_options = (project_root, overrides) => ({
60
+ project_root,
61
+ ...MODULE_SOURCE_PARTIAL,
62
+ ...overrides,
63
+ });
64
+ /**
65
+ * Validate `ModuleSourceOptions` format and consistency.
66
+ *
67
+ * Checks:
68
+ * 1. `project_root` is an absolute path (starts with `/`)
69
+ * 2. `source_paths` entries don't have leading/trailing slashes
70
+ * 3. `source_root` (if provided) doesn't have leading/trailing slashes
71
+ * 4. Multiple `source_paths` require explicit `source_root`
72
+ * 5. `source_root` is a prefix of all `source_paths`
73
+ *
74
+ * @throws Error if validation fails
75
+ *
76
+ * @example
77
+ * // Valid - single source path (source_root auto-derived)
78
+ * module_validate_source_options({
79
+ * project_root: '/home/user/project',
80
+ * source_paths: ['src/lib'],
81
+ * ...
82
+ * });
83
+ *
84
+ * @example
85
+ * // Valid - multiple source paths with explicit source_root
86
+ * module_validate_source_options({
87
+ * project_root: '/home/user/project',
88
+ * source_paths: ['src/lib', 'src/routes'],
89
+ * source_root: 'src',
90
+ * ...
91
+ * });
92
+ *
93
+ * @example
94
+ * // Invalid - multiple source paths without source_root
95
+ * module_validate_source_options({
96
+ * project_root: '/home/user/project',
97
+ * source_paths: ['src/lib', 'src/routes'], // throws
98
+ * ...
99
+ * });
100
+ */
101
+ export const module_validate_source_options = (options) => {
102
+ const { project_root, source_paths, source_root } = options;
103
+ // Validate project_root is absolute
104
+ if (!project_root.startsWith('/')) {
105
+ throw new Error(`project_root must be an absolute path (start with "/"): "${project_root}"`);
106
+ }
107
+ // Validate project_root doesn't have trailing slash (we add it internally)
108
+ if (project_root.endsWith('/')) {
109
+ throw new Error(`project_root should not have trailing slash: "${project_root}". ` +
110
+ `Trailing slashes are added internally for correct matching.`);
111
+ }
112
+ // Validate source_paths
113
+ if (source_paths.length === 0) {
114
+ throw new Error('source_paths must have at least one entry');
115
+ }
116
+ for (const source_path of source_paths) {
117
+ if (source_path.startsWith('/')) {
118
+ throw new Error(`source_paths entry should not start with "/": "${source_path}". ` +
119
+ `Paths are relative to project_root.`);
120
+ }
121
+ if (source_path.endsWith('/')) {
122
+ throw new Error(`source_paths entry should not end with "/": "${source_path}". ` +
123
+ `Trailing slashes are added internally for correct matching.`);
124
+ }
125
+ }
126
+ // Validate source_root if provided
127
+ if (source_root !== undefined) {
128
+ if (source_root.startsWith('/')) {
129
+ throw new Error(`source_root should not start with "/": "${source_root}". ` +
130
+ `Paths are relative to project_root.`);
131
+ }
132
+ if (source_root.endsWith('/')) {
133
+ throw new Error(`source_root should not end with "/": "${source_root}". ` +
134
+ `Trailing slashes are added internally for correct matching.`);
135
+ }
136
+ // Validate each source_path starts with source_root
137
+ for (const source_path of source_paths) {
138
+ // source_path should equal source_root or start with source_root/
139
+ if (source_path !== source_root && !source_path.startsWith(source_root + '/')) {
140
+ throw new Error(`source_paths entry "${source_path}" must start with source_root "${source_root}". ` +
141
+ `module_extract_path uses source_root to compute module paths.`);
142
+ }
143
+ }
144
+ }
145
+ else if (source_paths.length > 1) {
146
+ // Multiple source_paths without source_root - error
147
+ throw new Error(`source_root is required when source_paths has multiple entries. ` +
148
+ `Got source_paths: [${source_paths.map((p) => `"${p}"`).join(', ')}]. ` +
149
+ `Provide source_root to specify the common prefix for module path extraction.`);
150
+ }
16
151
  };
17
152
  /**
18
- * Extract module path relative to src/lib from absolute source ID.
153
+ * Get the effective source_root from options.
154
+ *
155
+ * Returns `source_root` if provided, otherwise returns `source_paths[0]` for single-path configs.
156
+ *
157
+ * @throws Error if source_root is required but not provided (multiple source_paths)
158
+ */
159
+ export const module_get_source_root = (options) => {
160
+ if (options.source_root !== undefined) {
161
+ return options.source_root;
162
+ }
163
+ if (options.source_paths.length === 1) {
164
+ return options.source_paths[0];
165
+ }
166
+ throw new Error(`source_root is required when source_paths has multiple entries. ` +
167
+ `Got source_paths: [${options.source_paths.map((p) => `"${p}"`).join(', ')}].`);
168
+ };
169
+ /**
170
+ * Extract module path relative to source root from absolute source ID.
171
+ *
172
+ * Uses proper path semantics: strips `project_root/source_root/` prefix.
173
+ *
174
+ * @param source_id Absolute path to the source file
175
+ * @param options Module source options for path extraction
176
+ *
177
+ * @example
178
+ * const options = module_create_source_options('/home/user/project');
179
+ * module_extract_path('/home/user/project/src/lib/foo.ts', options) // => 'foo.ts'
180
+ * module_extract_path('/home/user/project/src/lib/nested/bar.svelte', options) // => 'nested/bar.svelte'
19
181
  *
20
182
  * @example
21
- * module_extract_path('/home/user/project/src/lib/foo.ts') // => 'foo.ts'
22
- * module_extract_path('/home/user/project/src/lib/nested/bar.svelte') // => 'nested/bar.svelte'
183
+ * const options = module_create_source_options('/home/user/project', {
184
+ * source_paths: ['src/lib', 'src/routes'],
185
+ * source_root: 'src',
186
+ * });
187
+ * module_extract_path('/home/user/project/src/lib/foo.ts', options) // => 'lib/foo.ts'
188
+ * module_extract_path('/home/user/project/src/routes/page.svelte', options) // => 'routes/page.svelte'
23
189
  */
24
- export const module_extract_path = (source_id) => {
25
- const lib_index = source_id.indexOf('/src/lib/');
26
- return lib_index !== -1 ? source_id.substring(lib_index + 9) : source_id;
190
+ export const module_extract_path = (source_id, options) => {
191
+ const effective_root = module_get_source_root(options);
192
+ // Build the full prefix: project_root + '/' + source_root + '/'
193
+ const prefix = options.project_root + '/' + effective_root + '/';
194
+ if (source_id.startsWith(prefix)) {
195
+ return source_id.slice(prefix.length);
196
+ }
197
+ // Fallback: return full path if prefix doesn't match (shouldn't happen with valid inputs)
198
+ return source_id;
27
199
  };
28
200
  /**
29
201
  * Extract component name from a Svelte module path.
@@ -40,48 +212,84 @@ export const module_get_component_name = (module_path) => module_path.replace(/^
40
212
  * module_get_key('foo.ts') // => './foo.ts'
41
213
  */
42
214
  export const module_get_key = (module_path) => `./${module_path}`;
43
- export const module_is_typescript = (path) => path.endsWith('.ts') || path.endsWith('.js'); // hackyy but fine?
215
+ /**
216
+ * Check if a path is a TypeScript or JavaScript file.
217
+ *
218
+ * Includes both `.ts` and `.js` files since JS files are valid in TS projects.
219
+ * Excludes `.d.ts` declaration files - use a custom `get_analyzer` to include them.
220
+ */
221
+ export const module_is_typescript = (path) => (path.endsWith('.ts') && !path.endsWith('.d.ts')) || path.endsWith('.js');
44
222
  export const module_is_svelte = (path) => path.endsWith('.svelte');
45
223
  export const module_is_css = (path) => path.endsWith('.css');
46
224
  export const module_is_json = (path) => path.endsWith('.json');
47
225
  export const module_is_test = (path) => path.endsWith('.test.ts');
48
226
  /**
49
- * Check if a path matches source criteria.
227
+ * Check if a path is an analyzable source file.
50
228
  *
51
- * Checks source directory paths, file extensions, and exclusion patterns.
52
- * Uses defaults if no options provided.
229
+ * Combines all filtering: exclusion patterns, source directory paths,
230
+ * and analyzer availability. This is the single check for whether a
231
+ * file should be included in library analysis.
53
232
  *
54
- * Rejects nested repo paths by ensuring the first `/src/` leads to the source directory
55
- * (e.g. rejects `/src/fixtures/repos/foo/src/lib/index.ts` because `/src/fixtures/` `/src/lib/`).
233
+ * Uses proper path semantics with `startsWith` matching against
234
+ * `project_root/source_path/`. No heuristics needed - nested directories
235
+ * are correctly excluded by the prefix check.
56
236
  *
57
- * @param path Full path to check
58
- * @param options Configuration options (uses defaults if not provided)
59
- * @returns True if the path matches all criteria
237
+ * @param path Full absolute path to check
238
+ * @param options Module source options for filtering
239
+ * @returns True if the path is an analyzable source file
240
+ *
241
+ * @example
242
+ * const options = module_create_source_options('/home/user/project');
243
+ * module_is_source('/home/user/project/src/lib/foo.ts', options) // => true
244
+ * module_is_source('/home/user/project/src/lib/foo.test.ts', options) // => false (excluded)
245
+ * module_is_source('/home/user/project/src/fixtures/mini/src/lib/bar.ts', options) // => false (wrong prefix)
60
246
  */
61
- export const module_matches_source = (path, options) => {
62
- const opts = { ...MODULE_SOURCE_DEFAULTS, ...options };
63
- // Check if path is in one of the source directories
64
- // The first /src/ in the path must lead directly to the source directory
65
- // This rejects nested repos like /src/fixtures/repos/foo/src/lib/
66
- const in_source_dir = opts.source_paths.some((source_path) => {
67
- if (!path.includes(source_path))
68
- return false;
69
- // Find the first /src/ and verify it's part of the source_path
70
- const first_src_index = path.indexOf('/src/');
71
- if (first_src_index === -1)
72
- return false;
73
- // The source_path should start at that position
74
- return path.substring(first_src_index).startsWith(source_path);
247
+ export const module_is_source = (path, options) => {
248
+ // Check exclusion patterns first (fast regex check)
249
+ const is_excluded = options.exclude_patterns.some((pattern) => pattern.test(path));
250
+ if (is_excluded)
251
+ return false;
252
+ // Check if path starts with project_root/source_path/
253
+ // Using startsWith with trailing slash ensures correct directory matching
254
+ const in_source_dir = options.source_paths.some((source_path) => {
255
+ const full_prefix = options.project_root + '/' + source_path + '/';
256
+ return path.startsWith(full_prefix);
75
257
  });
76
258
  if (!in_source_dir)
77
259
  return false;
78
- // Check if extension matches
79
- const has_valid_extension = opts.extensions.some((ext) => path.endsWith(ext));
80
- if (!has_valid_extension)
81
- return false;
82
- // Check exclusion patterns
83
- const is_excluded = opts.exclude_patterns.some((pattern) => pattern.test(path));
84
- if (is_excluded)
85
- return false;
86
- return true;
260
+ // Check if file type is analyzable
261
+ return options.get_analyzer(path) !== null;
262
+ };
263
+ /**
264
+ * Extract dependencies and dependents for a module from source file info.
265
+ *
266
+ * Filters to only include source modules (excludes external packages, node_modules, tests).
267
+ * Returns sorted arrays of module paths (relative to source_root) for deterministic output.
268
+ *
269
+ * @param source_file The source file info to extract dependencies from
270
+ * @param options Module source options for filtering and path extraction
271
+ */
272
+ export const module_extract_dependencies = (source_file, options) => {
273
+ const dependencies = [];
274
+ const dependents = [];
275
+ // Extract dependencies (files this module imports) if provided
276
+ if (source_file.dependencies) {
277
+ for (const dep_id of source_file.dependencies) {
278
+ if (module_is_source(dep_id, options)) {
279
+ dependencies.push(module_extract_path(dep_id, options));
280
+ }
281
+ }
282
+ }
283
+ // Extract dependents (files that import this module) if provided
284
+ if (source_file.dependents) {
285
+ for (const dependent_id of source_file.dependents) {
286
+ if (module_is_source(dependent_id, options)) {
287
+ dependents.push(module_extract_path(dependent_id, options));
288
+ }
289
+ }
290
+ }
291
+ // Sort for deterministic output
292
+ dependencies.sort();
293
+ dependents.sort();
294
+ return { dependencies, dependents };
87
295
  };