create-storybook
Version:
Storybook installer: Develop, document, and test UI components in isolation
2,350 lines • 102 kB
JavaScript
import CJS_COMPAT_NODE_URL_xmjx33h9evr from 'node:url';
import CJS_COMPAT_NODE_PATH_xmjx33h9evr from 'node:path';
import CJS_COMPAT_NODE_MODULE_xmjx33h9evr from "node:module";
var __filename = CJS_COMPAT_NODE_URL_xmjx33h9evr.fileURLToPath(import.meta.url);
var __dirname = CJS_COMPAT_NODE_PATH_xmjx33h9evr.dirname(__filename);
var require = CJS_COMPAT_NODE_MODULE_xmjx33h9evr.createRequire(import.meta.url);
// ------------------------------------------------------------
// end of CJS compatibility banner, injected by Storybook's esbuild configuration
// ------------------------------------------------------------
import {
require_dist
} from "./chunk-6URFVAZ3.js";
import {
__commonJS,
__toESM
} from "./chunk-Y2VWGPHC.js";
// ../../node_modules/picocolors/picocolors.js
var require_picocolors = __commonJS({
"../../node_modules/picocolors/picocolors.js"(exports, module) {
var p = process || {}, argv = p.argv || [], env = p.env || {}, isColorSupported = !(env.NO_COLOR || argv.includes("--no-color")) && (!!env.FORCE_COLOR || argv.includes("--color") || p.platform === "win32" || (p.stdout || {}).isTTY && env.TERM !== "dumb" || !!env.CI), formatter = (open, close, replace = open) => (input) => {
let string = "" + input, index = string.indexOf(close, open.length);
return ~index ? open + replaceClose(string, close, replace, index) + close : open + string + close;
}, replaceClose = (string, close, replace, index) => {
let result = "", cursor = 0;
do
result += string.substring(cursor, index) + replace, cursor = index + close.length, index = string.indexOf(close, cursor);
while (~index);
return result + string.substring(cursor);
}, createColors = (enabled = isColorSupported) => {
let f = enabled ? formatter : () => String;
return {
isColorSupported: enabled,
reset: f("\x1B[0m", "\x1B[0m"),
bold: f("\x1B[1m", "\x1B[22m", "\x1B[22m\x1B[1m"),
dim: f("\x1B[2m", "\x1B[22m", "\x1B[22m\x1B[2m"),
italic: f("\x1B[3m", "\x1B[23m"),
underline: f("\x1B[4m", "\x1B[24m"),
inverse: f("\x1B[7m", "\x1B[27m"),
hidden: f("\x1B[8m", "\x1B[28m"),
strikethrough: f("\x1B[9m", "\x1B[29m"),
black: f("\x1B[30m", "\x1B[39m"),
red: f("\x1B[31m", "\x1B[39m"),
green: f("\x1B[32m", "\x1B[39m"),
yellow: f("\x1B[33m", "\x1B[39m"),
blue: f("\x1B[34m", "\x1B[39m"),
magenta: f("\x1B[35m", "\x1B[39m"),
cyan: f("\x1B[36m", "\x1B[39m"),
white: f("\x1B[37m", "\x1B[39m"),
gray: f("\x1B[90m", "\x1B[39m"),
bgBlack: f("\x1B[40m", "\x1B[49m"),
bgRed: f("\x1B[41m", "\x1B[49m"),
bgGreen: f("\x1B[42m", "\x1B[49m"),
bgYellow: f("\x1B[43m", "\x1B[49m"),
bgBlue: f("\x1B[44m", "\x1B[49m"),
bgMagenta: f("\x1B[45m", "\x1B[49m"),
bgCyan: f("\x1B[46m", "\x1B[49m"),
bgWhite: f("\x1B[47m", "\x1B[49m"),
blackBright: f("\x1B[90m", "\x1B[39m"),
redBright: f("\x1B[91m", "\x1B[39m"),
greenBright: f("\x1B[92m", "\x1B[39m"),
yellowBright: f("\x1B[93m", "\x1B[39m"),
blueBright: f("\x1B[94m", "\x1B[39m"),
magentaBright: f("\x1B[95m", "\x1B[39m"),
cyanBright: f("\x1B[96m", "\x1B[39m"),
whiteBright: f("\x1B[97m", "\x1B[39m"),
bgBlackBright: f("\x1B[100m", "\x1B[49m"),
bgRedBright: f("\x1B[101m", "\x1B[49m"),
bgGreenBright: f("\x1B[102m", "\x1B[49m"),
bgYellowBright: f("\x1B[103m", "\x1B[49m"),
bgBlueBright: f("\x1B[104m", "\x1B[49m"),
bgMagentaBright: f("\x1B[105m", "\x1B[49m"),
bgCyanBright: f("\x1B[106m", "\x1B[49m"),
bgWhiteBright: f("\x1B[107m", "\x1B[49m")
};
};
module.exports = createColors();
module.exports.createColors = createColors;
}
});
// src/initiate.ts
import { ProjectType as ProjectType22 } from "storybook/internal/cli";
import {
PackageManagerName as PackageManagerName2,
executeCommand as executeCommand2
} from "storybook/internal/common";
import { withTelemetry } from "storybook/internal/core-server";
import { logTracker as logTracker2, logger as logger16 } from "storybook/internal/node-logger";
// src/commands/PreflightCheckCommand.ts
var import_ts_dedent = __toESM(require_dist(), 1);
import { detectPnp } from "storybook/internal/cli";
import {
JsPackageManagerFactory,
PackageManagerName,
invalidateProjectRootCache
} from "storybook/internal/common";
import { CLI_COLORS, deprecate, logger as logger2 } from "storybook/internal/node-logger";
// src/scaffold-new-project.ts
import { readdirSync } from "node:fs";
import { rm } from "node:fs/promises";
import { executeCommand } from "storybook/internal/common";
import { logger, prompt } from "storybook/internal/node-logger";
import { GenerateNewProjectOnInitError } from "storybook/internal/server-errors";
import { telemetry } from "storybook/internal/telemetry";
var SUPPORTED_PROJECTS = {
"react-vite-ts": {
displayName: {
type: "React",
builder: "Vite",
language: "TS"
},
createScript: {
npm: "npm create vite@latest . -- --template react-ts",
yarn: "yarn create vite . --template react-ts",
pnpm: "pnpm create vite@latest . --template react-ts"
}
},
"nextjs-ts": {
displayName: {
type: "Next.js",
language: "TS"
},
createScript: {
npm: 'npm create next-app . -- --turbopack --typescript --use-npm --eslint --tailwind --no-app --import-alias="@/*" --src-dir --no-react-compiler',
// yarn doesn't support version ranges, so we have to use npx
yarn: 'npx create-next-app . --turbopack --typescript --use-yarn --eslint --tailwind --no-app --import-alias="@/*" --src-dir --no-react-compiler',
pnpm: 'pnpm create next-app . --turbopack --typescript --use-pnpm --eslint --tailwind --no-app --import-alias="@/*" --src-dir --no-react-compiler'
}
},
"vue-vite-ts": {
displayName: {
type: "Vue 3",
builder: "Vite",
language: "TS"
},
createScript: {
npm: "npm create vite@latest . -- --template vue-ts",
yarn: "yarn create vite . --template vue-ts",
pnpm: "pnpm create vite@latest . --template vue-ts"
}
},
"angular-cli": {
displayName: {
type: "Angular",
language: "TS"
},
createScript: {
npm: "npx -p @angular/cli@latest ng new angular-latest --directory . --routing=true --minimal=true --style=scss --strict --skip-git --skip-install",
yarn: "yarn dlx -p @angular/cli ng new angular-latest --directory . --routing=true --minimal=true --style=scss --strict --skip-git --package-manager=yarn --skip-install && touch yarn.lock && yarn set version berry && yarn config set nodeLinker node-modules",
pnpm: "pnpm --package @angular/cli dlx ng new angular-latest --directory . --routing=true --minimal=true --style=scss --strict --skip-git --package-manager=pnpm --skip-install"
}
},
"lit-vite-ts": {
displayName: {
type: "Lit",
builder: "Vite",
language: "TS"
},
createScript: {
npm: "npm create vite@latest . -- --template lit-ts",
yarn: "yarn create vite . --template lit-ts && touch yarn.lock && yarn set version berry && yarn config set nodeLinker pnp",
pnpm: "pnpm create vite@latest . --template lit-ts"
}
}
}, packageManagerToCoercedName = (packageManager) => {
switch (packageManager) {
case "npm":
return "npm";
case "pnpm":
return "pnpm";
default:
return "yarn";
}
}, buildProjectDisplayNameForPrint = ({ displayName }) => {
let { type, builder, language } = displayName;
return `${type} ${builder ? `+ ${builder} ` : ""}(${language})`;
}, scaffoldNewProject = async (packageManager, { disableTelemetry }) => {
let packageManagerName = packageManagerToCoercedName(packageManager), projectStrategy;
process.env.STORYBOOK_INIT_EMPTY_TYPE && (projectStrategy = process.env.STORYBOOK_INIT_EMPTY_TYPE), projectStrategy || (projectStrategy = await prompt.select({
message: "Empty directory detected:",
options: [
...Object.entries(SUPPORTED_PROJECTS).map(([key, value]) => ({
label: buildProjectDisplayNameForPrint(value),
value: key
})),
{
label: "Other",
value: "other",
hint: "To install Storybook on another framework, first generate a project with that framework and then rerun this command."
}
]
})), projectStrategy === "other" && (logger.warn(
"To install Storybook on another framework, first generate a project with that framework and then rerun this command."
), logger.outro("Exiting..."), process.exit(1));
let projectStrategyConfig = SUPPORTED_PROJECTS[projectStrategy], projectDisplayName = buildProjectDisplayNameForPrint(projectStrategyConfig), createScript = projectStrategyConfig.createScript[packageManagerName], spinner = prompt.spinner({
id: "create-new-project"
});
spinner.start(`Creating a new "${projectDisplayName}" project with ${packageManagerName}...`);
let targetDir = process.cwd();
try {
await rm(`${targetDir}/.cache`, { recursive: !0, force: !0 });
} catch {
}
try {
await rm(`${targetDir}/node_modules`, { recursive: !0, force: !0 });
} catch {
}
try {
spinner.message(`Executing ${createScript}`), await executeCommand({
command: createScript,
shell: !0,
stdio: "pipe",
cwd: targetDir
});
} catch (e) {
throw spinner.error(
`Failed to create a new "${projectDisplayName}" project with ${packageManagerName}`
), new GenerateNewProjectOnInitError({
error: e,
packageManager: packageManagerName,
projectType: projectStrategy
});
}
spinner.stop(`${projectDisplayName} project with ${packageManagerName} created successfully!`), disableTelemetry || await telemetry("scaffolded-empty", {
packageManager: packageManagerName,
projectType: projectStrategy
});
}, FILES_TO_IGNORE = [
".git",
".gitignore",
".DS_Store",
".cache",
"node_modules",
".yarnrc.yml",
".yarn"
], currentDirectoryIsEmpty = () => {
let cwdFolderEntries = readdirSync(process.cwd());
return cwdFolderEntries.length === 0 || cwdFolderEntries.every((entry) => FILES_TO_IGNORE.includes(entry));
};
// src/services/FeatureCompatibilityService.ts
import { AddonVitestService, ProjectType } from "storybook/internal/cli";
var ONBOARDING_PROJECT_TYPES = [
ProjectType.REACT,
ProjectType.REACT_SCRIPTS,
ProjectType.REACT_NATIVE_WEB,
ProjectType.NEXTJS,
ProjectType.VUE3,
ProjectType.ANGULAR
], FeatureCompatibilityService = class {
constructor(packageManager, addonVitestService = new AddonVitestService(packageManager)) {
this.packageManager = packageManager;
this.addonVitestService = addonVitestService;
}
/** Check if a project type supports onboarding */
static supportsOnboarding(projectType) {
return ONBOARDING_PROJECT_TYPES.includes(
projectType
);
}
/**
* Validate all compatibility checks for test feature
*
* @param packageManager - Package manager instance
* @param framework - Detected framework (e.g., 'nextjs', 'react-vite')
* @param builder - Detected builder (e.g. SupportedBuilder.Vite)
* @param directory - Project root directory
* @returns Compatibility result with reasons if incompatible
*/
async validateTestFeatureCompatibility(framework, builder, directory) {
let compatibilityResult = await this.addonVitestService.validateCompatibility({
framework,
builder,
projectRoot: directory
});
return compatibilityResult.compatible ? { compatible: !0 } : compatibilityResult;
}
};
// src/services/TelemetryService.ts
import { telemetry as telemetry2 } from "storybook/internal/telemetry";
import { Feature } from "storybook/internal/types";
// ../../node_modules/process-ancestry/dist/index.js
import os from "os";
import { execSync } from "child_process";
import { execSync as execSync2 } from "child_process";
function getProcessInfo(pid) {
try {
let output = execSync(`ps -p ${pid} -o pid=,ppid=,command=`, {
encoding: "utf8",
timeout: 5e3
// 5 second timeout
}).trim();
if (!output)
return null;
let [pidStr, ppidStr, ...commandParts] = output.split(/\s+/), parsedPid = pidStr ? parseInt(pidStr, 10) : NaN, parsedPpid = ppidStr ? parseInt(ppidStr, 10) : NaN;
return isNaN(parsedPid) || isNaN(parsedPpid) ? null : {
pid: parsedPid,
ppid: parsedPpid,
command: commandParts.join(" ") || void 0
};
} catch (error) {
return error instanceof Error && error.message.includes("timeout") && console.warn(`Process lookup timed out for PID ${pid}`), null;
}
}
function getAncestryUnix(startPid) {
let result = [], visited = /* @__PURE__ */ new Set(), currentPid = startPid, maxDepth = 1e3;
for (; currentPid && maxDepth > 0; ) {
if (visited.has(currentPid)) {
console.warn(`Detected cycle in process tree at PID ${currentPid}`);
break;
}
visited.add(currentPid);
let info = getProcessInfo(currentPid);
if (!info || info.ppid === 0 || info.ppid === 1) break;
result.push(info), currentPid = info.ppid, maxDepth--;
}
return maxDepth === 0 && console.warn(`Reached maximum depth limit while traversing process tree from PID ${startPid}`), result;
}
function getProcessInfo2(pid) {
try {
let output = execSync2(
`wmic process where (ProcessId=${pid}) get ProcessId,ParentProcessId,CommandLine /format:csv`,
{
encoding: "utf8",
timeout: 1e4
// 10 second timeout (wmic can be slower)
}
);
if (!output)
return null;
let fields = output.split(`
`).filter((line) => line.trim() && !line.startsWith("Node")).pop()?.split(",");
if (!fields || fields.length < 4) return null;
let [_node, commandLine, parentPid, thisPid] = fields, parsedPid = thisPid ? parseInt(thisPid.trim(), 10) : NaN, parsedPpid = parentPid ? parseInt(parentPid.trim(), 10) : NaN;
return isNaN(parsedPid) || isNaN(parsedPpid) ? null : {
pid: parsedPid,
ppid: parsedPpid,
command: commandLine?.trim() || void 0
};
} catch (error) {
return error instanceof Error && error.message.includes("timeout") && console.warn(`Process lookup timed out for PID ${pid}`), null;
}
}
function getAncestryWindows(startPid) {
let result = [], visited = /* @__PURE__ */ new Set(), currentPid = startPid, maxDepth = 1e3;
for (; currentPid && maxDepth > 0; ) {
if (visited.has(currentPid)) {
console.warn(`Detected cycle in process tree at PID ${currentPid}`);
break;
}
visited.add(currentPid);
let info = getProcessInfo2(currentPid);
if (!info || info.ppid === 0 || info.ppid === 4) break;
result.push(info), currentPid = info.ppid, maxDepth--;
}
return maxDepth === 0 && console.warn(`Reached maximum depth limit while traversing process tree from PID ${startPid}`), result;
}
function getProcessAncestry(pid = process.pid) {
if (typeof pid != "number" || !Number.isInteger(pid) || pid <= 0)
throw new Error("PID must be a positive integer");
return os.platform() === "win32" ? getAncestryWindows(pid) : getAncestryUnix(pid);
}
// src/services/VersionService.ts
import { versions } from "storybook/internal/common";
import { lt, prerelease } from "semver";
var VersionService = class {
/** Get the current Storybook version */
getCurrentVersion() {
return versions.storybook;
}
/** Get the latest Storybook version from the package manager */
async getLatestVersion(packageManager) {
return packageManager.latestVersion("storybook");
}
/** Check if the current version is a prerelease version */
isPrerelease(version) {
return !!prerelease(version);
}
/** Check if the current version is outdated compared to the latest version */
isOutdated(currentVersion, latestVersion) {
return lt(currentVersion, latestVersion);
}
/**
* Extract Storybook version from process ancestry Looks for version specifiers in command history
* like: create-storybook@1.0.0 or storybook@1.0.0
*/
getStorybookVersionFromAncestry(ancestry) {
for (let ancestor of ancestry.toReversed()) {
let match = ancestor.command?.match(/\s(?:create-storybook|storybook)@([^\s]+)/);
if (match)
return match[1];
}
}
/**
* Extract CLI integration from process ancestry Detects if Storybook was invoked via sv create or
* sv add commands
*/
getCliIntegrationFromAncestry(ancestry) {
for (let ancestor of ancestry.toReversed()) {
let match = ancestor.command?.match(/(?:^|\s)(sv(?:@[^ ]+)? (?:create|add))/i);
if (match)
return match[1].toLowerCase().includes("add") ? "sv add" : "sv create";
}
}
/** Get version info including current, latest, and prerelease status */
async getVersionInfo(packageManager) {
let currentVersion = this.getCurrentVersion(), latestVersion = await this.getLatestVersion(packageManager), isPrereleaseVersion = this.isPrerelease(currentVersion), isOutdatedVersion = latestVersion && !isPrereleaseVersion ? this.isOutdated(currentVersion, latestVersion) : !1;
return {
currentVersion,
latestVersion,
isPrerelease: isPrereleaseVersion,
isOutdated: isOutdatedVersion
};
}
};
// src/services/TelemetryService.ts
var TelemetryService = class {
constructor(disableTelemetry = !1) {
this.disableTelemetry = disableTelemetry, this.versionService = new VersionService();
}
/** Track a new user check step */
async trackNewUserCheck(newUser) {
await this.runTelemetryIfEnabled("init-step", {
step: "new-user-check",
newUser
});
}
/** Track install type selection */
async trackInstallType(installType) {
await this.runTelemetryIfEnabled("init-step", {
step: "install-type",
installType
});
}
/** Track Playwright prompt decision (install | skip | aborted) */
async trackPlaywrightPromptDecision(result) {
await this.runTelemetryIfEnabled("init-step", {
step: "playwright-install",
result
});
}
/** Track the main init event with all metadata */
async trackInit(data) {
await this.runTelemetryIfEnabled("init", data);
}
/** Track empty directory scaffolding event */
async trackScaffolded(data) {
await this.runTelemetryIfEnabled("scaffolded-empty", data);
}
/**
* Track init with complete context including version and CLI integration from ancestry This
* method encapsulates all telemetry gathering and tracking logic
*/
async trackInitWithContext(projectType, selectedFeatures, newUser) {
if (this.disableTelemetry)
return;
let versionSpecifier, cliIntegration;
try {
let ancestry = getProcessAncestry();
versionSpecifier = this.versionService.getStorybookVersionFromAncestry(ancestry), cliIntegration = this.versionService.getCliIntegrationFromAncestry(ancestry);
} catch {
}
let telemetryFeatures = {
dev: !0,
// Always true during init
docs: selectedFeatures.has(Feature.DOCS),
test: selectedFeatures.has(Feature.TEST),
onboarding: selectedFeatures.has(Feature.ONBOARDING)
};
await telemetry2("init", {
projectType,
features: telemetryFeatures,
newUser,
versionSpecifier,
cliIntegration
});
}
runTelemetryIfEnabled(...args) {
return this.disableTelemetry ? Promise.resolve() : telemetry2(...args);
}
};
// src/services/AddonService.ts
import { getPackageDetails } from "storybook/internal/common";
import { Feature as Feature2 } from "storybook/internal/types";
var AddonService = class {
/** Determine webpack compiler addon if needed */
getWebpackCompilerAddon(builder, webpackCompiler) {
if (!webpackCompiler)
return;
let compiler = webpackCompiler({ builder });
return compiler ? `@storybook/addon-webpack5-compiler-${compiler}` : void 0;
}
/** Get addons based on selected features */
getAddonsForFeatures(features) {
let addons = [];
return features.has(Feature2.TEST) && (addons.push("@chromatic-com/storybook"), addons.push("@storybook/addon-vitest")), features.has(Feature2.A11Y) && addons.push("@storybook/addon-a11y"), features.has(Feature2.DOCS) && addons.push("@storybook/addon-docs"), features.has(Feature2.ONBOARDING) && addons.push("@storybook/addon-onboarding"), addons;
}
/** Strip version numbers from addon names */
stripVersions(addons) {
return addons.map((addon) => getPackageDetails(addon)[0]);
}
/** Configure addons for the project */
configureAddons(features, extraAddons = [], builder, webpackCompiler) {
let compiler = this.getWebpackCompilerAddon(builder, webpackCompiler), addonsForMain = [
...compiler ? [compiler] : [],
...this.stripVersions(extraAddons)
].filter(Boolean), addonPackages = [...compiler ? [compiler] : [], ...extraAddons].filter(Boolean);
return {
addonsForMain,
addonPackages
};
}
};
// src/commands/PreflightCheckCommand.ts
var PreflightCheckCommand = class {
/** Execute preflight checks */
constructor(versionService = new VersionService()) {
this.versionService = versionService;
}
async execute(options) {
let isEmptyDirProject = options.force !== !0 && currentDirectoryIsEmpty(), packageManagerType = JsPackageManagerFactory.getPackageManagerType();
isEmptyDirProject && ((options.packageManager ? options.packageManager === PackageManagerName.YARN1 : packageManagerType === PackageManagerName.YARN1) && (logger2.warn("Empty directory with yarn1 is unsupported. Falling back to npm."), packageManagerType = PackageManagerName.NPM, options.packageManager = packageManagerType), logger2.intro(CLI_COLORS.info("Initializing a new project")), await scaffoldNewProject(packageManagerType, options), logger2.outro(CLI_COLORS.info("Project created successfully")), invalidateProjectRootCache()), logger2.intro(CLI_COLORS.info("Initializing Storybook"));
let packageManager = JsPackageManagerFactory.getPackageManager({
force: options.packageManager
});
return isEmptyDirProject && !options.skipInstall && await packageManager.installDependencies(), await detectPnp() && deprecate(import_ts_dedent.dedent`
As of Storybook 10.0, PnP is deprecated.
If you are using PnP, you can continue to use Storybook 10.0, but we recommend migrating to a different package manager or linker-mode. In future versions, PnP compatibility will be removed.
`), await this.displayVersionInfo(packageManager), { packageManager, isEmptyProject: isEmptyDirProject };
}
/** Display version information and warnings */
async displayVersionInfo(packageManager) {
let { currentVersion, latestVersion, isPrerelease, isOutdated } = await this.versionService.getVersionInfo(packageManager);
isOutdated && !isPrerelease ? logger2.warn(import_ts_dedent.dedent`
This version is behind the latest release, which is: ${latestVersion}!
You likely ran the init command through npx, which can use a locally cached version.
To get the latest, please run: ${CLI_COLORS.cta("npx storybook@latest init")}
You may want to ${CLI_COLORS.cta("CTRL+C")} to stop, and run with the latest version instead.
`) : isPrerelease ? logger2.warn(`This is a pre-release version: ${currentVersion}`) : logger2.info(`Adding Storybook version ${currentVersion} to your project`);
}
}, executePreflightCheck = async (options) => new PreflightCheckCommand().execute(options);
// src/commands/ProjectDetectionCommand.ts
var import_picocolors = __toESM(require_picocolors(), 1), import_ts_dedent3 = __toESM(require_dist(), 1);
import { ProjectType as ProjectType3 } from "storybook/internal/cli";
import { logger as logger4, prompt as prompt2 } from "storybook/internal/node-logger";
// src/services/ProjectTypeService.ts
import { existsSync as existsSync2 } from "node:fs";
import { resolve as resolve2 } from "node:path";
import { ProjectType as ProjectType2 } from "storybook/internal/cli";
import { HandledError, getProjectRoot } from "storybook/internal/common";
import { logger as logger3 } from "storybook/internal/node-logger";
import { NxProjectDetectedError } from "storybook/internal/server-errors";
import { SupportedLanguage } from "storybook/internal/types";
// ../../node_modules/empathic/find.mjs
import { join as join2 } from "node:path";
import { existsSync, statSync } from "node:fs";
// ../../node_modules/empathic/walk.mjs
import { dirname } from "node:path";
// ../../node_modules/empathic/resolve.mjs
import { isAbsolute, join, resolve } from "node:path";
function absolute(input, root) {
return isAbsolute(input) ? input : resolve(root || ".", input);
}
// ../../node_modules/empathic/walk.mjs
function up(base, options) {
let { last, cwd } = options || {}, tmp = absolute(base, cwd), root = absolute(last || "/", cwd), prev, arr = [];
for (; prev !== root && (arr.push(tmp), tmp = dirname(prev = tmp), tmp !== prev); )
;
return arr;
}
// ../../node_modules/empathic/find.mjs
function up2(name, options) {
let dir, tmp, start = options && options.cwd || "";
for (dir of up(start, options))
if (tmp = join2(dir, name), existsSync(tmp)) return tmp;
}
function any(names, options) {
let dir, start = options && options.cwd || "", j = 0, len = names.length, tmp;
for (dir of up(start, options))
for (j = 0; j < len; j++)
if (tmp = join2(dir, names[j]), existsSync(tmp)) return tmp;
}
// src/services/ProjectTypeService.ts
var import_ts_dedent2 = __toESM(require_dist(), 1);
import semver from "semver";
var ProjectTypeService = class {
constructor(jsPackageManager) {
this.jsPackageManager = jsPackageManager;
}
/** Sorted configuration to match a Storybook preset template */
getSupportedTemplates() {
return [
{
preset: ProjectType2.NUXT,
dependencies: ["nuxt"],
matcherFunction: ({ dependencies }) => dependencies?.every(Boolean) ?? !0
},
{
preset: ProjectType2.VUE3,
dependencies: {
// This Vue template works with Vue 3
vue: (versionRange) => versionRange === "next" || this.eqMajor(versionRange, 3)
},
matcherFunction: ({ dependencies }) => dependencies?.some(Boolean) ?? !1
},
{
preset: ProjectType2.EMBER,
dependencies: ["ember-cli"],
matcherFunction: ({ dependencies }) => dependencies?.every(Boolean) ?? !0
},
{
preset: ProjectType2.NEXTJS,
dependencies: ["next"],
matcherFunction: ({ dependencies }) => dependencies?.every(Boolean) ?? !0
},
{
preset: ProjectType2.QWIK,
dependencies: ["@builder.io/qwik"],
matcherFunction: ({ dependencies }) => dependencies?.every(Boolean) ?? !0
},
{
preset: ProjectType2.REACT_NATIVE,
dependencies: ["react-native", "react-native-scripts"],
matcherFunction: ({ dependencies }) => dependencies?.some(Boolean) ?? !1
},
{
preset: ProjectType2.REACT_SCRIPTS,
// For projects using a custom/forked `react-scripts` package.
files: ["/node_modules/.bin/react-scripts"],
// For standard CRA projects
dependencies: ["react-scripts"],
matcherFunction: ({ dependencies, files }) => (dependencies?.every(Boolean) || files?.every(Boolean)) ?? !1
},
{
preset: ProjectType2.ANGULAR,
dependencies: ["@angular/core"],
matcherFunction: ({ dependencies }) => dependencies?.every(Boolean) ?? !0
},
{
preset: ProjectType2.WEB_COMPONENTS,
dependencies: ["lit-element", "lit-html", "lit"],
matcherFunction: ({ dependencies }) => dependencies?.some(Boolean) ?? !1
},
{
preset: ProjectType2.PREACT,
dependencies: ["preact"],
matcherFunction: ({ dependencies }) => dependencies?.every(Boolean) ?? !0
},
{
// TODO: This only works because it is before the SVELTE template. could be more explicit
preset: ProjectType2.SVELTEKIT,
dependencies: ["@sveltejs/kit"],
matcherFunction: ({ dependencies }) => dependencies?.every(Boolean) ?? !0
},
{
preset: ProjectType2.SVELTE,
dependencies: ["svelte"],
matcherFunction: ({ dependencies }) => dependencies?.every(Boolean) ?? !0
},
{
preset: ProjectType2.SOLID,
dependencies: ["solid-js"],
matcherFunction: ({ dependencies }) => dependencies?.every(Boolean) ?? !0
},
// DO NOT MOVE ANY TEMPLATES BELOW THIS LINE
// React is part of every Template, after Storybook is initialized once
{
preset: ProjectType2.REACT,
dependencies: ["react"],
matcherFunction: ({ dependencies }) => dependencies?.every(Boolean) ?? !0
}
];
}
isStorybookInstantiated(configDir = resolve2(process.cwd(), ".storybook")) {
return existsSync2(configDir);
}
async validateProvidedType(projectTypeProvided) {
if (Object.values(ProjectType2).filter(
(t) => !["undetected", "unsupported", "nx"].includes(String(t))
).includes(projectTypeProvided))
return projectTypeProvided;
throw logger3.error(
`The provided project type ${projectTypeProvided} was not recognized by Storybook`
), new HandledError(`Unknown project type supplied: ${projectTypeProvided}`);
}
async autoDetectProjectType(options) {
try {
let detectedType = await this.detectProjectType(options);
if (detectedType === ProjectType2.UNDETECTED || detectedType === null)
throw logger3.error(import_ts_dedent2.dedent`
Unable to initialize Storybook in this directory.
Storybook couldn't detect a supported framework or configuration for your project. Make sure you're inside a framework project (e.g., React, Vue, Svelte, Angular, Next.js) and that its dependencies are installed.
Tips:
- Run init in an empty directory or create a new framework app first.
- If this directory contains unrelated files, try a new directory for Storybook.
`), new HandledError("Storybook failed to detect your project type");
if (detectedType === ProjectType2.NX)
throw new NxProjectDetectedError();
return detectedType;
} catch (err) {
throw err instanceof HandledError || err instanceof NxProjectDetectedError ? err : (logger3.error(String(err)), new HandledError(err instanceof Error ? err.message : String(err)));
}
}
async detectLanguage() {
let language = SupportedLanguage.JAVASCRIPT;
if (existsSync2("jsconfig.json"))
return language;
let isTypescriptDirectDependency = !!this.jsPackageManager.getAllDependencies().typescript, getModulePackageJSONVersion = async (pkg) => (await this.jsPackageManager.getModulePackageJSON(pkg))?.version ?? null, [
typescriptVersion,
prettierVersion,
babelPluginTransformTypescriptVersion,
typescriptEslintParserVersion,
eslintPluginStorybookVersion
] = await Promise.all([
getModulePackageJSONVersion("typescript"),
getModulePackageJSONVersion("prettier"),
getModulePackageJSONVersion("@babel/plugin-transform-typescript"),
getModulePackageJSONVersion("@typescript-eslint/parser"),
getModulePackageJSONVersion("eslint-plugin-storybook")
]), satisfies = (version, range) => version ? semver.satisfies(version, range, { includePrerelease: !0 }) : !1;
return isTypescriptDirectDependency && typescriptVersion ? satisfies(typescriptVersion, ">=4.9.0") && (!prettierVersion || semver.gte(prettierVersion, "2.8.0")) && (!babelPluginTransformTypescriptVersion || satisfies(babelPluginTransformTypescriptVersion, ">=7.20.0")) && (!typescriptEslintParserVersion || satisfies(typescriptEslintParserVersion, ">=5.44.0")) && (!eslintPluginStorybookVersion || satisfies(eslintPluginStorybookVersion, ">=0.6.8")) ? language = SupportedLanguage.TYPESCRIPT : logger3.warn(
"Detected TypeScript < 4.9 or incompatible tooling, populating with JavaScript examples"
) : existsSync2("tsconfig.json") && (language = SupportedLanguage.TYPESCRIPT), language;
}
eqMajor(versionRange, major) {
return semver.validRange(versionRange) ? semver.minVersion(versionRange)?.major === major : !1;
}
async detectProjectType(options) {
try {
if (this.isNxProject())
return ProjectType2.NX;
if (options.html)
return ProjectType2.HTML;
let { packageJson } = this.jsPackageManager.primaryPackageJson;
return this.detectFrameworkPreset(packageJson);
} catch {
return ProjectType2.UNDETECTED;
}
}
detectFrameworkPreset(packageJson) {
let result = [...this.getSupportedTemplates(), this.getUnsupportedTemplate()].find(
(framework) => this.getProjectType(packageJson, framework) !== null
);
return result ? result.preset : ProjectType2.UNDETECTED;
}
/** Template that matches unsupported frameworks */
getUnsupportedTemplate() {
return {
preset: ProjectType2.UNSUPPORTED,
dependencies: {},
matcherFunction: ({ dependencies }) => dependencies?.some(Boolean) ?? !1
};
}
getProjectType(packageJson, framework) {
let matcher = {
dependencies: [!1],
peerDependencies: [!1],
files: [!1]
}, { preset, files, dependencies, peerDependencies, matcherFunction } = framework, dependencySearches = [];
Array.isArray(dependencies) ? dependencySearches = dependencies.map((name) => [name, void 0]) : typeof dependencies == "object" && (dependencySearches = Object.entries(dependencies)), dependencySearches.length > 0 && (matcher.dependencies = dependencySearches.map(
([name, matchFn]) => this.hasDependency(packageJson, name, matchFn)
));
let peerDependencySearches = [];
return Array.isArray(peerDependencies) ? peerDependencySearches = peerDependencies.map((name) => [name, void 0]) : typeof peerDependencies == "object" && (peerDependencySearches = Object.entries(peerDependencies)), peerDependencySearches.length > 0 && (matcher.peerDependencies = peerDependencySearches.map(
([name, matchFn]) => this.hasPeerDependency(packageJson, name, matchFn)
)), Array.isArray(files) && files.length > 0 && (matcher.files = files.map((name) => existsSync2(name))), matcherFunction(matcher) ? preset : null;
}
hasDependency(packageJson, name, matcher) {
let version = packageJson.dependencies?.[name] || packageJson.devDependencies?.[name];
return version && typeof matcher == "function" ? matcher(version) : !!version;
}
hasPeerDependency(packageJson, name, matcher) {
let version = packageJson.peerDependencies?.[name];
return version && typeof matcher == "function" ? matcher(version) : !!version;
}
isNxProject() {
return up2("nx.json", { last: getProjectRoot() });
}
};
// src/commands/ProjectDetectionCommand.ts
var ProjectDetectionCommand = class {
constructor(options, jsPackageManager, projectTypeService = new ProjectTypeService(jsPackageManager)) {
this.options = options;
this.projectTypeService = projectTypeService;
}
/** Execute project type detection */
async execute() {
let projectType, projectTypeProvided = this.options.type;
if (projectTypeProvided)
projectType = await this.projectTypeService.validateProvidedType(projectTypeProvided), logger4.step(`Installing Storybook for user specified project type: ${projectTypeProvided}`);
else {
let detected = await this.projectTypeService.autoDetectProjectType(this.options);
projectType = detected, detected === ProjectType3.REACT_NATIVE && !this.options.yes && (projectType = await this.promptReactNativeVariant()), logger4.debug(`Project type detected: ${projectType}`);
}
await this.checkExistingInstallation(projectType);
let language = this.options.language || await this.projectTypeService.detectLanguage();
return { projectType, language };
}
/** Prompt user to select React Native variant */
async promptReactNativeVariant() {
return await prompt2.select({
message: "We've detected a React Native project. Install:",
options: [
{
label: `${import_picocolors.default.bold("React Native")}: Storybook on your device/simulator`,
value: ProjectType3.REACT_NATIVE
},
{
label: `${import_picocolors.default.bold("React Native Web")}: Storybook on web for docs, test, and sharing`,
value: ProjectType3.REACT_NATIVE_WEB
},
{
label: `${import_picocolors.default.bold("Both")}: Add both native and web Storybooks`,
value: ProjectType3.REACT_NATIVE_AND_RNW
}
]
});
}
/** Check if Storybook is already installed and handle force option */
async checkExistingInstallation(projectType) {
let storybookInstantiated = this.projectTypeService.isStorybookInstantiated(), options = this.options;
options.force !== !0 && options.yes !== !0 && storybookInstantiated && projectType !== ProjectType3.ANGULAR && (await prompt2.confirm({
message: import_ts_dedent3.dedent`We found a .storybook config directory in your project.
We assume that Storybook is already instantiated for your project. Do you still want to continue and force the initialization?`
}) || options.yes ? options.force = !0 : process.exit(0));
}
}, executeProjectDetection = (packageManager, options) => new ProjectDetectionCommand(options, packageManager).execute();
// src/commands/FrameworkDetectionCommand.ts
import { logger as logger6 } from "storybook/internal/node-logger";
// src/generators/GeneratorRegistry.ts
import { logger as logger5 } from "storybook/internal/node-logger";
var GeneratorRegistry = class {
constructor() {
this.generators = /* @__PURE__ */ new Map();
}
/** Register a generator for a specific project type */
register(generator) {
let { metadata } = generator;
this.generators.has(metadata.projectType) && logger5.warn(
`Generator for project type ${metadata.projectType} is already registered. Overwriting.`
), this.generators.set(metadata.projectType, generator);
}
/** Get a generator for a specific project type */
get(projectType) {
return this.generators.get(projectType);
}
}, generatorRegistry = new GeneratorRegistry();
// src/services/FrameworkDetectionService.ts
import { getProjectRoot as getProjectRoot2 } from "storybook/internal/common";
import { prompt as prompt3 } from "storybook/internal/node-logger";
import { SupportedBuilder, SupportedFramework } from "storybook/internal/types";
var import_ts_dedent4 = __toESM(require_dist(), 1), viteConfigFiles = ["vite.config.ts", "vite.config.js", "vite.config.mjs"], webpackConfigFiles = ["webpack.config.js"], rsbuildConfigFiles = ["rsbuild.config.ts", "rsbuild.config.js", "rsbuild.config.mjs"], FrameworkDetectionService = class {
constructor(jsPackageManager) {
this.jsPackageManager = jsPackageManager;
}
detectFramework(renderer, builder) {
if (Object.values(SupportedFramework).includes(renderer))
return renderer;
let maybeFramework = `${renderer}-${builder}`;
if (Object.values(SupportedFramework).includes(maybeFramework))
return maybeFramework;
throw new Error(`Could not find framework for renderer: ${renderer} and builder: ${builder}`);
}
async detectBuilder() {
let viteConfig = any(viteConfigFiles, { last: getProjectRoot2() }), webpackConfig = any(webpackConfigFiles, { last: getProjectRoot2() }), rsbuildConfig = any(rsbuildConfigFiles, { last: getProjectRoot2() }), dependencies = this.jsPackageManager.getAllDependencies(), hasVite = viteConfig || !!dependencies.vite, hasWebpack = webpackConfig || !!dependencies.webpack, hasRsbuild = rsbuildConfig || !!dependencies["@rsbuild/core"], detectedBuilders = [];
if (hasVite && detectedBuilders.push(SupportedBuilder.VITE), hasWebpack && detectedBuilders.push(SupportedBuilder.WEBPACK5), hasRsbuild && detectedBuilders.push(SupportedBuilder.RSBUILD), detectedBuilders.length === 1)
return detectedBuilders[0];
let options = [
{ label: "Vite", value: SupportedBuilder.VITE },
{ label: "Webpack 5", value: SupportedBuilder.WEBPACK5 },
{ label: "Rsbuild", value: SupportedBuilder.RSBUILD }
];
return prompt3.select({
message: import_ts_dedent4.dedent`
${detectedBuilders.length > 1 ? "Multiple builders were detected in your project. Please select one:" : "We were not able to detect the right builder for your project. Please select one:"}
`,
options
});
}
};
// src/commands/FrameworkDetectionCommand.ts
var FrameworkDetectionCommand = class {
constructor(packageManager, frameworkDetectionService = new FrameworkDetectionService(packageManager)) {
this.frameworkDetectionService = frameworkDetectionService;
}
async execute(projectType, options) {
let generatorModule = generatorRegistry.get(projectType);
if (!generatorModule)
throw new Error(`No generator found for project type: ${projectType}`);
let { metadata } = generatorModule, builder;
options.builder ? builder = options.builder : metadata.builderOverride ? typeof metadata.builderOverride == "function" ? builder = await metadata.builderOverride() : builder = metadata.builderOverride : builder = await this.frameworkDetectionService.detectBuilder();
let renderer = metadata.renderer, framework;
return metadata.framework !== void 0 ? typeof metadata.framework == "function" ? framework = metadata.framework(builder) : framework = metadata.framework : framework = this.frameworkDetectionService.detectFramework(renderer, builder), framework && logger6.step(`Framework detected: ${framework}`), {
framework,
renderer,
builder
};
}
}, executeFrameworkDetection = (projectType, packageManager, options) => new FrameworkDetectionCommand(packageManager).execute(projectType, options);
// src/commands/UserPreferencesCommand.ts
var import_picocolors2 = __toESM(require_picocolors(), 1), import_ts_dedent5 = __toESM(require_dist(), 1);
import { globalSettings } from "storybook/internal/cli";
import { isCI } from "storybook/internal/common";
import { logger as logger7, prompt as prompt4 } from "storybook/internal/node-logger";
import { Feature as Feature3 } from "storybook/internal/types";
var UserPreferencesCommand = class {
constructor(commandOptions, packageManager, featureService = new FeatureCompatibilityService(packageManager), telemetryService = new TelemetryService(commandOptions.disableTelemetry)) {
this.commandOptions = commandOptions;
this.featureService = featureService;
this.telemetryService = telemetryService;
}
/** Execute user preferences gathering */
async execute(options) {
let skipPrompt = !(process.stdout.isTTY && !isCI()) || !!this.commandOptions.yes, isTestFeatureAvailable = await this.isTestFeatureAvailable(
options.framework,
options.builder
), newUser = await this.promptNewUser(skipPrompt), commandOptionsFeatures = this.handleCommandOptionsFeatureFlag();
if (commandOptionsFeatures)
return {
newUser,
selectedFeatures: commandOptionsFeatures
};
let installType = !newUser && !this.commandOptions.features ? await this.promptInstallType(skipPrompt, isTestFeatureAvailable) : "recommended", selectedFeatures = this.determineFeatures(
installType,
newUser,
isTestFeatureAvailable,
options.projectType
);
return { newUser, selectedFeatures };
}
handleCommandOptionsFeatureFlag() {
return this.commandOptions.features && this.commandOptions.features?.length > 0 ? (logger7.warn(import_ts_dedent5.dedent`
Skipping feature validation as these features were explicitly selected:
${Array.from(this.commandOptions.features).join(", ")}
`), new Set(this.commandOptions.features)) : this.commandOptions.features?.length === 0 ? (logger7.warn(import_ts_dedent5.dedent`
All features have been disabled via --no-features flag.
`), /* @__PURE__ */ new Set()) : null;
}
/** Prompt user about onboarding */
async promptNewUser(skipPrompt) {
let settings = await globalSettings(), { skipOnboarding } = settings.value.init || {}, isNewUser = skipOnboarding !== void 0 ? !skipOnboarding : !0;
skipPrompt || skipOnboarding ? (settings.value.init ||= {}, settings.value.init.skipOnboarding = !!skipOnboarding) : (isNewUser = await prompt4.select({
message: "New to Storybook?",
options: [
{
label: `${import_picocolors2.default.bold("Yes:")} Help me with onboarding`,
value: !0
},
{
label: `${import_picocolors2.default.bold("No:")} Skip onboarding & don't ask again`,
value: !1
}
]
}), settings.value.init ||= {}, settings.value.init.skipOnboarding = !isNewUser, typeof isNewUser < "u" && await this.telemetryService.trackNewUserCheck(isNewUser));
try {
await settings.save();
} catch (err) {
logger7.warn(`Failed to save user settings: ${err}`);
}
return isNewUser;
}
/** Prompt user for install type */
async promptInstallType(skipPrompt, isTestFeatureAvailable) {
let installType = "recommended", recommendedLabel = isTestFeatureAvailable ? "Recommended: Component development, docs, and testing features." : "Recommended: Component development and docs";
return skipPrompt || (installType = await prompt4.select({
message: "What configuration should we install?",
options: [
{
label: recommendedLabel,
value: "recommended"
},
{
label: "Minimal: Just the essentials for component development.",
value: "light"
}
]
})), await this.telemetryService.trackInstallType(installType), installType;
}
/** Determine features based on install type and user status */
determineFeatures(installType, newUser, isTestFeatureAvailable, projectType) {
let features = /* @__PURE__ */ new Set();
return installType === "recommended" && (features.add(Feature3.DOCS), features.add(Feature3.A11Y), isTestFeatureAvailable && features.add(Feature3.TEST), newUser && FeatureCompatibilityService.supportsOnboarding(projectType) && features.add(Feature3.ONBOARDING)), features;
}
/** Validate test feature compatibility and prompt user if issues found */
async isTestFeatureAvailable(framework, builder) {
return (await this.featureService.validateTestFeatureCompatibility(
framework,
builder,
process.cwd()
)).compatible;
}
}, executeUserPreferences = ({
options,
packageManager,
...restOptions
}) => new UserPreferencesCommand(options, packageManager).execute(restOptions);
// src/generators/baseGenerator.ts
import { mkdir } from "node:fs/promises";
import { dirname as dirname2, join as join3 } from "node:path";
import { fileURLToPath } from "node:url";
import {
configureEslintPlugin,
copyTemplateFiles,
extractEslintInfo
} from "storybook/internal/cli";
import {
frameworkPackages,
getPackageDetails as getPackageDetails2,
isCI as isCI2,
optionalEnvToBoolean
} from "storybook/internal/common";
import { prompt as prompt5 } from "storybook/internal/node-logger";
import { SupportedFramework as SupportedFramework2, SupportedLanguage as SupportedLanguage3 } from "storybook/internal/types";
// ../../node_modules/tiny-invariant/dist/esm/tiny-invariant.js
var isProduction = process.env.NODE_ENV === "production", prefix = "Invariant failed";
function invariant(condition, message) {
if (!condition) {
if (isProduction)
throw new Error(prefix);
var provided = typeof message == "function" ? message() : message, value = provided ? "".concat(prefix, ": ").concat(provided) : prefix;
throw new Error(value);
}
}
// src/generators/baseGenerator.ts
var import_ts_dedent7 = __toESM(require_dist(), 1);
// src/generators/configure.ts
var import_ts_dedent6 = __toESM(require_dist(), 1);
import { stat, writeFile } from "node:fs/promises";
import { resolve as resolve3 } from "node:path";
import { logger as logger8 } from "storybook/internal/node-logger";
import { Feature as Feature4, SupportedLanguage as SupportedLanguage2 } from "storybook/internal/types";
var pathExists = async (path) => stat(path).then(() => !0).catch(() => !1);
async function configureMain({
addons,
extensions = ["js", "jsx", "mjs", "ts", "tsx"],
storybookConfigFolder,
language,
frameworkPackage,
prefixes = [],
features,
...custom
}) {
let srcPath = resolve3(storybookConfigFolder, "../src"), prefix2 = await pathExists(srcPath) ? "../src" : "../stories", stories = features.has(Feature4.DOCS) ? [`${prefix2}/**/*.mdx`] : [];
stories.push(`${prefix2}/**/*.stories.@(${extensions.join("|")})`);
let config = {
stories,
addons,
...custom
}, isTypescript = language === SupportedLanguage2.TYPESCRIPT, mainConfigTemplate = import_ts_dedent6.dedent`<<import>><<prefix>>const config<<type>> = <<mainContents>>;
export default config;`;
frameworkPackage || (mainConfigTemplate = mainConfigTemplate.replace("<<import>>", "").replace("<<type>>", ""), logger8.warn("Could not find framework package name"));
let mainContents = JSON.stringify(config, null, 2).replace(/['"]%%/g, "").replace(/%%['"]/g, ""), imports = [], finalPrefixes = [...prefixes];
custom.framework.includes("path.dirname(") && imports.push("import path from 'node:path';"), isTypescript ? imports.push(`import type { StorybookConfig } from '${frameworkPackage}';`) : finalPrefixes.push(`/** @type { import('${frameworkPackage}').StorybookConfig } */`);
let mainJsContents = "";
mainJsContents = mainConfigTemplate.replace("<<import>>", `${imports.join(`
`)}
`).replace("<<prefix>>", finalPrefixes.length > 0 ? `${finalPrefixes.join(`
`)}
` : "").replace("<<type>>", isTypescript ? ": StorybookConfig" : "").replace("<<mainContents>>", mainContents);
let mainPath = `./${storybookConfigFolder}/main.${isTypescript ? "ts" : "js"}`;
return await writeFile(mainPath, mainJsContents, { encoding: "utf8" }), { mainPath };
}
async function configurePreview(options) {
let { prefix: frameworkPrefix = "" } = options.frameworkPreviewParts || {}, isTypescript = options.language === SupportedLanguage2.TYPESCRIPT, previewConfigPath = `./${options.storybookConfigFolder}/preview.${isTypescript ? "ts" : "js"}`;
if (await pathExists(previewConfigPath))
return { previewConfigPath };
let frameworkPackage = options.frameworkPackage, prefix2 = [
isTypescript && frameworkPackage ? `import type { Preview } from '${frameworkPackage}'` : "",
frameworkPrefix
].filter(Boolean).join(`
`), preview = "";
return preview = import_ts_dedent6.dedent`
${prefix2}${prefix2.length > 0 ? `
` : ""}
${!isTypescript && frameworkPackage ? `/** @type { import('${frameworkPackage}').Preview } */
` : ""}const preview${isTypescript ? ": Preview" : ""} = {
parameters: {
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
},
};
export default preview;
`.replace(`
`, "").trim(), await writeFile(previewConfigPath, preview, { encoding: "utf8" }), { previewConfigPath };
}
// src/generators/baseGenerator.ts
var defaultOptions = {
extraPackages: [],
extraAddons: [],
staticDir: void 0,
addScripts: !0,
addComponents: !0,
webpackCompiler: () => {
},
extraMain: void 0,
extensions: void 0,
componentsDestinationPath: void 0,
storybookConfigFolder: ".storybook",
installFrameworkPackages: !0
}, getPackageByValue = (type, value, packages) => {
let foundPackage = value ? Object.entries(packages).find(([key, pkgValue]) => pkgValue === value)?.[0] : void 0;
if (foundPackage)
return foundPackage;
throw new Error(
import_ts_dedent7.dedent`
Could not find ${type} package for ${value}.
Make sure this package exists, and if it does, please file an issue as this might be a bug in Storybook.
`
);
}, applyGetAbsolutePathWrapper = (packageName) => `%%getAbsolutePath('${packageName}')%%`, applyAddonGetAbsolutePathWrapper = (pkg) => {
if (typeof pkg == "string")
return applyGetAbsolutePathWrapper(pkg);
let obj = { ...pkg };
return obj.name = applyGetAbsolutePathWrapper(pkg.name), obj;
}, getFrameworkDetails = ({
framework,
shouldApplyRequireWrapperOnPackageNames
}) => {
let frameworkPackage = getPackageByValue("framework", framework, frameworkPackages), frameworkPackagePath = shouldApplyRequireWrapperOnPackageNames ? applyGetAbsolutePathWrapper(frameworkPackage) : frameworkPackage;
return {
frameworkPackage,
frameworkPackagePath
};
}, hasFrameworkTemplates = (framework) => framework ? framework === "nuxt" ? !optionalEnvToBoolean(process.env.IN_STORYBOOK_SANDBOX) : [
SupportedFramework2.ANGULAR,
SupportedFramework2.EMBER,
SupportedFramework2.HTML_VITE,
SupportedFramework2.NEXTJS,
SupportedFramework2.NEXTJS_VITE,
SupportedFramework2.PREACT_VITE,
SupportedFramework2.REACT_NATIVE_WEB_VITE,
SupportedFramework2.REACT_VITE,
SupportedFramework2.REACT_WEBPACK5,
SupportedFramework2.SERVER_WEBPACK5,
SupportedFramework2.SOLID,
SupportedFramework2.SVELTE_VITE,
SupportedFramework2.SVELTEKIT,
SupportedFramework2.VUE3_VITE,
SupportedFramework2.WEB_COMPONENTS_VITE
].includes(framework) : !1;
async function baseGenerator(packageManager, npmOptions, { language, builder, framework, renderer, pnp, features, dependencyCollector }, _options) {
let options = { ...defaultOptions, ..._options }, shouldApplyRequireWrapperOnPackageNames = packageManager.isStorybookInMonorepo() || pnp, taskLog = prompt5.taskLog({
id: "base-generator",
title: "Generating Storybook configuration"
}), { frameworkPackagePath, frameworkPackage } = getFrameworkDetails({
framework,
shouldApplyRequireWrapperOnPackageNames
}), {
extraAddons = [],
extraPackages,
staticDir,
addScripts,
addComponents,
extraMain,
extensions,
storybookConfigFolder,
componentsDestinationPath,
webpackCompiler,
installFrameworkPackages
} = {
...defaultOptions,
...options
}, addonManager = new AddonService(), { addonsForMain: addons, addonPackages } = addonManager.configureAddons(
features,
extraAddons,
builder,
webpackCompiler
), { packageJson } = packageManager.primaryPackageJson, installedDependencies = new Set(
Object.keys({ ...packageJson.dependencies, ...packageJson.devDependencies })
), extraPackagesToInstall = typeof extraPackages == "function" ? await extraPackages({
builder
}) : extraPackages, allPackages = [
"storybook",
...installFrameworkPackages ? [frameworkPackage] : [],
...addonPackages,
...extraPackagesToInstall || []
].filter(Boolean), packagesToInstall = [...new Set(allPackages)].filter(
(packageToInstall) => !installedDependencies.has(getPackageDetails2(packageToInstall)[0])
), eslintPluginPackage = null;
try {
if (!isCI2()) {
let { hasEslint, isStorybookPluginInstalled, isFlatConfig, eslintConfigFile } = (
// TODO: Investigate why packageManager type does not match on CI
await extractEslintInfo(packageManager)
);
hasEslint && !isStorybookPluginInstalled && (eslintPluginPackage = "eslint-plugin-storybook", packagesToInstall.push(eslintPluginPackage), taskLog.message("- Configuring ESLint plugin"), await configureEslintPlugin({
eslintConfigFile,
// TODO: Investigate why packageManager type does not match on CI
packageManager,
isFlatConfig
}));
}
} catch {
}
let versionedPackages = await packageManager.getVersionedPackages(
packagesToInstall
);
versionedPackages.length > 0 && (npmOptions.type === "devDependencies" ? dependencyCollector.addDevDependencies(versionedPackages) : dependencyCollector.addDependencies(versionedPackages)), await mkdir(`./${storybookConfigFolder}`, { recursive: !0 });
let prefixes = shouldApplyRequireWrapperOnPackageNames ? [
'import { dirname } from "path"',
'import { fileURLToPath } from "url"',
language === SupportedLanguage3.JAVASCRIPT ? import_ts_dedent7.dedent`/**
* This function is used to resolve the absolute path of a package.
* It is needed in projects that use Yarn PnP or are set up within a monorepo.
*/
function getAbsolutePath(value) {
return dirname(fileURLToPath(import.meta.resolve(\`\${value}/package.json\`)))
}` : import_ts_dedent7.dedent`/**
* This function is used to resolve the absolute path of a package.
* It is needed in projects that use Yarn PnP or are set up within a monorepo.
*/
function getAbsolutePath(value: string): any {
return dirname(fileURLToPath(import.meta.resolve(\`\${value}/package.json\`)))
}`
] : [], configurationFileExtension = language === SupportedLanguage3.TYPESCRIPT ? "ts" : "js";
if (taskLog.message(`- Configuring main.${configurationFileExtension}`), await configureMain({
framework: frameworkPackagePath,
features,
frameworkPackage,
prefixes,
storybookConfigFolder,
addons: shouldApplyRequireWrapperOnPackageNames ? addons.map((addon) => applyAddonGetAbsolutePathWrapper(addon)) : addons,
extensions,
language,
...staticDir ? { staticDirs: [join3("..", staticDir)] } : null,
...extraMain
}), taskLog.message(`- Configuring preview.${configurationFileExtension}`), await configurePreview({
frameworkPreviewParts: _options.frameworkPreviewParts,
storybookConfigFolder,
language,
frameworkPackage
}), addScripts && (taskLog.message("- Adding Storybook command to package.json"), packageManager.addStorybookCommandInScripts({
port: 6006
})), addComponents) {
let templateLocation = hasFrameworkTemplates(framework) ? framework : renderer;
invariant(templateLocation, `Could not find template location for ${framework} or ${renderer}`), taskLog.message("- Copying framework templates"), await copyTemplateFiles({
templateLocation,
packageManager,
language,
destination: componentsDestinationPath,
commonAssetsDir: join3(
dirname2(fileURLToPath(import.meta.resolve("create-storybook/package.json"))),
"rendererAssets",
"common"
),
features
});
}
return taskLog.success("Storybook configuration generated", { showLog: !0 }), {
configDir: storybookConfigFolder,
storybookCommand: _options.storybookCommand,
shouldRunDev: _options.shouldRunDev
};
}
// src/commands/GeneratorExecutionCommand.ts
var GeneratorExecutionCommand = class {
/** Execute generator for the detected project type */
constructor(dependencyCollector, jsPackageManager, addonService = new AddonService()) {
this.dependencyCollector = dependencyCollector;
this.jsPackageManager = jsPackageManager;
this.addonService = addonService;
/** Execute the project-specific generator */
this.executeProjectGenerator = async ({
projectType,
frameworkInfo,
options,
selectedFeatures,
language
}) => {
let generator = generatorRegistry.get(projectType);
if (!generator)
throw new Error(`No generator found for project type: ${projectType}`);
let npmOptions = {
type: "devDependencies",
skipInstall: options.skipInstall
}, generatorModule = generator, frameworkOptions = await generatorModule.configure(this.jsPackageManager, {
framework: frameworkInfo.framework,
renderer: frameworkInfo.renderer,
builder: frameworkInfo.builder,
language,
linkable: !!options.linkable,
features: selectedFeatures,
dependencyCollector: this.dependencyCollector,
yes: options.yes
}), generatorOptions = {
language,
builder: frameworkInfo.builder,
framework: frameworkInfo.framework,
renderer: frameworkInfo.renderer,
linkable: !!options.linkable,
pnp: !!options.usePnp,
yes: !!options.yes,
projectType,
features: selectedFeatures,
dependencyCollector: this.dependencyCollector
};
if (frameworkOptions.skipGenerator)
return generatorModule.postConfigure && await generatorModule.postConfigure({ packageManager: this.jsPackageManager }), {
shouldRunDev: frameworkOptions.shouldRunDev,
storybookCommand: frameworkOptions.storybookCommand,
extraAddons: []
};
let extraAddons = this.addonService.getAddonsForFeatures(selectedFeatures), generatorResult = await baseGenerator(
this.jsPackageManager,
npmOptions,
generatorOptions,
{
...frameworkOptions,
extraAddons: [...frameworkOptions.extraAddons ?? [], ...extraAddons]
}
);
return generatorModule.postConfigure && await generatorModule.postConfigure({ packageManager: this.jsPackageManager }), {
...generatorResult,
extraAddons
};
};
}
async execute({
projectType,
options,
frameworkInfo,
selectedFeatures,
language
}) {
let generatorResult = await this.executeProjectGenerator({
projectType,
frameworkInfo,
options,
selectedFeatures,
language
});
return {
...generatorResult,
configDir: "configDir" in generatorResult ? generatorResult.configDir : void 0,
storybookCommand: generatorResult.storybookCommand !== void 0 ? generatorResult.storybookCommand : this.jsPackageManager.getRunCommand("storybook")
};
}
}, executeGeneratorExecution = ({
dependencyCollector,
packageManager,
...options
}) => new GeneratorExecutionCommand(dependencyCollector, packageManager).execute(options);
// src/commands/AddonConfigurationCommand.ts
var import_ts_dedent8 = __toESM(require_dist(), 1);
import { AddonVitestService as AddonVitestService2 } from "storybook/internal/cli";
import { CLI_COLORS as CLI_COLORS2, logger as logger9, prompt as prompt6 } from "storybook/internal/node-logger";
import { ErrorCollector } from "storybook/internal/telemetry";
var ADDON_INSTALLATION_INSTRUCTIONS = {
"@storybook/addon-vitest": "https://storybook.js.org/docs/writing-tests/integrations/vitest-addon#manual-setup"
}, AddonConfigurationCommand = class {
constructor(packageManager, commandOptions, addonVitestService = new AddonVitestService2(packageManager), telemetryService = new TelemetryService(commandOptions.disableTelemetry)) {
this.packageManager = packageManager;
this.commandOptions = commandOptions;
this.addonVitestService = addonVitestService;
this.telemetryService = telemetryService;
}
/** Execute addon configuration */
async execute({
addons,
configDir,
dependencyInstallationResult
}) {
if (!(dependencyInstallationResult.status === "success" && !this.commandOptions.skipInstall) && this.getAddonsWithInstructions(addons).length > 0)
return this.logManualAddonInstructions(addons), { status: "failed" };
if (!configDir || addons.length === 0)
return { status: "success" };
try {
let { hasFailures, addonResults } = await this.configureAddons(configDir, addons);
if (addonResults.has("@storybook/addon-vitest")) {
let { result } = await this.addonVitestService.installPlaywright({
yes: this.commandOptions.yes
});
await this.telemetryService.trackPlaywrightPromptDecision(result);
}
return { status: hasFailures ? "failed" : "success" };
} catch (e) {
return logger9.error("Unexpected error during addon configuration:"), logger9.error(e), { status: "failed" };
}
}
getAddonsWithInstructions(addons) {
return addons.filter((addon) => ADDON_INSTALLATION_INSTRUCTIONS[addon]);
}
logManualAddonInstructions(addons) {
let addonsWithInstructions = this.getAddonsWithInstructions(addons);
addonsWithInstructions.length > 0 && logger9.warn(import_ts_dedent8.dedent`
The following addons couldn't be configured:
${addonsWithInstructions.map((addon) => {
let manualInstructionLink = ADDON_INSTALLATION_INSTRUCTIONS[addon];
return `- ${addon}: ${manualInstructionLink}`;
}).join(`
`)}
${addonsWithInstructions.length > 0 ? "Please follow each addon's configuration instructions manually." : ""}
`);
}
/** Configure test addons (a11y and vitest) */
async configureAddons(configDir, addons) {
let { postinstallAddon } = await import("./postinstallAddon-SJ3IXJLQ.js"), task = prompt6.taskLog({
id: "configure-addons",
title: "Configuring addons..."
}), addonResults = /* @__PURE__ */ new Map();
for (let addon of addons)
try {
task.message(`Configuring ${addon}...`), await postinstallAddon(addon, {
packageManager: this.packageManager.type,
configDir,
yes: this.commandOptions.yes,
skipInstall: !0,
skipDependencyManagement: !0,
logger: logger9,
prompt: prompt6
}), task.message(`${addon} configured
`), addonResults.set(addon, null);
} catch (e) {
ErrorCollector.addError(e), addonResults.set(addon, e);
}
let hasFailures = [...addonResults.values()].some((result) => result !== null);
return hasFailures ? task.error("Failed to configure addons") : task.success("Addons configured successfully"), addons.forEach((addon, index) => {
let error = addonResults.get(addon);
logger9.log(CLI_COLORS2.muted(error ? `\u274C ${addon}` : `\u2705 ${addon}`), {
spacing: index === 0 ? 1 : 0
});
}), { hasFailures, addonResults };
}
}, executeAddonConfiguration = ({
packageManager,
options,
...rest
}) => new AddonConfigurationCommand(packageManager, options).execute(rest);
// src/commands/DependencyInstallationCommand.ts
import { AddonVitestService as AddonVitestService3 } from "storybook/internal/cli";
import { logger as logger10, prompt as prompt7 } from "storybook/internal/node-logger";
import { ErrorCollector as ErrorCollector2 } from "storybook/internal/telemetry";
import { Feature as Feature5 } from "storybook/internal/types";
var DependencyInstallationCommand = class {
constructor(dependencyCollector, packageManager, addonVitestService = new AddonVitestService3(packageManager)) {
this.dependencyCollector = dependencyCollector;
this.packageManager = packageManager;
this.addonVitestService = addonVitestService;
}
/** Execute dependency installation */
async execute({
skipInstall = !1,
selectedFeatures
}) {
if (await this.collectAddonDependencies(selectedFeatures), !this.dependencyCollector.hasPackages() && skipInstall)
return { status: "success" };
let { dependencies, devDependencies } = this.dependencyCollector.getAllPackages(), task = prompt7.taskLog({
id: "adding-dependencies",
title: "Adding dependencies to package.json"
});
if (dependencies.length > 0 && (task.message(`Adding dependencies:
` + dependencies.map((dep) => `- ${dep}`).join(`
`)), await this.packageManager.addDependencies(
{ type: "dependencies", skipInstall: !0 },
dependencies
)), devDependencies.length > 0 && (task.message(
`Adding devDependencies:
` + devDependencies.map((dep) => `- ${dep}`).join(`
`)
), await this.packageManager.addDependencies(
{ type: "devDependencies", skipInstall: !0 },
devDependencies
)), task.success("Dependencies added to package.json", { showLog: !0 }), !skipInstall && this.dependencyCollector.hasPackages())
try {
await this.packageManager.installDependencies();
} catch (err) {
return ErrorCollector2.addError(err), { status: "failed" };
}
return { status: "success" };
}
/** Collect addon dependencies without installing them */
async collectAddonDependencies(selectedFeatures) {
try {
if (selectedFeatures.has(Feature5.TEST)) {
let vitestDeps = await this.addonVitestService.collectDependencies();
this.dependencyCollector.addDevDependencies(vitestDeps);
}
} catch (err) {
logger10.warn(`Failed to collect addon dependencies: ${err}`);
}
}
}, executeDependencyInstallation = ({
dependencyCollector,
packageManager,
...props
}) => new DependencyInstallationCommand(dependencyCollector, packageManager).execute(props);
// src/commands/FinalizationCommand.ts
import fs from "node:fs/promises";
import { getProjectRoot as getProjectRoot3 } from "storybook/internal/common";
import { CLI_COLORS as CLI_COLORS3, logTracker, logger as logger11 } from "storybook/internal/node-logger";
import { ErrorCollector as ErrorCollector3 } from "storybook/internal/telemetry";
var import_ts_dedent9 = __toESM(require_dist(), 1), FinalizationCommand = class {
constructor(logfile) {
this.logfile = logfile;
}
/** Execute finalization steps */
async execute({ storybookCommand }) {
await this.updateGitignore(), ErrorCollector3.getErrors().length > 0 ? await this.printFailureMessage(storybookCommand) : this.printSuccessMessage(storybookCommand);
}
/** Update .gitignore with Storybook-specific entries */
async updateGitignore() {
let foundGitIgnoreFile = up2(".gitignore"), rootDirectory = getProjectRoot3();
if (!foundGitIgnoreFile || !foundGitIgnoreFile.includes(rootDirectory))
return;
let contents = await fs.readFile(foundGitIgnoreFile, "utf-8"), hasStorybookLog = contents.includes("*storybook.log"), hasStorybookStatic = contents.includes("storybook-static"), linesToAdd = [
hasStorybookLog ? "" : "*storybook.log",
hasStorybookStatic ? "" : "storybook-static"
].filter(Boolean).join(`
`);
linesToAdd && await fs.appendFile(foundGitIgnoreFile, `
${linesToAdd}
`);
}
async printFailureMessage(storybookCommand) {
logger11.warn(
"Storybook setup completed, but some non-blocking errors occurred. Please check the log file below for details."
), this.printNextSteps(storybookCommand);
try {
let logFile = await logTracker.writeToFile(this.logfile);
logger11.warn(`Debug logs are written to: ${logFile}`);
} catch {
}
}
/** Print success message with feature summary */
printSuccessMessage(storybookCommand) {
logger11.step(CLI_COLORS3.success("Storybook was successfully installed in your project!")), this.printNextSteps(storybookCommand);
}
printNextSteps(storybookCommand) {
storybookCommand && logger11.log(
`To run Storybook manually, run ${CLI_COLORS3.cta(storybookCommand)}. CTRL+C to stop.`
), logger11.log(import_ts_dedent9.dedent`
Wanna know more about Storybook? Check out ${CLI_COLORS3.cta("https://storybook.js.org/")}
Having trouble or want to chat? Join us at ${CLI_COLORS3.cta("https://discord.gg/storybook/")}
`);
}
}, executeFinalization = ({
logfile,
...params
}) => new FinalizationCommand(logfile).execute(params);
// src/dependency-collector.ts
var DependencyCollector = class {
constructor() {
this.packages = /* @__PURE__ */ new Map([
["dependencies", /* @__PURE__ */ new Map()],
["devDependencies", /* @__PURE__ */ new Map()]
]);
}
/** Add development dependencies */
addDevDependencies(packageNames) {
this.add("devDependencies", packageNames);
}
/** Add regular dependencies */
addDependencies(packageNames) {
this.add("dependencies", packageNames);
}
/** Get all packages across all types */
getAllPackages() {
return {
dependencies: this.getDependencies(),
devDependencies: this.getDevDependencies()
};
}
/** Check if collector has any packages */
hasPackages() {
return this.packages.get("dependencies").size > 0 || this.packages.get("devDependencies").size > 0;
}
/** Get all version conflicts across all dependency types */
getVersionConflicts() {
let conflicts = [];
for (let [type, typeMap] of this.packages.entries()) {
let packageNames = /* @__PURE__ */ new Map();
typeMap.forEach((version, name) => {
let versions2 = packageNames.get(name) || [];
versions2.push(version), packageNames.set(name, versions2);
}), packageNames.forEach((versions2, name) => {
versions2.length > 1 && new Set(versions2).size > 1 && conflicts.push({
packageName: name,
existingVersion: versions2[0],
newVersion: versions2[versions2.length - 1],
type
});
});
}
return conflicts;
}
/** Merge dependencies from another collector */
merge(other) {
let { dependencies, devDependencies } = other.getAllPackages();
this.addDependencies(dependencies), this.addDevDependencies(devDependencies);
}
/** Validate that all packages have valid version specifiers */
validate() {
let errors = [];
for (let [type, typeMap] of this.packages.entries())
typeMap.forEach((version, name) => {
(!name || name.trim() === "") && errors.push(`Invalid package name in ${type}: empty or whitespace`), version === "" && errors.push(`Package ${name} in ${type} has empty version`);
});
return {
valid: errors.length === 0,
errors
};
}
/** Get count of packages by type */
getPackageCount(type) {
return type ? this.packages.get(type).size : this.packages.get("dependencies").size + this.packages.get("devDependencies").size;
}
/**
* Add packages to the collector
*
* @param type - The dependency type (dependencies or devDependencies)
* @param packageNames - Array of package names, optionally with version specifiers (e.g.,
* 'react@18.0.0')
*/
add(type, packageNames) {
let typeMap = this.packages.get(type);
for (let pkg of packageNames) {
let { name, version } = this.parsePackage(pkg);
typeMap.has(name) ? version && typeMap.set(name, version) : typeMap.set(name, version || "latest");
}
}
/** Get all packages with their versions for a specific type */
getPackages(type) {
let typeMap = this.packages.get(type);
return Array.from(typeMap.entries()).map(
([name, version]) => version === "latest" ? name : `${name}@${version}`
);
}
/** Get all development dependencies */
getDevDependencies() {
return this.getPackages("devDependencies");
}
/** Get all regular dependencies */
getDependencies() {
return this.getPackages("dependencies");
}
/**
* Parse a package string into name and version
*
* @param pkg - Package string (e.g., 'react@18.0.0' or 'react')
*/
parsePackage(pkg) {
let scopedMatch = pkg.match(/^(@[^@]+\/[^@]+)(?:@(.+))?$/);
if (scopedMatch)
return {
name: scopedMatch[1],
version: scopedMatch[2]
};
let regularMatch = pkg.match(/^([^@]+)(?:@(.+))?$/);
return regularMatch ? {
name: regularMatch[1],
version: regularMatch[2]
} : { name: pkg };
}
};
// src/generators/ANGULAR/index.ts
var import_ts_dedent10 = __toESM(require_dist(), 1);
import { dirname as dirname3, join as join4 } from "node:path";
import { fileURLToPath as fileURLToPath2 } from "node:url";
import { AngularJSON, ProjectType as ProjectType4, copyTemplate } from "storybook/internal/cli";
import { logger as logger12, prompt as prompt8 } from "storybook/internal/node-logger";
import { SupportedBuilder as SupportedBuilder2, SupportedFramework as SupportedFramework3, SupportedRenderer } from "storybook/internal/types";
import semver2 from "semver";
var ANGULAR_default = {
metadata: {
projectType: ProjectType4.ANGULAR,
renderer: SupportedRenderer.ANGULAR,
framework: SupportedFramework3.ANGULAR,
builderOverride: SupportedBuilder2.WEBPACK5
},
configure: async (packageManager, context) => {
let angularJSON = new AngularJSON();
if (!angularJSON.projects || angularJSON.projects && Object.keys(angularJSON.projects).length === 0)
throw new Error(
"Storybook was not able to find any projects in your angular.json file. Are you sure this is an Angular CLI project?"
);
if (angularJSON.projectsWithoutStorybook.length === 0)
throw new Error(
"Every project in your workspace is already set up with Storybook. There is nothing to do!"
);
let angularProjectName = await angularJSON.getProjectName();
logger12.log(`Adding Storybook support to your "${angularProjectName}" project`);
let angularProject = angularJSON.getProjectSettingsByName(angularProjectName);
if (!angularProject)
throw new Error(
`Somehow we were not able to retrieve the "${angularProjectName}" project in your angular.json file. This is likely a bug in Storybook, please file an issue.`
);
let { root, projectType } = angularProject, { projects } = angularJSON, useCompodoc = context.yes ? !0 : await promptForCompoDocs(), storybookFolder = root ? `${root}/.storybook` : ".storybook";
angularJSON.addStorybookEntries({
angularProjectName,
storybookFolder,
useCompodoc,
root
}), angularJSON.write();
let angularVersion = packageManager.getDependencyVersion("@angular/core");
Object.keys(projects).length === 1 && packageManager.addScripts({
storybook: `ng run ${angularProjectName}:storybook`,
"build-storybook": `ng run ${angularProjectName}:build-storybook`
});
let projectTypeValue = projectType || "application";
projectTypeValue !== "application" && projectTypeValue !== "library" && (projectTypeValue = "application");
let templateDir = join4(
dirname3(fileURLToPath2(import.meta.resolve("create-storybook/package.json"))),
"templates",
"angular",
projectTypeValue
);
templateDir && copyTemplate(templateDir, root || void 0);
let devkitVersion = ((ngRange) => {
if (!ngRange)
return;
let min = semver2.minVersion(ngRange);
if (!min)
return;
let pre = min.prerelease && min.prerelease.length > 0 ? `-${min.prerelease.join(".")}` : "", versionCore = `0.${min.major * 100 + min.minor}.${min.patch}${pre}`;
return ngRange.trim().startsWith("^") ? `^${versionCore}` : versionCore;
})(angularVersion);
return {
extraPackages: [
...[
angularVersion ? `@angular-devkit/build-angular@${angularVersion}` : "@angular-devkit/build-angular",
devkitVersion ? `@angular-devkit/architect@${devkitVersion}` : "@angular-devkit/architect",
angularVersion ? `@angular-devkit/core@${angularVersion}` : "@angular-devkit/core",
angularVersion ? `@angular/platform-browser-dynamic@${angularVersion}` : "@angular/platform-browser-dynamic"
],
...useCompodoc ? ["@compodoc/compodoc", "@storybook/addon-docs"] : []
],
addScripts: !1,
// Handled above based on project count
componentsDestinationPath: root ? `${root}/src/stories` : void 0,
storybookConfigFolder: storybookFolder,
storybookCommand: `ng run ${angularProjectName}:storybook`,
...useCompodoc && {
frameworkPreviewParts: {
prefix: import_ts_dedent10.dedent`
import { setCompodocJson } from "@storybook/addon-docs/angular";
import docJson from "../documentation.json";
setCompodocJson(docJson);
`.trimStart()
}
}
};
}
};
function promptForCompoDocs() {
return logger12.log(
"Compodoc is a great tool to generate documentation for your Angular projects. Storybook can use the documentation generated by Compodoc to extract argument definitions and JSDOC comments to display them in the Storybook UI. We highly recommend using Compodoc for your Angular projects to get the best experience out of Storybook."
), prompt8.confirm({
message: "Do you want to use Compodoc for documentation?",
initialValue: !0
});
}
// src/generators/EMBER/index.ts
import { ProjectType as ProjectType5 } from "storybook/internal/cli";
import { SupportedBuilder as SupportedBuilder3, SupportedFramework as SupportedFramework4, SupportedRenderer as SupportedRenderer2 } from "storybook/internal/types";
var EMBER_default = {
metadata: {
projectType: ProjectType5.EMBER,
renderer: SupportedRenderer2.EMBER,
framework: SupportedFramework4.EMBER,
builderOverride: SupportedBuilder3.WEBPACK5
},
configure: async () => ({
staticDir: "dist"
})
};
// src/generators/HTML/index.ts
import { ProjectType as ProjectType6 } from "storybook/internal/cli";
import { SupportedBuilder as SupportedBuilder4, SupportedRenderer as SupportedRenderer3 } from "storybook/internal/types";
var HTML_default = {
metadata: {
projectType: ProjectType6.HTML,
renderer: SupportedRenderer3.HTML
},
configure: async () => ({
webpackCompiler: ({ builder }) => builder === SupportedBuilder4.WEBPACK5 ? "swc" : void 0
})
};
// src/generators/NEXTJS/index.ts
var import_ts_dedent11 = __toESM(require_dist(), 1);
import { existsSync as existsSync3 } from "node:fs";
import { readFile } from "node:fs/promises";
import { join as join5 } from "node:path";
import { ProjectType as ProjectType7 } from "storybook/internal/cli";
import { findFilesUp } from "storybook/internal/common";
import { logger as logger13, prompt as prompt9 } from "storybook/internal/node-logger";
import { SupportedBuilder as SupportedBuilder5, SupportedFramework as SupportedFramework5, SupportedRenderer as SupportedRenderer4 } from "storybook/internal/types";
var NEXT_CONFIG_FILES = [
"next.config.mjs",
"next.config.js",
"next.config.ts",
"next.config.mts"
], BABEL_CONFIG_FILES = [
".babelrc",
".babelrc.json",
".babelrc.js",
".babelrc.mjs",
".babelrc.cjs",
"babel.config.js",
"babel.config.json",
"babel.config.mjs",
"babel.config.cjs"
], NEXTJS_default = {
metadata: {
projectType: ProjectType7.NEXTJS,
renderer: SupportedRenderer4.REACT,
framework: (builder) => builder === SupportedBuilder5.VITE ? SupportedFramework5.NEXTJS_VITE : SupportedFramework5.NEXTJS,
builderOverride: async () => {
let nextConfigFile = findFilesUp(NEXT_CONFIG_FILES, process.cwd())[0];
if (!nextConfigFile)
return SupportedBuilder5.VITE;
let hasCustomWebpackConfig = (await readFile(nextConfigFile, "utf-8")).includes("webpack"), babelConfigFile = findFilesUp(BABEL_CONFIG_FILES, process.cwd())[0];
if (!hasCustomWebpackConfig && !babelConfigFile)
return SupportedBuilder5.VITE;
{
let reason = hasCustomWebpackConfig && babelConfigFile ? "custom webpack config and babel config" : hasCustomWebpackConfig ? "custom webpack config" : "custom babel config";
return logger13.info(import_ts_dedent11.dedent`
Storybook has two Next.js builder options: Webpack 5 and Vite.
We generally recommend nextjs-vite, which is much faster, more modern, and supports our latest testing features.
However, your project has a ${reason}, which is not supported by nextjs-vite, so please be aware of that if you choose that option.
`), prompt9.select({
message: "Which framework would you like to use?",
options: [
{ label: "@storybook/nextjs-vite", value: SupportedBuilder5.VITE },
{ label: "@storybook/nextjs (Webpack)", value: SupportedBuilder5.WEBPACK5 }
]
});
}
}
},
configure: async (packageManager, context) => {
let staticDir;
existsSync3(join5(process.cwd(), "public")) && (staticDir = "public");
let extraPackages = [];
return context.builder === SupportedBuilder5.VITE && extraPackages.push("vite"), {
staticDir,
extraPackages
};
}
};
// src/generators/NUXT/index.ts
import { ProjectType as ProjectType8 } from "storybook/internal/cli";
import { logger as logger14 } from "storybook/internal/node-logger";
import {
Feature as Feature6,
SupportedBuilder as SupportedBuilder6,
SupportedFramework as SupportedFramework6,
SupportedRenderer as SupportedRenderer5
} from "storybook/internal/types";
var NUXT_default = {
metadata: {
projectType: ProjectType8.NUXT,
renderer: SupportedRenderer5.VUE3,
framework: SupportedFramework6.NUXT,
builderOverride: SupportedBuilder6.VITE
},
configure: async (packageManager, context) => {
let extraStories = context.features.has(Feature6.DOCS) ? ["../components/**/*.mdx"] : [];
return extraStories.push("../components/**/*.stories.@(js|jsx|ts|tsx|mdx)"), logger14.info(
"Note: Nuxt requires dependency installation to configure modules. Dependencies will be installed even if --skip-install is specified."
), await packageManager.runPackageCommand({
args: ["nuxi", "module", "add", "@nuxtjs/storybook", "--skipInstall"]
}), {
extraPackages: ["@nuxtjs/storybook"],
installFrameworkPackages: !1,
componentsDestinationPath: "./components",
extraMain: {
stories: extraStories
}
};
}
};
// src/generators/PREACT/index.ts
import { ProjectType as ProjectType9 } from "storybook/internal/cli";
import { SupportedBuilder as SupportedBuilder7, SupportedRenderer as SupportedRenderer6 } from "storybook/internal/types";
var PREACT_default = {
metadata: {
projectType: ProjectType9.PREACT,
renderer: SupportedRenderer6.PREACT
},
configure: async () => ({
webpackCompiler: ({ builder }) => builder === SupportedBuilder7.WEBPACK5 ? "swc" : void 0
})
};
// src/generators/QWIK/index.ts
import { ProjectType as ProjectType10 } from "storybook/internal/cli";
import { SupportedFramework as SupportedFramework7, SupportedRenderer as SupportedRenderer7 } from "storybook/internal/types";
var QWIK_default = {
metadata: {
projectType: ProjectType10.QWIK,
renderer: SupportedRenderer7.QWIK,
framework: SupportedFramework7.QWIK
},
configure: async () => ({})
};
// src/generators/REACT/index.ts
import { ProjectType as ProjectType11 } from "storybook/internal/cli";
import { SupportedBuilder as SupportedBuilder8, SupportedLanguage as SupportedLanguage4, SupportedRenderer as SupportedRenderer8 } from "storybook/internal/types";
var REACT_default = {
metadata: {
projectType: ProjectType11.REACT,
renderer: SupportedRenderer8.REACT
},
configure: async (packageManager, { language }) => ({
extraPackages: language === SupportedLanguage4.JAVASCRIPT ? ["prop-types"] : [],
webpackCompiler: ({ builder }) => builder === SupportedBuilder8.WEBPACK5 ? "swc" : void 0
})
};
// src/generators/REACT_NATIVE/index.ts
var import_ts_dedent12 = __toESM(require_dist(), 1);
import { ProjectType as ProjectType12, copyTemplateFiles as copyTemplateFiles2, getBabelDependencies } from "storybook/internal/cli";
import { CLI_COLORS as CLI_COLORS4, logger as logger15 } from "storybook/internal/node-logger";
import { SupportedBuilder as SupportedBuilder9, SupportedLanguage as SupportedLanguage5, SupportedRenderer as SupportedRenderer9 } from "storybook/internal/types";
var REACT_NATIVE_default = {
metadata: {
projectType: ProjectType12.REACT_NATIVE,
renderer: SupportedRenderer9.REACT,
builderOverride: SupportedBuilder9.WEBPACK5,
framework: null
},
configure: async (packageManager, context) => {
let missingReactDom = !packageManager.getDependencyVersion("react-dom"), reactVersion = packageManager.getDependencyVersion("react"), dependencyCollector = context.dependencyCollector, packagesToResolve = [
...[
"react-native-safe-area-context",
"@react-native-async-storage/async-storage",
"@react-native-community/datetimepicker",
"@react-native-community/slider",
"react-native-reanimated",
"react-native-gesture-handler",
"@gorhom/bottom-sheet",
"react-native-svg"
].filter((dep) => !packageManager.getDependencyVersion(dep)),
"@storybook/addon-ondevice-controls",
"@storybook/addon-ondevice-actions",
"@storybook/react-native",
"storybook"
], versionedPackages = await packageManager.getVersionedPackages(packagesToResolve), packages = [
...await getBabelDependencies(packageManager),
...versionedPackages,
...missingReactDom && reactVersion ? [`react-dom@${reactVersion}`] : []
];
dependencyCollector.addDependencies(packages), packageManager.addScripts({
"storybook-generate": "sb-rn-get-stories"
});
let storybookConfigFolder = ".rnstorybook";
return await copyTemplateFiles2({
packageManager,
templateLocation: SupportedRenderer9.REACT_NATIVE,
language: SupportedLanguage5.TYPESCRIPT,
destination: storybookConfigFolder,
features: context.features
}), {
// Signal to skip baseGenerator by returning minimal config
storybookConfigFolder,
skipGenerator: !0,
storybookCommand: null,
shouldRunDev: !1
// React Native needs additional manual steps to configure the project
};
},
postConfigure: ({ packageManager }) => {
logger15.log(import_ts_dedent12.dedent`
${CLI_COLORS4.warning("The Storybook for React Native installation is not 100% automated.")}
To run Storybook for React Native, you will need to:
1. Replace the contents of your app entry with the following
${CLI_COLORS4.info(" export {default} from './.rnstorybook'; ")}
2. Wrap your metro config with the withStorybook enhancer function like this:
${CLI_COLORS4.info(" const { withStorybook } = require('@storybook/react-native/metro/withStorybook'); ")}
${CLI_COLORS4.info(" module.exports = withStorybook(defaultConfig); ")}
For more details go to:
https://github.com/storybookjs/react-native#getting-started
Then to start Storybook for React Native, run:
${CLI_COLORS4.cta(" " + packageManager.getRunCommand("start") + " ")}
`);
}
};
// src/generators/REACT_NATIVE_AND_RNW/index.ts
import { ProjectType as ProjectType14 } from "storybook/internal/cli";
import { SupportedBuilder as SupportedBuilder11, SupportedFramework as SupportedFramework9, SupportedRenderer as SupportedRenderer11 } from "storybook/internal/types";
// src/generators/REACT_NATIVE_WEB/index.ts
import { readdir, rm as rm2 } from "node:fs/promises";
import { join as join6 } from "node:path";
import { ProjectType as ProjectType13, cliStoriesTargetPath } from "storybook/internal/cli";
import {
SupportedBuilder as SupportedBuilder10,
SupportedFramework as SupportedFramework8,
SupportedLanguage as SupportedLanguage6,
SupportedRenderer as SupportedRenderer10
} from "storybook/internal/types";
var REACT_NATIVE_WEB_default = {
metadata: {
projectType: ProjectType13.REACT_NATIVE_WEB,
renderer: SupportedRenderer10.REACT,
framework: SupportedFramework8.REACT_NATIVE_WEB_VITE,
builderOverride: SupportedBuilder10.VITE
},
configure: async (packageManager, { language }) => {
let extraPackages = ["vite", "react-native-web"];
return language === SupportedLanguage6.JAVASCRIPT && extraPackages.push("prop-types"), {
extraPackages
};
},
postConfigure: async () => {
try {
let targetPath = await cliStoriesTargetPath(), cssFiles = (await readdir(targetPath)).filter((f) => f.endsWith(".css"));
await Promise.all(cssFiles.map((f) => rm2(join6(targetPath, f))));
} catch {
}
}
};
// src/generators/REACT_NATIVE_AND_RNW/index.ts
var REACT_NATIVE_AND_RNW_default = {
metadata: {
projectType: ProjectType14.REACT_NATIVE_AND_RNW,
renderer: SupportedRenderer11.REACT,
framework: SupportedFramework9.REACT_NATIVE_WEB_VITE,
builderOverride: SupportedBuilder11.VITE
},
configure: async (packageManager, context) => (await REACT_NATIVE_default.configure(packageManager, context), {
...await REACT_NATIVE_WEB_default.configure(
packageManager,
context
),
shouldRunDev: !1
// React Native needs additional manual steps to configure the project
}),
postConfigure: async ({ packageManager }) => {
await REACT_NATIVE_WEB_default.postConfigure(), REACT_NATIVE_default.postConfigure({ packageManager });
}
};
// src/generators/REACT_SCRIPTS/index.ts
var import_ts_dedent13 = __toESM(require_dist(), 1);
import { existsSync as existsSync4 } from "node:fs";
import { resolve as resolve4 } from "node:path";
import { fileURLToPath as fileURLToPath3 } from "node:url";
import { ProjectType as ProjectType15 } from "storybook/internal/cli";
import { SupportedBuilder as SupportedBuilder12, SupportedRenderer as SupportedRenderer12 } from "storybook/internal/types";
import semver3 from "semver";
var REACT_SCRIPTS_default = {
metadata: {
projectType: ProjectType15.REACT_SCRIPTS,
renderer: SupportedRenderer12.REACT,
builderOverride: SupportedBuilder12.WEBPACK5
},
configure: async (packageManager, context) => {
let monorepoRootPath = fileURLToPath3(new URL("../../../../../../..", import.meta.url)), extraMain = context.linkable ? {
webpackFinal: `%%(config) => {
// add monorepo root as a valid directory to import modules from
config.resolve.plugins.forEach((p) => {
if (Array.isArray(p.appSrcs)) {
p.appSrcs.push('${monorepoRootPath}');
}
});
return config;
}
%%`
} : {}, craVersion = (await packageManager.getModulePackageJSON("react-scripts"))?.version ?? null;
if (craVersion === null)
throw new Error(import_ts_dedent13.dedent`
It looks like you're trying to initialize Storybook in a CRA project that does not have react-scripts installed.
Please install it and make sure it's of version 5 or higher, which are the versions supported by Storybook 7.0+.
`);
if (craVersion && semver3.lt(craVersion, "5.0.0"))
throw new Error(import_ts_dedent13.dedent`
Storybook 7.0+ doesn't support react-scripts@<5.0.0.
https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#create-react-app-dropped-cra4-support
`);
return {
webpackCompiler: () => {
},
extraAddons: ["@storybook/preset-create-react-app"],
extraPackages: ["webpack", "prop-types"],
staticDir: existsSync4(resolve4("./public")) ? "public" : void 0,
extraMain
};
}
};
// src/generators/SERVER/index.ts
import { ProjectType as ProjectType16 } from "storybook/internal/cli";
import { SupportedBuilder as SupportedBuilder13, SupportedRenderer as SupportedRenderer13 } from "storybook/internal/types";
var SERVER_default = {
metadata: {
projectType: ProjectType16.SERVER,
renderer: SupportedRenderer13.SERVER,
builderOverride: SupportedBuilder13.WEBPACK5
},
configure: async () => ({
webpackCompiler: () => "swc",
extensions: ["json", "yaml", "yml"]
})
};
// src/generators/SOLID/index.ts
import { ProjectType as ProjectType17 } from "storybook/internal/cli";
import { SupportedBuilder as SupportedBuilder14, SupportedFramework as SupportedFramework10, SupportedRenderer as SupportedRenderer14 } from "storybook/internal/types";
var SOLID_default = {
metadata: {
projectType: ProjectType17.SOLID,
renderer: SupportedRenderer14.SOLID,
framework: SupportedFramework10.SOLID,
builderOverride: SupportedBuilder14.VITE
},
configure: async () => ({
addComponents: !0
})
};
// src/generators/SVELTE/index.ts
import { ProjectType as ProjectType18 } from "storybook/internal/cli";
import { SupportedRenderer as SupportedRenderer15 } from "storybook/internal/types";
var SVELTE_default = {
metadata: {
projectType: ProjectType18.SVELTE,
renderer: SupportedRenderer15.SVELTE
},
configure: async () => ({
extensions: ["js", "ts", "svelte"],
extraAddons: ["@storybook/addon-svelte-csf"]
})
};
// src/generators/SVELTEKIT/index.ts
import { ProjectType as ProjectType19 } from "storybook/internal/cli";
import { SupportedBuilder as SupportedBuilder15, SupportedFramework as SupportedFramework11, SupportedRenderer as SupportedRenderer16 } from "storybook/internal/types";
var SVELTEKIT_default = {
metadata: {
projectType: ProjectType19.SVELTEKIT,
renderer: SupportedRenderer16.SVELTE,
framework: SupportedFramework11.SVELTEKIT,
builderOverride: SupportedBuilder15.VITE
},
configure: async () => ({
extensions: ["js", "ts", "svelte"],
extraAddons: ["@storybook/addon-svelte-csf"]
})
};
// src/generators/VUE3/index.ts
import { ProjectType as ProjectType20 } from "storybook/internal/cli";
import { SupportedBuilder as SupportedBuilder16, SupportedRenderer as SupportedRenderer17 } from "storybook/internal/types";
var VUE3_default = {
metadata: {
projectType: ProjectType20.VUE3,
renderer: SupportedRenderer17.VUE3
},
configure: async () => ({
extraPackages: async ({ builder }) => builder === SupportedBuilder16.WEBPACK5 ? ["vue-loader@^17.0.0", "@vue/compiler-sfc@^3.2.0"] : [],
webpackCompiler: ({ builder }) => builder === SupportedBuilder16.WEBPACK5 ? "swc" : void 0
})
};
// src/generators/WEB-COMPONENTS/index.ts
import { ProjectType as ProjectType21 } from "storybook/internal/cli";
import { SupportedBuilder as SupportedBuilder17, SupportedRenderer as SupportedRenderer18 } from "storybook/internal/types";
var WEB_COMPONENTS_default = {
metadata: {
projectType: ProjectType21.WEB_COMPONENTS,
renderer: SupportedRenderer18.WEB_COMPONENTS
},
configure: async () => ({
extraPackages: ["lit"],
webpackCompiler: ({ builder }) => builder === SupportedBuilder17.WEBPACK5 ? "swc" : void 0
})
};
// src/generators/registerGenerators.ts
var setOfGenerators = /* @__PURE__ */ new Set([
REACT_default,
REACT_SCRIPTS_default,
REACT_NATIVE_default,
REACT_NATIVE_WEB_default,
REACT_NATIVE_AND_RNW_default,
VUE3_default,
NUXT_default,
ANGULAR_default,
NEXTJS_default,
SVELTE_default,
SVELTEKIT_default,
EMBER_default,
HTML_default,
WEB_COMPONENTS_default,
PREACT_default,
SOLID_default,
SERVER_default,
QWIK_default
]);
function registerAllGenerators() {
setOfGenerators.forEach((generator) => {
generatorRegistry.register(generator);
});
}
// src/initiate.ts
async function doInitiate(options) {
let telemetryService = new TelemetryService(options.disableTelemetry);
registerAllGenerators();
let dependencyCollector = new DependencyCollector(), { packageManager } = await executePreflightCheck(options), { projectType, language } = await executeProjectDetection(packageManager, options), { framework, builder, renderer } = await executeFrameworkDetection(
projectType,
packageManager,
options
), { newUser, selectedFeatures } = await executeUserPreferences({
packageManager,
options,
framework,
builder,
projectType
}), { configDir, storybookCommand, shouldRunDev, extraAddons } = await executeGeneratorExecution({
projectType,
packageManager,
frameworkInfo: { builder, framework, renderer },
options,
dependencyCollector,
selectedFeatures,
language
}), dependencyInstallationResult = await executeDependencyInstallation({
packageManager,
dependencyCollector,
skipInstall: !!options.skipInstall,
selectedFeatures
});
return dependencyCollector = null, await executeAddonConfiguration({
packageManager,
addons: extraAddons,
configDir,
dependencyInstallationResult,
options
}), await executeFinalization({
logfile: options.logfile,
storybookCommand
}), await telemetryService.trackInitWithContext(projectType, selectedFeatures, newUser), {
shouldRunDev: !!options.dev && !options.skipInstall && shouldRunDev !== !1 && dependencyInstallationResult.status === "success",
shouldOnboard: newUser,
projectType,
packageManager,
storybookCommand
};
}
var handleCommandFailure = async (logFilePath) => {
let logFile = await logTracker2.writeToFile(logFilePath);
logger16.error("Storybook encountered an error during initialization"), logger16.log(`Debug logs are written to: ${logFile}`), logger16.outro("Storybook exited with an error"), process.exit(1);
};
async function initiate(options) {
let initiateResult = await withTelemetry(
"init",
{
cliOptions: options,
printError: (err) => !err.handled && logger16.error(err)
},
async () => {
let result = await doInitiate(options);
return logger16.outro(""), result;
}
).catch(() => {
handleCommandFailure(options.logfile);
});
initiateResult?.shouldRunDev && await runStorybookDev(initiateResult);
}
async function runStorybookDev(result) {
let { projectType, packageManager, storybookCommand, shouldOnboard } = result;
if (storybookCommand)
try {
let supportsOnboarding = FeatureCompatibilityService.supportsOnboarding(projectType), flags = [];
packageManager.type === "npm" && flags.push("--silent"), (packageManager.type === PackageManagerName2.NPM || packageManager.type === PackageManagerName2.BUN) && projectType !== ProjectType22.ANGULAR && flags.push("--"), supportsOnboarding && shouldOnboard && flags.push("--initial-path=/onboarding"), flags.push("--quiet");
let [command, ...args] = [...storybookCommand.split(" "), ...flags];
executeCommand2({
command,
args,
stdio: "inherit"
});
} catch {
}
}
export {
doInitiate,
initiate
};