This commit is contained in:
Norbert de Langen 2022-06-28 10:03:37 +02:00
parent 0cf66eb8a8
commit 988982ae0e
No known key found for this signature in database
GPG Key ID: FD0E78AF9A837762
28 changed files with 2 additions and 1104 deletions

View File

@ -7,8 +7,6 @@ docs/public
storybook-static
built-storybooks
lib/cli/test
lib/manager-webpack5/prebuilt
lib/core-server/prebuilt
lib/codemod/src/transforms/__testfixtures__
scripts/storage
scripts/repros-generator

2
.gitignore vendored
View File

@ -32,8 +32,6 @@ cypress/screenshots
examples/ember-cli/ember-output
.verdaccio-cache
tsconfig.tsbuildinfo
lib/manager-webpack5/prebuilt
lib/manager-webpack5/prebuilt
examples/angular-cli/addon-jest.testresults.json
junit.xml
.next

View File

@ -46,7 +46,6 @@
"@storybook/csf-tools": "link:../../lib/csf-tools",
"@storybook/docs-tools": "link:../../lib/docs-tools",
"@storybook/jest": "^0.0.5",
"@storybook/manager-webpack5": "link:../../lib/manager-webpack5",
"@storybook/mdx1-csf": "canary",
"@storybook/node-logger": "link:../../lib/node-logger",
"@storybook/postinstall": "link:../../lib/postinstall",

View File

