1 |
|
2 |
|
3 | const wp = require('webpack');
|
4 | const R = require('ramda');
|
5 | const path = require('path');
|
6 | const merge = require('webpack-merge');
|
7 | const fsExtra = require('fs-extra');
|
8 | const { baseConfigFn, ssrConfig } = require('@rei/front-end-build-configs').profiles.application;
|
9 | const logger = require('./lib/logger');
|
10 | const lib = require('./lib');
|
11 | const devServer = require('./lib/dev-server');
|
12 |
|
13 | const projectPath = process.cwd();
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 | module.exports = function init(command, conf = {}) {
|
28 | const febsConfigArg = conf;
|
29 |
|
30 |
|
31 | const env = command.name
|
32 | ? command.name() === 'prod'
|
33 | ? 'production'
|
34 | : 'development'
|
35 | : conf.env;
|
36 |
|
37 |
|
38 | const fs = conf.fs || require('fs');
|
39 |
|
40 | if (conf.logLevel) logger.setLogLevel(conf.logLevel);
|
41 |
|
42 |
|
43 | const getOverridesConf = (confOverride) => {
|
44 | if (confOverride) return confOverride;
|
45 |
|
46 | const overridesConfFile = path.resolve(projectPath, './webpack.overrides.conf.js');
|
47 |
|
48 | if (fs.existsSync(overridesConfFile)) {
|
49 | logger.info('Using local webpack.overrides.conf.js...');
|
50 |
|
51 | const overridesConf = require(overridesConfFile);
|
52 |
|
53 |
|
54 | if (R.hasPath(['output', 'path'], overridesConf)) {
|
55 | logger.warn('Overriding the output path may break upstream expectations of asset locations.');
|
56 | }
|
57 |
|
58 | return overridesConf;
|
59 | }
|
60 |
|
61 | return {};
|
62 | };
|
63 |
|
64 | const memoize = fn => R.memoizeWith(R.identity, fn);
|
65 |
|
66 | const febsConfigPath = path.resolve(projectPath, './febs-config.json');
|
67 | const readJson = memoize(filePath => fsExtra.readJsonSync(filePath));
|
68 | const getFebsConfigJson = readJson.bind(null, febsConfigPath);
|
69 |
|
70 | const getFebsConfig = (febsConfig = {}) => {
|
71 | let febsConfigFileJSON;
|
72 | if (fs.existsSync(febsConfigPath)) {
|
73 | febsConfigFileJSON = getFebsConfigJson();
|
74 | } else if (febsConfigArg && (febsConfigArg.output || febsConfigArg.entry)) {
|
75 | febsConfigFileJSON = febsConfigArg;
|
76 | }
|
77 |
|
78 | if (febsConfigFileJSON) {
|
79 | logger.info('Using local febs-config.json...');
|
80 | logger.warn('Entries in febs-config.json will override those in webpack.overrides.conf.js.');
|
81 | }
|
82 |
|
83 | return R.merge(febsConfig, febsConfigFileJSON);
|
84 | };
|
85 |
|
86 | const isSSR = () => getFebsConfig().ssr;
|
87 |
|
88 | const getPackageName = () => {
|
89 | const projectPackageJson = path.join(projectPath, 'package.json');
|
90 | return require(projectPackageJson).name;
|
91 | };
|
92 |
|
93 | |
94 |
|
95 |
|
96 |
|
97 |
|
98 |
|
99 | const febsConfigMerge = (febsConfig, wpConf) => {
|
100 |
|
101 | const newOutputPath = R.hasPath(['output', 'path'], febsConfig)
|
102 | ? path.resolve(projectPath, febsConfig.output.path, getPackageName())
|
103 | : wpConf.output.path;
|
104 |
|
105 | const wpConfNewOutputPath = R.mergeDeepRight(wpConf, {
|
106 | output: {
|
107 | path: newOutputPath,
|
108 | },
|
109 | });
|
110 |
|
111 |
|
112 | if (febsConfig.entry) {
|
113 | const { entry } = febsConfig;
|
114 | const newEntries = R.zipObj(
|
115 | R.keys(entry),
|
116 | R.values(entry).map(
|
117 | entryArr => entryArr.map(
|
118 | entryPath => path.resolve(projectPath, entryPath)
|
119 | )
|
120 | )
|
121 | );
|
122 |
|
123 | return R.merge(R.dissoc('entry', wpConfNewOutputPath), {
|
124 | entry: newEntries,
|
125 | });
|
126 | }
|
127 |
|
128 | return wpConfNewOutputPath;
|
129 | };
|
130 |
|
131 | |
132 |
|
133 |
|
134 |
|
135 |
|
136 |
|
137 |
|
138 | const addVueSSRToWebpackConfig = R.curry((ssr, wpConf) => {
|
139 | if (!ssr) {
|
140 | return wpConf;
|
141 | }
|
142 |
|
143 | const pluginsToRemove = ['ManifestPlugin', 'CleanWebpackPlugin'];
|
144 |
|
145 |
|
146 | const plugins = wpConf.plugins
|
147 | .filter(plugin => !pluginsToRemove.includes(plugin.constructor.name));
|
148 |
|
149 | const pluginsFiltered = R.merge(R.dissoc('plugins', wpConf), {
|
150 | plugins,
|
151 | });
|
152 |
|
153 |
|
154 | return merge.smartStrategy({
|
155 | entry: 'replace',
|
156 | plugins: 'append',
|
157 | })(pluginsFiltered, ssrConfig);
|
158 | });
|
159 |
|
160 | |
161 |
|
162 |
|
163 |
|
164 |
|
165 |
|
166 |
|
167 |
|
168 |
|
169 | const getWebpackConfigBase = memoize((confOverride) => {
|
170 | const webpackConfigBase = baseConfigFn(env);
|
171 |
|
172 | logger.info(`Building in webpack ${webpackConfigBase.mode} mode...`);
|
173 |
|
174 | const configsToMerge = [webpackConfigBase];
|
175 |
|
176 |
|
177 | const wpMergeConf = {
|
178 | entry: 'replace',
|
179 | };
|
180 |
|
181 |
|
182 | configsToMerge.push(getOverridesConf(confOverride));
|
183 |
|
184 | const wpConf = merge.smartStrategy(wpMergeConf)(configsToMerge);
|
185 |
|
186 |
|
187 | return febsConfigMerge(getFebsConfig(), wpConf);
|
188 | });
|
189 |
|
190 | |
191 |
|
192 |
|
193 |
|
194 |
|
195 |
|
196 |
|
197 | const getWebpackConfigFn = ssr => R.compose(
|
198 | addVueSSRToWebpackConfig(ssr),
|
199 | getWebpackConfigBase
|
200 | );
|
201 |
|
202 | |
203 |
|
204 |
|
205 |
|
206 |
|
207 |
|
208 | const getWebpackConfig = ssr => getWebpackConfigFn(ssr);
|
209 |
|
210 | |
211 |
|
212 |
|
213 |
|
214 |
|
215 |
|
216 |
|
217 | const createWebpackCompiler = wpConf => wp(wpConf);
|
218 |
|
219 | |
220 |
|
221 |
|
222 |
|
223 | const createCompiler = ssr => R.compose(
|
224 | createWebpackCompiler,
|
225 | getWebpackConfig(ssr)
|
226 | );
|
227 |
|
228 | |
229 |
|
230 |
|
231 |
|
232 |
|
233 |
|
234 | const webpackCompileDone = (err, stats) => {
|
235 |
|
236 | if (!process.env.FEBS_TEST) {
|
237 | logger.info(stats.toString({
|
238 | chunks: false,
|
239 | colors: true,
|
240 | }));
|
241 | }
|
242 |
|
243 |
|
244 | if (stats.compilation.errors && stats.compilation.errors.length === 0) {
|
245 | return {
|
246 | err,
|
247 | stats,
|
248 | exitCode: 0,
|
249 | };
|
250 | }
|
251 |
|
252 |
|
253 | if (env === 'development') {
|
254 | return {
|
255 | err,
|
256 | stats,
|
257 | exitCode: 0,
|
258 | };
|
259 | }
|
260 |
|
261 |
|
262 |
|
263 | process.exitCode = 1;
|
264 | return {
|
265 | err,
|
266 | stats,
|
267 | exitCode: 1,
|
268 | };
|
269 | };
|
270 |
|
271 | |
272 |
|
273 |
|
274 |
|
275 |
|
276 | const runCompile = (ssr) => {
|
277 | const compilerFn = command.watch ? 'watch' : 'run';
|
278 |
|
279 | const compiler = createCompiler(ssr)();
|
280 |
|
281 | logger.info(`Compiling ${ssr ? 'SSR build' : 'client-side bundles'}:`);
|
282 |
|
283 | if (!ssr) {
|
284 | Object.keys(compiler.options.entry).forEach(e => logger.info(` ✔ ${e}`));
|
285 | }
|
286 |
|
287 | logger.info(`📝 Writing ${ssr ? 'vue-ssr-server-bundle.json' : 'assets'} to: ${path.relative(projectPath, compiler.outputPath)}...`);
|
288 |
|
289 | if (compilerFn === 'run') {
|
290 | compiler[compilerFn](webpackCompileDone);
|
291 | } else {
|
292 | compiler[compilerFn]({}, webpackCompileDone);
|
293 | }
|
294 | return compiler;
|
295 | };
|
296 |
|
297 | |
298 |
|
299 |
|
300 |
|
301 |
|
302 |
|
303 |
|
304 |
|
305 |
|
306 | const compile = function compile() {
|
307 |
|
308 | runCompile(false);
|
309 |
|
310 |
|
311 | if (isSSR()) {
|
312 | runCompile(true);
|
313 | }
|
314 | };
|
315 |
|
316 | |
317 |
|
318 |
|
319 |
|
320 | const startDevServerFn = wds => R.compose(
|
321 | devServer.bind(null, wds),
|
322 | createCompiler(false),
|
323 | );
|
324 |
|
325 | return {
|
326 | compile,
|
327 | createCompiler,
|
328 | webpackCompileDone,
|
329 | startDevServerFn,
|
330 | getWebpackConfig,
|
331 | addVueSSRToWebpackConfig,
|
332 | getWebpackConfigFn,
|
333 | febsConfigMerge,
|
334 | };
|
335 | };
|