@weasel-js/forge 1.6.0 → 1.7.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/README.md +33 -0
- package/dist/{FrameController-BspC9Zr5.d.ts → FrameController-HEcTM04H.d.ts} +24 -1
- package/dist/{chunk-A4JOEGYQ.js → chunk-7GQI2PDH.js} +7 -46
- package/dist/chunk-7GQI2PDH.js.map +1 -0
- package/dist/{chunk-PO5JLVYT.js → chunk-DDDCZ6XW.js} +98 -11
- package/dist/chunk-DDDCZ6XW.js.map +1 -0
- package/dist/{chunk-FWPBKKYB.js → chunk-ERRLEH6S.js} +10 -11
- package/dist/chunk-ERRLEH6S.js.map +1 -0
- package/dist/chunk-LLGBNJAZ.js +77 -0
- package/dist/chunk-LLGBNJAZ.js.map +1 -0
- package/dist/chunk-WEQSLPKT.js +9 -0
- package/dist/chunk-WEQSLPKT.js.map +1 -0
- package/dist/{chunk-VEKEBILL.js → chunk-YKN3IAEX.js} +12 -10
- package/dist/chunk-YKN3IAEX.js.map +1 -0
- package/dist/{config-DU_WuORS.d.ts → config-pDbcbGco.d.ts} +20 -3
- package/dist/csf/preview-api.js +1 -1
- package/dist/frame/index.css +63 -7
- package/dist/frame/index.css.map +1 -1
- package/dist/frame/index.d.ts +4 -2
- package/dist/frame/index.js +10 -3
- package/dist/frame/index.js.map +1 -1
- package/dist/index.d.ts +12 -4
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/shell/index.css +162 -3
- package/dist/shell/index.css.map +1 -1
- package/dist/shell/index.d.ts +39 -37
- package/dist/shell/index.js +166 -188
- package/dist/shell/index.js.map +1 -1
- package/dist/test/index.d.ts +1 -1
- package/dist/test/index.js +4 -3
- package/dist/test/index.js.map +1 -1
- package/dist/vite/index.js +344 -18
- package/dist/vite/index.js.map +1 -1
- package/package.json +10 -10
- package/dist/chunk-A4JOEGYQ.js.map +0 -1
- package/dist/chunk-FWPBKKYB.js.map +0 -1
- package/dist/chunk-PO5JLVYT.js.map +0 -1
- package/dist/chunk-VEKEBILL.js.map +0 -1
package/dist/test/index.d.ts
CHANGED
package/dist/test/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { StoryTrial } from '../chunk-
|
|
2
|
-
import
|
|
1
|
+
import { StoryTrial } from '../chunk-7GQI2PDH.js';
|
|
2
|
+
import '../chunk-LLGBNJAZ.js';
|
|
3
|
+
import { loadStories } from '../chunk-ERRLEH6S.js';
|
|
3
4
|
import '../chunk-2GXKNXTJ.js';
|
|
4
5
|
import '../chunk-HRGPFQBW.js';
|
|
5
|
-
import '../chunk-
|
|
6
|
+
import '../chunk-YKN3IAEX.js';
|
|
6
7
|
import { withValueAtPath } from '@weasel-js/labkit/config';
|
|
7
8
|
import { useState, useEffect } from 'react';
|
|
8
9
|
import { createRoot } from 'react-dom/client';
|
package/dist/test/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/test/runStory.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../src/test/runStory.tsx"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { withValueAtPath } from '@weasel-js/labkit/config';\nimport { type RefObject, useEffect, useState } from 'react';\nimport { createRoot, type Root } from 'react-dom/client';\nimport type { FrameSetup } from '../frame/FrameController';\nimport type { Viewport } from '../protocol/messages';\nimport { StoryTrial } from '../shell/StoryTrial';\nimport { loadStories } from '../story/load';\nimport type { LoadedStory } from '../story/types';\n\n/** Prefixes `error` with the phase it came from, keeping its stack. */\nfunction phased(phase: string, error: unknown): Error {\n const message = error instanceof Error ? error.message : String(error);\n const out = new Error(`${phase} fault: ${message}`, { cause: error });\n if (error instanceof Error && error.stack) out.stack = `${phase} fault: ${error.stack}`;\n return out;\n}\n\nconst settle = () =>\n new Promise<void>((resolve) => {\n requestAnimationFrame(() => setTimeout(resolve, 0));\n });\n\ninterface TestTrialProps {\n story: LoadedStory;\n setup: FrameSetup;\n hostRef: RefObject<HTMLElement | null>;\n /** The config in force after each commit, so `play` reads what the story last wrote. */\n onConfig: (config: unknown) => void;\n onRendered: () => void;\n onError: (error: unknown) => void;\n}\n\n/** The trial a story test runs in: its config and state, held the way a lab trial holds them. */\nfunction TestTrial({ story, setup, hostRef, onConfig, onRendered, onError }: TestTrialProps) {\n const [config, setConfigState] = useState<unknown>(() => story.config.defaults());\n const [state, setState] = useState<unknown>(null);\n useEffect(() => onConfig(config), [config, onConfig]);\n const ctx = {\n config,\n state,\n setConfig: (path: string, value: unknown) => setConfigState((prev: unknown) => withValueAtPath(prev, path, value)),\n setState,\n };\n return <StoryTrial story={story} setup={setup} ctx={ctx} hostRef={hostRef} onRendered={onRendered} onError={onError} />;\n}\n\nexport interface RunStoryOptions {\n /** The frame config (`frameConfig`). */\n setup?: FrameSetup;\n /** Resizes the page, for a story that names a viewport. Without it, stories run at whatever size the page is. */\n viewport?: (width: number, height: number) => Promise<void>;\n}\n\n/** Stops the story a timed-out test left mounted. */\nlet leftover: (() => unknown) | null = null;\n/** The page's size before any story resized it; a story with no viewport runs at this size. */\nlet pageSize: Viewport | null = null;\n\n/**\n * Renders one story in the test page the way the workshop does — defaults, no state, no globals — then plays it.\n * Throws on a render the story's boundary caught, or a play function that fails.\n */\nexport async function runStory(\n mod: Record<string, unknown>,\n exportName: string,\n file: string,\n autoTitle: string,\n { setup = {}, viewport }: RunStoryOptions = {},\n): Promise<void> {\n leftover?.();\n let story: LoadedStory | undefined;\n try {\n story = loadStories(mod, autoTitle, setup.parameters).find((s) => s.exportName === exportName);\n } catch (error) {\n throw phased('load', error);\n }\n if (!story) throw new Error(`load fault: ${file} has no story export \"${exportName}\"`);\n\n pageSize ??= { width: window.innerWidth, height: window.innerHeight };\n const size = story.viewport ?? pageSize;\n if (viewport && (window.innerWidth !== size.width || window.innerHeight !== size.height)) {\n await viewport(size.width, size.height);\n }\n\n const container = document.createElement('div');\n document.body.append(container);\n let root: Root | undefined;\n /** Returns what unmounting threw, having released everything else regardless. */\n const cleanup = (): { error: unknown } | null => {\n if (leftover === cleanup) leftover = null;\n let thrown: { error: unknown } | null = null;\n try {\n root?.unmount();\n } catch (error) {\n thrown = { error };\n } finally {\n root = undefined;\n container.remove();\n }\n return thrown;\n };\n leftover = cleanup;\n\n const hostRef: RefObject<HTMLElement | null> = { current: null };\n let config: unknown;\n let renderError: { error: unknown } | null = null;\n let markRendered: () => void = () => {};\n const rendered = new Promise<void>((resolve) => {\n markRendered = resolve;\n });\n const fail = (error: unknown) => {\n renderError ??= { error };\n // An error the boundary could not catch leaves nothing to announce `rendered`; the check below throws instead.\n markRendered();\n };\n const checkRender = () => {\n if (renderError) throw phased('render', renderError.error);\n };\n\n let failure: { error: unknown } | null = null;\n try {\n try {\n await setup.prepare?.(story);\n root = createRoot(container, { onUncaughtError: fail });\n root.render(\n <TestTrial\n story={story}\n setup={setup}\n hostRef={hostRef}\n onConfig={(next) => {\n config = next;\n }}\n onRendered={markRendered}\n onError={fail}\n />,\n );\n } catch (error) {\n throw phased('mount', error);\n }\n await rendered;\n await settle();\n checkRender();\n const host = hostRef.current;\n if (!host) throw new Error('render fault: the story host never mounted');\n if (story.play) {\n try {\n await story.play({ canvasElement: host, config, globals: {} });\n } catch (error) {\n throw phased('play', error);\n }\n await settle();\n checkRender();\n }\n } catch (error) {\n failure = { error };\n }\n const cleanupError = cleanup();\n if (failure) throw failure.error;\n if (cleanupError) throw phased('cleanup', cleanupError.error);\n}\n"]}
|
package/dist/vite/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { isGallery } from '../chunk-WEQSLPKT.js';
|
|
1
2
|
import { storyNameFromExport, storyId } from '../chunk-HRGPFQBW.js';
|
|
2
3
|
import { existsSync, readFileSync, mkdirSync, writeFileSync, globSync, statSync } from 'fs';
|
|
3
|
-
import { resolve,
|
|
4
|
+
import { resolve, sep, relative, matchesGlob, dirname, join, basename } from 'path';
|
|
4
5
|
import { fileURLToPath } from 'url';
|
|
5
6
|
import { parse } from '@babel/parser';
|
|
6
7
|
|
|
@@ -74,6 +75,275 @@ function autoTitle(file, root, stories) {
|
|
|
74
75
|
}
|
|
75
76
|
return titleFrom(importPath, ".");
|
|
76
77
|
}
|
|
78
|
+
var keyOf = (file, local) => `${file}#${local}`;
|
|
79
|
+
var fileOfKey = (key) => key.slice(0, key.lastIndexOf("#"));
|
|
80
|
+
var SCRIPT = /\.[cm]?[jt]sx?$/;
|
|
81
|
+
var DEFAULT_LOCAL = "*default*";
|
|
82
|
+
var TYPE_KEYS = /* @__PURE__ */ new Set(["typeAnnotation", "typeParameters", "typeArguments", "returnType", "superTypeParameters", "implements"]);
|
|
83
|
+
function createDepGraph({ root, read }) {
|
|
84
|
+
const modules = /* @__PURE__ */ new Map();
|
|
85
|
+
const resolutions = /* @__PURE__ */ new Map();
|
|
86
|
+
const info = (file) => {
|
|
87
|
+
if (!modules.has(file)) {
|
|
88
|
+
let parsed = null;
|
|
89
|
+
try {
|
|
90
|
+
parsed = moduleInfo(read(file), file);
|
|
91
|
+
} catch {
|
|
92
|
+
parsed = null;
|
|
93
|
+
}
|
|
94
|
+
modules.set(file, parsed);
|
|
95
|
+
}
|
|
96
|
+
return modules.get(file) ?? null;
|
|
97
|
+
};
|
|
98
|
+
return {
|
|
99
|
+
invalidate(file) {
|
|
100
|
+
const known = modules.has(file) || resolutions.has(file);
|
|
101
|
+
modules.delete(file);
|
|
102
|
+
resolutions.delete(file);
|
|
103
|
+
return known;
|
|
104
|
+
},
|
|
105
|
+
reset() {
|
|
106
|
+
resolutions.clear();
|
|
107
|
+
},
|
|
108
|
+
async build(entries, resolve4) {
|
|
109
|
+
const resolveFrom = (spec, importer) => {
|
|
110
|
+
let byImporter = resolutions.get(importer);
|
|
111
|
+
if (!byImporter) {
|
|
112
|
+
byImporter = /* @__PURE__ */ new Map();
|
|
113
|
+
resolutions.set(importer, byImporter);
|
|
114
|
+
}
|
|
115
|
+
let pending = byImporter.get(spec);
|
|
116
|
+
if (!pending) {
|
|
117
|
+
pending = resolve4(spec, importer).then(
|
|
118
|
+
(id) => {
|
|
119
|
+
const file = id?.split("?")[0];
|
|
120
|
+
return file && !file.startsWith("\0") && SCRIPT.test(file) && !file.split(/[\\/]/).includes("node_modules") ? file : null;
|
|
121
|
+
},
|
|
122
|
+
() => null
|
|
123
|
+
);
|
|
124
|
+
byImporter.set(spec, pending);
|
|
125
|
+
}
|
|
126
|
+
return pending;
|
|
127
|
+
};
|
|
128
|
+
const declaring = async (file, name, depth = 0) => {
|
|
129
|
+
const mod = info(file);
|
|
130
|
+
if (!mod || depth > 32) return null;
|
|
131
|
+
const exp = mod.exports.get(name);
|
|
132
|
+
if (exp?.kind === "local") {
|
|
133
|
+
const imp = mod.imports.get(exp.local);
|
|
134
|
+
return imp ? importKey(file, imp, depth + 1) : keyOf(file, exp.local);
|
|
135
|
+
}
|
|
136
|
+
if (exp?.kind === "from") {
|
|
137
|
+
const target = await resolveFrom(exp.spec, file);
|
|
138
|
+
return target ? declaring(target, exp.imported, depth + 1) : null;
|
|
139
|
+
}
|
|
140
|
+
if (name === "default") return null;
|
|
141
|
+
for (const spec of mod.stars) {
|
|
142
|
+
const target = await resolveFrom(spec, file);
|
|
143
|
+
const found = target ? await declaring(target, name, depth + 1) : null;
|
|
144
|
+
if (found) return found;
|
|
145
|
+
}
|
|
146
|
+
return null;
|
|
147
|
+
};
|
|
148
|
+
const importKey = async (importer, imp, depth = 0) => {
|
|
149
|
+
if (imp.imported === "*") return null;
|
|
150
|
+
const target = await resolveFrom(imp.spec, importer);
|
|
151
|
+
return target ? declaring(target, imp.imported, depth) : null;
|
|
152
|
+
};
|
|
153
|
+
const componentOf = async (entry) => {
|
|
154
|
+
const mod = info(entry.file);
|
|
155
|
+
if (!mod) return null;
|
|
156
|
+
const byTitle = entry.title.split("/").pop()?.replace(/\s+/g, "");
|
|
157
|
+
for (const local of [entry.componentName, byTitle]) {
|
|
158
|
+
const imp = local ? mod.imports.get(local) : void 0;
|
|
159
|
+
const key = imp ? await importKey(entry.file, imp) : null;
|
|
160
|
+
if (key) return key;
|
|
161
|
+
}
|
|
162
|
+
return null;
|
|
163
|
+
};
|
|
164
|
+
const titles = /* @__PURE__ */ new Map();
|
|
165
|
+
for (const entry of entries) {
|
|
166
|
+
if (isGallery(entry)) continue;
|
|
167
|
+
const list = titles.get(entry.title);
|
|
168
|
+
if (list) list.push(entry);
|
|
169
|
+
else titles.set(entry.title, [entry]);
|
|
170
|
+
}
|
|
171
|
+
const components = /* @__PURE__ */ new Map();
|
|
172
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
173
|
+
for (const [title, list] of titles) {
|
|
174
|
+
let key = null;
|
|
175
|
+
for (const file of new Set(list.map((e) => e.file))) {
|
|
176
|
+
key = await componentOf(list.find((e) => e.file === file));
|
|
177
|
+
if (key) break;
|
|
178
|
+
}
|
|
179
|
+
components.set(title, key);
|
|
180
|
+
if (key) byKey.set(key, [...byKey.get(key) ?? [], title]);
|
|
181
|
+
}
|
|
182
|
+
const componentFiles = new Set([...byKey.keys()].map(fileOfKey));
|
|
183
|
+
const reached = async (key) => {
|
|
184
|
+
const out = /* @__PURE__ */ new Set();
|
|
185
|
+
const home = fileOfKey(key);
|
|
186
|
+
const homeDir = dirname(home);
|
|
187
|
+
const visited = /* @__PURE__ */ new Set();
|
|
188
|
+
const walkFile = async (file, roots) => {
|
|
189
|
+
const mod = info(file);
|
|
190
|
+
if (!mod) return;
|
|
191
|
+
const locals = /* @__PURE__ */ new Set();
|
|
192
|
+
if (roots === null) {
|
|
193
|
+
for (const name of mod.refs.keys()) locals.add(name);
|
|
194
|
+
for (const name of mod.imports.keys()) locals.add(name);
|
|
195
|
+
} else {
|
|
196
|
+
const queue = [...roots];
|
|
197
|
+
while (queue.length > 0) {
|
|
198
|
+
const name = queue.pop();
|
|
199
|
+
if (locals.has(name)) continue;
|
|
200
|
+
locals.add(name);
|
|
201
|
+
const here = keyOf(file, name);
|
|
202
|
+
if (here !== key && byKey.has(here)) {
|
|
203
|
+
out.add(here);
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
for (const ref of mod.refs.get(name) ?? []) {
|
|
207
|
+
if (mod.refs.has(ref) || mod.imports.has(ref)) queue.push(ref);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
for (const name of locals) {
|
|
212
|
+
const imp = mod.imports.get(name);
|
|
213
|
+
if (!imp) continue;
|
|
214
|
+
const target = await importKey(file, imp);
|
|
215
|
+
if (target && byKey.has(target)) {
|
|
216
|
+
if (target !== key) out.add(target);
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
if (!imp.spec.startsWith(".")) continue;
|
|
220
|
+
const helper = await resolveFrom(imp.spec, file);
|
|
221
|
+
if (!helper || visited.has(helper) || componentFiles.has(helper)) continue;
|
|
222
|
+
if (relative(homeDir, helper).startsWith("..")) continue;
|
|
223
|
+
visited.add(helper);
|
|
224
|
+
await walkFile(helper, null);
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
visited.add(home);
|
|
228
|
+
await walkFile(home, [key.slice(home.length + 1)]);
|
|
229
|
+
return out;
|
|
230
|
+
};
|
|
231
|
+
const graph = {};
|
|
232
|
+
for (const [title, key] of components) {
|
|
233
|
+
graph[title] = { source: key ? relative(root, fileOfKey(key)).split(sep).join("/") : null, uses: [], usedBy: [] };
|
|
234
|
+
}
|
|
235
|
+
for (const [title, key] of components) {
|
|
236
|
+
if (!key) continue;
|
|
237
|
+
const uses = /* @__PURE__ */ new Set();
|
|
238
|
+
for (const target of await reached(key)) {
|
|
239
|
+
for (const other of byKey.get(target) ?? []) if (other !== title) uses.add(other);
|
|
240
|
+
}
|
|
241
|
+
const node = graph[title];
|
|
242
|
+
node.uses = [...uses].sort();
|
|
243
|
+
for (const other of node.uses) graph[other]?.usedBy.push(title);
|
|
244
|
+
}
|
|
245
|
+
for (const node of Object.values(graph)) node.usedBy.sort();
|
|
246
|
+
return Object.fromEntries(Object.entries(graph).sort(([a], [b]) => a.localeCompare(b)));
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
function moduleInfo(code, file) {
|
|
251
|
+
const plugins = ["typescript", "decorators-legacy", .../\.[cm]?[jt]sx$/.test(file) ? ["jsx"] : []];
|
|
252
|
+
const program = parse(code, { sourceType: "module", plugins }).program;
|
|
253
|
+
const imports = /* @__PURE__ */ new Map();
|
|
254
|
+
const exports = /* @__PURE__ */ new Map();
|
|
255
|
+
const stars = [];
|
|
256
|
+
const refs = /* @__PURE__ */ new Map();
|
|
257
|
+
const declare = (name, node) => refs.set(name, node ? namesIn(node) : /* @__PURE__ */ new Set());
|
|
258
|
+
const declareStatement = (decl) => {
|
|
259
|
+
if (!decl) return [];
|
|
260
|
+
switch (decl.type) {
|
|
261
|
+
case "FunctionDeclaration":
|
|
262
|
+
case "ClassDeclaration":
|
|
263
|
+
if (!decl.id) return [];
|
|
264
|
+
declare(decl.id.name, decl);
|
|
265
|
+
return [decl.id.name];
|
|
266
|
+
case "VariableDeclaration":
|
|
267
|
+
return decl.declarations.flatMap((d) => {
|
|
268
|
+
if (d.id.type !== "Identifier") return [];
|
|
269
|
+
declare(d.id.name, d.init);
|
|
270
|
+
return [d.id.name];
|
|
271
|
+
});
|
|
272
|
+
default:
|
|
273
|
+
return [];
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
for (const stmt of program.body) {
|
|
277
|
+
switch (stmt.type) {
|
|
278
|
+
case "ImportDeclaration":
|
|
279
|
+
if (stmt.importKind === "type") break;
|
|
280
|
+
for (const spec of stmt.specifiers) {
|
|
281
|
+
if (spec.type === "ImportSpecifier" && spec.importKind === "type") continue;
|
|
282
|
+
const imported = spec.type === "ImportNamespaceSpecifier" ? "*" : spec.type === "ImportDefaultSpecifier" ? "default" : nameOf(spec.imported);
|
|
283
|
+
imports.set(spec.local.name, { spec: stmt.source.value, imported });
|
|
284
|
+
}
|
|
285
|
+
break;
|
|
286
|
+
case "ExportNamedDeclaration":
|
|
287
|
+
if (stmt.exportKind === "type") break;
|
|
288
|
+
for (const name of declareStatement(stmt.declaration)) exports.set(name, { kind: "local", local: name });
|
|
289
|
+
for (const spec of stmt.specifiers) {
|
|
290
|
+
if (spec.type === "ExportSpecifier" && spec.exportKind === "type") continue;
|
|
291
|
+
if (spec.type !== "ExportSpecifier") continue;
|
|
292
|
+
const exported = nameOf(spec.exported);
|
|
293
|
+
const local = nameOf(spec.local);
|
|
294
|
+
exports.set(exported, stmt.source ? { kind: "from", spec: stmt.source.value, imported: local } : { kind: "local", local });
|
|
295
|
+
}
|
|
296
|
+
break;
|
|
297
|
+
case "ExportAllDeclaration":
|
|
298
|
+
if (stmt.exportKind !== "type") stars.push(stmt.source.value);
|
|
299
|
+
break;
|
|
300
|
+
case "ExportDefaultDeclaration": {
|
|
301
|
+
const decl = stmt.declaration;
|
|
302
|
+
const named = (decl.type === "FunctionDeclaration" || decl.type === "ClassDeclaration") && decl.id ? decl.id.name : null;
|
|
303
|
+
if (named) declare(named, decl);
|
|
304
|
+
else if (decl.type === "Identifier") {
|
|
305
|
+
exports.set("default", { kind: "local", local: decl.name });
|
|
306
|
+
break;
|
|
307
|
+
} else declare(DEFAULT_LOCAL, decl);
|
|
308
|
+
exports.set("default", { kind: "local", local: named ?? DEFAULT_LOCAL });
|
|
309
|
+
break;
|
|
310
|
+
}
|
|
311
|
+
default:
|
|
312
|
+
declareStatement(stmt);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return { imports, exports, stars, refs };
|
|
316
|
+
}
|
|
317
|
+
function nameOf(node) {
|
|
318
|
+
return node.type === "Identifier" ? node.name : node.value;
|
|
319
|
+
}
|
|
320
|
+
function namesIn(node) {
|
|
321
|
+
const out = /* @__PURE__ */ new Set();
|
|
322
|
+
const walk = (value, key) => {
|
|
323
|
+
if (key !== void 0 && TYPE_KEYS.has(key)) return;
|
|
324
|
+
if (Array.isArray(value)) {
|
|
325
|
+
for (const item of value) walk(item);
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
if (!value || typeof value !== "object" || typeof value.type !== "string") return;
|
|
329
|
+
const n = value;
|
|
330
|
+
if (n.type.startsWith("TS") && !("expression" in n)) return;
|
|
331
|
+
if (n.type === "Identifier" || n.type === "JSXIdentifier") {
|
|
332
|
+
out.add(n.name);
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
for (const [k, child] of Object.entries(n)) {
|
|
336
|
+
if (k === "leadingComments" || k === "trailingComments" || k === "innerComments" || k === "loc") continue;
|
|
337
|
+
if ((n.type === "MemberExpression" || n.type === "OptionalMemberExpression") && k === "property" && !n.computed) continue;
|
|
338
|
+
if (n.type === "JSXMemberExpression" && k === "property") continue;
|
|
339
|
+
if ((n.type === "ObjectProperty" || n.type === "ObjectMethod" || n.type === "ClassMethod" || n.type === "ClassProperty") && k === "key" && !n.computed) continue;
|
|
340
|
+
if (n.type === "JSXAttribute" && k === "name") continue;
|
|
341
|
+
walk(child, k);
|
|
342
|
+
}
|
|
343
|
+
};
|
|
344
|
+
walk(node);
|
|
345
|
+
return out;
|
|
346
|
+
}
|
|
77
347
|
function indexFile(code, file, autoTitle2) {
|
|
78
348
|
try {
|
|
79
349
|
const plugins = ["typescript", "decorators-legacy", .../\.[jt]sx$/.test(file) ? ["jsx"] : []];
|
|
@@ -119,7 +389,7 @@ function indexProgram(program, file, autoTitle2) {
|
|
|
119
389
|
for (const spec of stmt.specifiers) {
|
|
120
390
|
if (spec.type !== "ExportSpecifier" || spec.exportKind === "type") continue;
|
|
121
391
|
const name = exportedName(spec.exported);
|
|
122
|
-
const node = stmt.source ? void 0 : bindings.get(spec.local
|
|
392
|
+
const node = stmt.source ? void 0 : bindings.get(exportedName(spec.local));
|
|
123
393
|
if (name === "default") {
|
|
124
394
|
metaNode = node;
|
|
125
395
|
metaDoc ??= doc;
|
|
@@ -134,9 +404,11 @@ function indexProgram(program, file, autoTitle2) {
|
|
|
134
404
|
const componentNode = prop(meta, "component");
|
|
135
405
|
const componentName = componentNode?.type === "Identifier" ? componentNode.name : void 0;
|
|
136
406
|
const metaIsolate = isolateOf(meta, bindings, wrappers, "default");
|
|
407
|
+
const metaTags = tagsOf(meta);
|
|
137
408
|
return exported.filter(({ name }) => name !== "__namedExportsOrder").filter(({ name }) => (!include || include(name)) && (!exclude || !exclude(name))).map(({ name, node, doc }) => {
|
|
138
409
|
const spec = objectOf(node, bindings, wrappers);
|
|
139
410
|
const isolate = isolateOf(spec, bindings, wrappers, name) ?? metaIsolate;
|
|
411
|
+
const tags = combineTags(metaTags, tagsOf(spec));
|
|
140
412
|
return {
|
|
141
413
|
id: storyId(title, name),
|
|
142
414
|
title,
|
|
@@ -146,7 +418,8 @@ function indexProgram(program, file, autoTitle2) {
|
|
|
146
418
|
...doc === void 0 ? {} : { description: doc },
|
|
147
419
|
...metaDoc === void 0 ? {} : { componentDescription: metaDoc },
|
|
148
420
|
...componentName === void 0 ? {} : { componentName },
|
|
149
|
-
...isolate === void 0 ? {} : { isolate }
|
|
421
|
+
...isolate === void 0 ? {} : { isolate },
|
|
422
|
+
...tags === void 0 ? {} : { tags }
|
|
150
423
|
};
|
|
151
424
|
});
|
|
152
425
|
}
|
|
@@ -159,6 +432,20 @@ function isolateOf(obj, bindings, wrappers, exportName) {
|
|
|
159
432
|
if (literal === void 0) throw new Error(`${exportName}: isolate must be a string literal`);
|
|
160
433
|
return literal;
|
|
161
434
|
}
|
|
435
|
+
function tagsOf(obj) {
|
|
436
|
+
const value = prop(obj, "tags");
|
|
437
|
+
if (value?.type !== "ArrayExpression") return void 0;
|
|
438
|
+
return value.elements.flatMap((e) => e ? stringLiteral(e) ?? [] : []);
|
|
439
|
+
}
|
|
440
|
+
function combineTags(meta, own) {
|
|
441
|
+
if (meta === void 0 && own === void 0) return void 0;
|
|
442
|
+
const out = new Set(meta);
|
|
443
|
+
for (const tag of own ?? []) {
|
|
444
|
+
if (tag.startsWith("!")) out.delete(tag.slice(1));
|
|
445
|
+
else out.add(tag);
|
|
446
|
+
}
|
|
447
|
+
return [...out];
|
|
448
|
+
}
|
|
162
449
|
function stringLiteral(node) {
|
|
163
450
|
if (node.type === "StringLiteral") return node.value;
|
|
164
451
|
if (node.type === "TemplateLiteral" && node.expressions.length === 0) return node.quasis.map((q) => q.value.cooked ?? q.value.raw).join("");
|
|
@@ -239,7 +526,7 @@ function storybookShims() {
|
|
|
239
526
|
|
|
240
527
|
// src/vite/plugin.ts
|
|
241
528
|
var PREFIX = "virtual:forge/";
|
|
242
|
-
var MODULES = /* @__PURE__ */ new Set(["index.js", "importers.js", "frame-config.js", "shell-config.js", "shell-entry.js", "frame-entry.js"]);
|
|
529
|
+
var MODULES = /* @__PURE__ */ new Set(["index.js", "importers.js", "deps.js", "frame-config.js", "shell-config.js", "shell-entry.js", "frame-entry.js"]);
|
|
243
530
|
var PAGES2 = { "/": "shell-entry.js", "/index.html": "shell-entry.js", "/frame.html": "frame-entry.js" };
|
|
244
531
|
function ownEntries() {
|
|
245
532
|
const here = fileURLToPath(import.meta.url);
|
|
@@ -258,6 +545,9 @@ function forge(options) {
|
|
|
258
545
|
let base = "/";
|
|
259
546
|
let byFile = null;
|
|
260
547
|
let logger;
|
|
548
|
+
let deps = null;
|
|
549
|
+
let depsSent = null;
|
|
550
|
+
let depsPending = null;
|
|
261
551
|
const glob = () => {
|
|
262
552
|
const files2 = options.stories.flatMap(
|
|
263
553
|
(pattern) => globSync(pattern, { cwd: root, exclude: (name) => basename(String(name)) === "node_modules" }).map((f) => resolve(root, f)).filter((f) => statSync(f).isFile()).sort()
|
|
@@ -284,6 +574,7 @@ function forge(options) {
|
|
|
284
574
|
return byFile;
|
|
285
575
|
};
|
|
286
576
|
const index = () => [...files().values()].flat();
|
|
577
|
+
const depGraph = () => deps ??= createDepGraph({ root, read: (file) => readFileSync(file, "utf8") });
|
|
287
578
|
const configModule = (path) => path ? `export { default } from ${JSON.stringify(resolve(root, path))};
|
|
288
579
|
` : "export default {};\n";
|
|
289
580
|
const modules = {
|
|
@@ -315,6 +606,9 @@ const workshop = mountWorkshop({
|
|
|
315
606
|
stories: ${JSON.stringify(options.stories)},
|
|
316
607
|
});
|
|
317
608
|
import.meta.hot?.on('forge:index', (next) => workshop.setIndex(next));
|
|
609
|
+
// Read from source on first request, so the graph costs nothing until the page is up.
|
|
610
|
+
import('virtual:forge/deps.js').then((m) => workshop.setDependencies(m.default));
|
|
611
|
+
import.meta.hot?.on('forge:deps', (next) => workshop.setDependencies(next));
|
|
318
612
|
// A story edit reaches here as an update of the importers module, whose fresh import() URLs carry the new
|
|
319
613
|
// timestamps; the file it names arrives just before, so the reload waits for the importers that can fetch it.
|
|
320
614
|
const stale = new Set();
|
|
@@ -338,12 +632,34 @@ mountFrame({ index, importers, setup });
|
|
|
338
632
|
import.meta.hot?.accept('virtual:forge/importers.js', () => location.reload());
|
|
339
633
|
`
|
|
340
634
|
};
|
|
635
|
+
const invalidate = (server, names) => {
|
|
636
|
+
for (const env of Object.values(server.environments)) {
|
|
637
|
+
for (const name of names) {
|
|
638
|
+
const mod = env.moduleGraph.getModuleById(`\0${PREFIX}${name}`);
|
|
639
|
+
if (mod) env.moduleGraph.invalidateModule(mod);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
};
|
|
643
|
+
function redeps(server) {
|
|
644
|
+
if (depsSent === null || depsPending) return;
|
|
645
|
+
const resolve4 = async (spec, importer) => (await server.environments.client.pluginContainer.resolveId(spec, importer))?.id ?? null;
|
|
646
|
+
depsPending = Promise.resolve().then(async () => {
|
|
647
|
+
const graph = await depGraph().build(index(), resolve4);
|
|
648
|
+
const json = JSON.stringify(graph);
|
|
649
|
+
if (json === depsSent) return;
|
|
650
|
+
depsSent = json;
|
|
651
|
+
invalidate(server, ["deps.js"]);
|
|
652
|
+
server.ws.send({ type: "custom", event: "forge:deps", data: graph });
|
|
653
|
+
}).catch(logError).finally(() => {
|
|
654
|
+
depsPending = null;
|
|
655
|
+
});
|
|
656
|
+
}
|
|
341
657
|
function reindex(server, file, event) {
|
|
342
658
|
const current = files();
|
|
343
659
|
if (event === "change" && current.has(file)) server.ws.send({ type: "custom", event: "forge:story", data: { file } });
|
|
344
|
-
if (event === "change" && !current.has(file)) return;
|
|
345
|
-
if (event === "add" && !matches(file)) return;
|
|
346
|
-
if (event === "unlink" && !current.has(file)) return;
|
|
660
|
+
if (event === "change" && !current.has(file)) return false;
|
|
661
|
+
if (event === "add" && !matches(file)) return false;
|
|
662
|
+
if (event === "unlink" && !current.has(file)) return false;
|
|
347
663
|
const before = JSON.stringify(index());
|
|
348
664
|
if (event === "unlink") {
|
|
349
665
|
current.delete(file);
|
|
@@ -352,18 +668,14 @@ import.meta.hot?.accept('virtual:forge/importers.js', () => location.reload());
|
|
|
352
668
|
current.set(file, read(file));
|
|
353
669
|
} catch (err) {
|
|
354
670
|
logError(err);
|
|
355
|
-
return;
|
|
671
|
+
return false;
|
|
356
672
|
}
|
|
357
673
|
}
|
|
358
674
|
const next = index();
|
|
359
|
-
if (JSON.stringify(next) === before) return;
|
|
360
|
-
|
|
361
|
-
for (const name of ["index.js", "importers.js"]) {
|
|
362
|
-
const mod = env.moduleGraph.getModuleById(`\0${PREFIX}${name}`);
|
|
363
|
-
if (mod) env.moduleGraph.invalidateModule(mod);
|
|
364
|
-
}
|
|
365
|
-
}
|
|
675
|
+
if (JSON.stringify(next) === before) return false;
|
|
676
|
+
invalidate(server, ["index.js", "importers.js"]);
|
|
366
677
|
server.ws.send({ type: "custom", event: "forge:index", data: next });
|
|
678
|
+
return true;
|
|
367
679
|
}
|
|
368
680
|
return [
|
|
369
681
|
...options.storybookShims === false ? [] : [storybookShims()],
|
|
@@ -391,18 +703,32 @@ import.meta.hot?.accept('virtual:forge/importers.js', () => location.reload());
|
|
|
391
703
|
base = config.base;
|
|
392
704
|
logger = config.logger;
|
|
393
705
|
byFile = null;
|
|
706
|
+
deps = null;
|
|
707
|
+
depsSent = null;
|
|
394
708
|
},
|
|
395
709
|
resolveId(id) {
|
|
396
710
|
return id.startsWith(PREFIX) && MODULES.has(id.slice(PREFIX.length)) ? `\0${id}` : void 0;
|
|
397
711
|
},
|
|
398
|
-
load(id) {
|
|
712
|
+
async load(id) {
|
|
399
713
|
if (!id.startsWith(`\0${PREFIX}`)) return void 0;
|
|
400
|
-
|
|
714
|
+
const name = id.slice(PREFIX.length + 1);
|
|
715
|
+
if (name !== "deps.js") return modules[name]?.();
|
|
716
|
+
const graph = await depGraph().build(index(), async (spec, importer) => (await this.resolve(spec, importer))?.id ?? null);
|
|
717
|
+
depsSent = JSON.stringify(graph);
|
|
718
|
+
return `export default ${depsSent};
|
|
719
|
+
`;
|
|
401
720
|
},
|
|
402
721
|
configureServer(server) {
|
|
403
722
|
server.watcher.add([...files().keys()]);
|
|
404
723
|
for (const event of ["add", "change", "unlink"]) {
|
|
405
|
-
server.watcher.on(event, (
|
|
724
|
+
server.watcher.on(event, (path) => {
|
|
725
|
+
const file = resolve(path);
|
|
726
|
+
const indexed = reindex(server, file, event);
|
|
727
|
+
const added = event === "add" && /\.[cm]?[jt]sx?$/.test(file) && !file.split(sep).includes("node_modules");
|
|
728
|
+
if (added) depGraph().reset();
|
|
729
|
+
const read2 = added || event !== "add" && depGraph().invalidate(file);
|
|
730
|
+
if (indexed || read2) redeps(server);
|
|
731
|
+
});
|
|
406
732
|
}
|
|
407
733
|
server.middlewares.use((req, res, next) => {
|
|
408
734
|
const url = req.url ?? "/";
|