Skip to content

Commit c9b2a33

Browse files
committed
Added compatibility for specifier imports
1 parent ffb5682 commit c9b2a33

File tree

8 files changed

+125
-3
lines changed

8 files changed

+125
-3
lines changed

lib/get-exports.js

Lines changed: 72 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22

33
const getEsmExports = require('./get-esm-exports.js')
44
const { parse: parseCjs } = require('cjs-module-lexer')
5-
const { readFileSync } = require('fs')
5+
const { readFileSync, existsSync } = require('fs')
66
const { builtinModules } = require('module')
77
const { fileURLToPath, pathToFileURL } = require('url')
8-
const { dirname } = require('path')
8+
const { dirname, join } = require('path')
99

1010
function addDefault (arr) {
1111
return new Set(['default', ...arr])
@@ -27,6 +27,64 @@ function getExportsForNodeBuiltIn (name) {
2727

2828
const urlsBeingProcessed = new Set() // Guard against circular imports.
2929

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+
3088
async function getCjsExports (url, context, parentLoad, source) {
3189
if (urlsBeingProcessed.has(url)) {
3290
return []
@@ -46,8 +104,19 @@ async function getCjsExports (url, context, parentLoad, source) {
46104
if (re === '.') {
47105
re = './'
48106
}
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+
49118
// 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
51120

52121
if (newUrl.endsWith('.node') || newUrl.endsWith('.json')) {
53122
return

test/fixtures/package.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"name": "test-fixtures",
3+
"imports": {
4+
"#main-entry-point": {
5+
"require": {
6+
"node": "./something.js",
7+
"default": "./something.js"
8+
},
9+
"import": {
10+
"node":"./something.mjs",
11+
"default": "./something.js"
12+
}
13+
},
14+
"#main-entry-point-string" : "./something.js",
15+
"#main-entry-point-external" : "some-external-cjs-module"
16+
}
17+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = { ...require('#main-entry-point-external') }

test/fixtures/specifier-string.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = { ...require('#main-entry-point-string') }

test/fixtures/specifier.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = { ...require('#main-entry-point') }
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { foo } from '../fixtures/specifier-external.js'
2+
import Hook from '../../index.js'
3+
import { strictEqual } from 'assert'
4+
5+
Hook((exports, name) => {
6+
if (name.endsWith('fixtures/specifier-external.js')) {
7+
exports.foo = 'bar2'
8+
}
9+
})
10+
11+
strictEqual(foo, 'bar2')

test/hook/specifier-imports.mjs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { foo } from '../fixtures/specifier.js'
2+
import Hook from '../../index.js'
3+
import { strictEqual } from 'assert'
4+
5+
Hook((exports, name) => {
6+
if (name.endsWith('fixtures/specifier.js')) {
7+
exports.foo = 1
8+
}
9+
})
10+
11+
strictEqual(foo, 1)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { foo } from '../fixtures/specifier-string.js'
2+
import Hook from '../../index.js'
3+
import { strictEqual } from 'assert'
4+
5+
Hook((exports, name) => {
6+
if (name.endsWith('fixtures/specifier-string.js')) {
7+
exports.foo = 1
8+
}
9+
})
10+
11+
strictEqual(foo, 1)

0 commit comments

Comments
 (0)