Skip to content

Commit

Permalink
feat(compiler-sfc): support dynamic imports when resolving types
Browse files Browse the repository at this point in the history
  • Loading branch information
yyx990803 committed Apr 20, 2023
1 parent 7c3ca3c commit 4496456
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 23 deletions.
18 changes: 18 additions & 0 deletions packages/compiler-sfc/__tests__/compileScript/resolveType.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,24 @@ describe('resolveType', () => {
expect(deps && [...deps]).toStrictEqual(Object.keys(files))
})

test('relative (dynamic import)', () => {
const files = {
'/foo.ts': `export type P = { foo: string, bar: import('./bar').N }`,
'/bar.ts': 'export type N = number'
}
const { props, deps } = resolve(
`
defineProps<import('./foo').P>()
`,
files
)
expect(props).toStrictEqual({
foo: ['String'],
bar: ['Number']
})
expect(deps && [...deps]).toStrictEqual(Object.keys(files))
})

test('ts module resolve', () => {
const files = {
'/node_modules/foo/package.json': JSON.stringify({
Expand Down
75 changes: 52 additions & 23 deletions packages/compiler-sfc/src/script/resolveType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
TSEnumDeclaration,
TSExpressionWithTypeArguments,
TSFunctionType,
TSImportType,
TSIndexedAccessType,
TSInterfaceDeclaration,
TSMappedType,
Expand Down Expand Up @@ -168,6 +169,17 @@ function innerResolveTypeElements(
)
}
}
case 'TSImportType':
const sourceScope = importSourceToScope(
ctx,
node.argument,
scope,
node.argument.value
)
const resolved = resolveTypeReference(ctx, node, sourceScope)
if (resolved) {
return resolveTypeElements(ctx, resolved, resolved._ownerScope)
}
}
return ctx.error(`Unresolvable type: ${node.type}`, node, scope)
}
Expand Down Expand Up @@ -486,9 +498,14 @@ function resolveBuiltin(
}
}

type ReferenceTypes =
| TSTypeReference
| TSExpressionWithTypeArguments
| TSImportType

function resolveTypeReference(
ctx: TypeResolveContext,
node: (TSTypeReference | TSExpressionWithTypeArguments) & {
node: ReferenceTypes & {
_resolvedReference?: ScopeTypeNode
},
scope?: TypeScope,
Expand All @@ -511,7 +528,7 @@ function innerResolveTypeReference(
ctx: TypeResolveContext,
scope: TypeScope,
name: string | string[],
node: TSTypeReference | TSExpressionWithTypeArguments,
node: ReferenceTypes,
onlyExported: boolean
): ScopeTypeNode | undefined {
if (typeof name === 'string') {
Expand Down Expand Up @@ -555,11 +572,16 @@ function innerResolveTypeReference(
}
}

function getReferenceName(
node: TSTypeReference | TSExpressionWithTypeArguments
): string | string[] {
const ref = node.type === 'TSTypeReference' ? node.typeName : node.expression
if (ref.type === 'Identifier') {
function getReferenceName(node: ReferenceTypes): string | string[] {
const ref =
node.type === 'TSTypeReference'
? node.typeName
: node.type === 'TSExpressionWithTypeArguments'
? node.expression
: node.qualifier
if (!ref) {
return 'default'
} else if (ref.type === 'Identifier') {
return ref.name
} else {
return qualifiedNameToPath(ref)
Expand Down Expand Up @@ -599,27 +621,21 @@ type FS = NonNullable<SFCScriptCompileOptions['fs']>

function resolveTypeFromImport(
ctx: TypeResolveContext,
node: TSTypeReference | TSExpressionWithTypeArguments,
node: ReferenceTypes,
name: string,
scope: TypeScope
): ScopeTypeNode | undefined {
const { source, imported } = scope.imports[name]
const resolved = resolveImportSource(ctx, node, scope, source)
return resolveTypeReference(
ctx,
node,
fileToScope(ctx, resolved),
imported,
true
)
const sourceScope = importSourceToScope(ctx, node, scope, source)
return resolveTypeReference(ctx, node, sourceScope, imported, true)
}

function resolveImportSource(
function importSourceToScope(
ctx: TypeResolveContext,
node: Node,
scope: TypeScope,
source: string
): string {
): TypeScope {
const fs: FS = ctx.options.fs || ts?.sys
if (!fs) {
ctx.error(
Expand Down Expand Up @@ -657,7 +673,7 @@ function resolveImportSource(
if (resolved) {
// (hmr) register dependency file on ctx
;(ctx.deps || (ctx.deps = new Set())).add(resolved)
return normalizePath(resolved)
return fileToScope(ctx, normalizePath(resolved))
} else {
return ctx.error(
`Failed to resolve import source ${JSON.stringify(source)}.`,
Expand Down Expand Up @@ -938,14 +954,13 @@ function recordTypes(
}
}
} else if (stmt.type === 'ExportAllDeclaration') {
const targetFile = resolveImportSource(
const sourceScope = importSourceToScope(
ctx,
stmt.source,
scope,
stmt.source.value
)
const targetScope = fileToScope(ctx, targetFile)
Object.assign(scope.exportedTypes, targetScope.exportedTypes)
Object.assign(scope.exportedTypes, sourceScope.exportedTypes)
}
}
}
Expand Down Expand Up @@ -1134,7 +1149,7 @@ export function inferRuntimeType(
return [UNKNOWN_TYPE]
}

case 'TSTypeReference':
case 'TSTypeReference': {
const resolved = resolveTypeReference(ctx, node, scope)
if (resolved) {
return inferRuntimeType(ctx, resolved, resolved._ownerScope)
Expand Down Expand Up @@ -1197,6 +1212,7 @@ export function inferRuntimeType(
}
// cannot infer, fallback to UNKNOWN: ThisParameterType
return [UNKNOWN_TYPE]
}

case 'TSParenthesizedType':
return inferRuntimeType(ctx, node.typeAnnotation, scope)
Expand Down Expand Up @@ -1228,6 +1244,19 @@ export function inferRuntimeType(
case 'ClassDeclaration':
return ['Object']

case 'TSImportType': {
const sourceScope = importSourceToScope(
ctx,
node.argument,
scope,
node.argument.value
)
const resolved = resolveTypeReference(ctx, node, sourceScope)
if (resolved) {
return inferRuntimeType(ctx, resolved, resolved._ownerScope)
}
}

default:
return [UNKNOWN_TYPE] // no runtime check
}
Expand Down

0 comments on commit 4496456

Please sign in to comment.