UNPKG

@storybook/react

Version:
593 lines (573 loc) • 25.4 kB
import CJS_COMPAT_NODE_URL_0ccojhbbgp74 from 'node:url'; import CJS_COMPAT_NODE_PATH_0ccojhbbgp74 from 'node:path'; import CJS_COMPAT_NODE_MODULE_0ccojhbbgp74 from "node:module"; var __filename = CJS_COMPAT_NODE_URL_0ccojhbbgp74.fileURLToPath(import.meta.url); var __dirname = CJS_COMPAT_NODE_PATH_0ccojhbbgp74.dirname(__filename); var require = CJS_COMPAT_NODE_MODULE_0ccojhbbgp74.createRequire(import.meta.url); // ------------------------------------------------------------ // end of CJS compatibility banner, injected by Storybook's esbuild configuration // ------------------------------------------------------------ import { ComponentMetaManager, buildReactComponentDocgenFromResolved, buildStoryDocsFromResolved, cachedReadFileSync, dirname, extractArgTypes, getCodeSnippet, invalidateCache, invalidateParser, join, parseWithReactDocgen, path, resolveStoryFileComponents } from "./_node-chunks/chunk-ZUN4ALWM.js"; // src/preset.ts import path2, { join as join2 } from "node:path"; import { fileURLToPath as fileURLToPath3 } from "node:url"; import { getProjectRoot } from "storybook/internal/common"; // ../../core/src/shared/utils/module.ts import { createRequire, register } from "node:module"; import { fileURLToPath, pathToFileURL } from "node:url"; // ../../../node_modules/exsolve/dist/index.mjs import assert from "node:assert"; import v8 from "node:v8"; import { format, inspect } from "node:util"; var own$1 = {}.hasOwnProperty, classRegExp = /^([A-Z][a-z\d]*)+$/, kTypes = /* @__PURE__ */ new Set([ "string", "function", "number", "object", "Function", "Object", "boolean", "bigint", "symbol" ]), messages = /* @__PURE__ */ new Map(), nodeInternalPrefix = "__node_internal_", userStackTraceLimit; function formatList(array, type = "and") { return array.length < 3 ? array.join(` ${type} `) : `${array.slice(0, -1).join(", ")}, ${type} ${array.at(-1)}`; } function createError(sym, value, constructor) { return messages.set(sym, value), makeNodeErrorWithCode(constructor, sym); } function makeNodeErrorWithCode(Base, key) { return function(...parameters) { let limit = Error.stackTraceLimit; isErrorStackTraceLimitWritable() && (Error.stackTraceLimit = 0); let error = new Base(); isErrorStackTraceLimitWritable() && (Error.stackTraceLimit = limit); let message = getMessage(key, parameters, error); return Object.defineProperties(error, { message: { value: message, enumerable: !1, writable: !0, configurable: !0 }, toString: { value() { return `${this.name} [${key}]: ${this.message}`; }, enumerable: !1, writable: !0, configurable: !0 } }), captureLargerStackTrace(error), error.code = key, error; }; } function isErrorStackTraceLimitWritable() { try { if (v8.startupSnapshot.isBuildingSnapshot()) return !1; } catch { } let desc = Object.getOwnPropertyDescriptor(Error, "stackTraceLimit"); return desc === void 0 ? Object.isExtensible(Error) : own$1.call(desc, "writable") && desc.writable !== void 0 ? desc.writable : desc.set !== void 0; } function hideStackFrames(wrappedFunction) { let hidden = nodeInternalPrefix + wrappedFunction.name; return Object.defineProperty(wrappedFunction, "name", { value: hidden }), wrappedFunction; } var captureLargerStackTrace = hideStackFrames(function(error) { let stackTraceLimitIsWritable = isErrorStackTraceLimitWritable(); return stackTraceLimitIsWritable && (userStackTraceLimit = Error.stackTraceLimit, Error.stackTraceLimit = Number.POSITIVE_INFINITY), Error.captureStackTrace(error), stackTraceLimitIsWritable && (Error.stackTraceLimit = userStackTraceLimit), error; }); function getMessage(key, parameters, self) { let message = messages.get(key); if (assert.ok(message !== void 0, "expected `message` to be found"), typeof message == "function") return assert.ok(message.length <= parameters.length, `Code: ${key}; The provided arguments length (${parameters.length}) does not match the required ones (${message.length}).`), Reflect.apply(message, self, parameters); let regex = /%[dfijoOs]/g, expectedLength = 0; for (; regex.exec(message) !== null; ) expectedLength++; return assert.ok(expectedLength === parameters.length, `Code: ${key}; The provided arguments length (${parameters.length}) does not match the required ones (${expectedLength}).`), parameters.length === 0 ? message : (parameters.unshift(message), Reflect.apply(format, null, parameters)); } function determineSpecificType(value) { if (value == null) return String(value); if (typeof value == "function" && value.name) return `function ${value.name}`; if (typeof value == "object") return value.constructor && value.constructor.name ? `an instance of ${value.constructor.name}` : `${inspect(value, { depth: -1 })}`; let inspected = inspect(value, { colors: !1 }); return inspected.length > 28 && (inspected = `${inspected.slice(0, 25)}...`), `type ${typeof value} (${inspected})`; } var ERR_INVALID_ARG_TYPE = createError("ERR_INVALID_ARG_TYPE", (name, expected, actual) => { assert.ok(typeof name == "string", "'name' must be a string"), Array.isArray(expected) || (expected = [expected]); let message = "The "; if (name.endsWith(" argument")) message += `${name} `; else { let type = name.includes(".") ? "property" : "argument"; message += `"${name}" ${type} `; } message += "must be "; let types = [], instances = [], other = []; for (let value of expected) assert.ok(typeof value == "string", "All expected entries have to be of type string"), kTypes.has(value) ? types.push(value.toLowerCase()) : classRegExp.exec(value) === null ? (assert.ok(value !== "object", 'The value "object" should be written as "Object"'), other.push(value)) : instances.push(value); if (instances.length > 0) { let pos = types.indexOf("object"); pos !== -1 && (types.slice(pos, 1), instances.push("Object")); } return types.length > 0 && (message += `${types.length > 1 ? "one of type" : "of type"} ${formatList(types, "or")}`, (instances.length > 0 || other.length > 0) && (message += " or ")), instances.length > 0 && (message += `an instance of ${formatList(instances, "or")}`, other.length > 0 && (message += " or ")), other.length > 0 && (other.length > 1 ? message += `one of ${formatList(other, "or")}` : (other[0]?.toLowerCase() !== other[0] && (message += "an "), message += `${other[0]}`)), message += `. Received ${determineSpecificType(actual)}`, message; }, TypeError), ERR_INVALID_MODULE_SPECIFIER = createError( "ERR_INVALID_MODULE_SPECIFIER", /** * @param {string} request * @param {string} reason * @param {string} [base] */ (request, reason, base) => `Invalid module "${request}" ${reason}${base ? ` imported from ${base}` : ""}`, TypeError ), ERR_INVALID_PACKAGE_CONFIG = createError("ERR_INVALID_PACKAGE_CONFIG", (path$1, base, message) => `Invalid package config ${path$1}${base ? ` while importing ${base}` : ""}${message ? `. ${message}` : ""}`, Error), ERR_INVALID_PACKAGE_TARGET = createError("ERR_INVALID_PACKAGE_TARGET", (packagePath, key, target, isImport = !1, base) => { let relatedError = typeof target == "string" && !isImport && target.length > 0 && !target.startsWith("./"); return key === "." ? (assert.ok(isImport === !1), `Invalid "exports" main target ${JSON.stringify(target)} defined in the package config ${packagePath}package.json${base ? ` imported from ${base}` : ""}${relatedError ? '; targets must start with "./"' : ""}`) : `Invalid "${isImport ? "imports" : "exports"}" target ${JSON.stringify(target)} defined for '${key}' in the package config ${packagePath}package.json${base ? ` imported from ${base}` : ""}${relatedError ? '; targets must start with "./"' : ""}`; }, Error), ERR_MODULE_NOT_FOUND = createError("ERR_MODULE_NOT_FOUND", (path$1, base, exactUrl = !1) => `Cannot find ${exactUrl ? "module" : "package"} '${path$1}' imported from ${base}`, Error), ERR_NETWORK_IMPORT_DISALLOWED = createError("ERR_NETWORK_IMPORT_DISALLOWED", "import of '%s' by %s is not supported: %s", Error), ERR_PACKAGE_IMPORT_NOT_DEFINED = createError("ERR_PACKAGE_IMPORT_NOT_DEFINED", (specifier, packagePath, base) => `Package import specifier "${specifier}" is not defined${packagePath ? ` in package ${packagePath || ""}package.json` : ""} imported from ${base}`, TypeError), ERR_PACKAGE_PATH_NOT_EXPORTED = createError( "ERR_PACKAGE_PATH_NOT_EXPORTED", /** * @param {string} packagePath * @param {string} subpath * @param {string} [base] */ (packagePath, subpath, base) => subpath === "." ? `No "exports" main defined in ${packagePath}package.json${base ? ` imported from ${base}` : ""}` : `Package subpath '${subpath}' is not defined by "exports" in ${packagePath}package.json${base ? ` imported from ${base}` : ""}`, Error ), ERR_UNSUPPORTED_DIR_IMPORT = createError("ERR_UNSUPPORTED_DIR_IMPORT", "Directory import '%s' is not supported resolving ES modules imported from %s", Error), ERR_UNSUPPORTED_RESOLVE_REQUEST = createError("ERR_UNSUPPORTED_RESOLVE_REQUEST", 'Failed to resolve module specifier "%s" from "%s": Invalid relative URL or base scheme is not hierarchical.', TypeError), ERR_UNKNOWN_FILE_EXTENSION = createError("ERR_UNKNOWN_FILE_EXTENSION", (extension, path$1) => `Unknown file extension "${extension}" for ${path$1}`, TypeError), ERR_INVALID_ARG_VALUE = createError("ERR_INVALID_ARG_VALUE", (name, value, reason = "is invalid") => { let inspected = inspect(value); return inspected.length > 128 && (inspected = `${inspected.slice(0, 128)}...`), `The ${name.includes(".") ? "property" : "argument"} '${name}' ${reason}. Received ${inspected}`; }, TypeError), hasOwnProperty$1 = {}.hasOwnProperty; var hasOwnProperty = {}.hasOwnProperty; var RegExpPrototypeSymbolReplace = RegExp.prototype[Symbol.replace], own = {}.hasOwnProperty; var isWindows = process.platform === "win32", globalCache = globalThis.__EXSOLVE_CACHE__ ||= /* @__PURE__ */ new Map(); // ../../core/src/shared/utils/module.ts var importMetaResolve = (...args) => typeof import.meta.resolve != "function" && process.env.VITEST === "true" ? (console.warn( "importMetaResolve from within Storybook is being used in a Vitest test, but it shouldn't be. Please report this at https://github.com/storybookjs/storybook/issues/new?template=bug_report.yml" ), pathToFileURL(args[0]).href) : import.meta.resolve(...args), resolvePackageDir = (pkg, parent) => { try { return dirname(fileURLToPath(importMetaResolve(join(pkg, "package.json"), parent))); } catch { try { return dirname(fileURLToPath(importMetaResolve(join(pkg, "package.json")))); } catch { let req = createRequire(parent ?? import.meta.url); return dirname(req.resolve(join(pkg, "package.json"))); } } }; // src/componentManifest/reactDocgen/extractReactDocgenInfo.ts import { logger } from "storybook/internal/node-logger"; function getComponentDocgen(filePath, componentName) { try { let code = cachedReadFileSync(filePath, "utf-8"), docgens = parseWithReactDocgen(code, filePath); if (docgens.length === 0) return null; if (componentName) { let matchingDocgen = docgens.find( (docgen) => docgen.actualName === componentName || docgen.displayName === componentName || docgen.exportName === componentName ); return matchingDocgen ? { componentName: matchingDocgen.actualName || matchingDocgen.displayName || componentName, reactDocgen: { type: "success", data: matchingDocgen } } : null; } let firstDocgen = docgens[0]; return { componentName: firstDocgen.actualName || firstDocgen.displayName || "Unknown", reactDocgen: { type: "success", data: firstDocgen } }; } catch (error) { return logger.debug(`Error parsing component file for docgen ${filePath}: ${error}`), null; } } var extractArgTypesFromDocgen = ({ componentFilePath, componentExportName }) => { let docgen = getComponentDocgen(componentFilePath, componentExportName); return !docgen || docgen.reactDocgen.type !== "success" ? null : extractArgTypes({ __docgenInfo: docgen.reactDocgen.data }) ?? {}; }; // src/componentManifest/reactDocgen/extractReactTypescriptDocgenInfo.ts import { logger as logger2 } from "storybook/internal/node-logger"; // src/componentManifest/reactDocgen/utils.ts import { dirname as dirname2 } from "path"; var REACT_NODE_TYPES = /* @__PURE__ */ new Set([ "JSX.Element", "ComponentType", "ReactComponentType", "ReactElement", "ReactReactElement", "ElementType", "ReactElementType", "ReactNode", "ReactReactNode" ]), KNOWN_TYPE_NAMES = /* @__PURE__ */ new Set([ "boolean", "string", "number", "Date", ...REACT_NODE_TYPES, "signature", "union", "Array", "tuple", "literal", "null", "void", "any", "unknown" ]); var getTsConfig = async () => { try { let ts = await import("typescript"), tsconfigPath = ts.findConfigFile(process.cwd(), ts.sys.fileExists); if (console.log({ tsconfigPath }), tsconfigPath === void 0) return {}; let basePath = dirname2(tsconfigPath), { config, error } = ts.readConfigFile(tsconfigPath, ts.sys.readFile); return error ? {} : ts.parseJsonConfigFileContent(config, ts.sys, basePath, {}, tsconfigPath).options; } catch { return {}; } }; // src/componentManifest/reactDocgen/extractReactTypescriptDocgenInfo.ts var extractArgTypesFromDocgenTypescript = async ({ componentFilePath, componentExportName, reactDocgenTypescriptOptions }) => { try { let { withCompilerOptions } = (await import("react-docgen-typescript")).default, defaultOptions = { shouldExtractLiteralValuesFromEnum: !0, shouldRemoveUndefinedFromOptional: !0, propFilter: (prop) => prop.parent ? !/node_modules/.test(prop.parent.fileName) : !0, // We *need* this set so that RDT returns default values in the same format as react-docgen savePropValueAsString: !0 }, tsConfig = await getTsConfig(), mergedOptions = { ...defaultOptions, ...reactDocgenTypescriptOptions, // Always ensure savePropValueAsString is true for consistency savePropValueAsString: !0 }, docgens = withCompilerOptions( { ...tsConfig, noErrorTruncation: !0, strict: !0 }, mergedOptions ).parse(componentFilePath); if (!docgens || docgens.length === 0) return null; let targetDocgen = docgens[0]; if (componentExportName) { let match = docgens.find( (d) => d.displayName === componentExportName || d.filePath?.includes(componentExportName) ); if (match) targetDocgen = match; else return null; } return extractArgTypes({ __docgenInfo: targetDocgen }) ?? {}; } catch (error) { return logger2.debug( `Error parsing component file with react-docgen-typescript ${componentFilePath}: ${error}` ), null; } }; // src/componentManifest/generator.ts import { getStoryImportPathFromEntry, selectComponentEntriesByComponentId } from "storybook/internal/common"; // src/componentManifest/componentMetaManagerSingleton.ts import { logger as logger3 } from "storybook/internal/node-logger"; var componentMetaManagerPromise; function getSharedComponentMetaManager() { return componentMetaManagerPromise || (componentMetaManagerPromise = (async () => { try { let ts = await import("typescript"); return new ComponentMetaManager(ts); } catch { logger3.debug( "[reactComponentMeta] TypeScript not available, skipping component meta extraction." ); return; } })()), componentMetaManagerPromise; } // src/componentManifest/generator.ts var manifests = async (existingManifests = {}, options) => { let { manifestEntries, presets, watch } = options, typescriptOptions = await presets?.apply("typescript", {}) ?? {}, features = await presets?.apply("features", {}); if (features?.experimentalDocgenServer) return { ...existingManifests, components: { v: 0, components: {}, meta: { docgen: "react-component-meta", durationMs: 0 } } }; let docgenEngine = features?.experimentalReactComponentMeta ? "react-component-meta" : typescriptOptions.reactDocgen || "react-docgen"; invalidateCache(), invalidateParser(); let startTime = performance.now(), manager = docgenEngine === "react-component-meta" ? await getSharedComponentMetaManager() : void 0, entriesByUniqueComponent = [ ...selectComponentEntriesByComponentId(manifestEntries).values() ], resolvedEntries = await Promise.all( entriesByUniqueComponent.map(async (entry) => { let storyFilePath = getStoryImportPathFromEntry(entry); if (!storyFilePath) throw new Error(`No story file path for index entry ${entry.id}`); let storyPath = path.join(process.cwd(), storyFilePath), resolved = await resolveStoryFileComponents({ storyPath, title: entry.title, typescriptOptions, docgenEngine }); return { entry, storyFilePath, ...resolved }; }) ); docgenEngine === "react-component-meta" && manager && manager.batchExtract( resolvedEntries.flatMap(({ storyPath, component, subcomponents }) => [ ...component ? [{ storyPath, component }] : [], ...subcomponents.filter( (subcomponent) => subcomponent.component !== void 0 ).map((subcomponent) => ({ storyPath, component: subcomponent.component })) ]) ); let manifestEntryIds = new Set(manifestEntries.map((entry) => entry.id)), components = resolvedEntries.map( ({ storyPath, component, entry, storyFilePath, storyFile, csf, componentName, allComponents, subcomponents }) => buildReactComponentDocgenFromResolved({ entry, storyPath, storyFilePath, storyFile, csf, componentName, component, allComponents, subcomponents, docgenEngine, filterStoryIds: manifestEntryIds }) ).filter((component) => component !== void 0); manager && watch && manager.startWatching(); let durationMs = Math.round(performance.now() - startTime); return { ...existingManifests, components: { v: 0, components: Object.fromEntries(components.map((component) => [component.id, component])), meta: { docgen: docgenEngine, durationMs } } }; }; // src/enrichCsf.ts import { recast, types as t } from "storybook/internal/babel"; import { getPrettier } from "storybook/internal/common"; var enrichCsf = async (input, options) => { let features = await options.presets.apply("features"); if (!(!features.experimentalCodeExamples || features.experimentalDocgenServer)) return async (csf, csfSource) => { let promises = Object.keys(csf._stories).map(async (key) => { if (!csfSource._meta?.component) return; let { format: format2 } = await getPrettier(), node, snippet; try { node = getCodeSnippet(csfSource, key, csfSource._meta?.component); } catch (e) { if (!(e instanceof Error)) return; snippet = e.message; } try { !snippet && node && (snippet = await format2(recast.print(node).code, { filepath: join(process.cwd(), "component.tsx") })); } catch (e) { if (!(e instanceof Error)) return; snippet = e.message; } if (!snippet) return; let originalParameters = t.memberExpression( csf._metaIsFactory ? t.memberExpression(t.identifier(key), t.identifier("input")) : t.identifier(key), t.identifier("parameters") ), docsParameter = t.optionalMemberExpression( originalParameters, t.identifier("docs"), !1, !0 ); csf._ast.program.body.push( t.expressionStatement( t.assignmentExpression( "=", originalParameters, t.objectExpression([ t.spreadElement(originalParameters), t.objectProperty( t.identifier("docs"), t.objectExpression([ t.spreadElement(docsParameter), t.objectProperty( t.identifier("source"), t.objectExpression([ t.objectProperty(t.identifier("code"), t.stringLiteral(snippet)), t.spreadElement( t.optionalMemberExpression( docsParameter, t.identifier("source"), !1, !0 ) ) ]) ) ]) ) ]) ) ) ); }); await Promise.all(promises); }; }; // src/docgen/preset.ts import { fileURLToPath as fileURLToPath2 } from "node:url"; var experimental_docgenProvider = async (existing = []) => [ ...existing, { moduleSpecifier: fileURLToPath2(import.meta.resolve("@storybook/react/internal/docgen-worker")) } ]; // src/docgen/story-docs-preset.ts import { STORY_FILE_TEST_REGEXP, getStoryImportPathFromEntry as getStoryImportPathFromEntry3 } from "storybook/internal/common"; // src/docgen/build-story-docs.ts import { getStoryImportPathFromEntry as getStoryImportPathFromEntry2 } from "storybook/internal/common"; async function buildStoryDocsPayload(input, context) { let storyFilePath = getStoryImportPathFromEntry2(input.entry); if (!storyFilePath) return; let storyPath = (context.resolvePath ?? ((importPath) => path.join(process.cwd(), importPath)))(storyFilePath), resolved; try { resolved = await resolveStoryFileComponents({ storyPath, title: input.entry.title, typescriptOptions: context.typescriptOptions, docgenEngine: "react-component-meta" }); } catch { return; } let { csf, componentName, component, allComponents } = resolved; return buildStoryDocsFromResolved({ entry: input.entry, storyPath, storyFilePath, csf, componentName, component, allComponents }); } // src/docgen/story-docs-preset.ts var experimental_storyDocsProvider = async (nextStoryDocs, options) => { let typescriptOptionsPromise = options.presets?.apply( "typescript", {} ) ?? Promise.resolve({}); return async (input) => { let storyImportPath = getStoryImportPathFromEntry3(input.entry); if (!storyImportPath || !STORY_FILE_TEST_REGEXP.test(storyImportPath)) return nextStoryDocs(input); let ours = await buildStoryDocsPayload(input, { typescriptOptions: await typescriptOptionsPromise }); return ours ? { ...await nextStoryDocs(input), ...ours } : nextStoryDocs(input); }; }; // src/preset.ts var addons = [ import.meta.resolve("@storybook/react-dom-shim/preset") ], previewAnnotations = async (input = [], options) => { let [docsConfig, features] = await Promise.all([ options.presets.apply("docs", {}, options), options.presets.apply("features", {}, options) ]), docsEnabled = Object.keys(docsConfig).length > 0, experimentalRSC = features?.experimentalRSC; return [].concat(input).concat([ fileURLToPath3(import.meta.resolve("@storybook/react/entry-preview")), fileURLToPath3(import.meta.resolve("@storybook/react/entry-preview-argtypes")) ]).concat( docsEnabled ? [fileURLToPath3(import.meta.resolve("@storybook/react/entry-preview-docs"))] : [] ).concat( experimentalRSC ? [fileURLToPath3(import.meta.resolve("@storybook/react/entry-preview-rsc"))] : [] ); }, resolvedReact = async (existing) => { try { return { ...existing, react: resolvePackageDir("react"), reactDom: resolvePackageDir("react-dom") }; } catch { return existing; } }; async function internal_getArgTypesData(_input, options) { let { componentFilePath, componentExportName, presets } = options ?? {}; if (!componentFilePath) return null; let typescriptOptions = {}; if (presets) try { typescriptOptions = await presets.apply("typescript", {}); } catch { } let { reactDocgen = "react-docgen", reactDocgenTypescriptOptions } = typescriptOptions; if (reactDocgen === !1) return null; let resolvedFilePath = path2.isAbsolute(componentFilePath) ? componentFilePath : join2(getProjectRoot(), componentFilePath), argTypesData; return reactDocgen === "react-docgen-typescript" ? argTypesData = await extractArgTypesFromDocgenTypescript({ componentFilePath: resolvedFilePath, componentExportName, reactDocgenTypescriptOptions }) : argTypesData = extractArgTypesFromDocgen({ componentFilePath: resolvedFilePath, componentExportName }), argTypesData; } var optimizeViteDeps = ["react-dom/test-utils"]; export { addons, experimental_docgenProvider, enrichCsf as experimental_enrichCsf, manifests as experimental_manifests, experimental_storyDocsProvider, internal_getArgTypesData, optimizeViteDeps, previewAnnotations, resolvedReact };