1 | 'use strict'
|
2 |
|
3 | const path = require('path')
|
4 | const ConcatSource = require('webpack-sources').ConcatSource
|
5 | const ResolveDependency = require('./dependency/ResolveDependency')
|
6 | const InjectDependency = require('./dependency/InjectDependency')
|
7 | const ReplaceDependency = require('./dependency/ReplaceDependency')
|
8 | const NullFactory = require('webpack/lib/NullFactory')
|
9 | const normalize = require('./utils/normalize')
|
10 | const toPosix = require('./utils/to-posix')
|
11 | const addQuery = require('./utils/add-query')
|
12 | const DefinePlugin = require('webpack/lib/DefinePlugin')
|
13 | const ExternalsPlugin = require('webpack/lib/ExternalsPlugin')
|
14 | const AddModePlugin = require('./resolver/AddModePlugin')
|
15 | const CommonJsRequireDependency = require('webpack/lib/dependencies/CommonJsRequireDependency')
|
16 | const HarmonyImportSideEffectDependency = require('webpack/lib/dependencies/HarmonyImportSideEffectDependency')
|
17 | const RequireHeaderDependency = require('webpack/lib/dependencies/RequireHeaderDependency')
|
18 | const RemovedModuleDependency = require('./dependency/RemovedModuleDependency')
|
19 | const SplitChunksPlugin = require('webpack/lib/optimize/SplitChunksPlugin')
|
20 | const fixRelative = require('./utils/fix-relative')
|
21 | const parseRequest = require('./utils/parse-request')
|
22 | const matchCondition = require('./utils/match-condition')
|
23 |
|
24 | const isProductionLikeMode = options => {
|
25 | return options.mode === 'production' || !options.mode
|
26 | }
|
27 |
|
28 | const outputFilename = '[name].js'
|
29 | const publicPath = '/'
|
30 |
|
31 | function isChunkInPackage (chunkName, packageName) {
|
32 | return (new RegExp(`^${packageName}\\/`)).test(chunkName)
|
33 | }
|
34 |
|
35 | function getPackageCacheGroup (packageName) {
|
36 | if (packageName === 'main') {
|
37 | return {
|
38 | name: 'bundle',
|
39 | minChunks: 2,
|
40 | chunks: 'all'
|
41 | }
|
42 | } else {
|
43 | return {
|
44 | test: (module, chunks) => {
|
45 | return chunks.every((chunk) => {
|
46 | return isChunkInPackage(chunk.name, packageName)
|
47 | })
|
48 | },
|
49 | name: `${packageName}/bundle`,
|
50 | minChunks: 2,
|
51 | minSize: 1000,
|
52 | priority: 100,
|
53 | chunks: 'all'
|
54 | }
|
55 | }
|
56 | }
|
57 |
|
58 | let loaderOptions
|
59 |
|
60 | const externalsMap = {
|
61 | weui: /^weui-miniprogram/
|
62 | }
|
63 |
|
64 | class MpxWebpackPlugin {
|
65 | constructor (options = {}) {
|
66 | options.mode = options.mode || 'wx'
|
67 |
|
68 | options.srcMode = options.srcMode || options.mode
|
69 | if (options.mode !== options.srcMode && options.srcMode !== 'wx') {
|
70 | throw new Error('MpxWebpackPlugin supports srcMode to be "wx" only temporarily!')
|
71 | }
|
72 | if (options.mode === 'web' && options.srcMode !== 'wx') {
|
73 | throw new Error('MpxWebpackPlugin supports mode to be "web" only when srcMode is set to "wx"!')
|
74 | }
|
75 | if (!Array.isArray(options.externalClasses)) {
|
76 | options.externalClasses = ['custom-class', 'i-class']
|
77 | }
|
78 |
|
79 | options.externalClasses = options.externalClasses.map((className) => {
|
80 | return {
|
81 | className,
|
82 | replacement: className.replace(/-(.)/g, (matched, $1) => {
|
83 | return $1.toUpperCase()
|
84 | })
|
85 | }
|
86 | })
|
87 | options.resolveMode = options.resolveMode || 'webpack'
|
88 | options.writeMode = options.writeMode || 'changed'
|
89 | options.autoScopeRules = options.autoScopeRules || {}
|
90 | options.forceDisableInject = options.forceDisableInject || false
|
91 | options.forceDisableProxyCtor = options.forceDisableProxyCtor || false
|
92 | options.transMpxRules = options.transMpxRules || {
|
93 | include: () => true
|
94 | }
|
95 | if (options.autoSplit === undefined) {
|
96 |
|
97 | options.autoSplit = options.mode !== 'web'
|
98 | }
|
99 |
|
100 | options.defs = Object.assign({}, options.defs, {
|
101 | '__mpx_mode__': options.mode,
|
102 | '__mpx_src_mode__': options.srcMode
|
103 | })
|
104 |
|
105 | options.modeRules = options.modeRules || {}
|
106 | options.generateBuildMap = options.generateBuildMap || false
|
107 | options.attributes = options.attributes || []
|
108 | options.externals = (options.externals || []).map((external) => {
|
109 | return externalsMap[external] || external
|
110 | })
|
111 | options.forceUsePageCtor = options.forceUsePageCtor || false
|
112 | this.options = options
|
113 | }
|
114 |
|
115 | static loader (options) {
|
116 | loaderOptions = options
|
117 | return { loader: normalize.lib('loader'), options }
|
118 | }
|
119 |
|
120 | static pluginLoader (options) {
|
121 | return { loader: normalize.lib('plugin-loader'), options }
|
122 | }
|
123 |
|
124 | static wxsPreLoader (options) {
|
125 | return { loader: normalize.lib('wxs/wxs-pre-loader'), options }
|
126 | }
|
127 |
|
128 | static urlLoader (options) {
|
129 | return { loader: normalize.lib('url-loader'), options }
|
130 | }
|
131 |
|
132 | static fileLoader (options) {
|
133 | return { loader: normalize.lib('file-loader'), options }
|
134 | }
|
135 |
|
136 | runModeRules (request) {
|
137 | const { resourcePath, queryObj } = parseRequest(request)
|
138 | if (queryObj.mode) {
|
139 | return request
|
140 | }
|
141 | const mode = this.options.mode
|
142 | const modeRule = this.options.modeRules[mode]
|
143 | if (!modeRule) {
|
144 | return request
|
145 | }
|
146 | if (matchCondition(resourcePath, modeRule)) {
|
147 | return addQuery(request, { mode })
|
148 | }
|
149 | return request
|
150 | }
|
151 |
|
152 | apply (compiler) {
|
153 | if (!compiler.__mpx__) {
|
154 | compiler.__mpx__ = true
|
155 | } else {
|
156 | throw new Error('Multiple MpxWebpackPlugin instances exist in webpack compiler, please check webpack plugins config!')
|
157 | }
|
158 | const warnings = []
|
159 | const errors = []
|
160 | if (this.options.mode !== 'web') {
|
161 |
|
162 | if (compiler.options.output.publicPath && compiler.options.output.publicPath !== publicPath) {
|
163 | warnings.push(`webpack options: MpxWebpackPlugin accept options.output.publicPath to be ${publicPath} only, custom options.output.publicPath will be ignored!`)
|
164 | }
|
165 | compiler.options.output.publicPath = publicPath
|
166 | if (compiler.options.output.filename && compiler.options.output.filename !== outputFilename) {
|
167 | warnings.push(`webpack options: MpxWebpackPlugin accept options.output.filename to be ${outputFilename} only, custom options.output.filename will be ignored!`)
|
168 | }
|
169 | compiler.options.output.filename = compiler.options.output.chunkFilename = outputFilename
|
170 | }
|
171 |
|
172 | if (!compiler.options.node || !compiler.options.node.global) {
|
173 | compiler.options.node = compiler.options.node || {}
|
174 | compiler.options.node.global = true
|
175 | warnings.push(`webpack options: MpxWebpackPlugin strongly depends options.node.globel to be true, custom options.node will be ignored!`)
|
176 | }
|
177 |
|
178 | const resolvePlugin = new AddModePlugin('before-resolve', this.options.mode, 'resolve')
|
179 |
|
180 | if (Array.isArray(compiler.options.resolve.plugins)) {
|
181 | compiler.options.resolve.plugins.push(resolvePlugin)
|
182 | } else {
|
183 | compiler.options.resolve.plugins = [resolvePlugin]
|
184 | }
|
185 |
|
186 | let splitChunksPlugin
|
187 | let splitChunksOptions
|
188 |
|
189 | if (this.options.autoSplit) {
|
190 | compiler.options.optimization.runtimeChunk = {
|
191 | name: 'bundle'
|
192 | }
|
193 | splitChunksOptions = compiler.options.optimization.splitChunks
|
194 | delete compiler.options.optimization.splitChunks
|
195 | splitChunksPlugin = new SplitChunksPlugin(splitChunksOptions)
|
196 | splitChunksPlugin.apply(compiler)
|
197 | }
|
198 |
|
199 |
|
200 | if (this.options.writeMode === 'changed') {
|
201 | const writedFileContentMap = new Map()
|
202 | const originalWriteFile = compiler.outputFileSystem.writeFile
|
203 | compiler.outputFileSystem.writeFile = (filePath, content, callback) => {
|
204 | if (writedFileContentMap.has(filePath) && writedFileContentMap.get(filePath).equals(content)) {
|
205 | return callback()
|
206 | }
|
207 | writedFileContentMap.set(filePath, content)
|
208 | originalWriteFile(filePath, content, callback)
|
209 | }
|
210 | }
|
211 | const defs = this.options.defs
|
212 |
|
213 | const defsOpt = {
|
214 | '__mpx_wxs__': DefinePlugin.runtimeValue(({ module }) => {
|
215 | return JSON.stringify(!!module.wxs)
|
216 | })
|
217 | }
|
218 |
|
219 | Object.keys(defs).forEach((key) => {
|
220 | defsOpt[key] = JSON.stringify(defs[key])
|
221 | })
|
222 |
|
223 |
|
224 | new DefinePlugin(defsOpt).apply(compiler)
|
225 |
|
226 | new ExternalsPlugin('commonjs2', this.options.externals).apply(compiler)
|
227 |
|
228 | compiler.hooks.compilation.tap('MpxWebpackPlugin ', (compilation) => {
|
229 | compilation.hooks.normalModuleLoader.tap('MpxWebpackPlugin', (loaderContext, module) => {
|
230 |
|
231 | if (isProductionLikeMode(compiler.options)) {
|
232 | loaderContext.minimize = true
|
233 | }
|
234 | })
|
235 | })
|
236 |
|
237 | let mpx
|
238 |
|
239 | compiler.hooks.thisCompilation.tap('MpxWebpackPlugin', (compilation, { normalModuleFactory }) => {
|
240 | compilation.warnings = compilation.warnings.concat(warnings)
|
241 | compilation.errors = compilation.errors.concat(errors)
|
242 |
|
243 | const additionalAssets = {}
|
244 | if (!compilation.__mpx__) {
|
245 |
|
246 | mpx = compilation.__mpx__ = {
|
247 |
|
248 | pagesMap: {},
|
249 |
|
250 | pagesEntryMap: {},
|
251 |
|
252 | componentsMap: {
|
253 | main: {}
|
254 | },
|
255 |
|
256 | staticResourceMap: {
|
257 | main: {}
|
258 | },
|
259 | hasApp: false,
|
260 |
|
261 | staticResourceHit: {},
|
262 | loaderOptions,
|
263 | extractedMap: {},
|
264 | extractSeenFile: {},
|
265 | usingComponents: [],
|
266 |
|
267 | vueContentCache: new Map(),
|
268 | currentPackageRoot: '',
|
269 | wxsMap: {},
|
270 | wxsConentMap: {},
|
271 | forceDisableInject: this.options.forceDisableInject,
|
272 | forceUsePageCtor: this.options.forceUsePageCtor,
|
273 | resolveMode: this.options.resolveMode,
|
274 | mode: this.options.mode,
|
275 | srcMode: this.options.srcMode,
|
276 | globalMpxAttrsFilter: this.options.globalMpxAttrsFilter,
|
277 | externalClasses: this.options.externalClasses,
|
278 | projectRoot: this.options.projectRoot,
|
279 | autoScopeRules: this.options.autoScopeRules,
|
280 |
|
281 | nativeOptions: Object.assign({
|
282 | cssLangs: ['css', 'less', 'stylus', 'scss', 'sass']
|
283 | }, this.options.nativeOptions),
|
284 | defs: this.options.defs,
|
285 | i18n: this.options.i18n,
|
286 | appTitle: 'Mpx homepage',
|
287 | attributes: this.options.attributes,
|
288 | externals: this.options.externals,
|
289 | extract: (content, file, index, sideEffects) => {
|
290 | additionalAssets[file] = additionalAssets[file] || []
|
291 | if (!additionalAssets[file][index]) {
|
292 | additionalAssets[file][index] = content
|
293 | }
|
294 | sideEffects && sideEffects(additionalAssets)
|
295 | },
|
296 |
|
297 |
|
298 |
|
299 |
|
300 |
|
301 |
|
302 | getPackageInfo (resource, { outputPath, isStatic, error }) {
|
303 | let packageRoot = ''
|
304 | let packageName = 'main'
|
305 | const currentPackageRoot = mpx.currentPackageRoot
|
306 | const currentPackageName = currentPackageRoot || 'main'
|
307 | const { resourcePath, queryObj } = parseRequest(resource)
|
308 | const resourceMap = isStatic ? mpx.staticResourceMap : mpx.componentsMap
|
309 |
|
310 | if (!resourceMap.main[resourcePath]) {
|
311 | if (queryObj.packageName) {
|
312 | packageName = queryObj.packageName
|
313 | packageRoot = packageName === 'main' ? '' : packageName
|
314 | if (packageName !== currentPackageName && packageName !== 'main') {
|
315 | error && error(new Error(`根据小程序分包资源引用规则,资源只支持声明为当前分包或者主包,否则可能会导致资源无法引用的问题,当前资源的当前分包为${currentPackageName},资源查询字符串声明的分包为${packageName},请检查!`))
|
316 | }
|
317 | } else if (currentPackageRoot) {
|
318 | packageName = packageRoot = currentPackageRoot
|
319 | }
|
320 | }
|
321 |
|
322 | outputPath = toPosix(path.join(packageRoot, outputPath))
|
323 |
|
324 | const currentResourceMap = resourceMap[currentPackageName]
|
325 | const actualResourceMap = resourceMap[packageName]
|
326 |
|
327 | let alreadyOutputed = false
|
328 |
|
329 | if (actualResourceMap[resourcePath]) {
|
330 | outputPath = actualResourceMap[resourcePath]
|
331 | alreadyOutputed = true
|
332 | }
|
333 |
|
334 | currentResourceMap[resourcePath] = actualResourceMap[resourcePath] = outputPath
|
335 |
|
336 | if (isStatic && packageName !== 'main' && !mpx.staticResourceHit[resourcePath]) {
|
337 | mpx.staticResourceHit[resourcePath] = packageName
|
338 | }
|
339 |
|
340 | return {
|
341 | packageName,
|
342 | packageRoot,
|
343 | resourcePath,
|
344 | queryObj,
|
345 | outputPath,
|
346 | alreadyOutputed
|
347 | }
|
348 | }
|
349 | }
|
350 | }
|
351 |
|
352 | if (splitChunksPlugin) {
|
353 |
|
354 | compilation.hooks.finishModules.tap('MpxWebpackPlugin', (modules) => {
|
355 | let needInit = false
|
356 | Object.keys(mpx.componentsMap).forEach((packageName) => {
|
357 | if (!splitChunksOptions.cacheGroups.hasOwnProperty(packageName)) {
|
358 | needInit = true
|
359 | splitChunksOptions.cacheGroups[packageName] = getPackageCacheGroup(packageName)
|
360 | }
|
361 | })
|
362 | if (needInit) {
|
363 | splitChunksPlugin.options = SplitChunksPlugin.normalizeOptions(splitChunksOptions)
|
364 | }
|
365 | })
|
366 | }
|
367 |
|
368 | compilation.hooks.optimizeModules.tap('MpxWebpackPlugin', (modules) => {
|
369 | modules.forEach((module) => {
|
370 | if (module.needRemove) {
|
371 | let removed = false
|
372 | module.reasons.forEach((reason) => {
|
373 | if (reason.module) {
|
374 | if (reason.dependency instanceof HarmonyImportSideEffectDependency) {
|
375 | reason.module.removeDependency(reason.dependency)
|
376 | reason.module.addDependency(new RemovedModuleDependency(reason.dependency.request))
|
377 | removed = true
|
378 | } else if (reason.dependency instanceof CommonJsRequireDependency && reason.dependency.loc.range) {
|
379 | let index = reason.module.dependencies.indexOf(reason.dependency)
|
380 | if (index > -1 && reason.module.dependencies[index + 1] instanceof RequireHeaderDependency) {
|
381 | reason.module.dependencies.splice(index, 2)
|
382 | reason.module.addDependency(new RemovedModuleDependency(reason.dependency.request, reason.dependency.loc.range))
|
383 | removed = true
|
384 | }
|
385 | }
|
386 | }
|
387 | })
|
388 | if (removed) {
|
389 | module.chunksIterable.forEach((chunk) => {
|
390 | module.removeChunk(chunk)
|
391 | })
|
392 | module.disconnect()
|
393 | }
|
394 | }
|
395 | })
|
396 | })
|
397 |
|
398 | compilation.hooks.additionalAssets.tapAsync('MpxWebpackPlugin', (callback) => {
|
399 | for (let file in additionalAssets) {
|
400 | let content = new ConcatSource()
|
401 | if (additionalAssets[file].prefix) {
|
402 | additionalAssets[file].prefix.forEach((item) => {
|
403 | content.add(item)
|
404 | })
|
405 | }
|
406 | additionalAssets[file].forEach((item) => {
|
407 | content.add(item)
|
408 | })
|
409 | compilation.assets[file] = content
|
410 | }
|
411 | callback()
|
412 | })
|
413 |
|
414 | compilation.dependencyFactories.set(ResolveDependency, new NullFactory())
|
415 | compilation.dependencyTemplates.set(ResolveDependency, new ResolveDependency.Template())
|
416 |
|
417 | compilation.dependencyFactories.set(InjectDependency, new NullFactory())
|
418 | compilation.dependencyTemplates.set(InjectDependency, new InjectDependency.Template())
|
419 |
|
420 | compilation.dependencyFactories.set(ReplaceDependency, new NullFactory())
|
421 | compilation.dependencyTemplates.set(ReplaceDependency, new ReplaceDependency.Template())
|
422 |
|
423 | compilation.dependencyFactories.set(RemovedModuleDependency, normalModuleFactory)
|
424 | compilation.dependencyTemplates.set(RemovedModuleDependency, new RemovedModuleDependency.Template())
|
425 |
|
426 | normalModuleFactory.hooks.parser.for('javascript/auto').tap('MpxWebpackPlugin', (parser) => {
|
427 |
|
428 | parser.hooks.call.for('require').tap({ name: 'MpxWebpackPlugin', stage: -100 }, (expr) => {
|
429 | expr.loc.range = expr.range
|
430 | })
|
431 |
|
432 | parser.hooks.call.for('__mpx_resolve_path__').tap('MpxWebpackPlugin', (expr) => {
|
433 | if (expr.arguments[0]) {
|
434 | const resource = expr.arguments[0].value
|
435 | const { queryObj } = parseRequest(resource)
|
436 | const packageName = queryObj.packageName
|
437 | const pagesMap = mpx.pagesMap
|
438 | const componentsMap = mpx.componentsMap
|
439 | const staticResourceMap = mpx.staticResourceMap
|
440 | const publicPath = mpx.mode === 'web' ? '' : compilation.outputOptions.publicPath
|
441 | const range = expr.range
|
442 | const issuerResource = parser.state.module.issuer.resource
|
443 | const dep = new ResolveDependency(resource, packageName, pagesMap, componentsMap, staticResourceMap, publicPath, range, issuerResource)
|
444 | parser.state.current.addDependency(dep)
|
445 | return true
|
446 | }
|
447 | })
|
448 |
|
449 | const transHandler = (expr) => {
|
450 | const module = parser.state.module
|
451 | const current = parser.state.current
|
452 | const { queryObj, resourcePath } = parseRequest(module.resource)
|
453 | const localSrcMode = queryObj.mode
|
454 | const globalSrcMode = this.options.srcMode
|
455 | const srcMode = localSrcMode || globalSrcMode
|
456 | const mode = this.options.mode
|
457 |
|
458 | let target
|
459 |
|
460 | if (expr.type === 'Identifier') {
|
461 | target = expr
|
462 | } else if (expr.type === 'MemberExpression') {
|
463 | target = expr.object
|
464 | }
|
465 | if (!matchCondition(resourcePath, this.options.transMpxRules) || resourcePath.indexOf('@mpxjs') !== -1 || !target || mode === srcMode) {
|
466 | return
|
467 | }
|
468 |
|
469 | const type = target.name
|
470 |
|
471 | const name = type === 'wx' ? 'mpx' : 'createFactory'
|
472 | const replaceContent = type === 'wx' ? 'mpx' : `${name}(${JSON.stringify(type)})`
|
473 |
|
474 | const dep = new ReplaceDependency(replaceContent, target.range)
|
475 | current.addDependency(dep)
|
476 |
|
477 | let needInject = true
|
478 | for (let v of module.variables) {
|
479 | if (v.name === name) {
|
480 | needInject = false
|
481 | break
|
482 | }
|
483 | }
|
484 | if (needInject) {
|
485 | const expression = `require(${JSON.stringify(`@mpxjs/core/src/runtime/${name}`)})`
|
486 | const deps = []
|
487 | parser.parse(expression, {
|
488 | current: {
|
489 | addDependency: dep => {
|
490 | dep.userRequest = name
|
491 | deps.push(dep)
|
492 | }
|
493 | },
|
494 | module
|
495 | })
|
496 | module.addVariable(name, expression, deps)
|
497 | }
|
498 | }
|
499 |
|
500 |
|
501 | parser.hooks.evaluate.for('CallExpression').tap('MpxWebpackPlugin', (expr) => {
|
502 | if (/core-js/.test(parser.state.module.resource)) {
|
503 | const current = parser.state.current
|
504 | const arg0 = expr.arguments[0]
|
505 | const callee = expr.callee
|
506 | if (arg0 && arg0.value === 'return this' && callee.name === 'Function') {
|
507 | current.addDependency(new InjectDependency({
|
508 | content: '(function() { return this })() || ',
|
509 | index: expr.range[0]
|
510 | }))
|
511 | }
|
512 | }
|
513 | })
|
514 |
|
515 | if (this.options.srcMode !== this.options.mode) {
|
516 |
|
517 | parser.hooks.expression.for('wx').tap('MpxWebpackPlugin', transHandler)
|
518 |
|
519 |
|
520 |
|
521 |
|
522 |
|
523 |
|
524 |
|
525 |
|
526 |
|
527 |
|
528 |
|
529 | if (!this.options.forceDisableProxyCtor) {
|
530 | parser.hooks.call.for('Page').tap('MpxWebpackPlugin', (expr) => {
|
531 | transHandler(expr.callee)
|
532 | })
|
533 | parser.hooks.call.for('Component').tap('MpxWebpackPlugin', (expr) => {
|
534 | transHandler(expr.callee)
|
535 | })
|
536 | parser.hooks.call.for('App').tap('MpxWebpackPlugin', (expr) => {
|
537 | transHandler(expr.callee)
|
538 | })
|
539 | if (this.options.mode === 'ali') {
|
540 |
|
541 | parser.hooks.call.for('Behavior').tap('MpxWebpackPlugin', (expr) => {
|
542 | transHandler(expr.callee)
|
543 | })
|
544 | }
|
545 | }
|
546 | }
|
547 |
|
548 | const apiBlackListMap = [
|
549 | 'createApp',
|
550 | 'createPage',
|
551 | 'createComponent',
|
552 | 'createStore',
|
553 | 'toPureObject',
|
554 | 'mixin: injectMixins',
|
555 | 'injectMixins',
|
556 | 'observable',
|
557 | 'extendObservable',
|
558 | 'watch',
|
559 | 'use',
|
560 | 'set',
|
561 | 'get',
|
562 | 'remove',
|
563 | 'setConvertRule',
|
564 | 'createAction'
|
565 | ].reduce((map, api) => {
|
566 | map[api] = true
|
567 | return map
|
568 | }, {})
|
569 |
|
570 | const handler = (expr) => {
|
571 | const callee = expr.callee
|
572 | const args = expr.arguments
|
573 | const name = callee.object.name
|
574 | const { queryObj, resourcePath } = parseRequest(parser.state.module.resource)
|
575 |
|
576 | if (apiBlackListMap[callee.property.name || callee.property.value] || (name !== 'mpx' && name !== 'wx') || (name === 'wx' && !matchCondition(resourcePath, this.options.transMpxRules))) {
|
577 | return
|
578 | }
|
579 |
|
580 | const localSrcMode = queryObj.mode
|
581 | const globalSrcMode = this.options.srcMode
|
582 | const srcMode = localSrcMode || globalSrcMode
|
583 | const srcModeString = `__mpx_src_mode_${srcMode}__`
|
584 | const dep = new InjectDependency({
|
585 | content: args.length
|
586 | ? `, ${JSON.stringify(srcModeString)}`
|
587 | : JSON.stringify(srcModeString),
|
588 | index: expr.end - 1
|
589 | })
|
590 | parser.state.current.addDependency(dep)
|
591 | }
|
592 |
|
593 | if (this.options.srcMode !== this.options.mode) {
|
594 | parser.hooks.callAnyMember.for('imported var').tap('MpxWebpackPlugin', handler)
|
595 | parser.hooks.callAnyMember.for('mpx').tap('MpxWebpackPlugin', handler)
|
596 | parser.hooks.callAnyMember.for('wx').tap('MpxWebpackPlugin', handler)
|
597 | }
|
598 | })
|
599 | })
|
600 |
|
601 | compiler.hooks.normalModuleFactory.tap('MpxWebpackPlugin', (normalModuleFactory) => {
|
602 |
|
603 | normalModuleFactory.hooks.beforeResolve.tapAsync('MpxWebpackPlugin', (data, callback) => {
|
604 | let request = data.request
|
605 | let { queryObj, resource } = parseRequest(request)
|
606 | if (queryObj.resolve) {
|
607 |
|
608 | const pathLoader = normalize.lib('path-loader')
|
609 | const packageName = mpx.currentPackageRoot || 'main'
|
610 | resource = addQuery(resource, {
|
611 | packageName
|
612 | })
|
613 | data.request = `!!${pathLoader}!${resource}`
|
614 | } else if (queryObj.wxsModule) {
|
615 | const wxsPreLoader = normalize.lib('wxs/wxs-pre-loader')
|
616 | if (!/wxs-loader/.test(request)) {
|
617 | data.request = `!!${wxsPreLoader}!${resource}`
|
618 | }
|
619 | }
|
620 | callback(null, data)
|
621 | })
|
622 |
|
623 |
|
624 | normalModuleFactory.hooks.afterResolve.tapAsync('MpxWebpackPlugin', (data, callback) => {
|
625 | const isFromMpx = /\.(mpx|vue)/.test(data.resource)
|
626 | if (data.loaders && isFromMpx) {
|
627 | data.loaders.forEach((loader) => {
|
628 | if (/ts-loader/.test(loader.loader)) {
|
629 | loader.options = Object.assign({}, { appendTsSuffixTo: [/\.(mpx|vue)$/] })
|
630 | }
|
631 | })
|
632 | }
|
633 |
|
634 | data.resource = this.runModeRules(data.resource)
|
635 |
|
636 | if (mpx.currentPackageRoot) {
|
637 | const resourcePath = parseRequest(data.resource).resourcePath
|
638 |
|
639 | const staticResourceHit = mpx.staticResourceHit
|
640 | const packageName = mpx.currentPackageRoot || 'main'
|
641 |
|
642 | let needAddQuery = false
|
643 |
|
644 | if (staticResourceHit[resourcePath] && staticResourceHit[resourcePath] !== packageName) {
|
645 | needAddQuery = true
|
646 | }
|
647 |
|
648 | if (needAddQuery) {
|
649 |
|
650 | data.request = addQuery(data.request, {
|
651 | packageName
|
652 | })
|
653 | }
|
654 | }
|
655 | callback(null, data)
|
656 | })
|
657 | })
|
658 |
|
659 | compiler.hooks.emit.tapAsync('MpxWebpackPlugin', (compilation, callback) => {
|
660 | if (this.options.mode === 'web') return callback()
|
661 | const jsonpFunction = compilation.outputOptions.jsonpFunction
|
662 |
|
663 | function getTargetFile (file) {
|
664 | let targetFile = file
|
665 | const queryStringIdx = targetFile.indexOf('?')
|
666 | if (queryStringIdx >= 0) {
|
667 | targetFile = targetFile.substr(0, queryStringIdx)
|
668 | }
|
669 | return targetFile
|
670 | }
|
671 |
|
672 | const processedChunk = new Set()
|
673 | const rootName = compilation._preparedEntrypoints[0].name
|
674 |
|
675 | function processChunk (chunk, isRuntime, relativeChunks) {
|
676 | if (!chunk.files[0] || processedChunk.has(chunk)) {
|
677 | return
|
678 | }
|
679 |
|
680 | let originalSource = compilation.assets[chunk.files[0]]
|
681 | const source = new ConcatSource()
|
682 | source.add('\nvar window = window || {};\n\n')
|
683 |
|
684 | relativeChunks.forEach((relativeChunk, index) => {
|
685 | if (!relativeChunk.files[0]) return
|
686 | let chunkPath = getTargetFile(chunk.files[0])
|
687 | let relativePath = getTargetFile(relativeChunk.files[0])
|
688 | relativePath = path.relative(path.dirname(chunkPath), relativePath)
|
689 | relativePath = fixRelative(relativePath, mpx.mode)
|
690 | relativePath = toPosix(relativePath)
|
691 | if (index === 0) {
|
692 |
|
693 |
|
694 | if (mpx.mode === 'ali') {
|
695 | if (chunk.name === rootName) {
|
696 |
|
697 | source.add('// process ali subpackages runtime in root chunk\n' +
|
698 | 'var context = (function() { return this })() || Function("return this")();\n\n')
|
699 | source.add(`context[${JSON.stringify(jsonpFunction)}] = window[${JSON.stringify(jsonpFunction)}] = require("${relativePath}");\n`)
|
700 | } else {
|
701 |
|
702 | source.add('// process ali subpackages runtime in other chunk\n' +
|
703 | 'var context = (function() { return this })() || Function("return this")();\n\n')
|
704 | source.add(`window[${JSON.stringify(jsonpFunction)}] = context[${JSON.stringify(jsonpFunction)}];\n`)
|
705 | }
|
706 | } else {
|
707 | source.add(`window[${JSON.stringify(jsonpFunction)}] = require("${relativePath}");\n`)
|
708 | }
|
709 | } else {
|
710 | source.add(`require("${relativePath}");\n`)
|
711 | }
|
712 | })
|
713 |
|
714 | if (isRuntime) {
|
715 | source.add('var context = (function() { return this })() || Function("return this")();\n')
|
716 | source.add(`
|
717 | // Fix babel runtime in some quirky environment like ali & qq dev.
|
718 | if(!context.console) {
|
719 | try {
|
720 | context.console = console;
|
721 | context.setInterval = setInterval;
|
722 | context.setTimeout = setTimeout;
|
723 | context.JSON = JSON;
|
724 | context.Math = Math;
|
725 | context.RegExp = RegExp;
|
726 | context.Infinity = Infinity;
|
727 | context.isFinite = isFinite;
|
728 | context.parseFloat = parseFloat;
|
729 | context.parseInt = parseInt;
|
730 | context.Promise = Promise;
|
731 | context.WeakMap = WeakMap;
|
732 | context.Reflect = Reflect;
|
733 | context.RangeError = RangeError;
|
734 | context.TypeError = TypeError;
|
735 | context.Uint8Array = Uint8Array;
|
736 | context.DataView = DataView;
|
737 | context.ArrayBuffer = ArrayBuffer;
|
738 | context.Symbol = Symbol;
|
739 | } catch(e){
|
740 | }
|
741 | }
|
742 | \n`)
|
743 | if (mpx.mode === 'swan') {
|
744 | source.add('// swan runtime fix\n' +
|
745 | 'if (!context.navigator) {\n' +
|
746 | ' context.navigator = {};\n' +
|
747 | '}\n' +
|
748 | 'Object.defineProperty(context.navigator, "standalone",{\n' +
|
749 | ' configurable: true,' +
|
750 | ' enumerable: true,' +
|
751 | ' get () {\n' +
|
752 | ' return true;\n' +
|
753 | ' }\n' +
|
754 | '});\n\n')
|
755 | }
|
756 | source.add(originalSource)
|
757 | source.add(`\nmodule.exports = window[${JSON.stringify(jsonpFunction)}];\n`)
|
758 | } else {
|
759 | if (mpx.pluginMain === chunk.name) {
|
760 | source.add('module.exports =\n')
|
761 | }
|
762 | source.add(originalSource)
|
763 | }
|
764 |
|
765 | compilation.assets[chunk.files[0]] = source
|
766 | processedChunk.add(chunk)
|
767 | }
|
768 |
|
769 | compilation.chunkGroups.forEach((chunkGroup) => {
|
770 | if (!chunkGroup.isInitial()) {
|
771 | return
|
772 | }
|
773 |
|
774 | let runtimeChunk, entryChunk
|
775 | let middleChunks = []
|
776 |
|
777 | let chunksLength = chunkGroup.chunks.length
|
778 |
|
779 | chunkGroup.chunks.forEach((chunk, index) => {
|
780 | if (index === 0) {
|
781 | runtimeChunk = chunk
|
782 | } else if (index === chunksLength - 1) {
|
783 | entryChunk = chunk
|
784 | } else {
|
785 | middleChunks.push(chunk)
|
786 | }
|
787 | })
|
788 |
|
789 | if (runtimeChunk) {
|
790 | processChunk(runtimeChunk, true, [])
|
791 | if (middleChunks.length) {
|
792 | middleChunks.forEach((middleChunk) => {
|
793 | processChunk(middleChunk, false, [runtimeChunk])
|
794 | })
|
795 | }
|
796 | if (entryChunk) {
|
797 | middleChunks.unshift(runtimeChunk)
|
798 | processChunk(entryChunk, false, middleChunks)
|
799 | }
|
800 | }
|
801 | })
|
802 |
|
803 | if (this.options.generateBuildMap) {
|
804 | const pagesMap = compilation.__mpx__.pagesMap
|
805 | const componentsPackageMap = compilation.__mpx__.componentsMap
|
806 | const componentsMap = Object.keys(componentsPackageMap).map(item => componentsPackageMap[item]).reduce((pre, cur) => { return { ...pre, ...cur } }, {})
|
807 | const outputMap = JSON.stringify({ ...pagesMap, ...componentsMap })
|
808 | compilation.assets['../outputMap.json'] = {
|
809 | source: () => {
|
810 | return outputMap
|
811 | },
|
812 | size: () => {
|
813 | return Buffer.byteLength(outputMap, 'utf8')
|
814 | }
|
815 | }
|
816 | }
|
817 |
|
818 | callback()
|
819 | })
|
820 | }
|
821 | }
|
822 |
|
823 | module.exports = MpxWebpackPlugin
|