-
-
Notifications
You must be signed in to change notification settings - Fork 55
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(angular): Add Sentry setup in
main.ts
- Loading branch information
1 parent
a2b3649
commit b8544fa
Showing
3 changed files
with
231 additions
and
1 deletion.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ | ||
import type { Program } from '@babel/types'; | ||
|
||
// @ts-expect-error - magicast is ESM and TS complains about that. It works though | ||
import { builders, generateCode, type ProxifiedModule } from 'magicast'; | ||
|
||
export function updateAppEntryMod( | ||
originalAppModuleMod: ProxifiedModule<any>, | ||
dsn: string, | ||
selectedFeatures: { | ||
performance: boolean; | ||
replay: boolean; | ||
}, | ||
): ProxifiedModule<any> { | ||
originalAppModuleMod.imports.$add({ | ||
from: '@sentry/angular', | ||
imported: '*', | ||
local: 'Sentry', | ||
}); | ||
|
||
insertInitCall(originalAppModuleMod, dsn, selectedFeatures); | ||
|
||
return originalAppModuleMod; | ||
} | ||
|
||
export function insertInitCall( | ||
originalAppModuleMod: ProxifiedModule<any>, | ||
dsn: string, | ||
selectedFeatures: { | ||
performance: boolean; | ||
replay: boolean; | ||
}, | ||
): void { | ||
const initCallArgs = getInitCallArgs(dsn, selectedFeatures); | ||
const initCall = builders.functionCall('Sentry.init', initCallArgs); | ||
const originalAppModuleModAst = originalAppModuleMod.$ast as Program; | ||
|
||
const initCallInsertionIndex = getAfterImportsInsertionIndex( | ||
originalAppModuleModAst, | ||
); | ||
|
||
originalAppModuleModAst.body.splice( | ||
initCallInsertionIndex, | ||
0, | ||
// @ts-expect-error - string works here because the AST is proxified by magicast | ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument | ||
generateCode(initCall).code, | ||
); | ||
} | ||
|
||
export function getInitCallArgs( | ||
dsn: string, | ||
selectedFeatures: { | ||
performance: boolean; | ||
replay: boolean; | ||
}, | ||
): Record<string, unknown> { | ||
const initCallArgs = { | ||
dsn, | ||
} as Record<string, unknown>; | ||
|
||
if (selectedFeatures.replay || selectedFeatures.performance) { | ||
initCallArgs.integrations = []; | ||
|
||
if (selectedFeatures.performance) { | ||
// @ts-expect-error - Adding Proxified AST node to the array | ||
initCallArgs.integrations.push( | ||
builders.functionCall('Sentry.browserTracingIntegration'), | ||
); | ||
initCallArgs.tracesSampleRate = 1.0; | ||
} | ||
|
||
if (selectedFeatures.replay) { | ||
// @ts-expect-error - Adding Proxified AST node to the array | ||
initCallArgs.integrations.push( | ||
builders.functionCall('Sentry.replayIntegration'), | ||
); | ||
|
||
initCallArgs.replaysSessionSampleRate = 0.1; | ||
initCallArgs.replaysOnErrorSampleRate = 1.0; | ||
} | ||
} | ||
|
||
return initCallArgs; | ||
} | ||
|
||
/** | ||
* We want to insert the handleError function just after all imports | ||
*/ | ||
export function getAfterImportsInsertionIndex( | ||
originalEntryServerModAST: Program, | ||
): number { | ||
for (let x = originalEntryServerModAST.body.length - 1; x >= 0; x--) { | ||
if (originalEntryServerModAST.body[x].type === 'ImportDeclaration') { | ||
return x + 1; | ||
} | ||
} | ||
|
||
return 0; | ||
} |
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,86 @@ | ||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ | ||
|
||
// @ts-expect-error - magicast is ESM and TS complains about that. It works though | ||
import { loadFile, writeFile } from 'magicast'; | ||
|
||
import * as path from 'path'; | ||
|
||
// @ts-expect-error - clack is ESM and TS complains about that. It works though | ||
import clack from '@clack/prompts'; | ||
import chalk from 'chalk'; | ||
import { updateAppEntryMod } from './codemods/main'; | ||
|
||
export function hasSentryContent( | ||
fileName: string, | ||
fileContent: string, | ||
expectedContent = '@sentry/angular', | ||
): boolean { | ||
const includesContent = fileContent.includes(expectedContent); | ||
|
||
if (includesContent) { | ||
clack.log.warn( | ||
`File ${chalk.cyan( | ||
path.basename(fileName), | ||
)} already contains ${expectedContent}. | ||
Skipping adding Sentry functionality to ${chalk.cyan( | ||
path.basename(fileName), | ||
)}.`, | ||
); | ||
} | ||
|
||
return includesContent; | ||
} | ||
|
||
export async function initalizeSentryOnApplicationEntry( | ||
dsn: string, | ||
selectedFeatures: { | ||
performance: boolean; | ||
replay: boolean; | ||
}, | ||
): Promise<void> { | ||
const appEntryFilename = 'main.ts'; | ||
const appEntryPath = path.join(process.cwd(), 'src', appEntryFilename); | ||
|
||
const originalAppEntry = await loadFile(appEntryPath); | ||
|
||
if (hasSentryContent(appEntryPath, originalAppEntry.$code)) { | ||
return; | ||
} | ||
|
||
try { | ||
const updatedAppEntryMod = updateAppEntryMod( | ||
originalAppEntry, | ||
dsn, | ||
selectedFeatures, | ||
); | ||
|
||
await writeFile(updatedAppEntryMod.$ast, appEntryPath); | ||
} catch (error: unknown) { | ||
clack.log.error( | ||
`Error while adding Sentry to ${chalk.cyan(appEntryFilename)}`, | ||
); | ||
|
||
clack.log.info( | ||
chalk.dim( | ||
typeof error === 'object' && error != null && 'toString' in error | ||
? error.toString() | ||
: typeof error === 'string' | ||
? error | ||
: '', | ||
), | ||
); | ||
|
||
clack.log.warn( | ||
`Please refer to the documentation for manual setup: | ||
${chalk.underline( | ||
'https://docs.sentry.io/platforms/javascript/guides/angular/#configure', | ||
)}`, | ||
); | ||
|
||
return; | ||
} | ||
|
||
clack.log.success( | ||
`Successfully initialized Sentry on ${chalk.cyan(appEntryFilename)}`, | ||
); | ||
} |