import-in-the-middle 1.7.0 → 1.7.2

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 (38) hide show
  1. package/hook.js +89 -82
  2. package/lib/get-esm-exports.js +52 -151
  3. package/lib/get-exports.js +2 -2
  4. package/package.json +5 -7
  5. package/test/fixtures/a.mjs +4 -4
  6. package/test/fixtures/b.mjs +4 -5
  7. package/test/fixtures/bundle.mjs +4 -0
  8. package/test/fixtures/esm-exports.txt +1 -1
  9. package/test/fixtures/foo.mjs +5 -0
  10. package/test/fixtures/lib/baz.mjs +3 -0
  11. package/test/get-esm-exports/v18.19-get-esm-exports.js +1 -1
  12. package/test/hook/static-import-star.mjs +32 -0
  13. package/lib/get-pkg-json-type-module.js +0 -46
  14. package/lib/helpers.js +0 -5
  15. package/test/fixtures/env.mjs +0 -8
  16. package/test/fixtures/export-types/declarations.mjs +0 -19
  17. package/test/fixtures/export-types/default-class-anon.mjs +0 -1
  18. package/test/fixtures/export-types/default-class.mjs +0 -1
  19. package/test/fixtures/export-types/default-expression-array.mjs +0 -1
  20. package/test/fixtures/export-types/default-expression-num.mjs +0 -1
  21. package/test/fixtures/export-types/default-expression-string.mjs +0 -1
  22. package/test/fixtures/export-types/default-function-anon.mjs +0 -1
  23. package/test/fixtures/export-types/default-function.mjs +0 -1
  24. package/test/fixtures/export-types/default-generator-anon.mjs +0 -1
  25. package/test/fixtures/export-types/default-generator.mjs +0 -1
  26. package/test/fixtures/export-types/list.mjs +0 -12
  27. package/test/fixtures/reassign-let.mjs +0 -5
  28. package/test/fixtures/test-undefined-format/a.js +0 -8
  29. package/test/fixtures/test-undefined-format/b.js +0 -7
  30. package/test/fixtures/test-undefined-format/package.json +0 -9
  31. package/test/fixtures/use-env.mjs +0 -5
  32. package/test/hook/default-exports.mjs +0 -71
  33. package/test/hook/v16-declaration-exports.mjs +0 -84
  34. package/test/hook/v16-list-exports.mjs +0 -34
  35. package/test/other/v16-cyclical-dependency.mjs +0 -29
  36. package/test/other/v16-reassign-let.mjs +0 -29
  37. package/test/other/v16-test-undefined-format.mjs +0 -26
  38. /package/test/get-esm-exports/{v16-get-esm-exports.js → v20-get-esm-exports.js} +0 -0
package/hook.js CHANGED
@@ -2,41 +2,26 @@
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
+ const { randomBytes } = require('crypto')
7
6
  const specifiers = new Map()
8
7
  const isWin = process.platform === "win32"
9
- const warn = require('./lib/helpers')
10
8
 
11
9
  // FIXME: Typescript extensions are added temporarily until we find a better
12
10
  // way of supporting arbitrary extensions
13
11
  const EXTENSION_RE = /\.(js|mjs|cjs|ts|mts|cts)$/
14
- const EXTENSION_MJS_RE = /\.mjs$/
15
- const EXTENSION_JS_RE = /\.js$/
16
12
  const NODE_VERSION = process.versions.node.split('.')
17
13
  const NODE_MAJOR = Number(NODE_VERSION[0])
18
14
  const NODE_MINOR = Number(NODE_VERSION[1])
19
- const FILE_NAME = 'hook.js'
20
15
 
21
16
  let entrypoint
22
- let getExports
23
- let getEsmExports
24
- let getPkgJsonTypeModule
25
17
 
18
+ let getExports
26
19
  if (NODE_MAJOR >= 20 || (NODE_MAJOR == 18 && NODE_MINOR >= 19)) {
27
20
  getExports = require('./lib/get-exports.js')
28
21
  } else {
29
22
  getExports = (url) => import(url).then(Object.keys)
30
23
  }
