From 0b799cc316f3d31fb95bf35b680c52ace9c9b223 Mon Sep 17 00:00:00 2001 From: Daniel Williams Date: Tue, 19 Nov 2024 19:38:18 +0000 Subject: [PATCH] fix: remove references to v6 and watcher --- packages/react-native/bin/watcher.js | 2 - packages/react-native/package.json | 3 +- .../scripts/__snapshots__/loader.test.js.snap | 211 ------------------ packages/react-native/scripts/get-stories.js | 9 +- packages/react-native/scripts/handle-args.js | 2 +- packages/react-native/scripts/loader.js | 185 --------------- packages/react-native/scripts/loader.test.js | 179 --------------- packages/react-native/scripts/watcher.js | 84 ------- 8 files changed, 4 insertions(+), 671 deletions(-) delete mode 100755 packages/react-native/bin/watcher.js delete mode 100644 packages/react-native/scripts/__snapshots__/loader.test.js.snap delete mode 100644 packages/react-native/scripts/loader.js delete mode 100644 packages/react-native/scripts/loader.test.js delete mode 100644 packages/react-native/scripts/watcher.js diff --git a/packages/react-native/bin/watcher.js b/packages/react-native/bin/watcher.js deleted file mode 100755 index c247b116b5..0000000000 --- a/packages/react-native/bin/watcher.js +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/env node -require('../scripts/watcher'); diff --git a/packages/react-native/package.json b/packages/react-native/package.json index ddc2e20d5d..15b2bf13d8 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -19,8 +19,7 @@ "license": "MIT", "main": "dist/index.js", "bin": { - "sb-rn-get-stories": "./bin/get-stories.js", - "sb-rn-watcher": "./bin/watcher.js" + "sb-rn-get-stories": "./bin/get-stories.js" }, "exports": { ".": "./dist/index.js", diff --git a/packages/react-native/scripts/__snapshots__/loader.test.js.snap b/packages/react-native/scripts/__snapshots__/loader.test.js.snap deleted file mode 100644 index 24d64c6157..0000000000 --- a/packages/react-native/scripts/__snapshots__/loader.test.js.snap +++ /dev/null @@ -1,211 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`loader writeRequires when there are different file extensions writes the story imports 1`] = ` -" - /* do not change this file, it is auto generated by storybook. */ - - import { configure, addDecorator, addParameters, addArgsEnhancer } from '@storybook/react-native/V6'; - - global.STORIES = [{"titlePrefix":"","directory":"./scripts/mocks/file-extensions","files":"FakeStory.stories.tsx","importPathMatcher":"^\\\\.[\\\\\\\\/](?:scripts\\\\/mocks\\\\/file-extensions\\\\/FakeStory\\\\.stories\\\\.tsx)$"}] - - import "@storybook/addon-ondevice-notes/register"; -import "@storybook/addon-ondevice-controls/register"; -import "@storybook/addon-ondevice-backgrounds/register"; -import "@storybook/addon-ondevice-actions/register"; - - import { argsEnhancers } from "@storybook/addon-actions/dist/preview" - - - import { decorators, parameters } from './preview'; - - if (decorators) { - decorators.forEach((decorator) => addDecorator(decorator)); - } - - if (parameters) { - addParameters(parameters); - } - - - - try { - argsEnhancers.forEach(enhancer => addArgsEnhancer(enhancer)); - } catch{} - - - - const getStories=() => { - return {"./scripts/mocks/file-extensions/FakeStory.stories.tsx": require("./FakeStory.stories.tsx")}; - } - - configure(getStories, module, false) - - - " -`; - -exports[`loader writeRequires when there is a configuration object writes the story imports 1`] = ` -" - /* do not change this file, it is auto generated by storybook. */ - - import { configure, addDecorator, addParameters, addArgsEnhancer } from '@storybook/react-native/V6'; - - global.STORIES = [{"titlePrefix":"ComponentsPrefix","files":"**/*.stories.tsx","directory":"./scripts/mocks/configuration-objects/components","importPathMatcher":"^\\\\.[\\\\\\\\/](?:scripts\\\\/mocks\\\\/configuration-objects\\\\/components(?:\\\\/(?!\\\\.)(?:(?:(?!(?:^|\\\\/)\\\\.).)*?)\\\\/|\\\\/|$)(?!\\\\.)(?=.)[^/]*?\\\\.stories\\\\.tsx)$"}] - - import "@storybook/addon-ondevice-notes/register"; -import "@storybook/addon-ondevice-controls/register"; -import "@storybook/addon-ondevice-backgrounds/register"; -import "@storybook/addon-ondevice-actions/register"; - - import { argsEnhancers } from "@storybook/addon-actions/dist/preview" - - - import { decorators, parameters } from './preview'; - - if (decorators) { - decorators.forEach((decorator) => addDecorator(decorator)); - } - - if (parameters) { - addParameters(parameters); - } - - - - try { - argsEnhancers.forEach(enhancer => addArgsEnhancer(enhancer)); - } catch{} - - - - const getStories=() => { - return {"./scripts/mocks/configuration-objects/components/FakeStory.stories.tsx": require("./components/FakeStory.stories.tsx")}; - } - - configure(getStories, module, false) - - - " -`; - -exports[`loader writeRequires when there is a story glob and exclude paths globs writes the story imports 1`] = ` -" - /* do not change this file, it is auto generated by storybook. */ - - import { configure, addDecorator, addParameters, addArgsEnhancer } from '@storybook/react-native/V6'; - - global.STORIES = [{"titlePrefix":"","directory":"./scripts/mocks/exclude-config-files","files":"**/*.stories.tsx","importPathMatcher":"^\\\\.[\\\\\\\\/](?:scripts\\\\/mocks\\\\/exclude-config-files(?:\\\\/(?!\\\\.)(?:(?:(?!(?:^|\\\\/)\\\\.).)*?)\\\\/|\\\\/|$)(?!\\\\.)(?=.)[^/]*?\\\\.stories\\\\.tsx)$"}] - - import "@storybook/addon-ondevice-notes/register"; -import "@storybook/addon-ondevice-controls/register"; -import "@storybook/addon-ondevice-backgrounds/register"; -import "@storybook/addon-ondevice-actions/register"; - - import { argsEnhancers } from "@storybook/addon-actions/dist/preview" - - - import { decorators, parameters } from './preview'; - - if (decorators) { - decorators.forEach((decorator) => addDecorator(decorator)); - } - - if (parameters) { - addParameters(parameters); - } - - - - try { - argsEnhancers.forEach(enhancer => addArgsEnhancer(enhancer)); - } catch{} - - - - const getStories=() => { - return {"./scripts/mocks/exclude-config-files/include-components/FakeStory.stories.tsx": require("./include-components/FakeStory.stories.tsx")}; - } - - configure(getStories, module, false) - - - " -`; - -exports[`loader writeRequires when there is a story glob writes the story imports 1`] = ` -" - /* do not change this file, it is auto generated by storybook. */ - - import { configure, addDecorator, addParameters, addArgsEnhancer } from '@storybook/react-native/V6'; - - global.STORIES = [{"titlePrefix":"","directory":"./scripts/mocks/all-config-files","files":"FakeStory.stories.tsx","importPathMatcher":"^\\\\.[\\\\\\\\/](?:scripts\\\\/mocks\\\\/all-config-files\\\\/FakeStory\\\\.stories\\\\.tsx)$"}] - - import "@storybook/addon-ondevice-notes/register"; -import "@storybook/addon-ondevice-controls/register"; -import "@storybook/addon-ondevice-backgrounds/register"; -import "@storybook/addon-ondevice-actions/register"; - - import { argsEnhancers } from "@storybook/addon-actions/dist/preview" - - - import { decorators, parameters } from './preview'; - - if (decorators) { - decorators.forEach((decorator) => addDecorator(decorator)); - } - - if (parameters) { - addParameters(parameters); - } - - - - try { - argsEnhancers.forEach(enhancer => addArgsEnhancer(enhancer)); - } catch{} - - - - const getStories=() => { - return {"./scripts/mocks/all-config-files/FakeStory.stories.tsx": require("./FakeStory.stories.tsx")}; - } - - configure(getStories, module, false) - - - " -`; - -exports[`loader writeRequires when there is no preview does not add preview related stuff 1`] = ` -" - /* do not change this file, it is auto generated by storybook. */ - - import { configure, addDecorator, addParameters, addArgsEnhancer } from '@storybook/react-native/V6'; - - global.STORIES = [{"titlePrefix":"","directory":"./scripts/mocks/no-preview","files":"FakeStory.stories.tsx","importPathMatcher":"^\\\\.[\\\\\\\\/](?:scripts\\\\/mocks\\\\/no-preview\\\\/FakeStory\\\\.stories\\\\.tsx)$"}] - - import "@storybook/addon-ondevice-notes/register"; -import "@storybook/addon-ondevice-controls/register"; -import "@storybook/addon-ondevice-backgrounds/register"; -import "@storybook/addon-ondevice-actions/register"; - - import { argsEnhancers } from "@storybook/addon-actions/dist/preview" - - - - - try { - argsEnhancers.forEach(enhancer => addArgsEnhancer(enhancer)); - } catch{} - - - - const getStories=() => { - return {"./scripts/mocks/no-preview/FakeStory.stories.tsx": require("./FakeStory.stories.tsx")}; - } - - configure(getStories, module, false) - - - " -`; diff --git a/packages/react-native/scripts/get-stories.js b/packages/react-native/scripts/get-stories.js index cd82088e27..62bbceb245 100644 --- a/packages/react-native/scripts/get-stories.js +++ b/packages/react-native/scripts/get-stories.js @@ -1,10 +1,5 @@ const { getArguments } = require('./handle-args'); const args = getArguments(); -if (args.v6Store) { - const { writeRequires } = require('./loader'); - writeRequires(args); -} else { - const { generate } = require('./generate'); - generate(args); -} +const { generate } = require('./generate'); +generate(args); diff --git a/packages/react-native/scripts/handle-args.js b/packages/react-native/scripts/handle-args.js index 3b637e9eae..c051b5ddea 100644 --- a/packages/react-native/scripts/handle-args.js +++ b/packages/react-native/scripts/handle-args.js @@ -2,7 +2,7 @@ function getArguments() { const { program } = require('commander'); program - .description('Getter and watcher for react native storybook') + .description('Generator for the storybook.requires file used in react native storybook') .option( '-c, --config-path ', 'The path to your config folder relative to your project-dir', diff --git a/packages/react-native/scripts/loader.js b/packages/react-native/scripts/loader.js deleted file mode 100644 index 8dc8edd896..0000000000 --- a/packages/react-native/scripts/loader.js +++ /dev/null @@ -1,185 +0,0 @@ -const path = require('path'); -const fs = require('fs'); -const glob = require('glob'); -const prettier = require('prettier'); -const { normalizeStories } = require('@storybook/core/common'); -const { - toRequireContext, - getFilePathExtension, - getMain, - ensureRelativePathHasDot, - getPreviewExists, -} = require('./common'); - -const cwd = process.cwd(); - -// TODO check if we need clearDecorators(); - -// we clear decorators as a workaround for global decorators getting infinitely applied on HMR -const previewImports = ` - import { decorators, parameters } from './preview'; - - if (decorators) { - decorators.forEach((decorator) => addDecorator(decorator)); - } - - if (parameters) { - addParameters(parameters); - } -`; - -function normalizeExcludePaths(paths) { - // automatically convert a string to an array of a single string - if (typeof paths === 'string') { - return [paths]; - } - - // ensure the paths is an array and if any items exists, they are strings - if (Array.isArray(paths) && paths.every((p) => typeof p === 'string')) { - return paths; - } - - // when the paths aren't a string or an (empty) array of strings, return - return undefined; -} - -function writeRequires({ configPath, absolute = false, v6RequireContext = false }) { - const storybookRequiresLocation = path.resolve(cwd, configPath, 'storybook.requires.js'); - - const mainImport = getMain({ configPath }); - - const main = mainImport.default ?? mainImport; - - const reactNativeOptions = main.reactNativeOptions; - - const excludePaths = reactNativeOptions && reactNativeOptions.excludePaths; - - const normalizedExcludePaths = normalizeExcludePaths(excludePaths); - - const storiesSpecifiers = normalizeStories(main.stories, { - configDir: configPath, - workingDir: cwd, - }); - - let configure = ''; - - if (v6RequireContext) { - const contexts = storiesSpecifiers.map((specifier) => { - const { path: p, recursive: r, match: m } = toRequireContext(specifier); - - const pathToStory = ensureRelativePathHasDot(path.relative(configPath, p)); - - return `require.context('${pathToStory}', ${r}, ${m})`; - }); - - configure = ` - const stories = [${contexts.join(',')}]; - - configure(stories, module, false) - `; - } else { - const storyRequires = storiesSpecifiers.reduce((acc, specifier) => { - const paths = glob - .sync(specifier.files, { - cwd: path.resolve(cwd, specifier.directory), - absolute, - // default to always ignore (exclude) anything in node_modules - ignore: - normalizedExcludePaths !== undefined ? normalizedExcludePaths : ['**/node_modules'], - }) - .map((storyPath) => { - const pathWithDirectory = path.join(specifier.directory, storyPath); - - const requirePath = absolute - ? storyPath - : ensureRelativePathHasDot(path.relative(configPath, pathWithDirectory)); - - const absolutePath = absolute ? requirePath : path.resolve(configPath, requirePath); - - const pathRelativeToCwd = path.relative(cwd, absolutePath); - - const normalizePathForWindows = (str) => - path.sep === '\\' ? str.replace(/\\/g, '/') : str; - - return `"./${normalizePathForWindows( - pathRelativeToCwd - )}": require("${normalizePathForWindows(requirePath)}")`; - }); - return [...acc, ...paths]; - }, []); - - const path_obj_str = `{${storyRequires.join(',')}}`; - - configure = ` - const getStories=() => { - return ${path_obj_str}; - } - - configure(getStories, module, false) - `; - } - - fs.writeFileSync(storybookRequiresLocation, ''); - - const previewExists = getPreviewExists({ configPath }); - - let previewJs = previewExists ? previewImports : ''; - - const registerAddons = main.addons?.map((addon) => `import "${addon}/register";`).join('\n'); - - let enhancersImport = ''; - - let enhancers = ''; - - // TODO: implement presets or something similar - if (main.addons?.includes('@storybook/addon-ondevice-actions')) { - enhancersImport = 'import { argsEnhancers } from "@storybook/addon-actions/dist/preview"'; - - // try/catch is a temporary fix for https://github.com/storybookjs/react-native/issues/327 until a fix is found - enhancers = ` - try { - argsEnhancers.forEach(enhancer => addArgsEnhancer(enhancer)); - } catch{} - `; - } - - const normalizedStories = storiesSpecifiers.map((specifier) => ({ - ...specifier, - importPathMatcher: specifier.importPathMatcher.source, - })); - - const globalStories = `global.STORIES = ${JSON.stringify(normalizedStories)}`; - - const fileContent = ` - /* do not change this file, it is auto generated by storybook. */ - - import { configure, addDecorator, addParameters, addArgsEnhancer } from '@storybook/react-native/V6'; - - ${globalStories} - - ${registerAddons} - - ${enhancersImport} - - ${previewJs} - - ${enhancers} - - ${configure} - - `; - - const formattedFileContent = prettier.format(fileContent, { parser: 'babel' }); - - fs.writeFileSync(storybookRequiresLocation, formattedFileContent, { - encoding: 'utf8', - flag: 'w', - }); -} - -module.exports = { - writeRequires, - getMain, - getPreviewExists, - getFilePathExtension, -}; diff --git a/packages/react-native/scripts/loader.test.js b/packages/react-native/scripts/loader.test.js deleted file mode 100644 index 0ec1ad8e37..0000000000 --- a/packages/react-native/scripts/loader.test.js +++ /dev/null @@ -1,179 +0,0 @@ -const path = require('path'); -const { writeRequires, getMain, getPreviewExists } = require('./loader'); - -let pathMock; -let fileContentMock; - -jest.mock('fs', () => ({ - ...jest.requireActual('fs'), - writeFileSync: (filePath, fileContent, opts) => { - pathMock = filePath; - fileContentMock = fileContent; - }, -})); - -jest.mock('prettier', () => ({ - format(s, opts) { - return s; - }, -})); - -describe('loader', () => { - describe('getMain', () => { - it('should return the main js default export as an object', () => { - const main = getMain({ configPath: path.resolve(__dirname, 'mocks/all-config-files') }); - expect(main).toEqual({ - stories: ['./FakeStory.stories.tsx'], - addons: [ - '@storybook/addon-ondevice-notes', - '@storybook/addon-ondevice-controls', - '@storybook/addon-ondevice-backgrounds', - '@storybook/addon-ondevice-actions', - ], - }); - }); - - it('should also work with relative paths', () => { - // relative from where the command is run - const main = getMain({ configPath: './scripts/mocks/all-config-files' }); - expect(main).toEqual({ - stories: ['./FakeStory.stories.tsx'], - addons: [ - '@storybook/addon-ondevice-notes', - '@storybook/addon-ondevice-controls', - '@storybook/addon-ondevice-backgrounds', - '@storybook/addon-ondevice-actions', - ], - }); - }); - it('should work for any supported file extension', () => { - const main = getMain({ configPath: './scripts/mocks/file-extensions' }); - expect(main).toEqual({ - stories: ['./FakeStory.stories.tsx'], - addons: [ - '@storybook/addon-ondevice-notes', - '@storybook/addon-ondevice-controls', - '@storybook/addon-ondevice-backgrounds', - '@storybook/addon-ondevice-actions', - ], - }); - }); - }); - - describe('getPreviewExists', () => { - const supportedExtensions = ['js', 'jsx', 'ts', 'tsx']; - describe('when using a relative path', () => { - it('should return true if the preview exists', () => { - supportedExtensions.forEach((ext) => { - expect(getPreviewExists({ configPath: `scripts/mocks/preview-files/${ext}` })).toBe(true); - }); - }); - - it('should return false if the preview does not exist', () => { - expect(getPreviewExists({ configPath: './scripts/mocks/no-preview' })).toBe(false); - }); - - it('should return false if the preview does not match any of supportedExtensions values', () => { - expect(getPreviewExists({ configPath: './scripts/mocks/wrong-extension-preview' })).toBe( - false - ); - }); - }); - - describe('when using an absolute path', () => { - it('should return true if the preview exists', () => { - supportedExtensions.forEach((ext) => { - expect( - getPreviewExists({ - configPath: path.resolve(__dirname, `mocks/preview-files/${ext}`), - }) - ).toBe(true); - }); - }); - - it('should return false if the preview does not exist', () => { - expect(getPreviewExists({ configPath: path.resolve(__dirname, 'mocks/no-preview') })).toBe( - false - ); - }); - - it('should return false if the preview does not match any of supportedExtensions values', () => { - expect( - getPreviewExists({ configPath: path.resolve(__dirname, 'mocks/wrong-extension-preview') }) - ).toBe(false); - }); - }); - }); - - describe('writeRequires', () => { - describe('when there is a story glob', () => { - it('writes the story imports', () => { - writeRequires({ configPath: 'scripts/mocks/all-config-files' }); - expect(pathMock).toEqual( - path.resolve(__dirname, 'mocks/all-config-files/storybook.requires.js') - ); - expect(fileContentMock).toMatchSnapshot(); - }); - }); - - describe('when there are different file extensions', () => { - it('writes the story imports', () => { - writeRequires({ configPath: 'scripts/mocks/file-extensions' }); - expect(pathMock).toEqual( - path.resolve(__dirname, 'mocks/file-extensions/storybook.requires.js') - ); - expect(fileContentMock).toMatchSnapshot(); - }); - }); - - describe('when there is a story glob and exclude paths globs', () => { - it('writes the story imports', () => { - writeRequires({ configPath: 'scripts/mocks/exclude-config-files' }); - expect(pathMock).toEqual( - path.resolve(__dirname, 'mocks/exclude-config-files/storybook.requires.js') - ); - - expect(fileContentMock).toContain('include-components/FakeStory.stories.tsx'); - expect(fileContentMock).not.toContain('exclude-components/FakeStory.stories.tsx'); - - expect(fileContentMock).toMatchSnapshot(); - }); - }); - - describe('when there is no story glob or addons', () => { - it('throws an error', () => { - expect(() => writeRequires({ configPath: 'scripts/mocks/blank-config' })).toThrow(); - }); - }); - - describe('when there is no preview', () => { - it('does not add preview related stuff', () => { - writeRequires({ configPath: 'scripts/mocks/no-preview' }); - expect(pathMock).toEqual(path.resolve(__dirname, 'mocks/no-preview/storybook.requires.js')); - expect(fileContentMock).toMatchSnapshot(); - }); - }); - - describe('when the absolute option is true', () => { - it('should write absolute paths to the requires file', () => { - writeRequires({ configPath: 'scripts/mocks/all-config-files', absolute: true }); - expect(pathMock).toEqual( - path.resolve(__dirname, 'mocks/all-config-files/storybook.requires.js') - ); - expect(fileContentMock).toContain( - path.resolve(__dirname, 'mocks/all-config-files/FakeStory.stories.tsx') - ); - }); - }); - - describe('when there is a configuration object', () => { - it('writes the story imports', () => { - writeRequires({ configPath: 'scripts/mocks/configuration-objects' }); - expect(pathMock).toEqual( - path.resolve(__dirname, 'mocks/configuration-objects/storybook.requires.js') - ); - expect(fileContentMock).toMatchSnapshot(); - }); - }); - }); -}); diff --git a/packages/react-native/scripts/watcher.js b/packages/react-native/scripts/watcher.js deleted file mode 100644 index 13023bb099..0000000000 --- a/packages/react-native/scripts/watcher.js +++ /dev/null @@ -1,84 +0,0 @@ -const chokidar = require('chokidar'); -const path = require('path'); - -const { writeRequires, getMain, getFilePathExtension } = require('./loader'); - -const { getArguments } = require('./handle-args'); - -const args = getArguments(); - -if (!args.v6Store) { - console.log( - "in v7 you don't need the watcher anymore, if you are using v6 compat mode then pass the -v6 flag" - ); - - process.exit(0); -} - -const log = console.log.bind(console); - -const mainExt = getFilePathExtension(args, 'main'); -const previewExt = getFilePathExtension(args, 'preview'); - -const watchPaths = [`./main.${mainExt}`]; - -if (previewExt) { - watchPaths.push(`./preview.${previewExt}`); -} - -console.log(watchPaths); - -const updateRequires = (event, watchPath) => { - if (typeof watchPath === 'string') { - log(`event ${event} for file ${path.basename(watchPath)}`); - } - writeRequires(args); -}; - -const globs = getMain(args).stories; -// directory -// files -const globsStrings = globs.map((g) => { - if (typeof g === 'string') return g; - if (g.directory && g.files) { - return `${g.directory}/${g.files}`; - } -}); - -chokidar - .watch(watchPaths, { cwd: args.configPath }) - .on('change', (watchPath) => updateRequires('change', watchPath)); - -let isReady = false; - -chokidar - .watch(globsStrings, { cwd: args.configPath }) - .on('ready', () => { - log('Watcher is ready, performing initial write'); - writeRequires(args); - log('Waiting for changes, press r to manually re-write'); - isReady = true; - }) - .on('add', (watchPath) => { - if (isReady) { - updateRequires('add', watchPath); - } - }) - .on('unlink', (watchPath) => { - if (isReady) { - updateRequires('unlink', watchPath); - } - }); - -const readline = require('readline'); -readline.emitKeypressEvents(process.stdin); -process.stdin.setRawMode(true); -process.stdin.on('keypress', (str, key) => { - if (key.ctrl && key.name === 'c') { - process.exit(); - } - if (key.name === 'r') { - log('Detected "r" keypress, rewriting story imports...'); - writeRequires(args); - } -});