import-in-the-middle 1.6.0 → 1.7.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.js +71 -6
- package/lib/get-esm-exports.js +151 -52
- package/lib/get-exports.js +2 -2
- package/lib/get-pkg-json-type-module.js +46 -0
- package/lib/helpers.js +5 -0
- package/package.json +6 -4
- package/test/fixtures/a.mjs +7 -0
- package/test/fixtures/b.mjs +6 -0
- package/test/fixtures/env.mjs +8 -0
- package/test/fixtures/export-types/declarations.mjs +19 -0
- package/test/fixtures/export-types/default-class-anon.mjs +1 -0
- package/test/fixtures/export-types/default-class.mjs +1 -0
- package/test/fixtures/export-types/default-expression-array.mjs +1 -0
- package/test/fixtures/export-types/default-expression-num.mjs +1 -0
- package/test/fixtures/export-types/default-expression-string.mjs +1 -0
- package/test/fixtures/export-types/default-function-anon.mjs +1 -0
- package/test/fixtures/export-types/default-function.mjs +1 -0
- package/test/fixtures/export-types/default-generator-anon.mjs +1 -0
- package/test/fixtures/export-types/default-generator.mjs +1 -0
- package/test/fixtures/export-types/list.mjs +12 -0
- package/test/fixtures/reassign-let.mjs +5 -0
- package/test/fixtures/test-undefined-format/a.js +8 -0
- package/test/fixtures/test-undefined-format/b.js +7 -0
- package/test/fixtures/test-undefined-format/package.json +9 -0
- package/test/fixtures/use-env.mjs +5 -0
- package/test/get-esm-exports/v18.19-get-esm-exports.js +1 -1
- package/test/hook/default-exports.mjs +71 -0
- package/test/hook/v16-declaration-exports.mjs +84 -0
- package/test/hook/v16-list-exports.mjs +34 -0
- package/test/other/v16-cyclical-dependency.mjs +29 -0
- package/test/other/v16-reassign-let.mjs +29 -0
- package/test/other/v16-test-undefined-format.mjs +26 -0
- package/import-in-the-middle-1.6.0.tgz +0 -0
- /package/test/get-esm-exports/{v20-get-esm-exports.js → v16-get-esm-exports.js} +0 -0
package/hook.js
CHANGED
|
@@ -2,25 +2,41 @@
|
|
|
2
2
|
//
|
|
3
3
|
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc.
|
|
4
4
|
|
|
5
|
+
const fs = require('fs')
|
|
6
|
+
const { fileURLToPath } = require('url')
|
|
5
7
|
const specifiers = new Map()
|
|
6
8
|
const isWin = process.platform === "win32"
|
|
9
|
+
const warn = require('./lib/helpers')
|
|
7
10
|
|
|
8
11
|
// FIXME: Typescript extensions are added temporarily until we find a better
|
|
9
12
|
// way of supporting arbitrary extensions
|
|
10
13
|
const EXTENSION_RE = /\.(js|mjs|cjs|ts|mts|cts)$/
|
|
14
|
+
const EXTENSION_MJS_RE = /\.mjs$/
|
|
15
|
+
const EXTENSION_JS_RE = /\.js$/
|
|
11
16
|
const NODE_VERSION = process.versions.node.split('.')
|
|
12
17
|
const NODE_MAJOR = Number(NODE_VERSION[0])
|
|
13
18
|
const NODE_MINOR = Number(NODE_VERSION[1])
|
|
19
|
+
const FILE_NAME = 'hook.js'
|
|
14
20
|
|
|
15
21
|
let entrypoint
|
|
16
|
-
|
|
17
22
|
let getExports
|
|
23
|
+
let getEsmExports
|
|
24
|
+
let getPkgJsonTypeModule
|
|
25
|
+
|
|
18
26
|
if (NODE_MAJOR >= 20 || (NODE_MAJOR == 18 && NODE_MINOR >= 19)) {
|
|
19
27
|
getExports = require('./lib/get-exports.js')
|
|
20
28
|
} else {
|
|
21
29
|
getExports = (url) => import(url).then(Object.keys)
|
|
22
30
|
}
|
|
23
31
|
|
|
32
|
+
if (NODE_MAJOR >= 16) {
|
|
33
|
+
getEsmExports = require('./lib/get-esm-exports.js')
|
|
34
|
+
getPkgJsonTypeModule = require('./lib/get-pkg-json-type-module.js')
|
|
35
|
+
} else {
|
|
36
|
+
getEsmExports = undefined
|
|
37
|
+
getPkgJsonTypeModule = undefined
|
|
38
|
+
}
|
|
39
|
+
|
|
24
40
|
function hasIitm (url) {
|
|
25
41
|
try {
|
|
26
42
|
return new URL(url).searchParams.has('iitm')
|
|
@@ -108,12 +124,23 @@ function createHook (meta) {
|
|
|
108
124
|
) {
|
|
109
125
|
return url
|
|
110
126
|
}
|
|
111
|
-
|
|
127
|
+
|
|
128
|
+
// on Node's 16.0.0-16.12.0, url.format is undefined for the cyclical dependency test files ./test/fixtures/a.mjs & ./test/fixtures/b.mjs
|
|
129
|
+
// so explicitly set format to 'module' for files with a .mjs extension or cjs files that have type 'module in their package.json
|
|
130
|
+
// so that they can go through the ast parsing patch for Node >= 16
|
|
131
|
+
if (NODE_MAJOR === 16 && NODE_MINOR < 13) {
|
|
132
|
+
if (
|
|
133
|
+
(url.format === undefined && EXTENSION_MJS_RE.test(url.url)) ||
|
|
134
|
+
(EXTENSION_JS_RE.test(url.url) && getPkgJsonTypeModule(fileURLToPath(url.url)))
|
|
135
|
+
) {
|
|
136
|
+
url.format = 'module'
|
|
137
|
+
}
|
|
138
|
+
}
|
|
112
139
|
|
|
113
140
|
specifiers.set(url.url, specifier)
|
|
114
141
|
|
|
115
142
|
return {
|
|
116
|
-
url: addIitm(url.url),
|
|
143
|
+
url: url.format !== 'module' || NODE_MAJOR < 16 ? addIitm(url.url) : url.url,
|
|
117
144
|
shortCircuit: true,
|
|
118
145
|
format: url.format
|
|
119
146
|
}
|
|
@@ -122,9 +149,12 @@ function createHook (meta) {
|
|
|
122
149
|
const iitmURL = new URL('lib/register.js', meta.url).toString()
|
|
123
150
|
async function getSource (url, context, parentGetSource) {
|
|
124
151
|
if (hasIitm(url)) {
|
|
152
|
+
|
|
125
153
|
const realUrl = deleteIitm(url)
|
|
154
|
+
|
|
126
155
|
const exportNames = await getExports(realUrl, context, parentGetSource)
|
|
127
|
-
|
|
156
|
+
|
|
157
|
+
return {
|
|
128
158
|
source: `
|
|
129
159
|
import { register } from '${iitmURL}'
|
|
130
160
|
import * as namespace from ${JSON.stringify(url)}
|
|
@@ -139,6 +169,41 @@ set.${n} = (v) => {
|
|
|
139
169
|
`).join('\n')}
|
|
140
170
|
register(${JSON.stringify(realUrl)}, namespace, set, ${JSON.stringify(specifiers.get(realUrl))})
|
|
141
171
|
`
|
|
172
|
+
}
|
|
173
|
+
} else if (NODE_MAJOR >= 16 && context.format === 'module') {
|
|
174
|
+
let fileContents
|
|
175
|
+
const realPath = fileURLToPath(url)
|
|
176
|
+
try {
|
|
177
|
+
fileContents = fs.readFileSync(realPath, 'utf8')
|
|
178
|
+
} catch (parseError) {
|
|
179
|
+
warn(`Had trouble reading file: ${fileContents}, got error: ${parseError}`, FILE_NAME)
|
|
180
|
+
return parentGetSource(url, context, parentGetSource)
|
|
181
|
+
}
|
|
182
|
+
try {
|
|
183
|
+
const outPut = getEsmExports(fileContents, true, url)
|
|
184
|
+
fileContents = outPut.code
|
|
185
|
+
exportAlias = outPut.exportAlias
|
|
186
|
+
} catch (parseError) {
|
|
187
|
+
warn(`Tried AST parsing ${realPath}, got error: ${parseError}`, FILE_NAME)
|
|
188
|
+
return parentGetSource(url, context, parentGetSource)
|
|
189
|
+
}
|
|
190
|
+
const src = `${fileContents}
|
|
191
|
+
import { register as DATADOG_REGISTER_FUNC } from '${iitmURL}'
|
|
192
|
+
{
|
|
193
|
+
const set = {}
|
|
194
|
+
const namespace = {}
|
|
195
|
+
${Object.entries(exportAlias).map(([key, value]) => `
|
|
196
|
+
set.${key} = (v) => {
|
|
197
|
+
${value} = v
|
|
198
|
+
return true
|
|
199
|
+
}
|
|
200
|
+
namespace.${key} = ${value}
|
|
201
|
+
`).join('\n')}
|
|
202
|
+
DATADOG_REGISTER_FUNC(${JSON.stringify(url)}, namespace, set, ${JSON.stringify(specifiers.get(url))})
|
|
203
|
+
}
|
|
204
|
+
`
|
|
205
|
+
return {
|
|
206
|
+
source: src
|
|
142
207
|
}
|
|
143
208
|
}
|
|
144
209
|
|
|
@@ -147,7 +212,7 @@ register(${JSON.stringify(realUrl)}, namespace, set, ${JSON.stringify(specifiers
|
|
|
147
212
|
|
|
148
213
|
// For Node.js 16.12.0 and higher.
|
|
149
214
|
async function load (url, context, parentLoad) {
|
|
150
|
-
if (hasIitm(url)) {
|
|
215
|
+
if (hasIitm(url) || context.format === 'module') {
|
|
151
216
|
const { source } = await getSource(url, context, parentLoad)
|
|
152
217
|
return {
|
|
153
218
|
source,
|
|
@@ -167,7 +232,7 @@ register(${JSON.stringify(realUrl)}, namespace, set, ${JSON.stringify(specifiers
|
|
|
167
232
|
resolve,
|
|
168
233
|
getSource,
|
|
169
234
|
getFormat (url, context, parentGetFormat) {
|
|
170
|
-
if (hasIitm(url)) {
|
|
235
|
+
if (hasIitm(url) || context.format === 'module') {
|
|
171
236
|
return {
|
|
172
237
|
format: 'module'
|
|
173
238
|
}
|
package/lib/get-esm-exports.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const recast = require('recast')
|
|
3
4
|
const { Parser } = require('acorn')
|
|
4
|
-
const { importAssertions } = require('acorn-import-assertions')
|
|
5
|
+
const { importAssertions } = require('acorn-import-assertions')
|
|
6
|
+
const warn = require('./helpers')
|
|
7
|
+
|
|
8
|
+
const TS_EXTENSION_RE = /\.(ts|mts|cts)$/
|
|
5
9
|
|
|
6
10
|
const acornOpts = {
|
|
7
11
|
ecmaVersion: 'latest',
|
|
@@ -9,87 +13,182 @@ const acornOpts = {
|
|
|
9
13
|
}
|
|
10
14
|
|
|
11
15
|
const parser = Parser.extend(importAssertions)
|
|
16
|
+
const FILE_NAME = 'get-esm-exports'
|
|
17
|
+
|
|
18
|
+
function getEsmExports(moduleStr, generate=false, url=undefined) {
|
|
19
|
+
const exportSpecifierNames = new Set()
|
|
20
|
+
const exportAlias = {}
|
|
21
|
+
let ast
|
|
22
|
+
|
|
23
|
+
// if it's a typescript file, we need to parse it with recasts typescript parser
|
|
24
|
+
if (url && TS_EXTENSION_RE.test(url)) {
|
|
25
|
+
ast = recast.parse(moduleStr, {parser: require("recast/parsers/typescript")})
|
|
26
|
+
} else {
|
|
27
|
+
ast = recast.parse(moduleStr, {parser: {
|
|
28
|
+
parse(source) {
|
|
29
|
+
return parser.parse(source, acornOpts)
|
|
30
|
+
}
|
|
31
|
+
}})
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const iitmRenamedExport = 'iitmRenamedExport';
|
|
35
|
+
|
|
36
|
+
// Loop through the top-level declarations of the AST
|
|
37
|
+
for (const statement of ast.program.body) {
|
|
38
|
+
if (statement.type === 'ExportNamedDeclaration') {
|
|
39
|
+
const node = statement;
|
|
40
|
+
|
|
41
|
+
if (node.declaration) {
|
|
42
|
+
parseDeclaration(node.declaration, exportAlias);
|
|
43
|
+
} else {
|
|
44
|
+
parseSpecifiers(node.specifiers, exportAlias, exportSpecifierNames);
|
|
45
|
+
}
|
|
46
|
+
} else if (statement.type === 'ExportDefaultDeclaration') {
|
|
47
|
+
const node = statement;
|
|
12
48
|
|
|
13
|
-
|
|
14
|
-
|
|
49
|
+
if (['ObjectExpression', 'ArrayExpression', 'Literal'].includes(node.declaration.type) && generate) {
|
|
50
|
+
const variableDeclaration = {
|
|
51
|
+
type: 'VariableDeclaration',
|
|
52
|
+
kind: 'let',
|
|
53
|
+
declarations: [
|
|
54
|
+
{
|
|
55
|
+
type: 'VariableDeclarator',
|
|
56
|
+
id: {
|
|
57
|
+
type: 'Identifier',
|
|
58
|
+
name: iitmRenamedExport,
|
|
59
|
+
},
|
|
60
|
+
init: node.declaration,
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// Replace the current ExportDefaultDeclaration with the new VariableDeclaration
|
|
66
|
+
ast.program.body.splice(ast.program.body.indexOf(statement), 1, variableDeclaration);
|
|
67
|
+
|
|
68
|
+
const newExportDefaultDeclaration = {
|
|
69
|
+
type: 'ExportDefaultDeclaration',
|
|
70
|
+
declaration: {
|
|
71
|
+
type: 'Identifier',
|
|
72
|
+
name: iitmRenamedExport,
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// Insert the new ExportDefaultDeclaration after the VariableDeclaration
|
|
77
|
+
ast.program.body.splice(ast.program.body.indexOf(variableDeclaration) + 1, 0, newExportDefaultDeclaration);
|
|
78
|
+
} else if (['FunctionDeclaration', 'Identifier', 'ClassDeclaration'].includes(node.declaration.type) && generate) {
|
|
79
|
+
node.declaration.id = { type: 'Identifier', name: iitmRenamedExport };
|
|
80
|
+
}
|
|
81
|
+
exportAlias['default'] = iitmRenamedExport;
|
|
82
|
+
} else if (statement.type === 'ExportAllDeclaration') {
|
|
83
|
+
const node = statement;
|
|
84
|
+
const exportedName = node.exported ? node.exported.name : '*';
|
|
85
|
+
|
|
86
|
+
exportAlias[exportedName] = exportedName;
|
|
87
|
+
} else if (statement.type === 'ExportSpecifier') {
|
|
88
|
+
const node = statement;
|
|
89
|
+
exportSpecifierNames.add(node.local.name);
|
|
90
|
+
|
|
91
|
+
if (node.exported.name) {
|
|
92
|
+
exportAlias[node.exported.name] = node.local.name;
|
|
93
|
+
} else if (node.exported.value) {
|
|
94
|
+
exportAlias[node.exported.value] = node.local.name;
|
|
95
|
+
} else {
|
|
96
|
+
warn('unrecognized specifier export: ' + node.exported, FILE_NAME);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (exportSpecifierNames.size !== 0 && generate) {
|
|
102
|
+
convertExportSpecifierToLet(exportSpecifierNames, ast);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (generate) {
|
|
106
|
+
return {
|
|
107
|
+
exportAlias: exportAlias,
|
|
108
|
+
code: recast.print(ast).code,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return Object.keys(exportAlias);
|
|
15
113
|
}
|
|
16
114
|
|
|
17
|
-
function
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
115
|
+
function convertExportSpecifierToLet(exportSpecifierNames, ast) {
|
|
116
|
+
for (const statement of ast.program.body) {
|
|
117
|
+
if (statement.type === 'VariableDeclaration') {
|
|
118
|
+
const declaration = statement;
|
|
119
|
+
|
|
120
|
+
if (declaration.kind === 'const') {
|
|
121
|
+
for (const declarator of declaration.declarations) {
|
|
122
|
+
const variableName = declarator.id.name;
|
|
123
|
+
if (exportSpecifierNames.has(variableName)) {
|
|
124
|
+
declaration.kind = 'let';
|
|
125
|
+
}
|
|
28
126
|
}
|
|
29
|
-
|
|
30
|
-
case 'ExportDefaultDeclaration':
|
|
31
|
-
exportedNames.add('default')
|
|
32
|
-
break
|
|
33
|
-
case 'ExportAllDeclaration':
|
|
34
|
-
if (node.exported) {
|
|
35
|
-
exportedNames.add(node.exported.name)
|
|
36
|
-
} else {
|
|
37
|
-
exportedNames.add('*')
|
|
38
|
-
}
|
|
39
|
-
break
|
|
40
|
-
default:
|
|
41
|
-
warn('unrecognized export type: ' + node.type)
|
|
127
|
+
}
|
|
42
128
|
}
|
|
43
129
|
}
|
|
44
|
-
return Array.from(exportedNames)
|
|
45
130
|
}
|
|
46
131
|
|
|
47
|
-
function parseDeclaration
|
|
48
|
-
switch (
|
|
132
|
+
function parseDeclaration(declaration, exportAlias) {
|
|
133
|
+
switch (declaration.type) {
|
|
49
134
|
case 'FunctionDeclaration':
|
|
50
|
-
|
|
135
|
+
case 'ClassDeclaration':
|
|
136
|
+
exportAlias[declaration.id.name] = declaration.id.name
|
|
51
137
|
break
|
|
52
138
|
case 'VariableDeclaration':
|
|
53
|
-
for (const varDecl of
|
|
54
|
-
|
|
139
|
+
for (const varDecl of declaration.declarations) {
|
|
140
|
+
if (declaration.kind === 'const') {
|
|
141
|
+
declaration.kind = 'let'
|
|
142
|
+
}
|
|
143
|
+
parseVariableDeclaration(varDecl, exportAlias)
|
|
55
144
|
}
|
|
56
145
|
break
|
|
57
|
-
case 'ClassDeclaration':
|
|
58
|
-
exportedNames.add(node.declaration.id.name)
|
|
59
|
-
break
|
|
60
146
|
default:
|
|
61
|
-
warn('unknown declaration type: ' +
|
|
147
|
+
warn('unknown declaration type: ' + declaration.type, FILE_NAME)
|
|
62
148
|
}
|
|
63
149
|
}
|
|
64
150
|
|
|
65
|
-
function parseVariableDeclaration
|
|
66
|
-
switch (
|
|
151
|
+
function parseVariableDeclaration(varDecl, exportAlias) {
|
|
152
|
+
switch (varDecl.id.type) {
|
|
67
153
|
case 'Identifier':
|
|
68
|
-
|
|
154
|
+
exportAlias[varDecl.id.name] = varDecl.id.name
|
|
69
155
|
break
|
|
70
156
|
case 'ObjectPattern':
|
|
71
|
-
for (const prop of
|
|
72
|
-
|
|
157
|
+
for (const prop of varDecl.id.properties) {
|
|
158
|
+
exportAlias[prop.value.name] = prop.value.name
|
|
73
159
|
}
|
|
74
160
|
break
|
|
75
161
|
case 'ArrayPattern':
|
|
76
|
-
for (const elem of
|
|
77
|
-
|
|
162
|
+
for (const elem of varDecl.id.elements) {
|
|
163
|
+
if (elem) {
|
|
164
|
+
exportAlias[elem.name] = elem.name
|
|
165
|
+
}
|
|
78
166
|
}
|
|
79
167
|
break
|
|
80
168
|
default:
|
|
81
|
-
warn('unknown variable declaration type: ' +
|
|
169
|
+
warn('unknown variable declaration type: ' + varDecl.id.type, FILE_NAME)
|
|
82
170
|
}
|
|
83
171
|
}
|
|
84
172
|
|
|
85
|
-
function parseSpecifiers
|
|
86
|
-
for (const specifier of
|
|
87
|
-
if (specifier.
|
|
88
|
-
|
|
173
|
+
function parseSpecifiers(specifiers, exportAlias, exportSpecifierNames) {
|
|
174
|
+
for (const specifier of specifiers) {
|
|
175
|
+
if (specifier.type === 'ExportSpecifier') {
|
|
176
|
+
exportSpecifierNames.add(specifier.local.name)
|
|
177
|
+
if (specifier.exported && specifier.exported.name) {
|
|
178
|
+
exportAlias[specifier.exported.name] = specifier.local.name
|
|
179
|
+
} else if (specifier.exported && specifier.exported.value) {
|
|
180
|
+
exportAlias[specifier.exported.value] = specifier.local.name
|
|
181
|
+
} else {
|
|
182
|
+
warn('unrecognized specifier export: ' + specifier, FILE_NAME)
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
else if (specifier.exported.type === 'Identifier') {
|
|
186
|
+
exportAlias[specifier.exported.name] = specifier.exported.name
|
|
89
187
|
} else if (specifier.exported.type === 'Literal') {
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
|
|
188
|
+
exportAlias[specifier.exported.value] = specifier.exported.value
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
warn('unrecognized specifier type: ' + specifier.exported.type, FILE_NAME)
|
|
93
192
|
}
|
|
94
193
|
}
|
|
95
194
|
}
|
package/lib/get-exports.js
CHANGED
|
@@ -30,7 +30,7 @@ async function getExports (url, context, parentLoad) {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
if (format === 'module') {
|
|
33
|
-
return getEsmExports(source)
|
|
33
|
+
return getEsmExports(source, false, url)
|
|
34
34
|
}
|
|
35
35
|
if (format === 'commonjs') {
|
|
36
36
|
return addDefault(getCjsExports(source).exports)
|
|
@@ -38,7 +38,7 @@ async function getExports (url, context, parentLoad) {
|
|
|
38
38
|
|
|
39
39
|
// At this point our `format` is either undefined or not known by us. Fall
|
|
40
40
|
// back to parsing as ESM/CJS.
|
|
41
|
-
const esmExports = getEsmExports(source)
|
|
41
|
+
const esmExports = getEsmExports(source, false, url)
|
|
42
42
|
if (!esmExports.length) {
|
|
43
43
|
// TODO(bengl) it's might be possible to get here if somehow the format
|
|
44
44
|
// isn't set at first and yet we have an ESM module with no exports.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
const warn = require('./helpers')
|
|
4
|
+
|
|
5
|
+
const FILE_NAME = 'get-pkg-json-type-module'
|
|
6
|
+
|
|
7
|
+
function findNearestPackageJson(filePath) {
|
|
8
|
+
let currentDir = path.dirname(filePath)
|
|
9
|
+
|
|
10
|
+
while (currentDir !== '/') {
|
|
11
|
+
const packageJsonPath = path.join(currentDir, 'package.json')
|
|
12
|
+
try {
|
|
13
|
+
const packageJsonContent = fs.readFileSync(packageJsonPath, 'utf8')
|
|
14
|
+
return packageJsonContent;
|
|
15
|
+
} catch (error) {
|
|
16
|
+
// File does not exist, continue searching in the parent directory
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
currentDir = path.dirname(currentDir)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return null
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function isModuleType(filePath) {
|
|
26
|
+
const packageJsonContent = findNearestPackageJson(filePath)
|
|
27
|
+
|
|
28
|
+
if (!packageJsonContent) {
|
|
29
|
+
return false
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
const packageJson = JSON.parse(packageJsonContent)
|
|
34
|
+
|
|
35
|
+
if (packageJson.type === 'module') {
|
|
36
|
+
return true
|
|
37
|
+
} else {
|
|
38
|
+
return false
|
|
39
|
+
}
|
|
40
|
+
} catch (error) {
|
|
41
|
+
warn('Error reading or parsing package.json: ' + error.message, FILE_NAME)
|
|
42
|
+
return false
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
module.exports = isModuleType
|
package/lib/helpers.js
ADDED
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "import-in-the-middle",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "Intercept imports in Node.js",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"test": "c8 --check-coverage --lines
|
|
7
|
+
"test": "c8 --check-coverage --lines 70 imhotap --runner 'node test/runtest' --files test/{hook,low-level,other,get-esm-exports}/*",
|
|
8
8
|
"test:ts": "c8 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
|
},
|
|
@@ -34,9 +34,11 @@
|
|
|
34
34
|
"typescript": "^4.7.4"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"
|
|
37
|
+
"@babel/parser": "^7.23.5",
|
|
38
|
+
"acorn": "^8.11.2",
|
|
38
39
|
"acorn-import-assertions": "^1.9.0",
|
|
39
40
|
"cjs-module-lexer": "^1.2.2",
|
|
40
|
-
"module-details-from-path": "^1.0.3"
|
|
41
|
+
"module-details-from-path": "^1.0.3",
|
|
42
|
+
"recast": "^0.23.4"
|
|
41
43
|
}
|
|
42
44
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const o = { name5: 1, name6: 1 };
|
|
2
|
+
const array = [1, 1]
|
|
3
|
+
|
|
4
|
+
// Exporting declarations
|
|
5
|
+
export let name1 = 1, name2 = 1/*, … */; // also var
|
|
6
|
+
export const name3 = 1, name4 = 1/*, … */; // also var, let
|
|
7
|
+
export function functionName() { return 1 }
|
|
8
|
+
export class ClassName { getFoo() { return 1 } }
|
|
9
|
+
export function* generatorFunctionName() { return 1 }
|
|
10
|
+
export const { name5, name6: bar } = o;
|
|
11
|
+
export const [ name7, name8 ] = array;
|
|
12
|
+
export async function asyncFunctionName() { return 1 }
|
|
13
|
+
export async function* asyncGeneratorFunctionName() { yield 1 }
|
|
14
|
+
export const arrowFunction = () => {
|
|
15
|
+
return 1;
|
|
16
|
+
}
|
|
17
|
+
export const asyncArrowFunction = async () => {
|
|
18
|
+
return 1;
|
|
19
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default class { getFoo() { return 1 } }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default class ClassName { getFoo() { return 1 } }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default [1]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default 1
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default 'dog'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function () { return 1 }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function functionName() { return 1 }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function* () { return 1 }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function* generatorFunctionName() { return 1 }
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Export list
|
|
2
|
+
const name1 = 1
|
|
3
|
+
const name2 = 1
|
|
4
|
+
const name5 = 1
|
|
5
|
+
const variable1 = 1
|
|
6
|
+
const variable2 = 1
|
|
7
|
+
const variable3 = 1
|
|
8
|
+
const name6 = 1
|
|
9
|
+
export { name1, name2 };
|
|
10
|
+
export { variable1 as name3, variable2 as name4, /* …, */ name5 };
|
|
11
|
+
export { variable3 as "name" };
|
|
12
|
+
export { name6 as default /*, … */ };
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2.0 License.
|
|
2
|
+
//
|
|
3
|
+
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc.
|
|
4
|
+
|
|
5
|
+
import Hook from '../../index.js'
|
|
6
|
+
import a from '../fixtures/export-types/default-expression-array.mjs'
|
|
7
|
+
import n from '../fixtures/export-types/default-expression-num.mjs'
|
|
8
|
+
import s from '../fixtures/export-types/default-expression-string.mjs'
|
|
9
|
+
import fn from '../fixtures/export-types/default-function.mjs'
|
|
10
|
+
import cn from '../fixtures/export-types/default-class.mjs'
|
|
11
|
+
import gfn from '../fixtures/export-types/default-generator.mjs'
|
|
12
|
+
import afn from '../fixtures/export-types/default-function-anon.mjs'
|
|
13
|
+
import acn from '../fixtures/export-types/default-class-anon.mjs'
|
|
14
|
+
import agfn from '../fixtures/export-types/default-generator-anon.mjs'
|
|
15
|
+
import { strictEqual } from 'assert'
|
|
16
|
+
|
|
17
|
+
Hook((exports, name) => {
|
|
18
|
+
if (name.match(/default-expression-array\.m?js/)) {
|
|
19
|
+
exports.default[0] += 1
|
|
20
|
+
} else if (name.match(/default-expression-num\.m?js/)) {
|
|
21
|
+
exports.default += 1
|
|
22
|
+
}
|
|
23
|
+
else if (name.match(/default-expression-string\.m?js/)) {
|
|
24
|
+
exports.default += 'dawg'
|
|
25
|
+
}
|
|
26
|
+
else if (name.match(/default-function\.m?js/)) {
|
|
27
|
+
const orig = exports.default
|
|
28
|
+
exports.default = function () {
|
|
29
|
+
return orig() + 1
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
else if (name.match(/default-class\.m?js/)) {
|
|
33
|
+
exports.default.prototype.getFoo = function () {
|
|
34
|
+
return 2
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
else if (name.match(/default-generator\.m?js/)) {
|
|
38
|
+
const orig2 = exports.default
|
|
39
|
+
exports.default = function* () {
|
|
40
|
+
return orig2().next().value + 1
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
else if (name.match(/default-function-anon\.m?js/)) {
|
|
44
|
+
const orig = exports.default
|
|
45
|
+
exports.default = function () {
|
|
46
|
+
return orig() + 1
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
else if (name.match(/default-class-anon\.m?js/)) {
|
|
50
|
+
exports.default.prototype.getFoo = function () {
|
|
51
|
+
return 2
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
else if (name.match(/default-generator-anon\.m?js/)) {
|
|
55
|
+
const orig2 = exports.default
|
|
56
|
+
exports.default = function* () {
|
|
57
|
+
return orig2().next().value + 1
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
strictEqual(a[0], 2)
|
|
63
|
+
strictEqual(fn(), 2)
|
|
64
|
+
strictEqual(new cn().getFoo(), 2)
|
|
65
|
+
strictEqual(gfn().next().value, 2)
|
|
66
|
+
strictEqual(afn(), 2)
|
|
67
|
+
strictEqual(new acn().getFoo(), 2)
|
|
68
|
+
strictEqual(agfn().next().value, 2)
|
|
69
|
+
// the below tests won't work because literal default exports are static
|
|
70
|
+
// strictEqual(n, 2)
|
|
71
|
+
// strictEqual(s, 'dogdawg')
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2.0 License.
|
|
2
|
+
//
|
|
3
|
+
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc.
|
|
4
|
+
|
|
5
|
+
import Hook from '../../index.js'
|
|
6
|
+
import {
|
|
7
|
+
name1 as n1,
|
|
8
|
+
name2 as n2,
|
|
9
|
+
name3 as n3,
|
|
10
|
+
name4 as n4,
|
|
11
|
+
functionName as fn,
|
|
12
|
+
ClassName as cn,
|
|
13
|
+
generatorFunctionName as gfn,
|
|
14
|
+
name5 as n5,
|
|
15
|
+
bar as n6,
|
|
16
|
+
name7 as n7,
|
|
17
|
+
name8 as n8,
|
|
18
|
+
asyncFunctionName as afn,
|
|
19
|
+
asyncGeneratorFunctionName as agfn,
|
|
20
|
+
arrowFunction as arfn,
|
|
21
|
+
asyncArrowFunction as aarfn,
|
|
22
|
+
} from '../fixtures/export-types/declarations.mjs'
|
|
23
|
+
import { strictEqual } from 'assert'
|
|
24
|
+
|
|
25
|
+
Hook((exports, name) => {
|
|
26
|
+
if (name.match(/declarations\.m?js/)) {
|
|
27
|
+
exports.name1 += 1
|
|
28
|
+
exports.name2 += 1
|
|
29
|
+
exports.name3 += 1
|
|
30
|
+
exports.name4 += 1
|
|
31
|
+
const orig = exports.functionName
|
|
32
|
+
exports.functionName = function () {
|
|
33
|
+
return orig() + 1
|
|
34
|
+
}
|
|
35
|
+
exports.ClassName.prototype.getFoo = function () {
|
|
36
|
+
return 2
|
|
37
|
+
}
|
|
38
|
+
const orig2 = exports.generatorFunctionName
|
|
39
|
+
exports.generatorFunctionName = function* () {
|
|
40
|
+
return orig2().next().value + 1
|
|
41
|
+
}
|
|
42
|
+
exports.name5 += 1
|
|
43
|
+
exports.bar += 1
|
|
44
|
+
exports.name7 += 1
|
|
45
|
+
exports.name8 += 1
|
|
46
|
+
const asyncOrig = exports.asyncFunctionName
|
|
47
|
+
exports.asyncFunctionName = async function () {
|
|
48
|
+
return await asyncOrig() + 1
|
|
49
|
+
}
|
|
50
|
+
const asyncOrig2 = exports.asyncGeneratorFunctionName
|
|
51
|
+
exports.asyncGeneratorFunctionName = async function* () {
|
|
52
|
+
for await (const value of asyncOrig2()) {
|
|
53
|
+
yield value + 1;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const arrowOrig = exports.arrowFunction
|
|
57
|
+
exports.arrowFunction = () => {
|
|
58
|
+
return arrowOrig() + 1
|
|
59
|
+
}
|
|
60
|
+
const asyncArrowOrig = exports.asyncArrowFunction
|
|
61
|
+
exports.asyncArrowFunction = async () => {
|
|
62
|
+
return await asyncArrowOrig() + 1
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
strictEqual(n1, 2)
|
|
68
|
+
strictEqual(n2, 2)
|
|
69
|
+
strictEqual(n3, 2)
|
|
70
|
+
strictEqual(n4, 2)
|
|
71
|
+
strictEqual(fn(), 2)
|
|
72
|
+
strictEqual(new cn().getFoo(), 2)
|
|
73
|
+
strictEqual(gfn().next().value, 2)
|
|
74
|
+
strictEqual(n5, 2)
|
|
75
|
+
strictEqual(n6, 2)
|
|
76
|
+
strictEqual(n7, 2)
|
|
77
|
+
strictEqual(n8, 2)
|
|
78
|
+
strictEqual(await afn(), 2)
|
|
79
|
+
for await (const value of agfn()) {
|
|
80
|
+
strictEqual(value, 2)
|
|
81
|
+
}
|
|
82
|
+
strictEqual(arfn(), 2)
|
|
83
|
+
strictEqual(await aarfn(), 2)
|
|
84
|
+
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2.0 License.
|
|
2
|
+
//
|
|
3
|
+
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc.
|
|
4
|
+
|
|
5
|
+
import Hook from '../../index.js'
|
|
6
|
+
import n, {
|
|
7
|
+
name1 as n1,
|
|
8
|
+
name2 as n2,
|
|
9
|
+
name3 as n3,
|
|
10
|
+
name4 as n4,
|
|
11
|
+
name5 as n5,
|
|
12
|
+
"name" as n6,
|
|
13
|
+
} from '../fixtures/export-types/list.mjs'
|
|
14
|
+
import { strictEqual } from 'assert'
|
|
15
|
+
|
|
16
|
+
Hook((exports, name) => {
|
|
17
|
+
if (name.match(/list\.m?js/)) {
|
|
18
|
+
exports.name1 += 1
|
|
19
|
+
exports.name2 += 1
|
|
20
|
+
exports.name3 += 1
|
|
21
|
+
exports.name4 += 1
|
|
22
|
+
exports.name5 += 1
|
|
23
|
+
exports.name += 1
|
|
24
|
+
exports.default += 1
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
strictEqual(n1, 2)
|
|
29
|
+
strictEqual(n2, 2)
|
|
30
|
+
strictEqual(n3, 2)
|
|
31
|
+
strictEqual(n4, 2)
|
|
32
|
+
strictEqual(n5, 2)
|
|
33
|
+
strictEqual(n6, 2)
|
|
34
|
+
strictEqual(n, 2)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2.0 License.
|
|
2
|
+
//
|
|
3
|
+
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc.
|
|
4
|
+
|
|
5
|
+
import { spawn } from 'child_process'
|
|
6
|
+
import { strictEqual } from 'assert'
|
|
7
|
+
|
|
8
|
+
const nodeProcess = spawn('node', [
|
|
9
|
+
'--loader',
|
|
10
|
+
'./hook.mjs',
|
|
11
|
+
'./test/fixtures/a.mjs'
|
|
12
|
+
])
|
|
13
|
+
|
|
14
|
+
const expectedOutput = 'testB\ntestA'
|
|
15
|
+
let stdout = ''
|
|
16
|
+
let stderr = ''
|
|
17
|
+
|
|
18
|
+
nodeProcess.stdout.on('data', (data) => {
|
|
19
|
+
stdout += data.toString()
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
nodeProcess.stderr.on('data', (data) => {
|
|
23
|
+
stderr += data.toString()
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
nodeProcess.on('close', (code) => {
|
|
27
|
+
strictEqual(stderr, '', 'There should be no errors on stderr')
|
|
28
|
+
strictEqual(stdout.trim(), expectedOutput, 'The stdout should match the expected output')
|
|
29
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2.0 License.
|
|
2
|
+
//
|
|
3
|
+
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc.
|
|
4
|
+
|
|
5
|
+
import { spawn } from 'child_process'
|
|
6
|
+
import { strictEqual } from 'assert'
|
|
7
|
+
|
|
8
|
+
const nodeProcess = spawn('node', [
|
|
9
|
+
'--loader',
|
|
10
|
+
'./hook.mjs',
|
|
11
|
+
'./test/fixtures/reassign-let.mjs'
|
|
12
|
+
])
|
|
13
|
+
|
|
14
|
+
const expectedOutput = 'setting env, env.FOO is bar\nusing env from another module, env.FOO is bar'
|
|
15
|
+
let stdout = ''
|
|
16
|
+
let stderr = ''
|
|
17
|
+
|
|
18
|
+
nodeProcess.stdout.on('data', (data) => {
|
|
19
|
+
stdout += data.toString()
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
nodeProcess.stderr.on('data', (data) => {
|
|
23
|
+
stderr += data.toString()
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
nodeProcess.on('close', (code) => {
|
|
27
|
+
strictEqual(stderr, '', 'There should be no errors on stderr')
|
|
28
|
+
strictEqual(stdout.trim(), expectedOutput, 'The stdout should match the expected output')
|
|
29
|
+
})
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2.0 License.
|
|
2
|
+
//
|
|
3
|
+
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc.
|
|
4
|
+
|
|
5
|
+
import { spawn } from 'child_process'
|
|
6
|
+
import { strictEqual } from 'assert'
|
|
7
|
+
|
|
8
|
+
const nodeProcess = spawn('node', ['--loader', '../../../hook.mjs', './a.js'], {
|
|
9
|
+
cwd: './test/fixtures/test-undefined-format/'})
|
|
10
|
+
|
|
11
|
+
const expectedOutput = 'testB\ntestA'
|
|
12
|
+
let stdout = ''
|
|
13
|
+
let stderr = ''
|
|
14
|
+
|
|
15
|
+
nodeProcess.stdout.on('data', (data) => {
|
|
16
|
+
stdout += data.toString()
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
nodeProcess.stderr.on('data', (data) => {
|
|
20
|
+
stderr += data.toString()
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
nodeProcess.on('close', (code) => {
|
|
24
|
+
strictEqual(stderr, '', 'There should be no errors on stderr')
|
|
25
|
+
strictEqual(stdout.trim(), expectedOutput, 'The stdout should match the expected output')
|
|
26
|
+
});
|
|
Binary file
|
|
File without changes
|