2
2
3
3
const getEsmExports = require ( './get-esm-exports.js' )
4
4
const { parse : parseCjs } = require ( 'cjs-module-lexer' )
5
- const { readFileSync } = require ( 'fs' )
5
+ const { readFileSync, existsSync } = require ( 'fs' )
6
6
const { builtinModules } = require ( 'module' )
7
7
const { fileURLToPath, pathToFileURL } = require ( 'url' )
8
- const { dirname } = require ( 'path' )
8
+ const { dirname, join } = require ( 'path' )
9
9
10
10
function addDefault ( arr ) {
11
11
return new Set ( [ 'default' , ...arr ] )
@@ -27,6 +27,64 @@ function getExportsForNodeBuiltIn (name) {
27
27
28
28
const urlsBeingProcessed = new Set ( ) // Guard against circular imports.
29
29
30
+ /**
31
+ * This function looks for the package.json which contains the specifier trying to resolve.
32
+ * Once the package.json file has been found, we extract the file path from the specifier
33
+ * @param {* } specifier The specifier that is being search for inside the imports object
34
+ * @param {* } fromUrl The url from which the search starts from
35
+ * @returns file to url to file export
36
+ */
37
+ function resolvePackageImports ( specifier , fromUrl ) {
38
+ if ( ! specifier . startsWith ( '#' ) ) {
39
+ return null
40
+ }
41
+
42
+ try {
43
+ const fromPath = fileURLToPath ( fromUrl )
44
+ let currentDir = dirname ( fromPath )
45
+
46
+ // search for package.json file which has the real url to export
47
+ while ( currentDir !== dirname ( currentDir ) ) {
48
+ const packageJsonPath = join ( currentDir , 'package.json' )
49
+
50
+ if ( existsSync ( packageJsonPath ) ) {
51
+ const packageJson = JSON . parse ( readFileSync ( packageJsonPath , 'utf8' ) )
52
+
53
+ if ( packageJson . imports && packageJson . imports [ specifier ] ) {
54
+ const imports = packageJson . imports [ specifier ]
55
+
56
+ // Look for path inside packageJson
57
+ let resolvedPath
58
+ if ( typeof imports == 'object' ) {
59
+ const requireSpecifier = imports . require
60
+ const importSpecifier = imports . import
61
+ // look for the possibility of require and import which is standard for CJS/ESM
62
+ if ( requireSpecifier || importSpecifier ) {
63
+ // trying to resolve based on order of importance
64
+ resolvedPath = requireSpecifier . node || requireSpecifier . default || importSpecifier . node || importSpecifier . default
65
+ } else if ( imports . node || imports . default ) {
66
+ resolvedPath = imports . node || imports . default
67
+ }
68
+ } else if ( typeof imports == 'string' ) {
69
+ resolvedPath = imports
70
+ }
71
+
72
+ if ( existsSync ( resolvedPath ) ) {
73
+ return resolvedPath
74
+ }
75
+ }
76
+ // return if we find a package.json but did not find an import
77
+ return null
78
+ }
79
+ currentDir = dirname ( currentDir )
80
+ }
81
+ } catch ( error ) {
82
+ throw new Error ( `Failed to find sub path export: ${ specifier } ` )
83
+ }
84
+
85
+ return null
86
+ }
87
+
30
88
async function getCjsExports ( url , context , parentLoad , source ) {
31
89
if ( urlsBeingProcessed . has ( url ) ) {
32
90
return [ ]
@@ -46,8 +104,19 @@ async function getCjsExports (url, context, parentLoad, source) {
46
104
if ( re === '.' ) {
47
105
re = './'
48
106
}
107
+
108
+ let newUrl
109
+ // Entries in the import field should always start with #
110
+ if ( re . startsWith ( '#' ) ) {
111
+ re = resolvePackageImports ( re , url )
112
+ if ( ! re ) {
113
+ // Unable to resolve specifier import
114
+ return
115
+ }
116
+ }
117
+
49
118
// Resolve the re-exported module relative to the current module.
50
- const newUrl = pathToFileURL ( require . resolve ( re , { paths : [ dirname ( fileURLToPath ( url ) ) ] } ) ) . href
119
+ newUrl = pathToFileURL ( require . resolve ( re , { paths : [ dirname ( fileURLToPath ( url ) ) ] } ) ) . href
51
120
52
121
if ( newUrl . endsWith ( '.node' ) || newUrl . endsWith ( '.json' ) ) {
53
122
return
0 commit comments