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
47 changes: 41 additions & 6 deletions packages/cli/src/lib/sourcemap/inject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -835,6 +835,41 @@ export async function resolveDirectorySourcemaps(
return results;
}

/** Next.js build output: any path with a `.next` directory segment. */
const NEXT_DIST_DIR_RE = /(?:^|\/)\.next(?:\/|$)/;

/** Next.js server output, whose sourcemaps have separate switches. */
const NEXT_SERVER_DIR_RE = /(?:^|\/)\.next\/server(?:\/|$)/;

/**
* How to make the bundler that built `dir` emit sourcemaps.
*
* Nearly every zero-pairs report comes from `@sentry/nextjs`, which uploads
* `.next/server` and `.next/static/...`. The Vite and webpack settings do not
* apply there, so Next.js output gets its own advice (CLI-1FN).
*/
function sourcemapSettingHint(dir: string): string {
const posixDir = dir.replaceAll("\\", "/");
if (NEXT_SERVER_DIR_RE.test(posixDir)) {
return (
"For Next.js server output, check that next.config does not turn " +
"server sourcemaps off, e.g. a custom `webpack()` that sets " +
"`config.devtool`, or `experimental.serverSourceMaps: false`."
);
}
if (NEXT_DIST_DIR_RE.test(posixDir)) {
return (
"For Next.js, remove `productionBrowserSourceMaps: false` from " +
"next.config (`withSentryConfig` turns it on), or set " +
"`sourcemaps.disable: true` in `withSentryConfig` to skip the upload."
);
}
return (
"For Vite/Astro: `vite.environments.client.build.sourcemap: 'hidden'`. " +
"For webpack: `devtool: 'hidden-source-map'`."
);
}

/**
* Build an actionable error for the zero-pairs case, tailored to
* which side of the JS/map pairing is missing.
Expand All @@ -855,9 +890,8 @@ export function buildEmptyDiscoveryError(
if (jsFiles > 0 && mapFiles === 0) {
return new ValidationError(
`Found ${jsFiles} JS file(s) in '${dir}' but no companion .map ` +
"files. Your bundler is not emitting sourcemaps. For Vite/Astro: " +
"`vite.environments.client.build.sourcemap: 'hidden'`. For webpack: " +
"`devtool: 'hidden-source-map'`. Pass --allow-empty to suppress.",
"files. Your bundler is not emitting sourcemaps. " +
`${sourcemapSettingHint(dir)} Pass --allow-empty to suppress.`,
"directory",
);
}
Expand All @@ -871,9 +905,10 @@ export function buildEmptyDiscoveryError(
}
return new ValidationError(
`Found ${jsFiles} JS and ${mapFiles} .map file(s) in '${dir}' but ` +
"no JS file has a matching `<name>.map` companion. Check that your " +
"bundler emits JS and sourcemaps with matching basenames. Pass " +
"--allow-empty to suppress.",
"no JS file has a sourcemap: none has a sibling `<name>.map` or a " +
"`//# sourceMappingURL=` comment pointing at a map in this " +
`directory. ${sourcemapSettingHint(dir)} Pass --allow-empty to ` +
"suppress.",
"directory",
);
}
23 changes: 22 additions & 1 deletion packages/cli/test/commands/sourcemap/upload.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { uploadCommand } from "../../../src/commands/sourcemap/upload.js";
import * as sourcemapsApi from "../../../src/lib/api/sourcemaps.js";
import { ValidationError } from "../../../src/lib/errors.js";
import { getDebugIdSnippet } from "../../../src/lib/sourcemap/debug-id.js";
import { buildEmptyDiscoveryError } from "../../../src/lib/sourcemap/inject.js";

type InjectFuncArgs = {
ext?: string;
Expand Down Expand Up @@ -43,6 +44,26 @@ function makeContext() {
};
}

describe("buildEmptyDiscoveryError — bundler hint", () => {
test.each([
["/app/dist", /vite.*webpack/i],
["/app/.next/static/chunks", /productionBrowserSourceMaps: false/],
["/app/.next/static/immutable/chunks", /productionBrowserSourceMaps/],
["C:\\app\\.next\\static\\chunks", /productionBrowserSourceMaps/],
["/app/.next/server", /serverSourceMaps/],
["/app/not.next/static", /vite.*webpack/i],
])("%s", (dir, hint) => {
for (const diag of [
{ jsFiles: 3, mapFiles: 0 },
{ jsFiles: 3, mapFiles: 1 },
]) {
const msg = buildEmptyDiscoveryError(dir, diag).message;
expect(msg).toMatch(hint);
expect(msg).toContain("--allow-empty");
}
});
});

describe("sourcemap inject command — --allow-empty behavior", () => {
let dir: string;
let func: CmdFunc<InjectFuncArgs>;
Expand Down Expand Up @@ -126,7 +147,7 @@ describe("sourcemap inject command — --allow-empty behavior", () => {
const msg = (err as Error).message;
expect(msg).toContain("1 JS");
expect(msg).toContain("1 .map");
expect(msg).toContain("matching basename");
expect(msg).toContain("sourceMappingURL");
}
});

Expand Down
Loading