Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(metro): Use env for default Babel transformer path #4298

Merged
merged 2 commits into from
Nov 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@
> make sure you follow our [migration guide](https://docs.sentry.io/platforms/react-native/migration/) first.
<!-- prettier-ignore-end -->

## Unreleased

### Fixes

- Remove `.sentry` tmp directory and use environmental variables instead to save default Babel transformer path ([#4298](https://github.com/getsentry/sentry-react-native/pull/4298))
- This resolves concurrency issues when running multiple bundle processes

## 6.3.0-beta.1

### Features
Expand Down
7 changes: 2 additions & 5 deletions packages/core/src/js/tools/metroconfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import * as process from 'process';
import { env } from 'process';

import { enableLogger } from './enableLogger';
import { cleanDefaultBabelTransformerPath, saveDefaultBabelTransformerPath } from './sentryBabelTransformerUtils';
import { setSentryDefaultBabelTransformerPathEnv } from './sentryBabelTransformerUtils';
import { createSentryMetroSerializer, unstable_beforeAssetSerializationPlugin } from './sentryMetroSerializer';
import type { DefaultConfigOptions } from './vendor/expo/expoconfig';
export * from './sentryMetroSerializer';
Expand Down Expand Up @@ -143,10 +143,7 @@ export function withSentryBabelTransformer(config: MetroConfig): MetroConfig {
}

if (defaultBabelTransformerPath) {
saveDefaultBabelTransformerPath(defaultBabelTransformerPath);
process.on('exit', () => {
cleanDefaultBabelTransformerPath();
});
setSentryDefaultBabelTransformerPathEnv(defaultBabelTransformerPath);
}

return {
Expand Down
52 changes: 12 additions & 40 deletions packages/core/src/js/tools/sentryBabelTransformerUtils.ts
Original file line number Diff line number Diff line change
@@ -1,62 +1,34 @@
import { logger } from '@sentry/utils';
import * as fs from 'fs';
import * as path from 'path';
import * as process from 'process';

import type { BabelTransformer } from './vendor/metro/metroBabelTransformer';

/**
* Saves default Babel transformer path to the project root.
*/
export function saveDefaultBabelTransformerPath(defaultBabelTransformerPath: string): void {
try {
fs.mkdirSync(path.join(process.cwd(), '.sentry'), { recursive: true });
fs.writeFileSync(getDefaultBabelTransformerPath(), defaultBabelTransformerPath);
logger.debug('Saved default Babel transformer path');
} catch (e) {
// eslint-disable-next-line no-console
console.error('[Sentry] Failed to save default Babel transformer path:', e);
}
}
export const SENTRY_DEFAULT_BABEL_TRANSFORMER_PATH = 'SENTRY_DEFAULT_BABEL_TRANSFORMER_PATH';

/**
* Reads default Babel transformer path from the project root.
* Sets default Babel transformer path to the environment variables.
*/
export function readDefaultBabelTransformerPath(): string | undefined {
try {
return fs.readFileSync(getDefaultBabelTransformerPath()).toString();
} catch (e) {
// eslint-disable-next-line no-console
console.error('[Sentry] Failed to read default Babel transformer path:', e);
}
return undefined;
export function setSentryDefaultBabelTransformerPathEnv(defaultBabelTransformerPath: string): void {
process.env[SENTRY_DEFAULT_BABEL_TRANSFORMER_PATH] = defaultBabelTransformerPath;
logger.debug(`Saved default Babel transformer path ${defaultBabelTransformerPath}`);
}

/**
* Cleans default Babel transformer path from the project root.
* Reads default Babel transformer path from the environment variables.
*/
export function cleanDefaultBabelTransformerPath(): void {
try {
fs.unlinkSync(getDefaultBabelTransformerPath());
logger.debug('Cleaned default Babel transformer path');
} catch (e) {
// We don't want to fail the build if we can't clean the file
// eslint-disable-next-line no-console
console.error('[Sentry] Failed to clean default Babel transformer path:', e);
}
}

function getDefaultBabelTransformerPath(): string {
return path.join(process.cwd(), '.sentry/.defaultBabelTransformerPath');
export function getSentryDefaultBabelTransformerPathEnv(): string | undefined {
return process.env[SENTRY_DEFAULT_BABEL_TRANSFORMER_PATH];
}

/**
* Loads default Babel transformer from `@react-native/metro-config` -> `@react-native/metro-babel-transformer`.
*/
export function loadDefaultBabelTransformer(): BabelTransformer {
const defaultBabelTransformerPath = readDefaultBabelTransformerPath();
const defaultBabelTransformerPath = getSentryDefaultBabelTransformerPathEnv();
if (!defaultBabelTransformerPath) {
throw new Error('Default Babel Transformer Path not found in `.sentry` directory.');
throw new Error(
`Default Babel transformer path environment variable ${SENTRY_DEFAULT_BABEL_TRANSFORMER_PATH} is not set.`,
);
}

logger.debug(`Loading default Babel transformer from ${defaultBabelTransformerPath}`);
Expand Down
43 changes: 3 additions & 40 deletions packages/core/test/tools/metroconfig.test.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,5 @@
jest.mock('fs', () => {
return {
readFile: jest.fn(),
mkdirSync: jest.fn(),
writeFileSync: jest.fn(),
unlinkSync: jest.fn(),
};
});

import type { getDefaultConfig } from 'expo/metro-config';
import * as fs from 'fs';
import type { MetroConfig } from 'metro';
import * as path from 'path';
import * as process from 'process';

import {
Expand All @@ -19,6 +8,7 @@ import {
withSentryFramesCollapsed,
withSentryResolver,
} from '../../src/js/tools/metroconfig';
import { SENTRY_DEFAULT_BABEL_TRANSFORMER_PATH } from '../../src/js/tools/sentryBabelTransformerUtils';

type MetroFrame = Parameters<Required<Required<MetroConfig>['symbolicator']>['customizeFrame']>[0];

Expand Down Expand Up @@ -69,43 +59,16 @@ describe('metroconfig', () => {
},
);

test.each([
[{ transformer: { babelTransformerPath: 'babelTransformerPath' }, projectRoot: 'project/root' }],
[{ transformer: { babelTransformerPath: 'babelTransformerPath' } }],
])('save default babel transformer path to a file', () => {
test('save default babel transformer path to environment variable', () => {
const defaultBabelTransformerPath = '/default/babel/transformer';

withSentryBabelTransformer({
transformer: {
babelTransformerPath: defaultBabelTransformerPath,
},
projectRoot: 'project/root',
});

expect(fs.mkdirSync).toHaveBeenCalledWith(path.join(process.cwd(), '.sentry'), { recursive: true });
expect(fs.writeFileSync).toHaveBeenCalledWith(
path.join(process.cwd(), '.sentry/.defaultBabelTransformerPath'),
defaultBabelTransformerPath,
);
});

test('clean default babel transformer path file on exit', () => {
const processOnSpy: jest.SpyInstance = jest.spyOn(process, 'on');

const defaultBabelTransformerPath = 'defaultBabelTransformerPath';

withSentryBabelTransformer({
transformer: {
babelTransformerPath: defaultBabelTransformerPath,
},
projectRoot: 'project/root',
});

const actualExitHandler: () => void | undefined = processOnSpy.mock.calls[0][1];
actualExitHandler?.();

expect(processOnSpy).toHaveBeenCalledWith('exit', expect.any(Function));
expect(fs.unlinkSync).toHaveBeenCalledWith(path.join(process.cwd(), '.sentry/.defaultBabelTransformerPath'));
expect(process.env[SENTRY_DEFAULT_BABEL_TRANSFORMER_PATH]).toBe(defaultBabelTransformerPath);
});

test('return config with sentry babel transformer path', () => {
Expand Down
12 changes: 3 additions & 9 deletions packages/core/test/tools/sentryBabelTransformer.test.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
jest.mock('fs', () => {
return {
readFileSync: jest.fn(),
};
});
import * as process from 'process';

import * as fs from 'fs';
import { SENTRY_DEFAULT_BABEL_TRANSFORMER_PATH } from '../../src/js/tools/sentryBabelTransformerUtils';

// needs to be defined before sentryBabelTransformer is imported
// the transformer is created on import (side effect)
(fs.readFileSync as jest.Mock).mockReturnValue(require.resolve('./fixtures/mockBabelTransformer.js'));
process.env[SENTRY_DEFAULT_BABEL_TRANSFORMER_PATH] = require.resolve('./fixtures/mockBabelTransformer.js');

import * as SentryBabelTransformer from '../../src/js/tools/sentryBabelTransformer';
import type { BabelTransformerArgs } from '../../src/js/tools/vendor/metro/metroBabelTransformer';
Expand Down
Loading