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