mirror of
https://github.com/storybookjs/storybook.git
synced 2025-04-05 01:31:06 +08:00
Merge pull request #19718 from storybookjs/norbert/sb-798-figure-out-plan-for-package-structure-rework
prebundle the preview
This commit is contained in:
commit
d491ad3a97
@ -89,7 +89,7 @@
|
||||
"devDependencies": {
|
||||
"@storybook/jest": "^0.0.10",
|
||||
"@storybook/testing-library": "0.0.14-next.0",
|
||||
"@types/node": "^16.0.0 || ^18.0.0",
|
||||
"@types/node": "^16.0.0",
|
||||
"formik": "^2.2.9",
|
||||
"typescript": "^4.9.3"
|
||||
},
|
||||
|
@ -47,7 +47,7 @@
|
||||
"@storybook/node-logger": "7.0.0-alpha.52",
|
||||
"@storybook/store": "7.0.0-alpha.52",
|
||||
"@storybook/types": "7.0.0-alpha.52",
|
||||
"@types/node": "^16.0.0 || ^18.0.0",
|
||||
"@types/node": "^16.0.0",
|
||||
"@types/react": "^16.14.34",
|
||||
"@types/react-dom": "^16.9.14",
|
||||
"@types/semver": "^7.3.4",
|
||||
|
@ -54,7 +54,7 @@
|
||||
"@storybook/core-common": "7.0.0-alpha.52",
|
||||
"@storybook/html": "7.0.0-alpha.52",
|
||||
"@storybook/preset-html-webpack": "7.0.0-alpha.52",
|
||||
"@types/node": "^16.0.0 || ^18.0.0",
|
||||
"@types/node": "^16.0.0",
|
||||
"global": "^4.4.0",
|
||||
"react": "16.14.0",
|
||||
"react-dom": "16.14.0"
|
||||
|
@ -65,7 +65,7 @@
|
||||
"@storybook/node-logger": "7.0.0-alpha.52",
|
||||
"@storybook/preset-react-webpack": "7.0.0-alpha.52",
|
||||
"@storybook/react": "7.0.0-alpha.52",
|
||||
"@types/node": "^16.0.0 || ^18.0.0",
|
||||
"@types/node": "^16.0.0",
|
||||
"find-up": "^5.0.0",
|
||||
"fs-extra": "^9.0.1",
|
||||
"image-size": "^1.0.0",
|
||||
|
@ -54,7 +54,7 @@
|
||||
"@storybook/core-common": "7.0.0-alpha.52",
|
||||
"@storybook/preact": "7.0.0-alpha.52",
|
||||
"@storybook/preset-preact-webpack": "7.0.0-alpha.52",
|
||||
"@types/node": "^16.0.0 || ^18.0.0",
|
||||
"@types/node": "^16.0.0",
|
||||
"react": "16.14.0",
|
||||
"react-dom": "16.14.0"
|
||||
},
|
||||
|
@ -67,7 +67,7 @@
|
||||
"vite": "^3.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^16.0.0 || ^18.0.0",
|
||||
"@types/node": "^16.0.0",
|
||||
"typescript": "^4.9.3",
|
||||
"vite": "^3.1.3"
|
||||
},
|
||||
|
@ -54,7 +54,7 @@
|
||||
"@storybook/builder-webpack5": "7.0.0-alpha.52",
|
||||
"@storybook/preset-react-webpack": "7.0.0-alpha.52",
|
||||
"@storybook/react": "7.0.0-alpha.52",
|
||||
"@types/node": "^16.0.0 || ^18.0.0"
|
||||
"@types/node": "^16.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"jest-specific-snapshot": "^6.0.0",
|
||||
|
@ -54,7 +54,7 @@
|
||||
"@storybook/core-common": "7.0.0-alpha.52",
|
||||
"@storybook/preset-server-webpack": "7.0.0-alpha.52",
|
||||
"@storybook/server": "7.0.0-alpha.52",
|
||||
"@types/node": "^16.0.0 || ^18.0.0",
|
||||
"@types/node": "^16.0.0",
|
||||
"react": "16.14.0",
|
||||
"react-dom": "16.14.0"
|
||||
},
|
||||
|
@ -67,7 +67,7 @@
|
||||
"vite": "^3.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^16.0.0 || ^18.0.0",
|
||||
"@types/node": "^16.0.0",
|
||||
"typescript": "^4.9.3",
|
||||
"vite": "^3.1.3"
|
||||
},
|
||||
|
@ -54,7 +54,7 @@
|
||||
"@storybook/core-common": "7.0.0-alpha.52",
|
||||
"@storybook/preset-vue-webpack": "7.0.0-alpha.52",
|
||||
"@storybook/vue": "7.0.0-alpha.52",
|
||||
"@types/node": "^16.0.0 || ^18.0.0",
|
||||
"@types/node": "^16.0.0",
|
||||
"react": "16.14.0",
|
||||
"react-dom": "16.14.0"
|
||||
},
|
||||
|
@ -65,7 +65,7 @@
|
||||
"vue-docgen-api": "^4.40.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^16.0.0 || ^18.0.0",
|
||||
"@types/node": "^16.0.0",
|
||||
"typescript": "^4.9.3",
|
||||
"vite": "^3.1.3"
|
||||
},
|
||||
|
@ -54,7 +54,7 @@
|
||||
"@storybook/core-common": "7.0.0-alpha.52",
|
||||
"@storybook/preset-vue3-webpack": "7.0.0-alpha.52",
|
||||
"@storybook/vue3": "7.0.0-alpha.52",
|
||||
"@types/node": "^16.0.0 || ^18.0.0",
|
||||
"@types/node": "^16.0.0",
|
||||
"react": "16.14.0",
|
||||
"react-dom": "16.14.0"
|
||||
},
|
||||
|
@ -64,7 +64,7 @@
|
||||
"vite": "3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^16.0.0 || ^18.0.0",
|
||||
"@types/node": "^16.0.0",
|
||||
"typescript": "^4.9.3",
|
||||
"vite": "^3.1.0"
|
||||
},
|
||||
|
@ -57,7 +57,7 @@
|
||||
"@storybook/core-common": "7.0.0-alpha.52",
|
||||
"@storybook/preset-web-components-webpack": "7.0.0-alpha.52",
|
||||
"@storybook/web-components": "7.0.0-alpha.52",
|
||||
"@types/node": "^16.0.0 || ^18.0.0",
|
||||
"@types/node": "^16.0.0",
|
||||
"react": "16.14.0",
|
||||
"react-dom": "16.14.0"
|
||||
},
|
||||
|
@ -24,11 +24,6 @@
|
||||
"import": "./dist/index.mjs",
|
||||
"types": "./dist/index.d.ts"
|
||||
},
|
||||
"./shortcut": {
|
||||
"require": "./dist/shortcut.js",
|
||||
"import": "./dist/shortcut.mjs",
|
||||
"types": "./dist/shortcut.d.ts"
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"main": "dist/index.js",
|
||||
@ -79,8 +74,7 @@
|
||||
},
|
||||
"bundler": {
|
||||
"entries": [
|
||||
"./src/index.tsx",
|
||||
"./src/shortcut.ts"
|
||||
"./src/index.tsx"
|
||||
]
|
||||
},
|
||||
"gitHead": "d2494e3f51ce0f55bcb1ef693a6477c669fbe666"
|
||||
|
8
code/lib/api/shortcut.d.ts
vendored
8
code/lib/api/shortcut.d.ts
vendored
@ -1,8 +0,0 @@
|
||||
export type KeyCollection = string[];
|
||||
|
||||
export function shortcutToHumanString(shortcut: KeyCollection): string;
|
||||
export function eventToShortcut(e: KeyboardEvent): KeyCollection | null;
|
||||
export function shortcutMatchesShortcut(
|
||||
inputShortcut: KeyCollection,
|
||||
shortcut: KeyCollection
|
||||
): boolean;
|
@ -1 +0,0 @@
|
||||
export * from './dist/shortcut';
|
@ -64,6 +64,7 @@ import * as url from './modules/url';
|
||||
import * as version from './modules/versions';
|
||||
// eslint-disable-next-line import/no-cycle
|
||||
import * as globals from './modules/globals';
|
||||
import { eventToShortcut, shortcutMatchesShortcut, shortcutToHumanString } from './lib/shortcut';
|
||||
|
||||
const { ActiveTabs } = layout;
|
||||
|
||||
@ -480,3 +481,5 @@ export function useArgTypes(): API_ArgTypes {
|
||||
const current = useCurrentStory();
|
||||
return (current?.type === 'story' && current.argTypes) || {};
|
||||
}
|
||||
|
||||
export { eventToShortcut, shortcutToHumanString, shortcutMatchesShortcut };
|
||||
|
@ -125,8 +125,8 @@ const starter: StarterFunction = async function* starterGeneratorFn({
|
||||
|
||||
const coreDirOrigin = join(dirname(require.resolve('@storybook/manager/package.json')), 'dist');
|
||||
|
||||
router.use(`/sb-addons`, express.static(addonsDir));
|
||||
router.use(`/sb-manager`, express.static(coreDirOrigin));
|
||||
router.use(`/sb-addons`, express.static(addonsDir, { immutable: true, maxAge: '5m' }));
|
||||
router.use(`/sb-manager`, express.static(coreDirOrigin, { immutable: true, maxAge: '5m' }));
|
||||
|
||||
const { cssFiles, jsFiles } = await readOrderedFiles(addonsDir, compilation?.outputFiles);
|
||||
|
||||
|
@ -57,7 +57,7 @@ export const renderHTML = async (
|
||||
refs: Promise<Record<string, Ref>>,
|
||||
logLevel: Promise<string>,
|
||||
docsOptions: Promise<DocsOptions>,
|
||||
{ versionCheck, releaseNotesData, docsMode, previewUrl, serverChannelUrl }: Options
|
||||
{ versionCheck, releaseNotesData, previewUrl, serverChannelUrl, configType }: Options
|
||||
) => {
|
||||
const customHeadRef = await customHead;
|
||||
const titleRef = await title;
|
||||
@ -71,6 +71,7 @@ export const renderHTML = async (
|
||||
REFS: JSON.stringify(await refs, null, 2),
|
||||
LOGLEVEL: JSON.stringify(await logLevel, null, 2),
|
||||
DOCS_OPTIONS: JSON.stringify(await docsOptions, null, 2),
|
||||
CONFIG_TYPE: JSON.stringify(await configType, null, 2),
|
||||
// These two need to be double stringified because the UI expects a string
|
||||
VERSIONCHECK: JSON.stringify(JSON.stringify(versionCheck), null, 2),
|
||||
RELEASE_NOTES_DATA: JSON.stringify(JSON.stringify(releaseNotesData), null, 2),
|
||||
|
@ -8,6 +8,8 @@
|
||||
<link rel="shortcut icon" href="./favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
|
||||
<link href="./sb-preview/runtime.mjs" rel="preload" as="script">
|
||||
|
||||
<% if (typeof head !== 'undefined') { %> <%- head %> <% } %>
|
||||
|
||||
<style>
|
||||
@ -33,10 +35,12 @@
|
||||
</script>
|
||||
<% } %>
|
||||
|
||||
<script src="./sb-manager/runtime.mjs" type="module"></script>
|
||||
<script type="module">
|
||||
import './sb-manager/runtime.mjs';
|
||||
|
||||
<% files.js.forEach(file => { %>
|
||||
<script src="<%= file %>" type="module"></script>
|
||||
<% }); %>
|
||||
<% files.js.forEach(file => { %>
|
||||
import './<%= file %>';
|
||||
<% }); %>
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -14,17 +14,22 @@
|
||||
window.STORIES = '[STORIES HERE]';
|
||||
window.DOCS_OPTIONS = '[DOCS_OPTIONS HERE]';
|
||||
window.SERVER_CHANNEL_URL = '[SERVER_CHANNEL_URL HERE]';
|
||||
|
||||
|
||||
// We do this so that "module && module.hot" etc. in Storybook source code
|
||||
// doesn't fail (it will simply be disabled)
|
||||
window.module = undefined;
|
||||
</script>
|
||||
</script>
|
||||
<!-- [HEAD HTML SNIPPET HERE] -->
|
||||
</head>
|
||||
<body>
|
||||
<!-- [BODY HTML SNIPPET HERE] -->
|
||||
<div id="storybook-root"></div>
|
||||
<div id="storybook-docs"></div>
|
||||
<script type="module" src="/virtual:/@storybook/builder-vite/vite-app.js"></script>
|
||||
<script type="module">
|
||||
/* eslint-disable import/no-absolute-path, import/extensions, import/no-unresolved */
|
||||
|
||||
import '/sb-preview/runtime.mjs';
|
||||
import '/virtual:/@storybook/builder-vite/vite-app.js';
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -41,18 +41,22 @@
|
||||
"prep": "../../../scripts/prepare/bundle.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fal-works/esbuild-plugin-global-externals": "^2.1.2",
|
||||
"@joshwooding/vite-plugin-react-docgen-typescript": "0.0.5",
|
||||
"@storybook/client-api": "7.0.0-alpha.52",
|
||||
"@storybook/client-logger": "7.0.0-alpha.52",
|
||||
"@storybook/core-common": "7.0.0-alpha.52",
|
||||
"@storybook/mdx2-csf": "next",
|
||||
"@storybook/node-logger": "7.0.0-alpha.52",
|
||||
"@storybook/preview": "7.0.0-alpha.52",
|
||||
"@storybook/preview-web": "7.0.0-alpha.52",
|
||||
"@storybook/source-loader": "7.0.0-alpha.52",
|
||||
"@storybook/types": "7.0.0-alpha.52",
|
||||
"@vitejs/plugin-react": "^2.0.0",
|
||||
"browser-assert": "^1.2.1",
|
||||
"es-module-lexer": "^0.9.3",
|
||||
"express": "^4.17.1",
|
||||
"fs-extra": "^9.0.1",
|
||||
"glob": "^7.2.0",
|
||||
"glob-promise": "^4.2.0",
|
||||
"magic-string": "^0.26.1",
|
||||
@ -61,7 +65,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "^4.17.13",
|
||||
"@types/node": "^16.0.0 || ^18.0.0",
|
||||
"@types/node": "^16.0.0",
|
||||
"typescript": "^4.9.3",
|
||||
"vite": "^3.1.3"
|
||||
},
|
||||
|
@ -1,9 +1,11 @@
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as fs from 'fs-extra';
|
||||
import type { Builder, StorybookConfig as StorybookBaseConfig, Options } from '@storybook/types';
|
||||
import type { RequestHandler, Request, Response } from 'express';
|
||||
import type { RequestHandler } from 'express';
|
||||
import type { InlineConfig, UserConfig, ViteDevServer } from 'vite';
|
||||
import express from 'express';
|
||||
import { dirname, join, parse } from 'path';
|
||||
import { transformIframeHtml } from './transform-iframe-html';
|
||||
import { createViteServer } from './vite-server';
|
||||
import { build as viteBuild } from './build';
|
||||
@ -44,7 +46,7 @@ function iframeMiddleware(options: ExtendedOptions, server: ViteDevServer): Requ
|
||||
return;
|
||||
}
|
||||
|
||||
const indexHtml = fs.readFileSync(
|
||||
const indexHtml = await fs.readFile(
|
||||
require.resolve('@storybook/builder-vite/input/iframe.html'),
|
||||
'utf-8'
|
||||
);
|
||||
@ -75,12 +77,10 @@ export const start: ViteBuilder['start'] = async ({
|
||||
}) => {
|
||||
server = await createViteServer(options as ExtendedOptions, devServer);
|
||||
|
||||
// Just mock this endpoint (which is really Webpack-specific) so we don't get spammed with 404 in browser devtools
|
||||
// TODO: we should either show some sort of progress from Vite, or just try to disable the whole Loader in the Manager UI.
|
||||
router.get('/progress', (req: Request, res: Response) => {
|
||||
res.header('Cache-Control', 'no-cache');
|
||||
res.header('Content-Type', 'text/event-stream');
|
||||
});
|
||||
const previewResolvedDir = dirname(require.resolve('@storybook/preview/package.json'));
|
||||
const previewDirOrigin = join(previewResolvedDir, 'dist');
|
||||
|
||||
router.use(`/sb-preview`, express.static(previewDirOrigin, { immutable: true, maxAge: '5m' }));
|
||||
|
||||
router.use(iframeMiddleware(options as ExtendedOptions, server));
|
||||
router.use(server.middlewares);
|
||||
@ -93,5 +93,23 @@ export const start: ViteBuilder['start'] = async ({
|
||||
};
|
||||
|
||||
export const build: ViteBuilder['build'] = async ({ options }) => {
|
||||
return viteBuild(options as ExtendedOptions);
|
||||
const viteCompilation = viteBuild(options as ExtendedOptions);
|
||||
|
||||
const previewResolvedDir = dirname(require.resolve('@storybook/preview/package.json'));
|
||||
const previewDirOrigin = join(previewResolvedDir, 'dist');
|
||||
const previewDirTarget = join(options.outputDir || '', `sb-preview`);
|
||||
|
||||
const previewFiles = fs.copy(previewDirOrigin, previewDirTarget, {
|
||||
filter: (src) => {
|
||||
const { ext } = parse(src);
|
||||
if (ext) {
|
||||
return ext === '.mjs';
|
||||
}
|
||||
return true;
|
||||
},
|
||||
});
|
||||
|
||||
const [out] = await Promise.all([viteCompilation, previewFiles]);
|
||||
|
||||
return out;
|
||||
};
|
||||
|
@ -1,6 +1,8 @@
|
||||
import * as path from 'path';
|
||||
import { normalizePath, resolveConfig } from 'vite';
|
||||
import type { InlineConfig as ViteInlineConfig } from 'vite';
|
||||
import type { InlineConfig as ViteInlineConfig, UserConfig } from 'vite';
|
||||
import { globalExternals } from '@fal-works/esbuild-plugin-global-externals';
|
||||
import { definitions } from '@storybook/preview/globals';
|
||||
import { listStories } from './list-stories';
|
||||
|
||||
import type { ExtendedOptions } from './types';
|
||||
@ -113,11 +115,16 @@ export async function getOptimizeDeps(config: ViteInlineConfig, options: Extende
|
||||
const resolve = resolvedConfig.createResolver({ asSrc: false });
|
||||
const include = await asyncFilter(INCLUDE_CANDIDATES, async (id) => Boolean(await resolve(id)));
|
||||
|
||||
return {
|
||||
const optimizeDeps: UserConfig['optimizeDeps'] = {
|
||||
// We don't need to resolve the glob since vite supports globs for entries.
|
||||
entries: stories,
|
||||
// We need Vite to precompile these dependencies, because they contain non-ESM code that would break
|
||||
// if we served it directly to the browser.
|
||||
include,
|
||||
esbuildOptions: {
|
||||
plugins: [globalExternals(definitions)],
|
||||
},
|
||||
};
|
||||
|
||||
return optimizeDeps;
|
||||
}
|
||||
|
@ -101,6 +101,10 @@ export function codeGeneratorPlugin(options: ExtendedOptions): Plugin {
|
||||
if (source === virtualAddonSetupFile) {
|
||||
return virtualAddonSetupFile;
|
||||
}
|
||||
if (source === '/sb-preview/runtime.mjs') {
|
||||
return '/sb-preview/runtime.mjs';
|
||||
}
|
||||
|
||||
return undefined;
|
||||
},
|
||||
async load(id) {
|
||||
@ -127,6 +131,11 @@ export function codeGeneratorPlugin(options: ExtendedOptions): Plugin {
|
||||
return generateIframeScriptCode(options);
|
||||
}
|
||||
|
||||
// This is handled by the express router, not vite
|
||||
if (id === '/sb-preview/runtime.mjs') {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (id === iframeId) {
|
||||
return fs.readFileSync(
|
||||
require.resolve('@storybook/builder-vite/input/iframe.html'),
|
||||
|
@ -5,6 +5,7 @@ import type {
|
||||
InlineConfig as ViteInlineConfig,
|
||||
PluginOption,
|
||||
UserConfig as ViteConfig,
|
||||
InlineConfig,
|
||||
} from 'vite';
|
||||
import viteReact from '@vitejs/plugin-react';
|
||||
import { isPreservingSymlinks, getFrameworkName } from '@storybook/core-common';
|
||||
@ -42,12 +43,13 @@ export async function commonConfig(
|
||||
|
||||
const { config: userConfig = {} } = (await loadConfigFromFile(configEnv)) ?? {};
|
||||
|
||||
const sbConfig = {
|
||||
const sbConfig: InlineConfig = {
|
||||
configFile: false,
|
||||
cacheDir: 'node_modules/.cache/.vite-storybook',
|
||||
root: path.resolve(options.configDir, '..'),
|
||||
// Allow storybook deployed as subfolder. See https://github.com/storybookjs/builder-vite/issues/238
|
||||
base: './',
|
||||
|
||||
plugins: await pluginConfig(options),
|
||||
resolve: {
|
||||
preserveSymlinks: isPreservingSymlinks(),
|
||||
|
@ -36,6 +36,7 @@
|
||||
"types": "./dist/presets/preview-preset.d.ts"
|
||||
},
|
||||
"./templates/virtualModuleModernEntry.js.handlebars": "./templates/virtualModuleModernEntry.js.handlebars",
|
||||
"./templates/preview.ejs": "./templates/preview.ejs",
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"main": "dist/index.js",
|
||||
@ -65,18 +66,21 @@
|
||||
"@storybook/core-events": "7.0.0-alpha.52",
|
||||
"@storybook/core-webpack": "7.0.0-alpha.52",
|
||||
"@storybook/node-logger": "7.0.0-alpha.52",
|
||||
"@storybook/preview": "7.0.0-alpha.52",
|
||||
"@storybook/preview-web": "7.0.0-alpha.52",
|
||||
"@storybook/router": "7.0.0-alpha.52",
|
||||
"@storybook/store": "7.0.0-alpha.52",
|
||||
"@storybook/theming": "7.0.0-alpha.52",
|
||||
"@types/node": "^16.0.0 || ^18.0.0",
|
||||
"@types/node": "^16.0.0",
|
||||
"@types/semver": "^7.3.4",
|
||||
"babel-loader": "^8.3.0",
|
||||
"babel-plugin-named-exports-order": "^0.0.2",
|
||||
"browser-assert": "^1.2.1",
|
||||
"case-sensitive-paths-webpack-plugin": "^2.4.0",
|
||||
"css-loader": "^6.7.1",
|
||||
"express": "^4.17.1",
|
||||
"fork-ts-checker-webpack-plugin": "^7.2.8",
|
||||
"fs-extra": "^9.0.1",
|
||||
"global": "^4.4.0",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"path-browserify": "^1.0.1",
|
||||
@ -93,10 +97,12 @@
|
||||
"webpack-virtual-modules": "^0.4.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/pretty-hrtime": "^1.0.0",
|
||||
"@types/terser-webpack-plugin": "^5.2.0",
|
||||
"@types/webpack-dev-middleware": "^5.3.0",
|
||||
"@types/webpack-hot-middleware": "^2.25.6",
|
||||
"@types/webpack-virtual-modules": "^0.1.1",
|
||||
"pretty-hrtime": "^1.0.3",
|
||||
"typescript": "^4.9.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
@ -3,13 +3,24 @@ import webpack, { ProgressPlugin } from 'webpack';
|
||||
import webpackDevMiddleware from 'webpack-dev-middleware';
|
||||
import webpackHotMiddleware from 'webpack-hot-middleware';
|
||||
import { logger } from '@storybook/node-logger';
|
||||
import { useProgressReporting } from '@storybook/core-common';
|
||||
import type { Builder, Options } from '@storybook/types';
|
||||
import { checkWebpackVersion } from '@storybook/core-webpack';
|
||||
import { join } from 'path';
|
||||
import { dirname, join, parse } from 'path';
|
||||
import express from 'express';
|
||||
import fs from 'fs-extra';
|
||||
import { PREVIEW_BUILDER_PROGRESS } from '@storybook/core-events';
|
||||
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import prettyTime from 'pretty-hrtime';
|
||||
|
||||
export * from './types';
|
||||
|
||||
export const printDuration = (startTime: [number, number]) =>
|
||||
prettyTime(process.hrtime(startTime))
|
||||
.replace(' ms', ' milliseconds')
|
||||
.replace(' s', ' seconds')
|
||||
.replace(' m', ' minutes');
|
||||
|
||||
let compilation: ReturnType<typeof webpackDevMiddleware> | undefined;
|
||||
let reject: (reason?: any) => void;
|
||||
|
||||
@ -98,6 +109,7 @@ const starter: StarterFunction = async function* starterGeneratorFn({
|
||||
startTime,
|
||||
options,
|
||||
router,
|
||||
channel,
|
||||
}) {
|
||||
const webpackInstance = await executor.get(options);
|
||||
yield;
|
||||
@ -120,9 +132,40 @@ const starter: StarterFunction = async function* starterGeneratorFn({
|
||||
};
|
||||
}
|
||||
|
||||
const { handler, modulesCount } = await useProgressReporting(router, startTime, options);
|
||||
yield;
|
||||
new ProgressPlugin({ handler, modulesCount }).apply(compiler);
|
||||
const modulesCount = (await options.cache?.get('modulesCount').catch(() => {})) || 1000;
|
||||
let totalModules: number;
|
||||
let value = 0;
|
||||
|
||||
new ProgressPlugin({
|
||||
handler: (newValue, message, arg3) => {
|
||||
value = Math.max(newValue, value); // never go backwards
|
||||
const progress = { value, message: message.charAt(0).toUpperCase() + message.slice(1) };
|
||||
if (message === 'building') {
|
||||
// arg3 undefined in webpack5
|
||||
const counts = (arg3 && arg3.match(/(\d+)\/(\d+)/)) || [];
|
||||
const complete = parseInt(counts[1], 10);
|
||||
const total = parseInt(counts[2], 10);
|
||||
if (!Number.isNaN(complete) && !Number.isNaN(total)) {
|
||||
(progress as any).modules = { complete, total };
|
||||
totalModules = total;
|
||||
}
|
||||
}
|
||||
|
||||
if (value === 1) {
|
||||
if (options.cache) {
|
||||
options.cache.set('modulesCount', totalModules);
|
||||
}
|
||||
|
||||
if (!progress.message) {
|
||||
progress.message = `Completed in ${printDuration(startTime)}.`;
|
||||
}
|
||||
}
|
||||
|
||||
channel.emit(PREVIEW_BUILDER_PROGRESS, [progress]);
|
||||
},
|
||||
modulesCount,
|
||||
}).apply(compiler);
|
||||
|
||||
const middlewareOptions: Parameters<typeof webpackDevMiddleware>[1] = {
|
||||
publicPath: config.output?.publicPath as string,
|
||||
@ -131,6 +174,11 @@ const starter: StarterFunction = async function* starterGeneratorFn({
|
||||
|
||||
compilation = webpackDevMiddleware(compiler, middlewareOptions);
|
||||
|
||||
const previewResolvedDir = dirname(require.resolve('@storybook/preview/package.json'));
|
||||
const previewDirOrigin = join(previewResolvedDir, 'dist');
|
||||
|
||||
router.use(`/sb-preview`, express.static(previewDirOrigin, { immutable: true, maxAge: '5m' }));
|
||||
|
||||
router.use(compilation);
|
||||
router.use(webpackHotMiddleware(compiler as any));
|
||||
|
||||
@ -181,7 +229,7 @@ const builder: BuilderFunction = async function* builderGeneratorFn({ startTime,
|
||||
} as any as Stats;
|
||||
}
|
||||
|
||||
return new Promise<Stats>((succeed, fail) => {
|
||||
const webpackCompilation = new Promise<Stats>((succeed, fail) => {
|
||||
compiler.run((error, stats) => {
|
||||
if (error || !stats || stats.hasErrors()) {
|
||||
logger.error('=> Failed to build the preview');
|
||||
@ -238,6 +286,24 @@ const builder: BuilderFunction = async function* builderGeneratorFn({ startTime,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const previewResolvedDir = dirname(require.resolve('@storybook/preview/package.json'));
|
||||
const previewDirOrigin = join(previewResolvedDir, 'dist');
|
||||
const previewDirTarget = join(options.outputDir || '', `sb-preview`);
|
||||
|
||||
const previewFiles = fs.copy(previewDirOrigin, previewDirTarget, {
|
||||
filter: (src) => {
|
||||
const { ext } = parse(src);
|
||||
if (ext) {
|
||||
return ext === '.mjs';
|
||||
}
|
||||
return true;
|
||||
},
|
||||
});
|
||||
|
||||
const [webpackCompilationOutput] = await Promise.all([webpackCompilation, previewFiles]);
|
||||
|
||||
return webpackCompilationOutput;
|
||||
};
|
||||
|
||||
export const start = async (options: BuilderStartOptions) => {
|
||||
|
@ -30,3 +30,6 @@ export const babel = async (config: any, options: any) => ({
|
||||
});
|
||||
|
||||
export const babelLoaderRef = () => require.resolve('babel-loader');
|
||||
|
||||
export const previewMainTemplate = () =>
|
||||
require.resolve('@storybook/builder-webpack5/templates/preview.ejs');
|
||||
|
@ -1,4 +1,4 @@
|
||||
import path from 'path';
|
||||
import { dirname, join, resolve } from 'path';
|
||||
import { DefinePlugin, HotModuleReplacementPlugin, ProgressPlugin, ProvidePlugin } from 'webpack';
|
||||
import type { Configuration } from 'webpack';
|
||||
import HtmlWebpackPlugin from 'html-webpack-plugin';
|
||||
@ -24,27 +24,17 @@ import type { BuilderOptions, TypescriptOptions } from '../types';
|
||||
import { createBabelLoader } from './babel-loader-preview';
|
||||
|
||||
const storybookPaths: Record<string, string> = {
|
||||
global: path.dirname(require.resolve(`global/package.json`)),
|
||||
global: dirname(require.resolve(`global/package.json`)),
|
||||
...[
|
||||
'addons',
|
||||
// these packages are not pre-bundled because of react dependencies
|
||||
'api',
|
||||
'store',
|
||||
'channels',
|
||||
'channel-postmessage',
|
||||
'channel-websocket',
|
||||
'components',
|
||||
'core-events',
|
||||
'router',
|
||||
'theming',
|
||||
'preview-web',
|
||||
'client-api',
|
||||
'client-logger',
|
||||
].reduce(
|
||||
(acc, sbPackage) => ({
|
||||
...acc,
|
||||
[`@storybook/${sbPackage}`]: path.dirname(
|
||||
require.resolve(`@storybook/${sbPackage}/package.json`)
|
||||
),
|
||||
[`@storybook/${sbPackage}`]: dirname(require.resolve(`@storybook/${sbPackage}/package.json`)),
|
||||
}),
|
||||
{}
|
||||
),
|
||||
@ -54,7 +44,7 @@ export default async (
|
||||
options: Options & Record<string, any> & { typescriptOptions: TypescriptOptions }
|
||||
): Promise<Configuration> => {
|
||||
const {
|
||||
outputDir = path.join('.', 'public'),
|
||||
outputDir = join('.', 'public'),
|
||||
quiet,
|
||||
packageJson,
|
||||
configType,
|
||||
@ -66,21 +56,42 @@ export default async (
|
||||
serverChannelUrl,
|
||||
} = options;
|
||||
|
||||
const frameworkOptions = await presets.apply('frameworkOptions');
|
||||
|
||||
const isProd = configType === 'PRODUCTION';
|
||||
const envs = await presets.apply<Record<string, string>>('env');
|
||||
const logLevel = await presets.apply('logLevel', undefined);
|
||||
const workingDir = process.cwd();
|
||||
|
||||
const [
|
||||
coreOptions,
|
||||
frameworkOptions,
|
||||
envs,
|
||||
logLevel,
|
||||
headHtmlSnippet,
|
||||
bodyHtmlSnippet,
|
||||
template,
|
||||
docsOptions,
|
||||
entries,
|
||||
nonNormalizedStories,
|
||||
] = await Promise.all([
|
||||
presets.apply<CoreConfig>('core'),
|
||||
presets.apply('frameworkOptions'),
|
||||
presets.apply<Record<string, string>>('env'),
|
||||
presets.apply('logLevel', undefined),
|
||||
presets.apply('previewHead'),
|
||||
presets.apply('previewBody'),
|
||||
presets.apply<string>('previewMainTemplate'),
|
||||
presets.apply<DocsOptions>('docs'),
|
||||
presets.apply<string[]>('entries', [], options),
|
||||
presets.apply('stories', [], options),
|
||||
]);
|
||||
|
||||
const stories = normalizeStories(nonNormalizedStories, {
|
||||
configDir: options.configDir,
|
||||
workingDir,
|
||||
});
|
||||
|
||||
const headHtmlSnippet = await presets.apply('previewHead');
|
||||
const bodyHtmlSnippet = await presets.apply('previewBody');
|
||||
const template = await presets.apply<string>('previewMainTemplate');
|
||||
const coreOptions = await presets.apply<CoreConfig>('core');
|
||||
const builderOptions: BuilderOptions =
|
||||
typeof coreOptions.builder === 'string'
|
||||
? {}
|
||||
: coreOptions.builder?.options || ({} as BuilderOptions);
|
||||
const docsOptions = await presets.apply<DocsOptions>('docs');
|
||||
|
||||
const previewAnnotations = [
|
||||
...(await presets.apply<PreviewAnnotation[]>('previewAnnotations', [], options)).map(
|
||||
@ -97,21 +108,15 @@ export default async (
|
||||
),
|
||||
loadPreviewOrConfigFile(options),
|
||||
].filter(Boolean);
|
||||
const entries = (await presets.apply('entries', [], options)) as string[];
|
||||
const workingDir = process.cwd();
|
||||
const stories = normalizeStories(await presets.apply('stories', [], options), {
|
||||
configDir: options.configDir,
|
||||
workingDir,
|
||||
});
|
||||
|
||||
const virtualModuleMapping: Record<string, string> = {};
|
||||
if (features?.storyStoreV7) {
|
||||
const storiesFilename = 'storybook-stories.js';
|
||||
const storiesPath = path.resolve(path.join(workingDir, storiesFilename));
|
||||
const storiesPath = resolve(join(workingDir, storiesFilename));
|
||||
|
||||
const needPipelinedImport = !!builderOptions.lazyCompilation && !isProd;
|
||||
virtualModuleMapping[storiesPath] = toImportFn(stories, { needPipelinedImport });
|
||||
const configEntryPath = path.resolve(path.join(workingDir, 'storybook-config-entry.js'));
|
||||
const configEntryPath = resolve(join(workingDir, 'storybook-config-entry.js'));
|
||||
virtualModuleMapping[configEntryPath] = handlebars(
|
||||
await readTemplate(
|
||||
require.resolve(
|
||||
@ -128,14 +133,12 @@ export default async (
|
||||
} else {
|
||||
const rendererName = await getRendererName(options);
|
||||
|
||||
const rendererInitEntry = path.resolve(
|
||||
path.join(workingDir, 'storybook-init-renderer-entry.js')
|
||||
);
|
||||
const rendererInitEntry = resolve(join(workingDir, 'storybook-init-renderer-entry.js'));
|
||||
virtualModuleMapping[rendererInitEntry] = `import '${rendererName}';`;
|
||||
entries.push(rendererInitEntry);
|
||||
|
||||
const entryTemplate = await readTemplate(
|
||||
path.join(__dirname, '..', '..', 'templates', 'virtualModuleEntry.template.js')
|
||||
join(__dirname, '..', '..', 'templates', 'virtualModuleEntry.template.js')
|
||||
);
|
||||
|
||||
previewAnnotations.forEach((previewAnnotationFilename: string | undefined) => {
|
||||
@ -159,12 +162,12 @@ export default async (
|
||||
});
|
||||
if (stories.length > 0) {
|
||||
const storyTemplate = await readTemplate(
|
||||
path.join(__dirname, '..', '..', 'templates', 'virtualModuleStory.template.js')
|
||||
join(__dirname, '..', '..', 'templates', 'virtualModuleStory.template.js')
|
||||
);
|
||||
// NOTE: this file has a `.cjs` extension as it is a CJS file (from `dist/cjs`) and runs
|
||||
// in the user's webpack mode, which may be strict about the use of require/import.
|
||||
// See https://github.com/storybookjs/storybook/issues/14877
|
||||
const storiesFilename = path.resolve(path.join(workingDir, `generated-stories-entry.cjs`));
|
||||
const storiesFilename = resolve(join(workingDir, `generated-stories-entry.cjs`));
|
||||
virtualModuleMapping[storiesFilename] = interpolate(storyTemplate, {
|
||||
rendererName,
|
||||
})
|
||||
@ -192,7 +195,7 @@ export default async (
|
||||
devtool: 'cheap-module-source-map',
|
||||
entry: entries,
|
||||
output: {
|
||||
path: path.resolve(process.cwd(), outputDir),
|
||||
path: resolve(process.cwd(), outputDir),
|
||||
filename: isProd ? '[name].[contenthash:8].iframe.bundle.js' : '[name].iframe.bundle.js',
|
||||
publicPath: '',
|
||||
},
|
||||
@ -203,6 +206,29 @@ export default async (
|
||||
watchOptions: {
|
||||
ignored: /node_modules/,
|
||||
},
|
||||
externals: {
|
||||
...[
|
||||
// these packages are pre-bundled, so they are mapped to global shims
|
||||
'channels',
|
||||
'channel-postmessage',
|
||||
'channel-websocket',
|
||||
'core-events',
|
||||
'client-logger',
|
||||
'addons',
|
||||
'store',
|
||||
'preview-web',
|
||||
'client-api',
|
||||
'core-client',
|
||||
].reduce(
|
||||
(acc, sbPackage) => ({
|
||||
...acc,
|
||||
[`@storybook/${sbPackage}`]: `__STORYBOOK_MODULE_${sbPackage
|
||||
.toUpperCase()
|
||||
.replaceAll('-', '_')}__`,
|
||||
}),
|
||||
{}
|
||||
),
|
||||
},
|
||||
ignoreWarnings: [
|
||||
{
|
||||
message: /export '\S+' was not found in 'global'/,
|
||||
|
@ -36,8 +36,13 @@
|
||||
<% } %>
|
||||
<% } %>
|
||||
</script>
|
||||
<% } %> <% htmlWebpackPlugin.files.js.forEach(file => { %>
|
||||
<script src="<%= file %>"></script>
|
||||
<% }); %>
|
||||
<% } %>
|
||||
<script type="module">
|
||||
import './sb-preview/runtime.mjs';
|
||||
|
||||
<% htmlWebpackPlugin.files.js.forEach(file => { %>
|
||||
import './<%= file %>';
|
||||
<% }); %>
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -51,7 +51,7 @@
|
||||
"@storybook/types": "7.0.0-alpha.52",
|
||||
"@types/babel__core": "^7.1.20",
|
||||
"@types/express": "^4.7.0",
|
||||
"@types/node": "^16.0.0 || ^18.0.0",
|
||||
"@types/node": "^16.0.0",
|
||||
"@types/pretty-hrtime": "^1.0.0",
|
||||
"chalk": "^4.1.0",
|
||||
"esbuild": "^0.14.48",
|
||||
|
@ -23,7 +23,6 @@ export * from './utils/load-preview-or-config-file';
|
||||
export * from './utils/log-config';
|
||||
export * from './utils/normalize-stories';
|
||||
export * from './utils/paths';
|
||||
export * from './utils/progress-reporting';
|
||||
export * from './utils/readTemplate';
|
||||
export * from './utils/resolve-path-in-sb-cache';
|
||||
export * from './utils/symlinks';
|
||||
|
@ -244,10 +244,6 @@ async function loadPresets(
|
||||
return [];
|
||||
}
|
||||
|
||||
if (!level) {
|
||||
logger.info('=> Loading presets');
|
||||
}
|
||||
|
||||
return (
|
||||
await Promise.all(presets.map(async (preset) => loadPreset(preset, level, storybookOptions)))
|
||||
).reduce((acc, loaded) => {
|
||||
|
@ -1,67 +0,0 @@
|
||||
import type { Router, Request, Response } from 'express';
|
||||
import { printDuration } from './print-duration';
|
||||
|
||||
export const useProgressReporting = async (
|
||||
router: Router,
|
||||
startTime: [number, number],
|
||||
options: any
|
||||
): Promise<{ handler: any; modulesCount: number }> => {
|
||||
let value = 0;
|
||||
let totalModules: number;
|
||||
let reportProgress: (progress?: {
|
||||
value?: number;
|
||||
message: string;
|
||||
modules?: any;
|
||||
}) => void = () => {};
|
||||
|
||||
router.get('/progress', (request: Request, response: Response) => {
|
||||
let closed = false;
|
||||
const close = () => {
|
||||
closed = true;
|
||||
response.end();
|
||||
};
|
||||
response.on('close', close);
|
||||
|
||||
if (closed || response.writableEnded) return;
|
||||
response.setHeader('Cache-Control', 'no-cache');
|
||||
response.setHeader('Content-Type', 'text/event-stream');
|
||||
response.setHeader('Connection', 'keep-alive');
|
||||
response.flushHeaders();
|
||||
|
||||
reportProgress = (progress: any) => {
|
||||
if (closed || response.writableEnded) return;
|
||||
response.write(`data: ${JSON.stringify(progress)}\n\n`);
|
||||
response.flush();
|
||||
if (progress.value === 1) close();
|
||||
};
|
||||
});
|
||||
|
||||
const handler = (newValue: number, message: string, arg3: any) => {
|
||||
value = Math.max(newValue, value); // never go backwards
|
||||
const progress = { value, message: message.charAt(0).toUpperCase() + message.slice(1) };
|
||||
if (message === 'building') {
|
||||
// arg3 undefined in webpack5
|
||||
const counts = (arg3 && arg3.match(/(\d+)\/(\d+)/)) || [];
|
||||
const complete = parseInt(counts[1], 10);
|
||||
const total = parseInt(counts[2], 10);
|
||||
if (!Number.isNaN(complete) && !Number.isNaN(total)) {
|
||||
(progress as any).modules = { complete, total };
|
||||
totalModules = total;
|
||||
}
|
||||
}
|
||||
|
||||
if (value === 1) {
|
||||
if (options.cache) {
|
||||
options.cache.set('modulesCount', totalModules);
|
||||
}
|
||||
|
||||
if (!progress.message) {
|
||||
progress.message = `Completed in ${printDuration(startTime)}.`;
|
||||
}
|
||||
}
|
||||
reportProgress(progress);
|
||||
};
|
||||
|
||||
const modulesCount = (await options.cache?.get('modulesCount').catch(() => {})) || 1000;
|
||||
return { handler, modulesCount };
|
||||
};
|
@ -36,7 +36,3 @@ export function getPreviewHeadTemplate(
|
||||
|
||||
return interpolate(result, interpolations);
|
||||
}
|
||||
|
||||
export function getPreviewMainTemplate() {
|
||||
return `${sync(__dirname)}/templates/preview.ejs`;
|
||||
}
|
||||
|
@ -51,6 +51,8 @@ enum events {
|
||||
REGISTER_SUBSCRIPTION = 'registerSubscription',
|
||||
// Tell the manager that the user pressed a key in the preview
|
||||
PREVIEW_KEYDOWN = 'previewKeydown',
|
||||
// Tell the preview that the builder is in progress
|
||||
PREVIEW_BUILDER_PROGRESS = 'preview_builder_progress',
|
||||
// Used in the manager to change the story selection
|
||||
SELECT_STORY = 'selectStory',
|
||||
STORIES_COLLAPSE_ALL = 'storiesCollapseAll',
|
||||
@ -70,41 +72,42 @@ export default events;
|
||||
export const {
|
||||
CHANNEL_CREATED,
|
||||
CONFIG_ERROR,
|
||||
STORY_INDEX_INVALIDATED,
|
||||
STORY_SPECIFIED,
|
||||
SET_STORIES,
|
||||
SET_INDEX,
|
||||
SET_CONFIG,
|
||||
SET_CURRENT_STORY,
|
||||
CURRENT_STORY_WAS_SET,
|
||||
DOCS_RENDERED,
|
||||
FORCE_RE_RENDER,
|
||||
FORCE_REMOUNT,
|
||||
STORY_PREPARED,
|
||||
STORY_CHANGED,
|
||||
STORY_UNCHANGED,
|
||||
PRELOAD_ENTRIES,
|
||||
STORY_RENDERED,
|
||||
STORY_MISSING,
|
||||
STORY_ERRORED,
|
||||
STORY_THREW_EXCEPTION,
|
||||
STORY_RENDER_PHASE_CHANGED,
|
||||
PLAY_FUNCTION_THREW_EXCEPTION,
|
||||
UPDATE_STORY_ARGS,
|
||||
STORY_ARGS_UPDATED,
|
||||
RESET_STORY_ARGS,
|
||||
SET_GLOBALS,
|
||||
UPDATE_GLOBALS,
|
||||
GLOBALS_UPDATED,
|
||||
REGISTER_SUBSCRIPTION,
|
||||
NAVIGATE_URL,
|
||||
PLAY_FUNCTION_THREW_EXCEPTION,
|
||||
PRELOAD_ENTRIES,
|
||||
PREVIEW_BUILDER_PROGRESS,
|
||||
PREVIEW_KEYDOWN,
|
||||
REGISTER_SUBSCRIPTION,
|
||||
RESET_STORY_ARGS,
|
||||
SELECT_STORY,
|
||||
STORIES_COLLAPSE_ALL,
|
||||
STORIES_EXPAND_ALL,
|
||||
DOCS_RENDERED,
|
||||
SET_CONFIG,
|
||||
SET_CURRENT_STORY,
|
||||
SET_GLOBALS,
|
||||
SET_INDEX,
|
||||
SET_STORIES,
|
||||
SHARED_STATE_CHANGED,
|
||||
SHARED_STATE_SET,
|
||||
NAVIGATE_URL,
|
||||
STORIES_COLLAPSE_ALL,
|
||||
STORIES_EXPAND_ALL,
|
||||
STORY_ARGS_UPDATED,
|
||||
STORY_CHANGED,
|
||||
STORY_ERRORED,
|
||||
STORY_INDEX_INVALIDATED,
|
||||
STORY_MISSING,
|
||||
STORY_PREPARED,
|
||||
STORY_RENDER_PHASE_CHANGED,
|
||||
STORY_RENDERED,
|
||||
STORY_SPECIFIED,
|
||||
STORY_THREW_EXCEPTION,
|
||||
STORY_UNCHANGED,
|
||||
UPDATE_GLOBALS,
|
||||
UPDATE_QUERY_PARAMS,
|
||||
UPDATE_STORY_ARGS,
|
||||
} = events;
|
||||
|
||||
// Used to break out of the current render without showing a redbox
|
||||
|
@ -46,7 +46,7 @@
|
||||
"@storybook/store": "7.0.0-alpha.52",
|
||||
"@storybook/telemetry": "7.0.0-alpha.52",
|
||||
"@storybook/types": "7.0.0-alpha.52",
|
||||
"@types/node": "^16.0.0 || ^18.0.0",
|
||||
"@types/node": "^16.0.0",
|
||||
"@types/node-fetch": "^2.5.7",
|
||||
"@types/pretty-hrtime": "^1.0.0",
|
||||
"@types/semver": "^7.3.4",
|
||||
|
@ -25,7 +25,7 @@ import { outputStats } from './utils/output-stats';
|
||||
import { outputStartupInformation } from './utils/output-startup-information';
|
||||
import { updateCheck } from './utils/update-check';
|
||||
import { getServerPort, getServerChannelUrl } from './utils/server-address';
|
||||
import { getBuilders } from './utils/get-builders';
|
||||
import { getManagerBuilder, getPreviewBuilder } from './utils/get-builders';
|
||||
|
||||
export async function buildDevStandalone(options: CLIOptions & LoadOptions & BuilderOptions) {
|
||||
const { packageJson, versionUpdates, releaseNotes } = options;
|
||||
@ -76,16 +76,23 @@ export async function buildDevStandalone(options: CLIOptions & LoadOptions & Bui
|
||||
logger.warn(`you have not specified a framework in your ${options.configDir}/main.js`);
|
||||
}
|
||||
|
||||
logger.info('=> Loading presets');
|
||||
// Load first pass: We need to determine the builder
|
||||
// We need to do this because builders might introduce 'overridePresets' which we need to take into account
|
||||
// We hope to remove this in SB8
|
||||
let presets = await loadAllPresets({
|
||||
corePresets,
|
||||
overridePresets: [],
|
||||
...options,
|
||||
});
|
||||
|
||||
const [previewBuilder, managerBuilder] = await getBuilders({ ...options, presets });
|
||||
const { renderer } = await presets.apply<CoreConfig>('core', undefined);
|
||||
const { renderer, builder } = await presets.apply<CoreConfig>('core', undefined);
|
||||
const builderName = typeof builder === 'string' ? builder : builder?.name;
|
||||
const [previewBuilder, managerBuilder] = await Promise.all([
|
||||
getPreviewBuilder(builderName, options.configDir),
|
||||
getManagerBuilder(),
|
||||
]);
|
||||
|
||||
// Load second pass: all presets are applied in order
|
||||
presets = await loadAllPresets({
|
||||
corePresets: [
|
||||
require.resolve('./presets/common-preset'),
|
||||
|
@ -1,312 +0,0 @@
|
||||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import 'jest-specific-snapshot';
|
||||
import path from 'path';
|
||||
import { mkdtemp as mkdtempCb } from 'fs';
|
||||
import os from 'os';
|
||||
import { promisify } from 'util';
|
||||
import type { Configuration } from 'webpack';
|
||||
import {
|
||||
resolvePathInStorybookCache,
|
||||
createFileSystemCache,
|
||||
getProjectRoot,
|
||||
} from '@storybook/core-common';
|
||||
import { executor as previewExecutor } from '@storybook/builder-webpack5';
|
||||
import { executor as managerExecutor } from '@storybook/builder-manager';
|
||||
|
||||
import { sync as readUpSync } from 'read-pkg-up';
|
||||
import { buildDevStandalone } from './build-dev';
|
||||
import { buildStaticStandalone } from './build-static';
|
||||
|
||||
import { outputStats } from './utils/output-stats';
|
||||
|
||||
// @ts-expect-error (not strict)
|
||||
const { SNAPSHOT_OS } = global;
|
||||
const mkdtemp = promisify(mkdtempCb);
|
||||
const { packageJson } = readUpSync({ cwd: __dirname });
|
||||
|
||||
// this only applies to this file
|
||||
jest.setTimeout(10000);
|
||||
|
||||
// FIXME: this doesn't work
|
||||
|
||||
jest.mock('webpack', () => {
|
||||
const value = jest.fn(() => false);
|
||||
const actual = jest.requireActual('webpack');
|
||||
|
||||
Object.keys(actual).forEach((key) => {
|
||||
// @ts-expect-error (not strict)
|
||||
value[key] = actual[key];
|
||||
});
|
||||
return value;
|
||||
});
|
||||
|
||||
jest.mock('@storybook/telemetry', () => ({
|
||||
getStorybookMetadata: jest.fn(() => ({})),
|
||||
telemetry: jest.fn(() => ({})),
|
||||
}));
|
||||
jest.mock('fs-extra', () => ({
|
||||
copy: jest.fn(() => undefined),
|
||||
emptyDir: jest.fn(() => undefined),
|
||||
ensureDir: jest.fn(() => true),
|
||||
ensureFile: jest.fn(() => undefined),
|
||||
pathExists: jest.fn(() => true),
|
||||
readFile: jest.fn((f) => ''),
|
||||
readJSON: jest.fn(() => ({})),
|
||||
remove: jest.fn(() => undefined),
|
||||
writeFile: jest.fn(() => undefined),
|
||||
writeJSON: jest.fn(() => undefined),
|
||||
}));
|
||||
|
||||
jest.mock('./utils/StoryIndexGenerator', () => {
|
||||
const { StoryIndexGenerator } = jest.requireActual('./utils/StoryIndexGenerator');
|
||||
return {
|
||||
StoryIndexGenerator: class extends StoryIndexGenerator {
|
||||
initialize() {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
getIndex() {
|
||||
return { stories: {}, v: 3 };
|
||||
}
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('./utils/stories-json', () => ({
|
||||
extractStoriesJson: () => Promise.resolve(),
|
||||
useStoriesJson: () => {},
|
||||
}));
|
||||
|
||||
// we're not in the right directory for auto-title to work, so just
|
||||
// stub it out
|
||||
jest.mock('@storybook/store', () => {
|
||||
const actualStore = jest.requireActual('@storybook/store');
|
||||
return {
|
||||
...actualStore,
|
||||
autoTitle: () => 'auto-title',
|
||||
autoTitleFromSpecifier: () => 'auto-title-from-specifier',
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('http', () => ({
|
||||
...jest.requireActual('http'),
|
||||
// @ts-expect-error (not strict)
|
||||
createServer: () => ({ listen: (_options, cb) => cb(), on: jest.fn() }),
|
||||
}));
|
||||
jest.mock('ws');
|
||||
jest.mock('@storybook/node-logger', () => ({
|
||||
logger: {
|
||||
info: jest.fn(),
|
||||
warn: jest.fn(),
|
||||
error: jest.fn(),
|
||||
line: jest.fn(),
|
||||
trace: jest.fn(),
|
||||
},
|
||||
}));
|
||||
jest.mock('./utils/output-startup-information', () => ({
|
||||
outputStartupInformation: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('./utils/output-stats');
|
||||
jest.mock('./utils/open-in-browser', () => ({
|
||||
openInBrowser: jest.fn(),
|
||||
}));
|
||||
|
||||
const cache = createFileSystemCache({
|
||||
basePath: resolvePathInStorybookCache('dev-server'),
|
||||
ns: 'storybook-test', // Optional. A grouping namespace for items.
|
||||
});
|
||||
|
||||
const managerOnly = false;
|
||||
const baseOptions = {
|
||||
ignorePreview: managerOnly,
|
||||
// FIXME: this should just be ignorePreview everywhere
|
||||
managerOnly, // production
|
||||
docsMode: false,
|
||||
cache,
|
||||
configDir: path.resolve(`${__dirname}/__for-testing__/`),
|
||||
ci: true,
|
||||
managerCache: false,
|
||||
};
|
||||
|
||||
const ROOT = getProjectRoot();
|
||||
const CWD = process.cwd();
|
||||
const NODE_MODULES = /.*node_modules/g;
|
||||
const cleanRoots = (obj: any): any => {
|
||||
if (!obj) return obj;
|
||||
if (typeof obj === 'string')
|
||||
return obj.replace(CWD, 'CWD').replace(ROOT, 'ROOT').replace(NODE_MODULES, 'NODE_MODULES');
|
||||
if (Array.isArray(obj)) return obj.map(cleanRoots);
|
||||
if (obj instanceof RegExp) return cleanRoots(obj.toString());
|
||||
if (typeof obj === 'object') {
|
||||
return Object.fromEntries(
|
||||
Object.entries(obj).map(([key, val]) => {
|
||||
if (key === 'version' && typeof val === 'string') {
|
||||
return [key, '*'];
|
||||
}
|
||||
return [key, cleanRoots(val)];
|
||||
})
|
||||
);
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
|
||||
const getConfig = (fn: any, name: string): Configuration | null => {
|
||||
const call = fn.mock.calls.find((c: any) => c[0].name === name);
|
||||
if (!call) {
|
||||
return null;
|
||||
}
|
||||
return call[0];
|
||||
};
|
||||
|
||||
const prepareSnap = (
|
||||
get: any,
|
||||
name: string
|
||||
): Pick<Configuration, 'module' | 'entry' | 'plugins'> => {
|
||||
const config = getConfig(get(), name);
|
||||
if (!config) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const keys = Object.keys(config);
|
||||
const { module, entry, plugins } = config;
|
||||
|
||||
return cleanRoots({ keys, module, entry, plugins: plugins.map((p) => p.constructor.name) });
|
||||
};
|
||||
|
||||
const snap = (name: string) => `__snapshots__/${name}`;
|
||||
|
||||
// FIXME: we no longer have test cases
|
||||
// eslint-disable-next-line jest/no-disabled-tests
|
||||
describe.skip('FIXME', () => {
|
||||
// @ts-expect-error (not strict)
|
||||
describe.each([[]])('%s', (example: string) => {
|
||||
describe.each([
|
||||
['manager', managerExecutor],
|
||||
['preview', previewExecutor],
|
||||
])('%s', (component, executor) => {
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks();
|
||||
await cache.clear();
|
||||
});
|
||||
|
||||
it.each([
|
||||
['prod', buildStaticStandalone],
|
||||
['dev', buildDevStandalone],
|
||||
])('%s', async (mode, builder) => {
|
||||
const options = {
|
||||
...baseOptions,
|
||||
configDir: path.resolve(`${__dirname}/../../../examples/${example}/.storybook`),
|
||||
// Only add an outputDir in production mode.
|
||||
outputDir:
|
||||
mode === 'prod'
|
||||
? await mkdtemp(path.join(os.tmpdir(), 'storybook-static-'))
|
||||
: undefined,
|
||||
ignorePreview: component === 'manager',
|
||||
managerCache: component === 'preview',
|
||||
packageJson,
|
||||
};
|
||||
await builder(options);
|
||||
const config = prepareSnap(executor.get, component);
|
||||
expect(config).toMatchSpecificSnapshot(
|
||||
snap(`${example}_${component}-${mode}-${SNAPSHOT_OS}`)
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const progressPlugin = (config: any) =>
|
||||
config.plugins.find((p: any) => p.constructor.name === 'ProgressPlugin');
|
||||
|
||||
describe('dev cli flags', () => {
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks();
|
||||
await cache.clear();
|
||||
});
|
||||
|
||||
const cliOptions = { ...baseOptions, packageJson };
|
||||
|
||||
// eslint-disable-next-line jest/no-disabled-tests
|
||||
it.skip('baseline', async () => {
|
||||
await buildDevStandalone(cliOptions);
|
||||
const config = getConfig(previewExecutor.get, 'preview');
|
||||
expect(progressPlugin(config)).toBeTruthy();
|
||||
});
|
||||
|
||||
// eslint-disable-next-line jest/no-disabled-tests
|
||||
it.skip('--quiet', async () => {
|
||||
const options = { ...cliOptions, quiet: true };
|
||||
await buildDevStandalone(options);
|
||||
const config = getConfig(previewExecutor.get, 'preview');
|
||||
expect(progressPlugin(config)).toBeFalsy();
|
||||
});
|
||||
|
||||
it('--webpack-stats-json calls output-stats', async () => {
|
||||
await buildDevStandalone(cliOptions);
|
||||
expect(outputStats).not.toHaveBeenCalled();
|
||||
|
||||
await buildDevStandalone({ ...cliOptions, webpackStatsJson: '/tmp/dir' });
|
||||
expect(outputStats).toHaveBeenCalledWith(
|
||||
'/tmp/dir',
|
||||
expect.objectContaining({ toJson: expect.any(Function) })
|
||||
);
|
||||
});
|
||||
|
||||
describe.each([
|
||||
['root directory /', '/', "Won't remove directory '/'. Check your outputDir!"],
|
||||
['empty string ""', '', "Won't remove current directory. Check your outputDir!"],
|
||||
])('Invalid outputDir must throw: %s', (_, outputDir, expectedErrorMessage) => {
|
||||
const optionsWithInvalidDir = {
|
||||
...cliOptions,
|
||||
outputDir,
|
||||
};
|
||||
|
||||
it('production mode', async () => {
|
||||
expect.assertions(1);
|
||||
await expect(buildStaticStandalone(optionsWithInvalidDir)).rejects.toThrow(
|
||||
expectedErrorMessage
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Invalid staticDir must throw: root directory /', () => {
|
||||
const optionsWithInvalidStaticDir = {
|
||||
...cliOptions,
|
||||
staticDir: ['/'],
|
||||
};
|
||||
|
||||
it('production mode', async () => {
|
||||
expect.assertions(1);
|
||||
// @ts-expect-error (not strict)
|
||||
await expect(buildStaticStandalone(optionsWithInvalidStaticDir)).rejects.toThrow(
|
||||
"Won't copy root directory. Check your staticDirs!"
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('build cli flags', () => {
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks();
|
||||
await cache.clear();
|
||||
});
|
||||
const cliOptions = {
|
||||
...baseOptions,
|
||||
outputDir: `${__dirname}/storybook-static`,
|
||||
packageJson,
|
||||
};
|
||||
|
||||
it('does not call output-stats', async () => {
|
||||
await buildStaticStandalone(cliOptions);
|
||||
expect(outputStats).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('--webpack-stats-json calls output-stats', async () => {
|
||||
await buildStaticStandalone({ ...cliOptions, webpackStatsJson: '/tmp/dir' });
|
||||
expect(outputStats).toHaveBeenCalledWith(
|
||||
'/tmp/dir',
|
||||
expect.objectContaining({ toJson: expect.any(Function) })
|
||||
);
|
||||
});
|
||||
});
|
@ -18,10 +18,11 @@ import { getServer } from './utils/server-init';
|
||||
import { useStatics } from './utils/server-statics';
|
||||
import { useStoriesJson } from './utils/stories-json';
|
||||
import { useStorybookMetadata } from './utils/metadata';
|
||||
import type { ServerChannel } from './utils/get-server-channel';
|
||||
import { getServerChannel } from './utils/get-server-channel';
|
||||
|
||||
import { openInBrowser } from './utils/open-in-browser';
|
||||
import { getBuilders } from './utils/get-builders';
|
||||
import { getManagerBuilder, getPreviewBuilder } from './utils/get-builders';
|
||||
import { StoryIndexGenerator } from './utils/StoryIndexGenerator';
|
||||
import { summarizeIndex } from './utils/summarizeIndex';
|
||||
|
||||
@ -37,62 +38,24 @@ const versionStatus = (versionCheck: VersionCheck) => {
|
||||
};
|
||||
|
||||
export async function storybookDevServer(options: Options) {
|
||||
const startTime = process.hrtime();
|
||||
const app = express();
|
||||
const server = await getServer(app, options);
|
||||
|
||||
const [server, features, core] = await Promise.all([
|
||||
getServer(app, options),
|
||||
options.presets.apply<StorybookConfig['features']>('features'),
|
||||
options.presets.apply<CoreConfig>('core'),
|
||||
]);
|
||||
|
||||
const serverChannel = getServerChannel(server);
|
||||
|
||||
const features = await options.presets.apply<StorybookConfig['features']>('features');
|
||||
const core = await options.presets.apply<CoreConfig>('core');
|
||||
// try get index generator, if failed, send telemetry without storyCount, then rethrow the error
|
||||
let initializedStoryIndexGenerator: Promise<StoryIndexGenerator> = Promise.resolve(undefined);
|
||||
if (features?.buildStoriesJson || features?.storyStoreV7) {
|
||||
const workingDir = process.cwd();
|
||||
const directories = {
|
||||
configDir: options.configDir,
|
||||
workingDir,
|
||||
};
|
||||
const normalizedStories = normalizeStories(await options.presets.apply('stories'), directories);
|
||||
const storyIndexers = await options.presets.apply('storyIndexers', []);
|
||||
const docsOptions = await options.presets.apply<DocsOptions>('docs', {});
|
||||
const initializedStoryIndexGenerator: Promise<StoryIndexGenerator> = getStoryIndexGenerator(
|
||||
features,
|
||||
options,
|
||||
serverChannel
|
||||
);
|
||||
|
||||
const generator = new StoryIndexGenerator(normalizedStories, {
|
||||
...directories,
|
||||
storyIndexers,
|
||||
docs: docsOptions,
|
||||
workingDir,
|
||||
storiesV2Compatibility: !features?.breakingChangesV7 && !features?.storyStoreV7,
|
||||
storyStoreV7: features?.storyStoreV7,
|
||||
});
|
||||
|
||||
initializedStoryIndexGenerator = generator.initialize().then(() => generator);
|
||||
|
||||
useStoriesJson({
|
||||
router,
|
||||
initializedStoryIndexGenerator,
|
||||
normalizedStories,
|
||||
serverChannel,
|
||||
workingDir,
|
||||
});
|
||||
}
|
||||
|
||||
if (!core?.disableTelemetry) {
|
||||
initializedStoryIndexGenerator.then(async (generator) => {
|
||||
const storyIndex = await generator?.getIndex();
|
||||
const { versionCheck, versionUpdates } = options;
|
||||
const payload = storyIndex
|
||||
? {
|
||||
versionStatus: versionUpdates ? versionStatus(versionCheck) : 'disabled',
|
||||
storyIndex: summarizeIndex(storyIndex),
|
||||
}
|
||||
: undefined;
|
||||
telemetry('dev', payload, { configDir: options.configDir });
|
||||
});
|
||||
}
|
||||
|
||||
if (!core?.disableProjectJson) {
|
||||
useStorybookMetadata(router, options.configDir);
|
||||
}
|
||||
doTelemetry(core, initializedStoryIndexGenerator, options);
|
||||
|
||||
app.use(compression({ level: 1 }));
|
||||
|
||||
@ -119,8 +82,7 @@ export async function storybookDevServer(options: Options) {
|
||||
}
|
||||
|
||||
// User's own static files
|
||||
|
||||
await useStatics(router, options);
|
||||
const usingStatics = useStatics(router, options);
|
||||
|
||||
getMiddleware(options.configDir)(router);
|
||||
app.use(router);
|
||||
@ -129,49 +91,131 @@ export async function storybookDevServer(options: Options) {
|
||||
const proto = options.https ? 'https' : 'http';
|
||||
const { address, networkAddress } = getServerAddresses(port, host, proto);
|
||||
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
// FIXME: Following line doesn't match TypeScript signature at all 🤔
|
||||
// @ts-expect-error (Converted from ts-ignore)
|
||||
const listening = new Promise<void>((resolve, reject) => {
|
||||
// @ts-expect-error (Following line doesn't match TypeScript signature at all 🤔)
|
||||
server.listen({ port, host }, (error: Error) => (error ? reject(error) : resolve()));
|
||||
});
|
||||
|
||||
const [previewBuilder, managerBuilder] = await getBuilders(options);
|
||||
const builderName = typeof core?.builder === 'string' ? core.builder : core?.builder?.name;
|
||||
|
||||
const [previewBuilder, managerBuilder] = await Promise.all([
|
||||
getPreviewBuilder(builderName, options.configDir),
|
||||
getManagerBuilder(),
|
||||
]);
|
||||
|
||||
if (options.debugWebpack) {
|
||||
logConfig('Preview webpack config', await previewBuilder.getConfig(options));
|
||||
}
|
||||
|
||||
Promise.all([initializedStoryIndexGenerator, listening, usingStatics]).then(async () => {
|
||||
if (!options.ci && !options.smokeTest && options.open) {
|
||||
openInBrowser(host ? networkAddress : address);
|
||||
}
|
||||
});
|
||||
|
||||
const managerResult = await managerBuilder.start({
|
||||
startTime,
|
||||
startTime: process.hrtime(),
|
||||
options,
|
||||
router,
|
||||
server,
|
||||
channel: serverChannel,
|
||||
});
|
||||
|
||||
let previewResult;
|
||||
|
||||
if (!options.ignorePreview) {
|
||||
try {
|
||||
previewResult = await previewBuilder.start({
|
||||
startTime,
|
||||
previewResult = await previewBuilder
|
||||
.start({
|
||||
startTime: process.hrtime(),
|
||||
options,
|
||||
router,
|
||||
server,
|
||||
});
|
||||
} catch (error) {
|
||||
await managerBuilder?.bail();
|
||||
// For some reason, even when Webpack fails e.g. wrong main.js config,
|
||||
// the preview may continue to print to stdout, which can affect output
|
||||
// when we catch this error and process those errors (e.g. telemetry)
|
||||
// gets overwritten by preview progress output. Therefore, we should bail the preview too.
|
||||
await previewBuilder?.bail().catch();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
channel: serverChannel,
|
||||
})
|
||||
.catch(async (e: any) => {
|
||||
await managerBuilder?.bail().catch();
|
||||
// For some reason, even when Webpack fails e.g. wrong main.js config,
|
||||
// the preview may continue to print to stdout, which can affect output
|
||||
// when we catch this error and process those errors (e.g. telemetry)
|
||||
// gets overwritten by preview progress output. Therefore, we should bail the preview too.
|
||||
await previewBuilder?.bail().catch();
|
||||
|
||||
// TODO #13083 Move this to before starting the previewBuilder - when compiling the preview is so fast that it will be done before the browser is done opening
|
||||
if (!options.ci && !options.smokeTest && options.open) {
|
||||
openInBrowser(host ? networkAddress : address);
|
||||
// re-throw the error
|
||||
throw e;
|
||||
});
|
||||
}
|
||||
|
||||
return { previewResult, managerResult, address, networkAddress };
|
||||
}
|
||||
async function doTelemetry(
|
||||
core: CoreConfig,
|
||||
initializedStoryIndexGenerator: Promise<StoryIndexGenerator>,
|
||||
options: Options
|
||||
) {
|
||||
if (!core?.disableTelemetry) {
|
||||
initializedStoryIndexGenerator.then(async (generator) => {
|
||||
const storyIndex = await generator?.getIndex();
|
||||
const { versionCheck, versionUpdates } = options;
|
||||
const payload = storyIndex
|
||||
? {
|
||||
versionStatus: versionUpdates ? versionStatus(versionCheck) : 'disabled',
|
||||
storyIndex: summarizeIndex(storyIndex),
|
||||
}
|
||||
: undefined;
|
||||
telemetry('dev', payload, { configDir: options.configDir });
|
||||
});
|
||||
}
|
||||
|
||||
if (!core?.disableProjectJson) {
|
||||
useStorybookMetadata(router, options.configDir);
|
||||
}
|
||||
}
|
||||
|
||||
async function getStoryIndexGenerator(
|
||||
features: {
|
||||
postcss?: boolean;
|
||||
buildStoriesJson?: boolean;
|
||||
previewCsfV3?: boolean;
|
||||
storyStoreV7?: boolean;
|
||||
breakingChangesV7?: boolean;
|
||||
interactionsDebugger?: boolean;
|
||||
babelModeV7?: boolean;
|
||||
argTypeTargetsV7?: boolean;
|
||||
warnOnLegacyHierarchySeparator?: boolean;
|
||||
},
|
||||
options: Options,
|
||||
serverChannel: ServerChannel
|
||||
) {
|
||||
let initializedStoryIndexGenerator: Promise<StoryIndexGenerator> = Promise.resolve(undefined);
|
||||
if (features?.buildStoriesJson || features?.storyStoreV7) {
|
||||
const workingDir = process.cwd();
|
||||
const directories = {
|
||||
configDir: options.configDir,
|
||||
workingDir,
|
||||
};
|
||||
const stories = options.presets.apply('stories');
|
||||
const storyIndexers = options.presets.apply('storyIndexers', []);
|
||||
const docsOptions = options.presets.apply<DocsOptions>('docs', {});
|
||||
const normalizedStories = normalizeStories(await stories, directories);
|
||||
|
||||
const generator = new StoryIndexGenerator(normalizedStories, {
|
||||
...directories,
|
||||
storyIndexers: await storyIndexers,
|
||||
docs: await docsOptions,
|
||||
workingDir,
|
||||
storiesV2Compatibility: !features?.breakingChangesV7 && !features?.storyStoreV7,
|
||||
storyStoreV7: features?.storyStoreV7,
|
||||
});
|
||||
|
||||
initializedStoryIndexGenerator = generator.initialize().then(() => generator);
|
||||
|
||||
useStoriesJson({
|
||||
router,
|
||||
initializedStoryIndexGenerator,
|
||||
normalizedStories,
|
||||
serverChannel,
|
||||
workingDir,
|
||||
});
|
||||
}
|
||||
return initializedStoryIndexGenerator;
|
||||
}
|
||||
|
@ -1,11 +1,7 @@
|
||||
// eslint-disable-next-line @typescript-eslint/triple-slash-reference
|
||||
/// <reference path="./typings.d.ts" />
|
||||
|
||||
export {
|
||||
getPreviewHeadTemplate,
|
||||
getPreviewBodyTemplate,
|
||||
getPreviewMainTemplate,
|
||||
} from '@storybook/core-common';
|
||||
export { getPreviewHeadTemplate, getPreviewBodyTemplate } from '@storybook/core-common';
|
||||
|
||||
export * from './build-static';
|
||||
export * from './build-dev';
|
||||
|
@ -1,11 +1,6 @@
|
||||
import fs from 'fs-extra';
|
||||
import { deprecate } from '@storybook/node-logger';
|
||||
import {
|
||||
getPreviewBodyTemplate,
|
||||
getPreviewHeadTemplate,
|
||||
getPreviewMainTemplate,
|
||||
loadEnvs,
|
||||
} from '@storybook/core-common';
|
||||
import { getPreviewBodyTemplate, getPreviewHeadTemplate, loadEnvs } from '@storybook/core-common';
|
||||
import type {
|
||||
CLIOptions,
|
||||
CoreCommon_IndexerOptions,
|
||||
@ -41,8 +36,6 @@ export const previewBody = async (base: any, { configDir, presets }: Options) =>
|
||||
return getPreviewBodyTemplate(configDir, interpolations);
|
||||
};
|
||||
|
||||
export const previewMainTemplate = () => getPreviewMainTemplate();
|
||||
|
||||
export const typescript = () => ({
|
||||
check: false,
|
||||
// 'react-docgen' faster but produces lower quality typescript results
|
||||
|
@ -268,6 +268,7 @@ export class StoryIndexGenerator {
|
||||
const importPath = slash(normalizedPath);
|
||||
|
||||
const content = await fs.readFile(absolutePath, 'utf8');
|
||||
|
||||
const result: {
|
||||
title?: ComponentTitle;
|
||||
of?: Path;
|
||||
|
@ -1,10 +1,13 @@
|
||||
import type { Options, CoreConfig, Builder } from '@storybook/types';
|
||||
|
||||
async function getManagerBuilder() {
|
||||
export async function getManagerBuilder(): Promise<Builder<unknown>> {
|
||||
return import('@storybook/builder-manager');
|
||||
}
|
||||
|
||||
async function getPreviewBuilder(builderName: string, configDir: string) {
|
||||
export async function getPreviewBuilder(
|
||||
builderName: string,
|
||||
configDir: string
|
||||
): Promise<Builder<unknown>> {
|
||||
let builderPackage: string;
|
||||
if (builderName) {
|
||||
builderPackage = require.resolve(
|
||||
|
@ -18,7 +18,7 @@ export class ServerChannel {
|
||||
});
|
||||
}
|
||||
|
||||
emit(type: string, args: any[] = []) {
|
||||
emit(type: string, args: any = []) {
|
||||
const event = { type, args };
|
||||
const data = stringify(event, { maxDepth: 15, allowFunction: true });
|
||||
Array.from(this.webSocketServer.clients)
|
||||
|
@ -7,7 +7,7 @@ import { dedent } from 'ts-dedent';
|
||||
export function openInBrowser(address: string) {
|
||||
getDefaultBrowser(async (err: any, res: any) => {
|
||||
try {
|
||||
if (res && (res.isChrome || res.isChromium)) {
|
||||
if (res && (res.isChrome || res.isChromium || res.identity === 'com.brave.browser')) {
|
||||
// We use betterOpn for Chrome because it is better at handling which chrome tab
|
||||
// or window the preview loads in.
|
||||
betterOpn(address);
|
||||
|
@ -899,7 +899,7 @@ describe('useStoriesJson', () => {
|
||||
});
|
||||
|
||||
it('debounces invalidation events', async () => {
|
||||
(debounce as jest.Mock).mockImplementation(jest.requireActual('lodash/debounce'));
|
||||
(debounce as jest.Mock).mockImplementation(jest.requireActual('lodash/debounce') as any);
|
||||
|
||||
const mockServerChannel = { emit: jest.fn() } as any as ServerChannel;
|
||||
useStoriesJson({
|
||||
|
@ -45,7 +45,7 @@
|
||||
"@storybook/core-common": "7.0.0-alpha.52",
|
||||
"@storybook/node-logger": "7.0.0-alpha.52",
|
||||
"@storybook/types": "7.0.0-alpha.52",
|
||||
"@types/node": "^16.0.0 || ^18.0.0",
|
||||
"@types/node": "^16.0.0",
|
||||
"ts-dedent": "^2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
94
code/lib/preview/README.md
Normal file
94
code/lib/preview/README.md
Normal file
@ -0,0 +1,94 @@
|
||||
# Preview (Web)
|
||||
|
||||
This is the main API for the (web) version of the Storybook Preview.
|
||||
|
||||
The preview's job is:
|
||||
|
||||
1. Read and update the URL (via the URL Store)
|
||||
|
||||
2. Listen to instructions on the channel and emit events as things occur.
|
||||
|
||||
3. Render the current selection to the web view in either story or docs mode.
|
||||
|
||||
## V7 Store vs Legacy (V6)
|
||||
|
||||
The story store is designed to load stories 'on demand', and will operate in this fashion if the `storyStoreV7` feature is enabled.
|
||||
|
||||
However, for back-compat reasons, in v6 mode, we need to load all stories, synchronously on bootup, emitting the `SET_STORIES` event.
|
||||
|
||||
In V7 mode we do not emit that event, instead preferring the `STORY_PREPARED` event, with the data for the single story being rendered.
|
||||
|
||||
## Initialization
|
||||
|
||||
The preview is `initialized` in two ways.
|
||||
|
||||
### V7 Mode:
|
||||
|
||||
- `importFn` - is an async `import()` function
|
||||
|
||||
- `getProjectAnnotations` - is a simple function that evaluations `preview.js` and addon config files and combines them. If it errors, the Preview will show the error.
|
||||
|
||||
- No `getStoryIndex` function is passed, instead the preview creates a `StoryIndexClient` that pulls `stories.json` from node and watches the event stream for invalidation events.
|
||||
|
||||
### V6 Mode
|
||||
|
||||
- `importFn` - is a simulated `import()` function, that is synchronous, see `client-api` for details.
|
||||
- `getProjectAnnotations` - also evaluates `preview.js` et al, but watches for calls to `setStories`, and passes them to the `ClientApi`
|
||||
- `getStoryIndex` is a local function (that must be called _after_ `getProjectAnnotations`) that gets the list of stories added.
|
||||
|
||||
See `client-api` for more details on this process.
|
||||
|
||||
## Story Rendering and interruptions
|
||||
|
||||
The Preview is split into three parts responsible for state management:
|
||||
|
||||
- `PreviewWeb` - which story is rendered, receives events and (maybe) changes/re-renders stories
|
||||
- `StoryRender` - (imports +) prepares the story, renders it through the various phases
|
||||
- `DocsRender` - if a story renders in docs mode, it is "transformed" into a `DocsRender` once we know.
|
||||
|
||||
A rendering story goes through these phases:
|
||||
|
||||
- `preparing` - (maybe async) import the story file and prepare the story function.
|
||||
- `loading` - async loaders are running
|
||||
- `rendering` - the `renderToDOM` function for the framework is running
|
||||
- `playing` - the `play` function is running
|
||||
- `completed` - the story is done.
|
||||
|
||||
It also has two error states:
|
||||
|
||||
- `aborted` - the story was stopped midway (see below)
|
||||
- `errored` - there was an error thrown somewhere along the way.
|
||||
|
||||
### Re-rendering and aborting
|
||||
|
||||
A story may re-render due to various events, which can have implications if the story is not in the `completed` phase:
|
||||
|
||||
- `UPDATE_STORY_ARGS` / `UPDATE_GLOBALS` -- change of inputs
|
||||
- `FORCE_RE_RENDER` - re-render unchanged
|
||||
|
||||
If these events happen during a render:
|
||||
|
||||
- if the story is `preparing` or `loading`, leave thing unchanged and let the new `args`/`globals` be picked up by the render phase
|
||||
- otherwise, use the result of the previous `loaders` run, and simply re-render over the top
|
||||
|
||||
- `FORCE_REMOUNT` - remount (or equivalent) the component and re-render.
|
||||
|
||||
If this happens during a render, treat `loading` similarly, but:
|
||||
|
||||
- if the story is `rendering`, start a new render and abort the previous render immediately afterwards
|
||||
- if the story is `playing`, attempt to abort the previous play function, and start a new render.
|
||||
|
||||
### Changing story
|
||||
|
||||
Also the `SET_CURRENT_STORY` event may change the current story. We need to check:
|
||||
|
||||
- If the `storyId` changed
|
||||
- If the `viewMode` changed
|
||||
- If the story implementation changed (i.e if HMR occurred).
|
||||
|
||||
If the _previous_ story is still `preparing`, we cannot know if the implementation changed, so we
|
||||
abort the preparing immediately, and let the new story take over.
|
||||
|
||||
Otherwise, if all of the above are the same, we do nothing.
|
||||
|
||||
If they are different, and the old story is not `completed`, we try to abort it immediately. If that fails (e.g. the `play` function doesn't respond to the `abort` event), then we reload the window.
|
105
code/lib/preview/package.json
Normal file
105
code/lib/preview/package.json
Normal file
@ -0,0 +1,105 @@
|
||||
{
|
||||
"name": "@storybook/preview",
|
||||
"version": "7.0.0-alpha.52",
|
||||
"description": "",
|
||||
"keywords": [
|
||||
"storybook"
|
||||
],
|
||||
"homepage": "https://github.com/storybookjs/storybook/tree/main/code/lib/preview",
|
||||
"bugs": {
|
||||
"url": "https://github.com/storybookjs/storybook/issues"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/storybookjs/storybook.git",
|
||||
"directory": "code/lib/preview"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/storybook"
|
||||
},
|
||||
"license": "MIT",
|
||||
"sideEffects": false,
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/runtime.mjs",
|
||||
"require": "./dist/runtime.js",
|
||||
"types": "./dist/runtime.d.ts"
|
||||
},
|
||||
"./globals": {
|
||||
"import": "./dist/globals.mjs",
|
||||
"require": "./dist/globals.js",
|
||||
"types": "./dist/globals.d.ts"
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"main": "dist/runtime.js",
|
||||
"module": "dist/runtime.mjs",
|
||||
"types": "dist/runtime.d.ts",
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"*": [
|
||||
"dist/runtime.d.ts"
|
||||
],
|
||||
"globals": [
|
||||
"dist/globals.d.ts"
|
||||
]
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"dist/**/*",
|
||||
"README.md",
|
||||
"*.js",
|
||||
"*.d.ts"
|
||||
],
|
||||
"scripts": {
|
||||
"check": "../../../scripts/node_modules/.bin/tsc --noEmit",
|
||||
"prep": "../../../scripts/prepare/bundle.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/shelljs": "^0.8.7",
|
||||
"fs-extra": "^9.0.1",
|
||||
"shelljs": "^0.8.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@storybook/api": "7.0.0-alpha.52",
|
||||
"@storybook/channel-postmessage": "7.0.0-alpha.52",
|
||||
"@storybook/channel-websocket": "7.0.0-alpha.52",
|
||||
"@storybook/channels": "7.0.0-alpha.52",
|
||||
"@storybook/client-logger": "7.0.0-alpha.52",
|
||||
"@storybook/core-client": "7.0.0-alpha.52",
|
||||
"@storybook/core-common": "7.0.0-alpha.52",
|
||||
"@storybook/core-events": "7.0.0-alpha.52",
|
||||
"@storybook/csf": "next",
|
||||
"@storybook/preview-web": "7.0.0-alpha.52",
|
||||
"@storybook/router": "7.0.0-alpha.52",
|
||||
"@storybook/theming": "7.0.0-alpha.52",
|
||||
"@storybook/types": "7.0.0-alpha.52",
|
||||
"@types/qs": "^6.9.5",
|
||||
"@types/webpack-env": "^1.16.4",
|
||||
"ansi-to-html": "^0.6.11",
|
||||
"dequal": "^2.0.2",
|
||||
"global": "^4.4.0",
|
||||
"lodash": "^4.17.21",
|
||||
"memoizerific": "^1.11.3",
|
||||
"qs": "^6.10.0",
|
||||
"react": "16.14.0",
|
||||
"slash": "^3.0.0",
|
||||
"synchronous-promise": "^2.0.15",
|
||||
"ts-dedent": "^2.0.0",
|
||||
"ts-jest": "^28.0.8",
|
||||
"typescript": "~4.6.3",
|
||||
"util-deprecate": "^1.0.2"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"bundler": {
|
||||
"pre": "./scripts/generate-exports-file.ts",
|
||||
"entries": [
|
||||
"./src/runtime.ts",
|
||||
"./src/globals.ts"
|
||||
]
|
||||
},
|
||||
"gitHead": "d2494e3f51ce0f55bcb1ef693a6477c669fbe666"
|
||||
}
|
40
code/lib/preview/scripts/generate-exports-file.ts
Normal file
40
code/lib/preview/scripts/generate-exports-file.ts
Normal file
@ -0,0 +1,40 @@
|
||||
/* eslint-disable import/no-extraneous-dependencies, no-console */
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
import shelljs from 'shelljs';
|
||||
import { dedent } from 'ts-dedent';
|
||||
|
||||
const removeDefault = (input: string) => input !== 'default';
|
||||
|
||||
const location = path.join(__dirname, '..', 'src', 'globals', 'exports.ts');
|
||||
|
||||
const run = async () => {
|
||||
const { values } = await import('../src/globals/runtime');
|
||||
const data = Object.entries(values).reduce<Record<string, string[]>>((acc, [key, value]) => {
|
||||
acc[key] = Object.keys(value).filter(removeDefault);
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
console.log('Generating...');
|
||||
await fs.ensureFile(location);
|
||||
await fs.writeFile(
|
||||
location,
|
||||
dedent`
|
||||
// this file is generated by generate-exports-file.ts
|
||||
// this is done to prevent runtime dependencies from making it's way into the build/start script of the manager
|
||||
// the manager builder needs to know which dependencies are 'globalized' in the ui
|
||||
|
||||
export default ${JSON.stringify(data, null, 2)} as const;`
|
||||
);
|
||||
|
||||
console.log('Linting...');
|
||||
shelljs.exec(`yarn lint:js:cmd --fix ${location}`, {
|
||||
cwd: path.join(__dirname, '..', '..', '..'),
|
||||
});
|
||||
console.log('Done!');
|
||||
};
|
||||
|
||||
run().catch((e) => {
|
||||
console.error(e);
|
||||
process.exitCode = 1;
|
||||
});
|
1
code/lib/preview/src/globals.ts
Normal file
1
code/lib/preview/src/globals.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './globals/definitions';
|
35
code/lib/preview/src/globals/definitions.ts
Normal file
35
code/lib/preview/src/globals/definitions.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import type { ModuleInfo } from '@fal-works/esbuild-plugin-global-externals';
|
||||
import Exports from './exports';
|
||||
import { Keys } from './types';
|
||||
import type { Definitions } from './types';
|
||||
|
||||
/*
|
||||
* We create a map of a module's name to a ModuleInfo.
|
||||
* Which is a config object for a esbuild-plugin, to swap a import of a module to a reference of a global variable.
|
||||
* To get this plugin to do the best job it can, it needs to know all the exports in the ModuleInfo config object.
|
||||
* We generate this information via a script into `exports.ts`.
|
||||
*
|
||||
* It's really important that there are no actual to the runtime of the modules, hence the cumbersome generation.
|
||||
* But we also want to ensure we don't miss any exports, or globals.
|
||||
*
|
||||
* So in order to add additional modules to be swapped for globals, you need to add them to:
|
||||
* - `Keys` in `types.ts`
|
||||
* - `values` in `runtime.ts`.
|
||||
*
|
||||
* If you forget to do either, TypeScript will complain.
|
||||
*
|
||||
* This `definitions.ts` file is consumed by the `builder-*` package,
|
||||
* The `runtime.ts` file is used inside the preview's browser code runtime.
|
||||
*/
|
||||
|
||||
const createModuleInfo = (m: keyof typeof Keys): Required<ModuleInfo> => ({
|
||||
type: 'esm',
|
||||
varName: Keys[m],
|
||||
namedExports: Exports[m],
|
||||
defaultExport: true,
|
||||
});
|
||||
|
||||
export const definitions = Object.keys(Keys).reduce<Definitions>((acc, key) => {
|
||||
acc[key as keyof typeof Keys] = createModuleInfo(key as keyof typeof Keys);
|
||||
return acc;
|
||||
}, {} as Definitions);
|
200
code/lib/preview/src/globals/exports.ts
Normal file
200
code/lib/preview/src/globals/exports.ts
Normal file
@ -0,0 +1,200 @@
|
||||
// this file is generated by generate-exports-file.ts
|
||||
// this is done to prevent runtime dependencies from making it's way into the build/start script of the manager
|
||||
// the manager builder needs to know which dependencies are 'globalized' in the ui
|
||||
|
||||
export default {
|
||||
'@storybook/addons': [
|
||||
'AddonStore',
|
||||
'HooksContext',
|
||||
'addons',
|
||||
'applyHooks',
|
||||
'isSupportedType',
|
||||
'makeDecorator',
|
||||
'mockChannel',
|
||||
'types',
|
||||
'useArgs',
|
||||
'useCallback',
|
||||
'useChannel',
|
||||
'useEffect',
|
||||
'useGlobals',
|
||||
'useMemo',
|
||||
'useParameter',
|
||||
'useReducer',
|
||||
'useRef',
|
||||
'useState',
|
||||
'useStoryContext',
|
||||
],
|
||||
'@storybook/channel-postmessage': ['KEY', 'PostmsgTransport', 'createChannel'],
|
||||
'@storybook/channel-websocket': ['WebsocketTransport', 'createChannel'],
|
||||
'@storybook/channels': ['Channel'],
|
||||
'@storybook/client-api': [
|
||||
'ClientApi',
|
||||
'addArgTypes',
|
||||
'addArgTypesEnhancer',
|
||||
'addArgs',
|
||||
'addArgsEnhancer',
|
||||
'addDecorator',
|
||||
'addLoader',
|
||||
'addParameters',
|
||||
'addStepRunner',
|
||||
'getQueryParam',
|
||||
'getQueryParams',
|
||||
'setGlobalRender',
|
||||
'DEEPLY_EQUAL',
|
||||
'HooksContext',
|
||||
'NO_TARGET_NAME',
|
||||
'StoryStore',
|
||||
'applyHooks',
|
||||
'combineArgs',
|
||||
'combineParameters',
|
||||
'composeConfigs',
|
||||
'composeStepRunners',
|
||||
'composeStories',
|
||||
'composeStory',
|
||||
'decorateStory',
|
||||
'deepDiff',
|
||||
'defaultDecorateStory',
|
||||
'filterArgTypes',
|
||||
'getArrayField',
|
||||
'getField',
|
||||
'getObjectField',
|
||||
'getSingletonField',
|
||||
'getValuesFromArgTypes',
|
||||
'groupArgsByTarget',
|
||||
'inferControls',
|
||||
'mapArgsToTypes',
|
||||
'noTargetArgs',
|
||||
'normalizeComponentAnnotations',
|
||||
'normalizeInputType',
|
||||
'normalizeInputTypes',
|
||||
'normalizeProjectAnnotations',
|
||||
'normalizeStory',
|
||||
'prepareStory',
|
||||
'processCSFFile',
|
||||
'sanitizeStoryContextUpdate',
|
||||
'setProjectAnnotations',
|
||||
'sortStoriesV6',
|
||||
'sortStoriesV7',
|
||||
'useAddonState',
|
||||
'useArgs',
|
||||
'useCallback',
|
||||
'useChannel',
|
||||
'useEffect',
|
||||
'useGlobals',
|
||||
'useMemo',
|
||||
'useParameter',
|
||||
'useReducer',
|
||||
'useRef',
|
||||
'useSharedState',
|
||||
'useState',
|
||||
'useStoryContext',
|
||||
'userOrAutoTitle',
|
||||
'userOrAutoTitleFromSpecifier',
|
||||
'validateOptions',
|
||||
],
|
||||
'@storybook/client-logger': ['deprecate', 'logger', 'once', 'pretty'],
|
||||
'@storybook/core-client': ['ClientApi', 'StoryStore', 'start'],
|
||||
'@storybook/core-events': [
|
||||
'CHANNEL_CREATED',
|
||||
'CONFIG_ERROR',
|
||||
'CURRENT_STORY_WAS_SET',
|
||||
'DOCS_RENDERED',
|
||||
'FORCE_REMOUNT',
|
||||
'FORCE_RE_RENDER',
|
||||
'GLOBALS_UPDATED',
|
||||
'IGNORED_EXCEPTION',
|
||||
'NAVIGATE_URL',
|
||||
'PLAY_FUNCTION_THREW_EXCEPTION',
|
||||
'PRELOAD_ENTRIES',
|
||||
'PREVIEW_BUILDER_PROGRESS',
|
||||
'PREVIEW_KEYDOWN',
|
||||
'REGISTER_SUBSCRIPTION',
|
||||
'RESET_STORY_ARGS',
|
||||
'SELECT_STORY',
|
||||
'SET_CONFIG',
|
||||
'SET_CURRENT_STORY',
|
||||
'SET_GLOBALS',
|
||||
'SET_INDEX',
|
||||
'SET_STORIES',
|
||||
'SHARED_STATE_CHANGED',
|
||||
'SHARED_STATE_SET',
|
||||
'STORIES_COLLAPSE_ALL',
|
||||
'STORIES_EXPAND_ALL',
|
||||
'STORY_ARGS_UPDATED',
|
||||
'STORY_CHANGED',
|
||||
'STORY_ERRORED',
|
||||
'STORY_INDEX_INVALIDATED',
|
||||
'STORY_MISSING',
|
||||
'STORY_PREPARED',
|
||||
'STORY_RENDERED',
|
||||
'STORY_RENDER_PHASE_CHANGED',
|
||||
'STORY_SPECIFIED',
|
||||
'STORY_THREW_EXCEPTION',
|
||||
'STORY_UNCHANGED',
|
||||
'UPDATE_GLOBALS',
|
||||
'UPDATE_QUERY_PARAMS',
|
||||
'UPDATE_STORY_ARGS',
|
||||
],
|
||||
'@storybook/preview-web': [
|
||||
'DocsContext',
|
||||
'Preview',
|
||||
'PreviewWeb',
|
||||
'PreviewWithSelection',
|
||||
'composeConfigs',
|
||||
'simulateDOMContentLoaded',
|
||||
'simulatePageLoad',
|
||||
],
|
||||
'@storybook/store': [
|
||||
'DEEPLY_EQUAL',
|
||||
'HooksContext',
|
||||
'NO_TARGET_NAME',
|
||||
'StoryStore',
|
||||
'applyHooks',
|
||||
'combineArgs',
|
||||
'combineParameters',
|
||||
'composeConfigs',
|
||||
'composeStepRunners',
|
||||
'composeStories',
|
||||
'composeStory',
|
||||
'decorateStory',
|
||||
'deepDiff',
|
||||
'defaultDecorateStory',
|
||||
'filterArgTypes',
|
||||
'getArrayField',
|
||||
'getField',
|
||||
'getObjectField',
|
||||
'getSingletonField',
|
||||
'getValuesFromArgTypes',
|
||||
'groupArgsByTarget',
|
||||
'inferControls',
|
||||
'mapArgsToTypes',
|
||||
'noTargetArgs',
|
||||
'normalizeComponentAnnotations',
|
||||
'normalizeInputType',
|
||||
'normalizeInputTypes',
|
||||
'normalizeProjectAnnotations',
|
||||
'normalizeStory',
|
||||
'prepareStory',
|
||||
'processCSFFile',
|
||||
'sanitizeStoryContextUpdate',
|
||||
'setProjectAnnotations',
|
||||
'sortStoriesV6',
|
||||
'sortStoriesV7',
|
||||
'useAddonState',
|
||||
'useArgs',
|
||||
'useCallback',
|
||||
'useChannel',
|
||||
'useEffect',
|
||||
'useGlobals',
|
||||
'useMemo',
|
||||
'useParameter',
|
||||
'useReducer',
|
||||
'useRef',
|
||||
'useSharedState',
|
||||
'useState',
|
||||
'useStoryContext',
|
||||
'userOrAutoTitle',
|
||||
'userOrAutoTitleFromSpecifier',
|
||||
'validateOptions',
|
||||
],
|
||||
} as const;
|
26
code/lib/preview/src/globals/runtime.ts
Normal file
26
code/lib/preview/src/globals/runtime.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import * as ADDONS from '../modules/addons';
|
||||
import * as CHANNEL_POSTMESSAGE from '../modules/channel-postmessage';
|
||||
import * as CHANNEL_WEBSOCKET from '../modules/channel-websocket';
|
||||
import * as CHANNELS from '../modules/channels';
|
||||
import * as CLIENT_API from '../modules/client-api';
|
||||
import * as CLIENT_LOGGER from '../modules/client-logger';
|
||||
import * as CORE_CLIENT from '../modules/core-client';
|
||||
import * as CORE_EVENTS from '../modules/core-events';
|
||||
import * as PREVIEW_WEB from '../modules/preview-web';
|
||||
import * as STORE from '../modules/store';
|
||||
|
||||
import type { Keys } from './types';
|
||||
|
||||
// Here we map the name of a module to their VALUE in the global scope.
|
||||
export const values: Required<Record<keyof typeof Keys, any>> = {
|
||||
'@storybook/addons': ADDONS,
|
||||
'@storybook/channel-postmessage': CHANNEL_POSTMESSAGE,
|
||||
'@storybook/channel-websocket': CHANNEL_WEBSOCKET,
|
||||
'@storybook/channels': CHANNELS,
|
||||
'@storybook/client-api': CLIENT_API,
|
||||
'@storybook/client-logger': CLIENT_LOGGER,
|
||||
'@storybook/core-client': CORE_CLIENT,
|
||||
'@storybook/core-events': CORE_EVENTS,
|
||||
'@storybook/preview-web': PREVIEW_WEB,
|
||||
'@storybook/store': STORE,
|
||||
};
|
17
code/lib/preview/src/globals/types.ts
Normal file
17
code/lib/preview/src/globals/types.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import type { ModuleInfo } from '@fal-works/esbuild-plugin-global-externals';
|
||||
|
||||
// Here we map the name of a module to their NAME in the global scope.
|
||||
export enum Keys {
|
||||
'@storybook/addons' = '__STORYBOOK_MODULE_ADDONS__',
|
||||
'@storybook/channel-postmessage' = '__STORYBOOK_MODULE_CHANNEL_POSTMESSAGE__',
|
||||
'@storybook/channel-websocket' = '__STORYBOOK_MODULE_CHANNEL_WEBSOCKET__',
|
||||
'@storybook/channels' = '__STORYBOOK_MODULE_CHANNELS__',
|
||||
'@storybook/client-api' = '__STORYBOOK_MODULE_CLIENT_API__',
|
||||
'@storybook/client-logger' = '__STORYBOOK_MODULE_CLIENT_LOGGER__',
|
||||
'@storybook/core-client' = '__STORYBOOK_MODULE_CORE_CLIENT__',
|
||||
'@storybook/core-events' = '__STORYBOOK_MODULE_CORE_EVENTS__',
|
||||
'@storybook/preview-web' = '__STORYBOOK_MODULE_PREVIEW_WEB__',
|
||||
'@storybook/store' = '__STORYBOOK_MODULE_STORE__',
|
||||
}
|
||||
|
||||
export type Definitions = Required<Record<keyof typeof Keys, Required<ModuleInfo>>>;
|
2
code/lib/preview/src/modules/addons.ts
Normal file
2
code/lib/preview/src/modules/addons.ts
Normal file
@ -0,0 +1,2 @@
|
||||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
export * from '@storybook/addons';
|
2
code/lib/preview/src/modules/channel-postmessage.ts
Normal file
2
code/lib/preview/src/modules/channel-postmessage.ts
Normal file
@ -0,0 +1,2 @@
|
||||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
export * from '@storybook/channel-postmessage';
|
2
code/lib/preview/src/modules/channel-websocket.ts
Normal file
2
code/lib/preview/src/modules/channel-websocket.ts
Normal file
@ -0,0 +1,2 @@
|
||||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
export * from '@storybook/channel-websocket';
|
2
code/lib/preview/src/modules/channels.ts
Normal file
2
code/lib/preview/src/modules/channels.ts
Normal file
@ -0,0 +1,2 @@
|
||||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
export * from '@storybook/channels';
|
2
code/lib/preview/src/modules/client-api.ts
Normal file
2
code/lib/preview/src/modules/client-api.ts
Normal file
@ -0,0 +1,2 @@
|
||||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
export * from '@storybook/client-api';
|
2
code/lib/preview/src/modules/client-logger.ts
Normal file
2
code/lib/preview/src/modules/client-logger.ts
Normal file
@ -0,0 +1,2 @@
|
||||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
export * from '@storybook/client-logger';
|
2
code/lib/preview/src/modules/core-client.ts
Normal file
2
code/lib/preview/src/modules/core-client.ts
Normal file
@ -0,0 +1,2 @@
|
||||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
export * from '@storybook/core-client';
|
2
code/lib/preview/src/modules/core-events.ts
Normal file
2
code/lib/preview/src/modules/core-events.ts
Normal file
@ -0,0 +1,2 @@
|
||||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
export * from '@storybook/core-events';
|
2
code/lib/preview/src/modules/preview-web.ts
Normal file
2
code/lib/preview/src/modules/preview-web.ts
Normal file
@ -0,0 +1,2 @@
|
||||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
export * from '@storybook/preview-web';
|
2
code/lib/preview/src/modules/store.ts
Normal file
2
code/lib/preview/src/modules/store.ts
Normal file
@ -0,0 +1,2 @@
|
||||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
export * from '@storybook/store';
|
9
code/lib/preview/src/runtime.ts
Normal file
9
code/lib/preview/src/runtime.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { values } from './globals/runtime';
|
||||
import { Keys } from './globals/types';
|
||||
|
||||
const getKeys = Object.keys as <T extends object>(obj: T) => Array<keyof T>;
|
||||
|
||||
// Apply all the globals
|
||||
getKeys(Keys).forEach((key) => {
|
||||
(globalThis as any)[Keys[key]] = values[key];
|
||||
});
|
11
code/lib/preview/tsconfig.json
Normal file
11
code/lib/preview/tsconfig.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"strictPropertyInitialization": false,
|
||||
"useUnknownInCatchVariables": false,
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": []
|
||||
}
|
@ -57,7 +57,7 @@
|
||||
"@emotion/react": "^11.10.4",
|
||||
"@emotion/styled": "^11.10.4",
|
||||
"@types/fs-extra": "^9.0.6",
|
||||
"@types/node": "^16.0.0 || ^18.0.0",
|
||||
"@types/node": "^16.0.0",
|
||||
"deep-object-diff": "^1.1.0",
|
||||
"fs-extra": "^9.0.1",
|
||||
"global": "^4.4.0",
|
||||
|
@ -48,7 +48,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@storybook/csf": "next",
|
||||
"@types/node": "^16.0.0 || ^18.0.0",
|
||||
"@types/node": "^16.0.0",
|
||||
"synchronous-promise": "^2.0.15",
|
||||
"typescript": "^4.9.3"
|
||||
},
|
||||
|
@ -15,6 +15,10 @@ import type { Parameters, Tag } from './csf';
|
||||
export type BuilderName = 'webpack5' | '@storybook/builder-webpack5' | string;
|
||||
export type RendererName = string;
|
||||
|
||||
interface ServerChannel {
|
||||
emit(type: string, args?: any): void;
|
||||
}
|
||||
|
||||
export interface CoreConfig {
|
||||
builder?:
|
||||
| BuilderName
|
||||
@ -188,6 +192,7 @@ export interface Builder<Config, BuilderStats extends Stats = Stats> {
|
||||
startTime: ReturnType<typeof process.hrtime>;
|
||||
router: Router;
|
||||
server: Server;
|
||||
channel: ServerChannel;
|
||||
}) => Promise<void | {
|
||||
stats?: BuilderStats;
|
||||
totalTime: ReturnType<typeof process.hrtime>;
|
||||
|
@ -244,7 +244,7 @@
|
||||
"@types/fs-extra": "^9.0.6",
|
||||
"@types/js-yaml": "^3.12.6",
|
||||
"@types/lodash": "^4.14.167",
|
||||
"@types/node": "^16.0.0 || ^18.0.0",
|
||||
"@types/node": "^16.0.0",
|
||||
"@types/node-cleanup": "^2.1.1",
|
||||
"@types/prompts": "2.0.11",
|
||||
"@types/react": "^16.14.34",
|
||||
|
@ -51,7 +51,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@storybook/core-webpack": "7.0.0-alpha.52",
|
||||
"@types/node": "^16.0.0 || ^18.0.0",
|
||||
"@types/node": "^16.0.0",
|
||||
"html-loader": "^3.1.0",
|
||||
"react": "16.14.0",
|
||||
"react-dom": "16.14.0",
|
||||
|
@ -52,7 +52,7 @@
|
||||
"dependencies": {
|
||||
"@babel/plugin-transform-react-jsx": "^7.19.0",
|
||||
"@storybook/core-webpack": "7.0.0-alpha.52",
|
||||
"@types/node": "^16.0.0 || ^18.0.0",
|
||||
"@types/node": "^16.0.0",
|
||||
"react": "16.14.0",
|
||||
"react-dom": "16.14.0"
|
||||
},
|
||||
|
@ -79,7 +79,7 @@
|
||||
"@storybook/node-logger": "7.0.0-alpha.52",
|
||||
"@storybook/react": "7.0.0-alpha.52",
|
||||
"@storybook/react-docgen-typescript-plugin": "1.0.2-canary.6.9d540b91e815f8fc2f8829189deb00553559ff63.0",
|
||||
"@types/node": "^16.0.0 || ^18.0.0",
|
||||
"@types/node": "^16.0.0",
|
||||
"@types/semver": "^7.3.4",
|
||||
"babel-plugin-add-react-displayname": "^0.0.5",
|
||||
"babel-plugin-react-docgen": "^4.2.1",
|
||||
|
@ -58,7 +58,7 @@
|
||||
"@storybook/core-server": "7.0.0-alpha.52",
|
||||
"@storybook/core-webpack": "7.0.0-alpha.52",
|
||||
"@storybook/server": "7.0.0-alpha.52",
|
||||
"@types/node": "^16.0.0 || ^18.0.0",
|
||||
"@types/node": "^16.0.0",
|
||||
"global": "^4.4.0",
|
||||
"react": "16.14.0",
|
||||
"react-dom": "16.14.0",
|
||||
|
@ -62,7 +62,7 @@
|
||||
"dependencies": {
|
||||
"@storybook/core-webpack": "7.0.0-alpha.52",
|
||||
"@storybook/docs-tools": "7.0.0-alpha.52",
|
||||
"@types/node": "^16.0.0 || ^18.0.0",
|
||||
"@types/node": "^16.0.0",
|
||||
"react": "16.14.0",
|
||||
"react-dom": "16.14.0",
|
||||
"ts-loader": "^9.2.8",
|
||||
|
@ -62,7 +62,7 @@
|
||||
"dependencies": {
|
||||
"@storybook/core-webpack": "7.0.0-alpha.52",
|
||||
"@storybook/docs-tools": "7.0.0-alpha.52",
|
||||
"@types/node": "^16.0.0 || ^18.0.0",
|
||||
"@types/node": "^16.0.0",
|
||||
"react": "16.14.0",
|
||||
"react-dom": "16.14.0",
|
||||
"ts-loader": "^9.2.8",
|
||||
|
@ -56,7 +56,7 @@
|
||||
"@babel/plugin-syntax-import-meta": "^7.10.4",
|
||||
"@babel/preset-env": "^7.20.2",
|
||||
"@storybook/core-webpack": "7.0.0-alpha.52",
|
||||
"@types/node": "^16.0.0 || ^18.0.0",
|
||||
"@types/node": "^16.0.0",
|
||||
"babel-loader": "^7.0.0 || ^8.0.0",
|
||||
"babel-plugin-bundled-import-meta": "^0.3.1",
|
||||
"react": "16.14.0",
|
||||
|
@ -59,7 +59,7 @@
|
||||
"@storybook/store": "7.0.0-alpha.52",
|
||||
"@storybook/types": "7.0.0-alpha.52",
|
||||
"@types/estree": "^0.0.51",
|
||||
"@types/node": "^16.0.0 || ^18.0.0",
|
||||
"@types/node": "^16.0.0",
|
||||
"acorn": "^7.4.1",
|
||||
"acorn-jsx": "^5.3.1",
|
||||
"acorn-walk": "^7.2.0",
|
||||
|
@ -1,13 +1,10 @@
|
||||
import global from 'global';
|
||||
import { transparentize } from 'polished';
|
||||
import type { ComponentProps, FC } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React from 'react';
|
||||
import { styled, keyframes } from '@storybook/theming';
|
||||
import { Icons } from '../icon/icon';
|
||||
import { rotate360 } from '../shared/animation';
|
||||
|
||||
const { EventSource, CONFIG_TYPE } = global;
|
||||
|
||||
const LoaderWrapper = styled.div<{ size?: number }>(({ size = 32 }) => ({
|
||||
borderRadius: '50%',
|
||||
cursor: 'progress',
|
||||
@ -107,7 +104,7 @@ interface LoaderProps {
|
||||
size?: number;
|
||||
}
|
||||
|
||||
export const PureLoader: FC<LoaderProps & ComponentProps<typeof ProgressWrapper>> = ({
|
||||
export const Loader: FC<LoaderProps & ComponentProps<typeof ProgressWrapper>> = ({
|
||||
progress,
|
||||
error,
|
||||
size,
|
||||
@ -158,36 +155,3 @@ export const PureLoader: FC<LoaderProps & ComponentProps<typeof ProgressWrapper>
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const Loader: FC<ComponentProps<typeof PureLoader>> = (props) => {
|
||||
const [progress, setProgress] = useState(undefined);
|
||||
const [error, setError] = useState(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
// Don't listen for progress updates in static builds
|
||||
// Event source is not defined in IE 11
|
||||
if (CONFIG_TYPE !== 'DEVELOPMENT' || !EventSource) return undefined;
|
||||
|
||||
const eventSource = new EventSource('/progress');
|
||||
let lastProgress: Progress;
|
||||
|
||||
eventSource.onmessage = (event: any) => {
|
||||
try {
|
||||
lastProgress = JSON.parse(event.data);
|
||||
setProgress(lastProgress);
|
||||
} catch (e) {
|
||||
setError(e);
|
||||
eventSource.close();
|
||||
}
|
||||
};
|
||||
|
||||
eventSource.onerror = () => {
|
||||
if (lastProgress && lastProgress.value !== 1) setError(new Error('Connection closed'));
|
||||
eventSource.close();
|
||||
};
|
||||
|
||||
return () => eventSource.close();
|
||||
}, []);
|
||||
|
||||
return <PureLoader progress={progress} error={error} {...props} />;
|
||||
};
|
||||
|
@ -72,7 +72,7 @@ export { StorybookLogo } from './brand/StorybookLogo';
|
||||
export { StorybookIcon } from './brand/StorybookIcon';
|
||||
|
||||
// Loader
|
||||
export { Loader } from './Loader/Loader';
|
||||
export { Loader, PureLoader } from './Loader/Loader';
|
||||
|
||||
// Utils
|
||||
export { getStoryHref } from './utils/getStoryHref';
|
||||
|
@ -4,14 +4,14 @@ import path from 'path';
|
||||
import shelljs from 'shelljs';
|
||||
import { dedent } from 'ts-dedent';
|
||||
|
||||
const remove = () => (input: string) => input !== 'default';
|
||||
const removeDefault = (input: string) => input !== 'default';
|
||||
|
||||
const location = path.join(__dirname, '..', 'src', 'globals', 'exports.ts');
|
||||
|
||||
const run = async () => {
|
||||
const { values } = await import('../src/globals/runtime');
|
||||
const data = Object.entries(values).reduce<Record<string, string[]>>((acc, [key, value]) => {
|
||||
acc[key] = Object.keys(value).filter(remove());
|
||||
acc[key] = Object.keys(value).filter(removeDefault);
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
import type { ReactElement } from 'react';
|
||||
import React, { Component, Fragment } from 'react';
|
||||
import { shortcutToHumanString } from '@storybook/api/shortcut';
|
||||
import { styled } from '@storybook/theming';
|
||||
import { Tabs, Icons, IconButton } from '@storybook/components';
|
||||
import type { State } from '@storybook/api';
|
||||
import { shortcutToHumanString } from '@storybook/api';
|
||||
|
||||
const DesktopOnlyIconButton = styled(IconButton)({
|
||||
// Hides full screen icon at mobile breakpoint defined in app.js
|
||||
|
@ -1,11 +1,12 @@
|
||||
import React, { Fragment, useMemo, useEffect, useRef } from 'react';
|
||||
import React, { Fragment, useMemo, useEffect, useRef, useState } from 'react';
|
||||
import { Helmet } from 'react-helmet-async';
|
||||
import global from 'global';
|
||||
|
||||
import { type API, Consumer, type Combo, merge } from '@storybook/api';
|
||||
import { SET_CURRENT_STORY } from '@storybook/core-events';
|
||||
import { PREVIEW_BUILDER_PROGRESS, SET_CURRENT_STORY } from '@storybook/core-events';
|
||||
import { addons, types, type Addon } from '@storybook/addons';
|
||||
|
||||
import { Loader } from '@storybook/components';
|
||||
import { PureLoader } from '@storybook/components';
|
||||
import { Location } from '@storybook/router';
|
||||
|
||||
import * as S from './utils/components';
|
||||
@ -59,9 +60,23 @@ const createCanvas = (id: string, baseUrl = 'iframe.html', withLoader = true): A
|
||||
[getElements, ...defaultWrappers]
|
||||
);
|
||||
|
||||
const [progress, setProgress] = useState(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
if (global.CONFIG_TYPE === 'DEVELOPMENT') {
|
||||
const channel = addons.getServerChannel();
|
||||
|
||||
channel.on(PREVIEW_BUILDER_PROGRESS, (options) => {
|
||||
setProgress(options);
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
const refLoading = !!refs[refId] && !refs[refId].ready;
|
||||
const rootLoading = !refId && !(progress?.value === 1 || progress === undefined);
|
||||
const isLoading = entry
|
||||
? !!refs[refId] && !refs[refId].ready
|
||||
: !storiesFailed && !storiesConfigured;
|
||||
? refLoading || rootLoading
|
||||
: (!storiesFailed && !storiesConfigured) || rootLoading;
|
||||
|
||||
return (
|
||||
<ZoomConsumer>
|
||||
@ -70,7 +85,7 @@ const createCanvas = (id: string, baseUrl = 'iframe.html', withLoader = true): A
|
||||
<>
|
||||
{withLoader && isLoading && (
|
||||
<S.LoaderWrapper>
|
||||
<Loader id="preview-loader" role="progressbar" />
|
||||
<PureLoader id="preview-loader" role="progressbar" progress={progress} />
|
||||
</S.LoaderWrapper>
|
||||
)}
|
||||
<ApplyWrappers
|
||||
|
@ -4,8 +4,15 @@ import React, { Fragment, useMemo } from 'react';
|
||||
import { styled } from '@storybook/theming';
|
||||
|
||||
import { FlexBar, IconButton, Icons, Separator, TabButton, TabBar } from '@storybook/components';
|
||||
import { Consumer, type Combo, type API, type State, merge, type LeafEntry } from '@storybook/api';
|
||||
import { shortcutToHumanString } from '@storybook/api/shortcut';
|
||||
import {
|
||||
shortcutToHumanString,
|
||||
Consumer,
|
||||
type Combo,
|
||||
type API,
|
||||
type State,
|
||||
merge,
|
||||
type LeafEntry,
|
||||
} from '@storybook/api';
|
||||
import { addons, type Addon, types } from '@storybook/addons';
|
||||
|
||||
import { Location, type RenderData } from '@storybook/router';
|
||||
|
@ -5,7 +5,7 @@ import { Badge } from '@storybook/components';
|
||||
import type { API } from '@storybook/api';
|
||||
import { styled, useTheme } from '@storybook/theming';
|
||||
|
||||
import { shortcutToHumanString } from '@storybook/api/shortcut';
|
||||
import { shortcutToHumanString } from '@storybook/api';
|
||||
import { MenuItemIcon } from '../components/sidebar/Menu';
|
||||
|
||||
const focusableUIElements = {
|
||||
|
@ -78,6 +78,7 @@ export default {
|
||||
'P',
|
||||
'Placeholder',
|
||||
'Pre',
|
||||
'PureLoader',
|
||||
'ResetWrapper',
|
||||
'ScrollArea',
|
||||
'Separator',
|
||||
@ -123,6 +124,7 @@ export default {
|
||||
'NAVIGATE_URL',
|
||||
'PLAY_FUNCTION_THREW_EXCEPTION',
|
||||
'PRELOAD_ENTRIES',
|
||||
'PREVIEW_BUILDER_PROGRESS',
|
||||
'PREVIEW_KEYDOWN',
|
||||
'REGISTER_SUBSCRIPTION',
|
||||
'RESET_STORY_ARGS',
|
||||
@ -200,7 +202,10 @@ export default {
|
||||
'ManagerContext',
|
||||
'Provider',
|
||||
'combineParameters',
|
||||
'eventToShortcut',
|
||||
'merge',
|
||||
'shortcutMatchesShortcut',
|
||||
'shortcutToHumanString',
|
||||
'useAddonState',
|
||||
'useArgTypes',
|
||||
'useArgs',
|
||||
|
@ -25,13 +25,14 @@ class ReactProvider extends Provider {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
const channel = postMessage.createChannel({ page: 'manager' });
|
||||
const postMessageChannel = postMessage.createChannel({ page: 'manager' });
|
||||
|
||||
addons.setChannel(channel);
|
||||
channel.emit(CHANNEL_CREATED);
|
||||
addons.setChannel(postMessageChannel);
|
||||
|
||||
postMessageChannel.emit(CHANNEL_CREATED);
|
||||
|
||||
this.addons = addons;
|
||||
this.channel = channel;
|
||||
this.channel = postMessageChannel;
|
||||
|
||||
if (FEATURES?.storyStoreV7 && SERVER_CHANNEL_URL) {
|
||||
const serverChannel = webSocket.createChannel({ url: SERVER_CHANNEL_URL });
|
||||
|
@ -2,11 +2,7 @@ import type { ComponentProps, FC } from 'react';
|
||||
import React, { Component } from 'react';
|
||||
import { styled, keyframes } from '@storybook/theming';
|
||||
|
||||
import {
|
||||
eventToShortcut,
|
||||
shortcutToHumanString,
|
||||
shortcutMatchesShortcut,
|
||||
} from '@storybook/api/shortcut';
|
||||
import { eventToShortcut, shortcutToHumanString, shortcutMatchesShortcut } from '@storybook/api';
|
||||
import { Form, Icons } from '@storybook/components';
|
||||
import SettingsFooter from './SettingsFooter';
|
||||
|
||||
|
596
code/yarn.lock
596
code/yarn.lock
File diff suppressed because it is too large
Load Diff
@ -81,7 +81,7 @@
|
||||
"@types/express": "^4.17.11",
|
||||
"@types/fs-extra": "^9.0.6",
|
||||
"@types/js-yaml": "^3.12.6",
|
||||
"@types/node": "^16.0.0 || ^18.0.0",
|
||||
"@types/node": "^16.0.0",
|
||||
"@types/node-cleanup": "^2.1.1",
|
||||
"@types/node-fetch": "^2.5.7",
|
||||
"@types/prompts": "2.0.11",
|
||||
|
@ -19,7 +19,7 @@ export const compile: Task = {
|
||||
// To check if the code has been compiled as we need, we check the compiled output of
|
||||
// `@storybook/store`. To check if it has been built for publishing (i.e. `--no-link`),
|
||||
// we check if it built types or references source files directly.
|
||||
const contents = await readFile(resolve(codeDir, './lib/store/dist/index.d.ts'), 'utf8');
|
||||
const contents = await readFile(resolve(codeDir, './lib/preview/dist/runtime.d.ts'), 'utf8');
|
||||
const isLinkedContents = contents.indexOf(linkedContents) !== -1;
|
||||
if (link) return isLinkedContents;
|
||||
return !isLinkedContents;
|
||||
|
@ -3659,7 +3659,7 @@ __metadata:
|
||||
"@types/fs-extra": ^9.0.6
|
||||
"@types/js-yaml": ^3.12.6
|
||||
"@types/lodash": ^4
|
||||
"@types/node": ^16.0.0 || ^18.0.0
|
||||
"@types/node": ^16.0.0
|
||||
"@types/node-cleanup": ^2.1.1
|
||||
"@types/node-fetch": ^2.5.7
|
||||
"@types/prompts": 2.0.11
|
||||
@ -4257,10 +4257,10 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/node@npm:^16.0.0 || ^18.0.0":
|
||||
version: 18.11.9
|
||||
resolution: "@types/node@npm:18.11.9"
|
||||
checksum: aeaa925406f841c41679b32def9391a9892171e977105e025050e9f66e2830b4c50d0d974a1af0077ead3337a1f3bdf49ee7e7f402ebf2e034a3f97d9d240dba
|
||||
"@types/node@npm:^16.0.0":
|
||||
version: 16.18.3
|
||||
resolution: "@types/node@npm:16.18.3"
|
||||
checksum: 058ddd61a3d39f517bc9c30b82b9d6257d903e84c42ba66aae63bd13203b6deb2acf7f7e14caefd5d7cebadbe8c90604c04f9851cd41cd6a1bc2fc4dcec85f01
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user