mirror of
https://github.com/vuejs/vue.git
synced 2024-11-21 20:28:54 +00:00
wip: compileScript
This commit is contained in:
parent
846e3165d8
commit
d6c0c2a992
@ -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 += ';'
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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,
|
||||
|
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) {
|
||||
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)
|
||||
|
@ -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(
|
||||
|
Loading…
Reference in New Issue
Block a user