Skip to content

Commit

Permalink
Merge pull request #50 from mpvue/feature/smartprograme
Browse files Browse the repository at this point in the history
百度小程序支持和代码重构
  • Loading branch information
hucq authored Jan 10, 2019
2 parents 0bf0604 + bdaf962 commit 95e6d6e
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 93 deletions.
9 changes: 7 additions & 2 deletions lib/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ module.exports = function (content) {
var output = ''
var parts = parse(content, fileName, this.sourceMap)

// fix #153: 根组件没有 style 模块,不生成页面的 wxss,补齐内容方便加载 vendor.wxss
// fix #153: 根组件没有 style 模块,不生成页面的样式文件,补齐内容方便加载 vendor
if (!parts.styles.length) {
parts.styles.push(defaultPart('style'))
}
Expand All @@ -119,6 +119,11 @@ module.exports = function (content) {
hasComment: hasComment,
transformToRequire: options.transformToRequire,
preserveWhitespace: options.preserveWhitespace,
fileExt: options.fileExt || {
template: 'wxml',
style: 'wxss',
script: 'js'
},
buble: options.buble,
// only pass compilerModules if it's a path string
compilerModules: typeof options.compilerModules === 'string'
Expand Down Expand Up @@ -258,7 +263,7 @@ module.exports = function (content) {
var script = parts.script
if (script) {
// for mp js
// 需要解析组件的 components 给 wxml 生成用
// 需要解析组件的 components 给 mpml 生成用
script = compileMPScript.call(this, script, mpOptions, moduleId)

if (options.esModule) {
Expand Down
148 changes: 67 additions & 81 deletions lib/mp-compiler/index.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
// for mp
const compiler = require('mpvue-template-compiler')

const babel = require('babel-core')
const path = require('path')
const fs = require('fs')
const deepEqual = require('deep-equal')
const compiler = require('mpvue-template-compiler')

const { parseConfig, parseComponentsDeps, parseGlobalComponents, clearGlobalComponents } = require('./parse')
const { parseComponentsDeps: parseComponentsDepsTs } = require('./parse-ts')
const { genPageWxml } = require('./templates')
const { genPageML } = require('./templates')

const {
cacheFileInfo,
getFileInfo,
getCompNameAndSrc,
getCompInfo,
resolveTarget,
covertCCVar,
cacheSlots,
Expand All @@ -22,73 +20,57 @@ const {
getBabelrc
} = require('./util')

let slotsHookAdded = false

// 调用 compiler 生成 wxml
function genComponentWxml (compiled, options, emitFile, emitError, emitWarning) {
function genComponentMPML (compiled, options, emitFile, emitError, emitWarning, fileExt) {
options.components['slots'] = { src: '/components/slots', name: 'slots' }
const { code: wxmlCodeStr, compiled: cp, slots, importCode } = compiler.compileToWxml(compiled, options)
const { mpErrors, mpTips } = cp
const { code: mpmlContent, compiled: compiledResult, slots, importCode } = compiler.compileToWxml(compiled, options, fileExt)
const { mpErrors, mpTips } = compiledResult
// 缓存 slots,延迟编译
cacheSlots(slots, importCode)

if (mpErrors && mpErrors.length) {
emitError(
`\n Error compiling template:\n` +
mpErrors.map(e => ` - ${e}`).join('\n') + '\n'
)
emitError('\n Error compiling template:\n' + mpErrors.map(e => ` - ${e}`).join('\n') + '\n')
}
if (mpTips && mpTips.length) {
emitWarning(
mpTips.map(e => ` - ${e}`).join('\n') + '\n'
)
emitWarning(mpTips.map(e => ` - ${e}`).join('\n') + '\n')
}
return htmlBeautify(wxmlCodeStr)
return htmlBeautify(mpmlContent)
}

function createAppWxml (emitFile, resourcePath, rootComponent, context) {
function createPageMPML (emitFile, resourcePath, rootComponent, context, fileExt) {
const { src } = getFileInfo(resourcePath) || {}
const { name: componentName, filePath: wxmlSrc } = getCompNameAndSrc(context, rootComponent)
const wxmlContent = genPageWxml(componentName, wxmlSrc)
emitFile(`${src}.wxml`, wxmlContent)
const { name, filePath } = getCompInfo(context, rootComponent, fileExt)
const MPMLContent = genPageML(name, filePath, fileExt)
emitFile(`${src}.${fileExt.template}`, MPMLContent)
}
// 更新全局组件时,需要重新生成wxml,用这个字段保存所有需要更新的页面及其参数
const cacheCreateWxmlFns = {}

function createWxml ({ emitWarning, emitError, emitFile, resourcePath, context, compiled }) {
cacheCreateWxmlFns[resourcePath] = arguments
const { pageType, moduleId, components } = getFileInfo(resourcePath) || {}
// 更新全局组件时,需要重新生成 mpml,用这个字段保存所有需要更新的页面及其参数
const cacheCreateMPMLFns = {}

// TODO, 这儿传 options 进去
// {
// components: {
// 'com-a': { src: '../../components/comA$hash', name: 'comA$hash' }
// },
// pageType: 'component',
// name: 'comA$hash',
// moduleId: 'moduleId'
// }
const { name, filePath: wxmlSrc } = getCompNameAndSrc(context, resourcePath)
function createComponentMPML ({ emitWarning, emitError, emitFile, resourcePath, context, compiled, fileExt }) {
cacheCreateMPMLFns[resourcePath] = arguments
const { pageType, moduleId, components } = getFileInfo(resourcePath) || {}
const { name, filePath } = getCompInfo(context, resourcePath, fileExt)
const options = { components, pageType, name, moduleId }
const wxmlContent = genComponentWxml(compiled, options, emitFile, emitError, emitWarning)
emitFile(wxmlSrc, wxmlContent)
const MPMLContent = genComponentMPML(compiled, options, emitFile, emitError, emitWarning, fileExt)
emitFile(filePath, MPMLContent)
}

// 编译出 wxml
function compileWxml (compiled, html) {
let slotsHookAdded = false
function compileMPML (compiled, html, options) {
const fileExt = options.fileExt
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))
this.emitFile(`components/slots.${fileExt.template}`, htmlBeautify(content))
}
// reset flag after slots file emited
slotsHookAdded = false
})
}

return new Promise(resolve => {
const pollComponentsStatus = () => {
const { pageType, components } = getFileInfo(this.resourcePath) || {}
Expand All @@ -99,66 +81,66 @@ function compileWxml (compiled, html) {
}
}
pollComponentsStatus()
})
.then(() => {
createWxml({
emitWarning: this.emitWarning,
emitError: this.emitError,
emitFile: this.emitFile,
resourcePath: this.resourcePath,
context: this.options.context,
rootComponent: null,
compiled, html
})
}).then(() => {
createComponentMPML({
emitWarning: this.emitWarning,
emitError: this.emitError,
emitFile: this.emitFile,
resourcePath: this.resourcePath,
context: this.options.context,
rootComponent: null,
compiled, html,
fileExt
})
})
}

// 针对 .vue 单文件的脚本逻辑的处理
// 处理出当前单文件组件的子组件依赖
function compileMPScript (script, mpOptioins, moduleId) {
const { resourcePath, options, resolve, context } = this
const babelrc = getBabelrc(mpOptioins.globalBabelrc)
let result, metadata

let scriptContent = script.content
const babelOptions = { extends: babelrc, plugins: [parseComponentsDeps] }
if (script.src) { // 处理src
if (script.src) {
const scriptpath = path.join(path.dirname(resourcePath), script.src)
scriptContent = fs.readFileSync(scriptpath).toString()
}
if (script.lang === 'ts') { // 处理ts

let metadata
if (script.lang === 'ts') {
metadata = parseComponentsDepsTs(scriptContent)
} else {
result = babel.transform(scriptContent, babelOptions)
const result = babel.transform(scriptContent, babelOptions)
metadata = result.metadata
}

// metadata: importsMap, components
const { importsMap, components: originComponents } = metadata

// 处理子组件的信息
const components = {}
const fileInfo = resolveTarget(resourcePath, options.entry)

const callback = () => resolveComponent(resourcePath, fileInfo, importsMap, components, moduleId)
if (originComponents) {
resolveSrc(originComponents, components, resolve, context, options.context).then(() => {
resolveComponent(resourcePath, fileInfo, importsMap, components, moduleId)
}).catch(err => {
console.error(err)
resolveComponent(resourcePath, fileInfo, importsMap, components, moduleId)
})
resolveSrc(originComponents, components, resolve, context, options.context, mpOptioins.fileExt)
.then(() => callback())
.catch(err => {
console.error(err)
callback()
})
} else {
resolveComponent(resourcePath, fileInfo, importsMap, components, moduleId)
callback()
}

return script
}

// checkMPEntry 针对 entry main.js 的入口处理
// 编译出 app, page 的入口js/wxml/json

// checkMPEntry 针对 entry main.js 的入口处理: 编译出 app, page 的入口js、mpml、json
let globalComponents
function compileMP (content, mpOptioins) {
const { resourcePath, emitFile, resolve, context, options } = this

const fileInfo = resolveTarget(resourcePath, options.entry)
cacheFileInfo(resourcePath, fileInfo)
const { isApp, isPage } = fileInfo
Expand All @@ -182,50 +164,50 @@ function compileMP (content, mpOptioins) {

// 解析全局组件的路径
const components = {}
resolveSrc(globalComps, components, resolve, context, options.context).then(() => {
resolveSrc(globalComps, components, resolve, context, options.context, mpOptioins.fileExt).then(() => {
handleResult(components)
}).catch(err => {
console.error(err)
handleResult(components)
})
const handleResult = components => {
globalComponents = components
// 热更时,如果全局组件更新,需要重新生成所有的wxml
// 热更时,如果全局组件更新,需要重新生成所有的 mpml
if (oldGlobalComponents && !deepEqual(oldGlobalComponents, globalComponents)) {
// 更新所有页面的组件
Object.keys(cacheResolveComponents).forEach(k => {
resolveComponent(...cacheResolveComponents[k])
})
// 重新生成所有wxml
Object.keys(cacheCreateWxmlFns).forEach(k => {
createWxml(...cacheCreateWxmlFns[k])
// 重新生成所有 mpml
Object.keys(cacheCreateMPMLFns).forEach(k => {
createComponentMPML(...cacheCreateMPMLFns[k])
})
}
}
}

if (isApp || isPage) {
// 这儿应该异步在所有的模块都清晰后再生成
// 生成入口 wxml
// 生成入口 mpml
if (isPage && rootComponent) {
resolve(context, rootComponent, (err, rootComponentSrc) => {
if (err) return
// 这儿需要搞定 根组件的 路径
createAppWxml(emitFile, resourcePath, rootComponentSrc, this.options.context)
createPageMPML(emitFile, resourcePath, rootComponentSrc, this.options.context, mpOptioins.fileExt)
})
}
}

return content
}

function resolveSrc (originComponents, components, resolveFn, context, projectRoot) {
function resolveSrc (originComponents, components, resolveFn, context, projectRoot, fileExt) {
return Promise.all(Object.keys(originComponents).map(k => {
return new Promise((resolve, reject) => {
resolveFn(context, originComponents[k], (err, realSrc) => {
if (err) return reject(err)
const com = covertCCVar(k)
const { filePath, name } = getCompNameAndSrc(projectRoot, realSrc)
const { filePath, name } = getCompInfo(projectRoot, realSrc, fileExt)
components[com] = { src: filePath, name }
resolve()
})
Expand All @@ -247,4 +229,8 @@ function resolveComponent (resourcePath, fileInfo, importsMap, localComponents,
}
}

module.exports = { compileWxml, compileMPScript, compileMP }
module.exports = {
compileMP,
compileMPML,
compileMPScript
}
23 changes: 21 additions & 2 deletions lib/mp-compiler/templates.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
function genPageWxml (templateName, src) {
function genWXML (templateName, src) {
return `<import src="${src}" /><template is="${templateName}" data="{{ ...$root['0'], $root }}"/>`
}

module.exports = { genPageWxml }
function genSWANML (templateName, src) {
return `<import src="${src}" /><template is="${templateName}" data="{{{ ...$root['0'], $root }}}"/>`
}

function genPageML (templateName, src, fileExt = {}) {
let code
switch (fileExt.platform) {
case 'swan':
code = genSWANML(templateName, src)
break
case 'wx':
code = genWXML(templateName, src)
break
default:
code = genWXML(templateName, src)
}
return code
}

module.exports = { genPageML }
8 changes: 4 additions & 4 deletions lib/mp-compiler/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ function getFileInfo (resourcePath) {
// TODO: 调试时取个全名
var hash = require('hash-sum')
const cache = Object.create(null)
function getCompNameAndSrc (context, file) {
const filePath = `/${resolveSrc(context, file)}.wxml`
function getCompInfo (context, file, fileExt) {
const filePath = `/${resolveSrc(context, file)}.${fileExt.template}`
if (!cache[file]) {
cache[file] = hash(file)
}
Expand Down Expand Up @@ -91,7 +91,7 @@ function getSlots () {
return allImportCode + allSlots
}

// 包大小优化: build 模式不需要美化 wxml
// 包大小优化: build 模式不需要美化 mpml
const jsBeautify = require('js-beautify')
const isProduction = process.env.NODE_ENV === 'production'
function htmlBeautify (content) {
Expand Down Expand Up @@ -142,7 +142,7 @@ module.exports = {
defaultPart,
cacheFileInfo,
getFileInfo,
getCompNameAndSrc,
getCompInfo,
resolveTarget,
covertCCVar,
cacheSlots,
Expand Down
6 changes: 3 additions & 3 deletions lib/template-compiler/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ var hotReloadAPIPath = normalize.dep('vue-hot-reload-api')
var transformRequire = require('./modules/transform-require')

// for mp
var compileWxml = require('../mp-compiler').compileWxml
var compileMPML = require('../mp-compiler').compileMPML

module.exports = function (html) {
this.async()
Expand Down Expand Up @@ -43,8 +43,8 @@ module.exports = function (html) {
var compiled = compile(html, compilerOptions)
var code

// for mp => *.wxml
compileWxml.call(this, compiled, html)
// for mp => *.mpml
compileMPML.call(this, compiled, html, options)
.then(() => {
// tips
if (compiled.tips && compiled.tips.length) {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "mpvue-loader",
"version": "1.1.4",
"version": "1.2.0",
"description": "mpvue single-file component loader for Webpack",
"main": "index.js",
"repository": {
Expand Down

0 comments on commit 95e6d6e

Please sign in to comment.