refactoring
diff --git a/.gitignore b/.gitignore
index a76bb72..074de94 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,3 @@
 node_modules
 lib
-
-test/actual
-test/expect/*.js
+test/actual/*.js
\ No newline at end of file
diff --git a/README.md b/README.md
index 073d86a..321c3a2 100644
--- a/README.md
+++ b/README.md
@@ -5,16 +5,16 @@
 ## Install
 
 ```
-npm install weex-loader --save
+npm install weex-loader babel-loader babel-preset-es2015 babel-runtime babel-plugin-transform-runtime --save
 ```
 
 ## Feature
 
 0. Can load `.we` file.
-1. Can load parted files(`.js/.css/.tpl`) instead of one `.we` file.
-2. Can chain any loader you want when write parted files.
-3. Can require a CommonJS module.
-4. Can specify the name of a component.
+1. Can load parted files(`.js/.css/.html`) via a `src` attribute.
+2. Can specify a custom language to chain any loader.
+3. Can specify name when require `.we` file.
+4. Can write es2015 in script.
 
 ## Usage
 
@@ -45,18 +45,17 @@
 
 ### How to write parted files
 
-#### write .js/.css/.tpl anywhere
+#### write .js/.css/.html anywhere
 
 **main.js as script**
 ```javascript
 module.exports = {
-    data: {
+    data: function () {
+      return {
         text: 'Hello World'
+      }
     }
 }
-
-module.exports.style = require('./main.css');
-module.exports.template = require('./main.tpl');
 ```
 
 **main.css as style**
@@ -64,46 +63,40 @@
 .h1 {
     font-size: 60px;
     color: red;
-    padding-top: 20px;
-    padding-bottom: 20px;
-    padding-left: 20px;
-    padding-right: 20px;
 }
 ```
 
-**main.tpl as template**
+**main.html as template**
 ```html
-<container>
+<div>
     <text class="h1">{{text}}</text>
-</container>
+</div>
 ```
 
-Then change the entry to `main.js` in `webpack.config.js`
+**main.we**
+```html
+<template src="./main.html"></template>
+<style src="./main.css"></style>
+<script src="./main.js"></script>
+```
 
-#### add some loader in webpack config
+#### add some custom language for loaders
 
-**loader for script**
+**append a weex config in webpack config**
 ```javascript
-  {
-    test: /\.js(\?[^?]+)?$/,
-    loader: 'weex?type=script'
+  weex: {
+    lang: {
+      jade: ['jade-html'] // a jade langauge will chain "jade-html-loader"
+    }
   }
 ```
 
-**loader for style**
-```javascript
-  {
-    test: /\.css(\?[^?]+)?$/, 
-    loader: 'weex?type=style'
-  }
+**main.we**
 ```
-
-**loader for template**
-```javascript
-  {
-    test: /\.tpl(\?[^?]+)?$/, 
-    loader: 'weex?type=tpl'
-  }
+<template lang="jade">
+div
+  text Hello Weex
+</template>
 ```
 
 ### How to require a CommonJS module
@@ -111,41 +104,45 @@
 0. first, require a `path/to/module.js` in `script` like `var _ = require('lodash')`. 
 1. then use it in `script`.
 
-### How to embed a composed component
+### How to require `.we` file as component element
 
-0. first, require a `path/to/component.js` in `script` like `require('./sub.js')`.
-1. second, use it in `template` like `<sub></sub>`.
+0. first, require a `path/to/component.we` in `script` like `require('./foo.we')` or write inline element like `<element name="foo" src="./foo.we"></element>`.
+1. second, use it in `template` like `<foo></foo>`.
+
+```
+<element name="foo" src="./foo.we"></element>
+
+<template>
+  <div>
+    <foo></foo>
+    <bar></bar>
+  </div>
+</template>
+
+<script>
+  require('./bar.we')
+</script>
+```
 
 ### How to specify the name of a component
 
 0. By default, the name is the basename without extname of component path.
-1. Give a name query in require request, like `require('./sub.js?name="goto"')`
-2. use the name in `template` like `<goto></goto>`.
+1. Give a name query in require request, like `require('./foo.we?name="fooo"')`. Or specify a name attribute in element, like `<element name="fooo" src="./foo.we" ></element>`
+2. use the name in `template` like `<fooo></fooo>`.
 
-## Chain your favorite loader
-
-For examples:
-
-### write ES2015
-
-Only your need is chain babel-loader before weex-loader.
-
-```javascript
-  {
-    test: /\.js(\?[^?]+)?$/,
-    loader: 'weex?type=script!babel?presets[]=es2015'
-  }
 ```
+<element name="fooo" src="./foo.we"></element>
 
-### write SCSS
+<template>
+  <div>
+    <fooo></fooo>
+    <barr></barr>
+  </div>
+</template>
 
-Only your need is chain scss loader before weex-loader.
-
-```javascript
-  {
-    test: /\.scss(\?[^?]+)?$/, 
-    loader: 'weex?type=style!scss'
-  }
+<script>
+  require('./bar.we?name=barr')
+</script>
 ```
 
 ## Test
@@ -154,8 +151,3 @@
 npm run test
 ```
 will run mocha testing
-
-```bash
-npm run serve
-```
-then open `localhost:12581` on chrome, will run web testing
diff --git a/circle.yml b/circle.yml
index 1bd54b3..fe46540 100644
--- a/circle.yml
+++ b/circle.yml
@@ -1,3 +1,3 @@
 machine:
   node:
-    version: 4.2.1
+    version: 4
diff --git a/index.js b/index.js
index 9615572..18abae3 100644
--- a/index.js
+++ b/index.js
@@ -1 +1 @@
-module.exports = require('./lib/')
+module.exports = require('./lib/loader')
diff --git a/package.json b/package.json
index 2878299..70df19e 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "weex-loader",
-  "version": "0.2.0",
+  "version": "0.3.0-alpha",
   "description": "a webpack loader for weex",
   "main": "index.js",
   "author": "terrykingcha <terrykingcha@gmail.com>",
@@ -16,29 +16,30 @@
     "transformer"
   ],
   "scripts": {
-    "prepublish": "node_modules/babel-cli/bin/babel.js src --out-dir lib",
-    "test:lint": "eslint src",
+    "clear": "rm -f lib/* && rm -f test/actual/*",
+    "lint": "eslint --fix src",
+    "build": "node_modules/babel-cli/bin/babel.js src --out-dir lib",
     "test:build": "webpack --config test/webpack.config.js",
-    "test:transform": "transformer test/expect/*.we -o test/expect",
     "test:mocha": "mocha test/test.js",
-    "test": "npm run prepublish && npm run test:lint && npm run test:build && npm run test:transform && npm run test:mocha",
-    "serve": "serve ./test -p 12581",
-    "prebuild": "rm -f lib/* && node_modules/babel-cli/bin/babel.js src --out-dir lib",
-    "build": "webpack --config test/webpack.config.js"
+    "pretest": "npm run clear && npm run lint && npm run build",
+    "test": "npm run test:build && npm run test:mocha",
+    "ci": "npm run test",
+    "prepublish": "npm run clear && npm run lint && npm run build"
   },
   "devDependencies": {
     "babel-cli": "^6.10.1",
     "babel-core": "^6.10.4",
     "babel-loader": "^6.2.4",
+    "babel-plugin-transform-runtime": "^6.9.0",
     "babel-preset-es2015": "^6.9.0",
+    "babel-runtime": "^6.9.2",
     "chai": "^3.5.0",
     "eslint": "^2.13.1",
-    "less": "^2.6.1",
-    "less-loader": "^2.2.3",
-    "memory-fs": "^0.3.0",
+    "jade-html-loader": "0.0.3",
     "mocha": "^2.4.5",
     "parse5": "^2.1.5",
-    "serve": "^1.4.0",
+    "postcss-cssnext": "^2.7.0",
+    "postcss-loader": "^0.9.1",
     "sinon": "^1.17.3",
     "sinon-chai": "^2.8.0",
     "webpack": "~1.13.0"
diff --git a/src/config.js b/src/config.js
index c1e9e30..ded0c99 100644
--- a/src/config.js
+++ b/src/config.js
@@ -1,3 +1 @@
-import pkg from '../package.json'
-export const transformerVersion = pkg.dependencies['weex-transformer'].match(/\d+(?:\.\d+){0,2}/)[0]
 export const logLevel = false
diff --git a/src/element.js b/src/element.js
new file mode 100644
index 0000000..378a261
--- /dev/null
+++ b/src/element.js
@@ -0,0 +1,34 @@
+import loaderUtils from 'loader-utils'
+
+import {
+  extractBlocks
+} from './parser'
+
+module.exports = function (source) {
+  this.cacheable && this.cacheable()
+
+  const callback = this.async()
+  const loaderQuery = loaderUtils.parseQuery(this.query)
+  const resourceQuery = loaderUtils.parseQuery(this.resourceQuery)
+  const name = resourceQuery.name
+
+  let contentPromise
+
+  if (loaderQuery.extract) {
+    contentPromise =
+      extractBlocks(source, 'elements')
+  }
+  else {
+    contentPromise = Promise.resolve({ content: source })
+  }
+
+  contentPromise.then(elements => {
+    if (loaderQuery.raw) {
+      return elements[name].content
+    }
+  }).then(result => {
+    callback(null, result)
+  }).catch(e => {
+    callback(e, '')
+  })
+}
diff --git a/src/extract.js b/src/extract.js
new file mode 100644
index 0000000..28485b5
--- /dev/null
+++ b/src/extract.js
@@ -0,0 +1,32 @@
+import loaderUtils from 'loader-utils'
+
+import {
+  extractBlocks
+} from './parser'
+
+module.exports = function (source) {
+  this.cacheable && this.cacheable()
+
+  const callback = this.async()
+  const loaderQuery = loaderUtils.parseQuery(this.query)
+  const type = loaderQuery.type
+  let index = loaderQuery.index
+
+  if (index != null && index.match(/^\d+$/)) {
+    index = parseInt(index)
+  }
+
+  extractBlocks(source, type)
+    .then(result => {
+      if (index != null) {
+        return result[index].content
+      }
+      else {
+        return result.content
+      }
+    }).then(result => {
+      callback(null, result)
+    }).catch(e => {
+      callback(e, '')
+    })
+}
diff --git a/src/html-loader.js b/src/html-loader.js
deleted file mode 100644
index 20ae43f..0000000
--- a/src/html-loader.js
+++ /dev/null
@@ -1,61 +0,0 @@
-import loaderUtils from 'loader-utils'
-import blocker from 'weex-transformer/lib/blocker'
-import templater from 'weex-templater'
-
-import {
-    FUNC_START_REG,
-    FUNC_END_REG,
-    stringifyFunction
-} from './util'
-
-function extrackBlock (source, type) {
-  return new Promise((resolve, reject) => {
-    blocker.format(source, (err, ret) => {
-      if (err) {
-        reject(err)
-      } else {
-        resolve(ret[type])
-      }
-    })
-  })
-}
-
-function parseTemplate (source) {
-  return new Promise((resolve, reject) => {
-    templater.parse(source, (err, obj) => {
-      if (err) {
-        reject(err)
-      } else {
-        // 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)
-      }
-    })
-  })
-}
-
-module.exports = function(source) {
-    this.cacheable && this.cacheable()
-    const callback = this.async()
-
-    const params = {
-        loaderQuery: loaderUtils.parseQuery(this.query),
-        resourceQuery: loaderUtils.parseQuery(this.resourceQuery),
-        resourcePath: this.resourcePath
-    }
-
-    extrackBlock(source, 'template')
-        .then(template => {
-            if (params.loaderQuery.extract) {
-                return parseTemplate(template.content)
-            } else if (params.loaderQuery.raw) {
-                callback(null, template.content)
-            }
-        }).then(result => {
-            result = `module.exports = ${result}\n`
-            callback(null, result)
-        }).catch(e => {
-            callback(e, '')
-        })
-}
\ No newline at end of file
diff --git a/src/index.js b/src/index.js
deleted file mode 100644
index fbec04e..0000000
--- a/src/index.js
+++ /dev/null
@@ -1,70 +0,0 @@
-import loaderUtils from 'loader-utils'
-
-import {
-  parseScript,
-  parseStyle,
-  parseTemplate,
-  parseWeexFile
-} from './parser'
-import * as config from './config'
-import * as legacy from './legacy'
-
-function partedLoader (type, loader, params, source) {
-  let promise
-  switch (type) {
-    case 'js':
-    case 'script':
-      const transformerVersion = config.transformerVersion
-      promise = parseScript(loader, params, source,
-        { config: JSON.stringify({ transformerVersion }) })
-      break
-    case 'css':
-    case 'style':
-      promise = parseStyle(loader, params, source)
-      break
-    case 'html':
-    case 'tpl':
-    case 'template':
-      promise = parseTemplate(loader, params, source)
-      break
-    case 'we':
-    default:
-      promise = parseWeexFile(loader, params, source)
-      break
-  }
-  return promise
-}
-
-function loader (source) {
-  this.cacheable && this.cacheable()
-
-  const callback = this.async()
-  const params = {
-    loaderQuery: loaderUtils.parseQuery(this.query),
-    resourceQuery: loaderUtils.parseQuery(this.resourceQuery),
-    resourcePath: this.resourcePath
-  }
-  const type = params.loaderQuery.type || 'we'
-  const promise = partedLoader(type, this, params, source)
-
-  promise.then(result => {
-    if (type === 'style' || type === 'css' ||
-      type === 'html' || type === 'tpl' || type === 'template') {
-      result = 'module.exports=' + result
-    }
-    callback(null, result)
-  }).catch(err => {
-    this.emitError(err.toString())
-    callback(err.toString(), '')
-  })
-}
-
-loader.setLogLevel = level => {
-  config.logLevel = level
-}
-
-for (const key in legacy) {
-  loader[key] = legacy[key]
-}
-
-module.exports = loader
diff --git a/src/json.js b/src/json.js
new file mode 100644
index 0000000..59c2977
--- /dev/null
+++ b/src/json.js
@@ -0,0 +1,5 @@
+module.exports = function (source) {
+  this.cacheable && this.cacheable()
+
+  return `module.exports = ${source}`
+}
diff --git a/src/loader.js b/src/loader.js
index a6f03cf..674cd65 100644
--- a/src/loader.js
+++ b/src/loader.js
@@ -1,120 +1,379 @@
 import loaderUtils from 'loader-utils'
 import path from 'path'
-import parse5 from 'parse5'
+import fs from 'fs'
 import md5 from 'md5'
 
+import * as config from './config'
+import * as legacy from './legacy'
 import {
-    getNameByPath
+  parseFragment
+} from './parser'
+import {
+  getNameByPath,
+  getRequireString,
+  stringifyLoaders
 } from './util'
 
 const loaderPath = __dirname
-
-function getRequire(loaderContext, loader, filepath) {
-    return 'require(' +
-                loaderUtils.stringifyRequest(
-                    loaderContext,
-                    `!!${loader}!${filepath}`
-                ) +
-           ')\n'
+const defaultLoaders = {
+  main: path.resolve(loaderPath, 'loader.js'),
+  extract: path.resolve(loaderPath, 'extract.js'),
+  template: path.resolve(loaderPath, 'template.js'),
+  style: path.resolve(loaderPath, 'style.js'),
+  script: path.resolve(loaderPath, 'script.js'),
+  json: path.resolve(loaderPath, 'json.js'),
+  babel: 'babel-loader'
 }
 
-function getAttribute (node, name) {
-  if (node.attrs) {
-    var i = node.attrs.length
-    var attr
-    while (i--) {
-      attr = node.attrs[i]
-      if (attr.name === name) {
-        return attr.value
+function getLoaderString (type, config) {
+  config = config || {}
+  let customLoader
+  let loaders
+
+  if (config.lang && config.customLang[config.lang]) {
+    customLoader = config.customLang[config.lang]
+  }
+
+  if (type === 'main') {
+    loaders = [{
+      name: defaultLoaders.main
+    }]
+    return stringifyLoaders(loaders)
+  }
+
+  if (type === 'element') {
+    loaders = [{
+      name: defaultLoaders.main,
+      query: {
+        element: config.source ? undefined : true
+      }
+    }]
+    if (!config.source) {
+      loaders.push({
+        name: defaultLoaders.extract,
+        query: {
+          index: config.name,
+          type: 'elements'
+        }
+      })
+    }
+    return stringifyLoaders(loaders)
+  }
+
+  if (type === 'template') {
+    loaders = [{
+      name: defaultLoaders.template
+    }]
+    if (customLoader) {
+      loaders = loaders.concat(customLoader)
+    }
+    if (!config.source) {
+      loaders.push({
+        name: defaultLoaders.extract,
+        query: {
+          type: 'template'
+        }
+      })
+    }
+    if (config.element) {
+      loaders.push({
+        name: defaultLoaders.extract,
+        query: {
+          index: config.elementName,
+          type: 'elements'
+        }
+      })
+    }
+    return stringifyLoaders(loaders)
+  }
+
+  if (type === 'style') {
+    loaders = [{
+      name: defaultLoaders.style
+    }]
+    if (customLoader) {
+      loaders = loaders.concat(customLoader)
+    }
+    if (!config.source) {
+      loaders.push({
+        name: defaultLoaders.extract,
+        query: {
+          index: 0,
+          type: 'styles'
+        }
+      })
+    }
+    if (config.element) {
+      loaders.push({
+        name: defaultLoaders.extract,
+        query: {
+          index: config.elementName,
+          type: 'elements'
+        }
+      })
+    }
+    return stringifyLoaders(loaders)
+  }
+
+  if (type === 'script') {
+    loaders = [{
+      name: defaultLoaders.script
+    }]
+    if (customLoader) {
+      loaders = loaders.concat(customLoader)
+    }
+    else {
+      loaders.push({
+        name: defaultLoaders.babel,
+        query: {
+          presets: ['es2015'],
+          plugins: ['transform-runtime'],
+          comments: 'false'
+        }
+      })
+    }
+    if (!config.source) {
+      loaders.push({
+        name: defaultLoaders.extract,
+        query: {
+          index: 0,
+          type: 'scripts'
+        }
+      })
+    }
+    if (config.element) {
+      loaders.push({
+        name: defaultLoaders.extract,
+        query: {
+          index: config.elementName,
+          type: 'elements'
+        }
+      })
+    }
+    return stringifyLoaders(loaders)
+  }
+
+  if (type === 'config') {
+    loaders = [{
+      name: defaultLoaders.json
+    }]
+    if (!config.source) {
+      loaders.push({
+        name: defaultLoaders.extract,
+        query: {
+          type: 'config'
+        }
+      })
+    }
+    return stringifyLoaders(loaders)
+  }
+
+  if (type === 'data') {
+    loaders = [{
+      name: defaultLoaders.json
+    }]
+    if (!config.source) {
+      loaders.push({
+        name: defaultLoaders.extract,
+        query: {
+          type: 'data'
+        }
+      })
+    }
+    return stringifyLoaders(loaders)
+  }
+}
+
+function loader (source) {
+  this.cacheable && this.cacheable()
+
+  const options = this.options.weex || {}
+  const customLang = options.lang || {}
+
+  const loaderQuery = loaderUtils.parseQuery(this.query)
+  const resourceQuery = loaderUtils.parseQuery(this.resourceQuery)
+  const resourcePath = this.resourcePath
+  const isElement = loaderQuery.element
+  const isEntry = resourceQuery.entry
+  const name = isEntry ? md5(resourcePath) :
+                          (resourceQuery.name ||
+                            getNameByPath(resourcePath))
+
+  let output = ''
+
+  const frag = parseFragment(source)
+
+  const elementNames = []
+  if (frag.element.length) {
+    for (let i = 0; i < frag.element.length; i++) {
+      const element = frag.element[i]
+      if (!element.name) {
+        this.emitError('Element block need a name attribute')
+        return ''
+      }
+      elementNames.push(element.name)
+
+      let src = resourcePath
+      if (element.src) {
+        src = element.src
+      }
+
+      output += getRequireString(
+                  this,
+                  getLoaderString('element', {
+                    customLang,
+                    name: element.name,
+                    source: element.src
+                  }),
+                  `${src}?name=${element.name}`
+                )
+    }
+  }
+
+  if (frag.deps.length) {
+    for (const dep of frag.deps) {
+      const filepath = path.resolve(path.dirname(resourcePath), `${dep}.we`)
+      if (elementNames.indexOf(dep) < 0
+            && fs.existsSync(filepath)) {
+        output += getRequireString(
+                    this,
+                    getLoaderString('main'),
+                    `./${dep}.we`
+                  )
       }
     }
   }
-}
 
-function parse(source) {
-
-  const fragment = parse5.parseFragment(source, {
-    locationInfo: true
-  })
-
-  const output = {
-    template: [],
-    style: [],
-    script: []
+  if (!frag.template.length) {
+    this.emitError('Template block is required')
+    return ''
+  }
+  else {
+    const template = frag.template[0]
+    let src = resourcePath
+    if (template.src) {
+      src = template.src
+    }
+    output += 'var __weex_template__ = ' +
+                getRequireString(
+                  this,
+                  getLoaderString('template', {
+                    customLang,
+                    lang: template.lang,
+                    element: isElement,
+                    elementName: isElement ? name : undefined,
+                    source: template.src
+                  }),
+                  src
+                )
   }
 
-  fragment.childNodes.forEach(node => {
-    const type = node.tagName
-
-    if (!output[type]) {
-        return
+  if (frag.style.length) {
+    const style = frag.style[0]
+    let src = resourcePath
+    if (style.src) {
+      src = style.src
     }
+    output += 'var __weex_style__ = ' +
+                getRequireString(
+                  this,
+                  getLoaderString('style', {
+                    customLang,
+                    lang: style.lang,
+                    element: isElement,
+                    elementName: isElement ? name : undefined,
+                    source: style.src
+                  }),
+                  src
+                )
+  }
 
-    const lang = getAttribute(node, 'lang')
-    const src = getAttribute(node, 'src')
+  if (frag.script.length) {
+    const script = frag.script[0]
+    let src = resourcePath
+    if (script.src) {
+      src = script.src
+    }
+    output += 'var __weex_script__ = ' +
+                getRequireString(
+                  this,
+                  getLoaderString('script', {
+                    customLang,
+                    lang: script.lang,
+                    element: isElement,
+                    elementName: isElement ? name : undefined,
+                    source: script.src
+                  }),
+                  src
+                )
+  }
 
-    output[type].push({
-        lang,
-        src
-    })
-  })
+  if (isEntry && frag.data.length) {
+    const data = frag.data[0]
+    let src = resourcePath
+    if (data.src) {
+      src = data.src
+    }
+    output += 'var __weex_data__ = ' +
+                getRequireString(
+                  this,
+                  getLoaderString('data', {
+                    source: data.src
+                  }),
+                  src
+                )
+  }
+
+  if (isEntry && frag.config.length) {
+    const config = frag.config[0]
+    let src = resourcePath
+    if (config.src) {
+      src = config.src
+    }
+    output += 'var __weex_config__ = ' +
+                getRequireString(
+                  this,
+                  getLoaderString('config', {
+                    source: config.src
+                  }),
+                  src
+                )
+  }
+
+  output += `
+__weex_define__('@weex-component/${name}', [], function(__weex_require__, __weex_exports__, __weex_module__) {
+` + (
+  frag.script.length > 0 ? `
+    __weex_script__(__weex_require__, __weex_exports__, __weex_module__)
+` : ''
+) +
+`
+    __weex_module__.exports = __weex_module__.exports || {}
+    __weex_module__.exports.template = __weex_template__
+` + (
+  frag.style.length > 0 ? `
+    __weex_module__.exports.style = __weex_style__
+` : ''
+) + `
+})
+`
+  if (isEntry) {
+    output += `
+__weex_bootstrap__('@weex-component/${name}'`
+  + (frag.config.length > 0 ? `,__weex_config__` : ',undefined')
+  + (frag.data.length > 0 ? `,__weex_data__` : ',undefined')
++ `)`
+  }
 
   return output
 }
 
-module.exports = function(source) {
-    this.cacheable && this.cacheable()
+loader.setLogLevel = level => {
+  config.logLevel = level
+}
 
-    const defaultLoaders = {
-        main: path.resolve(loaderPath, 'loader.js'),
-        template: path.resolve(loaderPath, 'html-loader.js?extract'),
-        style: path.resolve(loaderPath, 'style-loader.js?extract'),
-        script: path.resolve(loaderPath, 'script-loader.js?extract')
-    }
-    const options = this.options.weex || {}
-    const loaders = Object.assign({}, defaultLoaders, options.loaders || {})
-    const loaderQuery = loaderUtils.parseQuery(this.query)
-    const resourceQuery = loaderUtils.parseQuery(this.resourceQuery)
-    const resourcePath = this.resourcePath
-    const isEntry = resourceQuery.entry
-    const name =  isEntry ? md5(resourcePath) :
-                    resourceQuery.name || getNameByPath(resourcePath)
+for (const key in legacy) {
+  loader[key] = legacy[key]
+}
 
-    let output = '';
-
-    const parts = parse(source)
-
-    if (parts.template.length) {
-        const template = parts.template[0]
-        output += 'var __weex_template__ = ' + getRequire(this, loaders['template'], resourcePath)
-    }
-
-    if (parts.style.length) {
-        const style = parts.style[0]
-        output += 'var __weex_style__ = ' + getRequire(this, loaders['style'], resourcePath)
-    }
-
-    if (parts.script.length) {
-        const script = parts.script[0]
-        output += 'var __weex_script__ = ' + getRequire(this, loaders['script'], resourcePath)
-    }
-
-
-
-    output += `
-__weex_define__('@weex-component/${name}', [], function(__weex_require__, __weex_exports__, __weex_module__) {
-    __weex_module__.exports = Object.assign({}, __weex_script__, {
-        template: __weex_template__,
-        style: __weex_style__
-    })
-})\n
-`
-    if (isEntry) {
-        output += `
-__weex_bootstrap__('@weex-component/${name}')\n
-`
-    }
-
-    return output;
-
-}
\ No newline at end of file
+module.exports = loader
diff --git a/src/parser.js b/src/parser.js
index ac1dd76..3b8ad50 100644
--- a/src/parser.js
+++ b/src/parser.js
@@ -1,211 +1,141 @@
+import parse5 from 'parse5'
 import blocker from 'weex-transformer/lib/blocker'
 import templater from 'weex-templater'
 import styler from 'weex-styler'
 import scripter from 'weex-scripter'
+import { checkTagName } from 'weex-templater/lib/validator'
 
-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
+  stringifyFunction
 } 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 getAttribute (node, name) {
+  if (node.attrs) {
+    let i = node.attrs.length
+    let attr
+    while (i--) {
+      attr = node.attrs[i]
+      if (attr.name === name) {
+        return attr.value
+      }
+    }
+  }
 }
 
-function separateBlocks (source, deps) {
-  return (resolve, reject) => {
+function extractDependencies (node, deps) {
+  if (node.childNodes) {
+    node.childNodes.forEach(child => {
+      checkTagName(child, {
+        result: {},
+        deps,
+        log: []
+      })
+      extractDependencies(child, deps)
+    })
+  }
+}
+
+export function parseFragment (source) {
+  const fragment = parse5.parseFragment(source, {
+    locationInfo: true
+  })
+
+  const output = {
+    deps: [],
+    element: [],
+    template: [],
+    style: [],
+    script: [],
+    data: [],
+    config: []
+  }
+
+  fragment.childNodes.forEach(node => {
+    let type
+
+    if (node.tagName === 'script') {
+      type = getAttribute(node, 'type')
+      if (type !== 'data' && type !== 'config') {
+        type = 'script'
+      }
+    }
+    else {
+      type = node.tagName
+    }
+
+    if (!output[type]) {
+      return
+    }
+
+    const name = getAttribute(node, 'name')
+    const src = getAttribute(node, 'src')
+    const lang = getAttribute(node, 'lang')
+
+    output[type].push({
+      name,
+      src,
+      lang,
+      node
+    })
+
+    if (type === 'template') {
+      const deps = []
+      extractDependencies(node.content, deps)
+      output.deps = deps
+    }
+  })
+
+  return output
+}
+
+export function extractBlocks (source, type) {
+  return new Promise((resolve, reject) => {
     blocker.format(source, (err, ret) => {
       if (err) {
         reject(err)
       }
       else {
-        ret.deps = deps
-        resolve(ret)
+        resolve(ret[type])
       }
     })
-  }
+  })
 }
 
-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) {
+export function parseTemplate (source) {
   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)
+        let parsed = JSON.stringify(obj.jsonTemplate, stringifyFunction, '  ')
+        parsed = parsed.replace(FUNC_START_REG, '').replace(FUNC_END_REG, '')
+        resolve({ parsed, log: obj.log })
       }
     })
   })
 }
 
-export function parseStyle (loader, params, source) {
+export function parseStyle (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))
+        const parsed = JSON.stringify(obj.jsonStyle, null, 2)
+        resolve({ parsed, log: obj.log })
       }
     })
   })
 }
 
-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)
+export function parseScript (source) {
+  return new Promise((resolve, reject) => {
+    const parsed = scripter.fix(source)
+    resolve({ parsed })
+  })
 }
-
diff --git a/src/script-loader.js b/src/script-loader.js
deleted file mode 100644
index 8874f4a..0000000
--- a/src/script-loader.js
+++ /dev/null
@@ -1,51 +0,0 @@
-import loaderUtils from 'loader-utils'
-import blocker from 'weex-transformer/lib/blocker'
-import scripter from 'weex-scripter'
-
-import {
-    FUNC_START_REG,
-    FUNC_END_REG,
-    stringifyFunction
-} from './util'
-
-function extrackBlock (source, type) {
-  return new Promise((resolve, reject) => {
-    blocker.format(source, (err, ret) => {
-      if (err) {
-        reject(err)
-      } else {
-        resolve(ret[type])
-      }
-    })
-  })
-}
-
-function parseScript(source) {
-  return new Promise((resolve, reject) => {
-    resolve(scripter.fix(source))
-  })
-}
-
-module.exports = function(source) {
-    this.cacheable && this.cacheable()
-    const callback = this.async()
-
-    const params = {
-        loaderQuery: loaderUtils.parseQuery(this.query),
-        resourceQuery: loaderUtils.parseQuery(this.resourceQuery),
-        resourcePath: this.resourcePath
-    }
-
-    extrackBlock(source, 'scripts')
-        .then(scripts => {
-            if (params.loaderQuery.extract) {
-                return parseScript(scripts[0].content)
-            } else if (params.loaderQuery.raw) {
-                callback(null, scripts[0].content)
-            }
-        }).then(result => {
-            callback(null, result)
-        }).catch(e => {
-            callback(e, '')
-        })
-}
\ No newline at end of file
diff --git a/src/script.js b/src/script.js
new file mode 100644
index 0000000..b0456a5
--- /dev/null
+++ b/src/script.js
@@ -0,0 +1,34 @@
+import {
+    parseScript
+} from './parser'
+
+module.exports = function (source) {
+  this.cacheable && this.cacheable()
+
+  const callback = this.async()
+
+  parseScript(source)
+        .then(({ parsed }) => {
+          let requireList = parsed.match(/require\([^()]+?\)/g)
+
+          if (requireList && requireList.length > 0) {
+            requireList = requireList.filter(str => {
+              if (str.indexOf('@weex-module') < 0) {
+                parsed = parsed.replace(str, '')
+                return true
+              }
+              return false
+            }).join('\n')
+          }
+
+          const result = `
+${requireList || ''}
+module.exports = function(require, exports, module){
+    ${parsed}
+}
+`
+          callback(null, result)
+        }).catch(e => {
+          callback(e, '')
+        })
+}
diff --git a/src/style-loader.js b/src/style-loader.js
deleted file mode 100644
index fb3e4f4..0000000
--- a/src/style-loader.js
+++ /dev/null
@@ -1,58 +0,0 @@
-import loaderUtils from 'loader-utils'
-import blocker from 'weex-transformer/lib/blocker'
-import styler from 'weex-styler'
-
-import {
-    FUNC_START_REG,
-    FUNC_END_REG,
-    stringifyFunction
-} from './util'
-
-function extrackBlock (source, type) {
-  return new Promise((resolve, reject) => {
-    blocker.format(source, (err, ret) => {
-      if (err) {
-        reject(err)
-      } else {
-        resolve(ret[type])
-      }
-    })
-  })
-}
-
-function parseStyle(source) {
-  return new Promise((resolve, reject) => {
-    styler.parse(source, (err, obj) => {
-      if (err) {
-        reject(err)
-      } else {
-        resolve(JSON.stringify(obj.jsonStyle, null, 2))
-      }
-    })
-  })
-}
-
-module.exports = function(source) {
-    this.cacheable && this.cacheable()
-    const callback = this.async()
-
-    const params = {
-        loaderQuery: loaderUtils.parseQuery(this.query),
-        resourceQuery: loaderUtils.parseQuery(this.resourceQuery),
-        resourcePath: this.resourcePath
-    }
-
-    extrackBlock(source, 'styles')
-        .then(styles => {
-            if (params.loaderQuery.extract) {
-                return parseStyle(styles[0].content)
-            } else if (params.loaderQuery.raw) {
-                callback(null, styles[0].content)
-            }
-        }).then(result => {
-            result = `module.exports = ${result}\n`
-            callback(null, result)
-        }).catch(e => {
-            callback(e, '')
-        })
-}
\ No newline at end of file
diff --git a/src/style.js b/src/style.js
new file mode 100644
index 0000000..01de0ca
--- /dev/null
+++ b/src/style.js
@@ -0,0 +1,25 @@
+import {
+  logWarn
+} from './util'
+
+import {
+  parseStyle
+} from './parser'
+
+module.exports = function (source) {
+  this.cacheable && this.cacheable()
+
+  const callback = this.async()
+
+  parseStyle(source)
+    .then(({ parsed, log }) => {
+      if (log && log.length) {
+        logWarn(this, log)
+      }
+      return `module.exports = ${parsed}\n`
+    }).then(result => {
+      callback(null, result)
+    }).catch(e => {
+      callback(e, '')
+    })
+}
diff --git a/src/template.js b/src/template.js
new file mode 100644
index 0000000..12332cf
--- /dev/null
+++ b/src/template.js
@@ -0,0 +1,25 @@
+import {
+  logWarn
+} from './util'
+
+import {
+  parseTemplate
+} from './parser'
+
+module.exports = function (source) {
+  this.cacheable && this.cacheable()
+
+  const callback = this.async()
+
+  parseTemplate(source)
+    .then(({ parsed, log }) => {
+      if (log && log.length) {
+        logWarn(this, log)
+      }
+      return `module.exports = ${parsed}\n`
+    }).then(result => {
+      callback(null, result)
+    }).catch(e => {
+      callback(e, '')
+    })
+}
diff --git a/src/util.js b/src/util.js
index 3417388..29a7cca 100644
--- a/src/util.js
+++ b/src/util.js
@@ -1,11 +1,8 @@
-import fs from 'fs'
 import path from 'path'
+import loaderUtils from 'loader-utils'
 
 import * as config from './config'
 
-export const MODULE_EXPORTS_REG = /module\.exports/g
-export const REQUIRE_REG = /require\((["'])(@weex\-module\/[^\)\1]+)\1\)/g
-
 export function getNameByPath (filepath) {
   return path.basename(filepath).replace(/\..*$/, '')
 }
@@ -17,12 +14,12 @@
 
 export function stringifyFunction (key, value) {
   if (typeof value === 'function') {
-    return FUNC_START + value.toString() + '#####FUN_E#####'
+    return FUNC_START + value.toString() + FUNC_END
   }
   return value
 }
 
-export function appendToWarn (loader, logs) {
+export function logWarn (loader, logs) {
   if (config.logLevel && logs && logs.length) {
     logs.forEach(log => {
       loader.emitWarning(log.reason + '\t@' + log.line + ':' + log.column)
@@ -30,13 +27,40 @@
   }
 }
 
-export function checkFileExist (name, resourcePath) {
-  const context = path.dirname(resourcePath)
-  const filename = './' + name + '.we'
-  const filepath = path.resolve(context, filename)
-  return fs.existsSync(filepath) ? filename : null
+export function getRequireString (loaderContext, loader, filepath) {
+  return 'require(' +
+                loaderUtils.stringifyRequest(
+                    loaderContext,
+                    `!!${loader}!${filepath}`
+                ) +
+           ')\n'
 }
 
-export function depHasRequired (content, dep) {
-  return !content.match(new RegExp('require\\(["\']./' + path.basename(dep) + '(.we)?["\']\\)', 'g'))
+export function stringifyLoaders (loaders) {
+  return loaders.map(loader => {
+    if (typeof loader === 'string') {
+      return loader
+    }
+    else {
+      const name = loader.name
+      const query = []
+      if (loader.query) {
+        for (const k in loader.query) {
+          const v = loader.query[k]
+          if (v != null) {
+            if (v === true) {
+              query.push(k)
+            }
+            else {
+              if (v instanceof Array) {
+                query.push(`${k}[]=${v.join(',')}`)
+              }
+              query.push(`${k}=${v}`)
+            }
+          }
+        }
+      }
+      return `${name}${query.length ? ('?' + query.join('&')) : ''}`
+    }
+  }).join('!')
 }
diff --git a/test/actual/.gitkeeper b/test/actual/.gitkeeper
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/actual/.gitkeeper
diff --git a/test/expect/a.js b/test/expect/a.js
new file mode 100644
index 0000000..0cb3d87
--- /dev/null
+++ b/test/expect/a.js
@@ -0,0 +1,15 @@
+{
+  "@weex-component/14d75138cf7189f2086409e4474268dd": {
+    "template": {
+      "type": "div",
+      "children": [
+        {
+          "type": "text",
+          "attr": {
+            "value": "Hello Weex"
+          }
+        }
+      ]
+    }
+  }
+}
\ No newline at end of file
diff --git a/test/expect/b.js b/test/expect/b.js
new file mode 100644
index 0000000..67292a8
--- /dev/null
+++ b/test/expect/b.js
@@ -0,0 +1,24 @@
+{
+  "@weex-component/2e192c4e656c41953a80de02c8a657a2": {
+    "template": {
+      "type": "div",
+      "children": [
+        {
+          "type": "text",
+          "classList": [
+            "hi"
+          ],
+          "attr": {
+            "value": "Hello Weex"
+          }
+        }
+      ]
+    },
+    "style": {
+      "hi": {
+        "fontSize": 26,
+        "color": "#FF0000"
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/test/expect/c.js b/test/expect/c.js
new file mode 100644
index 0000000..590d5ea
--- /dev/null
+++ b/test/expect/c.js
@@ -0,0 +1,25 @@
+{
+  "@weex-component/5f2196e6aad549d5070457fadf5d4cc0": {
+    "data": "function data() {\n\t        return {\n\t            name: 'Weex'\n\t        };\n\t    }",
+    "template": {
+      "type": "div",
+      "children": [
+        {
+          "type": "text",
+          "classList": [
+            "hello"
+          ],
+          "attr": {
+            "value": "function () {return 'Hello ' + (this.name)}"
+          }
+        }
+      ]
+    },
+    "style": {
+      "hello": {
+        "fontSize": 26,
+        "color": "#FF0000"
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/test/expect/d.js b/test/expect/d.js
new file mode 100644
index 0000000..23fee40
--- /dev/null
+++ b/test/expect/d.js
@@ -0,0 +1,57 @@
+{
+  "@weex-component/name": {
+    "data": "function data() {\n\t        return {\n\t            name: ''\n\t        };\n\t    }",
+    "template": {
+      "type": "div",
+      "children": [
+        {
+          "type": "text",
+          "classList": [
+            "name"
+          ],
+          "attr": {
+            "value": "function () {return this.name}"
+          }
+        }
+      ]
+    },
+    "style": {
+      "name": {
+        "fontSize": 26,
+        "color": "#FF0000"
+      }
+    }
+  },
+  "@weex-component/b8ad1bad658f6c5956190b94be78d6db": {
+    "data": "function data() {\n\t        return {\n\t            name: 'Weex'\n\t        };\n\t    }",
+    "template": {
+      "type": "div",
+      "style": {
+        "flexDirection": "row"
+      },
+      "children": [
+        {
+          "type": "text",
+          "classList": [
+            "hello"
+          ],
+          "attr": {
+            "value": "Hello"
+          }
+        },
+        {
+          "type": "name",
+          "attr": {
+            "name": "function () {return this.name}"
+          }
+        }
+      ]
+    },
+    "style": {
+      "hello": {
+        "fontSize": 26,
+        "color": "#008000"
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/test/expect/e.js b/test/expect/e.js
new file mode 100644
index 0000000..b03ed52
--- /dev/null
+++ b/test/expect/e.js
@@ -0,0 +1,71 @@
+{
+  "@weex-component/hi": {
+    "data": "function data() {\n\t        return {\n\t            hi: ''\n\t        };\n\t    }",
+    "template": {
+      "type": "div",
+      "children": [
+        {
+          "type": "text",
+          "classList": [
+            "hi"
+          ],
+          "attr": {
+            "value": "function () {return this.hi}"
+          }
+        }
+      ]
+    },
+    "style": {
+      "hi": {
+        "fontSize": 26,
+        "color": "#008000"
+      }
+    }
+  },
+  "@weex-component/name": {
+    "data": "function data() {\n\t        return {\n\t            name: ''\n\t        };\n\t    }",
+    "template": {
+      "type": "div",
+      "children": [
+        {
+          "type": "text",
+          "classList": [
+            "name"
+          ],
+          "attr": {
+            "value": "function () {return this.name}"
+          }
+        }
+      ]
+    },
+    "style": {
+      "name": {
+        "fontSize": 26,
+        "color": "#FF0000"
+      }
+    }
+  },
+  "@weex-component/1fac5618e94c17a7e12486a07120a1c2": {
+    "data": "function data() {\n\t        return {\n\t            hi: 'Hello',\n\t            name: 'Weex'\n\t        };\n\t    }",
+    "template": {
+      "type": "div",
+      "style": {
+        "flexDirection": "row"
+      },
+      "children": [
+        {
+          "type": "hi",
+          "attr": {
+            "hi": "function () {return this.hi}"
+          }
+        },
+        {
+          "type": "name",
+          "attr": {
+            "name": "function () {return this.name}"
+          }
+        }
+      ]
+    }
+  }
+}
\ No newline at end of file
diff --git a/test/expect/f.js b/test/expect/f.js
new file mode 100644
index 0000000..78e3ba2
--- /dev/null
+++ b/test/expect/f.js
@@ -0,0 +1,76 @@
+{
+  "@weex-component/hi": {
+    "data": "function data() {\n\t        return {\n\t            hi: ''\n\t        };\n\t    }",
+    "template": {
+      "type": "div",
+      "children": [
+        {
+          "type": "text",
+          "classList": [
+            "hi"
+          ],
+          "attr": {
+            "value": "function () {return this.hi}"
+          }
+        }
+      ]
+    },
+    "style": {
+      "hi": {
+        "fontSize": 26,
+        "color": "#008000"
+      }
+    }
+  },
+  "@weex-component/name": {
+    "data": "function data() {\n\t        return {\n\t            name: ''\n\t        };\n\t    }",
+    "template": {
+      "type": "div",
+      "children": [
+        {
+          "type": "text",
+          "classList": [
+            "name"
+          ],
+          "attr": {
+            "value": "function () {return this.name}"
+          }
+        }
+      ]
+    },
+    "style": {
+      "name": {
+        "fontSize": 26,
+        "color": "#FF0000"
+      }
+    }
+  },
+  "@weex-component/e65e091ca4375a7c5ce7d3dc1dd9648f": {
+    "data": "function data() {\n\t        return {\n\t            hi: 'Hello',\n\t            name: 'Weex'\n\t        };\n\t    }",
+    "template": {
+      "type": "div",
+      "classList": [
+        "wrap"
+      ],
+      "children": [
+        {
+          "type": "hi",
+          "attr": {
+            "hi": "function () {return this.hi}"
+          }
+        },
+        {
+          "type": "name",
+          "attr": {
+            "name": "function () {return this.name}"
+          }
+        }
+      ]
+    },
+    "style": {
+      "wrap": {
+        "flexDirection": "row"
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/test/expect/g.js b/test/expect/g.js
new file mode 100644
index 0000000..f02f953
--- /dev/null
+++ b/test/expect/g.js
@@ -0,0 +1,76 @@
+{
+  "@weex-component/name1": {
+    "data": "function data() {\n\t        return {\n\t            name: ''\n\t        };\n\t    }",
+    "template": {
+      "type": "div",
+      "children": [
+        {
+          "type": "text",
+          "classList": [
+            "name"
+          ],
+          "attr": {
+            "value": "function () {return this.name}"
+          }
+        }
+      ]
+    },
+    "style": {
+      "name": {
+        "fontSize": 26,
+        "color": "#FF0000"
+      }
+    }
+  },
+  "@weex-component/hi1": {
+    "data": "function data() {\n\t        return {\n\t            hi: ''\n\t        };\n\t    }",
+    "template": {
+      "type": "div",
+      "children": [
+        {
+          "type": "text",
+          "classList": [
+            "hi"
+          ],
+          "attr": {
+            "value": "function () {return this.hi}"
+          }
+        }
+      ]
+    },
+    "style": {
+      "hi": {
+        "fontSize": 26,
+        "color": "#008000"
+      }
+    }
+  },
+  "@weex-component/ea6e7eb66523432eacea8252752893ce": {
+    "data": "function data() {\n\t        return {\n\t            hi: 'Hello',\n\t            name: 'Weex'\n\t        };\n\t    }",
+    "template": {
+      "type": "div",
+      "classList": [
+        "wrap"
+      ],
+      "children": [
+        {
+          "type": "hi1",
+          "attr": {
+            "hi": "function () {return this.hi}"
+          }
+        },
+        {
+          "type": "name1",
+          "attr": {
+            "name": "function () {return this.name}"
+          }
+        }
+      ]
+    },
+    "style": {
+      "wrap": {
+        "flexDirection": "row"
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/test/expect/h.js b/test/expect/h.js
new file mode 100644
index 0000000..0107f71
--- /dev/null
+++ b/test/expect/h.js
@@ -0,0 +1,76 @@
+{
+  "@weex-component/hi": {
+    "data": "function data() {\n\t        return {\n\t            hi: ''\n\t        };\n\t    }",
+    "template": {
+      "type": "div",
+      "children": [
+        {
+          "type": "text",
+          "classList": [
+            "hi"
+          ],
+          "attr": {
+            "value": "function () {return this.hi}"
+          }
+        }
+      ]
+    },
+    "style": {
+      "hi": {
+        "fontSize": 26,
+        "color": "#008000"
+      }
+    }
+  },
+  "@weex-component/name": {
+    "data": "function data() {\n\t        return {\n\t            name: ''\n\t        };\n\t    }",
+    "template": {
+      "type": "div",
+      "children": [
+        {
+          "type": "text",
+          "classList": [
+            "name"
+          ],
+          "attr": {
+            "value": "function () {return this.name}"
+          }
+        }
+      ]
+    },
+    "style": {
+      "name": {
+        "fontSize": 26,
+        "color": "#FF0000"
+      }
+    }
+  },
+  "@weex-component/ee83bc6907b957bce242c32bf2c188f2": {
+    "data": "function data() {\n\t        return {\n\t            hi: 'Hello',\n\t            name: 'Weex'\n\t        };\n\t    }",
+    "template": {
+      "type": "div",
+      "classList": [
+        "wrap"
+      ],
+      "children": [
+        {
+          "type": "hi",
+          "attr": {
+            "hi": "function () {return this.hi}"
+          }
+        },
+        {
+          "type": "name",
+          "attr": {
+            "name": "function () {return this.name}"
+          }
+        }
+      ]
+    },
+    "style": {
+      "wrap": {
+        "flexDirection": "row"
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/test/expect/i.js b/test/expect/i.js
new file mode 100644
index 0000000..e4cfd6e
--- /dev/null
+++ b/test/expect/i.js
@@ -0,0 +1,24 @@
+{
+  "@weex-component/eebf10d54335bc996c5229787ede33eb": {
+    "template": {
+      "type": "div",
+      "children": [
+        {
+          "type": "text",
+          "classList": [
+            "hello"
+          ],
+          "attr": {
+            "value": "function () {return 'Hello ' + (this.name)}"
+          }
+        }
+      ]
+    },
+    "style": {
+      "hello": {
+        "fontSize": 26,
+        "color": "#FF0000"
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/test/expect/j.js b/test/expect/j.js
new file mode 100644
index 0000000..660882f
--- /dev/null
+++ b/test/expect/j.js
@@ -0,0 +1,31 @@
+{
+  "@weex-component/88780bcf66c93d797a309a82b762e1f9": {
+    "ready": "function ready() {\n\t        modal.toast({ 'message': 'ready' });\n\t    }",
+    "data": "function data() {\n\t        return {\n\t            hi: 'Hello',\n\t            name: 'Weex'\n\t        };\n\t    }",
+    "template": {
+      "type": "div",
+      "classList": [
+        "wrap"
+      ],
+      "children": [
+        {
+          "type": "text",
+          "attr": {
+            "value": "function () {return this.hi}"
+          }
+        },
+        {
+          "type": "text",
+          "attr": {
+            "value": "function () {return this.name}"
+          }
+        }
+      ]
+    },
+    "style": {
+      "wrap": {
+        "flexDirection": "row"
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/test/expect/k.js b/test/expect/k.js
new file mode 100644
index 0000000..2676b37
--- /dev/null
+++ b/test/expect/k.js
@@ -0,0 +1,45 @@
+{
+  "@weex-component/215a3c96c2c3802f228bf7ceec470c73": {
+    "ready": "function ready() {\n\t    _modal2.default.toast({ 'message': 'ready' });\n\t}",
+    "data": "function data() {\n\t    return {\n\t        hi: 'Hello',\n\t        name: 'Weex'\n\t    };\n\t}",
+    "template": {
+      "type": "div",
+      "classList": [
+        "wrap"
+      ],
+      "children": [
+        {
+          "type": "text",
+          "classList": [
+            "text-hi"
+          ],
+          "attr": {
+            "value": "function () {return this.hi}"
+          }
+        },
+        {
+          "type": "text",
+          "classList": [
+            "text-name"
+          ],
+          "attr": {
+            "value": "function () {return this.name}"
+          }
+        }
+      ]
+    },
+    "style": {
+      "wrap": {
+        "flexDirection": "row"
+      },
+      "text-hi": {
+        "color": "#FF0000",
+        "fontSize": 26
+      },
+      "text-name": {
+        "color": "#008000",
+        "fontSize": 26
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/test/npm-debug.log b/test/npm-debug.log
new file mode 100644
index 0000000..cad643e
--- /dev/null
+++ b/test/npm-debug.log
@@ -0,0 +1,158 @@
+0 info it worked if it ends with ok
+1 verbose cli [ '/Users/terry 1/versions/node/v4.2.2/bin/node',
+1 verbose cli   '/Users/terry/versions/node/v4.2.2/bin/npm',
+1 verbose cli   'install',
+1 verbose cli   'weex-loader',
+1 verbose cli   'babel-loader',
+1 verbose cli   ' ' ]
+2 info using npm@3.9.6
+3 info using node@v4.2.2
+4 silly loadCurrentTree Starting
+5 silly install loadCurrentTree
+6 silly install readLocalPackageData
+7 silly fetchPackageMetaData weex-loader
+8 silly fetchPackageMetaData babel-loader@^6.2.4
+9 silly fetchPackageMetaData
+10 silly fetchNamedPackageData babel-loader
+11 silly mapToRegistry name babel-loader
+12 silly mapToRegistry using default registry
+13 silly mapToRegistry registry http://registry.npmjs.org/
+14 silly mapToRegistry data Result {
+14 silly mapToRegistry   raw: 'babel-loader',
+14 silly mapToRegistry   scope: null,
+14 silly mapToRegistry   name: 'babel-loader',
+14 silly mapToRegistry   rawSpec: '',
+14 silly mapToRegistry   spec: 'latest',
+14 silly mapToRegistry   type: 'tag' }
+15 silly mapToRegistry uri http://registry.npmjs.org/babel-loader
+16 silly fetchNamedPackageData weex-loader
+17 silly mapToRegistry name weex-loader
+18 silly mapToRegistry using default registry
+19 silly mapToRegistry registry http://registry.npmjs.org/
+20 silly mapToRegistry data Result {
+20 silly mapToRegistry   raw: 'weex-loader',
+20 silly mapToRegistry   scope: null,
+20 silly mapToRegistry   name: 'weex-loader',
+20 silly mapToRegistry   rawSpec: '',
+20 silly mapToRegistry   spec: 'latest',
+20 silly mapToRegistry   type: 'tag' }
+21 silly mapToRegistry uri http://registry.npmjs.org/weex-loader
+22 silly fetchDirectoryPackageData null
+23 verbose request uri http://registry.npmjs.org/weex-loader
+24 verbose request no auth needed
+25 info attempt registry request try #1 at 03:16:37
+26 verbose request using bearer token for auth
+27 verbose request id b05395499c9e154f
+28 http request GET http://registry.npmjs.org/weex-loader
+29 verbose request uri http://registry.npmjs.org/babel-loader
+30 verbose request no auth needed
+31 info attempt registry request try #1 at 03:16:37
+32 verbose request using bearer token for auth
+33 verbose etag "EW23D9MKRNP2807DDKRUKPXG"
+34 http request GET http://registry.npmjs.org/babel-loader
+35 http 200 http://registry.npmjs.org/weex-loader
+36 verbose headers { server: 'CouchDB/1.5.0 (Erlang OTP/R16B03)',
+36 verbose headers   etag: '"84NTPFZCZIPAGB6NSL28IXNOE"',
+36 verbose headers   'content-type': 'application/json',
+36 verbose headers   'content-encoding': 'gzip',
+36 verbose headers   'cache-control': 'max-age=300',
+36 verbose headers   'transfer-encoding': 'chunked',
+36 verbose headers   'accept-ranges': 'bytes',
+36 verbose headers   date: 'Sat, 09 Jul 2016 19:13:18 GMT',
+36 verbose headers   via: '1.1 varnish',
+36 verbose headers   connection: 'keep-alive',
+36 verbose headers   'x-served-by': 'cache-lcy1123-LCY',
+36 verbose headers   'x-cache': 'MISS',
+36 verbose headers   'x-cache-hits': '0',
+36 verbose headers   'x-timer': 'S1468091598.000096,VS0,VE356',
+36 verbose headers   vary: 'Accept-Encoding' }
+37 silly get cb [ 200,
+37 silly get   { server: 'CouchDB/1.5.0 (Erlang OTP/R16B03)',
+37 silly get     etag: '"84NTPFZCZIPAGB6NSL28IXNOE"',
+37 silly get     'content-type': 'application/json',
+37 silly get     'content-encoding': 'gzip',
+37 silly get     'cache-control': 'max-age=300',
+37 silly get     'transfer-encoding': 'chunked',
+37 silly get     'accept-ranges': 'bytes',
+37 silly get     date: 'Sat, 09 Jul 2016 19:13:18 GMT',
+37 silly get     via: '1.1 varnish',
+37 silly get     connection: 'keep-alive',
+37 silly get     'x-served-by': 'cache-lcy1123-LCY',
+37 silly get     'x-cache': 'MISS',
+37 silly get     'x-cache-hits': '0',
+37 silly get     'x-timer': 'S1468091598.000096,VS0,VE356',
+37 silly get     vary: 'Accept-Encoding' } ]
+38 verbose get saving weex-loader to /Users/terry/.npm/registry.npmjs.org/weex-loader/.cache.json
+39 verbose correctMkdir /Users/terry/.npm correctMkdir not in flight; initializing
+40 http 200 http://registry.npmjs.org/babel-loader
+41 verbose headers { server: 'CouchDB/1.5.0 (Erlang OTP/R16B03)',
+41 verbose headers   etag: '"CN9KCPC8USQ6FOZKEY58UCARS"',
+41 verbose headers   'content-type': 'application/json',
+41 verbose headers   'content-encoding': 'gzip',
+41 verbose headers   'cache-control': 'max-age=300',
+41 verbose headers   'transfer-encoding': 'chunked',
+41 verbose headers   'accept-ranges': 'bytes',
+41 verbose headers   date: 'Sat, 09 Jul 2016 19:13:18 GMT',
+41 verbose headers   via: '1.1 varnish',
+41 verbose headers   connection: 'keep-alive',
+41 verbose headers   'x-served-by': 'cache-lcy1129-LCY',
+41 verbose headers   'x-cache': 'MISS',
+41 verbose headers   'x-cache-hits': '0',
+41 verbose headers   'x-timer': 'S1468091597.995738,VS0,VE698',
+41 verbose headers   vary: 'Accept-Encoding' }
+42 silly get cb [ 200,
+42 silly get   { server: 'CouchDB/1.5.0 (Erlang OTP/R16B03)',
+42 silly get     etag: '"CN9KCPC8USQ6FOZKEY58UCARS"',
+42 silly get     'content-type': 'application/json',
+42 silly get     'content-encoding': 'gzip',
+42 silly get     'cache-control': 'max-age=300',
+42 silly get     'transfer-encoding': 'chunked',
+42 silly get     'accept-ranges': 'bytes',
+42 silly get     date: 'Sat, 09 Jul 2016 19:13:18 GMT',
+42 silly get     via: '1.1 varnish',
+42 silly get     connection: 'keep-alive',
+42 silly get     'x-served-by': 'cache-lcy1129-LCY',
+42 silly get     'x-cache': 'MISS',
+42 silly get     'x-cache-hits': '0',
+42 silly get     'x-timer': 'S1468091597.995738,VS0,VE698',
+42 silly get     vary: 'Accept-Encoding' } ]
+43 verbose get saving babel-loader to /Users/terry/.npm/registry.npmjs.org/babel-loader/.cache.json
+44 verbose correctMkdir /Users/terry/.npm correctMkdir not in flight; initializing
+45 silly install normalizeTree
+46 silly loadCurrentTree Finishing
+47 silly loadIdealTree Starting
+48 silly install loadIdealTree
+49 silly cloneCurrentTree Starting
+50 silly install cloneCurrentTreeToIdealTree
+51 silly cloneCurrentTree Finishing
+52 silly loadShrinkwrap Starting
+53 silly install loadShrinkwrap
+54 silly loadShrinkwrap Finishing
+55 silly loadAllDepsIntoIdealTree Starting
+56 silly install loadAllDepsIntoIdealTree
+57 silly rollbackFailedOptional Starting
+58 silly rollbackFailedOptional Finishing
+59 silly runTopLevelLifecycles Starting
+60 silly runTopLevelLifecycles Finishing
+61 silly install printInstalled
+62 verbose stack Error: Refusing to install weex-loader as a dependency of itself
+62 verbose stack     at checkSelf (/Users/terry 1/versions/node/v4.2.2/lib/node_modules/npm/lib/install/validate-args.js:53:14)
+62 verbose stack     at Array.<anonymous> (/Users/terry 1/versions/node/v4.2.2/lib/node_modules/npm/node_modules/slide/lib/bind-actor.js:15:8)
+62 verbose stack     at LOOP (/Users/terry 1/versions/node/v4.2.2/lib/node_modules/npm/node_modules/slide/lib/chain.js:15:14)
+62 verbose stack     at chain (/Users/terry 1/versions/node/v4.2.2/lib/node_modules/npm/node_modules/slide/lib/chain.js:20:5)
+62 verbose stack     at /Users/terry 1/versions/node/v4.2.2/lib/node_modules/npm/lib/install/validate-args.js:16:5
+62 verbose stack     at /Users/terry 1/versions/node/v4.2.2/lib/node_modules/npm/node_modules/slide/lib/async-map.js:52:35
+62 verbose stack     at Array.forEach (native)
+62 verbose stack     at /Users/terry 1/versions/node/v4.2.2/lib/node_modules/npm/node_modules/slide/lib/async-map.js:52:11
+62 verbose stack     at Array.forEach (native)
+62 verbose stack     at asyncMap (/Users/terry 1/versions/node/v4.2.2/lib/node_modules/npm/node_modules/slide/lib/async-map.js:51:8)
+63 verbose cwd /Users/terry 1/Develop/taobao/weex/toolchain/weex-loader/test
+64 error Darwin 15.0.0
+65 error argv "/Users/terry 1/versions/node/v4.2.2/bin/node" "/Users/terry/versions/node/v4.2.2/bin/npm" "install" "weex-loader" "babel-loader" " "
+66 error node v4.2.2
+67 error npm  v3.9.6
+68 error code ENOSELF
+69 error Refusing to install weex-loader as a dependency of itself
+70 error If you need help, you may report this error at:
+70 error     <https://github.com/npm/npm/issues>
+71 verbose exit [ 1, true ]
diff --git a/test/spec/a.we b/test/spec/a.we
index 62ac6f9..1335e77 100644
--- a/test/spec/a.we
+++ b/test/spec/a.we
@@ -1,24 +1,3 @@
 <template>
-    <div>
-        <text class="hello">Hello {{name}}</text>
-    </div>
-</template>
-
-
-<style>
-.hello {
-    font-size: 26px;
-    color: red;
-}
-</style>
-
-
-<script>
-module.exports = {
-    data: function() {
-        return {
-            name: 'Weex'
-        }
-    }
-}
-</script>
\ No newline at end of file
+    <div><text>Hello Weex</text></div>
+</template>
\ No newline at end of file
diff --git a/test/spec/b.we b/test/spec/b.we
new file mode 100644
index 0000000..603ef5c
--- /dev/null
+++ b/test/spec/b.we
@@ -0,0 +1,10 @@
+<template>
+    <div><text class="hi">Hello Weex</text></div>
+</template>
+
+<style>
+.hi {
+    font-size: 26px;
+    color: red;
+}
+</style>
\ No newline at end of file
diff --git a/test/spec/c.we b/test/spec/c.we
new file mode 100644
index 0000000..62ac6f9
--- /dev/null
+++ b/test/spec/c.we
@@ -0,0 +1,24 @@
+<template>
+    <div>
+        <text class="hello">Hello {{name}}</text>
+    </div>
+</template>
+
+
+<style>
+.hello {
+    font-size: 26px;
+    color: red;
+}
+</style>
+
+
+<script>
+module.exports = {
+    data: function() {
+        return {
+            name: 'Weex'
+        }
+    }
+}
+</script>
\ No newline at end of file
diff --git a/test/spec/d.we b/test/spec/d.we
new file mode 100644
index 0000000..37208c2
--- /dev/null
+++ b/test/spec/d.we
@@ -0,0 +1,48 @@
+<element name="name">
+    <template>
+        <div><text class="name">{{name}}</text></div>
+    </template>
+
+    <style>
+    .name {
+        font-size: 26px;
+        color: red;
+    }
+    </style>
+
+    <script>
+    module.exports = {
+        data: function() {
+            return {
+                name: ''
+            }
+        }
+    }
+    </script>
+</element>
+
+<template>
+    <div style="flex-direction: row;">
+        <text class="hello">Hello</text>
+        <name name="{{name}}"></name>
+    </div>
+</template>
+
+
+<style>
+.hello {
+    font-size: 26px;
+    color: green;
+}
+</style>
+
+
+<script>
+module.exports = {
+    data: function() {
+        return {
+            name: 'Weex'
+        }
+    }
+}
+</script>
\ No newline at end of file
diff --git a/test/spec/data.json b/test/spec/data.json
new file mode 100644
index 0000000..b8e9b40
--- /dev/null
+++ b/test/spec/data.json
@@ -0,0 +1,3 @@
+{
+    "name": "Weex"
+}
\ No newline at end of file
diff --git a/test/spec/e.we b/test/spec/e.we
new file mode 100644
index 0000000..06d79d0
--- /dev/null
+++ b/test/spec/e.we
@@ -0,0 +1,63 @@
+<element name="hi">
+    <template>
+        <div><text class="hi">{{hi}}</text></div>
+    </template>
+
+    <style>
+    .hi {
+        font-size: 26px;
+        color: green;
+    }
+    </style>
+
+    <script>
+    module.exports = {
+        data: function() {
+            return {
+                hi: ''
+            }
+        }
+    }
+    </script>
+</element>
+
+<element name="name">
+    <template>
+        <div><text class="name">{{name}}</text></div>
+    </template>
+
+    <style>
+    .name {
+        font-size: 26px;
+        color: red;
+    }
+    </style>
+
+    <script>
+    module.exports = {
+        data: function() {
+            return {
+                name: ''
+            }
+        }
+    }
+    </script>
+</element>
+
+<template>
+    <div style="flex-direction: row;">
+        <hi hi="{{hi}}"></hi>
+        <name name="{{name}}"></name>
+    </div>
+</template>
+
+<script>
+module.exports = {
+    data: function() {
+        return {
+            hi: 'Hello',
+            name: 'Weex'
+        }
+    }
+}
+</script>
\ No newline at end of file
diff --git a/test/spec/f.css b/test/spec/f.css
new file mode 100644
index 0000000..11d03bc
--- /dev/null
+++ b/test/spec/f.css
@@ -0,0 +1,3 @@
+.wrap {
+    flex-direction: row;
+}
\ No newline at end of file
diff --git a/test/spec/f.html b/test/spec/f.html
new file mode 100644
index 0000000..3229bc9
--- /dev/null
+++ b/test/spec/f.html
@@ -0,0 +1,4 @@
+<div class="wrap">
+    <hi hi="{{hi}}"></hi>
+    <name name="{{name}}"></name>
+</div>  
\ No newline at end of file
diff --git a/test/spec/f.js b/test/spec/f.js
new file mode 100644
index 0000000..f8879f6
--- /dev/null
+++ b/test/spec/f.js
@@ -0,0 +1,8 @@
+module.exports = {
+    data: function() {
+        return {
+            hi: 'Hello',
+            name: 'Weex'
+        }
+    }
+}
\ No newline at end of file
diff --git a/test/spec/f.we b/test/spec/f.we
new file mode 100644
index 0000000..b955e53
--- /dev/null
+++ b/test/spec/f.we
@@ -0,0 +1,5 @@
+<element name="hi" src="./hi.we"></element>
+<element name="name" src="./name.we"></element>
+<template src="./f.html"></template>
+<style src="./f.css"></style>
+<script src="./f.js"></script>
\ No newline at end of file
diff --git a/test/spec/g.we b/test/spec/g.we
new file mode 100644
index 0000000..84fec79
--- /dev/null
+++ b/test/spec/g.we
@@ -0,0 +1,27 @@
+<element name="name1" src="./name.we"></element>
+
+<template>
+    <div class="wrap">
+        <hi1 hi="{{hi}}"></hi1>
+        <name1 name="{{name}}"></name1>
+    </div>
+</template>
+
+<style>
+    .wrap {
+        flex-direction: row;
+    }
+</style>
+
+<script>
+require('./hi.we?name=hi1')
+
+module.exports = {
+    data: function() {
+        return {
+            hi: 'Hello',
+            name: 'Weex'
+        }
+    }
+}
+</script>
\ No newline at end of file
diff --git a/test/spec/h.we b/test/spec/h.we
new file mode 100644
index 0000000..f8faf4b
--- /dev/null
+++ b/test/spec/h.we
@@ -0,0 +1,23 @@
+<template>
+    <div class="wrap">
+        <hi hi="{{hi}}"></hi>
+        <name name="{{name}}"></name>
+    </div>
+</template>
+
+<style>
+    .wrap {
+        flex-direction: row;
+    }
+</style>
+
+<script>
+module.exports = {
+    data: function() {
+        return {
+            hi: 'Hello',
+            name: 'Weex'
+        }
+    }
+}
+</script>
\ No newline at end of file
diff --git a/test/spec/hi.we b/test/spec/hi.we
new file mode 100644
index 0000000..42ce783
--- /dev/null
+++ b/test/spec/hi.we
@@ -0,0 +1,20 @@
+<template>
+    <div><text class="hi">{{hi}}</text></div>
+</template>
+
+<style>
+.hi {
+    font-size: 26px;
+    color: green;
+}
+</style>
+
+<script>
+module.exports = {
+    data: function() {
+        return {
+            hi: ''
+        }
+    }
+}
+</script>
\ No newline at end of file
diff --git a/test/spec/i.we b/test/spec/i.we
new file mode 100644
index 0000000..f0fc21f
--- /dev/null
+++ b/test/spec/i.we
@@ -0,0 +1,22 @@
+<template>
+    <div>
+        <text class="hello">Hello {{name}}</text>
+    </div>
+</template>
+
+
+<style>
+.hello {
+    font-size: 26px;
+    color: red;
+}
+</style>
+
+
+<script type="config">
+{
+    downgrade: true
+}
+</script>
+
+<script type="data" src="./data.json"></script>
\ No newline at end of file
diff --git a/test/spec/j.we b/test/spec/j.we
new file mode 100644
index 0000000..8d2e8af
--- /dev/null
+++ b/test/spec/j.we
@@ -0,0 +1,28 @@
+<template>
+    <div class="wrap">
+        <text>{{hi}}</text>
+        <text>{{name}}</text>
+    </div>
+</template>
+
+<style>
+    .wrap {
+        flex-direction: row;
+    }
+</style>
+
+<script>
+var modal = require('@weex-module/modal')
+
+module.exports = {
+    ready: function() {
+        modal.toast({'message': 'ready'})
+    },
+    data: function() {
+        return {
+            hi: 'Hello',
+            name: 'Weex'
+        }
+    }
+}
+</script>
\ No newline at end of file
diff --git a/test/spec/k.we b/test/spec/k.we
new file mode 100644
index 0000000..c07c5ba
--- /dev/null
+++ b/test/spec/k.we
@@ -0,0 +1,44 @@
+<template lang="jade">
+.wrap
+    text.text-hi {{hi}}
+    text.text-name {{name}}
+</template>
+
+<style lang="cssnext">
+    :root {
+        --mainColor: red;
+        --subColor: green;
+        --normalFont: {
+            font-size: 26px;
+        };
+    }
+
+    .wrap {
+        flex-direction: row;
+    }
+
+    .text-hi {
+        color: var(--mainColor);
+        @apply --normalFont;
+    }
+
+    .text-name {
+        color: var(--subColor);
+        @apply --normalFont;
+    }
+</style>
+
+<script>
+import modal from '@weex-module/modal'
+
+export function ready() {
+    modal.toast({'message': 'ready'})
+}
+
+export function data() {
+    return {
+        hi: 'Hello',
+        name: 'Weex'
+    }
+}
+</script>
\ No newline at end of file
diff --git a/test/spec/name.css b/test/spec/name.css
new file mode 100644
index 0000000..3c6e476
--- /dev/null
+++ b/test/spec/name.css
@@ -0,0 +1,4 @@
+.name {
+    font-size: 26px;
+    color: red;
+}
\ No newline at end of file
diff --git a/test/spec/name.html b/test/spec/name.html
new file mode 100644
index 0000000..258e5e5
--- /dev/null
+++ b/test/spec/name.html
@@ -0,0 +1 @@
+<div><text class="name">{{name}}</text></div>
diff --git a/test/spec/name.js b/test/spec/name.js
new file mode 100644
index 0000000..d2074a3
--- /dev/null
+++ b/test/spec/name.js
@@ -0,0 +1,7 @@
+module.exports = {
+    data: function() {
+        return {
+            name: ''
+        }
+    }
+}
\ No newline at end of file
diff --git a/test/spec/name.we b/test/spec/name.we
new file mode 100644
index 0000000..efa318e
--- /dev/null
+++ b/test/spec/name.we
@@ -0,0 +1,3 @@
+<template src="./name.html"></template>
+<style src="./name.css"></style>
+<script src="./name.js"></script>
\ No newline at end of file
diff --git a/test/test.js b/test/test.js
index 7f1bafc..4b65f67 100644
--- a/test/test.js
+++ b/test/test.js
@@ -1,5 +1,6 @@
+'use strict';
+
 const fs = require('fs');
-const MemoryFS = require("memory-fs");
 const path =require('path');
 
 const chai = require('chai');
@@ -8,33 +9,114 @@
 const expect = chai.expect;
 chai.use(sinonChai);
 
-const webpackConfig = require('./webpack.config.js');
-const webpack = require('webpack');
 
+function getActualString(name) {
+  const filepath = path.resolve(__dirname, 'actual', `${name}.js`);
+  const result = fs.readFileSync(filepath, 'utf-8');
+  return result.toString();
+} 
 
-describe('loader', () => {
-  it('simple', (done) => {
-    const config = Object.assign({}, webpackConfig, {
-      entry: {
-        a: path.resolve(__dirname, 'spec', 'a.we')
-      }
-    });
+function getExpectJSON(name) {
+  const filepath = path.resolve(__dirname, 'expect', `${name}.js`);
+  const result = fs.readFileSync(filepath, 'utf-8');
+  return JSON.parse(result.toString());
+}
 
-    const compiler = webpack(config);
-    const mfs = new MemoryFS;
-    // compiler.outputFileSystem = mfs;
-    compiler.run((err, stats) => {
-      if (err) {
-        return done(err);
-      }
+function stringifyActual(json) {
+  return JSON.stringify(json, function(key, value) {
+    if (typeof value === 'function') {
+      value = value.toString();
+    }
+    return value;
+  }, '  ');
+}
 
-      console.log(stats.toString({
-        chunks: false,
-        color: true
-      }))
-      // const result = fs.readFileSync(path.resolve(__dirname, 'actual', 'a.we'))
-      // console.log(result)
-      done()
-    })
+describe('build', () => {
+  let __weex_define__;
+  let __weex_bootstrap__;
+  let components;
+  let requireStub;
+  let bootstrapStub;
+
+  function expectActual(name) {
+    const actualStr = getActualString(name);
+    const fn = new Function('__weex_define__', '__weex_bootstrap__', actualStr);
+    fn(__weex_define__, __weex_bootstrap__);
+
+    // const filepath = path.resolve(__dirname, 'expect', `${name}.js`);
+    // fs.writeFileSync(filepath, stringifyActual(components), 'utf-8');
+
+    const expectJSON = getExpectJSON(name);
+    expect(JSON.parse(stringifyActual(components))).eql(expectJSON);
+    expect(components).to.include.keys(__weex_bootstrap__.firstCall.args[0]);
+  }
+
+  beforeEach(() => {
+    components = {};
+    requireStub = sinon.stub();
+    bootstrapStub = sinon.stub();
+
+    __weex_define__ = function(componentName, deps, factory) {
+      var __weex_require__ = requireStub;
+      var __weex_exports__ = {};
+      var __weex_module__ = {exports : __weex_exports__}
+
+      factory(__weex_require__, __weex_exports__, __weex_module__)
+      components[componentName] = __weex_module__.exports
+    }
+
+    __weex_bootstrap__ = bootstrapStub;
+
+  });
+
+  it('single template', () => {
+    expectActual('a');
+  });
+
+  it('template with style', () => {
+    expectActual('b');
+  });
+
+  it('template with style and script', () => {
+    expectActual('c');
+  });
+
+  it('template with single inline element', () => {
+    expectActual('d');
+  });
+
+  it('template with multiple inline elements', () => {
+    expectActual('e');
+  });
+
+  it('template via src', () => {
+    expectActual('f');
+  });
+
+  it('template via requiring src and specifing alias', () => {
+    expectActual('g');
+    expect(requireStub.callCount).eql(0);
+  });
+
+  it('template by finding elements under same folder', () => {
+    expectActual('h');
+  });
+
+  it('template with config and data', () => {
+    expectActual('i');
+    expect(bootstrapStub.firstCall.args[1]).is.not.undefined;
+    expect(bootstrapStub.firstCall.args[2]).is.not.undefined;
+  });
+
+  it('template and require weex modules', () => {
+    expectActual('j');
+    expect(requireStub.callCount).eql(1);
+    expect(requireStub.firstCall.args).eql(['@weex-module/modal']);
+  });
+
+  it('template by using custom language', () => {
+    expectActual('k');
+    expect(requireStub.callCount).eql(1);
+    expect(requireStub.firstCall.args).eql(['@weex-module/modal']);
   });
 })
diff --git a/test/webpack.config.js b/test/webpack.config.js
index a870a17..e9f9600 100644
--- a/test/webpack.config.js
+++ b/test/webpack.config.js
@@ -1,9 +1,19 @@
 var path = require('path')
+var cssnext = require('postcss-cssnext')
+
+var entry = {}
+var start = 'a'
+var end = 'k'
+var count = end.charCodeAt(0) - start.charCodeAt(0)
+
+new Array(count + 1).fill(0)
+  .forEach((n, i) => {
+    var name = String.fromCharCode(i + start.charCodeAt(0))
+    entry[name] = path.resolve(__dirname, 'spec', name + '.we?entry')
+  })
 
 module.exports = {
-  entry: {
-    a: path.resolve(__dirname, 'spec', 'a.we') + '?entry'
-  },
+  entry: entry,
   output: {
     path: path.resolve(__dirname, 'actual'),
     filename: '[name].js'
@@ -19,11 +29,15 @@
   resolveLoader: {
     modulesDirectories: ['./', './node_modules']
   },
+  postcss: function() {
+    return [cssnext({
+      browsers: ['last 1 version']
+    })]
+  },
   weex: {
-    loaders: {
-      es6: ['babel'],
-      cssnext: ['postcss-cssnext'],
-      jade: ['jade']
+    lang: {
+      cssnext: ['postcss'],
+      jade: ['jade-html']
     }
   }
 }