Skip to content
Open
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
Original file line number Diff line number Diff line change
@@ -1,24 +1,37 @@
import * as assert from 'node:assert/strict';
import * as fs from 'node:fs';
import * as path from 'node:path';
import { assertSourceMapUpload, findDebugIdsInFilesWithGlobCharacters } from '@sentry-internal/test-utils';
import {
assertSourceMapUpload,
findDebugIdsInFilesWithGlobCharacters,
findSourceMapFiles,
findSourceMappingUrlComments,
} from '@sentry-internal/test-utils';

const CLIENT_OUTPUT = path.join('.output', 'public');
const SERVER_OUTPUT = path.join('.output', 'server');

// The app does not set `build.sourcemap`, so the SDK enables hidden maps and must delete them after upload.
// TODO: The client maps stay in `.output/public`, because the SDK's deletion glob `./**/*.map` skips dot
// folders. Add `CLIENT_OUTPUT` to `strippedDirs` once the SDK deletes them.
assertSourceMapUpload({
outputDirs: [CLIENT_OUTPUT, SERVER_OUTPUT],
strippedDirs: [SERVER_OUTPUT],
markers: { 'client source': 'SOURCEMAP_MARKER_CLIENT', 'server source': 'SOURCEMAP_MARKER_SERVER' },
debugIdsWithoutSourceMap: [
// Written by the build, see `recordChunksWithoutSourceMap` in `vite.config.ts`.
...(fs.existsSync('.tmp_debug_ids_without_source_map')
? fs.readFileSync('.tmp_debug_ids_without_source_map', 'utf8').split('\n').filter(Boolean)
: []),
...findDebugIdsInFilesWithGlobCharacters({ outputDir: SERVER_OUTPUT }),
],
});
// The app does not set `build.sourcemap`, so the SDK enables hidden maps and asks the bundler plugin to
// remove them from the client output, also when no upload runs. `@sentry/nitro` handles the server output.
if (process.env.E2E_NO_UPLOAD) {
assert.deepEqual(findSourceMapFiles({ outputDir: CLIENT_OUTPUT }), [], `Expected no source maps in ${CLIENT_OUTPUT}`);
assert.deepEqual(
findSourceMappingUrlComments({ outputDir: CLIENT_OUTPUT }),
[],
`Expected no sourceMappingURL comments in ${CLIENT_OUTPUT}`,
);
} else {
assertSourceMapUpload({
outputDirs: [CLIENT_OUTPUT, SERVER_OUTPUT],
markers: { 'client source': 'SOURCEMAP_MARKER_CLIENT', 'server source': 'SOURCEMAP_MARKER_SERVER' },
debugIdsWithoutSourceMap: [
// Written by the build, see `recordChunksWithoutSourceMap` in `vite.config.ts`.
...(fs.existsSync('.tmp_debug_ids_without_source_map')
? fs.readFileSync('.tmp_debug_ids_without_source_map', 'utf8').split('\n').filter(Boolean)
: []),
...findDebugIdsInFilesWithGlobCharacters({ outputDir: SERVER_OUTPUT }),
],
});
}

