Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,5 @@ CLAUDE.local.md
.cursor/mcp.json
.vscode/mcp.json
.mcp.json
.nx/polygraph
.nx/polygraph
.omc
6 changes: 0 additions & 6 deletions code/addons/onboarding/src/preset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,6 @@ type Event = {
};

export const experimental_serverChannel = async (channel: Channel, options: Options) => {
const { disableTelemetry } = await options.presets.apply<CoreConfig>('core', {});

if (disableTelemetry) {
return channel;
}

channel.on(ADDON_ONBOARDING_CHANNEL, ({ type, ...event }: Event) => {
if (type === 'telemetry') {
telemetry('addon-onboarding', { ...event, addonVersion });
Expand Down
72 changes: 35 additions & 37 deletions code/addons/vitest/src/preset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,49 +257,47 @@ export const experimental_serverChannel = async (channel: Channel, options: Opti
});
});

if (!core.disableTelemetry) {
const enableCrashReports = core.enableCrashReports || options.enableCrashReports;

channel.on(STORYBOOK_ADDON_TEST_CHANNEL, (event: Event) => {
if (event.type !== 'test-run-completed') {
telemetry('addon-test', {
...event,
payload: {
...event.payload,
storyId: oneWayHash(event.payload.storyId),
},
});
}
});

store.subscribe('TOGGLE_WATCHING', async (event) => {
await telemetry('addon-test', {
watchMode: event.payload.to,
const enableCrashReports = core.enableCrashReports || options.enableCrashReports;

channel.on(STORYBOOK_ADDON_TEST_CHANNEL, (event: Event) => {
if (event.type !== 'test-run-completed') {
telemetry('addon-test', {
...event,
payload: {
...event.payload,
storyId: oneWayHash(event.payload.storyId),
},
});
}
});

store.subscribe('TOGGLE_WATCHING', async (event) => {
await telemetry('addon-test', {
watchMode: event.payload.to,
});
store.subscribe('TEST_RUN_COMPLETED', async (event) => {
const { unhandledErrors, startedAt, finishedAt, ...currentRun } = event.payload;
await telemetry('addon-test', {
...currentRun,
duration: (finishedAt ?? 0) - (startedAt ?? 0),
unhandledErrorCount: unhandledErrors.length,
...(enableCrashReports &&
unhandledErrors.length > 0 && {
unhandledErrors: unhandledErrors.map((error) => {
const { stacks, ...errorWithoutStacks } = error;
return sanitizeError(errorWithoutStacks);
}),
});
store.subscribe('TEST_RUN_COMPLETED', async (event) => {
const { unhandledErrors, startedAt, finishedAt, ...currentRun } = event.payload;
await telemetry('addon-test', {
...currentRun,
duration: (finishedAt ?? 0) - (startedAt ?? 0),
unhandledErrorCount: unhandledErrors.length,
...(enableCrashReports &&
unhandledErrors.length > 0 && {
unhandledErrors: unhandledErrors.map((error) => {
const { stacks, ...errorWithoutStacks } = error;
return sanitizeError(errorWithoutStacks);
}),
});
}),
});
});

if (enableCrashReports) {
store.subscribe('FATAL_ERROR', async (event) => {
await telemetry('addon-test', {
fatalError: cleanPaths(event.payload.error.message),
});
if (enableCrashReports) {
store.subscribe('FATAL_ERROR', async (event) => {
await telemetry('addon-test', {
fatalError: cleanPaths(event.payload.error.message),
});
}
});
}

return channel;
Expand Down
39 changes: 16 additions & 23 deletions code/addons/vitest/src/vitest-plugin/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import type { ViteUserConfig } from 'vitest/config';
import {
DEFAULT_FILES_PATTERN,
getInterpretedFile,
loadPreviewOrConfigFile,
normalizeStories,
optionalEnvToBoolean,
resolvePathInStorybookCache,
Expand All @@ -19,14 +18,9 @@ import {
experimental_loadStorybook,
mapStaticDir,
} from 'storybook/internal/core-server';
import {
componentTransform,
isCsfFactoryPreview,
readConfig,
vitestTransform,
} from 'storybook/internal/csf-tools';
import { componentTransform, readConfig, vitestTransform } from 'storybook/internal/csf-tools';
import { MainFileMissingError } from 'storybook/internal/server-errors';
import { telemetry } from 'storybook/internal/telemetry';
import { setTelemetryEnabled, telemetry } from 'storybook/internal/telemetry';
import { oneWayHash } from 'storybook/internal/telemetry';
import type { Presets } from 'storybook/internal/types';

Expand Down Expand Up @@ -448,22 +442,21 @@ export const storybookTest = async (options?: UserOptions): Promise<Plugin[]> =>
configureVitest(context) {
context.vitest.config.coverage.exclude.push('storybook-static');

if (
!core?.disableTelemetry &&
!optionalEnvToBoolean(process.env.STORYBOOK_DISABLE_TELEMETRY)
) {
// NOTE: we start telemetry immediately but do not wait on it. Typically it should complete
// before the tests do. If not we may miss the event, we are OK with that.
telemetry(
'test-run',
{
runner: 'vitest',
watch: context.vitest.config.watch,
coverage: !!context.vitest.config.coverage?.enabled,
},
{ configDir: finalOptions.configDir }
);
if (core?.disableTelemetry || optionalEnvToBoolean(process.env.STORYBOOK_DISABLE_TELEMETRY)) {
setTelemetryEnabled(false);
}

// NOTE: we start telemetry immediately but do not wait on it. Typically it should complete
// before the tests do. If not we may miss the event, we are OK with that.
telemetry(
'test-run',
{
runner: 'vitest',
watch: context.vitest.config.watch,
coverage: !!context.vitest.config.coverage?.enabled,
},
{ configDir: finalOptions.configDir }
);
},
async configureServer(server) {
if (staticDirs) {
Expand Down
12 changes: 7 additions & 5 deletions code/core/src/core-server/build-dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
} from 'storybook/internal/common';
import { CLI_COLORS, deprecate, logger, prompt } from 'storybook/internal/node-logger';
import { MissingBuilderError, NoStatsForViteDevError } from 'storybook/internal/server-errors';
import { oneWayHash, telemetry } from 'storybook/internal/telemetry';
import { oneWayHash, setTelemetryEnabled, telemetry } from 'storybook/internal/telemetry';
import type { BuilderOptions, CLIOptions, LoadOptions, Options } from 'storybook/internal/types';

import { global } from '@storybook/global';
Expand Down Expand Up @@ -181,6 +181,10 @@ export async function buildDevStandalone(

const { allowedHosts, renderer, builder, disableTelemetry } = await presets.apply('core', {});

if (disableTelemetry) {
setTelemetryEnabled(false);
}

// '0.0.0.0' binds to all interfaces, which is useful for Docker and other containerized environments.
// By default we allow requests from all hosts in this case, but the user should be made aware of the risk.
if (
Expand All @@ -198,10 +202,8 @@ export async function buildDevStandalone(
throw new MissingBuilderError();
}

if (!options.disableTelemetry && !disableTelemetry) {
if (versionCheck.success && !versionCheck.cached) {
telemetry('version-update');
}
if (versionCheck.success && !versionCheck.cached) {
telemetry('version-update');
}

const resolvedPreviewBuilder = typeof builder === 'string' ? builder : builder.name;
Expand Down
10 changes: 7 additions & 3 deletions code/core/src/core-server/build-static.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
resolveAddonName,
} from 'storybook/internal/common';
import { logger } from 'storybook/internal/node-logger';
import { getPrecedingUpgrade, telemetry } from 'storybook/internal/telemetry';
import { getPrecedingUpgrade, setTelemetryEnabled, telemetry } from 'storybook/internal/telemetry';
import type { BuilderOptions, CLIOptions, LoadOptions, Options } from 'storybook/internal/types';

import { global } from '@storybook/global';
Expand Down Expand Up @@ -111,8 +111,12 @@ export async function buildStaticStandalone(options: BuildStaticStandaloneOption
presets.apply('staticDirs'),
]);

if (core?.disableTelemetry) {
setTelemetryEnabled(false);
}

const invokedBy = process.env.STORYBOOK_INVOKED_BY;
if (!core?.disableTelemetry && invokedBy) {
if (invokedBy) {
// NOTE: we don't await this event to avoid slowing things down.
// This could result in telemetry events being lost.
telemetry('test-run', { runner: invokedBy, watch: false }, { configDir: options.configDir });
Expand Down Expand Up @@ -208,7 +212,7 @@ export async function buildStaticStandalone(options: BuildStaticStandaloneOption

// Now the code has successfully built, we can count this as a 'build' event.
// NOTE: we don't send the 'build' event for test runs as we want to be as fast as possible.
if (!core?.disableTelemetry && !options.test) {
if (!options.test) {
try {
const generator = await storyIndexGeneratorPromise;
const storyIndex = await generator?.getIndex();
Expand Down
6 changes: 2 additions & 4 deletions code/core/src/core-server/dev-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,10 +191,8 @@ export async function storybookDevServer(
process.exit(0);
}

if (!core?.disableTelemetry) {
process.on('SIGINT', cancelTelemetry);
process.on('SIGTERM', cancelTelemetry);
}
process.on('SIGINT', cancelTelemetry);
process.on('SIGTERM', cancelTelemetry);

return { previewResult, managerResult };
}
28 changes: 15 additions & 13 deletions code/core/src/core-server/presets/common-preset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
import { StoryIndexGenerator } from 'storybook/internal/core-server';
import { loadCsf } from 'storybook/internal/csf-tools';
import { logger } from 'storybook/internal/node-logger';
import { telemetry } from 'storybook/internal/telemetry';
import { setTelemetryEnabled, telemetry } from 'storybook/internal/telemetry';
import type {
CoreConfig,
Indexer,
Expand Down Expand Up @@ -176,12 +176,10 @@ export const experimental_serverAPI = (extension: Record<string, Function>, opti
const packageManager = JsPackageManagerFactory.getPackageManager({
configDir: options.configDir,
});
if (!options.disableTelemetry) {
removeAddon = async (id: string, opts: RemoveAddonOptions) => {
await telemetry('remove', { addon: id, source: 'api' });
return removeAddonBase(id, { ...opts, packageManager });
};
}
removeAddon = async (id: string, opts: RemoveAddonOptions) => {
await telemetry('remove', { addon: id, source: 'api' });
return removeAddonBase(id, { ...opts, packageManager });
};
return { ...extension, removeAddon };
};

Expand Down Expand Up @@ -274,14 +272,18 @@ export const experimental_serverChannel = async (
) => {
const coreOptions = await options.presets.apply('core');

if (coreOptions?.disableTelemetry) {
setTelemetryEnabled(false);
}

initializeChecklist();
initializeWhatsNew(channel, options, coreOptions);
initializeSaveStory(channel, options, coreOptions);
initializeWhatsNew(channel, options);
initializeSaveStory(channel, options);

initFileSearchChannel(channel, options, coreOptions);
initCreateNewStoryChannel(channel, options, coreOptions);
initGhostStoriesChannel(channel, options, coreOptions);
initOpenInEditorChannel(channel, options, coreOptions);
initFileSearchChannel(channel, options);
initCreateNewStoryChannel(channel, options);
initGhostStoriesChannel(channel, options);
initOpenInEditorChannel(channel);
initTelemetryChannel(channel, options);

return channel;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@ import {

import { initCreateNewStoryChannel } from './create-new-story-channel.ts';

vi.mock('storybook/internal/telemetry', async (importOriginal) => {
const actual = await importOriginal<typeof import('storybook/internal/telemetry')>();
return {
...actual,
telemetry: vi.fn(),
};
});

vi.mock('storybook/internal/common', async (importOriginal) => {
const actual = await importOriginal<typeof import('storybook/internal/common')>();
return {
Expand Down Expand Up @@ -56,23 +64,19 @@ describe('createNewStoryChannel', () => {
mockChannel.addListener(CREATE_NEW_STORYFILE_RESPONSE, createNewStoryFileEventListener);
const cwd = process.cwd();

initCreateNewStoryChannel(
mockChannel,
{
configDir: join(cwd, '.storybook'),
presets: {
apply: (val: string) => {
if (val === 'framework') {
return Promise.resolve('@storybook/nextjs');
}
if (val === 'stories') {
return Promise.resolve(['../src/**/*.stories.@(js|jsx|mjs|ts|tsx)']);
}
},
initCreateNewStoryChannel(mockChannel, {
configDir: join(cwd, '.storybook'),
presets: {
apply: (val: string) => {
if (val === 'framework') {
return Promise.resolve('@storybook/nextjs');
}
if (val === 'stories') {
return Promise.resolve(['../src/**/*.stories.@(js|jsx|mjs|ts|tsx)']);
}
},
} as any,
{ disableTelemetry: true }
);
},
} as any);

mockChannel.emit(CREATE_NEW_STORYFILE_REQUEST, {
id: 'components-page--default',
Expand Down Expand Up @@ -107,23 +111,19 @@ describe('createNewStoryChannel', () => {
throw new Error('Failed to write file');
});

initCreateNewStoryChannel(
mockChannel,
{
configDir: join(cwd, '.storybook'),
presets: {
apply: (val: string) => {
if (val === 'framework') {
return Promise.resolve('@storybook/nextjs');
}
if (val === 'stories') {
return Promise.resolve(['../src/**/*.stories.@(js|jsx|mjs|ts|tsx)']);
}
},
initCreateNewStoryChannel(mockChannel, {
configDir: join(cwd, '.storybook'),
presets: {
apply: (val: string) => {
if (val === 'framework') {
return Promise.resolve('@storybook/nextjs');
}
if (val === 'stories') {
return Promise.resolve(['../src/**/*.stories.@(js|jsx|mjs|ts|tsx)']);
}
},
} as any,
{ disableTelemetry: true }
);
},
} as any);

mockChannel.emit(CREATE_NEW_STORYFILE_REQUEST, {
id: 'components-page--default',
Expand Down
Loading
Loading