Skip to content

Commit ba80f79

Browse files
Lms24mydea
andauthored
feat(sveltekit): Update @sentry/vite-plugin to 2.x and adjust options API (#10813)
This PR updates `@sentry/vite-plugin` used in the SvelteKit SDK from version 0.6.x to the latest 2.x version. In line with this change, we now also decoupled the public API of the vite plugin from the SvelteKit SDK public API, meaning we can bump to a higher plugin major version while we stay in the same SDK major version. Concrete changes: - We now use DebugId-based sourcemap upload 🎉 - Due to architecture changes in `@sentry/vite-plugin`, our `sentrySvelteKit` plugin factory function returns 6 (5x sourcemap, 1x auto instrumentation) plugins instead of previously two. This is because the Vite plugin now consists of several individual plugins. - We still have to modify source maps a bit before uploading them and all of this should still happen as late as possible. So we still create a customized version of the uploading plugin and return that instead of the original one. - Also added direct usage of `MagicString` to inject global values needed during runtime. Previously, I just handed my modified code to the original plugin's `transform` hook which might have even messed up source maps. - The new API is a subset of all Sentry Vite plugin options. - I'm happy to add more options before merging this PR. I just don't think we need all of them right from the start. As an alternative, we could allow users to pass in arbitrary options and we type them as `unknown` or something along these lines 🤔 Example usage (showing all available options; all are optional) ```js sentrySvelteKit({ autoUploadSourceMaps: true, sourceMapsUploadOptions: { org: process.env.SENTRY_ORG, project: process.env.SENTRY_PROJECT, authToken: process.env.SENTRY_AUTH_TOKEN, release: { name: '1.0.1', inject: true }, sourcemaps: { assets: ['./build/*/**/*'], ignore: ['**/build/client/**/*'], filesToDeleteAfterUpload: ['./build/**/*.map'] } }, autoInstrument: true, debug: true }), ``` closes #9835 closes #8283 --------- Co-authored-by: Francesco Novy <[email protected]>
1 parent 20ab586 commit ba80f79

File tree

8 files changed

+429
-164
lines changed

8 files changed

+429
-164
lines changed

dev-packages/node-integration-tests/suites/public-api/startSpan/with-nested-spans/scenario.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import '@sentry/tracing';
2-
31
import * as Sentry from '@sentry/node';
42

53
Sentry.init({

packages/astro/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
"@sentry/node-experimental": "7.100.0",
5555
"@sentry/types": "7.100.0",
5656
"@sentry/utils": "7.100.0",
57-
"@sentry/vite-plugin": "^2.8.0"
57+
"@sentry/vite-plugin": "^2.14.2"
5858
},
5959
"devDependencies": {
6060
"astro": "^3.5.0",

packages/sveltekit/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,9 @@
4343
"@sentry/svelte": "7.100.0",
4444
"@sentry/types": "7.100.0",
4545
"@sentry/utils": "7.100.0",
46-
"@sentry/vite-plugin": "^0.6.1",
46+
"@sentry/vite-plugin": "^2.14.2",
4747
"magicast": "0.2.8",
48+
"magic-string": "0.30.7",
4849
"sorcery": "0.11.0"
4950
},
5051
"devDependencies": {

packages/sveltekit/src/vite/sentryVitePlugins.ts

Lines changed: 109 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
import type { SentryVitePluginOptions } from '@sentry/vite-plugin';
21
import type { Plugin } from 'vite';
32

43
import type { AutoInstrumentSelection } from './autoInstrument';
54
import { makeAutoInstrumentationPlugin } from './autoInstrument';
65
import type { SupportedSvelteKitAdapters } from './detectAdapter';
76
import { detectAdapter } from './detectAdapter';
8-
import { makeCustomSentryVitePlugin } from './sourceMaps';
7+
import { makeCustomSentryVitePlugins } from './sourceMaps';
98

9+
/**
10+
* Options related to source maps upload to Sentry
11+
*/
1012
type SourceMapsUploadOptions = {
1113
/**
1214
* If this flag is `true`, the Sentry plugins will automatically upload source maps to Sentry.
@@ -18,7 +20,101 @@ type SourceMapsUploadOptions = {
1820
* Options for the Sentry Vite plugin to customize and override the release creation and source maps upload process.
1921
* See [Sentry Vite Plugin Options](https://github.com/getsentry/sentry-javascript-bundler-plugins/tree/main/packages/vite-plugin#configuration) for a detailed description.
2022
*/
21-
sourceMapsUploadOptions?: Partial<SentryVitePluginOptions>;
23+
sourceMapsUploadOptions?: {
24+
/**
25+
* The auth token to use when uploading source maps to Sentry.
26+
*
27+
* Instead of specifying this option, you can also set the `SENTRY_AUTH_TOKEN` environment variable.
28+
*
29+
* To create an auth token, follow this guide:
30+
* @see https://docs.sentry.io/product/accounts/auth-tokens/#organization-auth-tokens
31+
*/
32+
authToken?: string;
33+
34+
/**
35+
* The organization slug of your Sentry organization.
36+
* Instead of specifying this option, you can also set the `SENTRY_ORG` environment variable.
37+
*/
38+
org?: string;
39+
40+
/**
41+
* The project slug of your Sentry project.
42+
* Instead of specifying this option, you can also set the `SENTRY_PROJECT` environment variable.
43+
*/
44+
project?: string;
45+
46+
/**
47+
* If this flag is `true`, the Sentry plugin will collect some telemetry data and send it to Sentry.
48+
* It will not collect any sensitive or user-specific data.
49+
*
50+
* @default true
51+
*/
52+
telemetry?: boolean;
53+
54+
/**
55+
* Options related to sourcemaps
56+
*/
57+
sourcemaps?: {
58+
/**
59+
* A glob or an array of globs that specify the build artifacts and source maps that will be uploaded to Sentry.
60+
*
61+
* If this option is not specified, sensible defaults based on your adapter and svelte.config.js
62+
* setup will be used. Use this option to override these defaults, for instance if you have a
63+
* customized build setup that diverges from SvelteKit's defaults.
64+
*
65+
* The globbing patterns must follow the implementation of the `glob` package.
66+
* @see https://www.npmjs.com/package/glob#glob-primer
67+
*/
68+
assets?: string | Array<string>;
69+
70+
/**
71+
* A glob or an array of globs that specifies which build artifacts should not be uploaded to Sentry.
72+
*
73+
* @default [] - By default no files are ignored. Thus, all files matching the `assets` glob
74+
* or the default value for `assets` are uploaded.
75+
*
76+
* The globbing patterns follow the implementation of the glob package. (https://www.npmjs.com/package/glob)
77+
*/
78+
ignore?: string | Array<string>;
79+
80+
/**
81+
* A glob or an array of globs that specifies the build artifacts that should be deleted after the artifact
82+
* upload to Sentry has been completed.
83+
*
84+
* @default [] - By default no files are deleted.
85+
*
86+
* The globbing patterns follow the implementation of the glob package. (https://www.npmjs.com/package/glob)
87+
*/
88+
filesToDeleteAfterUpload?: string | Array<string>;
89+
};
90+
91+
/**
92+
* Options related to managing the Sentry releases for a build.
93+
*
94+
* Note: Managing releases is optional and not required for uploading source maps.
95+
*/
96+
release?: {
97+
/**
98+
* Unique identifier for the release you want to create.
99+
* This value can also be specified via the SENTRY_RELEASE environment variable.
100+
*
101+
* Defaults to automatically detecting a value for your environment. This includes values for Cordova, Heroku,
102+
* AWS CodeBuild, CircleCI, Xcode, and Gradle, and otherwise uses the git HEAD's commit SHA (the latter requires
103+
* access to git CLI and for the root directory to be a valid repository).
104+
*
105+
* If you didn't provide a value and the plugin can't automatically detect one, no release will be created.
106+
*/
107+
name?: string;
108+
109+
/**
110+
* Whether the plugin should inject release information into the build for the SDK to pick it up when
111+
* sending events.
112+
*
113+
* Defaults to `true`.
114+
*/
115+
inject?: boolean;
116+
};
117+
};
22118
};
23119

24120
type AutoInstrumentOptions = {
@@ -79,7 +175,7 @@ export async function sentrySvelteKit(options: SentrySvelteKitPluginOptions = {}
79175
const mergedOptions = {
80176
...DEFAULT_PLUGIN_OPTIONS,
81177
...options,
82-
adapter: options.adapter || (await detectAdapter(options.debug || false)),
178+
adapter: options.adapter || (await detectAdapter(options.debug)),
83179
};
84180

85181
const sentryPlugins: Plugin[] = [];
@@ -100,12 +196,16 @@ export async function sentrySvelteKit(options: SentrySvelteKitPluginOptions = {}
100196
}
101197

102198
if (mergedOptions.autoUploadSourceMaps && process.env.NODE_ENV !== 'development') {
103-
const pluginOptions = {
104-
...mergedOptions.sourceMapsUploadOptions,
105-
debug: mergedOptions.debug, // override the plugin's debug flag with the one from the top-level options
199+
const sourceMapsUploadOptions = mergedOptions.sourceMapsUploadOptions;
200+
201+
const sentryVitePlugins = await makeCustomSentryVitePlugins({
202+
...sourceMapsUploadOptions,
203+
106204
adapter: mergedOptions.adapter,
107-
};
108-
sentryPlugins.push(await makeCustomSentryVitePlugin(pluginOptions));
205+
// override the plugin's debug flag with the one from the top-level options
206+
debug: mergedOptions.debug,
207+
});
208+
sentryPlugins.push(...sentryVitePlugins);
109209
}
110210

111211
return sentryPlugins;

packages/sveltekit/src/vite/sourceMaps.ts

Lines changed: 43 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { sentryVitePlugin } from '@sentry/vite-plugin';
99
import * as sorcery from 'sorcery';
1010
import type { Plugin } from 'vite';
1111

12+
import MagicString from 'magic-string';
1213
import { WRAPPED_MODULE_SUFFIX } from './autoInstrument';
1314
import type { SupportedSvelteKitAdapters } from './detectAdapter';
1415
import type { GlobalSentryValues } from './injectGlobalValues';
@@ -24,17 +25,13 @@ type Sorcery = {
2425
load(filepath: string): Promise<Chain>;
2526
};
2627

27-
type SentryVitePluginOptionsOptionalInclude = Omit<SentryVitePluginOptions, 'include'> & {
28-
include?: SentryVitePluginOptions['include'];
29-
};
30-
31-
type CustomSentryVitePluginOptions = SentryVitePluginOptionsOptionalInclude & {
28+
type CustomSentryVitePluginOptions = SentryVitePluginOptions & {
3229
adapter: SupportedSvelteKitAdapters;
3330
};
3431

3532
// storing this in the module scope because `makeCustomSentryVitePlugin` is called multiple times
3633
// and we only want to generate a uuid once in case we have to fall back to it.
37-
const release = detectSentryRelease();
34+
const releaseName = detectSentryRelease();
3835

3936
/**
4037
* Creates a new Vite plugin that uses the unplugin-based Sentry Vite plugin to create
@@ -52,28 +49,44 @@ const release = detectSentryRelease();
5249
*
5350
* @returns the custom Sentry Vite plugin
5451
*/
55-
export async function makeCustomSentryVitePlugin(options?: CustomSentryVitePluginOptions): Promise<Plugin> {
52+
export async function makeCustomSentryVitePlugins(options?: CustomSentryVitePluginOptions): Promise<Plugin[]> {
5653
const svelteConfig = await loadSvelteConfig();
5754

5855
const usedAdapter = options?.adapter || 'other';
5956
const outputDir = await getAdapterOutputDir(svelteConfig, usedAdapter);
60-
const hasSentryProperties = fs.existsSync(path.resolve(process.cwd(), 'sentry.properties'));
6157

6258
const defaultPluginOptions: SentryVitePluginOptions = {
63-
include: [`${outputDir}/client`, `${outputDir}/server`],
64-
configFile: hasSentryProperties ? 'sentry.properties' : undefined,
65-
release,
59+
release: {
60+
name: releaseName,
61+
},
6662
};
6763

6864
const mergedOptions = {
6965
...defaultPluginOptions,
7066
...options,
67+
release: {
68+
...defaultPluginOptions.release,
69+
...options?.release,
70+
},
7171
};
72+
const { debug } = mergedOptions;
7273

73-
const sentryPlugin: Plugin = sentryVitePlugin(mergedOptions);
74+
const sentryPlugins: Plugin[] = await sentryVitePlugin(mergedOptions);
7475

75-
const { debug } = mergedOptions;
76-
const { buildStart, renderChunk } = sentryPlugin;
76+
const sentryViteDebugIdUploadPlugin = sentryPlugins.find(
77+
plugin => plugin.name === 'sentry-vite-debug-id-upload-plugin',
78+
);
79+
80+
if (!sentryViteDebugIdUploadPlugin) {
81+
debug &&
82+
// eslint-disable-next-line no-console
83+
console.warn(
84+
'sentry-vite-debug-id-upload-plugin not found in sentryPlugins! Cannot modify plugin - returning default Sentry Vite plugins',
85+
);
86+
return sentryPlugins;
87+
}
88+
89+
const restOfSentryVitePlugins = sentryPlugins.filter(plugin => plugin.name !== 'sentry-vite-debug-id-upload-plugin');
7790

7891
let isSSRBuild = true;
7992

@@ -84,15 +97,10 @@ export async function makeCustomSentryVitePlugin(options?: CustomSentryVitePlugi
8497
};
8598

8699
const customPlugin: Plugin = {
87-
name: 'sentry-upload-source-maps',
100+
name: 'sentry-upload-sveltekit-source-maps',
88101
apply: 'build', // only apply this plugin at build time
89102
enforce: 'post', // this needs to be set to post, otherwise we don't pick up the output from the SvelteKit adapter
90103

91-
// These hooks are copied from the original Sentry Vite plugin.
92-
// They're mostly responsible for options parsing and release injection.
93-
buildStart,
94-
renderChunk,
95-
96104
// Modify the config to generate source maps
97105
config: config => {
98106
// eslint-disable-next-line no-console
@@ -114,8 +122,7 @@ export async function makeCustomSentryVitePlugin(options?: CustomSentryVitePlugi
114122
moduleSideEffects: true,
115123
};
116124
}
117-
// @ts-expect-error - this hook exists on the plugin!
118-
return sentryPlugin.resolveId(id, _importer, _ref);
125+
return null;
119126
},
120127

121128
load: id => {
@@ -138,16 +145,19 @@ export async function makeCustomSentryVitePlugin(options?: CustomSentryVitePlugi
138145
},
139146

140147
transform: async (code, id) => {
141-
let modifiedCode = code;
142148
// eslint-disable-next-line @sentry-internal/sdk/no-regexp-constructor -- not end user input + escaped anyway
143149
const isServerHooksFile = new RegExp(`/${escapeStringForRegex(serverHooksFile)}(.(js|ts|mjs|mts))?`).test(id);
144150

145151
if (isServerHooksFile) {
146-
const globalValuesImport = `; import "${VIRTUAL_GLOBAL_VALUES_FILE}";`;
147-
modifiedCode = `${code}\n${globalValuesImport}\n`;
152+
const ms = new MagicString(code);
153+
ms.append(`\n; import "${VIRTUAL_GLOBAL_VALUES_FILE}";\n`);
154+
return {
155+
code: ms.toString(),
156+
map: ms.generateMap({ hires: true }),
157+
};
148158
}
149-
// @ts-expect-error - this hook exists on the plugin!
150-
return sentryPlugin.transform(modifiedCode, id);
159+
160+
return null;
151161
},
152162

153163
// We need to start uploading source maps later than in the original plugin
@@ -205,8 +215,12 @@ export async function makeCustomSentryVitePlugin(options?: CustomSentryVitePlugi
205215
}
206216

207217
try {
218+
// So here, we're just calling the original plugin's `writeBundle` method to upload the source maps.
219+
// Our plugin hook expects output options to glob for source maps. We don't have this option in `closeBundle`.
220+
// So we just pass in the `outDir` we determined earlier.
221+
// Not pretty but my testing shows that it works.
208222
// @ts-expect-error - this hook exists on the plugin!
209-
await sentryPlugin.writeBundle();
223+
await sentryViteDebugIdUploadPlugin.writeBundle({ dir: outDir });
210224
} catch (_) {
211225
// eslint-disable-next-line no-console
212226
console.warn('[Source Maps Plugin] Failed to upload source maps!');
@@ -222,7 +236,7 @@ export async function makeCustomSentryVitePlugin(options?: CustomSentryVitePlugi
222236
},
223237
};
224238

225-
return customPlugin;
239+
return [...restOfSentryVitePlugins, customPlugin];
226240
}
227241

228242
function getFiles(dir: string): string[] {

0 commit comments

Comments
 (0)