diff --git a/lib/mp-compiler/index.js b/lib/mp-compiler/index.js index 7d991c0..345a8b8 100644 --- a/lib/mp-compiler/index.js +++ b/lib/mp-compiler/index.js @@ -23,22 +23,7 @@ const { getPageSrc } = require('./util') -let emitFileTimer = null - -function createSlotsWxml (emitFile, slots, importCode) { - cacheSlots(slots, importCode) - const content = getSlots() - // 100 delay 比较符合当前策略 - const delay = 100 - if (content.trim()) { - if (emitFileTimer) { - clearTimeout(emitFileTimer) - } - emitFileTimer = setTimeout(function () { - emitFile('components/slots.wxml', htmlBeautify(content)) - }, delay) - } -} +let slotsHookAdded = false // 调用 compiler 生成 wxml function genComponentWxml (compiled, options, emitFile, emitError, emitWarning) { @@ -47,7 +32,7 @@ function genComponentWxml (compiled, options, emitFile, emitError, emitWarning) const { mpErrors, mpTips } = cp // 缓存 slots,延迟编译 - createSlotsWxml(emitFile, slots, importCode) + cacheSlots(slots, importCode) if (mpErrors && mpErrors.length) { emitError( @@ -63,47 +48,66 @@ function genComponentWxml (compiled, options, emitFile, emitError, emitWarning) return htmlBeautify(wxmlCodeStr) } +function createAppWxml (emitFile, resourcePath, rootComponent) { + const { src } = getFileInfo(resourcePath) || {} + const componentName = getCompNameBySrc(rootComponent) + const wxmlContent = genPageWxml(componentName, src) + const wxmlSrc = src + emitFile(`${wxmlSrc}.wxml`, wxmlContent) +} // 更新全局组件时,需要重新生成wxml,用这个字段保存所有需要更新的页面及其参数 const cacheCreateWxmlFns = {} function createWxml (emitWarning, emitError, emitFile, resourcePath, rootComponent, compiled, html) { cacheCreateWxmlFns[resourcePath] = arguments - const { pageType, moduleId, components, src } = getFileInfo(resourcePath) || {} - - // 这儿一个黑魔法,和 webpack 约定的规范写法有点偏差! - if (!pageType || (components && !components.isCompleted)) { - return setTimeout(createWxml, 20, ...arguments) - } - - let wxmlContent = '' - let wxmlSrc = '' - - if (rootComponent) { - const componentName = getCompNameBySrc(rootComponent) - wxmlContent = genPageWxml(componentName, src) - wxmlSrc = src - } else { - // TODO, 这儿传 options 进去 - // { - // components: { - // 'com-a': { src: '../../components/comA$hash', name: 'comA$hash' } - // }, - // pageType: 'component', - // name: 'comA$hash', - // moduleId: 'moduleId' - // } - const name = getCompNameBySrc(resourcePath) - const options = { components, pageType, name, moduleId } - wxmlContent = genComponentWxml(compiled, options, emitFile, emitError, emitWarning) - wxmlSrc = `components/${name}` - } + const { pageType, moduleId, components } = getFileInfo(resourcePath) || {} + + // TODO, 这儿传 options 进去 + // { + // components: { + // 'com-a': { src: '../../components/comA$hash', name: 'comA$hash' } + // }, + // pageType: 'component', + // name: 'comA$hash', + // moduleId: 'moduleId' + // } + const name = getCompNameBySrc(resourcePath) + const options = { components, pageType, name, moduleId } + const wxmlContent = genComponentWxml(compiled, options, emitFile, emitError, emitWarning) + const wxmlSrc = `components/${name}` emitFile(`${wxmlSrc}.wxml`, wxmlContent) } // 编译出 wxml function compileWxml (compiled, html) { - return createWxml(this.emitWarning, this.emitError, this.emitFile, this.resourcePath, null, compiled, html) + if (!slotsHookAdded) { + // avoid add hook several times during compilation + slotsHookAdded = true + // TODO: support webpack4 + this._compilation.plugin('seal', () => { + const content = getSlots() + if (content.trim()) { + this.emitFile('components/slots.wxml', htmlBeautify(content)) + } + // reset flag after slots file emited + slotsHookAdded = false + }) + } + return new Promise(resolve => { + const pollComponentsStatus = () => { + const { pageType, components } = getFileInfo(this.resourcePath) || {} + if (!pageType || (components && !components.isCompleted)) { + setTimeout(pollComponentsStatus, 20) + } else { + resolve() + } + } + pollComponentsStatus() + }) + .then(() => { + createWxml(this.emitWarning, this.emitError, this.emitFile, this.resourcePath, null, compiled, html) + }) } // 针对 .vue 单文件的脚本逻辑的处理 @@ -150,7 +154,7 @@ function compileMPScript (script, mpOptioins, moduleId) { const startPageReg = /^\^/ let globalComponents function compileMP (content, mpOptioins) { - const { resourcePath, emitError, emitFile, emitWarning, resolve, context, options } = this + const { resourcePath, emitFile, resolve, context, options } = this const fileInfo = resolveTarget(resourcePath, options.entry) cacheFileInfo(resourcePath, fileInfo) @@ -230,7 +234,7 @@ function compileMP (content, mpOptioins) { resolve(context, rootComponent, (err, rootComponentSrc) => { if (err) return // 这儿需要搞定 根组件的 路径 - createWxml(emitWarning, emitError, emitFile, resourcePath, rootComponentSrc) + createAppWxml(emitFile, resourcePath, rootComponentSrc) }) } } diff --git a/lib/template-compiler/index.js b/lib/template-compiler/index.js index 9dfacd8..0fab149 100644 --- a/lib/template-compiler/index.js +++ b/lib/template-compiler/index.js @@ -10,6 +10,7 @@ var transformRequire = require('./modules/transform-require') var compileWxml = require('../mp-compiler').compileWxml module.exports = function (html) { + this.async() this.cacheable() var isServer = this.target === 'node' var isProduction = this.minimize || process.env.NODE_ENV === 'production' @@ -38,56 +39,57 @@ module.exports = function (html) { // for mp => *.wxml compileWxml.call(this, compiled, html) + .then(() => { + // tips + if (compiled.tips && compiled.tips.length) { + compiled.tips.forEach(tip => { + this.emitWarning(tip) + }) + } - // tips - if (compiled.tips && compiled.tips.length) { - compiled.tips.forEach(tip => { - this.emitWarning(tip) - }) - } - - var code - if (compiled.errors && compiled.errors.length) { - this.emitError( - `\n Error compiling template:\n${pad(html)}\n` + - compiled.errors.map(e => ` - ${e}`).join('\n') + '\n' - ) - code = vueOptions.esModule - ? `var esExports = {render:function(){},staticRenderFns: []}\nexport default esExports` - : 'module.exports={render:function(){},staticRenderFns:[]}' - } else { - var bubleOptions = options.buble - code = transpile( - 'var render = ' + toFunction(compiled.render) + '\n' + - 'var staticRenderFns = [' + compiled.staticRenderFns.map(toFunction).join(',') + ']', - bubleOptions - ) + '\n' - // mark with stripped (this enables Vue to use correct runtime proxy detection) - if (!isProduction && ( - !bubleOptions || - !bubleOptions.transforms || - bubleOptions.transforms.stripWith !== false - )) { - code += `render._withStripped = true\n` - } - var exports = `{ render: render, staticRenderFns: staticRenderFns }` - code += vueOptions.esModule - ? `var esExports = ${exports}\nexport default esExports` - : `module.exports = ${exports}` - } - // hot-reload - if (!isServer && !isProduction) { - var exportsName = vueOptions.esModule ? 'esExports' : 'module.exports' - code += - '\nif (module.hot) {\n' + - ' module.hot.accept()\n' + - ' if (module.hot.data) {\n' + - ' require("' + hotReloadAPIPath + '").rerender("' + options.id + '", ' + exportsName + ')\n' + - ' }\n' + - '}' - } + var code + if (compiled.errors && compiled.errors.length) { + this.emitError( + `\n Error compiling template:\n${pad(html)}\n` + + compiled.errors.map(e => ` - ${e}`).join('\n') + '\n' + ) + code = vueOptions.esModule + ? `var esExports = {render:function(){},staticRenderFns: []}\nexport default esExports` + : 'module.exports={render:function(){},staticRenderFns:[]}' + } else { + var bubleOptions = options.buble + code = transpile( + 'var render = ' + toFunction(compiled.render) + '\n' + + 'var staticRenderFns = [' + compiled.staticRenderFns.map(toFunction).join(',') + ']', + bubleOptions + ) + '\n' + // mark with stripped (this enables Vue to use correct runtime proxy detection) + if (!isProduction && ( + !bubleOptions || + !bubleOptions.transforms || + bubleOptions.transforms.stripWith !== false + )) { + code += `render._withStripped = true\n` + } + var exports = `{ render: render, staticRenderFns: staticRenderFns }` + code += vueOptions.esModule + ? `var esExports = ${exports}\nexport default esExports` + : `module.exports = ${exports}` + } + // hot-reload + if (!isServer && !isProduction) { + var exportsName = vueOptions.esModule ? 'esExports' : 'module.exports' + code += + '\nif (module.hot) {\n' + + ' module.hot.accept()\n' + + ' if (module.hot.data) {\n' + + ' require("' + hotReloadAPIPath + '").rerender("' + options.id + '", ' + exportsName + ')\n' + + ' }\n' + + '}' + } - return code + this.callback(null, code) + }) } function toFunction (code) {