1 | 'use strict'
|
2 |
|
3 | const webpack = require('webpack')
|
4 |
|
5 | const PnpWebpackPlugin = require('pnp-webpack-plugin')
|
6 | const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin')
|
7 | const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin')
|
8 | const tsFormatter = require('react-dev-utils/typescriptFormatter')
|
9 | const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin')
|
10 | const { resolve } = require('@mara/devkit')
|
11 | const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent')
|
12 | const VueLoaderPlugin = require('vue-loader/lib/plugin')
|
13 |
|
14 | const getStyleLoaders = require('./loaders/style-loader')
|
15 | const { SinaHybridPlugin, getCommonPkgConf } = require('../lib/hybrid')
|
16 | const config = require('../config')
|
17 | const { GLOB, VIEWS_DIR, TARGET } = require('../config/const')
|
18 | const { babelLoader } = require('./loaders/babel-loader')
|
19 | const {
|
20 | vueLoaderOptions,
|
21 | vueLoaderCacheConfig
|
22 | } = require('./loaders/vue-loader.conf')
|
23 | const { getEntries, isInstalled } = require('../lib/utils')
|
24 | const paths = config.paths
|
25 |
|
26 | module.exports = function(
|
27 | { entry, buildEnv, target, publicPath, version },
|
28 | cmd
|
29 | ) {
|
30 | const isDev = process.env.NODE_ENV === 'development'
|
31 | const isProd = process.env.NODE_ENV === 'production'
|
32 | const isLib = cmd === 'lib'
|
33 | const isDevOrBuildCmd = cmd === 'dev' || cmd === 'build'
|
34 | const isHybridMode = target === TARGET.APP
|
35 | const assetsDir = isLib ? '' : 'static/'
|
36 | const entryGlob = `${VIEWS_DIR}/${entry}/${GLOB.MAIN_ENTRY}`
|
37 | const useTypeScript = config.useTypeScript
|
38 | const { vueRuntimeOnly } = config.compiler
|
39 | const tsCompilerOptions = {
|
40 |
|
41 | module: 'esnext',
|
42 |
|
43 | target: 'esnext',
|
44 | moduleResolution: 'node',
|
45 | resolveJsonModule: true,
|
46 | noEmit: true,
|
47 |
|
48 |
|
49 | jsx: 'preserve'
|
50 | }
|
51 |
|
52 |
|
53 | const extensions = [
|
54 | '.web.mjs',
|
55 | '.mjs',
|
56 | '.web.js',
|
57 | '.js',
|
58 | '.web.ts',
|
59 | '.ts',
|
60 | '.web.tsx',
|
61 | '.tsx',
|
62 | '.web.jsx',
|
63 | '.jsx',
|
64 | '.vue',
|
65 | '.json'
|
66 | ]
|
67 |
|
68 |
|
69 | getStyleLoaders.publicPath = publicPath
|
70 | getStyleLoaders.isLib = isLib
|
71 |
|
72 | let externals = []
|
73 | let entryConf = {}
|
74 | let commonPkgPath = ''
|
75 |
|
76 | const useCommonPkg =
|
77 | isDevOrBuildCmd &&
|
78 | isHybridMode &&
|
79 | config.compiler.splitSNC &&
|
80 | isInstalled('@mfelibs/hybridcontainer')
|
81 |
|
82 |
|
83 | if (useCommonPkg) {
|
84 | const sncConf = getCommonPkgConf(entryGlob)
|
85 |
|
86 |
|
87 | entryConf = sncConf.entry
|
88 | commonPkgPath = sncConf.commonPkgPath
|
89 | externals.push(...sncConf.externals)
|
90 | } else {
|
91 | entryConf = getEntries(entryGlob, require.resolve('../lib/polyfills'))
|
92 | }
|
93 |
|
94 | const baseConfig = {
|
95 |
|
96 | entry: entryConf,
|
97 | output: {
|
98 | path: paths.dist,
|
99 | filename: 'static/js/[name].js',
|
100 | chunkFilename: 'static/js/[name].chunk.js',
|
101 |
|
102 | futureEmitAssets: true
|
103 | },
|
104 | resolve: {
|
105 |
|
106 | symlinks: false,
|
107 |
|
108 | extensions: extensions.filter(
|
109 | ext => useTypeScript || !ext.includes('ts')
|
110 | ),
|
111 |
|
112 |
|
113 | mainFields: ['source', 'browser', 'module', 'main'],
|
114 | modules: ['node_modules', paths.nodeModules],
|
115 | alias: {
|
116 |
|
117 |
|
118 |
|
119 | '~': paths.src,
|
120 |
|
121 | 'react-native$': 'react-native-web',
|
122 | vue$: `vue/dist/vue${vueRuntimeOnly ? '.runtime' : ''}.esm.js`
|
123 | },
|
124 | plugins: [
|
125 |
|
126 |
|
127 | PnpWebpackPlugin,
|
128 |
|
129 |
|
130 |
|
131 |
|
132 |
|
133 | new ModuleScopePlugin(paths.src, [paths.packageJson])
|
134 | ]
|
135 | },
|
136 | resolveLoader: {
|
137 | plugins: [
|
138 |
|
139 |
|
140 | PnpWebpackPlugin.moduleLoader(module)
|
141 | ]
|
142 | },
|
143 | module: {
|
144 |
|
145 |
|
146 |
|
147 | strictExportPresence: false,
|
148 | noParse: /^(vue|vue-router|vuex|vuex-router-sync)$/,
|
149 | rules: [
|
150 |
|
151 |
|
152 |
|
153 | {
|
154 | test: /\.css$/,
|
155 | exclude: /\.module\.css$/,
|
156 | oneOf: getStyleLoaders()
|
157 | },
|
158 | {
|
159 | test: /\.module\.css$/,
|
160 | oneOf: getStyleLoaders({
|
161 | modules: true,
|
162 | getLocalIdent: getCSSModuleLocalIdent
|
163 | })
|
164 | },
|
165 | {
|
166 | test: /\.less$/,
|
167 | oneOf: getStyleLoaders('less-loader')
|
168 | },
|
169 | {
|
170 | test: /\.(scss|sass)$/,
|
171 | oneOf: getStyleLoaders('sass-loader')
|
172 | },
|
173 | {
|
174 | test: /\.styl(us)?$/,
|
175 | oneOf: getStyleLoaders(
|
176 | {
|
177 | preferPathResolver: 'webpack'
|
178 | },
|
179 | 'stylus-loader'
|
180 | )
|
181 | },
|
182 | {
|
183 | test: /\.ejs$/,
|
184 | loader: require.resolve('marauder-ejs-loader')
|
185 | },
|
186 | {
|
187 | test: /\.art$/,
|
188 | loader: 'art-template-loader'
|
189 | },
|
190 | {
|
191 | test: /\.vue$/,
|
192 |
|
193 | use: [
|
194 | {
|
195 | loader: require.resolve('cache-loader'),
|
196 | options: vueLoaderCacheConfig
|
197 | },
|
198 | {
|
199 | loader: require.resolve('vue-loader'),
|
200 | options: vueLoaderOptions
|
201 | }
|
202 | ]
|
203 | },
|
204 | {
|
205 |
|
206 | oneOf: babelLoader(isProd, useTypeScript)
|
207 | },
|
208 | {
|
209 | test: /\.(bmp|png|jpe?g|gif|webp)(\?.*)?$/,
|
210 | loader: require.resolve('url-loader'),
|
211 | options: {
|
212 | limit: 1024 * 4,
|
213 | tinifyKeys: config.tinifyKeys,
|
214 | minify: isProd,
|
215 | fallback: require.resolve('@mara/image-loader'),
|
216 | name: `${assetsDir}img/[name].[contenthash:8].[ext]`
|
217 | }
|
218 | },
|
219 |
|
220 |
|
221 | {
|
222 | test: /\.(svg)(\?.*)?$/,
|
223 | loader: require.resolve('@mara/image-loader'),
|
224 | options: {
|
225 | minify: isProd,
|
226 | name: `${assetsDir}img/[name].[contenthash:8].[ext]`
|
227 | }
|
228 | },
|
229 | {
|
230 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
|
231 | loader: require.resolve('file-loader'),
|
232 | options: {
|
233 |
|
234 | name: `${assetsDir}fonts/[name].[hash:8].[ext]`
|
235 | }
|
236 | },
|
237 | {
|
238 | test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
|
239 | loader: require.resolve('file-loader'),
|
240 | options: {
|
241 | name: `${assetsDir}media/[name].[contenthash:8].[ext]`
|
242 | }
|
243 | },
|
244 | {
|
245 | test: /\.txt$/,
|
246 | loader: 'raw-loader'
|
247 | }
|
248 | ]
|
249 | },
|
250 | plugins: [
|
251 |
|
252 |
|
253 | new ModuleNotFoundPlugin(paths.app),
|
254 |
|
255 |
|
256 |
|
257 |
|
258 |
|
259 | new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
|
260 | !isLib && new webpack.DefinePlugin(buildEnv.stringified),
|
261 | new VueLoaderPlugin(),
|
262 |
|
263 | useTypeScript &&
|
264 | new ForkTsCheckerWebpackPlugin({
|
265 | typescript: resolve.sync('typescript', {
|
266 | basedir: paths.nodeModules
|
267 | }),
|
268 | vue: true,
|
269 | async: isDev,
|
270 |
|
271 |
|
272 |
|
273 | checkSyntacticErrors: true,
|
274 | tsconfig: paths.tsConfig,
|
275 | compilerOptions: tsCompilerOptions,
|
276 | reportFiles: [
|
277 |
|
278 | 'src/**',
|
279 | '!**/__tests__/**',
|
280 | '!**/?(*.)(spec|test).*',
|
281 | '!**/src/setupProxy.*',
|
282 | '!**/src/setupTests.*'
|
283 | ],
|
284 | watch: paths.src,
|
285 | silent: true,
|
286 | formatter: isProd ? tsFormatter : undefined
|
287 | })
|
288 | ].filter(Boolean),
|
289 |
|
290 |
|
291 | node: {
|
292 |
|
293 |
|
294 | setImmediate: false,
|
295 | module: 'empty',
|
296 | dgram: 'empty',
|
297 | dns: 'mock',
|
298 | fs: 'empty',
|
299 | http2: 'empty',
|
300 | net: 'empty',
|
301 | tls: 'empty',
|
302 | child_process: 'empty'
|
303 | },
|
304 |
|
305 | performance: {
|
306 | hints: false
|
307 | },
|
308 | externals: externals
|
309 | }
|
310 |
|
311 | if (isDevOrBuildCmd) {
|
312 |
|
313 |
|
314 |
|
315 | baseConfig.optimization = {
|
316 | splitChunks: {
|
317 | chunks(chunk) {
|
318 | const isServantOrSNC = /\.servant|__UNI_SNC__/.test(chunk.name)
|
319 |
|
320 |
|
321 |
|
322 | if (isServantOrSNC || (isHybridMode && isProd)) return false
|
323 |
|
324 | return !!config.compiler.splitChunks
|
325 | },
|
326 |
|
327 | automaticNameDelimiter: '_',
|
328 | name: true
|
329 | }
|
330 | }
|
331 |
|
332 | if (isHybridMode) {
|
333 |
|
334 | baseConfig.plugins.push(
|
335 | new SinaHybridPlugin(require('html-webpack-plugin'), {
|
336 | entry: entry,
|
337 | version: version,
|
338 | publicPath: publicPath,
|
339 | useCommonPkg: useCommonPkg,
|
340 | commonPkgPath: commonPkgPath
|
341 | })
|
342 | )
|
343 | }
|
344 | }
|
345 |
|
346 | if (!isLib && isInstalled('@mara/plugin-extract-comp-meta')) {
|
347 | const VueMetaPlugin = require('@mara/plugin-extract-comp-meta/lib/vue-meta-plugin')
|
348 |
|
349 | baseConfig.plugins.push(new VueMetaPlugin({ entry }))
|
350 | }
|
351 |
|
352 | return baseConfig
|
353 | }
|