import-in-the-middle 1.2.1 → 1.3.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.
package/hook.mjs CHANGED
@@ -4,36 +4,74 @@
4
4
 
5
5
  const specifiers = new Map()
6
6
 
7
- const EXTENSION_RE = /\.(js|mjs|cjs)$/
7
+ // FIXME: Typescript extensions are added temporarily until we find a better
8
+ // way of supporting arbitrary extensions
9
+ const EXTENSION_RE = /\.(js|mjs|cjs|ts|mts|cts)$/
10
+
11
+ const NODE_MAJOR = Number(process.versions.node.split('.')[0])
8
12
 
9
13
  let entrypoint
10
14
 
15
+ function hasIitm (url) {
16
+ try {
17
+ return new URL(url).searchParams.has('iitm')
18
+ } catch {
19
+ return false
20
+ }
21
+ }
22
+
23
+ function deleteIitm (url) {
24
+ let resultUrl
25
+ try {
26
+ const urlObj = new URL(url)
27
+ if (urlObj.searchParams.has('iitm')) {
28
+ urlObj.searchParams.delete('iitm')
29
+ resultUrl = urlObj.href
30
+ if (resultUrl.startsWith('file:node:')) {
31
+ resultUrl = resultUrl.replace('file:', '')
32
+ }
33
+ if (resultUrl.startsWith('file:///node:')) {
34
+ resultUrl = resultUrl.replace('file:///', '')
35
+ }
36
+ } else {
37
+ resultUrl = urlObj.href
38
+ }
39
+ } catch {
40
+ resultUrl = url
41
+ }
42
+ return resultUrl
43
+ }
44
+
45
+ function addIitm (url) {
46
+ const urlObj = new URL(url)
47
+ urlObj.searchParams.set('iitm', 'true')
48
+ return urlObj.protocol !== 'file:' && NODE_MAJOR < 18 ? 'file:' + urlObj.href : urlObj.href
49
+ }
50
+
11
51
  export async function resolve (specifier, context, parentResolve) {
12
52
  const { parentURL = '' } = context
13
53
 
14
- if (specifier.startsWith('file:iitm:')) {
15
- specifier = specifier.replace('file:iitm:', '')
16
- }
17
- const url = await parentResolve(specifier, context, parentResolve)
54
+ const url = await parentResolve(deleteIitm(specifier), context, parentResolve)
18
55
 
19
56
  if (parentURL === '' && !EXTENSION_RE.test(url.url)) {
20
57
  entrypoint = url.url
21
58
  return { url: url.url, format: 'commonjs' }
22
59
  }
23
60
 
24
- if (parentURL === import.meta.url || parentURL.startsWith('file:iitm:')) {
61
+ if (parentURL === import.meta.url || hasIitm(parentURL)) {
25
62
  return url
26
63
  }
27
64
 
28
65
  specifiers.set(url.url, specifier)
29
66
 
30
67
  return {
31
- url: `file:iitm:${url.url}`
68
+ url: addIitm(url.url),
69
+ shortCircuit: true
32
70
  }
33
71
  }
34
72
 
35
73
  export function getFormat (url, context, parentGetFormat) {
36
- if (url.startsWith('file:iitm:')) {
74
+ if (hasIitm(url)) {
37
75
  return {
38
76
  format: 'module'
39
77
  }
@@ -49,8 +87,8 @@ export function getFormat (url, context, parentGetFormat) {
49
87
 
50
88
  const iitmURL = new URL('lib/register.js', import.meta.url).toString()
51
89
  export async function getSource (url, context, parentGetSource) {
52
- if (url.startsWith('file:iitm:')) {
53
- const realUrl = url.replace('file:iitm:', '')
90
+ if (hasIitm(url)) {
91
+ const realUrl = deleteIitm(url)
54
92
  const realModule = await import(realUrl)
55
93
  const exportNames = Object.keys(realModule)
56
94
  return {
@@ -76,10 +114,11 @@ register('${realUrl}', namespace, set, '${specifiers.get(realUrl)}')
76
114
 
77
115
  // For Node.js 16.12.0 and higher.
78
116
  export async function load (url, context, parentLoad) {
79
- if (url.startsWith('file:iitm:')) {
117
+ if (hasIitm(url)) {
80
118
  const { source } = await getSource(url, context)
81
119
  return {
82
120
  source,
121
+ shortCircuit: true,
83
122
  format: 'module'
84
123
  }
85
124
  }
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "import-in-the-middle",
3
- "version": "1.2.1",
3
+ "version": "1.3.0",
4
4
  "description": "Intercept imports in Node.js",
5
5
  "main": "index.js",
6
6
  "scripts": {
7
7
  "test": "c8 --check-coverage --lines 90 imhotap --runner test/runtest --files test/{hook,low-level,other}/*",
8
+ "test:ts": "c8 imhotap --runner test/runtest --files test/typescript/*.test.mts",
8
9
  "coverage": "c8 --reporter html imhotap --runner test/runtest --files test/{hook,low-level,other}/* && echo '\nNow open coverage/index.html\n'"
9
10
  },
10
11
  "repository": {
@@ -26,8 +27,11 @@
26
27
  },
27
28
  "homepage": "https://github.com/DataDog/import-in-the-middle#readme",
28
29
  "devDependencies": {
30
+ "@types/node": "^18.0.6",
29
31
  "c8": "^7.8.0",
30
- "imhotap": "^2.0.0"
32
+ "imhotap": "^2.0.0",
33
+ "ts-node": "^10.9.1",
34
+ "typescript": "^4.7.4"
31
35
  },
32
36
  "dependencies": {
33
37
  "module-details-from-path": "^1.0.3"
@@ -0,0 +1,3 @@
1
+ export const sayHi = (name: string) => {
2
+ return `Hi ${name}`
3
+ };
package/test/runtest CHANGED
@@ -15,7 +15,12 @@ const args = [
15
15
  ...process.argv.slice(2)
16
16
  ]
17
17
  if (!filename.includes('disabled')) {
18
- args.unshift(`--experimental-loader=${path.join(__dirname, '..', 'hook.mjs')}`)
18
+ const isTypescript = path.extname(filename).slice(-2) === 'ts'
19
+ const loaderPath = isTypescript
20
+ ? path.join(__dirname, 'typescript', 'iitm-ts-node-loader.mjs')
21
+ : path.join(__dirname, '..', 'hook.mjs')
22
+
23
+ args.unshift(`--experimental-loader=${loaderPath}`)
19
24
  }
20
25
 
21
26
  spawn('node', args, { stdio: 'inherit' }).on('close', code => {
@@ -0,0 +1,18 @@
1
+ import * as iitm from '../../hook.mjs'
2
+ import * as tsNode from 'ts-node/esm.mjs'
3
+
4
+ const makeNext = (loader, fnName, parentResolveOrLoad) => {
5
+ return (specifierOrUrl, context) => {
6
+ return loader[fnName](specifierOrUrl, context, parentResolveOrLoad)
7
+ }
8
+ }
9
+
10
+ export async function resolve(specifier, context, defaultResolve) {
11
+ const next = makeNext(tsNode, 'resolve', defaultResolve)
12
+ return iitm.resolve(specifier, context, next)
13
+ }
14
+
15
+ export async function load(url, context, defaultLoad) {
16
+ let next = makeNext(tsNode, 'load', defaultLoad)
17
+ return iitm.load(url, context, next)
18
+ }
@@ -0,0 +1,11 @@
1
+ import assert from 'node:assert/strict'
2
+ import { addHook } from '../../index.js'
3
+ import { sayHi } from '../fixtures/say-hi.mjs'
4
+
5
+ addHook((url, exported) => {
6
+ if (url.toLowerCase().endsWith('say-hi.mts')) {
7
+ exported.sayHi = () => 'Hooked'
8
+ }
9
+ })
10
+
11
+ assert.equal(sayHi('test'), 'Hooked')
package/tsconfig.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "esnext",
4
+ "module": "esnext",
5
+ "moduleResolution": "node",
6
+ "experimentalDecorators": true,
7
+ "emitDecoratorMetadata": true,
8
+ "allowSyntheticDefaultImports": true,
9
+ "noEmit": true
10
+ },
11
+ "ts-node": {
12
+ "esm": true
13
+ }
14
+ }