-
-
Notifications
You must be signed in to change notification settings - Fork 72
/
esm.mts
104 lines (92 loc) · 2.87 KB
/
esm.mts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
import { fileURLToPath, pathToFileURL } from 'url'
import ts from 'typescript'
// @ts-expect-error
import { readDefaultTsConfig } from '../lib/read-default-tsconfig.js'
// @ts-expect-error
import { compile } from '../lib/register.js'
interface ResolveContext {
conditions: string[]
parentURL: string | undefined
}
interface ResolveResult {
format?: string
shortCircuit?: boolean
url: string
}
type ResolveArgs = [
specifier: string,
context?: ResolveContext,
nextResolve?: (...args: ResolveArgs) => Promise<ResolveResult>,
]
type ResolveFn = (...args: Required<ResolveArgs>) => Promise<ResolveResult>
const tsconfig: ts.CompilerOptions = readDefaultTsConfig()
tsconfig.module = ts.ModuleKind.ESNext
const moduleResolutionCache = ts.createModuleResolutionCache(ts.sys.getCurrentDirectory(), (x) => x, tsconfig)
const host: ts.ModuleResolutionHost = {
fileExists: ts.sys.fileExists,
readFile: ts.sys.readFile,
}
const EXTENSIONS: string[] = [ts.Extension.Ts, ts.Extension.Tsx, ts.Extension.Mts]
export const resolve: ResolveFn = async (specifier, context, nextResolve) => {
// entrypoint
if (!context.parentURL) {
return {
format: EXTENSIONS.some((ext) => specifier.endsWith(ext)) ? 'ts' : undefined,
url: specifier,
shortCircuit: true,
}
}
// import/require from external library
if (context.parentURL.includes('/node_modules/')) {
return nextResolve(specifier)
}
const { resolvedModule } = ts.resolveModuleName(
specifier,
fileURLToPath(context.parentURL),
tsconfig,
host,
moduleResolutionCache,
)
// import from local project to local project TS file
if (
resolvedModule &&
!resolvedModule.resolvedFileName.includes('/node_modules/') &&
EXTENSIONS.includes(resolvedModule.extension)
) {
return {
format: 'ts',
url: pathToFileURL(resolvedModule.resolvedFileName).href,
shortCircuit: true,
}
}
// import from local project to either:
// - something TS couldn't resolve
// - external library
// - local project non-TS file
return nextResolve(specifier)
}
interface LoadContext {
conditions: string[]
format: string | null | undefined
}
interface LoadResult {
format: string
shortCircuit?: boolean
source: string | ArrayBuffer | SharedArrayBuffer | Uint8Array
}
type LoadArgs = [url: string, context: LoadContext, nextLoad?: (...args: LoadArgs) => Promise<LoadResult>]
type LoadFn = (...args: Required<LoadArgs>) => Promise<LoadResult>
export const load: LoadFn = async (url, context, nextLoad) => {
if (context.format === 'ts') {
const { source } = await nextLoad(url, context)
const code = typeof source === 'string' ? source : Buffer.from(source).toString()
const compiled = await compile(code, url, tsconfig, true)
return {
format: 'module',
source: compiled,
shortCircuit: true,
}
} else {
return nextLoad(url, context)
}
}