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.
Files changed (34) hide show
  1. package/hook.js +71 -6
  2. package/lib/get-esm-exports.js +151 -52
  3. package/lib/get-exports.js +2 -2
  4. package/lib/get-pkg-json-type-module.js +46 -0
  5. package/lib/helpers.js +5 -0
  6. package/package.json +6 -4
  7. package/test/fixtures/a.mjs +7 -0
  8. package/test/fixtures/b.mjs +6 -0
  9. package/test/fixtures/env.mjs +8 -0
  10. package/test/fixtures/export-types/declarations.mjs +19 -0
  11. package/test/fixtures/export-types/default-class-anon.mjs +1 -0
  12. package/test/fixtures/export-types/default-class.mjs +1 -0
  13. package/test/fixtures/export-types/default-expression-array.mjs +1 -0
  14. package/test/fixtures/export-types/default-expression-num.mjs +1 -0
  15. package/test/fixtures/export-types/default-expression-string.mjs +1 -0
  16. package/test/fixtures/export-types/default-function-anon.mjs +1 -0
  17. package/test/fixtures/export-types/default-function.mjs +1 -0
  18. package/test/fixtures/export-types/default-generator-anon.mjs +1 -0
  19. package/test/fixtures/export-types/default-generator.mjs +1 -0
  20. package/test/fixtures/export-types/list.mjs +12 -0
  21. package/test/fixtures/reassign-let.mjs +5 -0
  22. package/test/fixtures/test-undefined-format/a.js +8 -0
  23. package/test/fixtures/test-undefined-format/b.js +7 -0
  24. package/test/fixtures/test-undefined-format/package.json +9 -0
  25. package/test/fixtures/use-env.mjs +5 -0
  26. package/test/get-esm-exports/v18.19-get-esm-exports.js +1 -1
  27. package/test/hook/default-exports.mjs +71 -0
  28. package/test/hook/v16-declaration-exports.mjs +84 -0
  29. package/test/hook/v16-list-exports.mjs +34 -0
  30. package/test/other/v16-cyclical-dependency.mjs +29 -0
  31. package/test/other/v16-reassign-let.mjs +29 -0
  32. package/test/other/v16-test-undefined-format.mjs +26 -0
  33. package/import-in-the-middle-1.6.0.tgz +0 -0
  34. /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
- return {
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
  }
@@ -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
- function warn (txt) {
14
- process.emitWarning(txt, 'get-esm-exports')
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 getEsmExports (moduleStr) {
18
- const exportedNames = new Set()
19
- const tree = parser.parse(moduleStr, acornOpts)
20
- for (const node of tree.body) {
21
- if (!node.type.startsWith('Export')) continue
22
- switch (node.type) {
23
- case 'ExportNamedDeclaration':
24
- if (node.declaration) {
25
- parseDeclaration(node, exportedNames)
26
- } else {
27
- parseSpecifiers(node, exportedNames)
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
- break
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 (node, exportedNames) {
48
- switch (node.declaration.type) {
132
+ function parseDeclaration(declaration, exportAlias) {
133
+ switch (declaration.type) {
49
134
  case 'FunctionDeclaration':
50
- exportedNames.add(node.declaration.id.name)
135
+ case 'ClassDeclaration':
136
+ exportAlias[declaration.id.name] = declaration.id.name
51
137
  break
52
138
  case 'VariableDeclaration':
53
- for (const varDecl of node.declaration.declarations) {
54
- parseVariableDeclaration(varDecl, exportedNames)
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: ' + node.delcaration.type)
147
+ warn('unknown declaration type: ' + declaration.type, FILE_NAME)
62
148
  }
63
149
  }
64
150
 
65
- function parseVariableDeclaration (node, exportedNames) {
66
- switch (node.id.type) {
151
+ function parseVariableDeclaration(varDecl, exportAlias) {
152
+ switch (varDecl.id.type) {
67
153
  case 'Identifier':
68
- exportedNames.add(node.id.name)
154
+ exportAlias[varDecl.id.name] = varDecl.id.name
69
155
  break
70
156
  case 'ObjectPattern':
71
- for (const prop of node.id.properties) {
72
- exportedNames.add(prop.value.name)
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 node.id.elements) {
77
- exportedNames.add(elem.name)
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: ' + node.id.type)
169
+ warn('unknown variable declaration type: ' + varDecl.id.type, FILE_NAME)
82
170
  }
83
171
  }
84
172
 
85
- function parseSpecifiers (node, exportedNames) {
86
- for (const specifier of node.specifiers) {
87
- if (specifier.exported.type === 'Identifier') {
88
- exportedNames.add(specifier.exported.name)
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
- exportedNames.add(specifier.exported.value)
91
- } else {
92
- warn('unrecognized specifier type: ' + specifier.exported.type)
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
  }
@@ -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
@@ -0,0 +1,5 @@
1
+ function warn (txt, fileName) {
2
+ process.emitWarning(txt, fileName)
3
+ }
4
+
5
+ module.exports = warn
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "import-in-the-middle",
3
- "version": "1.6.0",
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 85 imhotap --runner 'node test/runtest' --files test/{hook,low-level,other,get-esm-exports}/*",
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
- "acorn": "^8.8.2",
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,7 @@
1
+ import { testB } from './b.mjs';
2
+
3
+ export function testA() {
4
+ console.log("testA");
5
+ }
6
+
7
+ testB();
@@ -0,0 +1,6 @@
1
+ import { testA } from './a.mjs';
2
+
3
+ export function testB() {
4
+ console.log("testB");
5
+ testA();
6
+ }
@@ -0,0 +1,8 @@
1
+ let env = {FOO: 'baz'};
2
+
3
+ function setEnv(newEnv) {
4
+ console.log('setting env, env.FOO is', newEnv.FOO);
5
+ env = newEnv;
6
+ }
7
+
8
+ export { setEnv, env };
@@ -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,5 @@
1
+ import { setEnv } from "./env.mjs"
2
+ import {useEnv} from "./use-env.mjs"
3
+
4
+ setEnv({ FOO: 'bar'})
5
+ useEnv()
@@ -0,0 +1,8 @@
1
+ import { testB } from './b.js';
2
+
3
+ export function testA() {
4
+ console.log("testA");
5
+ }
6
+
7
+ testB();
8
+
@@ -0,0 +1,7 @@
1
+ import { testA } from './a.js';
2
+
3
+ export function testB() {
4
+ console.log("testB");
5
+ testA();
6
+ }
7
+
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "test-v16.12-undefined-format",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "test-undefined-format": "node --loader ../../../hook.mjs ./a.js"
7
+ },
8
+ "dependencies": {}
9
+ }
@@ -0,0 +1,5 @@
1
+ import {env} from "./env.mjs"
2
+
3
+ export function useEnv() {
4
+ console.log('using env from another module, env.FOO is', env.FOO)
5
+ }
@@ -1,3 +1,3 @@
1
1
  // v18.19.0 backported ESM hook execution to a separate thread,
2
2
  // thus being equivalent to >=v20.
3
- require('./v20-get-esm-exports')
3
+ require('./v16-get-esm-exports')
@@ -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