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

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

const migrate = require('../lib/index')
migrate('/Users/fxy/Documents/demo/my-v3-mp/src/wxcomponents',
'/Users/fxy/Documents/demo/my-v3-mp/src/wxcomponents-vue')
migrate('/Users/fxy/Downloads/wa-vantui_1.1/wxcomponents/vant',
'/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 validate = require('./validate')
const migraters = {
......@@ -15,9 +17,16 @@ module.exports = function migrate(input, out, options = {
if (!validate(input, out, options)) {
return
}
migrater.transform(input, out, options).forEach(file => {
console.log(`写入: ${file.path}`)
console.log(`${file.content}`)
// fs.outputFileSync(file.path, file.content)
const [files, assets] = migrater.transform(input, out, options)
files.forEach(file => {
console.log(`write: ${file.path}`)
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 = {
options: {
file: {
extname: '.wxml'
extname: {
template: '.wxml',
style: '.wxss'
}
},
transform: require('./transform')
......
const transformJson = require('./json-transformer')
const transformWxml = require('./wxml-transformer')
const transformWxss = require('./wxss-transformer')
const transformJs = require('./js-transformer')
const {
transformJsonFile
} = require('./json-transformer')
const {
transformTemplateFile
} = require('./template-transformer')
const {
transformStyleFile
} = require('./style-transformer')
const {
transformScriptFile
} = require('./script-transformer')
module.exports = function transformFile(input, out, options) {
const filepath = input.replace('.wxml', '')
module.exports = function transformFile(input, options) {
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 scriptCode = transformJs(filepath + '.js', usingComponentsCode, options)
const [usingComponentsCode] = transformJsonFile(filepath + '.json', deps)
return `
<template>
const [templateCode, wxsCode = ''] = transformTemplateFile(filepath + templateExtname)
const styleCode = transformStyleFile(filepath + styleExtname, options, deps) || ''
const scriptCode = transformScriptFile(filepath + '.js', usingComponentsCode, options, deps)
return [
`<template>
${templateCode}
</template>
${wxsCode}
......@@ -23,6 +40,7 @@ ${scriptCode}
</script>
<style>
${styleCode}
</style>
`
</style>`,
deps
]
}
......@@ -3,41 +3,62 @@ const glob = require('glob')
const transformFile = require('./file-transformer')
function generateVueFile(input, out, options) {
const content = transformFile(input, out, options)
const [content, deps] = transformFile(input, options)
return {
path: path.resolve(out, path.basename(input).replace(options.extname, '.vue')),
content
path: path.resolve(out, path.basename(input).replace(options.extname.template, '.vue')),
content,
deps
}
}
function generateVueFolder(input, out, options) {
return glob.sync('**/*.wxml', {
cwd: input
}).map(wxmlFile => {
return generateVueFile(
path.resolve(input, wxmlFile),
path.dirname(path.resolve(out, wxmlFile)),
options
)
const extname = options.extname.template
const files = []
const assets = []
const deps = []
glob.sync('**/*', {
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
)
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) {
console.error(`暂不支持转换整个 App`)
return [
[],
[]
]
}
module.exports = function transform(input, out, options) {
const ret = []
switch (options.target) {
case 'file':
ret.push(generateVueFile(input, out, options))
break
return [
[generateVueFile(input, out, options)],
[]
]
case 'folder':
ret.push(...generateVueFolder(input, out, options))
break
return generateVueFolder(input, out, options)
case 'app':
ret.push(...generateVueApp(input, out, options))
break
return generateVueApp(input, out, options)
}
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')
module.exports = function transformJson(filepath) {
if (!fs.existsSync(filepath)) {
return ['{}']
}
function transformJson(content) {
const {
usingComponents
} = require(filepath)
} = JSON.parse(content)
if (!usingComponents) {
return ['{}']
}
const usingComponentsCode = []
Object.keys(usingComponents).forEach(name => {
usingComponentsCode.push(`'${name}': require('${usingComponents[name]}').default`)
usingComponentsCode.push(`'${name}': require('${usingComponents[name]}.vue').default`)
})
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')
const parse = require('./parser')
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) {
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) {
return ''
}
......@@ -25,17 +34,29 @@ function genElement(node) {
}
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) {
if (node.children.length > 1) {
return [genChildren({
type: 'tag',
name: 'view',
attribs: {},
children: node.children
}), genWxs(state.wxs)]
if (shouldWrapper(node)) {
return [`<view>${genChildren(node).trim()}</view>`, genWxs(state.wxs)]
}
return [genChildren(node), genWxs(state.wxs)]
return [genChildren(node).trim(), genWxs(state.wxs)]
}
......@@ -8,12 +8,32 @@ const ATTRS = {
'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 isIdentifier = tokens.length === 1
return tokens.map(token => {
if (token[0] === 'text') {
if (identifier) {
return token[1]
}
return `'${token[1]}'`
} else if (token[0] === '!') { // {{ !loading }}
return `!${token[1]}`
} else if (token[0] === 'name') {
if (isIdentifier) {
return token[1]
......@@ -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 catchRE = /catch:?/
const captureBindRE = /capture-bind:?/
......@@ -55,6 +93,12 @@ function transformEvent(name, value, attribs) {
function transformAttr(name, value, attribs) {
if (
name.indexOf('v-') === 0 ||
name.indexOf(':') === 0
) { // 已提前处理
return
}
delete attribs[name]
if (transformDirective(name, value, attribs)) {
return
......@@ -74,6 +118,7 @@ function transformAttrs(node, state) {
if (!attribs) {
return
}
transformFor(attribs)
Object.keys(attribs).forEach(name => {
transformAttr(name, attribs[name], attribs)
})
......
......@@ -12,12 +12,13 @@ module.exports = function validate(input, out, options) {
}
const platformOptions = migraters[options.platform]
options.extname = platformOptions.file.extname
options.extname = platformOptions.extname
const templateExtname = options.extname.template
const stat = fs.lstatSync(input)
if (stat.isFile()) {
if (path.extname(input) !== options.extname) {
return console.error(`错误: 单文件转换需要传入 ${options.extname.substr(1)} 文件地址`)
if (path.extname(input) !== templateExtname) {
return console.error(`错误: 单文件转换需要传入 ${templateExtname.substr(1)} 文件地址`)
}
options.target = 'file'
} else if (stat.isDirectory()) {
......
......@@ -34,7 +34,10 @@ function parseProperty (name, property, watch) {
type.push(...property.optionalTypes);
}
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')) {
prop['default'] = property.value;
}
......@@ -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) {
const componentOptions = parseComponent(options);
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) {
return options
}
export { Behavior, Component };
export { Behavior, Component, getDate, getRegExp };
......@@ -64,13 +64,13 @@ module.exports = function (source) {
const frame = compiler.generateCodeFrame(source, start, end)
return ` ${msg}\n\n${pad(frame)}`
}).join(`\n\n`) +
'\n'
'\n at ' + finalOptions.filename + ':0' // fixed by xxxxxx
)
} else {
loaderContext.emitError(
`\n Error compiling template:\n${pad(compiled.source)}\n` +
compiled.errors.map(e => ` - ${e}`).join('\n') +
'\n'
'\n at ' + finalOptions.filename + ':0' // fixed by xxxxxx
)
}
}
......
......@@ -16,17 +16,24 @@ const {
// createTemplateCacheLoader
// } = require('../cache-loader')
const runtimePath = '@dcloudio/uni-mp-weixin/dist/mp.js'
function getProvides () {
return {
'__f__': [path.resolve(__dirname, '../format-log.js'), 'default'],
'Behavior': ['@dcloudio/uni-mp-weixin/dist/mp.js', 'Behavior'],
'Component': ['@dcloudio/uni-mp-weixin/dist/mp.js', 'Component']
'Behavior': [runtimePath, 'Behavior'],
'Component': [runtimePath, 'Component'],
'getDate': [runtimePath, 'getDate'],
'getRegExp': [runtimePath, 'getRegExp']
}
}
const v3 = {
vueConfig: {
parallel: false
parallel: false,
transpileDependencies: [
runtimePath
]
},
webpackConfig (webpackConfig, vueOptions, api) {
const isAppService = !!vueOptions.pluginOptions['uni-app-plus']['service']
......
......@@ -4,10 +4,17 @@ import {
import polyfill from './polyfill'
export * from './wxs'
global['__wxRoute'] = []
export function Component (options) {
const componentOptions = parseComponent(options)
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) {
......
......@@ -14,7 +14,10 @@ function parseProperty (name, property, watch) {
type.push(...property.optionalTypes)
}
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')) {
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.
先完成此消息的编辑!
想要评论请 注册