@@ -4,8 +4,9 @@ const compiler = require('mpvue-template-compiler')
44const babel = require ( 'babel-core' )
55const path = require ( 'path' )
66const fs = require ( 'fs' )
7+ const deepEqual = require ( 'deep-equal' )
78
8- const { parseConfig, parseComponentsDeps } = require ( './parse' )
9+ const { parseConfig, parseComponentsDeps, parseGlobalComponents , clearGlobalComponents } = require ( './parse' )
910const { parseComponentsDeps : parseComponentsDepsTs } = require ( './parse-ts' )
1011const { genScript, genStyle, genPageWxml } = require ( './templates' )
1112
@@ -22,22 +23,7 @@ const {
2223 getPageSrc
2324} = require ( './util' )
2425
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
4127
4228// 调用 compiler 生成 wxml
4329function genComponentWxml ( compiled , options , emitFile , emitError , emitWarning ) {
@@ -46,7 +32,7 @@ function genComponentWxml (compiled, options, emitFile, emitError, emitWarning)
4632 const { mpErrors, mpTips } = cp
4733
4834 // 缓存 slots,延迟编译
49- createSlotsWxml ( emitFile , slots , importCode )
35+ cacheSlots ( slots , importCode )
5036
5137 if ( mpErrors && mpErrors . length ) {
5238 emitError (
@@ -62,43 +48,66 @@ function genComponentWxml (compiled, options, emitFile, emitError, emitWarning)
6248 return htmlBeautify ( wxmlCodeStr )
6349}
6450
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 = { }
7560
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 } `
9578
9679 emitFile ( `${ wxmlSrc } .wxml` , wxmlContent )
9780}
9881
9982// 编译出 wxml
10083function 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+ } )
102111}
103112
104113// 针对 .vue 单文件的脚本逻辑的处理
@@ -124,53 +133,73 @@ function compileMPScript (script, mpOptioins, moduleId) {
124133
125134 // 处理子组件的信息
126135 const components = { }
136+ const fileInfo = resolveTarget ( this . resourcePath , this . options . entry )
127137 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 )
138143 } )
139- Promise . all ( allP )
140- . then ( res => {
141- components . isCompleted = true
142- } )
143- . catch ( err => {
144- console . error ( err )
145- components . isCompleted = true
146- } )
147144 } else {
148- components . isCompleted = true
145+ resolveComponent ( this . resourcePath , fileInfo , importsMap , components , moduleId )
149146 }
150147
151- const fileInfo = resolveTarget ( this . resourcePath , this . options . entry )
152- cacheFileInfo ( this . resourcePath , fileInfo , { importsMap, components, moduleId } )
153-
154148 return script
155149}
156150
157151// checkMPEntry 针对 entry main.js 的入口处理
158152// 编译出 app, page 的入口js/wxml/json
159153
160154const startPageReg = / ^ \^ /
161-
155+ let globalComponents
162156function 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
170158
171159 const fileInfo = resolveTarget ( resourcePath , options . entry )
172160 cacheFileInfo ( resourcePath , fileInfo )
173161 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+ }
174203
175204 if ( isApp || isPage ) {
176205 // 生成入口 json
@@ -205,12 +234,40 @@ function compileMP (content, mpOptioins) {
205234 resolve ( context , rootComponent , ( err , rootComponentSrc ) => {
206235 if ( err ) return
207236 // 这儿需要搞定 根组件的 路径
208- createWxml ( emitWarning , emitError , emitFile , resourcePath , rootComponentSrc )
237+ createAppWxml ( emitFile , resourcePath , rootComponentSrc )
209238 } )
210239 }
211240 }
212241
213242 return content
214243}
215244
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+
216273module . exports = { compileWxml, compileMPScript, compileMP }
0 commit comments