blob: ac1dd763bb2390781363845e40297be88c8c482c [file] [log] [blame]
import blocker from 'weex-transformer/lib/blocker'
import templater from 'weex-templater'
import styler from 'weex-styler'
import scripter from 'weex-scripter'
import md5 from 'md5'
import { transformerVersion } from './config'
import {
MODULE_EXPORTS_REG,
REQUIRE_REG,
FUNC_START_REG,
FUNC_END_REG,
getNameByPath,
checkFileExist,
depHasRequired,
stringifyFunction,
appendToWarn
} from './util'
export function parseWeexFile (loader, params, source, deps, elementName) {
return new Promise(
// separate source into <element>s, <template>, <style>s and <script>s
separateBlocks(source, deps || []))
// pre-parse non-javascript parts
.then(preParseBlocks(loader, params, elementName))
// join blocks together and parse as javascript finally
.then(parseBlocks(loader, params, elementName))
}
function separateBlocks (source, deps) {
return (resolve, reject) => {
blocker.format(source, (err, ret) => {
if (err) {
reject(err)
}
else {
ret.deps = deps
resolve(ret)
}
})
}
}
function preParseBlocks (loader, params) {
return (blocks) => {
const { deps, elements, template, styles, scripts, config, data } = blocks
const promises = [
Promise.resolve(),
Promise.resolve(),
Promise.resolve(),
Promise.resolve(scripts),
Promise.resolve(deps),
Promise.resolve(config),
Promise.resolve(data)
]
let content
// pre-parse sub elements
if (elements) {
const elPromises = []
Object.keys(elements).forEach(key => {
const el = elements[key]
elPromises.push(parseWeexFile(loader, params, el.content, deps, el.name))
})
promises[0] = Promise.all(elPromises)
}
// pre-parse template
if (template) {
content = template.content
promises[1] = parseTemplate(loader, params, content, deps)
}
// pre-parse styles
if (styles) {
content = styles.reduce((pre, cur) => {
return pre + '\n' + cur.content
}, '')
promises[2] = parseStyle(loader, params, content)
}
return Promise.all(promises)
}
}
function parseBlocks (loader, params, elementName) {
return (results) => {
const elements = results[0] || []
const template = results[1]
const style = results[2]
const scripts = results[3]
const deps = results[4] || []
const configResult = results[5]
const dataResult = results[6]
let content = ''
let config = {}
let data
if (scripts) {
content += scripts.reduce((pre, cur) => {
return pre + '\n;' + cur.content
}, '')
}
let requireContent = ''
if (deps.length) {
requireContent += deps.map(dep =>
depHasRequired(content, dep) ? 'require("' + dep + '");' : ''
).join('\n')
content = requireContent + '\n' + content
}
if (template) {
content += '\n;module.exports.template = module.exports.template || {}' +
'\n;Object.assign(module.exports.template, ' + template + ')'
}
if (style) {
content += '\n;module.exports.style = module.exports.style || {}' +
'\n;Object.assign(module.exports.style, ' + style + ')'
}
if (configResult) {
config = new Function('return ' + configResult.content.replace(/\n/g, ''))()
}
config.transformerVersion = transformerVersion
config = JSON.stringify(config, null, 2)
if (dataResult) {
data = new Function('return ' + dataResult.content.replace(/\n/g, ''))()
data = JSON.stringify(data, null, 2)
}
return parseScript(loader, params, content, { config, data, elementName, elements })
}
}
export function parseTemplate (loader, params, source, deps) {
return new Promise((resolve, reject) => {
templater.parse(source, (err, obj) => {
if (err) {
reject(err)
}
else {
appendToWarn(loader, obj.log)
// push valid obj.deps to deps
if (deps && obj.deps) {
obj.deps.map(
dep => checkFileExist(dep, params.resourcePath)
).forEach(dep => {
if (dep) {
deps.push(dep)
}
})
}
// parse json to string and treat function specially
let target = JSON.stringify(obj.jsonTemplate, stringifyFunction, ' ')
target = target.replace(FUNC_START_REG, '').replace(FUNC_END_REG, '')
resolve(target)
}
})
})
}
export function parseStyle (loader, params, source) {
return new Promise((resolve, reject) => {
styler.parse(source, (err, obj) => {
if (err) {
reject(err)
}
else {
appendToWarn(loader, obj.log)
resolve(JSON.stringify(obj.jsonStyle, null, 2))
}
})
})
}
export function parseScript (loader, params, source, env) {
const { config, data, elementName, elements } = env
// the entry component has a special resource query and not a sub element tag
const isEntry = params.resourceQuery.entry === true && !elementName
// resolve component name
const name = isEntry
? md5(source)
: (elementName || params.resourceQuery.name || getNameByPath(params.resourcePath))
// fix data option from an object to a function
let target = scripter.fix(source)
// wrap with __weex_define__(name, [], (r, e, m) {...})
target = target
.replace(MODULE_EXPORTS_REG, '__weex_module__.exports')
.replace(REQUIRE_REG, '__weex_require__($1$2$1)')
target = ';__weex_define__("@weex-component/' + name + '", [], ' +
'function(__weex_require__, __weex_exports__, __weex_module__)' +
'{\n' + target + '\n})'
// append __weex_bootstrap__ for entry component
if (isEntry) {
target += '\n;__weex_bootstrap__("@weex-component/' + name + '", ' +
String(config) + ',' +
String(data) + ')'
}
// join with elements deps
target = (elements || []).concat(target).join(';\n\n')
return Promise.resolve(target)
}