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.
- package/LICENSE +21 -0
- package/README.md +349 -0
- package/dist/analyze-core.d.ts +1386 -0
- package/dist/analyze-core.d.ts.map +1 -0
- package/dist/analyze-core.js +324 -0
- package/dist/analyze.d.ts +137 -0
- package/dist/analyze.d.ts.map +1 -0
- package/dist/analyze.js +148 -0
- package/dist/cli.d.ts +102 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +206 -0
- package/dist/concurrency.d.ts +38 -0
- package/dist/concurrency.d.ts.map +1 -0
- package/dist/concurrency.js +57 -0
- package/dist/declaration-build.d.ts +164 -0
- package/dist/declaration-build.d.ts.map +1 -0
- package/dist/declaration-build.js +15 -0
- package/dist/declaration-helpers.d.ts +169 -0
- package/dist/declaration-helpers.d.ts.map +1 -0
- package/dist/declaration-helpers.js +239 -0
- package/dist/dep-resolver.d.ts +166 -0
- package/dist/dep-resolver.d.ts.map +1 -0
- package/dist/dep-resolver.js +144 -0
- package/dist/diagnostics.d.ts +650 -0
- package/dist/diagnostics.d.ts.map +1 -0
- package/dist/diagnostics.js +377 -0
- package/dist/discovery.d.ts +115 -0
- package/dist/discovery.d.ts.map +1 -0
- package/dist/discovery.js +126 -0
- package/dist/exports.d.ts +105 -0
- package/dist/exports.d.ts.map +1 -0
- package/dist/exports.js +274 -0
- package/dist/files.d.ts +77 -0
- package/dist/files.d.ts.map +1 -0
- package/dist/files.js +95 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/log.d.ts +32 -0
- package/dist/log.d.ts.map +1 -0
- package/dist/log.js +11 -0
- package/dist/logo.d.ts +8 -0
- package/dist/logo.d.ts.map +1 -0
- package/dist/logo.js +9 -0
- package/dist/main.d.ts +11 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +18 -0
- package/dist/paths.d.ts +35 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +34 -0
- package/dist/postprocess.d.ts +145 -0
- package/dist/postprocess.d.ts.map +1 -0
- package/dist/postprocess.js +305 -0
- package/dist/session.d.ts +248 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +496 -0
- package/dist/source-config.d.ts +330 -0
- package/dist/source-config.d.ts.map +1 -0
- package/dist/source-config.js +356 -0
- package/dist/source.d.ts +126 -0
- package/dist/source.d.ts.map +1 -0
- package/dist/source.js +68 -0
- package/dist/svelte.d.ts +179 -0
- package/dist/svelte.d.ts.map +1 -0
- package/dist/svelte.js +898 -0
- package/dist/tsdoc.d.ts +145 -0
- package/dist/tsdoc.d.ts.map +1 -0
- package/dist/tsdoc.js +262 -0
- package/dist/types.d.ts +3381 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +588 -0
- package/dist/typescript-exports.d.ts +91 -0
- package/dist/typescript-exports.d.ts.map +1 -0
- package/dist/typescript-exports.js +656 -0
- package/dist/typescript-extract-class.d.ts +29 -0
- package/dist/typescript-extract-class.d.ts.map +1 -0
- package/dist/typescript-extract-class.js +251 -0
- package/dist/typescript-extract-function.d.ts +47 -0
- package/dist/typescript-extract-function.d.ts.map +1 -0
- package/dist/typescript-extract-function.js +108 -0
- package/dist/typescript-extract-shared.d.ts +166 -0
- package/dist/typescript-extract-shared.d.ts.map +1 -0
- package/dist/typescript-extract-shared.js +528 -0
- package/dist/typescript-extract-type-properties.d.ts +34 -0
- package/dist/typescript-extract-type-properties.d.ts.map +1 -0
- package/dist/typescript-extract-type-properties.js +241 -0
- package/dist/typescript-extract-type.d.ts +48 -0
- package/dist/typescript-extract-type.d.ts.map +1 -0
- package/dist/typescript-extract-type.js +238 -0
- package/dist/typescript-program.d.ts +211 -0
- package/dist/typescript-program.d.ts.map +1 -0
- package/dist/typescript-program.js +290 -0
- package/dist/vite.d.ts +116 -0
- package/dist/vite.d.ts.map +1 -0
- package/dist/vite.js +418 -0
- package/package.json +131 -0
- 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
|
+
};
|