Skip to content

Commit

Permalink
Address review comments.
Browse files Browse the repository at this point in the history
  • Loading branch information
onurtemizkan committed Dec 18, 2024
1 parent 9580fc3 commit e872236
Show file tree
Hide file tree
Showing 11 changed files with 99 additions and 56 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

## Unreleased

- feat: Add Angular Wizard ([#741](https://github.com/getsentry/sentry-wizard/pull/741))
- feat(nuxt): Add `import-in-the-middle` install step when using pnpm ([#727](https://github.com/getsentry/sentry-wizard/pull/727))
- fix(nuxt): Remove unused parameter in sentry-example-api template ([#734](https://github.com/getsentry/sentry-wizard/pull/734))
- feat: Add Angular Wizard ([#741](https://github.com/getsentry/sentry-wizard/pull/741))
- fix(nuxt): Remove option to downgrade override nitropack ([#744](https://github.com/getsentry/sentry-wizard/pull/744))

## 3.36.0
Expand Down
2 changes: 1 addition & 1 deletion e2e-tests/tests/angular-17.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ async function runWizardOnAngularProject(projectDir: string, integration: Integr
const sourcemapsConfigured = sourcemapsPrompted &&
(await wizardInstance.sendStdinAndWaitForOutput(
["./dist", KEYS.ENTER],
'Verify that your build tool is generating source maps.',
'Added a sentry:sourcemaps script to your package.json.',
), {
optional: true,
});
Expand Down
3 changes: 1 addition & 2 deletions e2e-tests/tests/angular-19.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { Integration } from "../../lib/Constants";
import { checkFileContents, checkFileExists, checkIfBuilds, checkIfRunsOnDevMode, checkIfRunsOnProdMode, checkPackageJson, cleanupGit, KEYS, revertLocalChanges, startWizardInstance } from "../utils";
import * as path from 'path';
import { TEST_ARGS } from "../utils";
import { option } from "yargs";

async function runWizardOnAngularProject(projectDir: string, integration: Integration) {
const wizardInstance = startWizardInstance(integration, projectDir);
Expand Down Expand Up @@ -43,7 +42,7 @@ async function runWizardOnAngularProject(projectDir: string, integration: Integr
const sourcemapsConfigured = sourcemapsPrompted &&
(await wizardInstance.sendStdinAndWaitForOutput(
["./dist", KEYS.ENTER],
'Verify that your build tool is generating source maps.',
'Added a sentry:sourcemaps script to your package.json.',
), {
optional: true,
});
Expand Down
75 changes: 53 additions & 22 deletions src/angular/angular-wizard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,25 @@ import chalk from 'chalk';
import type { WizardOptions } from '../utils/types';
import { traceStep, withTelemetry } from '../telemetry';
import {
abortIfCancelled,
confirmContinueIfNoOrDirtyGitRepo,
ensurePackageIsInstalled,
featureSelectionPrompt,
getOrAskForProjectData,
getPackageDotJson,
installPackage,
printWelcome,
runPrettierIfInstalled,
} from '../utils/clack-utils';
import { getPackageVersion, hasPackageInstalled } from '../utils/package-json';
import { gte, minVersion } from 'semver';
import { initalizeSentryOnAppModule, updateAppConfig } from './sdk-setup';
import { gte, minVersion, SemVer } from 'semver';
import {
initalizeSentryOnApplicationEntry,
updateAppConfig,
} from './sdk-setup';
import { addSourcemapEntryToAngularJSON } from './codemods/sourcemaps';
import { runSourcemapsWizard } from '../sourcemaps/sourcemaps-wizard';
import * as Sentry from '@sentry/node';

const MIN_SUPPORTED_ANGULAR_VERSION = '14.0.0';

Expand All @@ -38,7 +44,7 @@ async function runAngularWizardWithTelemetry(
options: WizardOptions,
): Promise<void> {
printWelcome({
wizardName: 'Sentry Remix Wizard',
wizardName: 'Sentry Angular Wizard',
promoCode: options.promoCode,
telemetryEnabled: options.telemetryEnabled,
});
Expand All @@ -49,24 +55,28 @@ async function runAngularWizardWithTelemetry(

await ensurePackageIsInstalled(packageJson, '@angular/core', 'Angular');

const installedAngularVersion = getPackageVersion(
'@angular/core',
packageJson,
);
let installedAngularVersion = getPackageVersion('@angular/core', packageJson);

if (!installedAngularVersion) {
clack.log.warn('Could not determine installed Angular version.');

return;
installedAngularVersion = await abortIfCancelled(
clack.text({
message: 'Please enter the installed Angular version:',
validate(value) {
if (!value) {
return 'Please enter the installed Angular version.';
}

if (!minVersion(value)) {
return `Invalid Angular version provided: ${value}`;
}
},
}),
);
}

const installedMinVersion = minVersion(installedAngularVersion);

if (!installedMinVersion) {
clack.log.warn('Could not determine minimum Angular version.');

return;
}
const installedMinVersion = minVersion(installedAngularVersion) as SemVer;

const isSupportedAngularVersion = gte(
installedMinVersion,
Expand All @@ -77,17 +87,29 @@ async function runAngularWizardWithTelemetry(
clack.log.warn(
`Angular version ${MIN_SUPPORTED_ANGULAR_VERSION} or higher is required.`,
);
clack.log.warn(
`Please refer to Sentry's version compatibility table for more information: ${chalk.underline(
'https://docs.sentry.io/platforms/javascript/guides/angular/#angular-version-compatibility',
)}`,
);

return;
}

const { selectedProject, authToken, sentryUrl, selfHosted } =
await getOrAskForProjectData(options, 'javascript-angular');

const sdkAlreadyInstalled = hasPackageInstalled(
'@sentry/angular',
packageJson,
);

Sentry.setTag('sdk-already-installed', sdkAlreadyInstalled);

await installPackage({
packageName: '@sentry/angular@^8',
packageNameDisplayLabel: '@sentry/angular',
alreadyInstalled: hasPackageInstalled('@sentry/angular', packageJson),
alreadyInstalled: sdkAlreadyInstalled,
});

const dsn = selectedProject.keys[0].dsn.public;
Expand All @@ -109,16 +131,19 @@ async function runAngularWizardWithTelemetry(
},
] as const);

await traceStep('Inject Sentry to Angular app config', async () => {
await initalizeSentryOnAppModule(dsn, selectedFeatures);
});
await traceStep(
'Initialize Sentry on Angular application entry point',
async () => {
await initalizeSentryOnApplicationEntry(dsn, selectedFeatures);
},
);

await traceStep('Update Angular project configuration', async () => {
await updateAppConfig(installedMinVersion, selectedFeatures.performance);
});

await traceStep('Setup for sourcemap uploads', async () => {
addSourcemapEntryToAngularJSON();
await addSourcemapEntryToAngularJSON();

if (!options.preSelectedProject) {
options.preSelectedProject = {
Expand Down Expand Up @@ -148,7 +173,13 @@ async function runAngularWizardWithTelemetry(
await runSourcemapsWizard(options, 'angular');
});

clack.log.success(
'Sentry has been successfully configured for your Angular project',
await traceStep('Run Prettier', async () => {
await runPrettierIfInstalled();
});

clack.outro(`
${chalk.green(
'Sentry has been successfully configured for your Angular project.',
)}`
);
}
20 changes: 11 additions & 9 deletions src/angular/codemods/app-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,7 @@ export function updateAppConfigMod(
function addSentryImport(originalAppConfigMod: ProxifiedModule<any>): void {
const imports = originalAppConfigMod.imports;
const hasSentryImport = imports.$items.some(
(item) =>
item.from === '@sentry/angular' &&
item.imported === '*' &&
item.local === 'Sentry',
(item) => item.from === '@sentry/angular',
);

if (!hasSentryImport) {
Expand All @@ -45,7 +42,7 @@ function addErrorHandlerImport(
): void {
const imports = originalAppConfigMod.imports;
const hasErrorHandler = imports.$items.some(
(item) => item.local === 'ErrorHandler',
(item) => item.local === 'ErrorHandler' && item.from === '@angular/core',
);

if (!hasErrorHandler) {
Expand All @@ -59,7 +56,9 @@ function addErrorHandlerImport(

function addRouterImport(originalAppConfigMod: ProxifiedModule<any>): void {
const imports = originalAppConfigMod.imports;
const hasRouter = imports.$items.some((item) => item.local === 'Router');
const hasRouter = imports.$items.some(
(item) => item.local === 'Router' && item.from === '@angular/router',
);

if (!hasRouter) {
imports.$add({
Expand All @@ -76,7 +75,8 @@ function addMissingImportsV19(
const imports = originalAppConfigMod.imports;

const hasProvideAppInitializer = imports.$items.some(
(item) => item.local === 'provideAppInitializer',
(item) =>
item.local === 'provideAppInitializer' && item.from === '@angular/core',
);

if (!hasProvideAppInitializer) {
Expand All @@ -87,7 +87,9 @@ function addMissingImportsV19(
});
}

const hasInject = imports.$items.some((item) => item.local === 'inject');
const hasInject = imports.$items.some(
(item) => item.local === 'inject' && item.from === '@angular/core',
);

if (!hasInject) {
imports.$add({
Expand All @@ -102,7 +104,7 @@ function addAppInitializer(originalAppConfigMod: ProxifiedModule<any>): void {
const imports = originalAppConfigMod.imports;

const hasAppInitializer = imports.$items.some(
(item) => item.local === 'APP_INITIALIZER',
(item) => item.local === 'APP_INITIALIZER' && item.from === '@angular/core',
);

if (!hasAppInitializer) {
Expand Down
2 changes: 1 addition & 1 deletion src/angular/codemods/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ 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 updateAppModuleMod(
export function updateAppEntryMod(
originalAppModuleMod: ProxifiedModule<any>,
dsn: string,
selectedFeatures: {
Expand Down
16 changes: 11 additions & 5 deletions src/angular/codemods/sourcemaps.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */

// @ts-ignore - clack is ESM and TS complains about that. It works though
import * as clack from '@clack/prompts';
import * as path from 'path';
import * as fs from 'fs';
import { configureAngularSourcemapGenerationFlow } from '../../sourcemaps/tools/angular';

export function addSourcemapEntryToAngularJSON(): void {
export async function addSourcemapEntryToAngularJSON(): Promise<void> {
const angularJsonPath = path.join(process.cwd(), 'angular.json');

const angularJSONFile = fs.readFileSync(angularJsonPath, 'utf-8');

const angularJson = JSON.parse(angularJSONFile);

if (!angularJson) {
throw new Error('Could not find in angular.json in your project');
await configureAngularSourcemapGenerationFlow();
}

const projects = Object.keys(angularJson.projects as Record<string, unknown>);

if (!projects.length) {
throw new Error('Could not find any projects in angular.json');
await configureAngularSourcemapGenerationFlow();
}

// Emit sourcemaps from all projects in angular.json
Expand All @@ -42,4 +44,8 @@ export function addSourcemapEntryToAngularJSON(): void {
}

fs.writeFileSync(angularJsonPath, JSON.stringify(angularJson, null, 2));

clack.log.info(
'Added sourcemap configuration to angular.json for all projects',
);
}
22 changes: 11 additions & 11 deletions src/angular/sdk-setup.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */

// @ts-expect-error - magicast is ESM and TS complains about that. It works though
import { loadFile, ProxifiedModule, writeFile } from 'magicast';
import { loadFile, type ProxifiedModule, 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 { updateAppModuleMod } from './codemods/main';
import { updateAppEntryMod } from './codemods/main';
import { updateAppConfigMod } from './codemods/app-config';
import type { SemVer } from 'semver';

Expand All @@ -33,33 +33,33 @@ Skipping adding Sentry functionality to ${chalk.cyan(
return includesContent;
}

export async function initalizeSentryOnAppModule(
export async function initalizeSentryOnApplicationEntry(
dsn: string,
selectedFeatures: {
performance: boolean;
replay: boolean;
},
): Promise<void> {
const appModuleFilename = 'main.ts';
const appModulePath = path.join(process.cwd(), 'src', appModuleFilename);
const appEntryFilename = 'main.ts';
const appEntryPath = path.join(process.cwd(), 'src', appEntryFilename);

const originalAppModule = await loadFile(appModulePath);
const originalAppEntry = await loadFile(appEntryPath);

if (hasSentryContent(appModulePath, originalAppModule.$code)) {
if (hasSentryContent(appEntryPath, originalAppEntry.$code)) {
return;
}

const updatedAppModuleMod = updateAppModuleMod(
originalAppModule,
const updatedAppEntryMod = updateAppEntryMod(
originalAppEntry,
dsn,
selectedFeatures,
);

await writeFile(updatedAppModuleMod.$ast, appModulePath);
await writeFile(updatedAppEntryMod.$ast, appEntryPath);

clack.log.success(
`Successfully initialized Sentry on your app module ${chalk.cyan(
appModuleFilename,
appEntryFilename,
)}`,
);
}
Expand Down
3 changes: 2 additions & 1 deletion src/sourcemaps/sourcemaps-wizard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,8 @@ async function startToolSetupFlow(
// Angular wizard handles the angular.json setup itself
!preSelectedTool || preSelectedTool !== 'angular'
? configureAngularSourcemapGenerationFlow
: undefined,
: // Not leaving this as undefined to avoid default. This is expected to be a no-op.
async () => Promise.resolve(),
);
break;
case 'nextjs':
Expand Down
4 changes: 2 additions & 2 deletions src/sourcemaps/tools/angular.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import clack from '@clack/prompts';
import chalk from 'chalk';
import { abortIfCancelled } from '../../utils/clack-utils';

const angularJsonTemplate = chalk.gray(`{
export const angularJsonTemplate = chalk.gray(`{
"projects": {
"your-project": {
"architect": {
Expand All @@ -22,7 +22,7 @@ export async function configureAngularSourcemapGenerationFlow(): Promise<void> {
`Enable generating source maps in your ${chalk.bold('angular.json')} file:`,
);

// Intentially logging directly to console here so that the code can be copied/pasted directly
// Intentionally logging directly to console here so that the code can be copied/pasted directly
// eslint-disable-next-line no-console
console.log(angularJsonTemplate);

Expand Down
Loading

0 comments on commit e872236

Please sign in to comment.