Commit d04f9cf1 authored by Evan You's avatar Evan You

feat: support configuring loader for custom blocks via resourceQuery

parent 600ca0cb
......@@ -5,8 +5,8 @@ const stylePostLoaderPath = require.resolve('./style-post-loader')
module.exports = code => code
// This pitching loader is responsible for catching all src import requests
// from within vue files and transform it into appropriate requests
// This pitching loader is responsible for intercepting all vue block requests
// and transform it into appropriate requests.
module.exports.pitch = function (remainingRequest) {
const query = qs.parse(this.resourceQuery.slice(1))
......@@ -14,32 +14,51 @@ module.exports.pitch = function (remainingRequest) {
return
}
const loaders = this.loaders.slice(1) // remove self
// loader.request contains both the resolved loader path and its options
// query (e.g. ??ref-0)
const toLoaderString = loader => loader.request
const genRequest = loaderStrings => {
// important: dedupe
loaderStrings = Array.from(new Set(loaderStrings))
return loaderUtils.stringifyRequest(this, '-!' + [
...loaderStrings,
this.resourcePath + this.resourceQuery
].join('!'))
}
// Inject style-post-loader before css-loader for scoped CSS and trimming
if (query.type === `style`) {
const cssLoaderIndex = this.loaders.findIndex(l => /\/css-loader/.test(l.request))
const cssLoaderIndex = loaders.findIndex(l => /\/css-loader/.test(l.request))
if (cssLoaderIndex) {
const afterLoaders = this.loaders.slice(1, cssLoaderIndex + 1).map(l => l.request)
const beforeLoaders = this.loaders.slice(cssLoaderIndex + 1).map(l => l.request)
const request = '-!' + [
const afterLoaders = loaders.slice(0, cssLoaderIndex + 1).map(toLoaderString)
const beforeLoaders = loaders.slice(cssLoaderIndex + 1).map(toLoaderString)
const request = genRequest([
...afterLoaders,
stylePostLoaderPath,
...beforeLoaders,
this.resourcePath + this.resourceQuery
].join('!')
...beforeLoaders
])
// use cjs to ensure exports from (vue-)style-loader/css-loader are intact
return `module.exports = require(${loaderUtils.stringifyRequest(this, request)})`
return `module.exports = require(${request})`
}
}
// for templates: inject the template compiler
if (query.type === `template`) {
const beforeLoaders = this.loaders.slice(1).map(l => l.request)
const request = '-!' + [
const beforeLoaders = loaders.map(toLoaderString)
const request = genRequest([
templateLoaderPath + `??vue-loader-options`,
...beforeLoaders,
this.resourcePath + this.resourceQuery
].join('!')
...beforeLoaders
])
// the template compiler uses esm exports
return `export * from ${loaderUtils.stringifyRequest(this, request)}`
return `export * from ${request}`
}
// When the user defines a rule that has only resourceQuery but no test,
// both that rule and the cloned rule will match, resulting in duplicated
// loaders. Therefore it is necessary to perform a dedupe here.
const dedupedRequest = genRequest(loaders.map(toLoaderString))
return `module.exports = require(${dedupedRequest})`
}
const qs = require('querystring')
const RuleSet = require('webpack/lib/RuleSet')
// TODO handling rules without `test` being matched twice
// e.g. a rule with just resourceQuery: /blockType=foo/
// TODO handle vueRule with oneOf
module.exports = class VueLoaderPlugin {
apply (compiler) {
// get a hold of the raw rules
......@@ -114,10 +110,10 @@ function cloneRule (rule, normalizedRule, vueUse) {
},
resourceQuery: query => {
const parsed = qs.parse(query.slice(1))
if (parsed.lang == null) {
const { resource, resourceQuery } = normalizedRule
if (resource && parsed.lang == null) {
return false
}
const { resource, resourceQuery } = normalizedRule
if (resource && !resource(`${currentResource}.${parsed.lang}`)) {
return false
}
......
const {
bundle
bundle,
mockBundleAndRun
} = require('./utils')
test('add custom blocks to the webpack output', done => {
......@@ -43,23 +44,22 @@ describe('example', function () {
})
})
// TODO
// test('passes Component to custom block loaders', done => {
// mockBundleAndRun({
// entry: 'custom-language.vue',
// module: {
// rules: [
// {
// resourceQuery: /blockType=documentation/,
// loader: require.resolve('./mock-loaders/docs')
// }
// ]
// }
// }, ({ module }) => {
// expect(module.__docs).toContain('This is example documentation for a component.')
// done()
// })
// })
test('passes Component to custom block loaders', done => {
mockBundleAndRun({
entry: 'custom-language.vue',
module: {
rules: [
{
resourceQuery: /blockType=documentation/,
loader: require.resolve('./mock-loaders/docs')
}
]
}
}, ({ module }) => {
expect(module.__docs).toContain('This is example documentation for a component.')
done()
})
})
test('custom blocks can be ignored', done => {
bundle({
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment