supported source-map
diff --git a/weex-loader/package.json b/weex-loader/package.json
index bc50969..78af6c8 100644
--- a/weex-loader/package.json
+++ b/weex-loader/package.json
@@ -49,6 +49,8 @@
     "weex-transformer": "^0.3.1"
   },
   "babel": {
-    "presets": ["es2015"]
+    "presets": [
+      "es2015"
+    ]
   }
 }
diff --git a/weex-loader/src/index.js b/weex-loader/src/index.js
index fbec04e..b3d4145 100644
--- a/weex-loader/src/index.js
+++ b/weex-loader/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/weex-loader/src/map.js b/weex-loader/src/map.js
new file mode 100644
index 0000000..6a925dd
--- /dev/null
+++ b/weex-loader/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/weex-loader/src/parser.js b/weex-loader/src/parser.js
index ac1dd76..24910de 100644
--- a/weex-loader/src/parser.js
+++ b/weex-loader/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/weex-loader/src/util.js b/weex-loader/src/util.js
index 3417388..d631779 100644
--- a/weex-loader/src/util.js
+++ b/weex-loader/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#####'