import-in-the-middle 1.7.1 → 1.7.2
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.
- package/hook.js +83 -11
- package/lib/get-esm-exports.js +1 -1
- package/package.json +3 -3
- package/test/fixtures/a.mjs +7 -0
- package/test/fixtures/b.mjs +5 -0
- package/test/fixtures/bundle.mjs +4 -0
- package/test/fixtures/esm-exports.txt +1 -1
- package/test/fixtures/foo.mjs +5 -0
- package/test/fixtures/lib/baz.mjs +3 -0
- package/test/hook/static-import-star.mjs +32 -0
package/hook.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
//
|
|
3
3
|
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc.
|
|
4
4
|
|
|
5
|
+
const { randomBytes } = require('crypto')
|
|
5
6
|
const specifiers = new Map()
|
|
6
7
|
const isWin = process.platform === "win32"
|
|
7
8
|
|
|
@@ -77,6 +78,76 @@ function needsToAddFileProtocol(urlObj) {
|
|
|
77
78
|
return !isFileProtocol(urlObj) && NODE_MAJOR < 18
|
|
78
79
|
}
|
|
79
80
|
|
|
81
|
+
/**
|
|
82
|
+
* Determines if a specifier represents an export all ESM line.
|
|
83
|
+
* Note that the expected `line` isn't 100% valid ESM. It is derived
|
|
84
|
+
* from the `getExports` function wherein we have recognized the true
|
|
85
|
+
* line and re-mapped it to one we expect.
|
|
86
|
+
*
|
|
87
|
+
* @param {string} line
|
|
88
|
+
* @returns {boolean}
|
|
89
|
+
*/
|
|
90
|
+
function isStarExportLine(line) {
|
|
91
|
+
return /^\* from /.test(line)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* @typedef {object} ProcessedModule
|
|
96
|
+
* @property {string[]} imports A set of ESM import lines to be added to the
|
|
97
|
+
* shimmed module source.
|
|
98
|
+
* @property {string[]} namespaces A set of identifiers representing the
|
|
99
|
+
* modules in `imports`, e.g. for `import * as foo from 'bar'`, "foo" will be
|
|
100
|
+
* present in this array.
|
|
101
|
+
* @property {string[]} setters The shimmed setters for all the exports
|
|
102
|
+
* from the module and any transitive export all modules.
|
|
103
|
+
*/
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Processes a module's exports and builds a set of new import statements,
|
|
107
|
+
* namespace names, and setter blocks. If an export all export if encountered,
|
|
108
|
+
* the target exports will be hoisted to the current module via a generated
|
|
109
|
+
* namespace.
|
|
110
|
+
*
|
|
111
|
+
* @param {object} params
|
|
112
|
+
* @param {string} params.srcUrl The full URL to the module to process.
|
|
113
|
+
* @param {object} params.context Provided by the loaders API.
|
|
114
|
+
* @param {function} parentGetSource Provides the source code for the parent
|
|
115
|
+
* module.
|
|
116
|
+
* @returns {Promise<ProcessedModule>}
|
|
117
|
+
*/
|
|
118
|
+
async function processModule({ srcUrl, context, parentGetSource }) {
|
|
119
|
+
const exportNames = await getExports(srcUrl, context, parentGetSource)
|
|
120
|
+
const imports = [`import * as namespace from ${JSON.stringify(srcUrl)}`]
|
|
121
|
+
const namespaces = ['namespace']
|
|
122
|
+
const setters = []
|
|
123
|
+
|
|
124
|
+
for (const n of exportNames) {
|
|
125
|
+
if (isStarExportLine(n) === true) {
|
|
126
|
+
const [_, modFile] = n.split('* from ')
|
|
127
|
+
const modUrl = new URL(modFile, srcUrl).toString()
|
|
128
|
+
const modName = Buffer.from(modFile, 'hex') + Date.now() + randomBytes(4).toString('hex')
|
|
129
|
+
|
|
130
|
+
imports.push(`import * as $${modName} from ${JSON.stringify(modUrl)}`)
|
|
131
|
+
namespaces.push(`$${modName}`)
|
|
132
|
+
|
|
133
|
+
const data = await processModule({ srcUrl: modUrl, context, parentGetSource })
|
|
134
|
+
Array.prototype.push.apply(setters, data.setters)
|
|
135
|
+
|
|
136
|
+
continue
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
setters.push(`
|
|
140
|
+
let $${n} = _.${n}
|
|
141
|
+
export { $${n} as ${n} }
|
|
142
|
+
set.${n} = (v) => {
|
|
143
|
+
$${n} = v
|
|
144
|
+
return true
|
|
145
|
+
}
|
|
146
|
+
`)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return { imports, namespaces, setters }
|
|
150
|
+
}
|
|
80
151
|
|
|
81
152
|
function addIitm (url) {
|
|
82
153
|
const urlObj = new URL(url)
|
|
@@ -123,21 +194,22 @@ function createHook (meta) {
|
|
|
123
194
|
async function getSource (url, context, parentGetSource) {
|
|
124
195
|
if (hasIitm(url)) {
|
|
125
196
|
const realUrl = deleteIitm(url)
|
|
126
|
-
const
|
|
197
|
+
const { imports, namespaces, setters } = await processModule({
|
|
198
|
+
srcUrl: realUrl,
|
|
199
|
+
context,
|
|
200
|
+
parentGetSource
|
|
201
|
+
})
|
|
202
|
+
|
|
127
203
|
return {
|
|
128
204
|
source: `
|
|
129
205
|
import { register } from '${iitmURL}'
|
|
130
|
-
|
|
206
|
+
${imports.join('\n')}
|
|
207
|
+
|
|
208
|
+
const _ = Object.assign({}, ...[${namespaces.join(', ')}])
|
|
131
209
|
const set = {}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
set.${n} = (v) => {
|
|
136
|
-
$${n} = v
|
|
137
|
-
return true
|
|
138
|
-
}
|
|
139
|
-
`).join('\n')}
|
|
140
|
-
register(${JSON.stringify(realUrl)}, namespace, set, ${JSON.stringify(specifiers.get(realUrl))})
|
|
210
|
+
|
|
211
|
+
${setters.join('\n')}
|
|
212
|
+
register(${JSON.stringify(realUrl)}, _, set, ${JSON.stringify(specifiers.get(realUrl))})
|
|
141
213
|
`
|
|
142
214
|
}
|
|
143
215
|
}
|
package/lib/get-esm-exports.js
CHANGED
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "import-in-the-middle",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.2",
|
|
4
4
|
"description": "Intercept imports in Node.js",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"test": "c8 --check-coverage --lines
|
|
8
|
-
"test:ts": "c8 imhotap --runner 'node test/runtest' --files test/typescript/*.test.mts",
|
|
7
|
+
"test": "c8 --reporter lcov --check-coverage --lines 70 imhotap --runner 'node test/runtest' --files test/{hook,low-level,other,get-esm-exports}/*",
|
|
8
|
+
"test:ts": "c8 --reporter lcov imhotap --runner 'node test/runtest' --files test/typescript/*.test.mts",
|
|
9
9
|
"coverage": "c8 --reporter html imhotap --runner 'node test/runtest' --files test/{hook,low-level,other,get-esm-exports}/* && echo '\nNow open coverage/index.html\n'"
|
|
10
10
|
},
|
|
11
11
|
"repository": {
|
|
@@ -23,7 +23,7 @@ export default class { /* … */ } //| default
|
|
|
23
23
|
export default function* () { /* … */ } //| default
|
|
24
24
|
|
|
25
25
|
// Aggregating modules
|
|
26
|
-
export * from "module-name"; //| *
|
|
26
|
+
export * from "module-name"; //| * from module-name
|
|
27
27
|
export * as name1 from "module-name"; //| name1
|
|
28
28
|
export { name1, /* …, */ nameN } from "module-name"; //| name1,nameN
|
|
29
29
|
export { import1 as name1, import2 as name2, /* …, */ nameN } from "module-name"; //| name1,name2,nameN
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { strictEqual } from 'assert'
|
|
2
|
+
import Hook from '../../index.js'
|
|
3
|
+
Hook((exports, name) => {
|
|
4
|
+
if (/bundle\.mjs/.test(name) === false) return
|
|
5
|
+
|
|
6
|
+
const bar = exports.default
|
|
7
|
+
exports.default = function wrappedBar() {
|
|
8
|
+
return bar() + '-wrapped'
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const foo = exports.foo
|
|
12
|
+
exports.foo = function wrappedFoo() {
|
|
13
|
+
return foo() + '-wrapped'
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const aFunc = exports.aFunc
|
|
17
|
+
exports.aFunc = function wrappedAFunc() {
|
|
18
|
+
return aFunc() + '-wrapped'
|
|
19
|
+
}
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
import {
|
|
23
|
+
default as bar,
|
|
24
|
+
foo,
|
|
25
|
+
aFunc,
|
|
26
|
+
baz
|
|
27
|
+
} from '../fixtures/bundle.mjs'
|
|
28
|
+
|
|
29
|
+
strictEqual(bar(), '42-wrapped')
|
|
30
|
+
strictEqual(foo(), 'foo-wrapped')
|
|
31
|
+
strictEqual(aFunc(), 'a-wrapped')
|
|
32
|
+
strictEqual(baz(), 'baz')
|