31
24
 
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
-
40
25
  function hasIitm (url) {
41
26
  try {
42
27
  return new URL(url).searchParams.has('iitm')
@@ -93,6 +78,76 @@ function needsToAddFileProtocol(urlObj) {
93
78
  return !isFileProtocol(urlObj) && NODE_MAJOR < 18
94
79
  }
95
80
 
81
+ /**
82
+ * Determines if a specifier represents an export all ESM line.
83
+ * Note that the expected `line` isn't 100% valid ESM. It is derived
84
+ * from the `getExports` function wherein we have recognized the true
85
+ * line and re-mapped it to one we expect.
86
+ *
87
+ * @param {string} line
88
+ * @returns {boolean}
89
+ */
90
+ function isStarExportLine(line) {
91
+ return /^\* from /.test(line)
92
+ }
93
+
94
+ /**
95
+ * @typedef {object} ProcessedModule
96
+ * @property {string[]} imports A set of ESM import lines to be added to the
97
+ * shimmed module source.
98
+ * @property {string[]} namespaces A set of identifiers representing the
99
+ * modules in `imports`, e.g. for `import * as foo from 'bar'`, "foo" will be
100
+ * present in this array.
101
+ * @property {string[]} setters The shimmed setters for all the exports
102
+ * from the module and any transitive export all modules.
103
+ */
104
+
105
+ /**
106
+ * Processes a module's exports and builds a set of new import statements,
107
+ * namespace names, and setter blocks. If an export all export if encountered,
108
+ * the target exports will be hoisted to the current module via a generated
109
+ * namespace.
110
+ *
111
+ * @param {object} params
112
+ * @param {string} params.srcUrl The full URL to the module to process.
113
+ * @param {object} params.context Provided by the loaders API.
114
+ * @param {function} parentGetSource Provides the source code for the parent
115
+ * module.
116
+ * @returns {Promise<ProcessedModule>}
117
+ */
118
+ async function processModule({ srcUrl, context, parentGetSource }) {
119
+ const exportNames = await getExports(srcUrl, context, parentGetSource)
120
+ const imports = [`import * as namespace from ${JSON.stringify(srcUrl)}`]
121
+ const namespaces = ['namespace']
122
+ const setters = []
123
+
124
+ for (const n of exportNames) {
125
+ if (isStarExportLine(n) === true) {
126
+ const [_, modFile] = n.split('* from ')
127
+ const modUrl = new URL(modFile, srcUrl).toString()
128
+ const modName = Buffer.from(modFile, 'hex') + Date.now() + randomBytes(4).toString('hex')
129
+
130
+ imports.push(`import * as $${modName} from ${JSON.stringify(modUrl)}`)
131
+ namespaces.push(`$${modName}`)
132
+
133
+ const data = await processModule({ srcUrl: modUrl, context, parentGetSource })
134
+ Array.prototype.push.apply(setters, data.setters)
135
+
136
+ continue
137
+ }
138
+
139
+ setters.push(`
140
+ let $${n} = _.${n}
141
+ export { $${n} as ${n} }
142
+ set.${n} = (v) => {
143
+ $${n} = v
144
+ return true
145
+ }
146
+ `)
147
+ }
148
+
149
+ return { imports, namespaces, setters }
150
+ }
96
151
 
97
152
  function addIitm (url) {
98
153
  const urlObj = new URL(url)
@@ -124,23 +179,12 @@ function createHook (meta) {
124
179
  ) {
125
180
  return url
126
181
  }
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
- }
182
+
139
183
 
140
184
  specifiers.set(url.url, specifier)
141
185
 
142
186
  return {
143
- url: url.format !== 'module' || NODE_MAJOR < 16 ? addIitm(url.url) : url.url,
187
+ url: addIitm(url.url),
144
188
  shortCircuit: true,
145
189
  format: url.format
146
190
  }
@@ -149,61 +193,24 @@ function createHook (meta) {
149
193
  const iitmURL = new URL('lib/register.js', meta.url).toString()
150
194
  async function getSource (url, context, parentGetSource) {
151
195
  if (hasIitm(url)) {
152
-
153
196
  const realUrl = deleteIitm(url)
154
-
155
- const exportNames = await getExports(realUrl, context, parentGetSource)
156
-
157
- return {
197
+ const { imports, namespaces, setters } = await processModule({
198
+ srcUrl: realUrl,
199
+ context,
200
+ parentGetSource
201
+ })
202
+
203
+ return {
158
204
  source: `
159
205
  import { register } from '${iitmURL}'
160
- import * as namespace from ${JSON.stringify(url)}
206
+ ${imports.join('\n')}
207
+
208
+ const _ = Object.assign({}, ...[${namespaces.join(', ')}])
161
209
  const set = {}
162
- ${exportNames.map((n) => `
163
- let $${n} = namespace.${n}
164
- export { $${n} as ${n} }
165
- set.${n} = (v) => {
166
- $${n} = v
167
- return true
168
- }
169
- `).join('\n')}
170
- register(${JSON.stringify(realUrl)}, namespace, set, ${JSON.stringify(specifiers.get(realUrl))})
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
- }
210
+
211
+ ${setters.join('\n')}
212
+ register(${JSON.stringify(realUrl)}, _, set, ${JSON.stringify(specifiers.get(realUrl))})
204
213
  `
205
- return {
206
- source: src
207
214
  }
208
215
  }
209
216
 
@@ -212,7 +219,7 @@ DATADOG_REGISTER_FUNC(${JSON.stringify(url)}, namespace, set, ${JSON.stringify(s
212
219
 
213
220
  // For Node.js 16.12.0 and higher.
214
221
  async function load (url, context, parentLoad) {
215
- if (hasIitm(url) || context.format === 'module') {
222
+ if (hasIitm(url)) {
216
223
  const { source } = await getSource(url, context, parentLoad)
217
224
  return {
218
225
  source,
@@ -232,7 +239,7 @@ DATADOG_REGISTER_FUNC(${JSON.stringify(url)}, namespace, set, ${JSON.stringify(s
232
239
  resolve,
233
240
  getSource,
234
241
  getFormat (url, context, parentGetFormat) {
235
- if (hasIitm(url) || context.format === 'module') {
242
+ if (hasIitm(url)) {
236
243
  return {
237
244
  format: 'module'
238
245
  }
@@ -1,11 +1,7 @@
1
1
  'use strict'
2
2
 
3
- const recast = require('recast')
4
3
  const { Parser } = require('acorn')
5
- const { importAssertions } = require('acorn-import-assertions')
6
- const warn = require('./helpers')
7
-
8
- const TS_EXTENSION_RE = /\.(ts|mts|cts)$/
4
+ const { importAssertions } = require('acorn-import-assertions');
9
5
 
10
6
  const acornOpts = {
11
7
  ecmaVersion: 'latest',
@@ -13,182 +9,87 @@ const acornOpts = {
13
9
  }
14
10
 
15
11
  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;
48
12
 
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);
13
+ function warn (txt) {
14
+ process.emitWarning(txt, 'get-esm-exports')
113
15
  }
114
16
 
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
- }
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)
126
28
  }
127
- }
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(`* from ${node.source.value}`)
38
+ }
39
+ break
40
+ default:
41
+ warn('unrecognized export type: ' + node.type)
128
42
  }
129
43
  }
44
+ return Array.from(exportedNames)
130
45
  }
131
46
 
132
- function parseDeclaration(declaration, exportAlias) {
133
- switch (declaration.type) {
47
+ function parseDeclaration (node, exportedNames) {
48
+ switch (node.declaration.type) {
134
49
  case 'FunctionDeclaration':
135
- case 'ClassDeclaration':
136
- exportAlias[declaration.id.name] = declaration.id.name
50
+ exportedNames.add(node.declaration.id.name)
137
51
  break
138
52
  case 'VariableDeclaration':
139
- for (const varDecl of declaration.declarations) {
140
- if (declaration.kind === 'const') {
141
- declaration.kind = 'let'
142
- }
143
- parseVariableDeclaration(varDecl, exportAlias)
53
+ for (const varDecl of node.declaration.declarations) {
54
+ parseVariableDeclaration(varDecl, exportedNames)
144
55
  }
145
56
  break
57
+ case 'ClassDeclaration':
58
+ exportedNames.add(node.declaration.id.name)
59
+ break
146
60
  default:
147
- warn('unknown declaration type: ' + declaration.type, FILE_NAME)
61
+ warn('unknown declaration type: ' + node.delcaration.type)
148
62
  }
149
63
  }
150
64
 
151
- function parseVariableDeclaration(varDecl, exportAlias) {
152
- switch (varDecl.id.type) {
65
+ function parseVariableDeclaration (node, exportedNames) {
66
+ switch (node.id.type) {
153
67
  case 'Identifier':
154
- exportAlias[varDecl.id.name] = varDecl.id.name
68
+ exportedNames.add(node.id.name)
155
69
  break
156
70
  case 'ObjectPattern':
157
- for (const prop of varDecl.id.properties) {
158
- exportAlias[prop.value.name] = prop.value.name
71
+ for (const prop of node.id.properties) {
72
+ exportedNames.add(prop.value.name)
159
73
  }
160
74
  break
161
75
  case 'ArrayPattern':
162
- for (const elem of varDecl.id.elements) {
163
- if (elem) {
164
- exportAlias[elem.name] = elem.name
165
- }
76
+ for (const elem of node.id.elements) {
77
+ exportedNames.add(elem.name)
166
78
  }
167
79
  break
168
80
  default:
169
- warn('unknown variable declaration type: ' + varDecl.id.type, FILE_NAME)
81
+ warn('unknown variable declaration type: ' + node.id.type)
170
82
  }
171
83
  }
172
84
 
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
85
+ function parseSpecifiers (node, exportedNames) {
86
+ for (const specifier of node.specifiers) {
87
+ if (specifier.exported.type === 'Identifier') {
88
+ exportedNames.add(specifier.exported.name)
187
89
  } else if (specifier.exported.type === 'Literal') {
188
- exportAlias[specifier.exported.value] = specifier.exported.value
189
- }
190
- else {
191
- warn('unrecognized specifier type: ' + specifier.exported.type, FILE_NAME)
90
+ exportedNames.add(specifier.exported.value)
91
+ } else {
92
+ warn('unrecognized specifier type: ' + specifier.exported.type)
192
93
  }
193
94
  }
194
95
  }
@@ -30,7 +30,7 @@ async function getExports (url, context, parentLoad) {
30
30
  }
31
31
 
32
32
  if (format === 'module') {
33
- return getEsmExports(source, false, url)
33
+ return getEsmExports(source)
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, false, url)
41
+ const esmExports = getEsmExports(source)
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.
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "import-in-the-middle",
3
- "version": "1.7.0",
3
+ "version": "1.7.2",
4
4
  "description": "Intercept imports in Node.js",
5
5
  "main": "index.js",
6
6
  "scripts": {
7
- "test": "c8 --check-coverage --lines 70 imhotap --runner 'node test/runtest' --files test/{hook,low-level,other,get-esm-exports}/*",
8
- "test:ts": "c8 imhotap --runner 'node test/runtest' --files test/typescript/*.test.mts",
7
+ "test": "c8 --reporter lcov --check-coverage --lines 70 imhotap --runner 'node test/runtest' --files test/{hook,low-level,other,get-esm-exports}/*",
8
+ "test:ts": "c8 --reporter lcov 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
  },
11
11
  "repository": {
@@ -34,11 +34,9 @@
34
34
  "typescript": "^4.7.4"
35
35
  },
36
36
  "dependencies": {
37
- "@babel/parser": "^7.23.5",
38
- "acorn": "^8.11.2",
37
+ "acorn": "^8.8.2",
39
38
  "acorn-import-assertions": "^1.9.0",
40
39
  "cjs-module-lexer": "^1.2.2",
41
- "module-details-from-path": "^1.0.3",
42
- "recast": "^0.23.4"
40
+ "module-details-from-path": "^1.0.3"
43
41
  }
44
42
  }
@@ -1,7 +1,7 @@
1
- import { testB } from './b.mjs';
1
+ export const a = 'a'
2
2
 
3
- export function testA() {
4
- console.log("testA");
3
+ export function aFunc() {
4
+ return a
5
5
  }
6
6
 
7
- testB();
7
+ export * from './foo.mjs'
@@ -1,6 +1,5 @@
1
- import { testA } from './a.mjs';
1
+ export const b = 'b'
2
2
 
3
- export function testB() {
4
- console.log("testB");
5
- testA();
6
- }
3
+ export function bFunc() {
4
+ return b
5
+ }
@@ -0,0 +1,4 @@
1
+ import bar from './something.mjs'
2
+ export default bar
3
+ export * from './a.mjs'
4
+ export * from './b.mjs'
@@ -23,7 +23,7 @@ export default class { /* … */ } //| default
23
23
  export default function* () { /* … */ } //| default
24
24
 
25
25
  // Aggregating modules
26
- export * from "module-name"; //| *
26
+ export * from "module-name"; //| * from module-name
27
27
  export * as name1 from "module-name"; //| name1
28
28
  export { name1, /* …, */ nameN } from "module-name"; //| name1,nameN
29
29
  export { import1 as name1, import2 as name2, /* …, */ nameN } from "module-name"; //| name1,name2,nameN
@@ -0,0 +1,5 @@
1
+ export function foo() {
2
+ return 'foo'
3
+ }
4
+
5
+ export * from './lib/baz.mjs'
@@ -0,0 +1,3 @@
1
+ export function baz() {
2
+ return 'baz'
3
+ }
@@ -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('./v16-get-esm-exports')
3
+ require('./v20-get-esm-exports')
@@ -0,0 +1,32 @@
1
+ import { strictEqual } from 'assert'
2
+ import Hook from '../../index.js'
3
+ Hook((exports, name) => {
4
+ if (/bundle\.mjs/.test(name) === false) return
5
+
6
+ const bar = exports.default
7
+ exports.default = function wrappedBar() {
8
+ return bar() + '-wrapped'
9
+ }
10
+
11
+ const foo = exports.foo
12
+ exports.foo = function wrappedFoo() {
13
+ return foo() + '-wrapped'
14
+ }
15
+
16
+ const aFunc = exports.aFunc
17
+ exports.aFunc = function wrappedAFunc() {
18
+ return aFunc() + '-wrapped'
19
+ }
20
+ })
21
+
22
+ import {
23
+ default as bar,
24
+ foo,
25
+ aFunc,
26
+ baz
27
+ } from '../fixtures/bundle.mjs'
28
+
29
+ strictEqual(bar(), '42-wrapped')
30
+ strictEqual(foo(), 'foo-wrapped')
31
+ strictEqual(aFunc(), 'a-wrapped')
32
+ strictEqual(baz(), 'baz')
@@ -1,46 +0,0 @@
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 DELETED
@@ -1,5 +0,0 @@
1
- function warn (txt, fileName) {
2
- process.emitWarning(txt, fileName)
3
- }
4
-
5
- module.exports = warn
@@ -1,8 +0,0 @@
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 };
@@ -1,19 +0,0 @@
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
- }
@@ -1 +0,0 @@
1
- export default class { getFoo() { return 1 } }
@@ -1 +0,0 @@
1
- export default class ClassName { getFoo() { return 1 } }
@@ -1 +0,0 @@
1
- export default [1]
@@ -1 +0,0 @@
1
- export default 1
@@ -1 +0,0 @@
1
- export default 'dog'
@@ -1 +0,0 @@
1
- export default function () { return 1 }
@@ -1 +0,0 @@
1
- export default function functionName() { return 1 }
@@ -1 +0,0 @@
1
- export default function* () { return 1 }
@@ -1 +0,0 @@
1
- export default function* generatorFunctionName() { return 1 }
@@ -1,12 +0,0 @@
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 /*, … */ };
@@ -1,5 +0,0 @@
1
- import { setEnv } from "./env.mjs"
2
- import {useEnv} from "./use-env.mjs"
3
-
4
- setEnv({ FOO: 'bar'})
5
- useEnv()
@@ -1,8 +0,0 @@
1
- import { testB } from './b.js';
2
-
3
- export function testA() {
4
- console.log("testA");
5
- }
6
-
7
- testB();
8
-
@@ -1,7 +0,0 @@
1
- import { testA } from './a.js';
2
-
3
- export function testB() {
4
- console.log("testB");
5
- testA();
6
- }
7
-
@@ -1,9 +0,0 @@
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
- }
@@ -1,5 +0,0 @@
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,71 +0,0 @@
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')
@@ -1,84 +0,0 @@
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
-
@@ -1,34 +0,0 @@
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)
@@ -1,29 +0,0 @@
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
- });
@@ -1,29 +0,0 @@
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
- })
@@ -1,26 +0,0 @@
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
- });