@@ -4,8 +4,9 @@ const compiler = require('mpvue-template-compiler')
4
4
const babel = require ( 'babel-core' )
5
5
const path = require ( 'path' )
6
6
const fs = require ( 'fs' )
7
+ const deepEqual = require ( 'deep-equal' )
7
8
8
- const { parseConfig, parseComponentsDeps } = require ( './parse' )
9
+ const { parseConfig, parseComponentsDeps, parseGlobalComponents , clearGlobalComponents } = require ( './parse' )
9
10
const { parseComponentsDeps : parseComponentsDepsTs } = require ( './parse-ts' )
10
11
const { genScript, genStyle, genPageWxml } = require ( './templates' )
11
12
@@ -22,22 +23,7 @@ const {
22
23
getPageSrc
23
24
} = require ( './util' )
24
25
25
- let emitFileTimer = null
26
-
27
- function createSlotsWxml ( emitFile , slots , importCode ) {
28
- cacheSlots ( slots , importCode )
29
- const content = getSlots ( )
30
- // 100 delay 比较符合当前策略
31
- const delay = 100
32
- if ( content . trim ( ) ) {
33
- if ( emitFileTimer ) {
34
- clearTimeout ( emitFileTimer )
35
- }
36
- emitFileTimer = setTimeout ( function ( ) {
37
- emitFile ( 'components/slots.wxml' , htmlBeautify ( content ) )
38
- } , delay )
39
- }
40
- }
26
+ let slotsHookAdded = false
41
27
42
28
// 调用 compiler 生成 wxml
43
29
function genComponentWxml ( compiled , options , emitFile , emitError , emitWarning ) {
@@ -46,7 +32,7 @@ function genComponentWxml (compiled, options, emitFile, emitError, emitWarning)
46
32
const { mpErrors, mpTips } = cp
47
33
48
34
// 缓存 slots,延迟编译
49
- createSlotsWxml ( emitFile , slots , importCode )
35
+ cacheSlots ( slots , importCode )
50
36
51
37
if ( mpErrors && mpErrors . length ) {
52
38
emitError (
@@ -62,43 +48,66 @@ function genComponentWxml (compiled, options, emitFile, emitError, emitWarning)
62
48
return htmlBeautify ( wxmlCodeStr )
63
49
}
64
50
65
- function createWxml ( emitWarning , emitError , emitFile , resourcePath , rootComponent , compiled , html ) {
66
- const { pageType, moduleId, components, src } = getFileInfo ( resourcePath ) || { }
67
-
68
- // 这儿一个黑魔法,和 webpack 约定的规范写法有点偏差!
69
- if ( ! pageType || ( components && ! components . isCompleted ) ) {
70
- return setTimeout ( createWxml , 20 , ...arguments )
71
- }
72
-
73
- let wxmlContent = ''
74
- let wxmlSrc = ''
51
+ function createAppWxml ( emitFile , resourcePath , rootComponent ) {
52
+ const { src } = getFileInfo ( resourcePath ) || { }
53
+ const componentName = getCompNameBySrc ( rootComponent )
54
+ const wxmlContent = genPageWxml ( componentName , src )
55
+ const wxmlSrc = src
56
+ emitFile ( `${ wxmlSrc } .wxml` , wxmlContent )
57
+ }
58
+ // 更新全局组件时,需要重新生成wxml,用这个字段保存所有需要更新的页面及其参数
59
+ const cacheCreateWxmlFns = { }
75
60
76
- if ( rootComponent ) {
77
- const componentName = getCompNameBySrc ( rootComponent )
78
- wxmlContent = genPageWxml ( componentName , src )
79
- wxmlSrc = src
80
- } else {
81
- // TODO, 这儿传 options 进去
82
- // {
83
- // components: {
84
- // 'com-a': { src: '../../components/comA$hash', name: 'comA$hash' }
85
- // },
86
- // pageType: 'component',
87
- // name: 'comA$hash',
88
- // moduleId: 'moduleId'
89
- // }
90
- const name = getCompNameBySrc ( resourcePath )
91
- const options = { components, pageType, name, moduleId }
92
- wxmlContent = genComponentWxml ( compiled , options , emitFile , emitError , emitWarning )
93
- wxmlSrc = `components/${ name } `
94
- }
61
+ function createWxml ( emitWarning , emitError , emitFile , resourcePath , rootComponent , compiled , html ) {
62
+ cacheCreateWxmlFns [ resourcePath ] = arguments
63
+ const { pageType, moduleId, components } = getFileInfo ( resourcePath ) || { }
64
+
65
+ // TODO, 这儿传 options 进去
66
+ // {
67
+ // components: {
68
+ // 'com-a': { src: '../../components/comA$hash', name: 'comA$hash' }
69
+ // },
70
+ // pageType: 'component',
71
+ // name: 'comA$hash',
72
+ // moduleId: 'moduleId'
73
+ // }
74
+ const name = getCompNameBySrc ( resourcePath )
75
+ const options = { components, pageType, name, moduleId }
76
+ const wxmlContent = genComponentWxml ( compiled , options , emitFile , emitError , emitWarning )
77
+ const wxmlSrc = `components/${ name } `
95
78
96
79
emitFile ( `${ wxmlSrc } .wxml` , wxmlContent )
97
80
}
98
81
99
82
// 编译出 wxml
100
83
function compileWxml ( compiled , html ) {
101
- return createWxml ( this . emitWarning , this . emitError , this . emitFile , this . resourcePath , null , compiled , html )
84
+ if ( ! slotsHookAdded ) {
85
+ // avoid add hook several times during compilation
86
+ slotsHookAdded = true
87
+ // TODO: support webpack4
88
+ this . _compilation . plugin ( 'seal' , ( ) => {
89
+ const content = getSlots ( )
90
+ if ( content . trim ( ) ) {
91
+ this . emitFile ( 'components/slots.wxml' , htmlBeautify ( content ) )
92
+ }
93
+ // reset flag after slots file emited
94
+ slotsHookAdded = false
95
+ } )
96
+ }
97
+ return new Promise ( resolve => {
98
+ const pollComponentsStatus = ( ) => {
99
+ const { pageType, components } = getFileInfo ( this . resourcePath ) || { }
100
+ if ( ! pageType || ( components && ! components . isCompleted ) ) {
101
+ setTimeout ( pollComponentsStatus , 20 )
102
+ } else {
103
+ resolve ( )
104
+ }
105
+ }
106
+ pollComponentsStatus ( )
107
+ } )
108
+ . then ( ( ) => {
109
+ createWxml ( this . emitWarning , this . emitError , this . emitFile , this . resourcePath , null , compiled , html )
110
+ } )
102
111
}
103
112
104
113
// 针对 .vue 单文件的脚本逻辑的处理
@@ -124,53 +133,73 @@ function compileMPScript (script, mpOptioins, moduleId) {
124
133
125
134
// 处理子组件的信息
126
135
const components = { }
136
+ const fileInfo = resolveTarget ( this . resourcePath , this . options . entry )
127
137
if ( originComponents ) {
128
- const allP = Object . keys ( originComponents ) . map ( k => {
129
- return new Promise ( ( resolve , reject ) => {
130
- this . resolve ( this . context , originComponents [ k ] , ( err , realSrc ) => {
131
- if ( err ) return reject ( err )
132
- const com = covertCCVar ( k )
133
- const comName = getCompNameBySrc ( realSrc )
134
- components [ com ] = { src : comName , name : comName }
135
- resolve ( )
136
- } )
137
- } )
138
+ resolveSrc ( originComponents , components , this . resolve , this . context ) . then ( ( ) => {
139
+ resolveComponent ( this . resourcePath , fileInfo , importsMap , components , moduleId )
140
+ } ) . catch ( err => {
141
+ console . error ( err )
142
+ resolveComponent ( this . resourcePath , fileInfo , importsMap , components , moduleId )
138
143
} )
139
- Promise . all ( allP )
140
- . then ( res => {
141
- components . isCompleted = true
142
- } )
143
- . catch ( err => {
144
- console . error ( err )
145
- components . isCompleted = true
146
- } )
147
144
} else {
148
- components . isCompleted = true
145
+ resolveComponent ( this . resourcePath , fileInfo , importsMap , components , moduleId )
149
146
}
150
147
151
- const fileInfo = resolveTarget ( this . resourcePath , this . options . entry )
152
- cacheFileInfo ( this . resourcePath , fileInfo , { importsMap, components, moduleId } )
153
-
154
148
return script
155
149
}
156
150
157
151
// checkMPEntry 针对 entry main.js 的入口处理
158
152
// 编译出 app, page 的入口js/wxml/json
159
153
160
154
const startPageReg = / ^ \^ /
161
-
155
+ let globalComponents
162
156
function compileMP ( content , mpOptioins ) {
163
- const { resourcePath, emitError, emitFile, emitWarning, resolve, context, options } = this
164
-
165
- const babelrc = getBabelrc ( mpOptioins . globalBabelrc )
166
- const { metadata } = babel . transform ( content , { extends : babelrc , plugins : [ parseConfig ] } )
167
-
168
- // metadata: config
169
- const { config, rootComponent } = metadata
157
+ const { resourcePath, emitFile, resolve, context, options } = this
170
158
171
159
const fileInfo = resolveTarget ( resourcePath , options . entry )
172
160
cacheFileInfo ( resourcePath , fileInfo )
173
161
const { src, name, isApp, isPage } = fileInfo
162
+ if ( isApp ) {
163
+ // 解析前将可能存在的全局组件清空
164
+ clearGlobalComponents ( )
165
+ }
166
+
167
+ const babelrc = getBabelrc ( mpOptioins . globalBabelrc )
168
+ // app入口进行全局component解析
169
+ const { metadata } = babel . transform ( content , { extends : babelrc , plugins : isApp ? [ parseConfig , parseGlobalComponents ] : [ parseConfig ] } )
170
+
171
+ // metadata: config
172
+ const { config, rootComponent, globalComponents : globalComps } = metadata
173
+
174
+ if ( isApp ) {
175
+ // 保存旧数据,用于对比
176
+ const oldGlobalComponents = globalComponents
177
+ // 开始解析组件路径时把全局组件清空,解析完成后再进行赋值,标志全局组件解析完成
178
+ globalComponents = null
179
+
180
+ // 解析全局组件的路径
181
+ const components = { }
182
+ resolveSrc ( globalComps , components , resolve , context ) . then ( ( ) => {
183
+ handleResult ( components )
184
+ } ) . catch ( err => {
185
+ console . error ( err )
186
+ handleResult ( components )
187
+ } )
188
+ const handleResult = components => {
189
+ globalComponents = components
190
+ // 热更时,如果全局组件更新,需要重新生成所有的wxml
191
+ if ( oldGlobalComponents && ! deepEqual ( oldGlobalComponents , globalComponents ) ) {
192
+ // 更新所有页面的组件
193
+ Object . keys ( cacheResolveComponents ) . forEach ( k => {
194
+ resolveComponent ( ...cacheResolveComponents [ k ] )
195
+ } )
196
+ // 重新生成所有wxml
197
+ Object . keys ( cacheCreateWxmlFns ) . forEach ( k => {
198
+ createWxml ( ...cacheCreateWxmlFns [ k ] )
199
+ } )
200
+ }
201
+ }
202
+ }
174
203
175
204
if ( isApp || isPage ) {
176
205
// 生成入口 json
@@ -205,12 +234,40 @@ function compileMP (content, mpOptioins) {
205
234
resolve ( context , rootComponent , ( err , rootComponentSrc ) => {
206
235
if ( err ) return
207
236
// 这儿需要搞定 根组件的 路径
208
- createWxml ( emitWarning , emitError , emitFile , resourcePath , rootComponentSrc )
237
+ createAppWxml ( emitFile , resourcePath , rootComponentSrc )
209
238
} )
210
239
}
211
240
}
212
241
213
242
return content
214
243
}
215
244
245
+ function resolveSrc ( originComponents , components , resolveFn , context ) {
246
+ return Promise . all ( Object . keys ( originComponents ) . map ( k => {
247
+ return new Promise ( ( resolve , reject ) => {
248
+ resolveFn ( context , originComponents [ k ] , ( err , realSrc ) => {
249
+ if ( err ) return reject ( err )
250
+ const com = covertCCVar ( k )
251
+ const comName = getCompNameBySrc ( realSrc )
252
+ components [ com ] = { src : comName , name : comName }
253
+ resolve ( )
254
+ } )
255
+ } )
256
+ } ) )
257
+ }
258
+
259
+ const cacheResolveComponents = { }
260
+ function resolveComponent ( resourcePath , fileInfo , importsMap , localComponents , moduleId ) {
261
+ // 需要等待全局组件解析完成
262
+ if ( ! globalComponents ) {
263
+ setTimeout ( resolveComponent , 20 , ...arguments )
264
+ } else {
265
+ // 保存当前所有参数,在热更时如果全局组件发生变化,需要进行组件更新
266
+ cacheResolveComponents [ resourcePath ] = arguments
267
+ const components = Object . assign ( { } , globalComponents , localComponents )
268
+ components . isCompleted = true
269
+ cacheFileInfo ( resourcePath , fileInfo , { importsMap, components, moduleId } )
270
+ }
271
+ }
272
+
216
273
module . exports = { compileWxml, compileMPScript, compileMP }
0 commit comments