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