@@ -22,7 +22,12 @@ import { formatError } from './formatError'
2222import VueLoaderPlugin from './plugin'
2323import { canInlineTemplate } from './resolveScript'
2424import { setDescriptor } from './descriptorCache'
25- import { getOptions , stringifyRequest as _stringifyRequest } from './util'
25+ import {
26+ getOptions ,
27+ stringifyRequest as _stringifyRequest ,
28+ genMatchResource ,
29+ testWebpack5 ,
30+ } from './util'
2631
2732export { VueLoaderPlugin }
2833
@@ -53,6 +58,7 @@ export interface VueLoaderOptions {
5358 exposeFilename ?: boolean
5459 appendExtension ?: boolean
5560 enableTsInTemplate ?: boolean
61+ experimentalInlineMatchResource ?: boolean
5662
5763 isServerBuild ?: boolean
5864}
@@ -64,7 +70,7 @@ const exportHelperPath = require.resolve('./exportHelper')
6470
6571export default function loader (
6672 this : LoaderContext < VueLoaderOptions > ,
67- source : string
73+ source : string ,
6874) {
6975 const loaderContext = this
7076
@@ -77,8 +83,8 @@ export default function loader(
7783 loaderContext . emitError (
7884 new Error (
7985 `vue-loader was used without the corresponding plugin. ` +
80- `Make sure to include VueLoaderPlugin in your webpack config.`
81- )
86+ `Make sure to include VueLoaderPlugin in your webpack config.` ,
87+ ) ,
8288 )
8389 errorEmitted = true
8490 }
@@ -92,18 +98,23 @@ export default function loader(
9298 rootContext,
9399 resourcePath,
94100 resourceQuery : _resourceQuery = '' ,
101+ _compiler,
95102 } = loaderContext
96103
104+ const isWebpack5 = testWebpack5 ( _compiler )
97105 const rawQuery = _resourceQuery . slice ( 1 )
98106 const incomingQuery = qs . parse ( rawQuery )
99107 const resourceQuery = rawQuery ? `&${ rawQuery } ` : ''
100108 const options = ( getOptions ( loaderContext ) || { } ) as VueLoaderOptions
109+ const enableInlineMatchResource =
110+ isWebpack5 && Boolean ( options . experimentalInlineMatchResource )
101111
102112 const isServer = options . isServerBuild ?? target === 'node'
103113 const isProduction =
104114 mode === 'production' || process . env . NODE_ENV === 'production'
105115
106116 const filename = resourcePath . replace ( / \? .* $ / , '' )
117+
107118 const { descriptor, errors } = parse ( source , {
108119 filename,
109120 sourceMap,
@@ -133,7 +144,7 @@ export default function loader(
133144 const id = hash (
134145 isProduction
135146 ? shortFilePath + '\n' + source . replace ( / \r \n / g, '\n' )
136- : shortFilePath
147+ : shortFilePath ,
137148 )
138149
139150 // if the query has a type field, this is a language block request
@@ -146,7 +157,7 @@ export default function loader(
146157 options ,
147158 loaderContext ,
148159 incomingQuery ,
149- ! ! options . appendExtension
160+ ! ! options . appendExtension ,
150161 )
151162 }
152163
@@ -169,10 +180,23 @@ export default function loader(
169180 if ( script || scriptSetup ) {
170181 const lang = script ?. lang || scriptSetup ?. lang
171182 isTS = ! ! ( lang && / t s x ? / . test ( lang ) )
183+ const externalQuery = Boolean ( script && ! scriptSetup && script . src )
184+ ? `&external`
185+ : ``
172186 const src = ( script && ! scriptSetup && script . src ) || resourcePath
173187 const attrsQuery = attrsToQuery ( ( scriptSetup || script ) ! . attrs , 'js' )
174- const query = `?vue&type=script${ attrsQuery } ${ resourceQuery } `
175- const scriptRequest = stringifyRequest ( src + query )
188+ const query = `?vue&type=script${ attrsQuery } ${ resourceQuery } ${ externalQuery } `
189+
190+ let scriptRequest : string
191+
192+ if ( enableInlineMatchResource ) {
193+ scriptRequest = stringifyRequest (
194+ genMatchResource ( this , src , query , lang || 'js' ) ,
195+ )
196+ } else {
197+ scriptRequest = stringifyRequest ( src + query )
198+ }
199+
176200 scriptImport =
177201 `import script from ${ scriptRequest } \n` +
178202 // support named exports
@@ -186,13 +210,27 @@ export default function loader(
186210 const useInlineTemplate = canInlineTemplate ( descriptor , isProduction )
187211 if ( descriptor . template && ! useInlineTemplate ) {
188212 const src = descriptor . template . src || resourcePath
213+ const externalQuery = Boolean ( descriptor . template . src ) ? `&external` : ``
189214 const idQuery = `&id=${ id } `
190215 const scopedQuery = hasScoped ? `&scoped=true` : ``
191216 const attrsQuery = attrsToQuery ( descriptor . template . attrs )
192217 const tsQuery =
193218 options . enableTsInTemplate !== false && isTS ? `&ts=true` : ``
194- const query = `?vue&type=template${ idQuery } ${ scopedQuery } ${ tsQuery } ${ attrsQuery } ${ resourceQuery } `
195- templateRequest = stringifyRequest ( src + query )
219+ const query = `?vue&type=template${ idQuery } ${ scopedQuery } ${ tsQuery } ${ attrsQuery } ${ resourceQuery } ${ externalQuery } `
220+
221+ if ( enableInlineMatchResource ) {
222+ templateRequest = stringifyRequest (
223+ genMatchResource (
224+ this ,
225+ src ,
226+ query ,
227+ options . enableTsInTemplate !== false && isTS ? 'ts' : 'js' ,
228+ ) ,
229+ )
230+ } else {
231+ templateRequest = stringifyRequest ( src + query )
232+ }
233+
196234 templateImport = `import { ${ renderFnName } } from ${ templateRequest } `
197235 propsToAttach . push ( [ renderFnName , renderFnName ] )
198236 }
@@ -209,19 +247,29 @@ export default function loader(
209247 . forEach ( ( style , i ) => {
210248 const src = style . src || resourcePath
211249 const attrsQuery = attrsToQuery ( style . attrs , 'css' )
250+ const lang = String ( style . attrs . lang || 'css' )
212251 // make sure to only pass id when necessary so that we don't inject
213252 // duplicate tags when multiple components import the same css file
214253 const idQuery = ! style . src || style . scoped ? `&id=${ id } ` : ``
215254 const inlineQuery = asCustomElement ? `&inline` : ``
216- const query = `?vue&type=style&index=${ i } ${ idQuery } ${ inlineQuery } ${ attrsQuery } ${ resourceQuery } `
217- const styleRequest = stringifyRequest ( src + query )
255+ const externalQuery = Boolean ( style . src ) ? `&external` : ``
256+ const query = `?vue&type=style&index=${ i } ${ idQuery } ${ inlineQuery } ${ attrsQuery } ${ resourceQuery } ${ externalQuery } `
257+
258+ let styleRequest
259+ if ( enableInlineMatchResource ) {
260+ styleRequest = stringifyRequest (
261+ genMatchResource ( this , src , query , lang ) ,
262+ )
263+ } else {
264+ styleRequest = stringifyRequest ( src + query )
265+ }
218266
219267 if ( style . module ) {
220268 if ( asCustomElement ) {
221269 loaderContext . emitError (
222270 new Error (
223- `<style module> is not supported in custom element mode.`
224- )
271+ `<style module> is not supported in custom element mode.` ,
272+ ) ,
225273 )
226274 }
227275 if ( ! hasCSSModules ) {
@@ -234,7 +282,7 @@ export default function loader(
234282 i ,
235283 styleRequest ,
236284 style . module ,
237- needsHotReload
285+ needsHotReload ,
238286 )
239287 } else {
240288 if ( ! isServer ) {
@@ -300,9 +348,27 @@ export default function loader(
300348 const issuerQuery = block . attrs . src
301349 ? `&issuerPath=${ qs . escape ( resourcePath ) } `
302350 : ''
303- const query = `?vue&type=custom&index=${ i } ${ blockTypeQuery } ${ issuerQuery } ${ attrsQuery } ${ resourceQuery } `
351+
352+ const externalQuery = Boolean ( block . attrs . src ) ? `&external` : ``
353+ const query = `?vue&type=custom&index=${ i } ${ blockTypeQuery } ${ issuerQuery } ${ attrsQuery } ${ resourceQuery } ${ externalQuery } `
354+
355+ let customRequest
356+
357+ if ( enableInlineMatchResource ) {
358+ customRequest = stringifyRequest (
359+ genMatchResource (
360+ this ,
361+ src as string ,
362+ query ,
363+ block . attrs . lang as string ,
364+ ) ,
365+ )
366+ } else {
367+ customRequest = stringifyRequest ( src + query )
368+ }
369+
304370 return (
305- `import block${ i } from ${ stringifyRequest ( src + query ) } \n` +
371+ `import block${ i } from ${ customRequest } \n` +
306372 `if (typeof block${ i } === 'function') block${ i } (script)`
307373 )
308374 } )
@@ -332,7 +398,9 @@ export default function loader(
332398 if ( ! propsToAttach . length ) {
333399 code += `\n\nconst __exports__ = script;`
334400 } else {
335- code += `\n\nimport exportComponent from ${ stringifyRequest ( exportHelperPath ) } `
401+ code += `\n\nimport exportComponent from ${ stringifyRequest (
402+ exportHelperPath ,
403+ ) } `
336404 code += `\nconst __exports__ = /*#__PURE__*/exportComponent(script, [${ propsToAttach
337405 . map ( ( [ key , val ] ) => `['${ key } ',${ val } ]` )
338406 . join ( ',' ) } ])`
0 commit comments