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
} from '@babel/parser'
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 {
Node,
Declaration,
@ -37,6 +39,10 @@ import {
import { walk } from 'estree-walker'
import { RawSourceMap } from 'source-map'
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
const DEFINE_PROPS = 'defineProps'
@ -52,7 +58,6 @@ const isBuiltInDir = makeMap(
)
export interface SFCScriptCompileOptions {
filename: string
/**
* Production mode. Used to determine whether to generate hashed CSS variables
*/
@ -82,10 +87,9 @@ export interface ImportBinding {
*/
export function compileScript(
sfc: SFCDescriptor,
options: SFCScriptCompileOptions
options: SFCScriptCompileOptions = {}
): SFCScriptBlock {
let { script, scriptSetup, source } = sfc
const { filename } = options
let { filename, script, scriptSetup, source } = sfc
const isProd = !!options.isProd
const genSourceMap = options.sourceMap !== false
let refBindings: string[] | undefined
@ -201,10 +205,10 @@ export function compileScript(
// magic-string state
const s = new MagicString(source)
const startOffset = scriptSetup.loc.start.offset
const endOffset = scriptSetup.loc.end.offset
const scriptStartOffset = script && script.loc.start.offset
const scriptEndOffset = script && script.loc.end.offset
const startOffset = scriptSetup.start
const endOffset = scriptSetup.end
const scriptStartOffset = script && script.start
const scriptEndOffset = script && script.end
function helper(key: string): string {
helperImports.add(key)
@ -1211,7 +1215,7 @@ export function compileScript(
// 11. finalize default export
let runtimeOptions = ``
if (!hasDefaultExportName && filename) {
if (!hasDefaultExportName && filename && filename !== DEFAULT_FILENAME) {
const match = filename.match(/([^/\\]+)\.\w+$/)
if (match) {
runtimeOptions += `\n __name: '${match[1]}',`
@ -1828,44 +1832,39 @@ function getObjectOrArrayExpressionKeys(value: Node): string[] {
const templateUsageCheckCache = new LRU<string, string>(512)
function resolveTemplateUsageCheckString(sfc: SFCDescriptor) {
const { content, ast } = sfc.template!
const { content } = sfc.template!
const cached = templateUsageCheckCache.get(content)
if (cached) {
return cached
}
let code = ''
transform(createRoot([ast]), {
nodeTransforms: [
node => {
if (node.type === NodeTypes.ELEMENT) {
if (
!parserOptions.isNativeTag!(node.tag) &&
!parserOptions.isBuiltInComponent!(node.tag)
) {
code += `,${camelize(node.tag)},${capitalize(camelize(node.tag))}`
parseHTML(content, {
...webCompilerOptions,
start(tag, attrs) {
if (!isBuiltInTag(tag) && !isReservedTag(tag)) {
code += `,${camelize(tag)},${capitalize(camelize(tag))}`
}
for (let i = 0; i < attrs.length; i++) {
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++) {
const prop = node.props[i]
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
)}`
}
}
if (value) {
code += `,${processExp(value, baseName)}`
}
} 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 += ';'

View File

@ -9,6 +9,8 @@ import {
import hash from 'hash-sum'
import LRU from 'lru-cache'
export const DEFAULT_FILENAME = 'anonymous.vue'
const cache = new LRU<string, SFCDescriptor>(100)
const splitRE = /\r?\n/g
@ -26,9 +28,9 @@ export interface ParseOptions {
export function parse(options: ParseOptions): SFCDescriptor {
const {
source,
filename = '',
filename = DEFAULT_FILENAME,
compiler,
compilerParseOptions = { pad: 'line' } as VueTemplateCompilerParseOptions,
compilerParseOptions = { pad: false } as VueTemplateCompilerParseOptions,
sourceRoot = '',
needMap = true
} = options
@ -49,6 +51,8 @@ export function parse(options: ParseOptions): SFCDescriptor {
output = parseComponent(source, compilerParseOptions)
}
output.filename = filename
if (needMap) {
if (output.script && !output.script.src) {
output.script.map = generateSourceMap(
@ -73,6 +77,7 @@ export function parse(options: ParseOptions): SFCDescriptor {
})
}
}
cache.set(cacheKey, output)
return output
}

View File

@ -4,6 +4,7 @@ import { makeMap } from 'shared/util'
import { ASTAttr, WarningMessage } from 'types/compiler'
import { BindingMetadata, RawSourceMap } from './types'
import { hmrShouldReload, ImportBinding } from './compileScript'
import { DEFAULT_FILENAME } from './parse'
const splitRE = /\r?\n/g
const replaceRE = /./g
@ -42,6 +43,7 @@ export interface SFCScriptBlock extends SFCBlock {
export interface SFCDescriptor {
source: string
filename: string
template: SFCBlock | null
script: SFCScriptBlock | null
scriptSetup: SFCScriptBlock | null
@ -70,11 +72,12 @@ export interface VueTemplateCompilerParseOptions {
* Parse a single-file component (*.vue) file into an SFC Descriptor Object.
*/
export function parseComponent(
content: string,
source: string,
options: VueTemplateCompilerParseOptions = {}
): SFCDescriptor {
const sfc: SFCDescriptor = {
source: content,
source,
filename: DEFAULT_FILENAME,
template: null,
script: null,
scriptSetup: null, // TODO
@ -167,8 +170,8 @@ export function parseComponent(
function end(tag: string, start: number) {
if (depth === 1 && currentBlock) {
currentBlock.end = start
let text = content.slice(currentBlock.start, currentBlock.end)
if (options.deindent !== false) {
let text = source.slice(currentBlock.start, currentBlock.end)
if (options.deindent) {
text = deindent(text)
}
// 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') {
if (pad === 'space') {
return content.slice(0, block.start).replace(replaceRE, ' ')
return source.slice(0, block.start).replace(replaceRE, ' ')
} 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'
return Array(offset).join(padChar)
}
}
parseHTML(content, {
parseHTML(source, {
warn,
start,
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) {
this.options = options
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.dataGenFns = pluckModuleFunction(options.modules, 'genData')
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>(
modules: Array<T> | undefined,
key: K
): Array<T[K]> {
return modules ? modules.map(m => m[key]).filter(_ => _) : []
): Array<Exclude<T[K], undefined>> {
return modules ? (modules.map(m => m[key]).filter(_ => _) as any) : []
}
export function addProp(