11import { URL , pathToFileURL , fileURLToPath } from 'url'
22import fs from 'fs'
3+ import path from 'path'
34import { transformSync } from 'esbuild'
4- import { createMatchPath , loadConfig } from 'tsconfig-paths'
5+ import typescript from '@rollup/plugin-typescript'
6+ import JoyCon from 'joycon'
57
6- const baseURL = pathToFileURL ( `${ process . cwd ( ) } /` ) . href
78const isWindows = process . platform === 'win32'
89
910const extensionsRegex = / \. ( m ? t s x ? | j s o n ) $ /
10- const excludeRegex = / ^ \w + : /
11- const tsExtensions = [ '.mts' , '.ts' , '.cts' , '.tsx' ] // https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-5.html
12- const jsExtensions = [ '.mjs' , '.js' , '.cjs' , '.jsx' ]
13- const extensions = [ ...tsExtensions , ...jsExtensions ]
14-
15- const tsconfig = loadConfig ( )
16-
17- const matchPath = tsconfig . resultType === 'success' ? createMatchPath ( tsconfig . absoluteBaseUrl , tsconfig . paths ) : undefined
11+ const tsconfigPath = new JoyCon ( { parseJSON : ( ) => { } } ) . loadSync ( [ 'tsconfig.json' ] ) . path
12+ const pluginTypescript = typescript ( {
13+ tsconfig : tsconfigPath ,
14+ } )
1815
1916function esbuildTransformSync ( rawSource , filename , url , format ) {
2017 const {
@@ -39,49 +36,75 @@ function esbuildTransformSync(rawSource, filename, url, format) {
3936 return { js, jsSourceMap }
4037}
4138
42- export const tryPathWithExtensions = ( path ) => {
43- for ( const ext of extensions ) {
44- const p = `${ path } ${ ext } `
45- if ( fs . existsSync ( p ) )
46- return p
39+ function getTsCompatSpecifier ( parentURL , specifier ) {
40+ let tsSpecifier
41+ let search
42+
43+ if ( specifier . startsWith ( './' ) || specifier . startsWith ( '../' ) ) {
44+ // Relative import
45+ const url = new URL ( specifier , parentURL )
46+ tsSpecifier = fileURLToPath ( url ) . replace ( / \. t s x ? $ / , '' )
47+ search = url . search
48+ }
49+ else {
50+ // Bare import
51+ tsSpecifier = specifier
52+ search = ''
53+ }
54+
55+ return {
56+ tsSpecifier,
57+ search,
4758 }
48- return null
4959}
5060
51- export function resolve ( specifier , context , defaultResolve ) {
52- // baseUrl & paths takes the highest precedence, as TypeScript behaves.
53- if ( matchPath ) {
54- const nodePath = matchPath ( specifier , undefined , undefined , extensions )
61+ function isValidURL ( s ) {
62+ try {
63+ return ! ! new URL ( s )
64+ }
65+ catch ( e ) {
66+ if ( e instanceof TypeError )
67+ return false
5568
56- if ( nodePath ) {
57- const foundPath = tryPathWithExtensions ( nodePath )
58- return {
59- url : pathToFileURL ( foundPath ) . href ,
60- format : extensionsRegex . test ( foundPath ) && 'module' ,
61- }
69+ throw e
70+ }
71+ }
72+
73+ export async function resolve ( specifier , context , defaultResolve ) {
74+ const {
75+ parentURL,
76+ } = context
77+
78+ let url
79+
80+ // According to Node's algorithm, we first check if it is a valid URL.
81+ // When the module is the entry point, node will provides a file URL to it.
82+ if ( isValidURL ( specifier ) ) {
83+ url = new URL ( specifier )
84+ }
85+ else {
86+ // Try to resolve the module according to typescript's algorithm,
87+ // and construct a valid url.
88+ const parsed = getTsCompatSpecifier ( parentURL , specifier )
89+ const path = pluginTypescript . resolveId ( parsed . tsSpecifier , fileURLToPath ( parentURL ) )
90+ if ( path ) {
91+ url = pathToFileURL ( path )
92+ url . search = parsed . search
6293 }
6394 }
6495
65- const { parentURL = baseURL } = context
66- const url = new URL ( specifier , parentURL )
67- if ( extensionsRegex . test ( url . pathname ) )
68- return { url : url . href , format : 'module' }
69-
70- // ignore `data:` and `node:` prefix etc.
71- if ( ! excludeRegex . test ( specifier ) ) {
72- // Try to resolve extension
73- const path = fileURLToPath ( url . href )
74- const foundPath = tryPathWithExtensions ( path )
75- if ( foundPath ) {
76- url . pathname = foundPath
96+ if ( url ) {
97+ // If the resolved file is typescript
98+ if ( extensionsRegex . test ( url . pathname ) ) {
7799 return {
78100 url : url . href ,
79- format : extensionsRegex . test ( url . pathname ) && 'module' ,
101+ format : 'module' ,
80102 }
81103 }
104+ // Else, for other types, use default resolve with the valid path
105+ return defaultResolve ( url . href , context , defaultResolve )
82106 }
83107
84- // Let Node.js handle all other specifiers.
85108 return defaultResolve ( specifier , context , defaultResolve )
86109}
87110
0 commit comments