@storybook/addon-vitest 0.0.0-pr-32921-sha-12ed7665 → 0.0.0-pr-32874-sha-fd398904

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.
@@ -1,29 +1,33 @@
1
- import CJS_COMPAT_NODE_URL_juf8p1r8sqm from 'node:url';
2
- import CJS_COMPAT_NODE_PATH_juf8p1r8sqm from 'node:path';
3
- import CJS_COMPAT_NODE_MODULE_juf8p1r8sqm from "node:module";
1
+ import CJS_COMPAT_NODE_URL_x7l8djn52xj from 'node:url';
2
+ import CJS_COMPAT_NODE_PATH_x7l8djn52xj from 'node:path';
3
+ import CJS_COMPAT_NODE_MODULE_x7l8djn52xj from "node:module";
4
4
 
5
- var __filename = CJS_COMPAT_NODE_URL_juf8p1r8sqm.fileURLToPath(import.meta.url);
6
- var __dirname = CJS_COMPAT_NODE_PATH_juf8p1r8sqm.dirname(__filename);
7
- var require = CJS_COMPAT_NODE_MODULE_juf8p1r8sqm.createRequire(import.meta.url);
5
+ var __filename = CJS_COMPAT_NODE_URL_x7l8djn52xj.fileURLToPath(import.meta.url);
6
+ var __dirname = CJS_COMPAT_NODE_PATH_x7l8djn52xj.dirname(__filename);
7
+ var require = CJS_COMPAT_NODE_MODULE_x7l8djn52xj.createRequire(import.meta.url);
8
8
 
9
9
  // ------------------------------------------------------------
10
10
  // end of CJS compatibility banner, injected by Storybook's esbuild configuration
11
11
  // ------------------------------------------------------------
12
12
  import {
13
+ execa,
13
14
  resolvePackageDir
14
- } from "./_node-chunks/chunk-DKHTLFGI.js";
15
+ } from "./_node-chunks/chunk-75RHCDJA.js";
15
16
  import {
16
- any
17
- } from "./_node-chunks/chunk-UM4KUWJO.js";
17
+ any,
18
+ getAddonNames,
19
+ up
20
+ } from "./_node-chunks/chunk-ATWWQSYY.js";
18
21
  import {
19
- DOCUMENTATION_LINK
20
- } from "./_node-chunks/chunk-4NV37VMK.js";
22
+ DOCUMENTATION_LINK,
23
+ SUPPORTED_FRAMEWORKS
24
+ } from "./_node-chunks/chunk-5EGXS55U.js";
21
25
  import {
22
26
  dirname,
23
27
  join,
24
28
  relative,
25
29
  resolve
26
- } from "./_node-chunks/chunk-YDCELX4B.js";
30
+ } from "./_node-chunks/chunk-ZIYTWRWN.js";
27
31
  import {
28
32
  require_compare,
29
33
  require_constants,
@@ -33,12 +37,12 @@ import {
33
37
  require_parse_options,
34
38
  require_re,
35
39
  require_semver
36
- } from "./_node-chunks/chunk-EKF5O6IB.js";
40
+ } from "./_node-chunks/chunk-44SFLJBP.js";
37
41
  import {
38
42
  __commonJS,
39
43
  __name,
40
44
  __toESM
41
- } from "./_node-chunks/chunk-BXS7J2KF.js";
45
+ } from "./_node-chunks/chunk-LB44Z253.js";
42
46
 
43
47
  // ../../node_modules/semver/functions/parse.js