@ -114,10 +114,7 @@ export const webpack5: Fix<Webpack5RunOptions> & CheckBuilder = {
},
async run({ result: { main, storybookVersion, webpackVersion }, packageManager, dryRun }) {
const deps = [
`@storybook/manager-webpack5@${storybookVersion}`,
`@storybook/builder-webpack5@${storybookVersion}`,
];
const deps = [`@storybook/builder-webpack5@${storybookVersion}`];
// this also gets called by 'cra5' fix so we need to add
// webpack5 at the project root so that it gets hoisted
if (!webpackVersion) {

View File

@ -39,7 +39,6 @@ export default {
'@storybook/html': '7.0.0-alpha.6',
'@storybook/html-webpack5': '7.0.0-alpha.6',
'@storybook/instrumenter': '7.0.0-alpha.6',
'@storybook/manager-webpack5': '7.0.0-alpha.6',
'@storybook/builder-manager': '7.0.0-alpha.6',
'@storybook/node-logger': '7.0.0-alpha.6',
'@storybook/postinstall': '7.0.0-alpha.6',

View File

@ -40,7 +40,6 @@
"@storybook/csf": "0.0.2--canary.4566f4d.1",
"@storybook/csf-tools": "7.0.0-alpha.6",
"@storybook/docs-mdx": "0.0.1-canary.1.4bea5cc.0",
"@storybook/manager-webpack5": "7.0.0-alpha.6",
"@storybook/node-logger": "7.0.0-alpha.6",
"@storybook/semver": "^7.3.2",
"@storybook/store": "7.0.0-alpha.6",

View File

@ -1,13 +0,0 @@
# Manager-Webpack5
Builder implemented with `webpack5` and `webpack5`-compatible loaders/plugins/config, used by `@storybook/core-server` to build the manager UI.
`webpack5` is the default. To configure your Storybook to run `webpack5`, install `@storybook/manager-webpack5` and `@storybook/builder-webpack5` as dev dependencies then update your `.storybook/main.js` configuration.
```js
module.exports = {
core: {
builder: 'webpack5',
},
};
```

View File

@ -1 +0,0 @@
module.exports = require('./dist/cjs/presets/manager-preset');

View File

@ -1,87 +0,0 @@
{
"name": "@storybook/manager-webpack5",
"version": "7.0.0-alpha.6",
"description": "Storybook framework-agnostic API",
"keywords": [
"storybook"
],
"homepage": "https://github.com/storybookjs/storybook/tree/main/lib/core",
"bugs": {
"url": "https://github.com/storybookjs/storybook/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/storybookjs/storybook.git",
"directory": "lib/core"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/storybook"
},
"license": "MIT",
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
"types": "dist/types/index.d.ts",
"files": [
"dist/**/*",
"dll/**/*",
"templates/**/*",
"types/**/*",
"*.js",
"*.d.ts",
"prebuilt/**/*",
"!prebuilt/report.html"
],
"scripts": {
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@babel/core": "^7.12.10",
"@babel/plugin-transform-template-literals": "^7.12.1",
"@babel/preset-react": "^7.12.10",
"@storybook/addons": "7.0.0-alpha.6",
"@storybook/core-client": "7.0.0-alpha.6",
"@storybook/core-common": "7.0.0-alpha.6",
"@storybook/core-webpack": "7.0.0-alpha.6",
"@storybook/node-logger": "7.0.0-alpha.6",
"@storybook/theming": "7.0.0-alpha.6",
"@storybook/ui": "7.0.0-alpha.6",
"@types/node": "^14.0.10 || ^16.0.0",
"babel-loader": "^8.2.5",
"case-sensitive-paths-webpack-plugin": "^2.4.0",
"chalk": "^4.1.0",
"core-js": "^3.8.2",
"css-loader": "^6.7.1",
"esbuild-loader": "^2.19.0",
"express": "^4.17.1",
"find-up": "^5.0.0",
"fs-extra": "^9.0.1",
"html-webpack-plugin": "^5.5.0",
"node-fetch": "^2.6.7",
"process": "^0.11.10",
"read-pkg-up": "^7.0.1",
"resolve-from": "^5.0.0",
"style-loader": "^3.3.1",
"telejson": "^6.0.8",
"terser-webpack-plugin": "^5.3.1",
"ts-dedent": "^2.0.0",
"util-deprecate": "^1.0.2",
"webpack": "5",
"webpack-dev-middleware": "^5.3.1",
"webpack-virtual-modules": "^0.4.3"
},
"devDependencies": {
"@types/terser-webpack-plugin": "^5.2.0",
"@types/webpack-dev-middleware": "^5.3.0",
"@types/webpack-virtual-modules": "^0.1.1"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
},
"publishConfig": {
"access": "public"
},
"gitHead": "a88dcca40ba169a373bad33dd76e9a4bd4f1f5ec"
}

View File

@ -1,6 +0,0 @@
import type { Options } from '@storybook/core-common';
export function getPrebuiltDir(options: Options): Promise<string | false>;
export type DEFAULT_ADDONS = string[];
export type IGNORED_ADDONS = string[];

View File

@ -1 +0,0 @@
module.exports = require('./dist/cjs/utils/prebuilt-manager');

View File

@ -1,259 +0,0 @@
import webpack, { ProgressPlugin } from 'webpack';
import type { Stats, Configuration } from 'webpack';
import webpackDevMiddleware from 'webpack-dev-middleware';
import { logger } from '@storybook/node-logger';
import { useProgressReporting } from '@storybook/core-common';
import type { Builder, Options } from '@storybook/core-common';
import { checkWebpackVersion } from '@storybook/core-webpack';
import findUp from 'find-up';
import fs from 'fs-extra';
import express from 'express';
import { getManagerWebpackConfig } from './manager-config';
import { clearManagerCache, useManagerCache } from './utils/manager-cache';
import { getPrebuiltDir } from './utils/prebuilt-manager';
let compilation: ReturnType<typeof webpackDevMiddleware>;
let reject: (reason?: any) => void;
type WebpackBuilder = Builder<Configuration, Stats>;
type Unpromise<T extends Promise<any>> = T extends Promise<infer U> ? U : never;
type BuilderStartOptions = Partial<Parameters<WebpackBuilder['start']>['0']>;
type BuilderStartResult = Unpromise<ReturnType<WebpackBuilder['start']>>;
type StarterFunction = (
options: BuilderStartOptions
) => AsyncGenerator<unknown, BuilderStartResult, void>;
type BuilderBuildOptions = Partial<Parameters<WebpackBuilder['build']>['0']>;
type BuilderBuildResult = Unpromise<ReturnType<WebpackBuilder['build']>>;
type BuilderFunction = (
options: BuilderBuildOptions
) => AsyncGenerator<unknown, BuilderBuildResult, void>;
export const WEBPACK_VERSION = '5';
export const getConfig: WebpackBuilder['getConfig'] = getManagerWebpackConfig;
export const makeStatsFromError = (err: string) =>
({
hasErrors: () => true,
hasWarnings: () => false,
toJson: () => ({ warnings: [] as any[], errors: [err] }),
} as any as Stats);
export const executor = {
get: async (options: Options) => {
const version = ((await options.presets.apply('webpackVersion')) || WEBPACK_VERSION) as string;
const webpackInstance =
(await options.presets.apply<{ default: typeof webpack }>('webpackInstance'))?.default ||
webpack;
checkWebpackVersion({ version }, WEBPACK_VERSION, `manager-webpack${WEBPACK_VERSION}`);
return webpackInstance;
},
};
let asyncIterator: ReturnType<StarterFunction> | ReturnType<BuilderFunction>;
export const bail: WebpackBuilder['bail'] = async () => {
if (asyncIterator) {
try {
// we tell the builder (that started) to stop ASAP and wait
await asyncIterator.throw(new Error());
} catch (e) {
//
}
}
if (reject) {
reject();
}
// we wait for the compiler to finish it's work, so it's command-line output doesn't interfere
return new Promise((res, rej) => {
if (process && compilation) {
try {
compilation.close(() => res());
logger.warn('Force closed manager build');
} catch (err) {
logger.warn('Unable to close manager build!');
res();
}
} else {
res();
}
});
};
/**
* This function is a generator so that we can abort it mid process
* in case of failure coming from other processes e.g. preview builder
*
* I am sorry for making you read about generators today :')
*/
const starter: StarterFunction = async function* starterGeneratorFn({
startTime,
options,
router,
}) {
const prebuiltDir = await getPrebuiltDir(options);
if (prebuiltDir && options.managerCache && !options.smokeTest) {
logger.info('=> Using prebuilt manager');
router.use('/', express.static(prebuiltDir));
return;
}
yield;
const config = await getConfig(options);
yield;
if (options.cache) {
// Retrieve the Storybook version number to bust cache on upgrades.
const packageFile = await findUp('package.json', { cwd: __dirname });
const { version: storybookVersion } = await fs.readJSON(packageFile);
const cacheKey = `managerConfig-webpack${WEBPACK_VERSION}@${storybookVersion}`;
if (options.managerCache) {
const [useCache, hasOutput] = await Promise.all([
// useManagerCache sets the cache, so it must run even if outputDir doesn't exist yet,
// otherwise the 2nd run won't be able to use the manager built on the 1st run.
useManagerCache(cacheKey, options, config),
fs.pathExists(options.outputDir),
]);
yield;
if (useCache && hasOutput && !options.smokeTest) {
logger.line(1); // force starting new line
logger.info('=> Using cached manager');
router.use('/', express.static(options.outputDir));
return;
}
} else if (!options.smokeTest && (await clearManagerCache(cacheKey, options))) {
yield;
logger.line(1); // force starting new line
logger.info('=> Cleared cached manager config');
}
}
const webpackInstance = await executor.get(options);
yield;
const compiler = webpackInstance(config);
if (!compiler) {
const err = `${config.name}: missing webpack compiler at runtime!`;
logger.error(err);
// eslint-disable-next-line consistent-return
return {
bail,
totalTime: process.hrtime(startTime),
stats: makeStatsFromError(err),
};
}
const { handler, modulesCount } = await useProgressReporting(router, startTime, options);
yield;
new ProgressPlugin({ handler, modulesCount }).apply(compiler);
const middlewareOptions: Parameters<typeof webpackDevMiddleware>[1] = {
publicPath: config.output?.publicPath as string,
writeToDisk: true,
};
compilation = webpackDevMiddleware(compiler, middlewareOptions);
router.use(compilation);
const stats = await new Promise<Stats>((ready, stop) => {
compilation.waitUntilValid(ready as any);
reject = stop;
});
yield;
if (!stats) {
throw new Error('no stats after building manager');
}
// eslint-disable-next-line consistent-return
return {
bail,
stats,
totalTime: process.hrtime(startTime),
};
};
export const start = async (options: BuilderStartOptions) => {
asyncIterator = starter(options);
let result;
do {
// eslint-disable-next-line no-await-in-loop
result = await asyncIterator.next();
} while (!result.done);
return result.value;
};
/**
* This function is a generator so that we can abort it mid process
* in case of failure coming from other processes e.g. preview builder
*
* I am sorry for making you read about generators today :')
*/
const builder: BuilderFunction = async function* builderGeneratorFn({ startTime, options }) {
logger.info('=> Compiling manager..');
const webpackInstance = await executor.get(options);
yield;
const config = await getConfig(options);
yield;
const compiler = webpackInstance(config);
if (!compiler) {
const err = `${config.name}: missing webpack compiler at runtime!`;
logger.error(err);
return Promise.resolve(makeStatsFromError(err));
}
yield;
return new Promise<Stats>((succeed, fail) => {
compiler.run((error, stats) => {
if (error || !stats || stats.hasErrors()) {
logger.error('=> Failed to build the manager');
if (error) {
logger.error(error.message);
}
if (stats && (stats.hasErrors() || stats.hasWarnings())) {
const { warnings = [], errors = [] } = stats.toJson({ warnings: true, errors: true });
errors.forEach((e) => logger.error(e.message));
warnings.forEach((e) => logger.error(e.message));
}
process.exitCode = 1;
fail(error || stats);
} else {
logger.trace({ message: '=> Manager built', time: process.hrtime(startTime) });
if (stats && stats.hasWarnings()) {
stats.toJson({ warnings: true }).warnings.forEach((e) => logger.warn(e.message));
}
succeed(stats);
}
});
});
};
export const build = async (options: BuilderStartOptions) => {
asyncIterator = builder(options);
let result;
do {
// eslint-disable-next-line no-await-in-loop
result = await asyncIterator.next();
} while (!result.done);
return result.value;
};
export const corePresets: WebpackBuilder['corePresets'] = [
require.resolve('./presets/manager-preset'),
];
export const overridePresets: WebpackBuilder['overridePresets'] = [];

View File

@ -1,147 +0,0 @@
import path from 'path';
import fs from 'fs-extra';
import findUp from 'find-up';
import resolveFrom from 'resolve-from';
import fetch from 'node-fetch';
import deprecate from 'util-deprecate';
import dedent from 'ts-dedent';
import type { Configuration } from 'webpack';
import type { Ref, Options } from '@storybook/core-common';
export const getAutoRefs = async (
options: Options,
disabledRefs: string[] = []
): Promise<Ref[]> => {
const location = await findUp('package.json', { cwd: options.configDir });
const directory = path.dirname(location);
const { dependencies, devDependencies } = await fs.readJSON(location);
const deps = Object.keys({ ...dependencies, ...devDependencies }).filter(
(dep) => !disabledRefs.includes(dep)
);
const list = await Promise.all(
deps.map(async (d) => {
try {
const l = resolveFrom(directory, path.join(d, 'package.json'));
const { storybook, name, version } = await fs.readJSON(l);
if (storybook?.url) {
return { id: name, ...storybook, version };
}
} catch {
return undefined;
}
return undefined;
})
);
return list.filter(Boolean);
};
const checkRef = (url: string) =>
fetch(`${url}/iframe.html`).then(
({ ok }) => ok,
() => false
);
const stripTrailingSlash = (url: string) => url.replace(/\/$/, '');
const toTitle = (input: string) => {
const result = input
.replace(/[A-Z]/g, (f) => ` ${f}`)
.replace(/[-_][A-Z]/gi, (f) => ` ${f.toUpperCase()}`)
.replace(/-/g, ' ')
.replace(/_/g, ' ');
return `${result.substring(0, 1).toUpperCase()}${result.substring(1)}`.trim();
};
const deprecatedDefinedRefDisabled = deprecate(
() => {},
dedent`
Deprecated parameter: disabled => disable
https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#deprecated-package-composition-disabled-parameter
`
);
export async function getManagerWebpackConfig(options: Options): Promise<Configuration> {
const { presets } = options;
const definedRefs: Record<string, any> | undefined = await presets.apply(
'refs',
undefined,
options
);
let disabledRefs: string[] = [];
if (definedRefs) {
disabledRefs = Object.entries(definedRefs)
.filter(([key, value]) => {
const { disable, disabled } = value;
if (disable || disabled) {
if (disabled) {
deprecatedDefinedRefDisabled();
}
delete definedRefs[key]; // Also delete the ref that is disabled in definedRefs
return true;
}
return false;
})
.map((ref) => ref[0]);
}
const autoRefs = await getAutoRefs(options, disabledRefs);
const entries = await presets.apply('managerEntries', [], options);
const refs: Record<string, Ref> = {};
if (autoRefs && autoRefs.length) {
autoRefs.forEach(({ id, url, title, version }) => {
refs[id.toLowerCase()] = {
id: id.toLowerCase(),
url: stripTrailingSlash(url),
title,
version,
};
});
}
if (definedRefs) {
Object.entries(definedRefs).forEach(([key, value]) => {
const url = typeof value === 'string' ? value : value.url;
const rest =
typeof value === 'string'
? { title: toTitle(key) }
: { ...value, title: value.title || toTitle(value.key || key) };
refs[key.toLowerCase()] = {
id: key.toLowerCase(),
...rest,
url: stripTrailingSlash(url),
};
});
}
if ((autoRefs && autoRefs.length) || definedRefs) {
entries.push(path.resolve(path.join(options.configDir, `generated-refs.js`)));
// verify the refs are publicly reachable, if they are not we'll require stories.json at runtime, otherwise the ref won't work
await Promise.all(
Object.entries(refs).map(async ([k, value]) => {
const ok = await checkRef(value.url);
refs[k] = { ...value, type: ok ? 'server-checked' : 'unknown' };
})
);
}
return presets.apply('managerWebpack', {}, { ...options, entries, refs }) as any;
}

View File

@ -1,15 +0,0 @@
import type { RuleSetRule } from 'webpack';
import { getProjectRoot } from '@storybook/core-common';
export const customManagerRuntimeLoader = () => {
return {
test: /\.(mjs|tsx?|jsx?)$/,
loader: require.resolve('esbuild-loader'),
options: {
loader: 'tsx',
target: 'chrome100',
},
include: [getProjectRoot()],
exclude: [/node_modules/, /dist/],
} as RuleSetRule;
};

View File

@ -1,219 +0,0 @@
import path, { dirname } from 'path';
import { DefinePlugin, ProvidePlugin } from 'webpack';
import type { Configuration } from 'webpack';
import HtmlWebpackPlugin from 'html-webpack-plugin';
// @ts-ignore // -- this has typings for webpack4 in it, won't work
import CaseSensitivePathsPlugin from 'case-sensitive-paths-webpack-plugin';
import VirtualModulePlugin from 'webpack-virtual-modules';
import TerserWebpackPlugin from 'terser-webpack-plugin';
import uiPaths from '@storybook/ui/paths';
import readPackage from 'read-pkg-up';
import type { PresetProperty, Options } from '@storybook/core-common';
import {
loadManagerOrAddonsFile,
resolvePathInStorybookCache,
stringifyProcessEnvs,
} from '@storybook/core-common';
import type { StorybookConfig } from '@storybook/core-webpack';
import { customManagerRuntimeLoader } from './custom-manager-runtime-loader';
import { getManagerHeadTemplate, getManagerMainTemplate, readTemplate } from '../utils/template';
import { ManagerWebpackOptions } from '../utils/types';
export const managerMainTemplate: PresetProperty<
'managerMainTemplate',
StorybookConfig & { managerMainTemplate: string }
> = async () => getManagerMainTemplate();
export const managerHead: PresetProperty<'managerHead', StorybookConfig & { managerHead: string }> =
async (_, { configDir }) => getManagerHeadTemplate(configDir, process.env);
export const staticDirs: PresetProperty<'staticDirs', StorybookConfig> = [
{
from: dirname(require.resolve('@storybook/ui/runtime')),
to: '/',
},
];
export async function managerWebpack(
_: Configuration,
{
configDir,
configType,
docsMode,
entries,
refs,
outputDir,
previewUrl,
versionCheck,
releaseNotesData,
presets,
features,
serverChannelUrl,
}: Options & ManagerWebpackOptions
): Promise<Configuration> {
const envs = await presets.apply<Record<string, string>>('env');
const logLevel = await presets.apply('logLevel', undefined);
const [managerMainTemplate, managerHeadTemplate, refsTemplate] = await Promise.all<
Promise<string>[]
>([
presets.apply('managerMainTemplate'),
presets.apply('managerHead'),
readTemplate('virtualModuleRef.template.js'),
]);
const isProd = configType === 'PRODUCTION';
const {
packageJson: { version },
} = await readPackage({ cwd: __dirname });
return {
name: 'manager',
mode: isProd ? 'production' : 'development',
bail: isProd,
devtool: false,
entry: entries,
output: {
path: outputDir,
filename: isProd ? '[name].[contenthash].manager.bundle.js' : '[name].manager.bundle.js',
publicPath: '',
},
watchOptions: {
ignored: /node_modules/,
},
plugins: [
refs
? new VirtualModulePlugin({
[path.resolve(path.join(configDir, `generated-refs.js`))]: refsTemplate.replace(
`'{{refs}}'`,
JSON.stringify(refs)
),
})
: null,
new HtmlWebpackPlugin({
filename: `index.html`,
// FIXME: `none` isn't a known option
chunksSortMode: 'none' as any,
alwaysWriteToDisk: true,
inject: false,
template: managerMainTemplate,
templateParameters: {
version,
globals: {
CONFIG_TYPE: configType,
LOGLEVEL: logLevel,
FEATURES: features,
VERSIONCHECK: JSON.stringify(versionCheck),
RELEASE_NOTES_DATA: JSON.stringify(releaseNotesData),
DOCS_MODE: docsMode, // global docs mode
PREVIEW_URL: previewUrl, // global preview URL
SERVER_CHANNEL_URL: serverChannelUrl,
},
managerHeadTemplate,
},
}),
new CaseSensitivePathsPlugin(),
// graphql sources check process variable
new DefinePlugin({
...stringifyProcessEnvs(envs),
NODE_ENV: JSON.stringify(envs.NODE_ENV),
}),
new ProvidePlugin({ process: require.resolve('process/browser.js') }),
// isProd &&
// BundleAnalyzerPlugin &&
// new BundleAnalyzerPlugin({ analyzerMode: 'static', openAnalyzer: false }),
].filter(Boolean),
module: {
rules: [
customManagerRuntimeLoader(),
{
test: /\.css$/,
use: [
require.resolve('style-loader'),
{
loader: require.resolve('css-loader'),
options: {
importLoaders: 1,
},
},
],
},
{
test: /\.(svg|ico|jpg|jpeg|png|apng|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\?.*)?$/,
type: 'asset/resource',
generator: {
filename: isProd
? 'static/media/[name].[contenthash:8][ext]'
: 'static/media/[path][name][ext]',
},
},
{
test: /\.(mp4|webm|wav|mp3|m4a|aac|oga)(\?.*)?$/,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 10000,
},
},
generator: {
filename: isProd
? 'static/media/[name].[contenthash:8][ext]'
: 'static/media/[path][name][ext]',
},
},
],
},
resolve: {
extensions: ['.mjs', '.js', '.jsx', '.json', '.cjs', '.ts', '.tsx'],
modules: ['node_modules'].concat(envs.NODE_PATH || []),
mainFields: ['browser', 'module', 'main'].filter(Boolean),
alias: uiPaths,
},
recordsPath: resolvePathInStorybookCache('public/records.json'),
performance: {
hints: false,
},
optimization: {
splitChunks: {
chunks: 'all',
},
runtimeChunk: true,
sideEffects: true,
usedExports: true,
concatenateModules: true,
minimizer: isProd
? [
new TerserWebpackPlugin({
parallel: true,
terserOptions: {
mangle: false,
sourceMap: true,
keep_fnames: true,
},
}),
]
: [],
},
};
}
export async function managerEntries(
installedAddons: string[],
options: Options
): Promise<string[]> {
const entries = [];
if (installedAddons && installedAddons.length) {
entries.push(...installedAddons);
}
const managerConfig = loadManagerOrAddonsFile(options);
if (managerConfig) {
entries.push(managerConfig);
}
return entries;
}

View File

@ -1,60 +0,0 @@
import type { Options } from '@storybook/core-common';
import { logger } from '@storybook/node-logger';
import fs from 'fs-extra';
import path from 'path';
import { stringify } from 'telejson';
import webpack from 'webpack';
// The main config file determines the managerConfig value, so is already handled.
// The other files don't affect the manager, so can be safely ignored.
const ignoredConfigFiles = [/^main\.(m?js|ts)$/, /^preview\.(m?js|ts)$/, /^preview-head\.html$/];
export const useManagerCache = async (
cacheKey: string,
options: Options,
managerConfig: webpack.Configuration
) => {
const [cachedISOTime, cachedConfig] = await options.cache
.get(cacheKey)
.then((str) => str.match(/^([0-9TZ.:+-]+)_(.*)/).slice(1))
.catch(() => []);
// Drop the `cache` property because it'll change as a result of writing to the cache.
const { cache: _, ...baseConfig } = managerConfig;
const configString = stringify(baseConfig);
await options.cache.set(cacheKey, `${new Date().toISOString()}_${configString}`);
if (configString !== cachedConfig || !cachedISOTime) {
logger.line(1); // force starting new line
logger.info('=> Ignoring cached manager due to change in manager config');
return false;
}
// Check the modification time for all files in the config dir (.storybook) and
// don't use the cache if any file has been modified since the cache was created.
const configFiles = await fs.readdir(options.configDir);
const cacheCreationDate = new Date(cachedISOTime);
try {
await Promise.all(
configFiles.map(async (file) => {
if (ignoredConfigFiles.some((pattern) => pattern.test(file))) return;
const filepath = path.join(options.configDir, file);
const { mtime: fileModificationDate } = await fs.stat(filepath);
if (fileModificationDate > cacheCreationDate) throw filepath;
})
);
return true;
} catch (e) {
if (e instanceof Error) throw e;
logger.line(1); // force starting new line
logger.info(`=> Ignoring cached manager due to change in ${e}`);
return false;
}
};
export const clearManagerCache = async (cacheKey: string, options: Options) => {
if (options.cache && options.cache.fileExists(cacheKey)) {
await options.cache.remove(cacheKey);
return true;
}
return false;
};

View File

@ -1,43 +0,0 @@
import { pathExists } from 'fs-extra';
import path from 'path';
import { getInterpretedFile, loadManagerOrAddonsFile, serverRequire } from '@storybook/core-common';
import type { Options } from '@storybook/core-common';
import { getAutoRefs } from '../manager-config';
// Addons automatically installed when running `sb init` (see baseGenerator.ts)
export const DEFAULT_ADDONS = ['@storybook/addon-links', '@storybook/addon-essentials'];
// Addons we can safely ignore because they don't affect the manager
export const IGNORED_ADDONS = [
'@storybook/preset-create-react-app',
'@storybook/preset-scss',
'@storybook/preset-typescript',
...DEFAULT_ADDONS,
];
export const getPrebuiltDir = async (options: Options): Promise<string | false> => {
const { configDir, smokeTest, managerCache } = options;
if (managerCache === false || smokeTest) return false;
const prebuiltDir = path.join(__dirname, '../../../prebuilt');
const hasPrebuiltManager = await pathExists(path.join(prebuiltDir, 'index.html'));
if (!hasPrebuiltManager) return false;
const hasManagerConfig = !!loadManagerOrAddonsFile({ configDir });
if (hasManagerConfig) return false;
const mainConfigFile = getInterpretedFile(path.resolve(configDir, 'main'));
if (!mainConfigFile) return false;
const { addons, refs, managerWebpack, features } = serverRequire(mainConfigFile);
if (!addons || refs || managerWebpack || features) return false;
if (DEFAULT_ADDONS.some((addon) => !addons.includes(addon))) return false;
if (addons.some((addon: string) => !IGNORED_ADDONS.includes(addon))) return false;
// Auto refs will not be listed in the config, so we have to verify there aren't any
const autoRefs = await getAutoRefs(options);
if (autoRefs.length > 0) return false;
return prebuiltDir;
};

View File

@ -1,47 +0,0 @@
import path, { dirname, join } from 'path';
import { readFile, pathExists } from 'fs-extra';
const interpolate = (string: string, data: Record<string, string> = {}) =>
Object.entries(data).reduce((acc, [k, v]) => acc.replace(new RegExp(`%${k}%`, 'g'), v), string);
export const getTemplatePath = async (template: string) => {
return join(
dirname(require.resolve('@storybook/manager-webpack5/package.json')),
'templates',
template
);
};
export const readTemplate = async (template: string) => {
const path = await getTemplatePath(template);
return readFile(path, 'utf8');
};
export async function getManagerHeadTemplate(
configDirPath: string,
interpolations: Record<string, string>
) {
const [base, head] = await Promise.all([
readTemplate('base-manager-head.html'),
pathExists(path.resolve(configDirPath, 'manager-head.html')).then<Promise<string> | false>(
(exists) => {
if (exists) {
return readFile(path.resolve(configDirPath, 'manager-head.html'), 'utf8');
}
return false;
}
),
] as [Promise<string>, Promise<string | false>]);
let result = base;
if (head) {
result += head;
}
return interpolate(result, interpolations);
}
export async function getManagerMainTemplate() {
return getTemplatePath(`manager.ejs`);
}

View File

@ -1,6 +0,0 @@
import type { Ref } from '@storybook/core-common';
export interface ManagerWebpackOptions {
entries: string[];
refs: Record<string, Ref>;
}

View File

@ -1,46 +0,0 @@
<style>
html, body {
overflow: hidden;
height: 100%;
width: 100%;
margin: 0;
padding: 0;
}
* {
box-sizing: border-box;
}
</style>
<script>
/* globals window */
/* eslint-disable no-underscore-dangle */
try {
if (window.top !== window) {
window.__REACT_DEVTOOLS_GLOBAL_HOOK__ = window.top.__REACT_DEVTOOLS_GLOBAL_HOOK__;
}
} catch (e) {
// eslint-disable-next-line no-console
console.warn('unable to connect to top frame for connecting dev tools');
}
window.onerror = function onerror(message, source, line, column, err) {
if (window.CONFIG_TYPE !== 'DEVELOPMENT') return;
// eslint-disable-next-line no-var, vars-on-top
var xhr = new window.XMLHttpRequest();
xhr.open('POST', '/runtime-error');
xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');
xhr.send(
JSON.stringify({
/* eslint-disable object-shorthand */
message: message,
source: source,
line: line,
column: column,
error: err && { message: err.message, name: err.name, stack: err.stack },
origin: 'manager',
/* eslint-enable object-shorthand */
})
);
};
</script>

View File

@ -1,53 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title><%= htmlWebpackPlugin.options.title || 'Storybook'%></title>
<% if (htmlWebpackPlugin.files.favicon) { %>
<link rel="shortcut icon" href="<%= htmlWebpackPlugin.files.favicon%>" />
<% } %>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<% if (typeof headHtmlSnippet !== 'undefined') { %> <%= headHtmlSnippet %> <% } %> <%
htmlWebpackPlugin.files.css.forEach(file => { %>
<link href="<%= file %>" rel="stylesheet" />
<% }); %>
<style>
#root[hidden],
#docs-root[hidden] {
display: none !important;
}
</style>
</head>
<body>
<% if (typeof bodyHtmlSnippet !== 'undefined') { %> <%= bodyHtmlSnippet %> <% } %>
<div id="root"></div>
<div id="docs-root"></div>
<% if (typeof globals !== 'undefined' && Object.keys(globals).length) { %>
<script>
<% for (var varName in globals) { %>
<% if (globals[varName] != undefined) { %>
window['<%=varName%>'] = <%= JSON.stringify(globals[varName]) %>;
<% } %>
<% } %>
</script>
<% } %>
<!--
<% htmlWebpackPlugin.files.js.forEach(file => { %>
<script src="<%= file %>"></script>
<% }); %>
-->
<script>
console.log('it is good to be here')
</script>
<script src="/runtime.mjs" type="module"></script>
</body>
</html>

