@storybook/cli 10.4.0-alpha.11 → 10.4.0-alpha.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_node-chunks/{block-dependencies-versions-2BVSMHVR.js → block-dependencies-versions-RWNRI4GB.js} +11 -11
- package/dist/_node-chunks/{block-experimental-addon-test-YTJO53FI.js → block-experimental-addon-test-PUHJ3NL6.js} +9 -9
- package/dist/_node-chunks/{block-major-version-LCXD4EE5.js → block-major-version-I3E62CBN.js} +9 -9
- package/dist/_node-chunks/{block-node-version-WZF6U3TL.js → block-node-version-NOND3SUE.js} +9 -9
- package/dist/_node-chunks/{block-webpack5-frameworks-EI3U2F35.js → block-webpack5-frameworks-22ZJLQUS.js} +11 -11
- package/dist/_node-chunks/{chunk-KSVLT5Q6.js → chunk-47LRJSKF.js} +6 -6
- package/dist/_node-chunks/{chunk-WCEKSVGD.js → chunk-6KUBKUXA.js} +7 -7
- package/dist/_node-chunks/{chunk-F3OYUBSA.js → chunk-M4GFZIKM.js} +7 -7
- package/dist/_node-chunks/chunk-OIPZEDTL.js +11 -0
- package/dist/_node-chunks/{chunk-6BWYOUU7.js → chunk-X5LJ3DDJ.js} +28 -17
- package/dist/_node-chunks/{globby-XYA3TN25.js → globby-KNFCECZ3.js} +8 -8
- package/dist/_node-chunks/{p-limit-U3C7RBHQ.js → p-limit-MF6JQ6VU.js} +7 -7
- package/dist/_node-chunks/{run-DB2FPOMF.js → run-OIEKD62P.js} +1302 -117
- package/dist/_node-chunks/setup-BYVI7PM2.js +281 -0
- package/dist/bin/index.js +7 -7
- package/package.json +4 -4
- package/dist/_node-chunks/chunk-6L6KWHQV.js +0 -11
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
1
|
+
import CJS_COMPAT_NODE_URL_k1889y8tin from 'node:url';
|
|
2
|
+
import CJS_COMPAT_NODE_PATH_k1889y8tin from 'node:path';
|
|
3
|
+
import CJS_COMPAT_NODE_MODULE_k1889y8tin from "node:module";
|
|
4
4
|
|
|
5
|
-
var __filename =
|
|
6
|
-
var __dirname =
|
|
7
|
-
var require =
|
|
5
|
+
var __filename = CJS_COMPAT_NODE_URL_k1889y8tin.fileURLToPath(import.meta.url);
|
|
6
|
+
var __dirname = CJS_COMPAT_NODE_PATH_k1889y8tin.dirname(__filename);
|
|
7
|
+
var require = CJS_COMPAT_NODE_MODULE_k1889y8tin.createRequire(import.meta.url);
|
|
8
8
|
|
|
9
9
|
// ------------------------------------------------------------
|
|
10
10
|
// end of CJS compatibility banner, injected by Storybook's esbuild configuration
|
|
@@ -23,20 +23,21 @@ import {
|
|
|
23
23
|
processAutoblockerResults,
|
|
24
24
|
require_picocolors,
|
|
25
25
|
shortenPath,
|
|
26
|
+
up,
|
|
26
27
|
updateMainConfig,
|
|
27
28
|
upgradeStorybookDependencies
|
|
28
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-X5LJ3DDJ.js";
|
|
29
30
|
import {
|
|
30
31
|
slash
|
|
31
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-6KUBKUXA.js";
|
|
32
33
|
import {
|
|
33
34
|
require_semver
|
|
34
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-M4GFZIKM.js";
|
|
35
36
|
import {
|
|
36
37
|
__commonJS,
|
|
37
38
|
__require,
|
|
38
39
|
__toESM
|
|
39
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-47LRJSKF.js";
|
|
40
41
|
|
|
41
42
|
// ../../../node_modules/envinfo/dist/envinfo.js
|
|
42
43
|
var require_envinfo = __commonJS({
|
|
@@ -4417,9 +4418,9 @@ var require_isexe = __commonJS({
|
|
|
4417
4418
|
if (typeof options == "function" && (cb = options, options = {}), !cb) {
|
|
4418
4419
|
if (typeof Promise != "function")
|
|
4419
4420
|
throw new TypeError("callback not provided");
|
|
4420
|
-
return new Promise(function(
|
|
4421
|
+
return new Promise(function(resolve4, reject) {
|
|
4421
4422
|
isexe(path4, options || {}, function(er, is) {
|
|
4422
|
-
er ? reject(er) :
|
|
4423
|
+
er ? reject(er) : resolve4(is);
|
|
4423
4424
|
});
|
|
4424
4425
|
});
|
|
4425
4426
|
}
|
|
@@ -4456,22 +4457,22 @@ var require_which = __commonJS({
|
|
|
4456
4457
|
};
|
|
4457
4458
|
}, which = (cmd, opt, cb) => {
|
|
4458
4459
|
typeof opt == "function" && (cb = opt, opt = {}), opt || (opt = {});
|
|
4459
|
-
let { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt), found = [], step = (i) => new Promise((
|
|
4460
|
+
let { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt), found = [], step = (i) => new Promise((resolve4, reject) => {
|
|
4460
4461
|
if (i === pathEnv.length)
|
|
4461
|
-
return opt.all && found.length ?
|
|
4462
|
+
return opt.all && found.length ? resolve4(found) : reject(getNotFoundError(cmd));
|
|
4462
4463
|
let ppRaw = pathEnv[i], pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw, pCmd = path4.join(pathPart, cmd), p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
|
|
4463
|
-
|
|
4464
|
-
}), subStep = (p, i, ii) => new Promise((
|
|
4464
|
+
resolve4(subStep(p, i, 0));
|
|
4465
|
+
}), subStep = (p, i, ii) => new Promise((resolve4, reject) => {
|
|
4465
4466
|
if (ii === pathExt.length)
|
|
4466
|
-
return
|
|
4467
|
+
return resolve4(step(i + 1));
|
|
4467
4468
|
let ext = pathExt[ii];
|
|
4468
4469
|
isexe(p + ext, { pathExt: pathExtExe }, (er, is) => {
|
|
4469
4470
|
if (!er && is)
|
|
4470
4471
|
if (opt.all)
|
|
4471
4472
|
found.push(p + ext);
|
|
4472
4473
|
else
|
|
4473
|
-
return
|
|
4474
|
-
return
|
|
4474
|
+
return resolve4(p + ext);
|
|
4475
|
+
return resolve4(subStep(p, i, ii + 1));
|
|
4475
4476
|
});
|
|
4476
4477
|
});
|
|
4477
4478
|
return cb ? step(0).then((res) => cb(null, res), cb) : step(0);
|
|
@@ -4710,17 +4711,16 @@ var require_cross_spawn = __commonJS({
|
|
|
4710
4711
|
var import_envinfo = __toESM(require_envinfo(), 1);
|
|
4711
4712
|
import { globalSettings } from "storybook/internal/cli";
|
|
4712
4713
|
import {
|
|
4713
|
-
HandledError as
|
|
4714
|
+
HandledError as HandledError3,
|
|
4714
4715
|
JsPackageManagerFactory as JsPackageManagerFactory3,
|
|
4715
4716
|
PackageManagerName as PackageManagerName3,
|
|
4716
|
-
isCI as isCI2,
|
|
4717
4717
|
optionalEnvToBoolean as optionalEnvToBoolean3,
|
|
4718
4718
|
removeAddon as remove,
|
|
4719
4719
|
versions as versions5
|
|
4720
4720
|
} from "storybook/internal/common";
|
|
4721
4721
|
import { withTelemetry } from "storybook/internal/core-server";
|
|
4722
|
-
import { CLI_COLORS as CLI_COLORS5, logTracker as logTracker4, logger as
|
|
4723
|
-
import { addToGlobalContext, telemetry as
|
|
4722
|
+
import { CLI_COLORS as CLI_COLORS5, logTracker as logTracker4, logger as logger24 } from "storybook/internal/node-logger";
|
|
4723
|
+
import { addToGlobalContext, telemetry as telemetry3 } from "storybook/internal/telemetry";
|
|
4724
4724
|
import { Option, program } from "commander";
|
|
4725
4725
|
|
|
4726
4726
|
// ../../../node_modules/leven/index.js
|
|
@@ -4761,7 +4761,7 @@ function leven(first, second, options) {
|
|
|
4761
4761
|
var import_picocolors17 = __toESM(require_picocolors(), 1);
|
|
4762
4762
|
|
|
4763
4763
|
// package.json
|
|
4764
|
-
var version = "10.4.0-alpha.
|
|
4764
|
+
var version = "10.4.0-alpha.13";
|
|
4765
4765
|
|
|
4766
4766
|
// src/add.ts
|
|
4767
4767
|
var import_semver = __toESM(require_semver(), 1);
|
|
@@ -5001,9 +5001,9 @@ var DIR_CWD = process.cwd(), require2 = createRequire2(DIR_CWD), postinstallAddo
|
|
|
5001
5001
|
} catch {
|
|
5002
5002
|
return;
|
|
5003
5003
|
}
|
|
5004
|
-
let postinstall = moduledLoaded?.default || moduledLoaded?.postinstall || moduledLoaded,
|
|
5004
|
+
let postinstall = moduledLoaded?.default || moduledLoaded?.postinstall || moduledLoaded, logger25 = options.logger;
|
|
5005
5005
|
if (!postinstall || typeof postinstall != "function") {
|
|
5006
|
-
|
|
5006
|
+
logger25.error(`Error finding postinstall function for ${addonName}`);
|
|
5007
5007
|
return;
|
|
5008
5008
|
}
|
|
5009
5009
|
try {
|
|
@@ -5024,7 +5024,7 @@ async function add(addon, {
|
|
|
5024
5024
|
configDir: userSpecifiedConfigDir,
|
|
5025
5025
|
yes,
|
|
5026
5026
|
skipInstall
|
|
5027
|
-
},
|
|
5027
|
+
}, logger25 = nodeLogger) {
|
|
5028
5028
|
let [addonName, inputVersion] = getVersionSpecifier(addon), { mainConfig, mainConfigPath, configDir, previewConfigPath, packageManager } = await getStorybookData({
|
|
5029
5029
|
configDir: userSpecifiedConfigDir,
|
|
5030
5030
|
packageManagerName: pkgMgr
|
|
@@ -5034,16 +5034,16 @@ async function add(addon, {
|
|
|
5034
5034
|
Unable to find storybook config directory. Please specify your Storybook config directory with the --config-dir flag.
|
|
5035
5035
|
`);
|
|
5036
5036
|
if (!mainConfigPath) {
|
|
5037
|
-
|
|
5037
|
+
logger25.error("Unable to find Storybook main.js config");
|
|
5038
5038
|
return;
|
|
5039
5039
|
}
|
|
5040
5040
|
let shouldAddToMain = !0;
|
|
5041
|
-
if (checkInstalled(addonName, mainConfig) && (shouldAddToMain = !1, !yes && (
|
|
5041
|
+
if (checkInstalled(addonName, mainConfig) && (shouldAddToMain = !1, !yes && (logger25.log(`The Storybook addon "${addonName}" is already present in ${mainConfigPath}.`), !await prompt.confirm({
|
|
5042
5042
|
message: "Do you wish to install it again?"
|
|
5043
5043
|
}))))
|
|
5044
5044
|
return;
|
|
5045
5045
|
let main = await readConfig(mainConfigPath);
|
|
5046
|
-
|
|
5046
|
+
logger25.log(`Verifying ${addonName}`);
|
|
5047
5047
|
let version2 = inputVersion;
|
|
5048
5048
|
if (!version2 && isCoreAddon(addonName) && (version2 = versions.storybook), !version2) {
|
|
5049
5049
|
let latestVersion = await packageManager.latestVersion(addonName);
|
|
@@ -5052,14 +5052,14 @@ async function add(addon, {
|
|
|
5052
5052
|
version2 = latestVersion;
|
|
5053
5053
|
}
|
|
5054
5054
|
let storybookVersion = versions.storybook, versionIsStorybook = version2 === versions.storybook;
|
|
5055
|
-
isCoreAddon(addonName) && !versionIsStorybook &&
|
|
5055
|
+
isCoreAddon(addonName) && !versionIsStorybook && logger25.warn(
|
|
5056
5056
|
`The version of ${addonName} (${version2}) you are installing is not the same as the version of Storybook you are using (${storybookVersion}). This may lead to unexpected behavior.`
|
|
5057
5057
|
);
|
|
5058
5058
|
let versionRange = packageManager.getDependencyVersion("storybook")?.match(/^[~^]/)?.[0] ?? "", addonWithVersion = versionIsStorybook ? `${addonName}@${versionRange}${storybookVersion}` : isValidVersion(version2) && !version2.includes("-pr-") ? `${addonName}@^${version2}` : `${addonName}@${version2}`;
|
|
5059
|
-
|
|
5059
|
+
logger25.log(`Installing ${addonWithVersion}`), await packageManager.addDependencies(
|
|
5060
5060
|
{ type: "devDependencies", writeOutputToFile: !1, skipInstall },
|
|
5061
5061
|
[addonWithVersion]
|
|
5062
|
-
), shouldAddToMain && (
|
|
5062
|
+
), shouldAddToMain && (logger25.log(`Adding '${addon}' to the "addons" field in ${mainConfigPath}`), await setupAddonInConfig({
|
|
5063
5063
|
addonName,
|
|
5064
5064
|
mainConfigCSFFile: main,
|
|
5065
5065
|
previewConfigPath,
|
|
@@ -5068,7 +5068,7 @@ async function add(addon, {
|
|
|
5068
5068
|
packageManager: packageManager.type,
|
|
5069
5069
|
configDir,
|
|
5070
5070
|
yes,
|
|
5071
|
-
logger:
|
|
5071
|
+
logger: logger25,
|
|
5072
5072
|
prompt,
|
|
5073
5073
|
skipInstall
|
|
5074
5074
|
});
|
|
@@ -5625,7 +5625,7 @@ import { logger as logger3 } from "storybook/internal/node-logger";
|
|
|
5625
5625
|
import { promises as fs } from "fs";
|
|
5626
5626
|
var maxConcurrentTasks = Math.max(1, os.cpus().length - 1);
|
|
5627
5627
|
async function runCodemod(globPattern = "**/*.{stories,story}.{js,jsx,ts,tsx,mjs,mjsx,mts,mtsx}", transform, { dryRun = !1, skipFormatting = !1 } = {}) {
|
|
5628
|
-
let modifiedCount = 0, unmodifiedCount = 0, errorCount = 0, { globby } = await import("./globby-
|
|
5628
|
+
let modifiedCount = 0, unmodifiedCount = 0, errorCount = 0, { globby } = await import("./globby-KNFCECZ3.js"), files = await globby(slash(globPattern), {
|
|
5629
5629
|
followSymbolicLinks: !0,
|
|
5630
5630
|
ignore: ["**/node_modules/**", "**/dist/**", "**/storybook-static/**", "**/build/**"]
|
|
5631
5631
|
});
|
|
@@ -5636,7 +5636,7 @@ Please try a different pattern.
|
|
|
5636
5636
|
`
|
|
5637
5637
|
), new Error("No files matched");
|
|
5638
5638
|
try {
|
|
5639
|
-
let pLimit = (await import("./p-limit-
|
|
5639
|
+
let pLimit = (await import("./p-limit-MF6JQ6VU.js")).default, limit = pLimit(maxConcurrentTasks);
|
|
5640
5640
|
await Promise.all(
|
|
5641
5641
|
files.map(
|
|
5642
5642
|
(file) => limit(async () => {
|
|
@@ -6479,7 +6479,7 @@ var addonA11yParameters = {
|
|
|
6479
6479
|
errors.push({ file: previewFileToUpdate, error });
|
|
6480
6480
|
}
|
|
6481
6481
|
}
|
|
6482
|
-
let { default: pLimit } = await import("./p-limit-
|
|
6482
|
+
let { default: pLimit } = await import("./p-limit-MF6JQ6VU.js"), limit = pLimit(10);
|
|
6483
6483
|
if (await Promise.all(
|
|
6484
6484
|
storyFilesToUpdate.map(
|
|
6485
6485
|
(file) => limit(async () => {
|
|
@@ -6633,7 +6633,7 @@ var addonGlobalsApi = {
|
|
|
6633
6633
|
}
|
|
6634
6634
|
};
|
|
6635
6635
|
async function transformStoryFiles(files, options, dryRun) {
|
|
6636
|
-
let errors = [], { default: pLimit } = await import("./p-limit-
|
|
6636
|
+
let errors = [], { default: pLimit } = await import("./p-limit-MF6JQ6VU.js"), limit = pLimit(10);
|
|
6637
6637
|
return await Promise.all(
|
|
6638
6638
|
files.map(
|
|
6639
6639
|
(file) => limit(async () => {
|
|
@@ -6825,7 +6825,7 @@ function transformPackageJson(content) {
|
|
|
6825
6825
|
return hasChanges ? JSON.stringify(packageJson, null, 2) : null;
|
|
6826
6826
|
}
|
|
6827
6827
|
var transformPackageJsonFiles = async (files, dryRun) => {
|
|
6828
|
-
let errors = [], { default: pLimit } = await import("./p-limit-
|
|
6828
|
+
let errors = [], { default: pLimit } = await import("./p-limit-MF6JQ6VU.js"), limit = pLimit(10);
|
|
6829
6829
|
return await Promise.all(
|
|
6830
6830
|
files.map(
|
|
6831
6831
|
(file) => limit(async () => {
|
|
@@ -6864,7 +6864,7 @@ var transformPackageJsonFiles = async (files, dryRun) => {
|
|
|
6864
6864
|
dryRun
|
|
6865
6865
|
);
|
|
6866
6866
|
errors.push(...packageJsonErrors);
|
|
6867
|
-
let { globby } = await import("./globby-
|
|
6867
|
+
let { globby } = await import("./globby-KNFCECZ3.js"), configFiles = await globby([`${configDir}/**/*`]), importErrors = await transformImportFiles(
|
|
6868
6868
|
[...storiesPaths, ...configFiles].filter(Boolean),
|
|
6869
6869
|
{
|
|
6870
6870
|
...consolidatedPackages,
|
|
@@ -7220,7 +7220,7 @@ var VITE_DEFAULT_VERSION = "^7.0.0", transformMainConfig = async (mainConfigPath
|
|
|
7220
7220
|
]);
|
|
7221
7221
|
}
|
|
7222
7222
|
mainConfigPath && (logger11.debug("Updating main config file..."), await transformMainConfig(mainConfigPath, dryRun)), logger11.debug("Scanning and updating import statements...");
|
|
7223
|
-
let { globby } = await import("./globby-
|
|
7223
|
+
let { globby } = await import("./globby-KNFCECZ3.js"), configFiles = await globby([`${configDir}/**/*`]), allFiles = [...storiesPaths, ...configFiles].filter(Boolean), transformErrors = await transformImportFiles2(
|
|
7224
7224
|
allFiles,
|
|
7225
7225
|
{
|
|
7226
7226
|
"@storybook/nextjs": "@storybook/nextjs-vite"
|
|
@@ -7430,7 +7430,7 @@ var getAllDependencies = (packageJson) => Object.keys({
|
|
|
7430
7430
|
let regex = new RegExp(`(['"])${renderer}(['"])`, "g");
|
|
7431
7431
|
return regex.test(source) ? source.replace(regex, `$1${framework}$2`) : null;
|
|
7432
7432
|
}, transformSourceFiles = async (files, renderer, framework, dryRun) => {
|
|
7433
|
-
let errors = [], { default: pLimit } = await import("./p-limit-
|
|
7433
|
+
let errors = [], { default: pLimit } = await import("./p-limit-MF6JQ6VU.js"), limit = pLimit(10);
|
|
7434
7434
|
return await Promise.all(
|
|
7435
7435
|
files.map(
|
|
7436
7436
|
(file) => limit(async () => {
|
|
@@ -7497,7 +7497,7 @@ var getAllDependencies = (packageJson) => Object.keys({
|
|
|
7497
7497
|
continue;
|
|
7498
7498
|
logger13.debug(`
|
|
7499
7499
|
Migrating ${rendererPackage} to ${selectedFramework}`);
|
|
7500
|
-
let { globby } = await import("./globby-
|
|
7500
|
+
let { globby } = await import("./globby-KNFCECZ3.js"), configFiles = await globby([`${configDir}/**/*`]);
|
|
7501
7501
|
await transformSourceFiles(
|
|
7502
7502
|
[...storiesPaths, ...configFiles].filter(Boolean),
|
|
7503
7503
|
rendererPackage,
|
|
@@ -7517,6 +7517,11 @@ import { existsSync as existsSync2 } from "node:fs";
|
|
|
7517
7517
|
import { readFile as readFile8, rename, writeFile as writeFile7 } from "node:fs/promises";
|
|
7518
7518
|
import { join as join2 } from "node:path";
|
|
7519
7519
|
import { dedent as dedent9 } from "ts-dedent";
|
|
7520
|
+
|
|
7521
|
+
// ../../core/src/shared/constants/config-folder.ts
|
|
7522
|
+
var RN_STORYBOOK_DIR = ".rnstorybook";
|
|
7523
|
+
|
|
7524
|
+
// src/automigrate/fixes/rnstorybook-config.ts
|
|
7520
7525
|
async function renameInFile(filePath, oldText, newText) {
|
|
7521
7526
|
try {
|
|
7522
7527
|
let updatedContent = (await readFile8(filePath, "utf8")).replaceAll(oldText, newText);
|
|
@@ -7527,7 +7532,7 @@ async function renameInFile(filePath, oldText, newText) {
|
|
|
7527
7532
|
}
|
|
7528
7533
|
var getDotStorybookReferences = async (searchDir) => {
|
|
7529
7534
|
try {
|
|
7530
|
-
let { globby } = await import("./globby-
|
|
7535
|
+
let { globby } = await import("./globby-KNFCECZ3.js"), { readFile: readFile10 } = await import("node:fs/promises"), files = await globby(`${searchDir}/**/*`, {
|
|
7531
7536
|
onlyFiles: !0,
|
|
7532
7537
|
gitignore: !0
|
|
7533
7538
|
}), referencedFiles = [];
|
|
@@ -7548,7 +7553,7 @@ var getDotStorybookReferences = async (searchDir) => {
|
|
|
7548
7553
|
async check({ packageManager, mainConfigPath }) {
|
|
7549
7554
|
if (!packageManager.getAllDependencies()["@storybook/react-native"])
|
|
7550
7555
|
return null;
|
|
7551
|
-
let projectDir = mainConfigPath ? join2(mainConfigPath, "..", "..") : process.cwd(), storybookDir = join2(projectDir, ".storybook"), rnStorybookDir = join2(projectDir,
|
|
7556
|
+
let projectDir = mainConfigPath ? join2(mainConfigPath, "..", "..") : process.cwd(), storybookDir = join2(projectDir, ".storybook"), rnStorybookDir = join2(projectDir, RN_STORYBOOK_DIR), { globby } = await import("./globby-KNFCECZ3.js"), requiresFiles = await globby(join2(storybookDir, "storybook.requires.*"));
|
|
7552
7557
|
return existsSync2(storybookDir) && requiresFiles.length > 0 && !existsSync2(rnStorybookDir) ? { storybookDir, rnStorybookDir } : null;
|
|
7553
7558
|
},
|
|
7554
7559
|
prompt() {
|
|
@@ -7558,7 +7563,7 @@ var getDotStorybookReferences = async (searchDir) => {
|
|
|
7558
7563
|
let instanceDir = packageManager.instanceDir, dotStorybookReferences = await getDotStorybookReferences(instanceDir);
|
|
7559
7564
|
dryRun || (await Promise.all(
|
|
7560
7565
|
dotStorybookReferences.map(async (ref) => {
|
|
7561
|
-
await renameInFile(ref, ".storybook",
|
|
7566
|
+
await renameInFile(ref, ".storybook", RN_STORYBOOK_DIR);
|
|
7562
7567
|
})
|
|
7563
7568
|
), await rename(storybookDir, rnStorybookDir));
|
|
7564
7569
|
}
|
|
@@ -9128,30 +9133,1192 @@ async function promptSelectedTemplate(choices) {
|
|
|
9128
9133
|
});
|
|
9129
9134
|
}
|
|
9130
9135
|
|
|
9136
|
+
// src/ai/index.ts
|
|
9137
|
+
import { writeFile as writeFile9 } from "node:fs/promises";
|
|
9138
|
+
import { resolve as resolve3 } from "node:path";
|
|
9139
|
+
import { cache } from "storybook/internal/common";
|
|
9140
|
+
import { logger as logger21 } from "storybook/internal/node-logger";
|
|
9141
|
+
import {
|
|
9142
|
+
getSessionId,
|
|
9143
|
+
isTelemetryModuleEnabled,
|
|
9144
|
+
snapshotPreviewFile,
|
|
9145
|
+
telemetry
|
|
9146
|
+
} from "storybook/internal/telemetry";
|
|
9147
|
+
import { SupportedLanguage as SupportedLanguage2 } from "storybook/internal/types";
|
|
9148
|
+
|
|
9149
|
+
// ../create-storybook/src/services/ProjectTypeService.ts
|
|
9150
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
9151
|
+
import { resolve as resolve2 } from "node:path";
|
|
9152
|
+
import { ProjectType as ProjectType2 } from "storybook/internal/cli";
|
|
9153
|
+
import { HandledError, getProjectRoot } from "storybook/internal/common";
|
|
9154
|
+
import { logger as logger20 } from "storybook/internal/node-logger";
|
|
9155
|
+
import { NxProjectDetectedError } from "storybook/internal/server-errors";
|
|
9156
|
+
import { SupportedLanguage } from "storybook/internal/types";
|
|
9157
|
+
|
|
9158
|
+
// ../../../node_modules/empathic/find.mjs
|
|
9159
|
+
import { join as join4 } from "node:path";
|
|
9160
|
+
import { existsSync as existsSync4, statSync } from "node:fs";
|
|
9161
|
+
function up2(name, options) {
|
|
9162
|
+
let dir, tmp, start = options && options.cwd || "";
|
|
9163
|
+
for (dir of up(start, options))
|
|
9164
|
+
if (tmp = join4(dir, name), existsSync4(tmp)) return tmp;
|
|
9165
|
+
}
|
|
9166
|
+
|
|
9167
|
+
// ../create-storybook/src/services/ProjectTypeService.ts
|
|
9168
|
+
var import_semver7 = __toESM(require_semver(), 1);
|
|
9169
|
+
import { dedent as dedent16 } from "ts-dedent";
|
|
9170
|
+
var ProjectTypeService = class {
|
|
9171
|
+
constructor(jsPackageManager) {
|
|
9172
|
+
this.jsPackageManager = jsPackageManager;
|
|
9173
|
+
}
|
|
9174
|
+
/** Sorted configuration to match a Storybook preset template */
|
|
9175
|
+
getSupportedTemplates() {
|
|
9176
|
+
return [
|
|
9177
|
+
{
|
|
9178
|
+
preset: ProjectType2.NUXT,
|
|
9179
|
+
dependencies: ["nuxt"],
|
|
9180
|
+
matcherFunction: ({ dependencies }) => dependencies?.every(Boolean) ?? !0
|
|
9181
|
+
},
|
|
9182
|
+
{
|
|
9183
|
+
preset: ProjectType2.VUE3,
|
|
9184
|
+
dependencies: {
|
|
9185
|
+
// This Vue template works with Vue 3
|
|
9186
|
+
vue: (versionRange) => versionRange === "next" || this.eqMajor(versionRange, 3)
|
|
9187
|
+
},
|
|
9188
|
+
matcherFunction: ({ dependencies }) => dependencies?.some(Boolean) ?? !1
|
|
9189
|
+
},
|
|
9190
|
+
{
|
|
9191
|
+
preset: ProjectType2.EMBER,
|
|
9192
|
+
dependencies: ["ember-cli"],
|
|
9193
|
+
matcherFunction: ({ dependencies }) => dependencies?.every(Boolean) ?? !0
|
|
9194
|
+
},
|
|
9195
|
+
{
|
|
9196
|
+
preset: ProjectType2.NEXTJS,
|
|
9197
|
+
dependencies: ["next"],
|
|
9198
|
+
matcherFunction: ({ dependencies }) => dependencies?.every(Boolean) ?? !0
|
|
9199
|
+
},
|
|
9200
|
+
{
|
|
9201
|
+
preset: ProjectType2.QWIK,
|
|
9202
|
+
dependencies: ["@builder.io/qwik"],
|
|
9203
|
+
matcherFunction: ({ dependencies }) => dependencies?.every(Boolean) ?? !0
|
|
9204
|
+
},
|
|
9205
|
+
{
|
|
9206
|
+
preset: ProjectType2.REACT_NATIVE,
|
|
9207
|
+
dependencies: ["react-native", "react-native-scripts"],
|
|
9208
|
+
matcherFunction: ({ dependencies }) => dependencies?.some(Boolean) ?? !1
|
|
9209
|
+
},
|
|
9210
|
+
{
|
|
9211
|
+
preset: ProjectType2.REACT_SCRIPTS,
|
|
9212
|
+
// For projects using a custom/forked `react-scripts` package.
|
|
9213
|
+
files: ["/node_modules/.bin/react-scripts"],
|
|
9214
|
+
// For standard CRA projects
|
|
9215
|
+
dependencies: ["react-scripts"],
|
|
9216
|
+
matcherFunction: ({ dependencies, files }) => (dependencies?.every(Boolean) || files?.every(Boolean)) ?? !1
|
|
9217
|
+
},
|
|
9218
|
+
{
|
|
9219
|
+
preset: ProjectType2.ANGULAR,
|
|
9220
|
+
dependencies: ["@angular/core"],
|
|
9221
|
+
matcherFunction: ({ dependencies }) => dependencies?.every(Boolean) ?? !0
|
|
9222
|
+
},
|
|
9223
|
+
{
|
|
9224
|
+
preset: ProjectType2.WEB_COMPONENTS,
|
|
9225
|
+
dependencies: ["lit-element", "lit-html", "lit"],
|
|
9226
|
+
matcherFunction: ({ dependencies }) => dependencies?.some(Boolean) ?? !1
|
|
9227
|
+
},
|
|
9228
|
+
{
|
|
9229
|
+
preset: ProjectType2.PREACT,
|
|
9230
|
+
dependencies: ["preact"],
|
|
9231
|
+
matcherFunction: ({ dependencies }) => dependencies?.every(Boolean) ?? !0
|
|
9232
|
+
},
|
|
9233
|
+
{
|
|
9234
|
+
// TODO: This only works because it is before the SVELTE template. could be more explicit
|
|
9235
|
+
preset: ProjectType2.SVELTEKIT,
|
|
9236
|
+
dependencies: ["@sveltejs/kit"],
|
|
9237
|
+
matcherFunction: ({ dependencies }) => dependencies?.every(Boolean) ?? !0
|
|
9238
|
+
},
|
|
9239
|
+
{
|
|
9240
|
+
preset: ProjectType2.SVELTE,
|
|
9241
|
+
dependencies: ["svelte"],
|
|
9242
|
+
matcherFunction: ({ dependencies }) => dependencies?.every(Boolean) ?? !0
|
|
9243
|
+
},
|
|
9244
|
+
{
|
|
9245
|
+
preset: ProjectType2.SOLID,
|
|
9246
|
+
dependencies: ["solid-js"],
|
|
9247
|
+
matcherFunction: ({ dependencies }) => dependencies?.every(Boolean) ?? !0
|
|
9248
|
+
},
|
|
9249
|
+
// DO NOT MOVE ANY TEMPLATES BELOW THIS LINE
|
|
9250
|
+
// React is part of every Template, after Storybook is initialized once
|
|
9251
|
+
{
|
|
9252
|
+
preset: ProjectType2.REACT,
|
|
9253
|
+
dependencies: ["react"],
|
|
9254
|
+
matcherFunction: ({ dependencies }) => dependencies?.every(Boolean) ?? !0
|
|
9255
|
+
}
|
|
9256
|
+
];
|
|
9257
|
+
}
|
|
9258
|
+
isStorybookInstantiated(configDir = resolve2(process.cwd(), ".storybook")) {
|
|
9259
|
+
return existsSync5(configDir);
|
|
9260
|
+
}
|
|
9261
|
+
async validateProvidedType(projectTypeProvided) {
|
|
9262
|
+
if (Object.values(ProjectType2).filter(
|
|
9263
|
+
(t10) => !["undetected", "unsupported", "nx"].includes(String(t10))
|
|
9264
|
+
).includes(projectTypeProvided))
|
|
9265
|
+
return projectTypeProvided;
|
|
9266
|
+
throw logger20.error(
|
|
9267
|
+
`The provided project type ${projectTypeProvided} was not recognized by Storybook`
|
|
9268
|
+
), new HandledError(`Unknown project type supplied: ${projectTypeProvided}`);
|
|
9269
|
+
}
|
|
9270
|
+
async autoDetectProjectType(options) {
|
|
9271
|
+
try {
|
|
9272
|
+
let detectedType = await this.detectProjectType(options);
|
|
9273
|
+
if (detectedType === ProjectType2.UNDETECTED || detectedType === null)
|
|
9274
|
+
throw logger20.error(dedent16`
|
|
9275
|
+
Unable to initialize Storybook in this directory.
|
|
9276
|
+
|
|
9277
|
+
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.
|
|
9278
|
+
|
|
9279
|
+
Tips:
|
|
9280
|
+
- Run init in an empty directory or create a new framework app first.
|
|
9281
|
+
- If this directory contains unrelated files, try a new directory for Storybook.
|
|
9282
|
+
`), new HandledError("Storybook failed to detect your project type");
|
|
9283
|
+
if (detectedType === ProjectType2.NX)
|
|
9284
|
+
throw new NxProjectDetectedError();
|
|
9285
|
+
return detectedType;
|
|
9286
|
+
} catch (err) {
|
|
9287
|
+
throw err instanceof HandledError || err instanceof NxProjectDetectedError ? err : (logger20.error(String(err)), new HandledError(err instanceof Error ? err.message : String(err)));
|
|
9288
|
+
}
|
|
9289
|
+
}
|
|
9290
|
+
async detectLanguage() {
|
|
9291
|
+
let language = SupportedLanguage.JAVASCRIPT;
|
|
9292
|
+
if (existsSync5("jsconfig.json"))
|
|
9293
|
+
return language;
|
|
9294
|
+
let isTypescriptDirectDependency = !!this.jsPackageManager.getAllDependencies().typescript, getModulePackageJSONVersion = async (pkg) => (await this.jsPackageManager.getModulePackageJSON(pkg))?.version ?? null, [
|
|
9295
|
+
typescriptVersion,
|
|
9296
|
+
prettierVersion,
|
|
9297
|
+
babelPluginTransformTypescriptVersion,
|
|
9298
|
+
typescriptEslintParserVersion,
|
|
9299
|
+
eslintPluginStorybookVersion
|
|
9300
|
+
] = await Promise.all([
|
|
9301
|
+
getModulePackageJSONVersion("typescript"),
|
|
9302
|
+
getModulePackageJSONVersion("prettier"),
|
|
9303
|
+
getModulePackageJSONVersion("@babel/plugin-transform-typescript"),
|
|
9304
|
+
getModulePackageJSONVersion("@typescript-eslint/parser"),
|
|
9305
|
+
getModulePackageJSONVersion("eslint-plugin-storybook")
|
|
9306
|
+
]), satisfies = (version2, range) => version2 ? import_semver7.default.satisfies(version2, range, { includePrerelease: !0 }) : !1;
|
|
9307
|
+
return isTypescriptDirectDependency && typescriptVersion ? satisfies(typescriptVersion, ">=4.9.0") && (!prettierVersion || import_semver7.default.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 : logger20.warn(
|
|
9308
|
+
"Detected TypeScript < 4.9 or incompatible tooling, populating with JavaScript examples"
|
|
9309
|
+
) : existsSync5("tsconfig.json") && (language = SupportedLanguage.TYPESCRIPT), language;
|
|
9310
|
+
}
|
|
9311
|
+
eqMajor(versionRange, major) {
|
|
9312
|
+
return import_semver7.default.validRange(versionRange) ? import_semver7.default.minVersion(versionRange)?.major === major : !1;
|
|
9313
|
+
}
|
|
9314
|
+
async detectProjectType(options) {
|
|
9315
|
+
try {
|
|
9316
|
+
if (this.isNxProject())
|
|
9317
|
+
return ProjectType2.NX;
|
|
9318
|
+
if (options.html)
|
|
9319
|
+
return ProjectType2.HTML;
|
|
9320
|
+
let { packageJson } = this.jsPackageManager.primaryPackageJson;
|
|
9321
|
+
return this.detectFrameworkPreset(packageJson);
|
|
9322
|
+
} catch {
|
|
9323
|
+
return ProjectType2.UNDETECTED;
|
|
9324
|
+
}
|
|
9325
|
+
}
|
|
9326
|
+
detectFrameworkPreset(packageJson) {
|
|
9327
|
+
let result = [...this.getSupportedTemplates(), this.getUnsupportedTemplate()].find(
|
|
9328
|
+
(framework) => this.getProjectType(packageJson, framework) !== null
|
|
9329
|
+
);
|
|
9330
|
+
return result ? result.preset : ProjectType2.UNDETECTED;
|
|
9331
|
+
}
|
|
9332
|
+
/** Template that matches unsupported frameworks */
|
|
9333
|
+
getUnsupportedTemplate() {
|
|
9334
|
+
return {
|
|
9335
|
+
preset: ProjectType2.UNSUPPORTED,
|
|
9336
|
+
dependencies: {},
|
|
9337
|
+
matcherFunction: ({ dependencies }) => dependencies?.some(Boolean) ?? !1
|
|
9338
|
+
};
|
|
9339
|
+
}
|
|
9340
|
+
getProjectType(packageJson, framework) {
|
|
9341
|
+
let matcher = {
|
|
9342
|
+
dependencies: [!1],
|
|
9343
|
+
peerDependencies: [!1],
|
|
9344
|
+
files: [!1]
|
|
9345
|
+
}, { preset, files, dependencies, peerDependencies, matcherFunction } = framework, dependencySearches = [];
|
|
9346
|
+
Array.isArray(dependencies) ? dependencySearches = dependencies.map((name) => [name, void 0]) : typeof dependencies == "object" && (dependencySearches = Object.entries(dependencies)), dependencySearches.length > 0 && (matcher.dependencies = dependencySearches.map(
|
|
9347
|
+
([name, matchFn]) => this.hasDependency(packageJson, name, matchFn)
|
|
9348
|
+
));
|
|
9349
|
+
let peerDependencySearches = [];
|
|
9350
|
+
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(
|
|
9351
|
+
([name, matchFn]) => this.hasPeerDependency(packageJson, name, matchFn)
|
|
9352
|
+
)), Array.isArray(files) && files.length > 0 && (matcher.files = files.map((name) => existsSync5(name))), matcherFunction(matcher) ? preset : null;
|
|
9353
|
+
}
|
|
9354
|
+
hasDependency(packageJson, name, matcher) {
|
|
9355
|
+
let version2 = packageJson.dependencies?.[name] || packageJson.devDependencies?.[name];
|
|
9356
|
+
return version2 && typeof matcher == "function" ? matcher(version2) : !!version2;
|
|
9357
|
+
}
|
|
9358
|
+
hasPeerDependency(packageJson, name, matcher) {
|
|
9359
|
+
let version2 = packageJson.peerDependencies?.[name];
|
|
9360
|
+
return version2 && typeof matcher == "function" ? matcher(version2) : !!version2;
|
|
9361
|
+
}
|
|
9362
|
+
isNxProject() {
|
|
9363
|
+
return up2("nx.json", { last: getProjectRoot() });
|
|
9364
|
+
}
|
|
9365
|
+
};
|
|
9366
|
+
|
|
9367
|
+
// src/ai/prompt.ts
|
|
9368
|
+
import { dedent as dedent18 } from "ts-dedent";
|
|
9369
|
+
|
|
9370
|
+
// src/ai/setup-prompts/pattern-copy-play.ts
|
|
9371
|
+
import { dedent as dedent17 } from "ts-dedent";
|
|
9372
|
+
function getDocsMarkdownUrl(path4, projectInfo) {
|
|
9373
|
+
let { majorVersion, renderer = "react", language = "ts" } = projectInfo ?? {}, versionSegment = majorVersion ? `/${majorVersion}` : "", params = new URLSearchParams();
|
|
9374
|
+
renderer && params.set("renderer", renderer), params.set("language", language);
|
|
9375
|
+
let query = params.toString();
|
|
9376
|
+
return `https://storybook.js.org/docs${versionSegment}/${path4}.md${query ? `?${query}` : ""}`;
|
|
9377
|
+
}
|
|
9378
|
+
function getTypeImportSource(projectInfo) {
|
|
9379
|
+
return projectInfo.framework || projectInfo.rendererPackage || "@storybook/react";
|
|
9380
|
+
}
|
|
9381
|
+
function getDocsReferenceSection(projectInfo) {
|
|
9382
|
+
let docsUrl = (path4) => getDocsMarkdownUrl(path4, projectInfo);
|
|
9383
|
+
return dedent17`
|
|
9384
|
+
### Storybook Documentation Reference
|
|
9385
|
+
|
|
9386
|
+
Use the following references to look up Storybook APIs, concepts, or examples:
|
|
9387
|
+
|
|
9388
|
+
- Full docs index: https://storybook.js.org/llms.txt
|
|
9389
|
+
- See code snippets only with codeOnly=true param e.g. ${docsUrl("writing-stories")}&codeOnly=true
|
|
9390
|
+
|
|
9391
|
+
Key documentation pages for this task:
|
|
9392
|
+
- Writing stories: ${docsUrl("writing-stories")}
|
|
9393
|
+
- Decorators: ${docsUrl("writing-stories/decorators")}
|
|
9394
|
+
- Args: ${docsUrl("writing-stories/args")}
|
|
9395
|
+
- Play functions: ${docsUrl("writing-stories/play-function")}
|
|
9396
|
+
- Vitest integration: ${docsUrl("writing-tests/vitest-plugin")}
|
|
9397
|
+
|
|
9398
|
+
Fetch these URLs directly when you need guidance on Storybook APIs or patterns.
|
|
9399
|
+
`;
|
|
9400
|
+
}
|
|
9401
|
+
function getPreviewConfigExample(projectInfo) {
|
|
9402
|
+
let configDir = projectInfo.configDir, typeImport = getTypeImportSource(projectInfo);
|
|
9403
|
+
return projectInfo.hasCsfFactoryPreview ? dedent17`
|
|
9404
|
+
\`\`\`tsx
|
|
9405
|
+
// ${configDir}/preview.tsx
|
|
9406
|
+
import '../src/index.css'; // import global styles
|
|
9407
|
+
import MockDate from 'mockdate';
|
|
9408
|
+
|
|
9409
|
+
import { definePreview } from 'storybook/preview';
|
|
9410
|
+
import { SessionProvider } from '../src/contexts/SessionContext';
|
|
9411
|
+
|
|
9412
|
+
export default definePreview({
|
|
9413
|
+
decorators: [
|
|
9414
|
+
(Story) => (
|
|
9415
|
+
<SessionProvider>
|
|
9416
|
+
<Story />
|
|
9417
|
+
</SessionProvider>
|
|
9418
|
+
),
|
|
9419
|
+
],
|
|
9420
|
+
async beforeEach() {
|
|
9421
|
+
localStorage.setItem('theme', 'dark');
|
|
9422
|
+
localStorage.setItem('sidebar:open', 'true');
|
|
9423
|
+
MockDate.set('2024-04-01T12:00:00Z');
|
|
9424
|
+
},
|
|
9425
|
+
});
|
|
9426
|
+
\`\`\`
|
|
9427
|
+
` : dedent17`
|
|
9428
|
+
\`\`\`tsx
|
|
9429
|
+
// ${configDir}/preview.tsx
|
|
9430
|
+
import type { Preview } from '${typeImport}';
|
|
9431
|
+
import MockDate from 'mockdate';
|
|
9432
|
+
import '../src/index.css'; // import global styles
|
|
9433
|
+
import { SessionProvider } from '../src/contexts/SessionContext';
|
|
9434
|
+
|
|
9435
|
+
const preview: Preview = {
|
|
9436
|
+
decorators: [
|
|
9437
|
+
(Story) => (
|
|
9438
|
+
<SessionProvider>
|
|
9439
|
+
<Story />
|
|
9440
|
+
</SessionProvider>
|
|
9441
|
+
),
|
|
9442
|
+
],
|
|
9443
|
+
async beforeEach() {
|
|
9444
|
+
localStorage.setItem('theme', 'dark');
|
|
9445
|
+
localStorage.setItem('sidebar:open', 'true');
|
|
9446
|
+
MockDate.set('2024-04-01T12:00:00Z');
|
|
9447
|
+
},
|
|
9448
|
+
};
|
|
9449
|
+
|
|
9450
|
+
export default preview;
|
|
9451
|
+
\`\`\`
|
|
9452
|
+
`;
|
|
9453
|
+
}
|
|
9454
|
+
function getMockDateExample(projectInfo) {
|
|
9455
|
+
let typeImport = getTypeImportSource(projectInfo);
|
|
9456
|
+
return projectInfo.hasCsfFactoryPreview ? dedent17`
|
|
9457
|
+
\`\`\`tsx
|
|
9458
|
+
import MockDate from 'mockdate';
|
|
9459
|
+
import { definePreview } from 'storybook/preview';
|
|
9460
|
+
|
|
9461
|
+
export default definePreview({
|
|
9462
|
+
async beforeEach() {
|
|
9463
|
+
MockDate.set('2024-04-01T12:00:00Z');
|
|
9464
|
+
},
|
|
9465
|
+
});
|
|
9466
|
+
\`\`\`
|
|
9467
|
+
` : dedent17`
|
|
9468
|
+
\`\`\`tsx
|
|
9469
|
+
import type { Preview } from '${typeImport}';
|
|
9470
|
+
import MockDate from 'mockdate';
|
|
9471
|
+
|
|
9472
|
+
const preview: Preview = {
|
|
9473
|
+
async beforeEach() {
|
|
9474
|
+
MockDate.set('2024-04-01T12:00:00Z');
|
|
9475
|
+
},
|
|
9476
|
+
};
|
|
9477
|
+
|
|
9478
|
+
export default preview;
|
|
9479
|
+
\`\`\`
|
|
9480
|
+
`;
|
|
9481
|
+
}
|
|
9482
|
+
function getMswPreviewExample(projectInfo) {
|
|
9483
|
+
let configDir = projectInfo.configDir, typeImport = getTypeImportSource(projectInfo);
|
|
9484
|
+
return projectInfo.hasCsfFactoryPreview ? dedent17`
|
|
9485
|
+
\`\`\`tsx
|
|
9486
|
+
// ${configDir}/preview.tsx
|
|
9487
|
+
import { definePreview } from 'storybook/preview';
|
|
9488
|
+
import { initialize, mswLoader } from 'msw-storybook-addon';
|
|
9489
|
+
import { mswHandlers } from './msw-handlers';
|
|
9490
|
+
|
|
9491
|
+
initialize({
|
|
9492
|
+
onUnhandledRequest: 'bypass',
|
|
9493
|
+
});
|
|
9494
|
+
|
|
9495
|
+
export default definePreview({
|
|
9496
|
+
loaders: [mswLoader],
|
|
9497
|
+
parameters: {
|
|
9498
|
+
msw: {
|
|
9499
|
+
handlers: mswHandlers,
|
|
9500
|
+
},
|
|
9501
|
+
},
|
|
9502
|
+
});
|
|
9503
|
+
\`\`\`
|
|
9504
|
+
` : dedent17`
|
|
9505
|
+
\`\`\`tsx
|
|
9506
|
+
// ${configDir}/preview.tsx
|
|
9507
|
+
import type { Preview } from '${typeImport}';
|
|
9508
|
+
import { initialize, mswLoader } from 'msw-storybook-addon';
|
|
9509
|
+
import { mswHandlers } from './msw-handlers';
|
|
9510
|
+
|
|
9511
|
+
initialize({
|
|
9512
|
+
onUnhandledRequest: 'bypass',
|
|
9513
|
+
});
|
|
9514
|
+
|
|
9515
|
+
const preview: Preview = {
|
|
9516
|
+
loaders: [mswLoader],
|
|
9517
|
+
parameters: {
|
|
9518
|
+
msw: {
|
|
9519
|
+
handlers: mswHandlers,
|
|
9520
|
+
},
|
|
9521
|
+
},
|
|
9522
|
+
};
|
|
9523
|
+
|
|
9524
|
+
export default preview;
|
|
9525
|
+
\`\`\`
|
|
9526
|
+
`;
|
|
9527
|
+
}
|
|
9528
|
+
function getStoryExample(projectInfo) {
|
|
9529
|
+
if (projectInfo.hasCsfFactoryPreview)
|
|
9530
|
+
return dedent17`
|
|
9531
|
+
\`\`\`tsx
|
|
9532
|
+
import preview from '#.storybook/preview';
|
|
9533
|
+
import { expect } from 'storybook/test';
|
|
9534
|
+
import { SomeComponent } from './SomeComponent';
|
|
9535
|
+
|
|
9536
|
+
const meta = preview.meta({
|
|
9537
|
+
component: SomeComponent,
|
|
9538
|
+
tags: ['ai-generated'],
|
|
9539
|
+
});
|
|
9540
|
+
|
|
9541
|
+
export const Default = meta.story({
|
|
9542
|
+
render: () => <SomeComponent variant="primary" disabled={false} />,
|
|
9543
|
+
play: async ({ canvas }) => {
|
|
9544
|
+
await expect(canvas.getByRole('button')).toBeVisible();
|
|
9545
|
+
},
|
|
9546
|
+
});
|
|
9547
|
+
\`\`\`
|
|
9548
|
+
`;
|
|
9549
|
+
let typeImport = getTypeImportSource(projectInfo);
|
|
9550
|
+
return dedent17`
|
|
9551
|
+
\`\`\`tsx
|
|
9552
|
+
import type { Meta, StoryObj } from '${typeImport}';
|
|
9553
|
+
import { expect } from 'storybook/test';
|
|
9554
|
+
import { SomeComponent } from './SomeComponent';
|
|
9555
|
+
|
|
9556
|
+
const meta = {
|
|
9557
|
+
component: SomeComponent,
|
|
9558
|
+
tags: ['ai-generated'],
|
|
9559
|
+
} satisfies Meta<typeof SomeComponent>;
|
|
9560
|
+
|
|
9561
|
+
export default meta;
|
|
9562
|
+
type Story = StoryObj<typeof meta>;
|
|
9563
|
+
|
|
9564
|
+
export const Default: Story = {
|
|
9565
|
+
render: () => <SomeComponent variant="primary" disabled={false} />,
|
|
9566
|
+
play: async ({ canvas }) => {
|
|
9567
|
+
await expect(canvas.getByRole('button')).toBeVisible();
|
|
9568
|
+
},
|
|
9569
|
+
};
|
|
9570
|
+
\`\`\`
|
|
9571
|
+
`;
|
|
9572
|
+
}
|
|
9573
|
+
function getNeedsWorkTagExample(projectInfo) {
|
|
9574
|
+
return projectInfo.hasCsfFactoryPreview ? dedent17`
|
|
9575
|
+
\`\`\`ts
|
|
9576
|
+
const meta = preview.meta({
|
|
9577
|
+
component: SomeComponent,
|
|
9578
|
+
tags: ['ai-generated', 'needs-work'],
|
|
9579
|
+
});
|
|
9580
|
+
\`\`\`
|
|
9581
|
+
` : dedent17`
|
|
9582
|
+
\`\`\`ts
|
|
9583
|
+
const meta = {
|
|
9584
|
+
component: SomeComponent,
|
|
9585
|
+
tags: ['ai-generated', 'needs-work'],
|
|
9586
|
+
} satisfies Meta<typeof SomeComponent>;
|
|
9587
|
+
\`\`\`
|
|
9588
|
+
`;
|
|
9589
|
+
}
|
|
9590
|
+
function getArgsStoryExample(projectInfo) {
|
|
9591
|
+
if (projectInfo.hasCsfFactoryPreview)
|
|
9592
|
+
return dedent17`
|
|
9593
|
+
\`\`\`tsx
|
|
9594
|
+
import preview from '#.storybook/preview';
|
|
9595
|
+
import { expect } from 'storybook/test';
|
|
9596
|
+
import { Button } from './Button';
|
|
9597
|
+
|
|
9598
|
+
const meta = preview.meta({
|
|
9599
|
+
component: Button,
|
|
9600
|
+
tags: ['ai-generated'],
|
|
9601
|
+
});
|
|
9602
|
+
|
|
9603
|
+
export const Primary = meta.story({
|
|
9604
|
+
args: {
|
|
9605
|
+
variant: 'primary',
|
|
9606
|
+
children: 'Save',
|
|
9607
|
+
},
|
|
9608
|
+
play: async ({ canvas }) => {
|
|
9609
|
+
await expect(canvas.getByRole('button', { name: /save/i })).toBeVisible();
|
|
9610
|
+
},
|
|
9611
|
+
});
|
|
9612
|
+
|
|
9613
|
+
export const Disabled = meta.story({
|
|
9614
|
+
args: {
|
|
9615
|
+
variant: 'primary',
|
|
9616
|
+
disabled: true,
|
|
9617
|
+
children: 'Save',
|
|
9618
|
+
},
|
|
9619
|
+
play: async ({ canvas }) => {
|
|
9620
|
+
await expect(canvas.getByRole('button')).toBeDisabled();
|
|
9621
|
+
},
|
|
9622
|
+
});
|
|
9623
|
+
\`\`\`
|
|
9624
|
+
`;
|
|
9625
|
+
let typeImport = getTypeImportSource(projectInfo);
|
|
9626
|
+
return dedent17`
|
|
9627
|
+
\`\`\`tsx
|
|
9628
|
+
import type { Meta, StoryObj } from '${typeImport}';
|
|
9629
|
+
import { expect } from 'storybook/test';
|
|
9630
|
+
import { Button } from './Button';
|
|
9631
|
+
|
|
9632
|
+
const meta = {
|
|
9633
|
+
component: Button,
|
|
9634
|
+
tags: ['ai-generated'],
|
|
9635
|
+
} satisfies Meta<typeof Button>;
|
|
9636
|
+
|
|
9637
|
+
export default meta;
|
|
9638
|
+
type Story = StoryObj<typeof meta>;
|
|
9639
|
+
|
|
9640
|
+
export const Primary: Story = {
|
|
9641
|
+
args: {
|
|
9642
|
+
variant: 'primary',
|
|
9643
|
+
children: 'Save',
|
|
9644
|
+
},
|
|
9645
|
+
play: async ({ canvas }) => {
|
|
9646
|
+
await expect(canvas.getByRole('button', { name: /save/i })).toBeVisible();
|
|
9647
|
+
},
|
|
9648
|
+
};
|
|
9649
|
+
|
|
9650
|
+
export const Disabled: Story = {
|
|
9651
|
+
args: {
|
|
9652
|
+
variant: 'primary',
|
|
9653
|
+
disabled: true,
|
|
9654
|
+
children: 'Save',
|
|
9655
|
+
},
|
|
9656
|
+
play: async ({ canvas }) => {
|
|
9657
|
+
await expect(canvas.getByRole('button')).toBeDisabled();
|
|
9658
|
+
},
|
|
9659
|
+
};
|
|
9660
|
+
\`\`\`
|
|
9661
|
+
`;
|
|
9662
|
+
}
|
|
9663
|
+
function getRenderCompositionExample(projectInfo) {
|
|
9664
|
+
if (projectInfo.hasCsfFactoryPreview)
|
|
9665
|
+
return dedent17`
|
|
9666
|
+
\`\`\`tsx
|
|
9667
|
+
import preview from '#.storybook/preview';
|
|
9668
|
+
import { expect } from 'storybook/test';
|
|
9669
|
+
import { Button } from './Button';
|
|
9670
|
+
import { Card } from './Card';
|
|
9671
|
+
|
|
9672
|
+
const meta = preview.meta({
|
|
9673
|
+
component: Button,
|
|
9674
|
+
tags: ['ai-generated'],
|
|
9675
|
+
});
|
|
9676
|
+
|
|
9677
|
+
export const InsideCard = meta.story({
|
|
9678
|
+
render: () => (
|
|
9679
|
+
<Card>
|
|
9680
|
+
<Button disabled={false}>Save</Button>
|
|
9681
|
+
</Card>
|
|
9682
|
+
),
|
|
9683
|
+
play: async ({ canvas, userEvent }) => {
|
|
9684
|
+
await expect(canvas.getByRole('button', { name: /save/i })).toBeVisible();
|
|
9685
|
+
await userEvent.click(canvas.getByRole('button', { name: /save/i }));
|
|
9686
|
+
},
|
|
9687
|
+
});
|
|
9688
|
+
\`\`\`
|
|
9689
|
+
`;
|
|
9690
|
+
let typeImport = getTypeImportSource(projectInfo);
|
|
9691
|
+
return dedent17`
|
|
9692
|
+
\`\`\`tsx
|
|
9693
|
+
import type { Meta, StoryObj } from '${typeImport}';
|
|
9694
|
+
import { expect } from 'storybook/test';
|
|
9695
|
+
import { Button } from './Button';
|
|
9696
|
+
import { Card } from './Card';
|
|
9697
|
+
|
|
9698
|
+
const meta = {
|
|
9699
|
+
component: Button,
|
|
9700
|
+
tags: ['ai-generated'],
|
|
9701
|
+
} satisfies Meta<typeof Button>;
|
|
9702
|
+
|
|
9703
|
+
export default meta;
|
|
9704
|
+
type Story = StoryObj<typeof meta>;
|
|
9705
|
+
|
|
9706
|
+
export const InsideCard: Story = {
|
|
9707
|
+
render: () => (
|
|
9708
|
+
<Card>
|
|
9709
|
+
<Button disabled={false}>Save</Button>
|
|
9710
|
+
</Card>
|
|
9711
|
+
),
|
|
9712
|
+
play: async ({ canvas, userEvent }) => {
|
|
9713
|
+
await expect(canvas.getByRole('button', { name: /save/i })).toBeVisible();
|
|
9714
|
+
await userEvent.click(canvas.getByRole('button', { name: /save/i }));
|
|
9715
|
+
},
|
|
9716
|
+
};
|
|
9717
|
+
\`\`\`
|
|
9718
|
+
`;
|
|
9719
|
+
}
|
|
9720
|
+
function getPageStoryExample(projectInfo) {
|
|
9721
|
+
if (projectInfo.hasCsfFactoryPreview)
|
|
9722
|
+
return dedent17`
|
|
9723
|
+
\`\`\`tsx
|
|
9724
|
+
import preview from '#.storybook/preview';
|
|
9725
|
+
import { expect } from 'storybook/test';
|
|
9726
|
+
import { ProductPage } from './ProductPage';
|
|
9727
|
+
|
|
9728
|
+
const meta = preview.meta({
|
|
9729
|
+
component: ProductPage,
|
|
9730
|
+
tags: ['ai-generated'],
|
|
9731
|
+
});
|
|
9732
|
+
|
|
9733
|
+
export const Default = meta.story({
|
|
9734
|
+
render: () => <ProductPage />,
|
|
9735
|
+
play: async ({ canvas }) => {
|
|
9736
|
+
await expect(
|
|
9737
|
+
canvas.getByRole('heading', { name: /products/i }),
|
|
9738
|
+
).toBeVisible();
|
|
9739
|
+
},
|
|
9740
|
+
});
|
|
9741
|
+
\`\`\`
|
|
9742
|
+
`;
|
|
9743
|
+
let typeImport = getTypeImportSource(projectInfo);
|
|
9744
|
+
return dedent17`
|
|
9745
|
+
\`\`\`tsx
|
|
9746
|
+
import type { Meta, StoryObj } from '${typeImport}';
|
|
9747
|
+
import { expect } from 'storybook/test';
|
|
9748
|
+
import { ProductPage } from './ProductPage';
|
|
9749
|
+
|
|
9750
|
+
const meta = {
|
|
9751
|
+
component: ProductPage,
|
|
9752
|
+
tags: ['ai-generated'],
|
|
9753
|
+
} satisfies Meta<typeof ProductPage>;
|
|
9754
|
+
|
|
9755
|
+
export default meta;
|
|
9756
|
+
type Story = StoryObj<typeof meta>;
|
|
9757
|
+
|
|
9758
|
+
export const Default: Story = {
|
|
9759
|
+
render: () => <ProductPage />,
|
|
9760
|
+
play: async ({ canvas }) => {
|
|
9761
|
+
await expect(
|
|
9762
|
+
canvas.getByRole('heading', { name: /products/i }),
|
|
9763
|
+
).toBeVisible();
|
|
9764
|
+
},
|
|
9765
|
+
};
|
|
9766
|
+
\`\`\`
|
|
9767
|
+
`;
|
|
9768
|
+
}
|
|
9769
|
+
function instructions(projectInfo) {
|
|
9770
|
+
let configDir = projectInfo.configDir, typeImport = getTypeImportSource(projectInfo);
|
|
9771
|
+
return dedent17`
|
|
9772
|
+
Attention: The following instructions must be followed in order to successfully set up Storybook in this project. Do not skip steps or attempt to do them out of order.
|
|
9773
|
+
|
|
9774
|
+
Your goal is to make Storybook fully functional in this project by analyzing the codebase,
|
|
9775
|
+
configuring the preview with the right decorators, and writing stories for some components.
|
|
9776
|
+
|
|
9777
|
+
The end state should be a Storybook where any component — from a small button to a full page — can be added without story-specific workarounds. All necessary providers, CSS, browser state, and network mocks should live in the shared preview so that just rendering the component in the story is enough.
|
|
9778
|
+
|
|
9779
|
+
After each created story, run Vitest to verify it renders.
|
|
9780
|
+
If the test fails, read the error, fix the issue, and re-run until it passes before moving on.
|
|
9781
|
+
|
|
9782
|
+
- Copy real patterns from the codebase
|
|
9783
|
+
- Keep the app code unchanged
|
|
9784
|
+
- Put the default setup in \`${configDir}/preview.tsx\`
|
|
9785
|
+
- Keep app mocking and runtime setup in \`${configDir}/preview.tsx\`, not in the stories
|
|
9786
|
+
|
|
9787
|
+
${getDocsReferenceSection(projectInfo)}
|
|
9788
|
+
|
|
9789
|
+
### Step 1: Analyze the codebase
|
|
9790
|
+
|
|
9791
|
+
Read enough of the app to understand the full runtime environment before writing any stories.
|
|
9792
|
+
|
|
9793
|
+
Do not stop at \`main.tsx\` or \`App.tsx\`.
|
|
9794
|
+
Follow imports into providers, pages, hooks, and shared components until you know:
|
|
9795
|
+
|
|
9796
|
+
- which providers exist
|
|
9797
|
+
- which CSS files are injected
|
|
9798
|
+
- which queries fetch data
|
|
9799
|
+
- which browser-state reads happen
|
|
9800
|
+
- which portals and portal roots exist
|
|
9801
|
+
- which pages and components show the real usage patterns
|
|
9802
|
+
|
|
9803
|
+
Example of what to copy:
|
|
9804
|
+
|
|
9805
|
+
\`\`\`tsx
|
|
9806
|
+
// src/main.tsx
|
|
9807
|
+
import "./index.css";
|
|
9808
|
+
import App from "./App";
|
|
9809
|
+
import { SessionProvider } from "./contexts/SessionContext";
|
|
9810
|
+
|
|
9811
|
+
createRoot(document.getElementById("root")!).render(
|
|
9812
|
+
<SessionProvider>
|
|
9813
|
+
<App />
|
|
9814
|
+
</SessionProvider>,
|
|
9815
|
+
);
|
|
9816
|
+
\`\`\`
|
|
9817
|
+
|
|
9818
|
+
That means Storybook should copy:
|
|
9819
|
+
|
|
9820
|
+
- the \`index.css\` import
|
|
9821
|
+
- the \`SessionProvider\`
|
|
9822
|
+
- the same provider order
|
|
9823
|
+
|
|
9824
|
+
Example of tracing the app deeper:
|
|
9825
|
+
|
|
9826
|
+
\`\`\`tsx
|
|
9827
|
+
// src/App.tsx
|
|
9828
|
+
function App() {
|
|
9829
|
+
const { products, loadMoreProducts } = useProducts();
|
|
9830
|
+
const { currentUser, signOut } = useSession();
|
|
9831
|
+
// ...
|
|
9832
|
+
}
|
|
9833
|
+
\`\`\`
|
|
9834
|
+
|
|
9835
|
+
\`\`\`ts
|
|
9836
|
+
// src/hooks/useProducts.ts
|
|
9837
|
+
const response = await fetch(apiBaseUrl + "/products?page=1");
|
|
9838
|
+
\`\`\`
|
|
9839
|
+
|
|
9840
|
+
\`\`\`ts
|
|
9841
|
+
// src/hooks/useTheme.ts
|
|
9842
|
+
const savedTheme = localStorage.getItem("theme");
|
|
9843
|
+
\`\`\`
|
|
9844
|
+
|
|
9845
|
+
That means the default Storybook setup should discover and prepare:
|
|
9846
|
+
|
|
9847
|
+
- provider state
|
|
9848
|
+
- MSW handlers for queries
|
|
9849
|
+
- browser-state values that are actually read during render
|
|
9850
|
+
|
|
9851
|
+
### Step 2: Build one default app environment in preview
|
|
9852
|
+
|
|
9853
|
+
Set up Storybook once so most stories work without story-specific setup.
|
|
9854
|
+
|
|
9855
|
+
Start with the smallest faithful environment:
|
|
9856
|
+
|
|
9857
|
+
- the real provider tree
|
|
9858
|
+
- the real root CSS
|
|
9859
|
+
- seeded browser state if the app reads it during render
|
|
9860
|
+
- MSW for network/data queries
|
|
9861
|
+
|
|
9862
|
+
It is fine to seed browser state such as \`localStorage\`, \`sessionStorage\`, and cookies when the app reads them during render.
|
|
9863
|
+
Seed only the specific app-owned keys and values you need.
|
|
9864
|
+
Do not clear all \`localStorage\`, \`sessionStorage\`, or cookies, and do not reset Storybook's own state.
|
|
9865
|
+
Do not mock or redefine the browser runtime itself.
|
|
9866
|
+
The stories run in Vitest browser mode, so the real browser environment should already exist.
|
|
9867
|
+
|
|
9868
|
+
${getPreviewConfigExample(projectInfo)}
|
|
9869
|
+
|
|
9870
|
+
Use this same idea for:
|
|
9871
|
+
|
|
9872
|
+
- providers
|
|
9873
|
+
- root CSS
|
|
9874
|
+
- browser state
|
|
9875
|
+
- dates, and if the app logic depends on them during render then always use \`mockdate\`
|
|
9876
|
+
|
|
9877
|
+
Example with the \`mockdate\` package:
|
|
9878
|
+
|
|
9879
|
+
${getMockDateExample(projectInfo)}
|
|
9880
|
+
|
|
9881
|
+
### Step 3: Support portals with preview-body.html
|
|
9882
|
+
|
|
9883
|
+
If the app uses portals, copy that setup into Storybook too.
|
|
9884
|
+
|
|
9885
|
+
Look for patterns like:
|
|
9886
|
+
|
|
9887
|
+
- \`createPortal(...)\`
|
|
9888
|
+
- modal, dialog, drawer, popover, tooltip, toast, or dropdown portal components
|
|
9889
|
+
- hard-coded roots such as \`#portal-root\`, \`#modal-root\`, \`#drawer-root\`, or \`#toast-root\`
|
|
9890
|
+
|
|
9891
|
+
Example of what to copy:
|
|
9892
|
+
|
|
9893
|
+
\`\`\`tsx
|
|
9894
|
+
// real component
|
|
9895
|
+
return createPortal(<ModalContent />, document.getElementById("portal-root")!);
|
|
9896
|
+
\`\`\`
|
|
9897
|
+
|
|
9898
|
+
That means Storybook should create the same portal root in \`${configDir}/preview-body.html\`:
|
|
9899
|
+
|
|
9900
|
+
\`\`\`html
|
|
9901
|
+
<!-- ${configDir}/preview-body.html -->
|
|
9902
|
+
<div id="portal-root"></div>
|
|
9903
|
+
\`\`\`
|
|
9904
|
+
|
|
9905
|
+
If the app uses multiple portal roots, create all of them there:
|
|
9906
|
+
|
|
9907
|
+
\`\`\`html
|
|
9908
|
+
<!-- ${configDir}/preview-body.html -->
|
|
9909
|
+
<div id="modal-root"></div>
|
|
9910
|
+
<div id="drawer-root"></div>
|
|
9911
|
+
<div id="toast-root"></div>
|
|
9912
|
+
\`\`\`
|
|
9913
|
+
|
|
9914
|
+
If a library portals directly to \`document.body\`, do not add extra roots for it.
|
|
9915
|
+
Make sure the copied page shell, CSS, and layout still allow overlays, fixed positioning, and z-index stacking to render correctly.
|
|
9916
|
+
|
|
9917
|
+
### Step 4: Mock side effects globally
|
|
9918
|
+
|
|
9919
|
+
All network/data queries should be handled by the default Storybook environment.
|
|
9920
|
+
|
|
9921
|
+
- Always use \`msw-storybook-addon\` for query mocking.
|
|
9922
|
+
- If you introduce MSW, run \`npx msw init ./public --save\` to create the worker file.
|
|
9923
|
+
- Make sure Storybook serves \`./public\` as a static dir so \`mockServiceWorker.js\` is available.
|
|
9924
|
+
- Do not mock \`fetch\` directly.
|
|
9925
|
+
- Network/data queries should return deterministic mock data.
|
|
9926
|
+
- If you need to change dependencies, first check the lockfile and use that package manager for the change.
|
|
9927
|
+
|
|
9928
|
+
Example of copying a real fetch pattern into shared handlers:
|
|
9929
|
+
|
|
9930
|
+
\`\`\`ts
|
|
9931
|
+
// real app hook
|
|
9932
|
+
const response = await fetch(
|
|
9933
|
+
apiBaseUrl +
|
|
9934
|
+
"/products?" +
|
|
9935
|
+
new URLSearchParams({
|
|
9936
|
+
page: "1",
|
|
9937
|
+
sort: "featured",
|
|
9938
|
+
}),
|
|
9939
|
+
);
|
|
9940
|
+
\`\`\`
|
|
9941
|
+
|
|
9942
|
+
\`\`\`ts
|
|
9943
|
+
// ${configDir}/msw-handlers.ts
|
|
9944
|
+
import { http, HttpResponse } from "msw";
|
|
9945
|
+
|
|
9946
|
+
export const mswHandlers = {
|
|
9947
|
+
products: [
|
|
9948
|
+
http.get("https://api.example.com/products", () =>
|
|
9949
|
+
HttpResponse.json({
|
|
9950
|
+
items: [
|
|
9951
|
+
{
|
|
9952
|
+
id: "product-1",
|
|
9953
|
+
name: "Example product",
|
|
9954
|
+
description: "Mock product description",
|
|
9955
|
+
imageUrl: "https://images.example.com/product.jpg",
|
|
9956
|
+
price: 42,
|
|
9957
|
+
},
|
|
9958
|
+
],
|
|
9959
|
+
}),
|
|
9960
|
+
),
|
|
9961
|
+
],
|
|
9962
|
+
};
|
|
9963
|
+
\`\`\`
|
|
9964
|
+
|
|
9965
|
+
${getMswPreviewExample(projectInfo)}
|
|
9966
|
+
|
|
9967
|
+
\`\`\`ts
|
|
9968
|
+
// ${configDir}/main.ts
|
|
9969
|
+
import type { StorybookConfig } from "${typeImport}";
|
|
9970
|
+
|
|
9971
|
+
const config: StorybookConfig = {
|
|
9972
|
+
staticDirs: ["../public"],
|
|
9973
|
+
};
|
|
9974
|
+
|
|
9975
|
+
export default config;
|
|
9976
|
+
\`\`\`
|
|
9977
|
+
|
|
9978
|
+
Keep these mocks global.
|
|
9979
|
+
Do not put fetch mocks in individual stories.
|
|
9980
|
+
Only add handlers for requests that the shared preview setup or the stories actually use.
|
|
9981
|
+
Do not add catch-all handlers that can hide unrelated failures.
|
|
9982
|
+
If the defaults are not enough, improve the shared default setup instead.
|
|
9983
|
+
Seed browser state when needed, but do not mock \`window\`, \`document\`, \`navigator\`, observers, or similar runtime APIs.
|
|
9984
|
+
The only exception is \`mockdate\` when date-based rendering exists.
|
|
9985
|
+
|
|
9986
|
+
### Step 5: Write stories
|
|
9987
|
+
|
|
9988
|
+
Try to find around 10 good candidate components for story files.
|
|
9989
|
+
Write colocated stories for top-level components, from low-level reusable components up to page components.
|
|
9990
|
+
Write up to 10 story files, or fewer only if the codebase clearly has fewer meaningful targets.
|
|
9991
|
+
|
|
9992
|
+
The stories should use JSX copied from real usage patterns in:
|
|
9993
|
+
|
|
9994
|
+
- pages
|
|
9995
|
+
- app shells
|
|
9996
|
+
- routes
|
|
9997
|
+
- tests
|
|
9998
|
+
- existing feature code
|
|
9999
|
+
|
|
10000
|
+
As a rule of thumb, each story file should have around 3 story exports when the component or page has enough meaningful states.
|
|
10001
|
+
It can have more when the real usage supports it, up to 10 story exports in one file.
|
|
10002
|
+
|
|
10003
|
+
Always show all imports explicitly in story and preview files.
|
|
10004
|
+
Do not rely on omitted or implied imports in examples or generated code.
|
|
10005
|
+
|
|
10006
|
+
#### Story tags
|
|
10007
|
+
|
|
10008
|
+
Every story meta must include the \`ai-generated\` tag to identify AI-created stories:
|
|
10009
|
+
|
|
10010
|
+
${getStoryExample(projectInfo)}
|
|
10011
|
+
|
|
10012
|
+
If a story could not be fully fixed after the self-healing loop (the test still fails
|
|
10013
|
+
or the rendering is incomplete), add the \`needs-work\` tag alongside \`ai-generated\`:
|
|
10014
|
+
|
|
10015
|
+
${getNeedsWorkTagExample(projectInfo)}
|
|
10016
|
+
|
|
10017
|
+
#### Args vs render
|
|
10018
|
+
|
|
10019
|
+
For simple components where props drive the state, prefer \`args\` stories — no \`render\` function needed:
|
|
10020
|
+
|
|
10021
|
+
${getArgsStoryExample(projectInfo)}
|
|
10022
|
+
|
|
10023
|
+
Use \`render\` when the story needs composition — wrapping the component in layout, combining multiple components, or passing children as JSX:
|
|
10024
|
+
|
|
10025
|
+
${getRenderCompositionExample(projectInfo)}
|
|
10026
|
+
|
|
10027
|
+
Keep app mocking and runtime setup in preview, not in the stories.
|
|
10028
|
+
Do not build large story-specific harnesses.
|
|
10029
|
+
Do not write story files for subcomponents, hooks, contexts, or helpers.
|
|
10030
|
+
Do not create new application components.
|
|
10031
|
+
Do not add a custom \`title\`.
|
|
10032
|
+
Do not stop after only a few easy targets if the codebase has more meaningful components or pages available.
|
|
10033
|
+
|
|
10034
|
+
### Step 6: Write a play function for every story
|
|
10035
|
+
|
|
10036
|
+
Every named story export must have a \`play\` function.
|
|
10037
|
+
The \`play\` function is not optional, even for simple stories.
|
|
10038
|
+
|
|
10039
|
+
The purpose of the \`play\` function is to prove that the story actually works in the copied Storybook environment:
|
|
10040
|
+
|
|
10041
|
+
- the story renders something real and non-empty
|
|
10042
|
+
- the decorators provide the needed context
|
|
10043
|
+
- the CSS is applied well enough for the intended state to be visible
|
|
10044
|
+
- the MSW mocks or seeded browser state are actually being used
|
|
10045
|
+
- important interactions, async loading states, and portals behave correctly
|
|
10046
|
+
|
|
10047
|
+
Use \`play\` functions to verify behavior, not just to click around.
|
|
10048
|
+
A story without assertions is incomplete.
|
|
10049
|
+
|
|
10050
|
+
Use tools from \`storybook/test\` such as:
|
|
10051
|
+
|
|
10052
|
+
- \`expect\`
|
|
10053
|
+
- \`waitFor\`
|
|
10054
|
+
|
|
10055
|
+
Prefer \`canvas\` and \`userEvent\` from the \`play\` context.
|
|
10056
|
+
Do not destructure \`canvasElement\` just to create \`const canvas = within(canvasElement)\`.
|
|
10057
|
+
Do not import \`userEvent\` from \`storybook/test\`; use \`userEvent\` from the \`play\` context instead.
|
|
10058
|
+
Only use \`canvasElement.ownerDocument\` when you need to query outside the canvas, such as for portals.
|
|
10059
|
+
|
|
10060
|
+
Example:
|
|
10061
|
+
|
|
10062
|
+
\`\`\`tsx
|
|
10063
|
+
import type { StoryObj } from "${typeImport}";
|
|
10064
|
+
|
|
10065
|
+
export const FilledForm: Story = {
|
|
10066
|
+
play: async ({ canvas, userEvent }) => {
|
|
10067
|
+
const emailInput = canvas.getByLabelText("email", {
|
|
10068
|
+
selector: "input",
|
|
10069
|
+
});
|
|
10070
|
+
|
|
10071
|
+
await userEvent.type(emailInput, "example-email@email.com", {
|
|
10072
|
+
delay: 100,
|
|
10073
|
+
});
|
|
10074
|
+
|
|
10075
|
+
const passwordInput = canvas.getByLabelText("password", {
|
|
10076
|
+
selector: "input",
|
|
10077
|
+
});
|
|
10078
|
+
|
|
10079
|
+
await userEvent.type(passwordInput, "ExamplePassword", {
|
|
10080
|
+
delay: 100,
|
|
10081
|
+
});
|
|
10082
|
+
|
|
10083
|
+
const submitButton = canvas.getByRole("button");
|
|
10084
|
+
await userEvent.click(submitButton);
|
|
10085
|
+
},
|
|
10086
|
+
};
|
|
10087
|
+
\`\`\`
|
|
10088
|
+
|
|
10089
|
+
The assertions should match the real pattern you copied:
|
|
10090
|
+
|
|
10091
|
+
- for provider-backed stories, assert the provider-dependent UI appears correctly
|
|
10092
|
+
- for mocked-data stories, wait for the mocked data to appear and assert on it
|
|
10093
|
+
- for CSS-sensitive states, assert on visibility, text layout, class-driven states, or meaningful computed styles
|
|
10094
|
+
- for routing or navigation stories, assert the routed state or navigation outcome
|
|
10095
|
+
- for portal stories, query from \`canvasElement.ownerDocument\` when the UI renders outside the canvas
|
|
10096
|
+
|
|
10097
|
+
Examples of useful checks:
|
|
10098
|
+
|
|
10099
|
+
- a themed button has the expected label and is visibly enabled or disabled
|
|
10100
|
+
- a modal opened through a decorator or provider is visible in the portal root
|
|
10101
|
+
- mocked API data appears in the page instead of a loading spinner forever
|
|
10102
|
+
- a selected tab actually shows the selected panel
|
|
10103
|
+
- a toast, alert, or badge has the expected accessible text and visual state
|
|
10104
|
+
- a CSS class or computed style confirms the real state that matters
|
|
10105
|
+
|
|
10106
|
+
### Step 7: Prove CSS is loaded in exactly one story named \`CssCheck\`
|
|
10107
|
+
|
|
10108
|
+
In exactly one story, named \`CssCheck\`, assert a component-specific computed style. \`toBeVisible\` passes on an unstyled component; a concrete style value proves the shared preview loaded the app's CSS.
|
|
10109
|
+
|
|
10110
|
+
Pick a visually distinctive component, read a styling value from its source, and assert it with \`getComputedStyle\`:
|
|
10111
|
+
|
|
10112
|
+
\`\`\`tsx
|
|
10113
|
+
export const CssCheck: Story = {
|
|
10114
|
+
args: { children: "Submit" },
|
|
10115
|
+
play: async ({ canvas }) => {
|
|
10116
|
+
const button = canvas.getByRole("button", { name: /submit/i });
|
|
10117
|
+
// PrimaryButton uses bg-blue-600 — fails if Tailwind / global CSS did not load.
|
|
10118
|
+
await expect(getComputedStyle(button).backgroundColor).toBe("rgb(37, 99, 235)");
|
|
10119
|
+
},
|
|
10120
|
+
};
|
|
10121
|
+
\`\`\`
|
|
10122
|
+
|
|
10123
|
+
### Step 8: Cover the patterns you found
|
|
10124
|
+
|
|
10125
|
+
Write stories for the real patterns in the codebase, for example:
|
|
10126
|
+
|
|
10127
|
+
- a low-level reusable component in real JSX usage
|
|
10128
|
+
- a provider-backed component
|
|
10129
|
+
- a browser-state-backed component
|
|
10130
|
+
- a fetched-data component
|
|
10131
|
+
- a real page component
|
|
10132
|
+
|
|
10133
|
+
Use \`App.tsx\` to inspect the real provider tree and usage patterns, but do not make a story for \`App\` when the codebase has actual page components.
|
|
10134
|
+
|
|
10135
|
+
Example page story:
|
|
10136
|
+
|
|
10137
|
+
${getPageStoryExample(projectInfo)}
|
|
10138
|
+
|
|
10139
|
+
### Step 9: Verify both rendering and types
|
|
10140
|
+
|
|
10141
|
+
As you work, verify the stories with Vitest:
|
|
10142
|
+
|
|
10143
|
+
\`\`\`bash
|
|
10144
|
+
npx vitest --project storybook <path-to-story-file>
|
|
10145
|
+
\`\`\`
|
|
10146
|
+
|
|
10147
|
+
Also verify types so you catch missing required props, broken imports, and preview typing issues. Run the same TypeScript command the project itself uses.
|
|
10148
|
+
|
|
10149
|
+
\`\`\`bash
|
|
10150
|
+
<project-specific-typescript-command>
|
|
10151
|
+
\`\`\`
|
|
10152
|
+
|
|
10153
|
+
After verification passes, review every changed file and remove anything that is not needed for the final solution, especially debug fixes, overly broad mocks, unnecessary dependencies, and eval artifacts.
|
|
10154
|
+
|
|
10155
|
+
Keep iterating until:
|
|
10156
|
+
|
|
10157
|
+
- every story you wrote passes
|
|
10158
|
+
- every story you wrote has a meaningful passing \`play\` function
|
|
10159
|
+
- the changed stories and preview setup pass the project's real TypeScript check
|
|
10160
|
+
- the rendered output looks sensible
|
|
10161
|
+
- the default global mocked environment is strong enough that stories do not need manual fetch overrides
|
|
10162
|
+
- stories no longer fail because the shared preview setup and story JSX are fixed
|
|
10163
|
+
- all passing stories have \`tags: ['ai-generated']\` in their meta
|
|
10164
|
+
- any stories that still need work have \`tags: ['ai-generated', 'needs-work']\` in their meta
|
|
10165
|
+
`;
|
|
10166
|
+
}
|
|
10167
|
+
|
|
10168
|
+
// src/ai/setup-prompts/index.ts
|
|
10169
|
+
var CURRENTLY_USED_PROMPT = {
|
|
10170
|
+
"pattern-copy-play": instructions
|
|
10171
|
+
}, FORMERLY_USED_PROMPTS = {
|
|
10172
|
+
setup: async () => (await import("./setup-BYVI7PM2.js")).instructions
|
|
10173
|
+
}, PROMPT_NAMES = [
|
|
10174
|
+
...Object.keys(CURRENTLY_USED_PROMPT),
|
|
10175
|
+
...Object.keys(FORMERLY_USED_PROMPTS)
|
|
10176
|
+
], DEFAULT_PROMPT_NAME = "pattern-copy-play", EVAL_SETUP_PROMPT_ENV = "EVAL_SETUP_PROMPT";
|
|
10177
|
+
function resolvePromptName() {
|
|
10178
|
+
let requested = process.env[EVAL_SETUP_PROMPT_ENV]?.trim();
|
|
10179
|
+
return requested && (Object.hasOwn(CURRENTLY_USED_PROMPT, requested) || Object.hasOwn(FORMERLY_USED_PROMPTS, requested)) ? requested : DEFAULT_PROMPT_NAME;
|
|
10180
|
+
}
|
|
10181
|
+
async function getPrompts(projectInfo) {
|
|
10182
|
+
let name = resolvePromptName(), builder = CURRENTLY_USED_PROMPT[name] ?? await FORMERLY_USED_PROMPTS[name]();
|
|
10183
|
+
return {
|
|
10184
|
+
prompts: [
|
|
10185
|
+
{
|
|
10186
|
+
name,
|
|
10187
|
+
description: "Set up Storybook for success",
|
|
10188
|
+
instructions: builder(projectInfo)
|
|
10189
|
+
}
|
|
10190
|
+
]
|
|
10191
|
+
};
|
|
10192
|
+
}
|
|
10193
|
+
|
|
10194
|
+
// src/ai/prompt.ts
|
|
10195
|
+
function getProjectOverview(projectInfo) {
|
|
10196
|
+
return dedent18`
|
|
10197
|
+
## Project Info
|
|
10198
|
+
|
|
10199
|
+
| Property | Value |
|
|
10200
|
+
|----------|-------|
|
|
10201
|
+
| Version | ${projectInfo.storybookVersion || "unknown"} |
|
|
10202
|
+
| Renderer | ${projectInfo.rendererPackage || "unknown"} |
|
|
10203
|
+
| Framework | ${projectInfo.framework || "unknown"} |
|
|
10204
|
+
| Builder | ${projectInfo.builderPackage || "unknown"} |
|
|
10205
|
+
| Config Dir | \`${projectInfo.configDir}\` |
|
|
10206
|
+
| CSF Format | ${projectInfo.hasCsfFactoryPreview ? "CSF Factory" : "CSF3"} |
|
|
10207
|
+
| Addons | ${projectInfo.addons.length > 0 ? projectInfo.addons.join(", ") : "none"} |
|
|
10208
|
+
`;
|
|
10209
|
+
}
|
|
10210
|
+
async function generateMarkdownOutput(projectInfo) {
|
|
10211
|
+
let { prompts: aiPrompts } = await getPrompts(projectInfo), sections = [];
|
|
10212
|
+
sections.push(dedent18`
|
|
10213
|
+
# Storybook Setup
|
|
10214
|
+
`), sections.push(getProjectOverview(projectInfo));
|
|
10215
|
+
for (let aiPrompt of aiPrompts)
|
|
10216
|
+
sections.push(aiPrompt.instructions);
|
|
10217
|
+
return { markdown: sections.join(`
|
|
10218
|
+
|
|
10219
|
+
`) };
|
|
10220
|
+
}
|
|
10221
|
+
|
|
10222
|
+
// src/ai/index.ts
|
|
10223
|
+
async function aiSetup(options) {
|
|
10224
|
+
let { configDir: userConfigDir, packageManager: packageManagerName, output } = options, projectInfo;
|
|
10225
|
+
try {
|
|
10226
|
+
let data = await getStorybookData({
|
|
10227
|
+
configDir: userConfigDir,
|
|
10228
|
+
packageManagerName
|
|
10229
|
+
}), majorVersion = data.versionInstalled ? parseMajorVersion(data.versionInstalled) : void 0;
|
|
10230
|
+
if (!data.frameworkPackage || !data.rendererPackage || !data.builderPackage) {
|
|
10231
|
+
logger21.error(
|
|
10232
|
+
"Could not detect framework, renderer, or builder from your Storybook config. Make sure you are running this command from your project root, or specify --config-dir."
|
|
10233
|
+
);
|
|
10234
|
+
return;
|
|
10235
|
+
}
|
|
10236
|
+
let language = await new ProjectTypeService(data.packageManager).detectLanguage() === SupportedLanguage2.TYPESCRIPT ? "ts" : "js";
|
|
10237
|
+
projectInfo = {
|
|
10238
|
+
storybookVersion: data.versionInstalled,
|
|
10239
|
+
majorVersion,
|
|
10240
|
+
framework: data.frameworkPackage,
|
|
10241
|
+
rendererPackage: data.rendererPackage,
|
|
10242
|
+
renderer: data.renderer,
|
|
10243
|
+
builderPackage: data.builderPackage,
|
|
10244
|
+
addons: data.addons ?? [],
|
|
10245
|
+
configDir: data.configDir,
|
|
10246
|
+
storiesPaths: data.storiesPaths,
|
|
10247
|
+
hasCsfFactoryPreview: data.hasCsfFactoryPreview,
|
|
10248
|
+
language
|
|
10249
|
+
};
|
|
10250
|
+
} catch (err) {
|
|
10251
|
+
logger21.error(
|
|
10252
|
+
`Failed to read Storybook configuration: ${err instanceof Error ? err.message : String(err)}`
|
|
10253
|
+
), logger21.log(
|
|
10254
|
+
"Make sure you are running this command from your project root, or specify --config-dir."
|
|
10255
|
+
);
|
|
10256
|
+
return;
|
|
10257
|
+
}
|
|
10258
|
+
if (projectInfo.rendererPackage !== "@storybook/react" || projectInfo.builderPackage !== "@storybook/builder-vite") {
|
|
10259
|
+
logger21.log(
|
|
10260
|
+
"AI-assisted setup is currently only available for projects using the React renderer with Vite builder. Detected renderer: " + projectInfo.rendererPackage + ", builder: " + projectInfo.builderPackage
|
|
10261
|
+
);
|
|
10262
|
+
return;
|
|
10263
|
+
}
|
|
10264
|
+
let markdownOutput = (await generateMarkdownOutput(projectInfo)).markdown;
|
|
10265
|
+
if (await telemetry("ai-setup", {
|
|
10266
|
+
cliOptions: {
|
|
10267
|
+
output: output ? "file" : void 0,
|
|
10268
|
+
configDir: projectInfo.configDir,
|
|
10269
|
+
packageManager: packageManagerName
|
|
10270
|
+
},
|
|
10271
|
+
project: {
|
|
10272
|
+
framework: projectInfo.framework,
|
|
10273
|
+
renderer: projectInfo.rendererPackage,
|
|
10274
|
+
builder: projectInfo.builderPackage,
|
|
10275
|
+
language: projectInfo.language,
|
|
10276
|
+
hasCsfFactoryPreview: projectInfo.hasCsfFactoryPreview
|
|
10277
|
+
}
|
|
10278
|
+
}), isTelemetryModuleEnabled()) {
|
|
10279
|
+
let resolvedConfigDir = resolve3(projectInfo.configDir), previewSnapshot = await snapshotPreviewFile(resolvedConfigDir), sessionId = await getSessionId(), pendingRecord = {
|
|
10280
|
+
timestamp: Date.now(),
|
|
10281
|
+
sessionId,
|
|
10282
|
+
configDir: resolvedConfigDir,
|
|
10283
|
+
...previewSnapshot
|
|
10284
|
+
};
|
|
10285
|
+
await cache.set("ai-setup-pending", pendingRecord);
|
|
10286
|
+
}
|
|
10287
|
+
if (output) {
|
|
10288
|
+
let outputPath = resolve3(output);
|
|
10289
|
+
await writeFile9(outputPath, markdownOutput, "utf-8"), logger21.log(`Prompt written to ${outputPath}`);
|
|
10290
|
+
} else
|
|
10291
|
+
logger21.log(markdownOutput);
|
|
10292
|
+
}
|
|
10293
|
+
function parseMajorVersion(version2) {
|
|
10294
|
+
let match = version2.match(/^(\d+)/);
|
|
10295
|
+
return match ? parseInt(match[1], 10) : void 0;
|
|
10296
|
+
}
|
|
10297
|
+
|
|
9131
10298
|
// src/upgrade.ts
|
|
9132
|
-
var import_cross_spawn3 = __toESM(require_cross_spawn(), 1), import_picocolors16 = __toESM(require_picocolors(), 1),
|
|
10299
|
+
var import_cross_spawn3 = __toESM(require_cross_spawn(), 1), import_picocolors16 = __toESM(require_picocolors(), 1), import_semver8 = __toESM(require_semver(), 1);
|
|
9133
10300
|
import { PackageManagerName as PackageManagerName2 } from "storybook/internal/common";
|
|
9134
|
-
import { HandledError, JsPackageManagerFactory as JsPackageManagerFactory2, isCorePackage as isCorePackage2 } from "storybook/internal/common";
|
|
10301
|
+
import { HandledError as HandledError2, JsPackageManagerFactory as JsPackageManagerFactory2, isCorePackage as isCorePackage2 } from "storybook/internal/common";
|
|
9135
10302
|
import {
|
|
9136
10303
|
CLI_COLORS as CLI_COLORS4,
|
|
9137
10304
|
createHyperlink,
|
|
9138
10305
|
logTracker as logTracker3,
|
|
9139
|
-
logger as
|
|
10306
|
+
logger as logger23,
|
|
9140
10307
|
prompt as prompt6
|
|
9141
10308
|
} from "storybook/internal/node-logger";
|
|
9142
10309
|
import {
|
|
9143
10310
|
UpgradeStorybookToLowerVersionError,
|
|
9144
10311
|
UpgradeStorybookUnknownCurrentVersionError
|
|
9145
10312
|
} from "storybook/internal/server-errors";
|
|
9146
|
-
import { telemetry } from "storybook/internal/telemetry";
|
|
9147
|
-
import { dedent as
|
|
10313
|
+
import { telemetry as telemetry2 } from "storybook/internal/telemetry";
|
|
10314
|
+
import { dedent as dedent19 } from "ts-dedent";
|
|
9148
10315
|
|
|
9149
10316
|
// src/automigrate/multi-project.ts
|
|
9150
|
-
import { CLI_COLORS as CLI_COLORS3, logger as
|
|
10317
|
+
import { CLI_COLORS as CLI_COLORS3, logger as logger22, prompt as prompt5 } from "storybook/internal/node-logger";
|
|
9151
10318
|
import { ErrorCollector, sanitizeError } from "storybook/internal/telemetry";
|
|
9152
10319
|
async function collectAutomigrationsAcrossProjects(options) {
|
|
9153
10320
|
let { fixes, projects, taskLog } = options, automigrationMap = /* @__PURE__ */ new Map();
|
|
9154
|
-
|
|
10321
|
+
logger22.debug(
|
|
9155
10322
|
`Starting automigration collection across ${projects.length} projects and ${fixes.length} fixes...`
|
|
9156
10323
|
);
|
|
9157
10324
|
function collectResult(fix, project, status, result) {
|
|
@@ -9173,10 +10340,10 @@ async function collectAutomigrationsAcrossProjects(options) {
|
|
|
9173
10340
|
}
|
|
9174
10341
|
for (let project of projects) {
|
|
9175
10342
|
let projectName = shortenPath(project.configDir);
|
|
9176
|
-
taskLog.message(`Checking automigrations for ${projectName}...`),
|
|
10343
|
+
taskLog.message(`Checking automigrations for ${projectName}...`), logger22.debug(`Processing project: ${projectName}`);
|
|
9177
10344
|
for (let fix of fixes)
|
|
9178
10345
|
try {
|
|
9179
|
-
|
|
10346
|
+
logger22.debug(`Checking fix ${fix.id} for project ${projectName}...`);
|
|
9180
10347
|
let checkOptions = {
|
|
9181
10348
|
packageManager: project.packageManager,
|
|
9182
10349
|
configDir: project.configDir,
|
|
@@ -9189,9 +10356,9 @@ async function collectAutomigrationsAcrossProjects(options) {
|
|
|
9189
10356
|
}, result = await fix.check(checkOptions);
|
|
9190
10357
|
result !== null ? collectResult(fix, project, "check_succeeded", result) : collectResult(fix, project, "not_applicable");
|
|
9191
10358
|
} catch (error) {
|
|
9192
|
-
collectResult(fix, project, "check_failed"),
|
|
10359
|
+
collectResult(fix, project, "check_failed"), logger22.debug(
|
|
9193
10360
|
`Failed to check fix ${fix.id} for project ${shortenPath(project.configDir)}.`
|
|
9194
|
-
),
|
|
10361
|
+
), logger22.debug(`${error instanceof Error ? error.stack : String(error)}`), ErrorCollector.addError(error);
|
|
9195
10362
|
}
|
|
9196
10363
|
}
|
|
9197
10364
|
let allAutomigrations = Array.from(automigrationMap.values()), applicableAutomigrations = allAutomigrations.filter(
|
|
@@ -9205,9 +10372,9 @@ async function collectAutomigrationsAcrossProjects(options) {
|
|
|
9205
10372
|
);
|
|
9206
10373
|
return taskLog.message(`
|
|
9207
10374
|
Automigrations detected:`), successAutomigrations.forEach((fixId) => {
|
|
9208
|
-
taskLog.message(`${CLI_COLORS3.success(`${
|
|
10375
|
+
taskLog.message(`${CLI_COLORS3.success(`${logger22.SYMBOLS.success} ${fixId}`)}`);
|
|
9209
10376
|
}), failedAutomigrations.forEach((fixId) => {
|
|
9210
|
-
taskLog.message(`${CLI_COLORS3.error(`${
|
|
10377
|
+
taskLog.message(`${CLI_COLORS3.error(`${logger22.SYMBOLS.error} ${fixId}`)}`);
|
|
9211
10378
|
}), failedAutomigrations.length > 0 ? taskLog.error(
|
|
9212
10379
|
`${failedAutomigrations.length} automigration ${failedAutomigrations.length > 1 ? "checks" : "check"} failed`
|
|
9213
10380
|
) : taskLog.success(
|
|
@@ -9225,12 +10392,12 @@ async function promptForAutomigrations(automigrations, options) {
|
|
|
9225
10392
|
if (automigrations.length === 0)
|
|
9226
10393
|
return [];
|
|
9227
10394
|
if (options.dryRun)
|
|
9228
|
-
return
|
|
9229
|
-
|
|
10395
|
+
return logger22.log("Detected automigrations (dry run - no changes will be made):"), automigrations.forEach(({ fix, reports: list }) => {
|
|
10396
|
+
logger22.log(` - ${fix.id} (${formatProjectDirs(list)})`);
|
|
9230
10397
|
}), [];
|
|
9231
10398
|
if (options.yes)
|
|
9232
|
-
return
|
|
9233
|
-
|
|
10399
|
+
return logger22.log("Running all detected automigrations:"), automigrations.forEach(({ fix, reports: list }) => {
|
|
10400
|
+
logger22.log(` - ${fix.id} (${formatProjectDirs(list)})`);
|
|
9234
10401
|
}), automigrations;
|
|
9235
10402
|
let choices = automigrations.map((am) => {
|
|
9236
10403
|
let hint = [];
|
|
@@ -9279,13 +10446,13 @@ async function runAutomigrationsForProjects(selectedAutomigrations, options) {
|
|
|
9279
10446
|
title: `${countPrefix}Running automigrations for ${projectName}`
|
|
9280
10447
|
}) : {
|
|
9281
10448
|
message: (message) => {
|
|
9282
|
-
|
|
10449
|
+
logger22.debug(`${message}`);
|
|
9283
10450
|
},
|
|
9284
10451
|
error: (message) => {
|
|
9285
|
-
|
|
10452
|
+
logger22.debug(`${message}`);
|
|
9286
10453
|
},
|
|
9287
10454
|
success: (message) => {
|
|
9288
|
-
|
|
10455
|
+
logger22.debug(`${message}`);
|
|
9289
10456
|
}
|
|
9290
10457
|
}, fixResults = {}, fixFailures = {};
|
|
9291
10458
|
for (let automigration of projectAutomigration) {
|
|
@@ -9319,11 +10486,11 @@ async function runAutomigrationsForProjects(selectedAutomigrations, options) {
|
|
|
9319
10486
|
storiesPaths: project2.storiesPaths,
|
|
9320
10487
|
yes
|
|
9321
10488
|
};
|
|
9322
|
-
await fix.run(runOptions), fixResults[fix.id] = "succeeded" /* SUCCEEDED */, taskLog.message(CLI_COLORS3.success(`${
|
|
10489
|
+
await fix.run(runOptions), fixResults[fix.id] = "succeeded" /* SUCCEEDED */, taskLog.message(CLI_COLORS3.success(`${logger22.SYMBOLS.success} ${fix.id}`));
|
|
9323
10490
|
}
|
|
9324
10491
|
} catch (error) {
|
|
9325
10492
|
let errorMessage = (error instanceof Error ? error.stack : String(error)) ?? "Unknown error";
|
|
9326
|
-
fixResults[fix.id] = "failed" /* FAILED */, fixFailures[fix.id] = sanitizeError(error), taskLog.message(CLI_COLORS3.error(`${
|
|
10493
|
+
fixResults[fix.id] = "failed" /* FAILED */, fixFailures[fix.id] = sanitizeError(error), taskLog.message(CLI_COLORS3.error(`${logger22.SYMBOLS.error} ${automigration.fix.id}`)), logger22.debug(errorMessage), ErrorCollector.addError(error);
|
|
9327
10494
|
}
|
|
9328
10495
|
}
|
|
9329
10496
|
let automigrationsWithErrors = Object.values(fixResults).filter(
|
|
@@ -9378,7 +10545,7 @@ async function runAutomigrations(projects, options) {
|
|
|
9378
10545
|
let project = projects.find((p) => p.configDir === configDir);
|
|
9379
10546
|
if (project) {
|
|
9380
10547
|
let oldConfigDir = project.configDir;
|
|
9381
|
-
project.configDir = project.configDir.replace(".storybook",
|
|
10548
|
+
project.configDir = project.configDir.replace(".storybook", RN_STORYBOOK_DIR), automigrationResults[project.configDir] = resultData, delete automigrationResults[oldConfigDir];
|
|
9382
10549
|
}
|
|
9383
10550
|
}
|
|
9384
10551
|
}), {
|
|
@@ -9433,28 +10600,28 @@ function logUpgradeResults(projectResults, detectedAutomigrations, doctorResults
|
|
|
9433
10600
|
doctorResults
|
|
9434
10601
|
);
|
|
9435
10602
|
if (failedProjects.length > 0) {
|
|
9436
|
-
if (logTracker3.enableLogWriting(),
|
|
10603
|
+
if (logTracker3.enableLogWriting(), logger23.step(
|
|
9437
10604
|
"The upgrade is complete, but some projects failed to upgrade or migrate completely. Please see the debug logs for more details."
|
|
9438
10605
|
), successfulProjects.length > 0) {
|
|
9439
10606
|
let successfulProjectsList = successfulProjects.map((dir) => ` \u2022 ${shortenPath(dir)}`).join(`
|
|
9440
10607
|
`);
|
|
9441
|
-
|
|
10608
|
+
logger23.log(`${CLI_COLORS4.success("Successfully upgraded:")}
|
|
9442
10609
|
${successfulProjectsList}`);
|
|
9443
10610
|
}
|
|
9444
10611
|
let failedProjectsList = failedProjects.map((dir) => ` \u2022 ${shortenPath(dir)}`).join(`
|
|
9445
10612
|
`);
|
|
9446
|
-
if (
|
|
10613
|
+
if (logger23.log(
|
|
9447
10614
|
`${CLI_COLORS4.error("Failed to upgrade:")}
|
|
9448
10615
|
Some automigrations failed, please check the logs in the log file for more details.
|
|
9449
10616
|
${failedProjectsList}`
|
|
9450
10617
|
), projectsWithNoFixes.length > 0) {
|
|
9451
10618
|
let projectList = projectsWithNoFixes.map((dir) => ` \u2022 ${shortenPath(dir)}`).join(`
|
|
9452
10619
|
`);
|
|
9453
|
-
|
|
10620
|
+
logger23.log(`${CLI_COLORS4.info("No applicable migrations:")}
|
|
9454
10621
|
${projectList}`);
|
|
9455
10622
|
}
|
|
9456
10623
|
} else
|
|
9457
|
-
Object.values(doctorResults).every((result) => result.status === "healthy") ?
|
|
10624
|
+
Object.values(doctorResults).every((result) => result.status === "healthy") ? logger23.step(`${CLI_COLORS4.success("Your project(s) have been upgraded successfully! \u{1F389}")}`) : logger23.step(
|
|
9458
10625
|
`${import_picocolors16.default.yellow("Your project(s) have been upgraded successfully, but some issues were found which need your attention, please check Storybook doctor logs above.")}`
|
|
9459
10626
|
);
|
|
9460
10627
|
let automigrationLinks = detectedAutomigrations.filter(
|
|
@@ -9468,9 +10635,9 @@ ${projectList}`);
|
|
|
9468
10635
|
...automigrationLinks
|
|
9469
10636
|
].join(`
|
|
9470
10637
|
`);
|
|
9471
|
-
|
|
10638
|
+
logger23.log(automigrationLinksMessage);
|
|
9472
10639
|
}
|
|
9473
|
-
|
|
10640
|
+
logger23.log(
|
|
9474
10641
|
`For a full list of changes, please check our migration guide: ${CLI_COLORS4.cta("https://storybook.js.org/docs/releases/migration-guide?ref=upgrade")}`
|
|
9475
10642
|
);
|
|
9476
10643
|
}
|
|
@@ -9491,7 +10658,7 @@ async function sendMultiUpgradeTelemetry(options) {
|
|
|
9491
10658
|
(result) => result.status !== "healthy"
|
|
9492
10659
|
).length;
|
|
9493
10660
|
try {
|
|
9494
|
-
await
|
|
10661
|
+
await telemetry2("multi-upgrade", {
|
|
9495
10662
|
totalDetectedProjects: allProjects.length,
|
|
9496
10663
|
totalSelectedProjects: selectedProjects.length,
|
|
9497
10664
|
projectsWithSuccessfulAutomigrations: successfulProjects.length,
|
|
@@ -9502,7 +10669,7 @@ async function sendMultiUpgradeTelemetry(options) {
|
|
|
9502
10669
|
hasUserInterrupted
|
|
9503
10670
|
});
|
|
9504
10671
|
} catch (error) {
|
|
9505
|
-
|
|
10672
|
+
logger23.debug(`Failed to send multi-upgrade telemetry: ${String(error)}`);
|
|
9506
10673
|
}
|
|
9507
10674
|
}
|
|
9508
10675
|
async function upgrade(options) {
|
|
@@ -9510,13 +10677,13 @@ async function upgrade(options) {
|
|
|
9510
10677
|
if (projectsResult === void 0 || projectsResult.selectedProjects.length === 0)
|
|
9511
10678
|
return;
|
|
9512
10679
|
let { allProjects, selectedProjects: storybookProjects } = projectsResult;
|
|
9513
|
-
storybookProjects.length > 1 ?
|
|
10680
|
+
storybookProjects.length > 1 ? logger23.info(`Upgrading the following projects:
|
|
9514
10681
|
${storybookProjects.map((p) => `${import_picocolors16.default.cyan(shortenPath(p.configDir))}: ${import_picocolors16.default.bold(p.beforeVersion)} -> ${import_picocolors16.default.bold(p.currentCLIVersion)}`).join(`
|
|
9515
|
-
`)}`) :
|
|
10682
|
+
`)}`) : logger23.info(
|
|
9516
10683
|
`Upgrading from ${import_picocolors16.default.bold(storybookProjects[0].beforeVersion)} to ${import_picocolors16.default.bold(storybookProjects[0].currentCLIVersion)}`
|
|
9517
10684
|
);
|
|
9518
10685
|
let automigrationResults = {}, doctorResults = {}, handleInterruption = async () => {
|
|
9519
|
-
throw
|
|
10686
|
+
throw logger23.log(`
|
|
9520
10687
|
|
|
9521
10688
|
Upgrade interrupted by user.`), allProjects.length > 1 && await sendMultiUpgradeTelemetry({
|
|
9522
10689
|
allProjects,
|
|
@@ -9524,16 +10691,16 @@ Upgrade interrupted by user.`), allProjects.length > 1 && await sendMultiUpgrade
|
|
|
9524
10691
|
projectResults: automigrationResults,
|
|
9525
10692
|
doctorResults,
|
|
9526
10693
|
hasUserInterrupted: !0
|
|
9527
|
-
}), new
|
|
10694
|
+
}), new HandledError2("Upgrade cancelled by user");
|
|
9528
10695
|
};
|
|
9529
10696
|
process.on("SIGINT", handleInterruption), process.on("SIGTERM", handleInterruption);
|
|
9530
10697
|
try {
|
|
9531
10698
|
if (processAutoblockerResults(storybookProjects, (message) => {
|
|
9532
|
-
|
|
10699
|
+
logger23.error(dedent19`Blockers detected\n\n${message}`);
|
|
9533
10700
|
}))
|
|
9534
|
-
throw new
|
|
10701
|
+
throw new HandledError2("Blockers detected");
|
|
9535
10702
|
if (storybookProjects.some((project) => {
|
|
9536
|
-
if (!project.isCanary && (0,
|
|
10703
|
+
if (!project.isCanary && (0, import_semver8.lt)(project.currentCLIVersion, project.beforeVersion))
|
|
9537
10704
|
throw new UpgradeStorybookToLowerVersionError({
|
|
9538
10705
|
beforeVersion: project.beforeVersion,
|
|
9539
10706
|
currentVersion: project.currentCLIVersion
|
|
@@ -9548,7 +10715,7 @@ Upgrade interrupted by user.`), allProjects.length > 1 && await sendMultiUpgrade
|
|
|
9548
10715
|
try {
|
|
9549
10716
|
let loggedPaths = [];
|
|
9550
10717
|
for (let project of storybookProjects) {
|
|
9551
|
-
|
|
10718
|
+
logger23.debug(`Updating dependencies in ${shortenPath(project.configDir)}...`);
|
|
9552
10719
|
let newPaths = project.packageManager.packageJsonPaths.map(shortenPath).filter((path4) => !loggedPaths.includes(path4));
|
|
9553
10720
|
newPaths.length > 0 && (task.message(newPaths.join(`
|
|
9554
10721
|
`)), loggedPaths.push(...newPaths)), await upgradeStorybookDependencies({
|
|
@@ -9565,16 +10732,18 @@ Upgrade interrupted by user.`), allProjects.length > 1 && await sendMultiUpgrade
|
|
|
9565
10732
|
task.error(`Failed to upgrade dependencies: ${String(err)}`);
|
|
9566
10733
|
}
|
|
9567
10734
|
}
|
|
9568
|
-
let
|
|
10735
|
+
let automigrationResults2 = {}, detectedAutomigrations = [];
|
|
10736
|
+
options.skipAutomigrations ? logger23.log("Skipping automigrations (--skip-automigrations).") : { automigrationResults: automigrationResults2, detectedAutomigrations } = await runAutomigrations(
|
|
9569
10737
|
storybookProjects,
|
|
9570
10738
|
options
|
|
9571
|
-
)
|
|
9572
|
-
rootPackageManager.
|
|
10739
|
+
);
|
|
10740
|
+
let rootPackageManager = storybookProjects.length > 1 ? JsPackageManagerFactory2.getPackageManager({ force: options.packageManager }) : storybookProjects[0].packageManager;
|
|
10741
|
+
rootPackageManager.type === "npm" ? await rootPackageManager.installDependencies({ force: !0 }) : await rootPackageManager.installDependencies(), rootPackageManager.type !== PackageManagerName2.YARN1 && rootPackageManager.isStorybookInMonorepo() && (logger23.warn(
|
|
9573
10742
|
"Since you are in a monorepo, we advise you to deduplicate your dependencies. We can do this for you but it might take some time."
|
|
9574
10743
|
), options.yes || await prompt6.confirm({
|
|
9575
10744
|
message: `Execute ${rootPackageManager.getRunCommand("dedupe")}?`,
|
|
9576
10745
|
initialValue: !0
|
|
9577
|
-
}) ? rootPackageManager.type === "npm" ? await rootPackageManager.dedupeDependencies({ force: !0 }) : await rootPackageManager.dedupeDependencies() :
|
|
10746
|
+
}) ? rootPackageManager.type === "npm" ? await rootPackageManager.dedupeDependencies({ force: !0 }) : await rootPackageManager.dedupeDependencies() : logger23.log(
|
|
9578
10747
|
`If you find any issues running Storybook, you can run ${rootPackageManager.getRunCommand("dedupe")} manually to deduplicate your dependencies and try again.`
|
|
9579
10748
|
));
|
|
9580
10749
|
let doctorProjects = storybookProjects.map((project) => ({
|
|
@@ -9583,7 +10752,7 @@ Upgrade interrupted by user.`), allProjects.length > 1 && await sendMultiUpgrade
|
|
|
9583
10752
|
storybookVersion: project.currentCLIVersion,
|
|
9584
10753
|
mainConfig: project.mainConfig
|
|
9585
10754
|
}));
|
|
9586
|
-
|
|
10755
|
+
logger23.step("Checking the health of your project(s).."), doctorResults = await runMultiProjectDoctor(doctorProjects), displayDoctorResults(doctorResults) && logTracker3.enableLogWriting(), logUpgradeResults(automigrationResults2, detectedAutomigrations, doctorResults);
|
|
9587
10756
|
for (let project of storybookProjects) {
|
|
9588
10757
|
let resultData = automigrationResults2[project.configDir] || {
|
|
9589
10758
|
automigrationStatuses: {},
|
|
@@ -9593,7 +10762,7 @@ Upgrade interrupted by user.`), allProjects.length > 1 && await sendMultiUpgrade
|
|
|
9593
10762
|
status === "has_issues" && doctorFailureCount++, status === "check_error" && doctorErrorCount++;
|
|
9594
10763
|
});
|
|
9595
10764
|
let automigrationFailureCount = Object.keys(resultData.automigrationErrors).length, automigrationPreCheckFailure = project.autoblockerCheckResults && project.autoblockerCheckResults.length > 0 ? project.autoblockerCheckResults?.map((result) => result.result !== null ? result.blocker.id : null).filter(Boolean) : null;
|
|
9596
|
-
await
|
|
10765
|
+
await telemetry2("upgrade", {
|
|
9597
10766
|
beforeVersion: project.beforeVersion,
|
|
9598
10767
|
afterVersion: project.currentCLIVersion,
|
|
9599
10768
|
automigrationResults: resultData.automigrationStatuses,
|
|
@@ -9619,13 +10788,13 @@ Upgrade interrupted by user.`), allProjects.length > 1 && await sendMultiUpgrade
|
|
|
9619
10788
|
// src/bin/run.ts
|
|
9620
10789
|
addToGlobalContext("cliVersion", versions5.storybook);
|
|
9621
10790
|
var handleCommandFailure = (logFilePath) => async (error) => {
|
|
9622
|
-
error instanceof
|
|
10791
|
+
error instanceof HandledError3 || logger24.error(String(error));
|
|
9623
10792
|
try {
|
|
9624
10793
|
let logFile = await logTracker4.writeToFile(logFilePath);
|
|
9625
|
-
|
|
10794
|
+
logger24.log(`Debug logs are written to: ${logFile}`);
|
|
9626
10795
|
} catch {
|
|
9627
10796
|
}
|
|
9628
|
-
|
|
10797
|
+
logger24.outro(""), process.exit(1);
|
|
9629
10798
|
}, command = (name) => program.command(name).option(
|
|
9630
10799
|
"--disable-telemetry",
|
|
9631
10800
|
"Disable sending telemetry data",
|
|
@@ -9635,34 +10804,30 @@ var handleCommandFailure = (logFilePath) => async (error) => {
|
|
|
9635
10804
|
"Write all debug logs to the specified file at the end of the run. Defaults to debug-storybook.log when [path] is not provided"
|
|
9636
10805
|
).option("--loglevel <trace | debug | info | warn | error | silent>", "Define log level", "info").hook("preAction", async (self2) => {
|
|
9637
10806
|
let options = self2.opts();
|
|
9638
|
-
options.debug &&
|
|
10807
|
+
options.debug && logger24.setLogLevel("debug"), options.loglevel && logger24.setLogLevel(options.loglevel), options.logfile && logTracker4.enableLogWriting();
|
|
9639
10808
|
try {
|
|
9640
10809
|
await globalSettings();
|
|
9641
10810
|
} catch (e) {
|
|
9642
|
-
|
|
10811
|
+
logger24.error(`Error loading global settings:
|
|
9643
10812
|
` + String(e));
|
|
9644
10813
|
}
|
|
9645
10814
|
}).hook("postAction", async (command2) => {
|
|
9646
10815
|
if (logTracker4.shouldWriteLogsToFile) {
|
|
9647
10816
|
try {
|
|
9648
10817
|
let logFile = await logTracker4.writeToFile(command2.getOptionValue("logfile"));
|
|
9649
|
-
|
|
10818
|
+
logger24.log(`Debug logs are written to: ${logFile}`);
|
|
9650
10819
|
} catch {
|
|
9651
10820
|
}
|
|
9652
|
-
|
|
10821
|
+
logger24.outro(CLI_COLORS5.success("Done!"));
|
|
9653
10822
|
}
|
|
9654
10823
|
});
|
|
9655
10824
|
command("init").description("Initialize Storybook into your project").option("-f --force", "Force add Storybook").option("-s --skip-install", "Skip installing deps").addOption(
|
|
9656
10825
|
new Option("--package-manager <type>", "Force package manager for installing deps").choices(
|
|
9657
10826
|
Object.values(PackageManagerName3)
|
|
9658
10827
|
)
|
|
9659
|
-
).option("--use-pnp", "Enable PnP mode for Yarn 2+").option("-p --parser <babel | babylon | flow | ts | tsx>", "jscodeshift parser").option("-t --type <type>", "Add Storybook for a specific project type").option("-y --yes", "Answer yes to all prompts").option("-b --builder <webpack5 | vite>", "Builder library").option("-l --linkable", "Prepare installation for link (contributor helper)").option(
|
|
9660
|
-
"--dev",
|
|
9661
|
-
"Launch the development server after completing initialization. Enabled by default (default: true)",
|
|
9662
|
-
!isCI2() && !optionalEnvToBoolean3(process.env.IN_STORYBOOK_SANDBOX)
|
|
9663
|
-
).option(
|
|
10828
|
+
).option("--use-pnp", "Enable PnP mode for Yarn 2+").option("-p --parser <babel | babylon | flow | ts | tsx>", "jscodeshift parser").option("-t --type <type>", "Add Storybook for a specific project type").option("-y --yes", "Answer yes to all prompts").option("-b --builder <webpack5 | vite>", "Builder library").option("-l --linkable", "Prepare installation for link (contributor helper)").option("--dev", "Launch the development server after completing initialization").option(
|
|
9664
10829
|
"--no-dev",
|
|
9665
|
-
"
|
|
10830
|
+
"Do not launch the Storybook development server after completing initialization (default)"
|
|
9666
10831
|
);
|
|
9667
10832
|
command("add <addon>").description("Add an addon to your Storybook").addOption(
|
|
9668
10833
|
new Option("--package-manager <type>", "Force package manager for installing deps").choices(
|
|
@@ -9670,7 +10835,7 @@ command("add <addon>").description("Add an addon to your Storybook").addOption(
|
|
|
9670
10835
|
)
|
|
9671
10836
|
).option("-c, --config-dir <dir-name>", "Directory where to load Storybook configurations from").option("--skip-install", "Skip installing deps").option("-s --skip-postinstall", "Skip package specific postinstall config modifications").option("-y --yes", "Skip prompting the user").option("--skip-doctor", "Skip doctor check").action((addonName, options) => {
|
|
9672
10837
|
withTelemetry("add", { cliOptions: options }, async () => {
|
|
9673
|
-
|
|
10838
|
+
logger24.intro(`Setting up your project for ${addonName}`), await add(addonName, options), await telemetry3("add", { addon: addonName, source: "cli" }), logger24.outro("Done!");
|
|
9674
10839
|
}).catch(handleCommandFailure);
|
|
9675
10840
|
});
|
|
9676
10841
|
command("remove <addon>").description("Remove an addon from your Storybook").addOption(
|
|
@@ -9679,7 +10844,7 @@ command("remove <addon>").description("Remove an addon from your Storybook").add
|
|
|
9679
10844
|
)
|
|
9680
10845
|
).option("-c, --config-dir <dir-name>", "Directory where to load Storybook configurations from").option("-s --skip-install", "Skip installing deps").action(
|
|
9681
10846
|
(addonName, options) => withTelemetry("remove", { cliOptions: options }, async () => {
|
|
9682
|
-
|
|
10847
|
+
logger24.intro(`Removing ${addonName} from your Storybook`);
|
|
9683
10848
|
let packageManager = JsPackageManagerFactory3.getPackageManager({
|
|
9684
10849
|
configDir: options.configDir,
|
|
9685
10850
|
force: options.packageManager
|
|
@@ -9688,7 +10853,7 @@ command("remove <addon>").description("Remove an addon from your Storybook").add
|
|
|
9688
10853
|
configDir: options.configDir,
|
|
9689
10854
|
packageManager,
|
|
9690
10855
|
skipInstall: options.skipInstall
|
|
9691
|
-
}), await
|
|
10856
|
+
}), await telemetry3("remove", { addon: addonName, source: "cli" }), logger24.outro("Done!");
|
|
9692
10857
|
}).catch(handleCommandFailure(options.logfile))
|
|
9693
10858
|
);
|
|
9694
10859
|
command("upgrade").description(`Upgrade your Storybook packages to v${versions5.storybook}`).addOption(
|
|
@@ -9696,6 +10861,9 @@ command("upgrade").description(`Upgrade your Storybook packages to v${versions5.
|
|
|
9696
10861
|
Object.values(PackageManagerName3)
|
|
9697
10862
|
)
|
|
9698
10863
|
).option("-y --yes", "Skip prompting the user").option("-f --force", "force the upgrade, skipping autoblockers").option("-n --dry-run", "Only check for upgrades, do not install").option("-s --skip-check", "Skip postinstall version and automigration checks").option(
|
|
10864
|
+
"--skip-automigrations",
|
|
10865
|
+
"Skip running automigrations entirely (only update package versions and install)"
|
|
10866
|
+
).option(
|
|
9699
10867
|
"-c, --config-dir <dir-name...>",
|
|
9700
10868
|
"Directory(ies) where to load Storybook configurations from"
|
|
9701
10869
|
).action(async (options) => {
|
|
@@ -9703,12 +10871,12 @@ command("upgrade").description(`Upgrade your Storybook packages to v${versions5.
|
|
|
9703
10871
|
"upgrade",
|
|
9704
10872
|
{ cliOptions: { ...options, configDir: options.configDir?.[0] } },
|
|
9705
10873
|
async () => {
|
|
9706
|
-
|
|
10874
|
+
logger24.intro(`Storybook upgrade - v${versions5.storybook}`), await upgrade(options), logger24.outro("Storybook upgrade completed!");
|
|
9707
10875
|
}
|
|
9708
10876
|
).catch(handleCommandFailure(options.logfile));
|
|
9709
10877
|
});
|
|
9710
10878
|
command("info").description("Prints debugging information about the local environment").action(async () => {
|
|
9711
|
-
|
|
10879
|
+
logger24.log(import_picocolors17.default.bold(`
|
|
9712
10880
|
Storybook Environment Info:`));
|
|
9713
10881
|
let activePackageManager = JsPackageManagerFactory3.getPackageManager().type.replace(/\d/, ""), output = await import_envinfo.default.run({
|
|
9714
10882
|
System: ["OS", "CPU", "Shell"],
|
|
@@ -9717,7 +10885,7 @@ Storybook Environment Info:`));
|
|
|
9717
10885
|
npmPackages: "{@storybook/*,*storybook*,sb,chromatic}",
|
|
9718
10886
|
npmGlobalPackages: "{@storybook/*,*storybook*,sb,chromatic}"
|
|
9719
10887
|
}), activePackageManagerLine = output.match(new RegExp(`${activePackageManager}:.*`, "i"));
|
|
9720
|
-
|
|
10888
|
+
logger24.log(
|
|
9721
10889
|
output.replace(
|
|
9722
10890
|
activePackageManagerLine,
|
|
9723
10891
|
import_picocolors17.default.bold(`${activePackageManagerLine} <----- active`)
|
|
@@ -9732,12 +10900,12 @@ command("migrate [migration]").description("Run a Storybook codemod migration on
|
|
|
9732
10900
|
'Rename suffix of matching files after codemod has been applied, e.g. ".js:.ts"'
|
|
9733
10901
|
).action((migration, options) => {
|
|
9734
10902
|
withTelemetry("migrate", { cliOptions: options }, async () => {
|
|
9735
|
-
|
|
10903
|
+
logger24.intro(`Running ${migration} migration`), await migrate(migration, options), logger24.outro("Migration completed");
|
|
9736
10904
|
}).catch(handleCommandFailure(options.logfile));
|
|
9737
10905
|
});
|
|
9738
10906
|
command("sandbox [filterValue]").alias("repro").description("Create a sandbox from a set of possible templates").option("-o --output <outDir>", "Define an output directory").option("--no-init", "Whether to download a template without an initialized Storybook", !1).action((filterValue, options) => {
|
|
9739
|
-
|
|
9740
|
-
|
|
10907
|
+
logger24.intro("Creating a Storybook sandbox..."), sandbox({ filterValue, ...options }).catch(handleCommandFailure).finally(() => {
|
|
10908
|
+
logger24.outro("Done!");
|
|
9741
10909
|
});
|
|
9742
10910
|
});
|
|
9743
10911
|
command("link <repo-url-or-directory>").description("Pull down a repro from a URL (or a local directory), link it, and run storybook").option("--local", "Link a local directory already in your file system").option("--no-start", "Start the storybook", !0).action(
|
|
@@ -9752,7 +10920,7 @@ command("automigrate [fixId]").description("Check storybook for incompatibilitie
|
|
|
9752
10920
|
"The renderer package for the framework Storybook is using."
|
|
9753
10921
|
).option("--skip-doctor", "Skip doctor check").option("--glob <pattern>", "Glob pattern for story files (for csf-factories codemod)").action(async (fixId, options) => {
|
|
9754
10922
|
withTelemetry("automigrate", { cliOptions: options }, async () => {
|
|
9755
|
-
|
|
10923
|
+
logger24.intro(fixId ? `Running ${fixId} automigration` : "Running automigrations"), await doAutomigrate({ fixId, ...options }), logger24.outro("Done");
|
|
9756
10924
|
}).catch(handleCommandFailure(options.logfile));
|
|
9757
10925
|
});
|
|
9758
10926
|
command("doctor").description("Check Storybook for known problems and provide suggestions or fixes").addOption(
|
|
@@ -9761,13 +10929,30 @@ command("doctor").description("Check Storybook for known problems and provide su
|
|
|
9761
10929
|
)
|
|
9762
10930
|
).option("-c, --config-dir <dir-name>", "Directory of Storybook configuration").action(async (options) => {
|
|
9763
10931
|
withTelemetry("doctor", { cliOptions: options }, async () => {
|
|
9764
|
-
|
|
10932
|
+
logger24.intro("Doctoring Storybook"), await doctor(options), logger24.outro("Done");
|
|
9765
10933
|
}).catch(handleCommandFailure(options.logfile));
|
|
9766
10934
|
});
|
|
10935
|
+
var aiCommand = command("ai").description("AI agent helpers for Storybook").option(
|
|
10936
|
+
"-o, --output <path>",
|
|
10937
|
+
"Write the prompt output to a file instead of printing it to stdout"
|
|
10938
|
+
);
|
|
10939
|
+
aiCommand.command("setup").description("Generate setup instructions to write stories for real components").addOption(
|
|
10940
|
+
new Option("--package-manager <type>", "Force package manager for installing deps").choices(
|
|
10941
|
+
Object.values(PackageManagerName3)
|
|
10942
|
+
)
|
|
10943
|
+
).option("-c, --config-dir <dir-name>", "Directory of Storybook configuration").action(async (options, cmd) => {
|
|
10944
|
+
let mergedOptions = { ...cmd.parent?.opts() ?? {}, ...options };
|
|
10945
|
+
await withTelemetry("ai-setup", { cliOptions: mergedOptions }, async () => {
|
|
10946
|
+
await aiSetup(mergedOptions);
|
|
10947
|
+
}).catch(handleCommandFailure(mergedOptions.logfile));
|
|
10948
|
+
});
|
|
10949
|
+
aiCommand.action(() => {
|
|
10950
|
+
aiCommand.outputHelp();
|
|
10951
|
+
});
|
|
9767
10952
|
program.on("command:*", ([invalidCmd]) => {
|
|
9768
10953
|
let errorMessage = ` Invalid command: ${import_picocolors17.default.bold(invalidCmd)}.
|
|
9769
10954
|
See --help for a list of available commands.`, suggestion = program.commands.map((cmd) => cmd.name()).find((cmd) => leven(cmd, invalidCmd) < 3);
|
|
9770
10955
|
suggestion && (errorMessage += `
|
|
9771
|
-
Did you mean ${import_picocolors17.default.yellow(suggestion)}?`),
|
|
10956
|
+
Did you mean ${import_picocolors17.default.yellow(suggestion)}?`), logger24.error(errorMessage), process.exit(1);
|
|
9772
10957
|
});
|
|
9773
10958
|
program.usage("<command> [options]").version(String(version)).parse(process.argv);
|