Skip to content

Commit

Permalink
fix: loaders for styles are not deduplicated when experiments.css is …
Browse files Browse the repository at this point in the history
…true
  • Loading branch information
xc2 committed Apr 15, 2024
1 parent 96a7493 commit 3ed63fa
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 20 deletions.
42 changes: 23 additions & 19 deletions lib/loaders/pitcher.js
Expand Up @@ -80,30 +80,34 @@ module.exports.pitch = function (remainingRequest) {
return
}

// Important: dedupe since both the original rule
// and the cloned rule would match a source import request.
// also make sure to dedupe based on loader path.
// assumes you'd probably never want to apply the same loader on the same
// file twice.
// Exception: in Vue CLI we do need two instances of postcss-loader
// for user config and inline minification. So we need to dedupe baesd on
// path AND query to be safe.
const loadersSeen = new Set()
loaders = loaders.filter((loader) => {
const identifier =
typeof loader === 'string' ? loader : loader.path + loader.query
if (!loadersSeen.has(identifier)) {
loadersSeen.add(identifier)
return true
}
return false
})

const genRequest = (loaders, lang) => {
// Important: dedupe since both the original rule
// and the cloned rule would match a source import request.
// also make sure to dedupe based on loader path.
// assumes you'd probably never want to apply the same loader on the same
// file twice.
// Exception: in Vue CLI we do need two instances of postcss-loader
// for user config and inline minification. So we need to dedupe baesd on
// path AND query to be safe.
const seen = new Map()
const loaderStrings = []
const enableInlineMatchResource =
isWebpack5 && options.experimentalInlineMatchResource

loaders.forEach((loader) => {
const identifier =
typeof loader === 'string' ? loader : loader.path + loader.query
const loaderStrings = loaders.map((loader) => {
const request = typeof loader === 'string' ? loader : loader.request
if (!seen.has(identifier)) {
seen.set(identifier, true)
// loader.request contains both the resolved loader path and its options
// query (e.g. ??ref-0)
loaderStrings.push(request)
}
// loader.request contains both the resolved loader path and its options
// query (e.g. ??ref-0)
return request
})
if (enableInlineMatchResource) {
return loaderUtils.stringifyRequest(
Expand Down
3 changes: 3 additions & 0 deletions package.json
Expand Up @@ -50,6 +50,9 @@
},
"prettier": {
"optional": true
},
"webpack": {
"optional": true
}
},
"dependencies": {
Expand Down
47 changes: 46 additions & 1 deletion test/style.spec.js
Expand Up @@ -3,7 +3,8 @@ const {
genId,
mockRender,
mockBundleAndRun,
DEFAULT_VUE_USE
DEFAULT_VUE_USE,
bundle
} = require('./utils')

test('scoped style', done => {
Expand Down Expand Up @@ -224,3 +225,47 @@ test('CSS Modules Extend', async () => {
})
})
})

const webpack5Test = /^5\./.test((require('webpack').version || '')) ? test : test.skip

webpack5Test('loaders should also be deduplicated when experiments.css is true', (done) => {
let loaders = []
bundle({
entry: 'extract-css.vue',
vue: { experimentalInlineMatchResource: true },
experiments: { css: true },
module: {
rules: [
{
test: /\.stylus$/,
use: ['stylus-loader'],
type: 'css'
}
]
},
plugins: [
{
apply(compiler) {
compiler.hooks.thisCompilation.tap('a', (compilation) => {
compilation.hooks.buildModule.tap('a', (module) => {
if (/[?&]lang=stylus/.test(module.resource)) {
const isPitch = module.loaders.some(item => /pitcher/.test(item.loader))
if (isPitch) {
return
}
// loaders for stylus after pitch
loaders = [...module.loaders]
}
})
})
}
}
]
}, () => {
const stylusLoaderCount = loaders.filter(item => /stylus-loader/.test(item.loader)).length
expect(stylusLoaderCount).toBe(1)

done()
})
})

0 comments on commit 3ed63fa

Please sign in to comment.