Skip to content

Commit 7208885

Browse files
committed
fix: support test-less oneOf rules
1 parent a37d5ba commit 7208885

File tree

4 files changed

+72
-51
lines changed

4 files changed

+72
-51
lines changed

Diff for: lib/loaders/noop.js

-5
This file was deleted.

Diff for: lib/loaders/pitch.js

+14-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const qs = require('querystring')
22
const loaderUtils = require('loader-utils')
3+
const selfPath = require.resolve('vue-loader')
34
const templateLoaderPath = require.resolve('./templateLoader')
45
const stylePostLoaderPath = require.resolve('./stylePostLoader')
56

@@ -31,7 +32,7 @@ module.exports.pitch = function (remainingRequest) {
3132

3233
// Inject style-post-loader before css-loader for scoped CSS and trimming
3334
if (query.type === `style`) {
34-
const cssLoaderIndex = loaders.findIndex(l => /(\/|\\)css-loader/.test(l.request))
35+
const cssLoaderIndex = loaders.findIndex(l => /(\/|\\)css-loader/.test(l.path))
3536
if (cssLoaderIndex) {
3637
const afterLoaders = loaders.slice(0, cssLoaderIndex + 1).map(toLoaderString)
3738
const beforeLoaders = loaders.slice(cssLoaderIndex + 1).map(toLoaderString)
@@ -40,6 +41,7 @@ module.exports.pitch = function (remainingRequest) {
4041
stylePostLoaderPath,
4142
...beforeLoaders
4243
])
44+
// console.log(request)
4345
// use cjs to ensure exports from (vue-)style-loader/css-loader are intact
4446
return `module.exports = require(${request})`
4547
}
@@ -52,13 +54,22 @@ module.exports.pitch = function (remainingRequest) {
5254
templateLoaderPath + `??vue-loader-options`,
5355
...beforeLoaders
5456
])
57+
// console.log(request)
5558
// the template compiler uses esm exports
5659
return `export * from ${request}`
5760
}
5861

62+
// if a custom block has no other matching loader other than vue-loader itself,
63+
// we should ignore it
64+
if (query.type === `custom` &&
65+
loaders.length === 1 &&
66+
loaders[0].path === selfPath) {
67+
return ``
68+
}
69+
5970
// When the user defines a rule that has only resourceQuery but no test,
6071
// both that rule and the cloned rule will match, resulting in duplicated
6172
// loaders. Therefore it is necessary to perform a dedupe here.
62-
const dedupedRequest = genRequest(loaders.map(toLoaderString))
63-
return `module.exports = require(${dedupedRequest})`
73+
const request = genRequest(loaders.map(toLoaderString))
74+
return `module.exports = require(${request})`
6475
}

Diff for: lib/plugin.js

+19-28
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,20 @@ module.exports = class VueLoaderPlugin {
1515
const clone = Object.assign({}, rule)
1616
delete clone.include
1717
const normalized = RuleSet.normalizeRule(clone)
18-
return !rule.enforce && normalized.resource(`foo.vue`)
18+
return !rule.enforce && normalized.resource && normalized.resource(`foo.vue`)
1919
})
2020
const vueRule = rawRules[vueRuleIndex]
2121

2222
if (!vueRule) {
2323
throw new Error(
24-
`VueLoaderPlugin Error: no matching rule for vue files are found.`
24+
`[VueLoaderPlugin Error] No matching rule for .vue files found.\n` +
25+
`Make sure there is at least one root-level rule that matches .vue files.`
2526
)
2627
}
2728

2829
if (vueRule.oneOf) {
2930
throw new Error(
30-
`vue-loader 15 currently does not support vue rules with oneOf.`
31+
`[VueLoaderPlugin Error] vue-loader 15 currently does not support vue rules with oneOf.`
3132
)
3233
}
3334

