Skip to content

Commit 55de28c

Browse files
committed
feat: use script-analyzed bindings when compiling template
1 parent 52abb32 commit 55de28c

14 files changed

+192
-128
lines changed

Diff for: .eslintrc.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
module.exports = {
22
root: true,
3-
extends: ['plugin:vue-libs/recommended']
3+
extends: ['plugin:vue-libs/recommended'],
4+
rules: {
5+
indent: 'off',
6+
'space-before-function-paren': 'off'
7+
}
48
}

Diff for: lib/codegen/customBlocks.js

+22-12
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,31 @@
11
const qs = require('querystring')
22
const { attrsToQuery } = require('./utils')
33

4-
module.exports = function genCustomBlocksCode (
4+
module.exports = function genCustomBlocksCode(
55
blocks,
66
resourcePath,
77
resourceQuery,
88
stringifyRequest
99
) {
10-
return `\n/* custom blocks */\n` + blocks.map((block, i) => {
11-
const src = block.attrs.src || resourcePath
12-
const attrsQuery = attrsToQuery(block.attrs)
13-
const issuerQuery = block.attrs.src ? `&issuerPath=${qs.escape(resourcePath)}` : ''
14-
const inheritQuery = resourceQuery ? `&${resourceQuery.slice(1)}` : ''
15-
const query = `?vue&type=custom&index=${i}&blockType=${qs.escape(block.type)}${issuerQuery}${attrsQuery}${inheritQuery}`
16-
return (
17-
`import block${i} from ${stringifyRequest(src + query)}\n` +
18-
`if (typeof block${i} === 'function') block${i}(component)`
19-
)
20-
}).join(`\n`) + `\n`
10+
return (
11+
`\n/* custom blocks */\n` +
12+
blocks
13+
.map((block, i) => {
14+
const src = block.attrs.src || resourcePath
15+
const attrsQuery = attrsToQuery(block.attrs)
16+
const issuerQuery = block.attrs.src
17+
? `&issuerPath=${qs.escape(resourcePath)}`
18+
: ''
19+
const inheritQuery = resourceQuery ? `&${resourceQuery.slice(1)}` : ''
20+
const query = `?vue&type=custom&index=${i}&blockType=${qs.escape(
21+
block.type
22+
)}${issuerQuery}${attrsQuery}${inheritQuery}`
23+
return (
24+
`import block${i} from ${stringifyRequest(src + query)}\n` +
25+
`if (typeof block${i} === 'function') block${i}(component)`
26+
)
27+
})
28+
.join(`\n`) +
29+
`\n`
30+
)
2131
}

Diff for: lib/codegen/styleInjection.js

+12-8
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ const { attrsToQuery } = require('./utils')
22
const hotReloadAPIPath = JSON.stringify(require.resolve('vue-hot-reload-api'))
33
const nonWhitespaceRE = /\S+/
44

5-
module.exports = function genStyleInjectionCode (
5+
module.exports = function genStyleInjectionCode(
66
loaderContext,
77
styles,
88
id,
@@ -18,7 +18,7 @@ module.exports = function genStyleInjectionCode (
1818
let hasCSSModules = false
1919
const cssModuleNames = new Map()
2020

21-
function genStyleRequest (style, i) {
21+
function genStyleRequest(style, i) {
2222
const src = style.src || resourcePath
2323
const attrsQuery = attrsToQuery(style.attrs, 'css')
2424
const inheritQuery = `&${loaderContext.resourceQuery.slice(1)}`
@@ -29,7 +29,7 @@ module.exports = function genStyleInjectionCode (
2929
return stringifyRequest(src + query)
3030
}
3131

32-
function genCSSModulesCode (style, request, i) {
32+
function genCSSModulesCode(style, request, i) {
3333
hasCSSModules = true
3434

3535
const moduleName = style.module === true ? '$style' : style.module
@@ -71,7 +71,8 @@ module.exports = function genStyleInjectionCode (
7171
}
7272

7373
// empty styles: with no `src` specified or only contains whitespaces
74-
const isNotEmptyStyle = style => style.src || nonWhitespaceRE.test(style.content)
74+
const isNotEmptyStyle = (style) =>
75+
style.src || nonWhitespaceRE.test(style.content)
7576
// explicit injection is needed in SSR (for critical CSS collection)
7677
// or in Shadow Mode (for injection into shadow root)
7778
// In these modes, vue-style-loader exports objects with the __inject__
@@ -89,10 +90,9 @@ module.exports = function genStyleInjectionCode (
8990
styles.forEach((style, i) => {
9091
if (isNotEmptyStyle(style)) {
9192
const request = genStyleRequest(style, i)
92-
styleInjectionCode += (
93+
styleInjectionCode +=
9394
`var style${i} = require(${request})\n` +
9495
`if (style${i}.__inject__) style${i}.__inject__(context)\n`
95-
)
9696
if (style.module) genCSSModulesCode(style, request, i)
9797
}
9898
})
@@ -112,11 +112,15 @@ function injectStyles (context) {
112112
${styleInjectionCode}
113113
}
114114
115-
${needsHotReload ? `
115+
${
116+
needsHotReload
117+
? `
116118
module.hot && module.hot.dispose(function (data) {
117119
disposed = true
118120
})
119-
` : ``}
121+
`
122+
: ``
123+
}
120124
121125
${cssModulesHotReloadCode}
122126
`.trim()

Diff for: lib/codegen/utils.js

+1-6
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,7 @@ const qs = require('querystring')
22

33
// these are built-in query parameters so should be ignored
44
// if the user happen to add them as attrs
5-
const ignoreList = [
6-
'id',
7-
'index',
8-
'src',
9-
'type'
10-
]
5+
const ignoreList = ['id', 'index', 'src', 'type']
116

127
// transform the attrs on a SFC block descriptor into a resourceQuery string
138
exports.attrsToQuery = (attrs, langFallback) => {

Diff for: lib/compiler.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@ exports.resolveCompiler = function (ctx, loaderContext) {
2626
})
2727
}
2828

29-
function loadFromContext (path, ctx) {
29+
function loadFromContext(path, ctx) {
3030
return require(require.resolve(path, {
3131
paths: [ctx]
3232
}))
3333
}
3434

35-
function loadTemplateCompiler (ctx, loaderContext) {
35+
function loadTemplateCompiler(ctx, loaderContext) {
3636
try {
3737
return loadFromContext('vue-template-compiler', ctx)
3838
} catch (e) {

Diff for: lib/descriptorCache.js

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
const fs = require('fs')
2+
const path = require('path')
3+
const { resolveCompiler } = require('./compiler')
4+
5+
const cache = new Map()
6+
7+
exports.setDescriptor = function setDescriptor(filename, entry) {
8+
cache.set(cleanQuery(filename), entry)
9+
}
10+
11+
exports.getDescriptor = function getDescriptor(
12+
filename,
13+
options,
14+
loaderContext
15+
) {
16+
filename = cleanQuery(filename)
17+
if (cache.has(filename)) {
18+
return cache.get(filename)
19+
}
20+
21+
// This function should only be called after the descriptor has been
22+
// cached by the main loader.
23+
// If this is somehow called without a cache hit, it's probably due to sub
24+
// loaders being run in separate threads. The only way to deal with this is to
25+
// read from disk directly...
26+
const source = fs.readFileSync(filename, 'utf-8')
27+
const sourceRoot = path.dirname(
28+
path.relative(loaderContext.rootContext, loaderContext.resourcePath)
29+
)
30+
const { compiler, templateCompiler } = resolveCompiler(
31+
loaderContext.rootContext
32+
)
33+
const descriptor = compiler.parse({
34+
source,
35+
compiler: options.compiler || templateCompiler,
36+
filename,
37+
sourceRoot,
38+
needMap: loaderContext.sourceMap
39+
})
40+
cache.set(filename, descriptor)
41+
return descriptor
42+
}
43+
44+
function cleanQuery(str) {
45+
const i = str.indexOf('?')
46+
return i > 0 ? str.slice(0, i) : str
47+
}

Diff for: lib/index.js

+7-6
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const genCustomBlocksCode = require('./codegen/customBlocks')
1111
const componentNormalizerPath = require.resolve('./runtime/componentNormalizer')
1212
const { NS } = require('./plugin')
1313
const { resolveCompiler } = require('./compiler')
14+
const { setDescriptor } = require('./descriptorCache')
1415

1516
let errorEmitted = false
1617

@@ -27,7 +28,7 @@ module.exports = function (source) {
2728
errorEmitted = true
2829
}
2930

30-
const stringifyRequest = r => loaderUtils.stringifyRequest(loaderContext, r)
31+
const stringifyRequest = (r) => loaderUtils.stringifyRequest(loaderContext, r)
3132

3233
const {
3334
target,
@@ -52,10 +53,7 @@ module.exports = function (source) {
5253
const context = rootContext || process.cwd()
5354
const sourceRoot = path.dirname(path.relative(context, resourcePath))
5455

55-
const { compiler, templateCompiler } = resolveCompiler(
56-
rootContext,
57-
loaderContext
58-
)
56+
const { compiler, templateCompiler } = resolveCompiler(context, loaderContext)
5957

6058
const descriptor = compiler.parse({
6159
source,
@@ -65,6 +63,9 @@ module.exports = function (source) {
6563
needMap: sourceMap
6664
})
6765

66+
// cache descriptor
67+
setDescriptor(filename, descriptor)
68+
6869
// if the query has a type field, this is a language block request
6970
// e.g. foo.vue?type=template&id=xxxxx
7071
// and we will return early
@@ -92,7 +93,7 @@ module.exports = function (source) {
9293
)
9394

9495
// feature information
95-
const hasScoped = descriptor.styles.some(s => s.scoped)
96+
const hasScoped = descriptor.styles.some((s) => s.scoped)
9697
const hasFunctional =
9798
descriptor.template && descriptor.template.attrs.functional
9899
const needsHotReload =

Diff for: lib/loaders/pitcher.js

+27-27
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,18 @@ const templateLoaderPath = require.resolve('./templateLoader')
66
const stylePostLoaderPath = require.resolve('./stylePostLoader')
77
const { resolveCompiler } = require('../compiler')
88

9-
const isESLintLoader = l => /(\/|\\|@)eslint-loader/.test(l.path)
10-
const isNullLoader = l => /(\/|\\|@)null-loader/.test(l.path)
11-
const isCSSLoader = l => /(\/|\\|@)css-loader/.test(l.path)
12-
const isCacheLoader = l => /(\/|\\|@)cache-loader/.test(l.path)
13-
const isPitcher = l => l.path !== __filename
14-
const isPreLoader = l => !l.pitchExecuted
15-
const isPostLoader = l => l.pitchExecuted
16-
17-
const dedupeESLintLoader = loaders => {
9+
const isESLintLoader = (l) => /(\/|\\|@)eslint-loader/.test(l.path)
10+
const isNullLoader = (l) => /(\/|\\|@)null-loader/.test(l.path)
11+
const isCSSLoader = (l) => /(\/|\\|@)css-loader/.test(l.path)
12+
const isCacheLoader = (l) => /(\/|\\|@)cache-loader/.test(l.path)
13+
const isPitcher = (l) => l.path !== __filename
14+
const isPreLoader = (l) => !l.pitchExecuted
15+
const isPostLoader = (l) => l.pitchExecuted
16+
17+
const dedupeESLintLoader = (loaders) => {
1818
const res = []
1919
let seen = false
20-
loaders.forEach(l => {
20+
loaders.forEach((l) => {
2121
if (!isESLintLoader(l)) {
2222
res.push(l)
2323
} else if (!seen) {
@@ -28,8 +28,8 @@ const dedupeESLintLoader = loaders => {
2828
return res
2929
}
3030

31-
const shouldIgnoreCustomBlock = loaders => {
32-
const actualLoaders = loaders.filter(loader => {
31+
const shouldIgnoreCustomBlock = (loaders) => {
32+
const actualLoaders = loaders.filter((loader) => {
3333
// vue-loader
3434
if (loader.path === selfPath) {
3535
return false
@@ -45,7 +45,7 @@ const shouldIgnoreCustomBlock = loaders => {
4545
return actualLoaders.length === 0
4646
}
4747

48-
module.exports = code => code
48+
module.exports = (code) => code
4949

5050
// This pitching loader is responsible for intercepting all vue block requests
5151
// and transform it into appropriate requests.
@@ -62,7 +62,7 @@ module.exports.pitch = function (remainingRequest) {
6262
// if this is an inline block, since the whole file itself is being linted,
6363
// remove eslint-loader to avoid duplicate linting.
6464
if (/\.vue$/.test(this.resourcePath)) {
65-
loaders = loaders.filter(l => !isESLintLoader(l))
65+
loaders = loaders.filter((l) => !isESLintLoader(l))
6666
} else {
6767
// This is a src import. Just make sure there's not more than 1 instance
6868
// of eslint present.
@@ -78,7 +78,7 @@ module.exports.pitch = function (remainingRequest) {
7878
return
7979
}
8080

81-
const genRequest = loaders => {
81+
const genRequest = (loaders) => {
8282
// Important: dedupe since both the original rule
8383
// and the cloned rule would match a source import request.
8484
// also make sure to dedupe based on loader path.
@@ -90,7 +90,7 @@ module.exports.pitch = function (remainingRequest) {
9090
const seen = new Map()
9191
const loaderStrings = []
9292

93-
loaders.forEach(loader => {
93+
loaders.forEach((loader) => {
9494
const identifier =
9595
typeof loader === 'string' ? loader : loader.path + loader.query
9696
const request = typeof loader === 'string' ? loader : loader.request
@@ -133,17 +133,17 @@ module.exports.pitch = function (remainingRequest) {
133133
const cacheLoader =
134134
cacheDirectory && cacheIdentifier
135135
? [
136-
`${require.resolve('cache-loader')}?${JSON.stringify({
137-
// For some reason, webpack fails to generate consistent hash if we
138-
// use absolute paths here, even though the path is only used in a
139-
// comment. For now we have to ensure cacheDirectory is a relative path.
140-
cacheDirectory: (path.isAbsolute(cacheDirectory)
141-
? path.relative(process.cwd(), cacheDirectory)
142-
: cacheDirectory
143-
).replace(/\\/g, '/'),
144-
cacheIdentifier: hash(cacheIdentifier) + '-vue-loader-template'
145-
})}`
146-
]
136+
`${require.resolve('cache-loader')}?${JSON.stringify({
137+
// For some reason, webpack fails to generate consistent hash if we
138+
// use absolute paths here, even though the path is only used in a
139+
// comment. For now we have to ensure cacheDirectory is a relative path.
140+
cacheDirectory: (path.isAbsolute(cacheDirectory)
141+
? path.relative(process.cwd(), cacheDirectory)
142+
: cacheDirectory
143+
).replace(/\\/g, '/'),
144+
cacheIdentifier: hash(cacheIdentifier) + '-vue-loader-template'
145+
})}`
146+
]
147147
: []
148148

149149
const preLoaders = loaders.filter(isPreLoader)

0 commit comments

Comments
 (0)