import-in-the-middle 1.10.0 → 1.11.1
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/.eslintrc.yaml +3 -0
- package/.release-please-manifest.json +1 -1
- package/CHANGELOG.md +19 -0
- package/README.md +44 -6
- package/hook.js +45 -3
- package/index.d.ts +36 -0
- package/index.js +75 -0
- package/lib/register.js +10 -1
- package/package.json +1 -1
- package/test/fixtures/import-after.mjs +16 -0
- package/test/fixtures/import.mjs +23 -0
- package/test/hook/v14-double-hook.mjs +24 -0
- package/test/register/v18.19-include-builtin.mjs +20 -0
- package/test/register/v18.19-include-message-port.mjs +14 -0
package/.eslintrc.yaml
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.11.1](https://github.com/nodejs/import-in-the-middle/compare/import-in-the-middle-v1.11.0...import-in-the-middle-v1.11.1) (2024-09-26)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* Support Hooking multiple times ([#153](https://github.com/nodejs/import-in-the-middle/issues/153)) ([e0d8080](https://github.com/nodejs/import-in-the-middle/commit/e0d808041eff228f4b4519454f7eea8f0930238a))
|
|
9
|
+
|
|
10
|
+
## [1.11.0](https://github.com/nodejs/import-in-the-middle/compare/import-in-the-middle-v1.10.0...import-in-the-middle-v1.11.0) (2024-07-29)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Features
|
|
14
|
+
|
|
15
|
+
* Optionally only wrap modules hooked in `--import` ([#146](https://github.com/nodejs/import-in-the-middle/issues/146)) ([71c8d7b](https://github.com/nodejs/import-in-the-middle/commit/71c8d7bac512df94566d12c96fc2e438b4de2e2a))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Bug Fixes
|
|
19
|
+
|
|
20
|
+
* `node:` prefixed build-in modules with `include`/`exclude` ([#149](https://github.com/nodejs/import-in-the-middle/issues/149)) ([736a944](https://github.com/nodejs/import-in-the-middle/commit/736a9446e209bc8649801a27cb431df663551dc5))
|
|
21
|
+
|
|
3
22
|
## [1.10.0](https://github.com/nodejs/import-in-the-middle/compare/import-in-the-middle-v1.9.1...import-in-the-middle-v1.10.0) (2024-07-22)
|
|
4
23
|
|
|
5
24
|
|
package/README.md
CHANGED
|
@@ -34,14 +34,14 @@ console.log(foo) // 1 more than whatever that module exported
|
|
|
34
34
|
This requires the use of an ESM loader hook, which can be added with the following
|
|
35
35
|
command-line option.
|
|
36
36
|
|
|
37
|
-
```
|
|
38
|
-
--loader=import-in-the-middle/hook.mjs
|
|
37
|
+
```shell
|
|
38
|
+
node --loader=import-in-the-middle/hook.mjs my-app.mjs
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
Since `--loader` has been deprecated you can also register the loader hook programmatically via the Node
|
|
42
42
|
[`module.register()`](https://nodejs.org/api/module.html#moduleregisterspecifier-parenturl-options)
|
|
43
43
|
API. However, for this to be able to hook non-dynamic imports, it needs to be
|
|
44
|
-
|
|
44
|
+
registered before your app code is evaluated via the `--import` command-line option.
|
|
45
45
|
|
|
46
46
|
`my-loader.mjs`
|
|
47
47
|
```js
|
|
@@ -54,9 +54,12 @@ node --import=./my-loader.mjs ./my-code.mjs
|
|
|
54
54
|
```
|
|
55
55
|
|
|
56
56
|
When registering the loader hook programmatically, it's possible to pass a list
|
|
57
|
-
of modules, file URLs or regular expressions to either exclude or specifically
|
|
58
|
-
include which modules are intercepted. This is useful if a module is not
|
|
57
|
+
of modules, file URLs or regular expressions to either `exclude` or specifically
|
|
58
|
+
`include` which modules are intercepted. This is useful if a module is not
|
|
59
59
|
compatible with the loader hook.
|
|
60
|
+
|
|
61
|
+
> **Note:** This feature is incompatible with the `{internals: true}` Hook option
|
|
62
|
+
|
|
60
63
|
```js
|
|
61
64
|
import * as module from 'module'
|
|
62
65
|
|
|
@@ -71,6 +74,41 @@ module.register('import-in-the-middle/hook.mjs', import.meta.url, {
|
|
|
71
74
|
})
|
|
72
75
|
```
|
|
73
76
|
|
|
77
|
+
### Only Intercepting Hooked modules
|
|
78
|
+
> **Note:** This feature is experimental and is incompatible with the `{internals: true}` Hook option
|
|
79
|
+
|
|
80
|
+
If you are `Hook`'ing all modules before they are imported, for example in a
|
|
81
|
+
module loaded via the Node.js `--import` CLI argument, you can configure the
|
|
82
|
+
loader to intercept only modules that were specifically hooked.
|
|
83
|
+
|
|
84
|
+
`instrument.mjs`
|
|
85
|
+
```js
|
|
86
|
+
import { register } from 'module'
|
|
87
|
+
import { Hook, createAddHookMessageChannel } from 'import-in-the-middle'
|
|
88
|
+
|
|
89
|
+
const { registerOptions, waitForAllMessagesAcknowledged } = createAddHookMessageChannel()
|
|
90
|
+
|
|
91
|
+
register('import-in-the-middle/hook.mjs', import.meta.url, registerOptions)
|
|
92
|
+
|
|
93
|
+
Hook(['fs'], (exported, name, baseDir) => {
|
|
94
|
+
// Instrument the fs module
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
// Ensure that the loader has acknowledged all the modules
|
|
98
|
+
// before we allow execution to continue
|
|
99
|
+
await waitForAllMessagesAcknowledged()
|
|
100
|
+
```
|
|
101
|
+
`my-app.mjs`
|
|
102
|
+
```js
|
|
103
|
+
import * as fs from 'fs'
|
|
104
|
+
// fs will be instrumented!
|
|
105
|
+
fs.readFileSync('file.txt')
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
```shell
|
|
109
|
+
node --import=./instrument.mjs ./my-app.mjs
|
|
110
|
+
```
|
|
111
|
+
|
|
74
112
|
## Limitations
|
|
75
113
|
|
|
76
114
|
* You cannot add new exports to a module. You can only modify existing ones.
|
package/hook.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
const { URL } = require('url')
|
|
6
6
|
const { inspect } = require('util')
|
|
7
|
+
const { builtinModules } = require('module')
|
|
7
8
|
const specifiers = new Map()
|
|
8
9
|
const isWin = process.platform === 'win32'
|
|
9
10
|
|
|
@@ -116,6 +117,11 @@ function isBareSpecifier (specifier) {
|
|
|
116
117
|
}
|
|
117
118
|
}
|
|
118
119
|
|
|
120
|
+
/**
|
|
121
|
+
* Determines whether the input is a bare specifier, file URL or a regular expression.
|
|
122
|
+
*
|
|
123
|
+
* - node: prefixed URL strings are considered bare specifiers in this context.
|
|
124
|
+
*/
|
|
119
125
|
function isBareSpecifierFileUrlOrRegex (input) {
|
|
120
126
|
if (input instanceof RegExp) {
|
|
121
127
|
return true
|
|
@@ -131,13 +137,21 @@ function isBareSpecifierFileUrlOrRegex (input) {
|
|
|
131
137
|
try {
|
|
132
138
|
// eslint-disable-next-line no-new
|
|
133
139
|
const url = new URL(input)
|
|
134
|
-
|
|
140
|
+
// We consider node: URLs bare specifiers in this context
|
|
141
|
+
return url.protocol === 'file:' || url.protocol === 'node:'
|
|
135
142
|
} catch (err) {
|
|
136
143
|
// Anything that fails parsing is a bare specifier
|
|
137
144
|
return true
|
|
138
145
|
}
|
|
139
146
|
}
|
|
140
147
|
|
|
148
|
+
/**
|
|
149
|
+
* Ensure an array only contains bare specifiers, file URLs or regular expressions.
|
|
150
|
+
*
|
|
151
|
+
* - We consider node: prefixed URL string as bare specifiers in this context.
|
|
152
|
+
* - For node built-in modules, we add additional node: prefixed modules to the
|
|
153
|
+
* output array.
|
|
154
|
+
*/
|
|
141
155
|
function ensureArrayWithBareSpecifiersFileUrlsAndRegex (array, type) {
|
|
142
156
|
if (!Array.isArray(array)) {
|
|
143
157
|
return undefined
|
|
@@ -149,6 +163,14 @@ function ensureArrayWithBareSpecifiersFileUrlsAndRegex (array, type) {
|
|
|
149
163
|
throw new Error(`'${type}' option only supports bare specifiers, file URLs or regular expressions. Invalid entries: ${inspect(invalid)}`)
|
|
150
164
|
}
|
|
151
165
|
|
|
166
|
+
// Rather than evaluate whether we have a node: scoped built-in-module for
|
|
167
|
+
// every call to resolve, we just add them to include/exclude now.
|
|
168
|
+
for (const each of array) {
|
|
169
|
+
if (typeof each === 'string' && !each.startsWith('node:') && builtinModules.includes(each)) {
|
|
170
|
+
array.push(`node:${each}`)
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
152
174
|
return array
|
|
153
175
|
}
|
|
154
176
|
|
|
@@ -237,6 +259,7 @@ async function processModule ({ srcUrl, context, parentGetSource, parentResolve,
|
|
|
237
259
|
$${n} = v
|
|
238
260
|
return true
|
|
239
261
|
}
|
|
262
|
+
get.${n} = () => $${n}
|
|
240
263
|
`)
|
|
241
264
|
}
|
|
242
265
|
}
|
|
@@ -259,13 +282,31 @@ function createHook (meta) {
|
|
|
259
282
|
if (data) {
|
|
260
283
|
includeModules = ensureArrayWithBareSpecifiersFileUrlsAndRegex(data.include, 'include')
|
|
261
284
|
excludeModules = ensureArrayWithBareSpecifiersFileUrlsAndRegex(data.exclude, 'exclude')
|
|
285
|
+
|
|
286
|
+
if (data.addHookMessagePort) {
|
|
287
|
+
data.addHookMessagePort.on('message', (modules) => {
|
|
288
|
+
if (includeModules === undefined) {
|
|
289
|
+
includeModules = []
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
for (const each of modules) {
|
|
293
|
+
if (!each.startsWith('node:') && builtinModules.includes(each)) {
|
|
294
|
+
includeModules.push(`node:${each}`)
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
includeModules.push(each)
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
data.addHookMessagePort.postMessage('ack')
|
|
301
|
+
}).unref()
|
|
302
|
+
}
|
|
262
303
|
}
|
|
263
304
|
}
|
|
264
305
|
|
|
265
306
|
async function resolve (specifier, context, parentResolve) {
|
|
266
307
|
cachedResolve = parentResolve
|
|
267
308
|
|
|
268
|
-
// See github.com/nodejs/import-in-the-middle/pull/76.
|
|
309
|
+
// See https://github.com/nodejs/import-in-the-middle/pull/76.
|
|
269
310
|
if (specifier === iitmURL) {
|
|
270
311
|
return {
|
|
271
312
|
url: specifier,
|
|
@@ -362,10 +403,11 @@ const _ = Object.assign(
|
|
|
362
403
|
namespace
|
|
363
404
|
)
|
|
364
405
|
const set = {}
|
|
406
|
+
const get = {}
|
|
365
407
|
|
|
366
408
|
${Array.from(setters.values()).join('\n')}
|
|
367
409
|
|
|
368
|
-
register(${JSON.stringify(realUrl)}, _, set, ${JSON.stringify(specifiers.get(realUrl))})
|
|
410
|
+
register(${JSON.stringify(realUrl)}, _, set, get, ${JSON.stringify(specifiers.get(realUrl))})
|
|
369
411
|
`
|
|
370
412
|
}
|
|
371
413
|
} catch (cause) {
|
package/index.d.ts
CHANGED
|
@@ -84,3 +84,39 @@ export declare function addHook(hookFn: HookFunction): void
|
|
|
84
84
|
* @param {HookFunction} hookFn The function to be removed.
|
|
85
85
|
*/
|
|
86
86
|
export declare function removeHook(hookFn: HookFunction): void
|
|
87
|
+
|
|
88
|
+
type CreateAddHookMessageChannelReturn<Data> = {
|
|
89
|
+
addHookMessagePort: MessagePort,
|
|
90
|
+
waitForAllMessagesAcknowledged: Promise<void>
|
|
91
|
+
registerOptions: { data?: Data; transferList?: any[]; }
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* EXPERIMENTAL
|
|
96
|
+
* This feature is experimental and may change in minor versions.
|
|
97
|
+
* **NOTE** This feature is incompatible with the {internals: true} Hook option.
|
|
98
|
+
*
|
|
99
|
+
* Creates a message channel with a port that can be used to add hooks to the
|
|
100
|
+
* list of exclusively included modules.
|
|
101
|
+
*
|
|
102
|
+
* This can be used to only wrap modules that are Hook'ed, however modules need
|
|
103
|
+
* to be hooked before they are imported.
|
|
104
|
+
*
|
|
105
|
+
* ```ts
|
|
106
|
+
* import { register } from 'module'
|
|
107
|
+
* import { Hook, createAddHookMessageChannel } from 'import-in-the-middle'
|
|
108
|
+
*
|
|
109
|
+
* const { registerOptions, waitForAllMessagesAcknowledged } = createAddHookMessageChannel()
|
|
110
|
+
*
|
|
111
|
+
* register('import-in-the-middle/hook.mjs', import.meta.url, registerOptions)
|
|
112
|
+
*
|
|
113
|
+
* Hook(['fs'], (exported, name, baseDir) => {
|
|
114
|
+
* // Instrument the fs module
|
|
115
|
+
* })
|
|
116
|
+
*
|
|
117
|
+
* // Ensure that the loader has acknowledged all the modules
|
|
118
|
+
* // before we allow execution to continue
|
|
119
|
+
* await waitForAllMessagesAcknowledged()
|
|
120
|
+
* ```
|
|
121
|
+
*/
|
|
122
|
+
export declare function createAddHookMessageChannel<Data = any>(): CreateAddHookMessageChannelReturn<Data>;
|
package/index.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
const path = require('path')
|
|
6
6
|
const parse = require('module-details-from-path')
|
|
7
7
|
const { fileURLToPath } = require('url')
|
|
8
|
+
const { MessageChannel } = require('worker_threads')
|
|
8
9
|
|
|
9
10
|
const {
|
|
10
11
|
importHooks,
|
|
@@ -31,6 +32,75 @@ function callHookFn (hookFn, namespace, name, baseDir) {
|
|
|
31
32
|
}
|
|
32
33
|
}
|
|
33
34
|
|
|
35
|
+
let sendModulesToLoader
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* EXPERIMENTAL
|
|
39
|
+
* This feature is experimental and may change in minor versions.
|
|
40
|
+
* **NOTE** This feature is incompatible with the {internals: true} Hook option.
|
|
41
|
+
*
|
|
42
|
+
* Creates a message channel with a port that can be used to add hooks to the
|
|
43
|
+
* list of exclusively included modules.
|
|
44
|
+
*
|
|
45
|
+
* This can be used to only wrap modules that are Hook'ed, however modules need
|
|
46
|
+
* to be hooked before they are imported.
|
|
47
|
+
*
|
|
48
|
+
* ```ts
|
|
49
|
+
* import { register } from 'module'
|
|
50
|
+
* import { Hook, createAddHookMessageChannel } from 'import-in-the-middle'
|
|
51
|
+
*
|
|
52
|
+
* const { registerOptions, waitForAllMessagesAcknowledged } = createAddHookMessageChannel()
|
|
53
|
+
*
|
|
54
|
+
* register('import-in-the-middle/hook.mjs', import.meta.url, registerOptions)
|
|
55
|
+
*
|
|
56
|
+
* Hook(['fs'], (exported, name, baseDir) => {
|
|
57
|
+
* // Instrument the fs module
|
|
58
|
+
* })
|
|
59
|
+
*
|
|
60
|
+
* // Ensure that the loader has acknowledged all the modules
|
|
61
|
+
* // before we allow execution to continue
|
|
62
|
+
* await waitForAllMessagesAcknowledged()
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
function createAddHookMessageChannel () {
|
|
66
|
+
const { port1, port2 } = new MessageChannel()
|
|
67
|
+
let pendingAckCount = 0
|
|
68
|
+
let resolveFn
|
|
69
|
+
|
|
70
|
+
sendModulesToLoader = (modules) => {
|
|
71
|
+
pendingAckCount++
|
|
72
|
+
port1.postMessage(modules)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
port1.on('message', () => {
|
|
76
|
+
pendingAckCount--
|
|
77
|
+
|
|
78
|
+
if (resolveFn && pendingAckCount <= 0) {
|
|
79
|
+
resolveFn()
|
|
80
|
+
}
|
|
81
|
+
}).unref()
|
|
82
|
+
|
|
83
|
+
function waitForAllMessagesAcknowledged () {
|
|
84
|
+
// This timer is to prevent the process from exiting with code 13:
|
|
85
|
+
// 13: Unsettled Top-Level Await.
|
|
86
|
+
const timer = setInterval(() => { }, 1000)
|
|
87
|
+
const promise = new Promise((resolve) => {
|
|
88
|
+
resolveFn = resolve
|
|
89
|
+
}).then(() => { clearInterval(timer) })
|
|
90
|
+
|
|
91
|
+
if (pendingAckCount === 0) {
|
|
92
|
+
resolveFn()
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return promise
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const addHookMessagePort = port2
|
|
99
|
+
const registerOptions = { data: { addHookMessagePort, include: [] }, transferList: [addHookMessagePort] }
|
|
100
|
+
|
|
101
|
+
return { registerOptions, addHookMessagePort, waitForAllMessagesAcknowledged }
|
|
102
|
+
}
|
|
103
|
+
|
|
34
104
|
function Hook (modules, options, hookFn) {
|
|
35
105
|
if ((this instanceof Hook) === false) return new Hook(modules, options, hookFn)
|
|
36
106
|
if (typeof modules === 'function') {
|
|
@@ -43,6 +113,10 @@ function Hook (modules, options, hookFn) {
|
|
|
43
113
|
}
|
|
44
114
|
const internals = options ? options.internals === true : false
|
|
45
115
|
|
|
116
|
+
if (sendModulesToLoader && Array.isArray(modules)) {
|
|
117
|
+
sendModulesToLoader(modules)
|
|
118
|
+
}
|
|
119
|
+
|
|
46
120
|
this._iitmHook = (name, namespace) => {
|
|
47
121
|
const filename = name
|
|
48
122
|
const isBuiltin = name.startsWith('node:')
|
|
@@ -92,3 +166,4 @@ module.exports = Hook
|
|
|
92
166
|
module.exports.Hook = Hook
|
|
93
167
|
module.exports.addHook = addHook
|
|
94
168
|
module.exports.removeHook = removeHook
|
|
169
|
+
module.exports.createAddHookMessageChannel = createAddHookMessageChannel
|
package/lib/register.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
const importHooks = [] // TODO should this be a Set?
|
|
6
6
|
const setters = new WeakMap()
|
|
7
|
+
const getters = new WeakMap()
|
|
7
8
|
const specifiers = new Map()
|
|
8
9
|
const toHook = []
|
|
9
10
|
|
|
@@ -12,6 +13,13 @@ const proxyHandler = {
|
|
|
12
13
|
return setters.get(target)[name](value)
|
|
13
14
|
},
|
|
14
15
|
|
|
16
|
+
get (target, name) {
|
|
17
|
+
if (name === Symbol.toStringTag) {
|
|
18
|
+
return 'Module'
|
|
19
|
+
}
|
|
20
|
+
return getters.get(target)[name]()
|
|
21
|
+
},
|
|
22
|
+
|
|
15
23
|
defineProperty (target, property, descriptor) {
|
|
16
24
|
if ((!('value' in descriptor))) {
|
|
17
25
|
throw new Error('Getters/setters are not supported for exports property descriptors.')
|
|
@@ -21,9 +29,10 @@ const proxyHandler = {
|
|
|
21
29
|
}
|
|
22
30
|
}
|
|
23
31
|
|
|
24
|
-
function register (name, namespace, set, specifier) {
|
|
32
|
+
function register (name, namespace, set, get, specifier) {
|
|
25
33
|
specifiers.set(name, specifier)
|
|
26
34
|
setters.set(namespace, set)
|
|
35
|
+
getters.set(namespace, get)
|
|
27
36
|
const proxy = new Proxy(namespace, proxyHandler)
|
|
28
37
|
importHooks.forEach(hook => hook(name, proxy))
|
|
29
38
|
toHook.push([name, proxy])
|
package/package.json
CHANGED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { strictEqual } from 'assert'
|
|
2
|
+
import { sep } from 'path'
|
|
3
|
+
import * as os from 'node:os'
|
|
4
|
+
import { Hook } from '../../index.js'
|
|
5
|
+
|
|
6
|
+
const hooked = []
|
|
7
|
+
|
|
8
|
+
Hook((_, name) => {
|
|
9
|
+
hooked.push(name)
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
strictEqual(hooked.length, 2)
|
|
13
|
+
strictEqual(hooked[0], 'path')
|
|
14
|
+
strictEqual(hooked[1], 'os')
|
|
15
|
+
strictEqual(sep, '@')
|
|
16
|
+
strictEqual(os.arch(), 'new_crazy_arch')
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { register } from 'module'
|
|
2
|
+
import { Hook, createAddHookMessageChannel } from '../../index.js'
|
|
3
|
+
// We've imported path here to ensure that the hook is still applied later even
|
|
4
|
+
// if the library is used here.
|
|
5
|
+
import * as path from 'path'
|
|
6
|
+
|
|
7
|
+
const { registerOptions, waitForAllMessagesAcknowledged } = createAddHookMessageChannel()
|
|
8
|
+
|
|
9
|
+
register('../../hook.mjs', import.meta.url, registerOptions)
|
|
10
|
+
|
|
11
|
+
Hook(['path'], (exports) => {
|
|
12
|
+
exports.sep = '@'
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
Hook(['os'], (exports) => {
|
|
16
|
+
exports.arch = function () {
|
|
17
|
+
return 'new_crazy_arch'
|
|
18
|
+
}
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
console.assert(path.sep !== '@')
|
|
22
|
+
|
|
23
|
+
await waitForAllMessagesAcknowledged()
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { fileURLToPath } from 'url'
|
|
2
|
+
import { join } from 'path'
|
|
3
|
+
import { strictEqual } from 'assert'
|
|
4
|
+
import Hook from '../../index.js'
|
|
5
|
+
|
|
6
|
+
const toWrap = join(fileURLToPath(import.meta.url), '..', '..', 'fixtures', 'foo.mjs')
|
|
7
|
+
|
|
8
|
+
Hook([toWrap], (exports) => {
|
|
9
|
+
const original = exports.foo
|
|
10
|
+
exports.foo = function foo () {
|
|
11
|
+
return original() + '-first'
|
|
12
|
+
}
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
Hook([toWrap], (exports) => {
|
|
16
|
+
const original = exports.foo
|
|
17
|
+
exports.foo = function foo () {
|
|
18
|
+
return original() + '-second'
|
|
19
|
+
}
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
const { foo } = await import('../fixtures/foo.mjs')
|
|
23
|
+
|
|
24
|
+
strictEqual(foo(), 'foo-first-second')
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { register } from 'module'
|
|
2
|
+
import Hook from '../../index.js'
|
|
3
|
+
import { strictEqual } from 'assert'
|
|
4
|
+
|
|
5
|
+
register('../../hook.mjs', import.meta.url, { data: { include: ['node:util', 'os'] } })
|
|
6
|
+
|
|
7
|
+
const hooked = []
|
|
8
|
+
|
|
9
|
+
Hook((exports, name) => {
|
|
10
|
+
hooked.push(name)
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
await import('util')
|
|
14
|
+
await import('node:os')
|
|
15
|
+
await import('fs')
|
|
16
|
+
await import('path')
|
|
17
|
+
|
|
18
|
+
strictEqual(hooked.length, 2)
|
|
19
|
+
strictEqual(hooked[0], 'util')
|
|
20
|
+
strictEqual(hooked[1], 'os')
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { spawnSync } from 'child_process'
|
|
2
|
+
|
|
3
|
+
const out = spawnSync(process.execPath,
|
|
4
|
+
['--import', './test/fixtures/import.mjs', './test/fixtures/import-after.mjs'],
|
|
5
|
+
{ stdio: 'inherit', env: {} }
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
if (out.error) {
|
|
9
|
+
console.error(out.error)
|
|
10
|
+
}
|
|
11
|
+
if (out.status !== 0) {
|
|
12
|
+
console.error(`Expected exit code 0, got ${out.status}`)
|
|
13
|
+
}
|
|
14
|
+
process.exit(out.status)
|