Skip to content

Commit

Permalink
feat(compiler-sfc): expose type resolve APIs
Browse files Browse the repository at this point in the history
  • Loading branch information
yyx990803 committed Apr 16, 2023
1 parent 6b13e04 commit f22e32e
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 33 deletions.
6 changes: 6 additions & 0 deletions packages/compiler-sfc/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export { compileTemplate } from './compileTemplate'
export { compileStyle, compileStyleAsync } from './compileStyle'
export { compileScript } from './compileScript'
export { rewriteDefault, rewriteDefaultAST } from './rewriteDefault'
export { resolveTypeElements, inferRuntimeType } from './script/resolveType'
export {
shouldTransform as shouldTransformRef,
transform as transformRef,
Expand Down Expand Up @@ -52,6 +53,11 @@ export type {
SFCStyleCompileResults
} from './compileStyle'
export type { SFCScriptCompileOptions } from './compileScript'
export type { ScriptCompileContext } from './script/context'
export type {
TypeResolveContext,
SimpleTypeResolveContext
} from './script/resolveType'
export type {
AssetURLOptions,
AssetURLTagConfig
Expand Down
8 changes: 5 additions & 3 deletions packages/compiler-sfc/src/script/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@ export class ScriptCompileContext {
scriptAst: Program | null
scriptSetupAst: Program | null

s = new MagicString(this.descriptor.source)
source = this.descriptor.source
filename = this.descriptor.filename
s = new MagicString(this.source)
startOffset = this.descriptor.scriptSetup?.loc.start.offset
endOffset = this.descriptor.scriptSetup?.loc.end.offset

// import / type analysis
scope: TypeScope | undefined
scope?: TypeScope
userImports: Record<string, ImportBinding> = Object.create(null)

// macros presence check
Expand Down Expand Up @@ -69,7 +71,7 @@ export class ScriptCompileContext {

constructor(
public descriptor: SFCDescriptor,
public options: SFCScriptCompileOptions
public options: Partial<SFCScriptCompileOptions>
) {
const { script, scriptSetup } = descriptor
const scriptLang = script && script.lang
Expand Down
102 changes: 72 additions & 30 deletions packages/compiler-sfc/src/script/resolveType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,32 @@ import { createCache } from '../cache'
import type TS from 'typescript'
import { join, extname, dirname } from 'path'

/**
* TypeResolveContext is compatible with ScriptCompileContext
* but also allows a simpler version of it with minimal required properties
* when resolveType needs to be used in a non-SFC context, e.g. in a babel
* plugin. The simplest context can be just:
* ```ts
* const ctx: SimpleTypeResolveContext = {
* filename: '...',
* source: '...',
* options: {},
* error() {},
* ast: []
* }
* ```
*/
export type SimpleTypeResolveContext = Pick<
ScriptCompileContext,
// required
'source' | 'filename' | 'error' | 'options'
> &
Partial<Pick<ScriptCompileContext, 'scope' | 'deps'>> & {
ast: Statement[]
}

export type TypeResolveContext = ScriptCompileContext | SimpleTypeResolveContext

type Import = Pick<ImportBinding, 'source' | 'imported'>

export interface TypeScope {
Expand Down Expand Up @@ -79,7 +105,7 @@ interface ResolvedElements {
* mapped to runtime props or emits.
*/
export function resolveTypeElements(
ctx: ScriptCompileContext,
ctx: TypeResolveContext,
node: Node & WithScope & { _resolvedElements?: ResolvedElements },
scope?: TypeScope
): ResolvedElements {
Expand All @@ -94,7 +120,7 @@ export function resolveTypeElements(
}

function innerResolveTypeElements(
ctx: ScriptCompileContext,
ctx: TypeResolveContext,
node: Node,
scope: TypeScope
): ResolvedElements {
Expand Down Expand Up @@ -138,19 +164,19 @@ function innerResolveTypeElements(
) {
return resolveBuiltin(ctx, node, typeName as any, scope)
}
ctx.error(
return ctx.error(
`Unresolvable type reference or unsupported built-in utlility type`,
node,
scope
)
}
}
}
ctx.error(`Unresolvable type: ${node.type}`, node, scope)
return ctx.error(`Unresolvable type: ${node.type}`, node, scope)
}

function typeElementsToMap(
ctx: ScriptCompileContext,
ctx: TypeResolveContext,
elements: TSTypeElement[],
scope = ctxToScope(ctx)
): ResolvedElements {
Expand Down Expand Up @@ -227,7 +253,7 @@ function createProperty(
}

function resolveInterfaceMembers(
ctx: ScriptCompileContext,
ctx: TypeResolveContext,
node: TSInterfaceDeclaration & WithScope,
scope: TypeScope
): ResolvedElements {
Expand All @@ -246,7 +272,7 @@ function resolveInterfaceMembers(
}

function resolveMappedType(
ctx: ScriptCompileContext,
ctx: TypeResolveContext,
node: TSMappedType,
scope: TypeScope
): ResolvedElements {
Expand All @@ -266,7 +292,7 @@ function resolveMappedType(
}

function resolveIndexType(
ctx: ScriptCompileContext,
ctx: TypeResolveContext,
node: TSIndexedAccessType,
scope: TypeScope
): (TSType & WithScope)[] {
Expand Down Expand Up @@ -297,7 +323,7 @@ function resolveIndexType(
}

function resolveArrayElementType(
ctx: ScriptCompileContext,
ctx: TypeResolveContext,
node: Node,
scope: TypeScope
): TSType[] {
Expand All @@ -322,11 +348,15 @@ function resolveArrayElementType(
}
}
}
ctx.error('Failed to resolve element type from target type', node)
return ctx.error(
'Failed to resolve element type from target type',
node,
scope
)
}

function resolveStringType(
ctx: ScriptCompileContext,
ctx: TypeResolveContext,
node: Node,
scope: TypeScope
): string[] {
Expand Down Expand Up @@ -373,11 +403,11 @@ function resolveStringType(
}
}
}
ctx.error('Failed to resolve index type into finite keys', node, scope)
return ctx.error('Failed to resolve index type into finite keys', node, scope)
}

function resolveTemplateKeys(
ctx: ScriptCompileContext,
ctx: TypeResolveContext,
node: TemplateLiteral,
scope: TypeScope
): string[] {
Expand Down Expand Up @@ -420,7 +450,7 @@ const SupportedBuiltinsSet = new Set([
type GetSetType<T> = T extends Set<infer V> ? V : never

function resolveBuiltin(
ctx: ScriptCompileContext,
ctx: TypeResolveContext,
node: TSTypeReference | TSExpressionWithTypeArguments,
name: GetSetType<typeof SupportedBuiltinsSet>,
scope: TypeScope
Expand Down Expand Up @@ -460,7 +490,7 @@ function resolveBuiltin(
}

function resolveTypeReference(
ctx: ScriptCompileContext,
ctx: TypeResolveContext,
node: (TSTypeReference | TSExpressionWithTypeArguments) & {
_resolvedReference?: Node
},
Expand All @@ -481,7 +511,7 @@ function resolveTypeReference(
}

function innerResolveTypeReference(
ctx: ScriptCompileContext,
ctx: TypeResolveContext,
scope: TypeScope,
name: string | string[],
node: TSTypeReference | TSExpressionWithTypeArguments,
Expand Down Expand Up @@ -536,14 +566,17 @@ function qualifiedNameToPath(node: Identifier | TSQualifiedName): string[] {

let ts: typeof TS

/**
* @private
*/
export function registerTS(_ts: any) {
ts = _ts
}

type FS = NonNullable<SFCScriptCompileOptions['fs']>

function resolveTypeFromImport(
ctx: ScriptCompileContext,
ctx: TypeResolveContext,
node: TSTypeReference | TSExpressionWithTypeArguments,
name: string,
scope: TypeScope
Expand Down Expand Up @@ -685,13 +718,16 @@ function resolveWithTS(

const fileToScopeCache = createCache<TypeScope>()

/**
* @private
*/
export function invalidateTypeCache(filename: string) {
fileToScopeCache.delete(filename)
tsConfigCache.delete(filename)
}

function fileToScope(
ctx: ScriptCompileContext,
ctx: TypeResolveContext,
filename: string,
fs: FS
): TypeScope {
Expand All @@ -717,7 +753,7 @@ function fileToScope(
}

function parseFile(
ctx: ScriptCompileContext,
ctx: TypeResolveContext,
filename: string,
content: string
): Statement[] {
Expand Down Expand Up @@ -763,24 +799,30 @@ function parseFile(
return []
}

function ctxToScope(ctx: ScriptCompileContext): TypeScope {
function ctxToScope(ctx: TypeResolveContext): TypeScope {
if (ctx.scope) {
return ctx.scope
}

const body =
'ast' in ctx
? ctx.ast
: ctx.scriptAst
? [...ctx.scriptAst.body, ...ctx.scriptSetupAst!.body]
: ctx.scriptSetupAst!.body

const scope: TypeScope = {
filename: ctx.descriptor.filename,
source: ctx.descriptor.source,
offset: ctx.startOffset!,
imports: Object.create(ctx.userImports),
filename: ctx.filename,
source: ctx.source,
offset: 'startOffset' in ctx ? ctx.startOffset! : 0,
imports:
'userImports' in ctx
? Object.create(ctx.userImports)
: recordImports(body),
types: Object.create(null),
exportedTypes: Object.create(null)
}

const body = ctx.scriptAst
? [...ctx.scriptAst.body, ...ctx.scriptSetupAst!.body]
: ctx.scriptSetupAst!.body

recordTypes(body, scope)

return (ctx.scope = scope)
Expand Down Expand Up @@ -894,7 +936,7 @@ function recordImport(node: Node, imports: TypeScope['imports']) {
}

export function inferRuntimeType(
ctx: ScriptCompileContext,
ctx: TypeResolveContext,
node: Node & WithScope,
scope = node._ownerScope || ctxToScope(ctx)
): string[] {
Expand Down Expand Up @@ -1052,7 +1094,7 @@ export function inferRuntimeType(
}

function flattenTypes(
ctx: ScriptCompileContext,
ctx: TypeResolveContext,
types: TSType[],
scope: TypeScope
): string[] {
Expand Down

0 comments on commit f22e32e

Please sign in to comment.