Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 3149f6d

Browse filesBrowse files
authored
feat: support experimental inline match resource (#2046)
1 parent 6ad8056 commit 3149f6d
Copy full SHA for 3149f6d

File tree

Expand file treeCollapse file tree

15 files changed

+335
-69
lines changed
Filter options
Expand file treeCollapse file tree

15 files changed

+335
-69
lines changed

‎.github/workflows/ci.yml

Copy file name to clipboardExpand all lines: .github/workflows/ci.yml
+12Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,15 @@ jobs:
3030
cache: 'yarn'
3131
- run: yarn install
3232
- run: yarn test
33+
34+
test-webpack5-inline-match-resource:
35+
runs-on: ubuntu-latest
36+
steps:
37+
- uses: actions/checkout@v2
38+
- name: Set node version to 16
39+
uses: actions/setup-node@v2
40+
with:
41+
node-version: 16
42+
cache: 'yarn'
43+
- run: yarn install
44+
- run: yarn test:match-resource

‎README.md

Copy file name to clipboardExpand all lines: README.md
+4Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
55
- [Documentation](https://vue-loader.vuejs.org)
66

7+
## v17.1+ Only Options
8+
9+
- `experimentalInlineMatchResource: boolean`: enable [Inline matchResource](https://webpack.js.org/api/loaders/#inline-matchresource) for rule matching for vue-loader.
10+
711
## v16+ Only Options
812

913
- `reactivityTransform: boolean`: enable [Vue Reactivity Transform](https://github.com/vuejs/rfcs/discussions/369) (SFCs only).

‎jest.config.js

Copy file name to clipboardExpand all lines: jest.config.js
+9-1Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
1-
console.log(`running tests with webpack ${process.env.WEBPACK4 ? '4' : '5'}...`)
1+
const isWebpack4 = process.env.WEBPACK4
2+
3+
console.log(
4+
`running tests with webpack ${isWebpack4 ? '4' : '5'}${
5+
!isWebpack4 && process.env.INLINE_MATCH_RESOURCE
6+
? ' with inline match resource enabled'
7+
: ''
8+
}...`
9+
)
210

311
module.exports = {
412
preset: 'ts-jest',

‎package.json

Copy file name to clipboardExpand all lines: package.json
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
"build": "tsc",
1515
"pretest": "tsc",
1616
"test": "jest",
17+
"pretest:match-resource": "tsc",
18+
"test:match-resource": "INLINE_MATCH_RESOURCE=true jest",
1719
"pretest:webpack4": "tsc",
1820
"test:webpack4": "WEBPACK4=true jest",
1921
"dev-example": "node example/devServer.js --config example/webpack.config.js --inline --hot",

‎src/index.ts

Copy file name to clipboardExpand all lines: src/index.ts
+76-9Lines changed: 76 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,12 @@ import { formatError } from './formatError'
2020
import VueLoaderPlugin from './plugin'
2121
import { canInlineTemplate } from './resolveScript'
2222
import { setDescriptor } from './descriptorCache'
23-
import { getOptions, stringifyRequest as _stringifyRequest } from './util'
23+
import {
24+
getOptions,
25+
stringifyRequest as _stringifyRequest,
26+
genMatchResource,
27+
testWebpack5,
28+
} from './util'
2429

2530
export { VueLoaderPlugin }
2631

@@ -51,6 +56,7 @@ export interface VueLoaderOptions {
5156
exposeFilename?: boolean
5257
appendExtension?: boolean
5358
enableTsInTemplate?: boolean
59+
experimentalInlineMatchResource?: boolean
5460

5561
isServerBuild?: boolean
5662
}
@@ -90,18 +96,23 @@ export default function loader(
9096
rootContext,
9197
resourcePath,
9298
resourceQuery: _resourceQuery = '',
99+
_compiler,
93100
} = loaderContext
94101

102+
const isWebpack5 = testWebpack5(_compiler)
95103
const rawQuery = _resourceQuery.slice(1)
96104
const incomingQuery = qs.parse(rawQuery)
97105
const resourceQuery = rawQuery ? `&${rawQuery}` : ''
98106
const options = (getOptions(loaderContext) || {}) as VueLoaderOptions
107+
const enableInlineMatchResource =
108+
isWebpack5 && Boolean(options.experimentalInlineMatchResource)
99109

100110
const isServer = options.isServerBuild ?? target === 'node'
101111
const isProduction =
102112
mode === 'production' || process.env.NODE_ENV === 'production'
103113

104114
const filename = resourcePath.replace(/\?.*$/, '')
115+
105116
const { descriptor, errors } = parse(source, {
106117
filename,
107118
sourceMap,
@@ -167,10 +178,23 @@ export default function loader(
167178
if (script || scriptSetup) {
168179
const lang = script?.lang || scriptSetup?.lang
169180
isTS = !!(lang && /tsx?/.test(lang))
181+
const externalQuery = Boolean(script && !scriptSetup && script.src)
182+
? `&external`
183+
: ``
170184
const src = (script && !scriptSetup && script.src) || resourcePath
171185
const attrsQuery = attrsToQuery((scriptSetup || script)!.attrs, 'js')
172-
const query = `?vue&type=script${attrsQuery}${resourceQuery}`
173-
const scriptRequest = stringifyRequest(src + query)
186+
const query = `?vue&type=script${attrsQuery}${resourceQuery}${externalQuery}`
187+
188+
let scriptRequest: string
189+
190+
if (enableInlineMatchResource) {
191+
scriptRequest = stringifyRequest(
192+
genMatchResource(this, src, query, lang || 'js')
193+
)
194+
} else {
195+
scriptRequest = stringifyRequest(src + query)
196+
}
197+
174198
scriptImport =
175199
`import script from ${scriptRequest}\n` +
176200
// support named exports
@@ -184,13 +208,27 @@ export default function loader(
184208
const useInlineTemplate = canInlineTemplate(descriptor, isProduction)
185209
if (descriptor.template && !useInlineTemplate) {
186210
const src = descriptor.template.src || resourcePath
211+
const externalQuery = Boolean(descriptor.template.src) ? `&external` : ``
187212
const idQuery = `&id=${id}`
188213
const scopedQuery = hasScoped ? `&scoped=true` : ``
189214
const attrsQuery = attrsToQuery(descriptor.template.attrs)
190215
const tsQuery =
191216
options.enableTsInTemplate !== false && isTS ? `&ts=true` : ``
192-
const query = `?vue&type=template${idQuery}${scopedQuery}${tsQuery}${attrsQuery}${resourceQuery}`
193-
templateRequest = stringifyRequest(src + query)
217+
const query = `?vue&type=template${idQuery}${scopedQuery}${tsQuery}${attrsQuery}${resourceQuery}${externalQuery}`
218+
219+
if (enableInlineMatchResource) {
220+
templateRequest = stringifyRequest(
221+
genMatchResource(
222+
this,
223+
src,
224+
query,
225+
options.enableTsInTemplate !== false && isTS ? 'ts' : 'js'
226+
)
227+
)
228+
} else {
229+
templateRequest = stringifyRequest(src + query)
230+
}
231+
194232
templateImport = `import { ${renderFnName} } from ${templateRequest}`
195233
propsToAttach.push([renderFnName, renderFnName])
196234
}
@@ -205,12 +243,23 @@ export default function loader(
205243
.forEach((style, i) => {
206244
const src = style.src || resourcePath
207245
const attrsQuery = attrsToQuery(style.attrs, 'css')
246+
const lang = String(style.attrs.lang || 'css')
208247
// make sure to only pass id when necessary so that we don't inject
209248
// duplicate tags when multiple components import the same css file
210249
const idQuery = !style.src || style.scoped ? `&id=${id}` : ``
211250
const inlineQuery = asCustomElement ? `&inline` : ``
212-
const query = `?vue&type=style&index=${i}${idQuery}${inlineQuery}${attrsQuery}${resourceQuery}`
213-
const styleRequest = stringifyRequest(src + query)
251+
const externalQuery = Boolean(style.src) ? `&external` : ``
252+
const query = `?vue&type=style&index=${i}${idQuery}${inlineQuery}${attrsQuery}${resourceQuery}${externalQuery}`
253+
254+
let styleRequest
255+
if (enableInlineMatchResource) {
256+
styleRequest = stringifyRequest(
257+
genMatchResource(this, src, query, lang)
258+
)
259+
} else {
260+
styleRequest = stringifyRequest(src + query)
261+
}
262+
214263
if (style.module) {
215264
if (asCustomElement) {
216265
loaderContext.emitError(
@@ -283,9 +332,27 @@ export default function loader(
283332
const issuerQuery = block.attrs.src
284333
? `&issuerPath=${qs.escape(resourcePath)}`
285334
: ''
286-
const query = `?vue&type=custom&index=${i}${blockTypeQuery}${issuerQuery}${attrsQuery}${resourceQuery}`
335+
336+
const externalQuery = Boolean(block.attrs.src) ? `&external` : ``
337+
const query = `?vue&type=custom&index=${i}${blockTypeQuery}${issuerQuery}${attrsQuery}${resourceQuery}${externalQuery}`
338+
339+
let customRequest
340+
341+
if (enableInlineMatchResource) {
342+
customRequest = stringifyRequest(
343+
genMatchResource(
344+
this,
345+
src as string,
346+
query,
347+
block.attrs.lang as string
348+
)
349+
)
350+
} else {
351+
customRequest = stringifyRequest(src + query)
352+
}
353+
287354
return (
288-
`import block${i} from ${stringifyRequest(src + query)}\n` +
355+
`import block${i} from ${customRequest}\n` +
289356
`if (typeof block${i} === 'function') block${i}(script)`
290357
)
291358
})

‎src/pitcher.ts

Copy file name to clipboardExpand all lines: src/pitcher.ts
+61-5Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { LoaderDefinitionFunction, LoaderContext } from 'webpack'
22
import * as qs from 'querystring'
3-
import { stringifyRequest } from './util'
3+
import { getOptions, stringifyRequest, testWebpack5 } from './util'
44
import { VueLoaderOptions } from '.'
55

66
const selfPath = require.resolve('./index')
@@ -58,7 +58,40 @@ export const pitch = function () {
5858
})
5959

6060
// Inject style-post-loader before css-loader for scoped CSS and trimming
61+
const isWebpack5 = testWebpack5(context._compiler)
62+
const options = (getOptions(context) || {}) as VueLoaderOptions
6163
if (query.type === `style`) {
64+
if (isWebpack5 && context._compiler?.options.experiments.css) {
65+
// If user enables `experiments.css`, then we are trying to emit css code directly.
66+
// Although we can target requests like `xxx.vue?type=style` to match `type: "css"`,
67+
// it will make the plugin a mess.
68+
if (!options.experimentalInlineMatchResource) {
69+
context.emitError(
70+
new Error(
71+
'`experimentalInlineMatchResource` should be enabled if `experiments.css` enabled currently'
72+
)
73+
)
74+
return ''
75+
}
76+
77+
if (query.inline || query.module) {
78+
context.emitError(
79+
new Error(
80+
'`inline` or `module` is currently not supported with `experiments.css` enabled'
81+
)
82+
)
83+
return ''
84+
}
85+
86+
const loaderString = [stylePostLoaderPath, ...loaders]
87+
.map((loader) => {
88+
return typeof loader === 'string' ? loader : loader.request
89+
})
90+
.join('!')
91+
return `@import "${context.resourcePath}${
92+
query.lang ? `.${query.lang}` : ''
93+
}${context.resourceQuery}!=!-!${loaderString}!${context.resource}";`
94+
}
6295
const cssLoaderIndex = loaders.findIndex(isCSSLoader)
6396
if (cssLoaderIndex > -1) {
6497
// if inlined, ignore any loaders after css-loader and replace w/ inline
@@ -71,7 +104,8 @@ export const pitch = function () {
71104
return genProxyModule(
72105
[...afterLoaders, stylePostLoaderPath, ...beforeLoaders],
73106
context,
74-
!!query.module || query.inline != null
107+
!!query.module || query.inline != null,
108+
(query.lang as string) || 'css'
75109
)
76110
}
77111
}
@@ -84,15 +118,21 @@ export const pitch = function () {
84118

85119
// Rewrite request. Technically this should only be done when we have deduped
86120
// loaders. But somehow this is required for block source maps to work.
87-
return genProxyModule(loaders, context, query.type !== 'template')
121+
return genProxyModule(
122+
loaders,
123+
context,
124+
query.type !== 'template',
125+
query.ts ? 'ts' : (query.lang as string)
126+
)
88127
}
89128

90129
function genProxyModule(
91130
loaders: (Loader | string)[],
92131
context: LoaderContext<VueLoaderOptions>,
93-
exportDefault = true
132+
exportDefault = true,
133+
lang = 'js'
94134
) {
95-
const request = genRequest(loaders, context)
135+
const request = genRequest(loaders, lang, context)
96136
// return a proxy module which simply re-exports everything from the
97137
// actual request. Note for template blocks the compiled module has no
98138
// default export.
@@ -104,12 +144,28 @@ function genProxyModule(
104144

105145
function genRequest(
106146
loaders: (Loader | string)[],
147+
lang: string,
107148
context: LoaderContext<VueLoaderOptions>
108149
) {
150+
const isWebpack5 = testWebpack5(context._compiler)
151+
const options = (getOptions(context) || {}) as VueLoaderOptions
152+
const enableInlineMatchResource =
153+
isWebpack5 && options.experimentalInlineMatchResource
154+
109155
const loaderStrings = loaders.map((loader) => {
110156
return typeof loader === 'string' ? loader : loader.request
111157
})
112158
const resource = context.resourcePath + context.resourceQuery
159+
160+
if (enableInlineMatchResource) {
161+
return stringifyRequest(
162+
context,
163+
`${context.resourcePath}${lang ? `.${lang}` : ''}${
164+
context.resourceQuery
165+
}!=!-!${[...loaderStrings, resource].join('!')}`
166+
)
167+
}
168+
113169
return stringifyRequest(
114170
context,
115171
'-!' + [...loaderStrings, resource].join('!')

‎src/plugin.ts

Copy file name to clipboard
+15-8Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,26 @@
1-
import webpack from 'webpack'
21
import type { Compiler } from 'webpack'
2+
import { testWebpack5 } from './util'
33

44
declare class VueLoaderPlugin {
55
static NS: string
66
apply(compiler: Compiler): void
77
}
88

9-
let Plugin: typeof VueLoaderPlugin
9+
const NS = 'vue-loader'
1010

11-
if (webpack.version && webpack.version[0] > '4') {
12-
// webpack5 and upper
13-
Plugin = require('./pluginWebpack5').default
14-
} else {
15-
// webpack4 and lower
16-
Plugin = require('./pluginWebpack4').default
11+
class Plugin {
12+
static NS = NS
13+
apply(compiler: Compiler) {
14+
let Ctor: typeof VueLoaderPlugin
15+
if (testWebpack5(compiler)) {
16+
// webpack5 and upper
17+
Ctor = require('./pluginWebpack5').default
18+
} else {
19+
// webpack4 and lower
20+
Ctor = require('./pluginWebpack4').default
21+
}
22+
new Ctor().apply(compiler)
23+
}
1724
}
1825

1926
export default Plugin

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.