1 |
|
2 | const _ = require('lodash');
|
3 | const path = require('path');
|
4 | const webpack = require('webpack');
|
5 | const merge = require('webpack-merge');
|
6 | const openBrowser = require('opn');
|
7 |
|
8 | const {getConfig, getRollupConfig} = require('./config/default');
|
9 | const {getOptimizationConfig} = require('./config/optimization');
|
10 | const {getPlugins} = require('./config/plugins');
|
11 | const {getLoaders} = require('./config/loaders');
|
12 | const getNodeConfig = require('./config/node');
|
13 | const {getDevServerConfig} = require('./config/devServer');
|
14 |
|
15 | function getDevTool(config) {
|
16 | if (!config.sourceMap) return false;
|
17 | if (config.isProduction) return '#source-map';
|
18 | return '#eval-source-map';
|
19 | }
|
20 |
|
21 | function getFileName(config) {
|
22 | if (config.library || !config.appendHash) return '[name].js';
|
23 |
|
24 | if (config.isProduction) return 'js/[name].[contenthash:7].js';
|
25 | return 'js/[name].js';
|
26 | }
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 | function getWebpackConfig(options = {}) {
|
33 | const env = options.env || process.env.NODE_ENV || 'development';
|
34 |
|
35 | const config = getConfig(options.config, env);
|
36 | options.config._final = config;
|
37 | const webpackConfig = options.webpackConfig || {};
|
38 |
|
39 | const isProduction = config.isProduction;
|
40 |
|
41 |
|
42 | process.env.NODE_ENV = isProduction ? 'production' : 'development';
|
43 |
|
44 | const cwd = config.cwd;
|
45 |
|
46 | config.sourcePath = path.join(cwd, config.sourcePath);
|
47 | config.destPath = path.join(cwd, config.destPath);
|
48 |
|
49 | const entry = {};
|
50 | _.forEach(config.entry, (value, key) => {
|
51 | entry[key] = path.join(config.sourcePath, value);
|
52 | });
|
53 |
|
54 | const baseConfig = {
|
55 | target: 'web',
|
56 | mode: isProduction ? 'production' : 'development',
|
57 | context: cwd,
|
58 | entry,
|
59 | output: {
|
60 | path: config.destPath,
|
61 | publicPath: config.publicUrl,
|
62 | filename: getFileName(config),
|
63 | chunkFilename: getFileName(config),
|
64 | },
|
65 | resolve: {
|
66 | extensions: ['.wasm', '.mjs', '.js', '.mjsx', '.jsx', '.vue', '.json'],
|
67 | modules: [
|
68 | path.join(cwd, 'node_modules'),
|
69 | path.join(__dirname, 'node_modules'),
|
70 | ],
|
71 | alias: {
|
72 | '@': config.sourcePath,
|
73 | res: config.sourcePath,
|
74 | js: path.join(config.sourcePath, 'js'),
|
75 | assets: path.join(config.sourcePath, 'assets'),
|
76 | components: path.join(config.sourcePath, 'js', 'components'),
|
77 | css: path.join(config.sourcePath, 'css'),
|
78 | img: path.join(config.sourcePath, 'img'),
|
79 | },
|
80 | },
|
81 | resolveLoader: {
|
82 | modules: [
|
83 | path.join(cwd, 'node_modules'),
|
84 | path.join(__dirname, 'node_modules'),
|
85 | ],
|
86 | },
|
87 | module: {
|
88 |
|
89 | noParse: /node_modules(.*)\/(firepad|jquery)\//,
|
90 | rules: getLoaders(config),
|
91 | },
|
92 | node: getNodeConfig(config),
|
93 | optimization: getOptimizationConfig(config),
|
94 | plugins: getPlugins(config),
|
95 | devtool: getDevTool(config),
|
96 | };
|
97 |
|
98 | if (config.library) {
|
99 | baseConfig.output.libraryTarget = config.libraryFormat || 'umd';
|
100 | baseConfig.output.umdNamedDefine = true;
|
101 | baseConfig.output.library = config.library === true ? 'Lib' : config.library;
|
102 | }
|
103 |
|
104 | if (!isProduction) {
|
105 |
|
106 |
|
107 | baseConfig.output.globalObject = 'this';
|
108 |
|
109 |
|
110 | baseConfig.performance = {hints: false};
|
111 | }
|
112 |
|
113 | return merge(baseConfig, webpackConfig);
|
114 | }
|
115 |
|
116 | function getProdConfig({config, webpackConfig}) {
|
117 | return getConfig({env: 'production', config, webpackConfig});
|
118 | }
|
119 |
|
120 | function getDevConfig({config, webpackConfig}) {
|
121 | return getConfig({env: 'development', config, webpackConfig});
|
122 | }
|
123 |
|
124 | function runWebpack({env, config, webpackConfig}) {
|
125 | const formatStats = require('./util/formatStats');
|
126 | return new Promise((resolve, reject) => {
|
127 | const finalWebpackConfig = getWebpackConfig({env, config, webpackConfig});
|
128 |
|
129 |
|
130 | webpack(finalWebpackConfig, (err, stats) => {
|
131 | if (err) {
|
132 | reject(err);
|
133 | return;
|
134 | }
|
135 |
|
136 | console.log(formatStats(stats, config.destPath));
|
137 | resolve();
|
138 | });
|
139 | });
|
140 | }
|
141 |
|
142 | function runDevWebpack({config = {}, webpackConfig = {}} = {}) {
|
143 | return runWebpack({env: 'development', config, webpackConfig});
|
144 | }
|
145 |
|
146 | function runProdWebpack({config = {}, webpackConfig = {}} = {}) {
|
147 | return runWebpack({env: 'production', config, webpackConfig});
|
148 | }
|
149 |
|
150 | function runDevServer({config = {}, webpackConfig = {}} = {}) {
|
151 |
|
152 | const WebpackDevServer = require('webpack-dev-server');
|
153 |
|
154 | const finalConfig = getConfig(config, 'development');
|
155 | finalConfig.isDevServer = true;
|
156 | finalConfig.destPath = '.';
|
157 | finalConfig.publicUrl = '/';
|
158 |
|
159 | const devServerConfig = getDevServerConfig(finalConfig);
|
160 | const finalWebpackConfig = getWebpackConfig({
|
161 | env: 'development',
|
162 | devServer: true,
|
163 | config: finalConfig,
|
164 | webpackConfig,
|
165 | });
|
166 |
|
167 |
|
168 | WebpackDevServer.addDevServerEntrypoints(finalWebpackConfig, {
|
169 | contentBase: devServerConfig.contentBase,
|
170 | hot: true,
|
171 | host: 'localhost',
|
172 | });
|
173 |
|
174 | let isFirstCompile = true;
|
175 | const compiler = webpack(finalWebpackConfig);
|
176 | compiler.hooks.done.tap('sm-webpack-dev-server', (stats) => {
|
177 | if (stats.hasErrors()) return;
|
178 |
|
179 | if (isFirstCompile) {
|
180 | isFirstCompile = false;
|
181 | if (finalConfig.openBrowser) {
|
182 | const port = devServerConfig.port;
|
183 | const protocol = devServerConfig.https ? 'https' : 'http';
|
184 | openBrowser(`${protocol}://localhost:${port}`);
|
185 | }
|
186 | }
|
187 | });
|
188 |
|
189 |
|
190 | const server = new WebpackDevServer(compiler, devServerConfig);
|
191 |
|
192 | return new Promise((resolve, reject) => {
|
193 | server.listen(devServerConfig.port, devServerConfig.host, (err) => {
|
194 | if (err) {
|
195 | reject(err);
|
196 | return;
|
197 | }
|
198 |
|
199 | resolve();
|
200 | });
|
201 | });
|
202 | }
|
203 |
|
204 | function runRollup(options = {}) {
|
205 | const rollup = require('rollup');
|
206 |
|
207 | const env = process.env.NODE_ENV || 'production';
|
208 | const config = getRollupConfig(options.config || {}, env);
|
209 | const cwd = config.cwd;
|
210 |
|
211 | config.entry = path.resolve(cwd, config.entry);
|
212 | config.dest = path.resolve(cwd, config.dest);
|
213 |
|
214 | const {getBabelConfig} = require('./config/babel');
|
215 | const plugins = [
|
216 | require('rollup-plugin-babel')({
|
217 | exclude: 'node_modules/**',
|
218 | ...getBabelConfig(config),
|
219 | }),
|
220 | ];
|
221 |
|
222 | if (config.minify) {
|
223 | plugins.push(
|
224 | require('rollup-plugin-uglify')()
|
225 | );
|
226 | }
|
227 |
|
228 | return rollup.rollup({
|
229 | entry: config.entry,
|
230 | plugins,
|
231 | }).then((bundle) => {
|
232 | bundle.write({
|
233 | format: config.libraryFormat || 'umd',
|
234 | moduleName: config.library === true ? 'Lib' : config.library,
|
235 | dest: config.dest,
|
236 | sourceMap: config.sourceMap,
|
237 | });
|
238 | });
|
239 | }
|
240 |
|
241 | module.exports = {
|
242 | getWebpackConfig,
|
243 | getProdConfig,
|
244 | getDevConfig,
|
245 | runWebpack,
|
246 | runDevWebpack,
|
247 | runProdWebpack,
|
248 | runDevServer,
|
249 | runRollup,
|
250 | };
|