View File

@ -1,5 +0,0 @@
import { addons } from '@storybook/addons';
addons.setConfig({
refs: '{{refs}}',
});

View File

@ -1,10 +0,0 @@
{
"extends": "../../tsconfig.json",
"include": [
"src/**/*",
"typings.d.ts"
],
"exclude": [
"src/**.test.ts"
]
}

View File

@ -1,9 +0,0 @@
declare module 'global';
declare module '@storybook/semver';
declare module 'lazy-universal-dotenv';
declare module '@storybook/theming/paths';
declare module '@storybook/ui/paths';
declare module 'better-opn';
declare module 'open';
declare module 'x-default-browser';
declare module '@storybook/ui';

View File

@ -82,7 +82,6 @@
"@storybook/core-server": { "implicitDependencies": [] },
"@storybook/csf-tools": { "implicitDependencies": [] },
"@storybook/manager-webpack5": { "implicitDependencies": [] },
"@storybook/builder-manager": { "implicitDependencies": [] },
"@storybook/addons": { "implicitDependencies": [] },
"@storybook/api": { "implicitDependencies": [] },

View File

@ -28,7 +28,6 @@
"@storybook/docs-tools": { "root": "lib/docs-tools", "type": "library" },
"@storybook/ember": { "root": "frameworks/ember", "type": "library" },
"@storybook/html-webpack5": { "root": "frameworks/html-webpack5", "type": "library" },
"@storybook/manager-webpack5": { "root": "lib/manager-webpack5", "type": "library" },
"@storybook/builder-manager": { "root": "lib/builder-manager", "type": "library" },
"@storybook/preact-webpack5": { "root": "frameworks/preact-webpack5", "type": "library" },
"@storybook/react-webpack5": { "root": "frameworks/react-webpack5", "type": "library" },

