Merge branch 'master' into sourcemap
diff --git a/src/index.js b/src/index.js
index fbec04e..b3d4145 100644
--- a/src/index.js
+++ b/src/index.js
@@ -4,12 +4,14 @@
parseScript,
parseStyle,
parseTemplate,
- parseWeexFile
+ parseWeex
} from './parser'
+import { getFilenameByPath } from './util'
import * as config from './config'
import * as legacy from './legacy'
+import { ScriptMap } from './map'
-function partedLoader (type, loader, params, source) {
+function partedLoader (type, loader, params, source, map) {
let promise
switch (type) {
case 'js':
@@ -29,7 +31,8 @@
break
case 'we':
default:
- promise = parseWeexFile(loader, params, source)
+ map.enable()
+ promise = parseWeex(loader, params, source, map)
break
}
return promise
@@ -45,15 +48,23 @@
resourcePath: this.resourcePath
}
const type = params.loaderQuery.type || 'we'
- const promise = partedLoader(type, this, params, source)
+ const { resourcePath } = params
+ const filename = getFilenameByPath(resourcePath)
+ const map = new ScriptMap(filename, source)
+
+ const promise = partedLoader(type, this, params, source, map)
promise.then(result => {
+ if (map.enabled) {
+ map.parse()
+ }
if (type === 'style' || type === 'css' ||
type === 'html' || type === 'tpl' || type === 'template') {
result = 'module.exports=' + result
}
- callback(null, result)
+ callback(null, result, map.toJSON())
}).catch(err => {
+ // console.error(err.stack)
this.emitError(err.toString())
callback(err.toString(), '')
})
diff --git a/src/map.js b/src/map.js
new file mode 100644
index 0000000..6a925dd
--- /dev/null
+++ b/src/map.js
@@ -0,0 +1,106 @@
+import { SourceMapGenerator } from 'source-map'
+
+export class ScriptMap {
+ constructor (filename, content) {
+ this.filename = filename
+ this.content = content
+ const generator = new SourceMapGenerator()
+ generator.setSourceContent(filename, content)
+ this.generator = generator
+ this.history = []
+ this.elements = {}
+ this.enabled = false
+ }
+
+ enable () {
+ this.enabled = true
+ }
+
+ start () {
+ if (!this.enabled) { return }
+ this.current = { elements: [], scripts: [] }
+ }
+
+ end () {
+ if (!this.enabled) { return }
+ const current = this.current
+ this.current = {}
+
+ const length = current.elements.length
+ if (length > 0) {
+ const children = this.history.splice(-length, length)
+ current.children = children
+ }
+
+ current.elements.concat(current.scripts).forEach(item => {
+ current.name = item.name
+ delete item.name
+ })
+
+ current.elements.forEach((info, index) => {
+ current.children[index].length = info.length
+ current.children[index].line = info.line
+ })
+
+ delete current.elements
+ this.history.push(current)
+ }
+
+ addElement (name, index, line, length) {
+ if (!this.enabled) { return }
+ this.current.elements.push({ name, index, line, length })
+ }
+ addScript (name, info, externalOffset) {
+ if (!this.enabled) { return }
+ this.current.scripts.push({ name, info, externalOffset })
+ }
+ setElementPosition (name, line, column) {
+ if (!this.enabled) { return }
+ this.elements[name] = { line, column }
+ }
+
+ parse (target, startLine) {
+ if (!this.enabled) { return }
+ target = target || this.history[0]
+ if (!target) { return }
+ startLine = startLine || 0
+
+ const { name, line, scripts, children } = target
+ const elInfo = this.elements[name] || {};
+
+ (scripts || []).forEach(script => {
+ const { info, externalOffset } = script
+ const { original, generated } = info
+ const scriptLength = info.length
+ this.add(
+ original.line + (elInfo.line || 1) - 2,
+ scriptLength,
+ generated.line + startLine + (line || 1) + externalOffset
+ )
+ });
+
+ (children || []).forEach(child => {
+ this.parse(child, startLine + (line || 1) - 1)
+ })
+
+ this.json = true
+ }
+
+ add (originalLine, length, generatedLine) {
+ if (!this.enabled) { return }
+ const option = {
+ source: this.filename,
+ original: { line: originalLine, column: 1 },
+ generated: { line: generatedLine, column: 1 }
+ }
+ for (let i = 0; i < length; i++) {
+ option.original.line = originalLine + i
+ option.generated.line = generatedLine + i
+ this.generator.addMapping(option)
+ }
+ }
+
+ toJSON () {
+ return this.json ? this.generator.toJSON() : null
+ }
+}
diff --git a/src/parser.js b/src/parser.js
index ac1dd76..24910de 100644
--- a/src/parser.js
+++ b/src/parser.js
@@ -18,14 +18,14 @@
appendToWarn
} from './util'
-export function parseWeexFile (loader, params, source, deps, elementName) {
+export function parseWeex (loader, params, source, map, 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))
+ .then(preParseBlocks(loader, params, map))
// join blocks together and parse as javascript finally
- .then(parseBlocks(loader, params, elementName))
+ .then(parseBlocks(loader, params, map, elementName))
}
function separateBlocks (source, deps) {
@@ -42,7 +42,7 @@
}
}
-function preParseBlocks (loader, params) {
+function preParseBlocks (loader, params, map) {
return (blocks) => {
const { deps, elements, template, styles, scripts, config, data } = blocks
const promises = [
@@ -60,7 +60,8 @@
const elPromises = []
Object.keys(elements).forEach(key => {
const el = elements[key]
- elPromises.push(parseWeexFile(loader, params, el.content, deps, el.name))
+ map.setElementPosition(el.name, el.line, el.column)
+ elPromises.push(parseWeex(loader, params, el.content, map, deps, el.name))
})
promises[0] = Promise.all(elPromises)
}
@@ -80,7 +81,7 @@
}
}
-function parseBlocks (loader, params, elementName) {
+function parseBlocks (loader, params, map, elementName) {
return (results) => {
const elements = results[0] || []
const template = results[1]
@@ -94,8 +95,19 @@
let config = {}
let data
+ const mapOffset = { basic: 0, subs: [] }
+
if (scripts) {
content += scripts.reduce((pre, cur) => {
+ const line = pre.split(/\r?\n/g).length
+ const column = 1
+ const oriLine = cur.line - 1
+ const oriColumn = cur.column
+ mapOffset.subs.push({
+ original: { line: oriLine, column: oriColumn },
+ generated: { line, column },
+ length: cur.content.split(/\r?\n/g).length
+ })
return pre + '\n;' + cur.content
}, '')
}
@@ -105,7 +117,10 @@
requireContent += deps.map(dep =>
depHasRequired(content, dep) ? 'require("' + dep + '");' : ''
).join('\n')
- content = requireContent + '\n' + content
+ if (requireContent) {
+ content = requireContent + '\n' + content
+ mapOffset.basic = requireContent.split(/\r?\n/g).length
+ }
}
if (template) {
@@ -129,7 +144,7 @@
data = JSON.stringify(data, null, 2)
}
- return parseScript(loader, params, content, { config, data, elementName, elements })
+ return parseScript(loader, params, content, { config, data, elementName, elements, map, mapOffset })
}
}
@@ -175,7 +190,7 @@
}
export function parseScript (loader, params, source, env) {
- const { config, data, elementName, elements } = env
+ const { config, data, elementName, elements, map, mapOffset } = env
// the entry component has a special resource query and not a sub element tag
const isEntry = params.resourceQuery.entry === true && !elementName
@@ -185,16 +200,29 @@
? md5(source)
: (elementName || params.resourceQuery.name || getNameByPath(params.resourcePath))
+ // join with elements deps
+ // 2 more lines at end
+ map && map.start()
+ const prefix = (elements || []).reduce((current, next, index) => {
+ map && map.addElement(name, index, current.split(/\r?\n/g).length, next.split(/\r?\n/g).length)
+ return current + next + ';\n\n'
+ }, '')
+
// fix data option from an object to a function
let target = scripter.fix(source)
// wrap with __weex_define__(name, [], (r, e, m) {...})
+ // 1 more line at start, 1 more line at end
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})'
+ mapOffset && mapOffset.subs.forEach(info => {
+ map.addScript(elementName || name, info, prefix.split(/\r?\n/g).length + mapOffset.basic)
+ })
+ map && map.end()
// append __weex_bootstrap__ for entry component
if (isEntry) {
@@ -203,9 +231,6 @@
String(data) + ')'
}
- // join with elements deps
- target = (elements || []).concat(target).join(';\n\n')
-
- return Promise.resolve(target)
+ return Promise.resolve(prefix + target)
}
diff --git a/src/util.js b/src/util.js
index 3417388..d631779 100644
--- a/src/util.js
+++ b/src/util.js
@@ -10,6 +10,10 @@
return path.basename(filepath).replace(/\..*$/, '')
}
+export function getFilenameByPath (filepath) {
+ return path.relative('.', filepath)
+}
+
export const FUNC_START = '#####FUN_S#####'
export const FUNC_START_REG = new RegExp('["\']' + FUNC_START, 'g')
export const FUNC_END = '#####FUN_E#####'