From 725aeb20d497a4130c921a674beb69fd43433035 Mon Sep 17 00:00:00 2001 From: Sjoerd Visscher Date: Thu, 7 May 2015 14:02:59 +0200 Subject: [PATCH 001/877] Initial commit --- LICENSE | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..2c8fd9923 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Q42 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + From 8bac681379828e6de7a2117ff80009b0ad748493 Mon Sep 17 00:00:00 2001 From: Sjoerd Visscher Date: Thu, 7 May 2015 14:08:53 +0200 Subject: [PATCH 002/877] Create README.md --- README.md | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 000000000..a55de700b --- /dev/null +++ b/README.md @@ -0,0 +1,83 @@ +# vue-multi-loader + +> Vue.js component loader for [Webpack](http://webpack.github.io), using Webpack loaders for the parts. + +It allows you to write your components in this format: + +``` html +// app.vue + + + + + +``` + +You can also mix preprocessor languages in the component file: + +``` html +// app.vue + + + + + +``` + +And you can import using the `src` attribute (note that there's no need for a `lang` attribute here, as Webpack will +be used to determine which loader applies): + +``` html + +``` + +## Usage + +Config Webpack: + +``` js +// webpack.config.js +module.exports = { + entry: "./main.js", + output: { + filename: "build.js" + }, + module: { + loaders: [ + { test: /\.vue$/, loader: "vue-multi-loader" }, + ] + } +} +``` + +And this is all you need to do in your main entry file: + +``` js +// main.js +var Vue = require('vue') +var appOptions = require('./app.vue') +var app = new Vue(appOptions).$mount('#app') +``` From f90373017aa1930e173e2d2150924464d12adfdb Mon Sep 17 00:00:00 2001 From: Sjoerd Visscher Date: Thu, 7 May 2015 14:30:39 +0200 Subject: [PATCH 003/877] First version --- .gitignore | 1 + index.js | 46 +++++++++++++++++++++++++++++++++ package.json | 24 ++++++++++++++++++ parser.js | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++ selector.js | 13 ++++++++++ 5 files changed, 156 insertions(+) create mode 100644 .gitignore create mode 100644 index.js create mode 100644 package.json create mode 100644 parser.js create mode 100644 selector.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..07e6e472c --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/node_modules diff --git a/index.js b/index.js new file mode 100644 index 000000000..bd7d15e51 --- /dev/null +++ b/index.js @@ -0,0 +1,46 @@ +module.exports = function (content) { + this.cacheable(); + var cb = this.async(); + var languages = {} + var output = '' + var vueUrl = this.resource; + var loaders = {} + var loaderPrefix = { + template: 'html!', + style: 'style!css!', + script: '' + } + + function loader(part, lang) { + var loader = loaders[lang] || loaderPrefix[part] + lang; + return loader ? loader + '!' : '' + } + + function getRequire(part, lang) { + return 'require(' + JSON.stringify('-!' + loader(part, lang) + require.resolve('./selector.js') + '?' + part + '/' + lang + '!' + vueUrl) + ')'; + } + + var me = this; + var url = "!!" + require.resolve("./parser.js") + "!" + vueUrl; + this.loadModule(url, function(err, source, map, module) { + if (err) return cb(err); + + var parts = me.exec(source, url); + + for (var lang in parts.style) + output += getRequire('style', lang) + '\n' + + for (var lang in parts.script) + output += 'module.exports = ' + getRequire('script', lang) + '\n' + + var hasTemplate = false; + for (var lang in parts.template) { + if (hasTemplate) + return cb(new Error('Only one template element allowed per vue component!')) + output += 'module.exports.template = ' + getRequire('template', lang); + hasTemplate = true; + } + + cb(null, output); + }) +} diff --git a/package.json b/package.json new file mode 100644 index 000000000..36a60bac3 --- /dev/null +++ b/package.json @@ -0,0 +1,24 @@ +{ + "name": "vue-multi-loader", + "version": "0.0.1", + "description": "Vue.js component loader for Webpack, using Webpack loaders for the parts", + "main": "index.js", + "repository": { + "type": "git", + "url": "https://github.com/Q42/vue-multi-loader.git" + }, + "keywords": [ + "vue", + "webpack", + "loader" + ], + "author": "Sjoerd Visscher", + "license": "ISC", + "bugs": { + "url": "https://github.com/Q42/vue-multi-loader/issues" + }, + "homepage": "https://github.com/Q42/vue-multi-loader", + "dependencies": { + "parse5": "^1.1.4" + } +} diff --git a/parser.js b/parser.js new file mode 100644 index 000000000..8586e9d6a --- /dev/null +++ b/parser.js @@ -0,0 +1,72 @@ +var parse5 = require('parse5') +var parser = new parse5.Parser() +var serializer = new parse5.TreeSerializer() + +module.exports = function (content) { + this.cacheable() + var cb = this.async() + + // only 1 template tag is allowed, while styles and + // scripts are concatenated. + var languages = {} + var output = { + template: {}, + style: {}, + script: {}, + includes: [] + } + + // parse the file into an HTML tree + var fragment = parser.parseFragment(content) + + // Walk through the top level nodes and check for their + // types & languages. If there are pre-processing needed, + // push it into a jobs list. + fragment.childNodes.forEach(function (node) { + var type = node.nodeName; + if (type == '#text') + return + if (checkSrc(node, output.includes)) + return + + var lang = checkLang(node) || '' + output[type][lang] = (output[type][lang] || '') + serialize(node) + '\n' + }) + + cb(null, 'module.exports = ' + JSON.stringify(output)) +} + +function checkLang (node) { + if (node.attrs) { + var i = node.attrs.length + while (i--) { + var attr = node.attrs[i] + if (attr.name === 'lang') { + return attr.value + } + } + } +} + +function checkSrc (node, arr) { + if (node.attrs) { + var i = node.attrs.length + while (i--) { + var attr = node.attrs[i] + if (attr.name === 'src') { + arr.push(attr.value) + return true + } + } + } + return false +} + +// Work around changes in parse5 >= 1.2.0 +function serialize (node) { + var childNode = node.childNodes[0] + if (childNode && childNode.nodeName === '#document-fragment') { + return serializer.serialize(childNode) + } + return serializer.serialize(node) +} diff --git a/selector.js b/selector.js new file mode 100644 index 000000000..3d0ce4bc1 --- /dev/null +++ b/selector.js @@ -0,0 +1,13 @@ +module.exports = function () { + this.cacheable() + var cb = this.async() + var path = this.query.substr(1).split('/') + + var me = this + var url = "!!" + require.resolve("./parser.js") + "!" + this.resource + this.loadModule(url, function(err, source) { + if (err) return cb(err) + var parts = me.exec(source, url) + cb(null, parts[path[0]][path[1]]) + }) +} From 60eefdcbb247588876f7c5393272da861d24b301 Mon Sep 17 00:00:00 2001 From: Sjoerd Visscher Date: Thu, 7 May 2015 16:13:54 +0200 Subject: [PATCH 004/877] Support src attributes --- .gitignore | 1 + index.js | 3 +++ package.json | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 07e6e472c..7a3a95d56 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /node_modules +/npm-debug.log diff --git a/index.js b/index.js index bd7d15e51..5db83c984 100644 --- a/index.js +++ b/index.js @@ -26,6 +26,9 @@ module.exports = function (content) { if (err) return cb(err); var parts = me.exec(source, url); + + for (var i = 0; i < parts.includes.length; i++) + output += 'require(' + JSON.stringify('./' + parts.includes[i]) + ')\n' for (var lang in parts.style) output += getRequire('style', lang) + '\n' diff --git a/package.json b/package.json index 36a60bac3..ab4f232f9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vue-multi-loader", - "version": "0.0.1", + "version": "0.0.2", "description": "Vue.js component loader for Webpack, using Webpack loaders for the parts", "main": "index.js", "repository": { From 9bc441f4bf8cfdab4f8f7ca1b7cdb7a372967dbe Mon Sep 17 00:00:00 2001 From: Sjoerd Visscher Date: Sun, 10 May 2015 20:16:11 +0200 Subject: [PATCH 005/877] v0.0.3 support html languages that generate a function --- index.js | 35 +++++++++++++++++++++------------ package.json | 2 +- parser.js | 55 +++++++++++++++++++++++----------------------------- selector.js | 16 +++++++-------- 4 files changed, 56 insertions(+), 52 deletions(-) diff --git a/index.js b/index.js index 5db83c984..7b4377847 100644 --- a/index.js +++ b/index.js @@ -1,19 +1,29 @@ module.exports = function (content) { this.cacheable(); var cb = this.async(); - var languages = {} - var output = '' + var languages = {}; + var output = ''; var vueUrl = this.resource; - var loaders = {} + var loaders = { + html: 'html', + css: 'style!css', + js: '' + }; var loaderPrefix = { - template: 'html!', + template: '', style: 'style!css!', script: '' - } + }; + var defaultLang = { + template: 'html', + style: 'css', + script: 'js' + }; function loader(part, lang) { - var loader = loaders[lang] || loaderPrefix[part] + lang; - return loader ? loader + '!' : '' + lang = lang || defaultLang[part]; + var loader = loaders[lang] !== undefined ? loaders[lang] : loaderPrefix[part] + lang; + return loader ? loader + '!' : ''; } function getRequire(part, lang) { @@ -26,21 +36,22 @@ module.exports = function (content) { if (err) return cb(err); var parts = me.exec(source, url); - + for (var i = 0; i < parts.includes.length; i++) - output += 'require(' + JSON.stringify('./' + parts.includes[i]) + ')\n' + output += 'require(' + JSON.stringify('./' + parts.includes[i]) + ')\n'; for (var lang in parts.style) - output += getRequire('style', lang) + '\n' + output += getRequire('style', lang) + '\n'; for (var lang in parts.script) - output += 'module.exports = ' + getRequire('script', lang) + '\n' + output += 'module.exports = ' + getRequire('script', lang) + '\n'; var hasTemplate = false; for (var lang in parts.template) { if (hasTemplate) - return cb(new Error('Only one template element allowed per vue component!')) + return cb(new Error('Only one template element allowed per vue component!')); output += 'module.exports.template = ' + getRequire('template', lang); + output += '\nif (module.exports.template instanceof Function) module.exports.template = module.exports.template({})'; hasTemplate = true; } diff --git a/package.json b/package.json index ab4f232f9..52cfa24ed 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vue-multi-loader", - "version": "0.0.2", + "version": "0.0.3", "description": "Vue.js component loader for Webpack, using Webpack loaders for the parts", "main": "index.js", "repository": { diff --git a/parser.js b/parser.js index 8586e9d6a..39c83313f 100644 --- a/parser.js +++ b/parser.js @@ -1,48 +1,41 @@ -var parse5 = require('parse5') -var parser = new parse5.Parser() -var serializer = new parse5.TreeSerializer() +var parse5 = require('parse5'); +var parser = new parse5.Parser(); +var serializer = new parse5.TreeSerializer(); module.exports = function (content) { - this.cacheable() - var cb = this.async() + this.cacheable(); + var cb = this.async(); - // only 1 template tag is allowed, while styles and - // scripts are concatenated. - var languages = {} + var languages = {}; var output = { template: {}, style: {}, script: {}, includes: [] - } - - // parse the file into an HTML tree - var fragment = parser.parseFragment(content) + }; - // Walk through the top level nodes and check for their - // types & languages. If there are pre-processing needed, - // push it into a jobs list. + var fragment = parser.parseFragment(content); fragment.childNodes.forEach(function (node) { var type = node.nodeName; if (type == '#text') - return + return; if (checkSrc(node, output.includes)) - return + return; - var lang = checkLang(node) || '' - output[type][lang] = (output[type][lang] || '') + serialize(node) + '\n' - }) + var lang = checkLang(node) || ''; + output[type][lang] = (output[type][lang] || '') + serialize(node) + '\n'; + }); - cb(null, 'module.exports = ' + JSON.stringify(output)) + cb(null, 'module.exports = ' + JSON.stringify(output)); } function checkLang (node) { if (node.attrs) { - var i = node.attrs.length + var i = node.attrs.length; while (i--) { - var attr = node.attrs[i] + var attr = node.attrs[i]; if (attr.name === 'lang') { - return attr.value + return attr.value; } } } @@ -50,12 +43,12 @@ function checkLang (node) { function checkSrc (node, arr) { if (node.attrs) { - var i = node.attrs.length + var i = node.attrs.length; while (i--) { - var attr = node.attrs[i] + var attr = node.attrs[i]; if (attr.name === 'src') { - arr.push(attr.value) - return true + arr.push(attr.value); + return true; } } } @@ -64,9 +57,9 @@ function checkSrc (node, arr) { // Work around changes in parse5 >= 1.2.0 function serialize (node) { - var childNode = node.childNodes[0] + var childNode = node.childNodes[0]; if (childNode && childNode.nodeName === '#document-fragment') { - return serializer.serialize(childNode) + return serializer.serialize(childNode); } - return serializer.serialize(node) + return serializer.serialize(node); } diff --git a/selector.js b/selector.js index 3d0ce4bc1..c4d85bed6 100644 --- a/selector.js +++ b/selector.js @@ -1,13 +1,13 @@ module.exports = function () { - this.cacheable() - var cb = this.async() - var path = this.query.substr(1).split('/') + this.cacheable(); + var cb = this.async(); + var path = this.query.substr(1).split('/'); - var me = this - var url = "!!" + require.resolve("./parser.js") + "!" + this.resource + var me = this; + var url = "!!" + require.resolve("./parser.js") + "!" + this.resource; this.loadModule(url, function(err, source) { - if (err) return cb(err) - var parts = me.exec(source, url) - cb(null, parts[path[0]][path[1]]) + if (err) return cb(err); + var parts = me.exec(source, url); + cb(null, parts[path[0]][path[1]]); }) } From 511329371e764b0ef85e1d072292ab68d8aae5d6 Mon Sep 17 00:00:00 2001 From: Sjoerd Visscher Date: Wed, 13 May 2015 15:56:25 +0200 Subject: [PATCH 006/877] Apply tips by @sokra https://github.com/webpack/webpack/issues/860#issuecomment-100227750 --- index.js | 8 +++++--- package.json | 1 + 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 7b4377847..be8abb981 100644 --- a/index.js +++ b/index.js @@ -1,9 +1,11 @@ +var loaderUtils = require("loader-utils"); + module.exports = function (content) { this.cacheable(); var cb = this.async(); var languages = {}; var output = ''; - var vueUrl = this.resource; + var vueUrl = loaderUtils.getRemainingRequest(this); var loaders = { html: 'html', css: 'style!css', @@ -27,7 +29,7 @@ module.exports = function (content) { } function getRequire(part, lang) { - return 'require(' + JSON.stringify('-!' + loader(part, lang) + require.resolve('./selector.js') + '?' + part + '/' + lang + '!' + vueUrl) + ')'; + return 'require(' + loaderUtils.stringifyRequest(this, '-!' + loader(part, lang) + require.resolve('./selector.js') + '?' + part + '/' + lang + '!' + vueUrl) + ')'; } var me = this; @@ -38,7 +40,7 @@ module.exports = function (content) { var parts = me.exec(source, url); for (var i = 0; i < parts.includes.length; i++) - output += 'require(' + JSON.stringify('./' + parts.includes[i]) + ')\n'; + output += 'require(' + loaderUtils.stringifyRequest(this, loaderUtils.urlToRequest(parts.includes[i])) + ')\n'; for (var lang in parts.style) output += getRequire('style', lang) + '\n'; diff --git a/package.json b/package.json index 52cfa24ed..d4f13b578 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ }, "homepage": "https://github.com/Q42/vue-multi-loader", "dependencies": { + "loader-utils": "^0.2.7", "parse5": "^1.1.4" } } From e1987d95b6469c4c7c169fb42ce61cf11862d040 Mon Sep 17 00:00:00 2001 From: Sjoerd Visscher Date: Thu, 14 May 2015 17:06:00 +0200 Subject: [PATCH 007/877] stringifyRequest strips trailing slashes even in querystrings --- selector.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selector.js b/selector.js index c4d85bed6..ffed1b3f9 100644 --- a/selector.js +++ b/selector.js @@ -8,6 +8,6 @@ module.exports = function () { this.loadModule(url, function(err, source) { if (err) return cb(err); var parts = me.exec(source, url); - cb(null, parts[path[0]][path[1]]); + cb(null, parts[path[0]][path[1]||'']); }) } From 19ea5d10ffcb3d26824ff559738511dd2505ff8f Mon Sep 17 00:00:00 2001 From: Sjoerd Visscher Date: Thu, 14 May 2015 17:06:29 +0200 Subject: [PATCH 008/877] Fix bug --- index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index be8abb981..7f09ce574 100644 --- a/index.js +++ b/index.js @@ -28,8 +28,9 @@ module.exports = function (content) { return loader ? loader + '!' : ''; } + var me = this; function getRequire(part, lang) { - return 'require(' + loaderUtils.stringifyRequest(this, '-!' + loader(part, lang) + require.resolve('./selector.js') + '?' + part + '/' + lang + '!' + vueUrl) + ')'; + return 'require(' + loaderUtils.stringifyRequest(me, '-!' + loader(part, lang) + require.resolve('./selector.js') + '?' + part + '/' + lang + '!' + vueUrl) + ')'; } var me = this; From 2dabd6620f3dbeeba2e709f449be70868c9a1faf Mon Sep 17 00:00:00 2001 From: Sjoerd Visscher Date: Thu, 14 May 2015 17:07:18 +0200 Subject: [PATCH 009/877] Use template-html-loader by default instead of trying to execute template functions --- index.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/index.js b/index.js index 7f09ce574..67fcc3016 100644 --- a/index.js +++ b/index.js @@ -12,7 +12,7 @@ module.exports = function (content) { js: '' }; var loaderPrefix = { - template: '', + template: 'html!template-html-loader?raw&engine=', style: 'style!css!', script: '' }; @@ -54,7 +54,6 @@ module.exports = function (content) { if (hasTemplate) return cb(new Error('Only one template element allowed per vue component!')); output += 'module.exports.template = ' + getRequire('template', lang); - output += '\nif (module.exports.template instanceof Function) module.exports.template = module.exports.template({})'; hasTemplate = true; } From 2f7d5df45fe481d4ca2de4ffab3211a7f4019ae9 Mon Sep 17 00:00:00 2001 From: Sjoerd Visscher Date: Thu, 14 May 2015 17:33:58 +0200 Subject: [PATCH 010/877] Loader configuration via query arguments --- index.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/index.js b/index.js index 67fcc3016..4e4a1b38f 100644 --- a/index.js +++ b/index.js @@ -6,11 +6,10 @@ module.exports = function (content) { var languages = {}; var output = ''; var vueUrl = loaderUtils.getRemainingRequest(this); - var loaders = { - html: 'html', - css: 'style!css', - js: '' - }; + var loaders = loaderUtils.parseQuery(this.query); + loaders.html = loaders.html || 'html'; + loaders.css = loaders.css || 'style!css'; + loaders.js = loaders.js || ''; var loaderPrefix = { template: 'html!template-html-loader?raw&engine=', style: 'style!css!', From e83905548f563f06ea3ecff2cbee5057f28453cb Mon Sep 17 00:00:00 2001 From: Sjoerd Visscher Date: Fri, 15 May 2015 00:31:47 +0200 Subject: [PATCH 011/877] Helper function for loader configuration --- index.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/index.js b/index.js index 4e4a1b38f..4b013ca29 100644 --- a/index.js +++ b/index.js @@ -59,3 +59,7 @@ module.exports = function (content) { cb(null, output); }) } + +module.exports.withLoaders = function (opts) { + return 'vue-multi-loader?' + JSON.stringify(opts).replace(/!/g, '\\u0021') +} From e42864d490526a9a59626eec662aae3bcb799cdd Mon Sep 17 00:00:00 2001 From: Sjoerd Visscher Date: Fri, 15 May 2015 00:42:03 +0200 Subject: [PATCH 012/877] source map support --- package.json | 3 ++- parser.js | 50 +++++++++++++++++++++++++++++++++++++++----------- selector.js | 3 ++- 3 files changed, 43 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index d4f13b578..987d7dda4 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "homepage": "https://github.com/Q42/vue-multi-loader", "dependencies": { "loader-utils": "^0.2.7", - "parse5": "^1.1.4" + "parse5": "^1.1.4", + "source-map": "^0.4.2" } } diff --git a/parser.js b/parser.js index 39c83313f..9b9990f6a 100644 --- a/parser.js +++ b/parser.js @@ -1,10 +1,14 @@ var parse5 = require('parse5'); -var parser = new parse5.Parser(); +var parser = new parse5.Parser(null, { locationInfo: true }); var serializer = new parse5.TreeSerializer(); +var SourceNode = require("source-map").SourceNode; +var loaderUtils = require("loader-utils"); module.exports = function (content) { this.cacheable(); var cb = this.async(); + var vueRequest = loaderUtils.getRemainingRequest(this); + var request = loaderUtils.getCurrentRequest(this); var languages = {}; var output = { @@ -14,6 +18,13 @@ module.exports = function (content) { includes: [] }; + function pos(offset) { + return { + line: content.substr(0, offset).split('\n').length, + col: offset - content.lastIndexOf('\n', offset - 1) + } + } + var fragment = parser.parseFragment(content); fragment.childNodes.forEach(function (node) { var type = node.nodeName; @@ -23,9 +34,35 @@ module.exports = function (content) { return; var lang = checkLang(node) || ''; - output[type][lang] = (output[type][lang] || '') + serialize(node) + '\n'; + + // Work around changes in parse5 >= 1.2.0 + var childNode = node.childNodes[0]; + if (childNode && childNode.nodeName === '#document-fragment') { + node = childNode; + } + + if (!node.childNodes.length) + return; + + var start = node.childNodes[0].__location.start; + var end = node.childNodes[node.childNodes.length - 1].__location.end; + var lines = content.substring(start, end).split('\n'); + var startPos = pos(start); + var sourceNodes = lines.map(function (line, i) { + return new SourceNode(startPos.line + i, i ? 0 : startPos.col, vueRequest, line + '\n'); + }); + output[type][lang] = (output[type][lang] || []).concat(sourceNodes) }); + for (var type in output) { + for (var lang in output[type]) { + var sourceNodes = output[type][lang]; + output[type][lang] = new SourceNode(1, 1, vueRequest, sourceNodes).toStringWithSourceMap({ + file: request + }) + } + } + cb(null, 'module.exports = ' + JSON.stringify(output)); } @@ -54,12 +91,3 @@ function checkSrc (node, arr) { } return false } - -// Work around changes in parse5 >= 1.2.0 -function serialize (node) { - var childNode = node.childNodes[0]; - if (childNode && childNode.nodeName === '#document-fragment') { - return serializer.serialize(childNode); - } - return serializer.serialize(node); -} diff --git a/selector.js b/selector.js index ffed1b3f9..956f2ea74 100644 --- a/selector.js +++ b/selector.js @@ -8,6 +8,7 @@ module.exports = function () { this.loadModule(url, function(err, source) { if (err) return cb(err); var parts = me.exec(source, url); - cb(null, parts[path[0]][path[1]||'']); + var part = parts[path[0]][path[1]||'']; + cb(null, part.code, part.map); }) } From f9945eeee6692d2d19fb03ac193ff47095b52416 Mon Sep 17 00:00:00 2001 From: Sjoerd Visscher Date: Fri, 15 May 2015 00:54:47 +0200 Subject: [PATCH 013/877] v0.0.4 --- README.md | 34 ++++++++++++++++++++++++++++++++++ package.json | 2 +- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a55de700b..aca832ae5 100644 --- a/README.md +++ b/README.md @@ -81,3 +81,37 @@ var Vue = require('vue') var appOptions = require('./app.vue') var app = new Vue(appOptions).$mount('#app') ``` + +## Loader configuration + +By default, `vue-multi-loader` will try to use the loader with the same name as +the `lang` attribute, but you can configure which loader should be used. + +For example, to extract out the generated css into a separate file, +use this configuration: + +``` js +// webpack.config.js +var ExtractTextPlugin = require("extract-text-webpack-plugin"); +var vue = require("vue-multi-loader"); + +module.exports = { + entry: "./main.js", + output: { + filename: "build.js" + }, + module: { + loaders: [ + { + test: /\.vue$/, loader: vue.withLoaders({ + css: ExtractTextPlugin.extract("css"), + stylus: ExtractTextPlugin.extract("css!stylus") + }) + }, + ] + }, + plugins: [ + new ExtractTextPlugin("[name].css") + ] +} +``` diff --git a/package.json b/package.json index 987d7dda4..c3f18bc47 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vue-multi-loader", - "version": "0.0.3", + "version": "0.0.4", "description": "Vue.js component loader for Webpack, using Webpack loaders for the parts", "main": "index.js", "repository": { From 89d7d515025a8dd5d72285796f39dfafd91a86c0 Mon Sep 17 00:00:00 2001 From: Sjoerd Visscher Date: Mon, 8 Jun 2015 13:39:55 +0200 Subject: [PATCH 014/877] v0.0.5 Parse with htmlparser2 Fixes #1 --- package.json | 2 +- parser.js | 56 ++++++++++++++-------------------------------------- 2 files changed, 16 insertions(+), 42 deletions(-) diff --git a/package.json b/package.json index c3f18bc47..92c06dc1a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vue-multi-loader", - "version": "0.0.4", + "version": "0.0.5", "description": "Vue.js component loader for Webpack, using Webpack loaders for the parts", "main": "index.js", "repository": { diff --git a/parser.js b/parser.js index 9b9990f6a..b6db5493e 100644 --- a/parser.js +++ b/parser.js @@ -1,5 +1,5 @@ var parse5 = require('parse5'); -var parser = new parse5.Parser(null, { locationInfo: true }); +var parser = new parse5.Parser(parse5.TreeAdapters.htmlparser2, { locationInfo: true }); var serializer = new parse5.TreeSerializer(); var SourceNode = require("source-map").SourceNode; var loaderUtils = require("loader-utils"); @@ -26,26 +26,26 @@ module.exports = function (content) { } var fragment = parser.parseFragment(content); - fragment.childNodes.forEach(function (node) { - var type = node.nodeName; - if (type == '#text') + fragment.children.forEach(function (node) { + if (node.attribs && node.attribs.src) { + output.includes.push(node.attribs.src) return; - if (checkSrc(node, output.includes)) + } + + if (!node.children || !node.children.length) return; - var lang = checkLang(node) || ''; + var lang = (node.attribs && node.attribs.lang) || ''; + var type = node.name; + if (!output[type]) + return; // Work around changes in parse5 >= 1.2.0 - var childNode = node.childNodes[0]; - if (childNode && childNode.nodeName === '#document-fragment') { - node = childNode; - } + if (node.children[0].type === 'root') + node = node.children[0]; - if (!node.childNodes.length) - return; - - var start = node.childNodes[0].__location.start; - var end = node.childNodes[node.childNodes.length - 1].__location.end; + var start = node.children[0].__location.start; + var end = node.children[node.children.length - 1].__location.end; var lines = content.substring(start, end).split('\n'); var startPos = pos(start); var sourceNodes = lines.map(function (line, i) { @@ -65,29 +65,3 @@ module.exports = function (content) { cb(null, 'module.exports = ' + JSON.stringify(output)); } - -function checkLang (node) { - if (node.attrs) { - var i = node.attrs.length; - while (i--) { - var attr = node.attrs[i]; - if (attr.name === 'lang') { - return attr.value; - } - } - } -} - -function checkSrc (node, arr) { - if (node.attrs) { - var i = node.attrs.length; - while (i--) { - var attr = node.attrs[i]; - if (attr.name === 'src') { - arr.push(attr.value); - return true; - } - } - } - return false -} From ff72520043253e2ed15c0bcf7bd2edc18ec6f987 Mon Sep 17 00:00:00 2001 From: Evan You Date: Mon, 8 Jun 2015 17:48:30 -0400 Subject: [PATCH 015/877] add peer dependencies --- package.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/package.json b/package.json index 92c06dc1a..1b0e58afd 100644 --- a/package.json +++ b/package.json @@ -22,5 +22,10 @@ "loader-utils": "^0.2.7", "parse5": "^1.1.4", "source-map": "^0.4.2" + }, + "peerDependencies": { + "css-loader": "^0.14.4", + "html-loader": "^0.3.0", + "style-loader": "^0.12.3" } } From 32d388f9223a81f162b2193c86b27f2eda1ee96d Mon Sep 17 00:00:00 2001 From: Evan You Date: Fri, 12 Jun 2015 00:13:38 -0400 Subject: [PATCH 016/877] renaming --- README.md | 8 ++++---- index.js | 2 +- package.json | 12 ++++++------ 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index aca832ae5..80a838c37 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# vue-multi-loader +# vue-loader > Vue.js component loader for [Webpack](http://webpack.github.io), using Webpack loaders for the parts. @@ -67,7 +67,7 @@ module.exports = { }, module: { loaders: [ - { test: /\.vue$/, loader: "vue-multi-loader" }, + { test: /\.vue$/, loader: "vue-loader" }, ] } } @@ -84,7 +84,7 @@ var app = new Vue(appOptions).$mount('#app') ## Loader configuration -By default, `vue-multi-loader` will try to use the loader with the same name as +By default, `vue-loader` will try to use the loader with the same name as the `lang` attribute, but you can configure which loader should be used. For example, to extract out the generated css into a separate file, @@ -93,7 +93,7 @@ use this configuration: ``` js // webpack.config.js var ExtractTextPlugin = require("extract-text-webpack-plugin"); -var vue = require("vue-multi-loader"); +var vue = require("vue-loader"); module.exports = { entry: "./main.js", diff --git a/index.js b/index.js index 4b013ca29..96622382d 100644 --- a/index.js +++ b/index.js @@ -61,5 +61,5 @@ module.exports = function (content) { } module.exports.withLoaders = function (opts) { - return 'vue-multi-loader?' + JSON.stringify(opts).replace(/!/g, '\\u0021') + return 'vue-loader?' + JSON.stringify(opts).replace(/!/g, '\\u0021') } diff --git a/package.json b/package.json index 1b0e58afd..a57636c6f 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,11 @@ { - "name": "vue-multi-loader", - "version": "0.0.5", - "description": "Vue.js component loader for Webpack, using Webpack loaders for the parts", + "name": "vue-loader", + "version": "1.1.7", + "description": "Vue.js component loader for Webpack", "main": "index.js", "repository": { "type": "git", - "url": "https://github.com/Q42/vue-multi-loader.git" + "url": "https://github.com/vuejs/vue-loader.git" }, "keywords": [ "vue", @@ -15,9 +15,9 @@ "author": "Sjoerd Visscher", "license": "ISC", "bugs": { - "url": "https://github.com/Q42/vue-multi-loader/issues" + "url": "https://github.com/vuejs/vue-loader/issues" }, - "homepage": "https://github.com/Q42/vue-multi-loader", + "homepage": "https://github.com/vuejs/vue-loader", "dependencies": { "loader-utils": "^0.2.7", "parse5": "^1.1.4", From 8fd05e41eabf464b1b3f0e731a3027eebe1a4588 Mon Sep 17 00:00:00 2001 From: Evan You Date: Fri, 12 Jun 2015 19:37:21 -0400 Subject: [PATCH 017/877] add a note for template loader --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 80a838c37..736b3d699 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,12 @@ var appOptions = require('./app.vue') var app = new Vue(appOptions).$mount('#app') ``` +## Pre-Processors + +By default `vue-loader` needs `html-loader`, `css-loader` and `style-loader` as peer dependencies. In order to use pre-processors, you need to install the corresponding Webpack loader for that language. + +**Note** For template pre-processors, use `template-html-loader` plus the raw templating engine. For example to use `jade`, you will need to install both `template-html-loader` and `jade` instead of `jade-loader`. + ## Loader configuration By default, `vue-loader` will try to use the loader with the same name as From fc534b24b1f6dd04dfce9a2a1ff2b0135b44c4ed Mon Sep 17 00:00:00 2001 From: Evan You Date: Fri, 12 Jun 2015 19:43:57 -0400 Subject: [PATCH 018/877] bump --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a57636c6f..338d6bd3f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vue-loader", - "version": "1.1.7", + "version": "2.0.0", "description": "Vue.js component loader for Webpack", "main": "index.js", "repository": { From 95dd6262c06a824ebec36307fd2aa7666318574d Mon Sep 17 00:00:00 2001 From: Evan You Date: Sun, 14 Jun 2015 18:41:36 -0400 Subject: [PATCH 019/877] some formatting and comments --- index.js | 107 ++++++++++++++++++++++++++++++++++++---------------- parser.js | 54 +++++++++++++------------- selector.js | 18 ++++----- 3 files changed, 111 insertions(+), 68 deletions(-) diff --git a/index.js b/index.js index 96622382d..6d7b492a2 100644 --- a/index.js +++ b/index.js @@ -1,65 +1,108 @@ -var loaderUtils = require("loader-utils"); +var loaderUtils = require("loader-utils") module.exports = function (content) { - this.cacheable(); - var cb = this.async(); - var languages = {}; - var output = ''; - var vueUrl = loaderUtils.getRemainingRequest(this); - var loaders = loaderUtils.parseQuery(this.query); - loaders.html = loaders.html || 'html'; - loaders.css = loaders.css || 'style!css'; - loaders.js = loaders.js || ''; + this.cacheable() + var cb = this.async() + var languages = {} + var output = '' + var vueUrl = loaderUtils.getRemainingRequest(this) + + // check if there are custom loaders specified with + // vueLoader.withLoaders(), otherwise use defaults + var loaders = loaderUtils.parseQuery(this.query) + loaders.html = loaders.html || 'html' + loaders.css = loaders.css || 'style!css' + loaders.js = loaders.js || '' + var loaderPrefix = { template: 'html!template-html-loader?raw&engine=', style: 'style!css!', script: '' - }; + } + var defaultLang = { template: 'html', style: 'css', script: 'js' - }; + } + /** + * Determine the loaders to use for an extracted part. + * + * @param {String} part - style|script|template + * @param {String} lang + * @return {String} + */ function loader(part, lang) { - lang = lang || defaultLang[part]; - var loader = loaders[lang] !== undefined ? loaders[lang] : loaderPrefix[part] + lang; - return loader ? loader + '!' : ''; + lang = lang || defaultLang[part] + var loader = loaders[lang] !== undefined + ? loaders[lang] + : loaderPrefix[part] + lang + return loader ? loader + '!' : '' } - var me = this; + /** + * Generate a require call for an extracted part. + * + * @param {String} part - style|script|template + * @param {String} lang + * @return {String} + */ + var self = this function getRequire(part, lang) { - return 'require(' + loaderUtils.stringifyRequest(me, '-!' + loader(part, lang) + require.resolve('./selector.js') + '?' + part + '/' + lang + '!' + vueUrl) + ')'; + return 'require(' + + loaderUtils.stringifyRequest(self, + '-!' + loader(part, lang) + + require.resolve('./selector.js') + '?' + part + '/' + lang + '!' + + vueUrl + ) + + ')' } - var me = this; - var url = "!!" + require.resolve("./parser.js") + "!" + vueUrl; + var self = this + var url = "!!" + require.resolve("./parser.js") + "!" + vueUrl this.loadModule(url, function(err, source, map, module) { - if (err) return cb(err); + if (err) return cb(err) - var parts = me.exec(source, url); + // up to this part, what we have done is basically executing + // parser.js on the raw vue file and get the parsing result + // which is an object that contains info about the vue file. + var parts = self.exec(source, url) - for (var i = 0; i < parts.includes.length; i++) - output += 'require(' + loaderUtils.stringifyRequest(this, loaderUtils.urlToRequest(parts.includes[i])) + ')\n'; + // add require for all the src imports + for (var i = 0; i < parts.includes.length; i++) { + var importReqeust = loaderUtils.urlToRequest(parts.includes[i]) + output += 'require(' + loaderUtils.stringifyRequest(this, importReqeust) + ')\n' + } - for (var lang in parts.style) - output += getRequire('style', lang) + '\n'; + // add require for styles + for (var lang in parts.style) { + output += getRequire('style', lang) + '\n' + } - for (var lang in parts.script) - output += 'module.exports = ' + getRequire('script', lang) + '\n'; + // add require for script + for (var lang in parts.script) { + output += 'module.exports = ' + getRequire('script', lang) + '\n' + } - var hasTemplate = false; + // add require for template + var hasTemplate = false for (var lang in parts.template) { if (hasTemplate) - return cb(new Error('Only one template element allowed per vue component!')); - output += 'module.exports.template = ' + getRequire('template', lang); - hasTemplate = true; + return cb(new Error('Only one template element allowed per vue component!')) + output += 'module.exports.template = ' + getRequire('template', lang) + hasTemplate = true } - cb(null, output); + // done + cb(null, output) }) } +/** + * Expose a way to specify custom loaders to be used at the + * end for the extracted parts of a component. + */ module.exports.withLoaders = function (opts) { return 'vue-loader?' + JSON.stringify(opts).replace(/!/g, '\\u0021') } diff --git a/parser.js b/parser.js index b6db5493e..d9e8f59b3 100644 --- a/parser.js +++ b/parser.js @@ -1,22 +1,22 @@ -var parse5 = require('parse5'); -var parser = new parse5.Parser(parse5.TreeAdapters.htmlparser2, { locationInfo: true }); -var serializer = new parse5.TreeSerializer(); -var SourceNode = require("source-map").SourceNode; -var loaderUtils = require("loader-utils"); +var parse5 = require('parse5') +var parser = new parse5.Parser(parse5.TreeAdapters.htmlparser2, { locationInfo: true }) +var serializer = new parse5.TreeSerializer() +var SourceNode = require("source-map").SourceNode +var loaderUtils = require("loader-utils") module.exports = function (content) { - this.cacheable(); - var cb = this.async(); - var vueRequest = loaderUtils.getRemainingRequest(this); - var request = loaderUtils.getCurrentRequest(this); + this.cacheable() + var cb = this.async() + var vueRequest = loaderUtils.getRemainingRequest(this) + var request = loaderUtils.getCurrentRequest(this) - var languages = {}; + var languages = {} var output = { template: {}, style: {}, script: {}, includes: [] - }; + } function pos(offset) { return { @@ -25,43 +25,43 @@ module.exports = function (content) { } } - var fragment = parser.parseFragment(content); + var fragment = parser.parseFragment(content) fragment.children.forEach(function (node) { if (node.attribs && node.attribs.src) { output.includes.push(node.attribs.src) - return; + return } if (!node.children || !node.children.length) - return; + return - var lang = (node.attribs && node.attribs.lang) || ''; - var type = node.name; + var lang = (node.attribs && node.attribs.lang) || '' + var type = node.name if (!output[type]) - return; + return // Work around changes in parse5 >= 1.2.0 if (node.children[0].type === 'root') - node = node.children[0]; + node = node.children[0] - var start = node.children[0].__location.start; - var end = node.children[node.children.length - 1].__location.end; - var lines = content.substring(start, end).split('\n'); - var startPos = pos(start); + var start = node.children[0].__location.start + var end = node.children[node.children.length - 1].__location.end + var lines = content.substring(start, end).split('\n') + var startPos = pos(start) var sourceNodes = lines.map(function (line, i) { - return new SourceNode(startPos.line + i, i ? 0 : startPos.col, vueRequest, line + '\n'); - }); + return new SourceNode(startPos.line + i, i ? 0 : startPos.col, vueRequest, line + '\n') + }) output[type][lang] = (output[type][lang] || []).concat(sourceNodes) - }); + }) for (var type in output) { for (var lang in output[type]) { - var sourceNodes = output[type][lang]; + var sourceNodes = output[type][lang] output[type][lang] = new SourceNode(1, 1, vueRequest, sourceNodes).toStringWithSourceMap({ file: request }) } } - cb(null, 'module.exports = ' + JSON.stringify(output)); + cb(null, 'module.exports = ' + JSON.stringify(output)) } diff --git a/selector.js b/selector.js index 956f2ea74..73062fd2e 100644 --- a/selector.js +++ b/selector.js @@ -1,14 +1,14 @@ module.exports = function () { - this.cacheable(); - var cb = this.async(); - var path = this.query.substr(1).split('/'); + this.cacheable() + var cb = this.async() + var path = this.query.substr(1).split('/') - var me = this; - var url = "!!" + require.resolve("./parser.js") + "!" + this.resource; + var self = this + var url = "!!" + require.resolve("./parser.js") + "!" + this.resource this.loadModule(url, function(err, source) { - if (err) return cb(err); - var parts = me.exec(source, url); - var part = parts[path[0]][path[1]||'']; - cb(null, part.code, part.map); + if (err) return cb(err) + var parts = self.exec(source, url) + var part = parts[path[0]][path[1]||''] + cb(null, part.code, part.map) }) } From 02eaf0cd02caf15e76c6fcd6babeae94d392a40b Mon Sep 17 00:00:00 2001 From: Evan You Date: Mon, 15 Jun 2015 15:12:09 -0400 Subject: [PATCH 020/877] fix source map --- index.js | 2 +- package.json | 3 +-- parser.js | 25 +------------------------ selector.js | 5 +++-- 4 files changed, 6 insertions(+), 29 deletions(-) diff --git a/index.js b/index.js index 6d7b492a2..1965481bb 100644 --- a/index.js +++ b/index.js @@ -61,7 +61,7 @@ module.exports = function (content) { var self = this var url = "!!" + require.resolve("./parser.js") + "!" + vueUrl - this.loadModule(url, function(err, source, map, module) { + this.loadModule(url, function(err, source) { if (err) return cb(err) // up to this part, what we have done is basically executing diff --git a/package.json b/package.json index 338d6bd3f..59521c024 100644 --- a/package.json +++ b/package.json @@ -20,8 +20,7 @@ "homepage": "https://github.com/vuejs/vue-loader", "dependencies": { "loader-utils": "^0.2.7", - "parse5": "^1.1.4", - "source-map": "^0.4.2" + "parse5": "^1.1.4" }, "peerDependencies": { "css-loader": "^0.14.4", diff --git a/parser.js b/parser.js index d9e8f59b3..203251486 100644 --- a/parser.js +++ b/parser.js @@ -1,7 +1,5 @@ var parse5 = require('parse5') var parser = new parse5.Parser(parse5.TreeAdapters.htmlparser2, { locationInfo: true }) -var serializer = new parse5.TreeSerializer() -var SourceNode = require("source-map").SourceNode var loaderUtils = require("loader-utils") module.exports = function (content) { @@ -18,13 +16,6 @@ module.exports = function (content) { includes: [] } - function pos(offset) { - return { - line: content.substr(0, offset).split('\n').length, - col: offset - content.lastIndexOf('\n', offset - 1) - } - } - var fragment = parser.parseFragment(content) fragment.children.forEach(function (node) { if (node.attribs && node.attribs.src) { @@ -46,22 +37,8 @@ module.exports = function (content) { var start = node.children[0].__location.start var end = node.children[node.children.length - 1].__location.end - var lines = content.substring(start, end).split('\n') - var startPos = pos(start) - var sourceNodes = lines.map(function (line, i) { - return new SourceNode(startPos.line + i, i ? 0 : startPos.col, vueRequest, line + '\n') - }) - output[type][lang] = (output[type][lang] || []).concat(sourceNodes) + output[type][lang] = content.substring(start, end).trim() }) - for (var type in output) { - for (var lang in output[type]) { - var sourceNodes = output[type][lang] - output[type][lang] = new SourceNode(1, 1, vueRequest, sourceNodes).toStringWithSourceMap({ - file: request - }) - } - } - cb(null, 'module.exports = ' + JSON.stringify(output)) } diff --git a/selector.js b/selector.js index 73062fd2e..c98db663c 100644 --- a/selector.js +++ b/selector.js @@ -8,7 +8,8 @@ module.exports = function () { this.loadModule(url, function(err, source) { if (err) return cb(err) var parts = self.exec(source, url) - var part = parts[path[0]][path[1]||''] - cb(null, part.code, part.map) + var type = path[0] + var lang = path[1] || '' + cb(null, parts[type][lang]) }) } From 95b486f51b44c5fc233ef85fc56a764d627eb0a5 Mon Sep 17 00:00:00 2001 From: Evan You Date: Mon, 15 Jun 2015 18:49:37 -0400 Subject: [PATCH 021/877] tests --- .gitignore | 1 + package.json | 22 ++++++++ test/fixtures/basic.js | 1 + test/fixtures/basic.vue | 19 +++++++ test/fixtures/pre.js | 1 + test/fixtures/pre.vue | 23 ++++++++ test/test.js | 120 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 187 insertions(+) create mode 100644 test/fixtures/basic.js create mode 100644 test/fixtures/basic.vue create mode 100644 test/fixtures/pre.js create mode 100644 test/fixtures/pre.vue create mode 100644 test/test.js diff --git a/.gitignore b/.gitignore index 7a3a95d56..e698d7971 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /node_modules /npm-debug.log +test/output diff --git a/package.json b/package.json index 59521c024..c8157b571 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,9 @@ "url": "https://github.com/vuejs/vue-loader/issues" }, "homepage": "https://github.com/vuejs/vue-loader", + "scripts": { + "test": "mocha test/test.js --slow 2000" + }, "dependencies": { "loader-utils": "^0.2.7", "parse5": "^1.1.4" @@ -26,5 +29,24 @@ "css-loader": "^0.14.4", "html-loader": "^0.3.0", "style-loader": "^0.12.3" + }, + "devDependencies": { + "babel-core": "^5.5.8", + "babel-loader": "^5.1.4", + "chai": "^3.0.0", + "css-loader": "^0.14.4", + "html-loader": "^0.3.0", + "jade": "^1.11.0", + "jsdom": "^5.4.3", + "mkdirp": "^0.5.1", + "mocha": "^2.2.5", + "node-libs-browser": "^0.5.2", + "object-assign": "^3.0.0", + "rimraf": "^2.4.0", + "source-map": "^0.4.2", + "style-loader": "^0.12.3", + "stylus-loader": "^1.2.0", + "template-html-loader": "0.0.3", + "webpack": "^1.9.11" } } diff --git a/test/fixtures/basic.js b/test/fixtures/basic.js new file mode 100644 index 000000000..b5a46fc5f --- /dev/null +++ b/test/fixtures/basic.js @@ -0,0 +1 @@ +window.testModule = require('./basic.vue') diff --git a/test/fixtures/basic.vue b/test/fixtures/basic.vue new file mode 100644 index 000000000..5cf4c68ac --- /dev/null +++ b/test/fixtures/basic.vue @@ -0,0 +1,19 @@ + + + + + diff --git a/test/fixtures/pre.js b/test/fixtures/pre.js new file mode 100644 index 000000000..2238f374f --- /dev/null +++ b/test/fixtures/pre.js @@ -0,0 +1 @@ +window.testModule = require('./pre.vue') diff --git a/test/fixtures/pre.vue b/test/fixtures/pre.vue new file mode 100644 index 000000000..74685ec49 --- /dev/null +++ b/test/fixtures/pre.vue @@ -0,0 +1,23 @@ + + + + + diff --git a/test/test.js b/test/test.js new file mode 100644 index 000000000..3c3ad50ab --- /dev/null +++ b/test/test.js @@ -0,0 +1,120 @@ +var fs = require('fs') +var path = require('path') +var webpack = require('webpack') +var jsdom = require('jsdom') +var expect = require('chai').expect +var assign = require('object-assign') +var rimraf = require('rimraf') +var SourceMapConsumer = require('source-map').SourceMapConsumer + +describe('vue-loader', function () { + + var outputDir = path.resolve(__dirname, './output') + var loaderPath = path.resolve(__dirname, '../') + var testHTML = ' ' + var globalConfig = { + output: { + path: outputDir, + filename: 'test.build.js' + }, + module: { + loaders: [ + { + test: /\.vue$/, + loader: loaderPath + } + ] + } + } + + beforeEach(function (done) { + rimraf(outputDir, done) + }) + + function getFile (file, cb) { + fs.readFile(path.resolve(outputDir, file), 'utf-8', function (err, data) { + expect(err).to.be.null + cb(data) + }) + } + + function test (options, assert) { + var config = assign({}, globalConfig, options) + webpack(config, function (err) { + expect(err).to.be.null + getFile('test.build.js', function (data) { + jsdom.env({ + html: testHTML, + src: [data], + done: function (err, window) { + if (err) { + console.log(err[0].data.error.stack) + expect(err).to.be.null + } + assert(window) + } + }) + }) + }) + } + + it('basic', function (done) { + test({ + entry: './test/fixtures/basic.js' + }, function (window) { + var module = window.testModule + expect(module.template).to.contain('

