import-in-the-middle 1.14.4 → 1.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "1.14.4"
2
+ ".": "1.15.0"
3
3
  }
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.15.0](https://github.com/nodejs/import-in-the-middle/compare/import-in-the-middle-v1.14.4...import-in-the-middle-v1.15.0) (2025-10-09)
4
+
5
+
6
+ ### Features
7
+
8
+ * Compatibility with specifier imports ([#211](https://github.com/nodejs/import-in-the-middle/issues/211)) ([83d662a](https://github.com/nodejs/import-in-the-middle/commit/83d662a8e1f9a7b8632bc78f7499ccc0ab4d12c2))
9
+
3
10
  ## [1.14.4](https://github.com/nodejs/import-in-the-middle/compare/import-in-the-middle-v1.14.3...import-in-the-middle-v1.14.4) (2025-09-25)
4
11
 
5
12
 
@@ -2,10 +2,10 @@
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,62 @@ 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 {string} specifier The specifier that is being search for inside the imports object
34
+ * @param {URL|string} fromUrl The url from which the search starts from
35
+ * @returns array with url and resolvedExport
36
+ */
37
+ function resolvePackageImports (specifier, fromUrl) {
38
+ try {
39
+ const fromPath = fileURLToPath(fromUrl)
40
+ let currentDir = dirname(fromPath)
41
+
42
+ // search for package.json file which has the real url to export
43
+ while (currentDir !== dirname(currentDir)) {
44
+ const packageJsonPath = join(currentDir, 'package.json')
45
+
46
+ if (existsSync(packageJsonPath)) {
47
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'))
48
+ if (packageJson.imports && packageJson.imports[specifier]) {
49
+ const imports = packageJson.imports[specifier]
50
+
51
+ // Look for path inside packageJson
52
+ let resolvedExport
53
+ if (imports && typeof imports === 'object') {
54
+ const requireExport = imports.require
55
+ const importExport = imports.import
56
+ // look for the possibility of require and import which is standard for CJS/ESM
57
+ if (requireExport || importExport) {
58
+ // trying to resolve based on order of importance
59
+ resolvedExport = requireExport.node || requireExport.default || importExport.node || importExport.default
60
+ } else if (imports.node || imports.default) {
61
+ resolvedExport = imports.node || imports.default
62
+ }
63
+ } else if (typeof imports === 'string') {
64
+ resolvedExport = imports
65
+ }
66
+
67
+ if (resolvedExport) {
68
+ const url = resolvedExport.startsWith('.')
69
+ ? pathToFileURL(join(currentDir, resolvedExport))
70
+ : fromUrl
71
+ return [url, resolvedExport]
72
+ }
73
+ }
74
+ // return if we find a package.json but did not find an import
75
+ return null
76
+ }
77
+
78
+ currentDir = dirname(currentDir)
79
+ }
80
+ } catch (cause) {
81
+ throw Error(`Failed to find export: ${specifier}`, { cause })
82
+ }
83
+ return null
84
+ }
85
+
30
86
  async function getCjsExports (url, context, parentLoad, source) {
31
87
  if (urlsBeingProcessed.has(url)) {
32
88
  return []
@@ -46,7 +102,14 @@ async function getCjsExports (url, context, parentLoad, source) {
46
102
  if (re === '.') {
47
103
  re = './'
48
104
  }
49
- // Resolve the re-exported module relative to the current module.
105
+
106
+ // Entries in the import field should always start with #
107
+ if (re.startsWith('#')) {
108
+ const resolved = resolvePackageImports(re, url)
109
+ if (!resolved) return
110
+ [url, re] = resolved
111
+ }
112
+
50
113
  const newUrl = pathToFileURL(require.resolve(re, { paths: [dirname(fileURLToPath(url))] })).href
51
114
 
52
115
  if (newUrl.endsWith('.node') || newUrl.endsWith('.json')) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "import-in-the-middle",
3
- "version": "1.14.4",
3
+ "version": "1.15.0",
4
4
  "description": "Intercept imports in Node.js",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -0,0 +1 @@
1
+ module.exports = { ...require('#main-entry-point') }
@@ -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.mjs"
12
+ }
13
+ },
14
+ "#main-entry-point-string" : "./something.js",
15
+ "#main-entry-point-external" : "some-external-cjs-module"
16
+ }
17
+ }
@@ -0,0 +1 @@
1
+ module.exports = { ...require('#main-entry-point-external') }
@@ -0,0 +1 @@
1
+ module.exports = { ...require('#main-entry-point-string') }
@@ -0,0 +1 @@
1
+ export * from '#main-entry-point'
@@ -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')
@@ -0,0 +1,10 @@
1
+ import { foo } from '../fixtures/specifier.mjs'
2
+ import Hook from '../../index.js'
3
+ import { strictEqual } from 'assert'
4
+ Hook((exports, name) => {
5
+ if (name.endsWith('fixtures/specifier.mjs')) {
6
+ exports.foo = 1
7
+ }
8
+ })
9
+
10
+ strictEqual(foo, 1)
@@ -0,0 +1,11 @@
1
+ import { foo } from '../fixtures/nested-folder/specifier.js'
2
+ import Hook from '../../index.js'
3
+ import { strictEqual } from 'assert'
4
+
5
+ Hook((exports, name) => {
6
+ if (name.endsWith('fixtures/nested-folder/specifier.js')) {
7
+ exports.foo = 1
8
+ }
9
+ })
10
+
11
+ strictEqual(foo, 1)
@@ -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)