UNPKG

storybook

Version:

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

300 lines (289 loc) • 13.9 kB
import CJS_COMPAT_NODE_URL_yr66iw5gef from 'node:url'; import CJS_COMPAT_NODE_PATH_yr66iw5gef from 'node:path'; import CJS_COMPAT_NODE_MODULE_yr66iw5gef from "node:module"; var __filename = CJS_COMPAT_NODE_URL_yr66iw5gef.fileURLToPath(import.meta.url); var __dirname = CJS_COMPAT_NODE_PATH_yr66iw5gef.dirname(__filename); var require = CJS_COMPAT_NODE_MODULE_yr66iw5gef.createRequire(import.meta.url); // ------------------------------------------------------------ // end of CJS compatibility banner, injected by Storybook's esbuild configuration // ------------------------------------------------------------ import { require_build } from "./chunk-COZFRL72.js"; import { StorybookError } from "./chunk-LJRKMWYC.js"; import { resolvePackageDir } from "./chunk-Q6B5LJGA.js"; import { relative } from "./chunk-VBZIULNK.js"; import { require_dist } from "./chunk-76KIYDRU.js"; import { require_picocolors } from "./chunk-RZ2YFNVH.js"; import { __toESM } from "./chunk-J4VC4I2M.js"; // src/core-server/utils/server-statics.ts 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, getProjectRoot, resolvePathInStorybookCache } from "storybook/internal/common"; import { CLI_COLORS, logger, once } from "storybook/internal/node-logger"; var import_picocolors = __toESM(require_picocolors(), 1), import_sirv = __toESM(require_build(), 1), import_ts_dedent = __toESM(require_dist(), 1); var cacheDir = resolvePathInStorybookCache("", "ignored-sub").split("ignored-sub")[0], files = /* @__PURE__ */ new Map(), readFileOnce = async (path) => { if (files.has(path)) return files.get(path); { let [data, stats] = await Promise.all([readFile(path, "utf-8"), stat(path)]), result = { data, mtime: stats.mtimeMs }; return files.set(path, result), result; } }, faviconWrapperPath = join( resolvePackageDir("storybook"), "/assets/browser/favicon-wrapper.svg" ), prepareNestedSvg = (svg) => { let [, openingTag, contents, closingTag] = svg?.match(/(<svg[^>]*>)(.*?)(<\/svg>)/s) ?? []; if (!openingTag || !contents || !closingTag) return svg; let width, height, modifiedTag = openingTag.replace(/width=["']([^"']*)["']/g, (_, value) => (width = parseFloat(value), 'width="32px"')).replace(/height=["']([^"']*)["']/g, (_, value) => (height = parseFloat(value), 'height="32px"')); return !/viewBox=["'][^"']*["']/.test(modifiedTag) && width && height && (modifiedTag = modifiedTag.replace(/>$/, ` viewBox="0 0 ${width} ${height}">`)), modifiedTag = modifiedTag.replace(/preserveAspectRatio=["'][^"']*["']/g, "").replace(/>$/, ' preserveAspectRatio="xMidYMid meet">'), modifiedTag + contents + closingTag; }; async function useStatics(app, options) { let staticDirs = await options.presets.apply("staticDirs") ?? [], faviconPath = await options.presets.apply("favicon"), faviconDir = resolve(faviconPath, ".."), faviconFile = basename(faviconPath); app.use(`/${faviconFile}`, async (req, res, next) => { let status = req.query.status; if (status && faviconFile.endsWith(".svg") && ["active", "critical", "negative", "positive", "warning"].includes(status)) { let [faviconInfo, faviconWrapperInfo] = await Promise.all([ readFileOnce(join(faviconDir, faviconFile)), readFileOnce(faviconWrapperPath) ]).catch((e) => (e instanceof Error && once.warn(`Failed to read favicon: ${e.message}`), [null, null])); if (faviconInfo && faviconWrapperInfo) { let 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; } } return req.url = `/${faviconFile}`, sirvWorkaround(faviconDir)(req, res, next); }), staticDirs.map((dir) => { try { let { staticDir, staticPath, targetEndpoint } = mapStaticDir(dir, options.configDir); if (!targetEndpoint.startsWith("/sb-") && !staticDir.startsWith(cacheDir)) { let relativeStaticDir = relative(getProjectRoot(), staticDir); logger.debug( `Serving static files from ${CLI_COLORS.info(relativeStaticDir)} at ${CLI_COLORS.info(targetEndpoint)}` ); } if (existsSync(staticPath) && statSync(staticPath).isFile()) { let staticPathDir = resolve(staticPath, ".."), 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) { e instanceof Error && logger.warn(e.message); } }); } var sirvWorkaround = (dir, opts = {}) => (req, res, next) => { let originalParsedUrl = req._parsedUrl, maybeNext = next ? () => { req._parsedUrl = originalParsedUrl, next(); } : void 0; (0, import_sirv.default)(dir, { dev: !0, etag: !0, extensions: [], ...opts })(req, res, maybeNext); }, parseStaticDir = (arg) => { let lastColonIndex = arg.lastIndexOf(":"), isWindowsRawDirOnly = win32.isAbsolute(arg) && lastColonIndex === 1, splitIndex = lastColonIndex !== -1 && !isWindowsRawDirOnly ? lastColonIndex : arg.length, [from, to] = [arg.slice(0, splitIndex), arg.slice(splitIndex + 1)], staticDir = isAbsolute(from) ? from : `./${from}`, 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. ` ); let targetDir = (to || (statSync(staticPath).isFile() ? basename(staticPath) : "/")).split(sep).join(posix.sep).replace(/^\/?/, "./"), targetEndpoint = targetDir.substring(1); return { staticDir, staticPath, targetDir, targetEndpoint }; }, mapStaticDir = (staticDir, configDir) => { let specifier = typeof staticDir == "string" ? staticDir : `${staticDir.from}:${staticDir.to}`, normalizedDir = isAbsolute(specifier) ? specifier : getDirectoryFromWorkingDir({ configDir, workingDir: process.cwd(), directory: specifier }); return parseStaticDir(normalizedDir); }; // src/core-server/withTelemetry.ts import { HandledError, cache, isCI, loadAllPresets } from "storybook/internal/common"; import { logger as logger2, prompt } from "storybook/internal/node-logger"; import { ErrorCollector, getPrecedingUpgrade, oneWayHash, telemetry } from "storybook/internal/telemetry"; var promptCrashReports = async () => { if (isCI() || !process.stdout.isTTY) return; let enableCrashReports = await prompt.confirm({ message: "Would you like to send anonymous crash reports to improve Storybook and fix bugs faster?", initialValue: !0 }); return await cache.set("enableCrashReports", enableCrashReports), enableCrashReports; }; async function getErrorLevel({ cliOptions, presetOptions, skipPrompt }) { if (cliOptions.disableTelemetry) return "none"; if (!presetOptions) return "error"; let core = await (await loadAllPresets(presetOptions)).apply("core"); if (core?.enableCrashReports !== void 0) return core.enableCrashReports ? "full" : "error"; if (core?.disableTelemetry) return "none"; let valueFromCache = await cache.get("enableCrashReports") ?? await cache.get("enableCrashreports"); if (valueFromCache !== void 0) return valueFromCache ? "full" : "error"; if (skipPrompt) return "error"; let valueFromPrompt = await promptCrashReports(); return valueFromPrompt !== void 0 ? valueFromPrompt ? "full" : "error" : "full"; } async function sendTelemetryError(_error, eventType, options, blocking = !0) { try { let errorLevel = "error"; try { errorLevel = await getErrorLevel(options); } catch { } if (errorLevel !== "none") { let precedingUpgrade = await getPrecedingUpgrade(), error = _error, errorHash; "message" in error ? errorHash = error.message ? oneWayHash(error.message) : "EMPTY_MESSAGE" : errorHash = "NO_MESSAGE"; let { code, name, category } = error; await telemetry( "error", { code, name, category, eventType, blocking, 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: !0, configDir: options.cliOptions.configDir || options.presetOptions?.configDir, enableCrashReports: errorLevel === "full" } ); } } catch { } } function isTelemetryEnabled(options) { return !(options.cliOptions.disableTelemetry || options.cliOptions.test === !0); } async function withTelemetry(eventType, options, run) { let enableTelemetry = isTelemetryEnabled(options), canceled = !1; async function cancelTelemetry() { canceled = !0, enableTelemetry && await telemetry("canceled", { eventType }, { stripMetadata: !0, immediate: !0 }), process.exit(0); } eventType === "init" && process.on("SIGINT", cancelTelemetry), enableTelemetry && telemetry("boot", { eventType }, { stripMetadata: !0 }); try { return await run(); } catch (error) { if (canceled) return; if (!(error instanceof HandledError || error instanceof StorybookError && error.isHandledError)) { let { printError = logger2.error } = options; printError(error); } throw enableTelemetry && await sendTelemetryError(error, eventType, options), error; } finally { if (enableTelemetry) { let errors = ErrorCollector.getErrors(); for (let error of errors) await sendTelemetryError(error, eventType, options, !1); process.off("SIGINT", cancelTelemetry); } } } // ../node_modules/es-toolkit/dist/function/debounce.mjs function debounce(func, debounceMs, { signal, edges } = {}) { let pendingThis, pendingArgs = null, leading = edges != null && edges.includes("leading"), trailing = edges == null || edges.includes("trailing"), invoke = () => { pendingArgs !== null && (func.apply(pendingThis, pendingArgs), pendingThis = void 0, pendingArgs = null); }, onTimerEnd = () => { trailing && invoke(), cancel(); }, timeoutId = null, schedule = () => { timeoutId != null && clearTimeout(timeoutId), timeoutId = setTimeout(() => { timeoutId = null, onTimerEnd(); }, debounceMs); }, cancelTimer = () => { timeoutId !== null && (clearTimeout(timeoutId), timeoutId = null); }, cancel = () => { cancelTimer(), pendingThis = void 0, pendingArgs = null; }, flush = () => { invoke(); }, debounced = function(...args) { if (signal?.aborted) return; pendingThis = this, pendingArgs = args; let isFirstCall = timeoutId == null; schedule(), leading && isFirstCall && invoke(); }; return debounced.schedule = schedule, debounced.cancel = cancel, debounced.flush = flush, signal?.addEventListener("abort", cancel, { once: !0 }), debounced; } // ../node_modules/es-toolkit/dist/function/throttle.mjs function throttle(func, throttleMs, { signal, edges = ["leading", "trailing"] } = {}) { let pendingAt = null, debounced = debounce(func, throttleMs, { signal, edges }), throttled = function(...args) { pendingAt == null ? pendingAt = Date.now() : Date.now() - pendingAt >= throttleMs && (pendingAt = Date.now(), debounced.cancel()), debounced.apply(this, args); }; return throttled.cancel = debounced.cancel, throttled.flush = debounced.flush, throttled; } // ../node_modules/es-toolkit/dist/function/partial.mjs function partial(func, ...partialArgs) { return partialImpl(func, placeholderSymbol, ...partialArgs); } function partialImpl(func, placeholder, ...partialArgs) { let partialed = function(...providedArgs) { let providedArgsIndex = 0, substitutedArgs = partialArgs.slice().map((arg) => arg === placeholder ? providedArgs[providedArgsIndex++] : arg), remainingArgs = providedArgs.slice(providedArgsIndex); return func.apply(this, substitutedArgs.concat(remainingArgs)); }; return func.prototype && (partialed.prototype = Object.create(func.prototype)), partialed; } var placeholderSymbol = Symbol("partial.placeholder"); partial.placeholder = placeholderSymbol; // ../node_modules/es-toolkit/dist/function/partialRight.mjs function partialRight(func, ...partialArgs) { return partialRightImpl(func, placeholderSymbol2, ...partialArgs); } function partialRightImpl(func, placeholder, ...partialArgs) { let partialedRight = function(...providedArgs) { let placeholderLength = partialArgs.filter((arg) => arg === placeholder).length, rangeLength = Math.max(providedArgs.length - placeholderLength, 0), remainingArgs = providedArgs.slice(0, rangeLength), providedArgsIndex = rangeLength, substitutedArgs = partialArgs.slice().map((arg) => arg === placeholder ? providedArgs[providedArgsIndex++] : arg); return func.apply(this, remainingArgs.concat(substitutedArgs)); }; return func.prototype && (partialedRight.prototype = Object.create(func.prototype)), partialedRight; } var placeholderSymbol2 = Symbol("partialRight.placeholder"); partialRight.placeholder = placeholderSymbol2; // ../node_modules/es-toolkit/dist/function/retry.mjs var DEFAULT_RETRIES = Number.POSITIVE_INFINITY; export { useStatics, parseStaticDir, mapStaticDir, debounce, throttle, getErrorLevel, sendTelemetryError, isTelemetryEnabled, withTelemetry };