Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
V
vue-loader
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
pi-plusplus
fork-from-github
vue-loader
Commits
59eebca0
Commit
59eebca0
authored
Dec 10, 2019
by
18566246732
Committed by
Haoqun Jiang
Dec 10, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: add support for webpack5 (#1613)
parent
92c02cb0
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
365 additions
and
160 deletions
+365
-160
plugin-webpack4.js
lib/plugin-webpack4.js
+163
-0
plugin-webpack5.js
lib/plugin-webpack5.js
+193
-0
plugin.js
lib/plugin.js
+9
-160
No files found.
lib/plugin-webpack4.js
0 → 100644
View file @
59eebca0
const
qs
=
require
(
'querystring'
)
const
RuleSet
=
require
(
'webpack/lib/RuleSet'
)
const
id
=
'vue-loader-plugin'
const
NS
=
'vue-loader'
class
VueLoaderPlugin
{
apply
(
compiler
)
{
// add NS marker so that the loader can detect and report missing plugin
if
(
compiler
.
hooks
)
{
// webpack 4
compiler
.
hooks
.
compilation
.
tap
(
id
,
compilation
=>
{
let
normalModuleLoader
if
(
Object
.
isFrozen
(
compilation
.
hooks
))
{
// webpack 5
normalModuleLoader
=
require
(
'webpack/lib/NormalModule'
).
getCompilationHooks
(
compilation
).
loader
}
else
{
normalModuleLoader
=
compilation
.
hooks
.
normalModuleLoader
}
normalModuleLoader
.
tap
(
id
,
loaderContext
=>
{
loaderContext
[
NS
]
=
true
})
})
}
else
{
// webpack < 4
compiler
.
plugin
(
'compilation'
,
compilation
=>
{
compilation
.
plugin
(
'normal-module-loader'
,
loaderContext
=>
{
loaderContext
[
NS
]
=
true
})
})
}
// use webpack's RuleSet utility to normalize user rules
const
rawRules
=
compiler
.
options
.
module
.
rules
const
{
rules
}
=
new
RuleSet
(
rawRules
)
// find the rule that applies to vue files
let
vueRuleIndex
=
rawRules
.
findIndex
(
createMatcher
(
`foo.vue`
))
if
(
vueRuleIndex
<
0
)
{
vueRuleIndex
=
rawRules
.
findIndex
(
createMatcher
(
`foo.vue.html`
))
}
const
vueRule
=
rules
[
vueRuleIndex
]
if
(
!
vueRule
)
{
throw
new
Error
(
`[VueLoaderPlugin Error] No matching rule for .vue files found.\n`
+
`Make sure there is at least one root-level rule that matches .vue or .vue.html files.`
)
}
if
(
vueRule
.
oneOf
)
{
throw
new
Error
(
`[VueLoaderPlugin Error] vue-loader 15 currently does not support vue rules with oneOf.`
)
}
// get the normlized "use" for vue files
const
vueUse
=
vueRule
.
use
// get vue-loader options
const
vueLoaderUseIndex
=
vueUse
.
findIndex
(
u
=>
{
return
/^vue-loader|
(\/
|
\\
|@
)
vue-loader/
.
test
(
u
.
loader
)
})
if
(
vueLoaderUseIndex
<
0
)
{
throw
new
Error
(
`[VueLoaderPlugin Error] No matching use for vue-loader is found.\n`
+
`Make sure the rule matching .vue files include vue-loader in its use.`
)
}
// make sure vue-loader options has a known ident so that we can share
// options by reference in the template-loader by using a ref query like
// template-loader??vue-loader-options
const
vueLoaderUse
=
vueUse
[
vueLoaderUseIndex
]
vueLoaderUse
.
ident
=
'vue-loader-options'
vueLoaderUse
.
options
=
vueLoaderUse
.
options
||
{}
// for each user rule (expect the vue rule), create a cloned rule
// that targets the corresponding language blocks in *.vue files.
const
clonedRules
=
rules
.
filter
(
r
=>
r
!==
vueRule
)
.
map
(
cloneRule
)
// global pitcher (responsible for injecting template compiler loader & CSS
// post loader)
const
pitcher
=
{
loader
:
require
.
resolve
(
'./loaders/pitcher'
),
resourceQuery
:
query
=>
{
const
parsed
=
qs
.
parse
(
query
.
slice
(
1
))
return
parsed
.
vue
!=
null
},
options
:
{
cacheDirectory
:
vueLoaderUse
.
options
.
cacheDirectory
,
cacheIdentifier
:
vueLoaderUse
.
options
.
cacheIdentifier
}
}
// replace original rules
compiler
.
options
.
module
.
rules
=
[
pitcher
,
...
clonedRules
,
...
rules
]
}
}
function
createMatcher
(
fakeFile
)
{
return
(
rule
,
i
)
=>
{
// #1201 we need to skip the `include` check when locating the vue rule
const
clone
=
Object
.
assign
({},
rule
)
delete
clone
.
include
const
normalized
=
RuleSet
.
normalizeRule
(
clone
,
{},
''
)
return
(
!
rule
.
enforce
&&
normalized
.
resource
&&
normalized
.
resource
(
fakeFile
)
)
}
}
function
cloneRule
(
rule
)
{
const
{
resource
,
resourceQuery
}
=
rule
// Assuming `test` and `resourceQuery` tests are executed in series and
// synchronously (which is true based on RuleSet's implementation), we can
// save the current resource being matched from `test` so that we can access
// it in `resourceQuery`. This ensures when we use the normalized rule's
// resource check, include/exclude are matched correctly.
let
currentResource
const
res
=
Object
.
assign
({},
rule
,
{
resource
:
{
test
:
resource
=>
{
currentResource
=
resource
return
true
}
},
resourceQuery
:
query
=>
{
const
parsed
=
qs
.
parse
(
query
.
slice
(
1
))
if
(
parsed
.
vue
==
null
)
{
return
false
}
if
(
resource
&&
parsed
.
lang
==
null
)
{
return
false
}
const
fakeResourcePath
=
`
${
currentResource
}
.
${
parsed
.
lang
}
`
if
(
resource
&&
!
resource
(
fakeResourcePath
))
{
return
false
}
if
(
resourceQuery
&&
!
resourceQuery
(
query
))
{
return
false
}
return
true
}
})
if
(
rule
.
oneOf
)
{
res
.
oneOf
=
rule
.
oneOf
.
map
(
cloneRule
)
}
return
res
}
VueLoaderPlugin
.
NS
=
NS
module
.
exports
=
VueLoaderPlugin
lib/plugin-webpack5.js
0 → 100644
View file @
59eebca0
const
qs
=
require
(
'querystring'
)
const
id
=
'vue-loader-plugin'
const
NS
=
'vue-loader'
const
BasicEffectRulePlugin
=
require
(
'webpack/lib/rules/BasicEffectRulePlugin'
)
const
BasicMatcherRulePlugin
=
require
(
'webpack/lib/rules/BasicMatcherRulePlugin'
)
const
RuleSetCompiler
=
require
(
'webpack/lib/rules/RuleSetCompiler'
)
const
UseEffectRulePlugin
=
require
(
'webpack/lib/rules/UseEffectRulePlugin'
)
const
ruleSetCompiler
=
new
RuleSetCompiler
([
new
BasicMatcherRulePlugin
(
'test'
,
'resource'
),
new
BasicMatcherRulePlugin
(
'include'
,
'resource'
),
new
BasicMatcherRulePlugin
(
'exclude'
,
'resource'
,
true
),
new
BasicMatcherRulePlugin
(
'resource'
),
new
BasicMatcherRulePlugin
(
'conditions'
),
new
BasicMatcherRulePlugin
(
'resourceQuery'
),
new
BasicMatcherRulePlugin
(
'realResource'
),
new
BasicMatcherRulePlugin
(
'issuer'
),
new
BasicMatcherRulePlugin
(
'compiler'
),
new
BasicEffectRulePlugin
(
'type'
),
new
BasicEffectRulePlugin
(
'sideEffects'
),
new
BasicEffectRulePlugin
(
'parser'
),
new
BasicEffectRulePlugin
(
'resolve'
),
new
UseEffectRulePlugin
()
])
class
VueLoaderPlugin
{
apply
(
compiler
)
{
// add NS marker so that the loader can detect and report missing plugin
compiler
.
hooks
.
compilation
.
tap
(
id
,
compilation
=>
{
const
normalModuleLoader
=
require
(
'webpack/lib/NormalModule'
).
getCompilationHooks
(
compilation
).
loader
normalModuleLoader
.
tap
(
id
,
loaderContext
=>
{
loaderContext
[
NS
]
=
true
})
})
const
rules
=
compiler
.
options
.
module
.
rules
let
rawVueRules
let
vueRules
=
[]
for
(
const
rawRule
of
rules
)
{
// skip the `include` check when locating the vue rule
const
clonedRawRule
=
Object
.
assign
({},
rawRule
)
delete
clonedRawRule
.
include
const
ruleSet
=
ruleSetCompiler
.
compile
([{
rules
:
[
clonedRawRule
]
}])
vueRules
=
ruleSet
.
exec
({
resource
:
'foo.vue'
})
if
(
!
vueRules
.
length
)
{
vueRules
=
ruleSet
.
exec
({
resource
:
'foo.vue.html'
})
}
if
(
vueRules
.
length
>
0
)
{
if
(
rawRule
.
oneOf
)
{
throw
new
Error
(
`[VueLoaderPlugin Error] vue-loader 15 currently does not support vue rules with oneOf.`
)
}
rawVueRules
=
rawRule
break
}
}
if
(
!
vueRules
.
length
)
{
throw
new
Error
(
`[VueLoaderPlugin Error] No matching rule for .vue files found.\n`
+
`Make sure there is at least one root-level rule that matches .vue or .vue.html files.`
)
}
// get the normlized "use" for vue files
const
vueUse
=
vueRules
.
filter
(
rule
=>
rule
.
type
===
'use'
).
map
(
rule
=>
rule
.
value
)
// get vue-loader options
const
vueLoaderUseIndex
=
vueUse
.
findIndex
(
u
=>
{
return
/^vue-loader|
(\/
|
\\
|@
)
vue-loader/
.
test
(
u
.
loader
)
})
if
(
vueLoaderUseIndex
<
0
)
{
throw
new
Error
(
`[VueLoaderPlugin Error] No matching use for vue-loader is found.\n`
+
`Make sure the rule matching .vue files include vue-loader in its use.`
)
}
// make sure vue-loader options has a known ident so that we can share
// options by reference in the template-loader by using a ref query like
// template-loader??vue-loader-options
const
vueLoaderUse
=
vueUse
[
vueLoaderUseIndex
]
vueLoaderUse
.
ident
=
'vue-loader-options'
vueLoaderUse
.
options
=
vueLoaderUse
.
options
||
{}
// for each user rule (expect the vue rule), create a cloned rule
// that targets the corresponding language blocks in *.vue files.
const
refs
=
new
Map
()
const
clonedRules
=
rules
.
filter
(
r
=>
r
!==
rawVueRules
)
.
map
((
rawRule
)
=>
cloneRule
(
rawRule
,
refs
))
// fix conflict with config.loader and config.options when using config.use
delete
rawVueRules
.
loader
delete
rawVueRules
.
options
rawVueRules
.
use
=
vueUse
// global pitcher (responsible for injecting template compiler loader & CSS
// post loader)
const
pitcher
=
{
loader
:
require
.
resolve
(
'./loaders/pitcher'
),
resourceQuery
:
query
=>
{
const
parsed
=
qs
.
parse
(
query
.
slice
(
1
))
return
parsed
.
vue
!=
null
},
options
:
{
cacheDirectory
:
vueLoaderUse
.
options
.
cacheDirectory
,
cacheIdentifier
:
vueLoaderUse
.
options
.
cacheIdentifier
}
}
// replace original rules
compiler
.
options
.
module
.
rules
=
[
pitcher
,
...
clonedRules
,
...
rules
]
}
}
function
cloneRule
(
rawRule
,
refs
)
{
const
rules
=
ruleSetCompiler
.
compileRules
(
'ruleSet'
,
[{
rules
:
[
rawRule
]
}],
refs
)
let
currentResource
const
conditions
=
rules
[
0
].
rules
.
map
(
rule
=>
rule
.
conditions
)
// shallow flat
.
reduce
((
prev
,
next
)
=>
prev
.
concat
(
next
),
[])
// do not process rule with enforce
if
(
!
rawRule
.
enforce
)
{
const
ruleUse
=
rules
[
0
].
rules
.
map
(
rule
=>
rule
.
effects
.
filter
(
effect
=>
effect
.
type
===
'use'
)
.
map
(
effect
=>
effect
.
value
)
)
// shallow flat
.
reduce
((
prev
,
next
)
=>
prev
.
concat
(
next
),
[])
// fix conflict with config.loader and config.options when using config.use
delete
rawRule
.
loader
delete
rawRule
.
options
rawRule
.
use
=
ruleUse
}
const
res
=
Object
.
assign
({},
rawRule
,
{
resource
:
resources
=>
{
currentResource
=
resources
return
true
},
resourceQuery
:
query
=>
{
const
parsed
=
qs
.
parse
(
query
.
slice
(
1
))
if
(
parsed
.
vue
==
null
)
{
return
false
}
if
(
!
conditions
)
{
return
false
}
const
fakeResourcePath
=
`
${
currentResource
}
.
${
parsed
.
lang
}
`
for
(
const
condition
of
conditions
)
{
// add support for resourceQuery
const
request
=
condition
.
property
===
'resourceQuery'
?
query
:
fakeResourcePath
if
(
condition
&&
!
condition
.
fn
(
request
))
{
return
false
}
}
return
true
}
})
delete
res
.
test
if
(
rawRule
.
oneOf
)
{
res
.
oneOf
=
rawRule
.
oneOf
.
map
(
rule
=>
cloneRule
(
rule
,
refs
))
}
return
res
}
VueLoaderPlugin
.
NS
=
NS
module
.
exports
=
VueLoaderPlugin
lib/plugin.js
View file @
59eebca0
const
qs
=
require
(
'querystring'
)
const
RuleSet
=
require
(
'webpack/lib/RuleSet'
)
const
id
=
'vue-loader-plugin'
const
NS
=
'vue-loader'
class
VueLoaderPlugin
{
apply
(
compiler
)
{
// add NS marker so that the loader can detect and report missing plugin
if
(
compiler
.
hooks
)
{
// webpack 4
compiler
.
hooks
.
compilation
.
tap
(
id
,
compilation
=>
{
let
normalModuleLoader
if
(
Object
.
isFrozen
(
compilation
.
hooks
))
{
// webpack 5
normalModuleLoader
=
require
(
'webpack/lib/NormalModule'
).
getCompilationHooks
(
compilation
).
loader
}
else
{
normalModuleLoader
=
compilation
.
hooks
.
normalModuleLoader
}
normalModuleLoader
.
tap
(
id
,
loaderContext
=>
{
loaderContext
[
NS
]
=
true
})
})
}
else
{
// webpack < 4
compiler
.
plugin
(
'compilation'
,
compilation
=>
{
compilation
.
plugin
(
'normal-module-loader'
,
loaderContext
=>
{
loaderContext
[
NS
]
=
true
})
})
}
// use webpack's RuleSet utility to normalize user rules
const
rawRules
=
compiler
.
options
.
module
.
rules
const
{
rules
}
=
new
RuleSet
(
rawRules
)
// find the rule that applies to vue files
let
vueRuleIndex
=
rawRules
.
findIndex
(
createMatcher
(
`foo.vue`
))
if
(
vueRuleIndex
<
0
)
{
vueRuleIndex
=
rawRules
.
findIndex
(
createMatcher
(
`foo.vue.html`
))
}
const
vueRule
=
rules
[
vueRuleIndex
]
if
(
!
vueRule
)
{
throw
new
Error
(
`[VueLoaderPlugin Error] No matching rule for .vue files found.\n`
+
`Make sure there is at least one root-level rule that matches .vue or .vue.html files.`
)
}
if
(
vueRule
.
oneOf
)
{
throw
new
Error
(
`[VueLoaderPlugin Error] vue-loader 15 currently does not support vue rules with oneOf.`
)
}
// get the normlized "use" for vue files
const
vueUse
=
vueRule
.
use
// get vue-loader options
const
vueLoaderUseIndex
=
vueUse
.
findIndex
(
u
=>
{
return
/^vue-loader|
(\/
|
\\
|@
)
vue-loader/
.
test
(
u
.
loader
)
})
if
(
vueLoaderUseIndex
<
0
)
{
throw
new
Error
(
`[VueLoaderPlugin Error] No matching use for vue-loader is found.\n`
+
`Make sure the rule matching .vue files include vue-loader in its use.`
)
}
// make sure vue-loader options has a known ident so that we can share
// options by reference in the template-loader by using a ref query like
// template-loader??vue-loader-options
const
vueLoaderUse
=
vueUse
[
vueLoaderUseIndex
]
vueLoaderUse
.
ident
=
'vue-loader-options'
vueLoaderUse
.
options
=
vueLoaderUse
.
options
||
{}
// for each user rule (expect the vue rule), create a cloned rule
// that targets the corresponding language blocks in *.vue files.
const
clonedRules
=
rules
.
filter
(
r
=>
r
!==
vueRule
)
.
map
(
cloneRule
)
// global pitcher (responsible for injecting template compiler loader & CSS
// post loader)
const
pitcher
=
{
loader
:
require
.
resolve
(
'./loaders/pitcher'
),
resourceQuery
:
query
=>
{
const
parsed
=
qs
.
parse
(
query
.
slice
(
1
))
return
parsed
.
vue
!=
null
},
options
:
{
cacheDirectory
:
vueLoaderUse
.
options
.
cacheDirectory
,
cacheIdentifier
:
vueLoaderUse
.
options
.
cacheIdentifier
}
}
// replace original rules
compiler
.
options
.
module
.
rules
=
[
pitcher
,
...
clonedRules
,
...
rules
]
}
}
function
createMatcher
(
fakeFile
)
{
return
(
rule
,
i
)
=>
{
// #1201 we need to skip the `include` check when locating the vue rule
const
clone
=
Object
.
assign
({},
rule
)
delete
clone
.
include
const
normalized
=
RuleSet
.
normalizeRule
(
clone
,
{},
''
)
return
(
!
rule
.
enforce
&&
normalized
.
resource
&&
normalized
.
resource
(
fakeFile
)
)
}
}
function
cloneRule
(
rule
)
{
const
{
resource
,
resourceQuery
}
=
rule
// Assuming `test` and `resourceQuery` tests are executed in series and
// synchronously (which is true based on RuleSet's implementation), we can
// save the current resource being matched from `test` so that we can access
// it in `resourceQuery`. This ensures when we use the normalized rule's
// resource check, include/exclude are matched correctly.
let
currentResource
const
res
=
Object
.
assign
({},
rule
,
{
resource
:
{
test
:
resource
=>
{
currentResource
=
resource
return
true
}
},
resourceQuery
:
query
=>
{
const
parsed
=
qs
.
parse
(
query
.
slice
(
1
))
if
(
parsed
.
vue
==
null
)
{
return
false
}
if
(
resource
&&
parsed
.
lang
==
null
)
{
return
false
}
const
fakeResourcePath
=
`
${
currentResource
}
.
${
parsed
.
lang
}
`
if
(
resource
&&
!
resource
(
fakeResourcePath
))
{
return
false
}
if
(
resourceQuery
&&
!
resourceQuery
(
query
))
{
return
false
}
return
true
}
})
if
(
rule
.
oneOf
)
{
res
.
oneOf
=
rule
.
oneOf
.
map
(
cloneRule
)
}
return
res
const
webpack
=
require
(
'webpack'
)
let
VueLoaderPlugin
=
null
if
(
webpack
.
version
[
0
]
>
4
)
{
// webpack5 and upper
VueLoaderPlugin
=
require
(
'./plugin-webpack5'
)
}
else
{
// webpack4 and lower
VueLoaderPlugin
=
require
(
'./plugin-webpack4'
)
}
VueLoaderPlugin
.
NS
=
NS
module
.
exports
=
VueLoaderPlugin
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment