Skip to content
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
3 changes: 2 additions & 1 deletion e2e-tests/tests/pnpm-workspace.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ describe('pnpm workspace', () => {
org: "${TEST_ARGS.ORG_SLUG}",
project: "${TEST_ARGS.PROJECT_SLUG}"
}), sveltekit()]
});"
});
"
`);
});

Expand Down
3 changes: 2 additions & 1 deletion e2e-tests/tests/sveltekit-tracing.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,8 @@ describe('Sveltekit with instrumentation and tracing', () => {
org: "${TEST_ARGS.ORG_SLUG}",
project: "${TEST_ARGS.PROJECT_SLUG}"
}), sveltekit()]
});"
});
"
`);
});

Expand Down
4 changes: 3 additions & 1 deletion src/react-native/javascript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { traceStep } from '../telemetry';
import { makeCodeSnippet, showCopyPasteInstructions } from '../utils/clack';
import { getFirstMatchedPath } from './glob';
import { RN_SDK_PACKAGE } from './react-native-wizard';
import { preserveTrailingNewline } from '../utils/ast-utils';

// @ts-expect-error - magicast is ESM and TS complains about that. It works though
import { generateCode, ProxifiedModule, parseModule } from 'magicast';
Expand Down Expand Up @@ -248,7 +249,8 @@ export async function wrapRootComponent() {

traceStep('add-sentry-wrap', () => {
try {
fs.writeFileSync(jsPath, generateCode(mod.$ast).code, 'utf-8');
const code = preserveTrailingNewline(js, generateCode(mod.$ast).code);
fs.writeFileSync(jsPath, code, 'utf-8');
clack.log.success(
`Added ${chalk.cyan('Sentry.wrap')} to ${chalk.cyan(jsRelativePath)}.`,
);
Expand Down
7 changes: 5 additions & 2 deletions src/react-router/codemods/react-router-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { parseModule, generateCode } from 'magicast';
import clack from '@clack/prompts';
import chalk from 'chalk';

import { findProperty } from '../../utils/ast-utils';
import { findProperty, preserveTrailingNewline } from '../../utils/ast-utils';

/**
* Extracts the ObjectExpression from various export patterns.
Expand Down Expand Up @@ -205,7 +205,10 @@ export default {
throw new Error('Failed to modify React Router config structure');
}

const code = generateCode(mod.$ast).code;
const code = preserveTrailingNewline(
configContent,
generateCode(mod.$ast).code,
);
await fs.promises.writeFile(configPath, code);

return { ssrWasChanged };
Expand Down
11 changes: 9 additions & 2 deletions src/react-router/codemods/vite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ import { parseModule, generateCode } from 'magicast';
import clack from '@clack/prompts';
import chalk from 'chalk';

import { hasSentryContent, findProperty } from '../../utils/ast-utils';
import {
hasSentryContent,
findProperty,
preserveTrailingNewline,
} from '../../utils/ast-utils';

/**
* Extracts ObjectExpression from function body.
Expand Down Expand Up @@ -199,7 +203,10 @@ export async function instrumentViteConfig(
throw new Error('Failed to modify Vite config structure');
}

const code = generateCode(mod.$ast).code;
const code = preserveTrailingNewline(
configContent,
generateCode(mod.$ast).code,
);
await fs.promises.writeFile(configPath, code);

return { wasConverted };
Expand Down
11 changes: 9 additions & 2 deletions src/sourcemaps/tools/vite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@ import {
SourceMapUploadToolConfigurationFunction,
SourceMapUploadToolConfigurationOptions,
} from './types';
import { findFile, hasSentryContent } from '../../utils/ast-utils';
import {
findFile,
hasSentryContent,
preserveTrailingNewline,
} from '../../utils/ast-utils';

import * as path from 'path';
import * as fs from 'fs';
Expand Down Expand Up @@ -163,7 +167,10 @@ export async function addVitePluginToConfig(
},
});

const code = generateCode(mod.$ast).code;
const code = preserveTrailingNewline(
viteConfigContent,
generateCode(mod.$ast).code,
);

await fs.promises.writeFile(viteConfigPath, code);

Expand Down
10 changes: 8 additions & 2 deletions src/sveltekit/sdk-setup/vite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ import * as recast from 'recast';
import x = recast.types;
import t = x.namedTypes;

import { hasSentryContent } from '../../utils/ast-utils';
import {
hasSentryContent,
preserveTrailingNewline,
} from '../../utils/ast-utils';
import { debug } from '../../utils/debug';
import { abortIfCancelled } from '../../utils/clack';
import type { ProjectInfo } from './types';
Expand Down Expand Up @@ -64,7 +67,10 @@ Skipping adding Sentry functionality to.`,

await modifyAndRecordFail(
async () => {
const code = generateCode(viteModule.$ast).code;
const code = preserveTrailingNewline(
viteConfigContent,
generateCode(viteModule.$ast).code,
);
await fs.promises.writeFile(viteConfigPath, code);
},
'write-file',
Expand Down
23 changes: 23 additions & 0 deletions src/utils/ast-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -329,3 +329,26 @@ export function findProperty(
p.key.name === name,
) as t.ObjectProperty | undefined; // Safe: predicate guarantees type
}

/**
* Preserves trailing newline from original content if present.
* Code generators like magicast/recast don't preserve trailing newlines,
* so this ensures we don't unnecessarily modify user files.
*
* @param originalContent - The original file content
* @param generatedCode - The code generated by AST transformation
* @returns The generated code with trailing newline preserved if original had one
*/
export function preserveTrailingNewline(
originalContent: string,
generatedCode: string,
): string {
const hadTrailingNewline = originalContent.endsWith('\n');
const hasTrailingNewline = generatedCode.endsWith('\n');

if (hadTrailingNewline && !hasTrailingNewline) {
return generatedCode + '\n';
}

return generatedCode;
}
20 changes: 12 additions & 8 deletions test/sourcemaps/tools/vite.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ export default defineConfig({
build: {
sourcemap: true
}
})`,
})
`,
],
[
'no build.sourcemap options',
Expand All @@ -62,10 +63,10 @@ export default defineConfig({
vue(),
],
build: {
test: 1,
test: 1,
}
})
`,
`,
`import { sentryVitePlugin } from "@sentry/vite-plugin";
export default defineConfig({
plugins: [vue(), sentryVitePlugin({
Expand All @@ -76,7 +77,8 @@ export default defineConfig({
test: 1,
sourcemap: true
}
})`,
})
`,
],
[
'keep sourcemap: "hidden"',
Expand All @@ -89,7 +91,7 @@ export default {
sourcemap: "hidden",
}
}
`,
`,
`import { sentryVitePlugin } from "@sentry/vite-plugin";
export default {
plugins: [vue(), sentryVitePlugin({
Expand All @@ -99,7 +101,8 @@ export default {
build: {
sourcemap: "hidden",
}
}`,
}
`,
],
[
'rewrite sourcemap: false to true',
Expand All @@ -114,7 +117,7 @@ const cfg = {
}

export default cfg;
`,
`,
`import { sentryVitePlugin } from "@sentry/vite-plugin";
const cfg = {
plugins: [vue(), sentryVitePlugin({
Expand All @@ -127,7 +130,8 @@ const cfg = {
}
}

export default cfg;`,
export default cfg;
`,
],
])(
'adds the plugin and enables source maps generation (%s)',
Expand Down
33 changes: 33 additions & 0 deletions test/utils/ast-utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
getOrSetObjectProperty,
hasSentryContent,
parseJsonC,
preserveTrailingNewline,
printJsonC,
setOrUpdateObjectProperty,
} from '../../src/utils/ast-utils';
Expand Down Expand Up @@ -221,6 +222,38 @@ describe('setOrUpdateObjectProperty', () => {
});
});

describe('preserveTrailingNewline', () => {
it('adds trailing newline if original had one but generated does not', () => {
const original = 'const foo = 1;\n';
const generated = 'const foo = 1;';
expect(preserveTrailingNewline(original, generated)).toBe(
'const foo = 1;\n',
);
});

it('does not add trailing newline if original did not have one', () => {
const original = 'const foo = 1;';
const generated = 'const foo = 1;';
expect(preserveTrailingNewline(original, generated)).toBe('const foo = 1;');
});

it('does not double trailing newline if both already have one', () => {
const original = 'const foo = 1;\n';
const generated = 'const foo = 1;\n';
expect(preserveTrailingNewline(original, generated)).toBe(
'const foo = 1;\n',
);
});

it('does not remove trailing newline if original did not have one but generated does', () => {
const original = 'const foo = 1;';
const generated = 'const foo = 1;\n';
expect(preserveTrailingNewline(original, generated)).toBe(
'const foo = 1;\n',
);
});
});

describe('parse and print JSON-C', () => {
it.each([
['simple JSON', "{'foo': 'bar'}"],
Expand Down
Loading