11import * as webpack from 'webpack'
22import qs from 'querystring'
33import loaderUtils from 'loader-utils'
4- import hash from 'hash-sum'
5- import { VueLoaderOptions } from 'src'
64
75const selfPath = require . resolve ( './index' )
8- const templateLoaderPath = require . resolve ( './templateLoader' )
6+ // const templateLoaderPath = require.resolve('./templateLoader')
97const stylePostLoaderPath = require . resolve ( './stylePostLoader' )
108
119// @types /webpack doesn't provide the typing for loaderContext.loaders...
@@ -20,161 +18,104 @@ const isESLintLoader = (l: Loader) => /(\/|\\|@)eslint-loader/.test(l.path)
2018const isNullLoader = ( l : Loader ) => / ( \/ | \\ | @ ) n u l l - l o a d e r / . test ( l . path )
2119const isCSSLoader = ( l : Loader ) => / ( \/ | \\ | @ ) c s s - l o a d e r / . test ( l . path )
2220const isCacheLoader = ( l : Loader ) => / ( \/ | \\ | @ ) c a c h e - l o a d e r / . test ( l . path )
23- const isPitcher = ( l : Loader ) => l . path !== __filename
24- const isPreLoader = ( l : Loader ) => ! l . pitchExecuted
25- const isPostLoader = ( l : Loader ) => l . pitchExecuted
26-
27- const dedupeESLintLoader = ( loaders : Loader [ ] ) => {
28- const res : Loader [ ] = [ ]
29- let seen = false
30- loaders . forEach ( ( l : Loader ) => {
31- if ( ! isESLintLoader ( l ) ) {
32- res . push ( l )
33- } else if ( ! seen ) {
34- seen = true
35- res . push ( l )
36- }
37- } )
38- return res
39- }
40-
41- const shouldIgnoreCustomBlock = ( loaders : Loader [ ] ) => {
42- const actualLoaders = loaders . filter ( loader => {
43- // vue-loader
44- if ( loader . path === selfPath ) {
45- return false
46- }
47-
48- // cache-loader
49- if ( isCacheLoader ( loader ) ) {
50- return false
51- }
52-
53- return true
54- } )
55- return actualLoaders . length === 0
56- }
21+ const isNotPitcher = ( l : Loader ) => l . path !== __filename
5722
5823const pitcher : webpack . loader . Loader = code => code
5924
6025module . exports = pitcher
6126
6227// This pitching loader is responsible for intercepting all vue block requests
6328// and transform it into appropriate requests.
64- pitcher . pitch = function ( ) {
29+ pitcher . pitch = function ( r ) {
6530 const context = this as webpack . loader . LoaderContext
66- const options = loaderUtils . getOptions ( context ) as VueLoaderOptions
67- const { cacheDirectory, cacheIdentifier } = options
68- const query = qs . parse ( context . resourceQuery . slice ( 1 ) )
69-
70- let loaders = context . loaders
71-
72- // if this is a language block request, eslint-loader may get matched
73- // multiple times
74- if ( query . type ) {
75- // if this is an inline block, since the whole file itself is being linted,
76- // remove eslint-loader to avoid duplicate linting.
77- if ( / \. v u e $ / . test ( context . resourcePath ) ) {
78- loaders = loaders . filter ( ( l : Loader ) => ! isESLintLoader ( l ) )
79- } else {
80- // This is a src import. Just make sure there's not more than 1 instance
81- // of eslint present.
82- loaders = dedupeESLintLoader ( loaders )
83- }
84- }
85-
86- // remove self
87- loaders = loaders . filter ( isPitcher )
31+ const rawLoaders = context . loaders . filter ( isNotPitcher )
32+ let loaders = rawLoaders
8833
8934 // do not inject if user uses null-loader to void the type (#1239)
9035 if ( loaders . some ( isNullLoader ) ) {
9136 return
9237 }
9338
94- const genRequest = ( loaders : Loader [ ] ) => {
95- // Important: dedupe since both the original rule
96- // and the cloned rule would match a source import request.
97- // also make sure to dedupe based on loader path.
98- // assumes you'd probably never want to apply the same loader on the same
99- // file twice.
100- // Exception: in Vue CLI we do need two instances of postcss-loader
101- // for user config and inline minification. So we need to dedupe baesd on
102- // path AND query to be safe.
103- const seen = new Map ( )
104- const loaderStrings : string [ ] = [ ]
105-
106- loaders . forEach ( loader => {
107- const identifier = typeof loader === 'string'
108- ? loader
109- : ( loader . path + loader . query )
110- const request = typeof loader === 'string' ? loader : loader . request
111- if ( ! seen . has ( identifier ) ) {
112- seen . set ( identifier , true )
113- // loader.request contains both the resolved loader path and its options
114- // query (e.g. ??ref-0)
115- loaderStrings . push ( request )
116- }
117- } )
118-
119- return loaderUtils . stringifyRequest ( context , '-!' + [
120- ...loaderStrings ,
121- context . resourcePath + context . resourceQuery
122- ] . join ( '!' ) )
39+ const query = qs . parse ( context . resourceQuery . slice ( 1 ) )
40+ const isInlineBlock = / \. v u e $ / . test ( context . resourcePath )
41+ // eslint-loader may get matched multiple times
42+ // if this is an inline block, since the whole file itself is being linted,
43+ // remove eslint-loader to avoid duplicate linting.
44+ if ( isInlineBlock ) {
45+ loaders = loaders . filter ( ( l : Loader ) => ! isESLintLoader ( l ) )
12346 }
12447
48+ // Important: dedupe loaders since both the original rule
49+ // and the cloned rule would match a source import request or a
50+ // resourceQuery-only rule that intends to target a custom block with no lang
51+ const seen = new Map ( )
52+ loaders = loaders . filter ( loader => {
53+ const identifier = typeof loader === 'string'
54+ ? loader
55+ // Dedupe based on both path and query if available. This is important
56+ // in Vue CLI so that postcss-loaders with different options can co-exist
57+ : ( loader . path + loader . query )
58+ if ( ! seen . has ( identifier ) ) {
59+ seen . set ( identifier , true )
60+ return true
61+ }
62+ } )
63+
12564 // Inject style-post-loader before css-loader for scoped CSS and trimming
12665 if ( query . type === `style` ) {
12766 const cssLoaderIndex = loaders . findIndex ( isCSSLoader )
12867 if ( cssLoaderIndex > - 1 ) {
12968 const afterLoaders = loaders . slice ( 0 , cssLoaderIndex + 1 )
13069 const beforeLoaders = loaders . slice ( cssLoaderIndex + 1 )
131- const request = genRequest ( [
70+ return genProxyModule ( [
13271 ...afterLoaders ,
13372 stylePostLoaderPath ,
13473 ...beforeLoaders
135- ] )
136- // console.log(request)
137- return `import mod from ${ request } ; export default mod; export * from ${ request } `
74+ ] , context )
13875 }
13976 }
14077
141- // for templates: inject the template compiler & optional cache
142- if ( query . type === `template` ) {
143- const path = require ( 'path' )
144- const cacheLoader = cacheDirectory && cacheIdentifier
145- ? [ `${ require . resolve ( 'cache-loader' ) } ?${ JSON . stringify ( {
146- // For some reason, webpack fails to generate consistent hash if we
147- // use absolute paths here, even though the path is only used in a
148- // comment. For now we have to ensure cacheDirectory is a relative path.
149- cacheDirectory : ( path . isAbsolute ( cacheDirectory )
150- ? path . relative ( process . cwd ( ) , cacheDirectory )
151- : cacheDirectory ) . replace ( / \\ / g, '/' ) ,
152- cacheIdentifier : hash ( cacheIdentifier ) + '-vue-loader-template'
153- } ) } `]
154- : [ ]
155-
156- const preLoaders = loaders . filter ( isPreLoader )
157- const postLoaders = loaders . filter ( isPostLoader )
158-
159- const request = genRequest ( [
160- ...cacheLoader ,
161- ...postLoaders ,
162- templateLoaderPath + `??vue-loader-options` ,
163- ...preLoaders
164- ] )
165- // console.log(request)
166- return `import mod from ${ request } ; export default mod;`
167- }
168-
16978 // if a custom block has no other matching loader other than vue-loader itself
17079 // or cache-loader, we should ignore it
17180 if ( query . type === `custom` && shouldIgnoreCustomBlock ( loaders ) ) {
17281 return ``
17382 }
17483
175- // When the user defines a rule that has only resourceQuery but no test,
176- // both that rule and the cloned rule will match, resulting in duplicated
177- // loaders. Therefore it is necessary to perform a dedupe here.
178- const request = genRequest ( loaders )
179- return `import mod from ${ request } ; export default mod; export * from ${ request } `
84+ // rewrite if we have deduped loaders
85+ if ( loaders . length !== rawLoaders . length ) {
86+ return genProxyModule ( loaders , context )
87+ }
88+ }
89+
90+ function genProxyModule ( loaders : Loader [ ] , context : webpack . loader . LoaderContext ) {
91+ const loaderStrings = loaders . map ( loader => {
92+ return typeof loader === 'string' ? loader : loader . request
93+ } )
94+ const resource = context . resourcePath + context . resourceQuery
95+ const request = loaderUtils . stringifyRequest ( context , '-!' + [
96+ ...loaderStrings ,
97+ resource
98+ ] . join ( '!' ) )
99+ // return a proxy module which simply re-exports everything from the
100+ // actual request.
101+ return (
102+ `import mod from ${ request } ;` +
103+ `export default mod;` +
104+ `export * from ${ request } `
105+ )
106+ }
107+
108+ function shouldIgnoreCustomBlock ( loaders : Loader [ ] ) {
109+ const actualLoaders = loaders . filter ( loader => {
110+ // vue-loader
111+ if ( loader . path === selfPath ) {
112+ return false
113+ }
114+ // cache-loader
115+ if ( isCacheLoader ( loader ) ) {
116+ return false
117+ }
118+ return true
119+ } )
120+ return actualLoaders . length === 0
180121}
0 commit comments