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,377 @@
1
+ /**
2
+ * Diagnostic collection for source analysis.
3
+ *
4
+ * Provides structured error/warning collection during TypeScript and Svelte
5
+ * analysis, replacing silent catch blocks with actionable diagnostics.
6
+ *
7
+ * ## Error Handling Contract
8
+ *
9
+ * The pipeline accumulates failures into the `diagnostics` array and keeps
10
+ * going — analysis continues, declarations/members reach the output with
11
+ * `partial: true` when extraction failed mid-flight, and the caller decides
12
+ * how to react. Producers include type resolution failures, individual
13
+ * member/prop extraction failures, svelte2tsx transform failures, source-map
14
+ * construction failures, import lex / resolver failures, and duplicate
15
+ * declarations.
16
+ *
17
+ * A small set of conditions still throws from public entry points — these are
18
+ * setup-level, not per-file: missing `tsconfig.json` (`loadTsconfig`), Svelte
19
+ * <5 detected (`transformSvelteSource`), or strict `discovery: 'exports'`
20
+ * mode with no resolvable exports (`discoverSourceFiles`). Wrap the top-level
21
+ * `analyze` / `analyzeFromFiles` call if you want to handle these.
22
+ *
23
+ * ## Usage Pattern
24
+ *
25
+ * ```ts
26
+ * const {modules, diagnostics} = await analyze({sourceFiles, sourceOptions});
27
+ *
28
+ * if (hasErrors(diagnostics)) {
29
+ * for (const err of errorsOf(diagnostics)) {
30
+ * console.error(formatDiagnostic(err));
31
+ * }
32
+ * }
33
+ * for (const warning of warningsOf(diagnostics)) {
34
+ * console.warn(formatDiagnostic(warning));
35
+ * }
36
+ * ```
37
+ *
38
+ * ## Schema-Validated Plain Data
39
+ *
40
+ * `AnalyzeResultJson.diagnostics` is `Array<Diagnostic>` — no wrapper object,
41
+ * no methods, no private fields. Round-trip-safe through `JSON.stringify` /
42
+ * `z.array(Diagnostic).parse` and symmetric with `AnalyzeResultJson.modules`
43
+ * (also Zod-validated). The full envelope is itself a Zod schema
44
+ * (`AnalyzeResultJson` in `analyze-core.ts`) with both fields defaulting to
45
+ * `[]`, so the whole `{modules, diagnostics}` shape round-trips through
46
+ * `JSON.stringify(result, compactReplacer)` / `AnalyzeResultJson.parse` even
47
+ * when one or both arrays are empty (the wire form becomes `{}` and `.parse()`
48
+ * restores the defaults). Use the free helpers `hasErrors`, `errorsOf`,
49
+ * `byKind` for queries; mutate the array with native `Array.push`.
50
+ *
51
+ * ## File Path Contract
52
+ *
53
+ * `Diagnostic.file` is always project-root-relative — no leading slash,
54
+ * no `./` prefix. `analyze` / `analyzeFromFiles` normalize paths from all
55
+ * sources (extraction, discovery, dependency resolution) before returning.
56
+ * Consumers that need an absolute path can rejoin with `projectRoot`.
57
+ *
58
+ * @module
59
+ */
60
+ import { z } from 'zod';
61
+ // ── Severity ─────────────────────────────────────────────────────────────────
62
+ /**
63
+ * Diagnostic severity levels.
64
+ *
65
+ * - `'error'` — analysis failed, declaration may be incomplete or missing data
66
+ * - `'warning'` — partial success, something seems off but analysis continued
67
+ */
68
+ export const DiagnosticSeverity = z.enum(['error', 'warning']);
69
+ // ── Kind ─────────────────────────────────────────────────────────────────────
70
+ /**
71
+ * Discriminant for `Diagnostic` variant types.
72
+ */
73
+ export const DiagnosticKind = z.enum([
74
+ 'type_extraction_failed',
75
+ 'signature_analysis_failed',
76
+ 'class_member_failed',
77
+ 'svelte_prop_failed',
78
+ 'module_skipped',
79
+ 'module_unreadable',
80
+ 'import_parse_failed',
81
+ 'duplicate_comment',
82
+ 'misplaced_tag',
83
+ 'unknown_param',
84
+ 'source_map_failed',
85
+ 'duplicate_declaration',
86
+ 'transform_failed',
87
+ 'resolver_failed',
88
+ ]);
89
+ // ── Variants ─────────────────────────────────────────────────────────────────
90
+ /**
91
+ * Base diagnostic fields shared by every variant.
92
+ *
93
+ * `line` / `column` are 1-based and `.optional()` — absent when the diagnostic
94
+ * has no precise source location (e.g., a module-level skip with no specific
95
+ * AST node). Mirrors the rest of the schema's "absent = missing key" rule;
96
+ * use `??` at the call site if you need a placeholder.
97
+ */
98
+ const baseDiagnosticFields = {
99
+ /**
100
+ * File path relative to project root (no leading slash, no `./` prefix).
101
+ *
102
+ * Normalized at every public API boundary — `session.setFile`/`setFiles`
103
+ * (ingest-time), `session.query` (query-time, via `analyzeCore`), and the
104
+ * one-shot wrappers `analyze` / `analyzeFromFiles`. Producers inside the
105
+ * pipeline may write absolute or virtual paths (e.g.,
106
+ * `Foo.svelte.__svelte2tsx__.ts`); normalization rewrites them to
107
+ * project-relative form before they reach consumers.
108
+ */
109
+ file: z.string(),
110
+ /** Line number (1-based), absent if location unavailable. */
111
+ line: z.number().int().positive().optional(),
112
+ /** Column number (1-based), absent if location unavailable. */
113
+ column: z.number().int().positive().optional(),
114
+ /** Human-readable description of the issue. */
115
+ message: z.string(),
116
+ severity: DiagnosticSeverity,
117
+ };
118
+ /**
119
+ * Type extraction failed (e.g., complex or recursive types).
120
+ */
121
+ export const TypeExtractionDiagnostic = z.strictObject({
122
+ kind: z.literal('type_extraction_failed'),
123
+ ...baseDiagnosticFields,
124
+ /** Name of the symbol whose type couldn't be extracted. */
125
+ symbolName: z.string(),
126
+ });
127
+ /**
128
+ * Function/method signature analysis failed.
129
+ */
130
+ export const SignatureAnalysisDiagnostic = z.strictObject({
131
+ kind: z.literal('signature_analysis_failed'),
132
+ ...baseDiagnosticFields,
133
+ /** Name of the function or method. */
134
+ functionName: z.string(),
135
+ });
136
+ /**
137
+ * Class member analysis failed.
138
+ */
139
+ export const ClassMemberDiagnostic = z.strictObject({
140
+ kind: z.literal('class_member_failed'),
141
+ ...baseDiagnosticFields,
142
+ /** Name of the class. */
143
+ className: z.string(),
144
+ /** Name of the member that failed. */
145
+ memberName: z.string(),
146
+ });
147
+ /**
148
+ * Svelte prop type resolution failed.
149
+ */
150
+ export const SveltePropDiagnostic = z.strictObject({
151
+ kind: z.literal('svelte_prop_failed'),
152
+ ...baseDiagnosticFields,
153
+ /** Name of the component. */
154
+ componentName: z.string(),
155
+ /** Name of the prop. */
156
+ propName: z.string(),
157
+ });
158
+ /**
159
+ * Module was skipped during the analysis pass.
160
+ *
161
+ * Always `warning` severity — the rest of the analysis still runs and the
162
+ * module's absence in `modules` reflects the skip. Reasons:
163
+ *
164
+ * - `not_in_program` — the file wasn't in the `ts.Program` (race between
165
+ * session ingest and `query`, or virtual file missing for a `.svelte`)
166
+ * - `no_analyzer` — file extension didn't match any analyzer
167
+ * - `requires_program` — Svelte file reached the non-Svelte dispatcher (caller
168
+ * should use the session API instead of `analyzeModule` directly)
169
+ *
170
+ * Discovery-time file-read failures are a separate kind: `module_unreadable`.
171
+ */
172
+ export const ModuleSkippedDiagnostic = z.strictObject({
173
+ kind: z.literal('module_skipped'),
174
+ ...baseDiagnosticFields,
175
+ /** Reason the module was skipped. */
176
+ reason: z.enum(['not_in_program', 'no_analyzer', 'requires_program']),
177
+ });
178
+ /**
179
+ * File discovered via package.json `exports` exists but couldn't be read.
180
+ *
181
+ * Always `error` severity — discovery-time, emitted by `discoverFromExports`
182
+ * when `readFile` fails (permission denied, FS error). Distinct from
183
+ * `module_skipped` so severity is a stable per-kind property and downstream
184
+ * consumers can route discovery failures separately from analysis-pass skips.
185
+ */
186
+ export const ModuleUnreadableDiagnostic = z.strictObject({
187
+ kind: z.literal('module_unreadable'),
188
+ ...baseDiagnosticFields,
189
+ });
190
+ /**
191
+ * Import parsing failed during dependency resolution.
192
+ */
193
+ export const ImportParseDiagnostic = z.strictObject({
194
+ kind: z.literal('import_parse_failed'),
195
+ ...baseDiagnosticFields,
196
+ });
197
+ /**
198
+ * Duplicate comment sources detected (e.g., both HTML and JSDoc `@module`,
199
+ * or both HTML `@component` and JSDoc for component `docComment`).
200
+ */
201
+ export const DuplicateCommentDiagnostic = z.strictObject({
202
+ kind: z.literal('duplicate_comment'),
203
+ ...baseDiagnosticFields,
204
+ /** Which comment type is duplicated. */
205
+ commentType: z.enum(['module_comment', 'doc_comment']),
206
+ });
207
+ /**
208
+ * Symbol-scope JSDoc tag (`@example`, `@deprecated`, `@since`, `@see`,
209
+ * `@throws`, `@mutates`, `@default`, `@nodocs`) found on a non-primary overload
210
+ * signature.
211
+ *
212
+ * Symbol-scope tags describe the function as a whole, not individual
213
+ * signatures. The primary signature's JSDoc feeds the parent declaration's
214
+ * symbol-level extraction; tags on non-primary overload signatures are
215
+ * silently dropped from output. This diagnostic surfaces them so authors
216
+ * can move them to the primary signature.
217
+ *
218
+ * `@default` and `@nodocs` are included here even though overloads never
219
+ * carry a `defaultValue` or per-overload exclusion: their presence on a
220
+ * non-primary signature is always a misplacement.
221
+ */
222
+ export const MisplacedTagDiagnostic = z.strictObject({
223
+ kind: z.literal('misplaced_tag'),
224
+ ...baseDiagnosticFields,
225
+ /** The tag name without the `@` prefix (e.g., `'example'`, `'deprecated'`). */
226
+ tagName: z.enum([
227
+ 'example',
228
+ 'deprecated',
229
+ 'since',
230
+ 'see',
231
+ 'throws',
232
+ 'mutates',
233
+ 'default',
234
+ 'nodocs',
235
+ ]),
236
+ /** Name of the function or method whose overload carries the misplaced tag. */
237
+ functionName: z.string(),
238
+ });
239
+ /**
240
+ * `@param` tag references a name that doesn't match any actual parameter.
241
+ *
242
+ * Usually a typo (`@param argz` for parameter `args`) or stale doc after a
243
+ * rename. The description is dropped; analysis continues.
244
+ */
245
+ export const UnknownParamDiagnostic = z.strictObject({
246
+ kind: z.literal('unknown_param'),
247
+ ...baseDiagnosticFields,
248
+ /** The `@param` key that didn't match a real parameter. */
249
+ paramName: z.string(),
250
+ /** Name of the function or method. */
251
+ functionName: z.string(),
252
+ });
253
+ /**
254
+ * A declaration `name` appears in more than one module.
255
+ *
256
+ * Library docs assume a flat namespace — two modules exporting the same name
257
+ * collide when consumers `import {name}`. Emitted at `warning` severity so
258
+ * the analysis result remains usable; consumers who want it fatal can promote
259
+ * via `onDuplicates: 'throw'` or by inspecting the diagnostic. The default
260
+ * slot (`name === 'default'`) is excluded — every module owns its own.
261
+ */
262
+ export const DuplicateDeclarationDiagnostic = z.strictObject({
263
+ kind: z.literal('duplicate_declaration'),
264
+ ...baseDiagnosticFields,
265
+ /** The duplicated declaration name. */
266
+ declarationName: z.string(),
267
+ /** Module paths where the name was defined (>= 2). */
268
+ modules: z.array(z.string()).min(2),
269
+ });
270
+ /**
271
+ * Source map parsing failed for a Svelte virtual file.
272
+ *
273
+ * Analysis continues using virtual-file positions, so downstream `line`/`column`
274
+ * fields on other diagnostics may point into the svelte2tsx-generated TS rather
275
+ * than the original `.svelte` source. Rare; usually signals a malformed or
276
+ * incompatible svelte2tsx output.
277
+ *
278
+ * Emitted at ingest time by `transformSvelteSource` (in `svelte.ts`); flows
279
+ * through `setFile`/`setFiles` ingest diagnostics rather than the analysis
280
+ * pass.
281
+ */
282
+ export const SourceMapFailedDiagnostic = z.strictObject({
283
+ kind: z.literal('source_map_failed'),
284
+ ...baseDiagnosticFields,
285
+ });
286
+ /**
287
+ * svelte2tsx transform threw for a `.svelte` file.
288
+ *
289
+ * Unrecoverable at ingest — the file's owned-entry stays in the session
290
+ * (`virtual: undefined`, `unfilteredDeps: []`, `transformFailed: true`) so
291
+ * `query()` can synthesize a placeholder `ModuleJson` (`partial: true`,
292
+ * empty `declarations`). The originating error message lives in `message`;
293
+ * this diagnostic is the authoritative cause-of-failure signal.
294
+ */
295
+ export const TransformFailedDiagnostic = z.strictObject({
296
+ kind: z.literal('transform_failed'),
297
+ ...baseDiagnosticFields,
298
+ });
299
+ /**
300
+ * Import resolver threw while resolving a specifier.
301
+ *
302
+ * Distinguishes a buggy resolver from a legitimately unresolvable specifier:
303
+ * resolvers that return `null` for unknown specifiers stay silent (the normal
304
+ * "external package" case); resolvers that *throw* surface here so consumers
305
+ * can fix the resolver. Recoverable — the session treats the throw as `null`
306
+ * and continues, so analysis still runs but with a missing dependency edge.
307
+ *
308
+ * Emitted at ingest time by the session's resolve phase.
309
+ */
310
+ export const ResolverFailedDiagnostic = z.strictObject({
311
+ kind: z.literal('resolver_failed'),
312
+ ...baseDiagnosticFields,
313
+ /** The import specifier the resolver failed on. */
314
+ specifier: z.string(),
315
+ });
316
+ /**
317
+ * Discriminated union of all diagnostic variants.
318
+ */
319
+ export const Diagnostic = z.discriminatedUnion('kind', [
320
+ TypeExtractionDiagnostic,
321
+ SignatureAnalysisDiagnostic,
322
+ ClassMemberDiagnostic,
323
+ SveltePropDiagnostic,
324
+ ModuleSkippedDiagnostic,
325
+ ModuleUnreadableDiagnostic,
326
+ ImportParseDiagnostic,
327
+ DuplicateCommentDiagnostic,
328
+ MisplacedTagDiagnostic,
329
+ UnknownParamDiagnostic,
330
+ SourceMapFailedDiagnostic,
331
+ DuplicateDeclarationDiagnostic,
332
+ TransformFailedDiagnostic,
333
+ ResolverFailedDiagnostic,
334
+ ]);
335
+ // ── Helpers ────────────────────────────────────────────────────────────────
336
+ /**
337
+ * Check if any error-severity diagnostics were collected.
338
+ */
339
+ export const hasErrors = (diagnostics) => diagnostics.some((d) => d.severity === 'error');
340
+ /**
341
+ * Check if any warning-severity diagnostics were collected.
342
+ */
343
+ export const hasWarnings = (diagnostics) => diagnostics.some((d) => d.severity === 'warning');
344
+ /**
345
+ * Get all error-severity diagnostics.
346
+ */
347
+ export const errorsOf = (diagnostics) => diagnostics.filter((d) => d.severity === 'error');
348
+ /**
349
+ * Get all warning-severity diagnostics.
350
+ */
351
+ export const warningsOf = (diagnostics) => diagnostics.filter((d) => d.severity === 'warning');
352
+ /**
353
+ * Get diagnostics of a specific kind, narrowed to the matching variant.
354
+ */
355
+ export const byKind = (diagnostics, kind) => diagnostics.filter((d) => d.kind === kind);
356
+ /**
357
+ * Format a diagnostic for display.
358
+ *
359
+ * Assumes `diagnostic.file` is project-root-relative (the contract enforced
360
+ * by `analyze` / `analyzeFromFiles`). The displayed path is prefixed with `./`.
361
+ *
362
+ * @param diagnostic - the diagnostic to format
363
+ * @returns formatted string like `'./file.ts:10:5: error: message'`
364
+ *
365
+ * @example
366
+ * ```ts
367
+ * for (const d of errorsOf(diagnostics)) {
368
+ * console.error(formatDiagnostic(d));
369
+ * }
370
+ * ```
371
+ */
372
+ export const formatDiagnostic = (diagnostic) => {
373
+ const { file, line, column, severity, message } = diagnostic;
374
+ const location = line !== undefined ? (column !== undefined ? `${line}:${column}` : `${line}`) : '';
375
+ const filePart = location ? `./${file}:${location}` : `./${file}`;
376
+ return `${filePart}: ${severity}: ${message}`;
377
+ };
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Source file discovery — exports-first with glob fallback.
3
+ *
4
+ * Used by `analyzeFromFiles` to locate source files from a project root.
5
+ * Build tool integrations using `analyze` should construct `SourceFileInfo[]`
6
+ * from their own module graph instead.
7
+ *
8
+ * @see `exports.ts` for `discoverFromExports` (package.json exports → source files)
9
+ * @see `files.ts` for `globFiles` (glob-based discovery)
10
+ *
11
+ * @module
12
+ */
13
+ import type { SourceFileInfo } from './source.js';
14
+ import { type ModuleSourceOptions } from './source-config.js';
15
+ import type { Diagnostic } from './diagnostics.js';
16
+ import type { AnalysisLog } from './log.js';
17
+ /**
18
+ * Discovery strategy for source files.
19
+ *
20
+ * - `'auto'` (default) — try package.json `exports` first, fall back to glob
21
+ * patterns when `exports` is missing or resolves to nothing
22
+ * - `'exports'` — package.json `exports` only, **throw** if `exports` is missing
23
+ * or resolves to no source files (strict mode for libraries that should
24
+ * always declare their public surface via `exports`)
25
+ * - `'glob'` — skip `exports` entirely, use glob patterns
26
+ *
27
+ * Providing `include` patterns implies `'glob'` semantics regardless of mode
28
+ * — when `discovery: 'auto'` and `include` is set, the auto fallback chain
29
+ * collapses to glob immediately. Combining `discovery: 'exports'` with
30
+ * `include` is a configuration error (the modes are contradictory) and
31
+ * throws at discovery time.
32
+ */
33
+ export type Discovery = 'auto' | 'exports' | 'glob';
34
+ /** Options for `discoverSourceFiles`. */
35
+ export interface DiscoverSourceFilesOptions {
36
+ /**
37
+ * Source options used to resolve the source directory for exports-based discovery.
38
+ *
39
+ * `sourceOptions.projectRoot` is the resolution base for `include` globs and the
40
+ * `sourceOptions.exclude` glob patterns. Build via `createSourceOptions` (which
41
+ * normalizes) or pass a normalized return from `normalizeSourceOptions`.
42
+ *
43
+ * `sourceOptions.exclude` is the single source of truth for exclusion globs —
44
+ * applied at both this discovery stage and analysis time (via `isSource()`).
45
+ */
46
+ sourceOptions: ModuleSourceOptions;
47
+ /**
48
+ * Glob patterns to include (relative to `projectRoot`).
49
+ *
50
+ * Filter for glob-based discovery. When `discovery` is `'auto'` (default),
51
+ * providing `include` collapses the chain to glob immediately. Combining
52
+ * `include` with `discovery: 'exports'` throws.
53
+ *
54
+ * When omitted, the glob fallback derives an include pattern from
55
+ * `sourceOptions.sourcePaths` via `deriveIncludePatterns`, so custom
56
+ * `sourcePaths` (e.g., `['packages/foo']`) discover files instead of
57
+ * silently defaulting to `src/lib`.
58
+ */
59
+ include?: Array<string>;
60
+ /**
61
+ * Discovery strategy.
62
+ *
63
+ * @default 'auto'
64
+ * @see {@link Discovery} for semantics of each variant
65
+ */
66
+ discovery?: Discovery;
67
+ /**
68
+ * Dist directory name relative to `projectRoot`, used for exports-based discovery.
69
+ *
70
+ * Maps dist paths from package.json exports back to source paths.
71
+ *
72
+ * @default 'dist'
73
+ */
74
+ distDir?: string;
75
+ /** Optional logger for status messages. */
76
+ log?: AnalysisLog;
77
+ }
78
+ /** Result of `discoverSourceFiles`. */
79
+ export interface DiscoverSourceFilesResult {
80
+ /** Discovered source files with content already loaded. */
81
+ files: Array<SourceFileInfo>;
82
+ /** Diagnostics collected during discovery (e.g., malformed package.json exports). */
83
+ diagnostics: Array<Diagnostic>;
84
+ }
85
+ /**
86
+ * Discover source files from a project root.
87
+ *
88
+ * Used internally by `analyzeFromFiles` for the discovery step. Standalone
89
+ * consumers can call it directly when they want the discovered file list
90
+ * without running full analysis.
91
+ *
92
+ * Strategy is selected by `discovery`:
93
+ * - `'auto'` (default) — try `exports` first, fall back to glob.
94
+ * - `'exports'` — `exports` only; **throws** if `exports` is missing or
95
+ * resolves to no source files. Combining with `include` is a configuration
96
+ * error and also throws.
97
+ * - `'glob'` — glob only; `include` parameterizes the search.
98
+ *
99
+ * Exclusion globs come from `sourceOptions.exclude` (the single source of
100
+ * truth, also applied at analysis time by `isSource()`).
101
+ *
102
+ * @param options - discovery configuration
103
+ * @returns discovered files (content loaded) and any diagnostics from the exports step
104
+ * @throws Error in strict `'exports'` mode when `exports` is missing or
105
+ * resolves to no source files, or when `include` is combined with
106
+ * `discovery: 'exports'`.
107
+ *
108
+ * @example
109
+ * ```ts
110
+ * const sourceOptions = createSourceOptions(process.cwd());
111
+ * const {files, diagnostics} = await discoverSourceFiles({sourceOptions});
112
+ * ```
113
+ */
114
+ export declare const discoverSourceFiles: (options: DiscoverSourceFilesOptions) => Promise<DiscoverSourceFilesResult>;
115
+ //# sourceMappingURL=discovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discovery.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/discovery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,aAAa,CAAC;AAChD,OAAO,EAAC,KAAK,mBAAmB,EAAgB,MAAM,oBAAoB,CAAC;AAC3E,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AACjD,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,UAAU,CAAC;AAI1C;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC;AAEpD,yCAAyC;AACzC,MAAM,WAAW,0BAA0B;IAC1C;;;;;;;;;OASG;IACH,aAAa,EAAE,mBAAmB,CAAC;IAEnC;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAExB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;IAEtB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,2CAA2C;IAC3C,GAAG,CAAC,EAAE,WAAW,CAAC;CAClB;AAED,uCAAuC;AACvC,MAAM,WAAW,yBAAyB;IACzC,2DAA2D;IAC3D,KAAK,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC;IAC7B,qFAAqF;IACrF,WAAW,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;CAC/B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,mBAAmB,GAC/B,SAAS,0BAA0B,KACjC,OAAO,CAAC,yBAAyB,CAgGnC,CAAC"}
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Source file discovery — exports-first with glob fallback.
3
+ *
4
+ * Used by `analyzeFromFiles` to locate source files from a project root.
5
+ * Build tool integrations using `analyze` should construct `SourceFileInfo[]`
6
+ * from their own module graph instead.
7
+ *
8
+ * @see `exports.ts` for `discoverFromExports` (package.json exports → source files)
9
+ * @see `files.ts` for `globFiles` (glob-based discovery)
10
+ *
11
+ * @module
12
+ */
13
+ import { getSourceRoot } from './source-config.js';
14
+ import { globFiles, deriveIncludePatterns } from './files.js';
15
+ import { discoverFromExports } from './exports.js';
16
+ /**
17
+ * Discover source files from a project root.
18
+ *
19
+ * Used internally by `analyzeFromFiles` for the discovery step. Standalone
20
+ * consumers can call it directly when they want the discovered file list
21
+ * without running full analysis.
22
+ *
23
+ * Strategy is selected by `discovery`:
24
+ * - `'auto'` (default) — try `exports` first, fall back to glob.
25
+ * - `'exports'` — `exports` only; **throws** if `exports` is missing or
26
+ * resolves to no source files. Combining with `include` is a configuration
27
+ * error and also throws.
28
+ * - `'glob'` — glob only; `include` parameterizes the search.
29
+ *
30
+ * Exclusion globs come from `sourceOptions.exclude` (the single source of
31
+ * truth, also applied at analysis time by `isSource()`).
32
+ *
33
+ * @param options - discovery configuration
34
+ * @returns discovered files (content loaded) and any diagnostics from the exports step
35
+ * @throws Error in strict `'exports'` mode when `exports` is missing or
36
+ * resolves to no source files, or when `include` is combined with
37
+ * `discovery: 'exports'`.
38
+ *
39
+ * @example
40
+ * ```ts
41
+ * const sourceOptions = createSourceOptions(process.cwd());
42
+ * const {files, diagnostics} = await discoverSourceFiles({sourceOptions});
43
+ * ```
44
+ */
45
+ export const discoverSourceFiles = async (options) => {
46
+ const { sourceOptions, include, discovery = 'auto', distDir, log } = options;
47
+ const { projectRoot, exclude } = sourceOptions;
48
+ // Reject contradictory configurations early. `include` parameterizes glob,
49
+ // so combining it with strict `'exports'` is a user error rather than a
50
+ // silent override.
51
+ if (discovery === 'exports' && include) {
52
+ throw new Error("discovery: 'exports' is incompatible with `include` — `include` is a glob filter. " +
53
+ "Use discovery: 'glob' (with include) or remove include for strict exports mode.");
54
+ }
55
+ // `include` collapses 'auto' to glob immediately — exports-discovery has
56
+ // no concept of include patterns, so honoring `include` under 'auto' would
57
+ // silently drop the user's filter on packages that have an `exports` field.
58
+ const effectiveStrategy = discovery === 'auto' && include ? 'glob' : discovery;
59
+ let files = null;
60
+ let diagnostics = [];
61
+ if (effectiveStrategy === 'exports' || effectiveStrategy === 'auto') {
62
+ const sourceDir = getSourceRoot(sourceOptions);
63
+ // Exports discovery is single-`sourceDir`-only: it maps every dist path
64
+ // through one source-dir prefix. When `sourcePaths.length > 1` and the
65
+ // auto-derived (or explicit) `sourceRoot` is `''`, every dist entry
66
+ // would resolve to project-root-relative paths instead of the user's
67
+ // actual subdirs — exports discovery cannot represent the layout.
68
+ // Short-circuit instead of letting it silently produce zero files
69
+ // (under 'auto' that falls through harmlessly to glob, but under
70
+ // 'exports' it would throw the misleading generic "resolved to no
71
+ // source files" error).
72
+ if (sourceOptions.sourcePaths.length > 1 && sourceDir === '') {
73
+ if (effectiveStrategy === 'exports') {
74
+ throw new Error("discovery: 'exports' failed — source paths share no common prefix " +
75
+ `(sourcePaths=${JSON.stringify(sourceOptions.sourcePaths)}, sourceRoot=''), ` +
76
+ 'so exports discovery cannot map dist paths to source files. ' +
77
+ "Use discovery: 'auto' (default) or 'glob' for this layout, or " +
78
+ 'restructure to a single source root.');
79
+ }
80
+ log?.info('Source paths share no common prefix — exports discovery cannot represent this layout; falling back to glob patterns');
81
+ }
82
+ else {
83
+ const exportsResult = await discoverFromExports({
84
+ projectRoot,
85
+ exclude,
86
+ sourceDir,
87
+ distDir,
88
+ });
89
+ const { files: exportsFiles, diagnostics: exportsDiagnostics } = exportsResult;
90
+ diagnostics = exportsDiagnostics;
91
+ if (exportsFiles && exportsFiles.length > 0) {
92
+ files = exportsFiles;
93
+ log?.info(`Discovered ${files.length} source files from package.json exports`);
94
+ }
95
+ else if (effectiveStrategy === 'exports') {
96
+ // Strict mode: no fallback. Throw with a message that names the
97
+ // failure mode so users can fix the package.json or relax the strictness.
98
+ const reason = exportsFiles === null
99
+ ? 'no `exports` field found in package.json'
100
+ : '`exports` field present but resolved to no source files';
101
+ throw new Error(`discovery: 'exports' failed — ${reason}. ` +
102
+ `Use discovery: 'auto' (default) to fall back to glob, or fix the package.json exports mapping.`);
103
+ }
104
+ else if (exportsFiles === null) {
105
+ log?.info('No package.json exports found, falling back to glob patterns');
106
+ }
107
+ else {
108
+ log?.warn('Package.json exports found but resolved no source files — falling back to glob patterns');
109
+ }
110
+ }
111
+ }
112
+ // Fall back to glob-based discovery (auto fallback or explicit glob mode).
113
+ // Derive include from `sourceOptions.sourcePaths` when none is supplied so
114
+ // custom `sourcePaths` (e.g. `['packages/foo']`) discover files instead of
115
+ // silently defaulting to `src/lib`. Mirrors the CLI's prior local
116
+ // derivation — single source of truth lives here now.
117
+ if (!files) {
118
+ files = await globFiles({
119
+ projectRoot,
120
+ include: include ?? deriveIncludePatterns(sourceOptions.sourcePaths),
121
+ exclude,
122
+ });
123
+ log?.info(`Discovered ${files.length} source files via glob`);
124
+ }
125
+ return { files, diagnostics };
126
+ };