mirror of https://github.com/vuejs/core.git
feat(compiler-sfc): support dynamic imports when resolving types
This commit is contained in:
parent
7c3ca3cc3e
commit
4496456d7d
|
@ -472,6 +472,24 @@ describe('resolveType', () => {
|
||||||
expect(deps && [...deps]).toStrictEqual(Object.keys(files))
|
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', () => {
|
test('ts module resolve', () => {
|
||||||
const files = {
|
const files = {
|
||||||
'/node_modules/foo/package.json': JSON.stringify({
|
'/node_modules/foo/package.json': JSON.stringify({
|
||||||
|
|
|
@ -7,6 +7,7 @@ import {
|
||||||
TSEnumDeclaration,
|
TSEnumDeclaration,
|
||||||
TSExpressionWithTypeArguments,
|
TSExpressionWithTypeArguments,
|
||||||
TSFunctionType,
|
TSFunctionType,
|
||||||
|
TSImportType,
|
||||||
TSIndexedAccessType,
|
TSIndexedAccessType,
|
||||||
TSInterfaceDeclaration,
|
TSInterfaceDeclaration,
|
||||||
TSMappedType,
|
TSMappedType,
|
||||||
|
@ -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)
|
return ctx.error(`Unresolvable type: ${node.type}`, node, scope)
|
||||||
}
|
}
|
||||||
|
@ -486,9 +498,14 @@ function resolveBuiltin(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ReferenceTypes =
|
||||||
|
| TSTypeReference
|
||||||
|
| TSExpressionWithTypeArguments
|
||||||
|
| TSImportType
|
||||||
|
|
||||||
function resolveTypeReference(
|
function resolveTypeReference(
|
||||||
ctx: TypeResolveContext,
|
ctx: TypeResolveContext,
|
||||||
node: (TSTypeReference | TSExpressionWithTypeArguments) & {
|
node: ReferenceTypes & {
|
||||||
_resolvedReference?: ScopeTypeNode
|
_resolvedReference?: ScopeTypeNode
|
||||||
},
|
},
|
||||||
scope?: TypeScope,
|
scope?: TypeScope,
|
||||||
|
@ -511,7 +528,7 @@ function innerResolveTypeReference(
|
||||||
ctx: TypeResolveContext,
|
ctx: TypeResolveContext,
|
||||||
scope: TypeScope,
|
scope: TypeScope,
|
||||||
name: string | string[],
|
name: string | string[],
|
||||||
node: TSTypeReference | TSExpressionWithTypeArguments,
|
node: ReferenceTypes,
|
||||||
onlyExported: boolean
|
onlyExported: boolean
|
||||||
): ScopeTypeNode | undefined {
|
): ScopeTypeNode | undefined {
|
||||||
if (typeof name === 'string') {
|
if (typeof name === 'string') {
|
||||||
|
@ -555,11 +572,16 @@ function innerResolveTypeReference(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getReferenceName(
|
function getReferenceName(node: ReferenceTypes): string | string[] {
|
||||||
node: TSTypeReference | TSExpressionWithTypeArguments
|
const ref =
|
||||||
): string | string[] {
|
node.type === 'TSTypeReference'
|
||||||
const ref = node.type === 'TSTypeReference' ? node.typeName : node.expression
|
? node.typeName
|
||||||
if (ref.type === 'Identifier') {
|
: node.type === 'TSExpressionWithTypeArguments'
|
||||||
|
? node.expression
|
||||||
|
: node.qualifier
|
||||||
|
if (!ref) {
|
||||||
|
return 'default'
|
||||||
|
} else if (ref.type === 'Identifier') {
|
||||||
return ref.name
|
return ref.name
|
||||||
} else {
|
} else {
|
||||||
return qualifiedNameToPath(ref)
|
return qualifiedNameToPath(ref)
|
||||||
|
@ -599,27 +621,21 @@ type FS = NonNullable<SFCScriptCompileOptions['fs']>
|
||||||
|
|
||||||
function resolveTypeFromImport(
|
function resolveTypeFromImport(
|
||||||
ctx: TypeResolveContext,
|
ctx: TypeResolveContext,
|
||||||
node: TSTypeReference | TSExpressionWithTypeArguments,
|
node: ReferenceTypes,
|
||||||
name: string,
|
name: string,
|
||||||
scope: TypeScope
|
scope: TypeScope
|
||||||
): ScopeTypeNode | undefined {
|
): ScopeTypeNode | undefined {
|
||||||
const { source, imported } = scope.imports[name]
|
const { source, imported } = scope.imports[name]
|
||||||
const resolved = resolveImportSource(ctx, node, scope, source)
|
const sourceScope = importSourceToScope(ctx, node, scope, source)
|
||||||
return resolveTypeReference(
|
return resolveTypeReference(ctx, node, sourceScope, imported, true)
|
||||||
ctx,
|
|
||||||
node,
|
|
||||||
fileToScope(ctx, resolved),
|
|
||||||
imported,
|
|
||||||
true
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveImportSource(
|
function importSourceToScope(
|
||||||
ctx: TypeResolveContext,
|
ctx: TypeResolveContext,
|
||||||
node: Node,
|
node: Node,
|
||||||
scope: TypeScope,
|
scope: TypeScope,
|
||||||
source: string
|
source: string
|
||||||
): string {
|
): TypeScope {
|
||||||
const fs: FS = ctx.options.fs || ts?.sys
|
const fs: FS = ctx.options.fs || ts?.sys
|
||||||
if (!fs) {
|
if (!fs) {
|
||||||
ctx.error(
|
ctx.error(
|
||||||
|
@ -657,7 +673,7 @@ function resolveImportSource(
|
||||||
if (resolved) {
|
if (resolved) {
|
||||||
// (hmr) register dependency file on ctx
|
// (hmr) register dependency file on ctx
|
||||||
;(ctx.deps || (ctx.deps = new Set())).add(resolved)
|
;(ctx.deps || (ctx.deps = new Set())).add(resolved)
|
||||||
return normalizePath(resolved)
|
return fileToScope(ctx, normalizePath(resolved))
|
||||||
} else {
|
} else {
|
||||||
return ctx.error(
|
return ctx.error(
|
||||||
`Failed to resolve import source ${JSON.stringify(source)}.`,
|
`Failed to resolve import source ${JSON.stringify(source)}.`,
|
||||||
|
@ -938,14 +954,13 @@ function recordTypes(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (stmt.type === 'ExportAllDeclaration') {
|
} else if (stmt.type === 'ExportAllDeclaration') {
|
||||||
const targetFile = resolveImportSource(
|
const sourceScope = importSourceToScope(
|
||||||
ctx,
|
ctx,
|
||||||
stmt.source,
|
stmt.source,
|
||||||
scope,
|
scope,
|
||||||
stmt.source.value
|
stmt.source.value
|
||||||
)
|
)
|
||||||
const targetScope = fileToScope(ctx, targetFile)
|
Object.assign(scope.exportedTypes, sourceScope.exportedTypes)
|
||||||
Object.assign(scope.exportedTypes, targetScope.exportedTypes)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1134,7 +1149,7 @@ export function inferRuntimeType(
|
||||||
return [UNKNOWN_TYPE]
|
return [UNKNOWN_TYPE]
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'TSTypeReference':
|
case 'TSTypeReference': {
|
||||||
const resolved = resolveTypeReference(ctx, node, scope)
|
const resolved = resolveTypeReference(ctx, node, scope)
|
||||||
if (resolved) {
|
if (resolved) {
|
||||||
return inferRuntimeType(ctx, resolved, resolved._ownerScope)
|
return inferRuntimeType(ctx, resolved, resolved._ownerScope)
|
||||||
|
@ -1197,6 +1212,7 @@ export function inferRuntimeType(
|
||||||
}
|
}
|
||||||
// cannot infer, fallback to UNKNOWN: ThisParameterType
|
// cannot infer, fallback to UNKNOWN: ThisParameterType
|
||||||
return [UNKNOWN_TYPE]
|
return [UNKNOWN_TYPE]
|
||||||
|
}
|
||||||
|
|
||||||
case 'TSParenthesizedType':
|
case 'TSParenthesizedType':
|
||||||
return inferRuntimeType(ctx, node.typeAnnotation, scope)
|
return inferRuntimeType(ctx, node.typeAnnotation, scope)
|
||||||
|
@ -1228,6 +1244,19 @@ export function inferRuntimeType(
|
||||||
case 'ClassDeclaration':
|
case 'ClassDeclaration':
|
||||||
return ['Object']
|
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:
|
default:
|
||||||
return [UNKNOWN_TYPE] // no runtime check
|
return [UNKNOWN_TYPE] // no runtime check
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue