提交 5b3477b5 编写于 作者: Q qiang

Merge branch 'v3' of github.com:dcloudio/uni-app into v3

const migrate = require('../lib/index') const migrate = require('../lib/index')
migrate('/Users/fxy/Documents/demo/my-v3-mp/src/wxcomponents', migrate('/Users/fxy/Downloads/wa-vantui_1.1/wxcomponents/vant',
'/Users/fxy/Documents/demo/my-v3-mp/src/wxcomponents-vue') '/Users/fxy/Downloads/wa-vantui_1.1/vant')
// const {
// parse
// } = require('mustache')
// console.log(parse('{{ !item.loading }}'))
// const {
// transformTemplate
// } = require('../lib/mp-weixin/transform/template-transformer')
// console.log(transformTemplate(
// `<view wx:for="{{ isSimple(columns) ? [columns] : columns }}" wx:for-item="item1" wx:key="{{ index }}"/>`
// ))
const {
transformTemplate
} = require('../lib/mp-weixin/transform/template-transformer')
function assertCodegen(wxmlCode, vueCode) {
expect(transformTemplate(wxmlCode)[0]).toBe(vueCode)
}
describe('wxml:compiler', () => {
it('generate v-if', () => {
assertCodegen(
'<block wx:if="{{ !loading }}">{{ item.name }}</block>',
`<block v-if="!loading">{{ item.name }}</block>`
)
})
it('generate v-for', () => {
assertCodegen(
'<view wx:for="{{ isSimple(columns) ? [columns] : columns }}" wx:for-item="item1" wx:key="{{ index }}"/>',
`<view v-for="(item1,index) in (isSimple(columns) ? [columns] : columns)" :key="index"></view>`
)
})
it('generate root element', () => {
assertCodegen(
'<view></view><view></view>',
`<view><view></view><view></view></view>`
)
assertCodegen(
`<view></view>
<wxs></wxs>`,
`<view></view>`
)
assertCodegen(
'<slot></slot>',
`<view><slot></slot></view>`
)
})
})
const path = require('path')
const fs = require('fs-extra') const fs = require('fs-extra')
const validate = require('./validate') const validate = require('./validate')
const migraters = { const migraters = {
...@@ -15,9 +17,16 @@ module.exports = function migrate(input, out, options = { ...@@ -15,9 +17,16 @@ module.exports = function migrate(input, out, options = {
if (!validate(input, out, options)) { if (!validate(input, out, options)) {
return return
} }
migrater.transform(input, out, options).forEach(file => { const [files, assets] = migrater.transform(input, out, options)
console.log(`写入: ${file.path}`) files.forEach(file => {
console.log(`${file.content}`) console.log(`write: ${file.path}`)
// fs.outputFileSync(file.path, file.content) fs.outputFileSync(file.path, file.content)
})
const styleExtname = options.extname.style
assets.forEach(asset => {
const src = path.resolve(input, asset)
const dest = path.resolve(out, asset.replace(styleExtname, '.css'))
console.log(`copy: ${dest}`)
fs.copySync(src, dest)
}) })
} }
module.exports = { module.exports = {
options: { options: {
file: { extname: {
extname: '.wxml' template: '.wxml',
style: '.wxss'
} }
}, },
transform: require('./transform') transform: require('./transform')
......
const transformJson = require('./json-transformer') const {
const transformWxml = require('./wxml-transformer') transformJsonFile
const transformWxss = require('./wxss-transformer') } = require('./json-transformer')
const transformJs = require('./js-transformer') const {
transformTemplateFile
} = require('./template-transformer')
const {
transformStyleFile
} = require('./style-transformer')
const {
transformScriptFile
} = require('./script-transformer')
module.exports = function transformFile(input, out, options) { module.exports = function transformFile(input, options) {
const filepath = input.replace('.wxml', '')
const [usingComponentsCode] = transformJson(filepath + '.json') const {
template: templateExtname,
style: styleExtname
} = options.extname
const [templateCode, wxsCode = ''] = transformWxml(filepath + '.wxml') const filepath = input.replace(templateExtname, '')
const deps = [
filepath + templateExtname
]
const styleCode = transformWxss(filepath + '.wxss') || '' const [usingComponentsCode] = transformJsonFile(filepath + '.json', deps)
const scriptCode = transformJs(filepath + '.js', usingComponentsCode, options)
return ` const [templateCode, wxsCode = ''] = transformTemplateFile(filepath + templateExtname)
<template>
const styleCode = transformStyleFile(filepath + styleExtname, options, deps) || ''
const scriptCode = transformScriptFile(filepath + '.js', usingComponentsCode, options, deps)
return [
`<template>
${templateCode} ${templateCode}
</template> </template>
${wxsCode} ${wxsCode}
...@@ -23,6 +40,7 @@ ${scriptCode} ...@@ -23,6 +40,7 @@ ${scriptCode}
</script> </script>
<style> <style>
${styleCode} ${styleCode}
</style> </style>`,
` deps
]
} }
...@@ -3,41 +3,62 @@ const glob = require('glob') ...@@ -3,41 +3,62 @@ const glob = require('glob')
const transformFile = require('./file-transformer') const transformFile = require('./file-transformer')
function generateVueFile(input, out, options) { function generateVueFile(input, out, options) {
const content = transformFile(input, out, options) const [content, deps] = transformFile(input, options)
return { return {
path: path.resolve(out, path.basename(input).replace(options.extname, '.vue')), path: path.resolve(out, path.basename(input).replace(options.extname.template, '.vue')),
content content,
deps
} }
} }
function generateVueFolder(input, out, options) { function generateVueFolder(input, out, options) {
return glob.sync('**/*.wxml', { const extname = options.extname.template
cwd: input const files = []
}).map(wxmlFile => { const assets = []
return generateVueFile( const deps = []
path.resolve(input, wxmlFile), glob.sync('**/*', {
path.dirname(path.resolve(out, wxmlFile)), cwd: input,
nodir: true
}).map(file => {
if (path.extname(file) === extname) {
const vueFile = generateVueFile(
path.resolve(input, file),
path.dirname(path.resolve(out, file)),
options options
) )
files.push(vueFile)
deps.push(...vueFile.deps)
} else {
assets.push(file)
}
}) })
return [files, assets.filter(asset => {
return !deps.includes(path.resolve(input, asset))
})]
} }
function generateVueApp(input, out, options) { function generateVueApp(input, out, options) {
console.error(`暂不支持转换整个 App`) console.error(`暂不支持转换整个 App`)
return [
[],
[]
]
} }
module.exports = function transform(input, out, options) { module.exports = function transform(input, out, options) {
const ret = []
switch (options.target) { switch (options.target) {
case 'file': case 'file':
ret.push(generateVueFile(input, out, options)) return [
break [generateVueFile(input, out, options)],
[]
]
case 'folder': case 'folder':
ret.push(...generateVueFolder(input, out, options)) return generateVueFolder(input, out, options)
break
case 'app': case 'app':
ret.push(...generateVueApp(input, out, options)) return generateVueApp(input, out, options)
break
} }
return ret return [
[],
[]
]
} }
const fs = require('fs')
const path = require('path')
const {
normalizePath
} = require('../util')
module.exports = function transformJs(filepath, usingComponentsCode, options) {
let jsCode = ''
if (!fs.existsSync(filepath)) {
jsCode = `
Component({})
`
} else {
jsCode = fs.readFileSync(filepath, 'utf8')
}
let route = normalizePath(filepath)
if (options.base) {
route = normalizePath(path.relative(options.base, filepath))
}
route = route.replace('.js', '')
return `
global['__wxRoute'] = '${route}'
global['__wxUsingComponents'] = ${usingComponentsCode}
${jsCode}
export default global['__wxComponents']['${route}']
`
}
const fs = require('fs') const fs = require('fs')
module.exports = function transformJson(filepath) { function transformJson(content) {
if (!fs.existsSync(filepath)) {
return ['{}']
}
const { const {
usingComponents usingComponents
} = require(filepath) } = JSON.parse(content)
if (!usingComponents) { if (!usingComponents) {
return ['{}'] return ['{}']
} }
const usingComponentsCode = [] const usingComponentsCode = []
Object.keys(usingComponents).forEach(name => { Object.keys(usingComponents).forEach(name => {
usingComponentsCode.push(`'${name}': require('${usingComponents[name]}').default`) usingComponentsCode.push(`'${name}': require('${usingComponents[name]}.vue').default`)
}) })
return [`{ return [`{
${usingComponentsCode.join(',')} ${usingComponentsCode.join(',\n')}
}`] }`]
} }
module.exports = {
transformJson,
transformJsonFile(filepath, deps) {
if (!fs.existsSync(filepath)) {
return ['{}']
}
deps.push(filepath)
return transformJson(fs.readFileSync(filepath, 'utf8').toString().trim())
}
}
const fs = require('fs')
const path = require('path')
const {
normalizePath
} = require('../util')
function transformScript(content, route, usingComponentsCode) {
return `global['__wxRoute'].push('${route}')
global['__wxUsingComponents'] = ${usingComponentsCode}
${content}
export default global['__wxComponents']['${route}']`
}
module.exports = {
transformScript,
transformScriptFile(filepath, usingComponentsCode, options, deps) {
let content = ''
if (!fs.existsSync(filepath)) {
content = `
Component({})
`
} else {
content = fs.readFileSync(filepath, 'utf8').toString().trim()
deps.push(filepath)
}
let route = normalizePath(filepath)
if (options.base) {
route = normalizePath(path.relative(options.base, filepath))
}
route = route.replace('.js', '')
return transformScript(content, route, usingComponentsCode, options)
}
}
const fs = require('fs')
function transformStyle(content, options) {
return content.replace(new RegExp(`\\${options.extname.style}`, 'g'), '.css')
}
module.exports = {
transformStyle,
transformStyleFile(filepath, options, deps) {
if (!fs.existsSync(filepath)) {
return ''
}
deps.push(filepath)
return transformStyle(fs.readFileSync(filepath, 'utf8').toString().trim(), options)
}
}
...@@ -3,6 +3,14 @@ const path = require('path') ...@@ -3,6 +3,14 @@ const path = require('path')
const parse = require('./parser') const parse = require('./parser')
const transform = require('./transform') const transform = require('./transform')
module.exports = function transformWxml(filepath) {
return transform(parse(fs.readFileSync(filepath, 'utf8'))) function transformTemplate(content) {
return transform(parse(content))
}
module.exports = {
transformTemplate,
transformTemplateFile(filepath) {
return transformTemplate(fs.readFileSync(filepath, 'utf8').toString().trim())
}
} }
const BOOL_ATTRS = [
'v-else'
]
function genAttrs(node) { function genAttrs(node) {
const attribs = node.attribs const attribs = node.attribs
const attribsArr = Object.keys(attribs).map(name => `${name}="${attribs[name]}"`) const attribsArr = Object.keys(attribs).map(name => {
if (BOOL_ATTRS.includes(name)) {
return name
}
return `${name}="${attribs[name]}"`
})
if (!attribsArr.length) { if (!attribsArr.length) {
return '' return ''
} }
...@@ -25,17 +34,29 @@ function genElement(node) { ...@@ -25,17 +34,29 @@ function genElement(node) {
} }
function genWxs(wxs) { function genWxs(wxs) {
return wxs.map(wxsNode => genElement(wxsNode)).join('') return wxs.map(wxsNode => genElement(wxsNode)).join('').trim()
}
function shouldWrapper(node) {
node.children = node.children.filter(child => { // remove \n
if (child.type === 'text' && !child.data.trim()) {
return false
}
return true
})
if (node.children.length > 1) { // multi root
return true
}
const rootNode = node.children[0]
if (rootNode && rootNode.name === 'slot') { // slot root
return true
}
return false
} }
module.exports = function generate(node, state) { module.exports = function generate(node, state) {
if (node.children.length > 1) { if (shouldWrapper(node)) {
return [genChildren({ return [`<view>${genChildren(node).trim()}</view>`, genWxs(state.wxs)]
type: 'tag', }
name: 'view', return [genChildren(node).trim(), genWxs(state.wxs)]
attribs: {},
children: node.children
}), genWxs(state.wxs)]
}
return [genChildren(node), genWxs(state.wxs)]
} }
...@@ -8,12 +8,32 @@ const ATTRS = { ...@@ -8,12 +8,32 @@ const ATTRS = {
'wx:else': 'v-else' 'wx:else': 'v-else'
} }
function parseMustache(expr) { const FOR = {
for: 'wx:for',
item: 'wx:for-item',
index: 'wx:for-index',
key: 'wx:key'
}
const FOR_DEFAULT = {
item: 'item',
index: 'index'
}
function parseMustache(expr, identifier = false) {
if (!expr) {
return ''
}
const tokens = parse(expr) const tokens = parse(expr)
const isIdentifier = tokens.length === 1 const isIdentifier = tokens.length === 1
return tokens.map(token => { return tokens.map(token => {
if (token[0] === 'text') { if (token[0] === 'text') {
if (identifier) {
return token[1]
}
return `'${token[1]}'` return `'${token[1]}'`
} else if (token[0] === '!') { // {{ !loading }}
return `!${token[1]}`
} else if (token[0] === 'name') { } else if (token[0] === 'name') {
if (isIdentifier) { if (isIdentifier) {
return token[1] return token[1]
...@@ -31,6 +51,24 @@ function transformDirective(name, value, attribs) { ...@@ -31,6 +51,24 @@ function transformDirective(name, value, attribs) {
} }
} }
function transformFor(attribs) {
const vFor = attribs[FOR.for]
if (!vFor) {
return
}
const vKey = parseMustache(attribs[FOR.key], true) // TODO
const vItem = parseMustache(attribs[FOR.item], true) || FOR_DEFAULT.item
const vIndex = parseMustache(attribs[FOR.index], true) || FOR_DEFAULT.index
attribs['v-for'] = `(${vItem},${vIndex}) in (${parseMustache(vFor)})`
vKey && (attribs[':key'] = vKey)
delete attribs[FOR.for]
delete attribs[FOR.item]
delete attribs[FOR.index]
delete attribs[FOR.key]
}
const bindRE = /bind:?/ const bindRE = /bind:?/
const catchRE = /catch:?/ const catchRE = /catch:?/
const captureBindRE = /capture-bind:?/ const captureBindRE = /capture-bind:?/
...@@ -55,6 +93,12 @@ function transformEvent(name, value, attribs) { ...@@ -55,6 +93,12 @@ function transformEvent(name, value, attribs) {
function transformAttr(name, value, attribs) { function transformAttr(name, value, attribs) {
if (
name.indexOf('v-') === 0 ||
name.indexOf(':') === 0
) { // 已提前处理
return
}
delete attribs[name] delete attribs[name]
if (transformDirective(name, value, attribs)) { if (transformDirective(name, value, attribs)) {
return return
...@@ -74,6 +118,7 @@ function transformAttrs(node, state) { ...@@ -74,6 +118,7 @@ function transformAttrs(node, state) {
if (!attribs) { if (!attribs) {
return return
} }
transformFor(attribs)
Object.keys(attribs).forEach(name => { Object.keys(attribs).forEach(name => {
transformAttr(name, attribs[name], attribs) transformAttr(name, attribs[name], attribs)
}) })
......
...@@ -12,12 +12,13 @@ module.exports = function validate(input, out, options) { ...@@ -12,12 +12,13 @@ module.exports = function validate(input, out, options) {
} }
const platformOptions = migraters[options.platform] const platformOptions = migraters[options.platform]
options.extname = platformOptions.file.extname options.extname = platformOptions.extname
const templateExtname = options.extname.template
const stat = fs.lstatSync(input) const stat = fs.lstatSync(input)
if (stat.isFile()) { if (stat.isFile()) {
if (path.extname(input) !== options.extname) { if (path.extname(input) !== templateExtname) {
return console.error(`错误: 单文件转换需要传入 ${options.extname.substr(1)} 文件地址`) return console.error(`错误: 单文件转换需要传入 ${templateExtname.substr(1)} 文件地址`)
} }
options.target = 'file' options.target = 'file'
} else if (stat.isDirectory()) { } else if (stat.isDirectory()) {
......
...@@ -34,7 +34,10 @@ function parseProperty (name, property, watch) { ...@@ -34,7 +34,10 @@ function parseProperty (name, property, watch) {
type.push(...property.optionalTypes); type.push(...property.optionalTypes);
} }
const prop = Object.create(null); const prop = Object.create(null);
prop.type = type; prop.type = type.filter(sType => sType !== null); // remove null
if (!prop.type.length) {
delete prop.type;
}
if (hasOwn(property, 'value')) { if (hasOwn(property, 'value')) {
prop['default'] = property.value; prop['default'] = property.value;
} }
...@@ -267,14 +270,37 @@ var polyfill = { ...@@ -267,14 +270,37 @@ var polyfill = {
} }
}; };
/**
* wxs getRegExp
*/
function getRegExp () {
const args = Array.prototype.slice.call(arguments);
args.unshift(RegExp);
return new (Function.prototype.bind.apply(RegExp, args))()
}
/**
* wxs getDate
*/
function getDate () {
const args = Array.prototype.slice.call(arguments);
args.unshift(Date);
return new (Function.prototype.bind.apply(Date, args))()
}
global['__wxRoute'] = [];
function Component (options) { function Component (options) {
const componentOptions = parseComponent(options); const componentOptions = parseComponent(options);
componentOptions.mixins.unshift(polyfill); componentOptions.mixins.unshift(polyfill);
global['__wxComponents'][global['__wxRoute']] = componentOptions; if (!global['__wxComponents']) {
global['__wxComponents'] = Object.create(null);
}
global['__wxComponents'][global['__wxRoute'].pop()] = componentOptions;
} }
function Behavior (options) { function Behavior (options) {
return options return options
} }
export { Behavior, Component }; export { Behavior, Component, getDate, getRegExp };
...@@ -64,13 +64,13 @@ module.exports = function (source) { ...@@ -64,13 +64,13 @@ module.exports = function (source) {
const frame = compiler.generateCodeFrame(source, start, end) const frame = compiler.generateCodeFrame(source, start, end)
return ` ${msg}\n\n${pad(frame)}` return ` ${msg}\n\n${pad(frame)}`
}).join(`\n\n`) + }).join(`\n\n`) +
'\n' '\n at ' + finalOptions.filename + ':0' // fixed by xxxxxx
) )
} else { } else {
loaderContext.emitError( loaderContext.emitError(
`\n Error compiling template:\n${pad(compiled.source)}\n` + `\n Error compiling template:\n${pad(compiled.source)}\n` +
compiled.errors.map(e => ` - ${e}`).join('\n') + compiled.errors.map(e => ` - ${e}`).join('\n') +
'\n' '\n at ' + finalOptions.filename + ':0' // fixed by xxxxxx
) )
} }
} }
......
...@@ -16,17 +16,24 @@ const { ...@@ -16,17 +16,24 @@ const {
// createTemplateCacheLoader // createTemplateCacheLoader
// } = require('../cache-loader') // } = require('../cache-loader')
const runtimePath = '@dcloudio/uni-mp-weixin/dist/mp.js'
function getProvides () { function getProvides () {
return { return {
'__f__': [path.resolve(__dirname, '../format-log.js'), 'default'], '__f__': [path.resolve(__dirname, '../format-log.js'), 'default'],
'Behavior': ['@dcloudio/uni-mp-weixin/dist/mp.js', 'Behavior'], 'Behavior': [runtimePath, 'Behavior'],
'Component': ['@dcloudio/uni-mp-weixin/dist/mp.js', 'Component'] 'Component': [runtimePath, 'Component'],
'getDate': [runtimePath, 'getDate'],
'getRegExp': [runtimePath, 'getRegExp']
} }
} }
const v3 = { const v3 = {
vueConfig: { vueConfig: {
parallel: false parallel: false,
transpileDependencies: [
runtimePath
]
}, },
webpackConfig (webpackConfig, vueOptions, api) { webpackConfig (webpackConfig, vueOptions, api) {
const isAppService = !!vueOptions.pluginOptions['uni-app-plus']['service'] const isAppService = !!vueOptions.pluginOptions['uni-app-plus']['service']
......
...@@ -4,10 +4,17 @@ import { ...@@ -4,10 +4,17 @@ import {
import polyfill from './polyfill' import polyfill from './polyfill'
export * from './wxs'
global['__wxRoute'] = []
export function Component (options) { export function Component (options) {
const componentOptions = parseComponent(options) const componentOptions = parseComponent(options)
componentOptions.mixins.unshift(polyfill) componentOptions.mixins.unshift(polyfill)
global['__wxComponents'][global['__wxRoute']] = componentOptions if (!global['__wxComponents']) {
global['__wxComponents'] = Object.create(null)
}
global['__wxComponents'][global['__wxRoute'].pop()] = componentOptions
} }
export function Behavior (options) { export function Behavior (options) {
......
...@@ -14,7 +14,10 @@ function parseProperty (name, property, watch) { ...@@ -14,7 +14,10 @@ function parseProperty (name, property, watch) {
type.push(...property.optionalTypes) type.push(...property.optionalTypes)
} }
const prop = Object.create(null) const prop = Object.create(null)
prop.type = type prop.type = type.filter(sType => sType !== null) // remove null
if (!prop.type.length) {
delete prop.type
}
if (hasOwn(property, 'value')) { if (hasOwn(property, 'value')) {
prop['default'] = property.value prop['default'] = property.value
} }
......
/**
* wxs getRegExp
*/
export function getRegExp () {
const args = Array.prototype.slice.call(arguments)
args.unshift(RegExp)
return new (Function.prototype.bind.apply(RegExp, args))()
}
/**
* wxs getDate
*/
export function getDate () {
const args = Array.prototype.slice.call(arguments)
args.unshift(Date)
return new (Function.prototype.bind.apply(Date, args))()
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册