Skip to content

Commit b80c48c

Browse files
committed
feat: support compile js files
1 parent 62c9a5d commit b80c48c

File tree

8 files changed

+126
-84
lines changed

8 files changed

+126
-84
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@
8787
},
8888
"ava": {
8989
"extensions": [
90+
"js",
9091
"ts",
9192
"tsx"
9293
],
@@ -95,7 +96,7 @@
9596
],
9697
"cache": false,
9798
"files": [
98-
"packages/**/*.spec.{ts,tsx}"
99+
"packages/**/*.spec.{js,ts,tsx}"
99100
],
100101
"environmentVariables": {
101102
"SWC_NODE_PROJECT": "./tsconfig.test.json"

packages/integrate-module/src/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import { supportedExtensions } from 'file-type'
77
import { foo } from './foo.mjs'
88
import { bar } from './subdirectory/bar.mjs'
99
import { baz } from './subdirectory/index.mjs'
10+
import { bar as subBar } from '@subdirectory/bar.mjs'
11+
import './js-module.mjs'
1012

1113
await test('file-type should work', () => {
1214
assert.ok(supportedExtensions.has('jpg'))
@@ -23,3 +25,7 @@ await test('resolve nested file path', () => {
2325
await test('resolve nested entry point', () => {
2426
assert.equal(baz(), 'baz')
2527
})
28+
29+
await test('resolve paths', () => {
30+
assert.equal(subBar(), 'bar')
31+
})
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/* eslint import/order: off */
2+
import assert from 'node:assert'
3+
import test from 'node:test'
4+
5+
import { supportedExtensions } from 'file-type'
6+
7+
import { foo } from './foo.mts'
8+
import { bar } from './subdirectory/bar.mts'
9+
import { baz } from './subdirectory/index.mts'
10+
import { bar as subBar } from '@subdirectory/bar.mts'
11+
12+
await test('js:file-type should work', () => {
13+
assert.ok(supportedExtensions.has('jpg'))
14+
})
15+
16+
await test('js:resolve adjacent file path', () => {
17+
assert.equal(foo(), 'foo')
18+
})
19+
20+
await test('js:resolve nested file path', () => {
21+
assert.equal(bar(), 'bar')
22+
})
23+
24+
await test('js:resolve nested entry point', () => {
25+
assert.equal(baz(), 'baz')
26+
})
27+
28+
await test('js:resolve paths', () => {
29+
assert.equal(subBar(), 'bar')
30+
})

packages/integrate-module/tsconfig.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@
44
"target": "ESNext",
55
"module": "ESNext",
66
"composite": true,
7-
"outDir": "dist"
7+
"outDir": "dist",
8+
"baseUrl": "./",
9+
"paths": {
10+
"@subdirectory/*": ["./src/subdirectory/*"],
11+
},
812
},
9-
"include": ["src"]
13+
"include": ["src"],
1014
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { FIXTURE } from '@integrate/fixture'
2+
import test from 'ava'
3+
4+
test('js should transpile paths', (t) => {
5+
t.is(FIXTURE, 'fixture')
6+
})

packages/register/esm.mts

Lines changed: 30 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,12 @@
1+
import type { LoadHook, ResolveHook } from 'node:module'
12
import { fileURLToPath, pathToFileURL } from 'url'
23

34
import ts from 'typescript'
45

56
// @ts-expect-error
67
import { readDefaultTsConfig } from '../lib/read-default-tsconfig.js'
78
// @ts-expect-error
8-
import { compile } from '../lib/register.js'
9-
10-
interface ResolveContext {
11-
conditions: string[]
12-
parentURL: string | undefined
13-
}
14-
15-
interface ResolveResult {
16-
format?: string
17-
shortCircuit?: boolean
18-
url: string
19-
}
20-
21-
type ResolveArgs = [
22-
specifier: string,
23-
context?: ResolveContext,
24-
nextResolve?: (...args: ResolveArgs) => Promise<ResolveResult>,
25-
]
26-
type ResolveFn = (...args: Required<ResolveArgs>) => Promise<ResolveResult>
9+
import { AVAILABLE_EXTENSION_PATTERN, AVAILABLE_TS_EXTENSION_PATTERN, compile } from '../lib/register.js'
2710

2811
const tsconfig: ts.CompilerOptions = readDefaultTsConfig()
2912
tsconfig.module = ts.ModuleKind.ESNext
@@ -33,25 +16,24 @@ const host: ts.ModuleResolutionHost = {
3316
fileExists: ts.sys.fileExists,
3417
readFile: ts.sys.readFile,
3518
}
36-
const EXTENSIONS: string[] = [ts.Extension.Ts, ts.Extension.Tsx, ts.Extension.Mts]
3719

38-
export const resolve: ResolveFn = async (specifier, context, nextResolve) => {
39-
const isTS = EXTENSIONS.some((ext) => specifier.endsWith(ext))
20+
export const resolve: ResolveHook = async (specifier, context, nextResolve) => {
21+
if (!AVAILABLE_EXTENSION_PATTERN.test(specifier)) {
22+
return nextResolve(specifier)
23+
}
4024

4125
// entrypoint
4226
if (!context.parentURL) {
4327
return {
44-
format: isTS ? 'ts' : undefined,
28+
importAttributes: {
29+
...context.importAttributes,
30+
swc: 'entrypoint',
31+
},
4532
url: specifier,
4633
shortCircuit: true,
4734
}
4835
}
4936

50-
// import/require from external library
51-
if (context.parentURL.includes('/node_modules/') && !isTS) {
52-
return nextResolve(specifier)
53-
}
54-
5537
const { resolvedModule } = ts.resolveModuleName(
5638
specifier.startsWith('file:') ? fileURLToPath(specifier) : specifier,
5739
fileURLToPath(context.parentURL),
@@ -60,50 +42,44 @@ export const resolve: ResolveFn = async (specifier, context, nextResolve) => {
6042
moduleResolutionCache,
6143
)
6244

63-
// import from local project to local project TS file
45+
// local project file
6446
if (
6547
resolvedModule &&
66-
!resolvedModule.resolvedFileName.includes('/node_modules/') &&
67-
EXTENSIONS.includes(resolvedModule.extension)
48+
(!resolvedModule.resolvedFileName.includes('/node_modules/') ||
49+
AVAILABLE_TS_EXTENSION_PATTERN.test(resolvedModule.resolvedFileName))
6850
) {
6951
return {
70-
format: 'ts',
7152
url: pathToFileURL(resolvedModule.resolvedFileName).href,
7253
shortCircuit: true,
54+
importAttributes: {
55+
...context.importAttributes,
56+
swc: resolvedModule.resolvedFileName,
57+
},
7358
}
7459
}
7560

76-
// import from local project to either:
77-
// - something TS couldn't resolve
78-
// - external library
79-
// - local project non-TS file
61+
// files could not resolved by typescript
8062
return nextResolve(specifier)
8163
}
8264

83-
interface LoadContext {
84-
conditions: string[]
85-
format: string | null | undefined
86-
}
87-
88-
interface LoadResult {
89-
format: string
90-
shortCircuit?: boolean
91-
source: string | ArrayBuffer | SharedArrayBuffer | Uint8Array
92-
}
93-
94-
type LoadArgs = [url: string, context: LoadContext, nextLoad?: (...args: LoadArgs) => Promise<LoadResult>]
95-
type LoadFn = (...args: Required<LoadArgs>) => Promise<LoadResult>
96-
9765
const tsconfigForSWCNode = {
9866
...tsconfig,
9967
paths: undefined,
10068
baseUrl: undefined,
10169
}
10270

103-
export const load: LoadFn = async (url, context, nextLoad) => {
104-
if (context.format === 'ts') {
105-
const { source } = await nextLoad(url, context)
106-
const code = typeof source === 'string' ? source : Buffer.from(source).toString()
71+
export const load: LoadHook = async (url, context, nextLoad) => {
72+
const swcAttribute = context.importAttributes.swc
73+
74+
if (swcAttribute) {
75+
delete context.importAttributes.swc
76+
77+
const { source } = await nextLoad(url, {
78+
...context,
79+
format: 'ts' as any,
80+
})
81+
82+
const code = !source || typeof source === 'string' ? source : Buffer.from(source).toString()
10783
const compiled = await compile(code, fileURLToPath(url), tsconfigForSWCNode, true)
10884
return {
10985
format: 'module',

packages/register/register.ts

Lines changed: 45 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,32 @@
1-
import { platform } from 'os'
2-
import { resolve } from 'path'
3-
41
import { Options, transform, transformSync } from '@swc-node/core'
52
import { installSourceMapSupport, SourcemapMap } from '@swc-node/sourcemap-support'
63
import { addHook } from 'pirates'
74
import * as ts from 'typescript'
85

96
import { readDefaultTsConfig, tsCompilerOptionsToSwcConfig } from './read-default-tsconfig'
107

11-
const DEFAULT_EXTENSIONS = ['.js', '.jsx', '.es6', '.es', '.mjs', '.ts', '.tsx']
12-
const PLATFORM = platform()
8+
const DEFAULT_EXTENSIONS = [
9+
ts.Extension.Js,
10+
ts.Extension.Ts,
11+
ts.Extension.Jsx,
12+
ts.Extension.Tsx,
13+
ts.Extension.Mjs,
14+
ts.Extension.Mts,
15+
ts.Extension.Cjs,
16+
ts.Extension.Cts,
17+
'.es6',
18+
'.es',
19+
]
20+
21+
export const AVAILABLE_TS_EXTENSION_PATTERN = new RegExp(
22+
`(?<!\\.d(${[ts.Extension.Ts, ts.Extension.Tsx, ts.Extension.Mts, ts.Extension.Cts].map((ext) => ext.replace(/^\./, '\\.')).join('|')}))$`,
23+
'i',
24+
)
25+
26+
export const AVAILABLE_EXTENSION_PATTERN = new RegExp(
27+
`(?<!\\.d(${DEFAULT_EXTENSIONS.map((ext) => ext.replace(/^\./, '\\.')).join('|')}))$`,
28+
'i',
29+
)
1330

1431
const injectInlineSourceMap = ({
1532
filename,
@@ -32,49 +49,51 @@ const injectInlineSourceMap = ({
3249
export function compile(
3350
sourcecode: string,
3451
filename: string,
35-
options: ts.CompilerOptions & { fallbackToTs?: (filename: string) => boolean },
52+
options: ts.CompilerOptions & {
53+
fallbackToTs?: (filename: string) => boolean
54+
},
3655
): string
3756

3857
export function compile(
3958
sourcecode: string,
4059
filename: string,
41-
options: ts.CompilerOptions & { fallbackToTs?: (filename: string) => boolean },
60+
options: ts.CompilerOptions & {
61+
fallbackToTs?: (filename: string) => boolean
62+
},
4263
async: false,
4364
): string
4465

4566
export function compile(
4667
sourcecode: string,
4768
filename: string,
48-
options: ts.CompilerOptions & { fallbackToTs?: (filename: string) => boolean },
69+
options: ts.CompilerOptions & {
70+
fallbackToTs?: (filename: string) => boolean
71+
},
4972
async: true,
5073
): Promise<string>
5174

5275
export function compile(
5376
sourcecode: string,
5477
filename: string,
55-
options: ts.CompilerOptions & { fallbackToTs?: (filename: string) => boolean },
78+
options: ts.CompilerOptions & {
79+
fallbackToTs?: (filename: string) => boolean
80+
},
5681
async: boolean,
5782
): string | Promise<string>
5883

5984
export function compile(
6085
sourcecode: string,
6186
filename: string,
62-
options: ts.CompilerOptions & { fallbackToTs?: (filename: string) => boolean },
87+
options: ts.CompilerOptions & {
88+
fallbackToTs?: (filename: string) => boolean
89+
},
6390
async = false,
6491
) {
65-
if (filename.endsWith('.d.ts')) {
66-
return ''
67-
}
68-
if (options.files && (options.files as string[]).length) {
69-
if (
70-
PLATFORM === 'win32' &&
71-
(options.files as string[]).every((file) => filename !== resolve(process.cwd(), file))
72-
) {
73-
return sourcecode
74-
}
75-
if (PLATFORM !== 'win32' && (options.files as string[]).every((file) => !filename.endsWith(file))) {
76-
return sourcecode
77-
}
92+
if (
93+
(filename.includes('node_modules') && !AVAILABLE_TS_EXTENSION_PATTERN.test(filename)) ||
94+
!AVAILABLE_EXTENSION_PATTERN.test(filename)
95+
) {
96+
return sourcecode
7897
}
7998
if (options && typeof options.fallbackToTs === 'function' && options.fallbackToTs(filename)) {
8099
delete options.fallbackToTs
@@ -96,7 +115,7 @@ export function compile(
96115
} else {
97116
swcRegisterConfig = tsCompilerOptionsToSwcConfig(options, filename)
98117
}
99-
118+
100119
if (async) {
101120
return transform(sourcecode, filename, swcRegisterConfig).then(({ code, map }) => {
102121
return injectInlineSourceMap({ filename, code, map })
@@ -108,8 +127,8 @@ export function compile(
108127
}
109128

110129
export function register(options: Partial<ts.CompilerOptions> = {}, hookOpts = {}) {
111-
if (!process.env.SWCRC) {
112-
options = Object.keys(options).length ? options : readDefaultTsConfig()
130+
if (!process.env.SWCRC) {
131+
options = Object.keys(options).length ? options : readDefaultTsConfig()
113132
}
114133
options.module = ts.ModuleKind.CommonJS
115134
installSourceMapSupport()

packages/register/tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@
1111
},
1212
],
1313
"include": ["."],
14-
"exclude": ["lib"],
14+
"exclude": ["lib", "__test__"],
1515
}

0 commit comments

Comments
 (0)