wip: compileScript

This commit is contained in:
Evan You 2022-06-14 15:26:57 +08:00
parent 846e3165d8
commit d6c0c2a992
7 changed files with 3157 additions and 50 deletions

View File

@ -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 += ';'

View File

@ -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
} }

View File

@ -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,

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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)

View File

@ -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(