storybook-builder-rsbuild
Version:
Rsbuild builder for Storybook
934 lines (910 loc) • 40.6 kB
JavaScript
import CJS_COMPAT_NODE_URL_9822f21edcc46e85 from 'node:url';
import CJS_COMPAT_NODE_PATH_9822f21edcc46e85 from 'node:path';
import CJS_COMPAT_NODE_MODULE_9822f21edcc46e85 from "node:module";
var __filename = CJS_COMPAT_NODE_URL_9822f21edcc46e85.fileURLToPath(import.meta.url);
var __dirname = CJS_COMPAT_NODE_PATH_9822f21edcc46e85.dirname(__filename);
var require = CJS_COMPAT_NODE_MODULE_9822f21edcc46e85.createRequire(import.meta.url);
// ------------------------------------------------------------
// end of CJS compatibility banner, injected by Storybook's esbuild configuration
// ------------------------------------------------------------
import {
__commonJS,
__require,
__toESM
} from "./_node-chunks/chunk-L73IVHZJ.js";
// ../../node_modules/.pnpm/pretty-hrtime@1.0.3/node_modules/pretty-hrtime/index.js
var require_pretty_hrtime = __commonJS({
"../../node_modules/.pnpm/pretty-hrtime@1.0.3/node_modules/pretty-hrtime/index.js"(exports, module) {
"use strict";
var minimalDesc = ["h", "min", "s", "ms", "\u03BCs", "ns"], verboseDesc = ["hour", "minute", "second", "millisecond", "microsecond", "nanosecond"], convert = [3600, 60, 1, 1e6, 1e3, 1];
module.exports = function(source, opts) {
var verbose, precise, i, spot, sourceAtStep, valAtStep, decimals, strAtStep, results, totalSeconds;
if (verbose = !1, precise = !1, opts && (verbose = opts.verbose || !1, precise = opts.precise || !1), !Array.isArray(source) || source.length !== 2 || typeof source[0] != "number" || typeof source[1] != "number")
return "";
for (source[1] < 0 && (totalSeconds = source[0] + source[1] / 1e9, source[0] = parseInt(totalSeconds), source[1] = parseFloat((totalSeconds % 1).toPrecision(9)) * 1e9), results = "", i = 0; i < 6 && (spot = i < 3 ? 0 : 1, sourceAtStep = source[spot], i !== 3 && i !== 0 && (sourceAtStep = sourceAtStep % convert[i - 1]), i === 2 && (sourceAtStep += source[1] / 1e9), valAtStep = sourceAtStep / convert[i], !(valAtStep >= 1 && (verbose && (valAtStep = Math.floor(valAtStep)), precise ? strAtStep = valAtStep.toString() : (decimals = valAtStep >= 10 ? 0 : 2, strAtStep = valAtStep.toFixed(decimals)), strAtStep.indexOf(".") > -1 && strAtStep[strAtStep.length - 1] === "0" && (strAtStep = strAtStep.replace(/\.?0+$/, "")), results && (results += " "), results += strAtStep, verbose ? (results += " " + verboseDesc[i], strAtStep !== "1" && (results += "s")) : results += " " + minimalDesc[i], !verbose))); i++)
;
return results;
};
}
});
// src/index.ts
var import_pretty_hrtime = __toESM(require_pretty_hrtime(), 1);
import { createServer } from "node:net";
import { dirname as dirname2, join as join4, parse } from "node:path";
import * as rsbuildReal from "@rsbuild/core";
import fs from "fs-extra";
import sirv from "sirv";
import { getPresets, resolveAddonName } from "storybook/internal/common";
import { WebpackInvocationError } from "storybook/internal/server-errors";
// src/chromatic-stats.ts
import { isAbsolute, relative } from "node:path";
var isRecord = (val) => val != null && typeof val == "object" && Array.isArray(val) === !1, toPosixPath = (filePath) => filePath.split("\\").join("/"), toNormalizedModulePath = (modulePath) => typeof modulePath != "string" || modulePath.length === 0 ? null : isAbsolute(modulePath) ? toPosixPath(relative(process.cwd(), modulePath)) : toPosixPath(modulePath), toAbsolutePathFromIdentifier = (identifier) => {
if (typeof identifier != "string" || identifier.length === 0)
return null;
let withoutQuery = identifier.slice(identifier.lastIndexOf("!") + 1).split("|")[0].split("?")[0];
return isAbsolute(withoutQuery) ? withoutQuery : null;
}, toReasonModuleName = (reason) => {
let moduleName = typeof reason.moduleName == "string" ? reason.moduleName : null;
if (typeof moduleName == "string" && /\s\+\s\d+\smodules?$/.test(moduleName) === !1)
return moduleName;
let fallbackAbsolutePath = toAbsolutePathFromIdentifier(reason.resolvedModuleIdentifier) ?? toAbsolutePathFromIdentifier(reason.moduleIdentifier), fallbackModuleName = toNormalizedModulePath(fallbackAbsolutePath);
return typeof fallbackModuleName == "string" && fallbackModuleName.includes("node_modules/") === !1 ? fallbackModuleName : moduleName;
}, isConcatenatedModuleName = (moduleName) => /\s\+\s\d+\smodules?$/.test(moduleName), toChromaticModule = (moduleInfo) => {
let rawName = typeof moduleInfo.name == "string" ? moduleInfo.name : null, normalizedNameForCondition = toNormalizedModulePath(
moduleInfo.nameForCondition
), name = moduleInfo.id == null && normalizedNameForCondition ? normalizedNameForCondition : rawName ?? normalizedNameForCondition;
if (typeof name != "string")
return null;
let normalizedId = (typeof moduleInfo.id == "string" || typeof moduleInfo.id == "number" ? moduleInfo.id : null) ?? name, normalizedReasons = Array.isArray(moduleInfo.reasons) ? Array.from(
new Set(
moduleInfo.reasons.filter(isRecord).map(toReasonModuleName).filter(
(moduleName) => typeof moduleName == "string"
)
)
).map((moduleName) => ({
moduleName
})) : [], normalizedNestedModules = Array.isArray(moduleInfo.modules) ? Array.from(
new Set(
moduleInfo.modules.filter(isRecord).map((nestedModule) => toNormalizedModulePath(nestedModule.name)).filter(
(nestedModuleName) => typeof nestedModuleName == "string"
)
)
).map((nestedModuleName) => ({
name: nestedModuleName
})) : [];
return normalizedId === null && normalizedReasons.length === 0 && normalizedNestedModules.length === 0 ? null : {
id: normalizedId,
name,
...normalizedNestedModules.length > 0 ? { modules: normalizedNestedModules } : {},
...normalizedReasons.length > 0 ? { reasons: normalizedReasons } : {}
};
}, collectModuleEntries = (moduleEntries, collected) => {
for (let entry of moduleEntries)
isRecord(entry) && (collected.push(entry), Array.isArray(entry.children) && collectModuleEntries(entry.children, collected), Array.isArray(entry.modules) && collectModuleEntries(entry.modules, collected));
}, withChromaticMinimalContract = (statsJson) => {
if (!isRecord(statsJson))
return statsJson;
let modules = statsJson.modules;
if (!Array.isArray(modules))
return statsJson;
let normalizedStatsJson = statsJson, flattenedModules = [];
collectModuleEntries(modules, flattenedModules);
let normalizedModules = [], visited = /* @__PURE__ */ new Set();
for (let moduleInfo of flattenedModules) {
let normalizedModule = toChromaticModule(moduleInfo);
if (!normalizedModule)
continue;
let moduleKey = `${String(normalizedModule.id)}::${normalizedModule.name}`;
if (!visited.has(moduleKey) && (visited.add(moduleKey), normalizedModules.push(normalizedModule), isConcatenatedModuleName(normalizedModule.name) && Array.isArray(normalizedModule.modules)))
for (let nestedModule of normalizedModule.modules) {
let nestedModuleKey = `${nestedModule.name}::${nestedModule.name}`;
visited.has(nestedModuleKey) || (visited.add(nestedModuleKey), normalizedModules.push({
id: nestedModule.name,
name: nestedModule.name,
...Array.isArray(normalizedModule.reasons) && normalizedModule.reasons.length > 0 ? { reasons: normalizedModule.reasons } : {}
}));
}
}
return normalizedStatsJson.modules = normalizedModules, normalizedStatsJson;
}, withStatsJsonCompat = (stats) => {
let originalToJsonCandidate = stats.toJson;
if (typeof originalToJsonCandidate != "function")
return stats;
let originalToJson = originalToJsonCandidate.bind(stats), toJsonWithCompat = (options, forToString) => {
if (options == null || typeof options == "object") {
let statsJson = originalToJson(
{
modules: !0,
reasons: !0,
nestedModules: !0,
...options
},
forToString
);
return withChromaticMinimalContract(statsJson);
}
return withChromaticMinimalContract(originalToJson(options, forToString));
};
return stats.toJson = toJsonWithCompat, stats;
};
// src/logger.ts
import { logger as rsbuildLogger } from "@rsbuild/core";
import picocolors from "picocolors";
import { logger } from "storybook/internal/node-logger";
function overrideRsbuildLogger() {
let logWithPrefix = (fn) => (msg) => fn(
`${picocolors.black(picocolors.bgBlueBright("Rsbuild"))} ${String(msg)}`
);
rsbuildLogger.override({
error: logWithPrefix(logger.error),
warn: logWithPrefix(logger.warn),
info: logWithPrefix((msg) => logger.log(msg, { spacing: 0 })),
start: logWithPrefix(logger.info),
ready: logWithPrefix(logger.info),
success: logWithPrefix(logger.info),
log: logWithPrefix(logger.info),
debug: logWithPrefix(logger.debug)
});
}
// src/preview/iframe-rsbuild.config.ts
import { createRequire } from "node:module";
import { dirname, join as join2, resolve as resolve3 } from "node:path";
import { fileURLToPath } from "node:url";
import { loadConfig, mergeRsbuildConfig, rspack } from "@rsbuild/core";
import { pluginTypeCheck } from "@rsbuild/plugin-type-check";
import CaseSensitivePathsPlugin from "case-sensitive-paths-webpack-plugin";
import { pluginHtmlMinifierTerser } from "rsbuild-plugin-html-minifier-terser";
import {
getBuilderOptions as getBuilderOptions2,
isPreservingSymlinks,
normalizeStories as normalizeStories2,
stringifyProcessEnvs
} from "storybook/internal/common";
import { logger as logger3 } from "storybook/internal/node-logger";
import { globalsNameReferenceMap } from "storybook/internal/preview/globals";
import { dedent } from "ts-dedent";
// src/preview/detect-msw.ts
import { existsSync } from "node:fs";
import { isAbsolute as isAbsolute2, resolve } from "node:path";
var MSW_SW_FILE = "mockServiceWorker.js";
async function isMswActive(options) {
let staticDirs = await options.presets.apply("staticDirs", []);
return Array.isArray(staticDirs) ? staticDirs.some((entry) => {
let dir = typeof entry == "string" ? entry : entry?.from;
if (typeof dir != "string" || dir.length === 0) return !1;
let abs = isAbsolute2(dir) ? dir : resolve(options.configDir, dir);
return existsSync(resolve(abs, MSW_SW_FILE));
}) : !1;
}
// src/preview/virtual-module-mapping.ts
import { join, resolve as resolve2 } from "node:path";
// ../../node_modules/.pnpm/slash@5.1.0/node_modules/slash/index.js
function slash(path) {
return path.startsWith("\\\\?\\") ? path : path.replace(/\\/g, "/");
}
// src/preview/virtual-module-mapping.ts
import {
getBuilderOptions,
loadPreviewOrConfigFile,
normalizeStories,
readTemplate
} from "storybook/internal/common";
// compiled/@storybook/core-webpack/index.js
import { createRequire as __WEBPACK_EXTERNAL_createRequire } from "module";
import * as __WEBPACK_EXTERNAL_MODULE_storybook_internal_common_7991bda6__ from "storybook/internal/common";
import * as __WEBPACK_EXTERNAL_MODULE_storybook_internal_node_logger_87de4309__ from "storybook/internal/node-logger";
var __webpack_modules__ = {
980: (__unused_webpack_module, exports) => {
var __webpack_unused_export__;
__webpack_unused_export__ = { value: !0 }, exports.TW = void 0;
function dedent2(templ) {
for (var values = [], _i = 1; _i < arguments.length; _i++)
values[_i - 1] = arguments[_i];
var strings = Array.from(typeof templ == "string" ? [templ] : templ);
strings[strings.length - 1] = strings[strings.length - 1].replace(
/\r?\n([\t ]*)$/,
""
);
var indentLengths = strings.reduce(function(arr, str) {
var matches = str.match(/\n([\t ]+|(?!\s).)/g);
return matches ? arr.concat(
matches.map(function(match) {
var _a, _b;
return (_b = (_a = match.match(/[\t ]/g)) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0;
})
) : arr;
}, []);
if (indentLengths.length) {
var pattern_1 = new RegExp(
`
[ ]{` + Math.min.apply(Math, indentLengths) + "}",
"g"
);
strings = strings.map(function(str) {
return str.replace(pattern_1, `
`);
});
}
strings[0] = strings[0].replace(/^\r?\n/, "");
var string = strings[0];
return values.forEach(function(value, i) {
var endentations = string.match(/(?:^|\n)( *)$/), endentation = endentations ? endentations[1] : "", indentedValue = value;
typeof value == "string" && value.includes(`
`) && (indentedValue = String(value).split(`
`).map(function(str, i2) {
return i2 === 0 ? str : "" + endentation + str;
}).join(`
`)), string += indentedValue + strings[i + 1];
}), string;
}
exports.TW = dedent2, __webpack_unused_export__ = dedent2;
}
}, __webpack_module_cache__ = {};
function __nccwpck_require__(moduleId) {
var cachedModule = __webpack_module_cache__[moduleId];
if (cachedModule !== void 0)
return cachedModule.exports;
var module = __webpack_module_cache__[moduleId] = { exports: {} }, threw = !0;
try {
__webpack_modules__[moduleId](module, module.exports, __nccwpck_require__), threw = !1;
} finally {
threw && delete __webpack_module_cache__[moduleId];
}
return module.exports;
}
__nccwpck_require__.d = (exports, definition) => {
for (var key in definition)
__nccwpck_require__.o(definition, key) && !__nccwpck_require__.o(exports, key) && Object.defineProperty(exports, key, {
enumerable: !0,
get: definition[key]
});
};
__nccwpck_require__.o = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop);
typeof __nccwpck_require__ < "u" && (__nccwpck_require__.ab = new URL(".", import.meta.url).pathname.slice(
import.meta.url.match(/^file:\/\/\/\w:/) ? 1 : 0,
-1
) + "/");
var __webpack_exports__ = {};
__nccwpck_require__.d(__webpack_exports__, {
il: () => checkWebpackVersion,
Tu: () => loadCustomWebpackConfig,
SV: () => mergeConfigs,
T1: () => toImportFn,
i8: () => toImportFnPart,
sg: () => toRequireContext,
HO: () => toRequireContextString,
hO: () => webpackIncludeRegexp
});
var external_node_url_namespaceObject = __WEBPACK_EXTERNAL_createRequire(
import.meta.url
)("node:url"), external_node_path_namespaceObject = __WEBPACK_EXTERNAL_createRequire(
import.meta.url
)("node:path"), external_node_module_namespaceObject = __WEBPACK_EXTERNAL_createRequire(
import.meta.url
)("node:module"), x = (y) => {
var x2 = {};
return __nccwpck_require__.d(x2, y), x2;
};
var common_namespaceObject = x({
globToRegexp: () => __WEBPACK_EXTERNAL_MODULE_storybook_internal_common_7991bda6__.globToRegexp,
serverRequire: () => __WEBPACK_EXTERNAL_MODULE_storybook_internal_common_7991bda6__.serverRequire
}), node_logger_x = (y) => {
var x2 = {};
return __nccwpck_require__.d(x2, y), x2;
};
var node_logger_namespaceObject = node_logger_x({
logger: () => __WEBPACK_EXTERNAL_MODULE_storybook_internal_node_logger_87de4309__.logger
}), dist = __nccwpck_require__(980), dist_filename = external_node_url_namespaceObject.fileURLToPath(
import.meta.url
), dist_dirname = external_node_path_namespaceObject.dirname(dist_filename), dist_require = external_node_module_namespaceObject.createRequire(
import.meta.url
), webpackConfigs = ["webpack.config", "webpackfile"], loadCustomWebpackConfig = async (configDir) => (0, common_namespaceObject.serverRequire)(
webpackConfigs.map(
(configName) => (0, external_node_path_namespaceObject.resolve)(configDir, configName)
)
), checkWebpackVersion = (webpack, specifier, caption) => {
if (!webpack.version) {
node_logger_namespaceObject.logger.info(
"Skipping webpack version check, no version available"
);
return;
}
webpack.version !== specifier && node_logger_namespaceObject.logger.warn((0, dist.TW)`
Unexpected webpack version in ${caption}:
- Received '${webpack.version}'
- Expected '${specifier}'
If you're using Webpack 5 in SB6.2 and upgrading, consider: https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#webpack-5-manager-build
For more info about Webpack 5 support: https://gist.github.com/shilman/8856ea1786dcd247139b47b270912324#troubleshooting
`);
};
function mergePluginsField(defaultPlugins = [], customPlugins = []) {
return [...defaultPlugins, ...customPlugins];
}
function mergeRulesField(defaultRules = [], customRules = []) {
return [...defaultRules, ...customRules];
}
function mergeExtensionsField({ extensions: defaultExtensions = [] }, { extensions: customExtensions = [] }) {
return [...defaultExtensions, ...customExtensions];
}
function mergeAliasField({ alias: defaultAlias = {} }, { alias: customAlias = {} }) {
return { ...defaultAlias, ...customAlias };
}
function mergeModuleField(a, b) {
return { ...a, ...b, rules: mergeRulesField(a.rules || [], b.rules || []) };
}
function mergeResolveField({ resolve: defaultResolve = {} }, { resolve: customResolve = {} }) {
return {
...defaultResolve,
...customResolve,
alias: mergeAliasField(defaultResolve, customResolve),
extensions: mergeExtensionsField(defaultResolve, customResolve)
};
}
function mergeOptimizationField({ optimization: defaultOptimization = {} }, { optimization: customOptimization = {} }) {
return { ...defaultOptimization, ...customOptimization };
}
function mergeConfigs(config, customConfig) {
return {
...customConfig,
...config,
devtool: customConfig.devtool || config.devtool,
plugins: mergePluginsField(config.plugins, customConfig.plugins),
module: mergeModuleField(config.module || {}, customConfig.module || {}),
resolve: mergeResolveField(config, customConfig),
optimization: mergeOptimizationField(config, customConfig)
};
}
function importPipeline() {
let importGate = Promise.resolve();
return async (importFn) => {
await importGate;
let moduleExportsPromise = importFn();
return importGate = importGate.then(async () => {
await moduleExportsPromise;
}), moduleExportsPromise;
};
}
function adjustRegexToExcludeNodeModules(originalRegex) {
let originalRegexString = originalRegex.source, startsWithCaret = originalRegexString.startsWith("^"), excludeNodeModulesPattern = startsWithCaret ? "(?!.*node_modules)" : "^(?!.*node_modules)", adjustedRegexString = startsWithCaret ? `^${excludeNodeModulesPattern}${originalRegexString.substring(1)}` : excludeNodeModulesPattern + originalRegexString;
return new RegExp(adjustedRegexString);
}
function webpackIncludeRegexp(specifier) {
let { directory, files } = specifier, directoryWithoutLeadingDots = directory.replace(/^(\.+\/)+/, "/"), webpackIncludeGlob = [".", ".."].includes(directory) ? files : `${directoryWithoutLeadingDots}/${files}`, webpackIncludeRegexpWithCaret = webpackIncludeGlob.includes("node_modules") ? (0, common_namespaceObject.globToRegexp)(webpackIncludeGlob) : adjustRegexToExcludeNodeModules(
(0, common_namespaceObject.globToRegexp)(webpackIncludeGlob)
);
return new RegExp(webpackIncludeRegexpWithCaret.source.replace(/^\^/, ""));
}
function toImportFnPart(specifier) {
let { directory, importPathMatcher } = specifier;
return (0, dist.TW)`
async (path) => {
if (!${importPathMatcher}.exec(path)) {
return;
}
const pathRemainder = path.substring(${directory.length + 1});
return import(
/* webpackChunkName: "[request]" */
/* webpackInclude: ${webpackIncludeRegexp(specifier)} */
'${directory}/' + pathRemainder
);
}
`;
}
function toImportFn(stories, { needPipelinedImport } = {}) {
let pipelinedImport = "const pipeline = (x) => x();";
return needPipelinedImport && (pipelinedImport = `
const importPipeline = ${importPipeline};
const pipeline = importPipeline();
`), (0, dist.TW)`
${pipelinedImport}
const importers = [
${stories.map(toImportFnPart).join(`,
`)}
];
export async function importFn(path) {
for (let i = 0; i < importers.length; i++) {
const moduleExports = await pipeline(() => importers[i](path));
if (moduleExports) {
return moduleExports;
}
}
}
`;
}
var toRequireContext = (specifier) => {
let { directory, files } = specifier, match = (0, common_namespaceObject.globToRegexp)(`./${files}`);
return {
path: directory,
recursive: files.includes("**") || files.split("/").length > 1,
match
};
}, toRequireContextString = (specifier) => {
let { path: p, recursive: r, match: m } = toRequireContext(specifier);
return `require.context('${p}', ${r}, ${m})`;
}, __webpack_exports__checkWebpackVersion = __webpack_exports__.il, __webpack_exports__loadCustomWebpackConfig = __webpack_exports__.Tu, __webpack_exports__mergeConfigs = __webpack_exports__.SV, __webpack_exports__toImportFn = __webpack_exports__.T1, __webpack_exports__toImportFnPart = __webpack_exports__.i8, __webpack_exports__toRequireContext = __webpack_exports__.sg, __webpack_exports__toRequireContextString = __webpack_exports__.HO, __webpack_exports__webpackIncludeRegexp = __webpack_exports__.hO;
// src/preview/virtual-module-mapping.ts
var getVirtualModules = async (options) => {
let virtualModules = {}, builderOptions = await getBuilderOptions(options), workingDir = process.cwd(), isProd = options.configType === "PRODUCTION", nonNormalizedStories = await options.presets.apply("stories", []), entries = [], stories = normalizeStories(nonNormalizedStories, {
configDir: options.configDir,
workingDir
}), previewAnnotations = [
...(await options.presets.apply(
"previewAnnotations",
[],
options
)).map((entry) => typeof entry == "object" ? entry.absolute : slash(entry)),
loadPreviewOrConfigFile(options)
].filter(Boolean), storiesFilename = "storybook-stories.js", storiesPath = resolve2(join(workingDir, storiesFilename)), needPipelinedImport = builderOptions.lazyCompilation !== !1 && !isProd;
virtualModules[storiesPath] = __webpack_exports__toImportFn(stories, {
needPipelinedImport
});
let configEntryPath = resolve2(join(workingDir, "storybook-config-entry.js"));
return virtualModules[configEntryPath] = (await readTemplate(
__require.resolve(
"storybook-builder-rsbuild/templates/virtualModuleModernEntry.js"
)
)).replaceAll("'{{storiesFilename}}'", `'./${storiesFilename}'`).replaceAll(
"'{{previewAnnotations}}'",
previewAnnotations.filter(Boolean).map((entry) => `'${entry}'`).join(",")
).replaceAll(
"'{{previewAnnotations_requires}}'",
previewAnnotations.filter(Boolean).map((entry) => `require('${entry}')`).join(",")
).replace(/\\/g, "\\\\"), entries.push(configEntryPath), {
virtualModules,
entries
};
};
// src/preview/iframe-rsbuild.config.ts
var require2 = createRequire(import.meta.url), getAbsolutePath = (input) => {
let storybookPath = fileURLToPath(
import.meta.resolve("storybook/package.json")
);
return dirname(
require2.resolve(join2(input, "package.json"), {
paths: [dirname(storybookPath)]
})
);
}, maybeGetAbsolutePath = (input) => {
try {
return getAbsolutePath(input);
} catch {
return !1;
}
}, builtInResolveExtensions = [
".mjs",
".js",
".jsx",
".ts",
".tsx",
".json",
".cjs"
], getRspackMajorVersion = (version) => {
if (typeof version != "string")
return null;
let major = Number.parseInt(version.split(".")[0] ?? "", 10);
return Number.isNaN(major) ? null : major;
}, rspackMajorVersion = getRspackMajorVersion(rspack.rspackVersion), RAW_QUERY_REGEX = /[?&]raw(?:&|=|$)/, globalPath = maybeGetAbsolutePath("@storybook/global"), storybookPaths = {
// biome-ignore lint/complexity/useLiteralKeys: dynamic key required for conditional spread
...globalPath ? { "@storybook/global": globalPath } : {}
}, iframe_rsbuild_config_default = async (options, extraWebpackConfig) => {
let { rsbuildConfigPath, addonDocs } = await getBuilderOptions2(options), webpackConfigFromPresets = await options.presets.apply("webpack", {}, options);
addonDocs && console.warn(
"`addonDocs` option is deprecated and will be removed in future versions. Please use `@storybook/addon-docs` option instead."
);
let {
outputDir = join2(".", "public"),
quiet,
packageJson,
configType,
presets,
previewUrl,
typescriptOptions,
features
} = options, isProd = configType === "PRODUCTION", workingDir = process.cwd(), [
coreOptions,
frameworkOptions,
envs,
logLevel,
headHtmlSnippet,
bodyHtmlSnippet,
template,
docsOptions,
entries,
nonNormalizedStories,
_modulesCount,
build2,
tagsOptions
] = await Promise.all([
presets.apply("core"),
presets.apply("frameworkOptions"),
presets.apply("env"),
presets.apply("logLevel", void 0),
presets.apply("previewHead"),
presets.apply("previewBody"),
presets.apply("previewMainTemplate"),
presets.apply("docs"),
presets.apply("entries", []),
presets.apply("stories", []),
options.cache?.get("modulesCount", 1e3),
options.presets.apply("build"),
presets.apply("tags", {})
]), stories = normalizeStories2(nonNormalizedStories, {
configDir: options.configDir,
workingDir
}), shouldCheckTs = typescriptOptions.check && !typescriptOptions.skipCompiler, tsCheckOptions = typescriptOptions.checkOptions || {}, builderOptions = await getBuilderOptions2(options), cacheConfig = builderOptions.fsCache ? !0 : void 0, shouldDisableDevFeatures = process.env.SB_RSBUILD_TEST_MINIMAL_DEV === "true", lazyCompilationConfig;
isProd || (shouldDisableDevFeatures ? lazyCompilationConfig = !1 : builderOptions.lazyCompilation !== void 0 ? lazyCompilationConfig = builderOptions.lazyCompilation : await isMswActive(options) ? (lazyCompilationConfig = !1, logger3.warn(dedent`
Detected mockServiceWorker.js in staticDirs. Lazy compilation has been disabled
automatically to avoid a Service Worker race that can leave the preview iframe
blank on cold story loads. Set \`lazyCompilation\` explicitly in your builder
options to override.
`)) : lazyCompilationConfig = {
entries: !1
});
let shouldDisableHmr = shouldDisableDevFeatures;
if (!template)
throw new Error(dedent`
Storybook's Webpack5 builder requires a template to be specified.
Somehow you've ended up with a falsy value for the template option.
Please file an issue at https://github.com/storybookjs/storybook with a reproduction.
`);
let externals = globalsNameReferenceMap;
build2?.test?.disableBlocks && (externals["@storybook/blocks"] = "__STORYBOOK_BLOCKS_EMPTY_MODULE__");
let { virtualModules: virtualModuleMapping, entries: dynamicEntries } = await getVirtualModules(options), contentFromConfig = {}, { content } = await loadConfig({
cwd: workingDir,
path: rsbuildConfigPath
}), { environments, ...withoutEnv } = content;
if (content.environments) {
let envCount = Object.keys(content.environments).length;
if (envCount === 0)
contentFromConfig = withoutEnv;
else if (envCount === 1)
contentFromConfig = mergeRsbuildConfig(
withoutEnv,
content.environments[0]
);
else {
let userEnv = builderOptions.environment;
if (typeof userEnv != "string")
throw new Error(
"You must specify an environment when there are multiple environments in the Rsbuild config."
);
if (Object.keys(content.environments).includes(userEnv))
contentFromConfig = mergeRsbuildConfig(
withoutEnv,
content.environments[userEnv]
);
else
throw new Error(
`The specified environment "${userEnv}" is not found in the Rsbuild config.`
);
}
} else
contentFromConfig = content;
let resourceFilename = isProd ? "static/media/[name].[contenthash:8][ext]" : "static/media/[path][name][ext]", rsbuildConfig = mergeRsbuildConfig(contentFromConfig, {
output: {
cleanDistPath: !1,
assetPrefix: "",
dataUriLimit: {
media: 1e4
},
sourceMap: {
js: options.build?.test?.disableSourcemaps ? !1 : "cheap-module-source-map",
css: !options.build?.test?.disableSourcemaps
},
distPath: {
root: resolve3(process.cwd(), outputDir)
},
filename: {
js: isProd ? "[name].[contenthash:8].iframe.bundle.js" : "[name].iframe.bundle.js",
image: resourceFilename,
font: resourceFilename,
media: resourceFilename
},
externals
},
server: {
// Storybook will handle public directory itself, disable Rsbuild's public dir
// feature to prevent overwriting Storybook's public directory.
publicDir: !1
},
dev: {
assetPrefix: "",
progressBar: !quiet,
hmr: shouldDisableHmr ? !1 : void 0
},
resolve: {
alias: {
...storybookPaths
}
},
source: {
define: {
...stringifyProcessEnvs(envs),
NODE_ENV: JSON.stringify(process.env.NODE_ENV)
}
},
// Rsbuild v1 compatible: `performance.chunkSplit` is deprecated in Rsbuild v2, use top-level `splitChunks` instead.
...rspackMajorVersion === 1 ? {
performance: {
chunkSplit: {
strategy: "custom",
splitChunks: {
chunks: "all"
}
},
buildCache: cacheConfig
}
} : {
splitChunks: {
preset: "none",
chunks: "all"
},
performance: {
buildCache: cacheConfig
}
},
plugins: [
shouldCheckTs ? pluginTypeCheck(tsCheckOptions) : null,
pluginHtmlMinifierTerser(() => ({
collapseWhitespace: !0,
removeComments: !0,
removeRedundantAttributes: !0,
removeScriptTypeAttributes: !1,
removeStyleLinkTypeAttributes: !0,
useShortDoctype: !0
}))
].filter(Boolean),
tools: {
swc: (config) => {
config.env ??= {}, config.env.bugfixes = !0;
},
rspack: (config, { addRules, appendRules, rspack: rspack2, mergeConfig }) => {
if (addRules({
test: /\.stories\.([tj])sx?$|(stories|story)\.mdx$/,
exclude: /node_modules/,
enforce: "post",
use: [
{
loader: require2.resolve(
"storybook-builder-rsbuild/loaders/export-order-loader"
)
}
]
}), config.module ??= {}, config.module.parser ??= {}, config.module.parser.javascript ??= {}, config.module.parser.javascript.unknownContextCritical = !1, config.resolve ??= {}, config.resolve.symlinks = !isPreservingSymlinks(), config.resolve.extensions = Array.from(
/* @__PURE__ */ new Set([
...config.resolve.extensions ?? [],
...builtInResolveExtensions
])
), config.watchOptions = {
ignored: /node_modules/
}, config.ignoreWarnings = [
...config.ignoreWarnings || [],
/export '\S+' was not found in 'global'/,
/export '\S+' was not found in '@storybook\/global'/
], config.resolve ??= {}, config.resolve.fallback ??= {
stream: !1,
path: require2.resolve("path-browserify"),
assert: require2.resolve("browser-assert"),
util: require2.resolve("util"),
url: require2.resolve("url"),
fs: !1,
constants: require2.resolve("constants-browserify")
}, config.optimization ??= {}, config.optimization.runtimeChunk = !0, config.optimization.usedExports = options.build?.test?.disableTreeShaking ? !1 : isProd, config.optimization.moduleIds = "named", config.module ??= {}, config.module.parser ??= {}, config.module.parser.javascript ??= {}, config.module.parser.javascript.exportsPresence = !1, !rspack2.experiments?.VirtualModulesPlugin)
throw new Error(
"rspack.experiments.VirtualModulesPlugin requires at least 1.5.0 version of @rsbuild/core, please upgrade or downgrade storybook-rsbuild-builder to lower version."
);
if (config.plugins ??= [], config.plugins.push(
...[
Object.keys(virtualModuleMapping).length > 0 ? new rspack2.experiments.VirtualModulesPlugin(
virtualModuleMapping
) : null,
new rspack2.ProvidePlugin({
process: require2.resolve("process/browser.js")
}),
new CaseSensitivePathsPlugin()
].filter(Boolean)
), rspackMajorVersion === 1) {
let experiments = config.experiments ??= {};
experiments.outputModule = !1;
}
return config.externalsType = "var", config.output ??= {}, config.output.module = !1, config.output.chunkFormat = "array-push", config.output.chunkLoading = "jsonp", lazyCompilationConfig !== void 0 && (config.lazyCompilation = lazyCompilationConfig), appendRules({
resourceQuery: RAW_QUERY_REGEX,
type: "asset/source"
}), mergeConfig(
config,
extraWebpackConfig || {},
webpackConfigFromPresets
);
},
htmlPlugin: {
filename: "iframe.html",
// FIXME: `none` isn't a known option
chunksSortMode: "none",
alwaysWriteToDisk: !0,
inject: !1,
template,
templateParameters: {
version: packageJson?.version ?? "0.0.0-storybook-rsbuild-unknown-version",
globals: {
CONFIG_TYPE: configType,
LOGLEVEL: logLevel,
FRAMEWORK_OPTIONS: frameworkOptions,
CHANNEL_OPTIONS: coreOptions.channelOptions,
FEATURES: features,
PREVIEW_URL: previewUrl,
STORIES: stories.map((specifier) => ({
...specifier,
importPathMatcher: specifier.importPathMatcher.source
})),
DOCS_OPTIONS: docsOptions,
TAGS_OPTIONS: tagsOptions,
...build2?.test?.disableBlocks ? { __STORYBOOK_BLOCKS_EMPTY_MODULE__: {} } : {}
},
headHtmlSnippet,
bodyHtmlSnippet
}
}
}
});
return rsbuildConfig.source ??= {}, rsbuildConfig.source.entry = {
main: [...entries ?? [], ...dynamicEntries]
}, rsbuildConfig;
};
// src/react-shims.ts
import { readFile } from "node:fs/promises";
import { isAbsolute as isAbsolute3, join as join3 } from "node:path";
import { resolvePackageDir } from "storybook/internal/common";
var getIsReactVersion18or19 = async (options) => {
let { legacyRootApi } = await options.presets.apply(
"frameworkOptions"
) || {};
if (legacyRootApi)
return !1;
let reactDom = (await options.presets.apply(
"resolvedReact",
{}
)).reactDom || resolvePackageDir("react-dom");
if (!isAbsolute3(reactDom))
return !1;
let { version } = JSON.parse(
await readFile(join3(reactDom, "package.json"), "utf-8")
);
return version.startsWith("18") || version.startsWith("19") || version.startsWith("0.0.0");
}, applyReactShims = async (_config, options) => await getIsReactVersion18or19(options) ? {} : {
resolve: {
alias: {
"@storybook/react-dom-shim": "@storybook/react-dom-shim/react-16"
}
}
};
// src/index.ts
var corePath = dirname2(__require.resolve("storybook/package.json")), printDuration = (startTime) => (0, import_pretty_hrtime.default)(process.hrtime(startTime)).replace(" ms", " milliseconds").replace(" s", " seconds").replace(" m", " minutes"), executor = {
get: async (options) => await options.presets.apply("rsbuildInstance") || rsbuildReal
}, isObject = (val) => val != null && typeof val == "object" && Array.isArray(val) === !1;
function nonNullables(value) {
return value !== void 0;
}
var rsbuild = async (_, options) => {
let { presets } = options, resolvedWebpackAddons = (await presets.apply("webpackAddons") ?? []).map((preset) => {
let addonOptions = isObject(preset) && preset.options || void 0, name = isObject(preset) ? preset.name : preset;
return resolveAddonName(options.configDir, name, addonOptions);
}).filter(nonNullables), { apply } = await getPresets(resolvedWebpackAddons, options), webpackAddonsConfig = await apply(
"webpackFinal",
// TODO: using empty webpack config as base for now. It's better to using the composed rspack
// config in `iframe-rsbuild.config.ts` as base config. But when `tools.rspack` is an async function,
// the following `tools.rspack` raise an `Promises are not supported` error.
{
output: {},
module: {},
plugins: [],
resolve: {},
// https://github.com/web-infra-dev/rsbuild/blob/8dc35dc1d1500d2f119875d46b6a07e27986d532/packages/core/src/provider/rspackConfig.ts#L167
devServer: void 0,
optimization: {},
performance: {},
externals: {},
experiments: {},
node: {},
stats: {},
entry: {}
},
options
), intrinsicRsbuildConfig = await iframe_rsbuild_config_default(options, webpackAddonsConfig), shimsConfig = await applyReactShims(intrinsicRsbuildConfig, options);
return intrinsicRsbuildConfig = rsbuildReal.mergeRsbuildConfig(
intrinsicRsbuildConfig,
shimsConfig
), await presets.apply(
"rsbuildFinal",
intrinsicRsbuildConfig,
options
);
}, getConfig = async (options) => {
let { presets } = options, typescriptOptions = await presets.apply("typescript", {}, options), frameworkOptions = await presets.apply("frameworkOptions");
return rsbuild({}, {
...options,
typescriptOptions,
frameworkOptions
});
}, server;
async function bail() {
return server?.close();
}
var start = async ({
startTime,
options,
router,
server: storybookServer
}) => {
overrideRsbuildLogger();
let { createRsbuild } = await executor.get(options), config = await getConfig(options), rsbuildBuild = await createRsbuild({
cwd: process.cwd(),
rsbuildConfig: {
...config,
server: {
...config.server,
port: await getRandomPort(options.host),
host: options.host,
htmlFallback: !1,
printUrls: !1
}
}
}), rsbuildServer = await rsbuildBuild.createDevServer(), waitFirstCompileDone = new Promise((resolve4) => {
rsbuildBuild.onDevCompileDone(({ stats: stats2, isFirstCompile }) => {
isFirstCompile && resolve4(stats2);
});
});
if (server = rsbuildServer, !rsbuildBuild)
throw new WebpackInvocationError({
// eslint-disable-next-line local-rules/no-uncategorized-errors
error: new Error("Missing Rsbuild build instance at runtime!")
});
let previewDirOrigin = join4(corePath, "dist/preview");
router.use(
"/sb-preview",
sirv(previewDirOrigin, { maxAge: 3e5, dev: !0, immutable: !0 })
), router.use(rsbuildServer.middlewares), rsbuildServer.connectWebSocket({ server: storybookServer });
let stats = await waitFirstCompileDone;
return await server.afterListen(), {
bail,
stats,
totalTime: process.hrtime(startTime)
};
}, build = async ({ options }) => {
overrideRsbuildLogger();
let { createRsbuild } = await executor.get(options), config = await getConfig(options), rsbuildBuild = await createRsbuild({
cwd: process.cwd(),
rsbuildConfig: config
}), previewDirOrigin = join4(corePath, "dist/preview"), previewDirTarget = join4(options.outputDir || "", "sb-preview"), stats;
rsbuildBuild.onAfterBuild((params) => {
stats = params.stats;
});
let previewFiles = fs.copy(previewDirOrigin, previewDirTarget, {
filter: (src) => {
let { ext } = parse(src);
return ext ? ext === ".js" : !0;
}
}), [{ close }] = await Promise.all([rsbuildBuild.build(), previewFiles]);
return await close(), withStatsJsonCompat(stats);
}, corePresets = [join4(__dirname, "./preview-preset.js")], previewMainTemplate = () => __require.resolve("storybook-builder-rsbuild/templates/preview.ejs");
function getRandomPort(host) {
return new Promise((resolve4, reject) => {
let server2 = createServer();
server2.unref(), server2.on("error", reject), server2.listen({ port: 0, host }, () => {
let { port } = server2.address();
server2.close(() => {
resolve4(port);
});
});
});
}
export {
bail,
build,
corePresets,
executor,
getConfig,
getVirtualModules,
previewMainTemplate,
printDuration,
start
};