console.log('All sourcemap assertions passed!');
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
"test:prod": "TEST_ENV=production playwright test",
"test:build": "pnpm install && pnpm build",
"test:assert": "node assert-build.ts && pnpm test:prod",
"test:build:no-upload": "pnpm install && E2E_NO_UPLOAD=1 pnpm build",
"test:assert:no-upload": "E2E_NO_UPLOAD=1 node assert-build.ts",
"test:build:tunnel-basepath": "pnpm install && E2E_TEST_TUNNEL=1 E2E_TEST_BASE_PATH=/app pnpm build",
"test:assert:tunnel-basepath": "E2E_TEST_TUNNEL=1 E2E_TEST_BASE_PATH=/app E2E_TEST_DSN=http://public@localhost:3031/1337 pnpm test:prod tunnel",
"test:assert:tunnel-basepath-dev": "E2E_TEST_TUNNEL=1 E2E_TEST_BASE_PATH=/app E2E_TEST_DSN=http://public@localhost:3031/1337 TEST_ENV=development playwright test tunnel"
Expand All @@ -36,6 +38,11 @@
"sentryTest": {
"optional": true,
"variants": [
{
"build-command": "pnpm test:build:no-upload",
"assert-command": "pnpm test:assert:no-upload",
"label": "solidstart-2 (no upload)"
},
{
"build-command": "pnpm test:build:tunnel-basepath",
"assert-command": "pnpm test:assert:tunnel-basepath",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,24 @@ import { defineConfig } from 'vite';
import * as fs from 'node:fs';

// Rolldown emits no map for chunks without mappable source (e.g. Vite's preload helper), so their debug
// IDs can't be uploaded. Recorded here because the SDK deletes all maps after the upload.
// IDs can't be uploaded. Recorded before the bundler plugin removes the maps from the bundle.
function recordChunksWithoutSourceMap() {
return {
name: 'record-chunks-without-source-map',
apply: 'build' as const,
enforce: 'post' as const,
writeBundle(_options: unknown, bundle: Record<string, { type: string; fileName: string; code?: string }>) {
for (const output of Object.values(bundle)) {
if (output.type !== 'chunk' || !output.code || bundle[`${output.fileName}.map`]) {
continue;
generateBundle: {
order: 'pre' as const,
handler(_options: unknown, bundle: Record<string, { type: string; fileName: string; code?: string }>) {
for (const output of Object.values(bundle)) {
if (output.type !== 'chunk' || !output.code || bundle[`${output.fileName}.map`]) {
continue;
}
for (const match of output.code.matchAll(/sentry-dbid-([\da-f-]{36})/gi)) {
fs.appendFileSync('.tmp_debug_ids_without_source_map', `${match[1]!.toLowerCase()}\n`);
}
}
for (const match of output.code.matchAll(/sentry-dbid-([\da-f-]{36})/gi)) {
fs.appendFileSync('.tmp_debug_ids_without_source_map', `${match[1]!.toLowerCase()}\n`);
}
}
},
},
};
}
Expand All @@ -37,7 +40,7 @@ export default defineConfig({
}),
sentrySolidStart({
sentryUrl: 'http://localhost:3032',
authToken: 'fake-auth-token',
authToken: process.env.E2E_NO_UPLOAD ? undefined : 'fake-auth-token',
org: 'test-org',
project: 'test-project',
release: { name: 'test-release' },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export default defineConfig(
},
{
sentryUrl: 'http://localhost:3032',
authToken: 'fake-auth-token',
authToken: process.env.E2E_NO_UPLOAD ? undefined : 'fake-auth-token',
org: 'test-org',
project: 'test-project',
release: { name: 'test-release' },
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,27 @@
import * as assert from 'node:assert/strict';
import * as path from 'node:path';
import { assertSourceMapUpload } from '@sentry-internal/test-utils';
import { assertSourceMapUpload, findSourceMapFiles, findSourceMappingUrlComments } from '@sentry-internal/test-utils';

const CLIENT_OUTPUT = path.join('.output', 'public');
const SERVER_OUTPUT = path.join('.output', 'server');

// The app does not set `build.sourcemap`, so the SDK enables hidden maps and must delete them after upload.
// Vinxi writes the server output after the SDK deleted the maps, so `.output/server` keeps Nitro's own maps,
// which carry no mappings. They are not served, so only the client output must be free of maps.
assertSourceMapUpload({
outputDirs: [CLIENT_OUTPUT, SERVER_OUTPUT],
strippedDirs: [CLIENT_OUTPUT],
markers: { 'client source': 'SOURCEMAP_MARKER_CLIENT', 'server source': 'SOURCEMAP_MARKER_SERVER' },
});
// The app does not set `build.sourcemap`, so the SDK enables hidden maps and asks the bundler plugin to
// remove them, also when no upload runs. Vinxi writes the server output after that, so `.output/server`
// keeps Nitro's own maps, which carry no mappings. They are not served, so only the client output must
// be free of maps.
if (process.env.E2E_NO_UPLOAD) {
assert.deepEqual(findSourceMapFiles({ outputDir: CLIENT_OUTPUT }), [], `Expected no source maps in ${CLIENT_OUTPUT}`);
assert.deepEqual(
findSourceMappingUrlComments({ outputDir: CLIENT_OUTPUT }),
[],
`Expected no sourceMappingURL comments in ${CLIENT_OUTPUT}`,
);
} else {
assertSourceMapUpload({
outputDirs: [CLIENT_OUTPUT, SERVER_OUTPUT],
strippedDirs: [CLIENT_OUTPUT],
markers: { 'client source': 'SOURCEMAP_MARKER_CLIENT', 'server source': 'SOURCEMAP_MARKER_SERVER' },
});
}

console.log('All sourcemap assertions passed!');
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
"test:build": "pnpm install && pnpm build",
"test:assert": "node assert-build.ts && pnpm test:prod && pnpm test:dev",
"test:dev": "TEST_ENV=development playwright test db",
"test:build:no-upload": "pnpm install && E2E_NO_UPLOAD=1 pnpm build",
"test:assert:no-upload": "E2E_NO_UPLOAD=1 node assert-build.ts",
"test:build:tunnel-basepath": "pnpm install && E2E_TEST_TUNNEL=1 E2E_TEST_BASE_PATH=/app pnpm build",
"test:assert:tunnel-basepath": "E2E_TEST_TUNNEL=1 E2E_TEST_BASE_PATH=/app E2E_TEST_DSN=http://public@localhost:3031/1337 TEST_ENV=production playwright test tunnel",
"test:assert:tunnel-basepath-dev": "E2E_TEST_TUNNEL=1 E2E_TEST_BASE_PATH=/app E2E_TEST_DSN=http://public@localhost:3031/1337 TEST_ENV=development playwright test tunnel"
Expand All @@ -35,6 +37,11 @@
},
"sentryTest": {
"variants": [
{
"build-command": "pnpm test:build:no-upload",
"assert-command": "pnpm test:assert:no-upload",
"label": "solidstart (no upload)"
},
{
"build-command": "pnpm test:build:tunnel-basepath",
"assert-command": "pnpm test:assert:tunnel-basepath",
Expand Down
10 changes: 9 additions & 1 deletion packages/solidstart/src/vite/sentrySolidStart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
import { setupSentryNitroModule } from '@sentry/nitro';
import { sentryOrchestrionPlugin } from '@sentry/server-utils/orchestrion/vite';
import type { Plugin, UserConfig } from 'vite';
import type { SourceMapState } from './sourceMaps';
import { makeAddSentryVitePluginSolidStart2, makeEnableSourceMapsVitePlugin } from './sourceMaps';

/**
Expand Down Expand Up @@ -56,7 +57,14 @@ export function sentrySolidStart(options: SentrySolidStartOptions = {}): Plugin[
plugins.push(sentryOrchestrionPlugin({ buildTimeInstrumentation: options.buildTimeInstrumentation }));

if (options.sourcemaps?.disable !== true) {
plugins.push(...makeAddSentryVitePluginSolidStart2(options), ...makeEnableSourceMapsVitePlugin(options));
const sourceMapState: SourceMapState = { sdkEnabledSourceMaps: false };

// The bundler plugin decides on the `build.sourcemap` it finds in its own `config` hook,
// so the plugin that sets it has to come first.
plugins.push(
...makeEnableSourceMapsVitePlugin(options, sourceMapState),
...makeAddSentryVitePluginSolidStart2(options, sourceMapState),
);
}

return plugins;
Expand Down
21 changes: 11 additions & 10 deletions packages/solidstart/src/vite/sentrySolidStartVite.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
import { warnOnRemovedBuildOptions } from '@sentry/core';
import type { Plugin, UserConfig } from 'vite';
import { makeBuildInstrumentationFilePlugin } from './buildInstrumentationFile';
import type { SourceMapState } from './sourceMaps';
import { makeAddSentryVitePlugin, makeEnableSourceMapsVitePlugin } from './sourceMaps';
import type { SentrySolidStartPluginOptions } from './types';

/**
* Various Sentry vite plugins to be used for SolidStart.
*/
export const sentrySolidStartVite = (
options: SentrySolidStartPluginOptions = {},
viteConfig: UserConfig,
tunnelBasePath = '',
): Plugin[] => {
export const sentrySolidStartVite = (options: SentrySolidStartPluginOptions = {}, tunnelBasePath = ''): Plugin[] => {
warnOnRemovedBuildOptions(options, ['unstable_sentryVitePluginOptions']);

const sentryPlugins: Plugin[] = [
Expand All @@ -30,10 +27,14 @@ export const sentrySolidStartVite = (

if (process.env.NODE_ENV !== 'development') {
if (options.sourcemaps?.disable !== true) {
const sourceMapsPlugin = makeAddSentryVitePlugin(options, viteConfig);
const enableSourceMapsPlugin = makeEnableSourceMapsVitePlugin(options);

sentryPlugins.push(...sourceMapsPlugin, ...enableSourceMapsPlugin);
const sourceMapState: SourceMapState = { sdkEnabledSourceMaps: false };

// The bundler plugin decides on the `build.sourcemap` it finds in its own `config` hook,
// so the plugin that sets it has to come first.
sentryPlugins.push(
...makeEnableSourceMapsVitePlugin(options, sourceMapState),
...makeAddSentryVitePlugin(options, sourceMapState),
);
}
}

Expand All @@ -49,7 +50,7 @@ export const addSentryPluginToVite = (
tunnelBasePath?: string,
): UserConfig => {
const plugins = Array.isArray(config.plugins) ? [...config.plugins] : [];
plugins.unshift(sentrySolidStartVite(options, config, tunnelBasePath));
plugins.unshift(sentrySolidStartVite(options, tunnelBasePath));

return {
...config,
Expand Down
Loading
Loading