-
-
Notifications
You must be signed in to change notification settings - Fork 9.4k
/
index.ts
180 lines (150 loc) · 5.87 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
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, checkWebpackVersion } from '@storybook/core-common';
import type { Builder, Options } from '@storybook/core-common';
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>;
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;
},
};
export const start: WebpackBuilder['start'] = async ({ 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;
}
const config = await getConfig(options);
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),
]);
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))) {
logger.line(1); // force starting new line
logger.info('=> Cleared cached manager config');
}
}
const webpackInstance = await executor.get(options);
const compiler = (webpackInstance as any)(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);
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);
reject = stop;
});
if (!stats) {
throw new Error('no stats after building preview');
}
// eslint-disable-next-line consistent-return
return {
bail,
stats,
totalTime: process.hrtime(startTime),
};
};
export const bail: WebpackBuilder['bail'] = (e: Error) => {
if (reject) {
reject();
}
if (process) {
try {
compilation.close();
logger.warn('Force closed preview build');
} catch (err) {
logger.warn('Unable to close preview build!');
}
}
throw e;
};
export const build: WebpackBuilder['build'] = async ({ options, startTime }) => {
logger.info('=> Compiling manager..');
const webpackInstance = await executor.get(options);
const config = await getConfig(options);
const compiler = webpackInstance(config);
if (!compiler) {
const err = `${config.name}: missing webpack compiler at runtime!`;
logger.error(err);
return Promise.resolve(makeStatsFromError(err));
}
return new Promise((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 corePresets: WebpackBuilder['corePresets'] = [
require.resolve('./presets/manager-preset'),
];
export const overridePresets: WebpackBuilder['overridePresets'] = [];