Skip to content

Commit 905b813

Browse files
committed
refactor: options cache without using this._compilation
1 parent 4b6c843 commit 905b813

File tree

7 files changed

+98
-13
lines changed

7 files changed

+98
-13
lines changed

lib/helpers.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ function ensureBang (loader) {
6767
}
6868

6969
function resolveLoaders (
70+
optionsId,
7071
options,
7172
moduleId,
7273
isProduction,
@@ -97,6 +98,7 @@ function resolveLoaders (
9798
id: moduleId,
9899
hasScoped,
99100
hasComment,
101+
optionsId,
100102
buble: bubleTemplateOptions
101103
})
102104

@@ -142,6 +144,7 @@ function resolveLoaders (
142144
}
143145

144146
module.exports = function createHelpers (
147+
optionsId,
145148
loaderContext,
146149
options,
147150
moduleId,
@@ -161,6 +164,7 @@ module.exports = function createHelpers (
161164
preLoaders,
162165
postLoaders
163166
} = resolveLoaders(
167+
optionsId,
164168
options,
165169
moduleId,
166170
isProduction,
@@ -304,6 +308,7 @@ module.exports = function createHelpers (
304308
'?' +
305309
JSON.stringify({
306310
// a marker for vue-style-loader to know that this is an import from a vue file
311+
optionsId,
307312
vue: true,
308313
id: moduleId,
309314
scoped: !!scoped,
@@ -359,8 +364,8 @@ module.exports = function createHelpers (
359364
defaultLoaders.html +
360365
'!' +
361366
templatePreprocessorPath +
362-
'?engine=' +
363-
lang +
367+
`?engine=${lang}` +
368+
`&optionsId=${optionsId}` +
364369
'!'
365370
)
366371
case 'styles':

lib/loader.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const parse = require('./parser')
44
const createHelpers = require('./helpers')
55
const loaderUtils = require('loader-utils')
66
const normalize = require('./utils/normalize')
7+
const { saveOptions } = require('./utils/options-cache')
78
const hotReloadAPIPath = normalize.dep('vue-hot-reload-api')
89
const componentNormalizerPath = normalize.lib('runtime/component-normalizer')
910

@@ -19,12 +20,10 @@ module.exports = function (content) {
1920
// enabled via vue-cli's --target web-component
2021
const isShadowMode = !!options.shadowMode
2122

22-
// share options between the main loader of style/template loaders
23-
Object.defineProperty(this._compilation, '__vueOptions__', {
24-
value: options,
25-
enumerable: false,
26-
configurable: true
27-
})
23+
// share options between the main loader of style/template loaders.
24+
// to support having multiple uses of vue-loader with different options,
25+
// we cache and retrieve options for each unique options object.
26+
const optionsId = saveOptions(options)
2827

2928
const filePath = this.resourcePath
3029
const fileName = path.basename(filePath)
@@ -68,6 +67,7 @@ module.exports = function (content) {
6867
getRequestString,
6968
getSrcRequestString
7069
} = createHelpers(
70+
optionsId,
7171
loaderContext,
7272
options,
7373
moduleId,

lib/selector.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ const parse = require('./parser')
77
const loaderUtils = require('loader-utils')
88

99
module.exports = function (content) {
10-
this.cacheable()
1110
const query = loaderUtils.getOptions(this) || {}
1211
const context = (this._compiler && this._compiler.context) || this.options.context || process.cwd()
1312
let filename = path.basename(this.resourcePath)

lib/style-compiler/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const postcss = require('postcss')
22
const loaderUtils = require('loader-utils')
3+
const { loadOptions } = require('../utils/options-cache')
34
const loadPostcssConfig = require('./load-postcss-config')
45

56
const trim = require('./plugins/trim')
@@ -8,7 +9,7 @@ const scopeId = require('./plugins/scope-id')
89
module.exports = function (css, map) {
910
const cb = this.async()
1011
const loaderOptions = loaderUtils.getOptions(this) || {}
11-
const inlineConfig = (this._compilation.__vueOptions__ || {}).postcss
12+
const inlineConfig = loadOptions(loaderOptions.optionsId).postcss
1213

1314
loadPostcssConfig(this, inlineConfig)
1415
.then(config => {

lib/template-compiler/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ const prettier = require('prettier')
22
const loaderUtils = require('loader-utils')
33
const normalize = require('../utils/normalize')
44
const compiler = require('vue-template-compiler')
5+
const { loadOptions } = require('../utils/options-cache')
56
const transpile = require('vue-template-es2015-compiler')
67
const hotReloadAPIPath = normalize.dep('vue-hot-reload-api')
78
const transformRequire = require('./modules/transform-require')
@@ -10,8 +11,8 @@ const transformSrcset = require('./modules/transform-srcset')
1011
module.exports = function (html) {
1112
const isServer = this.target === 'node'
1213
const isProduction = this.minimize || process.env.NODE_ENV === 'production'
13-
const vueOptions = this._compilation.__vueOptions__ || {}
1414
const options = loaderUtils.getOptions(this) || {}
15+
const vueOptions = loadOptions(options.optionsId)
1516
const needsHotReload = !isServer && !isProduction && vueOptions.hotReload !== false
1617
const defaultModules = [transformRequire(vueOptions.transformToRequire), transformSrcset()]
1718

lib/template-compiler/preprocessor.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
const cons = require('consolidate')
44
const loaderUtils = require('loader-utils')
5+
const { loadOptions } = require('../utils/options-cache')
56

67
module.exports = function (content) {
78
const callback = this.async()
@@ -19,8 +20,9 @@ module.exports = function (content) {
1920
}
2021

2122
// allow passing options to the template preprocessor via `template` option
22-
if (this._compilation.__vueOptions__) {
23-
Object.assign(opt, this._compilation.__vueOptions__.template)
23+
const vueOptions = loadOptions(opt.optionsId)
24+
if (vueOptions.template) {
25+
Object.assign(opt, vueOptions.template)
2426
}
2527

2628
// for relative includes

lib/utils/options-cache.js

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
Inter-loader Option idToOptions
3+
4+
Vue-loader works by delegating language blocks to sub-loaders.
5+
The main loader needs to share its options object with the sub-loaders.
6+
Technically we should pass everything the sub loaders need via their own
7+
options, however there are two problems with this approach:
8+
9+
1. Some options (e.g. postcss, compilerModules) may contain non-serializable
10+
values and cannot be passed via inline requests
11+
2. Passing everything via inline requests makes the module string extremely
12+
verbose, and can be quite annoying in error messages.
13+
14+
To get around this, we cache the options in this module here in order to share
15+
it between loaders.
16+
17+
- In order to support multiple uses of vue-loader with different options,
18+
each options object is cached with an id.
19+
- To share options across threads in threadMode, options are serialized and
20+
cached on disk.
21+
*/
22+
23+
const fs = require('fs')
24+
const path = require('path')
25+
const hash = require('hash-sum')
26+
27+
const optionsToId = new Map()
28+
const idToOptions = new Map()
29+
30+
exports.saveOptions = options => {
31+
if (optionsToId.has(options)) {
32+
return optionsToId.get(options)
33+
}
34+
35+
const threadMode = options.threadMode
36+
const serialized = threadMode ? serialize(options) : null
37+
const id = serialized ? hash(serialized) : String(idToOptions.size)
38+
39+
idToOptions.set(id, options)
40+
optionsToId.set(options, id)
41+
42+
if (serialized) {
43+
const fsidToOptionsPath = getidToOptionsPath(id)
44+
if (!fs.existsSync(fsidToOptionsPath)) {
45+
fs.writeFileSync(fsidToOptionsPath, serialized)
46+
}
47+
}
48+
49+
return id
50+
}
51+
52+
exports.loadOptions = id => {
53+
const res = idToOptions.get(id)
54+
if (res) {
55+
return res
56+
}
57+
const fsidToOptionsPath = getidToOptionsPath(id)
58+
if (fs.existsSync(fsidToOptionsPath)) {
59+
return JSON.parse(fs.readFileSync(fsidToOptionsPath, 'utf-8'))
60+
} else {
61+
return {}
62+
}
63+
}
64+
65+
function serialize (options) {
66+
let res
67+
try {
68+
res = JSON.stringify(options)
69+
} catch (e) {
70+
throw new Error(`options must be JSON serializable in thread mode.`)
71+
}
72+
return res
73+
}
74+
75+
function getidToOptionsPath (id) {
76+
return path.resolve(__dirname, `.options-idToOptions-${id}`)
77+
}

0 commit comments

Comments
 (0)