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 @@
|
|
|
1
|
+
{"version":3,"file":"analyze-core.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/analyze-core.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure two-phase analysis orchestrator.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from `analyze.ts` to break what would otherwise be a circular
|
|
5
|
+
* import: `session.ts → analyze.ts (for the orchestrator) → session.ts (for
|
|
6
|
+
* the wrappers)`. With the orchestrator here, both `session.ts` and
|
|
7
|
+
* `analyze.ts` import downward into this module without depending on each
|
|
8
|
+
* other.
|
|
9
|
+
*
|
|
10
|
+
* Public surface:
|
|
11
|
+
*
|
|
12
|
+
* - `analyzeCore` — runs phase 1 module dispatch + phase 2 re-export merge,
|
|
13
|
+
* returns `AnalyzeResultJson`. Caller supplies pre-prepared inputs (program,
|
|
14
|
+
* svelte virtuals, transform-failed IDs).
|
|
15
|
+
* - `analyzeModule` — the per-module dispatcher (TS / Svelte / CSS / JSON).
|
|
16
|
+
* `@internal`; exposed for tests and power users via the subpath.
|
|
17
|
+
* - `AnalyzeResultJson` / `OnDuplicates` / `OnDuplicatesCallback` — shared types
|
|
18
|
+
* surfaced through the main barrel directly from this module.
|
|
19
|
+
* - `throwOnDuplicates` — convenience callback paired with `OnDuplicates`.
|
|
20
|
+
* - `normalizeDiagnosticPaths` — boundary helper for build-tool integrations
|
|
21
|
+
* that bypass the session and collect their own diagnostics.
|
|
22
|
+
*
|
|
23
|
+
* @internal — module split is implementation detail; consumers go through
|
|
24
|
+
* `analyze.ts` / `session.ts` for the stable surface.
|
|
25
|
+
*
|
|
26
|
+
* @module
|
|
27
|
+
*/
|
|
28
|
+
import ts from 'typescript';
|
|
29
|
+
import { z } from 'zod';
|
|
30
|
+
import { ModuleJson } from './types.js';
|
|
31
|
+
import { Diagnostic } from './diagnostics.js';
|
|
32
|
+
import { analyzeTypescriptModule } from './typescript-exports.js';
|
|
33
|
+
import { analyzeSvelteModule } from './svelte.js';
|
|
34
|
+
import { stripVirtualSuffix, getComponentName } from './source.js';
|
|
35
|
+
import { extractPath, extractDependencies } from './source-config.js';
|
|
36
|
+
import { toPosixPath } from './paths.js';
|
|
37
|
+
import { sortModules, findDuplicates, mergeReExports, resolveComponentAliases, } from './postprocess.js';
|
|
38
|
+
/**
|
|
39
|
+
* Convenience `OnDuplicatesCallback` that throws on any duplicate.
|
|
40
|
+
*
|
|
41
|
+
* @throws Error listing every duplicate name and module location
|
|
42
|
+
*/
|
|
43
|
+
export const throwOnDuplicates = (duplicates) => {
|
|
44
|
+
if (duplicates.size === 0)
|
|
45
|
+
return;
|
|
46
|
+
throw new Error(formatDuplicates(duplicates));
|
|
47
|
+
};
|
|
48
|
+
const formatDuplicates = (duplicates) => {
|
|
49
|
+
const details = Array.from(duplicates)
|
|
50
|
+
.map(([name, occurrences]) => {
|
|
51
|
+
const locations = occurrences
|
|
52
|
+
.map(({ declaration, module }) => {
|
|
53
|
+
const lineInfo = declaration.sourceLine !== undefined ? `:${declaration.sourceLine}` : '';
|
|
54
|
+
return ` - ${module}${lineInfo} (${declaration.kind})`;
|
|
55
|
+
})
|
|
56
|
+
.join('\n');
|
|
57
|
+
return ` "${name}" found in:\n${locations}`;
|
|
58
|
+
})
|
|
59
|
+
.join('\n');
|
|
60
|
+
return (`Found ${duplicates.size} duplicate declaration name${duplicates.size === 1 ? '' : 's'} across modules. ` +
|
|
61
|
+
'The flat namespace requires unique names. To resolve: ' +
|
|
62
|
+
'(1) rename one of the conflicting declarations, or ' +
|
|
63
|
+
'(2) add /** @nodocs */ to exclude from documentation.\n' +
|
|
64
|
+
details);
|
|
65
|
+
};
|
|
66
|
+
const emitDuplicateDiagnostics = (diagnostics, duplicates) => {
|
|
67
|
+
for (const [name, occurrences] of duplicates) {
|
|
68
|
+
const first = occurrences[0];
|
|
69
|
+
const modules = occurrences.map((o) => o.module);
|
|
70
|
+
diagnostics.push({
|
|
71
|
+
kind: 'duplicate_declaration',
|
|
72
|
+
file: first.module,
|
|
73
|
+
line: first.declaration.sourceLine,
|
|
74
|
+
message: `Duplicate declaration "${name}" defined in: ${modules.join(', ')}`,
|
|
75
|
+
severity: 'warning',
|
|
76
|
+
declarationName: name,
|
|
77
|
+
modules,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
const dispatchOnDuplicates = (mode, duplicates, log) => {
|
|
82
|
+
if (duplicates.size === 0)
|
|
83
|
+
return;
|
|
84
|
+
if (mode === 'throw') {
|
|
85
|
+
throw new Error(formatDuplicates(duplicates));
|
|
86
|
+
}
|
|
87
|
+
const errorLog = log ?? { error: (msg) => console.error(msg) };
|
|
88
|
+
if (mode === 'warn') {
|
|
89
|
+
errorLog.error(formatDuplicates(duplicates));
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
mode(duplicates, errorLog);
|
|
93
|
+
};
|
|
94
|
+
// ── Result types ─────────────────────────────────────────────────────────────
|
|
95
|
+
/**
|
|
96
|
+
* Result of `analyze`, `analyzeFromFiles`, and `AnalysisSession.query`.
|
|
97
|
+
*
|
|
98
|
+
* Modules sorted alphabetically by `path`. Diagnostics are query-time
|
|
99
|
+
* (analysis-pass) diagnostics only when produced by `session.query`; one-shot
|
|
100
|
+
* wrappers concatenate ingest + query diagnostics into this same array.
|
|
101
|
+
*
|
|
102
|
+
* ## Schema-validated round-trip
|
|
103
|
+
*
|
|
104
|
+
* The envelope is a Zod schema (`AnalyzeResultJson`) — both fields default to
|
|
105
|
+
* `[]`, so `JSON.stringify(result, compactReplacer)` strips empty arrays on
|
|
106
|
+
* the wire and `AnalyzeResultJson.parse(JSON.parse(json))` restores them.
|
|
107
|
+
* Consumers programmatically ingesting analysis JSON should parse through the
|
|
108
|
+
* schema to get defaults restored; raw-JSON consumers (e.g., `jq`) treat
|
|
109
|
+
* missing keys as null-equivalent (`jq '.diagnostics | length'` returns `0`
|
|
110
|
+
* on `{}`) and don't need the parse step.
|
|
111
|
+
*
|
|
112
|
+
* Construction sites (one-shot wrappers, `session.query`) hand back hand-built
|
|
113
|
+
* objects without re-running `.parse()` — the inner `modules` and
|
|
114
|
+
* `diagnostics` arrays are already Zod-validated upstream, and the envelope
|
|
115
|
+
* schema is the type contract, not a validation gate.
|
|
116
|
+
*/
|
|
117
|
+
export const AnalyzeResultJson = z.strictObject({
|
|
118
|
+
modules: z.array(ModuleJson).default([]),
|
|
119
|
+
diagnostics: z.array(Diagnostic).default([]),
|
|
120
|
+
});
|
|
121
|
+
const toModuleAnalyzeResult = (raw) => {
|
|
122
|
+
const filtered = raw.declarations.filter((d) => !d.nodocs).map((d) => d.declaration);
|
|
123
|
+
const module = ModuleJson.parse({
|
|
124
|
+
path: raw.path,
|
|
125
|
+
declarations: filtered,
|
|
126
|
+
dependencies: raw.dependencies,
|
|
127
|
+
dependents: raw.dependents,
|
|
128
|
+
starExports: raw.starExports,
|
|
129
|
+
...(raw.moduleComment ? { moduleComment: raw.moduleComment } : {}),
|
|
130
|
+
});
|
|
131
|
+
return { module, reExports: raw.reExports };
|
|
132
|
+
};
|
|
133
|
+
// ── Module dispatch ──────────────────────────────────────────────────────────
|
|
134
|
+
/**
|
|
135
|
+
* Analyze a single non-Svelte source file and extract module metadata.
|
|
136
|
+
*
|
|
137
|
+
* @internal Single-module dispatcher used internally by the two-phase
|
|
138
|
+
* orchestrator. Importable from `svelte-docinfo/analyze-core.js` for tests
|
|
139
|
+
* and power users — not part of the stable barrel API and not guaranteed
|
|
140
|
+
* across minor versions.
|
|
141
|
+
*
|
|
142
|
+
* Dispatches on file type:
|
|
143
|
+
* - TypeScript/JS → `analyzeTypescriptModule`
|
|
144
|
+
* - CSS/JSON → minimal module (no declarations, dependency tracking only)
|
|
145
|
+
* - Svelte → `module_skipped: requires_program` (callers using session API
|
|
146
|
+
* should not reach this path; the session pre-handles Svelte)
|
|
147
|
+
*
|
|
148
|
+
* Returns `undefined` when the file is skipped; the diagnostic is added to
|
|
149
|
+
* `diagnostics` so the caller can keep iterating.
|
|
150
|
+
*/
|
|
151
|
+
export const analyzeModule = (sourceFile, program, options, diagnostics, log) => {
|
|
152
|
+
const checker = program.getTypeChecker();
|
|
153
|
+
const modulePath = extractPath(sourceFile.id, options);
|
|
154
|
+
const analyzerType = options.getAnalyzerType(sourceFile.id);
|
|
155
|
+
let raw;
|
|
156
|
+
if (analyzerType === 'svelte') {
|
|
157
|
+
diagnostics.push({
|
|
158
|
+
kind: 'module_skipped',
|
|
159
|
+
file: modulePath,
|
|
160
|
+
message: 'Svelte files require program integration. Use createAnalysisSession or analyze()/analyzeFromFiles() instead.',
|
|
161
|
+
severity: 'warning',
|
|
162
|
+
reason: 'requires_program',
|
|
163
|
+
});
|
|
164
|
+
log?.warn(`Svelte file skipped in analyzeModule: ${sourceFile.id}`);
|
|
165
|
+
return undefined;
|
|
166
|
+
}
|
|
167
|
+
else if (analyzerType === 'typescript') {
|
|
168
|
+
const tsSourceFile = program.getSourceFile(sourceFile.id);
|
|
169
|
+
if (!tsSourceFile) {
|
|
170
|
+
diagnostics.push({
|
|
171
|
+
kind: 'module_skipped',
|
|
172
|
+
file: modulePath,
|
|
173
|
+
message: `Could not get source file from program: ${sourceFile.id}`,
|
|
174
|
+
severity: 'warning',
|
|
175
|
+
reason: 'not_in_program',
|
|
176
|
+
});
|
|
177
|
+
log?.warn(`Could not get source file from program: ${sourceFile.id}`);
|
|
178
|
+
return undefined;
|
|
179
|
+
}
|
|
180
|
+
raw = analyzeTypescriptModule(sourceFile, tsSourceFile, modulePath, checker, options, diagnostics);
|
|
181
|
+
}
|
|
182
|
+
else if (analyzerType === 'css' || analyzerType === 'json') {
|
|
183
|
+
const { dependencies, dependents } = extractDependencies(sourceFile, options);
|
|
184
|
+
raw = {
|
|
185
|
+
path: modulePath,
|
|
186
|
+
declarations: [],
|
|
187
|
+
dependencies,
|
|
188
|
+
dependents,
|
|
189
|
+
starExports: [],
|
|
190
|
+
reExports: [],
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
diagnostics.push({
|
|
195
|
+
kind: 'module_skipped',
|
|
196
|
+
file: modulePath,
|
|
197
|
+
message: `No analyzer for file type: ${sourceFile.id}`,
|
|
198
|
+
severity: 'warning',
|
|
199
|
+
reason: 'no_analyzer',
|
|
200
|
+
});
|
|
201
|
+
log?.warn(`No analyzer for file: ${sourceFile.id}`);
|
|
202
|
+
return undefined;
|
|
203
|
+
}
|
|
204
|
+
return toModuleAnalyzeResult(raw);
|
|
205
|
+
};
|
|
206
|
+
/**
|
|
207
|
+
* Run the two-phase analysis loop on pre-prepared inputs.
|
|
208
|
+
*
|
|
209
|
+
* @internal Shared two-phase orchestrator used internally by `session.query`
|
|
210
|
+
* and the one-shot wrappers. Importable from `svelte-docinfo/analyze-core.js`
|
|
211
|
+
* for tests and power users — not part of the stable barrel API and not
|
|
212
|
+
* guaranteed across minor versions.
|
|
213
|
+
*
|
|
214
|
+
* Phase 1: per-file dispatch (TS / Svelte / CSS / JSON / placeholder for
|
|
215
|
+
* transform-failed Svelte). Phase 2: re-export merge, component-alias fill,
|
|
216
|
+
* sort, duplicate detection. Diagnostic paths are normalized to
|
|
217
|
+
* project-root-relative form before return.
|
|
218
|
+
*
|
|
219
|
+
* Dependents are read from `sourceFile.dependents` (caller-supplied via
|
|
220
|
+
* `extractDependencies`); `analyzeCore` does not compute them. `session.query`
|
|
221
|
+
* runs `computeDependents` on the owned set before invoking this.
|
|
222
|
+
*/
|
|
223
|
+
export const analyzeCore = (inputs) => {
|
|
224
|
+
const { sourceFiles, sourceOptions, program, svelteVirtualFiles, transformFailedIds, onDuplicates, log, } = inputs;
|
|
225
|
+
const checker = program.getTypeChecker();
|
|
226
|
+
const diagnostics = [];
|
|
227
|
+
const modules = [];
|
|
228
|
+
const collectedReExports = [];
|
|
229
|
+
// Phase 1: analyze every module, collect re-exports
|
|
230
|
+
for (const sourceFile of sourceFiles) {
|
|
231
|
+
// Failed-transform Svelte file: synthesize placeholder ModuleJson so
|
|
232
|
+
// the modules array reflects the full owned set. The originating
|
|
233
|
+
// transform_failed ingest diagnostic carries the cause; this slot
|
|
234
|
+
// is purely structural.
|
|
235
|
+
if (transformFailedIds?.has(sourceFile.id)) {
|
|
236
|
+
const modulePath = extractPath(sourceFile.id, sourceOptions);
|
|
237
|
+
const componentName = getComponentName(modulePath);
|
|
238
|
+
const { dependencies, dependents } = extractDependencies(sourceFile, sourceOptions);
|
|
239
|
+
modules.push(ModuleJson.parse({
|
|
240
|
+
path: modulePath,
|
|
241
|
+
declarations: [],
|
|
242
|
+
dependencies,
|
|
243
|
+
dependents,
|
|
244
|
+
starExports: [],
|
|
245
|
+
partial: true,
|
|
246
|
+
}));
|
|
247
|
+
log?.warn(`Svelte component ${componentName} marked partial (transform failed at ingest)`);
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
let result;
|
|
251
|
+
const virtualFile = svelteVirtualFiles.get(sourceFile.id);
|
|
252
|
+
if (virtualFile) {
|
|
253
|
+
const modulePath = extractPath(sourceFile.id, sourceOptions);
|
|
254
|
+
const raw = analyzeSvelteModule(sourceFile, modulePath, checker, sourceOptions, diagnostics, program, virtualFile);
|
|
255
|
+
if (raw) {
|
|
256
|
+
result = toModuleAnalyzeResult(raw);
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
log?.error(`Svelte module analysis failed: ${sourceFile.id}`);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
else {
|
|
263
|
+
result = analyzeModule(sourceFile, program, sourceOptions, diagnostics, log);
|
|
264
|
+
}
|
|
265
|
+
if (!result)
|
|
266
|
+
continue;
|
|
267
|
+
modules.push(result.module);
|
|
268
|
+
for (const reExport of result.reExports) {
|
|
269
|
+
collectedReExports.push({ reExportingModule: result.module.path, reExport });
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
// Phase 2a: build alsoExportedFrom arrays
|
|
273
|
+
mergeReExports(modules, collectedReExports);
|
|
274
|
+
// Phase 2b: fill component-only fields on renamed component aliases —
|
|
275
|
+
// canonical components are only fully populated after phase 1 finishes.
|
|
276
|
+
resolveComponentAliases(modules);
|
|
277
|
+
const sortedModules = sortModules(modules);
|
|
278
|
+
// Always run duplicate detection so a `duplicate_declaration` diagnostic
|
|
279
|
+
// reaches consumers regardless of `onDuplicates`. The `onDuplicates`
|
|
280
|
+
// callback/shortcut still fires for callers that want fail-fast or custom
|
|
281
|
+
// handling — diagnostics is the data, `onDuplicates` is the action.
|
|
282
|
+
const duplicates = findDuplicates(sortedModules);
|
|
283
|
+
emitDuplicateDiagnostics(diagnostics, duplicates);
|
|
284
|
+
if (onDuplicates) {
|
|
285
|
+
dispatchOnDuplicates(onDuplicates, duplicates, log);
|
|
286
|
+
}
|
|
287
|
+
normalizeDiagnosticPaths(diagnostics, sourceOptions.projectRoot);
|
|
288
|
+
return {
|
|
289
|
+
modules: sortedModules,
|
|
290
|
+
diagnostics,
|
|
291
|
+
};
|
|
292
|
+
};
|
|
293
|
+
/**
|
|
294
|
+
* Normalize `Diagnostic.file` to project-root-relative form, in place.
|
|
295
|
+
*
|
|
296
|
+
* Producers inside the analysis pipeline can write absolute paths or virtual
|
|
297
|
+
* paths (svelte2tsx output like `Foo.svelte.__svelte2tsx__.ts`). This pass
|
|
298
|
+
* collapses both to the public contract: a path relative to `projectRoot`
|
|
299
|
+
* with no leading slash and no `./` prefix.
|
|
300
|
+
*
|
|
301
|
+
* Exposed for build-tool integrations that bypass the session and collect
|
|
302
|
+
* their own discovery/dep diagnostics — they need the same normalization to
|
|
303
|
+
* match the public contract.
|
|
304
|
+
*
|
|
305
|
+
* @mutates diagnostics — rewrites each diagnostic's `file` field
|
|
306
|
+
*/
|
|
307
|
+
export const normalizeDiagnosticPaths = (diagnostics, projectRoot) => {
|
|
308
|
+
const prefix = projectRoot.endsWith('/') ? projectRoot : projectRoot + '/';
|
|
309
|
+
for (const d of diagnostics) {
|
|
310
|
+
// Posixify so producers that emitted native-separator paths (e.g., a
|
|
311
|
+
// custom discovery layer building `path.relative` results on Windows)
|
|
312
|
+
// match the POSIX prefix derived from the normalized projectRoot.
|
|
313
|
+
let file = toPosixPath(stripVirtualSuffix(d.file));
|
|
314
|
+
if (file.startsWith(prefix)) {
|
|
315
|
+
file = file.slice(prefix.length);
|
|
316
|
+
}
|
|
317
|
+
else if (file.startsWith('/')) {
|
|
318
|
+
// Absolute path outside projectRoot — drop leading slash so display
|
|
319
|
+
// stays consistent.
|
|
320
|
+
file = file.slice(1);
|
|
321
|
+
}
|
|
322
|
+
d.file = file;
|
|
323
|
+
}
|
|
324
|
+
};
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One-shot analysis wrappers — `analyze` and `analyzeFromFiles`.
|
|
3
|
+
*
|
|
4
|
+
* Both wrap a single-use `AnalysisSession`: `createSession → setFiles → query
|
|
5
|
+
* → dispose`. Incremental consumers (Vite plugin, LSP) should use
|
|
6
|
+
* `createAnalysisSession` directly so parsed ASTs and svelte2tsx output
|
|
7
|
+
* survive between calls.
|
|
8
|
+
*
|
|
9
|
+
* - `analyzeFromFiles` — high-level: file discovery from disk + dependency
|
|
10
|
+
* resolution + analysis. Recommended for CLI / build-time generation.
|
|
11
|
+
* - `analyze` — mid-level: caller supplies `SourceFileInfo[]`. For build-tool
|
|
12
|
+
* integrations that own discovery but don't run incrementally.
|
|
13
|
+
*
|
|
14
|
+
* @see `session.ts` for the persistent session API
|
|
15
|
+
* @see `analyze-core.ts` for the two-phase orchestrator
|
|
16
|
+
*
|
|
17
|
+
* @module
|
|
18
|
+
*/
|
|
19
|
+
import type { AnalysisLog } from './log.js';
|
|
20
|
+
import { type ModuleSourceOptions, type SourceOptionsDefaults } from './source-config.js';
|
|
21
|
+
import { type Discovery } from './discovery.js';
|
|
22
|
+
import { type ResolveImport } from './dep-resolver.js';
|
|
23
|
+
import { type AnalyzeResultJson, type OnDuplicates } from './analyze-core.js';
|
|
24
|
+
import type { SourceFileInfo } from './source.js';
|
|
25
|
+
/**
|
|
26
|
+
* Options for `analyze`.
|
|
27
|
+
*
|
|
28
|
+
* Requires pre-loaded `SourceFileInfo` arrays — use `analyzeFromFiles` for
|
|
29
|
+
* automatic file discovery and loading from disk, or `createAnalysisSession`
|
|
30
|
+
* for incremental use.
|
|
31
|
+
*/
|
|
32
|
+
export interface AnalyzeOptions {
|
|
33
|
+
/** Source files to analyze (must have content loaded). */
|
|
34
|
+
sourceFiles: ReadonlyArray<SourceFileInfo>;
|
|
35
|
+
/** Module source options for path extraction and source filtering. */
|
|
36
|
+
sourceOptions: ModuleSourceOptions;
|
|
37
|
+
/** Behavior when duplicate declaration names are found across modules. */
|
|
38
|
+
onDuplicates?: OnDuplicates;
|
|
39
|
+
/** Optional logger for status and diagnostic messages. */
|
|
40
|
+
log?: AnalysisLog;
|
|
41
|
+
/**
|
|
42
|
+
* Optional custom import resolver for the session default — a bare
|
|
43
|
+
* `ResolveImportFn` or a token-paired `ImportResolver` (see `ResolveImport`).
|
|
44
|
+
* For one-shot `analyze()` the session is single-use, so a bare function is
|
|
45
|
+
* the natural form; pass an `ImportResolver` with a stable `identity` only if
|
|
46
|
+
* you have a reason to control the cache scope.
|
|
47
|
+
*/
|
|
48
|
+
resolveImport?: ResolveImport;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Analyze library source files and extract metadata (one-shot).
|
|
52
|
+
*
|
|
53
|
+
* Wraps a single-use `AnalysisSession`. For repeated analyses of the same
|
|
54
|
+
* source set (e.g., a Vite plugin reacting to file edits), use
|
|
55
|
+
* `createAnalysisSession` directly.
|
|
56
|
+
*
|
|
57
|
+
* @returns analyzed modules (sorted alphabetically) + concatenated ingest +
|
|
58
|
+
* query diagnostics
|
|
59
|
+
* @throws Error if `sourceOptions` validation fails or `tsconfig.json` is
|
|
60
|
+
* missing at `projectRoot`. Also propagates `onDuplicates: 'throw'` errors.
|
|
61
|
+
*/
|
|
62
|
+
export declare const analyze: (options: AnalyzeOptions) => Promise<AnalyzeResultJson>;
|
|
63
|
+
/**
|
|
64
|
+
* Options for `analyzeFromFiles`.
|
|
65
|
+
*
|
|
66
|
+
* @see `AnalyzeOptions` for the build-tool-integration API where you supply
|
|
67
|
+
* `sourceFiles` and a fully-formed `sourceOptions: ModuleSourceOptions` directly.
|
|
68
|
+
*/
|
|
69
|
+
export interface AnalyzeFromFilesOptions {
|
|
70
|
+
/** Absolute path to project root directory. */
|
|
71
|
+
projectRoot: string;
|
|
72
|
+
/** Partial overrides for default source options (SvelteKit `src/lib` layout). */
|
|
73
|
+
sourceOptions?: Partial<SourceOptionsDefaults>;
|
|
74
|
+
/** Behavior when duplicate declaration names are found across modules. */
|
|
75
|
+
onDuplicates?: OnDuplicates;
|
|
76
|
+
/** Optional logger for status and diagnostic messages. */
|
|
77
|
+
log?: AnalysisLog;
|
|
78
|
+
/**
|
|
79
|
+
* Glob patterns to include (relative to `projectRoot`).
|
|
80
|
+
*
|
|
81
|
+
* Filters glob-based discovery. Providing `include` under the default
|
|
82
|
+
* `discovery: 'auto'` collapses the chain to glob immediately; combining
|
|
83
|
+
* with `discovery: 'exports'` throws.
|
|
84
|
+
*
|
|
85
|
+
* When omitted, the glob fallback derives an include from
|
|
86
|
+
* `sourceOptions.sourcePaths` via `deriveIncludePatterns`, so custom
|
|
87
|
+
* `sourcePaths` (e.g., `['packages/foo']`) survive the fallback instead of
|
|
88
|
+
* silently defaulting to `src/lib`.
|
|
89
|
+
*/
|
|
90
|
+
include?: Array<string>;
|
|
91
|
+
/** Glob patterns to exclude — fully replaces `sourceOptions.exclude` (no merge). */
|
|
92
|
+
exclude?: Array<string>;
|
|
93
|
+
/**
|
|
94
|
+
* Whether to resolve import dependencies (default `true`).
|
|
95
|
+
*
|
|
96
|
+
* When `false`, the session uses a no-op resolver that always returns
|
|
97
|
+
* `null`, so `ModuleJson.dependencies` / `dependents` stay empty.
|
|
98
|
+
* `analyzeFromFiles`'s discovery layer does not pre-populate
|
|
99
|
+
* `SourceFileInfo.dependencies`, so the session's pre-resolved fast path
|
|
100
|
+
* isn't reachable through this API — to exercise it, drive `analyze` or
|
|
101
|
+
* `createAnalysisSession` directly with files whose `dependencies` field
|
|
102
|
+
* is already filled in by your build tool.
|
|
103
|
+
*
|
|
104
|
+
* @default `true`
|
|
105
|
+
*/
|
|
106
|
+
resolveDependencies?: boolean;
|
|
107
|
+
/**
|
|
108
|
+
* Optional custom import resolver — a bare `ResolveImportFn` or a
|
|
109
|
+
* token-paired `ImportResolver` (see `ResolveImport`). One-shot use doesn't
|
|
110
|
+
* benefit from a stable cache identity, so the bare function form is the
|
|
111
|
+
* natural choice here; for long-lived consumers (Vite plugin, LSP) construct
|
|
112
|
+
* an `ImportResolver` with a stable `identity` and pass it via
|
|
113
|
+
* `createAnalysisSession` so cache hits survive across calls.
|
|
114
|
+
*
|
|
115
|
+
* Cannot be combined with `resolveDependencies: false` — resolution is then
|
|
116
|
+
* off, so the resolver would never be consulted; passing both throws.
|
|
117
|
+
*/
|
|
118
|
+
resolveImport?: ResolveImport;
|
|
119
|
+
/** Discovery strategy for source files. @default 'auto' */
|
|
120
|
+
discovery?: Discovery;
|
|
121
|
+
/** Dist directory name for exports-based discovery. @default 'dist' */
|
|
122
|
+
distDir?: string;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Analyze a library from files on disk with automatic file discovery.
|
|
126
|
+
*
|
|
127
|
+
* Recommended high-level API for one-shot use (CLI, build-time generation):
|
|
128
|
+
*
|
|
129
|
+
* 1. **Discovery** — `discoverSourceFiles` (exports-first, glob fallback)
|
|
130
|
+
* 2. **Ingest** — push discovered files into a single-use session
|
|
131
|
+
* 3. **Analysis** — `session.query()`
|
|
132
|
+
*
|
|
133
|
+
* @returns analyzed modules + concatenated ingest, discovery, and query diagnostics
|
|
134
|
+
* @throws Error if `sourceOptions` validation fails or `tsconfig.json` is missing
|
|
135
|
+
*/
|
|
136
|
+
export declare const analyzeFromFiles: (options: AnalyzeFromFilesOptions) => Promise<AnalyzeResultJson>;
|
|
137
|
+
//# sourceMappingURL=analyze.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyze.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/analyze.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAGH,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,UAAU,CAAC;AAE1C,OAAO,EAEN,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,EAC1B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAsB,KAAK,SAAS,EAAC,MAAM,gBAAgB,CAAC;AACnE,OAAO,EAAiB,KAAK,aAAa,EAAC,MAAM,mBAAmB,CAAC;AACrE,OAAO,EAEN,KAAK,iBAAiB,EACtB,KAAK,YAAY,EACjB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,aAAa,CAAC;AAIhD;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC9B,0DAA0D;IAC1D,WAAW,EAAE,aAAa,CAAC,cAAc,CAAC,CAAC;IAC3C,sEAAsE;IACtE,aAAa,EAAE,mBAAmB,CAAC;IACnC,0EAA0E;IAC1E,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,0DAA0D;IAC1D,GAAG,CAAC,EAAE,WAAW,CAAC;IAClB;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC;CAC9B;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,OAAO,GAAU,SAAS,cAAc,KAAG,OAAO,CAAC,iBAAiB,CAkBhF,CAAC;AAIF;;;;;GAKG;AACH,MAAM,WAAW,uBAAuB;IACvC,+CAA+C;IAC/C,WAAW,EAAE,MAAM,CAAC;IACpB,iFAAiF;IACjF,aAAa,CAAC,EAAE,OAAO,CAAC,qBAAqB,CAAC,CAAC;IAC/C,0EAA0E;IAC1E,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,0DAA0D;IAC1D,GAAG,CAAC,EAAE,WAAW,CAAC;IAClB;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACxB,oFAAoF;IACpF,OAAO,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACxB;;;;;;;;;;;;OAYG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B;;;;;;;;;;OAUG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,2DAA2D;IAC3D,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,gBAAgB,GAC5B,SAAS,uBAAuB,KAC9B,OAAO,CAAC,iBAAiB,CAqG3B,CAAC"}
|
package/dist/analyze.js
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One-shot analysis wrappers — `analyze` and `analyzeFromFiles`.
|
|
3
|
+
*
|
|
4
|
+
* Both wrap a single-use `AnalysisSession`: `createSession → setFiles → query
|
|
5
|
+
* → dispose`. Incremental consumers (Vite plugin, LSP) should use
|
|
6
|
+
* `createAnalysisSession` directly so parsed ASTs and svelte2tsx output
|
|
7
|
+
* survive between calls.
|
|
8
|
+
*
|
|
9
|
+
* - `analyzeFromFiles` — high-level: file discovery from disk + dependency
|
|
10
|
+
* resolution + analysis. Recommended for CLI / build-time generation.
|
|
11
|
+
* - `analyze` — mid-level: caller supplies `SourceFileInfo[]`. For build-tool
|
|
12
|
+
* integrations that own discovery but don't run incrementally.
|
|
13
|
+
*
|
|
14
|
+
* @see `session.ts` for the persistent session API
|
|
15
|
+
* @see `analyze-core.ts` for the two-phase orchestrator
|
|
16
|
+
*
|
|
17
|
+
* @module
|
|
18
|
+
*/
|
|
19
|
+
import { errorsOf, formatDiagnostic, warningsOf } from './diagnostics.js';
|
|
20
|
+
import { createAnalysisSession } from './session.js';
|
|
21
|
+
import { createSourceOptions, } from './source-config.js';
|
|
22
|
+
import { discoverSourceFiles } from './discovery.js';
|
|
23
|
+
import { noDepsResolver } from './dep-resolver.js';
|
|
24
|
+
import { normalizeDiagnosticPaths, } from './analyze-core.js';
|
|
25
|
+
/**
|
|
26
|
+
* Analyze library source files and extract metadata (one-shot).
|
|
27
|
+
*
|
|
28
|
+
* Wraps a single-use `AnalysisSession`. For repeated analyses of the same
|
|
29
|
+
* source set (e.g., a Vite plugin reacting to file edits), use
|
|
30
|
+
* `createAnalysisSession` directly.
|
|
31
|
+
*
|
|
32
|
+
* @returns analyzed modules (sorted alphabetically) + concatenated ingest +
|
|
33
|
+
* query diagnostics
|
|
34
|
+
* @throws Error if `sourceOptions` validation fails or `tsconfig.json` is
|
|
35
|
+
* missing at `projectRoot`. Also propagates `onDuplicates: 'throw'` errors.
|
|
36
|
+
*/
|
|
37
|
+
export const analyze = async (options) => {
|
|
38
|
+
const session = createAnalysisSession({
|
|
39
|
+
sourceOptions: options.sourceOptions,
|
|
40
|
+
log: options.log,
|
|
41
|
+
resolveImport: options.resolveImport,
|
|
42
|
+
});
|
|
43
|
+
try {
|
|
44
|
+
const ingest = await session.setFiles(options.sourceFiles);
|
|
45
|
+
const query = session.query({ onDuplicates: options.onDuplicates, log: options.log });
|
|
46
|
+
// Concat is safe by spec — ingest diagnostics are paths-normalized in
|
|
47
|
+
// session.setFiles; query diagnostics are normalized in analyze-core.
|
|
48
|
+
return {
|
|
49
|
+
modules: query.modules,
|
|
50
|
+
diagnostics: [...ingest.diagnostics, ...query.diagnostics],
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
finally {
|
|
54
|
+
session.dispose();
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Analyze a library from files on disk with automatic file discovery.
|
|
59
|
+
*
|
|
60
|
+
* Recommended high-level API for one-shot use (CLI, build-time generation):
|
|
61
|
+
*
|
|
62
|
+
* 1. **Discovery** — `discoverSourceFiles` (exports-first, glob fallback)
|
|
63
|
+
* 2. **Ingest** — push discovered files into a single-use session
|
|
64
|
+
* 3. **Analysis** — `session.query()`
|
|
65
|
+
*
|
|
66
|
+
* @returns analyzed modules + concatenated ingest, discovery, and query diagnostics
|
|
67
|
+
* @throws Error if `sourceOptions` validation fails or `tsconfig.json` is missing
|
|
68
|
+
*/
|
|
69
|
+
export const analyzeFromFiles = async (options) => {
|
|
70
|
+
const { projectRoot, include, exclude, resolveDependencies: shouldResolveDependencies = true, resolveImport, sourceOptions, onDuplicates, log, discovery, distDir, } = options;
|
|
71
|
+
// A custom resolver with resolution turned off is a contradiction — the
|
|
72
|
+
// resolver would never be consulted. Fail fast rather than silently dropping
|
|
73
|
+
// it (mirrors the `discovery: 'exports'` + `include` config-error throw).
|
|
74
|
+
if (!shouldResolveDependencies && resolveImport !== undefined) {
|
|
75
|
+
throw new Error('`resolveImport` cannot be combined with `resolveDependencies: false` — ' +
|
|
76
|
+
'dependency resolution is disabled, so the resolver would never be consulted. ' +
|
|
77
|
+
'Remove `resolveImport`, or set `resolveDependencies: true` (the default).');
|
|
78
|
+
}
|
|
79
|
+
// Top-level `exclude` is an ergonomic shortcut that fully replaces
|
|
80
|
+
// `sourceOptions.exclude` (no array merge). Apply before
|
|
81
|
+
// `createSourceOptions` so the normalized options carry a single source
|
|
82
|
+
// of truth.
|
|
83
|
+
const mergedSourceOptions = exclude !== undefined ? { ...sourceOptions, exclude } : sourceOptions;
|
|
84
|
+
const resolvedSourceOptions = createSourceOptions(projectRoot, mergedSourceOptions);
|
|
85
|
+
const normalizedProjectRoot = resolvedSourceOptions.projectRoot;
|
|
86
|
+
// Step 1: discover files.
|
|
87
|
+
const { files: discoveredFiles, diagnostics: discoveryDiagnostics } = await discoverSourceFiles({
|
|
88
|
+
sourceOptions: resolvedSourceOptions,
|
|
89
|
+
include,
|
|
90
|
+
discovery,
|
|
91
|
+
distDir,
|
|
92
|
+
log,
|
|
93
|
+
});
|
|
94
|
+
// Step 2: build the session-default resolver. When dep-resolution is
|
|
95
|
+
// disabled, install the shared no-deps resolver so the session's lex+resolve
|
|
96
|
+
// passes surface no edges (faster than relying on the default resolver to
|
|
97
|
+
// return null for everything). Otherwise forward the caller's resolver — the
|
|
98
|
+
// session normalizes the `ResolveImport` union (bare fn or token-paired) and
|
|
99
|
+
// falls back to its TS+tsconfig default when undefined.
|
|
100
|
+
const sessionResolver = shouldResolveDependencies
|
|
101
|
+
? resolveImport
|
|
102
|
+
: noDepsResolver;
|
|
103
|
+
// Step 3: ingest + analyze via single-use session.
|
|
104
|
+
const session = createAnalysisSession({
|
|
105
|
+
sourceOptions: resolvedSourceOptions,
|
|
106
|
+
log,
|
|
107
|
+
resolveImport: sessionResolver,
|
|
108
|
+
});
|
|
109
|
+
let result;
|
|
110
|
+
try {
|
|
111
|
+
const ingest = await session.setFiles(discoveredFiles);
|
|
112
|
+
const query = session.query({ onDuplicates, log });
|
|
113
|
+
result = {
|
|
114
|
+
modules: query.modules,
|
|
115
|
+
diagnostics: [...ingest.diagnostics, ...query.diagnostics],
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
finally {
|
|
119
|
+
session.dispose();
|
|
120
|
+
}
|
|
121
|
+
// Merge discovery diagnostics. `analyzeCore` and the session both already
|
|
122
|
+
// normalized their own; normalize discovery in place (idempotent for paths
|
|
123
|
+
// already project-relative) and append.
|
|
124
|
+
normalizeDiagnosticPaths(discoveryDiagnostics, normalizedProjectRoot);
|
|
125
|
+
for (const d of discoveryDiagnostics)
|
|
126
|
+
result.diagnostics.push(d);
|
|
127
|
+
if (log && shouldResolveDependencies) {
|
|
128
|
+
const totalDeps = result.modules.reduce((sum, m) => sum + m.dependencies.length, 0);
|
|
129
|
+
log.info(`Resolved ${totalDeps} module dependency edges`);
|
|
130
|
+
}
|
|
131
|
+
if (log && result.diagnostics.length > 0) {
|
|
132
|
+
const errors = errorsOf(result.diagnostics);
|
|
133
|
+
const warnings = warningsOf(result.diagnostics);
|
|
134
|
+
if (errors.length > 0) {
|
|
135
|
+
log.error(`Analysis completed with ${errors.length} error(s):`);
|
|
136
|
+
for (const diagnostic of errors) {
|
|
137
|
+
log.error(` ${formatDiagnostic(diagnostic)}`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (warnings.length > 0) {
|
|
141
|
+
log.warn(`Analysis completed with ${warnings.length} warning(s):`);
|
|
142
|
+
for (const diagnostic of warnings) {
|
|
143
|
+
log.warn(` ${formatDiagnostic(diagnostic)}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return result;
|
|
148
|
+
};
|