1
1
import * as webpack from 'webpack'
2
2
import qs from 'querystring'
3
3
import loaderUtils from 'loader-utils'
4
- import hash from 'hash-sum'
5
- import { VueLoaderOptions } from 'src'
6
4
7
5
const selfPath = require . resolve ( './index' )
8
- const templateLoaderPath = require . resolve ( './templateLoader' )
6
+ // const templateLoaderPath = require.resolve('./templateLoader')
9
7
const stylePostLoaderPath = require . resolve ( './stylePostLoader' )
10
8
11
9
// @types /webpack doesn't provide the typing for loaderContext.loaders...
@@ -20,161 +18,104 @@ const isESLintLoader = (l: Loader) => /(\/|\\|@)eslint-loader/.test(l.path)
20
18
const isNullLoader = ( l : Loader ) => / ( \/ | \\ | @ ) n u l l - l o a d e r / . test ( l . path )
21
19
const isCSSLoader = ( l : Loader ) => / ( \/ | \\ | @ ) c s s - l o a d e r / . test ( l . path )
22
20
const 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
57
22
58
23
const pitcher : webpack . loader . Loader = code => code
59
24
60
25
module . exports = pitcher
61
26
62
27
// This pitching loader is responsible for intercepting all vue block requests
63
28
// and transform it into appropriate requests.
64
- pitcher . pitch = function ( ) {
29
+ pitcher . pitch = function ( r ) {
65
30
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
88
33
89
34
// do not inject if user uses null-loader to void the type (#1239)
90
35
if ( loaders . some ( isNullLoader ) ) {
91
36
return
92
37
}
93
38
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 ) )
123
46
}
124
47
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
+
125
64
// Inject style-post-loader before css-loader for scoped CSS and trimming
126
65
if ( query . type === `style` ) {
127
66
const cssLoaderIndex = loaders . findIndex ( isCSSLoader )
128
67
if ( cssLoaderIndex > - 1 ) {
129
68
const afterLoaders = loaders . slice ( 0 , cssLoaderIndex + 1 )
130
69
const beforeLoaders = loaders . slice ( cssLoaderIndex + 1 )
131
- const request = genRequest ( [
70
+ return genProxyModule ( [
132
71
...afterLoaders ,
133
72
stylePostLoaderPath ,
134
73
...beforeLoaders
135
- ] )
136
- // console.log(request)
137
- return `import mod from ${ request } ; export default mod; export * from ${ request } `
74
+ ] , context )
138
75
}
139
76
}
140
77
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
-
169
78
// if a custom block has no other matching loader other than vue-loader itself
170
79
// or cache-loader, we should ignore it
171
80
if ( query . type === `custom` && shouldIgnoreCustomBlock ( loaders ) ) {
172
81
return ``
173
82
}
174
83
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
180
121
}
0 commit comments