mirror of
https://github.com/storybookjs/storybook.git
synced 2025-04-05 08:01:20 +08:00
Merge pull request #19156 from storybookjs/shilman/fix-sandbox-multiple-reacts
Build: Fix sandbox running multiple versions of react
This commit is contained in:
commit
05664e45b0
@ -13,7 +13,9 @@ export async function listStories(options: Options) {
|
||||
}).map(({ directory, files }) => {
|
||||
const pattern = path.join(directory, files);
|
||||
|
||||
return glob(path.isAbsolute(pattern) ? pattern : path.join(options.configDir, pattern));
|
||||
return glob(path.isAbsolute(pattern) ? pattern : path.join(options.configDir, pattern), {
|
||||
follow: true,
|
||||
});
|
||||
})
|
||||
)
|
||||
).reduce((carry, stories) => carry.concat(stories), []);
|
||||
|
16
code/renderers/react/template/stories/hooks.stories.tsx
Normal file
16
code/renderers/react/template/stories/hooks.stories.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
import React, { FC, useState } from 'react';
|
||||
|
||||
const ButtonWithState: FC = () => {
|
||||
const [count, setCount] = useState(0);
|
||||
return (
|
||||
<button type="button" onClick={() => setCount(count + 1)}>
|
||||
{`count: ${count}`}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
export default {
|
||||
component: ButtonWithState,
|
||||
};
|
||||
|
||||
export const Basic = {};
|
@ -25,6 +25,8 @@ import { ConfigFile, readConfig, writeConfig } from '../code/lib/csf-tools';
|
||||
import { babelParse } from '../code/lib/csf-tools/src/babelParse';
|
||||
import TEMPLATES from '../code/lib/cli/src/repro-templates';
|
||||
import { servePackages } from './utils/serve-packages';
|
||||
import { filterExistsInCodeDir, codeDir } from './utils/filterExistsInCodeDir';
|
||||
import { JsPackageManagerFactory } from '../code/lib/cli/src/js-package-manager';
|
||||
|
||||
type Template = keyof typeof TEMPLATES;
|
||||
const templates: Template[] = Object.keys(TEMPLATES) as any;
|
||||
@ -44,7 +46,6 @@ const defaultAddons = [
|
||||
'viewport',
|
||||
];
|
||||
const sandboxDir = path.resolve(__dirname, '../sandbox');
|
||||
const codeDir = path.resolve(__dirname, '../code');
|
||||
const reprosDir = path.resolve(__dirname, '../repros');
|
||||
|
||||
export const options = createOptions({
|
||||
@ -221,7 +222,7 @@ function addEsbuildLoaderToStories(mainConfig: ConfigFile) {
|
||||
...config.modules,
|
||||
rules: [
|
||||
{
|
||||
test: [/\\/code\\/[^/]*\\/[^/]*\\/template\\/stories\\//],
|
||||
test: [/\\/template-stories\\//],
|
||||
loader: '${loaderPath}',
|
||||
options: {
|
||||
loader: 'tsx',
|
||||
@ -263,43 +264,42 @@ function addPreviewAnnotations(mainConfig: ConfigFile, paths: string[]) {
|
||||
}
|
||||
|
||||
// paths are of the form 'renderers/react', 'addons/actions'
|
||||
async function addStories(paths: string[], { mainConfig }: { mainConfig: ConfigFile }) {
|
||||
// Add `stories` entries of the form
|
||||
// '../../../code/lib/store/template/stories/*.stories.@(js|jsx|ts|tsx)'
|
||||
async function addStories(
|
||||
packageDirs: string[],
|
||||
{ mainConfig, cwd }: { mainConfig: ConfigFile; cwd: string }
|
||||
) {
|
||||
// Link `stories` directories
|
||||
// '../../../code/lib/store/template/stories' to 'src/templates/lib/store'
|
||||
// if the directory <code>/lib/store/template/stories exists
|
||||
const extraStoryDirsAndExistence = await Promise.all(
|
||||
paths
|
||||
.map((p) => path.join(p, 'template', 'stories'))
|
||||
.map(async (p) => [p, await pathExists(path.resolve(codeDir, p))] as const)
|
||||
//
|
||||
// We link rather than reference relative dir to avoid Running two versions
|
||||
// of React in react-based projects
|
||||
await Promise.all(
|
||||
packageDirs.map(async (p) => {
|
||||
const source = path.join(codeDir, p, 'template', 'stories');
|
||||
await ensureSymlink(source, path.resolve(cwd, 'template-stories', p));
|
||||
})
|
||||
);
|
||||
|
||||
const stories = mainConfig.getFieldValue(['stories']) as string[];
|
||||
const extraStories = extraStoryDirsAndExistence
|
||||
.filter(([, exists]) => exists)
|
||||
.map(([p]) => ({
|
||||
directory: path.join('..', '..', '..', 'code', p),
|
||||
titlePrefix: p.split('/').slice(-4, -2).join('/'),
|
||||
files: '**/*.stories.@(js|jsx|ts|tsx)',
|
||||
}));
|
||||
mainConfig.setFieldValue(['stories'], [...stories, ...extraStories]);
|
||||
// FIXME: '*.@(mdx|stories.mdx|stories.tsx|stories.ts|stories.jsx|stories.js'
|
||||
const linkedStories = path.join('..', 'template-stories', '**', '*.stories.@(js|jsx|ts|tsx|mdx)');
|
||||
mainConfig.setFieldValue(['stories'], [...stories, linkedStories]);
|
||||
|
||||
// Add `config` entries of the form
|
||||
// '../../code/lib/store/template/stories/preview.ts'
|
||||
// if the file <code>/lib/store/template/stories/preview.ts exists
|
||||
const extraPreviewAndExistence = await Promise.all(
|
||||
extraStoryDirsAndExistence
|
||||
.filter(([, exists]) => exists)
|
||||
.map(([storiesPath]) => path.join(storiesPath, 'preview.ts'))
|
||||
.map(
|
||||
async (previewPath) =>
|
||||
[previewPath, await pathExists(path.resolve(codeDir, previewPath))] as const
|
||||
)
|
||||
const packageDirsWithPreview = await filterExistsInCodeDir(
|
||||
packageDirs,
|
||||
path.join('template', 'stories', 'preview.ts')
|
||||
);
|
||||
|
||||
const extraConfig = extraPreviewAndExistence
|
||||
.filter(([, exists]) => exists)
|
||||
.map(([p]) => path.join('..', '..', 'code', p));
|
||||
addPreviewAnnotations(mainConfig, extraConfig);
|
||||
const config = mainConfig.getFieldValue(['config']) as string[];
|
||||
const extraConfig = packageDirsWithPreview.map((p) => {
|
||||
const previewFile = path.join('template-stories', p, 'preview.ts');
|
||||
return `./${previewFile}`;
|
||||
});
|
||||
mainConfig.setFieldValue(['config'], [...(config || []), ...extraConfig]);
|
||||
}
|
||||
|
||||
type Workspace = { name: string; location: string };
|
||||
@ -320,6 +320,23 @@ function workspacePath(type: string, packageName: string, workspaces: Workspace[
|
||||
return workspace.location;
|
||||
}
|
||||
|
||||
function addExtraDependencies({
|
||||
cwd,
|
||||
dryRun,
|
||||
debug,
|
||||
}: {
|
||||
cwd: string;
|
||||
dryRun: boolean;
|
||||
debug: boolean;
|
||||
}) {
|
||||
const extraDeps = ['@storybook/jest'];
|
||||
if (debug) console.log('🎁 Adding extra deps', extraDeps);
|
||||
if (!dryRun) {
|
||||
const packageManager = JsPackageManagerFactory.getPackageManager(false, cwd);
|
||||
packageManager.addDependencies({ installAsDevDependencies: true }, extraDeps);
|
||||
}
|
||||
}
|
||||
|
||||
export async function sandbox(optionValues: OptionValues<typeof options>) {
|
||||
const { template, forceDelete, forceReuse, dryRun, debug, fromLocalRepro } = optionValues;
|
||||
|
||||
@ -405,7 +422,14 @@ export async function sandbox(optionValues: OptionValues<typeof options>) {
|
||||
for (const addon of [...defaultAddons, ...optionValues.addon]) {
|
||||
storiesToAdd.push(workspacePath('addon', `@storybook/addon-${addon}`, workspaces));
|
||||
}
|
||||
await addStories(storiesToAdd, { mainConfig });
|
||||
const existingStories = await filterExistsInCodeDir(
|
||||
storiesToAdd,
|
||||
path.join('template', 'stories')
|
||||
);
|
||||
await addStories(existingStories, {
|
||||
mainConfig,
|
||||
cwd,
|
||||
});
|
||||
|
||||
// Add some extra settings (see above for what these do)
|
||||
mainConfig.setFieldValue(['core', 'disableTelemetry'], true);
|
||||
@ -451,6 +475,9 @@ export async function sandbox(optionValues: OptionValues<typeof options>) {
|
||||
);
|
||||
}
|
||||
|
||||
// Some addon stories require extra dependencies
|
||||
addExtraDependencies({ cwd, dryRun, debug });
|
||||
|
||||
await addPackageScripts({
|
||||
cwd,
|
||||
scripts: {
|
||||
|
15
scripts/utils/filterExistsInCodeDir.ts
Normal file
15
scripts/utils/filterExistsInCodeDir.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import path from 'path';
|
||||
import { pathExists } from 'fs-extra';
|
||||
|
||||
export const codeDir = path.resolve(__dirname, '../../code');
|
||||
|
||||
// packageDirs of the form `lib/store`
|
||||
// paths to check of the form 'template/stories'
|
||||
export const filterExistsInCodeDir = async (packageDirs: string[], pathToCheck: string) =>
|
||||
(
|
||||
await Promise.all(
|
||||
packageDirs.map(async (p) =>
|
||||
(await pathExists(path.resolve(codeDir, path.join(p, pathToCheck)))) ? p : null
|
||||
)
|
||||
)
|
||||
).filter(Boolean);
|
Loading…
x
Reference in New Issue
Block a user