1 | const path = require('path')
|
2 | const async = require('async')
|
3 | const hash = require('hash-sum')
|
4 | const SingleEntryPlugin = require('webpack/lib/SingleEntryPlugin')
|
5 | const parseRequest = require('./utils/parse-request')
|
6 | const toPosix = require('./utils/to-posix')
|
7 |
|
8 |
|
9 | const defaultResultSource = '{}'
|
10 |
|
11 | module.exports = function (source) {
|
12 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 | }
|