View File

@ -1913,7 +1913,7 @@ __metadata:
languageName: node
linkType: hard
"@babel/plugin-transform-template-literals@npm:^7.12.1, @babel/plugin-transform-template-literals@npm:^7.16.7, @babel/plugin-transform-template-literals@npm:^7.18.2":
"@babel/plugin-transform-template-literals@npm:^7.16.7, @babel/plugin-transform-template-literals@npm:^7.18.2":
version: 7.18.2
resolution: "@babel/plugin-transform-template-literals@npm:7.18.2"
dependencies:
@ -8158,7 +8158,6 @@ __metadata:
"@storybook/csf": 0.0.2--canary.4566f4d.1
"@storybook/csf-tools": 7.0.0-alpha.6
"@storybook/docs-mdx": 0.0.1-canary.1.4bea5cc.0
"@storybook/manager-webpack5": 7.0.0-alpha.6
"@storybook/node-logger": 7.0.0-alpha.6
"@storybook/semver": ^7.3.2
"@storybook/store": 7.0.0-alpha.6
@ -8559,52 +8558,6 @@ __metadata:
languageName: node
linkType: hard
"@storybook/manager-webpack5@7.0.0-alpha.6, @storybook/manager-webpack5@workspace:lib/manager-webpack5":
version: 0.0.0-use.local
resolution: "@storybook/manager-webpack5@workspace:lib/manager-webpack5"
dependencies:
"@babel/core": ^7.12.10
"@babel/plugin-transform-template-literals": ^7.12.1
"@babel/preset-react": ^7.12.10
"@storybook/addons": 7.0.0-alpha.6
"@storybook/core-client": 7.0.0-alpha.6
"@storybook/core-common": 7.0.0-alpha.6
"@storybook/core-webpack": 7.0.0-alpha.6
"@storybook/node-logger": 7.0.0-alpha.6
"@storybook/theming": 7.0.0-alpha.6
"@storybook/ui": 7.0.0-alpha.6
"@types/node": ^14.0.10 || ^16.0.0
"@types/terser-webpack-plugin": ^5.2.0
"@types/webpack-dev-middleware": ^5.3.0
"@types/webpack-virtual-modules": ^0.1.1
babel-loader: ^8.2.5
case-sensitive-paths-webpack-plugin: ^2.4.0
chalk: ^4.1.0
core-js: ^3.8.2
css-loader: ^6.7.1
esbuild-loader: ^2.19.0
express: ^4.17.1
find-up: ^5.0.0
fs-extra: ^9.0.1
html-webpack-plugin: ^5.5.0
node-fetch: ^2.6.7
process: ^0.11.10
read-pkg-up: ^7.0.1
resolve-from: ^5.0.0
style-loader: ^3.3.1
telejson: ^6.0.8
terser-webpack-plugin: ^5.3.1
ts-dedent: ^2.0.0
util-deprecate: ^1.0.2
webpack: 5
webpack-dev-middleware: ^5.3.1
webpack-virtual-modules: ^0.4.3
peerDependenciesMeta:
typescript:
optional: true
languageName: unknown
linkType: soft
"@storybook/mdx1-csf@npm:^0.0.1":
version: 0.0.1
resolution: "@storybook/mdx1-csf@npm:0.0.1"
@ -20922,22 +20875,6 @@ __metadata:
languageName: node
linkType: hard
"esbuild-loader@npm:^2.19.0":
version: 2.19.0
resolution: "esbuild-loader@npm:2.19.0"
dependencies:
esbuild: ^0.14.39
joycon: ^3.0.1
json5: ^2.2.0
loader-utils: ^2.0.0
tapable: ^2.2.0
webpack-sources: ^2.2.0
peerDependencies:
webpack: ^4.40.0 || ^5.0.0
checksum: 309b4bd169ffd1d743ad7099182323a179dec789cd5eb4f5f0258c1c13034a154917679e2402bfcedd90c9b03d06d773e97fe4aa1056274dc43d30633c5707e1
languageName: node
linkType: hard
"esbuild-netbsd-64@npm:0.14.47":
version: 0.14.47
resolution: "esbuild-netbsd-64@npm:0.14.47"