From fdfe5d9c858d6f2ca826a6053c6d76131360ec5b Mon Sep 17 00:00:00 2001 From: HaoyCn Date: Thu, 18 Jan 2018 12:49:05 +0800 Subject: [PATCH 1/2] Support multiple rule definitions --- lib/loader.js | 18 +++++++- lib/style-compiler/index.js | 4 +- lib/template-compiler/index.js | 4 +- lib/template-compiler/preprocessor.js | 7 ++- test/fixtures/multiple-rules-1.vue | 9 ++++ test/fixtures/multiple-rules-2.vue | 9 ++++ test/fixtures/multiple-rules.js | 4 ++ test/test.js | 65 +++++++++++++++++++++++++++ 8 files changed, 115 insertions(+), 5 deletions(-) create mode 100644 test/fixtures/multiple-rules-1.vue create mode 100644 test/fixtures/multiple-rules-2.vue create mode 100644 test/fixtures/multiple-rules.js diff --git a/lib/loader.js b/lib/loader.js index 75ef7a6ff..63218b3f7 100644 --- a/lib/loader.js +++ b/lib/loader.js @@ -69,6 +69,18 @@ module.exports = function (content) { options.esModule = false } + let vueOptionId = 0 + if (this.options.__vueOptions__ && this.options.__vueOptions__ !== options) { + if (!this.options.__vueOptionsList__) { + Object.defineProperty(this.options, '__vueOptionsList__', { + value: [this.options.__vueOptions__], + enumerable: false, + configurable: true + }) + } + vueOptionId = this.options.__vueOptionsList__.push(options) - 1 + } + // #824 avoid multiple webpack runs complaining about unknown option Object.defineProperty(this.options, '__vueOptions__', { value: options, @@ -134,6 +146,7 @@ module.exports = function (content) { transformToRequire: options.transformToRequire, preserveWhitespace: options.preserveWhitespace, buble: bubleTemplateOptions, + optionId: vueOptionId, // only pass compilerModules if it's a path string compilerModules: typeof options.compilerModules === 'string' @@ -585,7 +598,8 @@ module.exports = function (content) { vue: true, id: moduleId, scoped: !!scoped, - hasInlineConfig: !!query.postcss + hasInlineConfig: !!query.postcss, + optionId: vueOptionId }) + '!' // normalize scss/sass/postcss if no specific loaders have been provided @@ -642,6 +656,8 @@ module.exports = function (content) { templatePreprocessorPath + '?engine=' + lang + + '&optionId=' + + vueOptionId + '!' ) case 'styles': diff --git a/lib/style-compiler/index.js b/lib/style-compiler/index.js index 42dae638f..bf6e6e48b 100644 --- a/lib/style-compiler/index.js +++ b/lib/style-compiler/index.js @@ -10,7 +10,9 @@ module.exports = function (css, map) { const cb = this.async() const query = loaderUtils.getOptions(this) || {} - let vueOptions = this.options.__vueOptions__ + let vueOptions = this.options.__vueOptionsList__ && this.options.__vueOptionsList__[query.optionId] + ? this.options.__vueOptionsList__[query.optionId] + : this.options.__vueOptions__ if (!vueOptions) { if (query.hasInlineConfig) { diff --git a/lib/template-compiler/index.js b/lib/template-compiler/index.js index 95d8ff00b..9e0434191 100644 --- a/lib/template-compiler/index.js +++ b/lib/template-compiler/index.js @@ -11,8 +11,10 @@ module.exports = function (html) { this.cacheable() const isServer = this.target === 'node' const isProduction = this.minimize || process.env.NODE_ENV === 'production' - const vueOptions = this.options.__vueOptions__ || {} const options = loaderUtils.getOptions(this) || {} + const vueOptions = this.options.__vueOptionsList__ && this.options.__vueOptionsList__[options.optionId] + ? this.options.__vueOptionsList__[options.optionId] + : (this.options.__vueOptions__ || {}) const needsHotReload = !isServer && !isProduction && vueOptions.hotReload !== false const defaultModules = [transformRequire(options.transformToRequire), transformSrcset()] let userModules = vueOptions.compilerModules || options.compilerModules diff --git a/lib/template-compiler/preprocessor.js b/lib/template-compiler/preprocessor.js index abdd7d093..f0b4bcf3f 100644 --- a/lib/template-compiler/preprocessor.js +++ b/lib/template-compiler/preprocessor.js @@ -19,9 +19,12 @@ module.exports = function (content) { ) } + const vueOptions = this.options.__vueOptionsList__ && this.options.__vueOptionsList__[opt.optionId] + ? this.options.__vueOptionsList__[opt.optionId] + : this.options.__vueOptions__ // allow passing options to the template preprocessor via `template` option - if (this.options.__vueOptions__) { - Object.assign(opt, this.options.__vueOptions__.template) + if (vueOptions) { + Object.assign(opt, vueOptions.template) } // for relative includes diff --git a/test/fixtures/multiple-rules-1.vue b/test/fixtures/multiple-rules-1.vue new file mode 100644 index 000000000..858b3eb1e --- /dev/null +++ b/test/fixtures/multiple-rules-1.vue @@ -0,0 +1,9 @@ + + + diff --git a/test/fixtures/multiple-rules-2.vue b/test/fixtures/multiple-rules-2.vue new file mode 100644 index 000000000..97d226939 --- /dev/null +++ b/test/fixtures/multiple-rules-2.vue @@ -0,0 +1,9 @@ + + + diff --git a/test/fixtures/multiple-rules.js b/test/fixtures/multiple-rules.js new file mode 100644 index 000000000..fa741e723 --- /dev/null +++ b/test/fixtures/multiple-rules.js @@ -0,0 +1,4 @@ +import Rule1 from './multiple-rules-1.vue' +import Rule2 from './multiple-rules-2.vue' + +window.rules = [Rule1, Rule2] diff --git a/test/test.js b/test/test.js index e87d7bc82..bcbcd2e43 100644 --- a/test/test.js +++ b/test/test.js @@ -1088,4 +1088,69 @@ describe('vue-loader', () => { done() }) }) + + it('multiple rule definitions', done => { + test({ + entry: './test/fixtures/multiple-rules.js', + module: { + rules: [ + { + test: /\.vue$/, + oneOf: [ + { + include: /-1\.vue$/, + loader: rawLoaderPath, + options: { + postcss: [ + css => { + css.walkDecls('font-size', decl => { + decl.value = `${parseInt(decl.value, 10) * 2}px` + }) + } + ], + compilerModules: [{ + postTransformNode: el => { + el.staticClass = '"multiple-rule-1"' + } + }] + } + }, + { + include: /-2\.vue$/, + loader: rawLoaderPath, + options: { + postcss: [ + css => { + css.walkDecls('font-size', decl => { + decl.value = `${parseInt(decl.value, 10) / 2}px` + }) + } + ], + compilerModules: [{ + postTransformNode: el => { + el.staticClass = '"multiple-rule-2"' + } + }] + } + } + ] + } + ] + } + }, (window, module) => { + const vnode1 = mockRender(window.rules[0]) + const vnode2 = mockRender(window.rules[1]) + expect(vnode1.data.staticClass).to.equal('multiple-rule-1') + expect(vnode2.data.staticClass).to.equal('multiple-rule-2') + const styles = window.document.querySelectorAll('style') + const expr = /\.multiple-rule-\d\s*\{\s*font-size:\s*([.0-9]+)px;/ + for (let i = 0, l = styles.length; i < l; i++) { + const content = styles[i].textContent + if (expr.test(content)) { + expect(parseFloat(RegExp.$1)).to.be.equal(14) + } + } + done() + }) + }) }) From 588cc01ad510259a8762f1b6bc9e643dd1d63be9 Mon Sep 17 00:00:00 2001 From: HaoyCn Date: Thu, 18 Jan 2018 13:26:20 +0800 Subject: [PATCH 2/2] Do not push duplicate options to list --- lib/loader.js | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/lib/loader.js b/lib/loader.js index 63218b3f7..99f1bc296 100644 --- a/lib/loader.js +++ b/lib/loader.js @@ -69,16 +69,21 @@ module.exports = function (content) { options.esModule = false } - let vueOptionId = 0 - if (this.options.__vueOptions__ && this.options.__vueOptions__ !== options) { - if (!this.options.__vueOptionsList__) { - Object.defineProperty(this.options, '__vueOptionsList__', { - value: [this.options.__vueOptions__], - enumerable: false, - configurable: true - }) - } - vueOptionId = this.options.__vueOptionsList__.push(options) - 1 + if (!this.options.__vueOptionsList__) { + const list = [] + list.queryList = [] + Object.defineProperty(this.options, '__vueOptionsList__', { + value: list, + enumerable: false, + configurable: true + }) + } + const vueOptionList = this.options.__vueOptionsList__ + const vueQueryList = vueOptionList.queryList + let vueOptionId = vueQueryList.indexOf(query) + if (vueOptionId < 0) { + vueQueryList.push(query) + vueOptionId = vueOptionList.push(options) - 1 } // #824 avoid multiple webpack runs complaining about unknown option