You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

234 lines
6.5 KiB

import fs from 'fs'
import path from 'path'
import * as Vue from '../../../packages/weex-vue-framework'
import { compile } from '../../../packages/weex-template-compiler'
import WeexRuntime from 'weex-js-runtime'
import styler from 'weex-styler'
const styleRE = /<\s*style\s*\w*>([^(<\/)]*)<\/\s*style\s*>/g
const scriptRE = /<\s*script.*>([^]*)<\/\s*script\s*>/
const templateRE = /<\s*template\s*([^>]*)>([^]*)<\/\s*template\s*>/
export function readFile (filename) {
return fs.readFileSync(path.resolve(__dirname, '../cases/', filename), 'utf8')
}
export function readObject (filename) {
return (new Function(`return ${readFile(filename)}`))()
}
console.debug = () => {}
// http://stackoverflow.com/a/35478115
const matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g
export function strToRegExp (str) {
return new RegExp(str.replace(matchOperatorsRe, '\\$&'))
}
function parseStatic (fns) {
return '[' + fns.map(fn => `function () { ${fn} }`).join(',') + ']'
}
export function compileAndStringify (template) {
const { render, staticRenderFns } = compile(template)
return {
render: `function () { ${render} }`,
staticRenderFns: parseStatic(staticRenderFns)
}
}
/**
* Compile *.vue file into js code
* @param {string} source raw text of *.vue file
* @param {string} componentName whether compile to a component
*/
export function compileVue (source, componentName) {
return new Promise((resolve, reject) => {
if (!templateRE.test(source)) {
return reject('No Template!')
}
const scriptMatch = scriptRE.exec(source)
const script = scriptMatch ? scriptMatch[1] : ''
const templateMatch = templateRE.exec(source)
const compileOptions = {}
if (/\s*recyclable\=?/i.test(templateMatch[1])) {
compileOptions.recyclable = true
}
const res = compile(templateMatch[2], compileOptions)
const name = 'test_case_' + (Math.random() * 99999999).toFixed(0)
const generateCode = styles => (`
try { weex.document.registerStyleSheets("${name}", [${JSON.stringify(styles)}]) } catch(e) {};
var ${name} = Object.assign({
_scopeId: "${name}",
style: ${JSON.stringify(styles)},
render: function () { ${res.render} },
${res['@render'] ? ('"@render": function () {' + res['@render'] + '},') : ''}
staticRenderFns: ${parseStatic(res.staticRenderFns)},
}, (function(){
var module = { exports: {} };
${script};
return module.exports;
})());
` + (componentName
? `Vue.component('${componentName}', ${name});\n`
: `${name}.el = 'body';new Vue(${name});`)
)
let cssText = ''
let styleMatch = null
while ((styleMatch = styleRE.exec(source))) {
cssText += `\n${styleMatch[1]}\n`
}
styler.parse(cssText, (error, result) => {
if (error) {
return reject(error)
}
resolve(generateCode(result.jsonStyle))
})
resolve(generateCode({}))
})
}
export function compileWithDeps (entryPath, deps) {
return new Promise((resolve, reject) => {
if (Array.isArray(deps)) {
Promise.all(deps.map(dep => {
return compileVue(readFile(dep.path), dep.name).catch(reject)
})).then(depCodes => {
compileVue(readFile(entryPath)).then(entryCode => {
resolve(depCodes.join('\n') + entryCode)
}).catch(reject)
}).catch(reject)
}
})
}
function isObject (object) {
return object !== null && typeof object === 'object'
}
function isEmptyObject (object) {
return isObject(object) && Object.keys(object).length < 1
}
function omitUseless (object) {
if (isObject(object)) {
delete object.ref
for (const key in object) {
omitUseless(object[key])
if (key === '@styleScope' ||
key === '@templateId' ||
key === 'bindingExpression') {
delete object[key]
}
if (key.charAt(0) !== '@' &&
(isEmptyObject(object[key]) || object[key] === undefined)) {
delete object[key]
}
}
}
return object
}
export function getRoot (instance) {
return omitUseless(instance.$getRoot())
}
// Get all binding events in the instance
export function getEvents (instance) {
const events = []
const recordEvent = node => {
if (!node) { return }
if (Array.isArray(node.event)) {
node.event.forEach(type => {
events.push({ ref: node.ref, type })
})
}
if (Array.isArray(node.children)) {
node.children.forEach(recordEvent)
}
}
recordEvent(instance.$getRoot())
return events
}
export function fireEvent (instance, ref, type, event = {}) {
const el = instance.document.getRef(ref)
if (el) {
instance.document.fireEvent(el, type, event)
}
}
export function createInstance (id, code, ...args) {
WeexRuntime.config.frameworks = { Vue }
const context = WeexRuntime.init(WeexRuntime.config)
context.registerModules({
timer: ['setTimeout', 'setInterval']
})
const instance = context.createInstance(id, `// { "framework": "Vue" }\n${code}`, ...args) || {}
instance.document = context.getDocument(id)
instance.$getRoot = () => context.getRoot(id)
instance.$refresh = (data) => context.refreshInstance(id, data)
instance.$destroy = () => {
delete instance.document
context.destroyInstance(id)
}
instance.$triggerHook = (id, hook, args) => {
instance.document.taskCenter.triggerHook(id, 'lifecycle', hook, { args })
}
return instance
}
export function compileAndExecute (template, additional = '') {
return new Promise(resolve => {
const id = String(Date.now() * Math.random())
const { render, staticRenderFns } = compile(template)
const instance = createInstance(id, `
new Vue({
el: '#whatever',
render: function () { ${render} },
staticRenderFns: ${parseStatic(staticRenderFns)},
${additional}
})
`)
setTimeout(() => resolve(instance), 10)
})
}
export function syncPromise (arr) {
let p = Promise.resolve()
arr.forEach(item => {
p = p.then(item)
})
return p
}
export function checkRefresh (instance, data, checker) {
return () => new Promise(res => {
instance.$refresh(data)
setTimeout(() => {
checker(getRoot(instance))
res()
})
})
}
export function addTaskHook (hook) {
global.callNative = function callNative (id, tasks) {
if (Array.isArray(tasks) && typeof hook === 'function') {
tasks.forEach(task => {
hook(id, {
module: task.module,
method: task.method,
args: Array.from(task.args)
})
})
}
}
}
export function resetTaskHook () {
delete global.callNative
}