storybook
Version:
Storybook: Develop, document, and test UI components in isolation
1,199 lines (1,167 loc) • 39.3 kB
JavaScript
import CJS_COMPAT_NODE_URL_q99y7iqlbzn from 'node:url';
import CJS_COMPAT_NODE_PATH_q99y7iqlbzn from 'node:path';
import CJS_COMPAT_NODE_MODULE_q99y7iqlbzn from "node:module";
var __filename = CJS_COMPAT_NODE_URL_q99y7iqlbzn.fileURLToPath(import.meta.url);
var __dirname = CJS_COMPAT_NODE_PATH_q99y7iqlbzn.dirname(__filename);
var require = CJS_COMPAT_NODE_MODULE_q99y7iqlbzn.createRequire(import.meta.url);
// ------------------------------------------------------------
// end of CJS compatibility banner, injected by Storybook's esbuild configuration
// ------------------------------------------------------------
import {
version
} from "./chunk-D2RKDVXH.js";
import {
createFileSystemCache,
resolvePathInStorybookCache,
up
} from "./chunk-CIADIQ6F.js";
import {
globalSettings
} from "./chunk-AOVN24BT.js";
import {
execaCommand,
getProjectRoot
} from "./chunk-CWTRQZTU.js";
import {
getSessionId,
nanoid,
set
} from "./chunk-ZHQALUV5.js";
import {
resolvePackageDir
} from "./chunk-7N53RHGS.js";
import {
slash
} from "./chunk-ALHPYGYU.js";
import {
require_picocolors
} from "./chunk-KABHBSS3.js";
import {
__commonJS,
__name,
__toESM
} from "./chunk-MB5KTO7X.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) {
defaults = defaults || {};
if (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: []
};
defaults = Object.assign(baseDefaults, defaults);
return /* @__PURE__ */ __name(function fetchRetry(input, init) {
var retries = defaults.retries;
var retryDelay = defaults.retryDelay;
var 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(resolve, reject) {
var wrappedFetch = /* @__PURE__ */ __name(function(attempt) {
var _input = typeof Request !== "undefined" && input instanceof Request ? input.clone() : input;
fetch2(_input, init).then(function(response) {
if (Array.isArray(retryOn) && retryOn.indexOf(response.status) === -1) {
resolve(response);
} else if (typeof retryOn === "function") {
try {
return Promise.resolve(retryOn(attempt, null, response)).then(function(retryOnResponse) {
if (retryOnResponse) {
retry2(attempt, null, response);
} else {
resolve(response);
}
}).catch(reject);
} catch (error) {
reject(error);
}
} else {
if (attempt < retries) {
retry2(attempt, null, response);
} else {
resolve(response);
}
}
}).catch(function(error) {
if (typeof retryOn === "function") {
try {
Promise.resolve(retryOn(attempt, error, null)).then(function(retryOnResponse) {
if (retryOnResponse) {
retry2(attempt, error, null);
} else {
reject(error);
}
}).catch(function(error2) {
reject(error2);
});
} catch (error2) {
reject(error2);
}
} else if (attempt < retries) {
retry2(attempt, error, null);
} else {
reject(error);
}
});
}, "wrappedFetch");
function retry2(attempt, error, response) {
var delay = typeof retryDelay === "function" ? retryDelay(attempt, error, response) : retryDelay;
setTimeout(function() {
wrappedFetch(++attempt);
}, delay);
}
__name(retry2, "retry");
wrappedFetch(0);
});
}, "fetchRetry");
};
function isPositiveInteger(value) {
return Number.isInteger(value) && value >= 0;
}
__name(isPositiveInteger, "isPositiveInteger");
function ArgumentError(message) {
this.name = "ArgumentError";
this.message = message;
}
__name(ArgumentError, "ArgumentError");
}
});
// src/telemetry/index.ts
import { logger as logger2 } from "storybook/internal/node-logger";
// src/telemetry/notify.ts
var import_picocolors = __toESM(require_picocolors(), 1);
import { cache } from "storybook/internal/common";
import { CLI_COLORS, logger } from "storybook/internal/node-logger";
var TELEMETRY_KEY_NOTIFY_DATE = "telemetry-notification-date";
var notify = /* @__PURE__ */ __name(async () => {
const telemetryNotifyDate = await cache.get(TELEMETRY_KEY_NOTIFY_DATE, null);
if (telemetryNotifyDate) {
return;
}
cache.set(TELEMETRY_KEY_NOTIFY_DATE, Date.now());
logger.log(
`${CLI_COLORS.info("Attention:")} Storybook now collects completely anonymous telemetry regarding usage. This information is used to shape Storybook's roadmap and prioritize features.`
);
logger.log(
`You can learn more, including how to opt-out if you'd not like to participate in this anonymous program, by visiting the following URL:`
);
logger.log(import_picocolors.default.cyan("https://storybook.js.org/telemetry"));
logger.log("");
}, "notify");
// src/telemetry/sanitize.ts
import path from "node:path";
function regexpEscape(str) {
return str.replace(/[-[/{}()*+?.\\^$|]/g, `\\$&`);
}
__name(regexpEscape, "regexpEscape");
function removeAnsiEscapeCodes(input = "") {
return input.replace(/\u001B\[[0-9;]*m/g, "");
}
__name(removeAnsiEscapeCodes, "removeAnsiEscapeCodes");
function cleanPaths(str, separator = path.sep) {
if (!str) {
return str;
}
const stack = process.cwd().split(separator);
while (stack.length > 1) {
const currentPath = stack.join(separator);
const currentRegex = new RegExp(regexpEscape(currentPath), `gi`);
str = str.replace(currentRegex, `$SNIP`);
const currentPath2 = stack.join(separator + separator);
const currentRegex2 = new RegExp(regexpEscape(currentPath2), `gi`);
str = str.replace(currentRegex2, `$SNIP`);
stack.pop();
}
return str;
}
__name(cleanPaths, "cleanPaths");
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
};
const errorString = cleanPaths(JSON.stringify(error), pathSeparator);
return JSON.parse(errorString);
} catch (err) {
return `Sanitization error: ${err?.message}`;
}
}
__name(sanitizeError, "sanitizeError");
// src/telemetry/storybook-metadata.ts
import { readFile } from "node:fs/promises";
import { dirname } from "node:path";
import {
getStorybookConfiguration,
getStorybookInfo,
isCI,
loadMainConfig,
versions
} 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, options) {
const process3 = execaCommand(command, { shell: true, buffer: false, ...options });
if (!process3.stdout) {
throw new Error("Unexpected missing stdout");
}
let lineCount = 0;
const rl = createInterface(process3.stdout);
rl.on("line", () => {
lineCount += 1;
});
await process3;
rl.close();
return lineCount;
}
__name(execCommandCountLines, "execCommandCountLines");
// src/telemetry/run-telemetry-operation.ts
var cache2 = createFileSystemCache({
basePath: resolvePathInStorybookCache("telemetry"),
ns: "storybook",
ttl: 24 * 60 * 60 * 1e3
// 24h
});
var runTelemetryOperation = /* @__PURE__ */ __name(async (cacheKey, operation) => {
let cached = await cache2.get(cacheKey);
if (cached === void 0) {
cached = await operation();
if (cached !== void 0) {
await cache2.set(cacheKey, cached);
}
}
return cached;
}, "runTelemetryOperation");
// src/telemetry/get-application-file-count.ts
var nameMatches = ["page", "screen"];
var extensions = ["js", "jsx", "ts", "tsx"];
var getApplicationFilesCountUncached = /* @__PURE__ */ __name(async (basePath) => {
const bothCasesNameMatches = nameMatches.flatMap((match) => [
match,
[match[0].toUpperCase(), ...match.slice(1)].join("")
]);
const globs = bothCasesNameMatches.flatMap(
(match) => extensions.map((extension) => `"${basePath}${sep}*${match}*.${extension}"`)
);
try {
const command = `git ls-files -- ${globs.join(" ")}`;
return await execCommandCountLines(command);
} catch {
return void 0;
}
}, "getApplicationFilesCountUncached");
var getApplicationFileCount = /* @__PURE__ */ __name(async (path3) => {
return runTelemetryOperation(
"applicationFiles",
async () => getApplicationFilesCountUncached(path3)
);
}, "getApplicationFileCount");
// src/telemetry/get-chromatic-version.ts
function getChromaticVersionSpecifier(packageJson) {
const dependency = packageJson.dependencies?.chromatic || packageJson.devDependencies?.chromatic || packageJson.peerDependencies?.chromatic;
if (dependency) {
return dependency;
}
return packageJson.scripts && Object.values(packageJson.scripts).find((s) => s?.match(/chromatic/)) ? "latest" : void 0;
}
__name(getChromaticVersionSpecifier, "getChromaticVersionSpecifier");
// src/telemetry/get-framework-info.ts
import { normalize } from "node:path";
import { frameworkPackages } from "storybook/internal/common";
// src/telemetry/package-json.ts
import { fileURLToPath, pathToFileURL } from "node:url";
var getActualPackageVersions = /* @__PURE__ */ __name(async (packages) => {
const packageNames = Object.keys(packages);
return Promise.all(packageNames.map(getActualPackageVersion));
}, "getActualPackageVersions");
var getActualPackageVersion = /* @__PURE__ */ __name(async (packageName) => {
try {
const packageJson = await getActualPackageJson(packageName);
return {
name: packageJson?.name || packageName,
version: packageJson?.version || null
};
} catch (err) {
return {
name: packageName,
version: null
};
}
}, "getActualPackageVersion");
var getActualPackageJson = /* @__PURE__ */ __name(async (packageName) => {
try {
let resolvedPackageJsonPath = up({
cwd: fileURLToPath(import.meta.resolve(packageName, process.cwd()))
});
if (!resolvedPackageJsonPath) {
resolvedPackageJsonPath = import.meta.resolve(`${packageName}/package.json`, process.cwd());
}
const { default: packageJson } = await import(pathToFileURL(resolvedPackageJsonPath).href, {
with: { type: "json" }
});
return packageJson;
} catch (err) {
return void 0;
}
}, "getActualPackageJson");
// src/telemetry/get-framework-info.ts
var knownRenderers = [
"html",
"react",
"svelte",
"vue3",
"preact",
"server",
"vue",
"web-components",
"angular",
"ember"
];
var knownBuilders = ["builder-webpack5", "builder-vite"];
function findMatchingPackage(packageJson, suffixes) {
const { name = "", version: version2, dependencies, devDependencies, peerDependencies } = packageJson;
const allDependencies = {
// We include the framework itself because it may be a renderer too (e.g. angular)
[name]: version2,
...dependencies,
...devDependencies,
...peerDependencies
};
return suffixes.map((suffix) => `@storybook/${suffix}`).find((pkg) => allDependencies[pkg]);
}
__name(findMatchingPackage, "findMatchingPackage");
var getFrameworkPackageName = /* @__PURE__ */ __name((packageNameOrPath) => {
const normalizedPath = normalize(packageNameOrPath).replace(new RegExp(/\\/, "g"), "/");
const knownFramework = Object.keys(frameworkPackages).find((pkg) => normalizedPath.endsWith(pkg));
return knownFramework || cleanPaths(packageNameOrPath).replace(/.*node_modules[\\/]/, "");
}, "getFrameworkPackageName");
async function getFrameworkInfo(mainConfig) {
if (!mainConfig?.framework) {
return {};
}
const rawName = typeof mainConfig.framework === "string" ? mainConfig.framework : mainConfig.framework?.name;
if (!rawName) {
return {};
}
const frameworkPackageJson = await getActualPackageJson(rawName);
if (!frameworkPackageJson) {
return {};
}
const builder = findMatchingPackage(frameworkPackageJson, knownBuilders);
const renderer = findMatchingPackage(frameworkPackageJson, knownRenderers);
const sanitizedFrameworkName = getFrameworkPackageName(rawName);
const frameworkOptions = typeof mainConfig.framework === "object" ? mainConfig.framework.options : {};
return {
framework: {
name: sanitizedFrameworkName,
options: frameworkOptions
},
builder,
renderer
};
}
__name(getFrameworkInfo, "getFrameworkInfo");
// 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)
);
}
__name(getHasRouterPackage, "getHasRouterPackage");
// 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"
};
var getMonorepoType = /* @__PURE__ */ __name(() => {
const keys = Object.keys(monorepoConfigs);
const monorepoType = keys.find((monorepo) => {
const configFile = join(getProjectRoot2(), monorepoConfigs[monorepo]);
return existsSync(configFile);
});
if (monorepoType) {
return monorepoType;
}
if (!existsSync(join(getProjectRoot2(), "package.json"))) {
return void 0;
}
const packageJson = JSON.parse(
readFileSync(join(getProjectRoot2(), "package.json"), { encoding: "utf8" })
);
if (packageJson?.workspaces) {
return "Workspaces";
}
return void 0;
}, "getMonorepoType");
// ../node_modules/package-manager-detector/dist/commands.mjs
function dashDashArg(agent, agentCommand) {
return (args) => {
if (args.length > 1) {
return [agent, agentCommand, args[0], "--", ...args.slice(1)];
} else {
return [agent, agentCommand, args[0]];
}
};
}
__name(dashDashArg, "dashDashArg");
function denoExecute() {
return (args) => {
return ["deno", "run", `npm:${args[0]}`, ...args.slice(1)];
};
}
__name(denoExecute, "denoExecute");
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,
"execute": ["npx", 0],
"execute-local": ["npx", 0],
"uninstall": ["npm", "uninstall", 0],
"global_uninstall": ["npm", "uninstall", "-g", 0]
};
var 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],
"execute": ["npx", 0],
"execute-local": dashDashArg("yarn", "exec"),
"uninstall": ["yarn", "remove", 0],
"global_uninstall": ["yarn", "global", "remove", 0]
};
var yarnBerry = {
...yarn,
"frozen": ["yarn", "install", "--immutable", 0],
"upgrade": ["yarn", "up", 0],
"upgrade-interactive": ["yarn", "up", "-i", 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]
};
var 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],
"execute": ["pnpm", "dlx", 0],
"execute-local": ["pnpm", "exec", 0],
"uninstall": ["pnpm", "remove", 0],
"global_uninstall": ["pnpm", "remove", "--global", 0]
};
var 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", 0],
"execute": ["bun", "x", 0],
"execute-local": ["bun", "x", 0],
"uninstall": ["bun", "remove", 0],
"global_uninstall": ["bun", "remove", "-g", 0]
};
var 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],
"execute": denoExecute(),
"execute-local": ["deno", "task", "--eval", 0],
"uninstall": ["deno", "remove", 0],
"global_uninstall": ["deno", "uninstall", "-g", 0]
};
var COMMANDS = {
"npm": npm,
"yarn": yarn,
"yarn@berry": yarnBerry,
"pnpm": pnpm,
// pnpm v6.x or below
"pnpm@6": {
...pnpm,
run: dashDashArg("pnpm", "run")
},
"bun": bun,
"deno": deno
};
// ../node_modules/package-manager-detector/dist/constants.mjs
var AGENTS = [
"npm",
"yarn",
"yarn@berry",
"pnpm",
"pnpm@6",
"bun",
"deno"
];
var 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"
};
var 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 {
const stat = await fs.stat(path22);
return type === "file" ? stat.isFile() : stat.isDirectory();
} catch {
return false;
}
}
__name(pathExists, "pathExists");
function* lookup(cwd = process2.cwd()) {
let directory = path2.resolve(cwd);
const { root } = path2.parse(directory);
while (directory && directory !== root) {
yield directory;
directory = path2.dirname(directory);
}
}
__name(lookup, "lookup");
async function parsePackageJson(filepath, onUnknown) {
return !filepath || !pathExists(filepath, "file") ? null : await handlePackageManager(filepath, onUnknown);
}
__name(parsePackageJson, "parsePackageJson");
async function detect(options = {}) {
const {
cwd,
strategies = ["lockfile", "packageManager-field", "devEngines-field"],
onUnknown
} = options;
let stopDir;
if (typeof options.stopDir === "string") {
const resolved = path2.resolve(options.stopDir);
stopDir = /* @__PURE__ */ __name((dir) => dir === resolved, "stopDir");
} else {
stopDir = options.stopDir;
}
for (const directory of lookup(cwd)) {
for (const strategy of strategies) {
switch (strategy) {
case "lockfile": {
for (const lock of Object.keys(LOCKS)) {
if (await pathExists(path2.join(directory, lock), "file")) {
const name = LOCKS[lock];
const result = await parsePackageJson(path2.join(directory, "package.json"), onUnknown);
if (result)
return result;
else
return { name, agent: name };
}
}
break;
}
case "packageManager-field":
case "devEngines-field": {
const result = await parsePackageJson(path2.join(directory, "package.json"), onUnknown);
if (result)
return result;
break;
}
case "install-metadata": {
for (const metadata of Object.keys(INSTALL_METADATA)) {
const fileOrDir = metadata.endsWith("/") ? "dir" : "file";
if (await pathExists(path2.join(directory, metadata), fileOrDir)) {
const name = INSTALL_METADATA[metadata];
const agent = name === "yarn" ? isMetadataYarnClassic(metadata) ? "yarn" : "yarn@berry" : name;
return { name, agent };
}
}
break;
}
}
}
if (stopDir?.(directory))
break;
}
return null;
}
__name(detect, "detect");
function getNameAndVer(pkg) {
const handelVer = /* @__PURE__ */ __name((version2) => version2?.match(/\d+(\.\d+){0,2}/)?.[0] ?? version2, "handelVer");
if (typeof pkg.packageManager === "string") {
const [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)
};
}
return void 0;
}
__name(getNameAndVer, "getNameAndVer");
async function handlePackageManager(filepath, onUnknown) {
try {
const pkg = JSON.parse(await fs.readFile(filepath, "utf8"));
let agent;
const nameAndVer = getNameAndVer(pkg);
if (nameAndVer) {
const name = nameAndVer.name;
const ver = nameAndVer.ver;
let version2 = ver;
if (name === "yarn" && ver && Number.parseInt(ver) > 1) {
agent = "yarn@berry";
version2 = "berry";
return { name, agent, version: version2 };
} else if (name === "pnpm" && ver && Number.parseInt(ver) < 7) {
agent = "pnpm@6";
return { name, agent, version: version2 };
} else if (AGENTS.includes(name)) {
agent = name;
return { name, agent, version: version2 };
} else {
return onUnknown?.(pkg.packageManager) ?? null;
}
}
} catch {
}
return null;
}
__name(handlePackageManager, "handlePackageManager");
function isMetadataYarnClassic(metadataPath) {
return metadataPath.endsWith(".yarn_integrity");
}
__name(isMetadataYarnClassic, "isMetadataYarnClassic");
// src/telemetry/get-package-manager-info.ts
var getPackageManagerInfo = /* @__PURE__ */ __name(async () => {
const packageManagerType = await detect({ cwd: getProjectRoot() });
if (!packageManagerType) {
return void 0;
}
let nodeLinker = "node_modules";
if (packageManagerType.name === "yarn") {
try {
const { stdout } = await execaCommand("yarn config get nodeLinker", {
cwd: getProjectRoot()
});
nodeLinker = stdout.trim();
} catch (e) {
}
}
if (packageManagerType.name === "pnpm") {
try {
const { stdout } = await execaCommand("pnpm config get nodeLinker", {
cwd: getProjectRoot()
});
nodeLinker = stdout.trim() ?? "isolated";
} catch (e) {
}
}
return {
type: packageManagerType.name,
version: packageManagerType.version,
agent: packageManagerType.agent,
nodeLinker
};
}, "getPackageManagerInfo");
// src/telemetry/get-portable-stories-usage.ts
var getPortableStoriesFileCountUncached = /* @__PURE__ */ __name(async (path3) => {
try {
const command = `git grep -l composeStor` + (path3 ? ` -- ${path3}` : "");
return await execCommandCountLines(command);
} catch (err) {
return err.exitCode === 1 ? 0 : void 0;
}
}, "getPortableStoriesFileCountUncached");
var getPortableStoriesFileCount = /* @__PURE__ */ __name(async (path3) => {
return runTelemetryOperation(
"portableStories",
async () => getPortableStoriesFileCountUncached(path3)
);
}, "getPortableStoriesFileCount");
// 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"
};
var sanitizeAddonName = /* @__PURE__ */ __name((name) => {
return cleanPaths(name).replace(/\/dist\/.*/, "").replace(/\.[mc]?[tj]?s[x]?$/, "").replace(/\/register$/, "").replace(/\/manager$/, "").replace(/\/preset$/, "");
}, "sanitizeAddonName");
var computeStorybookMetadata = /* @__PURE__ */ __name(async ({
packageJsonPath,
packageJson,
mainConfig,
configDir
}) => {
const settings = isCI() ? void 0 : await globalSettings();
const metadata = {
generatedAt: (/* @__PURE__ */ new Date()).getTime(),
userSince: settings?.value.userSince,
hasCustomBabel: false,
hasCustomWebpack: false,
hasStaticDirs: false,
hasStorybookEslint: false,
refCount: 0
};
const allDependencies = {
...packageJson?.dependencies,
...packageJson?.devDependencies,
...packageJson?.peerDependencies
};
const metaFramework = Object.keys(allDependencies).find((dep) => !!metaFrameworks[dep]);
if (metaFramework) {
const { version: version2 } = await getActualPackageVersion(metaFramework);
metadata.metaFramework = {
name: metaFrameworks[metaFramework],
packageName: metaFramework,
version: version2 || "unknown"
};
}
const testPackages = [
"playwright",
"vitest",
"jest",
"cypress",
"nightwatch",
"webdriver",
"@web/test-runner",
"puppeteer",
"karma",
"jasmine",
"chai",
"testing-library",
"@ngneat/spectator",
"wdio",
"msw",
"miragejs",
"sinon",
"chromatic"
];
const 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);
const monorepoType = getMonorepoType();
if (monorepoType) {
metadata.monorepo = monorepoType;
}
metadata.packageManager = await getPackageManagerInfo();
const 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;
if (typeof mainConfig.typescript === "object") {
metadata.typescriptOptions = mainConfig.typescript;
}
const frameworkInfo = await getFrameworkInfo(mainConfig);
if (typeof mainConfig.refs === "object") {
metadata.refCount = Object.keys(mainConfig.refs).length;
}
if (typeof mainConfig.features === "object") {
metadata.features = mainConfig.features;
}
const addons = {};
if (mainConfig.addons) {
mainConfig.addons.forEach((addon) => {
let addonName;
let options;
if (typeof addon === "string") {
addonName = sanitizeAddonName(addon);
} else {
if (addon.name.includes("addon-essentials")) {
options = addon.options;
}
addonName = sanitizeAddonName(addon.name);
}
addons[addonName] = {
options,
version: void 0
};
});
}
const chromaticVersionSpecifier = getChromaticVersionSpecifier(packageJson);
if (chromaticVersionSpecifier) {
addons.chromatic = {
version: void 0,
versionSpecifier: chromaticVersionSpecifier,
options: void 0
};
}
const addonVersions = await getActualPackageVersions(addons);
addonVersions.forEach(({ name, version: version2 }) => {
addons[name] = addons[name] || {
name,
version: version2
};
addons[name].version = version2 || void 0;
});
const addonNames = Object.keys(addons);
const storybookPackages = Object.keys(allDependencies).filter((dep) => dep.includes("storybook") && !addonNames.includes(dep)).reduce((acc, dep) => {
return {
...acc,
[dep]: { version: void 0 }
};
}, {});
const storybookPackageVersions = await getActualPackageVersions(storybookPackages);
storybookPackageVersions.forEach(({ name, version: version2 }) => {
storybookPackages[name] = storybookPackages[name] || {
name,
version: version2
};
storybookPackages[name].version = version2 || void 0;
});
const hasStorybookEslint = !!allDependencies["eslint-plugin-storybook"];
const storybookInfo = await getStorybookInfo(configDir);
try {
const { previewConfigPath: previewConfig } = storybookInfo;
if (previewConfig) {
const config = await readConfig(previewConfig);
const usesGlobals = !!(config.getFieldNode(["globals"]) || config.getFieldNode(["globalTypes"]));
metadata.preview = { ...metadata.preview, usesGlobals };
}
} catch (e) {
}
const portableStoriesFileCount = await getPortableStoriesFileCount();
const applicationFileCount = await getApplicationFileCount(dirname(packageJsonPath));
return {
...metadata,
...frameworkInfo,
portableStoriesFileCount,
applicationFileCount,
storybookVersion: version,
storybookVersionSpecifier: storybookInfo.version,
language,
storybookPackages,
addons,
hasStorybookEslint
};
}, "computeStorybookMetadata");
async function getPackageJsonDetails() {
const packageJsonPath = up();
if (packageJsonPath) {
return {
packageJsonPath,
packageJson: JSON.parse(await readFile(packageJsonPath, "utf8"))
};
}
return {
packageJsonPath: process.cwd(),
packageJson: {}
};
}
__name(getPackageJsonDetails, "getPackageJsonDetails");
var cachedMetadata;
var getStorybookMetadata = /* @__PURE__ */ __name(async (_configDir) => {
if (cachedMetadata) {
return cachedMetadata;
}
const { packageJson, packageJsonPath } = await getPackageJsonDetails();
const configDir = (_configDir || getStorybookConfiguration(
String(packageJson?.scripts?.storybook || ""),
"-c",
"--config-dir"
)) ?? ".storybook";
const mainConfig = await loadMainConfig({ configDir }).catch(() => void 0);
cachedMetadata = await computeStorybookMetadata({
mainConfig,
packageJson,
packageJsonPath,
configDir
});
return cachedMetadata;
}, "getStorybookMetadata");
// 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 } from "crypto";
var oneWayHash = /* @__PURE__ */ __name((payload) => {
const hash = createHash("sha256");
hash.update("storybook-telemetry-salt");
hash.update(payload);
return hash.digest("hex");
}, "oneWayHash");
// src/telemetry/anonymous-id.ts
function normalizeGitUrl(rawUrl) {
const urlWithoutHash = rawUrl.trim().replace(/#.*$/, "");
const urlWithoutUser = urlWithoutHash.replace(/^.*@/, "");
const urlWithoutScheme = urlWithoutUser.replace(/^.*\/\//, "");
const urlWithExtension = urlWithoutScheme.endsWith(".git") ? urlWithoutScheme : `${urlWithoutScheme}.git`;
return urlWithExtension.replace(":", "/");
}
__name(normalizeGitUrl, "normalizeGitUrl");
function unhashedProjectId(remoteUrl, projectRootPath) {
return `${normalizeGitUrl(remoteUrl)}${slash(projectRootPath)}`;
}
__name(unhashedProjectId, "unhashedProjectId");
var anonymousProjectId;
var getAnonymousProjectId = /* @__PURE__ */ __name(() => {
if (anonymousProjectId) {
return anonymousProjectId;
}
try {
const projectRootPath = relative(getProjectRoot3(), process.cwd());
const originBuffer = execSync(`git config --local --get remote.origin.url`, {
timeout: 1e3,
stdio: `pipe`
});
anonymousProjectId = oneWayHash(unhashedProjectId(String(originBuffer), projectRootPath));
} catch (_) {
}
return anonymousProjectId;
}, "getAnonymousProjectId");
// src/telemetry/fetch.ts
var fetch = global.fetch;
// src/telemetry/telemetry.ts
var retryingFetch = (0, import_fetch_retry.default)(fetch);
var URL = process.env.STORYBOOK_TELEMETRY_URL || "https://storybook.js.org/event-log";
var tasks = [];
var addToGlobalContext = /* @__PURE__ */ __name((key, value) => {
globalContext[key] = value;
}, "addToGlobalContext");
var getOperatingSystem = /* @__PURE__ */ __name(() => {
try {
const platform2 = os.platform();
if (platform2 === "win32") {
return "Windows";
}
if (platform2 === "darwin") {
return "macOS";
}
if (platform2 === "linux") {
return "Linux";
}
return `Other: ${platform2}`;
} catch (_err) {
return "Unknown";
}
}, "getOperatingSystem");
var globalContext = {
inCI: isCI2(),
isTTY: process.stdout.isTTY,
platform: getOperatingSystem(),
nodeVersion: process.versions.node,
storybookVersion: getVersionNumber()
};
var prepareRequest = /* @__PURE__ */ __name(async (data, context, options) => {
const { eventType, payload, metadata, ...rest } = data;
const sessionId = await getSessionId();
const eventId = nanoid();
const 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: /* @__PURE__ */ __name((attempt) => 2 ** attempt * (typeof options?.retryDelay === "number" && !Number.isNaN(options?.retryDelay) ? options.retryDelay : 1e3), "retryDelay")
});
}, "prepareRequest");
function getVersionNumber() {
try {
return JSON.parse(readFileSync2(join2(resolvePackageDir("storybook"), "package.json"), "utf8")).version;
} catch (e) {
return version;
}
}
__name(getVersionNumber, "getVersionNumber");
async function sendTelemetry(data, options = { retryDelay: 1e3, immediate: false }) {
const { eventType, payload, metadata, ...rest } = data;
const context = options.stripMetadata ? globalContext : {
...globalContext,
anonymousId: getAnonymousProjectId()
};
let request;
try {
request = prepareRequest(data, context, options);
tasks.push(request);
const sessionId = await getSessionId();
const eventId = nanoid();
const body = { ...rest, eventType, eventId, sessionId, metadata, payload, context };
const waitFor = options.immediate ? tasks : [request];
await Promise.all([...waitFor, set(eventType, body)]);
} catch (err) {
} finally {
tasks = tasks.filter((task) => task !== request);
}
}
__name(sendTelemetry, "sendTelemetry");
// src/telemetry/index.ts
var isExampleStoryId = /* @__PURE__ */ __name((storyId) => storyId.startsWith("example-button--") || storyId.startsWith("example-header--") || storyId.startsWith("example-page--"), "isExampleStoryId");
var telemetry = /* @__PURE__ */ __name(async (eventType, payload = {}, options = {}) => {
if (eventType !== "boot" && options.notify !== false) {
await notify();
}
const telemetryData = {
eventType,
payload
};
try {
if (!options?.stripMetadata) {
telemetryData.metadata = await getStorybookMetadata(options?.configDir);
}
} catch (error) {
payload.metadataErrorMessage = sanitizeError(error).message;
if (options?.enableCrashReports) {
payload.metadataError = sanitizeError(error);
}
} finally {
const { error } = payload;
if (error) {
payload.error = sanitizeError(error);
}
if (!payload.error || options?.enableCrashReports) {
if (process.env?.STORYBOOK_TELEMETRY_DEBUG) {
logger2.info("\n[telemetry]");
logger2.info(JSON.stringify(telemetryData, null, 2));
}
await sendTelemetry(telemetryData, options);
}
}
}, "telemetry");
export {
removeAnsiEscapeCodes,
cleanPaths,
sanitizeError,
metaFrameworks,
sanitizeAddonName,
computeStorybookMetadata,
getStorybookMetadata,
oneWayHash,
addToGlobalContext,
isExampleStoryId,
telemetry
};