@@ -42,7 +43,8 @@ module.exports = class VueLoaderPlugin {
4243

4344
if (vueLoaderUseIndex < 0) {
4445
throw new Error(
45-
`VueLoaderPlugin Error: no matching use for vue-loader is found.`
46+
`[VueLoaderPlugin Error] No matching use for vue-loader is found.\n` +
47+
`Make sure the rule matching .vue files include vue-loader in its use.`
4648
)
4749
}
4850

@@ -78,31 +80,19 @@ module.exports = class VueLoaderPlugin {
7880
const baseRules = rawRules.filter(r => r !== vueRule)
7981
const normalizedRules = rawNormalizedRules.filter(r => r !== normalizedVueRule)
8082

81-
const customFallbackRule = {
82-
loader: require.resolve('./loaders/noop'),
83-
resourceQuery: /type=custom/
84-
}
85-
86-
// construct a new rule for vue file, with oneOf containing
87-
// multiple rules with dynamic resourceQuery functions that applies to
88-
// different language blocks in a raw vue file.
89-
const constructedRule = {
90-
test: /\.vue$/,
91-
oneOf: baseRules.map((rule, i) => {
92-
// for each user rule, create a cloned rule by checking if the rule
93-
// matches the lang specified in the resourceQuery.
94-
return cloneRule(rule, normalizedRules[i], normalizedVueUse)
95-
}).concat(customFallbackRule, vueRule)
96-
}
97-
98-
// replace the original vue rule with our new constructed rule.
99-
rawRules.splice(vueRuleIndex, 1, constructedRule)
83+
// for each user rule, inject a cloned rule by checking if the rule
84+
// matches the lang specified in the resourceQuery.
85+
rawRules.unshift.apply(rawRules, baseRules.map((rule, i) => {
86+
return cloneRule(rule, normalizedRules[i], normalizedVueUse)
87+
}))
10088

10189
// inject global pitcher (responsible for injecting template compiler
10290
// loader & CSS post loader)
10391
rawRules.unshift({
10492
loader: require.resolve('./loaders/pitch')
10593
})
94+
95+
// console.log(rawRules)
10696
}
10797
}
10898

@@ -116,7 +106,7 @@ function cloneRule (rule, normalizedRule, vueUse) {
116106
const res = Object.assign({}, rule, {
117107
test: resource => {
118108
currentResource = resource
119-
return true
109+
return /\.vue$/.test(resource)
120110
},
121111
resourceQuery: query => {
122112
const parsed = qs.parse(query.slice(1))
@@ -133,12 +123,13 @@ function cloneRule (rule, normalizedRule, vueUse) {
133123
}
134124
return true
135125
},
136-
use: [
137-
...(normalizedRule.use || []).map(cleanUse),
138-
...rule.oneOf ? [] : vueUse
139-
]
126+
use: (normalizedRule.use || []).map(cleanUse)
140127
})
141128

129+
if (!res.use.length) {
130+
delete res.use
131+
}
132+
142133
// delete shorthand since we have normalized use
143134
delete res.loader
144135
delete res.options

Diff for: test/edgeCases.spec.js

+39-15
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,23 @@ const {
55

66
const normalizeNewline = require('normalize-newline')
77

8+
const assertComponent = ({ window, module }, done) => {
9+
const vnode = mockRender(module, {
10+
msg: 'hi'
11+
})
12+
13+
// <h2 class="red">{{msg}}</h2>
14+
expect(vnode.tag).toBe('h2')
15+
expect(vnode.data.staticClass).toBe('red')
16+
expect(vnode.children[0].text).toBe('hi')
17+
18+
expect(module.data().msg).toContain('Hello from Component A!')
19+
let style = window.document.querySelector('style').textContent
20+
style = normalizeNewline(style)
21+
expect(style).toContain('comp-a h2 {\n color: #f00;\n}')
22+
done()
23+
}
24+
825
test('vue rule with include', done => {
926
mockBundleAndRun({
1027
entry: 'basic.vue',
@@ -15,20 +32,27 @@ test('vue rule with include', done => {
1532
loader: 'vue-loader'
1633
}
1734
}
18-
}, ({ window, module, rawModule }) => {
19-
const vnode = mockRender(module, {
20-
msg: 'hi'
21-
})
22-
23-
// <h2 class="red">{{msg}}</h2>
24-
expect(vnode.tag).toBe('h2')
25-
expect(vnode.data.staticClass).toBe('red')
26-
expect(vnode.children[0].text).toBe('hi')
35+
}, res => assertComponent(res, done))
36+
})
2737

28-
expect(module.data().msg).toContain('Hello from Component A!')
29-
let style = window.document.querySelector('style').textContent
30-
style = normalizeNewline(style)
31-
expect(style).toContain('comp-a h2 {\n color: #f00;\n}')
32-
done()
33-
})
38+
test('test-less oneOf rules', done => {
39+
mockBundleAndRun({
40+
entry: 'basic.vue',
41+
modify: config => {
42+
config.module.rules = [
43+
{
44+
test: /\.vue$/,
45+
loader: 'vue-loader'
46+
},
47+
{
48+
oneOf: [
49+
{
50+
test: /\.css$/,
51+
use: ['vue-style-loader', 'css-loader']
52+
}
53+
]
54+
}
55+
]
56+
}
57+
}, res => assertComponent(res, done))
3458
})

0 commit comments

Comments
 (0)