├── .gitignore
├── LICENSE
├── README.md
├── index.js
├── package.json
├── parser.js
└── selector.js
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /npm-debug.log
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Q42
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ### This is now the official Vue loader, the latest version can be found at https://github.com/vuejs/vue-loader
2 |
3 | # vue-multi-loader
4 |
5 | > Vue.js component loader for [Webpack](http://webpack.github.io), using Webpack loaders for the parts.
6 |
7 | It allows you to write your components in this format:
8 |
9 | ``` html
10 | // app.vue
11 |
16 |
17 |
18 | {{msg}}
19 |
20 |
21 |
30 | ```
31 |
32 | You can also mix preprocessor languages in the component file:
33 |
34 | ``` html
35 | // app.vue
36 |
40 |
41 |
42 | h1(class="red") {{msg}}
43 |
44 |
45 |
50 | ```
51 |
52 | And you can import using the `src` attribute (note that there's no need for a `lang` attribute here, as Webpack will
53 | be used to determine which loader applies):
54 |
55 | ``` html
56 |
57 | ```
58 |
59 | ## Usage
60 |
61 | Config Webpack:
62 |
63 | ``` js
64 | // webpack.config.js
65 | module.exports = {
66 | entry: "./main.js",
67 | output: {
68 | filename: "build.js"
69 | },
70 | module: {
71 | loaders: [
72 | { test: /\.vue$/, loader: "vue-multi-loader" },
73 | ]
74 | }
75 | }
76 | ```
77 |
78 | And this is all you need to do in your main entry file:
79 |
80 | ``` js
81 | // main.js
82 | var Vue = require('vue')
83 | var appOptions = require('./app.vue')
84 | var app = new Vue(appOptions).$mount('#app')
85 | ```
86 |
87 | ## Loader configuration
88 |
89 | By default, `vue-multi-loader` will try to use the loader with the same name as
90 | the `lang` attribute, but you can configure which loader should be used.
91 |
92 | For example, to extract out the generated css into a separate file,
93 | use this configuration:
94 |
95 | ``` js
96 | // webpack.config.js
97 | var ExtractTextPlugin = require("extract-text-webpack-plugin");
98 | var vue = require("vue-multi-loader");
99 |
100 | module.exports = {
101 | entry: "./main.js",
102 | output: {
103 | filename: "build.js"
104 | },
105 | module: {
106 | loaders: [
107 | {
108 | test: /\.vue$/, loader: vue.withLoaders({
109 | css: ExtractTextPlugin.extract("css"),
110 | stylus: ExtractTextPlugin.extract("css!stylus")
111 | })
112 | },
113 | ]
114 | },
115 | plugins: [
116 | new ExtractTextPlugin("[name].css")
117 | ]
118 | }
119 | ```
120 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | var loaderUtils = require("loader-utils");
2 |
3 | module.exports = function (content) {
4 | this.cacheable();
5 | var cb = this.async();
6 | var languages = {};
7 | var output = '';
8 | var vueUrl = loaderUtils.getRemainingRequest(this);
9 | var loaders = loaderUtils.parseQuery(this.query);
10 | loaders.html = loaders.html || 'html';
11 | loaders.css = loaders.css || 'style!css';
12 | loaders.js = loaders.js || '';
13 | var loaderPrefix = {
14 | template: 'html!template-html-loader?raw&engine=',
15 | style: 'style!css!',
16 | script: ''
17 | };
18 | var defaultLang = {
19 | template: 'html',
20 | style: 'css',
21 | script: 'js'
22 | };
23 |
24 | function loader(part, lang) {
25 | lang = lang || defaultLang[part];
26 | var loader = loaders[lang] !== undefined ? loaders[lang] : loaderPrefix[part] + lang;
27 | return loader ? loader + '!' : '';
28 | }
29 |
30 | var me = this;
31 | function getRequire(part, lang) {
32 | return 'require(' + loaderUtils.stringifyRequest(me, '-!' + loader(part, lang) + require.resolve('./selector.js') + '?' + part + '/' + lang + '!' + vueUrl) + ')';
33 | }
34 |
35 | var me = this;
36 | var url = "!!" + require.resolve("./parser.js") + "!" + vueUrl;
37 | this.loadModule(url, function(err, source, map, module) {
38 | if (err) return cb(err);
39 |
40 | var parts = me.exec(source, url);
41 |
42 | for (var i = 0; i < parts.includes.length; i++)
43 | output += 'require(' + loaderUtils.stringifyRequest(this, loaderUtils.urlToRequest(parts.includes[i])) + ')\n';
44 |
45 | for (var lang in parts.style)
46 | output += getRequire('style', lang) + '\n';
47 |
48 | for (var lang in parts.script)
49 | output += 'module.exports = ' + getRequire('script', lang) + '\n';
50 |
51 | var hasTemplate = false;
52 | for (var lang in parts.template) {
53 | if (hasTemplate)
54 | return cb(new Error('Only one template element allowed per vue component!'));
55 | output += 'module.exports.template = ' + getRequire('template', lang);
56 | hasTemplate = true;
57 | }
58 |
59 | cb(null, output);
60 | })
61 | }
62 |
63 | module.exports.withLoaders = function (opts) {
64 | return 'vue-multi-loader?' + JSON.stringify(opts).replace(/!/g, '\\u0021')
65 | }
66 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-multi-loader",
3 | "version": "0.0.5",
4 | "description": "Vue.js component loader for Webpack, using Webpack loaders for the parts",
5 | "main": "index.js",
6 | "repository": {
7 | "type": "git",
8 | "url": "https://github.com/Q42/vue-multi-loader.git"
9 | },
10 | "keywords": [
11 | "vue",
12 | "webpack",
13 | "loader"
14 | ],
15 | "author": "Sjoerd Visscher",
16 | "license": "ISC",
17 | "bugs": {
18 | "url": "https://github.com/Q42/vue-multi-loader/issues"
19 | },
20 | "homepage": "https://github.com/Q42/vue-multi-loader",
21 | "dependencies": {
22 | "loader-utils": "^0.2.7",
23 | "parse5": "^1.1.4",
24 | "source-map": "^0.4.2"
25 | },
26 | "peerDependencies": {
27 | "css-loader": "^0.14.4",
28 | "html-loader": "^0.3.0",
29 | "style-loader": "^0.12.3"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/parser.js:
--------------------------------------------------------------------------------
1 | var parse5 = require('parse5');
2 | var parser = new parse5.Parser(parse5.TreeAdapters.htmlparser2, { locationInfo: true });
3 | var serializer = new parse5.TreeSerializer();
4 | var SourceNode = require("source-map").SourceNode;
5 | var loaderUtils = require("loader-utils");
6 |
7 | module.exports = function (content) {
8 | this.cacheable();
9 | var cb = this.async();
10 | var vueRequest = loaderUtils.getRemainingRequest(this);
11 | var request = loaderUtils.getCurrentRequest(this);
12 |
13 | var languages = {};
14 | var output = {
15 | template: {},
16 | style: {},
17 | script: {},
18 | includes: []
19 | };
20 |
21 | function pos(offset) {
22 | return {
23 | line: content.substr(0, offset).split('\n').length,
24 | col: offset - content.lastIndexOf('\n', offset - 1)
25 | }
26 | }
27 |
28 | var fragment = parser.parseFragment(content);
29 | fragment.children.forEach(function (node) {
30 | if (node.attribs && node.attribs.src) {
31 | output.includes.push(node.attribs.src)
32 | return;
33 | }
34 |
35 | if (!node.children || !node.children.length)
36 | return;
37 |
38 | var lang = (node.attribs && node.attribs.lang) || '';
39 | var type = node.name;
40 | if (!output[type])
41 | return;
42 |
43 | // Work around changes in parse5 >= 1.2.0
44 | if (node.children[0].type === 'root')
45 | node = node.children[0];
46 |
47 | var start = node.children[0].__location.start;
48 | var end = node.children[node.children.length - 1].__location.end;
49 | var lines = content.substring(start, end).split('\n');
50 | var startPos = pos(start);
51 | var sourceNodes = lines.map(function (line, i) {
52 | return new SourceNode(startPos.line + i, i ? 0 : startPos.col, vueRequest, line + '\n');
53 | });
54 | output[type][lang] = (output[type][lang] || []).concat(sourceNodes)
55 | });
56 |
57 | for (var type in output) {
58 | for (var lang in output[type]) {
59 | var sourceNodes = output[type][lang];
60 | output[type][lang] = new SourceNode(1, 1, vueRequest, sourceNodes).toStringWithSourceMap({
61 | file: request
62 | })
63 | }
64 | }
65 |
66 | cb(null, 'module.exports = ' + JSON.stringify(output));
67 | }
68 |
--------------------------------------------------------------------------------
/selector.js:
--------------------------------------------------------------------------------
1 | module.exports = function () {
2 | this.cacheable();
3 | var cb = this.async();
4 | var path = this.query.substr(1).split('/');
5 |
6 | var me = this;
7 | var url = "!!" + require.resolve("./parser.js") + "!" + this.resource;
8 | this.loadModule(url, function(err, source) {
9 | if (err) return cb(err);
10 | var parts = me.exec(source, url);
11 | var part = parts[path[0]][path[1]||''];
12 | cb(null, part.code, part.map);
13 | })
14 | }
15 |
--------------------------------------------------------------------------------