Skip to content

百度小程序支持和代码重构 #50

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 10, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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