mirror of
https://github.com/vuejs/vue.git
synced 2024-11-22 04:39:46 +00:00
wip: compileScript
This commit is contained in:
parent
846e3165d8
commit
d6c0c2a992
@ -10,7 +10,9 @@ import {
|
|||||||
ParserPlugin
|
ParserPlugin
|
||||||
} from '@babel/parser'
|
} from '@babel/parser'
|
||||||
import { generateCodeFrame } from 'compiler/codeframe'
|
import { generateCodeFrame } from 'compiler/codeframe'
|
||||||
import { camelize, capitalize, makeMap } from 'shared/util'
|
import { camelize, capitalize, isBuiltInTag, makeMap } from 'shared/util'
|
||||||
|
import { parseHTML } from 'compiler/parser/html-parser'
|
||||||
|
import { baseOptions as webCompilerOptions } from 'web/compiler/options'
|
||||||
import {
|
import {
|
||||||
Node,
|
Node,
|
||||||
Declaration,
|
Declaration,
|
||||||
@ -37,6 +39,10 @@ import {
|
|||||||
import { walk } from 'estree-walker'
|
import { walk } from 'estree-walker'
|
||||||
import { RawSourceMap } from 'source-map'
|
import { RawSourceMap } from 'source-map'
|
||||||
import { warnOnce } from './warn'
|
import { warnOnce } from './warn'
|
||||||
|
import { isReservedTag } from 'web/util'
|
||||||
|
import { dirRE } from 'compiler/parser'
|
||||||
|
import { parseText } from 'compiler/parser/text-parser'
|
||||||
|
import { DEFAULT_FILENAME } from './parse'
|
||||||
|
|
||||||
// Special compiler macros
|
// Special compiler macros
|
||||||
const DEFINE_PROPS = 'defineProps'
|
const DEFINE_PROPS = 'defineProps'
|
||||||
@ -52,7 +58,6 @@ const isBuiltInDir = makeMap(
|
|||||||
)
|
)
|
||||||
|
|
||||||
export interface SFCScriptCompileOptions {
|
export interface SFCScriptCompileOptions {
|
||||||
filename: string
|
|
||||||
/**
|
/**
|
||||||
* Production mode. Used to determine whether to generate hashed CSS variables
|
* Production mode. Used to determine whether to generate hashed CSS variables
|
||||||
*/
|
*/
|
||||||
@ -82,10 +87,9 @@ export interface ImportBinding {
|
|||||||
*/
|
*/
|
||||||
export function compileScript(
|
export function compileScript(
|
||||||
sfc: SFCDescriptor,
|
sfc: SFCDescriptor,
|
||||||
options: SFCScriptCompileOptions
|
options: SFCScriptCompileOptions = {}
|
||||||
): SFCScriptBlock {
|
): SFCScriptBlock {
|
||||||
let { script, scriptSetup, source } = sfc
|
let { filename, script, scriptSetup, source } = sfc
|
||||||
const { filename } = options
|
|
||||||
const isProd = !!options.isProd
|
const isProd = !!options.isProd
|
||||||
const genSourceMap = options.sourceMap !== false
|
const genSourceMap = options.sourceMap !== false
|
||||||
let refBindings: string[] | undefined
|
let refBindings: string[] | undefined
|
||||||
@ -201,10 +205,10 @@ export function compileScript(
|
|||||||
|
|
||||||
// magic-string state
|
// magic-string state
|
||||||
const s = new MagicString(source)
|
const s = new MagicString(source)
|
||||||
const startOffset = scriptSetup.loc.start.offset
|
const startOffset = scriptSetup.start
|
||||||
const endOffset = scriptSetup.loc.end.offset
|
const endOffset = scriptSetup.end
|
||||||
const scriptStartOffset = script && script.loc.start.offset
|
const scriptStartOffset = script && script.start
|
||||||
const scriptEndOffset = script && script.loc.end.offset
|
const scriptEndOffset = script && script.end
|
||||||
|
|
||||||
function helper(key: string): string {
|
function helper(key: string): string {
|
||||||
helperImports.add(key)
|
helperImports.add(key)
|
||||||
@ -1211,7 +1215,7 @@ export function compileScript(
|
|||||||
|
|
||||||
// 11. finalize default export
|
// 11. finalize default export
|
||||||
let runtimeOptions = ``
|
let runtimeOptions = ``
|
||||||
if (!hasDefaultExportName && filename) {
|
if (!hasDefaultExportName && filename && filename !== DEFAULT_FILENAME) {
|
||||||
const match = filename.match(/([^/\\]+)\.\w+$/)
|
const match = filename.match(/([^/\\]+)\.\w+$/)
|
||||||
if (match) {
|
if (match) {
|
||||||
runtimeOptions += `\n __name: '${match[1]}',`
|
runtimeOptions += `\n __name: '${match[1]}',`
|
||||||
@ -1828,44 +1832,39 @@ function getObjectOrArrayExpressionKeys(value: Node): string[] {
|
|||||||
const templateUsageCheckCache = new LRU<string, string>(512)
|
const templateUsageCheckCache = new LRU<string, string>(512)
|
||||||
|
|
||||||
function resolveTemplateUsageCheckString(sfc: SFCDescriptor) {
|
function resolveTemplateUsageCheckString(sfc: SFCDescriptor) {
|
||||||
const { content, ast } = sfc.template!
|
const { content } = sfc.template!
|
||||||
const cached = templateUsageCheckCache.get(content)
|
const cached = templateUsageCheckCache.get(content)
|
||||||
if (cached) {
|
if (cached) {
|
||||||
return cached
|
return cached
|
||||||
}
|
}
|
||||||
|
|
||||||
let code = ''
|
let code = ''
|
||||||
transform(createRoot([ast]), {
|
|
||||||
nodeTransforms: [
|
parseHTML(content, {
|
||||||
node => {
|
...webCompilerOptions,
|
||||||
if (node.type === NodeTypes.ELEMENT) {
|
start(tag, attrs) {
|
||||||
if (
|
if (!isBuiltInTag(tag) && !isReservedTag(tag)) {
|
||||||
!parserOptions.isNativeTag!(node.tag) &&
|
code += `,${camelize(tag)},${capitalize(camelize(tag))}`
|
||||||
!parserOptions.isBuiltInComponent!(node.tag)
|
}
|
||||||
) {
|
for (let i = 0; i < attrs.length; i++) {
|
||||||
code += `,${camelize(node.tag)},${capitalize(camelize(node.tag))}`
|
const { name, value } = attrs[i]
|
||||||
|
if (dirRE.test(name)) {
|
||||||
|
const baseName = name.replace(dirRE, '')
|
||||||
|
if (!isBuiltInDir(baseName)) {
|
||||||
|
code += `,v${capitalize(camelize(baseName))}`
|
||||||
}
|
}
|
||||||
for (let i = 0; i < node.props.length; i++) {
|
if (value) {
|
||||||
const prop = node.props[i]
|
code += `,${processExp(value, baseName)}`
|
||||||
if (prop.type === NodeTypes.DIRECTIVE) {
|
|
||||||
if (!isBuiltInDir(prop.name)) {
|
|
||||||
code += `,v${capitalize(camelize(prop.name))}`
|
|
||||||
}
|
|
||||||
if (prop.exp) {
|
|
||||||
code += `,${processExp(
|
|
||||||
(prop.exp as SimpleExpressionNode).content,
|
|
||||||
prop.name
|
|
||||||
)}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if (node.type === NodeTypes.INTERPOLATION) {
|
|
||||||
code += `,${processExp(
|
|
||||||
(node.content as SimpleExpressionNode).content
|
|
||||||
)}`
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
},
|
||||||
|
chars(text) {
|
||||||
|
const res = parseText(text)
|
||||||
|
if (res) {
|
||||||
|
code += `,${processExp(res.expression)}`
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
code += ';'
|
code += ';'
|
||||||
|
@ -9,6 +9,8 @@ import {
|
|||||||
import hash from 'hash-sum'
|
import hash from 'hash-sum'
|
||||||
import LRU from 'lru-cache'
|
import LRU from 'lru-cache'
|
||||||
|
|
||||||
|
export const DEFAULT_FILENAME = 'anonymous.vue'
|
||||||
|
|
||||||
const cache = new LRU<string, SFCDescriptor>(100)
|
const cache = new LRU<string, SFCDescriptor>(100)
|
||||||
|
|
||||||
const splitRE = /\r?\n/g
|
const splitRE = /\r?\n/g
|
||||||
@ -26,9 +28,9 @@ export interface ParseOptions {
|
|||||||
export function parse(options: ParseOptions): SFCDescriptor {
|
export function parse(options: ParseOptions): SFCDescriptor {
|
||||||
const {
|
const {
|
||||||
source,
|
source,
|
||||||
filename = '',
|
filename = DEFAULT_FILENAME,
|
||||||
compiler,
|
compiler,
|
||||||
compilerParseOptions = { pad: 'line' } as VueTemplateCompilerParseOptions,
|
compilerParseOptions = { pad: false } as VueTemplateCompilerParseOptions,
|
||||||
sourceRoot = '',
|
sourceRoot = '',
|
||||||
needMap = true
|
needMap = true
|
||||||
} = options
|
} = options
|
||||||
@ -49,6 +51,8 @@ export function parse(options: ParseOptions): SFCDescriptor {
|
|||||||
output = parseComponent(source, compilerParseOptions)
|
output = parseComponent(source, compilerParseOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
output.filename = filename
|
||||||
|
|
||||||
if (needMap) {
|
if (needMap) {
|
||||||
if (output.script && !output.script.src) {
|
if (output.script && !output.script.src) {
|
||||||
output.script.map = generateSourceMap(
|
output.script.map = generateSourceMap(
|
||||||
@ -73,6 +77,7 @@ export function parse(options: ParseOptions): SFCDescriptor {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cache.set(cacheKey, output)
|
cache.set(cacheKey, output)
|
||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import { makeMap } from 'shared/util'
|
|||||||
import { ASTAttr, WarningMessage } from 'types/compiler'
|
import { ASTAttr, WarningMessage } from 'types/compiler'
|
||||||
import { BindingMetadata, RawSourceMap } from './types'
|
import { BindingMetadata, RawSourceMap } from './types'
|
||||||
import { hmrShouldReload, ImportBinding } from './compileScript'
|
import { hmrShouldReload, ImportBinding } from './compileScript'
|
||||||
|
import { DEFAULT_FILENAME } from './parse'
|
||||||
|
|
||||||
const splitRE = /\r?\n/g
|
const splitRE = /\r?\n/g
|
||||||
const replaceRE = /./g
|
const replaceRE = /./g
|
||||||
@ -42,6 +43,7 @@ export interface SFCScriptBlock extends SFCBlock {
|
|||||||
|
|
||||||
export interface SFCDescriptor {
|
export interface SFCDescriptor {
|
||||||
source: string
|
source: string
|
||||||
|
filename: string
|
||||||
template: SFCBlock | null
|
template: SFCBlock | null
|
||||||
script: SFCScriptBlock | null
|
script: SFCScriptBlock | null
|
||||||
scriptSetup: SFCScriptBlock | null
|
scriptSetup: SFCScriptBlock | null
|
||||||
@ -70,11 +72,12 @@ export interface VueTemplateCompilerParseOptions {
|
|||||||
* Parse a single-file component (*.vue) file into an SFC Descriptor Object.
|
* Parse a single-file component (*.vue) file into an SFC Descriptor Object.
|
||||||
*/
|
*/
|
||||||
export function parseComponent(
|
export function parseComponent(
|
||||||
content: string,
|
source: string,
|
||||||
options: VueTemplateCompilerParseOptions = {}
|
options: VueTemplateCompilerParseOptions = {}
|
||||||
): SFCDescriptor {
|
): SFCDescriptor {
|
||||||
const sfc: SFCDescriptor = {
|
const sfc: SFCDescriptor = {
|
||||||
source: content,
|
source,
|
||||||
|
filename: DEFAULT_FILENAME,
|
||||||
template: null,
|
template: null,
|
||||||
script: null,
|
script: null,
|
||||||
scriptSetup: null, // TODO
|
scriptSetup: null, // TODO
|
||||||
@ -167,8 +170,8 @@ export function parseComponent(
|
|||||||
function end(tag: string, start: number) {
|
function end(tag: string, start: number) {
|
||||||
if (depth === 1 && currentBlock) {
|
if (depth === 1 && currentBlock) {
|
||||||
currentBlock.end = start
|
currentBlock.end = start
|
||||||
let text = content.slice(currentBlock.start, currentBlock.end)
|
let text = source.slice(currentBlock.start, currentBlock.end)
|
||||||
if (options.deindent !== false) {
|
if (options.deindent) {
|
||||||
text = deindent(text)
|
text = deindent(text)
|
||||||
}
|
}
|
||||||
// pad content so that linters and pre-processors can output correct
|
// pad content so that linters and pre-processors can output correct
|
||||||
@ -184,15 +187,15 @@ export function parseComponent(
|
|||||||
|
|
||||||
function padContent(block: SFCBlock, pad: true | 'line' | 'space') {
|
function padContent(block: SFCBlock, pad: true | 'line' | 'space') {
|
||||||
if (pad === 'space') {
|
if (pad === 'space') {
|
||||||
return content.slice(0, block.start).replace(replaceRE, ' ')
|
return source.slice(0, block.start).replace(replaceRE, ' ')
|
||||||
} else {
|
} else {
|
||||||
const offset = content.slice(0, block.start).split(splitRE).length
|
const offset = source.slice(0, block.start).split(splitRE).length
|
||||||
const padChar = block.type === 'script' && !block.lang ? '//\n' : '\n'
|
const padChar = block.type === 'script' && !block.lang ? '//\n' : '\n'
|
||||||
return Array(offset).join(padChar)
|
return Array(offset).join(padChar)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
parseHTML(content, {
|
parseHTML(source, {
|
||||||
warn,
|
warn,
|
||||||
start,
|
start,
|
||||||
end,
|
end,
|
||||||
|
1393
packages/compiler-sfc/test/__snapshots__/compileScript.spec.ts.snap
Normal file
1393
packages/compiler-sfc/test/__snapshots__/compileScript.spec.ts.snap
Normal file
File diff suppressed because it is too large
Load Diff
1708
packages/compiler-sfc/test/compileScript.spec.ts
Normal file
1708
packages/compiler-sfc/test/compileScript.spec.ts
Normal file
File diff suppressed because it is too large
Load Diff
@ -36,7 +36,6 @@ export class CodegenState {
|
|||||||
constructor(options: CompilerOptions) {
|
constructor(options: CompilerOptions) {
|
||||||
this.options = options
|
this.options = options
|
||||||
this.warn = options.warn || baseWarn
|
this.warn = options.warn || baseWarn
|
||||||
//@ts-expect-error `this.transforms ` is a different type than `options.modules.transformCode`
|
|
||||||
this.transforms = pluckModuleFunction(options.modules, 'transformCode')
|
this.transforms = pluckModuleFunction(options.modules, 'transformCode')
|
||||||
this.dataGenFns = pluckModuleFunction(options.modules, 'genData')
|
this.dataGenFns = pluckModuleFunction(options.modules, 'genData')
|
||||||
this.directives = extend(extend({}, baseDirectives), options.directives)
|
this.directives = extend(extend({}, baseDirectives), options.directives)
|
||||||
|
@ -13,8 +13,8 @@ export function baseWarn(msg: string, range?: Range) {
|
|||||||
export function pluckModuleFunction<T, K extends keyof T>(
|
export function pluckModuleFunction<T, K extends keyof T>(
|
||||||
modules: Array<T> | undefined,
|
modules: Array<T> | undefined,
|
||||||
key: K
|
key: K
|
||||||
): Array<T[K]> {
|
): Array<Exclude<T[K], undefined>> {
|
||||||
return modules ? modules.map(m => m[key]).filter(_ => _) : []
|
return modules ? (modules.map(m => m[key]).filter(_ => _) as any) : []
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addProp(
|
export function addProp(
|
||||||
|
Loading…
Reference in New Issue
Block a user