Skip to content

Commit d04f9cf

Browse files
committed
feat: support configuring loader for custom blocks via resourceQuery
1 parent 600ca0c commit d04f9cf

File tree

3 files changed

+55
-40
lines changed

3 files changed

+55
-40
lines changed

Diff for: lib/pitch.js

+35-16
Original file line numberDiff line numberDiff line change
@@ -5,41 +5,60 @@ const stylePostLoaderPath = require.resolve('./style-post-loader')
55

66
module.exports = code => code
77

8-
// This pitching loader is responsible for catching all src import requests
9-
// from within vue files and transform it into appropriate requests
8+
// This pitching loader is responsible for intercepting all vue block requests
9+
// and transform it into appropriate requests.
1010
module.exports.pitch = function (remainingRequest) {
1111
const query = qs.parse(this.resourceQuery.slice(1))
1212

1313
if (query.vue == null) {
1414
return
1515
}
1616

17+
const loaders = this.loaders.slice(1) // remove self
18+
19+
// loader.request contains both the resolved loader path and its options
20+
// query (e.g. ??ref-0)
21+
const toLoaderString = loader => loader.request
22+
23+
const genRequest = loaderStrings => {
24+
// important: dedupe
25+
loaderStrings = Array.from(new Set(loaderStrings))
26+
return loaderUtils.stringifyRequest(this, '-!' + [
27+
...loaderStrings,
28+
this.resourcePath + this.resourceQuery
29+
].join('!'))
30+
}
31+
1732
// Inject style-post-loader before css-loader for scoped CSS and trimming
1833
if (query.type === `style`) {
19-
const cssLoaderIndex = this.loaders.findIndex(l => /\/css-loader/.test(l.request))
34+
const cssLoaderIndex = loaders.findIndex(l => /\/css-loader/.test(l.request))
2035
if (cssLoaderIndex) {
21-
const afterLoaders = this.loaders.slice(1, cssLoaderIndex + 1).map(l => l.request)
22-
const beforeLoaders = this.loaders.slice(cssLoaderIndex + 1).map(l => l.request)
23-
const request = '-!' + [
36+
const afterLoaders = loaders.slice(0, cssLoaderIndex + 1).map(toLoaderString)
37+
const beforeLoaders = loaders.slice(cssLoaderIndex + 1).map(toLoaderString)
38+
const request = genRequest([
2439
...afterLoaders,
2540
stylePostLoaderPath,
26-
...beforeLoaders,
27-
this.resourcePath + this.resourceQuery
28-
].join('!')
41+
...beforeLoaders
42+
])
2943
// use cjs to ensure exports from (vue-)style-loader/css-loader are intact
30-
return `module.exports = require(${loaderUtils.stringifyRequest(this, request)})`
44+
return `module.exports = require(${request})`
3145
}
3246
}
3347

3448
// for templates: inject the template compiler
3549
if (query.type === `template`) {
36-
const beforeLoaders = this.loaders.slice(1).map(l => l.request)
37-
const request = '-!' + [
50+
const beforeLoaders = loaders.map(toLoaderString)
51+
const request = genRequest([
3852
templateLoaderPath + `??vue-loader-options`,
39-
...beforeLoaders,
40-
this.resourcePath + this.resourceQuery
41-
].join('!')
53+
...beforeLoaders
54+
])
4255
// the template compiler uses esm exports
43-
return `export * from ${loaderUtils.stringifyRequest(this, request)}`
56+
return `export * from ${request}`
4457
}
58+
59+
// When the user defines a rule that has only resourceQuery but no test,
60+
// both that rule and the cloned rule will match, resulting in duplicated
61+
// loaders. Therefore it is necessary to perform a dedupe here.
62+
const dedupedRequest = genRequest(loaders.map(toLoaderString))
63+
return `module.exports = require(${dedupedRequest})`
4564
}

Diff for: lib/plugin.js

+2-6
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
const qs = require('querystring')
22
const RuleSet = require('webpack/lib/RuleSet')
33

4-
// TODO handling rules without `test` being matched twice
5-
// e.g. a rule with just resourceQuery: /blockType=foo/
6-
74
// TODO handle vueRule with oneOf
8-
95
module.exports = class VueLoaderPlugin {
106
apply (compiler) {
117
// get a hold of the raw rules
@@ -114,10 +110,10 @@ function cloneRule (rule, normalizedRule, vueUse) {
114110
},
115111
resourceQuery: query => {
116112
const parsed = qs.parse(query.slice(1))
117-
if (parsed.lang == null) {
113+
const { resource, resourceQuery } = normalizedRule
114+
if (resource && parsed.lang == null) {
118115
return false
119116
}
120-
const { resource, resourceQuery } = normalizedRule
121117
if (resource && !resource(`${currentResource}.${parsed.lang}`)) {
122118
return false
123119
}

Diff for: test/custom.spec.js

+18-18
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const {
2-
bundle
2+
bundle,
3+
mockBundleAndRun
34
} = require('./utils')
45

56
test('add custom blocks to the webpack output', done => {
@@ -43,23 +44,22 @@ describe('example', function () {
4344
})
4445
})
4546

46-
// TODO
47-
// test('passes Component to custom block loaders', done => {
48-
// mockBundleAndRun({
49-
// entry: 'custom-language.vue',
50-
// module: {
51-
// rules: [
52-
// {
53-
// resourceQuery: /blockType=documentation/,
54-
// loader: require.resolve('./mock-loaders/docs')
55-
// }
56-
// ]
57-
// }
58-
// }, ({ module }) => {
59-
// expect(module.__docs).toContain('This is example documentation for a component.')
60-
// done()
61-
// })
62-
// })
47+
test('passes Component to custom block loaders', done => {
48+
mockBundleAndRun({
49+
entry: 'custom-language.vue',
50+
module: {
51+
rules: [
52+
{
53+
resourceQuery: /blockType=documentation/,
54+
loader: require.resolve('./mock-loaders/docs')
55+
}
56+
]
57+
}
58+
}, ({ module }) => {
59+
expect(module.__docs).toContain('This is example documentation for a component.')
60+
done()
61+
})
62+
})
6363

6464
test('custom blocks can be ignored', done => {
6565
bundle({

0 commit comments

Comments
 (0)