UNPKG

storybook

Version:

Storybook: Develop, document, and test UI components in isolation

305 lines (299 loc) • 11.2 kB
import CJS_COMPAT_NODE_URL_q99y7iqlbzn from 'node:url'; import CJS_COMPAT_NODE_PATH_q99y7iqlbzn from 'node:path'; import CJS_COMPAT_NODE_MODULE_q99y7iqlbzn from "node:module"; var __filename = CJS_COMPAT_NODE_URL_q99y7iqlbzn.fileURLToPath(import.meta.url); var __dirname = CJS_COMPAT_NODE_PATH_q99y7iqlbzn.dirname(__filename); var require = CJS_COMPAT_NODE_MODULE_q99y7iqlbzn.createRequire(import.meta.url); // ------------------------------------------------------------ // end of CJS compatibility banner, injected by Storybook's esbuild configuration // ------------------------------------------------------------ import { require_build } from "./chunk-UXQJJRYH.js"; import { resolvePackageDir } from "./chunk-7N53RHGS.js"; import { require_prompts } from "./chunk-KCF2Q4OQ.js"; import { require_dist } from "./chunk-IMHUIAVE.js"; import { require_picocolors } from "./chunk-KABHBSS3.js"; import { __name, __toESM } from "./chunk-MB5KTO7X.js"; // src/core-server/utils/server-statics.ts var import_picocolors = __toESM(require_picocolors(), 1); var import_sirv = __toESM(require_build(), 1); var import_ts_dedent = __toESM(require_dist(), 1); import { existsSync, statSync } from "node:fs"; import { readFile, stat } from "node:fs/promises"; import { basename, isAbsolute, join, posix, resolve, sep, win32 } from "node:path"; import { getDirectoryFromWorkingDir, resolvePathInStorybookCache } from "storybook/internal/common"; import { logger, once } from "storybook/internal/node-logger"; var cacheDir = resolvePathInStorybookCache("", "ignored-sub").split("ignored-sub")[0]; var files = /* @__PURE__ */ new Map(); var readFileOnce = /* @__PURE__ */ __name(async (path) => { if (files.has(path)) { return files.get(path); } else { const [data, stats] = await Promise.all([readFile(path, "utf-8"), stat(path)]); const result = { data, mtime: stats.mtimeMs }; files.set(path, result); return result; } }, "readFileOnce"); var faviconWrapperPath = join( resolvePackageDir("storybook"), "/assets/browser/favicon-wrapper.svg" ); var prepareNestedSvg = /* @__PURE__ */ __name((svg) => { const [, openingTag, contents, closingTag] = svg?.match(/(<svg[^>]*>)(.*?)(<\/svg>)/s) ?? []; if (!openingTag || !contents || !closingTag) { return svg; } let width; let height; let modifiedTag = openingTag.replace(/width=["']([^"']*)["']/g, (_, value) => { width = parseFloat(value); return 'width="32px"'; }).replace(/height=["']([^"']*)["']/g, (_, value) => { height = parseFloat(value); return 'height="32px"'; }); const hasViewBox = /viewBox=["'][^"']*["']/.test(modifiedTag); if (!hasViewBox && width && height) { modifiedTag = modifiedTag.replace(/>$/, ` viewBox="0 0 ${width} ${height}">`); } modifiedTag = modifiedTag.replace(/preserveAspectRatio=["'][^"']*["']/g, "").replace(/>$/, ' preserveAspectRatio="xMidYMid meet">'); return modifiedTag + contents + closingTag; }, "prepareNestedSvg"); async function useStatics(app, options) { const staticDirs = await options.presets.apply("staticDirs") ?? []; const faviconPath = await options.presets.apply("favicon"); const faviconDir = resolve(faviconPath, ".."); const faviconFile = basename(faviconPath); app.use(`/${faviconFile}`, async (req, res, next) => { const status = req.query.status; if (status && faviconFile.endsWith(".svg") && ["active", "critical", "negative", "positive", "warning"].includes(status)) { const [faviconInfo, faviconWrapperInfo] = await Promise.all([ readFileOnce(join(faviconDir, faviconFile)), readFileOnce(faviconWrapperPath) ]).catch((e) => { if (e instanceof Error) { once.warn(`Failed to read favicon: ${e.message}`); } return [null, null]; }); if (faviconInfo && faviconWrapperInfo) { const svg = faviconWrapperInfo.data.replace('<g id="mask"', `<g mask="url(#${status}-mask)"`).replace('<use id="status"', `<use href="#${status}"`).replace('<use id="icon" />', prepareNestedSvg(faviconInfo.data)); res.setHeader("Content-Type", "image/svg+xml"); res.setHeader("ETag", `"${faviconWrapperInfo.mtime}-${faviconInfo.mtime}"`); res.end(svg); return; } } req.url = `/${faviconFile}`; return sirvWorkaround(faviconDir)(req, res, next); }); staticDirs.map((dir) => { try { const { staticDir, staticPath, targetEndpoint } = mapStaticDir(dir, options.configDir); if (!targetEndpoint.startsWith("/sb-") && !staticDir.startsWith(cacheDir)) { logger.info( `=> Serving static files from ${import_picocolors.default.cyan(staticDir)} at ${import_picocolors.default.cyan(targetEndpoint)}` ); } if (existsSync(staticPath) && statSync(staticPath).isFile()) { const staticPathDir = resolve(staticPath, ".."); const staticPathFile = basename(staticPath); app.use(targetEndpoint, (req, res, next) => { req.url = `/${staticPathFile}`; sirvWorkaround(staticPathDir)(req, res, next); }); } else { app.use(targetEndpoint, sirvWorkaround(staticPath)); } } catch (e) { if (e instanceof Error) { logger.warn(e.message); } } }); } __name(useStatics, "useStatics"); var sirvWorkaround = /* @__PURE__ */ __name((dir, opts = {}) => (req, res, next) => { const originalParsedUrl = req._parsedUrl; const maybeNext = next ? () => { req._parsedUrl = originalParsedUrl; next(); } : void 0; (0, import_sirv.default)(dir, { dev: true, etag: true, extensions: [], ...opts })(req, res, maybeNext); }, "sirvWorkaround"); var parseStaticDir = /* @__PURE__ */ __name((arg) => { const lastColonIndex = arg.lastIndexOf(":"); const isWindowsAbsolute = win32.isAbsolute(arg); const isWindowsRawDirOnly = isWindowsAbsolute && lastColonIndex === 1; const splitIndex = lastColonIndex !== -1 && !isWindowsRawDirOnly ? lastColonIndex : arg.length; const [from, to] = [arg.slice(0, splitIndex), arg.slice(splitIndex + 1)]; const staticDir = isAbsolute(from) ? from : `./${from}`; const staticPath = resolve(staticDir); if (!existsSync(staticPath)) { throw new Error( import_ts_dedent.dedent` Failed to load static files, no such directory: ${import_picocolors.default.cyan(staticPath)} Make sure this directory exists. ` ); } const targetRaw = to || (statSync(staticPath).isFile() ? basename(staticPath) : "/"); const target = targetRaw.split(sep).join(posix.sep); const targetDir = target.replace(/^\/?/, "./"); const targetEndpoint = targetDir.substring(1); return { staticDir, staticPath, targetDir, targetEndpoint }; }, "parseStaticDir"); var mapStaticDir = /* @__PURE__ */ __name((staticDir, configDir) => { const specifier = typeof staticDir === "string" ? staticDir : `${staticDir.from}:${staticDir.to}`; const normalizedDir = isAbsolute(specifier) ? specifier : getDirectoryFromWorkingDir({ configDir, workingDir: process.cwd(), directory: specifier }); return parseStaticDir(normalizedDir); }, "mapStaticDir"); // src/core-server/withTelemetry.ts var import_prompts = __toESM(require_prompts(), 1); import { HandledError, cache, isCI, loadAllPresets } from "storybook/internal/common"; import { logger as logger2 } from "storybook/internal/node-logger"; import { getPrecedingUpgrade, oneWayHash, telemetry } from "storybook/internal/telemetry"; var promptCrashReports = /* @__PURE__ */ __name(async () => { if (isCI() || !process.stdout.isTTY) { return void 0; } const { enableCrashReports } = await (0, import_prompts.default)({ type: "confirm", name: "enableCrashReports", message: `Would you like to help improve Storybook by sending anonymous crash reports?`, initial: true }); await cache.set("enableCrashReports", enableCrashReports); return enableCrashReports; }, "promptCrashReports"); async function getErrorLevel({ cliOptions, presetOptions, skipPrompt }) { if (cliOptions.disableTelemetry) { return "none"; } if (!presetOptions) { return "full"; } const presets = await loadAllPresets(presetOptions); const core = await presets.apply("core"); if (core?.enableCrashReports !== void 0) { return core.enableCrashReports ? "full" : "error"; } if (core?.disableTelemetry) { return "none"; } const valueFromCache = await cache.get("enableCrashReports") ?? await cache.get("enableCrashreports"); if (valueFromCache !== void 0) { return valueFromCache ? "full" : "error"; } if (skipPrompt) { return "error"; } const valueFromPrompt = await promptCrashReports(); if (valueFromPrompt !== void 0) { return valueFromPrompt ? "full" : "error"; } return "full"; } __name(getErrorLevel, "getErrorLevel"); async function sendTelemetryError(_error, eventType, options) { try { let errorLevel = "error"; try { errorLevel = await getErrorLevel(options); } catch (err) { } if (errorLevel !== "none") { const precedingUpgrade = await getPrecedingUpgrade(); const error = _error; let errorHash; if ("message" in error) { errorHash = error.message ? oneWayHash(error.message) : "EMPTY_MESSAGE"; } else { errorHash = "NO_MESSAGE"; } const { code, name, category } = error; await telemetry( "error", { code, name, category, eventType, precedingUpgrade, error: errorLevel === "full" ? error : void 0, errorHash, // if we ever end up sending a non-error instance, we'd like to know isErrorInstance: error instanceof Error }, { immediate: true, configDir: options.cliOptions.configDir || options.presetOptions?.configDir, enableCrashReports: errorLevel === "full" } ); } } catch (err) { } } __name(sendTelemetryError, "sendTelemetryError"); async function withTelemetry(eventType, options, run) { const enableTelemetry = !(options.cliOptions.disableTelemetry || options.cliOptions.test === true); let canceled = false; async function cancelTelemetry() { canceled = true; if (enableTelemetry) { await telemetry("canceled", { eventType }, { stripMetadata: true, immediate: true }); } process.exit(0); } __name(cancelTelemetry, "cancelTelemetry"); if (eventType === "init") { process.on("SIGINT", cancelTelemetry); } if (enableTelemetry) { telemetry("boot", { eventType }, { stripMetadata: true }); } try { return await run(); } catch (error) { if (canceled) { return void 0; } if (!(error instanceof HandledError)) { const { printError = logger2.error } = options; printError(error); } if (enableTelemetry) { await sendTelemetryError(error, eventType, options); } throw error; } finally { process.off("SIGINT", cancelTelemetry); } } __name(withTelemetry, "withTelemetry"); export { useStatics, parseStaticDir, mapStaticDir, getErrorLevel, sendTelemetryError, withTelemetry };