Skip to content

Commit c1d57a6

Browse files
committed
feat(api): dts resolver for all bundlers
1 parent 74dadb1 commit c1d57a6

File tree

7 files changed

+83
-144
lines changed

7 files changed

+83
-144
lines changed

.changeset/two-pumas-ring.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
"@vue-macros/better-define": minor
3+
"@vue-macros/api": minor
4+
"@vue-macros/define-prop": patch
5+
---
6+
7+
DTS resolver for all bundlers
8+

packages/api/src/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
export * from '@vue-macros/common'
22

33
export * from './vue'
4-
export * from './resolve'
54
export * from './ts'

packages/api/src/resolve.ts

Lines changed: 0 additions & 88 deletions
This file was deleted.

packages/api/src/ts/namespace.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { isTSDeclaration } from './is'
2-
import { resolveTSFileId } from './resolve-file'
2+
import { resolveDts } from './resolve-file'
33
import {
44
resolveTSReferencedType,
55
type TSResolvedType,
@@ -45,7 +45,7 @@ export async function resolveTSNamespace(scope: TSScope): Promise<void> {
4545
type: stmt.declaration,
4646
})
4747
} else if (stmt.type === 'ExportAllDeclaration') {
48-
const resolved = await resolveTSFileId(stmt.source.value, file.filePath)
48+
const resolved = await resolveDts(stmt.source.value, file.filePath)
4949
if (!resolved) continue
5050

5151
const sourceScope = await getTSFile(resolved)
@@ -56,7 +56,7 @@ export async function resolveTSNamespace(scope: TSScope): Promise<void> {
5656
let sourceExports: TSNamespace
5757

5858
if (stmt.source) {
59-
const resolved = await resolveTSFileId(stmt.source.value, file.filePath)
59+
const resolved = await resolveDts(stmt.source.value, file.filePath)
6060
if (!resolved) continue
6161

6262
const scope = await getTSFile(resolved)
@@ -112,7 +112,7 @@ export async function resolveTSNamespace(scope: TSScope): Promise<void> {
112112
type: stmt,
113113
})
114114
} else if (stmt.type === 'ImportDeclaration') {
115-
const resolved = await resolveTSFileId(stmt.source.value, file.filePath)
115+
const resolved = await resolveDts(stmt.source.value, file.filePath)
116116
if (!resolved) continue
117117

118118
const importScope = await getTSFile(resolved)
Lines changed: 67 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,80 @@
1-
import { lstatSync } from 'node:fs'
21
import path from 'node:path'
2+
import { tsFileCache } from './scope'
3+
import type { ResolverFactory } from 'oxc-resolver'
4+
import type { ModuleNode, Plugin } from 'vite'
35

4-
export type ResolveTSFileIdImpl = (
5-
id: string,
6-
importer: string,
7-
) => Promise<string | undefined> | string | undefined
6+
let typesResolver: ResolverFactory
87

9-
export const resolveTSFileId: ResolveTSFileIdImpl = (id, importer) => {
10-
return resolveTSFileIdImpl(id, importer)
8+
const referencedFiles = new Map<string /* file */, Set<string /* importer */>>()
9+
10+
function collectReferencedFile(importer: string, file: string) {
11+
if (!importer) return
12+
if (!referencedFiles.has(file)) {
13+
referencedFiles.set(file, new Set([importer]))
14+
} else {
15+
referencedFiles.get(file)!.add(importer)
16+
}
1117
}
1218

13-
/**
14-
* @limitation don't node_modules and JavaScript file
15-
*/
16-
export const resolveTSFileIdNode: ResolveTSFileIdImpl = (
19+
const resolveCache = new Map<
20+
string /* importer */,
21+
Map<string /* id */, string /* result */>
22+
>()
23+
24+
export async function resolveDts(
1725
id: string,
1826
importer: string,
19-
) => {
20-
return (
21-
tryResolve(id, importer) ||
22-
tryResolve(`${id}.ts`, importer) ||
23-
tryResolve(`${id}.d.ts`, importer) ||
24-
tryResolve(`${id}/index`, importer) ||
25-
tryResolve(`${id}/index.ts`, importer) ||
26-
tryResolve(`${id}/index.d.ts`, importer)
27+
): Promise<string | undefined> {
28+
const cached = resolveCache.get(importer)?.get(id)
29+
if (cached) return cached
30+
31+
if (!typesResolver) {
32+
const { ResolverFactory } = await import('oxc-resolver')
33+
typesResolver = new ResolverFactory({
34+
mainFields: ['types'],
35+
conditionNames: ['types', 'import'],
36+
extensions: ['.d.ts', '.ts'],
37+
})
38+
}
39+
40+
const { error, path: resolved } = await typesResolver.async(
41+
path.dirname(importer),
42+
id,
2743
)
28-
}
29-
function tryResolve(id: string, importer: string) {
30-
const filePath = path.resolve(importer, '..', id)
31-
try {
32-
const stat = lstatSync(filePath)
33-
if (stat.isFile()) return filePath
34-
} catch {
35-
return
44+
if (error || !resolved) return
45+
46+
collectReferencedFile(importer, resolved)
47+
if (resolveCache.has(importer)) {
48+
resolveCache.get(importer)!.set(id, resolved)
49+
} else {
50+
resolveCache.set(importer, new Map([[id, resolved]]))
3651
}
52+
return resolved
3753
}
3854

39-
let resolveTSFileIdImpl: ResolveTSFileIdImpl = resolveTSFileIdNode
55+
export const resolveDtsHMR: NonNullable<Plugin['handleHotUpdate']> = ({
56+
file,
57+
server,
58+
modules,
59+
}) => {
60+
const cache = new Map<string, Set<ModuleNode>>()
61+
if (tsFileCache[file]) delete tsFileCache[file]
62+
63+
const affected = getAffectedModules(file)
64+
return [...modules, ...affected]
4065

41-
export function setResolveTSFileIdImpl(impl: ResolveTSFileIdImpl): void {
42-
resolveTSFileIdImpl = impl
66+
function getAffectedModules(file: string): Set<ModuleNode> {
67+
if (cache.has(file)) return cache.get(file)!
68+
if (!referencedFiles.has(file)) return new Set([])
69+
70+
const modules = new Set<ModuleNode>([])
71+
cache.set(file, modules)
72+
for (const importer of referencedFiles.get(file)!) {
73+
const mods = server.moduleGraph.getModulesByFile(importer)
74+
if (mods) mods.forEach((m) => modules.add(m))
75+
76+
getAffectedModules(importer).forEach((m) => modules.add(m))
77+
}
78+
return modules
79+
}
4380
}

packages/better-define/src/index.ts

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import process from 'node:process'
2-
import { OxcResolve, setResolveTSFileIdImpl } from '@vue-macros/api'
2+
import { resolveDtsHMR } from '@vue-macros/api'
33
import {
44
createFilter,
55
detectVueVersion,
@@ -15,7 +15,6 @@ import {
1515
} from 'unplugin'
1616
import { generatePluginName } from '#macros' with { type: 'macro' }
1717
import { transformBetterDefine } from './core'
18-
import type { PluginContext } from 'rollup'
1918

2019
export type Options = BaseOptions
2120
export type OptionsResolved = MarkRequired<
@@ -47,18 +46,10 @@ const plugin: UnpluginInstance<Options | undefined, false> = createUnplugin(
4746
const options = resolveOptions(userOptions, framework)
4847
const filter = createFilter(options)
4948

50-
const { resolve, handleHotUpdate } = OxcResolve()
51-
5249
return {
5350
name,
5451
enforce: 'pre',
5552

56-
buildStart() {
57-
if (framework === 'rollup' || framework === 'vite') {
58-
setResolveTSFileIdImpl(resolve(this as PluginContext))
59-
}
60-
},
61-
6253
transformInclude: filter,
6354
async transform(code, id) {
6455
try {
@@ -74,7 +65,7 @@ const plugin: UnpluginInstance<Options | undefined, false> = createUnplugin(
7465
options.isProduction ??= config.isProduction
7566
},
7667

77-
handleHotUpdate,
68+
handleHotUpdate: resolveDtsHMR,
7869
},
7970
}
8071
},

packages/define-prop/src/index.ts

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import process from 'node:process'
2-
import { OxcResolve, setResolveTSFileIdImpl } from '@vue-macros/api'
2+
import { resolveDtsHMR } from '@vue-macros/api'
33
import {
44
createFilter,
55
detectVueVersion,
@@ -17,7 +17,6 @@ import {
1717
import { generatePluginName } from '#macros' with { type: 'macro' }
1818
import { transformDefineProp, type Edition } from './core'
1919
import { helperCode, helperId } from './core/helper'
20-
import type { PluginContext } from 'rollup'
2120

2221
export interface Options extends BaseOptions {
2322
edition?: Edition
@@ -52,18 +51,11 @@ const plugin: UnpluginInstance<Options | undefined, false> = createUnplugin(
5251
(userOptions = {}, { framework }) => {
5352
const options = resolveOptions(userOptions, framework)
5453
const filter = createFilter(options)
55-
const { resolve, handleHotUpdate } = OxcResolve()
5654

5755
return {
5856
name,
5957
enforce: 'pre',
6058

61-
buildStart() {
62-
if (framework === 'rollup' || framework === 'vite') {
63-
setResolveTSFileIdImpl(resolve(this as PluginContext))
64-
}
65-
},
66-
6759
resolveId(id) {
6860
if (id === normalizePath(helperId)) return id
6961
},
@@ -91,7 +83,7 @@ const plugin: UnpluginInstance<Options | undefined, false> = createUnplugin(
9183
options.isProduction ??= config.isProduction
9284
},
9385

94-
handleHotUpdate,
86+
handleHotUpdate: resolveDtsHMR,
9587
},
9688
}
9789
},

0 commit comments

Comments
 (0)