This repository has been archived by the owner on Dec 12, 2024. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add transform to extract node config into separate file (#41)
Co-authored-by: Lukas Stracke <[email protected]>
- Loading branch information
Showing
16 changed files
with
593 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import path from 'path'; | ||
import url from 'url'; | ||
|
||
import { log } from '@clack/prompts'; | ||
import chalk from 'chalk'; | ||
|
||
import { runJscodeshift } from '../../utils/jscodeshift.js'; | ||
|
||
/** | ||
* @type {import('types').Transformer} | ||
*/ | ||
export default { | ||
name: 'Move @sentry/node config into instrument.js file', | ||
transform: async (files, options) => { | ||
if (options.sdk !== '@sentry/node') { | ||
// this transform only applies to the Node SDK | ||
return; | ||
} | ||
|
||
log.info(`Moving your Sentry config into a dedicated ${chalk.cyan('instrument.js')} file. | ||
In version 8, the order of importing and initializing Sentry matters a lot. | ||
We therefore move your Sentry initialization into a separate file and import it right on top of your file. | ||
This ensures correct ordering. | ||
More information: ${chalk.gray('https://docs.sentry.io/platforms/javascript/guides/node/#configure')}`); | ||
|
||
log.info( | ||
`${chalk.underline( | ||
'Important:' | ||
)} If you're using EcmaScript Modules (ESM) mode, you need to load the ${chalk.cyan( | ||
'instrument.js' | ||
)} file differently. | ||
Please read this guide: ${chalk.gray('https://docs.sentry.io/platforms/javascript/guides/node/install/esm')} | ||
After you've done that, remove import of ${chalk.cyan('instrument.js')} from your file(s). | ||
This only applies if you are actually using ESM natively with Node. | ||
If you use ${chalk.gray('import')} syntax but transpile to CommonJS (e.g. TypeScript), you're all set up already! | ||
` | ||
); | ||
|
||
const transformPath = path.join(path.dirname(url.fileURLToPath(import.meta.url)), './transform.cjs'); | ||
await runJscodeshift(transformPath, files, options); | ||
}, | ||
}; |
194 changes: 194 additions & 0 deletions
194
src/transformers/nodeInstrumentFile/nodeInstrumentFile.test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,194 @@ | ||
import { rmSync } from 'node:fs'; | ||
import { beforeEach } from 'node:test'; | ||
|
||
import { afterEach, describe, it, expect, vi } from 'vitest'; | ||
|
||
import { fileExists, getDirFileContent, getFixturePath, makeTmpDir } from '../../../test-helpers/testPaths.js'; | ||
|
||
import tracingConfigTransformer from './index.js'; | ||
|
||
describe('transformers | nodeInstrumentFile', () => { | ||
let tmpDir = ''; | ||
|
||
beforeEach(() => { | ||
vi.clearAllMocks(); | ||
}); | ||
|
||
afterEach(() => { | ||
if (tmpDir) { | ||
rmSync(tmpDir, { force: true, recursive: true }); | ||
tmpDir = ''; | ||
} | ||
}); | ||
|
||
it('has correct name', () => { | ||
expect(tracingConfigTransformer.name).toEqual('Move @sentry/node config into instrument.js file'); | ||
}); | ||
|
||
it('works with app without Sentry', async () => { | ||
tmpDir = makeTmpDir(getFixturePath('noSentry')); | ||
await tracingConfigTransformer.transform([tmpDir], { filePatterns: [], sdk: '@sentry/node' }); | ||
|
||
const actual1 = getDirFileContent(tmpDir, 'app.js'); | ||
expect(actual1).toEqual(getDirFileContent(`${process.cwd()}/test-fixtures/noSentry`, 'app.js')); | ||
}); | ||
|
||
it('works with browser SDK', async () => { | ||
tmpDir = makeTmpDir(getFixturePath('nodeInstrumentFile/browserApp')); | ||
await tracingConfigTransformer.transform([tmpDir], { filePatterns: [], sdk: '@sentry/node' }); | ||
|
||
const actual1 = getDirFileContent(tmpDir, 'app.js'); | ||
expect(actual1).toEqual( | ||
getDirFileContent(`${process.cwd()}/test-fixtures/nodeInstrumentFile/browserApp`, 'app.js') | ||
); | ||
expect(fileExists(tmpDir, 'instrument.js')).toBe(false); | ||
}); | ||
|
||
it('works with existing tracing.js', async () => { | ||
tmpDir = makeTmpDir(getFixturePath('nodeInstrumentFile/nodeAppTracingFile')); | ||
await tracingConfigTransformer.transform([tmpDir], { filePatterns: [], sdk: '@sentry/node' }); | ||
|
||
const actual1 = getDirFileContent(tmpDir, 'app.js'); | ||
const actual2 = getDirFileContent(tmpDir, 'tracing.js'); | ||
expect(actual1).toEqual( | ||
getDirFileContent(`${process.cwd()}/test-fixtures/nodeInstrumentFile/nodeAppTracingFile`, 'app.js') | ||
); | ||
expect(actual2).toEqual( | ||
getDirFileContent(`${process.cwd()}/test-fixtures/nodeInstrumentFile/nodeAppTracingFile`, 'tracing.js') | ||
); | ||
}); | ||
|
||
it('works with node SDK & simple import', async () => { | ||
tmpDir = makeTmpDir(getFixturePath('nodeInstrumentFile/nodeAppImport')); | ||
await tracingConfigTransformer.transform([tmpDir], { filePatterns: [], sdk: '@sentry/node' }); | ||
|
||
const actual1 = getDirFileContent(tmpDir, 'app.mjs'); | ||
expect(actual1).toEqual( | ||
`import "./instrument"; | ||
// do something now! | ||
console.log('Hello, World!'); | ||
` | ||
); | ||
expect(fileExists(tmpDir, 'instrument.mjs')).toBe(true); | ||
expect(getDirFileContent(tmpDir, 'instrument.mjs')).toEqual( | ||
`import * as Sentry from '@sentry/node'; | ||
Sentry.init({ | ||
dsn: 'https://example.com', | ||
});` | ||
); | ||
}); | ||
|
||
it('works with node SDK & other imports', async () => { | ||
tmpDir = makeTmpDir(getFixturePath('nodeInstrumentFile/nodeAppImports')); | ||
await tracingConfigTransformer.transform([tmpDir], { filePatterns: [], sdk: '@sentry/node' }); | ||
|
||
const actual1 = getDirFileContent(tmpDir, 'app.js'); | ||
expect(actual1).toEqual( | ||
`import "./instrument"; | ||
import { setTag } from '@sentry/node'; | ||
import { somethingElse } from 'other-package'; | ||
setTag('key', 'value'); | ||
// do something now! | ||
console.log('Hello, World!', somethingElse.doThis()); | ||
` | ||
); | ||
expect(fileExists(tmpDir, 'instrument.js')).toBe(true); | ||
expect(getDirFileContent(tmpDir, 'instrument.js')).toEqual( | ||
`import { init, getActiveSpan } from '@sentry/node'; | ||
import { something } from 'other-package'; | ||
init({ | ||
dsn: 'https://example.com', | ||
beforeSend(event) { | ||
event.extra.hasSpan = !!getActiveSpan(); | ||
event.extra.check = something(); | ||
return event; | ||
} | ||
});` | ||
); | ||
}); | ||
|
||
it('works with node SDK & simple require', async () => { | ||
tmpDir = makeTmpDir(getFixturePath('nodeInstrumentFile/nodeAppRequire')); | ||
await tracingConfigTransformer.transform([tmpDir], { filePatterns: [], sdk: '@sentry/node' }); | ||
|
||
const actual1 = getDirFileContent(tmpDir, 'app.js'); | ||
expect(actual1).toEqual( | ||
`require("./instrument"); | ||
// do something now! | ||
console.log('Hello, World!'); | ||
` | ||
); | ||
expect(fileExists(tmpDir, 'instrument.js')).toBe(true); | ||
expect(getDirFileContent(tmpDir, 'instrument.js')).toEqual( | ||
`const Sentry = require('@sentry/node'); | ||
Sentry.init({ | ||
dsn: 'https://example.com', | ||
});` | ||
); | ||
}); | ||
|
||
it('works with node SDK & other requires', async () => { | ||
tmpDir = makeTmpDir(getFixturePath('nodeInstrumentFile/nodeAppRequires')); | ||
await tracingConfigTransformer.transform([tmpDir], { filePatterns: [], sdk: '@sentry/node' }); | ||
|
||
const actual1 = getDirFileContent(tmpDir, 'app.js'); | ||
expect(actual1).toEqual( | ||
`require("./instrument"); | ||
const { | ||
setTag | ||
} = require('@sentry/node'); | ||
const { | ||
somethingElse | ||
} = require('other-package'); | ||
setTag('key', 'value'); | ||
// do something now! | ||
console.log('Hello, World!', somethingElse.doThis()); | ||
` | ||
); | ||
expect(fileExists(tmpDir, 'instrument.js')).toBe(true); | ||
expect(getDirFileContent(tmpDir, 'instrument.js')).toEqual( | ||
`const { | ||
init, | ||
getActiveSpan | ||
} = require('@sentry/node'); | ||
const { | ||
something | ||
} = require('other-package'); | ||
init({ | ||
dsn: 'https://example.com', | ||
beforeSend(event) { | ||
event.extra.hasSpan = !!getActiveSpan(); | ||
event.extra.check = something(); | ||
return event; | ||
} | ||
});` | ||
); | ||
}); | ||
|
||
it('works with node SDK & typescript', async () => { | ||
tmpDir = makeTmpDir(getFixturePath('nodeInstrumentFile/nodeAppTypescript')); | ||
await tracingConfigTransformer.transform([tmpDir], { filePatterns: [], sdk: '@sentry/node' }); | ||
|
||
const actual1 = getDirFileContent(tmpDir, 'app.ts'); | ||
expect(actual1).toEqual( | ||
`import "./instrument"; | ||
// do something now! | ||
console.log('Hello, World!'); | ||
` | ||
); | ||
expect(fileExists(tmpDir, 'instrument.ts')).toBe(true); | ||
expect(getDirFileContent(tmpDir, 'instrument.ts')).toEqual( | ||
`import * as Sentry from '@sentry/node'; | ||
Sentry.init({ | ||
dsn: 'https://example.com' as string, | ||
});` | ||
); | ||
}); | ||
}); |
Oops, something went wrong.