Skip to content

Commit

Permalink
feat: support configuring loader for custom blocks via resourceQuery
Browse files Browse the repository at this point in the history
  • Loading branch information
yyx990803 committed Mar 21, 2018
1 parent 600ca0c commit d04f9cf
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 40 deletions.
51 changes: 35 additions & 16 deletions lib/pitch.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,41 +5,60 @@ 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))

if (query.vue == null) {
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})`
}
8 changes: 2 additions & 6 deletions lib/plugin.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
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
Expand Down Expand Up @@ -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
}
Expand Down
36 changes: 18 additions & 18 deletions test/custom.spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const {
bundle
bundle,
mockBundleAndRun
} = require('./utils')

test('add custom blocks to the webpack output', done => {
Expand Down Expand Up @@ -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({
Expand Down

0 comments on commit d04f9cf

Please sign in to comment.