UNPKG

5.05 kBJavaScriptView Raw
1const path = require('path')
2const async = require('async')
3const hash = require('hash-sum')
4const SingleEntryPlugin = require('webpack/lib/SingleEntryPlugin')
5const parseRequest = require('./utils/parse-request')
6const toPosix = require('./utils/to-posix')
7
8// webpack4中.json文件会走json parser,抽取内容的占位内容必须为合法json,否则会在parse阶段报错
9const defaultResultSource = '{}'
10
11module.exports = function (source) {
12 // 该loader中会在每次编译中动态添加entry,不能缓存,否则watch不好使
13 this.cacheable(false)
14
15 const nativeCallback = this.async()
16 const mpx = this._compilation.__mpx__
17
18 if (!mpx) {
19 return nativeCallback(null, source)
20 }
21
22 const packageName = 'main'
23 const pagesMap = mpx.pagesMap
24 const componentsMap = mpx.componentsMap[packageName]
25 const extract = mpx.extract
26 const resourceName = this._compilation._preparedEntrypoints[0].name
27 this._compilation._preparedEntrypoints.pop()
28
29 let entryDeps = new Set()
30
31 let cacheCallback
32
33 const checkEntryDeps = (callback) => {
34 callback = callback || cacheCallback
35 if (callback && entryDeps.size === 0) {
36 callback()
37 } else {
38 cacheCallback = callback
39 }
40 }
41
42 const addEntrySafely = (resource, name, callback) => {
43 const dep = SingleEntryPlugin.createDependency(resource, name)
44 entryDeps.add(dep)
45 this._compilation.addEntry(this._compiler.context, dep, name, (err, module) => {
46 entryDeps.delete(dep)
47 checkEntryDeps()
48 callback(err, module)
49 })
50 }
51
52 // 初次处理json
53 const callback = (err) => {
54 checkEntryDeps(() => {
55 if (err) return nativeCallback(err)
56 extract(JSON.stringify(pluginEntry), resourceName + '.json', 0)
57 nativeCallback(null, defaultResultSource)
58 })
59 }
60
61 let pluginEntry
62 try {
63 pluginEntry = JSON.parse(source)
64 } catch (err) {
65 return callback(err)
66 }
67
68 function getName (raw) {
69 const match = /^(.*?)(\.[^.]*)?$/.exec(raw)
70 return match[1]
71 }
72
73 let processMain, processComponents, processPages
74
75 processMain = processComponents = processPages = (callback) => {
76 callback()
77 }
78
79 if (pluginEntry.main) {
80 processMain = function (main, callback) {
81 this.resolve(this.context, main, (err, result) => {
82 if (err) return callback(err)
83 let mainPath = getName(path.join('', main))
84 mainPath = toPosix(mainPath)
85 if (/^\./.test(mainPath)) {
86 return callback(new Error(`Main's path ${main} which is referenced in ${this.context} must be a subdirectory of ${this.context}!`))
87 }
88 pluginEntry.main = mainPath + '.js'
89 addEntrySafely(result, mainPath, callback)
90 mpx.pluginMain = mainPath
91 })
92 }.bind(this, pluginEntry.main)
93 }
94
95 if (pluginEntry.publicComponents) {
96 processComponents = function (components, callback) {
97 async.forEachOf(components, (component, name, callback) => {
98 this.resolve(this.context, component, (err, result) => {
99 if (err) return callback(err)
100 result = parseRequest(result).resourcePath
101 let parsed = path.parse(result)
102 let componentName = parsed.name
103 let dirName = componentName + hash(result)
104 let componentPath = path.join('components', dirName, componentName)
105 componentPath = toPosix(componentPath)
106 // 如果之前已经创建了入口,直接return
107 if (componentsMap[result] === componentPath) return callback()
108 componentsMap[result] = componentPath
109 pluginEntry.publicComponents[name] = componentPath
110 addEntrySafely(result, componentPath, callback)
111 })
112 }, callback)
113 }.bind(this, pluginEntry.publicComponents)
114 }
115
116 if (pluginEntry.pages) {
117 processPages = function (pages, callback) {
118 async.forEachOf(pages, (page, name, callback) => {
119 this.resolve(this.context, page, (err, result) => {
120 if (err) return callback(err)
121 result = parseRequest(result).resourcePath
122 let pagePath = getName(path.join('', page))
123 pagePath = toPosix(pagePath)
124 if (/^\./.test(pagePath)) {
125 return callback(new Error(`Page's path ${page} which is referenced in ${this.context} must be a subdirectory of ${this.context}!`))
126 }
127 // 如果存在page命名冲突,return err
128 for (let key in pagesMap) {
129 if (pagesMap[key] === pagePath && key !== result) {
130 return callback(new Error(`Resources in ${result} and ${key} are registered with same page path ${pagePath}, which is not allowed!`))
131 }
132 }
133 // 如果之前已经创建了入口,直接return
134 if (pagesMap[result] === pagePath) return callback()
135 pagesMap[result] = pagePath
136 pluginEntry.pages[name] = pagePath
137 addEntrySafely(result, pagePath, callback)
138 })
139 }, callback)
140 }.bind(this, pluginEntry.pages)
141 }
142
143 async.parallel([processMain, processComponents, processPages], callback)
144}