@onlook/storybook-plugin 0.4.0-beta.15 → 0.4.0-beta.16
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/dist/index.js +167 -27
- package/dist/preset/index.js +167 -27
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';
|
|
2
|
-
import
|
|
2
|
+
import path8, { dirname, join, relative } from 'path';
|
|
3
3
|
import crypto, { createHash } from 'crypto';
|
|
4
4
|
import fs5, { existsSync } from 'fs';
|
|
5
5
|
import { fileURLToPath } from 'url';
|
|
@@ -61,7 +61,7 @@ function envContainmentPlugin(options) {
|
|
|
61
61
|
const resolveOutcomes = /* @__PURE__ */ new Map();
|
|
62
62
|
const roots = /* @__PURE__ */ new Set([normalizeSlashes(process.cwd())]);
|
|
63
63
|
const matchTarget = (absPath) => {
|
|
64
|
-
const norm = normalizeSlashes(
|
|
64
|
+
const norm = normalizeSlashes(path8.normalize(absPath));
|
|
65
65
|
for (const rel of byModule.keys()) {
|
|
66
66
|
for (const root of roots) {
|
|
67
67
|
const target = `${root}/${rel}`;
|
|
@@ -96,10 +96,10 @@ function envContainmentPlugin(options) {
|
|
|
96
96
|
if (source.startsWith("\0")) return null;
|
|
97
97
|
const cleaned = stripQuery(source);
|
|
98
98
|
let direct = null;
|
|
99
|
-
if (
|
|
99
|
+
if (path8.isAbsolute(cleaned)) {
|
|
100
100
|
direct = cleaned;
|
|
101
101
|
} else if (importer && (cleaned.startsWith("./") || cleaned.startsWith("../"))) {
|
|
102
|
-
direct =
|
|
102
|
+
direct = path8.resolve(path8.dirname(stripQuery(importer)), cleaned);
|
|
103
103
|
}
|
|
104
104
|
if (direct) {
|
|
105
105
|
const rel = matchTarget(direct);
|
|
@@ -108,7 +108,7 @@ function envContainmentPlugin(options) {
|
|
|
108
108
|
}
|
|
109
109
|
if (!specifierMightMatch(cleaned)) return null;
|
|
110
110
|
if (typeof this.resolve !== "function") return null;
|
|
111
|
-
const importerDir = importer ?
|
|
111
|
+
const importerDir = importer ? path8.dirname(stripQuery(importer)) : "";
|
|
112
112
|
const outcomeKey = `${importerDir}|${source}`;
|
|
113
113
|
const memoized = resolveOutcomes.get(outcomeKey);
|
|
114
114
|
if (memoized !== void 0) return memoized;
|
|
@@ -328,7 +328,7 @@ function componentLocPlugin(options = {}) {
|
|
|
328
328
|
sourceFilename: filepath
|
|
329
329
|
});
|
|
330
330
|
let mutated = false;
|
|
331
|
-
const relativePath =
|
|
331
|
+
const relativePath = path8.relative(root, filepath);
|
|
332
332
|
traverse(ast, {
|
|
333
333
|
JSXElement(nodePath) {
|
|
334
334
|
const opening = nodePath.node.openingElement;
|
|
@@ -387,9 +387,9 @@ function fastRefreshTolerantExportsPlugin() {
|
|
|
387
387
|
}
|
|
388
388
|
};
|
|
389
389
|
}
|
|
390
|
-
var CACHE_DIR =
|
|
391
|
-
var SCREENSHOTS_DIR =
|
|
392
|
-
var MANIFEST_PATH =
|
|
390
|
+
var CACHE_DIR = path8.join(process.cwd(), ".storybook-cache");
|
|
391
|
+
var SCREENSHOTS_DIR = path8.join(CACHE_DIR, "screenshots");
|
|
392
|
+
var MANIFEST_PATH = path8.join(CACHE_DIR, "manifest.json");
|
|
393
393
|
var VIEWPORT_WIDTH = 1920;
|
|
394
394
|
var VIEWPORT_HEIGHT = 1080;
|
|
395
395
|
var MIN_COMPONENT_WIDTH = 420;
|
|
@@ -580,8 +580,8 @@ async function detectRenderState(page, opts = {}) {
|
|
|
580
580
|
}
|
|
581
581
|
}
|
|
582
582
|
function getScreenshotPath(storyId, theme) {
|
|
583
|
-
const storyDir =
|
|
584
|
-
return
|
|
583
|
+
const storyDir = path8.join(SCREENSHOTS_DIR, storyId);
|
|
584
|
+
return path8.join(storyDir, `${theme}.png`);
|
|
585
585
|
}
|
|
586
586
|
async function captureScreenshotBuffer(storyId, theme, width = VIEWPORT_WIDTH, height = VIEWPORT_HEIGHT, storybookUrl = "http://localhost:6006", loadTimeoutMs = 9e4, renderTimeoutMs = 2e4) {
|
|
587
587
|
const browser = await getBrowser();
|
|
@@ -669,7 +669,7 @@ async function captureScreenshotBuffer(storyId, theme, width = VIEWPORT_WIDTH, h
|
|
|
669
669
|
async function generateScreenshot(storyId, theme, storybookUrl = "http://localhost:6006", timeoutMs = 3e4) {
|
|
670
670
|
try {
|
|
671
671
|
ensureCacheDirectories();
|
|
672
|
-
const storyDir =
|
|
672
|
+
const storyDir = path8.join(SCREENSHOTS_DIR, storyId);
|
|
673
673
|
if (!fs5.existsSync(storyDir)) {
|
|
674
674
|
fs5.mkdirSync(storyDir, { recursive: true });
|
|
675
675
|
}
|
|
@@ -790,7 +790,7 @@ async function fetchStorybookIndex() {
|
|
|
790
790
|
}
|
|
791
791
|
function getStoriesForFile(filePath) {
|
|
792
792
|
if (!cachedIndex) return [];
|
|
793
|
-
const fileName =
|
|
793
|
+
const fileName = path8.basename(filePath);
|
|
794
794
|
return Object.values(cachedIndex.entries).filter((entry) => entry.type === "story" && entry.importPath.endsWith(fileName)).map((entry) => entry.id);
|
|
795
795
|
}
|
|
796
796
|
async function regenerateScreenshotsForFiles(files) {
|
|
@@ -985,8 +985,8 @@ function softStoryRerenderPlugin() {
|
|
|
985
985
|
preview.onStoriesChanged({ importFn: newModule.importFn });
|
|
986
986
|
}
|
|
987
987
|
});`;
|
|
988
|
-
const out = code.replace(pattern, (_match, quote,
|
|
989
|
-
return replacement.replace("$QUOTE$$PATH$$QUOTE$", `${quote}${
|
|
988
|
+
const out = code.replace(pattern, (_match, quote, path9) => {
|
|
989
|
+
return replacement.replace("$QUOTE$$PATH$$QUOTE$", `${quote}${path9}${quote}`);
|
|
990
990
|
});
|
|
991
991
|
return out === code ? null : { code: out, map: null };
|
|
992
992
|
}
|
|
@@ -997,14 +997,14 @@ function resolveTsconfigAliases(root) {
|
|
|
997
997
|
try {
|
|
998
998
|
const config = readTsconfigAliasConfig(root);
|
|
999
999
|
if (!config) return [];
|
|
1000
|
-
const base =
|
|
1000
|
+
const base = path8.resolve(root, config.baseUrl ?? ".");
|
|
1001
1001
|
const aliases = [];
|
|
1002
1002
|
for (const [key, targets] of Object.entries(config.paths)) {
|
|
1003
1003
|
const target = targets[0];
|
|
1004
1004
|
if (!target) continue;
|
|
1005
1005
|
if (key.endsWith("/*") && target.endsWith("/*")) {
|
|
1006
1006
|
const findPrefix = key.slice(0, -1);
|
|
1007
|
-
const targetDir =
|
|
1007
|
+
const targetDir = path8.resolve(base, target.slice(0, -2));
|
|
1008
1008
|
aliases.push({
|
|
1009
1009
|
find: new RegExp(`^${escapeRegExp(findPrefix)}`),
|
|
1010
1010
|
replacement: `${targetDir}/`
|
|
@@ -1012,7 +1012,7 @@ function resolveTsconfigAliases(root) {
|
|
|
1012
1012
|
} else if (!key.includes("*") && !target.includes("*")) {
|
|
1013
1013
|
aliases.push({
|
|
1014
1014
|
find: new RegExp(`^${escapeRegExp(key)}$`),
|
|
1015
|
-
replacement:
|
|
1015
|
+
replacement: path8.resolve(base, target)
|
|
1016
1016
|
});
|
|
1017
1017
|
}
|
|
1018
1018
|
}
|
|
@@ -1023,7 +1023,7 @@ function resolveTsconfigAliases(root) {
|
|
|
1023
1023
|
}
|
|
1024
1024
|
function readTsconfigAliasConfig(root) {
|
|
1025
1025
|
for (const candidate of TSCONFIG_CANDIDATES) {
|
|
1026
|
-
const abs =
|
|
1026
|
+
const abs = path8.join(root, candidate);
|
|
1027
1027
|
let text;
|
|
1028
1028
|
try {
|
|
1029
1029
|
text = fs5.readFileSync(abs, "utf-8");
|
|
@@ -1104,6 +1104,120 @@ function findGitRoot(startPath) {
|
|
|
1104
1104
|
}
|
|
1105
1105
|
return null;
|
|
1106
1106
|
}
|
|
1107
|
+
var STORY_FILE_PATTERN = /\.stories\.(js|jsx|mjs|ts|tsx|mdx)$/;
|
|
1108
|
+
function isStoryFile(file) {
|
|
1109
|
+
return STORY_FILE_PATTERN.test(file);
|
|
1110
|
+
}
|
|
1111
|
+
function missingFromIndex(index, files) {
|
|
1112
|
+
const importPaths = [];
|
|
1113
|
+
for (const entry of Object.values(index.entries ?? {})) {
|
|
1114
|
+
if (typeof entry.importPath === "string") importPaths.push(entry.importPath);
|
|
1115
|
+
}
|
|
1116
|
+
return files.filter((file) => {
|
|
1117
|
+
const base = path8.basename(file);
|
|
1118
|
+
return !importPaths.some((importPath) => importPath.endsWith(base));
|
|
1119
|
+
});
|
|
1120
|
+
}
|
|
1121
|
+
function portFromArgv(argv) {
|
|
1122
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
1123
|
+
const arg = argv[i];
|
|
1124
|
+
if (arg === void 0) continue;
|
|
1125
|
+
let raw;
|
|
1126
|
+
if (arg === "-p" || arg === "--port") {
|
|
1127
|
+
raw = argv[i + 1];
|
|
1128
|
+
} else {
|
|
1129
|
+
raw = /^(?:-p|--port)=(\d+)$/.exec(arg)?.[1];
|
|
1130
|
+
}
|
|
1131
|
+
if (raw === void 0) continue;
|
|
1132
|
+
const parsed = Number.parseInt(raw, 10);
|
|
1133
|
+
if (Number.isInteger(parsed) && parsed > 0 && parsed < 65536) return parsed;
|
|
1134
|
+
}
|
|
1135
|
+
return null;
|
|
1136
|
+
}
|
|
1137
|
+
async function defaultFetchIndex(port) {
|
|
1138
|
+
try {
|
|
1139
|
+
const res = await fetch(`http://localhost:${port}/index.json?_t=${Date.now()}`, {
|
|
1140
|
+
cache: "no-store",
|
|
1141
|
+
signal: AbortSignal.timeout(2e3)
|
|
1142
|
+
});
|
|
1143
|
+
if (!res.ok) return null;
|
|
1144
|
+
return await res.json();
|
|
1145
|
+
} catch {
|
|
1146
|
+
return null;
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
1150
|
+
function createStoryIndexBroadcaster({
|
|
1151
|
+
ports,
|
|
1152
|
+
notify,
|
|
1153
|
+
fetchIndex = defaultFetchIndex,
|
|
1154
|
+
debounceMs = 500,
|
|
1155
|
+
probeIntervalMs = 500,
|
|
1156
|
+
probeCeilingMs = 15e3
|
|
1157
|
+
}) {
|
|
1158
|
+
const candidatePorts = [...new Set(ports)];
|
|
1159
|
+
const pendingAdds = /* @__PURE__ */ new Set();
|
|
1160
|
+
let addDebounce = null;
|
|
1161
|
+
let unlinkDebounce = null;
|
|
1162
|
+
let probing = false;
|
|
1163
|
+
const scheduleProbe = () => {
|
|
1164
|
+
if (addDebounce) clearTimeout(addDebounce);
|
|
1165
|
+
addDebounce = setTimeout(() => {
|
|
1166
|
+
addDebounce = null;
|
|
1167
|
+
void probeThenNotify();
|
|
1168
|
+
}, debounceMs);
|
|
1169
|
+
};
|
|
1170
|
+
const probeThenNotify = async () => {
|
|
1171
|
+
if (probing) return;
|
|
1172
|
+
probing = true;
|
|
1173
|
+
const files = Array.from(pendingAdds);
|
|
1174
|
+
const deadline = Date.now() + probeCeilingMs;
|
|
1175
|
+
try {
|
|
1176
|
+
if (files.length === 0) return;
|
|
1177
|
+
while (Date.now() < deadline) {
|
|
1178
|
+
for (const candidate of candidatePorts) {
|
|
1179
|
+
const index = await fetchIndex(candidate);
|
|
1180
|
+
if (index && missingFromIndex(index, files).length === 0) {
|
|
1181
|
+
console.log(
|
|
1182
|
+
"[STORYBOOK_PLUGIN] Index caught up with new story files \u2014 broadcasting",
|
|
1183
|
+
{ files, port: candidate }
|
|
1184
|
+
);
|
|
1185
|
+
for (const file of files) pendingAdds.delete(file);
|
|
1186
|
+
notify();
|
|
1187
|
+
return;
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
await sleep(probeIntervalMs);
|
|
1191
|
+
}
|
|
1192
|
+
console.warn("[STORYBOOK_PLUGIN] Index probe hit ceiling \u2014 broadcasting anyway", {
|
|
1193
|
+
files,
|
|
1194
|
+
probeCeilingMs
|
|
1195
|
+
});
|
|
1196
|
+
for (const file of files) pendingAdds.delete(file);
|
|
1197
|
+
notify();
|
|
1198
|
+
} finally {
|
|
1199
|
+
probing = false;
|
|
1200
|
+
if (pendingAdds.size > 0) scheduleProbe();
|
|
1201
|
+
}
|
|
1202
|
+
};
|
|
1203
|
+
return {
|
|
1204
|
+
onStoryFileAdded(file) {
|
|
1205
|
+
if (!isStoryFile(file)) return;
|
|
1206
|
+
pendingAdds.add(file);
|
|
1207
|
+
scheduleProbe();
|
|
1208
|
+
},
|
|
1209
|
+
onStoryFileRemoved(file) {
|
|
1210
|
+
if (!isStoryFile(file)) return;
|
|
1211
|
+
pendingAdds.delete(file);
|
|
1212
|
+
if (unlinkDebounce) clearTimeout(unlinkDebounce);
|
|
1213
|
+
unlinkDebounce = setTimeout(() => {
|
|
1214
|
+
unlinkDebounce = null;
|
|
1215
|
+
console.log("[STORYBOOK_PLUGIN] Story file removed \u2014 broadcasting", { file });
|
|
1216
|
+
notify();
|
|
1217
|
+
}, debounceMs);
|
|
1218
|
+
}
|
|
1219
|
+
};
|
|
1220
|
+
}
|
|
1107
1221
|
|
|
1108
1222
|
// src/storybook-onlook-plugin.ts
|
|
1109
1223
|
function errorCode(error) {
|
|
@@ -1170,7 +1284,7 @@ var TAILWIND_ENTRY_CANDIDATES = [
|
|
|
1170
1284
|
var TAILWIND_DIRECTIVE_PATTERN = /@import\s+['"](?:tailwindcss|tailwindcss\/.+)['"]|@tailwind\s+(?:base|components|utilities)/;
|
|
1171
1285
|
function detectTailwindEntryCss(cwd) {
|
|
1172
1286
|
for (const rel of TAILWIND_ENTRY_CANDIDATES) {
|
|
1173
|
-
const abs =
|
|
1287
|
+
const abs = path8.join(cwd, rel);
|
|
1174
1288
|
try {
|
|
1175
1289
|
if (!fs5.existsSync(abs)) continue;
|
|
1176
1290
|
const head = fs5.readFileSync(abs, "utf-8").slice(0, 2048);
|
|
@@ -1307,7 +1421,7 @@ var serveMetadataAndScreenshots = (req, res, next) => {
|
|
|
1307
1421
|
return;
|
|
1308
1422
|
}
|
|
1309
1423
|
if (req.url?.startsWith("/screenshots/")) {
|
|
1310
|
-
const screenshotPath =
|
|
1424
|
+
const screenshotPath = path8.join(
|
|
1311
1425
|
process.cwd(),
|
|
1312
1426
|
".storybook-cache",
|
|
1313
1427
|
req.url.replace("/screenshots/", "screenshots/")
|
|
@@ -1328,7 +1442,7 @@ var serveMetadataAndScreenshots = (req, res, next) => {
|
|
|
1328
1442
|
`[STORYBOOK_PLUGIN] Generating screenshot on-demand: ${storyId}/${theme}`
|
|
1329
1443
|
);
|
|
1330
1444
|
captureScreenshotBuffer(storyId, theme).then(({ buffer }) => {
|
|
1331
|
-
const storyDir =
|
|
1445
|
+
const storyDir = path8.join(
|
|
1332
1446
|
process.cwd(),
|
|
1333
1447
|
".storybook-cache",
|
|
1334
1448
|
"screenshots",
|
|
@@ -1447,7 +1561,7 @@ function storybookOnlookPlugin(options = {}) {
|
|
|
1447
1561
|
configureServer(server) {
|
|
1448
1562
|
console.log("[STORYBOOK_PLUGIN] Configuring server middleware");
|
|
1449
1563
|
server.middlewares.use(serveMetadataAndScreenshots);
|
|
1450
|
-
const cachedManifestPath =
|
|
1564
|
+
const cachedManifestPath = path8.join(
|
|
1451
1565
|
process.cwd(),
|
|
1452
1566
|
".storybook-cache",
|
|
1453
1567
|
"manifest.json"
|
|
@@ -1466,7 +1580,7 @@ function storybookOnlookPlugin(options = {}) {
|
|
|
1466
1580
|
server.watcher.on("change", scheduleRefresh);
|
|
1467
1581
|
const storyGlobs = options.storyGlobs ?? ["**/*.stories.@(js|jsx|mjs|ts|tsx|mdx)"];
|
|
1468
1582
|
const resolvedStoryGlobs = storyGlobs.map(
|
|
1469
|
-
(glob) =>
|
|
1583
|
+
(glob) => path8.isAbsolute(glob) ? glob : path8.join(process.cwd(), glob)
|
|
1470
1584
|
);
|
|
1471
1585
|
if (resolvedStoryGlobs.length > 0) {
|
|
1472
1586
|
server.watcher.add(resolvedStoryGlobs);
|
|
@@ -1474,10 +1588,36 @@ function storybookOnlookPlugin(options = {}) {
|
|
|
1474
1588
|
storyGlobs: resolvedStoryGlobs
|
|
1475
1589
|
});
|
|
1476
1590
|
}
|
|
1591
|
+
const probeCandidatePorts = [
|
|
1592
|
+
...new Set(
|
|
1593
|
+
[portFromArgv(process.argv), port, server.config.server.port].filter(
|
|
1594
|
+
(p) => typeof p === "number"
|
|
1595
|
+
)
|
|
1596
|
+
)
|
|
1597
|
+
];
|
|
1598
|
+
console.log("[STORYBOOK_PLUGIN] Story add/unlink index broadcast enabled", {
|
|
1599
|
+
probeCandidatePorts
|
|
1600
|
+
});
|
|
1601
|
+
const indexBroadcaster = createStoryIndexBroadcaster({
|
|
1602
|
+
ports: probeCandidatePorts,
|
|
1603
|
+
notify: notifyOnbookIndexChanged
|
|
1604
|
+
});
|
|
1605
|
+
let indexBroadcastWatcherReady = false;
|
|
1606
|
+
server.watcher.once("ready", () => {
|
|
1607
|
+
indexBroadcastWatcherReady = true;
|
|
1608
|
+
});
|
|
1609
|
+
server.watcher.on("add", (file) => {
|
|
1610
|
+
if (!indexBroadcastWatcherReady) return;
|
|
1611
|
+
indexBroadcaster.onStoryFileAdded(file);
|
|
1612
|
+
});
|
|
1613
|
+
server.watcher.on("unlink", (file) => {
|
|
1614
|
+
if (!indexBroadcastWatcherReady) return;
|
|
1615
|
+
indexBroadcaster.onStoryFileRemoved(file);
|
|
1616
|
+
});
|
|
1477
1617
|
const tailwindEntryPath = (() => {
|
|
1478
1618
|
if (options.tailwindEntryCss === false) return null;
|
|
1479
1619
|
if (typeof options.tailwindEntryCss === "string") {
|
|
1480
|
-
return
|
|
1620
|
+
return path8.isAbsolute(options.tailwindEntryCss) ? options.tailwindEntryCss : path8.join(process.cwd(), options.tailwindEntryCss);
|
|
1481
1621
|
}
|
|
1482
1622
|
return detectTailwindEntryCss(process.cwd());
|
|
1483
1623
|
})();
|
|
@@ -1493,7 +1633,7 @@ function storybookOnlookPlugin(options = {}) {
|
|
|
1493
1633
|
});
|
|
1494
1634
|
server.watcher.on("add", (file) => {
|
|
1495
1635
|
if (!/\.tsx?$/.test(file)) return;
|
|
1496
|
-
const dir =
|
|
1636
|
+
const dir = path8.dirname(file);
|
|
1497
1637
|
const isNewDir = !seenDirs.has(dir);
|
|
1498
1638
|
seenDirs.add(dir);
|
|
1499
1639
|
if (!watcherReady || !isNewDir) return;
|
|
@@ -1524,7 +1664,7 @@ function storybookOnlookPlugin(options = {}) {
|
|
|
1524
1664
|
configurePreviewServer(server) {
|
|
1525
1665
|
console.log("[STORYBOOK_PLUGIN] Configuring preview server middleware");
|
|
1526
1666
|
server.middlewares.use(serveMetadataAndScreenshots);
|
|
1527
|
-
refreshManifest(
|
|
1667
|
+
refreshManifest(path8.join(process.cwd(), ".storybook-cache", "manifest.json"));
|
|
1528
1668
|
},
|
|
1529
1669
|
handleHotUpdate: handleStoryFileChange
|
|
1530
1670
|
};
|
package/dist/preset/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';
|
|
2
2
|
import crypto, { createHash } from 'crypto';
|
|
3
3
|
import fs5, { existsSync } from 'fs';
|
|
4
|
-
import
|
|
4
|
+
import path8, { dirname, join, relative } from 'path';
|
|
5
5
|
import { fileURLToPath } from 'url';
|
|
6
6
|
import generateModule from '@babel/generator';
|
|
7
7
|
import { parse } from '@babel/parser';
|
|
@@ -45,7 +45,7 @@ function componentLocPlugin(options = {}) {
|
|
|
45
45
|
sourceFilename: filepath
|
|
46
46
|
});
|
|
47
47
|
let mutated = false;
|
|
48
|
-
const relativePath =
|
|
48
|
+
const relativePath = path8.relative(root, filepath);
|
|
49
49
|
traverse(ast, {
|
|
50
50
|
JSXElement(nodePath) {
|
|
51
51
|
const opening = nodePath.node.openingElement;
|
|
@@ -117,7 +117,7 @@ function envContainmentPlugin(options) {
|
|
|
117
117
|
const resolveOutcomes = /* @__PURE__ */ new Map();
|
|
118
118
|
const roots = /* @__PURE__ */ new Set([normalizeSlashes(process.cwd())]);
|
|
119
119
|
const matchTarget = (absPath) => {
|
|
120
|
-
const norm = normalizeSlashes(
|
|
120
|
+
const norm = normalizeSlashes(path8.normalize(absPath));
|
|
121
121
|
for (const rel of byModule.keys()) {
|
|
122
122
|
for (const root of roots) {
|
|
123
123
|
const target = `${root}/${rel}`;
|
|
@@ -152,10 +152,10 @@ function envContainmentPlugin(options) {
|
|
|
152
152
|
if (source.startsWith("\0")) return null;
|
|
153
153
|
const cleaned = stripQuery(source);
|
|
154
154
|
let direct = null;
|
|
155
|
-
if (
|
|
155
|
+
if (path8.isAbsolute(cleaned)) {
|
|
156
156
|
direct = cleaned;
|
|
157
157
|
} else if (importer && (cleaned.startsWith("./") || cleaned.startsWith("../"))) {
|
|
158
|
-
direct =
|
|
158
|
+
direct = path8.resolve(path8.dirname(stripQuery(importer)), cleaned);
|
|
159
159
|
}
|
|
160
160
|
if (direct) {
|
|
161
161
|
const rel = matchTarget(direct);
|
|
@@ -164,7 +164,7 @@ function envContainmentPlugin(options) {
|
|
|
164
164
|
}
|
|
165
165
|
if (!specifierMightMatch(cleaned)) return null;
|
|
166
166
|
if (typeof this.resolve !== "function") return null;
|
|
167
|
-
const importerDir = importer ?
|
|
167
|
+
const importerDir = importer ? path8.dirname(stripQuery(importer)) : "";
|
|
168
168
|
const outcomeKey = `${importerDir}|${source}`;
|
|
169
169
|
const memoized = resolveOutcomes.get(outcomeKey);
|
|
170
170
|
if (memoized !== void 0) return memoized;
|
|
@@ -267,9 +267,9 @@ function fastRefreshTolerantExportsPlugin() {
|
|
|
267
267
|
}
|
|
268
268
|
};
|
|
269
269
|
}
|
|
270
|
-
var CACHE_DIR =
|
|
271
|
-
var SCREENSHOTS_DIR =
|
|
272
|
-
var MANIFEST_PATH =
|
|
270
|
+
var CACHE_DIR = path8.join(process.cwd(), ".storybook-cache");
|
|
271
|
+
var SCREENSHOTS_DIR = path8.join(CACHE_DIR, "screenshots");
|
|
272
|
+
var MANIFEST_PATH = path8.join(CACHE_DIR, "manifest.json");
|
|
273
273
|
var VIEWPORT_WIDTH = 1920;
|
|
274
274
|
var VIEWPORT_HEIGHT = 1080;
|
|
275
275
|
var MIN_COMPONENT_WIDTH = 420;
|
|
@@ -460,8 +460,8 @@ async function detectRenderState(page, opts = {}) {
|
|
|
460
460
|
}
|
|
461
461
|
}
|
|
462
462
|
function getScreenshotPath(storyId, theme) {
|
|
463
|
-
const storyDir =
|
|
464
|
-
return
|
|
463
|
+
const storyDir = path8.join(SCREENSHOTS_DIR, storyId);
|
|
464
|
+
return path8.join(storyDir, `${theme}.png`);
|
|
465
465
|
}
|
|
466
466
|
async function captureScreenshotBuffer(storyId, theme, width = VIEWPORT_WIDTH, height = VIEWPORT_HEIGHT, storybookUrl = "http://localhost:6006", loadTimeoutMs = 9e4, renderTimeoutMs = 2e4) {
|
|
467
467
|
const browser = await getBrowser();
|
|
@@ -549,7 +549,7 @@ async function captureScreenshotBuffer(storyId, theme, width = VIEWPORT_WIDTH, h
|
|
|
549
549
|
async function generateScreenshot(storyId, theme, storybookUrl = "http://localhost:6006", timeoutMs = 3e4) {
|
|
550
550
|
try {
|
|
551
551
|
ensureCacheDirectories();
|
|
552
|
-
const storyDir =
|
|
552
|
+
const storyDir = path8.join(SCREENSHOTS_DIR, storyId);
|
|
553
553
|
if (!fs5.existsSync(storyDir)) {
|
|
554
554
|
fs5.mkdirSync(storyDir, { recursive: true });
|
|
555
555
|
}
|
|
@@ -670,7 +670,7 @@ async function fetchStorybookIndex() {
|
|
|
670
670
|
}
|
|
671
671
|
function getStoriesForFile(filePath) {
|
|
672
672
|
if (!cachedIndex) return [];
|
|
673
|
-
const fileName =
|
|
673
|
+
const fileName = path8.basename(filePath);
|
|
674
674
|
return Object.values(cachedIndex.entries).filter((entry) => entry.type === "story" && entry.importPath.endsWith(fileName)).map((entry) => entry.id);
|
|
675
675
|
}
|
|
676
676
|
async function regenerateScreenshotsForFiles(files) {
|
|
@@ -981,8 +981,8 @@ function softStoryRerenderPlugin() {
|
|
|
981
981
|
preview.onStoriesChanged({ importFn: newModule.importFn });
|
|
982
982
|
}
|
|
983
983
|
});`;
|
|
984
|
-
const out = code.replace(pattern, (_match, quote,
|
|
985
|
-
return replacement.replace("$QUOTE$$PATH$$QUOTE$", `${quote}${
|
|
984
|
+
const out = code.replace(pattern, (_match, quote, path9) => {
|
|
985
|
+
return replacement.replace("$QUOTE$$PATH$$QUOTE$", `${quote}${path9}${quote}`);
|
|
986
986
|
});
|
|
987
987
|
return out === code ? null : { code: out, map: null };
|
|
988
988
|
}
|
|
@@ -993,14 +993,14 @@ function resolveTsconfigAliases(root) {
|
|
|
993
993
|
try {
|
|
994
994
|
const config = readTsconfigAliasConfig(root);
|
|
995
995
|
if (!config) return [];
|
|
996
|
-
const base =
|
|
996
|
+
const base = path8.resolve(root, config.baseUrl ?? ".");
|
|
997
997
|
const aliases = [];
|
|
998
998
|
for (const [key, targets] of Object.entries(config.paths)) {
|
|
999
999
|
const target = targets[0];
|
|
1000
1000
|
if (!target) continue;
|
|
1001
1001
|
if (key.endsWith("/*") && target.endsWith("/*")) {
|
|
1002
1002
|
const findPrefix = key.slice(0, -1);
|
|
1003
|
-
const targetDir =
|
|
1003
|
+
const targetDir = path8.resolve(base, target.slice(0, -2));
|
|
1004
1004
|
aliases.push({
|
|
1005
1005
|
find: new RegExp(`^${escapeRegExp(findPrefix)}`),
|
|
1006
1006
|
replacement: `${targetDir}/`
|
|
@@ -1008,7 +1008,7 @@ function resolveTsconfigAliases(root) {
|
|
|
1008
1008
|
} else if (!key.includes("*") && !target.includes("*")) {
|
|
1009
1009
|
aliases.push({
|
|
1010
1010
|
find: new RegExp(`^${escapeRegExp(key)}$`),
|
|
1011
|
-
replacement:
|
|
1011
|
+
replacement: path8.resolve(base, target)
|
|
1012
1012
|
});
|
|
1013
1013
|
}
|
|
1014
1014
|
}
|
|
@@ -1019,7 +1019,7 @@ function resolveTsconfigAliases(root) {
|
|
|
1019
1019
|
}
|
|
1020
1020
|
function readTsconfigAliasConfig(root) {
|
|
1021
1021
|
for (const candidate of TSCONFIG_CANDIDATES) {
|
|
1022
|
-
const abs =
|
|
1022
|
+
const abs = path8.join(root, candidate);
|
|
1023
1023
|
let text;
|
|
1024
1024
|
try {
|
|
1025
1025
|
text = fs5.readFileSync(abs, "utf-8");
|
|
@@ -1100,6 +1100,120 @@ function findGitRoot(startPath) {
|
|
|
1100
1100
|
}
|
|
1101
1101
|
return null;
|
|
1102
1102
|
}
|
|
1103
|
+
var STORY_FILE_PATTERN = /\.stories\.(js|jsx|mjs|ts|tsx|mdx)$/;
|
|
1104
|
+
function isStoryFile(file) {
|
|
1105
|
+
return STORY_FILE_PATTERN.test(file);
|
|
1106
|
+
}
|
|
1107
|
+
function missingFromIndex(index, files) {
|
|
1108
|
+
const importPaths = [];
|
|
1109
|
+
for (const entry of Object.values(index.entries ?? {})) {
|
|
1110
|
+
if (typeof entry.importPath === "string") importPaths.push(entry.importPath);
|
|
1111
|
+
}
|
|
1112
|
+
return files.filter((file) => {
|
|
1113
|
+
const base = path8.basename(file);
|
|
1114
|
+
return !importPaths.some((importPath) => importPath.endsWith(base));
|
|
1115
|
+
});
|
|
1116
|
+
}
|
|
1117
|
+
function portFromArgv(argv) {
|
|
1118
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
1119
|
+
const arg = argv[i];
|
|
1120
|
+
if (arg === void 0) continue;
|
|
1121
|
+
let raw;
|
|
1122
|
+
if (arg === "-p" || arg === "--port") {
|
|
1123
|
+
raw = argv[i + 1];
|
|
1124
|
+
} else {
|
|
1125
|
+
raw = /^(?:-p|--port)=(\d+)$/.exec(arg)?.[1];
|
|
1126
|
+
}
|
|
1127
|
+
if (raw === void 0) continue;
|
|
1128
|
+
const parsed = Number.parseInt(raw, 10);
|
|
1129
|
+
if (Number.isInteger(parsed) && parsed > 0 && parsed < 65536) return parsed;
|
|
1130
|
+
}
|
|
1131
|
+
return null;
|
|
1132
|
+
}
|
|
1133
|
+
async function defaultFetchIndex(port) {
|
|
1134
|
+
try {
|
|
1135
|
+
const res = await fetch(`http://localhost:${port}/index.json?_t=${Date.now()}`, {
|
|
1136
|
+
cache: "no-store",
|
|
1137
|
+
signal: AbortSignal.timeout(2e3)
|
|
1138
|
+
});
|
|
1139
|
+
if (!res.ok) return null;
|
|
1140
|
+
return await res.json();
|
|
1141
|
+
} catch {
|
|
1142
|
+
return null;
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
1146
|
+
function createStoryIndexBroadcaster({
|
|
1147
|
+
ports,
|
|
1148
|
+
notify,
|
|
1149
|
+
fetchIndex = defaultFetchIndex,
|
|
1150
|
+
debounceMs = 500,
|
|
1151
|
+
probeIntervalMs = 500,
|
|
1152
|
+
probeCeilingMs = 15e3
|
|
1153
|
+
}) {
|
|
1154
|
+
const candidatePorts = [...new Set(ports)];
|
|
1155
|
+
const pendingAdds = /* @__PURE__ */ new Set();
|
|
1156
|
+
let addDebounce = null;
|
|
1157
|
+
let unlinkDebounce = null;
|
|
1158
|
+
let probing = false;
|
|
1159
|
+
const scheduleProbe = () => {
|
|
1160
|
+
if (addDebounce) clearTimeout(addDebounce);
|
|
1161
|
+
addDebounce = setTimeout(() => {
|
|
1162
|
+
addDebounce = null;
|
|
1163
|
+
void probeThenNotify();
|
|
1164
|
+
}, debounceMs);
|
|
1165
|
+
};
|
|
1166
|
+
const probeThenNotify = async () => {
|
|
1167
|
+
if (probing) return;
|
|
1168
|
+
probing = true;
|
|
1169
|
+
const files = Array.from(pendingAdds);
|
|
1170
|
+
const deadline = Date.now() + probeCeilingMs;
|
|
1171
|
+
try {
|
|
1172
|
+
if (files.length === 0) return;
|
|
1173
|
+
while (Date.now() < deadline) {
|
|
1174
|
+
for (const candidate of candidatePorts) {
|
|
1175
|
+
const index = await fetchIndex(candidate);
|
|
1176
|
+
if (index && missingFromIndex(index, files).length === 0) {
|
|
1177
|
+
console.log(
|
|
1178
|
+
"[STORYBOOK_PLUGIN] Index caught up with new story files \u2014 broadcasting",
|
|
1179
|
+
{ files, port: candidate }
|
|
1180
|
+
);
|
|
1181
|
+
for (const file of files) pendingAdds.delete(file);
|
|
1182
|
+
notify();
|
|
1183
|
+
return;
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
await sleep(probeIntervalMs);
|
|
1187
|
+
}
|
|
1188
|
+
console.warn("[STORYBOOK_PLUGIN] Index probe hit ceiling \u2014 broadcasting anyway", {
|
|
1189
|
+
files,
|
|
1190
|
+
probeCeilingMs
|
|
1191
|
+
});
|
|
1192
|
+
for (const file of files) pendingAdds.delete(file);
|
|
1193
|
+
notify();
|
|
1194
|
+
} finally {
|
|
1195
|
+
probing = false;
|
|
1196
|
+
if (pendingAdds.size > 0) scheduleProbe();
|
|
1197
|
+
}
|
|
1198
|
+
};
|
|
1199
|
+
return {
|
|
1200
|
+
onStoryFileAdded(file) {
|
|
1201
|
+
if (!isStoryFile(file)) return;
|
|
1202
|
+
pendingAdds.add(file);
|
|
1203
|
+
scheduleProbe();
|
|
1204
|
+
},
|
|
1205
|
+
onStoryFileRemoved(file) {
|
|
1206
|
+
if (!isStoryFile(file)) return;
|
|
1207
|
+
pendingAdds.delete(file);
|
|
1208
|
+
if (unlinkDebounce) clearTimeout(unlinkDebounce);
|
|
1209
|
+
unlinkDebounce = setTimeout(() => {
|
|
1210
|
+
unlinkDebounce = null;
|
|
1211
|
+
console.log("[STORYBOOK_PLUGIN] Story file removed \u2014 broadcasting", { file });
|
|
1212
|
+
notify();
|
|
1213
|
+
}, debounceMs);
|
|
1214
|
+
}
|
|
1215
|
+
};
|
|
1216
|
+
}
|
|
1103
1217
|
|
|
1104
1218
|
// src/storybook-onlook-plugin.ts
|
|
1105
1219
|
function errorCode(error) {
|
|
@@ -1166,7 +1280,7 @@ var TAILWIND_ENTRY_CANDIDATES = [
|
|
|
1166
1280
|
var TAILWIND_DIRECTIVE_PATTERN = /@import\s+['"](?:tailwindcss|tailwindcss\/.+)['"]|@tailwind\s+(?:base|components|utilities)/;
|
|
1167
1281
|
function detectTailwindEntryCss(cwd) {
|
|
1168
1282
|
for (const rel of TAILWIND_ENTRY_CANDIDATES) {
|
|
1169
|
-
const abs =
|
|
1283
|
+
const abs = path8.join(cwd, rel);
|
|
1170
1284
|
try {
|
|
1171
1285
|
if (!fs5.existsSync(abs)) continue;
|
|
1172
1286
|
const head = fs5.readFileSync(abs, "utf-8").slice(0, 2048);
|
|
@@ -1303,7 +1417,7 @@ var serveMetadataAndScreenshots = (req, res, next) => {
|
|
|
1303
1417
|
return;
|
|
1304
1418
|
}
|
|
1305
1419
|
if (req.url?.startsWith("/screenshots/")) {
|
|
1306
|
-
const screenshotPath =
|
|
1420
|
+
const screenshotPath = path8.join(
|
|
1307
1421
|
process.cwd(),
|
|
1308
1422
|
".storybook-cache",
|
|
1309
1423
|
req.url.replace("/screenshots/", "screenshots/")
|
|
@@ -1324,7 +1438,7 @@ var serveMetadataAndScreenshots = (req, res, next) => {
|
|
|
1324
1438
|
`[STORYBOOK_PLUGIN] Generating screenshot on-demand: ${storyId}/${theme}`
|
|
1325
1439
|
);
|
|
1326
1440
|
captureScreenshotBuffer(storyId, theme).then(({ buffer }) => {
|
|
1327
|
-
const storyDir =
|
|
1441
|
+
const storyDir = path8.join(
|
|
1328
1442
|
process.cwd(),
|
|
1329
1443
|
".storybook-cache",
|
|
1330
1444
|
"screenshots",
|
|
@@ -1443,7 +1557,7 @@ function storybookOnlookPlugin(options = {}) {
|
|
|
1443
1557
|
configureServer(server) {
|
|
1444
1558
|
console.log("[STORYBOOK_PLUGIN] Configuring server middleware");
|
|
1445
1559
|
server.middlewares.use(serveMetadataAndScreenshots);
|
|
1446
|
-
const cachedManifestPath =
|
|
1560
|
+
const cachedManifestPath = path8.join(
|
|
1447
1561
|
process.cwd(),
|
|
1448
1562
|
".storybook-cache",
|
|
1449
1563
|
"manifest.json"
|
|
@@ -1462,7 +1576,7 @@ function storybookOnlookPlugin(options = {}) {
|
|
|
1462
1576
|
server.watcher.on("change", scheduleRefresh);
|
|
1463
1577
|
const storyGlobs = options.storyGlobs ?? ["**/*.stories.@(js|jsx|mjs|ts|tsx|mdx)"];
|
|
1464
1578
|
const resolvedStoryGlobs = storyGlobs.map(
|
|
1465
|
-
(glob) =>
|
|
1579
|
+
(glob) => path8.isAbsolute(glob) ? glob : path8.join(process.cwd(), glob)
|
|
1466
1580
|
);
|
|
1467
1581
|
if (resolvedStoryGlobs.length > 0) {
|
|
1468
1582
|
server.watcher.add(resolvedStoryGlobs);
|
|
@@ -1470,10 +1584,36 @@ function storybookOnlookPlugin(options = {}) {
|
|
|
1470
1584
|
storyGlobs: resolvedStoryGlobs
|
|
1471
1585
|
});
|
|
1472
1586
|
}
|
|
1587
|
+
const probeCandidatePorts = [
|
|
1588
|
+
...new Set(
|
|
1589
|
+
[portFromArgv(process.argv), port, server.config.server.port].filter(
|
|
1590
|
+
(p) => typeof p === "number"
|
|
1591
|
+
)
|
|
1592
|
+
)
|
|
1593
|
+
];
|
|
1594
|
+
console.log("[STORYBOOK_PLUGIN] Story add/unlink index broadcast enabled", {
|
|
1595
|
+
probeCandidatePorts
|
|
1596
|
+
});
|
|
1597
|
+
const indexBroadcaster = createStoryIndexBroadcaster({
|
|
1598
|
+
ports: probeCandidatePorts,
|
|
1599
|
+
notify: notifyOnbookIndexChanged
|
|
1600
|
+
});
|
|
1601
|
+
let indexBroadcastWatcherReady = false;
|
|
1602
|
+
server.watcher.once("ready", () => {
|
|
1603
|
+
indexBroadcastWatcherReady = true;
|
|
1604
|
+
});
|
|
1605
|
+
server.watcher.on("add", (file) => {
|
|
1606
|
+
if (!indexBroadcastWatcherReady) return;
|
|
1607
|
+
indexBroadcaster.onStoryFileAdded(file);
|
|
1608
|
+
});
|
|
1609
|
+
server.watcher.on("unlink", (file) => {
|
|
1610
|
+
if (!indexBroadcastWatcherReady) return;
|
|
1611
|
+
indexBroadcaster.onStoryFileRemoved(file);
|
|
1612
|
+
});
|
|
1473
1613
|
const tailwindEntryPath = (() => {
|
|
1474
1614
|
if (options.tailwindEntryCss === false) return null;
|
|
1475
1615
|
if (typeof options.tailwindEntryCss === "string") {
|
|
1476
|
-
return
|
|
1616
|
+
return path8.isAbsolute(options.tailwindEntryCss) ? options.tailwindEntryCss : path8.join(process.cwd(), options.tailwindEntryCss);
|
|
1477
1617
|
}
|
|
1478
1618
|
return detectTailwindEntryCss(process.cwd());
|
|
1479
1619
|
})();
|
|
@@ -1489,7 +1629,7 @@ function storybookOnlookPlugin(options = {}) {
|
|
|
1489
1629
|
});
|
|
1490
1630
|
server.watcher.on("add", (file) => {
|
|
1491
1631
|
if (!/\.tsx?$/.test(file)) return;
|
|
1492
|
-
const dir =
|
|
1632
|
+
const dir = path8.dirname(file);
|
|
1493
1633
|
const isNewDir = !seenDirs.has(dir);
|
|
1494
1634
|
seenDirs.add(dir);
|
|
1495
1635
|
if (!watcherReady || !isNewDir) return;
|
|
@@ -1520,7 +1660,7 @@ function storybookOnlookPlugin(options = {}) {
|
|
|
1520
1660
|
configurePreviewServer(server) {
|
|
1521
1661
|
console.log("[STORYBOOK_PLUGIN] Configuring preview server middleware");
|
|
1522
1662
|
server.middlewares.use(serveMetadataAndScreenshots);
|
|
1523
|
-
refreshManifest(
|
|
1663
|
+
refreshManifest(path8.join(process.cwd(), ".storybook-cache", "manifest.json"));
|
|
1524
1664
|
},
|
|
1525
1665
|
handleHotUpdate: handleStoryFileChange
|
|
1526
1666
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onlook/storybook-plugin",
|
|
3
|
-
"version": "0.4.0-beta.
|
|
3
|
+
"version": "0.4.0-beta.16",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"onlook-storybook": "./dist/cli/index.js"
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"storybook": "^10.1.11"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
|
-
"@onbook/tsconfig": "
|
|
55
|
+
"@onbook/tsconfig": "workspace:*",
|
|
56
56
|
"@types/babel__generator": "^7.6.8",
|
|
57
57
|
"@types/babel__traverse": "^7.20.6",
|
|
58
58
|
"@types/node": "^22.15.32",
|