@@ -6,44 +6,66 @@ import { fileURLToPath, pathToFileURL } from 'node:url'
6
6
type RegisteredModule = {
7
7
source : string
8
8
loaded : boolean
9
- filename : string
9
+ filepath : string
10
+ // lazily parsed json string
11
+ parsedJson ?: any
10
12
}
11
13
const registeredModules = new Map < string , RegisteredModule > ( )
12
14
13
15
const require = createRequire ( import . meta. url )
14
16
15
17
let hookedIn = false
16
18
19
+ function parseJson ( matchedModule : RegisteredModule ) {
20
+ if ( matchedModule . parsedJson ) {
21
+ return matchedModule . parsedJson
22
+ }
23
+
24
+ try {
25
+ const jsonContent = JSON . parse ( matchedModule . source )
26
+ matchedModule . parsedJson = jsonContent
27
+ return jsonContent
28
+ } catch ( error ) {
29
+ throw new Error ( `Failed to parse JSON module: ${ matchedModule . filepath } ` , { cause : error } )
30
+ }
31
+ }
32
+
17
33
function seedCJSModuleCacheAndReturnTarget ( matchedModule : RegisteredModule , parent : Module ) {
18
34
if ( matchedModule . loaded ) {
19
- return matchedModule . filename
35
+ return matchedModule . filepath
20
36
}
21
- const { source, filename } = matchedModule
37
+ const { source, filepath } = matchedModule
22
38
23
- const mod = new Module ( filename )
39
+ const mod = new Module ( filepath )
24
40
mod . parent = parent
25
- mod . filename = filename
26
- mod . path = dirname ( filename )
41
+ mod . filename = filepath
42
+ mod . path = dirname ( filepath )
27
43
// @ts -expect-error - private untyped API
28
44
mod . paths = Module . _nodeModulePaths ( mod . path )
29
- require . cache [ filename ] = mod
45
+ require . cache [ filepath ] = mod
30
46
31
- const wrappedSource = `(function (exports, require, module, __filename, __dirname) { ${ source } \n});`
32
47
try {
33
- const compiled = vm . runInThisContext ( wrappedSource , {
34
- filename,
35
- lineOffset : 0 ,
36
- displayErrors : true ,
37
- } )
38
- compiled ( mod . exports , createRequire ( pathToFileURL ( filename ) ) , mod , filename , dirname ( filename ) )
48
+ if ( filepath . endsWith ( '.json' ) ) {
49
+ Object . assign ( mod . exports , parseJson ( matchedModule ) )
50
+ } else {
51
+ const wrappedSource = `(function (exports, require, module, __filename, __dirname) { ${ source } \n});`
52
+ const compiled = vm . runInThisContext ( wrappedSource , {
53
+ filename : filepath ,
54
+ lineOffset : 0 ,
55
+ displayErrors : true ,
56
+ } )
57
+ const modRequire = createRequire ( pathToFileURL ( filepath ) )
58
+ compiled ( mod . exports , modRequire , mod , filepath , dirname ( filepath ) )
59
+ }
39
60
mod . loaded = matchedModule . loaded = true
40
61
} catch ( error ) {
41
- throw new Error ( `Failed to compile CJS module: ${ filename } ` , { cause : error } )
62
+ throw new Error ( `Failed to compile CJS module: ${ filepath } ` , { cause : error } )
42
63
}
43
64
44
- return filename
65
+ return filepath
45
66
}
46
67
68
+ // ideally require.extensions could be used, but it does NOT include '.cjs', so hardcoding instead
47
69
const exts = [ '.js' , '.cjs' , '.json' ]
48
70
49
71
function tryWithExtensions ( filename : string ) {
@@ -80,7 +102,7 @@ export function registerCJSModules(baseUrl: URL, modules: Map<string, string>) {
80
102
for ( const [ filename , source ] of modules . entries ( ) ) {
81
103
const target = join ( basePath , filename )
82
104
83
- registeredModules . set ( target , { source, loaded : false , filename : target } )
105
+ registeredModules . set ( target , { source, loaded : false , filepath : target } )
84
106
}
85
107
86
108
if ( ! hookedIn ) {
@@ -101,8 +123,30 @@ export function registerCJSModules(baseUrl: URL, modules: Map<string, string>) {
101
123
let matchedModule = tryMatchingWithIndex ( target )
102
124
103
125
if ( ! isRelative && ! target . startsWith ( '/' ) ) {
126
+ const packageName = target . startsWith ( '@' )
127
+ ? target . split ( '/' ) . slice ( 0 , 2 ) . join ( '/' )
128
+ : target . split ( '/' ) [ 0 ]
129
+ const moduleInPackagePath = target . slice ( packageName . length + 1 )
130
+
104
131
for ( const nodeModulePaths of args [ 1 ] . paths ) {
105
- const potentialPath = join ( nodeModulePaths , target )
132
+ const potentialPackageJson = join ( nodeModulePaths , packageName , 'package.json' )
133
+
134
+ const maybePackageJson = registeredModules . get ( potentialPackageJson )
135
+
136
+ let relativeTarget = moduleInPackagePath
137
+
138
+ let pkgJson : any = null
139
+ if ( maybePackageJson ) {
140
+ pkgJson = parseJson ( maybePackageJson )
141
+
142
+ // TODO: exports and anything else like that
143
+ if ( moduleInPackagePath . length === 0 && pkgJson . main ) {
144
+ relativeTarget = pkgJson . main
145
+ }
146
+ }
147
+
148
+ const potentialPath = join ( nodeModulePaths , packageName , relativeTarget )
149
+
106
150
matchedModule = tryMatchingWithIndex ( potentialPath )
107
151
if ( matchedModule ) {
108
152
break
0 commit comments