forked from nodejs/import-in-the-middle
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhook.mjs
132 lines (113 loc) · 3.18 KB
/
hook.mjs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2.0 License.
//
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc.
const specifiers = new Map()
// FIXME: Typescript extensions are added temporarily until we find a better
// way of supporting arbitrary extensions
const EXTENSION_RE = /\.(js|mjs|cjs|ts|mts|cts)$/
const NODE_MAJOR = Number(process.versions.node.split('.')[0])
let entrypoint
function hasIitm (url) {
try {
return new URL(url).searchParams.has('iitm')
} catch {
return false
}
}
function deleteIitm (url) {
let resultUrl
try {
const urlObj = new URL(url)
if (urlObj.searchParams.has('iitm')) {
urlObj.searchParams.delete('iitm')
resultUrl = urlObj.href
if (resultUrl.startsWith('file:node:')) {
resultUrl = resultUrl.replace('file:', '')
}
if (resultUrl.startsWith('file:///node:')) {
resultUrl = resultUrl.replace('file:///', '')
}
} else {
resultUrl = urlObj.href
}
} catch {
resultUrl = url
}
return resultUrl
}
function addIitm (url) {
const urlObj = new URL(url)
urlObj.searchParams.set('iitm', 'true')
if (NODE_MAJOR === 17) {
return urlObj.protocol !== 'file:' ? 'file:' + urlObj.href : urlObj.href
}
return urlObj.protocol !== 'file:' && urlObj.protocol !== 'node:' && NODE_MAJOR < 18
? 'file:' + urlObj.href
: urlObj.href
}
export async function resolve (specifier, context, parentResolve) {
const { parentURL = '' } = context
const url = await parentResolve(deleteIitm(specifier), context, parentResolve)
if (parentURL === '' && !EXTENSION_RE.test(url.url)) {
entrypoint = url.url
return { url: url.url, format: 'commonjs' }
}
if (parentURL === import.meta.url || hasIitm(parentURL)) {
return url
}
specifiers.set(url.url, specifier)
return {
url: addIitm(url.url),
shortCircuit: true
}
}
export function getFormat (url, context, parentGetFormat) {
if (hasIitm(url)) {
return {
format: 'module'
}
}
if (url === entrypoint) {
return {
format: 'commonjs'
}
}
return parentGetFormat(url, context, parentGetFormat)
}
const iitmURL = new URL('lib/register.js', import.meta.url).toString()
export async function getSource (url, context, parentGetSource) {
if (hasIitm(url)) {
const realUrl = deleteIitm(url)
const realModule = await import(realUrl)
const exportNames = Object.keys(realModule)
return {
source: `
import { register } from '${iitmURL}'
import * as namespace from '${url}'
const set = {}
${exportNames.map((n) => `
let $${n} = namespace.${n}
export { $${n} as ${n} }
set.${n} = (v) => {
$${n} = v
return true
}
`).join('\n')}
register('${realUrl}', namespace, set, '${specifiers.get(realUrl)}')
`
}
}
return parentGetSource(url, context, parentGetSource)
}
// For Node.js 16.12.0 and higher.
export async function load (url, context, parentLoad) {
if (hasIitm(url)) {
const { source } = await getSource(url, context)
return {
source,
shortCircuit: true,
format: 'module'
}
}
return parentLoad(url, context, parentLoad)
}