@contrast/esm-hooks 2.4.1 → 2.5.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/lib/common.mjs CHANGED
@@ -12,51 +12,17 @@
12
12
  * engineered, modified, repackaged, sold, redistributed or otherwise used in a
13
13
  * way not consistent with the End User License Agreement.
14
14
  */
15
-
15
+ // @ts-check
16
16
  import { readdir } from 'node:fs/promises';
17
17
  import path from 'node:path';
18
+ import { fileURLToPath, pathToFileURL } from 'node:url';
18
19
 
19
20
  const REDIRECTS_PATH = './redirects';
20
21
 
21
22
  export const mappings = await makeMappings();
22
23
 
23
- // why do we need to fix paths? Because windows, node, and URLs don't get
24
- // along very well.
25
- //
26
- // the load() hook receives an URL. When the URL is converted into an URL object,
27
- // the pathname property will be '/C:/Users/.../redirects' on windows. And that
28
- // doesn't start with a drive letter, so when fsp.readdir() gets it, node decides
29
- // to add a drive letter: 'C:/C:/Users/.../redirects'. So we have to strip off the
30
- // the leading / so the drive letter is recognized and another isn't added. it seems
31
- // like a node bug (or possibly windows bug) but i don't have time to research the
32
- // standards to figure out what URL.pathname should be in a windows file: context.
33
- //
34
- // maybe this should be in the load() hook?
35
- //
36
- // should
37
- /**
38
- *
39
- * @param {string} path
40
- * @returns {string}
41
- */
42
- export function fixPath(p) {
43
- if (p.match(/^\/[A-Z]:/)) {
44
- p = p.slice(1);
45
- }
46
- return p;
47
- }
48
-
49
- import { getFileType } from './get-file-type.mjs';
50
- export { getFileType };
51
-
52
- async function makeMappings() {
53
- /**
54
- * @typedef { 'builtin' | 'commonjs' | 'module' } TargetType
55
- * @typedef {string} RequireSpecifier
56
- */
57
- /**
58
- * @type {Record<RequireSpecifier, {url: URL, target: TargetType}>}
59
- */
24
+ export async function makeMappings() {
25
+ /** @type {Record<string, string>} */
60
26
  const mappings = Object.create(null);
61
27
 
62
28
  // the name of the directory is the format of the target being loaded. so "cjs"
@@ -80,12 +46,15 @@ async function makeMappings() {
80
46
 
81
47
  // get an absolute path because reading the redirect file is going to be executed
82
48
  // in another context.
83
- const p = path.join(REDIRECTS_PATH, dir);
84
- const redirectDir = new URL(p, import.meta.url);
85
- const dirpath = fixPath(redirectDir.pathname);
49
+ const redirectsPath = path.resolve(
50
+ path.dirname(fileURLToPath(import.meta.url)),
51
+ REDIRECTS_PATH,
52
+ dir,
53
+ );
86
54
 
87
- await recursiveReaddir(dirpath);
55
+ await recursiveReaddir(redirectsPath);
88
56
 
57
+ /** @param {string} dirpath */
89
58
  // eslint-disable-next-line no-inner-declarations
90
59
  async function recursiveReaddir(dirpath) {
91
60
  const dirents = await readdir(dirpath, { withFileTypes: true });
@@ -101,13 +70,15 @@ async function makeMappings() {
101
70
  if (!dirent.name.endsWith('.mjs')) {
102
71
  continue;
103
72
  }
104
- // it a file that ends with .mjs, so it's a redirect file.
105
- const redirectURL = new URL(path.join(p, ...pathStack, dirent.name), import.meta.url);
73
+ // it's a file that ends with .mjs, so it's a redirect file.
74
+ const redirectURL = pathToFileURL(path.join(dirpath, dirent.name));
106
75
 
107
- // all redirects point to .mjs files; the target property specifies the type of the
108
- // the file that will be loaded by the .mjs. target. target is builtin, commonjs, or
109
- // module.
110
- // e.g., //'node-fetch': {url: new URL(`${p}/node-fetch.mjs`, import.meta.url), target: 'module'},
76
+ // all redirects point to .mjs files.
77
+ // the 'csi-flag' query specifies the type of file that will be loaded
78
+ // by the redirecting .mjs file:
79
+ // e.g.,
80
+ // 'node-fetch': 'file://.../redirects/esm/node-fetch.mjs?csi-flag=module`
81
+ // 'fs/promises': 'file://.../redirects/builtin/fs/promises.mjs?csi-flag=builtin`,
111
82
  //
112
83
  // there is a separate builtin directory because putting a colon in a filename doesn't work on windows. so
113
84
  // that's why the mapping below adds the `node:` prefix.
@@ -115,13 +86,26 @@ async function makeMappings() {
115
86
  if (pathStack.length) {
116
87
  name = `${pathStack.join('/')}/${name}`;
117
88
  }
89
+
90
+ // set flag to module or commonjs. i'm not sure this is needed but am keeping it
91
+ // in place until we have to implement esm-native module rewrites/wrapping. this
92
+ // is the point at which resolve() communicates to load().
93
+ //
94
+ // builtin's are probably the most likely to be loaded, so they're first.
95
+ // some tweaks might be needed when we start to patch esm-native modules
96
+ // in esm-native-code:
97
+ // https://nodejs.org/docs/latest-v20.x/api/esm.html#builtin-modules
98
+ // https://nodejs.org/docs/latest-v20.x/api/module.html#modulesyncbuiltinesmexports
118
99
  if (dir === 'builtin') {
119
- mappings[name] = { url: redirectURL, target: 'builtin' };
120
- mappings[`node:${name}`] = { url: redirectURL, target: 'builtin' };
100
+ redirectURL.searchParams.set('csi-flag', 'builtin');
101
+ mappings[name] = redirectURL.href;
102
+ mappings[`node:${name}`] = redirectURL.href;
121
103
  } else if (dir === 'cjs') {
122
- mappings[name] = { url: redirectURL, target: 'commonjs' };
104
+ redirectURL.searchParams.set('csi-flag', 'commonjs');
105
+ mappings[name] = redirectURL.href;
123
106
  } else if (dir === 'esm') {
124
- mappings[name] = { url: redirectURL, target: 'module' };
107
+ redirectURL.searchParams.set('csi-flag', 'module');
108
+ mappings[name] = redirectURL.href;
125
109
  } else {
126
110
  throw new Error(`target type ${dir} not yet implemented`);
127
111
  }
@@ -131,6 +115,3 @@ async function makeMappings() {
131
115
 
132
116
  return mappings;
133
117
  }
134
-
135
- // useful for tests
136
- export { makeMappings };
@@ -12,11 +12,11 @@
12
12
  * engineered, modified, repackaged, sold, redistributed or otherwise used in a
13
13
  * way not consistent with the End User License Agreement.
14
14
  */
15
- import fs from 'node:fs';
16
- import path from 'path';
17
- import M from 'module';
18
-
19
- import { findPackageJsonSync } from '@contrast/find-package-json';
15
+ import { readFile } from 'node:fs/promises';
16
+ import path from 'node:path';
17
+ import M from 'node:module';
18
+ import { fileURLToPath } from 'node:url';
19
+ import { findPackageJson } from '@contrast/find-package-json';
20
20
 
21
21
  const isBuiltin = M.isBuiltin || function(pathname) {
22
22
  if (pathname.startsWith('node:')) {
@@ -25,30 +25,26 @@ const isBuiltin = M.isBuiltin || function(pathname) {
25
25
  return M.builtinModules.includes(pathname);
26
26
  };
27
27
 
28
+ /** @typedef { 'builtin' | 'commonjs' | 'module' } Format */
29
+
28
30
  /**
29
- *
30
- * @param {string} path
31
- * @returns {string}
31
+ * @param {string | URL} filename
32
+ * @param {import('@contrast/find-package-json').Options["stopAt"]=} stopAt
33
+ * @returns {Promise<Format | null>}
32
34
  */
33
- export function fixPath(p) {
34
- if (p.match(/^(\\|\/)[A-Za-z]:/)) {
35
- p = p.slice(1);
35
+ export async function getFileType(filename, stopAt) {
36
+ try {
37
+ filename = fileURLToPath(filename);
38
+ } catch (err) {
39
+ // already a path or node: url
36
40
  }
37
- return p;
38
- }
39
41
 
40
- export function getFileType(filename) {
41
- let pathname = filename.pathname || filename;
42
- // presumes 16.17+
43
- if (isBuiltin(pathname)) {
42
+ if (isBuiltin(filename)) {
44
43
  return 'builtin';
45
44
  }
46
45
 
47
- pathname = pathname.replace('file://', '');
48
-
49
- // if the file extension specifies the type, there's no need to do extra
50
- // IO.
51
- const ext = path.extname(pathname);
46
+ // if the file extension specifies the type, there's no need to do extra IO.
47
+ const ext = path.extname(filename);
52
48
  if (ext === '.mjs') {
53
49
  return 'module';
54
50
  } else if (ext === '.cjs') {
@@ -58,14 +54,12 @@ export function getFileType(filename) {
58
54
  // Node assumes `commonjs` if `type` is not set in package.json
59
55
  let parentType = 'commonjs';
60
56
  try {
61
- // should this have stopAt set at the application root? do we know what the
62
- // root of the application is?
63
- const pkg = findPackageJsonSync({ cwd: fixPath(path.dirname(pathname)) });
57
+ const pkg = await findPackageJson({ cwd: filename, stopAt });
64
58
  if (pkg) {
65
- let json = fs.readFileSync(pkg, 'utf8');
66
- json = JSON.parse(json);
67
- if (json.type) {
68
- parentType = json.type;
59
+ const json = await readFile(pkg, 'utf8');
60
+ const { type } = JSON.parse(json);
61
+ if (type) {
62
+ parentType = type;
69
63
  }
70
64
  }
71
65
  } catch (err) {
package/lib/hooks.mjs CHANGED
@@ -13,29 +13,41 @@
13
13
  * way not consistent with the End User License Agreement.
14
14
  */
15
15
 
16
- import { readFile as rf } from 'node:fs/promises';
17
16
  import * as process from 'node:process';
18
- import { fixPath, getFileType, mappings } from './common.mjs';
17
+ import { fileURLToPath, pathToFileURL } from 'node:url';
18
+ import { mappings } from './common.mjs';
19
+ import { getFileType } from './get-file-type.mjs';
19
20
  import { default as initLoaderAgent } from './loader-agent.mjs';
20
21
  const [major, minor] = process.versions.node.split('.').map(it => +it);
21
22
  const isLT16_12 = major < 16 || (major === 16 && minor < 12);
22
23
 
23
- const readFile = rf;
24
+ /**
25
+ * @typedef {Object} ResolveResult
26
+ * @prop {string=} format A hint to the load hook (it might be ignored) 'builtin' | 'commonjs' | 'json' | 'module' | 'wasm'
27
+ * @prop {Object=} importAssertions The import assertions to use when caching the module (optional; if excluded the input will be used) (before v21)
28
+ * @prop {Object=} importAttributes The import attributes to use when caching the module (optional; if excluded the input will be used) (after v21)
29
+ * @prop {boolean=} shortCircuit A signal that this hook intends to terminate the chain of `resolve` hooks. Default: `false`
30
+ * @prop {string} url The absolute URL to which this input resolves
31
+ */
32
+
33
+ /**
34
+ * @typedef {Object} LoadResult
35
+ * @prop {string} format
36
+ * @prop {boolean=} shortCircuit A signal that this hook intends to terminate the chain of resolve hooks. Default: `false`
37
+ * @prop {string | ArrayBuffer | TypedArray} source The source for Node.js to evaluate
38
+ */
24
39
 
25
40
  /**
26
41
  * Agent instance with minimum footprint that handles functionality related esm module loading.
27
42
  * - We handles redirects to force require.
28
43
  * - Module rewriting via exported load hook
44
+ * @type {import('./loader-agent.mjs').LoaderAgent=}
29
45
  */
30
46
  let loaderAgent;
31
47
 
32
48
  /**
33
- * @param {{
34
- * modes: string[],
35
- * port: import('node:worker_threads').MessagePort,
36
- * appInfo: import('@contrast/common').AppInfo,
37
- * agentVersion: string,
38
- * }} data
49
+ * Module.register callback
50
+ * @param {import('./loader-agent.mjs').InitData} data
39
51
  */
40
52
  async function initialize(data = {}) {
41
53
  loaderAgent = initLoaderAgent(data);
@@ -43,116 +55,127 @@ async function initialize(data = {}) {
43
55
  await loaderAgent.install();
44
56
  }
45
57
 
58
+ /**
59
+ * @param {string} specifier
60
+ * @param {Object} context
61
+ * @param {string[]} context.conditions Export conditions of the relevant package.json
62
+ * @param {Object=} context.importAssertions An object whose key-value pairs represent the assertions for the module to import (before v21)
63
+ * @param {Object=} context.importAttributes An object whose key-value pairs represent the attributes for the module to import (after v21)
64
+ * @param {string=} context.parentURL The module importing this one, or undefined if this is the Node.js entry point
65
+ * @param {(specifier, context) => Promise<ResolveResult>} nextResolve The subsequent resolve hook in the chain, or the Node.js default resolve hook after the last user-supplied resolve hook
66
+ * @returns {Promise<ResolveResult>}
67
+ */
46
68
  async function resolve(specifier, context, nextResolve) {
47
69
  loaderAgent?.esmHooks?.debugEsmResolve?.(specifier);
48
70
 
71
+ if (!loaderAgent?.enable) {
72
+ return protectedNextResolve(specifier, context, nextResolve);
73
+ }
74
+
49
75
  let isFlaggedToPatch = false;
50
76
  if (context.parentURL) {
51
- isFlaggedToPatch = context.parentURL.endsWith('csi-flag=', context.parentURL.length - 1);
77
+ isFlaggedToPatch = new URL(context.parentURL).searchParams.has('csi-flag');
52
78
  }
53
79
 
54
- if (loaderAgent?.enable && !isFlaggedToPatch && specifier in mappings) {
55
- // eslint-disable-next-line prefer-const
56
- let { url, format, target } = mappings[specifier];
57
- // set flag to module or commonjs. i'm not sure this is needed but am keeping it
58
- // in place until we have to implement esm-native module rewrites/wrapping. this
59
- // is the point at which resolve() communicates to load().
60
- //
61
- // builtin's are probably the most likely to be loaded, so they're first.
62
- // some tweaks might be needed when we start to patch esm-native modules
63
- // in esm-native-code:
64
- // https://nodejs.org/docs/latest-v20.x/api/esm.html#builtin-modules
65
- // https://nodejs.org/docs/latest-v20.x/api/module.html#modulesyncbuiltinesmexports
66
- if (target === 'builtin') {
67
- url = `${url.href}?csi-flag=m`;
68
- format = 'module';
69
- } else if (target === 'commonjs') {
70
- url = `${url.href}?csi-flag=c`;
71
- format = getFileType(url) || format || 'commonjs';
72
- } else if (target === 'module') {
73
- url = `${url.href}?csi-flag=m`;
74
- format = getFileType(url) || format || 'module';
75
- } else {
76
- throw new Error(`unexpected target ${target} for ${specifier}`);
77
- }
78
-
80
+ if (!isFlaggedToPatch && specifier in mappings) {
79
81
  return {
80
- url,
81
- format,
82
+ url: mappings[specifier],
83
+ format: 'module',
82
84
  shortCircuit: true,
83
85
  };
84
86
  }
85
87
 
86
- return protectedNextResolve(nextResolve, specifier, context);
88
+ return protectedNextResolve(specifier, context, nextResolve);
87
89
  }
88
90
 
91
+ /**
92
+ * @param {string} url The URL returned by the resolve chain
93
+ * @param {Object} context
94
+ * @param {string[]} context.conditions Export conditions of the relevant package.json
95
+ * @param {string=} context.format The format optionally supplied by the resolve hook chain
96
+ * @param {Object=} context.importAssertions (before v21)
97
+ * @param {Object=} context.importAttributes (after v21)
98
+ * @param {(url, context) => Promise<LoadResult>} nextLoad The subsequent load hook in the chain, or the Node.js default load hook after the last user-supplied load hook
99
+ * @returns {Promise<LoadResult>}
100
+ */
89
101
  async function load(url, context, nextLoad) {
90
102
  loaderAgent?.esmHooks?.debugEsmLoad?.(url);
91
103
 
92
104
  const urlObject = new URL(url);
105
+ const type = await getFileType(url, loaderAgent?.appInfo.app_dir);
93
106
 
94
- if (loaderAgent?.enable && urlObject.searchParams.has('csi-flag')) {
95
- const { pathname } = urlObject;
96
- const source = await readFile(fixPath(pathname), 'utf8');
97
- return {
98
- source,
99
- format: 'module',
100
- shortCircuit: true,
101
- };
107
+ // if it's not a builtin or a flagged file, it needs to be rewritten.
108
+ // if it's not an es module it will be rewritten by the require hooks.
109
+ if (!loaderAgent?.enable || urlObject.searchParams.has('csi-flag') || type !== 'module') {
110
+ return nextLoad(url, context);
102
111
  }
103
112
 
104
- if (urlObject.pathname.endsWith('.node')) {
105
- const metaUrl = JSON.stringify(url);
106
- const addon = urlObject.pathname;
113
+ const filename = fileURLToPath(url);
114
+
115
+ // Handles the event that other hooks redirect an import to a `.node` addon.
116
+ if (filename.endsWith('.node')) {
107
117
  return {
108
- source: `require("node:module").createRequire(${metaUrl})("${addon}")`,
118
+ source: `module.exports = require("${filename}");`,
109
119
  format: 'commonjs',
110
120
  shortCircuit: true,
111
121
  };
112
122
  }
113
123
 
114
- // if it's not a builtin, a .node addon, or a flagged file, it needs to be
115
- // rewritten if it's a module.
116
- if (urlObject.pathname.match(/(.js|.mjs|.cjs)$/)) {
117
- // if it's not a module it will be rewritten by the require hooks.
118
- const type = getFileType(urlObject);
119
- if (type !== 'module') {
120
- return nextLoad(url, context);
121
- }
124
+ if (loaderAgent.config.agent.node.rewrite.cache.enable) {
125
+ const cached = await loaderAgent.rewriter.cache.find(filename);
122
126
 
123
- const filename = fixPath(urlObject.pathname);
124
- const source = await readFile(filename, 'utf8');
125
- let result;
126
-
127
- if (loaderAgent?.enable) {
128
- const rewriteOptions = {
129
- filename,
130
- isModule: type === 'module',
131
- inject: true,
132
- wrap: type !== 'module', // cannot wrap modules
133
- };
134
- result = await loaderAgent.rewriter.rewrite(source, rewriteOptions);
135
-
136
- if (process.env.CSI_EXPOSE_CORE) {
137
- // only do this in testing scenarios (todo: compose this functionality into test agents instead)
138
- loaderAgent.esmHooks.portClient.sendStatus('rewriting file during ESM load hook', rewriteOptions);
139
- }
127
+ if (cached) {
128
+ return nextLoad(pathToFileURL(cached).href, context);
140
129
  }
130
+ }
141
131
 
142
- return {
143
- source: result?.code || source,
144
- format: type || 'commonjs', // don't know what else to do here. log?
145
- shortCircuit: true,
146
- };
132
+ /** @type {import('@contrast/rewriter').RewriteOpts} */
133
+ const rewriteOptions = {
134
+ filename,
135
+ isModule: true,
136
+ inject: true,
137
+ wrap: false, // cannot wrap modules
138
+ trim: false,
139
+ };
140
+
141
+ const { source } = await nextLoad(url, context);
142
+ const rewriteResult = await loaderAgent.rewriter.rewrite(
143
+ source.toString(), // might be a Buffer
144
+ rewriteOptions
145
+ );
146
+
147
+ if (process.env.CSI_EXPOSE_CORE) {
148
+ // only do this in testing scenarios (todo: compose this functionality into test agents instead)
149
+ loaderAgent.esmHooks.portClient.sendStatus('rewriting file during ESM load hook', rewriteOptions);
147
150
  }
148
151
 
149
- // this never gets called, so hmmm.
150
- return nextLoad(url, context);
152
+ if (loaderAgent.config.agent.node.rewrite.cache.enable) {
153
+ loaderAgent.rewriter.cache.write(filename, rewriteResult);
154
+ }
155
+
156
+ return {
157
+ source: rewriteResult.code,
158
+ format: 'module',
159
+ shortCircuit: true,
160
+ };
151
161
  }
152
162
 
153
- async function protectedNextResolve(nextResolve, specifier, context) {
163
+ /**
164
+ * from https://github.com/iambumblehead/esmock/blob/main/src/esmockLoader.js#L89
165
+ *
166
+ * new versions of node: when multiple loaders are used and context
167
+ * is passed to nextResolve, the process crashes in a recursive call
168
+ * see: /esmock/issues/#48
169
+ *
170
+ * old versions of node: if context.parentURL is defined, and context
171
+ * is not passed to nextResolve, the tests fail
172
+ *
173
+ * later versions of node v16 include 'node-addons'
174
+ * @type {typeof resolve}
175
+ */
176
+ async function protectedNextResolve(specifier, context, nextResolve) {
154
177
  if (context.parentURL) {
155
- if (context.conditions.at(-1) === 'node-addons' || context.importAssertions || isLT16_12) {
178
+ if (context.conditions.at(-1) === 'node-addons' || context.importAssertions || context.importAttributes || isLT16_12) {
156
179
  return nextResolve(specifier, context);
157
180
  }
158
181
  }
package/lib/index.mjs CHANGED
@@ -16,7 +16,6 @@
16
16
  import Module from 'node:module';
17
17
  import { MessageChannel } from 'node:worker_threads';
18
18
  import { default as debugMethods } from './debug-methods.mjs';
19
- import { initialize, load, resolve } from './hooks.mjs';
20
19
  import { default as portClient } from './post-message/main-client.mjs';
21
20
 
22
21
  export default function init(core) {
@@ -26,7 +25,6 @@ export default function init(core) {
26
25
  } = new MessageChannel();
27
26
 
28
27
  const esmHooks = core.esmHooks = {
29
- hooks: { resolve, load }, // remove when all LTS versions support Module.register
30
28
  getActiveModes() {
31
29
  const modes = [];
32
30
  if (core.config.getEffectiveValue('assess.enable')) {
@@ -53,12 +51,14 @@ export default function init(core) {
53
51
  // Instantiate loader agent via register (if available) or manually.
54
52
  // we can simplify when all LTS versions support register
55
53
  if (Module.register) {
56
- await Module.register(new URL('./hooks.mjs', import.meta.url).href, {
54
+ await Module.register('./hooks.mjs', import.meta.url, {
57
55
  data,
58
56
  transferList: [workerPort],
59
57
  });
60
58
  } else {
59
+ const { initialize, resolve, load } = await import('./hooks.mjs');
61
60
  await initialize(data);
61
+ esmHooks.hooks = { resolve, load };
62
62
  }
63
63
 
64
64
  // these have side-effects, so compose during installation phase
@@ -19,18 +19,34 @@ import { default as debugMethods } from './debug-methods.mjs';
19
19
  import { default as portClient } from './post-message/loader-client.mjs';
20
20
  const require = Module.createRequire(import.meta.url);
21
21
 
22
- // TODO: language
22
+ /**
23
+ * @typedef {Object} InitData
24
+ * @prop {import('@contrast/rewriter').Mode[]} modes
25
+ * @prop {import('node:worker_threads').MessagePort} port
26
+ * @prop {import('@contrast/common').AppInfo} appInfo
27
+ * @prop {string} agentVersion
28
+ */
29
+
30
+ /**
31
+ * @typedef {Object} LoaderAgent
32
+ * @prop {import('@contrast/common').Messages} messages
33
+ * @prop {import('@contrast/common').AppInfo} appInfo
34
+ * @prop {string} agentVersion
35
+ * @prop {boolean} enable
36
+ * @prop {Record<never, never>} esmHooks
37
+ * @prop {import('@contrast/config').Config} config
38
+ * @prop {import('@contrast/logger').Logger} logger
39
+ * @prop {import('@contrast/rewriter').Rewriter} rewriter
40
+ */
41
+
23
42
  const ERROR_MESSAGE = 'An error prevented the Contrast agent from initializing in the loader thread.';
24
43
 
25
44
  /**
26
- * @param {{
27
- * modes: string[],
28
- * port: import('node:worker_threads').MessagePort,
29
- * appInfo: import('@contrast/common').AppInfo,
30
- * agentVersion: string,
31
- * }} data
45
+ * @param {InitData} data
46
+ * @returns {LoaderAgent}
32
47
  */
33
48
  export default function init({ appInfo, agentVersion, port, modes }) {
49
+ /** @type {LoaderAgent} */
34
50
  const core = {
35
51
  appInfo,
36
52
  agentVersion,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrast/esm-hooks",
3
- "version": "2.4.1",
3
+ "version": "2.5.0",
4
4
  "type": "module",
5
5
  "description": "Support for loading and instrumenting ECMAScript modules",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -19,8 +19,8 @@
19
19
  "test": "../scripts/test.sh"
20
20
  },
21
21
  "dependencies": {
22
- "@contrast/common": "1.19.0",
23
- "@contrast/core": "1.30.0",
22
+ "@contrast/common": "1.20.0",
23
+ "@contrast/core": "1.31.0",
24
24
  "@contrast/find-package-json": "^1.0.0"
25
25
  }
26
26
  }