@weasel-js/forge 1.9.3 → 1.10.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 +34 -4
- package/dist/{FrameController-CpcY0DNB.d.ts → FrameController-UeTY7ubF.d.ts} +26 -36
- package/dist/{chunk-Q2EZXJFE.js → chunk-4NM3RTYK.js} +70 -56
- package/dist/chunk-4NM3RTYK.js.map +1 -0
- package/dist/chunk-5DXUNLV2.js +6 -0
- package/dist/chunk-5DXUNLV2.js.map +1 -0
- package/dist/{chunk-GZPQMGXJ.js → chunk-DFKCLZKS.js} +5 -4
- package/dist/chunk-DFKCLZKS.js.map +1 -0
- package/dist/{chunk-CJX3W5HY.js → chunk-DKWHGSBZ.js} +4 -3
- package/dist/chunk-DKWHGSBZ.js.map +1 -0
- package/dist/{chunk-BGOHALHO.js → chunk-ENXU34PI.js} +23 -111
- package/dist/chunk-ENXU34PI.js.map +1 -0
- package/dist/chunk-FL2MUPUL.js +10 -0
- package/dist/chunk-FL2MUPUL.js.map +1 -0
- package/dist/{chunk-MGQWCPYE.js → chunk-GB6PRJET.js} +4 -5
- package/dist/chunk-GB6PRJET.js.map +1 -0
- package/dist/{chunk-JVRHBI3V.js → chunk-V4RI7GWD.js} +53 -59
- package/dist/chunk-V4RI7GWD.js.map +1 -0
- package/dist/{chunk-HRGPFQBW.js → chunk-ZAWXMUGZ.js} +6 -3
- package/dist/chunk-ZAWXMUGZ.js.map +1 -0
- package/dist/{config-BfsYw5lg.d.ts → config-Cz7ZyGF-.d.ts} +28 -5
- package/dist/csf/preview-api.d.ts +5 -2
- package/dist/csf/preview-api.js +2 -2
- package/dist/csf/preview-api.js.map +1 -1
- package/dist/frame/index.css +19 -2
- package/dist/frame/index.css.map +1 -1
- package/dist/frame/index.d.ts +2 -2
- package/dist/frame/index.js +7 -41
- package/dist/frame/index.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/shell/index.css +93 -62
- package/dist/shell/index.css.map +1 -1
- package/dist/shell/index.d.ts +10 -17
- package/dist/shell/index.js +724 -587
- package/dist/shell/index.js.map +1 -1
- package/dist/test/index.d.ts +4 -2
- package/dist/test/index.js +8 -8
- package/dist/test/index.js.map +1 -1
- package/dist/test/play.d.ts +2 -4
- package/dist/test/play.js.map +1 -1
- package/dist/vite/index.d.ts +19 -2
- package/dist/vite/index.js +751 -162
- package/dist/vite/index.js.map +1 -1
- package/package.json +10 -5
- package/dist/chunk-BGOHALHO.js.map +0 -1
- package/dist/chunk-CJX3W5HY.js.map +0 -1
- package/dist/chunk-GZPQMGXJ.js.map +0 -1
- package/dist/chunk-HRGPFQBW.js.map +0 -1
- package/dist/chunk-JVRHBI3V.js.map +0 -1
- package/dist/chunk-KJGXJD3U.js +0 -15
- package/dist/chunk-KJGXJD3U.js.map +0 -1
- package/dist/chunk-MGQWCPYE.js.map +0 -1
- package/dist/chunk-Q2EZXJFE.js.map +0 -1
package/dist/vite/index.js
CHANGED
|
@@ -1,19 +1,23 @@
|
|
|
1
|
+
import { DEFAULT_TITLE } from '../chunk-5DXUNLV2.js';
|
|
1
2
|
import { isGallery } from '../chunk-WEQSLPKT.js';
|
|
2
|
-
import { storyNameFromExport, storyId } from '../chunk-
|
|
3
|
-
import { readFileSync, existsSync, mkdirSync, writeFileSync, globSync, statSync } from 'fs';
|
|
4
|
-
import { resolve, sep, relative,
|
|
3
|
+
import { PROP_ARG_TYPES_EXPORT, storyNameFromExport, storyId } from '../chunk-ZAWXMUGZ.js';
|
|
4
|
+
import { readFileSync, mkdtempSync, existsSync, mkdirSync, writeFileSync, globSync, statSync, watch, rmSync } from 'fs';
|
|
5
|
+
import { resolve, sep, relative, join, dirname, basename, matchesGlob, posix } from 'path';
|
|
5
6
|
import { parse } from '@babel/parser';
|
|
7
|
+
import { execFile } from 'child_process';
|
|
6
8
|
import { fileURLToPath } from 'url';
|
|
9
|
+
import { tmpdir } from 'os';
|
|
7
10
|
|
|
8
11
|
// src/vite/html.ts
|
|
9
|
-
|
|
12
|
+
var escaped = (text) => text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
13
|
+
function html(entry, mode = "serve", title = DEFAULT_TITLE) {
|
|
10
14
|
const src = `${mode === "serve" ? "/@id/" : ""}virtual:forge/${entry}`;
|
|
11
15
|
return `<!doctype html>
|
|
12
16
|
<html lang="en">
|
|
13
17
|
<head>
|
|
14
18
|
<meta charset="utf-8" />
|
|
15
19
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
16
|
-
<title
|
|
20
|
+
<title>${escaped(title)}</title>
|
|
17
21
|
</head>
|
|
18
22
|
<body>
|
|
19
23
|
<div id="root"></div>
|
|
@@ -26,13 +30,13 @@ function html(entry, mode = "serve") {
|
|
|
26
30
|
// src/vite/build.ts
|
|
27
31
|
var DIR = "node_modules/.forge";
|
|
28
32
|
var PAGES = { index: "shell-entry.js", frame: "frame-entry.js" };
|
|
29
|
-
function writePages(root) {
|
|
33
|
+
function writePages(root, title) {
|
|
30
34
|
const dir = join(root, DIR);
|
|
31
35
|
mkdirSync(dir, { recursive: true });
|
|
32
36
|
return Object.fromEntries(
|
|
33
37
|
Object.entries(PAGES).map(([name, entry]) => {
|
|
34
38
|
const file = join(dir, `${name}.html`);
|
|
35
|
-
writeFileSync(file, html(entry, "build"));
|
|
39
|
+
writeFileSync(file, html(entry, "build", title));
|
|
36
40
|
return [name, file];
|
|
37
41
|
})
|
|
38
42
|
);
|
|
@@ -47,34 +51,6 @@ function hoistPages(bundle, emit) {
|
|
|
47
51
|
emit({ type: "asset", fileName: `${name}.html`, source: up(asset.source) });
|
|
48
52
|
}
|
|
49
53
|
}
|
|
50
|
-
var posix = (path) => path.split(sep).join("/");
|
|
51
|
-
var dotted = (path) => /^\.{1,2}([/\\]|$)/.test(path) ? path : `./${path}`;
|
|
52
|
-
function globDirectory(pattern) {
|
|
53
|
-
const segments = pattern.split("/");
|
|
54
|
-
const magic = segments.findIndex((segment) => /^!|(^|[^\\])[*?[{(]/.test(segment));
|
|
55
|
-
return magic === -1 ? dirname(pattern) : segments.slice(0, magic).join("/");
|
|
56
|
-
}
|
|
57
|
-
function titleParts(parts) {
|
|
58
|
-
const last = parts[parts.length - 1];
|
|
59
|
-
if (last === void 0) return parts;
|
|
60
|
-
const stripped = last.replace(/(?:[.](?:story|stories))?([.][^.]+)$/i, "");
|
|
61
|
-
if (parts.length === 1) return [stripped];
|
|
62
|
-
const nextToLast = parts[parts.length - 2];
|
|
63
|
-
if (stripped && nextToLast && stripped.toLowerCase() === nextToLast.toLowerCase()) return [...parts.slice(0, -2), stripped];
|
|
64
|
-
if (stripped && (/^(story|stories)([.][^.]+)$/i.test(last) || /^index$/i.test(stripped))) return parts.slice(0, -1);
|
|
65
|
-
return [...parts.slice(0, -1), stripped];
|
|
66
|
-
}
|
|
67
|
-
var titleFrom = (importPath, directory) => titleParts(importPath.replace(directory, "").split("/").filter(Boolean)).join("/");
|
|
68
|
-
function autoTitle(file, root, stories) {
|
|
69
|
-
const path = posix(relative(root, file));
|
|
70
|
-
const importPath = dotted(path);
|
|
71
|
-
for (const pattern of stories) {
|
|
72
|
-
if (!matchesGlob(path, pattern.replace(/^\.\//, ""))) continue;
|
|
73
|
-
const directory = dotted(posix(relative(root, resolve(root, globDirectory(pattern))))).replace(/\/$/, "");
|
|
74
|
-
return titleFrom(importPath, directory);
|
|
75
|
-
}
|
|
76
|
-
return titleFrom(importPath, ".");
|
|
77
|
-
}
|
|
78
54
|
var keyOf = (file, local) => `${file}#${local}`;
|
|
79
55
|
var fileOfKey = (key) => key.slice(0, key.lastIndexOf("#"));
|
|
80
56
|
var SCRIPT = /\.[cm]?[jt]sx?$/;
|
|
@@ -105,7 +81,7 @@ function createDepGraph({ root, read }) {
|
|
|
105
81
|
reset() {
|
|
106
82
|
resolutions.clear();
|
|
107
83
|
},
|
|
108
|
-
async build(entries,
|
|
84
|
+
async build(entries, resolve5) {
|
|
109
85
|
const resolveFrom = (spec, importer) => {
|
|
110
86
|
let byImporter = resolutions.get(importer);
|
|
111
87
|
if (!byImporter) {
|
|
@@ -114,7 +90,7 @@ function createDepGraph({ root, read }) {
|
|
|
114
90
|
}
|
|
115
91
|
let pending = byImporter.get(spec);
|
|
116
92
|
if (!pending) {
|
|
117
|
-
pending =
|
|
93
|
+
pending = resolve5(spec, importer).then(
|
|
118
94
|
(id) => {
|
|
119
95
|
const file = id?.split("?")[0];
|
|
120
96
|
return file && !file.startsWith("\0") && SCRIPT.test(file) && !file.split(/[\\/]/).includes("node_modules") ? file : null;
|
|
@@ -344,6 +320,92 @@ function namesIn(node) {
|
|
|
344
320
|
walk(node);
|
|
345
321
|
return out;
|
|
346
322
|
}
|
|
323
|
+
var runGit = (args, cwd) => new Promise((done, fail) => {
|
|
324
|
+
execFile(
|
|
325
|
+
"git",
|
|
326
|
+
["-c", "core.quotepath=off", ...args],
|
|
327
|
+
{ cwd, maxBuffer: 1 << 30 },
|
|
328
|
+
(error, stdout) => error ? fail(error) : done(stdout)
|
|
329
|
+
);
|
|
330
|
+
});
|
|
331
|
+
var LOG_FORMAT = "--format=%x00%H%x09%aI";
|
|
332
|
+
function fileDates(log, paths) {
|
|
333
|
+
const out = /* @__PURE__ */ new Map();
|
|
334
|
+
const names = new Map(paths.map((path) => [path, path]));
|
|
335
|
+
let commit = "";
|
|
336
|
+
let date = "";
|
|
337
|
+
for (const line of log.split("\n")) {
|
|
338
|
+
if (names.size === 0) break;
|
|
339
|
+
if (line.startsWith("\0")) {
|
|
340
|
+
[commit = "", date = ""] = line.slice(1).split(" ");
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
343
|
+
const [status, ...files] = line.split(" ");
|
|
344
|
+
const name = files.at(-1);
|
|
345
|
+
const path = name === void 0 ? void 0 : names.get(name);
|
|
346
|
+
if (!status || name === void 0 || path === void 0) continue;
|
|
347
|
+
out.set(path, { created: date, modified: out.get(path)?.modified ?? date, commit });
|
|
348
|
+
const renamed = status.startsWith("R") ? files[0] : void 0;
|
|
349
|
+
if (status === "A" || renamed !== void 0) names.delete(name);
|
|
350
|
+
if (renamed !== void 0) names.set(renamed, path);
|
|
351
|
+
}
|
|
352
|
+
return out;
|
|
353
|
+
}
|
|
354
|
+
function versionOf(named) {
|
|
355
|
+
const tag = named.trim().split(/[~^]/)[0] ?? "";
|
|
356
|
+
return /(?:^v?|@)(\d+\.\d+\.\d+[-+.\w]*)$/.exec(tag)?.[1] ?? null;
|
|
357
|
+
}
|
|
358
|
+
function packageOf(file, top) {
|
|
359
|
+
for (let dir = dirname(file); dir.startsWith(top); dir = dirname(dir)) {
|
|
360
|
+
const manifest = join(dir, "package.json");
|
|
361
|
+
if (existsSync(manifest)) {
|
|
362
|
+
try {
|
|
363
|
+
const name = JSON.parse(readFileSync(manifest, "utf8")).name;
|
|
364
|
+
if (typeof name === "string") return name;
|
|
365
|
+
} catch {
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
if (dir === top) break;
|
|
369
|
+
}
|
|
370
|
+
return null;
|
|
371
|
+
}
|
|
372
|
+
function createHistoryReader(root, git = runGit) {
|
|
373
|
+
let cached = null;
|
|
374
|
+
async function load(sources) {
|
|
375
|
+
const [top = "", prefix = ""] = (await git(["rev-parse", "--show-toplevel", "--show-prefix"], root)).split("\n");
|
|
376
|
+
const inRepo = new Map(sources.map((source) => [posix.normalize(posix.join(prefix, source)), source]));
|
|
377
|
+
const log = await git(["log", "--name-status", "-M", LOG_FORMAT], top);
|
|
378
|
+
const dates = fileDates(log, [...inRepo.keys()]);
|
|
379
|
+
const byPackage = /* @__PURE__ */ new Map();
|
|
380
|
+
for (const path of dates.keys()) {
|
|
381
|
+
const name = packageOf(resolve(top, path), top);
|
|
382
|
+
byPackage.set(name, [...byPackage.get(name) ?? [], path]);
|
|
383
|
+
}
|
|
384
|
+
const history = {};
|
|
385
|
+
for (const [name, paths] of byPackage) {
|
|
386
|
+
const refs = ["v[0-9]*", "[0-9]*", ...name === null ? [] : [`${name}@[0-9]*`]].map((ref) => `--refs=${ref}`);
|
|
387
|
+
const commits = paths.map((path) => dates.get(path).commit);
|
|
388
|
+
const named = (await git(["name-rev", "--tags", "--name-only", ...refs, ...commits], top)).split("\n");
|
|
389
|
+
paths.forEach((path, i) => {
|
|
390
|
+
const { created, modified } = dates.get(path);
|
|
391
|
+
history[inRepo.get(path)] = { created, modified, introduced: versionOf(named[i] ?? "") };
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
return history;
|
|
395
|
+
}
|
|
396
|
+
return {
|
|
397
|
+
async read(sources) {
|
|
398
|
+
try {
|
|
399
|
+
const head = (await git(["rev-parse", "HEAD"], root)).trim();
|
|
400
|
+
const key = [...sources].sort().join("\n");
|
|
401
|
+
if (cached?.head !== head || cached.sources !== key) cached = { head, sources: key, history: await load(sources) };
|
|
402
|
+
return cached.history;
|
|
403
|
+
} catch {
|
|
404
|
+
return {};
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
}
|
|
347
409
|
function parseFile(code, file) {
|
|
348
410
|
const plugins = ["typescript", "decorators-legacy", .../\.[jt]sx$/.test(file) ? ["jsx"] : []];
|
|
349
411
|
return parse(code, { sourceType: "module", plugins }).program;
|
|
@@ -365,11 +427,59 @@ function calleeOf(node, bindings, depth = 0) {
|
|
|
365
427
|
return void 0;
|
|
366
428
|
}
|
|
367
429
|
}
|
|
430
|
+
var LITERALS = /* @__PURE__ */ new Set([
|
|
431
|
+
"StringLiteral",
|
|
432
|
+
"NumericLiteral",
|
|
433
|
+
"BooleanLiteral",
|
|
434
|
+
"NullLiteral",
|
|
435
|
+
"BigIntLiteral",
|
|
436
|
+
"RegExpLiteral",
|
|
437
|
+
"TemplateLiteral",
|
|
438
|
+
"ArrayExpression",
|
|
439
|
+
"ArrowFunctionExpression",
|
|
440
|
+
"FunctionExpression",
|
|
441
|
+
"FunctionDeclaration",
|
|
442
|
+
"ClassExpression",
|
|
443
|
+
"JSXElement",
|
|
444
|
+
"JSXFragment"
|
|
445
|
+
]);
|
|
446
|
+
function isLiteralValue(node, bindings, depth = 0) {
|
|
447
|
+
if (!node || depth > 16) return false;
|
|
448
|
+
switch (node.type) {
|
|
449
|
+
case "TSSatisfiesExpression":
|
|
450
|
+
case "TSAsExpression":
|
|
451
|
+
case "TSTypeAssertion":
|
|
452
|
+
case "TSNonNullExpression":
|
|
453
|
+
case "ParenthesizedExpression":
|
|
454
|
+
return isLiteralValue(node.expression, bindings, depth + 1);
|
|
455
|
+
case "Identifier":
|
|
456
|
+
return isLiteralValue(bindings.get(node.name), bindings, depth + 1);
|
|
457
|
+
case "ObjectExpression":
|
|
458
|
+
return node.properties.every((p) => p.type !== "SpreadElement");
|
|
459
|
+
default:
|
|
460
|
+
return LITERALS.has(node.type);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
368
463
|
function stringLiteral(node) {
|
|
369
464
|
if (node.type === "StringLiteral") return node.value;
|
|
370
465
|
if (node.type === "TemplateLiteral" && node.expressions.length === 0) return node.quasis.map((q) => q.value.cooked ?? q.value.raw).join("");
|
|
371
466
|
return void 0;
|
|
372
467
|
}
|
|
468
|
+
function stringOf(node, bindings, depth = 0) {
|
|
469
|
+
if (!node || depth > 16) return void 0;
|
|
470
|
+
switch (node.type) {
|
|
471
|
+
case "TSSatisfiesExpression":
|
|
472
|
+
case "TSAsExpression":
|
|
473
|
+
case "TSTypeAssertion":
|
|
474
|
+
case "TSNonNullExpression":
|
|
475
|
+
case "ParenthesizedExpression":
|
|
476
|
+
return stringOf(node.expression, bindings, depth + 1);
|
|
477
|
+
case "Identifier":
|
|
478
|
+
return stringOf(bindings.get(node.name), bindings, depth + 1);
|
|
479
|
+
default:
|
|
480
|
+
return stringLiteral(node);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
373
483
|
function isDefaultExport(stmt) {
|
|
374
484
|
if (stmt.type === "ExportDefaultDeclaration") return true;
|
|
375
485
|
return stmt.type === "ExportNamedDeclaration" && stmt.specifiers.some((s) => s.type === "ExportSpecifier" && exportedName(s.exported) === "default");
|
|
@@ -416,10 +526,58 @@ function stringProp(obj, key) {
|
|
|
416
526
|
return value?.type === "StringLiteral" ? value.value : void 0;
|
|
417
527
|
}
|
|
418
528
|
|
|
529
|
+
// src/vite/nativeExports.ts
|
|
530
|
+
var isFn = (node) => node?.type === "ArrowFunctionExpression" || node?.type === "FunctionExpression" || node?.type === "FunctionDeclaration";
|
|
531
|
+
var foreignFrom = (known) => ({
|
|
532
|
+
value: (name) => known.values[name],
|
|
533
|
+
call: (name) => known.calls[name],
|
|
534
|
+
reexport: (name) => known.reexports[name]
|
|
535
|
+
});
|
|
536
|
+
function returnsStory(statements, scope, depth) {
|
|
537
|
+
return statements.some((stmt) => {
|
|
538
|
+
if (stmt.type === "ReturnStatement") return mayBeStory(stmt.argument ?? void 0, scope, depth);
|
|
539
|
+
if (stmt.type === "BlockStatement") return returnsStory(stmt.body, scope, depth);
|
|
540
|
+
if (stmt.type !== "IfStatement") return false;
|
|
541
|
+
return returnsStory([stmt.consequent, ...stmt.alternate ? [stmt.alternate] : []], scope, depth);
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
function mayReturnStory(fn, scope, depth = 0) {
|
|
545
|
+
return fn.body.type === "BlockStatement" ? returnsStory(fn.body.body, scope, depth + 1) : mayBeStory(fn.body, scope, depth + 1);
|
|
546
|
+
}
|
|
547
|
+
function mayBeStory(node, scope, depth = 0) {
|
|
548
|
+
if (!node) return true;
|
|
549
|
+
if (depth > 16) return false;
|
|
550
|
+
switch (node.type) {
|
|
551
|
+
case "TSSatisfiesExpression":
|
|
552
|
+
case "TSAsExpression":
|
|
553
|
+
case "TSTypeAssertion":
|
|
554
|
+
case "TSNonNullExpression":
|
|
555
|
+
case "ParenthesizedExpression":
|
|
556
|
+
return mayBeStory(node.expression, scope, depth + 1);
|
|
557
|
+
case "Identifier":
|
|
558
|
+
return scope.bindings.has(node.name) ? mayBeStory(scope.bindings.get(node.name), scope, depth + 1) : scope.foreign?.value(node.name) ?? true;
|
|
559
|
+
case "CallExpression": {
|
|
560
|
+
if (node.callee.type !== "Identifier") return false;
|
|
561
|
+
if (scope.wrappers.has(node.callee.name)) return true;
|
|
562
|
+
const fn = scope.bindings.get(node.callee.name);
|
|
563
|
+
if (isFn(fn)) return mayReturnStory(fn, scope, depth);
|
|
564
|
+
return !scope.bindings.has(node.callee.name) && (scope.foreign?.call(node.callee.name) ?? false);
|
|
565
|
+
}
|
|
566
|
+
case "ConditionalExpression":
|
|
567
|
+
return mayBeStory(node.consequent, scope, depth + 1) || mayBeStory(node.alternate, scope, depth + 1);
|
|
568
|
+
case "LogicalExpression":
|
|
569
|
+
return mayBeStory(node.left, scope, depth + 1) || mayBeStory(node.right, scope, depth + 1);
|
|
570
|
+
case "ObjectExpression":
|
|
571
|
+
return node.properties.some((p) => p.type === "SpreadElement" && mayBeStory(p.argument, scope, depth + 1));
|
|
572
|
+
default:
|
|
573
|
+
return !isLiteralValue(node, scope.bindings);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
419
577
|
// src/vite/indexFile.ts
|
|
420
|
-
function indexFile(code, file, autoTitle2, wrappers = /* @__PURE__ */ new Set()) {
|
|
578
|
+
function indexFile(code, file, autoTitle2, wrappers = /* @__PURE__ */ new Set(), foreign) {
|
|
421
579
|
try {
|
|
422
|
-
return indexProgram(parseFile(code, file), file, autoTitle2, wrappers);
|
|
580
|
+
return indexProgram(parseFile(code, file), file, autoTitle2, wrappers, foreign);
|
|
423
581
|
} catch (err) {
|
|
424
582
|
throw new Error(`${file}: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
|
|
425
583
|
}
|
|
@@ -464,11 +622,13 @@ function scan(program, extra) {
|
|
|
464
622
|
const decl = stmt.type === "ExportNamedDeclaration" ? stmt.declaration : stmt;
|
|
465
623
|
if (decl?.type === "VariableDeclaration") {
|
|
466
624
|
for (const d of decl.declarations) if (d.id.type === "Identifier" && d.init) bindings.set(d.id.name, d.init);
|
|
625
|
+
} else if (decl?.type === "FunctionDeclaration" && decl.id) {
|
|
626
|
+
bindings.set(decl.id.name, decl);
|
|
467
627
|
}
|
|
468
628
|
}
|
|
469
629
|
return { bindings, wrappers };
|
|
470
630
|
}
|
|
471
|
-
function indexProgram(program, file, autoTitle2, extra) {
|
|
631
|
+
function indexProgram(program, file, autoTitle2, extra, foreign) {
|
|
472
632
|
const { bindings, wrappers } = scan(program, extra);
|
|
473
633
|
let metaNode;
|
|
474
634
|
let metaDoc;
|
|
@@ -486,7 +646,7 @@ function indexProgram(program, file, autoTitle2, extra) {
|
|
|
486
646
|
if (d.id.type === "Identifier") exported.push({ name: d.id.name, node: d.init ?? void 0, doc: docOf(d) ?? doc });
|
|
487
647
|
}
|
|
488
648
|
} else if (decl?.type === "FunctionDeclaration" && decl.id) {
|
|
489
|
-
exported.push({ name: decl.id.name, node:
|
|
649
|
+
exported.push({ name: decl.id.name, node: decl, doc });
|
|
490
650
|
}
|
|
491
651
|
for (const spec of stmt.specifiers) {
|
|
492
652
|
if (spec.type !== "ExportSpecifier" || spec.exportKind === "type") continue;
|
|
@@ -495,12 +655,13 @@ function indexProgram(program, file, autoTitle2, extra) {
|
|
|
495
655
|
if (name === "default") {
|
|
496
656
|
metaNode = node;
|
|
497
657
|
metaDoc ??= doc;
|
|
498
|
-
} else exported.push({ name, node, doc });
|
|
658
|
+
} else if (stmt.source) exported.push({ name, node, doc, from: true });
|
|
659
|
+
else exported.push({ name, node: node ?? (spec.local.type === "Identifier" ? spec.local : void 0), doc });
|
|
499
660
|
}
|
|
500
661
|
}
|
|
501
662
|
if (!metaNode && !program.body.some(isDefaultExport)) return [];
|
|
502
663
|
const meta = objectOf(metaNode, bindings, wrappers);
|
|
503
|
-
const title =
|
|
664
|
+
const title = stringOf(prop(meta, "title"), bindings) ?? autoTitle2;
|
|
504
665
|
const include = storyFilter(meta, "includeStories");
|
|
505
666
|
const exclude = storyFilter(meta, "excludeStories");
|
|
506
667
|
const componentNode = prop(meta, "component");
|
|
@@ -508,7 +669,7 @@ function indexProgram(program, file, autoTitle2, extra) {
|
|
|
508
669
|
const metaIsolate = isolateOf(meta, bindings, wrappers, "default");
|
|
509
670
|
const metaTags = tagsOf(meta);
|
|
510
671
|
const native = wrappers.has(calleeOf(metaNode, bindings) ?? "");
|
|
511
|
-
return exported.filter(({ name }) => name !== "__namedExportsOrder").filter(({ name }) => (!include || include(name)) && (!exclude || !exclude(name))).map(({ name, node, doc }) => {
|
|
672
|
+
return exported.filter(({ name }) => name !== "__namedExportsOrder").filter(({ name }) => (!include || include(name)) && (!exclude || !exclude(name))).filter((e) => !native || (e.from ? foreign?.reexport(e.name) ?? true : mayBeStory(e.node, { bindings, wrappers, foreign }))).map(({ name, node, doc }) => {
|
|
512
673
|
const spec = objectOf(node, bindings, wrappers);
|
|
513
674
|
const isolate = isolateOf(spec, bindings, wrappers, name) ?? metaIsolate;
|
|
514
675
|
const tags = combineTags(metaTags, tagsOf(spec));
|
|
@@ -569,36 +730,105 @@ function storyFilter(obj, key) {
|
|
|
569
730
|
}
|
|
570
731
|
return null;
|
|
571
732
|
}
|
|
572
|
-
function
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
733
|
+
function storyGlobOf(path, stories) {
|
|
734
|
+
return stories.find((pattern) => matchesGlob(path, pattern.replace(/^\.\//, "")));
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
// src/vite/autoTitle.ts
|
|
738
|
+
var posix2 = (path) => path.split(sep).join("/");
|
|
739
|
+
var dotted = (path) => /^\.{1,2}([/\\]|$)/.test(path) ? path : `./${path}`;
|
|
740
|
+
function globDirectory(pattern) {
|
|
741
|
+
const segments = pattern.split("/");
|
|
742
|
+
const magic = segments.findIndex((segment) => /^!|(^|[^\\])[*?[{(]/.test(segment));
|
|
743
|
+
return magic === -1 ? dirname(pattern) : segments.slice(0, magic).join("/");
|
|
744
|
+
}
|
|
745
|
+
function titleParts(parts) {
|
|
746
|
+
const last = parts[parts.length - 1];
|
|
747
|
+
if (last === void 0) return parts;
|
|
748
|
+
const stripped = last.replace(/(?:[.](?:story|stories))?([.][^.]+)$/i, "");
|
|
749
|
+
if (parts.length === 1) return [stripped];
|
|
750
|
+
const nextToLast = parts[parts.length - 2];
|
|
751
|
+
if (stripped && nextToLast && stripped.toLowerCase() === nextToLast.toLowerCase()) return [...parts.slice(0, -2), stripped];
|
|
752
|
+
if (stripped && (/^(story|stories)([.][^.]+)$/i.test(last) || /^index$/i.test(stripped))) return parts.slice(0, -1);
|
|
753
|
+
return [...parts.slice(0, -1), stripped];
|
|
754
|
+
}
|
|
755
|
+
var titleFrom = (importPath, directory) => titleParts(importPath.replace(directory, "").split("/").filter(Boolean)).join("/");
|
|
756
|
+
function autoTitle(file, root, stories) {
|
|
757
|
+
const path = posix2(relative(root, file));
|
|
758
|
+
const importPath = dotted(path);
|
|
759
|
+
const pattern = storyGlobOf(path, stories);
|
|
760
|
+
if (pattern === void 0) return titleFrom(importPath, ".");
|
|
761
|
+
const directory = dotted(posix2(relative(root, resolve(root, globDirectory(pattern))))).replace(/\/$/, "");
|
|
762
|
+
return titleFrom(importPath, directory);
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// src/vite/foreignStories.ts
|
|
766
|
+
var MAX_DEPTH = 8;
|
|
767
|
+
async function settled(read, ask) {
|
|
768
|
+
const known = { values: {}, calls: {}, reexports: {} };
|
|
769
|
+
const asked = /* @__PURE__ */ new Set();
|
|
770
|
+
for (; ; ) {
|
|
771
|
+
const fresh = [];
|
|
772
|
+
const answer = (kind) => (name) => {
|
|
773
|
+
if (!asked.has(`${kind}:${name}`)) fresh.push([kind, name]);
|
|
774
|
+
return known[kind][name];
|
|
775
|
+
};
|
|
776
|
+
const result = read({ value: answer("values"), call: answer("calls"), reexport: answer("reexports") });
|
|
777
|
+
if (fresh.length === 0) return { result, known };
|
|
778
|
+
for (const [kind, name] of fresh) {
|
|
779
|
+
asked.add(`${kind}:${name}`);
|
|
780
|
+
const found = await ask(kind, name);
|
|
781
|
+
if (found !== void 0) known[kind][name] = found;
|
|
578
782
|
}
|
|
579
|
-
const source = !relative(join(dir, "src"), here).startsWith("..");
|
|
580
|
-
return realms.map((realm) => join(dir, source ? `src/${realm}/index.ts` : `dist/${realm}/index.js`));
|
|
581
783
|
}
|
|
582
|
-
return [];
|
|
583
784
|
}
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
const hit = SHIMS.find(([pattern]) => pattern.test(id));
|
|
594
|
-
if (!hit) return void 0;
|
|
595
|
-
return this.resolve(hit[1], fileURLToPath(import.meta.url), { skipSelf: true });
|
|
785
|
+
async function foreignStories(file, wrappers, own, trace, read) {
|
|
786
|
+
const answers = /* @__PURE__ */ new Map();
|
|
787
|
+
const exported = async (at, name, called, depth) => {
|
|
788
|
+
const key = await trace.declaring(at, name);
|
|
789
|
+
if (!key) return void 0;
|
|
790
|
+
if (own.has(key)) return called && own.get(key) === "story";
|
|
791
|
+
const memo = `${key}:${called}`;
|
|
792
|
+
if (!answers.has(memo)) {
|
|
793
|
+
answers.set(memo, depth > MAX_DEPTH ? Promise.resolve(void 0) : declared(key, called, depth));
|
|
596
794
|
}
|
|
795
|
+
return answers.get(memo);
|
|
597
796
|
};
|
|
797
|
+
const declared = async (key, called, depth) => {
|
|
798
|
+
const at = key.slice(0, key.lastIndexOf("#"));
|
|
799
|
+
const local = key.slice(at.length + 1);
|
|
800
|
+
let scope;
|
|
801
|
+
try {
|
|
802
|
+
scope = scan(parseFile(read(at), at), /* @__PURE__ */ new Set());
|
|
803
|
+
} catch {
|
|
804
|
+
return void 0;
|
|
805
|
+
}
|
|
806
|
+
const node = scope.bindings.get(local);
|
|
807
|
+
if (!node) return void 0;
|
|
808
|
+
const { result } = await settled(
|
|
809
|
+
(foreign) => called ? isFn(node) && mayReturnStory(node, { ...scope, foreign }) : mayBeStory(node, { ...scope, foreign }),
|
|
810
|
+
asking(at, depth + 1)
|
|
811
|
+
);
|
|
812
|
+
return result;
|
|
813
|
+
};
|
|
814
|
+
const asking = (at, depth) => async (kind, name) => {
|
|
815
|
+
const mod = trace.info(at);
|
|
816
|
+
const exp = mod?.exports.get(name);
|
|
817
|
+
const from = kind === "reexports" ? exp?.kind === "from" ? exp : void 0 : mod?.imports.get(name);
|
|
818
|
+
if (!from || from.imported === "*") return void 0;
|
|
819
|
+
const target = await trace.target(from.spec, at);
|
|
820
|
+
return target ? exported(target, from.imported, kind === "calls", depth) : void 0;
|
|
821
|
+
};
|
|
822
|
+
try {
|
|
823
|
+
const program = parseFile(read(file), file);
|
|
824
|
+
return (await settled((foreign) => void indexProgram(program, file, "", wrappers, foreign), asking(file, 0))).known;
|
|
825
|
+
} catch {
|
|
826
|
+
return { values: {}, calls: {}, reexports: {} };
|
|
827
|
+
}
|
|
598
828
|
}
|
|
599
829
|
|
|
600
|
-
// src/vite/
|
|
601
|
-
function
|
|
830
|
+
// src/vite/trace.ts
|
|
831
|
+
function createTracer(read) {
|
|
602
832
|
const modules = /* @__PURE__ */ new Map();
|
|
603
833
|
const info = (file) => {
|
|
604
834
|
if (!modules.has(file)) {
|
|
@@ -612,18 +842,20 @@ function createWrapperResolver(read) {
|
|
|
612
842
|
}
|
|
613
843
|
return modules.get(file) ?? null;
|
|
614
844
|
};
|
|
615
|
-
return
|
|
616
|
-
const
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
845
|
+
return (resolve5) => {
|
|
846
|
+
const trace = {
|
|
847
|
+
reads: /* @__PURE__ */ new Set(),
|
|
848
|
+
unresolved: false,
|
|
849
|
+
info,
|
|
850
|
+
async target(spec, importer) {
|
|
851
|
+
const id = (await resolve5(spec, importer).catch(() => null))?.split("?")[0];
|
|
852
|
+
if (!id) trace.unresolved = true;
|
|
853
|
+
return id && !id.startsWith("\0") ? id : null;
|
|
854
|
+
},
|
|
855
|
+
declaring: (at, name) => declaring(at, name, 0)
|
|
624
856
|
};
|
|
625
|
-
const declaring = async (at, name, depth
|
|
626
|
-
reads.add(at);
|
|
857
|
+
const declaring = async (at, name, depth) => {
|
|
858
|
+
trace.reads.add(at);
|
|
627
859
|
const mod = info(at);
|
|
628
860
|
if (!mod || depth > 32) return null;
|
|
629
861
|
const exp = mod.exports.get(name);
|
|
@@ -631,39 +863,402 @@ function createWrapperResolver(read) {
|
|
|
631
863
|
const imp = mod.imports.get(exp.local);
|
|
632
864
|
if (!imp) return `${at}#${exp.local}`;
|
|
633
865
|
if (imp.imported === "*") return null;
|
|
634
|
-
const next = await target(imp.spec, at);
|
|
866
|
+
const next = await trace.target(imp.spec, at);
|
|
635
867
|
return next ? declaring(next, imp.imported, depth + 1) : null;
|
|
636
868
|
}
|
|
637
869
|
if (exp?.kind === "from") {
|
|
638
|
-
const next = await target(exp.spec, at);
|
|
870
|
+
const next = await trace.target(exp.spec, at);
|
|
639
871
|
return next ? declaring(next, exp.imported, depth + 1) : null;
|
|
640
872
|
}
|
|
641
873
|
if (name === "default") return null;
|
|
642
874
|
for (const spec of mod.stars) {
|
|
643
|
-
const next = await target(spec, at);
|
|
875
|
+
const next = await trace.target(spec, at);
|
|
644
876
|
const found = next ? await declaring(next, name, depth + 1) : null;
|
|
645
877
|
if (found) return found;
|
|
646
878
|
}
|
|
647
879
|
return null;
|
|
648
880
|
};
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
881
|
+
return trace;
|
|
882
|
+
};
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
// src/vite/wrappers.ts
|
|
886
|
+
var NO_FOREIGN = { values: {}, calls: {}, reexports: {} };
|
|
887
|
+
function sameResolution(a, b) {
|
|
888
|
+
const key = (r) => JSON.stringify([[...r?.wrappers ?? []].sort(), r?.foreign ?? NO_FOREIGN]);
|
|
889
|
+
return key(a) === key(b);
|
|
890
|
+
}
|
|
891
|
+
function createWrapperResolver(read) {
|
|
892
|
+
const tracer = createTracer(read);
|
|
893
|
+
return async (file, callees, resolve5) => {
|
|
894
|
+
const trace = tracer(resolve5);
|
|
895
|
+
const wrappers = /* @__PURE__ */ new Set();
|
|
896
|
+
const direct = [...trace.info(file)?.imports.values() ?? []].some((imp) => imp.spec === "@weasel-js/forge");
|
|
897
|
+
if (callees.length === 0 && !direct) return { wrappers, foreign: NO_FOREIGN, reads: trace.reads, unresolved: false };
|
|
898
|
+
const forge2 = await trace.target("@weasel-js/forge", file);
|
|
899
|
+
const own = /* @__PURE__ */ new Map();
|
|
900
|
+
if (forge2) {
|
|
901
|
+
for (const name of ["meta", "story"]) {
|
|
902
|
+
const key = await trace.declaring(forge2, name);
|
|
903
|
+
if (key) own.set(key, name);
|
|
904
|
+
}
|
|
905
|
+
for (const callee of callees) {
|
|
906
|
+
const from = await trace.target(callee.spec, file);
|
|
907
|
+
const key = from ? await trace.declaring(from, callee.imported) : null;
|
|
908
|
+
if (key && own.has(key)) wrappers.add(callee.local);
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
const foreign = forge2 ? await foreignStories(file, wrappers, own, trace, read) : NO_FOREIGN;
|
|
912
|
+
return { wrappers, foreign, reads: trace.reads, unresolved: trace.unresolved };
|
|
913
|
+
};
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
// src/vite/indexStory.ts
|
|
917
|
+
function indexStory(code, file, source, resolved) {
|
|
918
|
+
const foreign = resolved ? foreignFrom(resolved.foreign) : void 0;
|
|
919
|
+
return indexFile(code, file, autoTitle(file, source.root, source.stories), resolved?.wrappers, foreign);
|
|
920
|
+
}
|
|
921
|
+
async function indexStoryResolved(code, file, source, resolve5, decide = createWrapperResolver((path) => readFileSync(path, "utf8"))) {
|
|
922
|
+
const resolution = await decide(file, foreignCallees(code, file), resolve5);
|
|
923
|
+
return { entries: indexStory(code, file, source, resolution), resolution };
|
|
924
|
+
}
|
|
925
|
+
function ownEntries(realms) {
|
|
926
|
+
const here = fileURLToPath(import.meta.url);
|
|
927
|
+
for (let dir = dirname(here); dir !== dirname(dir); dir = dirname(dir)) {
|
|
928
|
+
const manifest = join(dir, "package.json");
|
|
929
|
+
if (!existsSync(manifest) || JSON.parse(readFileSync(manifest, "utf8")).name !== "@weasel-js/forge") {
|
|
930
|
+
continue;
|
|
931
|
+
}
|
|
932
|
+
const source = !relative(join(dir, "src"), here).startsWith("..");
|
|
933
|
+
return realms.map((realm) => join(dir, source ? `src/${realm}/index.ts` : `dist/${realm}/index.js`));
|
|
934
|
+
}
|
|
935
|
+
return [];
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
// src/vite/entries.ts
|
|
939
|
+
function shellEntry(base, stories) {
|
|
940
|
+
return `import { mountWorkshop } from '@weasel-js/forge/shell';
|
|
941
|
+
import '@weasel-js/labkit/styles.css';
|
|
942
|
+
import '@weasel-js/forge/shell.css';
|
|
943
|
+
import index from 'virtual:forge/index.js';
|
|
944
|
+
import importers from 'virtual:forge/importers.js';
|
|
945
|
+
import config from 'virtual:forge/shell-config.js';
|
|
946
|
+
import setup from 'virtual:forge/frame-config.js';
|
|
947
|
+
|
|
948
|
+
const workshop = mountWorkshop({
|
|
949
|
+
index,
|
|
950
|
+
importers,
|
|
951
|
+
setup,
|
|
952
|
+
config,
|
|
953
|
+
frameUrl: ${JSON.stringify(`${base}frame.html`)},
|
|
954
|
+
stories: ${JSON.stringify(stories)},
|
|
955
|
+
});
|
|
956
|
+
import.meta.hot?.on('forge:index', (next) => workshop.setIndex(next));
|
|
957
|
+
// Read from source on first request, so the graph costs nothing until the page is up.
|
|
958
|
+
import('virtual:forge/deps.js').then((m) => workshop.setDependencies(m.default));
|
|
959
|
+
import.meta.hot?.on('forge:deps', (next) => workshop.setDependencies(next));
|
|
960
|
+
// An edit to a story file, or to a module one imports, reaches here as an update of the importers module, whose
|
|
961
|
+
// fresh import() URLs carry the new timestamps; the files it reaches arrive just before, so the reload waits for
|
|
962
|
+
// the importers that can fetch them.
|
|
963
|
+
const stale = new Set();
|
|
964
|
+
import.meta.hot?.on('forge:story', ({ file }) => stale.add(file));
|
|
965
|
+
import.meta.hot?.accept('virtual:forge/importers.js', (next) => {
|
|
966
|
+
if (!next) return;
|
|
967
|
+
workshop.setImporters(next.default);
|
|
968
|
+
for (const file of stale) workshop.reloadStory(file);
|
|
969
|
+
stale.clear();
|
|
970
|
+
});
|
|
971
|
+
`;
|
|
972
|
+
}
|
|
973
|
+
function frameEntry(styles) {
|
|
974
|
+
return `import { mountFrame } from '@weasel-js/forge/frame';
|
|
975
|
+
import '@weasel-js/forge/frame.css';
|
|
976
|
+
${styles.map((path) => `import ${JSON.stringify(path)};
|
|
977
|
+
`).join("")}import index from 'virtual:forge/index.js';
|
|
978
|
+
import importers from 'virtual:forge/importers.js';
|
|
979
|
+
import setup from 'virtual:forge/frame-config.js';
|
|
980
|
+
|
|
981
|
+
mountFrame({ index, importers, setup });
|
|
982
|
+
// The importers module is shared with the workshop page, which accepts a story edit in place; a frame document
|
|
983
|
+
// showing the old module reloads instead, and without this the edit would dead-end here and reload every page.
|
|
984
|
+
import.meta.hot?.accept('virtual:forge/importers.js', () => location.reload());
|
|
985
|
+
`;
|
|
986
|
+
}
|
|
987
|
+
var PLATFORM = /[\\/]node_modules[\\/](@types[\\/]react(-dom)?|typescript)[\\/]/;
|
|
988
|
+
async function createPropReader(root, stories, { skip } = {}) {
|
|
989
|
+
const skipped = (file) => PLATFORM.test(file) || skip?.(file) === true;
|
|
990
|
+
let tsm;
|
|
991
|
+
try {
|
|
992
|
+
tsm = (await import('typescript')).default;
|
|
993
|
+
} catch {
|
|
994
|
+
return null;
|
|
995
|
+
}
|
|
996
|
+
const { sys } = tsm;
|
|
997
|
+
const configFile = tsm.findConfigFile(root, sys.fileExists);
|
|
998
|
+
const read = configFile ? tsm.readConfigFile(configFile, sys.readFile) : void 0;
|
|
999
|
+
const parsed = read?.config ? tsm.parseJsonConfigFileContent(read.config, { ...sys, readDirectory: () => [] }, dirname(configFile)) : void 0;
|
|
1000
|
+
const options = {
|
|
1001
|
+
jsx: tsm.JsxEmit.ReactJSX,
|
|
1002
|
+
moduleResolution: tsm.ModuleResolutionKind.Bundler,
|
|
1003
|
+
module: tsm.ModuleKind.ESNext,
|
|
1004
|
+
target: tsm.ScriptTarget.ESNext,
|
|
1005
|
+
strict: true,
|
|
1006
|
+
...parsed?.options,
|
|
1007
|
+
allowJs: true,
|
|
1008
|
+
noEmit: true,
|
|
1009
|
+
skipLibCheck: true
|
|
1010
|
+
};
|
|
1011
|
+
const host = {
|
|
1012
|
+
getScriptFileNames: () => [...stories()],
|
|
1013
|
+
getScriptVersion: (file) => String(sys.getModifiedTime?.(file)?.getTime() ?? 0),
|
|
1014
|
+
getScriptSnapshot: (file) => {
|
|
1015
|
+
const text = sys.readFile(file);
|
|
1016
|
+
return text === void 0 ? void 0 : tsm.ScriptSnapshot.fromString(text);
|
|
1017
|
+
},
|
|
1018
|
+
getCurrentDirectory: () => root,
|
|
1019
|
+
getCompilationSettings: () => options,
|
|
1020
|
+
getDefaultLibFileName: tsm.getDefaultLibFilePath,
|
|
1021
|
+
fileExists: sys.fileExists,
|
|
1022
|
+
readFile: sys.readFile,
|
|
1023
|
+
readDirectory: sys.readDirectory,
|
|
1024
|
+
directoryExists: sys.directoryExists,
|
|
1025
|
+
getDirectories: sys.getDirectories
|
|
1026
|
+
};
|
|
1027
|
+
const service = tsm.createLanguageService(host, tsm.createDocumentRegistry());
|
|
1028
|
+
return {
|
|
1029
|
+
read(file) {
|
|
1030
|
+
const program = service.getProgram();
|
|
1031
|
+
const source = program?.getSourceFile(file);
|
|
1032
|
+
if (!program || !source) return null;
|
|
1033
|
+
const checker = program.getTypeChecker();
|
|
1034
|
+
const props = componentProps(tsm, checker, source);
|
|
1035
|
+
return props ? propTable(tsm, checker, props, source, skipped) : null;
|
|
1036
|
+
}
|
|
1037
|
+
};
|
|
1038
|
+
}
|
|
1039
|
+
function componentProps(tsm, checker, source) {
|
|
1040
|
+
const module = checker.getSymbolAtLocation(source);
|
|
1041
|
+
const exported = module && checker.tryGetMemberInModuleExports("default", module);
|
|
1042
|
+
if (!exported) return null;
|
|
1043
|
+
const meta = checker.getTypeOfSymbolAtLocation(
|
|
1044
|
+
exported.flags & tsm.SymbolFlags.Alias ? checker.getAliasedSymbol(exported) : exported,
|
|
1045
|
+
source
|
|
1046
|
+
);
|
|
1047
|
+
const named = meta.getProperty("component");
|
|
1048
|
+
if (!named) return null;
|
|
1049
|
+
const component = checker.getNonNullableType(checker.getTypeOfSymbolAtLocation(named, source));
|
|
1050
|
+
for (const part of component.isUnion() ? component.types : [component]) {
|
|
1051
|
+
const [call] = part.getCallSignatures();
|
|
1052
|
+
const [param] = call?.getParameters() ?? [];
|
|
1053
|
+
if (call) return param ? checker.getTypeOfSymbolAtLocation(param, source) : null;
|
|
1054
|
+
const instance = part.getConstructSignatures()[0]?.getReturnType();
|
|
1055
|
+
const held = instance?.getProperty("props");
|
|
1056
|
+
if (held) return checker.getTypeOfSymbolAtLocation(held, source);
|
|
1057
|
+
}
|
|
1058
|
+
return null;
|
|
1059
|
+
}
|
|
1060
|
+
function propTable(tsm, checker, props, at, skipped) {
|
|
1061
|
+
const argTypes = {};
|
|
1062
|
+
const sources = /* @__PURE__ */ new Set();
|
|
1063
|
+
for (const prop2 of checker.getPropertiesOfType(checker.getApparentType(props))) {
|
|
1064
|
+
const files = (prop2.getDeclarations() ?? []).map((declaration) => declaration.getSourceFile().fileName);
|
|
1065
|
+
if (files.length > 0 && files.every(skipped)) continue;
|
|
1066
|
+
const argType = controlFor(
|
|
1067
|
+
tsm,
|
|
1068
|
+
checker.getNonNullableType(checker.getTypeOfSymbolAtLocation(prop2, at)),
|
|
1069
|
+
() => writtenOrder(tsm, checker, prop2)
|
|
1070
|
+
);
|
|
1071
|
+
if (!argType) continue;
|
|
1072
|
+
const description = tsm.displayPartsToString(prop2.getDocumentationComment(checker)).replace(/\s+/g, " ").trim();
|
|
1073
|
+
argTypes[prop2.getName()] = description ? { ...argType, description } : argType;
|
|
1074
|
+
for (const file of files) if (!/[\\/]node_modules[\\/]/.test(file)) sources.add(file);
|
|
1075
|
+
}
|
|
1076
|
+
return { argTypes, sources: [...sources] };
|
|
1077
|
+
}
|
|
1078
|
+
function controlFor(tsm, type, written) {
|
|
1079
|
+
const { TypeFlags } = tsm;
|
|
1080
|
+
const parts = type.isUnion() ? type.types : [type];
|
|
1081
|
+
const every = (flags) => parts.every((part) => part.flags & flags);
|
|
1082
|
+
if (every(TypeFlags.BooleanLike)) return { control: { type: "boolean" } };
|
|
1083
|
+
if (type.flags & TypeFlags.String) return { control: { type: "text" } };
|
|
1084
|
+
if (type.flags & TypeFlags.Number) return { control: { type: "number" } };
|
|
1085
|
+
if (parts.length > 1 && every(TypeFlags.StringLiteral)) {
|
|
1086
|
+
const options = parts.map((part) => part.value);
|
|
1087
|
+
const order = written();
|
|
1088
|
+
const same = order?.length === options.length && options.every((option) => order.includes(option));
|
|
1089
|
+
return { options: same ? order : options };
|
|
1090
|
+
}
|
|
1091
|
+
return null;
|
|
1092
|
+
}
|
|
1093
|
+
function writtenOrder(tsm, checker, prop2) {
|
|
1094
|
+
const [declaration, ...others] = prop2.getDeclarations() ?? [];
|
|
1095
|
+
if (!declaration || others.length > 0) return null;
|
|
1096
|
+
if (!tsm.isPropertySignature(declaration) && !tsm.isPropertyDeclaration(declaration)) return null;
|
|
1097
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1098
|
+
const literals = (node) => {
|
|
1099
|
+
if (!node || seen.has(node)) return null;
|
|
1100
|
+
seen.add(node);
|
|
1101
|
+
if (tsm.isParenthesizedTypeNode(node)) return literals(node.type);
|
|
1102
|
+
if (tsm.isLiteralTypeNode(node)) {
|
|
1103
|
+
if (node.literal.kind === tsm.SyntaxKind.NullKeyword) return [];
|
|
1104
|
+
return tsm.isStringLiteralLike(node.literal) ? [node.literal.text] : null;
|
|
1105
|
+
}
|
|
1106
|
+
if (node.kind === tsm.SyntaxKind.UndefinedKeyword) return [];
|
|
1107
|
+
if (tsm.isUnionTypeNode(node)) {
|
|
1108
|
+
const parts = node.types.map(literals);
|
|
1109
|
+
return parts.every((part) => part !== null) ? [...new Set(parts.flat())] : null;
|
|
1110
|
+
}
|
|
1111
|
+
if (tsm.isTypeReferenceNode(node) && !node.typeArguments) {
|
|
1112
|
+
const named = checker.getSymbolAtLocation(node.typeName);
|
|
1113
|
+
const target = named && named.flags & tsm.SymbolFlags.Alias ? checker.getAliasedSymbol(named) : named;
|
|
1114
|
+
const [alias, ...rest] = target?.getDeclarations() ?? [];
|
|
1115
|
+
if (alias && rest.length === 0 && tsm.isTypeAliasDeclaration(alias) && !alias.typeParameters) {
|
|
1116
|
+
return literals(alias.type);
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
return null;
|
|
1120
|
+
};
|
|
1121
|
+
return literals(declaration.type);
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
// src/vite/propControls.ts
|
|
1125
|
+
function propControls(stories, options = {}) {
|
|
1126
|
+
let root = process.cwd();
|
|
1127
|
+
let reader = null;
|
|
1128
|
+
return {
|
|
1129
|
+
name: "weaselforge:prop-controls",
|
|
1130
|
+
configResolved(config) {
|
|
1131
|
+
root = config.root;
|
|
1132
|
+
reader = null;
|
|
1133
|
+
},
|
|
1134
|
+
async transform(code, id) {
|
|
1135
|
+
if (!stories().includes(id)) return void 0;
|
|
1136
|
+
const props = await (reader ??= createPropReader(root, stories, options));
|
|
1137
|
+
let table = null;
|
|
1138
|
+
try {
|
|
1139
|
+
table = props?.read(id) ?? null;
|
|
1140
|
+
} catch (err) {
|
|
1141
|
+
this.warn(`could not read the props for ${id}: ${err instanceof Error ? err.message : String(err)}`);
|
|
1142
|
+
}
|
|
1143
|
+
if (!table || Object.keys(table.argTypes).length === 0) return void 0;
|
|
1144
|
+
for (const source of table.sources) this.addWatchFile(source);
|
|
1145
|
+
return { code: `${code}
|
|
1146
|
+
export const ${PROP_ARG_TYPES_EXPORT} = ${JSON.stringify(table.argTypes)};
|
|
1147
|
+
`, map: null };
|
|
1148
|
+
}
|
|
1149
|
+
};
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
// src/vite/servePages.ts
|
|
1153
|
+
var PAGES2 = { "/": "shell-entry.js", "/index.html": "shell-entry.js", "/frame.html": "frame-entry.js" };
|
|
1154
|
+
function servePages(server, base, title) {
|
|
1155
|
+
server.middlewares.use((req, res, next) => {
|
|
1156
|
+
const url = req.url ?? "/";
|
|
1157
|
+
const path = url.split("?")[0] ?? "/";
|
|
1158
|
+
const prefix = base.endsWith("/") ? base.slice(0, -1) : base;
|
|
1159
|
+
const page = path === prefix ? "/" : path.startsWith(`${prefix}/`) ? path.slice(prefix.length) : null;
|
|
1160
|
+
const entry = req.method === "GET" && page !== null ? PAGES2[page] : void 0;
|
|
1161
|
+
if (!entry) return next();
|
|
1162
|
+
server.transformIndexHtml(url, html(entry, "serve", title())).then(
|
|
1163
|
+
(doc) => {
|
|
1164
|
+
res.statusCode = 200;
|
|
1165
|
+
res.setHeader("Content-Type", "text/html");
|
|
1166
|
+
res.end(doc);
|
|
1167
|
+
},
|
|
1168
|
+
(err) => next(err)
|
|
653
1169
|
);
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
1170
|
+
});
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
// src/vite/shellTitle.ts
|
|
1174
|
+
function shellTitle(code, file) {
|
|
1175
|
+
let program;
|
|
1176
|
+
try {
|
|
1177
|
+
program = parseFile(code, file);
|
|
1178
|
+
} catch {
|
|
1179
|
+
return void 0;
|
|
1180
|
+
}
|
|
1181
|
+
const bindings = /* @__PURE__ */ new Map();
|
|
1182
|
+
let config;
|
|
1183
|
+
for (const stmt of program.body) {
|
|
1184
|
+
if (stmt.type === "VariableDeclaration") {
|
|
1185
|
+
for (const d of stmt.declarations) if (d.id.type === "Identifier" && d.init) bindings.set(d.id.name, d.init);
|
|
1186
|
+
}
|
|
1187
|
+
if (stmt.type === "ExportDefaultDeclaration") config = stmt.declaration;
|
|
1188
|
+
if (stmt.type !== "ExportNamedDeclaration" || stmt.source) continue;
|
|
1189
|
+
for (const spec of stmt.specifiers) {
|
|
1190
|
+
if (spec.type === "ExportSpecifier" && exportedName(spec.exported) === "default") config = spec.local;
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
const wrapper = calleeOf(config, bindings);
|
|
1194
|
+
return stringOf(prop(objectOf(config, bindings, new Set(wrapper ? [wrapper] : [])), "title"), bindings);
|
|
1195
|
+
}
|
|
1196
|
+
var SHIMS = [
|
|
1197
|
+
[/^@?storybook\/preview-api$/, "@weasel-js/forge/preview-api"],
|
|
1198
|
+
[/^@?storybook\/test$/, "@weasel-js/forge/play"]
|
|
1199
|
+
];
|
|
1200
|
+
function storybookShims() {
|
|
1201
|
+
return {
|
|
1202
|
+
name: "weaselforge:storybook-shims",
|
|
1203
|
+
enforce: "pre",
|
|
1204
|
+
resolveId(id) {
|
|
1205
|
+
const hit = SHIMS.find(([pattern]) => pattern.test(id));
|
|
1206
|
+
if (!hit) return void 0;
|
|
1207
|
+
return this.resolve(hit[1], fileURLToPath(import.meta.url), { skipSelf: true });
|
|
658
1208
|
}
|
|
659
|
-
return { wrappers, reads, unresolved };
|
|
660
1209
|
};
|
|
661
1210
|
}
|
|
1211
|
+
var WAIT_MS = 5e3;
|
|
1212
|
+
var REWRITE_MS = 250;
|
|
1213
|
+
var watchDir = (dir, onEvent) => {
|
|
1214
|
+
const watcher = watch(dir, onEvent);
|
|
1215
|
+
watcher.on("error", () => {
|
|
1216
|
+
});
|
|
1217
|
+
watcher.unref();
|
|
1218
|
+
return () => watcher.close();
|
|
1219
|
+
};
|
|
1220
|
+
function fileEventsArrive({ waitMs = WAIT_MS, watch = watchDir } = {}) {
|
|
1221
|
+
const dir = mkdtempSync(join(tmpdir(), "forge-watch-"));
|
|
1222
|
+
return new Promise((resolve5) => {
|
|
1223
|
+
let settled2 = false;
|
|
1224
|
+
let writes = 0;
|
|
1225
|
+
let stop;
|
|
1226
|
+
let rewrite;
|
|
1227
|
+
let deadline;
|
|
1228
|
+
const settle = (arrived) => {
|
|
1229
|
+
if (settled2) return;
|
|
1230
|
+
settled2 = true;
|
|
1231
|
+
clearInterval(rewrite);
|
|
1232
|
+
clearTimeout(deadline);
|
|
1233
|
+
stop?.();
|
|
1234
|
+
rmSync(dir, { recursive: true, force: true });
|
|
1235
|
+
resolve5(arrived);
|
|
1236
|
+
};
|
|
1237
|
+
const write = () => writeFileSync(join(dir, "probe"), String(writes++));
|
|
1238
|
+
try {
|
|
1239
|
+
stop = watch(dir, () => settle(true));
|
|
1240
|
+
write();
|
|
1241
|
+
} catch {
|
|
1242
|
+
settle(false);
|
|
1243
|
+
return;
|
|
1244
|
+
}
|
|
1245
|
+
rewrite = setInterval(write, REWRITE_MS);
|
|
1246
|
+
deadline = setTimeout(() => settle(false), waitMs);
|
|
1247
|
+
rewrite.unref();
|
|
1248
|
+
deadline.unref();
|
|
1249
|
+
});
|
|
1250
|
+
}
|
|
1251
|
+
var SILENT = `[forge] a file written ${WAIT_MS / 1e3} s ago has not been reported by the OS file watcher, so an edit will not reach the page until file events arrive again. \`server.watch: { usePolling: true }\` serves edits without them. On macOS, look for \`fseventsd\` held at 100% CPU.`;
|
|
1252
|
+
async function warnWhenWatchIsSilent(server, probe = fileEventsArrive) {
|
|
1253
|
+
const { watch } = server.config.server;
|
|
1254
|
+
if (watch === null || watch?.usePolling) return;
|
|
1255
|
+
if (!await probe()) server.config.logger.warn(SILENT);
|
|
1256
|
+
}
|
|
662
1257
|
|
|
663
1258
|
// src/vite/plugin.ts
|
|
664
1259
|
var PREFIX = "virtual:forge/";
|
|
665
1260
|
var MODULES = /* @__PURE__ */ new Set(["index.js", "importers.js", "deps.js", "frame-config.js", "shell-config.js", "shell-entry.js", "frame-entry.js"]);
|
|
666
|
-
var
|
|
1261
|
+
var SCRIPT2 = /\.[cm]?[jt]sx?$/;
|
|
667
1262
|
function forge(options) {
|
|
668
1263
|
let root = process.cwd();
|
|
669
1264
|
let base = "/";
|
|
@@ -682,9 +1277,9 @@ function forge(options) {
|
|
|
682
1277
|
};
|
|
683
1278
|
const matches = (file) => {
|
|
684
1279
|
const path = relative(root, file).split(sep).join("/");
|
|
685
|
-
return !path.split("/").includes("node_modules") && options.stories
|
|
1280
|
+
return !path.split("/").includes("node_modules") && storyGlobOf(path, options.stories) !== void 0;
|
|
686
1281
|
};
|
|
687
|
-
const read = (file) =>
|
|
1282
|
+
const read = (file) => indexStory(readFileSync(file, "utf8"), file, { root, stories: options.stories }, wrappersOf.get(file));
|
|
688
1283
|
const logError = (err) => logger?.error(`[forge] ${err instanceof Error ? err.message : String(err)}`);
|
|
689
1284
|
const files = () => {
|
|
690
1285
|
byFile ??= new Map(
|
|
@@ -711,9 +1306,9 @@ function forge(options) {
|
|
|
711
1306
|
} catch {
|
|
712
1307
|
continue;
|
|
713
1308
|
}
|
|
714
|
-
const before = wrappersOf.get(file)
|
|
1309
|
+
const before = wrappersOf.get(file);
|
|
715
1310
|
wrappersOf.set(file, next);
|
|
716
|
-
if (before
|
|
1311
|
+
if (sameResolution(before, next)) continue;
|
|
717
1312
|
try {
|
|
718
1313
|
current.set(file, read(file));
|
|
719
1314
|
} catch (err) {
|
|
@@ -722,6 +1317,26 @@ function forge(options) {
|
|
|
722
1317
|
}
|
|
723
1318
|
};
|
|
724
1319
|
const depGraph = () => deps ??= createDepGraph({ root, read: (file) => readFileSync(file, "utf8") });
|
|
1320
|
+
let history = null;
|
|
1321
|
+
async function graphOf(resolveImport) {
|
|
1322
|
+
const graph = await depGraph().build(index(), resolveImport);
|
|
1323
|
+
const sources = Object.values(graph).flatMap((entry) => entry.source === null ? [] : [entry.source]);
|
|
1324
|
+
const histories = await (history ??= createHistoryReader(root)).read(sources);
|
|
1325
|
+
for (const entry of Object.values(graph)) {
|
|
1326
|
+
const found = entry.source === null ? void 0 : histories[entry.source];
|
|
1327
|
+
if (found) entry.history = found;
|
|
1328
|
+
}
|
|
1329
|
+
return graph;
|
|
1330
|
+
}
|
|
1331
|
+
const titleUnder = (at) => {
|
|
1332
|
+
if (!options.shellConfig) return void 0;
|
|
1333
|
+
const file = resolve(at, options.shellConfig);
|
|
1334
|
+
try {
|
|
1335
|
+
return shellTitle(readFileSync(file, "utf8"), file);
|
|
1336
|
+
} catch {
|
|
1337
|
+
return void 0;
|
|
1338
|
+
}
|
|
1339
|
+
};
|
|
725
1340
|
const configModule = (path) => path ? `export { default } from ${JSON.stringify(resolve(root, path))};
|
|
726
1341
|
` : "export default {};\n";
|
|
727
1342
|
const modules = {
|
|
@@ -736,48 +1351,8 @@ ${lines.join("\n")}
|
|
|
736
1351
|
},
|
|
737
1352
|
"frame-config.js": () => configModule(options.frameConfig),
|
|
738
1353
|
"shell-config.js": () => configModule(options.shellConfig),
|
|
739
|
-
"shell-entry.js": () =>
|
|
740
|
-
|
|
741
|
-
import '@weasel-js/forge/shell.css';
|
|
742
|
-
import index from 'virtual:forge/index.js';
|
|
743
|
-
import importers from 'virtual:forge/importers.js';
|
|
744
|
-
import config from 'virtual:forge/shell-config.js';
|
|
745
|
-
import setup from 'virtual:forge/frame-config.js';
|
|
746
|
-
|
|
747
|
-
const workshop = mountWorkshop({
|
|
748
|
-
index,
|
|
749
|
-
importers,
|
|
750
|
-
setup,
|
|
751
|
-
config,
|
|
752
|
-
frameUrl: ${JSON.stringify(`${base}frame.html`)},
|
|
753
|
-
stories: ${JSON.stringify(options.stories)},
|
|
754
|
-
});
|
|
755
|
-
import.meta.hot?.on('forge:index', (next) => workshop.setIndex(next));
|
|
756
|
-
// Read from source on first request, so the graph costs nothing until the page is up.
|
|
757
|
-
import('virtual:forge/deps.js').then((m) => workshop.setDependencies(m.default));
|
|
758
|
-
import.meta.hot?.on('forge:deps', (next) => workshop.setDependencies(next));
|
|
759
|
-
// A story edit reaches here as an update of the importers module, whose fresh import() URLs carry the new
|
|
760
|
-
// timestamps; the file it names arrives just before, so the reload waits for the importers that can fetch it.
|
|
761
|
-
const stale = new Set();
|
|
762
|
-
import.meta.hot?.on('forge:story', ({ file }) => stale.add(file));
|
|
763
|
-
import.meta.hot?.accept('virtual:forge/importers.js', (next) => {
|
|
764
|
-
if (!next) return;
|
|
765
|
-
workshop.setImporters(next.default);
|
|
766
|
-
for (const file of stale) workshop.reloadStory(file);
|
|
767
|
-
stale.clear();
|
|
768
|
-
});
|
|
769
|
-
`,
|
|
770
|
-
"frame-entry.js": () => `import { mountFrame } from '@weasel-js/forge/frame';
|
|
771
|
-
import '@weasel-js/forge/frame.css';
|
|
772
|
-
import index from 'virtual:forge/index.js';
|
|
773
|
-
import importers from 'virtual:forge/importers.js';
|
|
774
|
-
import setup from 'virtual:forge/frame-config.js';
|
|
775
|
-
|
|
776
|
-
mountFrame({ index, importers, setup });
|
|
777
|
-
// The importers module is shared with the workshop page, which accepts a story edit in place; a frame document
|
|
778
|
-
// showing the old module reloads instead, and without this the edit would dead-end here and reload every page.
|
|
779
|
-
import.meta.hot?.accept('virtual:forge/importers.js', () => location.reload());
|
|
780
|
-
`
|
|
1354
|
+
"shell-entry.js": () => shellEntry(base, options.stories),
|
|
1355
|
+
"frame-entry.js": () => frameEntry((options.frameStyles ?? []).map((path) => resolve(root, path)))
|
|
781
1356
|
};
|
|
782
1357
|
const invalidate = (server, names) => {
|
|
783
1358
|
for (const env of Object.values(server.environments)) {
|
|
@@ -789,9 +1364,9 @@ import.meta.hot?.accept('virtual:forge/importers.js', () => location.reload());
|
|
|
789
1364
|
};
|
|
790
1365
|
function redeps(server) {
|
|
791
1366
|
if (depsSent === null || depsPending) return;
|
|
792
|
-
const
|
|
1367
|
+
const resolve5 = async (spec, importer) => (await server.environments.client.pluginContainer.resolveId(spec, importer))?.id ?? null;
|
|
793
1368
|
depsPending = Promise.resolve().then(async () => {
|
|
794
|
-
const graph = await
|
|
1369
|
+
const graph = await graphOf(resolve5);
|
|
795
1370
|
const json = JSON.stringify(graph);
|
|
796
1371
|
if (json === depsSent) return;
|
|
797
1372
|
depsSent = json;
|
|
@@ -801,13 +1376,28 @@ import.meta.hot?.accept('virtual:forge/importers.js', () => location.reload());
|
|
|
801
1376
|
depsPending = null;
|
|
802
1377
|
});
|
|
803
1378
|
}
|
|
1379
|
+
const storiesReaching = (changed) => {
|
|
1380
|
+
const current = files();
|
|
1381
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1382
|
+
const reached = /* @__PURE__ */ new Set();
|
|
1383
|
+
const climb = (mod) => {
|
|
1384
|
+
if (seen.has(mod)) return;
|
|
1385
|
+
seen.add(mod);
|
|
1386
|
+
if (mod.file && current.has(mod.file)) reached.add(mod.file);
|
|
1387
|
+
if (mod.isSelfAccepting && !(mod.file && SCRIPT2.test(mod.file))) return;
|
|
1388
|
+
for (const importer of mod.importers) climb(importer);
|
|
1389
|
+
};
|
|
1390
|
+
for (const mod of changed) climb(mod);
|
|
1391
|
+
return [...reached];
|
|
1392
|
+
};
|
|
804
1393
|
async function reindex(server, file, event) {
|
|
805
1394
|
const current = files();
|
|
806
|
-
if (event === "change" && current.has(file)) server.ws.send({ type: "custom", event: "forge:story", data: { file } });
|
|
807
1395
|
const own = event === "add" ? matches(file) : current.has(file);
|
|
808
1396
|
const through = [...wrappersOf].filter(([story, { reads, unresolved }]) => story !== file && (reads.has(file) || event === "add" && unresolved)).map(([story]) => story);
|
|
809
1397
|
if (!own && through.length === 0) return false;
|
|
810
1398
|
const before = JSON.stringify(index());
|
|
1399
|
+
const importable = () => JSON.stringify([...current].filter(([, entries]) => entries.length > 0).map(([path]) => path));
|
|
1400
|
+
const importableBefore = importable();
|
|
811
1401
|
if (own && event === "unlink") {
|
|
812
1402
|
current.delete(file);
|
|
813
1403
|
wrappersOf.delete(file);
|
|
@@ -825,10 +1415,13 @@ import.meta.hot?.accept('virtual:forge/importers.js', () => location.reload());
|
|
|
825
1415
|
if (JSON.stringify(next) === before) return false;
|
|
826
1416
|
invalidate(server, ["index.js", "importers.js"]);
|
|
827
1417
|
server.ws.send({ type: "custom", event: "forge:index", data: next });
|
|
1418
|
+
const importers = server.environments.client.moduleGraph.getModuleById(`\0${PREFIX}importers.js`);
|
|
1419
|
+
if (importers && importable() !== importableBefore) await server.environments.client.reloadModule(importers);
|
|
828
1420
|
return true;
|
|
829
1421
|
}
|
|
830
1422
|
return [
|
|
831
1423
|
...options.storybookShims === false ? [] : [storybookShims()],
|
|
1424
|
+
...options.propControls === false ? [] : [propControls(() => [...files().keys()], options.propControls === true ? void 0 : options.propControls)],
|
|
832
1425
|
{
|
|
833
1426
|
name: "weaselforge",
|
|
834
1427
|
config(config, { command }) {
|
|
@@ -840,7 +1433,7 @@ import.meta.hot?.accept('virtual:forge/importers.js', () => location.reload());
|
|
|
840
1433
|
];
|
|
841
1434
|
const optimizeDeps = { entries };
|
|
842
1435
|
if (command !== "build") return { optimizeDeps };
|
|
843
|
-
return { optimizeDeps, build: { rolldownOptions: { input: writePages(at) } } };
|
|
1436
|
+
return { optimizeDeps, build: { rolldownOptions: { input: writePages(at, titleUnder(at)) } } };
|
|
844
1437
|
},
|
|
845
1438
|
generateBundle: {
|
|
846
1439
|
order: "post",
|
|
@@ -856,6 +1449,7 @@ import.meta.hot?.accept('virtual:forge/importers.js', () => location.reload());
|
|
|
856
1449
|
wrappersOf.clear();
|
|
857
1450
|
settledAll = null;
|
|
858
1451
|
deps = null;
|
|
1452
|
+
history = null;
|
|
859
1453
|
depsSent = null;
|
|
860
1454
|
},
|
|
861
1455
|
resolveId(id) {
|
|
@@ -869,17 +1463,22 @@ import.meta.hot?.accept('virtual:forge/importers.js', () => location.reload());
|
|
|
869
1463
|
await settledAll;
|
|
870
1464
|
}
|
|
871
1465
|
if (name !== "deps.js") return modules[name]?.();
|
|
872
|
-
const graph = await
|
|
1466
|
+
const graph = await graphOf(async (spec, importer) => (await this.resolve(spec, importer))?.id ?? null);
|
|
873
1467
|
depsSent = JSON.stringify(graph);
|
|
874
1468
|
return `export default ${depsSent};
|
|
875
1469
|
`;
|
|
876
1470
|
},
|
|
1471
|
+
hotUpdate({ modules: modules2, server }) {
|
|
1472
|
+
if (this.environment.name !== "client") return;
|
|
1473
|
+
for (const file of storiesReaching(modules2)) server.ws.send({ type: "custom", event: "forge:story", data: { file } });
|
|
1474
|
+
},
|
|
877
1475
|
configureServer(server) {
|
|
878
1476
|
server.watcher.add([...files().keys()]);
|
|
1477
|
+
void warnWhenWatchIsSilent(server);
|
|
879
1478
|
for (const event of ["add", "change", "unlink"]) {
|
|
880
1479
|
server.watcher.on(event, (path) => {
|
|
881
1480
|
const file = resolve(path);
|
|
882
|
-
const added = event === "add" &&
|
|
1481
|
+
const added = event === "add" && SCRIPT2.test(file) && !file.split(sep).includes("node_modules");
|
|
883
1482
|
if (added) depGraph().reset();
|
|
884
1483
|
const read2 = added || event !== "add" && depGraph().invalidate(file);
|
|
885
1484
|
reindex(server, file, event).then((indexed) => {
|
|
@@ -887,22 +1486,7 @@ import.meta.hot?.accept('virtual:forge/importers.js', () => location.reload());
|
|
|
887
1486
|
}, logError);
|
|
888
1487
|
});
|
|
889
1488
|
}
|
|
890
|
-
server
|
|
891
|
-
const url = req.url ?? "/";
|
|
892
|
-
const path = url.split("?")[0] ?? "/";
|
|
893
|
-
const prefix = base.endsWith("/") ? base.slice(0, -1) : base;
|
|
894
|
-
const page = path === prefix ? "/" : path.startsWith(`${prefix}/`) ? path.slice(prefix.length) : null;
|
|
895
|
-
const entry = req.method === "GET" && page !== null ? PAGES2[page] : void 0;
|
|
896
|
-
if (!entry) return next();
|
|
897
|
-
server.transformIndexHtml(url, html(entry)).then(
|
|
898
|
-
(doc) => {
|
|
899
|
-
res.statusCode = 200;
|
|
900
|
-
res.setHeader("Content-Type", "text/html");
|
|
901
|
-
res.end(doc);
|
|
902
|
-
},
|
|
903
|
-
(err) => next(err)
|
|
904
|
-
);
|
|
905
|
-
});
|
|
1489
|
+
servePages(server, base, () => titleUnder(root));
|
|
906
1490
|
}
|
|
907
1491
|
}
|
|
908
1492
|
];
|
|
@@ -914,7 +1498,7 @@ function forgeTest(options) {
|
|
|
914
1498
|
if (id.startsWith("\0") || id.includes("?")) return false;
|
|
915
1499
|
const path = relative(root, id).split(sep).join("/");
|
|
916
1500
|
if (path.startsWith("../") || path.split("/").includes("node_modules")) return false;
|
|
917
|
-
return options.stories
|
|
1501
|
+
return storyGlobOf(path, options.stories) !== void 0;
|
|
918
1502
|
};
|
|
919
1503
|
return [
|
|
920
1504
|
...options.storybookShims === false ? [] : [storybookShims()],
|
|
@@ -933,9 +1517,14 @@ function forgeTest(options) {
|
|
|
933
1517
|
configResolved(config) {
|
|
934
1518
|
root = config.root;
|
|
935
1519
|
},
|
|
936
|
-
transform(code, id) {
|
|
1520
|
+
async transform(code, id) {
|
|
937
1521
|
if (!matches(id)) return void 0;
|
|
938
|
-
const entries =
|
|
1522
|
+
const { entries } = await indexStoryResolved(
|
|
1523
|
+
code,
|
|
1524
|
+
id,
|
|
1525
|
+
{ root, stories: options.stories },
|
|
1526
|
+
async (spec, importer) => (await this.resolve(spec, importer))?.id ?? null
|
|
1527
|
+
);
|
|
939
1528
|
const title = entries[0]?.title;
|
|
940
1529
|
if (title === void 0) return void 0;
|
|
941
1530
|
const q = (value) => JSON.stringify(value);
|