Skip to content

Commit

Permalink
feat: update esm module resolver
Browse files Browse the repository at this point in the history
  • Loading branch information
yeliex authored and Brooooooklyn committed Jun 28, 2024
1 parent 014cf6a commit 92f05d4
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 46 deletions.
3 changes: 2 additions & 1 deletion packages/integrate-module/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import test from 'node:test'

import { supportedExtensions } from 'file-type'

import { bar as subBar } from '@subdirectory/bar.mjs'
import { CompiledClass } from './compiled.js'
import { foo } from './foo.mjs'
import { bar } from './subdirectory/bar.mjs'
import { baz } from './subdirectory/index.mjs'
import { bar as subBar } from '@subdirectory/bar.mjs'
import './js-module.mjs'

await test('file-type should work', () => {
Expand Down
6 changes: 3 additions & 3 deletions packages/integrate-module/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
"outDir": "dist",
"baseUrl": "./",
"paths": {
"@subdirectory/*": ["./src/subdirectory/*"],
},
"@subdirectory/*": ["./src/subdirectory/*"]
}
},
"include": ["src"],
"include": ["src", "package.json"]
}
128 changes: 86 additions & 42 deletions packages/register/esm.mts
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import type { LoadHook, ResolveHook } from 'node:module'
import { fileURLToPath, pathToFileURL } from 'url'
import { createRequire, type LoadFnOutput, type LoadHook, type ResolveFnOutput, type ResolveHook } from 'node:module'
import { fileURLToPath, parse as parseUrl, pathToFileURL } from 'url'

import debugFactory from 'debug'
import ts from 'typescript'


// @ts-expect-error
import { readDefaultTsConfig } from '../lib/read-default-tsconfig.js'
// @ts-expect-error
import { AVAILABLE_EXTENSION_PATTERN, AVAILABLE_TS_EXTENSION_PATTERN, compile } from '../lib/register.js'
import { AVAILABLE_TS_EXTENSION_PATTERN, compile } from '../lib/register.js'

const debug = debugFactory('@swc-node')

const tsconfig: ts.CompilerOptions = readDefaultTsConfig()
tsconfig.module = ts.ModuleKind.ESNext
Expand All @@ -17,21 +21,40 @@ const host: ts.ModuleResolutionHost = {
readFile: ts.sys.readFile,
}

const addShortCircuitSignal = <T extends ResolveFnOutput | LoadFnOutput>(input: T): T => {
return {
...input,
shortCircuit: true,
}
}

const INTERNAL_MODULE_PATTERN = /^(data|node|nodejs):/

export const resolve: ResolveHook = async (specifier, context, nextResolve) => {
if (specifier.startsWith('file:') && !AVAILABLE_EXTENSION_PATTERN.test(specifier)) {
return nextResolve(specifier)
debug('resolve', specifier, JSON.stringify(context))

if (INTERNAL_MODULE_PATTERN.test(specifier)) {
debug('resolved original caused by internal format', specifier)

return addShortCircuitSignal({
url: specifier,
})
}

// entrypoint
if (!context.parentURL) {
return {
importAttributes: {
...context.importAttributes,
swc: 'entrypoint',
},
const parsedUrl = parseUrl(specifier)

// as entrypoint, just return specifier
if (!context.parentURL || parsedUrl.protocol === 'file:') {
debug('resolved original caused by protocol', specifier)
return addShortCircuitSignal({
url: specifier,
shortCircuit: true,
}
format: 'module',
})
}

// import attributes, support json currently
if (context.importAttributes?.type) {
return addShortCircuitSignal(await nextResolve(specifier))
}

const { resolvedModule } = ts.resolveModuleName(
Expand All @@ -45,21 +68,39 @@ export const resolve: ResolveHook = async (specifier, context, nextResolve) => {
// local project file
if (
resolvedModule &&
(!resolvedModule.resolvedFileName.includes('/node_modules/') ||
AVAILABLE_TS_EXTENSION_PATTERN.test(resolvedModule.resolvedFileName))
!resolvedModule.resolvedFileName.includes('/node_modules/') &&
AVAILABLE_TS_EXTENSION_PATTERN.test(resolvedModule.resolvedFileName)
) {
return {
debug('resolved by typescript', specifier, resolvedModule.resolvedFileName)

return addShortCircuitSignal({
...context,
url: pathToFileURL(resolvedModule.resolvedFileName).href,
shortCircuit: true,
importAttributes: {
...context.importAttributes,
swc: resolvedModule.resolvedFileName,
},
}
format: 'module',
})
}

// files could not resolved by typescript
return nextResolve(specifier)
try {
// files could not resolved by typescript or resolved as dts, fallback to use node resolver
const res = await nextResolve(specifier)
debug('fallback resolved by node', specifier, res.url, res.format)
return addShortCircuitSignal(res)
} catch (resolveError) {
// fallback to cjs resolve as may import non-esm files
try {
const resolution = pathToFileURL(createRequire(process.cwd()).resolve(specifier)).toString()

debug('resolved by node commonjs', specifier, resolution)

return addShortCircuitSignal({
format: 'commonjs',
url: resolution,
})
} catch (error) {
debug('resolved by cjs error', specifier, error)
throw resolveError
}
}
}

const tsconfigForSWCNode = {
Expand All @@ -69,24 +110,27 @@ const tsconfigForSWCNode = {
}

export const load: LoadHook = async (url, context, nextLoad) => {
const swcAttribute = context.importAttributes.swc

if (swcAttribute) {
delete context.importAttributes.swc

const { source } = await nextLoad(url, {
...context,
format: 'ts' as any,
})
debug('load', url, JSON.stringify(context))

const code = !source || typeof source === 'string' ? source : Buffer.from(source).toString()
const compiled = await compile(code, fileURLToPath(url), tsconfigForSWCNode, true)
return {
format: 'module',
source: compiled,
shortCircuit: true,
}
} else {
if (['builtin', 'json', 'wasm'].includes(context.format)) {
debug('load original caused by internal format', url)
return nextLoad(url, context)
}

const { source, format } = await nextLoad(url, {
...context,
})

debug('loaded', url, format)

const code = !source || typeof source === 'string' ? source : Buffer.from(source).toString()
const compiled = await compile(code, url, tsconfigForSWCNode, true)

debug('compiled', url, format)

return addShortCircuitSignal({
// for lazy: ts-node think format would undefined, actually it should not, keep it as original temporarily
format,
source: compiled,
})
}

0 comments on commit 92f05d4

Please sign in to comment.