44
48
  var require_parse = __commonJS({
@@ -360,7 +364,7 @@ var require_coerce = __commonJS({
360
364
  var SemVer = require_semver();
361
365
  var parse = require_parse();
362
366
  var { safeRe: re, t } = require_re();
363
- var coerce = /* @__PURE__ */ __name((version, options) => {
367
+ var coerce2 = /* @__PURE__ */ __name((version, options) => {
364
368
  if (version instanceof SemVer) {
365
369
  return version;
366
370
  }
@@ -395,7 +399,7 @@ var require_coerce = __commonJS({
395
399
  const build = options.includePrerelease && match[6] ? `+${match[6]}` : "";
396
400
  return parse(`${major}.${minor}.${patch}${prerelease}${build}`, options);
397
401
  }, "coerce");
398
- module.exports = coerce;
402
+ module.exports = coerce2;
399
403
  }
400
404
  });
401
405
 
@@ -1440,7 +1444,7 @@ var require_semver2 = __commonJS({
1440
1444
  var gte = require_gte();
1441
1445
  var lte = require_lte();
1442
1446
  var cmp = require_cmp();
1443
- var coerce = require_coerce();
1447
+ var coerce2 = require_coerce();
1444
1448
  var Comparator = require_comparator();
1445
1449
  var Range = require_range();
1446
1450
  var satisfies2 = require_satisfies();
@@ -1478,7 +1482,7 @@ var require_semver2 = __commonJS({
1478
1482
  gte,
1479
1483
  lte,
1480
1484
  cmp,
1481
- coerce,
1485
+ coerce: coerce2,
1482
1486
  Comparator,
1483
1487
  Range,
1484
1488
  satisfies: satisfies2,
@@ -1509,23 +1513,56 @@ var require_semver2 = __commonJS({
1509
1513
  import { existsSync } from "node:fs";
1510
1514
  import * as fs2 from "node:fs/promises";
1511
1515
  import { writeFile } from "node:fs/promises";
1512
- import { babelParse, generate } from "storybook/internal/babel";
1513
- import { AddonVitestService } from "storybook/internal/cli";
1516
+ import { isAbsolute, posix, sep } from "node:path";
1517
+ import { fileURLToPath, pathToFileURL } from "node:url";
1518
+ import { babelParse, generate, traverse } from "storybook/internal/babel";
1514
1519
  import {
1515
1520
  JsPackageManagerFactory,
1516
1521
  formatFileContent,
1522
+ getInterpretedFile,
1517
1523
  getProjectRoot,
1518
- getStorybookInfo
1524
+ isCI as isCI2,
1525
+ loadMainConfig,
1526
+ scanAndTransformFiles,
1527
+ transformImportFiles
1519
1528
  } from "storybook/internal/common";
1520
- import { CLI_COLORS } from "storybook/internal/node-logger";
1521
- import {
1522
- AddonVitestPostinstallError,
1523
- AddonVitestPostinstallPrerequisiteCheckError
1524
- } from "storybook/internal/server-errors";
1525
- import { SupportedFramework } from "storybook/internal/types";
1529
+ import { experimental_loadStorybook } from "storybook/internal/core-server";
1530
+ import { readConfig, writeConfig } from "storybook/internal/csf-tools";
1531
+ import { logger as logger2 } from "storybook/internal/node-logger";
1532
+
1533
+ // ../../node_modules/empathic/package.mjs
1534
+ function up2(options) {
1535
+ return up("package.json", options);
1536
+ }
1537
+ __name(up2, "up");
1538
+
1539
+ // src/postinstall.ts
1526
1540
  var import_semver = __toESM(require_semver2(), 1);
1541
+ import prompts from "prompts";
1527
1542
  import { dedent } from "ts-dedent";
1528
1543
 
1544
+ // src/postinstall-logger.ts
1545
+ import { isCI } from "storybook/internal/common";
1546
+ import { colors, logger } from "storybook/internal/node-logger";
1547
+ var fancy = process.platform !== "win32" || isCI() || process.env.TERM === "xterm-256color";
1548
+ var step = colors.gray("\u203A");
1549
+ var info = colors.blue(fancy ? "\u2139" : "i");
1550
+ var success = colors.green(fancy ? "\u2714" : "\u221A");
1551
+ var warning = colors.orange(fancy ? "\u26A0" : "\u203C");
1552
+ var error = colors.red(fancy ? "\u2716" : "\xD7");
1553
+ var baseOptions = {
1554
+ borderStyle: "round",
1555
+ padding: 1
1556
+ };
1557
+ var print = /* @__PURE__ */ __name((message, options) => {
1558
+ logger.line(1);
1559
+ logger.logBox(message, { ...baseOptions, ...options });
1560
+ }, "print");
1561
+ var printInfo = /* @__PURE__ */ __name((title, message, options) => print(message, { borderColor: "blue", title, ...options }), "printInfo");
1562
+ var printWarning = /* @__PURE__ */ __name((title, message, options) => print(message, { borderColor: "yellow", title, ...options }), "printWarning");
1563
+ var printError = /* @__PURE__ */ __name((title, message, options) => print(message, { borderColor: "red", title, ...options }), "printError");
1564
+ var printSuccess = /* @__PURE__ */ __name((title, message, options) => print(message, { borderColor: "green", title, ...options }), "printSuccess");
1565
+
1529
1566
  // src/updateVitestFile.ts
1530
1567
  import * as fs from "node:fs/promises";
1531
1568
  var loadTemplate = /* @__PURE__ */ __name(async (name, replacements) => {
@@ -1579,7 +1616,10 @@ var updateConfigFile = /* @__PURE__ */ __name((source, target) => {
1579
1616
  } else if (targetExportDefault.declaration.type === "CallExpression" && targetExportDefault.declaration.callee.type === "Identifier" && targetExportDefault.declaration.callee.name === "defineConfig" && targetExportDefault.declaration.arguments[0]?.type === "ObjectExpression") {
1580
1617
  canHandleConfig = true;
1581
1618
  } else if (targetExportDefault.declaration.type === "CallExpression" && targetExportDefault.declaration.callee.type === "Identifier" && targetExportDefault.declaration.callee.name === "mergeConfig" && targetExportDefault.declaration.arguments.length >= 2) {
1582
- canHandleConfig = true;
1619
+ const defineConfigNodes = targetExportDefault.declaration.arguments.filter(
1620
+ (arg) => arg?.type === "CallExpression" && arg.callee.type === "Identifier" && arg.callee.name === "defineConfig" && arg.arguments[0]?.type === "ObjectExpression"
1621
+ );
1622
+ canHandleConfig = defineConfigNodes.length > 0;
1583
1623
  }
1584
1624
  if (!canHandleConfig) {
1585
1625
  return false;
@@ -1612,24 +1652,20 @@ var updateConfigFile = /* @__PURE__ */ __name((source, target) => {
1612
1652
  mergeProperties(properties, exportDefault.declaration.arguments[0].properties);
1613
1653
  updated = true;
1614
1654
  } else if (exportDefault.declaration.type === "CallExpression" && exportDefault.declaration.callee.type === "Identifier" && exportDefault.declaration.callee.name === "mergeConfig" && exportDefault.declaration.arguments.length >= 2) {
1615
- const configObjectNodes = [];
1616
- for (const arg of exportDefault.declaration.arguments) {
1617
- if (arg?.type === "CallExpression" && arg.callee.type === "Identifier" && arg.callee.name === "defineConfig" && arg.arguments[0]?.type === "ObjectExpression") {
1618
- configObjectNodes.push(arg.arguments[0]);
1619
- } else if (arg?.type === "ObjectExpression") {
1620
- configObjectNodes.push(arg);
1621
- }
1622
- }
1623
- const configObjectWithTest = configObjectNodes.find(
1624
- (obj) => obj.properties.some(
1655
+ const defineConfigNodes = exportDefault.declaration.arguments.filter(
1656
+ (arg) => arg?.type === "CallExpression" && arg.callee.type === "Identifier" && arg.callee.name === "defineConfig" && arg.arguments[0]?.type === "ObjectExpression"
1657
+ );
1658
+ const defineConfigNodeWithTest = defineConfigNodes.find(
1659
+ (node) => node.arguments[0].type === "ObjectExpression" && node.arguments[0].properties.some(
1625
1660
  (p) => p.type === "ObjectProperty" && p.key.type === "Identifier" && p.key.name === "test"
1626
1661
  )
1627
1662
  );
1628
- const targetConfigObject = configObjectWithTest || configObjectNodes[0];
1629
- if (!targetConfigObject) {
1663
+ const defineConfigNode = defineConfigNodeWithTest || defineConfigNodes[0];
1664
+ if (!defineConfigNode) {
1630
1665
  return false;
1631
1666
  }
1632
- const existingTestProp = targetConfigObject.properties.find(
1667
+ const defineConfigProps = defineConfigNode.arguments[0];
1668
+ const existingTestProp = defineConfigProps.properties.find(
1633
1669
  (p) => p.type === "ObjectProperty" && p.key.type === "Identifier" && p.key.name === "test"
1634
1670
  );
1635
1671
  if (existingTestProp && existingTestProp.value.type === "ObjectExpression") {
@@ -1641,16 +1677,6 @@ var updateConfigFile = /* @__PURE__ */ __name((source, target) => {
1641
1677
  (p) => p.type === "ObjectProperty" && p.key.type === "Identifier" && (p.key.name === "workspace" || p.key.name === "projects")
1642
1678
  );
1643
1679
  if (workspaceOrProjectsProp && workspaceOrProjectsProp.value.type === "ArrayExpression") {
1644
- const coverageProp = existingTestProp.value.properties.find(
1645
- (p) => p.type === "ObjectProperty" && p.key.type === "Identifier" && p.key.name === "coverage"
1646
- );
1647
- const testPropsWithoutCoverage = existingTestProp.value.properties.filter(
1648
- (p) => p !== coverageProp
1649
- );
1650
- const testConfigForProject = {
1651
- type: "ObjectExpression",
1652
- properties: testPropsWithoutCoverage
1653
- };
1654
1680
  const existingTestProject = {
1655
1681
  type: "ObjectExpression",
1656
1682
  properties: [
@@ -1664,28 +1690,25 @@ var updateConfigFile = /* @__PURE__ */ __name((source, target) => {
1664
1690
  {
1665
1691
  type: "ObjectProperty",
1666
1692
  key: { type: "Identifier", name: "test" },
1667
- value: testConfigForProject,
1693
+ value: existingTestProp.value,
1668
1694
  computed: false,
1669
1695
  shorthand: false
1670
1696
  }
1671
1697
  ]
1672
1698
  };
1673
1699
  workspaceOrProjectsProp.value.elements.unshift(existingTestProject);
1674
- targetConfigObject.properties = targetConfigObject.properties.filter(
1700
+ defineConfigProps.properties = defineConfigProps.properties.filter(
1675
1701
  (p) => p !== existingTestProp
1676
1702
  );
1677
- if (coverageProp && templateTestProp.value.type === "ObjectExpression") {
1678
- templateTestProp.value.properties.unshift(coverageProp);
1679
- }
1680
- mergeProperties(properties, targetConfigObject.properties);
1703
+ mergeProperties(properties, defineConfigProps.properties);
1681
1704
  } else {
1682
- mergeProperties(properties, targetConfigObject.properties);
1705
+ mergeProperties(properties, defineConfigProps.properties);
1683
1706
  }
1684
1707
  } else {
1685
- mergeProperties(properties, targetConfigObject.properties);
1708
+ mergeProperties(properties, defineConfigProps.properties);
1686
1709
  }
1687
1710
  } else {
1688
- mergeProperties(properties, targetConfigObject.properties);
1711
+ mergeProperties(properties, defineConfigProps.properties);
1689
1712
  }
1690
1713
  updated = true;
1691
1714
  }
@@ -1734,156 +1757,290 @@ var updateWorkspaceFile = /* @__PURE__ */ __name((source, target) => {
1734
1757
  var ADDON_NAME = "@storybook/addon-vitest";
1735
1758
  var EXTENSIONS = [".ts", ".tsx", ".js", ".jsx", ".cts", ".mts", ".cjs", ".mjs"];
1736
1759
  var addonA11yName = "@storybook/addon-a11y";
1760
+ var hasErrors = false;
1761
+ function nameMatches(name, pattern) {
1762
+ if (name === pattern) {
1763
+ return true;
1764
+ }
1765
+ if (name.includes(`${pattern}${sep}`)) {
1766
+ return true;
1767
+ }
1768
+ if (name.includes(`${pattern}${posix.sep}`)) {
1769
+ return true;
1770
+ }
1771
+ return false;
1772
+ }
1773
+ __name(nameMatches, "nameMatches");
1774
+ var logErrors = /* @__PURE__ */ __name((...args) => {
1775
+ hasErrors = true;
1776
+ printError(...args);
1777
+ }, "logErrors");
1778
+ var findFile = /* @__PURE__ */ __name((basename, extensions = EXTENSIONS) => any(
1779
+ extensions.map((ext) => basename + ext),
1780
+ { last: getProjectRoot() }
1781
+ ), "findFile");
1737
1782
  async function postInstall(options) {
1738
- const errors = [];
1739
- const { logger, prompt } = options;
1783
+ printSuccess(
1784
+ "\u{1F44B} Howdy!",
1785
+ dedent`
1786
+ I'm the installation helper for ${ADDON_NAME}
1787
+
1788
+ Hold on for a moment while I look at your project and get it set up...
1789
+ `
1790
+ );
1740
1791
  const packageManager = JsPackageManagerFactory.getPackageManager({
1741
1792
  force: options.packageManager
1742
1793
  });
1743
- const findFile = /* @__PURE__ */ __name((basename, extensions = EXTENSIONS) => any(
1744
- extensions.map((ext) => basename + ext),
1745
- { last: getProjectRoot(), cwd: options.configDir }
1746
- ), "findFile");
1747
- const vitestVersionSpecifier = await packageManager.getInstalledVersion("vitest");
1748
- const isVitest3_2To4 = vitestVersionSpecifier ? (0, import_semver.satisfies)(vitestVersionSpecifier, ">=3.2.0 <4.0.0") : false;
1749
- const isVitest4OrNewer = vitestVersionSpecifier ? (0, import_semver.satisfies)(vitestVersionSpecifier, ">=4.0.0") : true;
1750
- const info = await getStorybookInfo(options.configDir);
1794
+ const info2 = await getStorybookInfo(options);
1751
1795
  const allDeps = packageManager.getAllDependencies();
1752
- const addonVitestService = new AddonVitestService();
1753
- const compatibilityResult = await addonVitestService.validateCompatibility({
1754
- packageManager,
1755
- framework: info.framework,
1756
- builder: info.builder
1757
- });
1758
- let result = null;
1759
- if (!compatibilityResult.compatible && compatibilityResult.reasons) {
1760
- const reasons = compatibilityResult.reasons.map((r) => `\u2022 ${CLI_COLORS.error(r)}`);
1761
- reasons.unshift(dedent`
1762
- Automated setup failed
1763
- We have found incompatibilities due to the following package incompatibilities:
1764
- `);
1765
- reasons.push(
1766
- dedent`
1767
- You can fix these issues and rerun the command to reinstall. If you wish to roll back the installation, remove ${ADDON_NAME} from the "addons" array
1768
- in your main Storybook config file and remove the dependency from your package.json file.
1796
+ const dependencies = ["vitest", "@vitest/browser", "playwright"].filter((p) => !allDeps[p]);
1797
+ const vitestVersionSpecifier = await packageManager.getInstalledVersion("vitest");
1798
+ const coercedVitestVersion = vitestVersionSpecifier ? (0, import_semver.coerce)(vitestVersionSpecifier) : null;
1799
+ const isVitest3_2OrNewer = vitestVersionSpecifier ? (0, import_semver.satisfies)(vitestVersionSpecifier, ">=3.2.0") : true;
1800
+ const mainJsPath = getInterpretedFile(resolve(options.configDir, "main"));
1801
+ const config = await readConfig(mainJsPath);
1802
+ const hasCustomWebpackConfig = !!config.getFieldNode(["webpackFinal"]);
1803
+ const isInteractive = process.stdout.isTTY && !isCI2();
1804
+ if (nameMatches(info2.frameworkPackageName, "@storybook/nextjs") && !hasCustomWebpackConfig) {
1805
+ const out = options.yes || !isInteractive ? { migrateToNextjsVite: !!options.yes } : await prompts({
1806
+ type: "confirm",
1807
+ name: "migrateToNextjsVite",
1808
+ message: dedent`
1809
+ The addon requires the use of @storybook/nextjs-vite to work with Next.js.
1810
+ https://storybook.js.org/docs/next/${DOCUMENTATION_LINK}#install-and-set-up
1769
1811
 
1770
- Please check the documentation for more information about its requirements and installation:
1771
- https://storybook.js.org/docs/next/${DOCUMENTATION_LINK}
1772
- `
1773
- );
1774
- result = reasons.map((r) => r.trim()).join("\n\n");
1775
- }
1776
- if (result) {
1777
- logger.error(result);
1778
- throw new AddonVitestPostinstallPrerequisiteCheckError({
1779
- reasons: compatibilityResult.reasons
1812
+ Do you want to migrate?
1813
+ `,
1814
+ initial: true
1780
1815
  });
1816
+ if (out.migrateToNextjsVite) {
1817
+ await packageManager.addDependencies({ type: "devDependencies", skipInstall: true }, [
1818
+ "@storybook/nextjs-vite"
1819
+ ]);
1820
+ await packageManager.removeDependencies(["@storybook/nextjs"]);
1821
+ traverse(config._ast, {
1822
+ StringLiteral(path) {
1823
+ if (path.node.value === "@storybook/nextjs") {
1824
+ path.node.value = "@storybook/nextjs-vite";
1825
+ }
1826
+ }
1827
+ });
1828
+ await writeConfig(config, mainJsPath);
1829
+ info2.frameworkPackageName = "@storybook/nextjs-vite";
1830
+ info2.builderPackageName = "@storybook/builder-vite";
1831
+ await scanAndTransformFiles({
1832
+ promptMessage: "Enter a glob to scan for all @storybook/nextjs imports to substitute with @storybook/nextjs-vite:",
1833
+ force: options.yes,
1834
+ dryRun: false,
1835
+ transformFn: /* @__PURE__ */ __name((files, options2, dryRun) => transformImportFiles(files, options2, dryRun), "transformFn"),
1836
+ transformOptions: {
1837
+ "@storybook/nextjs": "@storybook/nextjs-vite"
1838
+ }
1839
+ });
1840
+ }
1781
1841
  }
1782
- if (!options.skipDependencyManagement) {
1783
- const versionedDependencies = await addonVitestService.collectDependencies(packageManager);
1784
- if (info.framework === SupportedFramework.NEXTJS) {
1785
- const allDeps2 = packageManager.getAllDependencies();
1786
- if (!allDeps2["@storybook/nextjs-vite"]) {
1842
+ const annotationsImport = SUPPORTED_FRAMEWORKS.find(
1843
+ (f) => nameMatches(info2.frameworkPackageName, f)
1844
+ ) ? info2.frameworkPackageName === "@storybook/nextjs" ? "@storybook/nextjs-vite" : info2.frameworkPackageName : null;
1845
+ const isRendererSupported = !!annotationsImport;
1846
+ const prerequisiteCheck = /* @__PURE__ */ __name(async () => {
1847
+ const reasons = [];
1848
+ if (hasCustomWebpackConfig) {
1849
+ reasons.push("\u2022 The addon can not be used with a custom Webpack configuration.");
1850
+ }
1851
+ if (!nameMatches(info2.frameworkPackageName, "@storybook/nextjs") && !nameMatches(info2.builderPackageName, "@storybook/builder-vite")) {
1852
+ reasons.push(
1853
+ "\u2022 The addon can only be used with a Vite-based Storybook framework or Next.js."
1854
+ );
1855
+ }
1856
+ if (!isRendererSupported) {
1857
+ reasons.push(dedent`
1858
+ • The addon cannot yet be used with ${info2.frameworkPackageName}
1859
+ `);
1860
+ }
1861
+ if (coercedVitestVersion && !(0, import_semver.satisfies)(coercedVitestVersion, ">=3.0.0")) {
1862
+ reasons.push(dedent`
1863
+ • The addon requires Vitest 3.0.0 or higher. You are currently using ${vitestVersionSpecifier}.
1864
+ Please update all of your Vitest dependencies and try again.
1865
+ `);
1866
+ }
1867
+ const mswVersionSpecifier = await packageManager.getInstalledVersion("msw");
1868
+ const coercedMswVersion = mswVersionSpecifier ? (0, import_semver.coerce)(mswVersionSpecifier) : null;
1869
+ if (coercedMswVersion && !(0, import_semver.satisfies)(coercedMswVersion, ">=2.0.0")) {
1870
+ reasons.push(dedent`
1871
+ • The addon uses Vitest behind the scenes, which supports only version 2 and above of MSW. However, we have detected version ${coercedMswVersion.version} in this project.
1872
+ Please update the 'msw' package and try again.
1873
+ `);
1874
+ }
1875
+ if (nameMatches(info2.frameworkPackageName, "@storybook/nextjs")) {
1876
+ const nextVersion = await packageManager.getInstalledVersion("next");
1877
+ if (!nextVersion) {
1878
+ reasons.push(dedent`
1879
+ • You are using @storybook/nextjs without having "next" installed.
1880
+ Please install "next" or use a different Storybook framework integration and try again.
1881
+ `);
1787
1882
  }
1788
1883
  }
1789
- const v8Version = await packageManager.getInstalledVersion("@vitest/coverage-v8");
1790
- const istanbulVersion = await packageManager.getInstalledVersion("@vitest/coverage-istanbul");
1791
- if (!v8Version && !istanbulVersion) {
1792
- logger.step(
1884
+ if (reasons.length > 0) {
1885
+ reasons.unshift(
1886
+ `@storybook/addon-vitest's automated setup failed due to the following package incompatibilities:`
1887
+ );
1888
+ reasons.push("--------------------------------");
1889
+ reasons.push(
1793
1890
  dedent`
1794
- You don't seem to have a coverage reporter installed. Vitest needs either V8 or Istanbul to generate coverage reports.
1795
-
1796
- Adding "@vitest/coverage-v8" to enable coverage reporting.
1797
- Read more about Vitest coverage providers at https://vitest.dev/guide/coverage.html#coverage-providers
1891
+ You can fix these issues and rerun the command to reinstall. If you wish to roll back the installation, remove ${ADDON_NAME} from the "addons" array
1892
+ in your main Storybook config file and remove the dependency from your package.json file.
1798
1893
  `
1799
1894
  );
1895
+ if (!isRendererSupported) {
1896
+ reasons.push(
1897
+ dedent`
1898
+ Please check the documentation for more information about its requirements and installation:
1899
+ https://storybook.js.org/docs/next/${DOCUMENTATION_LINK}
1900
+ `
1901
+ );
1902
+ } else {
1903
+ reasons.push(
1904
+ dedent`
1905
+ Fear not, however, you can follow the manual installation process instead at:
1906
+ https://storybook.js.org/docs/next/${DOCUMENTATION_LINK}#manual-setup
1907
+ `
1908
+ );
1909
+ }
1910
+ return reasons.map((r) => r.trim()).join("\n\n");
1800
1911
  }
1801
- if (versionedDependencies.length > 0) {
1802
- logger.step("Adding dependencies to your package.json");
1803
- logger.log(" " + versionedDependencies.join(", "));
1804
- await packageManager.addDependencies(
1805
- { type: "devDependencies", skipInstall: true },
1806
- versionedDependencies
1807
- );
1912
+ return null;
1913
+ }, "prerequisiteCheck");
1914
+ const result = await prerequisiteCheck();
1915
+ if (result) {
1916
+ logErrors("\u26D4\uFE0F Sorry!", result);
1917
+ logger2.line(1);
1918
+ return;
1919
+ }
1920
+ if (info2.frameworkPackageName === "@storybook/nextjs") {
1921
+ printInfo(
1922
+ "\u{1F37F} Just so you know...",
1923
+ dedent`
1924
+ It looks like you're using Next.js.
1925
+
1926
+ Adding "@storybook/nextjs-vite/vite-plugin" so you can use it with Vitest.
1927
+
1928
+ More info about the plugin at https://github.com/storybookjs/vite-plugin-storybook-nextjs
1929
+ `
1930
+ );
1931
+ try {
1932
+ const storybookVersion = await packageManager.getInstalledVersion("storybook");
1933
+ dependencies.push(`@storybook/nextjs-vite@^${storybookVersion}`);
1934
+ } catch (e) {
1935
+ console.error("Failed to install @storybook/nextjs-vite. Please install it manually");
1808
1936
  }
1809
- if (!options.skipInstall) {
1810
- await packageManager.installDependencies();
1937
+ }
1938
+ const v8Version = await packageManager.getInstalledVersion("@vitest/coverage-v8");
1939
+ const istanbulVersion = await packageManager.getInstalledVersion("@vitest/coverage-istanbul");
1940
+ if (!v8Version && !istanbulVersion) {
1941
+ printInfo(
1942
+ "\u{1F648} Let me cover this for you",
1943
+ dedent`
1944
+ You don't seem to have a coverage reporter installed. Vitest needs either V8 or Istanbul to generate coverage reports.
1945
+
1946
+ Adding "@vitest/coverage-v8" to enable coverage reporting.
1947
+ Read more about Vitest coverage providers at https://vitest.dev/guide/coverage.html#coverage-providers
1948
+ `
1949
+ );
1950
+ dependencies.push(`@vitest/coverage-v8`);
1951
+ }
1952
+ const versionedDependencies = dependencies.map((p) => {
1953
+ if (p.includes("vitest")) {
1954
+ return vitestVersionSpecifier ? `${p}@${vitestVersionSpecifier}` : p;
1811
1955
  }
1956
+ return p;
1957
+ });
1958
+ if (versionedDependencies.length > 0) {
1959
+ await packageManager.addDependencies(
1960
+ { type: "devDependencies", skipInstall: true },
1961
+ versionedDependencies
1962
+ );
1963
+ logger2.line(1);
1964
+ logger2.plain(`${step} Installing dependencies:`);
1965
+ logger2.plain(" " + versionedDependencies.join(", "));
1812
1966
  }
1813
- if (!options.skipDependencyManagement) {
1814
- if (!options.skipInstall) {
1815
- const playwrightErrors = await addonVitestService.installPlaywright(packageManager, {
1816
- yes: options.yes
1967
+ await packageManager.installDependencies();
1968
+ logger2.line(1);
1969
+ if (options.skipInstall) {
1970
+ logger2.plain("Skipping Playwright installation, please run this command manually:");
1971
+ logger2.plain(" npx playwright install chromium --with-deps");
1972
+ } else {
1973
+ logger2.plain(`${step} Configuring Playwright with Chromium (this might take some time):`);
1974
+ logger2.plain(" npx playwright install chromium --with-deps");
1975
+ try {
1976
+ await packageManager.executeCommand({
1977
+ command: "npx",
1978
+ args: ["playwright", "install", "chromium", "--with-deps"]
1817
1979
  });
1818
- errors.push(...playwrightErrors);
1819
- } else {
1820
- logger.warn(dedent`
1821
- Playwright browser binaries installation skipped. Please run the following command manually later:
1822
- ${CLI_COLORS.cta("npx playwright install chromium --with-deps")}
1823
- `);
1980
+ } catch (e) {
1981
+ console.error("Failed to install Playwright. Please install it manually");
1824
1982
  }
1825
1983
  }
1826
1984
  const fileExtension = allDeps.typescript || findFile("tsconfig", [...EXTENSIONS, ".json"]) ? "ts" : "js";
1827
1985
  const vitestSetupFile = resolve(options.configDir, `vitest.setup.${fileExtension}`);
1828
1986
  if (existsSync(vitestSetupFile)) {
1829
- const errorMessage = dedent`
1830
- Found an existing Vitest setup file:
1831
- ${vitestSetupFile}
1832
- Please refer to the documentation to complete the setup manually:
1833
- https://storybook.js.org/docs/next/${DOCUMENTATION_LINK}#manual-setup
1834
- `;
1835
- logger.line();
1836
- logger.error(`${errorMessage}
1837
- `);
1838
- errors.push("Found existing Vitest setup file");
1839
- } else {
1840
- logger.step(`Creating a Vitest setup file for Storybook:`);
1841
- logger.log(`${vitestSetupFile}
1842
- `);
1843
- const previewExists = EXTENSIONS.map((ext) => resolve(options.configDir, `preview${ext}`)).some(
1844
- existsSync
1845
- );
1846
- const annotationsImport = info.frameworkPackage;
1847
- const imports = [`import { setProjectAnnotations } from '${annotationsImport}';`];
1848
- const projectAnnotations = [];
1849
- if (previewExists) {
1850
- imports.push(`import * as projectAnnotations from './preview';`);
1851
- projectAnnotations.push("projectAnnotations");
1852
- }
1853
- await writeFile(
1854
- vitestSetupFile,
1987
+ logErrors(
1988
+ "\u{1F6A8} Oh no!",
1855
1989
  dedent`
1990
+ Found an existing Vitest setup file:
1991
+ ${vitestSetupFile}
1992
+
1993
+ Please refer to the documentation to complete the setup manually:
1994
+ https://storybook.js.org/docs/next/${DOCUMENTATION_LINK}#manual-setup
1995
+ `
1996
+ );
1997
+ logger2.line(1);
1998
+ return;
1999
+ }
2000
+ logger2.line(1);
2001
+ logger2.plain(`${step} Creating a Vitest setup file for Storybook:`);
2002
+ logger2.plain(` ${vitestSetupFile}`);
2003
+ const previewExists = EXTENSIONS.map((ext) => resolve(options.configDir, `preview${ext}`)).some(
2004
+ existsSync
2005
+ );
2006
+ const imports = [`import { setProjectAnnotations } from '${annotationsImport}';`];
2007
+ const projectAnnotations = [];
2008
+ if (previewExists) {
2009
+ imports.push(`import * as projectAnnotations from './preview';`);
2010
+ projectAnnotations.push("projectAnnotations");
2011
+ }
2012
+ await writeFile(
2013
+ vitestSetupFile,
2014
+ dedent`
1856
2015
  ${imports.join("\n")}
1857
2016
 
1858
2017
  // This is an important step to apply the right configuration when testing your stories.
1859
2018
  // More info at: https://storybook.js.org/docs/api/portable-stories/portable-stories-vitest#setprojectannotations
1860
2019
  setProjectAnnotations([${projectAnnotations.join(", ")}]);
1861
2020
  `
1862
- );
1863
- }
1864
- const vitestWorkspaceFile = findFile("vitest.workspace", [".ts", ".js", ".json"]);
2021
+ );
2022
+ const vitestWorkspaceFile = findFile("vitest.workspace", [".ts", ".js", ".json"]) || findFile("vitest.projects", [".ts", ".js", ".json"]);
1865
2023
  const viteConfigFile = findFile("vite.config");
1866
2024
  const vitestConfigFile = findFile("vitest.config");
1867
2025
  const vitestShimFile = findFile("vitest.shims.d");
1868
2026
  const rootConfig = vitestConfigFile || viteConfigFile;
2027
+ const browserConfig = `{
2028
+ enabled: true,
2029
+ headless: true,
2030
+ provider: 'playwright',
2031
+ instances: [{ browser: 'chromium' }]
2032
+ }`;
1869
2033
  if (fileExtension === "ts" && !vitestShimFile) {
1870
2034
  await writeFile(
1871
2035
  "vitest.shims.d.ts",
1872
- isVitest4OrNewer ? '/// <reference types="@vitest/browser-playwright" />' : '/// <reference types="@vitest/browser/providers/playwright" />'
2036
+ '/// <reference types="@vitest/browser/providers/playwright" />'
1873
2037
  );
1874
2038
  }
1875
- const getTemplateName = /* @__PURE__ */ __name(() => {
1876
- if (isVitest4OrNewer) {
1877
- return "vitest.config.4.template.ts";
1878
- } else if (isVitest3_2To4) {
1879
- return "vitest.config.3.2.template.ts";
1880
- }
1881
- return "vitest.config.template.ts";
1882
- }, "getTemplateName");
1883
2039
  if (vitestWorkspaceFile) {
1884
2040
  const workspaceTemplate = await loadTemplate("vitest.workspace.template.ts", {
1885
2041
  EXTENDS_WORKSPACE: viteConfigFile ? relative(dirname(vitestWorkspaceFile), viteConfigFile) : "",
1886
2042
  CONFIG_DIR: options.configDir,
2043
+ BROWSER_CONFIG: browserConfig,
1887
2044
  SETUP_FILE: relative(dirname(vitestWorkspaceFile), vitestSetupFile)
1888
2045
  }).then((t) => t.replace(`
1889
2046
  'ROOT_CONFIG',`, "").replace(/\s+extends: '',/, ""));
@@ -1892,12 +2049,14 @@ async function postInstall(options) {
1892
2049
  const target = babelParse(workspaceFile);
1893
2050
  const updated = updateWorkspaceFile(source, target);
1894
2051
  if (updated) {
1895
- logger.step(`Updating your Vitest workspace file...`);
1896
- logger.log(`${vitestWorkspaceFile}`);
2052
+ logger2.line(1);
2053
+ logger2.plain(`${step} Updating your Vitest workspace file:`);
2054
+ logger2.plain(` ${vitestWorkspaceFile}`);
1897
2055
  const formattedContent = await formatFileContent(vitestWorkspaceFile, generate(target).code);
1898
2056
  await writeFile(vitestWorkspaceFile, formattedContent);
1899
2057
  } else {
1900
- logger.error(
2058
+ logErrors(
2059
+ "\u{1F6A8} Oh no!",
1901
2060
  dedent`
1902
2061
  Could not update existing Vitest workspace file:
1903
2062
  ${vitestWorkspaceFile}
@@ -1909,18 +2068,21 @@ async function postInstall(options) {
1909
2068
  https://storybook.js.org/docs/next/${DOCUMENTATION_LINK}#manual-setup
1910
2069
  `
1911
2070
  );
1912
- errors.push("Unable to update existing Vitest workspace file");
2071
+ logger2.line(1);
2072
+ return;
1913
2073
  }
1914
2074
  } else if (rootConfig) {
1915
2075
  let target, updated;
1916
2076
  const configFile = await fs2.readFile(rootConfig, "utf8");
2077
+ const hasProjectsConfig = configFile.includes("projects:");
1917
2078
  const configFileHasTypeReference = configFile.match(
1918
2079
  /\/\/\/\s*<reference\s+types=["']vitest\/config["']\s*\/>/
1919
2080
  );
1920
- const templateName = getTemplateName();
2081
+ const templateName = hasProjectsConfig || isVitest3_2OrNewer ? "vitest.config.3.2.template.ts" : "vitest.config.template.ts";
1921
2082
  if (templateName) {
1922
2083
  const configTemplate = await loadTemplate(templateName, {
1923
2084
  CONFIG_DIR: options.configDir,
2085
+ BROWSER_CONFIG: browserConfig,
1924
2086
  SETUP_FILE: relative(dirname(rootConfig), vitestSetupFile)
1925
2087
  });
1926
2088
  const source = babelParse(configTemplate);
@@ -1928,38 +2090,45 @@ async function postInstall(options) {
1928
2090
  updated = updateConfigFile(source, target);
1929
2091
  }
1930
2092
  if (target && updated) {
1931
- logger.step(`Updating your ${vitestConfigFile ? "Vitest" : "Vite"} config file:`);
1932
- logger.log(` ${rootConfig}`);
2093
+ logger2.line(1);
2094
+ logger2.plain(`${step} Updating your ${vitestConfigFile ? "Vitest" : "Vite"} config file:`);
2095
+ logger2.plain(` ${rootConfig}`);
1933
2096
  const formattedContent = await formatFileContent(rootConfig, generate(target).code);
1934
- const shouldAddReference = !configFileHasTypeReference && !vitestConfigFile;
1935
2097
  await writeFile(
1936
2098
  rootConfig,
1937
- shouldAddReference ? '/// <reference types="vitest/config" />\n' + formattedContent : formattedContent
2099
+ configFileHasTypeReference ? formattedContent : '/// <reference types="vitest/config" />\n' + formattedContent
1938
2100
  );
1939
2101
  } else {
1940
- logger.error(dedent`
2102
+ logErrors(
2103
+ "\u{1F6A8} Oh no!",
2104
+ dedent`
1941
2105
  We were unable to update your existing ${vitestConfigFile ? "Vitest" : "Vite"} config file.
1942
2106
 
1943
2107
  Please refer to the documentation to complete the setup manually:
1944
2108
  https://storybook.js.org/docs/writing-tests/integrations/vitest-addon#manual-setup
1945
- `);
1946
- errors.push("Unable to update existing Vitest config file");
2109
+ `
2110
+ );
1947
2111
  }
1948
2112
  } else {
1949
- const parentDir = dirname(options.configDir);
1950
- const newConfigFile = resolve(parentDir, `vitest.config.${fileExtension}`);
1951
- const configTemplate = await loadTemplate(getTemplateName(), {
1952
- CONFIG_DIR: options.configDir,
1953
- SETUP_FILE: relative(dirname(newConfigFile), vitestSetupFile)
1954
- });
1955
- logger.step(`Creating a Vitest config file:`);
1956
- logger.log(`${newConfigFile}`);
2113
+ const newConfigFile = resolve(`vitest.config.${fileExtension}`);
2114
+ const configTemplate = await loadTemplate(
2115
+ isVitest3_2OrNewer ? "vitest.config.3.2.template.ts" : "vitest.config.template.ts",
2116
+ {
2117
+ CONFIG_DIR: options.configDir,
2118
+ BROWSER_CONFIG: browserConfig,
2119
+ SETUP_FILE: relative(dirname(newConfigFile), vitestSetupFile)
2120
+ }
2121
+ );
2122
+ logger2.line(1);
2123
+ logger2.plain(`${step} Creating a Vitest config file:`);
2124
+ logger2.plain(` ${newConfigFile}`);
1957
2125
  const formattedContent = await formatFileContent(newConfigFile, configTemplate);
1958
2126
  await writeFile(newConfigFile, formattedContent);
1959
2127
  }
1960
- const a11yAddon = info.addons.find((addon) => addon.includes(addonA11yName));
2128
+ const a11yAddon = info2.addons.find((addon) => addon.includes(addonA11yName));
1961
2129
  if (a11yAddon) {
1962
2130
  try {
2131
+ logger2.plain(`${step} Setting up ${addonA11yName} for @storybook/addon-vitest:`);
1963
2132
  const command = ["automigrate", "addon-a11y-addon-test"];
1964
2133
  command.push("--loglevel", "silent");
1965
2134
  command.push("--yes", "--skip-doctor");
@@ -1972,55 +2141,99 @@ async function postInstall(options) {
1972
2141
  if (options.configDir !== ".storybook") {
1973
2142
  command.push("--config-dir", `"${options.configDir}"`);
1974
2143
  }
1975
- const remoteCommand = packageManager.getRemoteRunCommand("storybook", command);
1976
- const [cmd, ...args] = remoteCommand.split(" ");
1977
- await prompt.executeTask(
1978
- // TODO: Remove stdio: 'ignore' once we have a way to log the output of the command properly
1979
- () => packageManager.executeCommand({ command: cmd, args, stdio: "ignore" }),
1980
- {
1981
- intro: "Setting up a11y addon for @storybook/addon-vitest",
1982
- error: "Failed to setup a11y addon for @storybook/addon-vitest",
1983
- success: "a11y addon setup successfully"
1984
- }
1985
- );
2144
+ await execa("storybook", command, {
2145
+ stdio: "inherit"
2146
+ });
1986
2147
  } catch (e) {
1987
- logger.line();
1988
- logger.error(dedent`
1989
- Could not automatically set up ${addonA11yName} for @storybook/addon-vitest.
2148
+ logErrors(
2149
+ "\u{1F6A8} Oh no!",
2150
+ dedent`
2151
+ We have detected that you have ${addonA11yName} installed but could not automatically set it up for @storybook/addon-vitest:
2152
+
2153
+ ${e instanceof Error ? e.message : String(e)}
2154
+
1990
2155
  Please refer to the documentation to complete the setup manually:
1991
2156
  https://storybook.js.org/docs/writing-tests/accessibility-testing#test-addon-integration
1992
- `);
1993
- errors.push(
1994
- "The @storybook/addon-a11y couldn't be set up for the Vitest addon" + (e instanceof Error ? e.stack : String(e))
2157
+ `
1995
2158
  );
1996
2159
  }
1997
2160
  }
1998
2161
  const runCommand = rootConfig ? `npx vitest --project=storybook` : `npx vitest`;
1999
- logger.line();
2000
- if (errors.length === 0) {
2001
- logger.step(CLI_COLORS.success("@storybook/addon-vitest setup completed successfully"));
2002
- logger.log(dedent`
2162
+ if (!hasErrors) {
2163
+ printSuccess(
2164
+ "\u{1F389} All done!",
2165
+ dedent`
2003
2166
  @storybook/addon-vitest is now configured and you're ready to run your tests!
2167
+
2004
2168
  Here are a couple of tips to get you started:
2005
-
2006
- You can run tests with "${CLI_COLORS.cta(runCommand)}"
2007
- • Vitest IDE extension shows all stories as tests in your editor!
2008
-
2169
+ • You can run tests with "${runCommand}"
2170
+ When using the Vitest extension in your editor, all of your stories will be shown as tests!
2171
+
2009
2172
  Check the documentation for more information about its features and options at:
2010
2173
  https://storybook.js.org/docs/next/${DOCUMENTATION_LINK}
2011
- `);
2174
+ `
2175
+ );
2012
2176
  } else {
2013
- logger.warn(
2177
+ printWarning(
2178
+ "\u26A0\uFE0F Done, but with errors!",
2014
2179
  dedent`
2015
- Done, but with errors!
2016
- @storybook/addon-vitest was installed successfully, but there were some errors during the setup process. Please refer to the documentation to complete the setup manually and check the errors above:
2180
+ @storybook/addon-vitest was installed successfully, but there were some errors during the setup process.
2181
+
2182
+ Please refer to the documentation to complete the setup manually and check the errors above:
2017
2183
  https://storybook.js.org/docs/next/${DOCUMENTATION_LINK}#manual-setup
2018
2184
  `
2019
2185
  );
2020
- throw new AddonVitestPostinstallError({ errors });
2021
2186
  }
2187
+ logger2.line(1);
2022
2188
  }
2023
2189
  __name(postInstall, "postInstall");
2190
+ async function getPackageNameFromPath(input) {
2191
+ const path = input.startsWith("file://") ? fileURLToPath(input) : input;
2192
+ if (!isAbsolute(path)) {
2193
+ return path;
2194
+ }
2195
+ const packageJsonPath = up2({ cwd: path });
2196
+ if (!packageJsonPath) {
2197
+ throw new Error(`Could not find package.json in path: ${path}`);
2198
+ }
2199
+ const { default: packageJson } = await import(pathToFileURL(packageJsonPath).href, {
2200
+ with: { type: "json" }
2201
+ });
2202
+ return packageJson.name;
2203
+ }
2204
+ __name(getPackageNameFromPath, "getPackageNameFromPath");
2205
+ async function getStorybookInfo({ configDir, packageManager: pkgMgr }) {
2206
+ const packageManager = JsPackageManagerFactory.getPackageManager({ force: pkgMgr, configDir });
2207
+ const { packageJson } = packageManager.primaryPackageJson;
2208
+ const config = await loadMainConfig({ configDir });
2209
+ const { presets } = await experimental_loadStorybook({
2210
+ configDir,
2211
+ packageJson
2212
+ });
2213
+ const framework = await presets.apply("framework", {});
2214
+ const core = await presets.apply("core", {});
2215
+ const { builder, renderer } = core;
2216
+ if (!builder) {
2217
+ throw new Error("Could not detect your Storybook builder.");
2218
+ }
2219
+ const frameworkPackageName = await getPackageNameFromPath(
2220
+ typeof framework === "string" ? framework : framework.name
2221
+ );
2222
+ const builderPackageName = await getPackageNameFromPath(
2223
+ typeof builder === "string" ? builder : builder.name
2224
+ );
2225
+ let rendererPackageName;
2226
+ if (renderer) {
2227
+ rendererPackageName = await getPackageNameFromPath(renderer);
2228
+ }
2229
+ return {
2230
+ frameworkPackageName,
2231
+ builderPackageName,
2232
+ rendererPackageName,
2233
+ addons: getAddonNames(config)
2234
+ };
2235
+ }
2236
+ __name(getStorybookInfo, "getStorybookInfo");
2024
2237
  export {
2025
2238
  postInstall as default
2026
2239
  };