|
|
@@ -1,11 +1,13 @@
|
|
|
import {
|
|
|
+ Expression,
|
|
|
Identifier,
|
|
|
- Node as _Node,
|
|
|
+ Node,
|
|
|
Statement,
|
|
|
TSCallSignatureDeclaration,
|
|
|
TSEnumDeclaration,
|
|
|
TSExpressionWithTypeArguments,
|
|
|
TSFunctionType,
|
|
|
+ TSInterfaceDeclaration,
|
|
|
TSMappedType,
|
|
|
TSMethodSignature,
|
|
|
TSModuleBlock,
|
|
|
@@ -18,81 +20,108 @@ import {
|
|
|
TSTypeReference,
|
|
|
TemplateLiteral
|
|
|
} from '@babel/types'
|
|
|
-import { UNKNOWN_TYPE } from './utils'
|
|
|
-import { ScriptCompileContext } from './context'
|
|
|
-import { ImportBinding } from '../compileScript'
|
|
|
-import { TSInterfaceDeclaration } from '@babel/types'
|
|
|
+import { UNKNOWN_TYPE, getId, getImportedName } from './utils'
|
|
|
+import { ScriptCompileContext, resolveParserPlugins } from './context'
|
|
|
+import { ImportBinding, SFCScriptCompileOptions } from '../compileScript'
|
|
|
import { capitalize, hasOwn } from '@vue/shared'
|
|
|
-import { Expression } from '@babel/types'
|
|
|
+import path from 'path'
|
|
|
+import { parse as babelParse } from '@babel/parser'
|
|
|
+import { parse } from '../parse'
|
|
|
+
|
|
|
+type Import = Pick<ImportBinding, 'source' | 'imported'>
|
|
|
|
|
|
export interface TypeScope {
|
|
|
filename: string
|
|
|
- imports: Record<string, ImportBinding>
|
|
|
- types: Record<string, Node>
|
|
|
- parent?: TypeScope
|
|
|
+ source: string
|
|
|
+ imports: Record<string, Import>
|
|
|
+ types: Record<
|
|
|
+ string,
|
|
|
+ Node & {
|
|
|
+ // scope types always has ownerScope attached
|
|
|
+ _ownerScope: TypeScope
|
|
|
+ }
|
|
|
+ >
|
|
|
+ exportedTypes: Record<
|
|
|
+ string,
|
|
|
+ Node & {
|
|
|
+ // scope types always has ownerScope attached
|
|
|
+ _ownerScope: TypeScope
|
|
|
+ }
|
|
|
+ >
|
|
|
}
|
|
|
|
|
|
-interface WithScope {
|
|
|
+export interface WithScope {
|
|
|
_ownerScope?: TypeScope
|
|
|
}
|
|
|
|
|
|
interface ResolvedElements {
|
|
|
- props: Record<string, (TSPropertySignature | TSMethodSignature) & WithScope>
|
|
|
+ props: Record<
|
|
|
+ string,
|
|
|
+ (TSPropertySignature | TSMethodSignature) & {
|
|
|
+ // resolved props always has ownerScope attached
|
|
|
+ _ownerScope: TypeScope
|
|
|
+ }
|
|
|
+ >
|
|
|
calls?: (TSCallSignatureDeclaration | TSFunctionType)[]
|
|
|
}
|
|
|
|
|
|
-type Node = _Node &
|
|
|
- WithScope & {
|
|
|
- _resolvedElements?: ResolvedElements
|
|
|
- }
|
|
|
-
|
|
|
/**
|
|
|
* Resolve arbitrary type node to a list of type elements that can be then
|
|
|
* mapped to runtime props or emits.
|
|
|
*/
|
|
|
export function resolveTypeElements(
|
|
|
ctx: ScriptCompileContext,
|
|
|
- node: Node
|
|
|
+ node: Node & WithScope & { _resolvedElements?: ResolvedElements },
|
|
|
+ scope?: TypeScope
|
|
|
): ResolvedElements {
|
|
|
if (node._resolvedElements) {
|
|
|
return node._resolvedElements
|
|
|
}
|
|
|
- return (node._resolvedElements = innerResolveTypeElements(ctx, node))
|
|
|
+ return (node._resolvedElements = innerResolveTypeElements(
|
|
|
+ ctx,
|
|
|
+ node,
|
|
|
+ node._ownerScope || scope || ctxToScope(ctx)
|
|
|
+ ))
|
|
|
}
|
|
|
|
|
|
function innerResolveTypeElements(
|
|
|
ctx: ScriptCompileContext,
|
|
|
- node: Node
|
|
|
+ node: Node,
|
|
|
+ scope: TypeScope
|
|
|
): ResolvedElements {
|
|
|
switch (node.type) {
|
|
|
case 'TSTypeLiteral':
|
|
|
- return typeElementsToMap(ctx, node.members, node._ownerScope)
|
|
|
+ return typeElementsToMap(ctx, node.members, scope)
|
|
|
case 'TSInterfaceDeclaration':
|
|
|
- return resolveInterfaceMembers(ctx, node)
|
|
|
+ return resolveInterfaceMembers(ctx, node, scope)
|
|
|
case 'TSTypeAliasDeclaration':
|
|
|
case 'TSParenthesizedType':
|
|
|
- return resolveTypeElements(ctx, node.typeAnnotation)
|
|
|
+ return resolveTypeElements(ctx, node.typeAnnotation, scope)
|
|
|
case 'TSFunctionType': {
|
|
|
return { props: {}, calls: [node] }
|
|
|
}
|
|
|
case 'TSUnionType':
|
|
|
case 'TSIntersectionType':
|
|
|
return mergeElements(
|
|
|
- node.types.map(t => resolveTypeElements(ctx, t)),
|
|
|
+ node.types.map(t => resolveTypeElements(ctx, t, scope)),
|
|
|
node.type
|
|
|
)
|
|
|
case 'TSMappedType':
|
|
|
- return resolveMappedType(ctx, node)
|
|
|
+ return resolveMappedType(ctx, node, scope)
|
|
|
case 'TSIndexedAccessType': {
|
|
|
if (
|
|
|
node.indexType.type === 'TSLiteralType' &&
|
|
|
node.indexType.literal.type === 'StringLiteral'
|
|
|
) {
|
|
|
- const resolved = resolveTypeElements(ctx, node.objectType)
|
|
|
+ const resolved = resolveTypeElements(ctx, node.objectType, scope)
|
|
|
const key = node.indexType.literal.value
|
|
|
const targetType = resolved.props[key].typeAnnotation
|
|
|
if (targetType) {
|
|
|
- return resolveTypeElements(ctx, targetType.typeAnnotation)
|
|
|
+ return resolveTypeElements(
|
|
|
+ ctx,
|
|
|
+ targetType.typeAnnotation,
|
|
|
+ resolved.props[key]._ownerScope
|
|
|
+ )
|
|
|
} else {
|
|
|
break
|
|
|
}
|
|
|
@@ -105,9 +134,9 @@ function innerResolveTypeElements(
|
|
|
}
|
|
|
case 'TSExpressionWithTypeArguments': // referenced by interface extends
|
|
|
case 'TSTypeReference': {
|
|
|
- const resolved = resolveTypeReference(ctx, node)
|
|
|
+ const resolved = resolveTypeReference(ctx, node, scope)
|
|
|
if (resolved) {
|
|
|
- return resolveTypeElements(ctx, resolved)
|
|
|
+ return resolveTypeElements(ctx, resolved, resolved._ownerScope)
|
|
|
} else {
|
|
|
const typeName = getReferenceName(node)
|
|
|
if (
|
|
|
@@ -118,7 +147,7 @@ function innerResolveTypeElements(
|
|
|
return resolveBuiltin(ctx, node, typeName as any)
|
|
|
}
|
|
|
ctx.error(
|
|
|
- `Failed to resolved type reference, or unsupported built-in utlility type.`,
|
|
|
+ `Failed to resolve type reference, or unsupported built-in utlility type.`,
|
|
|
node
|
|
|
)
|
|
|
}
|
|
|
@@ -135,18 +164,13 @@ function typeElementsToMap(
|
|
|
const res: ResolvedElements = { props: {} }
|
|
|
for (const e of elements) {
|
|
|
if (e.type === 'TSPropertySignature' || e.type === 'TSMethodSignature') {
|
|
|
- ;(e as Node)._ownerScope = scope
|
|
|
- const name =
|
|
|
- e.key.type === 'Identifier'
|
|
|
- ? e.key.name
|
|
|
- : e.key.type === 'StringLiteral'
|
|
|
- ? e.key.value
|
|
|
- : null
|
|
|
+ ;(e as WithScope)._ownerScope = scope
|
|
|
+ const name = getId(e.key)
|
|
|
if (name && !e.computed) {
|
|
|
- res.props[name] = e
|
|
|
+ res.props[name] = e as ResolvedElements['props'][string]
|
|
|
} else if (e.key.type === 'TemplateLiteral') {
|
|
|
for (const key of resolveTemplateKeys(ctx, e.key)) {
|
|
|
- res.props[key] = e
|
|
|
+ res.props[key] = e as ResolvedElements['props'][string]
|
|
|
}
|
|
|
} else {
|
|
|
ctx.error(
|
|
|
@@ -172,11 +196,15 @@ function mergeElements(
|
|
|
if (!hasOwn(baseProps, key)) {
|
|
|
baseProps[key] = props[key]
|
|
|
} else {
|
|
|
- baseProps[key] = createProperty(baseProps[key].key, {
|
|
|
- type,
|
|
|
- // @ts-ignore
|
|
|
- types: [baseProps[key], props[key]]
|
|
|
- })
|
|
|
+ baseProps[key] = createProperty(
|
|
|
+ baseProps[key].key,
|
|
|
+ {
|
|
|
+ type,
|
|
|
+ // @ts-ignore
|
|
|
+ types: [baseProps[key], props[key]]
|
|
|
+ },
|
|
|
+ baseProps[key]._ownerScope
|
|
|
+ )
|
|
|
}
|
|
|
}
|
|
|
if (calls) {
|
|
|
@@ -188,8 +216,9 @@ function mergeElements(
|
|
|
|
|
|
function createProperty(
|
|
|
key: Expression,
|
|
|
- typeAnnotation: TSType
|
|
|
-): TSPropertySignature {
|
|
|
+ typeAnnotation: TSType,
|
|
|
+ scope: TypeScope
|
|
|
+): TSPropertySignature & { _ownerScope: TypeScope } {
|
|
|
return {
|
|
|
type: 'TSPropertySignature',
|
|
|
key,
|
|
|
@@ -197,18 +226,20 @@ function createProperty(
|
|
|
typeAnnotation: {
|
|
|
type: 'TSTypeAnnotation',
|
|
|
typeAnnotation
|
|
|
- }
|
|
|
+ },
|
|
|
+ _ownerScope: scope
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function resolveInterfaceMembers(
|
|
|
ctx: ScriptCompileContext,
|
|
|
- node: TSInterfaceDeclaration & WithScope
|
|
|
+ node: TSInterfaceDeclaration & WithScope,
|
|
|
+ scope: TypeScope
|
|
|
): ResolvedElements {
|
|
|
const base = typeElementsToMap(ctx, node.body.body, node._ownerScope)
|
|
|
if (node.extends) {
|
|
|
for (const ext of node.extends) {
|
|
|
- const { props } = resolveTypeElements(ctx, ext)
|
|
|
+ const { props } = resolveTypeElements(ctx, ext, scope)
|
|
|
for (const key in props) {
|
|
|
if (!hasOwn(base.props, key)) {
|
|
|
base.props[key] = props[key]
|
|
|
@@ -221,7 +252,8 @@ function resolveInterfaceMembers(
|
|
|
|
|
|
function resolveMappedType(
|
|
|
ctx: ScriptCompileContext,
|
|
|
- node: TSMappedType
|
|
|
+ node: TSMappedType,
|
|
|
+ scope: TypeScope
|
|
|
): ResolvedElements {
|
|
|
const res: ResolvedElements = { props: {} }
|
|
|
if (!node.typeParameter.constraint) {
|
|
|
@@ -234,7 +266,8 @@ function resolveMappedType(
|
|
|
type: 'Identifier',
|
|
|
name: key
|
|
|
},
|
|
|
- node.typeAnnotation!
|
|
|
+ node.typeAnnotation!,
|
|
|
+ scope
|
|
|
)
|
|
|
}
|
|
|
return res
|
|
|
@@ -357,32 +390,52 @@ function resolveTypeReference(
|
|
|
node: (TSTypeReference | TSExpressionWithTypeArguments) & {
|
|
|
_resolvedReference?: Node
|
|
|
},
|
|
|
- scope = ctxToScope(ctx)
|
|
|
-): Node | undefined {
|
|
|
+ scope?: TypeScope,
|
|
|
+ name?: string,
|
|
|
+ onlyExported = false
|
|
|
+): (Node & WithScope) | undefined {
|
|
|
if (node._resolvedReference) {
|
|
|
return node._resolvedReference
|
|
|
}
|
|
|
- const name = getReferenceName(node)
|
|
|
- return (node._resolvedReference = innerResolveTypeReference(scope, name))
|
|
|
+ return (node._resolvedReference = innerResolveTypeReference(
|
|
|
+ ctx,
|
|
|
+ scope || ctxToScope(ctx),
|
|
|
+ name || getReferenceName(node),
|
|
|
+ node,
|
|
|
+ onlyExported
|
|
|
+ ))
|
|
|
}
|
|
|
|
|
|
function innerResolveTypeReference(
|
|
|
+ ctx: ScriptCompileContext,
|
|
|
scope: TypeScope,
|
|
|
- name: string | string[]
|
|
|
+ name: string | string[],
|
|
|
+ node: TSTypeReference | TSExpressionWithTypeArguments,
|
|
|
+ onlyExported: boolean
|
|
|
): Node | undefined {
|
|
|
if (typeof name === 'string') {
|
|
|
if (scope.imports[name]) {
|
|
|
- // TODO external import
|
|
|
- } else if (scope.types[name]) {
|
|
|
- return scope.types[name]
|
|
|
+ return resolveTypeFromImport(ctx, scope, scope.imports[name], node)
|
|
|
+ } else {
|
|
|
+ const types = onlyExported ? scope.exportedTypes : scope.types
|
|
|
+ return types[name]
|
|
|
}
|
|
|
} else {
|
|
|
- const ns = innerResolveTypeReference(scope, name[0])
|
|
|
+ const ns = innerResolveTypeReference(
|
|
|
+ ctx,
|
|
|
+ scope,
|
|
|
+ name[0],
|
|
|
+ node,
|
|
|
+ onlyExported
|
|
|
+ )
|
|
|
if (ns && ns.type === 'TSModuleDeclaration') {
|
|
|
const childScope = moduleDeclToScope(ns, scope)
|
|
|
return innerResolveTypeReference(
|
|
|
+ ctx,
|
|
|
childScope,
|
|
|
- name.length > 2 ? name.slice(1) : name[name.length - 1]
|
|
|
+ name.length > 2 ? name.slice(1) : name[name.length - 1],
|
|
|
+ node,
|
|
|
+ true
|
|
|
)
|
|
|
}
|
|
|
}
|
|
|
@@ -407,20 +460,125 @@ function qualifiedNameToPath(node: Identifier | TSQualifiedName): string[] {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+function resolveTypeFromImport(
|
|
|
+ ctx: ScriptCompileContext,
|
|
|
+ scope: TypeScope,
|
|
|
+ { source, imported }: Import,
|
|
|
+ node: TSTypeReference | TSExpressionWithTypeArguments
|
|
|
+): Node | undefined {
|
|
|
+ const fs = ctx.options.fs
|
|
|
+ if (!fs) {
|
|
|
+ ctx.error(
|
|
|
+ `fs options for compileScript are required for resolving imported types`,
|
|
|
+ node
|
|
|
+ )
|
|
|
+ }
|
|
|
+ // TODO (hmr) register dependency file on ctx
|
|
|
+ const containingFile = scope.filename
|
|
|
+ if (source.startsWith('.')) {
|
|
|
+ // relative import - fast path
|
|
|
+ const filename = path.join(containingFile, '..', source)
|
|
|
+ const resolved = resolveExt(filename, fs)
|
|
|
+ if (resolved) {
|
|
|
+ return resolveTypeReference(
|
|
|
+ ctx,
|
|
|
+ node,
|
|
|
+ fileToScope(ctx, resolved, fs),
|
|
|
+ imported,
|
|
|
+ true
|
|
|
+ )
|
|
|
+ } else {
|
|
|
+ ctx.error(`Failed to resolve import source for type`, node)
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // TODO module or aliased import - use full TS resolution
|
|
|
+ return
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function resolveExt(
|
|
|
+ filename: string,
|
|
|
+ fs: NonNullable<SFCScriptCompileOptions['fs']>
|
|
|
+) {
|
|
|
+ const tryResolve = (filename: string) => {
|
|
|
+ if (fs.fileExists(filename)) return filename
|
|
|
+ }
|
|
|
+ return (
|
|
|
+ tryResolve(filename) ||
|
|
|
+ tryResolve(filename + `.ts`) ||
|
|
|
+ tryResolve(filename + `.d.ts`) ||
|
|
|
+ tryResolve(filename + `/index.ts`) ||
|
|
|
+ tryResolve(filename + `/index.d.ts`)
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+function fileToScope(
|
|
|
+ ctx: ScriptCompileContext,
|
|
|
+ filename: string,
|
|
|
+ fs: NonNullable<SFCScriptCompileOptions['fs']>
|
|
|
+): TypeScope {
|
|
|
+ // TODO cache
|
|
|
+ const source = fs.readFile(filename)
|
|
|
+ const body = parseFile(ctx, filename, source)
|
|
|
+ const scope: TypeScope = {
|
|
|
+ filename,
|
|
|
+ source,
|
|
|
+ types: Object.create(null),
|
|
|
+ exportedTypes: Object.create(null),
|
|
|
+ imports: recordImports(body)
|
|
|
+ }
|
|
|
+ recordTypes(body, scope)
|
|
|
+ return scope
|
|
|
+}
|
|
|
+
|
|
|
+function parseFile(
|
|
|
+ ctx: ScriptCompileContext,
|
|
|
+ filename: string,
|
|
|
+ content: string
|
|
|
+): Statement[] {
|
|
|
+ const ext = path.extname(filename)
|
|
|
+ if (ext === '.ts' || ext === '.tsx') {
|
|
|
+ return babelParse(content, {
|
|
|
+ plugins: resolveParserPlugins(
|
|
|
+ ext.slice(1),
|
|
|
+ ctx.options.babelParserPlugins
|
|
|
+ ),
|
|
|
+ sourceType: 'module'
|
|
|
+ }).program.body
|
|
|
+ } else if (ext === '.vue') {
|
|
|
+ const {
|
|
|
+ descriptor: { script, scriptSetup }
|
|
|
+ } = parse(content)
|
|
|
+ const scriptContent = (script?.content || '') + (scriptSetup?.content || '')
|
|
|
+ const lang = script?.lang || scriptSetup?.lang
|
|
|
+ return babelParse(scriptContent, {
|
|
|
+ plugins: resolveParserPlugins(lang!, ctx.options.babelParserPlugins),
|
|
|
+ sourceType: 'module'
|
|
|
+ }).program.body
|
|
|
+ }
|
|
|
+ return []
|
|
|
+}
|
|
|
+
|
|
|
function ctxToScope(ctx: ScriptCompileContext): TypeScope {
|
|
|
if (ctx.scope) {
|
|
|
return ctx.scope
|
|
|
}
|
|
|
|
|
|
+ const scope: TypeScope = {
|
|
|
+ filename: ctx.descriptor.filename,
|
|
|
+ source: ctx.descriptor.source,
|
|
|
+ imports: Object.create(ctx.userImports),
|
|
|
+ types: Object.create(null),
|
|
|
+ exportedTypes: Object.create(null)
|
|
|
+ }
|
|
|
+
|
|
|
const body = ctx.scriptAst
|
|
|
? [...ctx.scriptAst.body, ...ctx.scriptSetupAst!.body]
|
|
|
: ctx.scriptSetupAst!.body
|
|
|
|
|
|
- return (ctx.scope = {
|
|
|
- filename: ctx.descriptor.filename,
|
|
|
- imports: ctx.userImports,
|
|
|
- types: recordTypes(body)
|
|
|
- })
|
|
|
+ recordTypes(body, scope)
|
|
|
+
|
|
|
+ return (ctx.scope = scope)
|
|
|
}
|
|
|
|
|
|
function moduleDeclToScope(
|
|
|
@@ -430,27 +588,56 @@ function moduleDeclToScope(
|
|
|
if (node._resolvedChildScope) {
|
|
|
return node._resolvedChildScope
|
|
|
}
|
|
|
- const types: TypeScope['types'] = Object.create(parent.types)
|
|
|
const scope: TypeScope = {
|
|
|
- filename: parent.filename,
|
|
|
- imports: Object.create(parent.imports),
|
|
|
- types: recordTypes((node.body as TSModuleBlock).body, types),
|
|
|
- parent
|
|
|
- }
|
|
|
- for (const key of Object.keys(types)) {
|
|
|
- types[key]._ownerScope = scope
|
|
|
+ ...parent,
|
|
|
+ types: Object.create(parent.types),
|
|
|
+ imports: Object.create(parent.imports)
|
|
|
}
|
|
|
+ recordTypes((node.body as TSModuleBlock).body, scope)
|
|
|
return (node._resolvedChildScope = scope)
|
|
|
}
|
|
|
|
|
|
-function recordTypes(
|
|
|
- body: Statement[],
|
|
|
- types: Record<string, Node> = Object.create(null)
|
|
|
-) {
|
|
|
- for (const s of body) {
|
|
|
- recordType(s, types)
|
|
|
+function recordTypes(body: Statement[], scope: TypeScope) {
|
|
|
+ const { types, exportedTypes, imports } = scope
|
|
|
+ for (const stmt of body) {
|
|
|
+ recordType(stmt, types)
|
|
|
+ }
|
|
|
+ for (const stmt of body) {
|
|
|
+ if (stmt.type === 'ExportNamedDeclaration') {
|
|
|
+ if (stmt.declaration) {
|
|
|
+ recordType(stmt.declaration, types)
|
|
|
+ recordType(stmt.declaration, exportedTypes)
|
|
|
+ } else {
|
|
|
+ for (const spec of stmt.specifiers) {
|
|
|
+ if (spec.type === 'ExportSpecifier') {
|
|
|
+ const local = spec.local.name
|
|
|
+ const exported = getId(spec.exported)
|
|
|
+ if (stmt.source) {
|
|
|
+ // re-export, register an import + export as a type reference
|
|
|
+ imports[local] = {
|
|
|
+ source: stmt.source.value,
|
|
|
+ imported: local
|
|
|
+ }
|
|
|
+ exportedTypes[exported] = {
|
|
|
+ type: 'TSTypeReference',
|
|
|
+ typeName: {
|
|
|
+ type: 'Identifier',
|
|
|
+ name: local
|
|
|
+ },
|
|
|
+ _ownerScope: scope
|
|
|
+ }
|
|
|
+ } else if (types[local]) {
|
|
|
+ // exporting local defined type
|
|
|
+ exportedTypes[exported] = types[local]
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ for (const key of Object.keys(types)) {
|
|
|
+ types[key]._ownerScope = scope
|
|
|
}
|
|
|
- return types
|
|
|
}
|
|
|
|
|
|
function recordType(node: Node, types: Record<string, Node>) {
|
|
|
@@ -465,12 +652,6 @@ function recordType(node: Node, types: Record<string, Node>) {
|
|
|
case 'TSTypeAliasDeclaration':
|
|
|
types[node.id.name] = node.typeAnnotation
|
|
|
break
|
|
|
- case 'ExportNamedDeclaration': {
|
|
|
- if (node.declaration) {
|
|
|
- recordType(node.declaration, types)
|
|
|
- }
|
|
|
- break
|
|
|
- }
|
|
|
case 'VariableDeclaration': {
|
|
|
if (node.declare) {
|
|
|
for (const decl of node.declarations) {
|
|
|
@@ -486,9 +667,29 @@ function recordType(node: Node, types: Record<string, Node>) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+export function recordImports(body: Statement[]) {
|
|
|
+ const imports: TypeScope['imports'] = Object.create(null)
|
|
|
+ for (const s of body) {
|
|
|
+ recordImport(s, imports)
|
|
|
+ }
|
|
|
+ return imports
|
|
|
+}
|
|
|
+
|
|
|
+function recordImport(node: Node, imports: TypeScope['imports']) {
|
|
|
+ if (node.type !== 'ImportDeclaration') {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ for (const s of node.specifiers) {
|
|
|
+ imports[s.local.name] = {
|
|
|
+ imported: getImportedName(s),
|
|
|
+ source: node.source.value
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
export function inferRuntimeType(
|
|
|
ctx: ScriptCompileContext,
|
|
|
- node: Node,
|
|
|
+ node: Node & WithScope,
|
|
|
scope = node._ownerScope || ctxToScope(ctx)
|
|
|
): string[] {
|
|
|
switch (node.type) {
|