1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 | const fs = require('fs');
|
12 | const path = require('path');
|
13 |
|
14 | const webpack = require('webpack');
|
15 | const chalk = require('chalk');
|
16 | const webpackHotMiddleware = require('webpack-hot-middleware');
|
17 | const rimraf = require('rimraf');
|
18 |
|
19 | const webpackDevMiddleware = require('../lib/simple-webpack-dev-middleware');
|
20 | const {getWebpackConfig} = require('./get-webpack-config.js');
|
21 | const {
|
22 | DeferredState,
|
23 | SyncState,
|
24 | MergedDeferredState,
|
25 | } = require('./shared-state-containers.js');
|
26 | const mergeChunkMetadata = require('./merge-chunk-metadata');
|
27 | const loadFusionRC = require('./load-fusionrc.js');
|
28 |
|
29 | const Worker = require('jest-worker').default;
|
30 |
|
31 | function getErrors(info) {
|
32 | let errors = [].concat(info.errors);
|
33 | if (info.children.length) {
|
34 | errors = errors.concat(
|
35 | info.children.reduce((x, child) => {
|
36 | return x.concat(getErrors(child));
|
37 | }, [])
|
38 | );
|
39 | }
|
40 | return dedupeErrors(errors);
|
41 | }
|
42 |
|
43 | function getWarnings(info) {
|
44 | let warnings = [].concat(info.warnings);
|
45 | if (info.children.length) {
|
46 | warnings = warnings.concat(
|
47 | info.children.reduce((x, child) => {
|
48 | return x.concat(getWarnings(child));
|
49 | }, [])
|
50 | );
|
51 | }
|
52 | return dedupeErrors(warnings);
|
53 | }
|
54 |
|
55 | function dedupeErrors(items) {
|
56 | const re = /BabelLoaderError(.|\n)+( {4}at transpile)/gim;
|
57 | const set = new Set(items.map(item => item.replace(re, '$2')));
|
58 | return Array.from(set);
|
59 | }
|
60 |
|
61 | function getStatsLogger({dir, logger, env}) {
|
62 | return (err, stats) => {
|
63 |
|
64 |
|
65 | const isProd = env === 'production';
|
66 |
|
67 | if (err) {
|
68 | logger.error(err.stack || err);
|
69 | if (err.details) {
|
70 | logger.error(err.details);
|
71 | }
|
72 | return;
|
73 | }
|
74 |
|
75 | const file = path.resolve(dir, '.fusion/stats.json');
|
76 | const info = stats.toJson({context: path.resolve(dir)});
|
77 | fs.writeFile(file, JSON.stringify(info, null, 2), () => {});
|
78 |
|
79 | if (stats.hasErrors()) {
|
80 | getErrors(info).forEach(e => logger.error(e));
|
81 | }
|
82 |
|
83 | if (isProd) {
|
84 | info.children.forEach(child => {
|
85 | child.assets
|
86 | .slice()
|
87 | .filter(asset => {
|
88 | return !asset.name.endsWith('.map');
|
89 | })
|
90 | .sort((a, b) => {
|
91 | return b.size - a.size;
|
92 | })
|
93 | .forEach(asset => {
|
94 | logger.info(`Entrypoint: ${chalk.bold(child.name)}`);
|
95 | logger.info(`Asset: ${chalk.bold(asset.name)}`);
|
96 | logger.info(`Size: ${chalk.bold(asset.size)} bytes`);
|
97 | });
|
98 | });
|
99 | }
|
100 | if (stats.hasWarnings()) {
|
101 | getWarnings(info).forEach(e => logger.warn(e));
|
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 | function Compiler(
|
133 | {
|
134 | dir = '.',
|
135 | env,
|
136 | hmr = true,
|
137 | forceLegacyBuild,
|
138 | preserveNames,
|
139 | watch = false,
|
140 | logger = console,
|
141 | minify = true,
|
142 | serverless = false,
|
143 | modernBuildOnly = false,
|
144 | skipSourceMaps = false,
|
145 | maxWorkers,
|
146 | } /*: CompilerOpts */
|
147 | ) /*: CompilerType */ {
|
148 | const root = path.resolve(dir);
|
149 | const fusionConfig = loadFusionRC(root);
|
150 | const legacyPkgConfig = loadLegacyPkgConfig(root);
|
151 |
|
152 | const clientChunkMetadata = new DeferredState();
|
153 | const legacyClientChunkMetadata = new DeferredState();
|
154 | const legacyBuildEnabled = new SyncState(
|
155 | (forceLegacyBuild || !watch || env === 'production') &&
|
156 | !(modernBuildOnly || fusionConfig.modernBuildOnly)
|
157 | );
|
158 | const mergedClientChunkMetadata = new MergedDeferredState(
|
159 | [
|
160 | {deferred: clientChunkMetadata, enabled: new SyncState(true)},
|
161 | {deferred: legacyClientChunkMetadata, enabled: legacyBuildEnabled},
|
162 | ],
|
163 | mergeChunkMetadata
|
164 | );
|
165 |
|
166 | const state = {
|
167 | clientChunkMetadata,
|
168 | legacyClientChunkMetadata,
|
169 | mergedClientChunkMetadata,
|
170 | i18nManifest: new Map(),
|
171 | i18nDeferredManifest: new DeferredState(),
|
172 | legacyBuildEnabled,
|
173 | };
|
174 |
|
175 | let worker = createWorker(maxWorkers);
|
176 |
|
177 | const sharedOpts = {
|
178 | dir: root,
|
179 | dev: env === 'development',
|
180 | hmr,
|
181 | watch,
|
182 | state,
|
183 | fusionConfig,
|
184 | legacyPkgConfig,
|
185 | skipSourceMaps,
|
186 | preserveNames,
|
187 |
|
188 | zopfli: fusionConfig.zopfli != undefined ? fusionConfig.zopfli : true,
|
189 | gzip: fusionConfig.gzip != undefined ? fusionConfig.gzip : true,
|
190 | brotli: fusionConfig.brotli != undefined ? fusionConfig.brotli : true,
|
191 | minify,
|
192 | worker,
|
193 | };
|
194 | const compiler = webpack([
|
195 | getWebpackConfig({id: 'client-modern', ...sharedOpts}),
|
196 | getWebpackConfig({
|
197 | id: serverless ? 'serverless' : 'server',
|
198 | ...sharedOpts,
|
199 | }),
|
200 | ]);
|
201 | if (process.env.LOG_END_TIME == 'true') {
|
202 | compiler.hooks.done.tap('BenchmarkTimingPlugin', stats => {
|
203 |
|
204 | console.log(`End time: ${Date.now()}`);
|
205 | });
|
206 | }
|
207 |
|
208 | if (watch) {
|
209 | compiler.hooks.watchRun.tap('StartWorkersAgain', () => {
|
210 | if (worker === void 0) worker = createWorker(maxWorkers);
|
211 | });
|
212 | compiler.hooks.watchClose.tap('KillWorkers', stats => {
|
213 | if (worker !== void 0) worker.end();
|
214 | worker = void 0;
|
215 | });
|
216 | } else
|
217 | compiler.hooks.done.tap('KillWorkers', stats => {
|
218 | if (worker !== void 0) worker.end();
|
219 | worker = void 0;
|
220 | });
|
221 |
|
222 | const statsLogger = getStatsLogger({dir, logger, env});
|
223 |
|
224 | this.on = (type, callback) => compiler.hooks[type].tap('compiler', callback);
|
225 | this.start = cb => {
|
226 | cb = cb || function noop(err, stats) {};
|
227 |
|
228 |
|
229 |
|
230 | let hasCalledCb = false;
|
231 | const handler = (err, stats) => {
|
232 | statsLogger(err, stats);
|
233 | if (!hasCalledCb) {
|
234 | hasCalledCb = true;
|
235 | cb(err, stats);
|
236 | }
|
237 | };
|
238 | if (watch) {
|
239 | return compiler.watch({}, handler);
|
240 | } else {
|
241 | compiler.run(handler);
|
242 |
|
243 | return {
|
244 | close() {},
|
245 | invalidate() {},
|
246 | };
|
247 | }
|
248 | };
|
249 |
|
250 | this.getMiddleware = () => {
|
251 | const dev = webpackDevMiddleware(compiler);
|
252 | const hot = webpackHotMiddleware(compiler, {log: false});
|
253 | return (req, res, next) => {
|
254 | dev(req, res, err => {
|
255 | if (err) return next(err);
|
256 | return hot(req, res, next);
|
257 | });
|
258 | };
|
259 | };
|
260 |
|
261 | this.clean = () => {
|
262 | return new Promise((resolve, reject) => {
|
263 | rimraf(`${dir}/.fusion`, e => (e ? reject(e) : resolve()));
|
264 | });
|
265 | };
|
266 |
|
267 | return this;
|
268 | }
|
269 |
|
270 | function loadLegacyPkgConfig(dir) {
|
271 | const appPkgJsonPath = path.join(dir, 'package.json');
|
272 | const legacyPkgConfig = {};
|
273 | if (fs.existsSync(appPkgJsonPath)) {
|
274 |
|
275 | const appPkg = require(appPkgJsonPath);
|
276 | if (typeof appPkg.node !== 'undefined') {
|
277 |
|
278 | console.warn(
|
279 | [
|
280 | `Warning: using a top-level "node" field in your app package.json to override node built-in shimming is deprecated.`,
|
281 | `Please use the "nodeBuiltins" field in .fusionrc.js instead.`,
|
282 | `See: https://github.com/fusionjs/fusion-cli/blob/master/docs/fusionrc.md#nodebuiltins`,
|
283 | ].join(' ')
|
284 | );
|
285 | }
|
286 | legacyPkgConfig.node = appPkg.node;
|
287 | }
|
288 | return legacyPkgConfig;
|
289 | }
|
290 |
|
291 | function createWorker(maxWorkers /* maxWorkers?: number */) {
|
292 | if (require('os').cpus().length < 2) return void 0;
|
293 | return new Worker(require.resolve('./loaders/babel-worker.js'), {
|
294 | exposedMethods: ['runTransformation'],
|
295 | forkOptions: {stdio: 'inherit'},
|
296 | numWorkers: maxWorkers,
|
297 | });
|
298 | }
|
299 |
|
300 | module.exports.Compiler = Compiler;
|