Skip to content

Commit 7fa366f

Browse files
authored
ref(profiling): Conditionally shim cjs globals (#13267)
The shims should only be applied if the globals are not present, else it results in double decl and a runtime error. The profiling SDK should gracefully handle env where the shims are already provided. I couldn't find a way to modify the shim as it is hardcoded in the plugin we are using so I went with the replace plugin approach and a placeholder value #poormansmacros.
1 parent 703b5d4 commit 7fa366f

File tree

5 files changed

+79
-7
lines changed

5 files changed

+79
-7
lines changed

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1168,7 +1168,7 @@ jobs:
11681168
- name: Set up Node
11691169
uses: actions/setup-node@v4
11701170
with:
1171-
node-version-file: 'dev-packages/e2e-tests/package.json'
1171+
node-version: 22
11721172
- name: Restore caches
11731173
uses: ./.github/actions/restore-cache
11741174
with:
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Because bundlers can now predetermine a static set of binaries we need to ensure those binaries
2+
// actually exists, else we risk a compile time error when bundling the package. This could happen
3+
// if we added a new binary in cpu_profiler.ts, but forgot to prebuild binaries for it. Because CI
4+
// only runs integration and unit tests, this change would be missed and could end up in a release.
5+
// Therefor, once all binaries are precompiled in CI and tests pass, run esbuild with bundle:true
6+
// which will copy all binaries to the outfile folder and throw if any of them are missing.
7+
import esbuild from 'esbuild';
8+
9+
console.log('Running build using esbuild version', esbuild.version);
10+
11+
esbuild.buildSync({
12+
platform: 'node',
13+
entryPoints: ['./index.ts'],
14+
outfile: './dist/index.shimmed.mjs',
15+
target: 'esnext',
16+
format: 'esm',
17+
bundle: true,
18+
loader: { '.node': 'copy' },
19+
banner: {
20+
js: `
21+
import { dirname } from 'node:path';
22+
import { fileURLToPath } from 'node:url';
23+
import { createRequire } from 'node:module';
24+
const require = createRequire(import.meta.url);
25+
const __filename = fileURLToPath(import.meta.url);
26+
const __dirname = dirname(__filename);
27+
`,
28+
},
29+
});

dev-packages/e2e-tests/test-applications/node-profiling/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
"private": true,
55
"scripts": {
66
"typecheck": "tsc --noEmit",
7-
"build": "node build.mjs",
8-
"test": "npm run build && node dist/index.js",
9-
"clean": "npx rimraf node_modules",
7+
"build": "node build.mjs && node build.shimmed.mjs",
8+
"test": "node dist/index.js && node --experimental-require-module dist/index.js && node dist/index.shimmed.mjs",
9+
"clean": "npx rimraf node_modules dist",
1010
"test:build": "npm run typecheck && npm run build",
1111
"test:assert": "npm run test"
1212
},
Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,49 @@
11
import commonjs from '@rollup/plugin-commonjs';
2-
import esmshim from '@rollup/plugin-esm-shim';
32
import { makeBaseNPMConfig, makeNPMConfigVariants } from '@sentry-internal/rollup-utils';
43

5-
export default makeNPMConfigVariants(
4+
export const ESMShim = `
5+
import cjsUrl from 'node:url';
6+
import cjsPath from 'node:path';
7+
import cjsModule from 'node:module';
8+
9+
if(typeof __filename === 'undefined'){
10+
globalThis.__filename = cjsUrl.fileURLToPath(import.meta.url);
11+
}
12+
13+
if(typeof __dirname === 'undefined'){
14+
globalThis.__dirname = cjsPath.dirname(__filename);
15+
}
16+
17+
if(typeof require === 'undefined'){
18+
globalThis.require = cjsModule.createRequire(import.meta.url);
19+
}
20+
`;
21+
22+
function makeESMShimPlugin(shim) {
23+
return {
24+
transform(code) {
25+
const SHIM_REGEXP = /\/\/ #START_SENTRY_ESM_SHIM[\s\S]*?\/\/ #END_SENTRY_ESM_SHIM/;
26+
return code.replace(SHIM_REGEXP, shim);
27+
},
28+
};
29+
}
30+
31+
const variants = makeNPMConfigVariants(
632
makeBaseNPMConfig({
733
packageSpecificConfig: {
834
output: { dir: 'lib', preserveModules: false },
9-
plugins: [commonjs(), esmshim()],
35+
plugins: [commonjs()],
1036
},
1137
}),
1238
);
39+
40+
for (const variant of variants) {
41+
if (variant.output.format === 'esm') {
42+
variant.plugins.push(makeESMShimPlugin(ESMShim));
43+
} else {
44+
// Remove the ESM shim comment
45+
variant.plugins.push(makeESMShimPlugin(''));
46+
}
47+
}
48+
49+
export default variants;

packages/profiling-node/src/cpu_profiler.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ import type {
1515
} from './types';
1616
import type { ProfileFormat } from './types';
1717

18+
// #START_SENTRY_ESM_SHIM
19+
// When building for ESM, we shim require to use createRequire and __dirname.
20+
// We need to do this because .node extensions in esm are not supported.
21+
// The comment below this line exists as a placeholder for where to insert the shim.
22+
// #END_SENTRY_ESM_SHIM
23+
1824
const stdlib = familySync();
1925
const platform = process.env['BUILD_PLATFORM'] || _platform();
2026
const arch = process.env['BUILD_ARCH'] || _arch();

0 commit comments

Comments
 (0)