diff --git a/lib/telemetry/src/storybook-metadata.test.ts b/lib/telemetry/src/storybook-metadata.test.ts index d3366777160c..380c5343a6da 100644 --- a/lib/telemetry/src/storybook-metadata.test.ts +++ b/lib/telemetry/src/storybook-metadata.test.ts @@ -1,6 +1,7 @@ import type { PackageJson, StorybookConfig } from '@storybook/core-common'; -import { computeStorybookMetadata, metaFrameworks } from './storybook-metadata'; +import path from 'path'; +import { computeStorybookMetadata, metaFrameworks, sanitizeAddonName } from './storybook-metadata'; const packageJsonMock: PackageJson = { name: 'some-user-project', @@ -38,6 +39,60 @@ jest.mock('detect-package-manager', () => ({ getNpmVersion: () => '3.1.1', })); +describe('sanitizeAddonName', () => { + const originalSep = path.sep; + beforeEach(() => { + // @ts-expect-error the property is read only but we can change it for testing purposes + path.sep = originalSep; + }); + + test('special addon names', () => { + const addonNames = [ + '@storybook/preset-create-react-app', + 'storybook-addon-deprecated/register', + 'storybook-addon-ends-with-js/register.js', + '@storybook/addon-knobs/preset', + '@storybook/addon-ends-with-js/preset.js', + '@storybook/addon-postcss/dist/index.js', + '../local-addon/register.js', + '../../', + ].map(sanitizeAddonName); + + expect(addonNames).toEqual([ + '@storybook/preset-create-react-app', + 'storybook-addon-deprecated', + 'storybook-addon-ends-with-js', + '@storybook/addon-knobs', + '@storybook/addon-ends-with-js', + '@storybook/addon-postcss', + '../local-addon', + '../../', + ]); + }); + + test('Windows paths', () => { + // @ts-expect-error the property is read only but we can change it for testing purposes + path.sep = '\\'; + const cwdMockPath = `C:\\Users\\username\\storybook-app`; + jest.spyOn(process, `cwd`).mockImplementationOnce(() => cwdMockPath); + + expect(sanitizeAddonName(`${cwdMockPath}\\local-addon\\themes.js`)).toEqual( + '$SNIP\\local-addon\\themes' + ); + }); + + test('Linux paths', () => { + // @ts-expect-error the property is read only but we can change it for testing purposes + path.sep = '/'; + const cwdMockPath = `/Users/username/storybook-app`; + jest.spyOn(process, `cwd`).mockImplementationOnce(() => cwdMockPath); + + expect(sanitizeAddonName(`${cwdMockPath}/local-addon/themes.js`)).toEqual( + '$SNIP/local-addon/themes' + ); + }); +}); + describe('await computeStorybookMetadata', () => { test('should return frameworkOptions from mainjs', async () => { const reactResult = await computeStorybookMetadata({ diff --git a/lib/telemetry/src/storybook-metadata.ts b/lib/telemetry/src/storybook-metadata.ts index 562cb132e406..003314c0ff81 100644 --- a/lib/telemetry/src/storybook-metadata.ts +++ b/lib/telemetry/src/storybook-metadata.ts @@ -11,6 +11,7 @@ import type { StorybookConfig, PackageJson } from '@storybook/core-common'; import type { StorybookMetadata, Dependency, StorybookAddon } from './types'; import { getActualPackageVersion, getActualPackageVersions } from './package-versions'; import { getMonorepoType } from './get-monorepo-type'; +import { cleanPaths } from './sanitize'; export const metaFrameworks = { next: 'Next', @@ -47,6 +48,15 @@ const getFrameworkOptions = (mainConfig: any) => { return undefined; }; +export const sanitizeAddonName = (name: string) => { + return cleanPaths(name) + .replace(/\/dist\/.*/, '') + .replace(/\.[mc]?[tj]?s[x]?$/, '') + .replace(/\/register$/, '') + .replace(/\/manager$/, '') + .replace(/\/preset$/, ''); +}; + // Analyze a combination of information from main.js and package.json // to provide telemetry over a Storybook project export const computeStorybookMetadata = async ({ @@ -128,16 +138,17 @@ export const computeStorybookMetadata = async ({ const addons: Record = {}; if (mainConfig.addons) { mainConfig.addons.forEach((addon) => { - let result; + let addonName; let options; + if (typeof addon === 'string') { - result = addon.replace('/register', '').replace('/preset', ''); + addonName = sanitizeAddonName(addon); } else { options = addon.options; - result = addon.name; + addonName = sanitizeAddonName(addon.name); } - addons[result] = { + addons[addonName] = { options, version: undefined, };