@contrast/esm-hooks 1.0.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/README.md ADDED
@@ -0,0 +1,21 @@
1
+ ### Using [ECMAScript Modules (ESM)](https://nodejs.org/docs/latest-v12.x/api/esm.html#esm_ecmascript_modules)
2
+
3
+ > NOTE: ECMAScript instrumentation is experimental and Contrast requires at least Node version 14.15.0 for support.
4
+
5
+ ECMAScript modules are the _new_ official standard format to package JavaScript
6
+ code for reuse. ES Modules are loaded with the `import module from 'module'`
7
+ syntax.
8
+
9
+ When instrumenting an application that utilizes ECMAScript Modules, use the
10
+ following method to start the application. This is the appropriate method for
11
+ instrumenting an application that uses CJS, ESM, or a combination of both.
12
+
13
+ ```
14
+ Usage: node --experimental-loader @contrast/protect/esm-loader.mjs index.mjs [agent arguments] -- [app arguments]
15
+
16
+ Options:
17
+
18
+ -h, --help output usage information
19
+ -V, --version output the version number
20
+ -c, --configFile <path> path to agent config file
21
+ ```
@@ -0,0 +1,27 @@
1
+ import path from 'path';
2
+ import parent from 'parent-package-json';
3
+
4
+ export default function getType(filename) {
5
+
6
+ let pathname = filename.pathname || filename;
7
+
8
+ if (pathname.startsWith('node:')) {
9
+ return 'builtin';
10
+ }
11
+ pathname = pathname.replace('file://', '');
12
+
13
+ let parentType = 'commonjs';
14
+ try {
15
+ parentType = parent(pathname).parse().type;
16
+ } catch (err) {
17
+ // Node assumes `commonjs ` if there's no `type` set in package.json
18
+ }
19
+
20
+ const ext = path.extname(pathname);
21
+ if (ext === '.mjs' || (ext === '.js' && parentType === 'module')) {
22
+ return 'module';
23
+ } else if (ext === '.cjs' || (ext === '.js' && parentType !== 'module')) {
24
+ return 'commonjs';
25
+ }
26
+ return 'unknown';
27
+ }
package/lib/index.mjs ADDED
@@ -0,0 +1,66 @@
1
+ import { promises as fs } from 'fs';
2
+ import { fileURLToPath } from 'url';
3
+ import initCore from '@contrast/core';
4
+ import getType from './get-file-type.mjs';
5
+
6
+ export default function (core = initCore()) {
7
+
8
+ const esmHooks = {
9
+
10
+ async getSource(url, context, defaultGetSource) {
11
+ const filename = fileURLToPath(url);
12
+ core.logger.trace('getSource %s', filename);
13
+ return defaultGetSource(url, context, defaultGetSource);
14
+ },
15
+
16
+ async transformSource(source, context, defaultTransformSource) {
17
+ let result;
18
+ const filename = fileURLToPath(context.url);
19
+ core.logger.trace('transformSource %s', filename);
20
+ try {
21
+ result = core.rewriter.rewrite(source.toString(), { filename, sourceType: 'module' });
22
+ return { source: result.code };
23
+ } catch (err) {
24
+ core.logger.error(
25
+ { err, result },
26
+ 'Failed to compile rewritten code for %s. compiling original code.',
27
+ filename,
28
+ );
29
+ return defaultTransformSource(source, context, defaultTransformSource);
30
+ }
31
+ },
32
+
33
+ async load(url, context, defaultLoad) {
34
+ const type = getType(url);
35
+
36
+ if (type === 'builtin' || type === 'unknown') {
37
+ core.logger.debug(
38
+ 'Skipping rewrite for %s module %s, loading original code',
39
+ type,
40
+ url
41
+ );
42
+ return defaultLoad(url, context, defaultLoad);
43
+ }
44
+
45
+ const filename = fileURLToPath(url);
46
+ core.logger.trace('load %s', filename);
47
+
48
+ let result;
49
+ try {
50
+ const source = await fs.readFile(filename, 'utf8');
51
+ result = core.rewriter.rewrite(source, { filename, sourceType: type === 'commonjs' ? 'script' : 'module' });
52
+ return { format: type, source: result.code };
53
+ } catch (err) {
54
+ core.logger.error(
55
+ { err, result },
56
+ 'Failed to load rewritten code for %s. loading original code.',
57
+ filename,
58
+ );
59
+ return defaultLoad(url, context, defaultLoad);
60
+ }
61
+ }
62
+ };
63
+
64
+ core.esmHooks = esmHooks;
65
+ return core.esmHooks;
66
+ }
package/package.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "@contrast/esm-hooks",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "description": "Support for loading and instrumenting ECMAScript modules",
6
+ "license": "UNLICENSED",
7
+ "author": "Contrast Security <nodejs@contrastsecurity.com> (https://www.contrastsecurity.com)",
8
+ "main": "lib/index.mjs",
9
+ "engines": {
10
+ "npm": ">= 8.4.0",
11
+ "node": ">= 14.15.0"
12
+ },
13
+ "scripts": {
14
+ "test": "../scripts/test.sh"
15
+ },
16
+ "dependencies": {
17
+ "@contrast/core": "^1.0.0",
18
+ "parent-package-json": "^2.0.1"
19
+ }
20
+ }