storybook
Version:
Storybook: Develop, document, and test UI components in isolation
929 lines (897 loc) • 33.8 kB
JavaScript
import CJS_COMPAT_NODE_URL_yr66iw5gef from 'node:url';
import CJS_COMPAT_NODE_PATH_yr66iw5gef from 'node:path';
import CJS_COMPAT_NODE_MODULE_yr66iw5gef from "node:module";
var __filename = CJS_COMPAT_NODE_URL_yr66iw5gef.fileURLToPath(import.meta.url);
var __dirname = CJS_COMPAT_NODE_PATH_yr66iw5gef.dirname(__filename);
var require = CJS_COMPAT_NODE_MODULE_yr66iw5gef.createRequire(import.meta.url);
// ------------------------------------------------------------
// end of CJS compatibility banner, injected by Storybook's esbuild configuration
// ------------------------------------------------------------
import {
version
} from "./chunk-CMI5GRPA.js";
import {
createFileSystemCache,
execa,
execaCommand,
getProjectRoot,
resolvePathInStorybookCache,
up
} from "./chunk-QZJSO2TW.js";
import {
getSessionId,
nanoid,
set
} from "./chunk-N54KYQ6P.js";
import {
globalSettings
} from "./chunk-S3DQCJWT.js";
import {
resolvePackageDir
} from "./chunk-Q6B5LJGA.js";
import {
slash
} from "./chunk-GVOXGNOU.js";
import {
__commonJS,
__toESM
} from "./chunk-J4VC4I2M.js";
// ../node_modules/fetch-retry/index.js
var require_fetch_retry = __commonJS({
"../node_modules/fetch-retry/index.js"(exports, module) {
"use strict";
module.exports = function(fetch2, defaults) {
if (defaults = defaults || {}, typeof fetch2 != "function")
throw new ArgumentError("fetch must be a function");
if (typeof defaults != "object")
throw new ArgumentError("defaults must be an object");
if (defaults.retries !== void 0 && !isPositiveInteger(defaults.retries))
throw new ArgumentError("retries must be a positive integer");
if (defaults.retryDelay !== void 0 && !isPositiveInteger(defaults.retryDelay) && typeof defaults.retryDelay != "function")
throw new ArgumentError("retryDelay must be a positive integer or a function returning a positive integer");
if (defaults.retryOn !== void 0 && !Array.isArray(defaults.retryOn) && typeof defaults.retryOn != "function")
throw new ArgumentError("retryOn property expects an array or function");
var baseDefaults = {
retries: 3,
retryDelay: 1e3,
retryOn: []
};
return defaults = Object.assign(baseDefaults, defaults), function(input, init) {
var retries = defaults.retries, retryDelay = defaults.retryDelay, retryOn = defaults.retryOn;
if (init && init.retries !== void 0)
if (isPositiveInteger(init.retries))
retries = init.retries;
else
throw new ArgumentError("retries must be a positive integer");
if (init && init.retryDelay !== void 0)
if (isPositiveInteger(init.retryDelay) || typeof init.retryDelay == "function")
retryDelay = init.retryDelay;
else
throw new ArgumentError("retryDelay must be a positive integer or a function returning a positive integer");
if (init && init.retryOn)
if (Array.isArray(init.retryOn) || typeof init.retryOn == "function")
retryOn = init.retryOn;
else
throw new ArgumentError("retryOn property expects an array or function");
return new Promise(function(resolve2, reject) {
var wrappedFetch = function(attempt) {
var _input = typeof Request < "u" && input instanceof Request ? input.clone() : input;
fetch2(_input, init).then(function(response) {
if (Array.isArray(retryOn) && retryOn.indexOf(response.status) === -1)
resolve2(response);
else if (typeof retryOn == "function")
try {
return Promise.resolve(retryOn(attempt, null, response)).then(function(retryOnResponse) {
retryOnResponse ? retry2(attempt, null, response) : resolve2(response);
}).catch(reject);
} catch (error) {
reject(error);
}
else
attempt < retries ? retry2(attempt, null, response) : resolve2(response);
}).catch(function(error) {
if (typeof retryOn == "function")
try {
Promise.resolve(retryOn(attempt, error, null)).then(function(retryOnResponse) {
retryOnResponse ? retry2(attempt, error, null) : reject(error);
}).catch(function(error2) {
reject(error2);
});
} catch (error2) {
reject(error2);
}
else attempt < retries ? retry2(attempt, error, null) : reject(error);
});
};
function retry2(attempt, error, response) {
var delay = typeof retryDelay == "function" ? retryDelay(attempt, error, response) : retryDelay;
setTimeout(function() {
wrappedFetch(++attempt);
}, delay);
}
wrappedFetch(0);
});
};
};
function isPositiveInteger(value) {
return Number.isInteger(value) && value >= 0;
}
function ArgumentError(message) {
this.name = "ArgumentError", this.message = message;
}
}
});
// src/telemetry/index.ts
import { logger as logger2 } from "storybook/internal/node-logger";
// src/telemetry/notify.ts
import { cache } from "storybook/internal/common";
import { logger } from "storybook/internal/node-logger";
var TELEMETRY_KEY_NOTIFY_DATE = "telemetry-notification-date", called = !1, notify = async () => {
called || (called = !0, await cache.get(TELEMETRY_KEY_NOTIFY_DATE, null) || (cache.set(TELEMETRY_KEY_NOTIFY_DATE, Date.now()), logger.info(
"Storybook collects completely anonymous usage telemetry. We use it to shape Storybook's roadmap and prioritize features. You can learn more, including how to opt out, at https://storybook.js.org/telemetry"
)));
};
// src/telemetry/sanitize.ts
import path from "node:path";
function regexpEscape(str) {
return str.replace(/[-[/{}()*+?.\\^$|]/g, "\\$&");
}
function removeAnsiEscapeCodes(input = "") {
return input.replace(/\u001B\[[0-9;]*m/g, "");
}
function cleanPaths(str, separator = path.sep) {
if (!str)
return str;
let stack = process.cwd().split(separator);
for (; stack.length > 1; ) {
let currentPath = stack.join(separator), currentRegex = new RegExp(regexpEscape(currentPath), "gi");
str = str.replace(currentRegex, "$SNIP");
let currentPath2 = stack.join(separator + separator), currentRegex2 = new RegExp(regexpEscape(currentPath2), "gi");
str = str.replace(currentRegex2, "$SNIP"), stack.pop();
}
return str;
}
function sanitizeError(error, pathSeparator = path.sep) {
try {
error = {
...JSON.parse(JSON.stringify(error)),
message: removeAnsiEscapeCodes(error.message),
stack: removeAnsiEscapeCodes(error.stack),
cause: error.cause,
name: error.name
};
let errorString = cleanPaths(JSON.stringify(error), pathSeparator);
return JSON.parse(errorString);
} catch (err) {
return `Sanitization error: ${err?.message}`;
}
}
// src/telemetry/storybook-metadata.ts
import { createHash } from "node:crypto";
import { existsSync as existsSync2 } from "node:fs";
import { readFile } from "node:fs/promises";
import { dirname, resolve } from "node:path";
import {
getStorybookConfiguration,
getStorybookInfo as getStorybookInfo2,
isCI,
loadMainConfig,
versions
} from "storybook/internal/common";
import { getInterpretedFile } from "storybook/internal/common";
import { readConfig } from "storybook/internal/csf-tools";
// src/telemetry/get-application-file-count.ts
import { sep } from "node:path";
// src/telemetry/exec-command-count-lines.ts
import { createInterface } from "node:readline";
async function execCommandCountLines(command, args, options) {
let process3 = execa(command, args, { buffer: !1, ...options });
if (!process3.stdout)
throw new Error("Unexpected missing stdout");
let lineCount = 0, rl = createInterface(process3.stdout);
return rl.on("line", () => {
lineCount += 1;
}), await process3, rl.close(), lineCount;
}
// src/telemetry/run-telemetry-operation.ts
var cache2 = createFileSystemCache({
basePath: resolvePathInStorybookCache("telemetry"),
ns: "storybook",
ttl: 1440 * 60 * 1e3
// 24h
}), runTelemetryOperation = async (cacheKey, operation) => {
let cached = await cache2.get(cacheKey);
return cached === void 0 && (cached = await operation(), cached !== void 0 && await cache2.set(cacheKey, cached)), cached;
};
// src/telemetry/get-application-file-count.ts
var nameMatches = ["page", "screen"], extensions = ["js", "jsx", "ts", "tsx"], getApplicationFilesCountUncached = async (basePath) => {
let globs = nameMatches.flatMap((match) => [
match,
[match[0].toUpperCase(), ...match.slice(1)].join("")
]).flatMap(
(match) => extensions.map((extension) => `${basePath}${sep}*${match}*.${extension}`)
);
try {
return await execCommandCountLines("git", ["ls-files", "--", ...globs]);
} catch {
return;
}
}, getApplicationFileCount = async (path3) => runTelemetryOperation(
"applicationFiles",
async () => getApplicationFilesCountUncached(path3)
);
// src/telemetry/get-chromatic-version.ts
function getChromaticVersionSpecifier(packageJson) {
let dependency = packageJson.dependencies?.chromatic || packageJson.devDependencies?.chromatic || packageJson.peerDependencies?.chromatic;
return dependency || (packageJson.scripts && Object.values(packageJson.scripts).find((s) => s?.match(/chromatic/)) ? "latest" : void 0);
}
// src/telemetry/get-framework-info.ts
import { getStorybookInfo } from "storybook/internal/common";
var cleanAndSanitizePath = (path3) => cleanPaths(path3).replace(/.*node_modules[\\/]/, "");
async function getFrameworkInfo(mainConfig, configDir) {
let { frameworkPackage, rendererPackage, builderPackage } = await getStorybookInfo(configDir), frameworkOptions = typeof mainConfig.framework == "object" ? mainConfig.framework.options : {};
return {
framework: {
name: frameworkPackage ? cleanAndSanitizePath(frameworkPackage) : void 0,
options: frameworkOptions
},
builder: builderPackage ? cleanAndSanitizePath(builderPackage) : void 0,
renderer: rendererPackage ? cleanAndSanitizePath(rendererPackage) : void 0
};
}
// src/telemetry/get-has-router-package.ts
var routerPackages = /* @__PURE__ */ new Set([
"react-router",
"react-router-dom",
"remix",
"@tanstack/react-router",
"expo-router",
"@reach/router",
"react-easy-router",
"@remix-run/router",
"wouter",
"wouter-preact",
"preact-router",
"vue-router",
"unplugin-vue-router",
"@angular/router",
"@solidjs/router",
// metaframeworks that imply routing
"next",
"react-scripts",
"gatsby",
"nuxt",
"@sveltejs/kit"
]);
function getHasRouterPackage(packageJson) {
return Object.keys(packageJson?.dependencies ?? {}).some(
(depName) => routerPackages.has(depName)
);
}
// src/telemetry/get-monorepo-type.ts
import { existsSync, readFileSync } from "node:fs";
import { join } from "node:path";
import { getProjectRoot as getProjectRoot2 } from "storybook/internal/common";
var monorepoConfigs = {
Nx: "nx.json",
Turborepo: "turbo.json",
Lerna: "lerna.json",
Rush: "rush.json",
Lage: "lage.config.json"
}, getMonorepoType = () => {
let monorepoType = Object.keys(monorepoConfigs).find((monorepo) => {
let configFile = join(getProjectRoot2(), monorepoConfigs[monorepo]);
return existsSync(configFile);
});
if (monorepoType)
return monorepoType;
if (!existsSync(join(getProjectRoot2(), "package.json")))
return;
if (JSON.parse(
readFileSync(join(getProjectRoot2(), "package.json"), { encoding: "utf8" })
)?.workspaces)
return "Workspaces";
};
// ../node_modules/package-manager-detector/dist/commands.mjs
function dashDashArg(agent, agentCommand) {
return (args) => args.length > 1 ? [agent, agentCommand, args[0], "--", ...args.slice(1)] : [agent, agentCommand, args[0]];
}
function denoExecute() {
return (args) => ["deno", "run", `npm:${args[0]}`, ...args.slice(1)];
}
var npm = {
agent: ["npm", 0],
run: dashDashArg("npm", "run"),
install: ["npm", "i", 0],
frozen: ["npm", "ci", 0],
global: ["npm", "i", "-g", 0],
add: ["npm", "i", 0],
upgrade: ["npm", "update", 0],
"upgrade-interactive": null,
dedupe: ["npm", "dedupe", 0],
execute: ["npx", 0],
"execute-local": ["npx", 0],
uninstall: ["npm", "uninstall", 0],
global_uninstall: ["npm", "uninstall", "-g", 0]
}, yarn = {
agent: ["yarn", 0],
run: ["yarn", "run", 0],
install: ["yarn", "install", 0],
frozen: ["yarn", "install", "--frozen-lockfile", 0],
global: ["yarn", "global", "add", 0],
add: ["yarn", "add", 0],
upgrade: ["yarn", "upgrade", 0],
"upgrade-interactive": ["yarn", "upgrade-interactive", 0],
dedupe: null,
execute: ["npx", 0],
"execute-local": dashDashArg("yarn", "exec"),
uninstall: ["yarn", "remove", 0],
global_uninstall: ["yarn", "global", "remove", 0]
}, yarnBerry = {
...yarn,
frozen: ["yarn", "install", "--immutable", 0],
upgrade: ["yarn", "up", 0],
"upgrade-interactive": ["yarn", "up", "-i", 0],
dedupe: ["yarn", "dedupe", 0],
execute: ["yarn", "dlx", 0],
"execute-local": ["yarn", "exec", 0],
// Yarn 2+ removed 'global', see https://github.com/yarnpkg/berry/issues/821
global: ["npm", "i", "-g", 0],
global_uninstall: ["npm", "uninstall", "-g", 0]
}, pnpm = {
agent: ["pnpm", 0],
run: ["pnpm", "run", 0],
install: ["pnpm", "i", 0],
frozen: ["pnpm", "i", "--frozen-lockfile", 0],
global: ["pnpm", "add", "-g", 0],
add: ["pnpm", "add", 0],
upgrade: ["pnpm", "update", 0],
"upgrade-interactive": ["pnpm", "update", "-i", 0],
dedupe: ["pnpm", "dedupe", 0],
execute: ["pnpm", "dlx", 0],
"execute-local": ["pnpm", "exec", 0],
uninstall: ["pnpm", "remove", 0],
global_uninstall: ["pnpm", "remove", "--global", 0]
}, bun = {
agent: ["bun", 0],
run: ["bun", "run", 0],
install: ["bun", "install", 0],
frozen: ["bun", "install", "--frozen-lockfile", 0],
global: ["bun", "add", "-g", 0],
add: ["bun", "add", 0],
upgrade: ["bun", "update", 0],
"upgrade-interactive": ["bun", "update", "-i", 0],
dedupe: null,
execute: ["bun", "x", 0],
"execute-local": ["bun", "x", 0],
uninstall: ["bun", "remove", 0],
global_uninstall: ["bun", "remove", "-g", 0]
}, deno = {
agent: ["deno", 0],
run: ["deno", "task", 0],
install: ["deno", "install", 0],
frozen: ["deno", "install", "--frozen", 0],
global: ["deno", "install", "-g", 0],
add: ["deno", "add", 0],
upgrade: ["deno", "outdated", "--update", 0],
"upgrade-interactive": ["deno", "outdated", "--update", 0],
dedupe: null,
execute: denoExecute(),
"execute-local": ["deno", "task", "--eval", 0],
uninstall: ["deno", "remove", 0],
global_uninstall: ["deno", "uninstall", "-g", 0]
}, COMMANDS = {
npm,
yarn,
"yarn@berry": yarnBerry,
pnpm,
// pnpm v6.x or below
"pnpm@6": {
...pnpm,
run: dashDashArg("pnpm", "run")
},
bun,
deno
};
// ../node_modules/package-manager-detector/dist/constants.mjs
var AGENTS = [
"npm",
"yarn",
"yarn@berry",
"pnpm",
"pnpm@6",
"bun",
"deno"
], LOCKS = {
"bun.lock": "bun",
"bun.lockb": "bun",
"deno.lock": "deno",
"pnpm-lock.yaml": "pnpm",
"pnpm-workspace.yaml": "pnpm",
"yarn.lock": "yarn",
"package-lock.json": "npm",
"npm-shrinkwrap.json": "npm"
}, INSTALL_METADATA = {
"node_modules/.deno/": "deno",
"node_modules/.pnpm/": "pnpm",
"node_modules/.yarn-state.yml": "yarn",
// yarn v2+ (node-modules)
"node_modules/.yarn_integrity": "yarn",
// yarn v1
"node_modules/.package-lock.json": "npm",
".pnp.cjs": "yarn",
// yarn v3+ (pnp)
".pnp.js": "yarn",
// yarn v2 (pnp)
"bun.lock": "bun",
"bun.lockb": "bun"
};
// ../node_modules/package-manager-detector/dist/detect.mjs
import fs from "node:fs/promises";
import path2 from "node:path";
import process2 from "node:process";
async function pathExists(path22, type) {
try {
let stat = await fs.stat(path22);
return type === "file" ? stat.isFile() : stat.isDirectory();
} catch {
return !1;
}
}
function* lookup(cwd = process2.cwd()) {
let directory = path2.resolve(cwd), { root } = path2.parse(directory);
for (; directory && directory !== root; )
yield directory, directory = path2.dirname(directory);
}
async function parsePackageJson(filepath, onUnknown) {
return !filepath || !pathExists(filepath, "file") ? null : await handlePackageManager(filepath, onUnknown);
}
async function detect(options = {}) {
let {
cwd,
strategies = ["lockfile", "packageManager-field", "devEngines-field"],
onUnknown
} = options, stopDir;
if (typeof options.stopDir == "string") {
let resolved = path2.resolve(options.stopDir);
stopDir = (dir) => dir === resolved;
} else
stopDir = options.stopDir;
for (let directory of lookup(cwd)) {
for (let strategy of strategies)
switch (strategy) {
case "lockfile": {
for (let lock of Object.keys(LOCKS))
if (await pathExists(path2.join(directory, lock), "file")) {
let name = LOCKS[lock], result = await parsePackageJson(path2.join(directory, "package.json"), onUnknown);
return result || { name, agent: name };
}
break;
}
case "packageManager-field":
case "devEngines-field": {
let result = await parsePackageJson(path2.join(directory, "package.json"), onUnknown);
if (result)
return result;
break;
}
case "install-metadata": {
for (let metadata of Object.keys(INSTALL_METADATA)) {
let fileOrDir = metadata.endsWith("/") ? "dir" : "file";
if (await pathExists(path2.join(directory, metadata), fileOrDir)) {
let name = INSTALL_METADATA[metadata], agent = name === "yarn" ? isMetadataYarnClassic(metadata) ? "yarn" : "yarn@berry" : name;
return { name, agent };
}
}
break;
}
}
if (stopDir?.(directory))
break;
}
return null;
}
function getNameAndVer(pkg) {
let handelVer = (version2) => version2?.match(/\d+(\.\d+){0,2}/)?.[0] ?? version2;
if (typeof pkg.packageManager == "string") {
let [name, ver] = pkg.packageManager.replace(/^\^/, "").split("@");
return { name, ver: handelVer(ver) };
}
if (typeof pkg.devEngines?.packageManager?.name == "string")
return {
name: pkg.devEngines.packageManager.name,
ver: handelVer(pkg.devEngines.packageManager.version)
};
}
async function handlePackageManager(filepath, onUnknown) {
try {
let pkg = JSON.parse(await fs.readFile(filepath, "utf8")), agent, nameAndVer = getNameAndVer(pkg);
if (nameAndVer) {
let name = nameAndVer.name, ver = nameAndVer.ver, version2 = ver;
return name === "yarn" && ver && Number.parseInt(ver) > 1 ? (agent = "yarn@berry", version2 = "berry", { name, agent, version: version2 }) : name === "pnpm" && ver && Number.parseInt(ver) < 7 ? (agent = "pnpm@6", { name, agent, version: version2 }) : AGENTS.includes(name) ? (agent = name, { name, agent, version: version2 }) : onUnknown?.(pkg.packageManager) ?? null;
}
} catch {
}
return null;
}
function isMetadataYarnClassic(metadataPath) {
return metadataPath.endsWith(".yarn_integrity");
}
// src/telemetry/get-package-manager-info.ts
var getPackageManagerInfo = async () => {
let packageManagerType = await detect({ cwd: getProjectRoot() });
if (!packageManagerType)
return;
let nodeLinker = "node_modules";
if (packageManagerType.name === "yarn")
try {
let { stdout } = await execaCommand("yarn config get nodeLinker", {
cwd: getProjectRoot()
});
nodeLinker = stdout.trim();
} catch {
}
if (packageManagerType.name === "pnpm")
try {
let { stdout } = await execaCommand("pnpm config get nodeLinker", {
cwd: getProjectRoot()
});
nodeLinker = stdout.trim() ?? "isolated";
} catch {
}
return {
type: packageManagerType.name,
version: packageManagerType.version,
agent: packageManagerType.agent,
nodeLinker
};
};
// src/telemetry/get-portable-stories-usage.ts
var getPortableStoriesFileCountUncached = async (path3) => {
try {
return await execCommandCountLines("git", [
"grep",
"-l",
"composeStor",
...path3 ? ["--", path3] : []
]);
} catch (err) {
return err.exitCode === 1 ? 0 : void 0;
}
}, getPortableStoriesFileCount = async (path3) => runTelemetryOperation(
"portableStories",
async () => getPortableStoriesFileCountUncached(path3)
);
// src/telemetry/package-json.ts
import { fileURLToPath, pathToFileURL } from "node:url";
var getActualPackageVersions = async (packages) => {
let packageNames = Object.keys(packages);
return Promise.all(packageNames.map(getActualPackageVersion));
}, getActualPackageVersion = async (packageName) => {
try {
let packageJson = await getActualPackageJson(packageName);
return {
name: packageJson?.name || packageName,
version: packageJson?.version || null
};
} catch {
return {
name: packageName,
version: null
};
}
}, getActualPackageJson = async (packageName) => {
try {
let resolvedPackageJsonPath = up({
cwd: fileURLToPath(import.meta.resolve(packageName, process.cwd()))
});
resolvedPackageJsonPath || (resolvedPackageJsonPath = import.meta.resolve(`${packageName}/package.json`, process.cwd()));
let { default: packageJson } = await import(pathToFileURL(resolvedPackageJsonPath).href, {
with: { type: "json" }
});
return packageJson;
} catch {
return;
}
};
// src/telemetry/storybook-metadata.ts
var metaFrameworks = {
next: "Next",
"react-scripts": "CRA",
gatsby: "Gatsby",
"@nuxtjs/storybook": "nuxt",
"@nrwl/storybook": "nx",
"@vue/cli-service": "vue-cli",
"@sveltejs/kit": "sveltekit",
"@tanstack/react-router": "tanstack-react",
"@react-router/dev": "react-router",
"@remix-run/dev": "remix"
}, sanitizeAddonName = (name) => cleanPaths(name).replace(/\/dist\/.*/, "").replace(/\.[mc]?[tj]?s[x]?$/, "").replace(/\/register$/, "").replace(/\/manager$/, "").replace(/\/preset$/, ""), computeStorybookMetadata = async ({
packageJsonPath,
packageJson,
mainConfig,
configDir
}) => {
let settings = isCI() ? void 0 : await globalSettings(), metadata = {
generatedAt: (/* @__PURE__ */ new Date()).getTime(),
userSince: settings?.value.userSince,
hasCustomBabel: !1,
hasCustomWebpack: !1,
hasStaticDirs: !1,
hasStorybookEslint: !1,
refCount: 0
}, allDependencies = {
...packageJson?.dependencies,
...packageJson?.devDependencies,
...packageJson?.peerDependencies
}, metaFramework = Object.keys(allDependencies).find((dep) => !!metaFrameworks[dep]);
if (metaFramework) {
let { version: version2 } = await getActualPackageVersion(metaFramework);
metadata.metaFramework = {
name: metaFrameworks[metaFramework],
packageName: metaFramework,
version: version2 || "unknown"
};
}
let testPackages = [
"playwright",
"vitest",
"jest",
"cypress",
"nightwatch",
"webdriver",
"@web/test-runner",
"puppeteer",
"karma",
"jasmine",
"chai",
"testing-library",
"@ngneat/spectator",
"wdio",
"msw",
"miragejs",
"sinon",
"chromatic"
], testPackageDeps = Object.keys(allDependencies).filter(
(dep) => testPackages.find((pkg) => dep.includes(pkg))
);
metadata.testPackages = Object.fromEntries(
await Promise.all(
testPackageDeps.map(async (dep) => [dep, (await getActualPackageVersion(dep))?.version])
)
), metadata.hasRouterPackage = getHasRouterPackage(packageJson);
let monorepoType = getMonorepoType();
monorepoType && (metadata.monorepo = monorepoType), metadata.packageManager = await getPackageManagerInfo();
let language = allDependencies.typescript ? "typescript" : "javascript";
if (!mainConfig)
return {
...metadata,
storybookVersionSpecifier: versions.storybook,
language
};
metadata.hasCustomBabel = !!mainConfig.babel, metadata.hasCustomWebpack = !!mainConfig.webpackFinal, metadata.hasStaticDirs = !!mainConfig.staticDirs, typeof mainConfig.typescript == "object" && (metadata.typescriptOptions = mainConfig.typescript);
let frameworkInfo = await getFrameworkInfo(mainConfig, configDir);
typeof mainConfig.refs == "object" && (metadata.refCount = Object.keys(mainConfig.refs).length), typeof mainConfig.features == "object" && (metadata.features = mainConfig.features);
let addons = {};
mainConfig.addons && mainConfig.addons.forEach((addon) => {
let addonName, options;
typeof addon == "string" ? addonName = sanitizeAddonName(addon) : (addon.name.includes("addon-essentials") && (options = addon.options), addonName = sanitizeAddonName(addon.name)), addons[addonName] = {
options,
version: void 0
};
});
let chromaticVersionSpecifier = getChromaticVersionSpecifier(packageJson);
chromaticVersionSpecifier && (addons.chromatic = {
version: void 0,
versionSpecifier: chromaticVersionSpecifier,
options: void 0
}), (await getActualPackageVersions(addons)).forEach(({ name, version: version2 }) => {
addons[name] = addons[name] || {
name,
version: version2
}, addons[name].version = version2 || void 0;
});
let addonNames = Object.keys(addons), storybookPackages = Object.keys(allDependencies).filter((dep) => dep.includes("storybook") && !addonNames.includes(dep)).reduce((acc, dep) => ({
...acc,
[dep]: { version: void 0 }
}), {});
(await getActualPackageVersions(storybookPackages)).forEach(({ name, version: version2 }) => {
storybookPackages[name] = storybookPackages[name] || {
name,
version: version2
}, storybookPackages[name].version = version2 || void 0;
});
let hasStorybookEslint = !!allDependencies["eslint-plugin-storybook"], storybookInfo = await getStorybookInfo2(configDir);
try {
let { previewConfigPath: previewConfig } = storybookInfo;
if (previewConfig) {
let config = await readConfig(previewConfig), usesGlobals = !!(config.getFieldNode(["globals"]) || config.getFieldNode(["globalTypes"]));
metadata.preview = { ...metadata.preview, usesGlobals };
}
} catch {
}
let portableStoriesFileCount = await getPortableStoriesFileCount(), applicationFileCount = await getApplicationFileCount(dirname(packageJsonPath));
return {
...metadata,
...frameworkInfo,
portableStoriesFileCount,
applicationFileCount,
storybookVersion: version,
storybookVersionSpecifier: storybookInfo.versionSpecifier ?? "",
language,
storybookPackages,
addons,
hasStorybookEslint
};
};
async function getPackageJsonDetails() {
let packageJsonPath = up();
return packageJsonPath ? {
packageJsonPath,
packageJson: JSON.parse(await readFile(packageJsonPath, "utf8"))
} : {
packageJsonPath: process.cwd(),
packageJson: {}
};
}
var metadataCache = /* @__PURE__ */ new Map();
async function hashMainConfig(configDir) {
try {
let mainPath = getInterpretedFile(resolve(configDir, "main"));
if (!mainPath || !existsSync2(mainPath))
return "missing";
let content = await readFile(mainPath);
return createHash("sha256").update(new Uint8Array(content)).digest("hex");
} catch {
return "unknown";
}
}
var getStorybookMetadata = async (_configDir) => {
let { packageJson, packageJsonPath } = await getPackageJsonDetails(), configDir = (_configDir || getStorybookConfiguration(
String(packageJson?.scripts?.storybook || ""),
"-c",
"--config-dir"
)) ?? ".storybook", contentHash = await hashMainConfig(configDir), cacheKey = `${configDir}::${contentHash}`, cached = metadataCache.get(cacheKey);
if (cached)
return cached;
let mainConfig = await loadMainConfig({ configDir }).catch(() => {
}), computed = await computeStorybookMetadata({
mainConfig,
packageJson,
packageJsonPath,
configDir
});
return metadataCache.set(cacheKey, computed), computed;
};
// src/telemetry/telemetry.ts
var import_fetch_retry = __toESM(require_fetch_retry(), 1);
import { readFileSync as readFileSync2 } from "node:fs";
import * as os from "node:os";
import { join as join2 } from "node:path";
import { isCI as isCI2 } from "storybook/internal/common";
// src/telemetry/anonymous-id.ts
import { relative } from "node:path";
import { getProjectRoot as getProjectRoot3 } from "storybook/internal/common";
import { execSync } from "child_process";
// src/telemetry/one-way-hash.ts
import { createHash as createHash2 } from "crypto";
var oneWayHash = (payload) => {
let hash = createHash2("sha256");
return hash.update("storybook-telemetry-salt"), hash.update(payload), hash.digest("hex");
};
// src/telemetry/anonymous-id.ts
function normalizeGitUrl(rawUrl) {
let urlWithoutScheme = rawUrl.trim().replace(/#.*$/, "").replace(/^.*@/, "").replace(/^.*\/\//, "");
return (urlWithoutScheme.endsWith(".git") ? urlWithoutScheme : `${urlWithoutScheme}.git`).replace(":", "/");
}
function unhashedProjectId(remoteUrl, projectRootPath) {
return `${normalizeGitUrl(remoteUrl)}${slash(projectRootPath)}`;
}
var anonymousProjectId, getAnonymousProjectId = () => {
if (anonymousProjectId)
return anonymousProjectId;
try {
let projectRootPath = relative(getProjectRoot3(), process.cwd()), originBuffer = execSync("git config --local --get remote.origin.url", {
timeout: 1e3,
stdio: "pipe"
});
anonymousProjectId = oneWayHash(unhashedProjectId(String(originBuffer), projectRootPath));
} catch {
}
return anonymousProjectId;
};
// src/telemetry/fetch.ts
var fetch = global.fetch;
// src/telemetry/telemetry.ts
var retryingFetch = (0, import_fetch_retry.default)(fetch), URL = process.env.STORYBOOK_TELEMETRY_URL || "https://storybook.js.org/event-log", tasks = [], addToGlobalContext = (key, value) => {
globalContext[key] = value;
}, getOperatingSystem = () => {
try {
let platform2 = os.platform();
return platform2 === "win32" ? "Windows" : platform2 === "darwin" ? "macOS" : platform2 === "linux" ? "Linux" : `Other: ${platform2}`;
} catch {
return "Unknown";
}
}, globalContext = {
inCI: isCI2(),
isTTY: process.stdout.isTTY,
platform: getOperatingSystem(),
nodeVersion: process.versions.node,
storybookVersion: getVersionNumber()
}, prepareRequest = async (data, context, options) => {
let { eventType, payload, metadata, ...rest } = data, sessionId = await getSessionId(), eventId = nanoid(), body = { ...rest, eventType, eventId, sessionId, metadata, payload, context };
return retryingFetch(URL, {
method: "post",
body: JSON.stringify(body),
headers: { "Content-Type": "application/json" },
retries: 3,
retryOn: [503, 504],
retryDelay: (attempt) => 2 ** attempt * (typeof options?.retryDelay == "number" && !Number.isNaN(options?.retryDelay) ? options.retryDelay : 1e3)
});
};
function getVersionNumber() {
try {
return JSON.parse(readFileSync2(join2(resolvePackageDir("storybook"), "package.json"), "utf8")).version;
} catch {
return version;
}
}
async function sendTelemetry(data, options = { retryDelay: 1e3, immediate: !1 }) {
let { eventType, payload, metadata, ...rest } = data, context = options.stripMetadata ? globalContext : {
...globalContext,
anonymousId: getAnonymousProjectId()
}, request;
try {
request = prepareRequest(data, context, options), tasks.push(request);
let sessionId = await getSessionId(), eventId = nanoid(), body = { ...rest, eventType, eventId, sessionId, metadata, payload, context }, waitFor = options.immediate ? tasks : [request];
await Promise.all([...waitFor, set(eventType, body)]);
} catch {
} finally {
tasks = tasks.filter((task) => task !== request);
}
}
// src/telemetry/error-collector.ts
var ErrorCollector = class _ErrorCollector {
constructor() {
this.errors = [];
}
static getInstance() {
return _ErrorCollector.instance || (_ErrorCollector.instance = new _ErrorCollector()), _ErrorCollector.instance;
}
static addError(error) {
this.getInstance().errors.push(error);
}
static getErrors() {
return this.getInstance().errors;
}
};
// src/telemetry/index.ts
var isExampleStoryId = (storyId) => storyId.startsWith("example-button--") || storyId.startsWith("example-header--") || storyId.startsWith("example-page--"), telemetry = async (eventType, payload = {}, options = {}) => {
eventType !== "boot" && options.notify !== !1 && await notify();
let telemetryData = {
eventType,
payload
};
try {
options?.stripMetadata || (telemetryData.metadata = await getStorybookMetadata(options?.configDir));
} catch (error) {
payload.metadataErrorMessage = sanitizeError(error).message, options?.enableCrashReports && (payload.metadataError = sanitizeError(error));
} finally {
let { error } = payload;
error && (payload.error = sanitizeError(error)), (!payload.error || options?.enableCrashReports) && (process.env?.STORYBOOK_TELEMETRY_DEBUG && (logger2.info("[telemetry]"), logger2.info(JSON.stringify(telemetryData, null, 2))), await sendTelemetry(telemetryData, options));
}
};
export {
removeAnsiEscapeCodes,
cleanPaths,
sanitizeError,
metaFrameworks,
sanitizeAddonName,
computeStorybookMetadata,
getStorybookMetadata,
oneWayHash,
addToGlobalContext,
ErrorCollector,
isExampleStoryId,
telemetry
};