From 2ab061ad7f1259459baefd7f6b5a27f05209d653 Mon Sep 17 00:00:00 2001 From: Michael Shilman Date: Mon, 8 Apr 2024 00:51:10 +0800 Subject: [PATCH 1/6] Portable stories: Add telemetry --- code/lib/telemetry/package.json | 1 + .../src/__fixtures__/.storybook/a.js | 1 + code/lib/telemetry/src/__fixtures__/a.js | 1 + code/lib/telemetry/src/__fixtures__/b.js | 1 + code/lib/telemetry/src/__fixtures__/b.txt | 1 + code/lib/telemetry/src/__fixtures__/foo/a.js | 1 + .../src/get-portable-stories-usage.test.ts | 19 +++++++ .../src/get-portable-stories-usage.ts | 51 +++++++++++++++++++ code/lib/telemetry/src/storybook-metadata.ts | 3 ++ code/lib/telemetry/src/types.ts | 1 + code/yarn.lock | 1 + 11 files changed, 81 insertions(+) create mode 100644 code/lib/telemetry/src/__fixtures__/.storybook/a.js create mode 100644 code/lib/telemetry/src/__fixtures__/a.js create mode 100644 code/lib/telemetry/src/__fixtures__/b.js create mode 100644 code/lib/telemetry/src/__fixtures__/b.txt create mode 100644 code/lib/telemetry/src/__fixtures__/foo/a.js create mode 100644 code/lib/telemetry/src/get-portable-stories-usage.test.ts create mode 100644 code/lib/telemetry/src/get-portable-stories-usage.ts diff --git a/code/lib/telemetry/package.json b/code/lib/telemetry/package.json index 49ee940b1a3e..f39fdba366a7 100644 --- a/code/lib/telemetry/package.json +++ b/code/lib/telemetry/package.json @@ -51,6 +51,7 @@ "detect-package-manager": "^2.0.1", "fetch-retry": "^5.0.2", "fs-extra": "^11.1.0", + "glob": "^10.0.0", "read-pkg-up": "^7.0.1" }, "devDependencies": { diff --git a/code/lib/telemetry/src/__fixtures__/.storybook/a.js b/code/lib/telemetry/src/__fixtures__/.storybook/a.js new file mode 100644 index 000000000000..121caf9bf78f --- /dev/null +++ b/code/lib/telemetry/src/__fixtures__/.storybook/a.js @@ -0,0 +1 @@ +// composeStory; diff --git a/code/lib/telemetry/src/__fixtures__/a.js b/code/lib/telemetry/src/__fixtures__/a.js new file mode 100644 index 000000000000..121caf9bf78f --- /dev/null +++ b/code/lib/telemetry/src/__fixtures__/a.js @@ -0,0 +1 @@ +// composeStory; diff --git a/code/lib/telemetry/src/__fixtures__/b.js b/code/lib/telemetry/src/__fixtures__/b.js new file mode 100644 index 000000000000..71f6f1360f21 --- /dev/null +++ b/code/lib/telemetry/src/__fixtures__/b.js @@ -0,0 +1 @@ +// composeStories; diff --git a/code/lib/telemetry/src/__fixtures__/b.txt b/code/lib/telemetry/src/__fixtures__/b.txt new file mode 100644 index 000000000000..f4e2490eebe9 --- /dev/null +++ b/code/lib/telemetry/src/__fixtures__/b.txt @@ -0,0 +1 @@ +composeStory \ No newline at end of file diff --git a/code/lib/telemetry/src/__fixtures__/foo/a.js b/code/lib/telemetry/src/__fixtures__/foo/a.js new file mode 100644 index 000000000000..121caf9bf78f --- /dev/null +++ b/code/lib/telemetry/src/__fixtures__/foo/a.js @@ -0,0 +1 @@ +// composeStory; diff --git a/code/lib/telemetry/src/get-portable-stories-usage.test.ts b/code/lib/telemetry/src/get-portable-stories-usage.test.ts new file mode 100644 index 000000000000..d3bb1cffc8b5 --- /dev/null +++ b/code/lib/telemetry/src/get-portable-stories-usage.test.ts @@ -0,0 +1,19 @@ +import { describe, it, expect } from 'vitest'; +import { join } from 'path'; + +import { getPortableStoriesFiles } from './get-portable-stories-usage'; + +describe('getPortableStoriesFiles', () => { + it('should ignores node_modules, non-source files', async () => { + const base = join(__dirname, '__fixtures__'); + const usage = (await getPortableStoriesFiles(base)).map((f) => f.replace(base, '')); + expect(usage).toMatchInlineSnapshot(` + [ + "/b.js", + "/a.js", + "/foo/a.js", + "/.storybook/a.js", + ] + `); + }); +}); diff --git a/code/lib/telemetry/src/get-portable-stories-usage.ts b/code/lib/telemetry/src/get-portable-stories-usage.ts new file mode 100644 index 000000000000..8cacc9dcb336 --- /dev/null +++ b/code/lib/telemetry/src/get-portable-stories-usage.ts @@ -0,0 +1,51 @@ +import { readFile } from 'fs/promises'; +import { join } from 'path'; +import { glob } from 'glob'; + +import { createFileSystemCache, resolvePathInStorybookCache } from '@storybook/core-common'; + +const cache = createFileSystemCache({ + basePath: resolvePathInStorybookCache('portable-stories'), + ns: 'storybook', + ttl: 24 * 60 * 60 * 1000, +}); + +export const containsPortableStories = async (filename: string) => { + if (/sb\-preview\/runtime.m?js$/i.test(filename)) return null; + + const fileContent = await readFile(filename, 'utf-8'); + const contains = /composeStor[y|ies]/g.test(fileContent); + return contains ? filename : null; +}; + +export const getPortableStoriesFiles = async (base: string) => { + const files = await glob('**/*.{js,mjs,cjs,jsx,ts,mts,cts,tsx}', { + ignore: ['**/node_modules/**', '**/storybook-static/**', '**/dist/**'], + dot: true, + cwd: base, + }); + + const hits = []; + const chunkSize = 10; + for (let i = 0; i < files.length; i += chunkSize) { + const chunk = files.slice(i, i + chunkSize); + const results = ( + await Promise.all(chunk.map((f: string) => containsPortableStories(join(base, f)))) + ).filter(Boolean); + if (results.length > 0) { + hits.push(...results); + } + } + return hits as string[]; +}; + +const CACHE_KEY = 'portableStories'; +export const getPortableStoriesFileCount = async () => { + let cached = await cache.get(CACHE_KEY); + if (!cached) { + const files = await getPortableStoriesFiles(process.cwd()); + cached = { usage: files.length }; + await cache.set(CACHE_KEY, cached); + } + return cached.usage; +}; diff --git a/code/lib/telemetry/src/storybook-metadata.ts b/code/lib/telemetry/src/storybook-metadata.ts index e9ff8d844954..1d895b39a44c 100644 --- a/code/lib/telemetry/src/storybook-metadata.ts +++ b/code/lib/telemetry/src/storybook-metadata.ts @@ -15,6 +15,7 @@ import { getMonorepoType } from './get-monorepo-type'; import { cleanPaths } from './sanitize'; import { getFrameworkInfo } from './get-framework-info'; import { getChromaticVersionSpecifier } from './get-chromatic-version'; +import { getPortableStoriesFileCount } from './get-portable-stories-usage'; export const metaFrameworks = { next: 'Next', @@ -177,10 +178,12 @@ export const computeStorybookMetadata = async ({ } const storybookVersion = storybookPackages[storybookInfo.frameworkPackage]?.version; + const portableStoriesFileCount = await getPortableStoriesFileCount(); return { ...metadata, ...frameworkInfo, + portableStoriesFileCount, storybookVersion, storybookVersionSpecifier: storybookInfo.version, language, diff --git a/code/lib/telemetry/src/types.ts b/code/lib/telemetry/src/types.ts index 846e7adb9556..774aed918cce 100644 --- a/code/lib/telemetry/src/types.ts +++ b/code/lib/telemetry/src/types.ts @@ -60,6 +60,7 @@ export type StorybookMetadata = { preview?: { usesGlobals?: boolean; }; + portableStoriesFileCount?: number; }; export interface Payload { diff --git a/code/yarn.lock b/code/yarn.lock index 472dd503b241..0ba465a0a6c6 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -6646,6 +6646,7 @@ __metadata: detect-package-manager: "npm:^2.0.1" fetch-retry: "npm:^5.0.2" fs-extra: "npm:^11.1.0" + glob: "npm:^10.0.0" nanoid: "npm:^4.0.2" node-fetch: "npm:^3.3.1" read-pkg-up: "npm:^7.0.1" From ffd7277c4848ec63af764b6718e7dd052320aff9 Mon Sep 17 00:00:00 2001 From: Michael Shilman Date: Tue, 9 Jul 2024 10:40:03 +0800 Subject: [PATCH 2/6] Use git grep for efficiency --- .../telemetry/get-portable-stories-usage.ts | 51 +++++++------------ 1 file changed, 17 insertions(+), 34 deletions(-) diff --git a/code/core/src/telemetry/get-portable-stories-usage.ts b/code/core/src/telemetry/get-portable-stories-usage.ts index 9ef00aff39d3..bdf987bb0b3b 100644 --- a/code/core/src/telemetry/get-portable-stories-usage.ts +++ b/code/core/src/telemetry/get-portable-stories-usage.ts @@ -1,51 +1,34 @@ -import { readFile } from 'fs/promises'; -import { join } from 'path'; -import { glob } from 'glob'; +import { execaCommand } from 'execa'; import { createFileSystemCache, resolvePathInStorybookCache } from '../common'; const cache = createFileSystemCache({ basePath: resolvePathInStorybookCache('portable-stories'), ns: 'storybook', - ttl: 24 * 60 * 60 * 1000, + ttl: 24 * 60 * 60 * 1000, // 24h }); -export const containsPortableStories = async (filename: string) => { - if (/sb\-preview\/runtime.m?js$/i.test(filename)) return null; - - const fileContent = await readFile(filename, 'utf-8'); - const contains = /composeStor[y|ies]/g.test(fileContent); - return contains ? filename : null; -}; - -export const getPortableStoriesFiles = async (base: string) => { - const files = await glob('**/*.{js,mjs,cjs,jsx,ts,mts,cts,tsx}', { - ignore: ['**/node_modules/**', '**/storybook-static/**', '**/dist/**'], - dot: true, - cwd: base, +const getPortableStoriesFileCountUncached = async () => { + const { stdout } = await execaCommand(`git grep -m1 -c composeStor`, { + cwd: process.cwd(), + shell: true, }); - - const hits = []; - const chunkSize = 10; - for (let i = 0; i < files.length; i += chunkSize) { - const chunk = files.slice(i, i + chunkSize); - const results = ( - await Promise.all(chunk.map((f: string) => containsPortableStories(join(base, f)))) - ).filter(Boolean); - if (results.length > 0) { - hits.push(...results); - } - } - return hits as string[]; + return stdout.split('\n').filter(Boolean).length; }; const CACHE_KEY = 'portableStories'; export const getPortableStoriesFileCount = async () => { let cached = await cache.get(CACHE_KEY); if (!cached) { - const files = await getPortableStoriesFiles(process.cwd()); - cached = { usage: files.length }; - await cache.set(CACHE_KEY, cached); + try { + const count = await getPortableStoriesFileCountUncached(); + cached = { count }; + await cache.set(CACHE_KEY, cached); + } catch (err: any) { + // exit code 1 if no matches are found + const count = err.exitCode === 1 ? 0 : null; + cached = { count }; + } } - return cached.usage; + return cached.count; }; From 9170d4b80ad9104d65eff1b874e45df7a200db05 Mon Sep 17 00:00:00 2001 From: Michael Shilman Date: Tue, 9 Jul 2024 10:52:47 +0800 Subject: [PATCH 3/6] Delete unused fixtures --- code/core/src/telemetry/__fixtures__/.storybook/a.js | 1 - code/core/src/telemetry/__fixtures__/a.js | 1 - code/core/src/telemetry/__fixtures__/b.js | 1 - code/core/src/telemetry/__fixtures__/b.txt | 1 - code/core/src/telemetry/__fixtures__/foo/a.js | 1 - 5 files changed, 5 deletions(-) delete mode 100644 code/core/src/telemetry/__fixtures__/.storybook/a.js delete mode 100644 code/core/src/telemetry/__fixtures__/a.js delete mode 100644 code/core/src/telemetry/__fixtures__/b.js delete mode 100644 code/core/src/telemetry/__fixtures__/b.txt delete mode 100644 code/core/src/telemetry/__fixtures__/foo/a.js diff --git a/code/core/src/telemetry/__fixtures__/.storybook/a.js b/code/core/src/telemetry/__fixtures__/.storybook/a.js deleted file mode 100644 index 121caf9bf78f..000000000000 --- a/code/core/src/telemetry/__fixtures__/.storybook/a.js +++ /dev/null @@ -1 +0,0 @@ -// composeStory; diff --git a/code/core/src/telemetry/__fixtures__/a.js b/code/core/src/telemetry/__fixtures__/a.js deleted file mode 100644 index 121caf9bf78f..000000000000 --- a/code/core/src/telemetry/__fixtures__/a.js +++ /dev/null @@ -1 +0,0 @@ -// composeStory; diff --git a/code/core/src/telemetry/__fixtures__/b.js b/code/core/src/telemetry/__fixtures__/b.js deleted file mode 100644 index 71f6f1360f21..000000000000 --- a/code/core/src/telemetry/__fixtures__/b.js +++ /dev/null @@ -1 +0,0 @@ -// composeStories; diff --git a/code/core/src/telemetry/__fixtures__/b.txt b/code/core/src/telemetry/__fixtures__/b.txt deleted file mode 100644 index f4e2490eebe9..000000000000 --- a/code/core/src/telemetry/__fixtures__/b.txt +++ /dev/null @@ -1 +0,0 @@ -composeStory \ No newline at end of file diff --git a/code/core/src/telemetry/__fixtures__/foo/a.js b/code/core/src/telemetry/__fixtures__/foo/a.js deleted file mode 100644 index 121caf9bf78f..000000000000 --- a/code/core/src/telemetry/__fixtures__/foo/a.js +++ /dev/null @@ -1 +0,0 @@ -// composeStory; From e9d28dba3070fb425b0dbb6b7cb84a1c9b7ebfc7 Mon Sep 17 00:00:00 2001 From: Michael Shilman Date: Tue, 9 Jul 2024 10:54:04 +0800 Subject: [PATCH 4/6] Fix tests --- .../get-portable-stories-usage.test.ts | 18 +++++------------- .../telemetry/get-portable-stories-usage.ts | 2 +- docs/configure/telemetry.mdx | 1 + 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/code/core/src/telemetry/get-portable-stories-usage.test.ts b/code/core/src/telemetry/get-portable-stories-usage.test.ts index d3bb1cffc8b5..c91efd993d45 100644 --- a/code/core/src/telemetry/get-portable-stories-usage.test.ts +++ b/code/core/src/telemetry/get-portable-stories-usage.test.ts @@ -1,19 +1,11 @@ import { describe, it, expect } from 'vitest'; -import { join } from 'path'; -import { getPortableStoriesFiles } from './get-portable-stories-usage'; +import { getPortableStoriesFileCountUncached } from './get-portable-stories-usage'; -describe('getPortableStoriesFiles', () => { +describe('getPortableStoriesFileCountUncached', () => { it('should ignores node_modules, non-source files', async () => { - const base = join(__dirname, '__fixtures__'); - const usage = (await getPortableStoriesFiles(base)).map((f) => f.replace(base, '')); - expect(usage).toMatchInlineSnapshot(` - [ - "/b.js", - "/a.js", - "/foo/a.js", - "/.storybook/a.js", - ] - `); + const usage = await getPortableStoriesFileCountUncached(); + // verify git grep -m1 -c composeStor | wc -l + expect(usage).toMatchInlineSnapshot(`14`); }); }); diff --git a/code/core/src/telemetry/get-portable-stories-usage.ts b/code/core/src/telemetry/get-portable-stories-usage.ts index bdf987bb0b3b..81b3334bc179 100644 --- a/code/core/src/telemetry/get-portable-stories-usage.ts +++ b/code/core/src/telemetry/get-portable-stories-usage.ts @@ -8,7 +8,7 @@ const cache = createFileSystemCache({ ttl: 24 * 60 * 60 * 1000, // 24h }); -const getPortableStoriesFileCountUncached = async () => { +export const getPortableStoriesFileCountUncached = async () => { const { stdout } = await execaCommand(`git grep -m1 -c composeStor`, { cwd: process.cwd(), shell: true, diff --git a/docs/configure/telemetry.mdx b/docs/configure/telemetry.mdx index 6764db42fede..eec5fcd51e66 100644 --- a/docs/configure/telemetry.mdx +++ b/docs/configure/telemetry.mdx @@ -84,6 +84,7 @@ Will generate the following output: "hasStaticDirs": false, "hasStorybookEslint": false, "refCount": 0, + "portableStoriesFileCount": 0, "packageManager": { "type": "yarn", "version": "3.1.1" From 0ae3a63f15bd0f5b11a48f4a7ce87923e4382845 Mon Sep 17 00:00:00 2001 From: Michael Shilman Date: Wed, 7 Aug 2024 06:49:55 +0800 Subject: [PATCH 5/6] Update code/core/src/telemetry/get-portable-stories-usage.test.ts --- code/core/src/telemetry/get-portable-stories-usage.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/core/src/telemetry/get-portable-stories-usage.test.ts b/code/core/src/telemetry/get-portable-stories-usage.test.ts index c91efd993d45..b07eaec0a2e2 100644 --- a/code/core/src/telemetry/get-portable-stories-usage.test.ts +++ b/code/core/src/telemetry/get-portable-stories-usage.test.ts @@ -5,7 +5,7 @@ import { getPortableStoriesFileCountUncached } from './get-portable-stories-usag describe('getPortableStoriesFileCountUncached', () => { it('should ignores node_modules, non-source files', async () => { const usage = await getPortableStoriesFileCountUncached(); - // verify git grep -m1 -c composeStor | wc -l + // you can verify with: `git grep -m1 -c composeStor | wc -l` expect(usage).toMatchInlineSnapshot(`14`); }); }); From fee28485d036a46c48b8ec8c45787ea11124f13a Mon Sep 17 00:00:00 2001 From: Michael Shilman Date: Wed, 7 Aug 2024 08:53:23 +0800 Subject: [PATCH 6/6] Update tests --- .../docgen-components/8428-js-static-prop-types/docgen.snapshot | 2 +- .../9556-ts-react-default-exports/docgen.snapshot | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/code/renderers/react/template/stories/docgen-components/8428-js-static-prop-types/docgen.snapshot b/code/renderers/react/template/stories/docgen-components/8428-js-static-prop-types/docgen.snapshot index 516d6a99d828..2fb12d5579e7 100644 --- a/code/renderers/react/template/stories/docgen-components/8428-js-static-prop-types/docgen.snapshot +++ b/code/renderers/react/template/stories/docgen-components/8428-js-static-prop-types/docgen.snapshot @@ -1,4 +1,4 @@ -function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } +function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; } function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } import React from 'react'; diff --git a/code/renderers/react/template/stories/docgen-components/9556-ts-react-default-exports/docgen.snapshot b/code/renderers/react/template/stories/docgen-components/9556-ts-react-default-exports/docgen.snapshot index 96720e8f4df7..deeed4ffa007 100644 --- a/code/renderers/react/template/stories/docgen-components/9556-ts-react-default-exports/docgen.snapshot +++ b/code/renderers/react/template/stories/docgen-components/9556-ts-react-default-exports/docgen.snapshot @@ -1,4 +1,4 @@ -function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } +function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } import React from 'react'; export const Button = ({ isDisabled = false,