{{msg}}

') + expect(module.data().msg).to.contain('Hello from Component A!') + var style = window.document.querySelector('style').textContent + expect(style).to.contain('comp-a h2 {\n color: #f00;\n}') + done() + }) + }) + + it('pre-processors', function (done) { + test({ + entry: './test/fixtures/pre.js' + }, function (window) { + var module = window.testModule + expect(module.template).to.contain( + '

This is the app

' + + '' + + '' + ) + expect(module.data().msg).to.contain('Hello from babel!') + var style = window.document.querySelector('style').textContent + expect(style).to.contain('body {\n font: 100% Helvetica, sans-serif;\n color: #999;\n}') + done() + }) + }) + + it('source-map', function (done) { + var config = assign({}, globalConfig, { + entry: './test/fixtures/basic.js', + devtool: 'source-map' + }) + webpack(config, function (err) { + expect(err).to.be.null + getFile('test.build.js.map', function (map) { + var smc = new SourceMapConsumer(JSON.parse(map)) + getFile('test.build.js', function (code) { + var line + code.split('\n').some(function (l, i) { + if (l.indexOf('Hello from Component A') > -1) { + line = i + 1 + return true + } + }) + var pos = smc.originalPositionFor({ + line: line, + column: 0 + }) + expect(pos.source.indexOf('webpack:///test/fixtures/basic.vue') > -1) + expect(pos.line).to.equal(4) + done() + }) + }) + }) + }) + +}) From 7c8535b4967a578f37a340f988753f3fdc5f58cf Mon Sep 17 00:00:00 2001 From: Evan You Date: Mon, 15 Jun 2015 18:49:58 -0400 Subject: [PATCH 022/877] 2.0.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c8157b571..9460d859a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vue-loader", - "version": "2.0.0", + "version": "2.0.1", "description": "Vue.js component loader for Webpack", "main": "index.js", "repository": { From e9feb634fc34a142d64e56859f65aa4ca211dad5 Mon Sep 17 00:00:00 2001 From: Evan You Date: Mon, 15 Jun 2015 19:28:07 -0400 Subject: [PATCH 023/877] ci --- README.md | 2 +- circle.yml | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 circle.yml diff --git a/README.md b/README.md index 736b3d699..a8230ddf5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# vue-loader +# vue-loader [![Build Status](https://img.shields.io/circleci/project/vuejs/vue-loader.svg)](https://circleci.com/gh/vuejs/vue-loader) > Vue.js component loader for [Webpack](http://webpack.github.io), using Webpack loaders for the parts. diff --git a/circle.yml b/circle.yml new file mode 100644 index 000000000..72b639b85 --- /dev/null +++ b/circle.yml @@ -0,0 +1,3 @@ +machine: + node: + version: iojs-v2.3.0 From 6fb52acc02e57083e920b75dcd94bba9f1d47b9b Mon Sep 17 00:00:00 2001 From: Evan You Date: Mon, 15 Jun 2015 19:37:37 -0400 Subject: [PATCH 024/877] restructure files --- index.js | 109 +-------------------------------- lib/loader.js | 108 ++++++++++++++++++++++++++++++++ parser.js => lib/parser.js | 0 selector.js => lib/selector.js | 0 4 files changed, 109 insertions(+), 108 deletions(-) create mode 100644 lib/loader.js rename parser.js => lib/parser.js (100%) rename selector.js => lib/selector.js (100%) diff --git a/index.js b/index.js index 1965481bb..18abae36c 100644 --- a/index.js +++ b/index.js @@ -1,108 +1 @@ -var loaderUtils = require("loader-utils") - -module.exports = function (content) { - this.cacheable() - var cb = this.async() - var languages = {} - var output = '' - var vueUrl = loaderUtils.getRemainingRequest(this) - - // check if there are custom loaders specified with - // vueLoader.withLoaders(), otherwise use defaults - var loaders = loaderUtils.parseQuery(this.query) - loaders.html = loaders.html || 'html' - loaders.css = loaders.css || 'style!css' - loaders.js = loaders.js || '' - - var loaderPrefix = { - template: 'html!template-html-loader?raw&engine=', - style: 'style!css!', - script: '' - } - - var defaultLang = { - template: 'html', - style: 'css', - script: 'js' - } - - /** - * Determine the loaders to use for an extracted part. - * - * @param {String} part - style|script|template - * @param {String} lang - * @return {String} - */ - function loader(part, lang) { - lang = lang || defaultLang[part] - var loader = loaders[lang] !== undefined - ? loaders[lang] - : loaderPrefix[part] + lang - return loader ? loader + '!' : '' - } - - /** - * Generate a require call for an extracted part. - * - * @param {String} part - style|script|template - * @param {String} lang - * @return {String} - */ - var self = this - function getRequire(part, lang) { - return 'require(' + - loaderUtils.stringifyRequest(self, - '-!' + loader(part, lang) + - require.resolve('./selector.js') + '?' + part + '/' + lang + '!' + - vueUrl - ) + - ')' - } - - var self = this - var url = "!!" + require.resolve("./parser.js") + "!" + vueUrl - this.loadModule(url, function(err, source) { - if (err) return cb(err) - - // up to this part, what we have done is basically executing - // parser.js on the raw vue file and get the parsing result - // which is an object that contains info about the vue file. - var parts = self.exec(source, url) - - // add require for all the src imports - for (var i = 0; i < parts.includes.length; i++) { - var importReqeust = loaderUtils.urlToRequest(parts.includes[i]) - output += 'require(' + loaderUtils.stringifyRequest(this, importReqeust) + ')\n' - } - - // add require for styles - for (var lang in parts.style) { - output += getRequire('style', lang) + '\n' - } - - // add require for script - for (var lang in parts.script) { - output += 'module.exports = ' + getRequire('script', lang) + '\n' - } - - // add require for template - var hasTemplate = false - for (var lang in parts.template) { - if (hasTemplate) - return cb(new Error('Only one template element allowed per vue component!')) - output += 'module.exports.template = ' + getRequire('template', lang) - hasTemplate = true - } - - // done - cb(null, output) - }) -} - -/** - * Expose a way to specify custom loaders to be used at the - * end for the extracted parts of a component. - */ -module.exports.withLoaders = function (opts) { - return 'vue-loader?' + JSON.stringify(opts).replace(/!/g, '\\u0021') -} +module.exports = require('./lib/loader') diff --git a/lib/loader.js b/lib/loader.js new file mode 100644 index 000000000..1965481bb --- /dev/null +++ b/lib/loader.js @@ -0,0 +1,108 @@ +var loaderUtils = require("loader-utils") + +module.exports = function (content) { + this.cacheable() + var cb = this.async() + var languages = {} + var output = '' + var vueUrl = loaderUtils.getRemainingRequest(this) + + // check if there are custom loaders specified with + // vueLoader.withLoaders(), otherwise use defaults + var loaders = loaderUtils.parseQuery(this.query) + loaders.html = loaders.html || 'html' + loaders.css = loaders.css || 'style!css' + loaders.js = loaders.js || '' + + var loaderPrefix = { + template: 'html!template-html-loader?raw&engine=', + style: 'style!css!', + script: '' + } + + var defaultLang = { + template: 'html', + style: 'css', + script: 'js' + } + + /** + * Determine the loaders to use for an extracted part. + * + * @param {String} part - style|script|template + * @param {String} lang + * @return {String} + */ + function loader(part, lang) { + lang = lang || defaultLang[part] + var loader = loaders[lang] !== undefined + ? loaders[lang] + : loaderPrefix[part] + lang + return loader ? loader + '!' : '' + } + + /** + * Generate a require call for an extracted part. + * + * @param {String} part - style|script|template + * @param {String} lang + * @return {String} + */ + var self = this + function getRequire(part, lang) { + return 'require(' + + loaderUtils.stringifyRequest(self, + '-!' + loader(part, lang) + + require.resolve('./selector.js') + '?' + part + '/' + lang + '!' + + vueUrl + ) + + ')' + } + + var self = this + var url = "!!" + require.resolve("./parser.js") + "!" + vueUrl + this.loadModule(url, function(err, source) { + if (err) return cb(err) + + // up to this part, what we have done is basically executing + // parser.js on the raw vue file and get the parsing result + // which is an object that contains info about the vue file. + var parts = self.exec(source, url) + + // add require for all the src imports + for (var i = 0; i < parts.includes.length; i++) { + var importReqeust = loaderUtils.urlToRequest(parts.includes[i]) + output += 'require(' + loaderUtils.stringifyRequest(this, importReqeust) + ')\n' + } + + // add require for styles + for (var lang in parts.style) { + output += getRequire('style', lang) + '\n' + } + + // add require for script + for (var lang in parts.script) { + output += 'module.exports = ' + getRequire('script', lang) + '\n' + } + + // add require for template + var hasTemplate = false + for (var lang in parts.template) { + if (hasTemplate) + return cb(new Error('Only one template element allowed per vue component!')) + output += 'module.exports.template = ' + getRequire('template', lang) + hasTemplate = true + } + + // done + cb(null, output) + }) +} + +/** + * Expose a way to specify custom loaders to be used at the + * end for the extracted parts of a component. + */ +module.exports.withLoaders = function (opts) { + return 'vue-loader?' + JSON.stringify(opts).replace(/!/g, '\\u0021') +} diff --git a/parser.js b/lib/parser.js similarity index 100% rename from parser.js rename to lib/parser.js diff --git a/selector.js b/lib/selector.js similarity index 100% rename from selector.js rename to lib/selector.js From 43ed65a250fa3989d30aa3187b0e2d9b8fd3be14 Mon Sep 17 00:00:00 2001 From: Evan You Date: Mon, 15 Jun 2015 19:39:43 -0400 Subject: [PATCH 025/877] fix license [ci skip] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9460d859a..95f085aef 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "loader" ], "author": "Sjoerd Visscher", - "license": "ISC", + "license": "MIT", "bugs": { "url": "https://github.com/vuejs/vue-loader/issues" }, From 541a01a3b0b079f339c3dc6222332b0fbb274480 Mon Sep 17 00:00:00 2001 From: Sjoerd Visscher Date: Tue, 23 Jun 2015 15:01:33 +0200 Subject: [PATCH 026/877] Support empty template tags --- lib/parser.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/parser.js b/lib/parser.js index 203251486..433a62717 100644 --- a/lib/parser.js +++ b/lib/parser.js @@ -32,8 +32,11 @@ module.exports = function (content) { return // Work around changes in parse5 >= 1.2.0 - if (node.children[0].type === 'root') + if (node.children[0].type === 'root') { node = node.children[0] + if (!node.children.length) + return; + } var start = node.children[0].__location.start var end = node.children[node.children.length - 1].__location.end From 6ac8b2e8c2c4b4e587f10e3ed5e390a4bdadb8a8 Mon Sep 17 00:00:00 2001 From: Evan You Date: Wed, 15 Jul 2015 23:36:08 -0700 Subject: [PATCH 027/877] add eslint --- .eslintrc | 179 ++++++++++++++++++++++++++++++++++++++++++++++++ lib/loader.js | 28 ++++---- lib/parser.js | 16 ++--- lib/selector.js | 4 +- package.json | 4 +- 5 files changed, 205 insertions(+), 26 deletions(-) create mode 100644 .eslintrc diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 000000000..068f0d4a4 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,179 @@ +{ + "env": { + "browser": true, + "node": true + }, + + "rules": { + "accessor-pairs": 2, + "array-bracket-spacing": 0, + "block-scoped-var": 0, + "brace-style": [2, "1tbs", { "allowSingleLine": true }], + "camelcase": 0, + "comma-dangle": [2, "never"], + "comma-spacing": [2, { "before": false, "after": true }], + "comma-style": [2, "last"], + "complexity": 0, + "computed-property-spacing": 0, + "consistent-return": 0, + "consistent-this": 0, + "constructor-super": 2, + "curly": [2, "multi-line"], + "default-case": 0, + "dot-location": [2, "property"], + "dot-notation": 0, + "eol-last": 2, + "eqeqeq": [2, "allow-null"], + "func-names": 0, + "func-style": 0, + "generator-star": 0, + "generator-star-spacing": [2, { "before": true, "after": true }], + "global-strict": 0, + "guard-for-in": 0, + "handle-callback-err": [2, "^(err|error)$" ], + "indent": [2, 2], + "key-spacing": [2, { "beforeColon": false, "afterColon": true }], + "linebreak-style": 0, + "lines-around-comment": 0, + "max-depth": 0, + "max-len": 0, + "max-nested-callbacks": 0, + "max-params": 0, + "max-statements": 0, + "new-cap": [2, { "newIsCap": true, "capIsNew": false }], + "new-parens": 2, + "newline-after-var": 0, + "no-alert": 0, + "no-array-constructor": 2, + "no-bitwise": 0, + "no-caller": 2, + "no-catch-shadow": 0, + "no-comma-dangle": 0, + "no-cond-assign": 2, + "no-console": 0, + "no-constant-condition": 0, + "no-continue": 0, + "no-control-regex": 2, + "no-debugger": 2, + "no-delete-var": 2, + "no-div-regex": 0, + "no-dupe-args": 2, + "no-dupe-keys": 2, + "no-duplicate-case": 2, + "no-else-return": 0, + "no-empty": 0, + "no-empty-character-class": 2, + "no-empty-class": 0, + "no-empty-label": 2, + "no-eq-null": 0, + "no-eval": 2, + "no-ex-assign": 2, + "no-extend-native": 2, + "no-extra-bind": 2, + "no-extra-boolean-cast": 2, + "no-extra-parens": 0, + "no-extra-semi": 0, + "no-extra-strict": 0, + "no-fallthrough": 2, + "no-floating-decimal": 2, + "no-func-assign": 2, + "no-implied-eval": 2, + "no-inline-comments": 0, + "no-inner-declarations": [2, "functions"], + "no-invalid-regexp": 2, + "no-irregular-whitespace": 2, + "no-iterator": 2, + "no-label-var": 2, + "no-labels": 2, + "no-lone-blocks": 2, + "no-lonely-if": 0, + "no-loop-func": 0, + "no-mixed-requires": 0, + "no-mixed-spaces-and-tabs": 2, + "no-multi-spaces": 2, + "no-multi-str": 2, + "no-multiple-empty-lines": [2, { "max": 1 }], + "no-native-reassign": 2, + "no-negated-in-lhs": 2, + "no-nested-ternary": 0, + "no-new": 2, + "no-new-func": 0, + "no-new-object": 2, + "no-new-require": 2, + "no-new-wrappers": 2, + "no-obj-calls": 2, + "no-octal": 2, + "no-octal-escape": 2, + "no-param-reassign": 0, + "no-path-concat": 0, + "no-plusplus": 0, + "no-process-env": 0, + "no-process-exit": 0, + "no-proto": 0, + "no-redeclare": 2, + "no-regex-spaces": 2, + "no-reserved-keys": 0, + "no-restricted-modules": 0, + "no-return-assign": 2, + "no-script-url": 0, + "no-self-compare": 2, + "no-sequences": 2, + "no-shadow": 0, + "no-shadow-restricted-names": 2, + "no-space-before-semi": 0, + "no-spaced-func": 2, + "no-sparse-arrays": 2, + "no-sync": 0, + "no-ternary": 0, + "no-this-before-super": 2, + "no-throw-literal": 2, + "no-trailing-spaces": 2, + "no-undef": 2, + "no-undef-init": 2, + "no-undefined": 0, + "no-underscore-dangle": 0, + "no-unexpected-multiline": 2, + "no-unneeded-ternary": 2, + "no-unreachable": 2, + "no-unused-expressions": 0, + "no-unused-vars": [2, { "vars": "all", "args": "none" }], + "no-use-before-define": 0, + "no-var": 0, + "no-void": 0, + "no-warning-comments": 0, + "no-with": 2, + "object-curly-spacing": 0, + "object-shorthand": 0, + "one-var": [2, { "initialized": "never" }], + "operator-assignment": 0, + "operator-linebreak": [2, "after"], + "padded-blocks": 0, + "prefer-const": 0, + "quote-props": 0, + "quotes": [2, "single", "avoid-escape"], + "radix": 2, + "semi": [2, "never"], + "semi-spacing": 0, + "sort-vars": 0, + "space-after-function-name": 0, + "space-after-keywords": [2, "always"], + "space-before-blocks": [2, "always"], + "space-before-function-paren": [2, "always"], + "space-before-function-parentheses": 0, + "space-in-brackets": 0, + "space-in-parens": [2, "never"], + "space-infix-ops": 2, + "space-return-throw-case": 2, + "space-unary-ops": [2, { "words": true, "nonwords": false }], + "spaced-comment": [2, "always", { "markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!"] }], + "spaced-line-comment": 0, + "strict": 0, + "use-isnan": 2, + "valid-jsdoc": 0, + "valid-typeof": 2, + "vars-on-top": 0, + "wrap-iife": [2, "any"], + "wrap-regex": 0, + "yoda": [2, "never"] + } +} diff --git a/lib/loader.js b/lib/loader.js index 1965481bb..5ce83ff03 100644 --- a/lib/loader.js +++ b/lib/loader.js @@ -1,9 +1,9 @@ -var loaderUtils = require("loader-utils") +var loaderUtils = require('loader-utils') module.exports = function (content) { + var self = this this.cacheable() var cb = this.async() - var languages = {} var output = '' var vueUrl = loaderUtils.getRemainingRequest(this) @@ -11,8 +11,8 @@ module.exports = function (content) { // vueLoader.withLoaders(), otherwise use defaults var loaders = loaderUtils.parseQuery(this.query) loaders.html = loaders.html || 'html' - loaders.css = loaders.css || 'style!css' - loaders.js = loaders.js || '' + loaders.css = loaders.css || 'style!css' + loaders.js = loaders.js || '' var loaderPrefix = { template: 'html!template-html-loader?raw&engine=', @@ -33,7 +33,7 @@ module.exports = function (content) { * @param {String} lang * @return {String} */ - function loader(part, lang) { + function loader (part, lang) { lang = lang || defaultLang[part] var loader = loaders[lang] !== undefined ? loaders[lang] @@ -48,8 +48,7 @@ module.exports = function (content) { * @param {String} lang * @return {String} */ - var self = this - function getRequire(part, lang) { + function getRequire (part, lang) { return 'require(' + loaderUtils.stringifyRequest(self, '-!' + loader(part, lang) + @@ -59,15 +58,15 @@ module.exports = function (content) { ')' } - var self = this - var url = "!!" + require.resolve("./parser.js") + "!" + vueUrl - this.loadModule(url, function(err, source) { + var url = '!!' + require.resolve('./parser.js') + '!' + vueUrl + this.loadModule(url, function (err, source) { if (err) return cb(err) // up to this part, what we have done is basically executing // parser.js on the raw vue file and get the parsing result // which is an object that contains info about the vue file. var parts = self.exec(source, url) + var lang // add require for all the src imports for (var i = 0; i < parts.includes.length; i++) { @@ -76,20 +75,21 @@ module.exports = function (content) { } // add require for styles - for (var lang in parts.style) { + for (lang in parts.style) { output += getRequire('style', lang) + '\n' } // add require for script - for (var lang in parts.script) { + for (lang in parts.script) { output += 'module.exports = ' + getRequire('script', lang) + '\n' } // add require for template var hasTemplate = false - for (var lang in parts.template) { - if (hasTemplate) + for (lang in parts.template) { + if (hasTemplate) { return cb(new Error('Only one template element allowed per vue component!')) + } output += 'module.exports.template = ' + getRequire('template', lang) hasTemplate = true } diff --git a/lib/parser.js b/lib/parser.js index 433a62717..6f8441fd2 100644 --- a/lib/parser.js +++ b/lib/parser.js @@ -1,14 +1,9 @@ var parse5 = require('parse5') var parser = new parse5.Parser(parse5.TreeAdapters.htmlparser2, { locationInfo: true }) -var loaderUtils = require("loader-utils") module.exports = function (content) { this.cacheable() var cb = this.async() - var vueRequest = loaderUtils.getRemainingRequest(this) - var request = loaderUtils.getCurrentRequest(this) - - var languages = {} var output = { template: {}, style: {}, @@ -23,19 +18,22 @@ module.exports = function (content) { return } - if (!node.children || !node.children.length) + if (!node.children || !node.children.length) { return + } var lang = (node.attribs && node.attribs.lang) || '' var type = node.name - if (!output[type]) + if (!output[type]) { return + } // Work around changes in parse5 >= 1.2.0 if (node.children[0].type === 'root') { node = node.children[0] - if (!node.children.length) - return; + if (!node.children.length) { + return + } } var start = node.children[0].__location.start diff --git a/lib/selector.js b/lib/selector.js index c98db663c..63d157e56 100644 --- a/lib/selector.js +++ b/lib/selector.js @@ -4,8 +4,8 @@ module.exports = function () { var path = this.query.substr(1).split('/') var self = this - var url = "!!" + require.resolve("./parser.js") + "!" + this.resource - this.loadModule(url, function(err, source) { + var url = '!!' + require.resolve('./parser.js') + '!' + this.resource + this.loadModule(url, function (err, source) { if (err) return cb(err) var parts = self.exec(source, url) var type = path[0] diff --git a/package.json b/package.json index 95f085aef..455ecf1d1 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,8 @@ }, "homepage": "https://github.com/vuejs/vue-loader", "scripts": { - "test": "mocha test/test.js --slow 2000" + "lint": "eslint lib", + "test": "eslint lib && mocha test/test.js --slow 2000" }, "dependencies": { "loader-utils": "^0.2.7", @@ -35,6 +36,7 @@ "babel-loader": "^5.1.4", "chai": "^3.0.0", "css-loader": "^0.14.4", + "eslint": "^1.0.0-rc-1", "html-loader": "^0.3.0", "jade": "^1.11.0", "jsdom": "^5.4.3", From e682a36fc0bcbd5cbd6efd7182e5077b76d30e64 Mon Sep 17 00:00:00 2001 From: Evan You Date: Wed, 15 Jul 2015 23:45:36 -0700 Subject: [PATCH 028/877] upgrade deps & use parse5 default adaptor --- lib/parser.js | 38 ++++++++++++++++++++++++++------------ package.json | 6 +++--- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/lib/parser.js b/lib/parser.js index 6f8441fd2..d53e196fd 100644 --- a/lib/parser.js +++ b/lib/parser.js @@ -1,5 +1,5 @@ var parse5 = require('parse5') -var parser = new parse5.Parser(parse5.TreeAdapters.htmlparser2, { locationInfo: true }) +var parser = new parse5.Parser(null, { locationInfo: true }) module.exports = function (content) { this.cacheable() @@ -12,34 +12,48 @@ module.exports = function (content) { } var fragment = parser.parseFragment(content) - fragment.children.forEach(function (node) { - if (node.attribs && node.attribs.src) { - output.includes.push(node.attribs.src) + fragment.childNodes.forEach(function (node) { + var src = getAttribute(node, 'src') + if (src) { + output.includes.push(src) return } - if (!node.children || !node.children.length) { + if (!node.childNodes || !node.childNodes.length) { return } - var lang = (node.attribs && node.attribs.lang) || '' - var type = node.name + var lang = getAttribute(node, 'lang') || '' + var type = node.tagName if (!output[type]) { return } // Work around changes in parse5 >= 1.2.0 - if (node.children[0].type === 'root') { - node = node.children[0] - if (!node.children.length) { + if (node.childNodes[0].nodeName === '#document-fragment') { + node = node.childNodes[0] + if (!node.childNodes.length) { return } } - var start = node.children[0].__location.start - var end = node.children[node.children.length - 1].__location.end + var start = node.childNodes[0].__location.start + var end = node.childNodes[node.childNodes.length - 1].__location.end output[type][lang] = content.substring(start, end).trim() }) cb(null, 'module.exports = ' + JSON.stringify(output)) } + +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 + } + } + } +} diff --git a/package.json b/package.json index 455ecf1d1..1e33f06a9 100644 --- a/package.json +++ b/package.json @@ -23,11 +23,11 @@ "test": "eslint lib && mocha test/test.js --slow 2000" }, "dependencies": { - "loader-utils": "^0.2.7", - "parse5": "^1.1.4" + "loader-utils": "^0.2.10", + "parse5": "^1.5.0" }, "peerDependencies": { - "css-loader": "^0.14.4", + "css-loader": "^0.15.4", "html-loader": "^0.3.0", "style-loader": "^0.12.3" }, From c5119809212d37a674bebd14fecfc9f7c11b515d Mon Sep 17 00:00:00 2001 From: Evan You Date: Thu, 16 Jul 2015 00:04:10 -0700 Subject: [PATCH 029/877] fix and add tests for import --- lib/loader.js | 6 ++++-- lib/parser.js | 12 +++++++++--- test/fixtures/import.css | 1 + test/fixtures/import.js | 1 + test/fixtures/import.vue | 1 + test/test.js | 12 +++++++++++- 6 files changed, 27 insertions(+), 6 deletions(-) create mode 100644 test/fixtures/import.css create mode 100644 test/fixtures/import.js create mode 100644 test/fixtures/import.vue diff --git a/lib/loader.js b/lib/loader.js index 5ce83ff03..efce04317 100644 --- a/lib/loader.js +++ b/lib/loader.js @@ -70,8 +70,10 @@ module.exports = function (content) { // add require for all the src imports for (var i = 0; i < parts.includes.length; i++) { - var importReqeust = loaderUtils.urlToRequest(parts.includes[i]) - output += 'require(' + loaderUtils.stringifyRequest(this, importReqeust) + ')\n' + var include = parts.includes[i] + output += 'require(' + loaderUtils.stringifyRequest(this, + '-!' + loader(include.type, include.lang) + include.src + ) + ')\n' } // add require for styles diff --git a/lib/parser.js b/lib/parser.js index d53e196fd..7766f732e 100644 --- a/lib/parser.js +++ b/lib/parser.js @@ -12,10 +12,18 @@ module.exports = function (content) { } var fragment = parser.parseFragment(content) + fragment.childNodes.forEach(function (node) { + var type = node.tagName + var lang = getAttribute(node, 'lang') || '' var src = getAttribute(node, 'src') + if (src) { - output.includes.push(src) + output.includes.push({ + src: src, + type: type, + lang: lang + }) return } @@ -23,8 +31,6 @@ module.exports = function (content) { return } - var lang = getAttribute(node, 'lang') || '' - var type = node.tagName if (!output[type]) { return } diff --git a/test/fixtures/import.css b/test/fixtures/import.css new file mode 100644 index 000000000..5ce768ca5 --- /dev/null +++ b/test/fixtures/import.css @@ -0,0 +1 @@ +h1 { color: red; } diff --git a/test/fixtures/import.js b/test/fixtures/import.js new file mode 100644 index 000000000..a27caa453 --- /dev/null +++ b/test/fixtures/import.js @@ -0,0 +1 @@ +window.testModule = require('./import.vue') diff --git a/test/fixtures/import.vue b/test/fixtures/import.vue new file mode 100644 index 000000000..3425a8854 --- /dev/null +++ b/test/fixtures/import.vue @@ -0,0 +1 @@ + ' var outputDir = path.resolve(__dirname, './output') var loaderPath = path.resolve(__dirname, '../') - var testHTML = ' ' var globalConfig = { output: { path: outputDir, @@ -88,6 +88,16 @@ describe('vue-loader', function () { }) }) + it('import', function (done) { + test({ + entry: './test/fixtures/import.js' + }, function (window) { + var style = window.document.querySelector('style').textContent + expect(style).to.contain('h1 { color: red; }') + done() + }) + }) + it('source-map', function (done) { var config = assign({}, globalConfig, { entry: './test/fixtures/basic.js', From 8a39647ec6f9aa2a32ceb169772a4b4cfc47422d Mon Sep 17 00:00:00 2001 From: Evan You Date: Thu, 16 Jul 2015 00:04:30 -0700 Subject: [PATCH 030/877] bump 2.0.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1e33f06a9..969b8c5c4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vue-loader", - "version": "2.0.1", + "version": "2.0.2", "description": "Vue.js component loader for Webpack", "main": "index.js", "repository": { From 14d63bb57e1cf38da7cae2e5a99f12188af138ea Mon Sep 17 00:00:00 2001 From: Evan You Date: Thu, 16 Jul 2015 00:11:40 -0700 Subject: [PATCH 031/877] fix dependencies --- package.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/package.json b/package.json index 969b8c5c4..3e757ad2e 100644 --- a/package.json +++ b/package.json @@ -35,9 +35,7 @@ "babel-core": "^5.5.8", "babel-loader": "^5.1.4", "chai": "^3.0.0", - "css-loader": "^0.14.4", "eslint": "^1.0.0-rc-1", - "html-loader": "^0.3.0", "jade": "^1.11.0", "jsdom": "^5.4.3", "mkdirp": "^0.5.1", @@ -46,7 +44,6 @@ "object-assign": "^3.0.0", "rimraf": "^2.4.0", "source-map": "^0.4.2", - "style-loader": "^0.12.3", "stylus-loader": "^1.2.0", "template-html-loader": "0.0.3", "webpack": "^1.9.11" From 1eaf7a63cdc85bfb6ca3696a0a48e64a0e80a7d5 Mon Sep 17 00:00:00 2001 From: Evan You Date: Thu, 16 Jul 2015 00:43:22 -0700 Subject: [PATCH 032/877] still include default loaders as dev deps --- package.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/package.json b/package.json index 3e757ad2e..4418c6ed4 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,9 @@ "babel-core": "^5.5.8", "babel-loader": "^5.1.4", "chai": "^3.0.0", + "css-loader": "^0.15.4", "eslint": "^1.0.0-rc-1", + "html-loader": "^0.3.0", "jade": "^1.11.0", "jsdom": "^5.4.3", "mkdirp": "^0.5.1", @@ -44,6 +46,7 @@ "object-assign": "^3.0.0", "rimraf": "^2.4.0", "source-map": "^0.4.2", + "style-loader": "^0.12.3", "stylus-loader": "^1.2.0", "template-html-loader": "0.0.3", "webpack": "^1.9.11" From 72f199135485fa149badc394b12696987bfc6a4e Mon Sep 17 00:00:00 2001 From: Evan You Date: Thu, 16 Jul 2015 00:45:12 -0700 Subject: [PATCH 033/877] bump 2.0.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4418c6ed4..e5ee41521 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vue-loader", - "version": "2.0.2", + "version": "2.0.3", "description": "Vue.js component loader for Webpack", "main": "index.js", "repository": { From 39f322d50bb9918b2dedf2d6ab98b9887774f7ca Mon Sep 17 00:00:00 2001 From: Evan You Date: Thu, 16 Jul 2015 00:49:47 -0700 Subject: [PATCH 034/877] readme + bump 2.1.0 (because src import handling is a breaking change) --- README.md | 7 ++++--- package.json | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a8230ddf5..b5f69249a 100644 --- a/README.md +++ b/README.md @@ -47,13 +47,14 @@ module.exports = ``` -And you can import using the `src` attribute (note that there's no need for a `lang` attribute here, as Webpack will -be used to determine which loader applies): +And you can import using the `src` attribute: ``` html - + ``` +**NOTE**: Starting from version 2.1.0, `src` imports follow similar rules to `require()` calls, which means for relative paths you need to start with `./`, and you can import resources from node modules: ` diff --git a/test/test.js b/test/test.js index 337ab8aaf..67f490e17 100644 --- a/test/test.js +++ b/test/test.js @@ -90,7 +90,17 @@ describe('vue-loader', function () { it('import', function (done) { test({ - entry: './test/fixtures/import.js' + entry: './test/fixtures/import.vue' + }, function (window) { + var style = window.document.querySelector('style').textContent + expect(style).to.contain('h1 { color: red; }') + done() + }) + }) + + it('local-css', function (done) { + test({ + entry: './test/fixtures/local-css.vue' }, function (window) { var style = window.document.querySelector('style').textContent expect(style).to.contain('h1 { color: red; }') From 9c93331cf7a843aa2b1b0948dc15ba39012d454d Mon Sep 17 00:00:00 2001 From: Evan You Date: Thu, 16 Jul 2015 05:59:02 -0700 Subject: [PATCH 037/877] local css --- lib/css-rewriter.js | 5 --- lib/loader.js | 61 ++++++++++++++++++++++--------------- lib/style-rewriter.js | 33 ++++++++++++++++++++ lib/template-rewriter.js | 30 ++++++++++++++++++ package.json | 4 ++- test/fixtures/local-css.js | 1 + test/fixtures/local-css.vue | 15 ++++++++- test/test.js | 11 +++++-- 8 files changed, 126 insertions(+), 34 deletions(-) delete mode 100644 lib/css-rewriter.js create mode 100644 lib/style-rewriter.js create mode 100644 lib/template-rewriter.js create mode 100644 test/fixtures/local-css.js diff --git a/lib/css-rewriter.js b/lib/css-rewriter.js deleted file mode 100644 index 9648e4d4f..000000000 --- a/lib/css-rewriter.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = function (css) { - this.cacheable() - console.log(css) - return css -} diff --git a/lib/loader.js b/lib/loader.js index 7873879fc..f3d823a6c 100644 --- a/lib/loader.js +++ b/lib/loader.js @@ -1,7 +1,17 @@ var loaderUtils = require('loader-utils') var selectorPath = require.resolve('./selector') var parserPath = require.resolve('./parser') -var cssRewriterPath = require.resolve('./css-rewriter') + +var defaultLang = { + template: 'html', + style: 'css', + script: 'js' +} + +var rewriters = { + template: require.resolve('./template-rewriter') + '!', + style: require.resolve('./style-rewriter') + '!' +} module.exports = function (content) { var self = this @@ -17,25 +27,13 @@ module.exports = function (content) { loaders.css = loaders.css || 'style!css' loaders.js = loaders.js || '' - var loaderPrefix = { - template: 'html!template-html-loader?raw&engine=', - style: 'style!css!', - script: '' - } - - var defaultLang = { - template: 'html', - style: 'css', - script: 'js' - } - - function getRequire (type, part, index) { + function getRequire (type, part, index, local) { return 'require(' + loaderUtils.stringifyRequest(self, // disable system loaders (except post loaders) '-!' + // get loader string for pre-processors - getLoaderString(type, part) + + getLoaderString(type, part, local) + // select the corresponding part from the vue file getSelectorString(type, index || 0) + // the url to the actual vuefile @@ -54,14 +52,25 @@ module.exports = function (content) { ')\n' } - function getLoaderString (type, part) { + function getLoaderString (type, part, local) { var lang = part.lang || defaultLang[type] - var localCssString = type === 'style' && part.local - ? cssRewriterPath + '!' - : '' - return loaders[lang] !== undefined - ? loaders[lang] + '!' + localCssString - : loaderPrefix[type] + localCssString + lang + '!' + var rewriter = local ? rewriters[type] || '' : '' + var loader = loaders[lang] + if (loader !== undefined) { + // lang with default or pre-configured loader + if (loader) loader += '!' + return loader + rewriter + } else { + // unknown lang, assume a loader for it is used + switch (type) { + case 'template': + return 'html!' + rewriter + 'template-html-loader?raw&engine=' + lang + '!' + case 'style': + return 'style!css!' + rewriter + lang + '!' + case 'script': + return lang + '!' + } + } } function getSelectorString (type, index) { @@ -78,6 +87,7 @@ module.exports = function (content) { // parser.js on the raw vue file and get the parsing result // which is an object that contains info about the vue file. var parts = self.exec(source, url) + var hasLocalStyles = false // add requires for src imports parts.includes.forEach(function (include) { @@ -86,19 +96,20 @@ module.exports = function (content) { // add requires for styles parts.style.forEach(function (style, i) { - output += getRequire('style', style, i) + if (style.local) hasLocalStyles = true + output += getRequire('style', style, i, hasLocalStyles) }) // only one script tag allowed if (parts.script.length) { output += 'module.exports = ' + - getRequire('script', parts.script[0]) + getRequire('script', parts.script[0], 0, hasLocalStyles) } // only one template tag allowed if (parts.template.length) { output += 'module.exports.template = ' + - getRequire('template', parts.template[0]) + getRequire('template', parts.template[0], 0, hasLocalStyles) } // done diff --git a/lib/style-rewriter.js b/lib/style-rewriter.js new file mode 100644 index 000000000..3a13625b8 --- /dev/null +++ b/lib/style-rewriter.js @@ -0,0 +1,33 @@ +var postcss = require('postcss') +var nested = require('postcss-nested') +var hash = require('hash-sum') + +var currentClass +var rootRE = /:root\b/g +var processRoot = postcss.plugin('process-root', function () { + return function (root) { + root.each(function (node) { + node.each(function (node) { + if (rootRE.test(node.selector)) { + // replace :root selector + node.selector = node.selector.replace(rootRE, currentClass) + // move the node to the outer scope to avoid nesting + node.moveBefore(root.nodes[0]) + } + }) + }) + } +}) + +module.exports = function (css) { + this.cacheable() + var cb = this.async() + var cls = currentClass = '.v-' + hash(this.resourcePath) + css = cls + '{' + css + '}' + postcss([processRoot, nested]) + .process(css) + .then(function (result) { + cb(null, result) + }) + .catch(cb) +} diff --git a/lib/template-rewriter.js b/lib/template-rewriter.js new file mode 100644 index 000000000..7ec2bf725 --- /dev/null +++ b/lib/template-rewriter.js @@ -0,0 +1,30 @@ +var parse5 = require('parse5') +var parser = new parse5.Parser() +var serializer = new parse5.Serializer() +var hash = require('hash-sum') + +module.exports = function (html) { + this.cacheable() + var cls = 'v-' + hash(this.resourcePath) + var tree = parser.parseFragment(html) + tree.childNodes.forEach(function (node) { + if (node.attrs) { + var hasClass = false + for (var i = 0, l = node.attrs.length; i < l; i++) { + var attr = node.attrs[i] + if (attr.name === 'class') { + attr.value += ' ' + cls + hasClass = true + break + } + } + if (!hasClass) { + node.attrs.push({ + name: 'class', + value: cls + }) + } + } + }) + return serializer.serialize(tree) +} diff --git a/package.json b/package.json index b43ca86d0..b2dc28a51 100644 --- a/package.json +++ b/package.json @@ -23,8 +23,10 @@ "test": "eslint lib && mocha test/test.js --slow 2000" }, "dependencies": { + "hash-sum": "^1.0.2", "loader-utils": "^0.2.10", - "parse5": "^1.5.0" + "parse5": "^1.5.0", + "postcss": "^4.1.16" }, "peerDependencies": { "css-loader": "^0.15.4", diff --git a/test/fixtures/local-css.js b/test/fixtures/local-css.js new file mode 100644 index 000000000..519ba68d9 --- /dev/null +++ b/test/fixtures/local-css.js @@ -0,0 +1 @@ +window.testModule = require('./local-css.vue') diff --git a/test/fixtures/local-css.vue b/test/fixtures/local-css.vue index 85d8826c1..529b5f05f 100644 --- a/test/fixtures/local-css.vue +++ b/test/fixtures/local-css.vue @@ -1,3 +1,16 @@ + + diff --git a/test/test.js b/test/test.js index 67f490e17..544ddcaa7 100644 --- a/test/test.js +++ b/test/test.js @@ -100,10 +100,17 @@ describe('vue-loader', function () { it('local-css', function (done) { test({ - entry: './test/fixtures/local-css.vue' + entry: './test/fixtures/local-css.js' }, function (window) { + var module = window.testModule + expect(module.template).to.contain( + '

hi

\n' + + '

hi

' + ) var style = window.document.querySelector('style').textContent - expect(style).to.contain('h1 { color: red; }') + expect(style).to.match(/div\.v-5f0cf35a\.test {\s+color: blue;\n}/) + expect(style).to.match(/\.v-5f0cf35a {\s+color: red;\n}/) + expect(style).to.match(/\.v-5f0cf35a h1 {\s+color: green;\n}/) done() }) }) From 1cc8f5b286bf0a67dca413e844b3c8ad0274ebf8 Mon Sep 17 00:00:00 2001 From: Evan You Date: Thu, 16 Jul 2015 06:00:01 -0700 Subject: [PATCH 038/877] include postcss-nested as dep --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index b2dc28a51..88e7abbb7 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,8 @@ "hash-sum": "^1.0.2", "loader-utils": "^0.2.10", "parse5": "^1.5.0", - "postcss": "^4.1.16" + "postcss": "^4.1.16", + "postcss-nested": "^0.3.2" }, "peerDependencies": { "css-loader": "^0.15.4", From fdb9e43b434f9340ab200d8de7b51f8c9487f08a Mon Sep 17 00:00:00 2001 From: Evan You Date: Thu, 16 Jul 2015 06:18:08 -0700 Subject: [PATCH 039/877] make local styles work with import --- lib/loader.js | 15 ++++++++------- lib/parser.js | 20 +++++++++++++------- test/fixtures/import.local.css | 1 + test/fixtures/import.vue | 3 ++- test/test.js | 26 ++++++++++++++------------ 5 files changed, 38 insertions(+), 27 deletions(-) create mode 100644 test/fixtures/import.local.css diff --git a/lib/loader.js b/lib/loader.js index f3d823a6c..5a015ee87 100644 --- a/lib/loader.js +++ b/lib/loader.js @@ -42,12 +42,12 @@ module.exports = function (content) { ')\n' } - function getRequireForImport (include) { + function getRequireForImport (impt) { return 'require(' + loaderUtils.stringifyRequest(self, '-!' + - getLoaderString(include.type, include) + - include.src + getLoaderString('style', impt, impt.local) + + impt.src ) + ')\n' } @@ -90,20 +90,21 @@ module.exports = function (content) { var hasLocalStyles = false // add requires for src imports - parts.includes.forEach(function (include) { - output += getRequireForImport(include) + parts.styleImports.forEach(function (impt) { + if (impt.local) hasLocalStyles = true + output += getRequireForImport(impt) }) // add requires for styles parts.style.forEach(function (style, i) { if (style.local) hasLocalStyles = true - output += getRequire('style', style, i, hasLocalStyles) + output += getRequire('style', style, i, style.local) }) // only one script tag allowed if (parts.script.length) { output += 'module.exports = ' + - getRequire('script', parts.script[0], 0, hasLocalStyles) + getRequire('script', parts.script[0], 0) } // only one template tag allowed diff --git a/lib/parser.js b/lib/parser.js index 79411b25b..c7a764deb 100644 --- a/lib/parser.js +++ b/lib/parser.js @@ -8,7 +8,7 @@ module.exports = function (content) { template: [], style: [], script: [], - includes: [] + styleImports: [] } var fragment = parser.parseFragment(content) @@ -17,13 +17,18 @@ module.exports = function (content) { var type = node.tagName var lang = getAttribute(node, 'lang') var src = getAttribute(node, 'src') - var local = getAttribute(node, 'local') + var local = getAttribute(node, 'local') != null if (src) { - output.includes.push({ + if (type !== 'style') { + return cb(new Error( + '[vue-loader] src import is only supported for + diff --git a/test/test.js b/test/test.js index 544ddcaa7..a1e68185a 100644 --- a/test/test.js +++ b/test/test.js @@ -88,17 +88,7 @@ describe('vue-loader', function () { }) }) - it('import', function (done) { - test({ - entry: './test/fixtures/import.vue' - }, function (window) { - var style = window.document.querySelector('style').textContent - expect(style).to.contain('h1 { color: red; }') - done() - }) - }) - - it('local-css', function (done) { + it('local style', function (done) { test({ entry: './test/fixtures/local-css.js' }, function (window) { @@ -115,7 +105,19 @@ describe('vue-loader', function () { }) }) - it('source-map', function (done) { + it('style import', function (done) { + test({ + entry: './test/fixtures/import.vue' + }, function (window) { + var styles = window.document.querySelectorAll('style') + expect(styles[0].textContent).to.match(/h1 { color: red; }/) + // import with local + expect(styles[1].textContent).to.match(/\.v-5751b9b6 h1 {\s+color: green;\n}/) + done() + }) + }) + + it('source map', function (done) { var config = assign({}, globalConfig, { entry: './test/fixtures/basic.js', devtool: 'source-map' From ee69e2597c9e8325aabaa8cd16ecb2b3402fe94d Mon Sep 17 00:00:00 2001 From: Evan You Date: Thu, 16 Jul 2015 06:44:51 -0700 Subject: [PATCH 040/877] generate hash in tests --- test/test.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/test/test.js b/test/test.js index a1e68185a..0403243fd 100644 --- a/test/test.js +++ b/test/test.js @@ -5,6 +5,7 @@ var jsdom = require('jsdom') var expect = require('chai').expect var assign = require('object-assign') var rimraf = require('rimraf') +var hash = require('hash-sum') var SourceMapConsumer = require('source-map').SourceMapConsumer describe('vue-loader', function () { @@ -98,9 +99,10 @@ describe('vue-loader', function () { '

hi

' ) var style = window.document.querySelector('style').textContent - expect(style).to.match(/div\.v-5f0cf35a\.test {\s+color: blue;\n}/) - expect(style).to.match(/\.v-5f0cf35a {\s+color: red;\n}/) - expect(style).to.match(/\.v-5f0cf35a h1 {\s+color: green;\n}/) + var cls = '.v-' + hash(require.resolve('./fixtures/local-css.vue')) + expect(style).to.contain('div' + cls + '.test {\n color: blue;\n}') + expect(style).to.contain(cls + ' {\n color: red;\n}') + expect(style).to.contain(cls + ' h1 {\n color: green;\n}') done() }) }) @@ -110,9 +112,10 @@ describe('vue-loader', function () { entry: './test/fixtures/import.vue' }, function (window) { var styles = window.document.querySelectorAll('style') - expect(styles[0].textContent).to.match(/h1 { color: red; }/) + expect(styles[0].textContent).to.contain('h1 { color: red; }') // import with local - expect(styles[1].textContent).to.match(/\.v-5751b9b6 h1 {\s+color: green;\n}/) + var cls = '.v-' + hash(require.resolve('./fixtures/import.local.css')) + expect(styles[1].textContent).to.contain(cls + ' h1 {\n color: green;\n}') done() }) }) From 95da83773fcab0c92e0d59f22f766c46d1aba027 Mon Sep 17 00:00:00 2001 From: Evan You Date: Thu, 16 Jul 2015 06:46:30 -0700 Subject: [PATCH 041/877] fix it for real --- test/test.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/test.js b/test/test.js index 0403243fd..2352b5fab 100644 --- a/test/test.js +++ b/test/test.js @@ -94,12 +94,12 @@ describe('vue-loader', function () { entry: './test/fixtures/local-css.js' }, function (window) { var module = window.testModule + var cls = '.v-' + hash(require.resolve('./fixtures/local-css.vue')) expect(module.template).to.contain( - '

hi

\n' + - '

hi

' + '

hi

\n' + + '

hi

' ) var style = window.document.querySelector('style').textContent - var cls = '.v-' + hash(require.resolve('./fixtures/local-css.vue')) expect(style).to.contain('div' + cls + '.test {\n color: blue;\n}') expect(style).to.contain(cls + ' {\n color: red;\n}') expect(style).to.contain(cls + ' h1 {\n color: green;\n}') From 8bbc858742e28e72862d9e2dfb9cb69c0987a7f5 Mon Sep 17 00:00:00 2001 From: Evan You Date: Thu, 16 Jul 2015 07:12:49 -0700 Subject: [PATCH 042/877] lift rules that start with body --- lib/style-rewriter.js | 15 +++++++++++---- test/fixtures/local-css.vue | 3 +++ test/test.js | 12 +++++++++--- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/lib/style-rewriter.js b/lib/style-rewriter.js index 3a13625b8..14f1eaf6f 100644 --- a/lib/style-rewriter.js +++ b/lib/style-rewriter.js @@ -4,15 +4,22 @@ var hash = require('hash-sum') var currentClass var rootRE = /:root\b/g +var liftRE = /^(html|head|body)\b/ var processRoot = postcss.plugin('process-root', function () { return function (root) { + var lifted = 0 + function lift (node) { + node.moveBefore(root.nodes[lifted++]) + } root.each(function (node) { node.each(function (node) { - if (rootRE.test(node.selector)) { + var sel = node.selector + if (liftRE.test(sel)) { + lift(node) + } else if (rootRE.test(sel)) { // replace :root selector - node.selector = node.selector.replace(rootRE, currentClass) - // move the node to the outer scope to avoid nesting - node.moveBefore(root.nodes[0]) + node.selector = sel.replace(rootRE, currentClass) + lift(node) } }) }) diff --git a/test/fixtures/local-css.vue b/test/fixtures/local-css.vue index 529b5f05f..7a2f2328b 100644 --- a/test/fixtures/local-css.vue +++ b/test/fixtures/local-css.vue @@ -1,4 +1,7 @@ - + diff --git a/test/fixtures/local-css.js b/test/fixtures/local-css.js deleted file mode 100644 index 519ba68d9..000000000 --- a/test/fixtures/local-css.js +++ /dev/null @@ -1 +0,0 @@ -window.testModule = require('./local-css.vue') diff --git a/test/fixtures/scoped-css.js b/test/fixtures/scoped-css.js new file mode 100644 index 000000000..607e0d58f --- /dev/null +++ b/test/fixtures/scoped-css.js @@ -0,0 +1 @@ +window.testModule = require('./scoped-css.vue') diff --git a/test/fixtures/local-css.vue b/test/fixtures/scoped-css.vue similarity index 92% rename from test/fixtures/local-css.vue rename to test/fixtures/scoped-css.vue index 7a2f2328b..0a335d6e6 100644 --- a/test/fixtures/local-css.vue +++ b/test/fixtures/scoped-css.vue @@ -1,4 +1,4 @@ - - - - - -``` - -And you can import using the `src` attribute: - -``` html - -``` - -**NOTE**: Starting from version 2.1.0, `src` imports follow similar rules to `require()` calls, which means for relative paths you need to start with `./`, and you can import resources from node modules: ` +``` + +The `lang` attribute will be used to automatically locate the loader to use, and you can pass Webpack loader queries in it as well: + +``` html + +``` + +#### A Note on Dependencies + +By default, `vue-loader` requires `html-loader`, `css-loader` and `style-loader` as peer dependencies. In order to use pre-processors, you also need to install the corresponding Webpack loader for that language. + +Also, for template pre-processors, you should install `template-html-loader` plus the raw templating engine. For example to use `jade`, you will need to install both `template-html-loader` and `jade` instead of `jade-loader`. + +## Style Imports + +If you want, you can separate your styles into a separate file and import it using the `src` attribute: -**Note** For template pre-processors, use `template-html-loader` plus the raw templating engine. For example to use `jade`, you will need to install both `template-html-loader` and `jade` instead of `jade-loader`. +``` html + +``` + +Beware that `src` imports follow similar rules to `require()` calls, which means for relative paths you need to start with `./`, and you can import resources from node modules: ` ``` -#### A Note on Dependencies +#### A Note on Loader Dependencies -By default, `vue-loader` requires `html-loader`, `css-loader` and `style-loader` as peer dependencies. In order to use pre-processors, you also need to install the corresponding Webpack loader for that language. +By default, `vue-loader` requires `vue-html-loader`, `css-loader` and `style-loader` as peer dependencies. In order to use pre-processors, you also need to install the corresponding Webpack loader for that language. -Also, for template pre-processors, you should install `template-html-loader` plus the raw templating engine. For example to use `jade`, you will need to install both `template-html-loader` and `jade` instead of `jade-loader`. +#### Template Pre-Processors + +For template pre-processors, you should install `template-html-loader` plus the raw templating engine. For example to use `jade`, you will need to install both `template-html-loader` and `jade` instead of `jade-loader`. ## Style Imports @@ -103,7 +105,7 @@ Beware that `src` imports follow similar rules to `require()` calls, which means ## Asset URL Handling -By default, `vue-loader` automatically processes your style and template files with `css-loader` and `html-loader` - this means that all asset URLs such as ``, `background: url(...)` and CSS `@import` are **resolved as module dependencies**. +By default, `vue-loader` automatically processes your style and template files with `css-loader` and `vue-html-loader` - this means that all asset URLs such as ``, `background: url(...)` and CSS `@import` are **resolved as module dependencies**. For example, `url(image.png)` will be translated into `require('./image.png')`. Because `.png` is not JavaScript, you will need to configure Webpack to use [file-loader](https://github.com/webpack/file-loader) or [url-loader](https://github.com/webpack/url-loader) to handle them. This may feel cumbersome, but it gives you some very powerful benefits in managing your static assets this way: @@ -111,7 +113,7 @@ For example, `url(image.png)` will be translated into `require('./image.png')`. 2. `url-loader` allows you to conditionally load a file as a inline Data URL if they are smaller than a given threshold. -For more details, see the respective documentations for [html-loader](https://github.com/webpack/html-loader) and [css-loader](https://github.com/webpack/css-loader). +For more details, see the respective documentations for [vue-html-loader](https://github.com/vuejs/vue-html-loader) and [css-loader](https://github.com/webpack/css-loader). ## Advanced Loader configuration From 5282f7e86e0e40c080bfbb09be6b7e3278faa398 Mon Sep 17 00:00:00 2001 From: Evan You Date: Thu, 1 Oct 2015 15:12:20 -0400 Subject: [PATCH 058/877] bump 3.0.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 62a378c75..eaf019091 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vue-loader", - "version": "3.0.3", + "version": "3.0.4", "description": "Vue.js component loader for Webpack", "main": "index.js", "repository": { From a977487148c7bcbbbf353347679c48c4832cebdd Mon Sep 17 00:00:00 2001 From: Evan You Date: Tue, 13 Oct 2015 17:34:16 -0400 Subject: [PATCH 059/877] new scoped css implementation --- lib/id-generator.js | 12 ++++++++++ lib/style-rewriter.js | 44 ++++++++++-------------------------- lib/template-rewriter.js | 34 +++++++++++++--------------- package.json | 2 -- test/fixtures/scoped-css.vue | 8 +------ test/test.js | 20 ++++------------ 6 files changed, 46 insertions(+), 74 deletions(-) create mode 100644 lib/id-generator.js diff --git a/lib/id-generator.js b/lib/id-generator.js new file mode 100644 index 000000000..e1c242d95 --- /dev/null +++ b/lib/id-generator.js @@ -0,0 +1,12 @@ +// keep track of unique ids for component modules. + +var ids = Object.create(null) +var count = 0 + +exports.get = function (path) { + var id = ids[path] + if (!id) { + id = ids[path] = ++count + } + return '_v-' + id +} diff --git a/lib/style-rewriter.js b/lib/style-rewriter.js index 8837e65ea..4088e9a4e 100644 --- a/lib/style-rewriter.js +++ b/lib/style-rewriter.js @@ -1,37 +1,18 @@ var postcss = require('postcss') -var nested = require('postcss-nested') var selectorParser = require('postcss-selector-parser') -var hash = require('hash-sum') +var idGen = require('./id-generator') -var liftRE = /^(html|head|body)\b/ -var scopeRE = /:scope\b/ -var processRoot = postcss.plugin('process-root', function () { +var currentId +var addId = postcss.plugin('add-id', function () { return function (root) { - var lifted = 0 - function lift (node) { - node.moveBefore(root.nodes[lifted++]) - } root.each(function (node) { - node.each(function (node) { - var kept = [] - selectorParser(function (selectors) { - selectors.each(function (selector) { - var sel = selector.toString() - if (liftRE.test(sel)) { - lift(node.clone({ - selector: sel - })) - } else { - kept.push(sel) - } - }) - }).process(node.selector) - if (!kept.length) { - node.removeSelf() - } else { - node.selector = kept.join(',').replace(scopeRE, '&') - } - }) + node.selector = selectorParser(function (selectors) { + selectors.each(function (selector) { + selector.append(selectorParser.attribute({ + attribute: currentId + })) + }) + }).process(node.selector).result }) } }) @@ -39,9 +20,8 @@ var processRoot = postcss.plugin('process-root', function () { module.exports = function (css) { this.cacheable() var cb = this.async() - var cls = '.v-' + hash(this.resourcePath) - css = cls + '{' + css + '}' - postcss([processRoot, nested]) + currentId = idGen.get(this.resourcePath) + postcss([addId]) .process(css) .then(function (result) { cb(null, result) diff --git a/lib/template-rewriter.js b/lib/template-rewriter.js index 7ec2bf725..f91ade954 100644 --- a/lib/template-rewriter.js +++ b/lib/template-rewriter.js @@ -1,30 +1,28 @@ var parse5 = require('parse5') var parser = new parse5.Parser() var serializer = new parse5.Serializer() -var hash = require('hash-sum') +var idGen = require('./id-generator') module.exports = function (html) { this.cacheable() - var cls = 'v-' + hash(this.resourcePath) + var id = idGen.get(this.resourcePath) var tree = parser.parseFragment(html) - tree.childNodes.forEach(function (node) { + walk(tree, function (node) { if (node.attrs) { - var hasClass = false - for (var i = 0, l = node.attrs.length; i < l; i++) { - var attr = node.attrs[i] - if (attr.name === 'class') { - attr.value += ' ' + cls - hasClass = true - break - } - } - if (!hasClass) { - node.attrs.push({ - name: 'class', - value: cls - }) - } + node.attrs.push({ + name: id, + value: '' + }) } }) return serializer.serialize(tree) } + +function walk (tree, fn) { + if (tree.childNodes) { + tree.childNodes.forEach(function (node) { + fn(node) + walk(node, fn) + }) + } +} diff --git a/package.json b/package.json index eaf019091..c6f915162 100644 --- a/package.json +++ b/package.json @@ -23,11 +23,9 @@ "test": "eslint lib && mocha test/test.js --slow 5000" }, "dependencies": { - "hash-sum": "^1.0.2", "loader-utils": "^0.2.10", "parse5": "^1.5.0", "postcss": "^4.1.16", - "postcss-nested": "^0.3.2", "postcss-selector-parser": "^1.1.2" }, "peerDependencies": { diff --git a/test/fixtures/scoped-css.vue b/test/fixtures/scoped-css.vue index ee925ca5d..515ee88f1 100644 --- a/test/fixtures/scoped-css.vue +++ b/test/fixtures/scoped-css.vue @@ -1,13 +1,7 @@ ``` -The `lang` attribute will be used to automatically locate the loader to use, and you can pass Webpack loader queries in it as well: +You can also include Webpack loader queries in the `lang` attribute: ``` html ``` -#### A Note on Loader Dependencies +## Template Pre-Processors -By default, `vue-loader` requires `vue-html-loader`, `css-loader` and `style-loader` as peer dependencies. In order to use pre-processors, you also need to install the corresponding Webpack loader for that language. +For template pre-processors, you should install `template-html-loader` plus the raw templating engine. For example to use `jade`, you will need to install both `template-html-loader` and `jade` instead of `jade-loader`. -#### Template Pre-Processors +## Scoped Styles -For template pre-processors, you should install `template-html-loader` plus the raw templating engine. For example to use `jade`, you will need to install both `template-html-loader` and `jade` instead of `jade-loader`. +When a ` + +``` + +Into the following: + +``` html + + +``` + +#### Notes + +1. You can include both scoped and non-scoped styles in the same component. + +2. A child component's root node will be affected by both the parent's scoped CSS and the child's scoped CSS. + +3. Partials are not affected by scoped styles. + +## Hot Reload + +When using `webpack-dev-server` in hot mode, `vue-loader` enables hot component reloading for Vue.js 1.0.0+. An example config: + +``` js +module.exports = { + entry: ['webpack/hot/dev-server', './src/main.js'], + output: { + publicPath: '/static/', + filename: 'build.js' + }, + module: { + loaders: [ + { test: /\.vue$/, loader: 'vue' }, + ] + }, + devtool: '#source-map' +} +``` + +In `index.html`, include the bundle: + +``` html + +``` + +Then, run the dev server with: + +``` bash +webpack-dev-server --hot --config build/webpack.dev.config.js +``` + +Finally, visit `http://localhost:8080/webpack-dev-server/` to see the app with hot reloading. + +For a complete example with hot reloading in action, see [vue-hackernews](https://github.com/vuejs/vue-hackernews). ## Style Imports @@ -180,4 +251,6 @@ module.exports = { ## Example Project -See [vue-loader-example](https://github.com/vuejs/vue-loader-example). +For a simple setup example, see [vue-loader-example](https://github.com/vuejs/vue-loader-example). + +For an actual project with dev setup with hot reloading and production setup with minification, see [vue-hackernews](https://github.com/vuejs/vue-hackernews). From d67b6ea26c8537d85519eb8ade42478505cce3bc Mon Sep 17 00:00:00 2001 From: Evan You Date: Wed, 14 Oct 2015 22:29:10 -0400 Subject: [PATCH 066/877] adjust order in readme --- README.md | 52 ++++++++++++++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 6f0822f3d..7078d4c7d 100644 --- a/README.md +++ b/README.md @@ -33,10 +33,10 @@ It allows you to write your components in this format: - [Basic Usage](#basic-usage) - [Pre-Processors](#pre-processors) - [Template Pre-Processors](#template-pre-processors) -- [Scoped Styles](#scoped-styles) -- [Hot Reload](#hot-reload) - [Style Imports](#style-imports) - [Asset URL Handling](#asset-url-handling) +- [Scoped Styles](#scoped-styles) +- [Hot Reload](#hot-reload) - [Advanced Loader Configuration](#advanced-loader-configuration) - [ES6 with Babel Example](#example-using-es6-with-babel) - [Extract CSS Example](#example-extracting-css-into-a-single-file) @@ -92,8 +92,32 @@ You can also include Webpack loader queries in the `lang` attribute: For template pre-processors, you should install `template-html-loader` plus the raw templating engine. For example to use `jade`, you will need to install both `template-html-loader` and `jade` instead of `jade-loader`. +## Style Imports + +If you want, you can separate your styles into a separate file and import it using the `src` attribute: + +``` html + +``` + +Beware that `src` imports follow similar rules to `require()` calls, which means for relative paths you need to start with `./`, and you can import resources from node modules: ` -``` - -Beware that `src` imports follow similar rules to `require()` calls, which means for relative paths you need to start with `./`, and you can import resources from node modules: ` - + + ``` ## Table of Contents - [Basic Usage](#basic-usage) -- [Pre-Processors](#pre-processors) +- [ES2015 by Default](#es2015-by-default) +- [CSS Pre-Processors](#css-pre-processors) - [Template Pre-Processors](#template-pre-processors) - [Style Imports](#style-imports) - [Asset URL Handling](#asset-url-handling) - [Scoped CSS](#scoped-css) - [Hot Reload](#hot-reload) - [Advanced Loader Configuration](#advanced-loader-configuration) - - [ES6 with Babel Example](#example-using-es6-with-babel) - - [Extract CSS Example](#example-extracting-css-into-a-single-file) - [Example Project](https://github.com/vuejs/vue-loader-example) ## Basic Usage @@ -66,11 +65,56 @@ And this is all you need to do in your main entry file: ``` js // main.js var Vue = require('vue') -var appOptions = require('./app.vue') -var app = new Vue(appOptions).$mount('#app') +var App = require('./app.vue') + +new Vue({ + el: 'body', + components: { + app: App + } +}) +``` + +In your HTML: + +``` html + + + + +``` + +## ES2015 by Default + +`vue-loader` automatically applies Babel transforms to the JavaScript inside `*.vue` components. Write ES2015 today! + +The default Babel options used for Vue.js components are: + +``` js +{ + // use babel-runtime library for common helpers + optional: ['runtime'], + // use loose mode for faster builds + loose: 'all', + // disable non-standard stuff (e.g. JSX) + nonStandard: false +} +``` + +If you wish to mofidy this, you can add a `babel` field in your webpack config, which will be merged with the default options. For example: + +``` js +// webpack.config.js +module.exports = { + // other configs... + babel: { + // enable stage 0 transforms + stage: 0 + } +} ``` -## Pre-Processors +## CSS Pre-Processors `vue-loader` allows you to use other Webpack loaders to pre-process the different parts inside your `*.vue` components. Just specify the loader to use with the `lang` attribute (you also need to install the loader, obviously): @@ -204,9 +248,7 @@ For a complete example with hot reloading in action, see [vue-hackernews](https: By default, `vue-loader` will try to use the loader with the same name as the `lang` attribute, but you can configure which loader should be used. -#### Example: Using ES6 with Babel - -To apply Babel transforms to all your JavaScript, use this Webpack config: +#### Example: Use CoffeeScript for all ` + ``` Then, run the dev server with: ``` bash -webpack-dev-server --inline --hot --config webpack.example.config.js +webpack-dev-server --inline --hot ``` -Finally, visit `http://localhost:8080/webpack-dev-server/` to see the app with hot reloading. +Finally, visit `http://localhost:8080/` to see the app with hot reloading. For a complete example with hot reloading in action, see [vue-hackernews](https://github.com/vuejs/vue-hackernews). From 24e42a018ef0d1f2f99a464813e8fedc0cf426e4 Mon Sep 17 00:00:00 2001 From: Evan You Date: Fri, 30 Oct 2015 10:39:29 -0400 Subject: [PATCH 098/877] show badge for master status --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fb6dfac68..7c9a2507c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# vue-loader [![Build Status](https://img.shields.io/circleci/project/vuejs/vue-loader.svg)](https://circleci.com/gh/vuejs/vue-loader) [![npm package](https://img.shields.io/npm/v/vue-loader.svg)](https://www.npmjs.com/package/vue-loader) +# vue-loader [![Build Status](https://circleci.com/gh/vuejs/vue-loader/tree/master.svg?style=shield)](https://circleci.com/gh/vuejs/vue-loader/tree/master) [![npm package](https://img.shields.io/npm/v/vue-loader.svg)](https://www.npmjs.com/package/vue-loader) > Vue.js component loader for [Webpack](http://webpack.github.io), using Webpack loaders for the parts. From e8611e5f5f034b2b83290fd4f736317d5672b8c6 Mon Sep 17 00:00:00 2001 From: Evan You Date: Fri, 30 Oct 2015 10:40:58 -0400 Subject: [PATCH 099/877] use fury.io badge [ci skip] --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7c9a2507c..61e3561bd 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# vue-loader [![Build Status](https://circleci.com/gh/vuejs/vue-loader/tree/master.svg?style=shield)](https://circleci.com/gh/vuejs/vue-loader/tree/master) [![npm package](https://img.shields.io/npm/v/vue-loader.svg)](https://www.npmjs.com/package/vue-loader) +# vue-loader [![Build Status](https://circleci.com/gh/vuejs/vue-loader/tree/master.svg?style=shield)](https://circleci.com/gh/vuejs/vue-loader/tree/master) [![npm package](https://badge.fury.io/js/vue-loader.svg)](https://www.npmjs.com/package/vue-loader) > Vue.js component loader for [Webpack](http://webpack.github.io), using Webpack loaders for the parts. From b14b1e7c8cb3bb77ded7a544d913d8f8d4cde176 Mon Sep 17 00:00:00 2001 From: Evan You Date: Fri, 30 Oct 2015 11:36:57 -0400 Subject: [PATCH 100/877] fix scoped css for rules with pseudo classes --- lib/style-rewriter.js | 6 +++++- test/fixtures/scoped-css.vue | 3 +++ test/test.js | 1 + 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/style-rewriter.js b/lib/style-rewriter.js index 094c84b2d..b20e5d828 100644 --- a/lib/style-rewriter.js +++ b/lib/style-rewriter.js @@ -9,7 +9,11 @@ var addId = postcss.plugin('add-id', function () { root.each(function (node) { node.selector = selectorParser(function (selectors) { selectors.each(function (selector) { - selector.append(selectorParser.attribute({ + var node = null + selector.each(function (n) { + if (n.type !== 'pseudo') node = n + }) + selector.insertAfter(node, selectorParser.attribute({ attribute: currentId })) }) diff --git a/test/fixtures/scoped-css.vue b/test/fixtures/scoped-css.vue index 515ee88f1..580745de0 100644 --- a/test/fixtures/scoped-css.vue +++ b/test/fixtures/scoped-css.vue @@ -2,6 +2,9 @@ .test { color: yellow; } +.test:after { + content: 'bye!'; +} h1 { color: green; } diff --git a/test/test.js b/test/test.js index 9bda3b9ad..3eeddfa93 100644 --- a/test/test.js +++ b/test/test.js @@ -101,6 +101,7 @@ describe('vue-loader', function () { ) var style = window.document.querySelector('style').textContent expect(style).to.contain('.test[' + id + '] {\n color: yellow;\n}') + expect(style).to.contain('.test[' + id + ']:after {\n content: \'bye!\';\n}') expect(style).to.contain('h1[' + id + '] {\n color: green;\n}') done() }) From 4d4aa1812f6f7407b1a4049812abaad6dc0fe938 Mon Sep 17 00:00:00 2001 From: Evan You Date: Fri, 30 Oct 2015 11:37:06 -0400 Subject: [PATCH 101/877] bump --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c59bcb11a..c9a4707f1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vue-loader", - "version": "6.0.0", + "version": "6.0.1", "description": "Vue.js component loader for Webpack", "main": "index.js", "repository": { From a8e29755e7906b67bb31972f0b331b03c035212e Mon Sep 17 00:00:00 2001 From: Evan You Date: Sun, 1 Nov 2015 00:24:00 -0400 Subject: [PATCH 102/877] fix media query with scoped styles --- lib/style-rewriter.js | 14 ++++++++++++-- test/fixtures/media-query.js | 1 + test/fixtures/media-query.vue | 7 +++++++ test/test.js | 11 +++++++++++ 4 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 test/fixtures/media-query.js create mode 100644 test/fixtures/media-query.vue diff --git a/lib/style-rewriter.js b/lib/style-rewriter.js index b20e5d828..fe1a01c10 100644 --- a/lib/style-rewriter.js +++ b/lib/style-rewriter.js @@ -6,7 +6,14 @@ var loaderUtils = require('loader-utils') var currentId var addId = postcss.plugin('add-id', function () { return function (root) { - root.each(function (node) { + root.each(function rewriteSelector (node) { + if (!node.selector) { + // handle media queries + if (node.type === 'atrule' && node.name === 'media') { + node.each(rewriteSelector) + } + return + } node.selector = selectorParser(function (selectors) { selectors.each(function (selector) { var node = null @@ -46,5 +53,8 @@ module.exports = function (css) { .then(function (result) { cb(null, result) }) - .catch(cb) + .catch(function (e) { + console.log(e) + cb(e) + }) } diff --git a/test/fixtures/media-query.js b/test/fixtures/media-query.js new file mode 100644 index 000000000..1a292f332 --- /dev/null +++ b/test/fixtures/media-query.js @@ -0,0 +1 @@ +window.testModule = require('./media-query.vue') diff --git a/test/fixtures/media-query.vue b/test/fixtures/media-query.vue new file mode 100644 index 000000000..9dbe0ee21 --- /dev/null +++ b/test/fixtures/media-query.vue @@ -0,0 +1,7 @@ + diff --git a/test/test.js b/test/test.js index 3eeddfa93..9408613f9 100644 --- a/test/test.js +++ b/test/test.js @@ -159,4 +159,15 @@ describe('vue-loader', function () { }) }) + it('media-query', function (done) { + test({ + entry: './test/fixtures/media-query.js' + }, function (window) { + var style = window.document.querySelector('style').textContent + var id = '_v-' + hash(require.resolve('./fixtures/media-query.vue')) + expect(style).to.contain('@media print {\n .foo[' + id + '] {\n color: #000;\n }\n}') + done() + }) + }) + }) From 812f24ddf3353202af4fdae5a6a8942980c45b62 Mon Sep 17 00:00:00 2001 From: Evan You Date: Sun, 1 Nov 2015 11:17:19 -0500 Subject: [PATCH 103/877] fix compat with extract css plugin (close #51) --- lib/loader.js | 27 ++++++++++++++++++++++++--- package.json | 3 ++- test/fixtures/extract-css.js | 1 + test/fixtures/extract-css.vue | 10 ++++++++++ test/test.js | 22 ++++++++++++++++++++++ 5 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 test/fixtures/extract-css.js create mode 100644 test/fixtures/extract-css.vue diff --git a/lib/loader.js b/lib/loader.js index 315a4689d..c18a6d137 100644 --- a/lib/loader.js +++ b/lib/loader.js @@ -65,10 +65,23 @@ module.exports = function (content) { var lang = part.lang || defaultLang[type] var loader = loaders[lang] var rewriter = getRewriter(type, scoped) + var cssRE = /\b(css!?)\b/ + var htmlRE = /\b((vue-)?html!?)\b/ if (loader !== undefined) { - // lang with default or pre-configured loader - if (loader) loader += '!' - return loader + rewriter + // inject rewriter before css/html loader for + // extractTextPlugin use cases + if (cssRE.test(loader)) { + loader = loader.replace(cssRE, function (m, $1) { + return ensureBang($1) + rewriter + }) + } else if (htmlRE.test(loader)) { + loader = loader.replace(htmlRE, function (m, $1) { + return ensureBang($1) + rewriter + }) + } else { + loader = ensureBang(loader) + rewriter + } + return ensureBang(loader) } else { // unknown lang, assume a loader for it is used switch (type) { @@ -99,6 +112,14 @@ module.exports = function (content) { '&index=' + index + '!' } + function ensureBang (loader) { + if (loader.charAt(loader.length - 1) !== '!') { + return loader + '!' + } else { + return loader + } + } + var url = '!!' + parserPath + '!' + vueUrl this.loadModule(url, function (err, source) { if (err) return cb(err) diff --git a/package.json b/package.json index c9a4707f1..c0e2d1481 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "chai": "^3.0.0", "css-loader": "^0.21.0", "eslint": "^1.6.0", - "vue-html-loader": "^1.0.0", + "extract-text-webpack-plugin": "^0.8.2", "jade": "^1.11.0", "jsdom": "^6.5.1", "mkdirp": "^0.5.1", @@ -56,6 +56,7 @@ "style-loader": "^0.13.0", "stylus-loader": "^1.4.0", "template-html-loader": "^0.0.3", + "vue-html-loader": "^1.0.0", "webpack": "^1.12.2" } } diff --git a/test/fixtures/extract-css.js b/test/fixtures/extract-css.js new file mode 100644 index 000000000..ce602cf28 --- /dev/null +++ b/test/fixtures/extract-css.js @@ -0,0 +1 @@ +require('./extract-css.vue') diff --git a/test/fixtures/extract-css.vue b/test/fixtures/extract-css.vue new file mode 100644 index 000000000..75034b07c --- /dev/null +++ b/test/fixtures/extract-css.vue @@ -0,0 +1,10 @@ + + + diff --git a/test/test.js b/test/test.js index 9408613f9..7de307a1b 100644 --- a/test/test.js +++ b/test/test.js @@ -7,6 +7,7 @@ var assign = require('object-assign') var rimraf = require('rimraf') var hash = require('hash-sum') var SourceMapConsumer = require('source-map').SourceMapConsumer +var ExtractTextPlugin = require("extract-text-webpack-plugin") describe('vue-loader', function () { @@ -170,4 +171,25 @@ describe('vue-loader', function () { }) }) + it('extract CSS', function (done) { + webpack(Object.assign({}, globalConfig, { + entry: './test/fixtures/extract-css.js', + vue: { + loaders: { + css: ExtractTextPlugin.extract('css'), + stylus: ExtractTextPlugin.extract('css!stylus') + } + }, + plugins: [ + new ExtractTextPlugin('test.output.css') + ] + }), function (err) { + expect(err).to.be.null + getFile('test.output.css', function (data) { + expect(data).to.contain('h1 {\n color: #f00;\n}\nh2 {\n color: green;\n}') + done() + }) + }) + }) + }) From 4d45aedbe214e90384d9c7feb731bb15d8edf634 Mon Sep 17 00:00:00 2001 From: Evan You Date: Sun, 1 Nov 2015 11:28:45 -0500 Subject: [PATCH 104/877] bump 6.0.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c0e2d1481..59ea2487a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vue-loader", - "version": "6.0.1", + "version": "6.0.2", "description": "Vue.js component loader for Webpack", "main": "index.js", "repository": { From 1e7dd932edbf562c316875b11499ccd4ca4b6437 Mon Sep 17 00:00:00 2001 From: Evan You Date: Sun, 1 Nov 2015 11:44:26 -0500 Subject: [PATCH 105/877] also repsect external autoprefixer options --- lib/style-rewriter.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/style-rewriter.js b/lib/style-rewriter.js index fe1a01c10..aa9b55413 100644 --- a/lib/style-rewriter.js +++ b/lib/style-rewriter.js @@ -2,6 +2,7 @@ var postcss = require('postcss') var selectorParser = require('postcss-selector-parser') var hash = require('hash-sum') var loaderUtils = require('loader-utils') +var assign = require('object-assign') var currentId var addId = postcss.plugin('add-id', function () { @@ -42,7 +43,12 @@ module.exports = function (css) { processors.push(addId) } if (autoprefixOptions !== false) { - autoprefixOptions = autoprefixOptions || { remove: false } + autoprefixOptions = assign( + {}, + // also respect autoprefixer-loader options + this.options.autoprefixer, + autoprefixOptions + ) var autoprefixer = require('autoprefixer')(autoprefixOptions) processors.push(autoprefixer) } From 9375f5cfefe532d420baca92e9448904d4249cc8 Mon Sep 17 00:00:00 2001 From: Evan You Date: Sun, 1 Nov 2015 15:15:30 -0500 Subject: [PATCH 106/877] fix custom loaders with queries --- lib/loader.js | 11 +++-------- test/test.js | 2 +- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/lib/loader.js b/lib/loader.js index c18a6d137..78bd57456 100644 --- a/lib/loader.js +++ b/lib/loader.js @@ -15,6 +15,7 @@ var defaultLoaders = { js: 'babel?optional[]=runtime&loose=all&nonStandard=false' } +var rewriterInjectRE = /\b((css|(vue-)?html)(\?[^!]+)?!?)\b/ var rewriters = { template: require.resolve('./template-rewriter'), style: require.resolve('./style-rewriter') @@ -65,17 +66,11 @@ module.exports = function (content) { var lang = part.lang || defaultLang[type] var loader = loaders[lang] var rewriter = getRewriter(type, scoped) - var cssRE = /\b(css!?)\b/ - var htmlRE = /\b((vue-)?html!?)\b/ if (loader !== undefined) { // inject rewriter before css/html loader for // extractTextPlugin use cases - if (cssRE.test(loader)) { - loader = loader.replace(cssRE, function (m, $1) { - return ensureBang($1) + rewriter - }) - } else if (htmlRE.test(loader)) { - loader = loader.replace(htmlRE, function (m, $1) { + if (rewriterInjectRE.test(loader)) { + loader = loader.replace(rewriterInjectRE, function (m, $1) { return ensureBang($1) + rewriter }) } else { diff --git a/test/test.js b/test/test.js index 7de307a1b..058d8be4b 100644 --- a/test/test.js +++ b/test/test.js @@ -177,7 +177,7 @@ describe('vue-loader', function () { vue: { loaders: { css: ExtractTextPlugin.extract('css'), - stylus: ExtractTextPlugin.extract('css!stylus') + stylus: ExtractTextPlugin.extract('css?sourceMap!stylus') } }, plugins: [ From 12628f61d776b4bf737538f5dfa6911adeea2a3c Mon Sep 17 00:00:00 2001 From: Evan You Date: Sun, 1 Nov 2015 15:15:55 -0500 Subject: [PATCH 107/877] bump 6.0.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 59ea2487a..58c0f0b67 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vue-loader", - "version": "6.0.2", + "version": "6.0.3", "description": "Vue.js component loader for Webpack", "main": "index.js", "repository": { From 788223473941b17b94fa0c07c7677f67ec2af746 Mon Sep 17 00:00:00 2001 From: Evan You Date: Sun, 1 Nov 2015 22:28:12 -0500 Subject: [PATCH 108/877] properly support sourcemaps in style-rewriter --- lib/style-rewriter.js | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/lib/style-rewriter.js b/lib/style-rewriter.js index aa9b55413..74975ffef 100644 --- a/lib/style-rewriter.js +++ b/lib/style-rewriter.js @@ -30,18 +30,21 @@ var addId = postcss.plugin('add-id', function () { } }) -module.exports = function (css) { +module.exports = function (css, map) { this.cacheable() var cb = this.async() var query = loaderUtils.parseQuery(this.query) var options = this.options.vue || {} var autoprefixOptions = options.autoprefixer + var plugins = [] - var processors = [] + // scoped css if (query.scoped) { - processors.push(addId) + plugins.push(addId) } + + // autoprefixer if (autoprefixOptions !== false) { autoprefixOptions = assign( {}, @@ -50,14 +53,29 @@ module.exports = function (css) { autoprefixOptions ) var autoprefixer = require('autoprefixer')(autoprefixOptions) - processors.push(autoprefixer) + plugins.push(autoprefixer) + } + + // postcss options, for source maps + var file = loaderUtils.getRemainingRequest(this) + var opts + opts = { + from: file, + to: file, + map: { + inline: false, + annotation: false + } + } + if (map) { + opts.map.prev = map } currentId = '_v-' + hash(this.resourcePath) - postcss(processors) - .process(css) + postcss(plugins) + .process(css, opts) .then(function (result) { - cb(null, result) + cb(null, result.css, result.map) }) .catch(function (e) { console.log(e) From 73def6f7b0bd498f132f3182d966182596e81a27 Mon Sep 17 00:00:00 2001 From: Evan You Date: Sun, 1 Nov 2015 22:28:36 -0500 Subject: [PATCH 109/877] bump 6.0.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 58c0f0b67..44860ce87 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vue-loader", - "version": "6.0.3", + "version": "6.0.4", "description": "Vue.js component loader for Webpack", "main": "index.js", "repository": { From 6af8b3b55190a8df7cbf5536f5bf278266541ac2 Mon Sep 17 00:00:00 2001 From: Evan You Date: Fri, 6 Nov 2015 17:10:51 -0500 Subject: [PATCH 110/877] support user-provided postcss plugins --- README.md | 40 +++++++++++++++++++++++++++++----------- lib/style-rewriter.js | 2 +- package.json | 2 +- 3 files changed, 31 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 61e3561bd..e8172fc6c 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ export default { - [Basic Usage](#basic-usage) - [ES2015 by Default](#es2015-by-default) - [CSS Pre-Processors](#css-pre-processors) -- [Autoprefixing](#autoprefixing) +- [PostCSS](#postcss) - [Template Pre-Processors](#template-pre-processors) - [Style Imports](#style-imports) - [Asset URL Handling](#asset-url-handling) @@ -133,19 +133,35 @@ You can also include Webpack loader queries in the `lang` attribute: ``` -## Autoprefixing +## PostCSS -Starting in 6.0.0, Any CSS output via `vue-loader` is piped through [autoprefixer](https://github.com/postcss/autoprefixer) by default. You can customize this behavior in the `vue` section of your webpack config: +Any CSS output via `vue-loader` 6.0+ is piped through [PostCSS](https://github.com/postcss/postcss) for [scoped CSS](#scoped-css) rewriting and aut-prefixed by default using [autoprefixer](https://github.com/postcss/autoprefixer). You can configure autoprefixer or provide your own PostCSS plugins in the `vue` section of your webpack config: ``` js // webpack.config.js module.exports = { // other configs... vue: { - // disable autoprefixing - autoprefixer: false, - // OR: custom options - autoprefixer: { browsers: ['last 2 versions'] } + // configure autoprefixer + autoprefixer: { + browsers: ['last 2 versions'] + } + } +} +``` + +Using other PostCSS plugins: + +``` js +// webpack.config.js +module.exports = { + // other configs... + vue: { + // use custom postcss plugins + postcss: [require('cssnext')()], + // disable vue-loader autoprefixing. + // this is a good idea since cssnext comes with it too. + autoprefixer: false } } ``` @@ -180,7 +196,7 @@ For more details, see the respective documentations for [vue-html-loader](https: > Experimental -When a ` ``` @@ -212,7 +228,9 @@ Into the following: 2. A child component's root node will be affected by both the parent's scoped CSS and the child's scoped CSS. -3. Partials are not affected by scoped styles. +3. If you are nesting a component inside itself, the styles may still leak down since they are considered the same component. + +4. Partials are not affected by scoped styles. ## Hot Reload diff --git a/lib/style-rewriter.js b/lib/style-rewriter.js index 74975ffef..d8edb3e1f 100644 --- a/lib/style-rewriter.js +++ b/lib/style-rewriter.js @@ -37,7 +37,7 @@ module.exports = function (css, map) { var query = loaderUtils.parseQuery(this.query) var options = this.options.vue || {} var autoprefixOptions = options.autoprefixer - var plugins = [] + var plugins = options.postcss || [] // scoped css if (query.scoped) { diff --git a/package.json b/package.json index 44860ce87..ec3bd6b63 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vue-loader", - "version": "6.0.4", + "version": "6.0.5", "description": "Vue.js component loader for Webpack", "main": "index.js", "repository": { From f2b2d558a35b38596486483aafd8e48c52154050 Mon Sep 17 00:00:00 2001 From: Daiwei Date: Sun, 8 Nov 2015 20:41:38 -0500 Subject: [PATCH 111/877] Upgrade to Babel 6 --- lib/loader.js | 2 +- package.json | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/loader.js b/lib/loader.js index 78bd57456..e3b1d2e3d 100644 --- a/lib/loader.js +++ b/lib/loader.js @@ -12,7 +12,7 @@ var defaultLang = { var defaultLoaders = { html: 'vue-html', css: 'style!css', - js: 'babel?optional[]=runtime&loose=all&nonStandard=false' + js: 'babel?presets[]=es2015&plugins[]=transform-runtime' } var rewriterInjectRE = /\b((css|(vue-)?html)(\?[^!]+)?!?)\b/ diff --git a/package.json b/package.json index ec3bd6b63..8566da610 100644 --- a/package.json +++ b/package.json @@ -35,13 +35,17 @@ "vue-html-loader": "^1.0.0", "css-loader": "^0.21.0", "style-loader": "^0.13.0", - "babel-loader": "^5.3.2", - "babel-runtime": "^5.8.25", + "babel-loader": "^6.0.1", + "babel-plugin-transform-runtime": "^6.1.2", + "babel-preset-es2015": "^6.1.2", + "babel-runtime": "^6.0.14", "vue-hot-reload-api": "^1.2.0" }, "devDependencies": { - "babel-core": "^5.8.25", - "babel-loader": "^5.3.2", + "babel-core": "^6.1.2", + "babel-loader": "^6.0.1", + "babel-plugin-transform-runtime": "^6.1.2", + "babel-preset-es2015": "^6.1.2", "chai": "^3.0.0", "css-loader": "^0.21.0", "eslint": "^1.6.0", From e1f1430c2bb84399c687fa55b2c8034f02b8d96c Mon Sep 17 00:00:00 2001 From: Evan You Date: Mon, 9 Nov 2015 13:07:17 -0500 Subject: [PATCH 112/877] fix test issues for babel 6 --- lib/loader.js | 11 +++++++++-- package.json | 7 ++++--- test/fixtures/basic.vue | 4 ++-- test/fixtures/pre.vue | 2 +- test/test.js | 2 +- 5 files changed, 17 insertions(+), 9 deletions(-) diff --git a/lib/loader.js b/lib/loader.js index e3b1d2e3d..d59a272b0 100644 --- a/lib/loader.js +++ b/lib/loader.js @@ -29,6 +29,11 @@ module.exports = function (content) { var options = this.options.vue || {} var vueUrl = loaderUtils.getRemainingRequest(this) + // respect user babel options + if (this.options.babel) { + defaultLoaders.js = 'babel' + } + // check if there are custom loaders specified with // vueLoader.withLoaders(), otherwise use defaults var loaders = assign({}, defaultLoaders, options.loaders) @@ -139,8 +144,9 @@ module.exports = function (content) { // add require for script if (parts.script.length) { - output += 'module.exports = ' + - getRequire('script', parts.script[0], 0) + output += + 'module.exports = ' + getRequire('script', parts.script[0], 0) + '\n' + + 'if (module.exports.__esModule) module.exports = module.exports.default\n' } // add require for template @@ -178,6 +184,7 @@ module.exports = function (content) { 'hotAPI.createRecord(id, module.exports)\n' + 'module.hot.accept(' + JSON.stringify(accepted) + ', function () {\n' + 'var newOptions = ' + (scriptString ? 'require(' + scriptString + ')\n' : 'null\n') + + 'if (newOptions.__esModule) newOptions = newOptions.default\n' + 'var newTemplate = ' + (templateString ? 'require(' + templateString + ')\n' : 'null\n') + 'hotAPI.update(id, newOptions, newTemplate)\n' + '})\n' + diff --git a/package.json b/package.json index 8566da610..637b5ea5a 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "homepage": "https://github.com/vuejs/vue-loader", "scripts": { "lint": "eslint lib", - "test": "eslint lib && mocha test/test.js --slow 5000" + "test": "eslint lib && mocha test/test.js --slow 5000 --timeout 10000" }, "dependencies": { "autoprefixer": "^6.0.3", @@ -35,7 +35,7 @@ "vue-html-loader": "^1.0.0", "css-loader": "^0.21.0", "style-loader": "^0.13.0", - "babel-loader": "^6.0.1", + "babel-loader": "^6.1.0", "babel-plugin-transform-runtime": "^6.1.2", "babel-preset-es2015": "^6.1.2", "babel-runtime": "^6.0.14", @@ -43,9 +43,10 @@ }, "devDependencies": { "babel-core": "^6.1.2", - "babel-loader": "^6.0.1", + "babel-loader": "^6.1.0", "babel-plugin-transform-runtime": "^6.1.2", "babel-preset-es2015": "^6.1.2", + "babel-runtime": "^6.0.14", "chai": "^3.0.0", "css-loader": "^0.21.0", "eslint": "^1.6.0", diff --git a/test/fixtures/basic.vue b/test/fixtures/basic.vue index 5cf4c68ac..47a9344d8 100644 --- a/test/fixtures/basic.vue +++ b/test/fixtures/basic.vue @@ -9,8 +9,8 @@ comp-a h2 { \ No newline at end of file diff --git a/test/test.js b/test/test.js index 2c5b5dd7a..243e5555a 100644 --- a/test/test.js +++ b/test/test.js @@ -130,6 +130,16 @@ describe('vue-loader', function () { }) }) + it('script import', function (done) { + test({ + entry: './test/fixtures/script-import-entry.js' + }, function (window) { + var module = window.testModule + expect(module.data().msg).to.contain('Hello from Component A!') + done() + }) + }) + it('source map', function (done) { var config = Object.assign({}, globalConfig, { entry: './test/fixtures/basic.js', From b385a1af11ab635840f5114487843f61ba245ff6 Mon Sep 17 00:00:00 2001 From: Evan You Date: Wed, 16 Dec 2015 17:38:20 -0500 Subject: [PATCH 150/877] docs wip --- .gitignore | 5 +- README.md | 29 +---- docs/README.md | 37 ++++++ docs/SUMMARY.md | 16 +++ docs/assets/CNAME | 1 + docs/assets/circle.yml | 4 + docs/book.json | 19 +++ docs/configurations/advanced.md | 33 +++++ docs/configurations/asset-url.md | 38 ++++++ docs/configurations/es2015.md | 70 ++++++++++ docs/configurations/extract-css.md | 34 +++++ docs/configurations/inline-loaders.md | 62 +++++++++ docs/configurations/postcss.md | 49 +++++++ docs/deploy.sh | 10 ++ docs/features/hot-reload.md | 0 docs/features/scoped-css.md | 0 docs/getting-started.md | 176 ++++++++++++++++++++++++++ docs/start/spec.md | 78 ++++++++++++ docs/start/tutorial.md | 175 +++++++++++++++++++++++++ docs/workflow/linting.md | 0 docs/workflow/testing.md | 0 package.json | 8 +- 22 files changed, 818 insertions(+), 26 deletions(-) create mode 100644 docs/README.md create mode 100644 docs/SUMMARY.md create mode 100644 docs/assets/CNAME create mode 100644 docs/assets/circle.yml create mode 100644 docs/book.json create mode 100644 docs/configurations/advanced.md create mode 100644 docs/configurations/asset-url.md create mode 100644 docs/configurations/es2015.md create mode 100644 docs/configurations/extract-css.md create mode 100644 docs/configurations/inline-loaders.md create mode 100644 docs/configurations/postcss.md create mode 100644 docs/deploy.sh create mode 100644 docs/features/hot-reload.md create mode 100644 docs/features/scoped-css.md create mode 100644 docs/getting-started.md create mode 100644 docs/start/spec.md create mode 100644 docs/start/tutorial.md create mode 100644 docs/workflow/linting.md create mode 100644 docs/workflow/testing.md diff --git a/.gitignore b/.gitignore index e698d7971..0374b0709 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ -/node_modules -/npm-debug.log +node_modules +npm-debug.log test/output +docs/_book diff --git a/README.md b/README.md index d0058e44d..59d9fd2fe 100644 --- a/README.md +++ b/README.md @@ -4,28 +4,7 @@ It allows you to write your components in this format: -``` html - - - - - - -``` +![screenshot](http://blog.evanyou.me/images/vue-component.png) ## Table of Contents @@ -39,6 +18,9 @@ export default { - [Asset URL Handling](#asset-url-handling) - [Scoped CSS](#scoped-css) - [Hot Reload](#hot-reload) +- [Syntax Highlighting](#syntax-highlighting) +- [Linting](#linting) +- [Testing](#testing) - [Advanced Loader Configuration](#advanced-loader-configuration) - [Example Project](https://github.com/vuejs/vue-loader-example) @@ -172,11 +154,12 @@ For template pre-processors, you should install `template-html-loader` plus the ## Src Imports -If you want, you can separate your template and styles into separate files and import them using the `src` attribute: +If you want, you can separate your templates, styles or scripts into separate files and import them using the `src` attribute: ``` html + ``` Beware that `src` imports follow similar rules to `require()` calls, which means for relative paths you need to start with `./`, and you can import resources from installed NPM packages, e.g. ` +``` + +Under the hood, the text content inside the ` +``` + +However, note this makes your Vue component Webpack-specific and not compatible with Browserify and [vueify](https://github.com/vuejs/vueify). **If you intend to ship your Vue component as a reusable 3rd-party component, avoid using this syntax.** diff --git a/docs/configurations/postcss.md b/docs/configurations/postcss.md new file mode 100644 index 000000000..e80fe68fb --- /dev/null +++ b/docs/configurations/postcss.md @@ -0,0 +1,49 @@ +# PostCSS and Autoprefixer + +Any CSS output processed by `vue-loader` is piped through [PostCSS](https://github.com/postcss/postcss) for [scoped CSS](../features/scoped-css.md) rewriting and aut-prefixed by default using [autoprefixer](https://github.com/postcss/autoprefixer). + +### Configuring Autoprefixer + +You can configure autoprefixer using the `autoprefixer` option under the `vue` section of your webpack config. See possible [autoprefixer options](https://github.com/postcss/autoprefixer#options). Also, you can pass in `false` to disable autoprefixing. + +Example: + +``` js +// webpack.config.js +module.exports = { + // other options... + module: { + loaders: [ + { + test: /\.vue$/, + loader: 'vue' + } + ] + }, + // vue-loader configurations + vue: { + // configure autoprefixer + autoprefixer: { + browsers: ['last 2 versions'] + } + } +} +``` + +### Using Custom PostCSS Plugins + +To use custom PostCSS pugins, pass an array to the `postcss` option under the `vue` section. Example using [CSSNext](http://cssnext.io/): + +``` js +// webpack.config.js +module.exports = { + // other configs... + vue: { + // use custom postcss plugins + postcss: [require('cssnext')()], + // disable vue-loader autoprefixing. + // this is a good idea since cssnext comes with it too. + autoprefixer: false + } +} +``` diff --git a/docs/deploy.sh b/docs/deploy.sh new file mode 100644 index 000000000..1242aaf57 --- /dev/null +++ b/docs/deploy.sh @@ -0,0 +1,10 @@ +cd docs +rm -rf _book +gitbook build +cp assets/circle.yml _book/circle.yml +cp assets/CNAME _book/CNAME +cd _book +git init +git add -A +git commit -m 'update book' +git push -f git@github.com:vuejs/vue-loader.git master:gh-pages diff --git a/docs/features/hot-reload.md b/docs/features/hot-reload.md new file mode 100644 index 000000000..e69de29bb diff --git a/docs/features/scoped-css.md b/docs/features/scoped-css.md new file mode 100644 index 000000000..e69de29bb diff --git a/docs/getting-started.md b/docs/getting-started.md new file mode 100644 index 000000000..beac721df --- /dev/null +++ b/docs/getting-started.md @@ -0,0 +1,176 @@ +# Getting Started + +### Syntax Highlighting + +First thing first, you will probably want proper syntax highlighting for `*.vue` components. Currently there are syntax highlighting support for [Sublime Text](https://github.com/vuejs/vue-syntax-highlight), [Atom](https://atom.io/packages/language-vue) and [Vim](https://github.com/posva/vim-vue). Contributions for other editors/IDEs are highly appreciated! If you are not using any pre-processors in Vue components, you can also get by by treating `*.vue` files as HTML in your editor. + +### Project Structure + +We are going to walk through setting up a Webpack + `vue-loader` project from scratch. If you are interested in ready-to-run examples, check out [vue-loader-example](https://github.com/vuejs/vue-loader-example) and [vue-hackernews](https://github.com/vuejs/vue-hackernews). However, if you are not already a Webpack expert, I highly recommend going through the following tutorial to understand how the pieces fit together. + +A simple `vue-loader` based project structure looks like this: + +``` bash +. +├── index.html +├── main.js +├── components +│   ├── App.vue +│   ├── ComponentA.vue +│   └── ComponentB.vue +├── package.json +└── webpack.config.js +``` + +### Installing Dependencies + +Before we write any code, we need to install the proper NPM dependencies. Let's run: + +``` bash +# Create a package.json file. +# fill in the questions as you desire. +npm init + +# Install everything we need +npm install\ + webpack webpack-dev-server\ + vue-loader vue-html-loader css-loader style-loader vue-hot-reload-api\ + babel-loader babel-core babel-plugin-transform-runtime babel-preset-es2015\ + babel-runtime@5\ + --save-dev +``` + +That's a lot of dependencies, I know! This is mostly because `vue-loader` need to have other webpack loaders as **peer dependencies** rather than nested dependencies so that Webpack can find them.[^(1)] + +You may also notice that we are using `babel-runtime` version 5 instead of the latest 6.x - this is [intentional](https://github.com/vuejs/vue-loader/issues/96#issuecomment-162910917). + +After proper installation, your `package.json`'s `devDependencies` field should look like this: + +``` json +... + "devDependencies": { + "babel-core": "^6.3.17", + "babel-loader": "^6.2.0", + "babel-plugin-transform-runtime": "^6.3.13", + "babel-preset-es2015": "^6.3.13", + "babel-runtime": "^5.8.34", + "css-loader": "^0.23.0", + "style-loader": "^0.13.0", + "vue-hot-reload-api": "^1.2.2", + "vue-html-loader": "^1.0.0", + "vue-loader": "^7.2.0", + "webpack": "^1.12.9", + "webpack-dev-server": "^1.14.0" + }, +... +``` + +### Configuring Webpack + +Here's the most basic Webpack configuration for `vue-loader`: + +``` js +// webpack.config.js +module.exports = { + // entry point of our application + entry: './main.js', + // where to place the compiled bundle + output: { + path: __dirname, + filename: 'build.js' + }, + module: { + // `loaders` is an array of loaders to use. + // here we are only configuring vue-loader + loaders: [ + { + test: /\.vue$/, // a regex for matching all files that end in `.vue` + loader: 'vue' // loader to use for matched files + } + ] + } +} +``` + +With the above configuration, when you write the following in your JavaScript code: + +``` js +var MyComponent = require('./my-component.vue') +``` + +Webpack knows it needs to pipe the contents of `./my-component.vue` through `vue-loader`, because the filename matches the regex we provided in the config. + +### Creating Other Files + +The app entry point, `main.js` typically looks like this: + +``` js +// main.js +var Vue = require('vue') +// require a *.vue component +var App = require('./components/App.vue') + +// mount a root Vue instance +new Vue({ + el: 'body', + components: { + // include the required component + // in the options + app: App + } +}) +``` + +Inside a `*.vue` component's ` +``` + +Next, let's create an `index.html` that simply uses the bundled file: + +``` html + + + + + +``` + +### Running It + +Finally, it's time to get it running! We will simply use [NPM scripts](https://docs.npmjs.com/misc/scripts) as our task runner, which is sufficient in most cases. Add the following to your `package.json`: + +``` json +... +"scripts": { + "dev": "webpack-dev-server --inline --hot" +} +... +``` + +Then run: + +``` bash +npm run dev +``` + +And you should see your app working at `http://localhost:8080`, with hot-reloading enabled! + +--- + +[^(1)] If you are using NPM version 2.x, when you do `npm install vue-loader --save-dev` it will install and save all the peer dependencies for you. However, if you are using NPM 3.x, these peer dependencies will no longer be automatically installed. You will have to install them explicitly like we did above. Another way to deal with it is to simply copy `vue-loader`'s peer dependencies into your `package.json`'s `devDependencies` field and then run `npm install`. diff --git a/docs/start/spec.md b/docs/start/spec.md new file mode 100644 index 000000000..a420ce17f --- /dev/null +++ b/docs/start/spec.md @@ -0,0 +1,78 @@ +# Vue Component Spec + +A `*.vue` file is a custom file format that uses HTML-like syntax to describe a Vue component. Each `*.vue` file consists of three types of top-level language blocks: ` ' - var outputDir = path.resolve(__dirname, './output') - var loaderPath = 'expose?vueModule!' + path.resolve(__dirname, '../') - var globalConfig = { - output: { - path: outputDir, - filename: 'test.build.js' - }, - module: { - loaders: [ - { - test: /\.vue$/, - loader: loaderPath - } - ] +function bundle (options, cb) { + var config = Object.assign({}, globalConfig, options) + var webpackCompiler = webpack(config) + webpackCompiler.outputFileSystem = mfs + webpackCompiler.run(function (err, stats) { + expect(err).to.be.null + if (stats.compilation.errors.length) { + stats.compilation.errors.forEach(function (err) { + console.error(err.message) + }) } - } - - beforeEach(function (done) { - rimraf(outputDir, done) + expect(stats.compilation.errors).to.be.empty + cb(mfs.readFileSync('/test.build.js').toString()) }) +} - function getFile (file, cb) { - fs.readFile(path.resolve(outputDir, file), 'utf-8', function (err, data) { - expect(err).to.be.not.exist - cb(data) - }) - } - - function test (options, assert) { - var config = Object.assign({}, globalConfig, options) - webpack(config, function (err, stats) { - if (stats.compilation.errors.length) { - stats.compilation.errors.forEach(function (err) { - console.error(err.message) - }) +function test (options, assert) { + bundle(options, function (code) { + jsdom.env({ + html: ' ', + src: [code], + done: function (err, window) { + if (err) { + console.log(err[0].data.error.stack) + expect(err).to.be.null + } + assert(window) } - expect(stats.compilation.errors).to.be.empty - getFile('test.build.js', function (data) { - jsdom.env({ - html: testHTML, - src: [data], - done: function (err, window) { - if (err) { - console.log(err[0].data.error.stack) - expect(err).to.be.null - } - assert(window) - } - }) - }) }) - } + }) +} + +function assertRenderFn (options, template) { + var compiled = compiler.compile(template) + expect(options.render.toString()).to.equal('function (){' + compiled.render + '}') +} +describe('vue-loader', function () { it('basic', function (done) { test({ entry: './test/fixtures/basic.vue' @@ -164,30 +159,26 @@ describe('vue-loader', function () { entry: './test/fixtures/basic.vue', devtool: 'source-map' }) - webpack(config, function (err) { - expect(err).to.be.null - getFile('test.build.js.map', function (map) { - var smc = new SourceMapConsumer(JSON.parse(map)) - getFile('test.build.js', function (code) { - var line - var col - var targetRE = /^\s+msg: 'Hello from Component A!'/ - code.split(/\r?\n/g).some(function (l, i) { - if (targetRE.test(l)) { - line = i + 1 - col = l.length - return true - } - }) - var pos = smc.originalPositionFor({ - line: line, - column: col - }) - expect(pos.source.indexOf('basic.vue') > -1) - expect(pos.line).to.equal(9) - done() - }) + bundle(config, function (code) { + var map = mfs.readFileSync('/test.build.js.map').toString() + var smc = new SourceMapConsumer(JSON.parse(map)) + var line + var col + var targetRE = /^\s+msg: 'Hello from Component A!'/ + code.split(/\r?\n/g).some(function (l, i) { + if (targetRE.test(l)) { + line = i + 1 + col = l.length + return true + } }) + var pos = smc.originalPositionFor({ + line: line, + column: col + }) + expect(pos.source.indexOf('basic.vue') > -1) + expect(pos.line).to.equal(9) + done() }) }) @@ -203,7 +194,7 @@ describe('vue-loader', function () { }) it('extract CSS', function (done) { - webpack(Object.assign({}, globalConfig, { + bundle(Object.assign({}, globalConfig, { entry: './test/fixtures/extract-css.vue', vue: { loaders: { @@ -214,12 +205,10 @@ describe('vue-loader', function () { plugins: [ new ExtractTextPlugin('test.output.css') ] - }), function (err) { - expect(err).to.be.null - getFile('test.output.css', function (data) { - expect(data).to.contain('h1 {\n color: #f00;\n}\n\n\n\n\n\n\nh2 {\n color: green;\n}') - done() - }) + }), function () { + var css = mfs.readFileSync('/test.output.css').toString() + expect(css).to.contain('h1 {\n color: #f00;\n}\n\n\n\n\n\n\nh2 {\n color: green;\n}') + done() }) }) From 60703baa693ec6160812ec80954b9005a4294126 Mon Sep 17 00:00:00 2001 From: Evan You Date: Wed, 15 Jun 2016 22:29:47 -0400 Subject: [PATCH 299/877] better template-loader error handling --- lib/template-loader.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/template-loader.js b/lib/template-loader.js index af3d6e083..ce26b75b4 100644 --- a/lib/template-loader.js +++ b/lib/template-loader.js @@ -27,10 +27,10 @@ module.exports = function (content) { } if (!cons[opt.engine]) { - throw new Error( + return callback(new Error( 'Template engine \'' + opt.engine + '\' ' + 'isn\'t available in Consolidate.js' - ) + )) } // for relative includes @@ -38,7 +38,7 @@ module.exports = function (content) { cons[opt.engine].render(content, opt, function (err, html) { if (err) { - throw err + return callback(err) } exportContent(html) }) From 911efa209078a214299ccfe69e1142176922a1cc Mon Sep 17 00:00:00 2001 From: Evan You Date: Tue, 21 Jun 2016 01:11:45 -0400 Subject: [PATCH 300/877] move source map logic into vue-loader --- lib/parser.js | 52 +++++++++++++++++++++++++++++++++++++++++++++------ package.json | 1 + test/test.js | 2 +- 3 files changed, 48 insertions(+), 7 deletions(-) diff --git a/lib/parser.js b/lib/parser.js index 4f674da24..d65b14fe4 100644 --- a/lib/parser.js +++ b/lib/parser.js @@ -1,6 +1,10 @@ var compiler = require('vue-template-compiler') var cache = require('lru-cache')(100) var hash = require('hash-sum') +var SourceMapGenerator = require('source-map').SourceMapGenerator + +var splitRE = /\r?\n/g +var emptyRE = /^(?:\/\/)?\s*$/ module.exports = function (content, filename, needMap) { var cacheKey = hash(filename + content) @@ -8,12 +12,48 @@ module.exports = function (content, filename, needMap) { var filenameWithHash = filename + '?' + cacheKey var output = cache.get(cacheKey) if (output) return output - output = compiler.parseComponent(content, { - pad: true, - map: needMap - ? { filename: filenameWithHash } - : false - }) + output = compiler.parseComponent(content, { pad: true }) + if (needMap) { + if (output.script && !output.script.src) { + output.script.map = generateSourceMap( + filenameWithHash, + content, + output.script.content + ) + } + if (output.styles) { + output.styles.forEach(style => { + if (!style.src) { + style.map = generateSourceMap( + filenameWithHash, + content, + style.content + ) + } + }) + } + } cache.set(cacheKey, output) return output } + +function generateSourceMap (filename, source, generated) { + var map = new SourceMapGenerator() + map.setSourceContent(filename, source) + generated.split(splitRE).forEach((line, index) => { + if (!emptyRE.test(line)) { + map.addMapping({ + source: filename, + original: { + line: index + 1, + column: 0 + }, + generated: { + line: index + 1, + column: 0 + } + }) + } + }) + return map.toJSON() +} diff --git a/package.json b/package.json index a9191b130..63261e9f0 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "object-assign": "^4.0.0", "postcss": "^5.0.10", "postcss-selector-parser": "^2.0.0", + "source-map": "^0.5.6", "vue-hot-reload-api": "^2.0.1", "vue-style-loader": "^1.0.0", "vue-template-compiler": "^2.0.0-alpha.2" diff --git a/test/test.js b/test/test.js index 6b661a83a..af4fb772b 100644 --- a/test/test.js +++ b/test/test.js @@ -157,7 +157,7 @@ describe('vue-loader', function () { it('source map', function (done) { var config = Object.assign({}, globalConfig, { entry: './test/fixtures/basic.vue', - devtool: 'source-map' + devtool: '#source-map' }) bundle(config, function (code) { var map = mfs.readFileSync('/test.build.js.map').toString() From 63a1031e2105748bcf2c0f156c5e9133804a082d Mon Sep 17 00:00:00 2001 From: Evan You Date: Tue, 21 Jun 2016 01:11:50 -0400 Subject: [PATCH 301/877] 9.0.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 63261e9f0..f9456fa6d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vue-loader", - "version": "9.0.1", + "version": "9.0.2", "description": "Vue.js component loader for Webpack", "main": "index.js", "repository": { From 0256778a51e7727bc47145882795210f54403853 Mon Sep 17 00:00:00 2001 From: Evan You Date: Tue, 21 Jun 2016 03:45:07 -0400 Subject: [PATCH 302/877] remove file-loader peerdep --- package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index f9456fa6d..bc27eae51 100644 --- a/package.json +++ b/package.json @@ -42,8 +42,7 @@ "vue-template-compiler": "^2.0.0-alpha.2" }, "peerDependencies": { - "css-loader": "^0.23.1", - "file-loader": "^0.8.5" + "css-loader": "^0.23.1" }, "devDependencies": { "babel-core": "^6.8.0", From a218d67049e2629ed32550674c7d772a9793d56a Mon Sep 17 00:00:00 2001 From: Evan You Date: Tue, 21 Jun 2016 03:45:10 -0400 Subject: [PATCH 303/877] 9.0.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bc27eae51..c65cb3249 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vue-loader", - "version": "9.0.2", + "version": "9.0.3", "description": "Vue.js component loader for Webpack", "main": "index.js", "repository": { From 584189c9542add147c484f75b7a6c2a7e7fc08f4 Mon Sep 17 00:00:00 2001 From: Evan You Date: Sat, 25 Jun 2016 08:57:10 -0400 Subject: [PATCH 304/877] do not insert style for server bundle + trim css --- lib/loader.js | 17 +++++++++-------- lib/style-rewriter.js | 12 +++++++++++- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/lib/loader.js b/lib/loader.js index 9034bdc0d..f247615a9 100644 --- a/lib/loader.js +++ b/lib/loader.js @@ -32,6 +32,7 @@ var defaultLang = { module.exports = function (content) { this.cacheable() + var isServer = this.options.target === 'node' var loaderContext = this var options = this.options.vue || {} var query = loaderUtils.parseQuery(this.query) @@ -133,17 +134,17 @@ module.exports = function (content) { } var parts = parse(content, fileName, this.sourceMap) - var hasScoped = false + var hasScoped = parts.styles.some(function (s) { return s.scoped }) var output = 'var __vue_script__\n' // add requires for styles - parts.styles.forEach(function (style, i) { - var scoped = style.scoped - if (scoped) hasScoped = true - output += style.src - ? getRequireForImport('styles', style, scoped) - : getRequire('styles', style, i, scoped) - }) + if (!isServer) { + parts.styles.forEach(function (style, i) { + output += style.src + ? getRequireForImport('styles', style, style.scoped) + : getRequire('styles', style, i, style.scoped) + }) + } // add require for script var script = parts.script diff --git a/lib/style-rewriter.js b/lib/style-rewriter.js index d392f2403..32f9cd080 100644 --- a/lib/style-rewriter.js +++ b/lib/style-rewriter.js @@ -27,6 +27,16 @@ var addId = postcss.plugin('add-id', function (opts) { } }) +var trim = postcss.plugin('trim', function (opts) { + return function (css) { + css.walk(function (node) { + if (node.type === 'rule' || node.type == 'atrule') { + node.raws.before = node.raws.after = '\n' + } + }) + } +}) + module.exports = function (css, map) { this.cacheable() var cb = this.async() @@ -44,7 +54,7 @@ module.exports = function (css, map) { } else if (isObject(postcssOptions) && postcssOptions.plugins) { plugins = postcssOptions.plugins } - plugins = plugins ? plugins.slice() : [] // make sure to copy it + plugins = [trim].concat(plugins || []) // scoped css if (query.scoped) { From af26a063af0b99cbc518ee07e42012361f0ff9f9 Mon Sep 17 00:00:00 2001 From: Evan You Date: Sat, 25 Jun 2016 09:45:08 -0400 Subject: [PATCH 305/877] do not include hot-reload for server --- lib/loader.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/loader.js b/lib/loader.js index f247615a9..80dd6f6ab 100644 --- a/lib/loader.js +++ b/lib/loader.js @@ -200,6 +200,7 @@ module.exports = function (content) { 'module.exports = __exports__' // hot reload if ( + !isServer && !this.minimize && process.env.NODE_ENV !== 'production' && (parts.script || parts.template) From 7381356b7b82ca1f4434327be52c166afc34a88f Mon Sep 17 00:00:00 2001 From: Evan You Date: Sat, 25 Jun 2016 11:25:04 -0400 Subject: [PATCH 306/877] fix eslint --- lib/style-rewriter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/style-rewriter.js b/lib/style-rewriter.js index 32f9cd080..3702c90a5 100644 --- a/lib/style-rewriter.js +++ b/lib/style-rewriter.js @@ -30,7 +30,7 @@ var addId = postcss.plugin('add-id', function (opts) { var trim = postcss.plugin('trim', function (opts) { return function (css) { css.walk(function (node) { - if (node.type === 'rule' || node.type == 'atrule') { + if (node.type === 'rule' || node.type === 'atrule') { node.raws.before = node.raws.after = '\n' } }) From 0aabf82f2ebd7f266188c13e3db735c85e23a907 Mon Sep 17 00:00:00 2001 From: Evan You Date: Sat, 25 Jun 2016 11:30:44 -0400 Subject: [PATCH 307/877] fix css tests --- test/test.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/test.js b/test/test.js index af4fb772b..d7cdc8d48 100644 --- a/test/test.js +++ b/test/test.js @@ -126,10 +126,10 @@ describe('vue-loader', function () { entry: './test/fixtures/style-import.vue' }, function (window) { var styles = window.document.querySelectorAll('style') - expect(styles[0].textContent).to.contain('h1 { color: red; }') + expect(styles[0].textContent).to.contain('h1 { color: red;\n}') // import with scoped var id = 'data-v-' + genId(require.resolve('./fixtures/style-import.vue')) - expect(styles[1].textContent).to.contain('h1[' + id + '] { color: green; }') + expect(styles[1].textContent).to.contain('h1[' + id + '] { color: green;\n}') done() }) }) @@ -188,7 +188,7 @@ describe('vue-loader', function () { }, function (window) { var style = window.document.querySelector('style').textContent var id = 'data-v-' + genId(require.resolve('./fixtures/media-query.vue')) - expect(style).to.contain('@media print {\n .foo[' + id + '] {\n color: #000;\n }\n}') + expect(style).to.contain('@media print {\n.foo[' + id + '] {\n color: #000;\n}\n}') done() }) }) @@ -207,7 +207,7 @@ describe('vue-loader', function () { ] }), function () { var css = mfs.readFileSync('/test.output.css').toString() - expect(css).to.contain('h1 {\n color: #f00;\n}\n\n\n\n\n\n\nh2 {\n color: green;\n}') + expect(css).to.contain('h1 {\n color: #f00;\n}\n\nh2 {\n color: green;\n}') done() }) }) @@ -245,8 +245,8 @@ describe('vue-loader', function () { var module = window.vueModule assertRenderFn(module, '\n') var style = window.document.querySelector('style').textContent - expect(style).to.contain('html { background-image: url(logo.c9e00e.png); }') - expect(style).to.contain('body { background-image: url(logo.c9e00e.png); }') + expect(style).to.contain('html { background-image: url(logo.c9e00e.png);\n}') + expect(style).to.contain('body { background-image: url(logo.c9e00e.png);\n}') done() }) }) From 518557b627cb68cdb4145100441549f638e6fb1d Mon Sep 17 00:00:00 2001 From: Evan You Date: Tue, 28 Jun 2016 16:20:02 -0400 Subject: [PATCH 308/877] nicer output with named exports support --- lib/loader.js | 65 ++++++++++++++++++----------------------- package.json | 4 +-- test/fixtures/basic.vue | 4 +++ test/test.js | 36 ++++++++++++----------- 4 files changed, 54 insertions(+), 55 deletions(-) diff --git a/lib/loader.js b/lib/loader.js index 80dd6f6ab..7bcc9d5b1 100644 --- a/lib/loader.js +++ b/lib/loader.js @@ -41,10 +41,11 @@ module.exports = function (content) { var moduleId = 'data-v-' + genId(filePath) var styleRewriter = styleRewriterPath + '?id=' + moduleId + var isProduction = this.minimize || process.env.NODE_ENV === 'production' + var needCssSourceMap = + !isProduction && this.sourceMap && - !this.minimize && - process.env.NODE_ENV !== 'production' && options.cssSourceMap !== false var defaultLoaders = { @@ -135,10 +136,11 @@ module.exports = function (content) { var parts = parse(content, fileName, this.sourceMap) var hasScoped = parts.styles.some(function (s) { return s.scoped }) - var output = 'var __vue_script__\n' + var output = 'var __vue_exports__, __vue_options__\n' // add requires for styles - if (!isServer) { + if (!isServer && parts.styles.length) { + output += '\n/* styles */\n' parts.styles.forEach(function (style, i) { output += style.src ? getRequireForImport('styles', style, style.scoped) @@ -149,32 +151,26 @@ module.exports = function (content) { // add require for script var script = parts.script if (script) { + output += '\n/* script */\n' output += - '__vue_script__ = ' + ( + '__vue_exports__ = ' + ( script.src ? getRequireForImport('script', script) : getRequire('script', script) ) - // check and warn named exports - if (!this.minimize) { - output += - 'if (__vue_script__ &&\n' + - ' __vue_script__.__esModule &&\n' + - ' Object.keys(__vue_script__).length > 1) {\n' + - ' console.warn(' + JSON.stringify( - '[vue-loader] ' + path.relative(process.cwd(), filePath) + - ': named exports in *.vue files are ignored.' - ) + ')' + - '}\n' - } } - // plain require() compatibility - var exports = 'if (__exports__.__esModule) __exports__ = __exports__.default\n' + var exports = + '__vue_options__ = __vue_exports__ || {}\n' + + // ES6 modules interop + 'if (__vue_options__.__esModule) __vue_options__ = __vue_options__.default\n' + + // constructor export interop + 'if (typeof __vue_options__ === "function") __vue_options__ = __vue_options__.options\n' // add require for template var template = parts.template if (template) { + output += '\n/* template */\n' output += 'var __vue_template__ = ' + ( template.src ? getRequireForImport('template', template) @@ -182,9 +178,6 @@ module.exports = function (content) { ) // attach render functions to exported options exports += - 'var __vue_options__ = (typeof __exports__ === "function" ' + - '? (__exports__.options || (__exports__.options = {})) ' + - ': __exports__)\n' + '__vue_options__.render = __vue_template__.render\n' + '__vue_options__.staticRenderFns = __vue_template__.staticRenderFns\n' // attach scoped id @@ -194,39 +187,39 @@ module.exports = function (content) { } if (!query.inject) { - output += - 'var __exports__ = __vue_script__ || {}\n' + - exports + - 'module.exports = __exports__' + output += exports // hot reload if ( !isServer && - !this.minimize && - process.env.NODE_ENV !== 'production' && + !isProduction && (parts.script || parts.template) ) { output += - '\nif (module.hot) {(function () {' + + '\n/* hot reload */\n' + + 'if (module.hot) {(function () {\n' + ' var hotAPI = require("' + hotReloadAPIPath + '")\n' + ' hotAPI.install(require("vue"), false)\n' + ' if (!hotAPI.compatible) return\n' + ' module.hot.accept()\n' + ' if (!module.hot.data) {\n' + // initial insert - ' hotAPI.createRecord("' + moduleId + '", __exports__)\n' + + ' hotAPI.createRecord("' + moduleId + '", __vue_options__)\n' + ' } else {\n' + // update - ' hotAPI.reload("' + moduleId + '", __exports__)\n' + + ' hotAPI.reload("' + moduleId + '", __vue_options__)\n' + ' }\n' + - '})()}' + '})()}\n' } + // final export + output += '\nmodule.exports = __vue_exports__ || __vue_options__\n' } else { + // inject-loader support output += + '\n/* dependency injection */\n' + 'module.exports = function (injections) {\n' + - ' var __exports__ = __vue_script__\n' + - ' ? __vue_script__(injections)\n' + - ' : {}\n' + exports + - ' return __exports__\n' + + ' __vue_exports__ = __vue_exports__(injections)\n' + + exports + + ' return __vue_exports__ || __vue_options__\n' + '}' } diff --git a/package.json b/package.json index c65cb3249..59b161dc0 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "homepage": "https://github.com/vuejs/vue-loader", "scripts": { "lint": "eslint lib", - "test": "eslint lib && mocha test/test.js --slow 5000 --timeout 10000", + "test": "eslint lib && mocha --slow 5000 --timeout 10000", "docs": "cd docs && gitbook serve", "deploy-docs": "bash ./docs/deploy.sh" }, @@ -39,7 +39,7 @@ "source-map": "^0.5.6", "vue-hot-reload-api": "^2.0.1", "vue-style-loader": "^1.0.0", - "vue-template-compiler": "^2.0.0-alpha.2" + "vue-template-compiler": "^2.0.0-alpha.8" }, "peerDependencies": { "css-loader": "^0.23.1" diff --git a/test/fixtures/basic.vue b/test/fixtures/basic.vue index 5dce8c774..96f47a6b7 100644 --- a/test/fixtures/basic.vue +++ b/test/fixtures/basic.vue @@ -3,6 +3,10 @@ diff --git a/test/test.js b/test/test.js index 4d16cbe0e..abe47a253 100644 --- a/test/test.js +++ b/test/test.js @@ -333,4 +333,19 @@ describe('vue-loader', function () { done() }) }) + + it('allows to export extended constructor', function (done) { + test({ + entry: './test/fixtures/extend.vue' + }, function (window, Module) { + // extend.vue should export Vue constructor + var vnode = mockRender(Module.options, { + msg: 'success' + }) + expect(vnode.tag).to.equal('div') + expect(vnode.children[0]).to.equal('success') + expect(new Module().msg === 'success') + done() + }) + }) }) From cd74def6a49f5ca03942a2737c5d32e5ab049b32 Mon Sep 17 00:00:00 2001 From: Evan You Date: Tue, 13 Sep 2016 15:29:19 -0400 Subject: [PATCH 340/877] fix eslint plugin dep --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index e10a01810..595cd6c36 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,7 @@ "css-loader": "^0.23.1", "eslint": "^2.9.0", "eslint-config-vue": "^1.0.0", + "eslint-plugin-html": "^1.5.2", "expose-loader": "^0.7.1", "extract-text-webpack-plugin": "^1.0.1", "file-loader": "^0.8.5", From 9ce2eee790167a96d208cbb68fcc5b2950768d39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=28=C2=B4=E3=83=BB=CF=89=E3=83=BB=EF=BD=80=29?= Date: Wed, 14 Sep 2016 11:14:43 -0500 Subject: [PATCH 341/877] add esModule support (#349) * add alternative module exports * add esModule test and docs --- docs/en/options.md | 8 ++++++++ lib/loader.js | 6 +++++- test/test.js | 20 ++++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/docs/en/options.md b/docs/en/options.md index 61a1f2b47..d11e1eefd 100644 --- a/docs/en/options.md +++ b/docs/en/options.md @@ -73,3 +73,11 @@ - type: `Object` Pass options to the template rendering engine (via [consolidate](https://github.com/tj/consolidate.js)) if you are using a non-html templating language. + +### esModule + +- ^9.4.3 +- type: `Boolean` +- default: `undefined` + + Whether to emit esModule compatible code. By default vue-loader will emit default export in commonjs format like `module.exports = ....`. When `esModule` is set to true, default export will be transpiled into `exports.__esModule = true; exports = ...`. Useful for interoperating with transpiler other than Bable, like TypeScript. diff --git a/lib/loader.js b/lib/loader.js index 196d8307d..abd3d7ba0 100644 --- a/lib/loader.js +++ b/lib/loader.js @@ -236,7 +236,11 @@ module.exports = function (content) { '")}\n' } // final export - output += '\nmodule.exports = __vue_exports__\n' + if (options.esModule) { + output += '\nexports.__esModule = true;\nexports["default"] = __vue_exports__\n' + } else { + output += '\nmodule.exports = __vue_exports__\n' + } } else { // inject-loader support output += diff --git a/test/test.js b/test/test.js index abe47a253..056599c18 100644 --- a/test/test.js +++ b/test/test.js @@ -348,4 +348,24 @@ describe('vue-loader', function () { done() }) }) + + it('support es compatible modules', function (done) { + test({ + entry: './test/fixtures/basic.vue', + vue: { + esModule: true + } + }, function (window, module, rawModule) { + expect(rawModule.__esModule).to.equal(true) + var vnode = mockRender(rawModule.default, { + msg: 'hi' + }) + expect(vnode.tag).to.equal('h2') + expect(vnode.data.staticClass).to.equal('red') + expect(vnode.children[0]).to.equal('hi') + + expect(rawModule.default.data().msg).to.contain('Hello from Component A!') + done() + }) + }) }) From 9d9accce8ac675eeec6e5b24fe731f76a2a945bb Mon Sep 17 00:00:00 2001 From: Evan You Date: Mon, 19 Sep 2016 23:17:12 -0400 Subject: [PATCH 342/877] webpack 2 options interop --- lib/loader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/loader.js b/lib/loader.js index abd3d7ba0..40424fbf9 100644 --- a/lib/loader.js +++ b/lib/loader.js @@ -41,7 +41,7 @@ module.exports = function (content) { this.cacheable() var isServer = this.options.target === 'node' var loaderContext = this - var options = this.options.vue || {} + var options = this.vue || this.options.vue || {} var query = loaderUtils.parseQuery(this.query) var filePath = this.resourcePath var fileName = path.basename(filePath) From bae8d2920753d4c74a42d3ffb0e935ea3e5d4825 Mon Sep 17 00:00:00 2001 From: Evan You Date: Mon, 19 Sep 2016 23:17:20 -0400 Subject: [PATCH 343/877] 9.5.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 595cd6c36..2421d4974 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vue-loader", - "version": "9.4.2", + "version": "9.5.0", "description": "Vue.js component loader for Webpack", "main": "index.js", "repository": { From 75a2b33cdf8c5d47f330ec07ab4460ca43c4106c Mon Sep 17 00:00:00 2001 From: Evan You Date: Wed, 28 Sep 2016 19:21:50 -0400 Subject: [PATCH 344/877] fix vue options in template compiler and style rewriter --- lib/style-rewriter.js | 2 +- lib/template-compiler.js | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/style-rewriter.js b/lib/style-rewriter.js index 3702c90a5..886a46306 100644 --- a/lib/style-rewriter.js +++ b/lib/style-rewriter.js @@ -42,7 +42,7 @@ module.exports = function (css, map) { var cb = this.async() var query = loaderUtils.parseQuery(this.query) - var options = this.options.vue || {} + var options = this.vue || this.options.vue || {} var postcssOptions = options.postcss // postcss plugins diff --git a/lib/template-compiler.js b/lib/template-compiler.js index c704b509b..9bca81777 100644 --- a/lib/template-compiler.js +++ b/lib/template-compiler.js @@ -49,8 +49,9 @@ module.exports = function (html) { this.cacheable() var query = loaderUtils.parseQuery(this.query) var isServer = this.options.target === 'node' - if (this.options.vue && this.options.vue.transformToRequire) { - Object.assign(transformToRequire, this.options.vue.transformToRequire) + var vueOptions = this.vue || this.options.vue || {} + if (vueOptions.transformToRequire) { + Object.assign(transformToRequire, vueOptions.transformToRequire) } var compiled = compiler.compile(html, options) var code From 8462e55b0f3223dfb106dbcd7764b53bbb00e819 Mon Sep 17 00:00:00 2001 From: Evan You Date: Wed, 28 Sep 2016 20:06:17 -0400 Subject: [PATCH 345/877] update docs and readme --- README.md | 17 ++- docs/en/SUMMARY.md | 6 +- docs/en/configurations/advanced.md | 21 ++- docs/en/configurations/asset-url.md | 45 ++---- docs/en/configurations/extract-css.md | 33 ++++- docs/en/features/es2015.md | 71 +--------- docs/en/features/hot-reload.md | 37 +---- docs/en/features/postcss.md | 46 ++---- docs/en/getting-started.md | 176 ----------------------- docs/en/options.md | 44 +++--- docs/en/start/setup.md | 17 +++ docs/en/start/tutorial.md | 186 ------------------------- docs/en/workflow/production.md | 2 +- docs/en/workflow/testing-with-mocks.md | 2 - docs/en/workflow/testing.md | 2 + 15 files changed, 153 insertions(+), 552 deletions(-) delete mode 100644 docs/en/getting-started.md create mode 100644 docs/en/start/setup.md delete mode 100644 docs/en/start/tutorial.md diff --git a/README.md b/README.md index fab68c12c..c10c312aa 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,22 @@ > Vue.js component loader for [Webpack](http://webpack.github.io). +**NOTE: the master branch now hosts 9.x which only works with Vue 2.0. For version 8.x which works with Vue 1.x, see the [8.x branch](https://github.com/vuejs/vue-loader/tree/8.x).** + It allows you to write your components in this format: ![screenshot](http://blog.evanyou.me/images/vue-component.png) -For detailed usage, checkout the [documentation](http://vuejs.github.io/vue-loader/). +The best way to get started is with [vue-cli](https://github.com/vuejs/vue-cli): + +``` js +npm install -g vue-cli +vue init webpack-simple hello +cd hello +npm install +npm run dev +``` -There are also some example projects: +This will setup a basic Webpack + `vue-loader` project for you, with `*.vue` files and hot-reloading working out of the box! -- [vue-loader-example](https://github.com/vuejs/vue-loader-example/) -- [vue-hackernews](https://github.com/vuejs/vue-hackernews) +For advanced `vue-loader` configuration, checkout the [documentation](http://vuejs.github.io/vue-loader/). diff --git a/docs/en/SUMMARY.md b/docs/en/SUMMARY.md index 1c4d10f64..50e9da249 100644 --- a/docs/en/SUMMARY.md +++ b/docs/en/SUMMARY.md @@ -1,10 +1,10 @@ - Getting Started - [Vue Component Spec](start/spec.md) - - [Basic Tutorial](start/tutorial.md) + - [Setting Up a Project](start/setup.md) - Features - - [ES2015 and Babel](features/es2015.md) + - [ES2015](features/es2015.md) - [Scoped CSS](features/scoped-css.md) - - [PostCSS and Autoprefixer](features/postcss.md) + - [PostCSS](features/postcss.md) - [Hot Reload](features/hot-reload.md) - Configurations - [Pre-Processors](configurations/pre-processors.md) diff --git a/docs/en/configurations/advanced.md b/docs/en/configurations/advanced.md index 71e7984a4..1578a3133 100644 --- a/docs/en/configurations/advanced.md +++ b/docs/en/configurations/advanced.md @@ -2,7 +2,7 @@ Sometimes you may want to apply a custom loader string to a language instead of letting `vue-loader` infer it. Or you may simply want to overwrite the built-in loader configuration for the default languages. To do that, add a `vue` block in your Webpack config file, and specify the `loaders` option. -Example: +Webpack 1.x Example: ``` js // webpack.config.js @@ -30,4 +30,23 @@ module.exports = { } ``` +Webpack 2.x: + +``` js +var webpack = require('webpack') + +module.exports = { + // ... + plugins: [ + new webpack.LoaderOptionsPlugin({ + vue: { + loaders: { + // ... + } + } + }) + ] +} +``` + A more practical usage of the advanced loader configuration is [extracting CSS inside components into a single file](./extract-css.md). diff --git a/docs/en/configurations/asset-url.md b/docs/en/configurations/asset-url.md index d6ba44b54..938e258fb 100644 --- a/docs/en/configurations/asset-url.md +++ b/docs/en/configurations/asset-url.md @@ -1,38 +1,23 @@ # Asset URL Handling -By default, `vue-loader` automatically processes your style and template files with [css-loader](https://github.com/webpack/css-loader) and [vue-html-loader](https://github.com/vuejs/vue-html-loader) (which is just a Vue-specific fork of [html-loader](https://github.com/webpack/html-loader)). What this means is that all asset URLs such as ``, `background: url(...)` and CSS `@import` are **resolved as module dependencies**. +By default, `vue-loader` automatically processes your style and template files with [css-loader](https://github.com/webpack/css-loader) and the Vue template compiler. In this compilation process, all asset URLs such as ``, `background: url(...)` and CSS `@import` are **resolved as module dependencies**. -For example, `url(image.png)` will be translated into `require('./image.png')`. Because `.png` is not JavaScript, you will need to configure Webpack to use [file-loader](https://github.com/webpack/file-loader) or [url-loader](https://github.com/webpack/url-loader) to handle them. This may feel cumbersome, but it gives us some very powerful benefits by managing static assets this way: +For example, `url(./image.png)` will be translated into `require('./image.png')`, and -1. `file-loader` allows you to designate where to copy and place the asset file, and how to name it using version hashes. The best part though, is that you can use relative URLs based on the folder structure of your source files, and Webpack will auto-rewrite them into different URLs in the bundled files based on the configuration. - -2. `url-loader` allows you to conditionally inline a file as base-64 data URL if they are smaller than a given threshold. This can reduce the amount of HTTP requests for trivial files. If the file is larger than the threshold, it automatically falls back to `file-loader`. - -Here's an example Webpack config that handles `.png`, `jpg` and `.gif` files, and inlining any file smaller than 10kb as base64 data URL: - -``` bash -npm install url-loader file-loader --save-dev +``` html + ``` +will be compiled into: + ``` js -// webpack.config.js -module.exports = { - // ... other options - module: { - loaders: [ - // ... other loaders - { - test: /\.(png|jpg|gif)$/, - loader: 'url', - query: { - // limit for base64 inlining in bytes - limit: 10000, - // custom naming format if file is larger than - // the threshold - name: '[name].[ext]?[hash]' - } - } - ] - } -} +createElement('img', { attrs: { src: require('../image.png') }}) ``` + +Because `.png` is not a JavaScript file, you will need to configure Webpack to use [file-loader](https://github.com/webpack/file-loader) or [url-loader](https://github.com/webpack/url-loader) to handle them. The project scaffolded with `vue-cli` has also configured this for you. + +The benefits of all this are: + +1. `file-loader` allows you to designate where to copy and place the asset file, and how to name it using version hashes for better caching. Moreoever, this also means **you can just place images next to your `*.vue` files and use relative paths based on the folder structure instead of worrying about deployment URLs**. With proper config, Webpack will auto-rewrite the file paths into correct URLs in the bundled output. + +2. `url-loader` allows you to conditionally inline a file as base-64 data URL if they are smaller than a given threshold. This can reduce the amount of HTTP requests for trivial files. If the file is larger than the threshold, it automatically falls back to `file-loader`. diff --git a/docs/en/configurations/extract-css.md b/docs/en/configurations/extract-css.md index 453e666a5..a48ccf163 100644 --- a/docs/en/configurations/extract-css.md +++ b/docs/en/configurations/extract-css.md @@ -2,13 +2,15 @@ Example config to extract all the processed CSS in all Vue components into a single CSS file: +### Webpack 1.x + ``` bash npm install extract-text-webpack-plugin --save-dev ``` ``` js // webpack.config.js -var ExtractTextPlugin = require("extract-text-webpack-plugin"); +var ExtractTextPlugin = require("extract-text-webpack-plugin") module.exports = { // other options... @@ -32,3 +34,32 @@ module.exports = { ] } ``` + +### Webpack 2.x + +``` bash +npm install extract-text-webpack-plugin@2.x --save-dev +``` + +``` js +// webpack.config.js +var webpack = require('webpack') +var ExtractTextPlugin = require("extract-text-webpack-plugin") + +module.exports = { + // other options... + plugins: [ + new webpack.LoaderOptionsPlugin({ + vue: { + loaders: { + css: ExtractTextPlugin.extract({ + loader: 'css-loader', + fallbackLoader: 'vue-style-loader' // <- this is a dep of vue-loader, so no need to explicitly install if using npm3 + }) + } + } + }), + new ExtractTextPlugin("style.css") + ] +} +``` diff --git a/docs/en/features/es2015.md b/docs/en/features/es2015.md index dae2dd4f8..9ef7de272 100644 --- a/docs/en/features/es2015.md +++ b/docs/en/features/es2015.md @@ -1,6 +1,6 @@ -# ES2015 and Babel Configuration +# ES2015 -`vue-loader` ships with ES2015 (aka ES6) enabled by default in ` -``` - -Next, let's create an `index.html` that simply uses the bundled file: - -``` html - - - - - -``` - -### Running It - -Finally, it's time to get it running! We will simply use [NPM scripts](https://docs.npmjs.com/misc/scripts) as our task runner, which is sufficient in most cases. Add the following to your `package.json`: - -``` json -... -"scripts": { - "dev": "webpack-dev-server --inline --hot" -} -... -``` - -Then run: - -``` bash -npm run dev -``` - -And you should see your app working at `http://localhost:8080`, with hot-reloading enabled! - ---- - -[^(1)] If you are using NPM version 2.x, when you do `npm install vue-loader --save-dev` it will install and save all the peer dependencies for you. However, if you are using NPM 3.x, these peer dependencies will no longer be automatically installed. You will have to install them explicitly like we did above. Another way to deal with it is to simply copy `vue-loader`'s peer dependencies into your `package.json`'s `devDependencies` field and then run `npm install`. diff --git a/docs/en/options.md b/docs/en/options.md index d11e1eefd..0c196ef84 100644 --- a/docs/en/options.md +++ b/docs/en/options.md @@ -1,5 +1,33 @@ # Options Reference +## Usage Difference Between Webpack 1 & 2 + +For Webpack 1.x: add a root `vue` block in your Webpack config: + +``` js +module.exports = { + // ... + vue: { + // vue-loader options + } +} +``` + +For Webpack 2: use `webpack.LoaderOptionsPlugin`: + +``` js +module.exports = { + // ... + plugins: [ + new webpack.LoaderOptionsPlugin({ + vue: { + // vue-loader options + } + }) + ] +} +``` + ### loaders - type: `Object` @@ -21,13 +49,6 @@ } ``` -### autoprefixer - -- type: `Boolean` -- default: `true` - - Whether to enable autoprefixer for CSS inside `*.vue` files. - ### postcss - type: `Array` or `Function` or `Object` @@ -40,7 +61,6 @@ vue: { // note: do not nest the `postcss` option under `loaders` postcss: [require('postcss-cssnext')()], - autoprefixer: false, loaders: { // ... } @@ -67,16 +87,8 @@ Note this is automatically set to `false` if the `devtool` option is not present in the main Webpack config. -### template - -- ^8.4.0 -- type: `Object` - - Pass options to the template rendering engine (via [consolidate](https://github.com/tj/consolidate.js)) if you are using a non-html templating language. - ### esModule -- ^9.4.3 - type: `Boolean` - default: `undefined` diff --git a/docs/en/start/setup.md b/docs/en/start/setup.md new file mode 100644 index 000000000..761e281d2 --- /dev/null +++ b/docs/en/start/setup.md @@ -0,0 +1,17 @@ +# Setting Up a Project + +### Syntax Highlighting + +First thing first, you will probably want proper syntax highlighting for `*.vue` components. Currently there are syntax highlighting support for [Sublime Text](https://github.com/vuejs/vue-syntax-highlight), [Atom](https://atom.io/packages/language-vue) and [Vim](https://github.com/posva/vim-vue). Contributions for other editors/IDEs are highly appreciated! If you are not using any pre-processors in Vue components, you can also get by by treating `*.vue` files as HTML in your editor. + +### Using `vue-cli` + +It's recommended to scaffold a project using `vue-loader` with `vue-cli`: + +``` bash +npm install -g vue-cli +vue init webpack-simple hello-vue +cd hello-vue +npm install +npm run dev # ready to go! +``` diff --git a/docs/en/start/tutorial.md b/docs/en/start/tutorial.md deleted file mode 100644 index e713eec9e..000000000 --- a/docs/en/start/tutorial.md +++ /dev/null @@ -1,186 +0,0 @@ -# Basic Tutorial - -We are going to walk through setting up a Webpack + `vue-loader` project from scratch. If you are interested in ready-to-run examples, check out [vue-cli](https://github.com/vuejs/vue-cli) to quickly scaffold new projects. However, if you are not already a Webpack expert, I highly recommend going through the following tutorial to understand how the pieces fit together. - -### Project Structure - -A simple `vue-loader` based project structure looks like this: - -``` bash -. -├── index.html -├── main.js -├── components -│   ├── App.vue -│   ├── ComponentA.vue -│   └── ComponentB.vue -├── package.json -└── webpack.config.js -``` - -### Installing Dependencies - -Before we write any code, we need to install the proper NPM dependencies. Let's run: - -``` bash -# Create a package.json file. -# fill in the questions as you desire. -npm init - -# Install everything we need -npm install\ - webpack webpack-dev-server\ - vue-loader vue-html-loader css-loader vue-style-loader vue-hot-reload-api\ - babel-loader babel-core babel-plugin-transform-runtime babel-preset-es2015\ - babel-runtime\ - --save-dev -npm install vue --save -``` - -That's a lot of dependencies, I know! This is mostly because `vue-loader` need to have other webpack loaders as **peer dependencies** rather than nested dependencies so that Webpack can find them.[^(1)] - -> Note: In previous versions of `vue-loader` we used to explicitly install `babel-runtime` 5.x to avoid duplicate dependencies - this is no longer necessary after recent babel upgrade. - -After proper installation, your `package.json`'s `devDependencies` field should look like this: - -``` json -... - "devDependencies": { - "babel-core": "^6.3.17", - "babel-loader": "^6.2.0", - "babel-plugin-transform-runtime": "^6.3.13", - "babel-preset-es2015": "^6.3.13", - "babel-runtime": "^5.8.34", - "css-loader": "^0.23.0", - "vue-hot-reload-api": "^1.2.2", - "vue-html-loader": "^1.0.0", - "vue-style-loader": "^1.0.0", - "vue-loader": "^7.2.0", - "webpack": "^1.12.9", - "webpack-dev-server": "^1.14.0" - }, - "dependencies": { - "vue": "^1.0.13" - }, -... -``` - -### Configuring Webpack - -Here's the most basic Webpack configuration for `vue-loader`: - -``` js -// webpack.config.js -module.exports = { - // entry point of our application - entry: './main.js', - // where to place the compiled bundle - output: { - path: __dirname, - filename: 'build.js' - }, - module: { - // `loaders` is an array of loaders to use. - // here we are only configuring vue-loader - loaders: [ - { - test: /\.vue$/, // a regex for matching all files that end in `.vue` - loader: 'vue' // loader to use for matched files - } - ] - } -} -``` - -With the above configuration, when you write the following in your JavaScript code: - -``` js -var MyComponent = require('./my-component.vue') -``` - -Webpack knows it needs to pipe the contents of `./my-component.vue` through `vue-loader`, because the filename matches the regex we provided in the config. - -### Creating Other Files - -The app entry point, `main.js` typically looks like this (using ES2015 syntax): - -``` js -// main.js -import Vue from 'vue' -// require a *.vue component -import App from './components/App' - -// mount a root Vue instance -new Vue({ - el: 'body', - components: { - // include the required component - // in the options - app: App - } -}) -``` - -Inside a `*.vue` component's ` -``` - -Next, let's create an `index.html` that simply uses the bundled file: - -``` html - - - - - -``` - -### Running It - -Finally, it's time to get it running! We will simply use [NPM scripts](https://docs.npmjs.com/misc/scripts) as our task runner, which is sufficient in most cases. Add the following to your `package.json`: - -``` json -... -"scripts": { - "dev": "webpack-dev-server --inline --hot", - "build": "webpack -p" -} -... -``` - -Then run: - -``` bash -npm run dev -``` - -And you should see your app working at `http://localhost:8080`, with hot-reloading enabled! To build, minify and write your bundle to disk, run: - -``` bash -npm run build -``` - -Note that Webpack uses the `webpack.config.js` if it finds one. If you named your Webpack config file with a different name, you need to specify it using the `--config /path/to/your/file` command line option. - ---- - -[^(1)] If you are using NPM version 2.x, when you do `npm install vue-loader --save-dev` it will install and save all the peer dependencies for you. However, if you are using NPM 3.x, these peer dependencies will no longer be automatically installed. You will have to install them explicitly like we did above. Another way to deal with it is to simply copy `vue-loader`'s peer dependencies into your `package.json`'s `devDependencies` field and then run `npm install`. diff --git a/docs/en/workflow/production.md b/docs/en/workflow/production.md index fe25b559f..fcb84569c 100644 --- a/docs/en/workflow/production.md +++ b/docs/en/workflow/production.md @@ -34,6 +34,6 @@ Obviously we don't want to use this config during development, so there are seve 1. Dynamically build up the configuration object based on an environment variable; -2. Or, use two separate Webpack config files, one for development and one for production. And maybe share some common options between them in a third file, as shown in [vue-loader-example](https://github.com/vuejs/vue-loader-example/tree/master/build). +2. Or, use two separate Webpack config files, one for development and one for production. And maybe share some common options between them in a third file, as shown in [vue-hackernews-2.0](https://github.com/vuejs/vue-hackernews-2.0). It's really up to you as long as it achieves the goal. diff --git a/docs/en/workflow/testing-with-mocks.md b/docs/en/workflow/testing-with-mocks.md index 97f099fa2..10ee2fbe5 100644 --- a/docs/en/workflow/testing-with-mocks.md +++ b/docs/en/workflow/testing-with-mocks.md @@ -1,7 +1,5 @@ # Testing with Mocks -> This feature requires `vue-loader@^7.3.0`. - In a real world application, our components most likely have external dependencies. When writing unit tests for components, it would be ideal if we can mock these external dependencies so that our tests only rely the behavior of the component being tested. `vue-loader` provides a feature that allows you to inject arbitrary dependencies to a `*.vue` component, using [inject-loader](https://github.com/plasticine/inject-loader). The general idea is that instead of directly importing the component module, we use `inject-loader` to create a "module factory" function for that module. When this function gets called with an object of mocks, it returns an instance of the module with the mocks injected. diff --git a/docs/en/workflow/testing.md b/docs/en/workflow/testing.md index 515ef50d0..1893b09d5 100644 --- a/docs/en/workflow/testing.md +++ b/docs/en/workflow/testing.md @@ -1,5 +1,7 @@ # Testing +> The [webpack vue-cli template](https://github.com/vuejs-templates/webpack) offers pre-configured unit testing and e2e testing setups for you. + When testing `*.vue` files, we cannot use a plain CommonJS-based test runner because it won't know how to handle `*.vue` files. Instead, we still use Webpack + vue-loader to bundle our test files. The recommended setup is using [Karma](http://karma-runner.github.io/0.13/index.html) and [karma-webpack](https://github.com/webpack/karma-webpack). Karma is a test runner that launches browsers and runs your tests for you. You can choose what browsers you want to test in and what test framework (e.g. Mocha or Jasmine) you want to use. Here is an example Karma configuration that runs the tests inside [PhantomJS](http://phantomjs.org/) with the [Jasmine](http://jasmine.github.io/edge/introduction.html) test framework: From 1fc953aff2c84956404224332d5fbc7176d5ef0f Mon Sep 17 00:00:00 2001 From: Evan You Date: Wed, 28 Sep 2016 20:07:04 -0400 Subject: [PATCH 346/877] 9.5.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2421d4974..0f7c5986e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vue-loader", - "version": "9.5.0", + "version": "9.5.1", "description": "Vue.js component loader for Webpack", "main": "index.js", "repository": { From e14ba8ff33240383824a0286a5bb273e9e0260ba Mon Sep 17 00:00:00 2001 From: Raymond Rutjes Date: Mon, 10 Oct 2016 21:43:20 +0200 Subject: [PATCH 347/877] Update es2015.md (#392) --- docs/en/features/es2015.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/features/es2015.md b/docs/en/features/es2015.md index 9ef7de272..385f9ac41 100644 --- a/docs/en/features/es2015.md +++ b/docs/en/features/es2015.md @@ -30,4 +30,4 @@ Since `vue-loader` only processes `*.vue` files, you'd need to tell Webpack to p ### Configuring Babel with `.babelrc` -`babel-laoder` respects [`.babelrc`](https://babeljs.io/docs/usage/babelrc/), so it is the recommended approach to configure the Babel presets and plugins. +`babel-loader` respects [`.babelrc`](https://babeljs.io/docs/usage/babelrc/), so it is the recommended approach to configure the Babel presets and plugins. From 4fd133db364f35babff3f329167630f318ece1a5 Mon Sep 17 00:00:00 2001 From: Raymond Rutjes Date: Mon, 10 Oct 2016 21:43:28 +0200 Subject: [PATCH 348/877] Update options.md (#393) --- docs/en/options.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/options.md b/docs/en/options.md index 0c196ef84..8fc66c111 100644 --- a/docs/en/options.md +++ b/docs/en/options.md @@ -92,4 +92,4 @@ module.exports = { - type: `Boolean` - default: `undefined` - Whether to emit esModule compatible code. By default vue-loader will emit default export in commonjs format like `module.exports = ....`. When `esModule` is set to true, default export will be transpiled into `exports.__esModule = true; exports = ...`. Useful for interoperating with transpiler other than Bable, like TypeScript. + Whether to emit esModule compatible code. By default vue-loader will emit default export in commonjs format like `module.exports = ....`. When `esModule` is set to true, default export will be transpiled into `exports.__esModule = true; exports = ...`. Useful for interoperating with transpiler other than Babel, like TypeScript. From 43e331978d61cb153897221b61468fc8d77c06aa Mon Sep 17 00:00:00 2001 From: Aji Date: Tue, 11 Oct 2016 03:43:56 +0800 Subject: [PATCH 349/877] Change a character at line 33 (#391) Change a character 'babel-laoder' to 'babel-loader' From 84487b63d2e622fda36255825033bb2adefc713a Mon Sep 17 00:00:00 2001 From: Obura Tongoi Date: Mon, 10 Oct 2016 12:44:06 -0700 Subject: [PATCH 350/877] Minor typo (#387) Change traspile to transpile --- docs/en/features/es2015.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/features/es2015.md b/docs/en/features/es2015.md index 385f9ac41..f57542600 100644 --- a/docs/en/features/es2015.md +++ b/docs/en/features/es2015.md @@ -24,7 +24,7 @@ export default { We are using ES2015's Object literal shorthand here to define the child components. `{ ComponentA }` is simply shorthand for `{ ComponentA: ComponentA }`. Vue will automatically convert the key to `component-a`, so you can use the imported component in the template as ``. -### Traspiling Normal `.js` Files +### Transpiling Normal `.js` Files Since `vue-loader` only processes `*.vue` files, you'd need to tell Webpack to process normal `*.js` files with `babel-loader` or `buble-loader` in the Webpack config file. The project scaffolded with `vue-cli` already does it for you. From 5d7696847adc571636f20d805b7c1bb5f5936028 Mon Sep 17 00:00:00 2001 From: Lar Van Der Jagt Date: Mon, 10 Oct 2016 15:45:12 -0400 Subject: [PATCH 351/877] Fix link to deprecated example repo (#375) The top of the page contains the correct link, but this reference is still outdated. --- docs/en/workflow/testing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/workflow/testing.md b/docs/en/workflow/testing.md index 1893b09d5..03c0a64c5 100644 --- a/docs/en/workflow/testing.md +++ b/docs/en/workflow/testing.md @@ -98,4 +98,4 @@ Finally, run: npm test ``` -Again, [vue-loader-example](https://github.com/vuejs/vue-loader-example) contains a fully working example with tests. +Again, [webpack vue-cli template](https://github.com/vuejs-templates/webpack) contains a fully working example with tests. From 8f9dff5789c7512213b7878638aaf3759ca564d9 Mon Sep 17 00:00:00 2001 From: Nikola Kovacs Date: Mon, 10 Oct 2016 22:02:39 +0200 Subject: [PATCH 352/877] Add Webpack 2.x configuration for eslint (#378) --- docs/en/workflow/linting.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/docs/en/workflow/linting.md b/docs/en/workflow/linting.md index 417d5b75a..431047436 100644 --- a/docs/en/workflow/linting.md +++ b/docs/en/workflow/linting.md @@ -66,3 +66,28 @@ module.exports = { } } ``` + +For Webpack 2.x: + +``` js +// webpack.config.js +module.exports = { + // ... other options + module: { + rules: [ + // only lint local *.vue files + { + enforce: 'pre', + test: /.vue$/, + loader: 'eslint', + exclude: /node_modules/ + }, + // but use vue-loader for all *.vue files + { + test: /.vue$/, + loader: 'vue' + } + ] + } +} +``` From b95750bf504b738db5a6f0f51ea775a571ab5fcd Mon Sep 17 00:00:00 2001 From: Sam Herbert Date: Mon, 10 Oct 2016 14:02:56 -0600 Subject: [PATCH 353/877] Update production.md (#373) "setup described in the Vue.js guide" was linking to v1 docs and 404ing. Updated to link to correct v2.0 docs --- docs/en/workflow/production.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/workflow/production.md b/docs/en/workflow/production.md index fcb84569c..9642520c3 100644 --- a/docs/en/workflow/production.md +++ b/docs/en/workflow/production.md @@ -3,7 +3,7 @@ There are two things to do when building our bundle for production: 1. Minify our application code; -2. Use the [setup described in the Vue.js guide](http://vuejs.org/guide/application.html#Deploying_for_Production) to strip all the warnings from Vue.js source code. +2. Use the [setup described in the Vue.js guide](http://vuejs.org/guide/single-file-components.html#Deploying-for-Production) to strip all the warnings from Vue.js source code. Here's an example config: From cd358d62497b475188c999e66a6c7c1598fd23bb Mon Sep 17 00:00:00 2001 From: Chris Fritz Date: Tue, 11 Oct 2016 13:33:39 -0400 Subject: [PATCH 354/877] Provide default for name option (#396) * Automatically set name option unless already set Use the filename to set a component's name option, if one isn't provided. This allows more helpful debugging messages by default. * remove unnecessary object check for __vue_options__ * add test for automatic name setting based on filenames * remove redundant if statement from loader.js --- lib/loader.js | 4 +++- test/fixtures/named.vue | 9 +++++++++ test/fixtures/unnamed-5tr@ng3_f1l3n@me$.vue | 3 +++ test/fixtures/unnamed.vue | 3 +++ test/test.js | 19 +++++++++++++++++++ 5 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 test/fixtures/named.vue create mode 100644 test/fixtures/unnamed-5tr@ng3_f1l3n@me$.vue create mode 100644 test/fixtures/unnamed.vue diff --git a/lib/loader.js b/lib/loader.js index 40424fbf9..ee14c74c0 100644 --- a/lib/loader.js +++ b/lib/loader.js @@ -180,7 +180,9 @@ module.exports = function (content) { // constructor export interop 'if (typeof __vue_options__ === "function") {\n' + ' __vue_options__ = __vue_options__.options\n' + - '}\n' + '}\n' + + // add name based on filename if not already set + '__vue_options__.name = __vue_options__.name || "' + fileName.replace(/(\.vue|[^a-z0-9\-])/gi, '') + '"\n' // add require for template var template = parts.template diff --git a/test/fixtures/named.vue b/test/fixtures/named.vue new file mode 100644 index 000000000..f2edcd883 --- /dev/null +++ b/test/fixtures/named.vue @@ -0,0 +1,9 @@ + + + diff --git a/test/fixtures/unnamed-5tr@ng3_f1l3n@me$.vue b/test/fixtures/unnamed-5tr@ng3_f1l3n@me$.vue new file mode 100644 index 000000000..92122e949 --- /dev/null +++ b/test/fixtures/unnamed-5tr@ng3_f1l3n@me$.vue @@ -0,0 +1,3 @@ + diff --git a/test/fixtures/unnamed.vue b/test/fixtures/unnamed.vue new file mode 100644 index 000000000..92122e949 --- /dev/null +++ b/test/fixtures/unnamed.vue @@ -0,0 +1,3 @@ + diff --git a/test/test.js b/test/test.js index 056599c18..61818d08f 100644 --- a/test/test.js +++ b/test/test.js @@ -108,6 +108,25 @@ describe('vue-loader', function () { }) }) + it('automatic name setting based on filename', function (done) { + test({ + entry: './test/fixtures/unnamed.vue' + }, function (window, module, rawModule) { + expect(module.name).to.equal('unnamed') + test({ + entry: './test/fixtures/named.vue' + }, function (window, module, rawModule) { + expect(module.name).to.equal('custom-name') + test({ + entry: './test/fixtures/unnamed-5tr@ng3_f1l3n@me$.vue' + }, function (window, module, rawModule) { + expect(module.name).to.equal('unnamed-5trng3f1l3nme') + done() + }) + }) + }) + }) + it('pre-processors', function (done) { test({ entry: './test/fixtures/pre.vue' From f2b09066d2a55fd09aa414c28bfc92f446f913f7 Mon Sep 17 00:00:00 2001 From: J Bruni Date: Tue, 11 Oct 2016 14:33:51 -0300 Subject: [PATCH 355/877] Update production.md (#373) (#395) --- docs/en/workflow/production.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/workflow/production.md b/docs/en/workflow/production.md index 9642520c3..a54081095 100644 --- a/docs/en/workflow/production.md +++ b/docs/en/workflow/production.md @@ -3,7 +3,7 @@ There are two things to do when building our bundle for production: 1. Minify our application code; -2. Use the [setup described in the Vue.js guide](http://vuejs.org/guide/single-file-components.html#Deploying-for-Production) to strip all the warnings from Vue.js source code. +2. Use the [setup described in the Vue.js guide](https://vuejs.org/guide/deployment.html) to strip all the warnings from Vue.js source code. Here's an example config: From 90dbaeb02bff428157a81ba894875857d3d5f852 Mon Sep 17 00:00:00 2001 From: Evan You Date: Tue, 11 Oct 2016 13:46:13 -0400 Subject: [PATCH 356/877] rename docs deploy script --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0f7c5986e..9403610cb 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "lint": "eslint lib", "test": "eslint lib && mocha --slow 5000 --timeout 10000", "docs": "cd docs && gitbook serve", - "deploy-docs": "bash ./docs/deploy.sh" + "docs:deploy": "bash ./docs/deploy.sh" }, "dependencies": { "consolidate": "^0.14.0", From 488a078413e6a05c551d456f589446a64d19401d Mon Sep 17 00:00:00 2001 From: Evan You Date: Tue, 11 Oct 2016 13:48:14 -0400 Subject: [PATCH 357/877] use hash-sum for module id generation --- lib/gen-id.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/gen-id.js b/lib/gen-id.js index 25281aec5..8c9621dbf 100644 --- a/lib/gen-id.js +++ b/lib/gen-id.js @@ -1,8 +1,8 @@ // utility for generating a uid for each component file // used in scoped CSS rewriting -var fileUid = 1 -var fileRegistry = Object.create(null) +var hash = require('hash-sum') +var cache = Object.create(null) module.exports = function genId (file) { - return fileRegistry[file] || (fileRegistry[file] = fileUid++) + return cache[file] || (cache[file] = hash(file)) } From 8b0520e33fae2d4977250c2480c90ef458863432 Mon Sep 17 00:00:00 2001 From: Joshua Bemenderfer Date: Tue, 11 Oct 2016 12:54:58 -0500 Subject: [PATCH 358/877] [Fix for Webpack 2 Beta 24+] Get loader options from query instead of raw this.options (#361) * Get options from query instead of this.options. In Webpack 2, query can be passed an object and this.options is mounted to this.query * Prioritize Query, re-add this.vue --- lib/loader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/loader.js b/lib/loader.js index ee14c74c0..011a01826 100644 --- a/lib/loader.js +++ b/lib/loader.js @@ -41,8 +41,8 @@ module.exports = function (content) { this.cacheable() var isServer = this.options.target === 'node' var loaderContext = this - var options = this.vue || this.options.vue || {} var query = loaderUtils.parseQuery(this.query) + var options = this.query ? query : this.vue || this.options.vue || {} var filePath = this.resourcePath var fileName = path.basename(filePath) var moduleId = 'data-v-' + genId(filePath) From fcc02bd8cb1a38ee463696f41d7578a0a389eef8 Mon Sep 17 00:00:00 2001 From: Evan You Date: Tue, 11 Oct 2016 13:57:42 -0400 Subject: [PATCH 359/877] merge query and old config options --- lib/loader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/loader.js b/lib/loader.js index 011a01826..7bcebcaab 100644 --- a/lib/loader.js +++ b/lib/loader.js @@ -42,7 +42,7 @@ module.exports = function (content) { var isServer = this.options.target === 'node' var loaderContext = this var query = loaderUtils.parseQuery(this.query) - var options = this.query ? query : this.vue || this.options.vue || {} + var options = Object.assign({}, this.options.vue, this.vue, query) var filePath = this.resourcePath var fileName = path.basename(filePath) var moduleId = 'data-v-' + genId(filePath) From 336a4c096d523bb86dc092e1157111c9b6aff9e7 Mon Sep 17 00:00:00 2001 From: Evan You Date: Tue, 11 Oct 2016 14:03:20 -0400 Subject: [PATCH 360/877] update docs for webpack 2 new option usage --- docs/en/configurations/advanced.md | 26 +++++++++++++++----------- docs/en/configurations/extract-css.md | 27 ++++++++++++++++----------- docs/en/options.md | 18 +++++++++++------- 3 files changed, 42 insertions(+), 29 deletions(-) diff --git a/docs/en/configurations/advanced.md b/docs/en/configurations/advanced.md index 1578a3133..2dae6aca4 100644 --- a/docs/en/configurations/advanced.md +++ b/docs/en/configurations/advanced.md @@ -30,22 +30,26 @@ module.exports = { } ``` -Webpack 2.x: +Webpack 2.x (^2.1.0-beta.25): ``` js -var webpack = require('webpack') - module.exports = { - // ... - plugins: [ - new webpack.LoaderOptionsPlugin({ - vue: { - loaders: { - // ... + // other options... + module: { + // module.rules is the same as module.loaders in 1.x + rules: [ + { + test: /\.vue$/, + loader: 'vue', + // vue-loader options goes here + options: { + loaders: { + // ... + } } } - }) - ] + ] + } } ``` diff --git a/docs/en/configurations/extract-css.md b/docs/en/configurations/extract-css.md index a48ccf163..b0701a04b 100644 --- a/docs/en/configurations/extract-css.md +++ b/docs/en/configurations/extract-css.md @@ -35,7 +35,7 @@ module.exports = { } ``` -### Webpack 2.x +### Webpack 2.x (^2.1.0-beta.25) ``` bash npm install extract-text-webpack-plugin@2.x --save-dev @@ -43,22 +43,27 @@ npm install extract-text-webpack-plugin@2.x --save-dev ``` js // webpack.config.js -var webpack = require('webpack') var ExtractTextPlugin = require("extract-text-webpack-plugin") module.exports = { // other options... - plugins: [ - new webpack.LoaderOptionsPlugin({ - vue: { - loaders: { - css: ExtractTextPlugin.extract({ - loader: 'css-loader', - fallbackLoader: 'vue-style-loader' // <- this is a dep of vue-loader, so no need to explicitly install if using npm3 - }) + module: { + rules: [ + { + test: /\.vue$/, + loader: 'vue', + options: { + loaders: { + css: ExtractTextPlugin.extract({ + loader: 'css-loader', + fallbackLoader: 'vue-style-loader' // <- this is a dep of vue-loader, so no need to explicitly install if using npm3 + }) + } } } - }), + ] + }, + plugins: [ new ExtractTextPlugin("style.css") ] } diff --git a/docs/en/options.md b/docs/en/options.md index 8fc66c111..701dc984a 100644 --- a/docs/en/options.md +++ b/docs/en/options.md @@ -13,18 +13,22 @@ module.exports = { } ``` -For Webpack 2: use `webpack.LoaderOptionsPlugin`: +For Webpack 2 (^2.1.0-beta.25): ``` js module.exports = { // ... - plugins: [ - new webpack.LoaderOptionsPlugin({ - vue: { - // vue-loader options + module: { + rules: [ + { + test: /\.vue$/, + loader: 'vue', + options: { + // vue-loader options + } } - }) - ] + ] + } } ``` From eca04eb1f23a0c06250cf9fb0a663236239c2154 Mon Sep 17 00:00:00 2001 From: Evan You Date: Tue, 11 Oct 2016 14:03:27 -0400 Subject: [PATCH 361/877] 9.5.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9403610cb..dc771fcd5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vue-loader", - "version": "9.5.1", + "version": "9.5.2", "description": "Vue.js component loader for Webpack", "main": "index.js", "repository": { From d310aa2c5f67748b64ab0fab473c5465a38e3be6 Mon Sep 17 00:00:00 2001 From: Evan You Date: Tue, 11 Oct 2016 17:37:49 -0400 Subject: [PATCH 362/877] include component file location during dev --- lib/loader.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/loader.js b/lib/loader.js index 7bcebcaab..a91381fff 100644 --- a/lib/loader.js +++ b/lib/loader.js @@ -182,7 +182,9 @@ module.exports = function (content) { ' __vue_options__ = __vue_options__.options\n' + '}\n' + // add name based on filename if not already set - '__vue_options__.name = __vue_options__.name || "' + fileName.replace(/(\.vue|[^a-z0-9\-])/gi, '') + '"\n' + '__vue_options__.name = __vue_options__.name || ' + JSON.stringify(fileName.replace(/(\.vue|[^a-z0-9\-])/gi, '')) + '\n' + + // add filename in dev + (isProduction ? '' : ('__vue_options__.__file = ' + JSON.stringify(filePath))) + '\n' // add require for template var template = parts.template From 9e0ad9b6e47ee828e1d37aad91ad97e1c93494fb Mon Sep 17 00:00:00 2001 From: Evan You Date: Tue, 11 Oct 2016 17:37:58 -0400 Subject: [PATCH 363/877] 9.5.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index dc771fcd5..5357e3f61 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vue-loader", - "version": "9.5.2", + "version": "9.5.3", "description": "Vue.js component loader for Webpack", "main": "index.js", "repository": { From deee354071fee02df651fde2f39fdbab8805275f Mon Sep 17 00:00:00 2001 From: Evan You Date: Wed, 12 Oct 2016 23:06:49 -0400 Subject: [PATCH 364/877] revert auto name-setting --- lib/loader.js | 2 -- test/fixtures/named.vue | 9 --------- test/fixtures/unnamed-5tr@ng3_f1l3n@me$.vue | 3 --- test/fixtures/unnamed.vue | 3 --- test/test.js | 18 ++++-------------- 5 files changed, 4 insertions(+), 31 deletions(-) delete mode 100644 test/fixtures/named.vue delete mode 100644 test/fixtures/unnamed-5tr@ng3_f1l3n@me$.vue delete mode 100644 test/fixtures/unnamed.vue diff --git a/lib/loader.js b/lib/loader.js index a91381fff..72dae401a 100644 --- a/lib/loader.js +++ b/lib/loader.js @@ -181,8 +181,6 @@ module.exports = function (content) { 'if (typeof __vue_options__ === "function") {\n' + ' __vue_options__ = __vue_options__.options\n' + '}\n' + - // add name based on filename if not already set - '__vue_options__.name = __vue_options__.name || ' + JSON.stringify(fileName.replace(/(\.vue|[^a-z0-9\-])/gi, '')) + '\n' + // add filename in dev (isProduction ? '' : ('__vue_options__.__file = ' + JSON.stringify(filePath))) + '\n' diff --git a/test/fixtures/named.vue b/test/fixtures/named.vue deleted file mode 100644 index f2edcd883..000000000 --- a/test/fixtures/named.vue +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/test/fixtures/unnamed-5tr@ng3_f1l3n@me$.vue b/test/fixtures/unnamed-5tr@ng3_f1l3n@me$.vue deleted file mode 100644 index 92122e949..000000000 --- a/test/fixtures/unnamed-5tr@ng3_f1l3n@me$.vue +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/test/fixtures/unnamed.vue b/test/fixtures/unnamed.vue deleted file mode 100644 index 92122e949..000000000 --- a/test/fixtures/unnamed.vue +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/test/test.js b/test/test.js index 61818d08f..c7327255f 100644 --- a/test/test.js +++ b/test/test.js @@ -108,22 +108,12 @@ describe('vue-loader', function () { }) }) - it('automatic name setting based on filename', function (done) { + it('expose filename', function (done) { test({ - entry: './test/fixtures/unnamed.vue' + entry: './test/fixtures/basic.vue' }, function (window, module, rawModule) { - expect(module.name).to.equal('unnamed') - test({ - entry: './test/fixtures/named.vue' - }, function (window, module, rawModule) { - expect(module.name).to.equal('custom-name') - test({ - entry: './test/fixtures/unnamed-5tr@ng3_f1l3n@me$.vue' - }, function (window, module, rawModule) { - expect(module.name).to.equal('unnamed-5trng3f1l3nme') - done() - }) - }) + expect(module.__file).to.equal(path.resolve(__dirname, './fixtures/basic.vue')) + done() }) }) From 992d6a1e2a61d2c8a76d54408f3e9ff3548c2288 Mon Sep 17 00:00:00 2001 From: Evan You Date: Wed, 12 Oct 2016 23:18:12 -0400 Subject: [PATCH 365/877] share options across all sub-loaders --- lib/loader.js | 2 +- lib/style-rewriter.js | 2 +- lib/template-compiler.js | 2 +- lib/template-loader.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/loader.js b/lib/loader.js index 72dae401a..47a33560a 100644 --- a/lib/loader.js +++ b/lib/loader.js @@ -42,7 +42,7 @@ module.exports = function (content) { var isServer = this.options.target === 'node' var loaderContext = this var query = loaderUtils.parseQuery(this.query) - var options = Object.assign({}, this.options.vue, this.vue, query) + var options = this.options.__vueOptions__ = Object.assign({}, this.options.vue, this.vue, query) var filePath = this.resourcePath var fileName = path.basename(filePath) var moduleId = 'data-v-' + genId(filePath) diff --git a/lib/style-rewriter.js b/lib/style-rewriter.js index 886a46306..814516e15 100644 --- a/lib/style-rewriter.js +++ b/lib/style-rewriter.js @@ -42,7 +42,7 @@ module.exports = function (css, map) { var cb = this.async() var query = loaderUtils.parseQuery(this.query) - var options = this.vue || this.options.vue || {} + var options = this.options.__vueOptions__ var postcssOptions = options.postcss // postcss plugins diff --git a/lib/template-compiler.js b/lib/template-compiler.js index 9bca81777..655de314c 100644 --- a/lib/template-compiler.js +++ b/lib/template-compiler.js @@ -49,7 +49,7 @@ module.exports = function (html) { this.cacheable() var query = loaderUtils.parseQuery(this.query) var isServer = this.options.target === 'node' - var vueOptions = this.vue || this.options.vue || {} + var vueOptions = this.options.__vueOptions__ if (vueOptions.transformToRequire) { Object.assign(transformToRequire, vueOptions.transformToRequire) } diff --git a/lib/template-loader.js b/lib/template-loader.js index ce26b75b4..57a596870 100644 --- a/lib/template-loader.js +++ b/lib/template-loader.js @@ -6,7 +6,7 @@ module.exports = function (content) { this.cacheable && this.cacheable() var callback = this.async() var opt = loaderUtils.parseQuery(this.query) - var vue = this.options.vue + var vue = this.options.__vueOptions__ if (vue && vue.template) { for (var key in vue.template) { opt[key] = vue.template[key] From eeb3c43adf0585c11a4c4d0d285fd8d99517cbce Mon Sep 17 00:00:00 2001 From: Evan You Date: Wed, 12 Oct 2016 23:50:41 -0400 Subject: [PATCH 366/877] expose preserveWhitespace option --- lib/template-compiler.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/template-compiler.js b/lib/template-compiler.js index 655de314c..9dd63cbf5 100644 --- a/lib/template-compiler.js +++ b/lib/template-compiler.js @@ -9,8 +9,8 @@ var hotReloadAPIPath = normalize.dep('vue-hot-reload-api') var defaultTransformToRequire = { img: 'src' } -var transformToRequire = Object.assign({}, defaultTransformToRequire) -var options = { +var transformToRequire = defaultTransformToRequire +var defaultCompileOptions = { modules: [{ postTransformNode (el) { for (var tag in transformToRequire) { @@ -51,9 +51,15 @@ module.exports = function (html) { var isServer = this.options.target === 'node' var vueOptions = this.options.__vueOptions__ if (vueOptions.transformToRequire) { - Object.assign(transformToRequire, vueOptions.transformToRequire) + transformToRequire = Object.assign( + {}, + defaultTransformToRequire, + vueOptions.transformToRequire + ) } - var compiled = compiler.compile(html, options) + var compiled = compiler.compile(html, Object.assign({ + preserveWhitespace: vueOptions.preserveWhitespace + }, defaultCompileOptions)) var code if (compiled.errors.length) { var self = this From 8d1cb2fd283beaea228be835033505996a631c56 Mon Sep 17 00:00:00 2001 From: Evan You Date: Wed, 12 Oct 2016 23:58:03 -0400 Subject: [PATCH 367/877] add documentation for additional options --- docs/en/options.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/en/options.md b/docs/en/options.md index 701dc984a..2b63b3999 100644 --- a/docs/en/options.md +++ b/docs/en/options.md @@ -97,3 +97,17 @@ module.exports = { - default: `undefined` Whether to emit esModule compatible code. By default vue-loader will emit default export in commonjs format like `module.exports = ....`. When `esModule` is set to true, default export will be transpiled into `exports.__esModule = true; exports = ...`. Useful for interoperating with transpiler other than Babel, like TypeScript. + +### preserveWhitespace + +- type: `Boolean` +- default: `true` + + If set to `false`, the whitespaces between HTML tags in templates will be ignored. + +### transformToRequire + +- type: `{ [tag: string]: string | Array }` +- default: `{ img: 'src' }` + + During template compilation, the compiler can transform certain attributes, such as `src` URLs, into `require` calls, so that the target asset can be handled by Webpack. The default config transforms the `src` attribute on `` tags. From b422cfc945e2e293c17cde96d84001da3d4dcec9 Mon Sep 17 00:00:00 2001 From: Evan You Date: Wed, 12 Oct 2016 23:58:08 -0400 Subject: [PATCH 368/877] 9.6.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5357e3f61..bbb5678a2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vue-loader", - "version": "9.5.3", + "version": "9.6.0", "description": "Vue.js component loader for Webpack", "main": "index.js", "repository": { From 55113636ad0239ddc7d72a22a06cf236b40f37eb Mon Sep 17 00:00:00 2001 From: Evan You Date: Thu, 13 Oct 2016 00:17:02 -0400 Subject: [PATCH 369/877] expose buble option --- lib/loader.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/loader.js b/lib/loader.js index 47a33560a..f2a61c005 100644 --- a/lib/loader.js +++ b/lib/loader.js @@ -55,10 +55,11 @@ module.exports = function (content) { this.sourceMap && options.cssSourceMap !== false + var bubleOptions = hasBuble && options.buble ? '?' + JSON.stringify(options.buble) : '' var defaultLoaders = { html: templateCompilerPath + '?id=' + moduleId, css: styleLoaderPath + '!css-loader' + (needCssSourceMap ? '?sourceMap' : ''), - js: hasBuble ? 'buble-loader' : hasBabel ? 'babel-loader' : '' + js: hasBuble ? ('buble-loader' + bubleOptions) : hasBabel ? 'babel-loader' : '' } // check if there are custom loaders specified via From b4d3818a500f66ae61253b98cfc2186fedfe6876 Mon Sep 17 00:00:00 2001 From: Evan You Date: Thu, 13 Oct 2016 00:18:44 -0400 Subject: [PATCH 370/877] add buble docs --- docs/en/options.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/en/options.md b/docs/en/options.md index 2b63b3999..ee08ff1a9 100644 --- a/docs/en/options.md +++ b/docs/en/options.md @@ -111,3 +111,18 @@ module.exports = { - default: `{ img: 'src' }` During template compilation, the compiler can transform certain attributes, such as `src` URLs, into `require` calls, so that the target asset can be handled by Webpack. The default config transforms the `src` attribute on `` tags. + +### buble + +- type: `Object` +- default: `{}` + + Configure options for `buble-loader` (if present). For example, to enable Object spread operator: + + ``` js + vue: { + buble: { + objectAssign: 'Object.assign' + } + } + ``` From 70ca3ff64c1ae69bb4b93a142e937fccd2f06c89 Mon Sep 17 00:00:00 2001 From: Evan You Date: Thu, 13 Oct 2016 00:18:48 -0400 Subject: [PATCH 371/877] 9.7.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bbb5678a2..b2960264a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vue-loader", - "version": "9.6.0", + "version": "9.7.0", "description": "Vue.js component loader for Webpack", "main": "index.js", "repository": { From 123f4e2089cd91f7ed6b06f7875b05547d5c7e55 Mon Sep 17 00:00:00 2001 From: Evan You Date: Thu, 13 Oct 2016 04:54:44 -0400 Subject: [PATCH 372/877] use pug instead of jade --- docs/en/configurations/pre-processors.md | 6 +++--- package.json | 2 +- test/fixtures/pre.vue | 2 +- test/fixtures/{template-import.jade => template-import.pug} | 0 test/fixtures/template-import.vue | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) rename test/fixtures/{template-import.jade => template-import.pug} (100%) diff --git a/docs/en/configurations/pre-processors.md b/docs/en/configurations/pre-processors.md index 737577bc8..dcc60e12f 100644 --- a/docs/en/configurations/pre-processors.md +++ b/docs/en/configurations/pre-processors.md @@ -34,14 +34,14 @@ npm install coffee-loader --save-dev ### Templates -Processing templates is a little different, because most Webpack template loaders such as `jade-loader` return a template function instead of a compiled HTML string. Instead of using `jade-loader`, we can just install the original `jade`: +Processing templates is a little different, because most Webpack template loaders such as `pug-loader` return a template function instead of a compiled HTML string. Instead of using `pug-loader`, we can just install the original `pug`: ``` bash -npm install jade --save-dev +npm install pug --save-dev ``` ``` html -