| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207 |
- import type { CallExpression, Node, ObjectPattern, Program } from '@babel/types'
- import type { SFCDescriptor } from '../parse'
- import { generateCodeFrame, isArray } from '@vue/shared'
- import { type ParserPlugin, parse as babelParse } from '@babel/parser'
- import type { ImportBinding, SFCScriptCompileOptions } from '../compileScript'
- import type { PropsDestructureBindings } from './defineProps'
- import type { ModelDecl } from './defineModel'
- import type { BindingMetadata } from '../../../compiler-core/src'
- import MagicString from 'magic-string'
- import type { TypeScope } from './resolveType'
- import { warn } from '../warn'
- import { isJS, isTS } from './utils'
- export class ScriptCompileContext {
- isJS: boolean
- isTS: boolean
- isCE = false
- scriptAst: Program | null
- scriptSetupAst: Program | null
- source: string = this.descriptor.source
- filename: string = this.descriptor.filename
- s: MagicString = new MagicString(this.source)
- startOffset: number | undefined =
- this.descriptor.scriptSetup?.loc.start.offset
- endOffset: number | undefined = this.descriptor.scriptSetup?.loc.end.offset
- // import / type analysis
- scope?: TypeScope
- globalScopes?: TypeScope[]
- userImports: Record<string, ImportBinding> = Object.create(null)
- // macros presence check
- hasDefinePropsCall = false
- hasDefineEmitCall = false
- hasDefineExposeCall = false
- hasDefaultExportName = false
- hasDefaultExportRender = false
- hasDefineOptionsCall = false
- hasDefineSlotsCall = false
- hasDefineModelCall = false
- // defineProps
- propsCall: CallExpression | undefined
- propsDecl: Node | undefined
- propsRuntimeDecl: Node | undefined
- propsTypeDecl: Node | undefined
- propsDestructureDecl: ObjectPattern | undefined
- propsDestructuredBindings: PropsDestructureBindings = Object.create(null)
- propsDestructureRestId: string | undefined
- propsRuntimeDefaults: Node | undefined
- // defineEmits
- emitsRuntimeDecl: Node | undefined
- emitsTypeDecl: Node | undefined
- emitDecl: Node | undefined
- // defineModel
- modelDecls: Record<string, ModelDecl> = Object.create(null)
- // defineOptions
- optionsRuntimeDecl: Node | undefined
- // codegen
- bindingMetadata: BindingMetadata = {}
- helperImports: Set<string> = new Set()
- helper(key: string): string {
- this.helperImports.add(key)
- return `_${key}`
- }
- /**
- * to be exposed on compiled script block for HMR cache busting
- */
- deps?: Set<string>
- /**
- * cache for resolved fs
- */
- fs?: NonNullable<SFCScriptCompileOptions['fs']>
- constructor(
- public descriptor: SFCDescriptor,
- public options: Partial<SFCScriptCompileOptions>,
- ) {
- const { script, scriptSetup } = descriptor
- const scriptLang = script && script.lang
- const scriptSetupLang = scriptSetup && scriptSetup.lang
- this.isJS = isJS(scriptLang, scriptSetupLang)
- this.isTS = isTS(scriptLang, scriptSetupLang)
- const customElement = options.customElement
- const filename = this.descriptor.filename
- if (customElement) {
- this.isCE =
- typeof customElement === 'boolean'
- ? customElement
- : customElement(filename)
- }
- // resolve parser plugins
- const plugins: ParserPlugin[] = resolveParserPlugins(
- (scriptLang || scriptSetupLang)!,
- options.babelParserPlugins,
- )
- function parse(input: string, offset: number): Program {
- try {
- return babelParse(input, {
- plugins,
- sourceType: 'module',
- }).program
- } catch (e: any) {
- e.message = `[vue/compiler-sfc] ${e.message}\n\n${
- descriptor.filename
- }\n${generateCodeFrame(
- descriptor.source,
- e.pos + offset,
- e.pos + offset + 1,
- )}`
- throw e
- }
- }
- this.scriptAst =
- descriptor.script &&
- parse(descriptor.script.content, descriptor.script.loc.start.offset)
- this.scriptSetupAst =
- descriptor.scriptSetup &&
- parse(descriptor.scriptSetup!.content, this.startOffset!)
- }
- getString(node: Node, scriptSetup = true): string {
- const block = scriptSetup
- ? this.descriptor.scriptSetup!
- : this.descriptor.script!
- return block.content.slice(node.start!, node.end!)
- }
- warn(msg: string, node: Node, scope?: TypeScope): void {
- warn(generateError(msg, node, this, scope))
- }
- error(msg: string, node: Node, scope?: TypeScope): never {
- throw new Error(
- `[@vue/compiler-sfc] ${generateError(msg, node, this, scope)}`,
- )
- }
- }
- function generateError(
- msg: string,
- node: Node,
- ctx: ScriptCompileContext,
- scope?: TypeScope,
- ) {
- const offset = scope ? scope.offset : ctx.startOffset!
- return `${msg}\n\n${(scope || ctx.descriptor).filename}\n${generateCodeFrame(
- (scope || ctx.descriptor).source,
- node.start! + offset,
- node.end! + offset,
- )}`
- }
- export function resolveParserPlugins(
- lang: string,
- userPlugins?: ParserPlugin[],
- dts = false,
- ): ParserPlugin[] {
- const plugins: ParserPlugin[] = []
- if (
- !userPlugins ||
- !userPlugins.some(
- p =>
- p === 'importAssertions' ||
- p === 'importAttributes' ||
- (isArray(p) && p[0] === 'importAttributes'),
- )
- ) {
- plugins.push('importAttributes')
- }
- if (lang === 'jsx' || lang === 'tsx' || lang === 'mtsx') {
- plugins.push('jsx')
- } else if (userPlugins) {
- // If don't match the case of adding jsx
- // should remove the jsx from user options
- userPlugins = userPlugins.filter(p => p !== 'jsx')
- }
- if (
- lang === 'ts' ||
- lang === 'mts' ||
- lang === 'tsx' ||
- lang === 'cts' ||
- lang === 'mtsx'
- ) {
- plugins.push(['typescript', { dts }], 'explicitResourceManagement')
- if (!userPlugins || !userPlugins.includes('decorators')) {
- plugins.push('decorators-legacy')
- }
- }
- if (userPlugins) {
- plugins.push(...userPlugins)
- }
- return plugins
- }
|