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
package/dist/vite.js ADDED
@@ -0,0 +1,418 @@
1
+ /**
2
+ * Vite plugin for svelte-docinfo.
3
+ *
4
+ * Drives a long-lived `AnalysisSession` against a debounce-batched watcher.
5
+ * Every file change funnels into a `pendingChanges` Map and flushes via one
6
+ * `setFiles` + one `query` per debounce window — multi-file editor saves
7
+ * collapse to a single re-analysis instead of N serialized round-trips.
8
+ *
9
+ * The plugin no longer maintains a `fileCache.content` mirror — `setFile`
10
+ * returns `{changed: boolean}` so HMR invalidation reacts to that flag
11
+ * directly. `session.list()` / `session.has()` cover owned-set introspection.
12
+ *
13
+ * Consumers import the analysis result:
14
+ *
15
+ * ```ts
16
+ * import {modules, diagnostics} from 'virtual:svelte-docinfo';
17
+ * ```
18
+ *
19
+ * For TypeScript support, add to your `app.d.ts`:
20
+ *
21
+ * ```ts
22
+ * /// <reference types="svelte-docinfo/virtual-svelte-docinfo.js" />
23
+ * ```
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * // vite.config.ts
28
+ * import svelteDocinfo from './vite.js';
29
+ *
30
+ * export default defineConfig({
31
+ * plugins: [
32
+ * sveltekit(),
33
+ * svelteDocinfo(),
34
+ * ],
35
+ * });
36
+ * ```
37
+ *
38
+ * @module
39
+ */
40
+ import { readFile } from 'node:fs/promises';
41
+ import { createAnalysisSession } from './session.js';
42
+ import { discoverSourceFiles } from './discovery.js';
43
+ import { compactReplacer } from './declaration-helpers.js';
44
+ import { createSourceOptions, isSource, } from './source-config.js';
45
+ import { noDepsResolver } from './dep-resolver.js';
46
+ import { toPosixPath } from './paths.js';
47
+ const VIRTUAL_ID = 'virtual:svelte-docinfo';
48
+ const RESOLVED_VIRTUAL_ID = '\0virtual:svelte-docinfo';
49
+ /** Stable resolver identity for Vite's plugin-container resolver in dev mode. */
50
+ const VITE_DEV_IDENTITY = 'vite-plugin-container';
51
+ /** Stable resolver identity for the Rollup `this.resolve` resolver in build mode. */
52
+ const VITE_BUILD_IDENTITY = 'vite-rollup-build';
53
+ /**
54
+ * Vite plugin: analyzes TypeScript and Svelte source files via a persistent
55
+ * `AnalysisSession` and serves the result as `virtual:svelte-docinfo`.
56
+ *
57
+ * In dev mode, watches source files and triggers debounced HMR updates on
58
+ * changes via `setFiles` batches drained from a pending-changes Map.
59
+ * In build mode, runs analysis once during `buildStart`.
60
+ */
61
+ const svelteDocinfo = (options = {}) => {
62
+ const { include, exclude, resolveDependencies = true, discovery, distDir, sourceOptions, onDuplicates, hmrDebounceMs = 100, } = options;
63
+ let projectRoot;
64
+ let resolvedSourceOptions;
65
+ let logger = null;
66
+ let isDev = false;
67
+ let modulesJson = '[]';
68
+ let diagnosticsJson = '[]';
69
+ let cachedModuleCode = null;
70
+ let server = null;
71
+ let session = null;
72
+ // Discovery diagnostics survive across HMR cycles (the discovery pass only
73
+ // runs at cold start). Tracked here because the session has no concept of
74
+ // "discovery" — it ingests files the plugin hands to it.
75
+ let discoveryDiagnostics = [];
76
+ // ── Resolver wiring ──────────────────────────────────────────────────────
77
+ // Dev resolver: server.pluginContainer.resolveId. Stable identity across
78
+ // the dev session — cache hits work across HMR cycles.
79
+ const viteDevResolver = {
80
+ resolve: async (specifier, fromFile) => {
81
+ try {
82
+ const r = await server.pluginContainer.resolveId(specifier, fromFile);
83
+ return r?.id ?? null;
84
+ }
85
+ catch {
86
+ return null;
87
+ }
88
+ },
89
+ identity: VITE_DEV_IDENTITY,
90
+ };
91
+ // Build resolver: Rollup `this.resolve` is per-build; we re-bind in
92
+ // buildStart while keeping a stable identity so cache hits survive
93
+ // `vite build --watch` rebuilds.
94
+ let buildResolveFn = null;
95
+ const buildResolver = {
96
+ resolve: (specifier, fromFile) => buildResolveFn ? buildResolveFn(specifier, fromFile) : Promise.resolve(null),
97
+ identity: VITE_BUILD_IDENTITY,
98
+ };
99
+ // ── In-flight analysis tracking ─────────────────────────────────────────
100
+ // Initial analysis runs once in buildStart; HMR flushes run on debounce.
101
+ // `load()` awaits both so we never serve stale JSON to a freshly-importing
102
+ // module.
103
+ let initialPromise = null;
104
+ let hmrFlushPromise = null;
105
+ const waitInflight = async () => {
106
+ if (initialPromise)
107
+ await initialPromise;
108
+ if (hmrFlushPromise)
109
+ await hmrFlushPromise;
110
+ };
111
+ // Pending-changes Map: path → newContent (string) or unlink marker (null).
112
+ // Drained by `flushBatch`; survives across multiple flush cycles when new
113
+ // events arrive mid-flight.
114
+ const pendingChanges = new Map();
115
+ // Per-path event sequence. Watcher events (change/add/unlink) bump the
116
+ // counter and stamp the path with the new value; async handlers (the
117
+ // `change` handler awaits readFile before enqueueing) capture the seq at
118
+ // dispatch and drop their result if a newer event has stamped the path
119
+ // in between. Without this, an `unlink` arriving during a `change`'s
120
+ // in-flight readFile gets clobbered when the read resolves with stale
121
+ // content for a now-deleted file.
122
+ let eventSeqCounter = 0;
123
+ const eventSeqs = new Map();
124
+ const stampEvent = (path) => {
125
+ const seq = ++eventSeqCounter;
126
+ eventSeqs.set(path, seq);
127
+ return seq;
128
+ };
129
+ const isLatestEvent = (path, seq) => eventSeqs.get(path) === seq;
130
+ const buildModuleCode = () => {
131
+ if (cachedModuleCode !== null)
132
+ return cachedModuleCode;
133
+ const lines = [
134
+ `export const modules = ${modulesJson};`,
135
+ `export const diagnostics = ${diagnosticsJson};`,
136
+ `export default {modules, diagnostics};`,
137
+ ];
138
+ if (isDev) {
139
+ lines.push(`if (import.meta.hot) { import.meta.hot.accept(); }`);
140
+ }
141
+ cachedModuleCode = lines.join('\n');
142
+ return cachedModuleCode;
143
+ };
144
+ const updateOutputFromQuery = (modules, queryDiagnostics) => {
145
+ // `JSON.stringify([], compactReplacer)` returns the JS `undefined` because
146
+ // the replacer strips empty arrays — we embed the JSON into a template
147
+ // literal where that would interpolate as the literal text `"undefined"`,
148
+ // so handle the empty-modules case explicitly.
149
+ const newModulesJson = modules.length === 0 ? '[]' : JSON.stringify(modules, compactReplacer);
150
+ // Pull cumulative ingest diagnostics from the session — every owned
151
+ // entry's `ingestDiagnostics` are walked there. Discovery diagnostics
152
+ // don't pass through the session (file discovery happens before ingest),
153
+ // so we maintain those separately and merge at publish time.
154
+ const ingest = session ? session.allIngestDiagnostics() : [];
155
+ const merged = [...ingest, ...discoveryDiagnostics, ...queryDiagnostics];
156
+ const newDiagnosticsJson = JSON.stringify(merged);
157
+ if (newModulesJson === modulesJson && newDiagnosticsJson === diagnosticsJson)
158
+ return false;
159
+ modulesJson = newModulesJson;
160
+ diagnosticsJson = newDiagnosticsJson;
161
+ cachedModuleCode = null;
162
+ return true;
163
+ };
164
+ const sendHmrInvalidation = () => {
165
+ if (!server)
166
+ return;
167
+ const mod = server.moduleGraph.getModuleById(RESOLVED_VIRTUAL_ID);
168
+ if (!mod)
169
+ return;
170
+ server.moduleGraph.invalidateModule(mod);
171
+ // Hardcoded Vite URL encoding for \0 prefix on virtual modules.
172
+ // Not a documented Vite public API — if Vite changes the encoding in a
173
+ // future release, HMR silently stops firing (initial load still works
174
+ // through the public resolveId/load hooks). Same pattern as
175
+ // vite_plugin_fuz_css; update both if Vite exposes a proper API.
176
+ const hmrPath = '/@id/__x00__virtual:svelte-docinfo';
177
+ server.hot.send({
178
+ type: 'update',
179
+ updates: [
180
+ {
181
+ type: 'js-update',
182
+ path: hmrPath,
183
+ acceptedPath: hmrPath,
184
+ timestamp: Date.now(),
185
+ },
186
+ ],
187
+ });
188
+ };
189
+ const errorLog = (err) => {
190
+ const log = logger ?? { error: (msg) => console.error(msg) };
191
+ const message = err instanceof Error ? (err.stack ?? err.message) : String(err);
192
+ log.error(`[svelte-docinfo] ${message}`);
193
+ };
194
+ const isWatchedFile = (file) => isSource(file, resolvedSourceOptions);
195
+ // ── Initial analysis (cold start in buildStart) ──────────────────────────
196
+ const runInitialAnalysis = async () => {
197
+ if (!session)
198
+ return;
199
+ const { files, diagnostics } = await discoverSourceFiles({
200
+ sourceOptions: resolvedSourceOptions,
201
+ include,
202
+ discovery,
203
+ distDir,
204
+ log: logger ?? undefined,
205
+ });
206
+ discoveryDiagnostics = diagnostics;
207
+ await session.setFiles(files);
208
+ const result = session.query({ onDuplicates, log: logger ?? undefined });
209
+ updateOutputFromQuery(result.modules, result.diagnostics);
210
+ };
211
+ // ── HMR flush: drain pendingChanges via setFiles + query ─────────────────
212
+ const flushBatch = async () => {
213
+ if (initialPromise)
214
+ await initialPromise;
215
+ if (!session)
216
+ return;
217
+ if (pendingChanges.size === 0)
218
+ return;
219
+ // Drain. New events arriving during this flush land in pendingChanges
220
+ // and are picked up by the surrounding while-loop in `scheduleFlush`.
221
+ const drained = [...pendingChanges];
222
+ pendingChanges.clear();
223
+ const toAdd = [];
224
+ const toDelete = [];
225
+ for (const [path, content] of drained) {
226
+ if (content === null)
227
+ toDelete.push(path);
228
+ else
229
+ toAdd.push({ id: path, content });
230
+ }
231
+ // Apply deletions first so re-adds (rare but possible: unlink+add same
232
+ // path within one debounce window) work correctly.
233
+ for (const path of toDelete)
234
+ await session.deleteFile(path);
235
+ let anyChanged = toDelete.length > 0;
236
+ if (toAdd.length > 0) {
237
+ const ingest = await session.setFiles(toAdd);
238
+ if (ingest.changedIds.size > 0)
239
+ anyChanged = true;
240
+ }
241
+ if (!anyChanged)
242
+ return;
243
+ // `updateOutputFromQuery` pulls the cumulative ingest diagnostics from
244
+ // `session.allIngestDiagnostics()` itself — no per-batch survival
245
+ // tracking needed at the plugin layer.
246
+ const result = session.query({ onDuplicates, log: logger ?? undefined });
247
+ const updated = updateOutputFromQuery(result.modules, result.diagnostics);
248
+ if (updated)
249
+ sendHmrInvalidation();
250
+ };
251
+ const scheduleFlush = () => {
252
+ if (hmrFlushPromise)
253
+ return hmrFlushPromise;
254
+ hmrFlushPromise = (async () => {
255
+ await new Promise((r) => setTimeout(r, hmrDebounceMs));
256
+ try {
257
+ while (pendingChanges.size > 0) {
258
+ await flushBatch();
259
+ }
260
+ }
261
+ catch (err) {
262
+ errorLog(err);
263
+ }
264
+ finally {
265
+ hmrFlushPromise = null;
266
+ }
267
+ })();
268
+ return hmrFlushPromise;
269
+ };
270
+ const enqueueChange = (path, content) => {
271
+ pendingChanges.set(path, content);
272
+ // `eventSeqs[path]` is no longer needed once the latest event has been
273
+ // enqueued: any older in-flight handler will fail `isLatestEvent`
274
+ // against `undefined`, and any newer event will re-stamp on arrival.
275
+ // Bounds the Map's growth in long-running dev sessions with churn.
276
+ eventSeqs.delete(path);
277
+ void scheduleFlush();
278
+ };
279
+ return {
280
+ name: 'vite-plugin-svelte-docinfo',
281
+ configResolved(config) {
282
+ projectRoot = options.projectRoot ?? config.root;
283
+ // `exclude` shortcut overrides `sourceOptions.exclude` (same precedence
284
+ // as `analyzeFromFiles`).
285
+ const mergedSourceOptions = exclude !== undefined ? { ...sourceOptions, exclude } : sourceOptions;
286
+ resolvedSourceOptions = createSourceOptions(projectRoot, mergedSourceOptions);
287
+ // Reject contradictory discovery config upfront so failures are at
288
+ // config-load time rather than first analysis.
289
+ if (discovery === 'exports' && include) {
290
+ throw new Error("svelte-docinfo: discovery: 'exports' is incompatible with `include`. " +
291
+ "Use discovery: 'glob' (with include) or remove include for strict exports mode.");
292
+ }
293
+ logger = config.logger;
294
+ isDev = config.command === 'serve';
295
+ },
296
+ async buildStart() {
297
+ // Choose resolver based on mode; create the session here (after
298
+ // configResolved set sourceOptions; for dev, after configureServer
299
+ // set `server`). Identity stable across rebuilds.
300
+ let sessionResolver;
301
+ if (!resolveDependencies) {
302
+ sessionResolver = noDepsResolver;
303
+ }
304
+ else if (server) {
305
+ sessionResolver = viteDevResolver;
306
+ }
307
+ else {
308
+ const ctxResolve = this.resolve.bind(this);
309
+ buildResolveFn = async (specifier, fromFile) => {
310
+ try {
311
+ const r = await ctxResolve(specifier, fromFile);
312
+ return r?.id ?? null;
313
+ }
314
+ catch {
315
+ return null;
316
+ }
317
+ };
318
+ sessionResolver = buildResolver;
319
+ }
320
+ // Recreate session on each buildStart so `vite build --watch` cycles
321
+ // get a fresh LS — the document registry across builds isn't a
322
+ // guaranteed-stable contract from Vite's side.
323
+ session?.dispose();
324
+ session = createAnalysisSession({
325
+ sourceOptions: resolvedSourceOptions,
326
+ resolveImport: sessionResolver,
327
+ log: logger ?? undefined,
328
+ });
329
+ initialPromise = runInitialAnalysis().finally(() => {
330
+ initialPromise = null;
331
+ });
332
+ await initialPromise;
333
+ },
334
+ resolveId(id) {
335
+ if (id === VIRTUAL_ID)
336
+ return RESOLVED_VIRTUAL_ID;
337
+ return undefined;
338
+ },
339
+ async load(id) {
340
+ if (id !== RESOLVED_VIRTUAL_ID)
341
+ return undefined;
342
+ // Wait for any in-flight analysis (initial or HMR-triggered) so we
343
+ // never serve stale JSON to a freshly-importing module.
344
+ await waitInflight();
345
+ return buildModuleCode();
346
+ },
347
+ configureServer(devServer) {
348
+ server = devServer;
349
+ devServer.watcher.on('change', (rawFile) => {
350
+ // Watcher emits native-separator paths on Windows. Posixify at
351
+ // the boundary so eventSeqs/pendingChanges keys, isWatchedFile
352
+ // checks, and downstream session calls all share one canonical id.
353
+ const file = toPosixPath(rawFile);
354
+ if (!isWatchedFile(file))
355
+ return;
356
+ // Stamp the event before awaiting readFile so a later unlink/add
357
+ // for the same path bumps the seq and our resolution drops below.
358
+ const seq = stampEvent(file);
359
+ // `.catch(errorLog)` — a sync throw before the first `await` would
360
+ // otherwise become an unhandled rejection.
361
+ void (async () => {
362
+ // Re-read the file. Editors that touch mtime without changing
363
+ // content (formatters saving idempotently) trigger spurious
364
+ // events; the session's content-equality check in setFile
365
+ // handles that case — `changed: false` short-circuits the
366
+ // HMR invalidation.
367
+ let newContent;
368
+ try {
369
+ newContent = await readFile(file, 'utf-8');
370
+ }
371
+ catch {
372
+ // File became unreadable between event and read — treat as unlink.
373
+ if (isLatestEvent(file, seq))
374
+ enqueueChange(file, null);
375
+ return;
376
+ }
377
+ // A newer event (unlink/add/change) stamped this path while we
378
+ // were reading — discard our result so we don't overwrite it
379
+ // with stale content (e.g. ingesting a since-deleted file).
380
+ if (!isLatestEvent(file, seq))
381
+ return;
382
+ enqueueChange(file, newContent);
383
+ })().catch(errorLog);
384
+ });
385
+ devServer.watcher.on('add', (rawFile) => {
386
+ const file = toPosixPath(rawFile);
387
+ if (!isWatchedFile(file))
388
+ return;
389
+ const seq = stampEvent(file);
390
+ void (async () => {
391
+ try {
392
+ const content = await readFile(file, 'utf-8');
393
+ if (!isLatestEvent(file, seq))
394
+ return;
395
+ enqueueChange(file, content);
396
+ }
397
+ catch (err) {
398
+ errorLog(err);
399
+ }
400
+ })().catch(errorLog);
401
+ });
402
+ devServer.watcher.on('unlink', (rawFile) => {
403
+ const file = toPosixPath(rawFile);
404
+ // Use session.has() as the gate: paths outside source paths were
405
+ // never owned, so nothing to do.
406
+ if (!session?.has(file))
407
+ return;
408
+ stampEvent(file);
409
+ enqueueChange(file, null);
410
+ });
411
+ devServer.httpServer?.on('close', () => {
412
+ session?.dispose();
413
+ session = null;
414
+ });
415
+ },
416
+ };
417
+ };
418
+ export default svelteDocinfo;
package/package.json ADDED
@@ -0,0 +1,131 @@
1
+ {
2
+ "name": "svelte-docinfo",
3
+ "version": "0.1.0",
4
+ "description": "static analysis for TypeScript and Svelte",
5
+ "glyph": "📜",
6
+ "logo": "logo.svg",
7
+ "logo_alt": "a cyan scroll with curling ends",
8
+ "license": "MIT",
9
+ "homepage": "https://svelte-docinfo.fuz.dev/",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/fuzdev/svelte-docinfo.git"
13
+ },
14
+ "keywords": [
15
+ "svelte",
16
+ "typescript",
17
+ "documentation",
18
+ "static-analysis",
19
+ "api-docs",
20
+ "component",
21
+ "jsdoc",
22
+ "tsdoc",
23
+ "vite-plugin"
24
+ ],
25
+ "scripts": {
26
+ "start": "gro dev",
27
+ "dev": "gro dev",
28
+ "build": "gro build",
29
+ "check": "gro check",
30
+ "test": "gro test",
31
+ "setup-examples": "npm install --prefix examples/api && npm install --prefix examples/vite",
32
+ "preview": "vite preview",
33
+ "deploy": "gro deploy"
34
+ },
35
+ "type": "module",
36
+ "bin": {
37
+ "svelte-docinfo": "dist/main.js"
38
+ },
39
+ "dependencies": {
40
+ "@jridgewell/trace-mapping": "^0.3.25",
41
+ "commander": "^14.0.3",
42
+ "es-module-lexer": "^2.0.0",
43
+ "picomatch": "^4.0.4",
44
+ "tinyglobby": "^0.2.15",
45
+ "typescript": "^5.9.3",
46
+ "zod": "^4.3.6"
47
+ },
48
+ "peerDependencies": {
49
+ "svelte": "^5.0.0",
50
+ "svelte2tsx": "^0.7.30"
51
+ },
52
+ "peerDependenciesMeta": {
53
+ "svelte": {
54
+ "optional": true
55
+ },
56
+ "svelte2tsx": {
57
+ "optional": true
58
+ }
59
+ },
60
+ "devDependencies": {
61
+ "@fuzdev/fuz_code": "^0.45.1",
62
+ "@fuzdev/fuz_css": "^0.60.0",
63
+ "@fuzdev/fuz_ui": "^0.194.0",
64
+ "@fuzdev/fuz_util": "^0.61.1",
65
+ "@fuzdev/gro": "^0.199.1",
66
+ "@ryanatkn/eslint-config": "^0.12.1",
67
+ "@sveltejs/acorn-typescript": "^1.0.9",
68
+ "@sveltejs/adapter-static": "^3.0.10",
69
+ "@sveltejs/kit": "^2.61.1",
70
+ "@sveltejs/package": "^2.5.7",
71
+ "@sveltejs/vite-plugin-svelte": "^6.2.4",
72
+ "@types/estree": "^1.0.8",
73
+ "@types/node": "^24.10.4",
74
+ "@types/picomatch": "^4.0.2",
75
+ "@webref/css": "^8.2.0",
76
+ "eslint": "^9.39.1",
77
+ "eslint-plugin-svelte": "^3.13.1",
78
+ "esm-env": "^1.2.2",
79
+ "magic-string": "^0.30.21",
80
+ "prettier": "^3.7.4",
81
+ "prettier-plugin-svelte": "^3.4.1",
82
+ "svelte": "^5.55.10",
83
+ "svelte-check": "^4.3.5",
84
+ "svelte2tsx": "^0.7.52",
85
+ "tslib": "^2.8.1",
86
+ "typescript-eslint": "^8.48.1",
87
+ "vite": "^7.3.1",
88
+ "vitest": "^4.0.15",
89
+ "zimmerframe": "^1.1.4"
90
+ },
91
+ "prettier": {
92
+ "plugins": [
93
+ "prettier-plugin-svelte"
94
+ ],
95
+ "useTabs": true,
96
+ "printWidth": 100,
97
+ "singleQuote": true,
98
+ "bracketSpacing": false,
99
+ "overrides": [
100
+ {
101
+ "files": "package.json",
102
+ "options": {
103
+ "useTabs": false
104
+ }
105
+ }
106
+ ]
107
+ },
108
+ "sideEffects": [],
109
+ "files": [
110
+ "dist",
111
+ "virtual-svelte-docinfo.d.ts"
112
+ ],
113
+ "exports": {
114
+ "./package.json": "./package.json",
115
+ ".": {
116
+ "types": "./dist/index.d.ts",
117
+ "default": "./dist/index.js"
118
+ },
119
+ "./virtual-svelte-docinfo.js": {
120
+ "types": "./virtual-svelte-docinfo.d.ts"
121
+ },
122
+ "./*.js": {
123
+ "types": "./dist/*.d.ts",
124
+ "default": "./dist/*.js"
125
+ },
126
+ "./*.ts": {
127
+ "types": "./dist/*.d.ts",
128
+ "default": "./dist/*.js"
129
+ }
130
+ }
131
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Type declaration for the `virtual:svelte-docinfo` virtual module
3
+ * provided by the Vite plugin (`vite.ts`).
4
+ *
5
+ * Reference in your `app.d.ts`:
6
+ *
7
+ * ```ts
8
+ * /// <reference types="svelte-docinfo/virtual-svelte-docinfo.js" />
9
+ * ```
10
+ *
11
+ * ## Wire shape vs Zod output shape
12
+ *
13
+ * The plugin serializes `modules` through `compactReplacer`, which strips
14
+ * empty arrays and `false` values to keep the bundle small. So fields like
15
+ * `declarations`, `members`, `props` that are `.default([])` in the schema
16
+ * arrive as `undefined` on any module that has none, and `.default(false)`
17
+ * booleans arrive as `undefined` when false.
18
+ *
19
+ * `ModuleJsonInput` (the `z.input` of `ModuleJson`) reflects this — its
20
+ * default-bearing fields are optional. Consumers either use optional
21
+ * chaining (`mod.declarations?.length`) or run the value through
22
+ * `AnalyzeResultJson.parse({modules, diagnostics})` to restore defaults.
23
+ *
24
+ * `diagnostics` is serialized without the replacer, so the array is always
25
+ * present and `Diagnostic` itself has no defaults to strip — the static
26
+ * type matches the runtime shape exactly.
27
+ *
28
+ * ## Why this file lives at the package root, not `src/lib/`
29
+ *
30
+ * `svelte.config.js` aliases `'svelte-docinfo' → 'src/lib'` (load-bearing
31
+ * for fuz_ui's `library_gen`, a circular dev-dep). `@sveltejs/package`
32
+ * applies that alias to every file under `src/lib/`, including hand-written
33
+ * `.d.ts`, rewriting the `'svelte-docinfo/...'` imports below to relative
34
+ * paths. TypeScript doesn't resolve relative imports inside ambient
35
+ * `declare module` blocks in consumer projects — types silently collapse
36
+ * to `any`.
37
+ *
38
+ * Living at the root keeps the file out of `svelte-package`'s reach;
39
+ * `gro.config.ts` and `package.json` `files` re-register it for npm.
40
+ * Do not "fix" this by moving the file into `src/lib/`.
41
+ */
42
+ declare module 'virtual:svelte-docinfo' {
43
+ import type {ModuleJsonInput} from 'svelte-docinfo/types.js';
44
+ import type {Diagnostic} from 'svelte-docinfo/diagnostics.js';
45
+ export const modules: Array<ModuleJsonInput>;
46
+ export const diagnostics: Array<Diagnostic>;
47
+ const data: {modules: Array<ModuleJsonInput>; diagnostics: Array<Diagnostic>};
48
+ export default data;
49
+ }