@contrast/rewriter 1.37.0 → 1.37.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.
- package/LICENSE +1 -1
- package/lib/cache.js +25 -2
- package/lib/index.js +96 -12
- package/lib/rewrite-is-deadzoned.js +1 -1
- package/package.json +7 -5
package/LICENSE
CHANGED
package/lib/cache.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright:
|
|
2
|
+
* Copyright: 2026 Contrast Security, Inc
|
|
3
3
|
* Contact: support@contrastsecurity.com
|
|
4
4
|
* License: Commercial
|
|
5
5
|
|
|
@@ -142,7 +142,7 @@ module.exports.Cache = class Cache {
|
|
|
142
142
|
}
|
|
143
143
|
|
|
144
144
|
/**
|
|
145
|
-
*
|
|
145
|
+
* Asynchronously looks up and returns the source map for a previously rewritten file.
|
|
146
146
|
* @param {string} filename
|
|
147
147
|
* @returns {Promise<string | undefined>}
|
|
148
148
|
*/
|
|
@@ -164,6 +164,29 @@ module.exports.Cache = class Cache {
|
|
|
164
164
|
}
|
|
165
165
|
}
|
|
166
166
|
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Synchronously looks up and returns the source map for a previously rewritten file.
|
|
170
|
+
* @param {string} filename
|
|
171
|
+
* @returns {string | undefined}
|
|
172
|
+
*/
|
|
173
|
+
readMapSync(filename) {
|
|
174
|
+
const filenameCached = this.getCachedFilename(filename);
|
|
175
|
+
const sourceMap = `${filenameCached}.map`;
|
|
176
|
+
try {
|
|
177
|
+
return fs.readFileSync(sourceMap, 'utf8');
|
|
178
|
+
} catch (err) {
|
|
179
|
+
// @ts-expect-error ts treats errors poorly.
|
|
180
|
+
if (err.code !== 'ENOENT') {
|
|
181
|
+
this.logger.warn(
|
|
182
|
+
{ err, filename, filenameCached, sourceMap },
|
|
183
|
+
'An unexpected error occurred finding source map.'
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
return undefined;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
167
190
|
/**
|
|
168
191
|
* Looks up and returns the string content of a previously rewritten file
|
|
169
192
|
* synchronously. Used when we need to block on cache lookups.
|
package/lib/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright:
|
|
2
|
+
* Copyright: 2026 Contrast Security, Inc
|
|
3
3
|
* Contact: support@contrastsecurity.com
|
|
4
4
|
* License: Commercial
|
|
5
5
|
|
|
@@ -14,12 +14,18 @@
|
|
|
14
14
|
*/
|
|
15
15
|
// @ts-check
|
|
16
16
|
'use strict';
|
|
17
|
-
|
|
18
17
|
const { arch, platform } = require('node:os');
|
|
19
|
-
|
|
18
|
+
// @ts-expect-error
|
|
19
|
+
const { SourceMapConsumer: SourceMapConsumerSync } = require('@contrast/synchronous-source-maps');
|
|
20
20
|
const { parseSync, transform, transformSync } = require('@swc/core');
|
|
21
|
+
// @ts-expect-error `@contrast/agent-swc-plugin` .d.ts file doesn't exist.
|
|
22
|
+
const { defaultRewriter, defaultUnwriter } = require('@contrast/agent-swc-plugin');
|
|
23
|
+
const { funcInfo } = require('@contrast/fn-inspect');
|
|
24
|
+
const { primordials } = require('@contrast/common');
|
|
21
25
|
const { Cache } = require('./cache');
|
|
22
26
|
|
|
27
|
+
const MAGIC_COMMENT_PREFIX = '//# sourceMappingURL=';
|
|
28
|
+
|
|
23
29
|
/**
|
|
24
30
|
* @typedef {Object} Core
|
|
25
31
|
* @prop {import('@contrast/common').AppInfo} appInfo
|
|
@@ -35,10 +41,6 @@ const { Cache } = require('./cache');
|
|
|
35
41
|
* @prop {boolean=} inject if true, injects ContrastMethods on the global object
|
|
36
42
|
* @prop {boolean=} minify if true, minifies the output when source maps are enabled
|
|
37
43
|
*/
|
|
38
|
-
|
|
39
|
-
// @ts-expect-error `@contrast/agent-swc-plugin` .d.ts file doesn't exist.
|
|
40
|
-
const { defaultRewriter, defaultUnwriter } = require('@contrast/agent-swc-plugin');
|
|
41
|
-
|
|
42
44
|
class Rewriter {
|
|
43
45
|
/**
|
|
44
46
|
* @param {Core} core
|
|
@@ -66,11 +68,10 @@ class Rewriter {
|
|
|
66
68
|
* @param {RewriteOpts} opts
|
|
67
69
|
* @returns {import('@swc/core').Options}
|
|
68
70
|
*/
|
|
69
|
-
|
|
71
|
+
swcOptions(opts) {
|
|
70
72
|
const nodeCfg = this.core.config.agent.node;
|
|
71
73
|
const sourceMaps = nodeCfg.source_maps.enable;
|
|
72
74
|
const minify = opts.minify && nodeCfg.source_maps.enable && nodeCfg.rewrite.minify;
|
|
73
|
-
|
|
74
75
|
return {
|
|
75
76
|
swcrc: false,
|
|
76
77
|
filename: opts.filename,
|
|
@@ -85,7 +86,7 @@ class Rewriter {
|
|
|
85
86
|
assess: this.modes.has('assess'),
|
|
86
87
|
inject: opts.inject,
|
|
87
88
|
}]],
|
|
88
|
-
cacheRoot:
|
|
89
|
+
cacheRoot: primordials.PathJoin(nodeCfg.rewrite.cache.path, '.swc'),
|
|
89
90
|
},
|
|
90
91
|
parser: {
|
|
91
92
|
syntax: 'ecmascript',
|
|
@@ -101,6 +102,45 @@ class Rewriter {
|
|
|
101
102
|
};
|
|
102
103
|
}
|
|
103
104
|
|
|
105
|
+
/**
|
|
106
|
+
* Make all of the source map paths absolute.
|
|
107
|
+
* Mutates Output result:
|
|
108
|
+
* - magic sourceMappingURL comment in result.code
|
|
109
|
+
* - result.map.sources array
|
|
110
|
+
* @param {RewriteOpts} rewriteOpts
|
|
111
|
+
* @param {import('@swc/core').Output} result
|
|
112
|
+
*/
|
|
113
|
+
adjustMap(rewriteOpts, result) {
|
|
114
|
+
if (rewriteOpts.filename && result.map) {
|
|
115
|
+
const map = JSON.parse(result.map);
|
|
116
|
+
const cachedFilename = this.cache.getCachedFilename(rewriteOpts.filename);
|
|
117
|
+
const absoluteCachedMapName = `${cachedFilename}.map`;
|
|
118
|
+
const origMapURL = `${primordials.PathBasename(rewriteOpts.filename)}.map`;
|
|
119
|
+
const comment = `${MAGIC_COMMENT_PREFIX}${origMapURL}`;
|
|
120
|
+
|
|
121
|
+
// 1) magic comment
|
|
122
|
+
// https://github.com/nodejs/node/blob/main/lib/internal/source_map/source_map_cache.js#L134-L136
|
|
123
|
+
const i = result.code.lastIndexOf(comment);
|
|
124
|
+
if (i >= 0) {
|
|
125
|
+
let code = primordials.StringPrototypeSubstr.call(result.code, 0, i);
|
|
126
|
+
code += `${MAGIC_COMMENT_PREFIX}${absoluteCachedMapName}`;
|
|
127
|
+
result.code = code;
|
|
128
|
+
}
|
|
129
|
+
// 2) map.sources array
|
|
130
|
+
for (let i = 0; i < map.sources?.length; i++) {
|
|
131
|
+
const resolved = primordials.PathResolve(rewriteOpts.filename, '..', map.sources[i]);
|
|
132
|
+
map.sources[i] = resolved;
|
|
133
|
+
}
|
|
134
|
+
// 3) map.file
|
|
135
|
+
if (map.file) {
|
|
136
|
+
map.file = cachedFilename;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// reasign modified map
|
|
140
|
+
result.map = primordials.JSONStringify(map);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
104
144
|
/**
|
|
105
145
|
* Rewrites the provided source code string asynchronously. this is used in an ESM
|
|
106
146
|
* context. CJS cannot use this because `require` is synchronous.
|
|
@@ -111,7 +151,10 @@ class Rewriter {
|
|
|
111
151
|
*/
|
|
112
152
|
async rewrite(content, opts = {}) {
|
|
113
153
|
this.logger.trace({ opts }, 'rewriting %s', opts.filename);
|
|
114
|
-
|
|
154
|
+
const rewriteOpts = this.swcOptions(opts);
|
|
155
|
+
const ret = await transform(content, rewriteOpts);
|
|
156
|
+
this.adjustMap(rewriteOpts, ret);
|
|
157
|
+
return ret;
|
|
115
158
|
}
|
|
116
159
|
|
|
117
160
|
/**
|
|
@@ -125,7 +168,10 @@ class Rewriter {
|
|
|
125
168
|
*/
|
|
126
169
|
rewriteSync(content, opts = {}) {
|
|
127
170
|
this.logger.trace({ opts }, 'rewriting %s', opts.filename);
|
|
128
|
-
|
|
171
|
+
const rewriteOpts = this.swcOptions(opts);
|
|
172
|
+
const result = transformSync(content, rewriteOpts);
|
|
173
|
+
this.adjustMap(rewriteOpts, result);
|
|
174
|
+
return result;
|
|
129
175
|
}
|
|
130
176
|
|
|
131
177
|
/**
|
|
@@ -149,6 +195,44 @@ class Rewriter {
|
|
|
149
195
|
sourceMaps: false,
|
|
150
196
|
}).code;
|
|
151
197
|
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* TODO implement for async scenarios using newer versions of source-map
|
|
201
|
+
* @param {function} func
|
|
202
|
+
*/
|
|
203
|
+
async funcInfo(func) {
|
|
204
|
+
throw new Error('funcInfo is not implemented');
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* @param {function} func
|
|
209
|
+
*/
|
|
210
|
+
funcInfoSync(func) {
|
|
211
|
+
/** @type any */
|
|
212
|
+
const fnInfo = funcInfo(func);
|
|
213
|
+
|
|
214
|
+
if (
|
|
215
|
+
!fnInfo?.file ||
|
|
216
|
+
!Number.isFinite(fnInfo.lineNumber) ||
|
|
217
|
+
!Number.isFinite(fnInfo.column) ||
|
|
218
|
+
!this.core.config.agent.node.source_maps.enable
|
|
219
|
+
) return fnInfo;
|
|
220
|
+
|
|
221
|
+
const map = this.cache.readMapSync(fnInfo.file);
|
|
222
|
+
if (!map) return fnInfo;
|
|
223
|
+
|
|
224
|
+
const orig = new SourceMapConsumerSync(map).originalPositionFor({
|
|
225
|
+
line: fnInfo.lineNumber + 1,
|
|
226
|
+
column: fnInfo.column,
|
|
227
|
+
});
|
|
228
|
+
if (orig) {
|
|
229
|
+
fnInfo.file = orig.source ? primordials.PathResolve(fnInfo.file, '..', orig.source) : fnInfo.file;
|
|
230
|
+
fnInfo.lineNumber = orig.line;
|
|
231
|
+
fnInfo.column = orig.column;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return fnInfo;
|
|
235
|
+
}
|
|
152
236
|
}
|
|
153
237
|
|
|
154
238
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contrast/rewriter",
|
|
3
|
-
"version": "1.37.
|
|
3
|
+
"version": "1.37.2",
|
|
4
4
|
"description": "A transpilation tool mainly used for instrumentation",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"author": "Contrast Security <nodejs@contrastsecurity.com> (https://www.contrastsecurity.com)",
|
|
@@ -21,10 +21,12 @@
|
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@contrast/agent-swc-plugin": "3.2.0",
|
|
24
|
-
"@contrast/common": "1.39.
|
|
25
|
-
"@contrast/config": "1.55.
|
|
26
|
-
"@contrast/core": "1.60.
|
|
27
|
-
"@contrast/
|
|
24
|
+
"@contrast/common": "1.39.1",
|
|
25
|
+
"@contrast/config": "1.55.1",
|
|
26
|
+
"@contrast/core": "1.60.1",
|
|
27
|
+
"@contrast/fn-inspect": "^5.0.2",
|
|
28
|
+
"@contrast/logger": "1.33.1",
|
|
29
|
+
"@contrast/synchronous-source-maps": "^1.1.5",
|
|
28
30
|
"@swc/core": "1.13.3"
|
|
29
31
|
}
|
|
30
32
|
}
|