Skip to content

Commit 95897c6

Browse files
betegonclaude
andcommitted
fix: use file-local createRequire for relative lazy requires in src/
The require-shim.mjs anchors globalThis.require at the project root (package.json), which works for node:* builtins but breaks relative paths — require("../telemetry.js") resolves to /project-root/../ instead of relative to the calling file. The shim assumed relative require() calls in src/ only ran during script/*.ts tsx runs, but pnpm cli (which also uses tsx) hits both db/index.ts and list-command.ts at runtime. db/index.ts crashed hard; list-command.ts was silently swallowed by try/catch, leaving getSubcommandsForRoute() always returning an empty map. Fix: replace the global require() with a file-local createRequire anchored to import.meta.url in each affected file. Update the shim comment to reflect the constraint. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
1 parent 48200d5 commit 95897c6

13 files changed

Lines changed: 53 additions & 111 deletions

File tree

script/require-shim.mjs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@
88
*
99
* The `require` function is anchored at the project root (package.json) so
1010
* that `node:*` builtins and npm package requires resolve correctly. Note
11-
* that relative `require("./foo.js")` calls will resolve from the project
12-
* root, not from the calling file — this is acceptable because all relative
13-
* `require()` calls in `src/` are behind runtime-only code paths (DB init,
14-
* telemetry) that don't execute during tsx script runs.
11+
* that relative `require("./foo.js")` calls resolve from the project root,
12+
* not from the calling file. Files in `src/` that use lazy relative requires
13+
* must use a file-local `createRequire(import.meta.url)` instead of relying
14+
* on this global shim.
1515
*
1616
* Usage: NODE_OPTIONS="--import ./script/require-shim.mjs" tsx script/...
1717
* Or in package.json scripts via the `pnpm tsx` alias.

src/lib/custom-ca.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@
1717
*/
1818

1919
import { readFileSync } from "node:fs";
20+
import { createRequire } from "node:module";
2021
import { rootCertificates } from "node:tls";
22+
23+
const _require = createRequire(import.meta.url);
2124
import { getDefaultCaCert } from "./db/defaults.js";
2225
import { getEnv } from "./env.js";
2326
import { logger } from "./logger.js";
@@ -30,7 +33,7 @@ import { isSentrySaasUrl } from "./sentry-urls.js";
3033
* option instead.
3134
*/
3235
const setDefaultCACertificates: ((certs: string[]) => void) | undefined = (
33-
require("node:tls") as {
36+
_require("node:tls") as {
3437
setDefaultCACertificates?: (certs: string[]) => void;
3538
}
3639
).setDefaultCACertificates;

src/lib/db/index.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,12 @@
55
*/
66

77
import { chmodSync, mkdirSync } from "node:fs";
8+
import { createRequire } from "node:module";
89
import { join } from "node:path";
910
import { getEnv } from "../env.js";
11+
12+
const _require = createRequire(import.meta.url);
13+
1014
import { migrateFromJson } from "./migration.js";
1115
import { initSchema, runMigrations } from "./schema.js";
1216
import { Database } from "./sqlite.js";
@@ -30,7 +34,7 @@ let rawDb: Database | null = null;
3034
let dbOpenedPath: string | null = null;
3135

3236
export function getConfigDir(): string {
33-
const { homedir } = require("node:os");
37+
const { homedir } = _require("node:os");
3438
return (
3539
getEnv()[CONFIG_DIR_ENV_VAR] || join(homedir(), DEFAULT_CONFIG_DIR_NAME)
3640
);
@@ -107,7 +111,7 @@ export function getDatabase(): Database {
107111
if (getEnv().SENTRY_CLI_NO_TELEMETRY === "1") {
108112
db = rawDb;
109113
} else {
110-
const { createTracedDatabase } = require("../telemetry.js") as {
114+
const { createTracedDatabase } = _require("../telemetry.js") as {
111115
createTracedDatabase: (d: Database) => Database;
112116
};
113117
db = createTracedDatabase(rawDb);

src/lib/db/migration.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@
33
*/
44

55
import { rmSync } from "node:fs";
6+
import { createRequire } from "node:module";
67
import { join } from "node:path";
8+
9+
const _require = createRequire(import.meta.url);
10+
711
import { logger } from "../logger.js";
812
import { getConfigDir } from "./index.js";
913
import type { Database } from "./sqlite.js";
@@ -34,14 +38,14 @@ function markMigrationCompleted(db: Database): void {
3438

3539
function oldConfigExists(): boolean {
3640
const configPath = join(getConfigDir(), OLD_CONFIG_FILENAME);
37-
const { existsSync } = require("node:fs");
41+
const { existsSync } = _require("node:fs");
3842
return existsSync(configPath);
3943
}
4044

4145
function readOldConfig(): OldConfig | null {
4246
const configPath = join(getConfigDir(), OLD_CONFIG_FILENAME);
4347
try {
44-
const { readFileSync } = require("node:fs");
48+
const { readFileSync } = _require("node:fs");
4549
const content = readFileSync(configPath, "utf-8");
4650
return JSON.parse(content);
4751
} catch {

src/lib/db/schema.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,14 @@
1111
* - Migration checks
1212
*/
1313

14+
import { createRequire } from "node:module";
1415
import { getEnv } from "../env.js";
1516
import { stringifyUnknown } from "../errors.js";
1617
import { logger } from "../logger.js";
1718
import type { Database } from "./sqlite.js";
1819

20+
const _require = createRequire(import.meta.url);
21+
1922
export const CURRENT_SCHEMA_VERSION = 16;
2023

2124
/** Environment variable to disable auto-repair */
@@ -671,7 +674,7 @@ export function tryRepairAndRetry<T>(
671674
let repairSucceeded = false;
672675
try {
673676
// Dynamic imports to avoid circular dependencies with db/index.js
674-
const { getRawDatabase } = require("./index.js") as {
677+
const { getRawDatabase } = _require("./index.js") as {
675678
getRawDatabase: () => Database;
676679
};
677680

src/lib/db/sqlite.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@
88
* Uses `node:sqlite` (Node 22.15+ with `--experimental-sqlite` flag).
99
*/
1010

11+
import { createRequire } from "node:module";
1112
import { logger } from "../logger.js";
1213

14+
const _require = createRequire(import.meta.url);
15+
1316
const log = logger.withTag("sqlite");
1417

1518
/** Valid SQLite binding value. */
@@ -58,7 +61,7 @@ function wrapStatement(stmt: any): StatementWrapper {
5861
* Uses `node:sqlite` (Node 22.15+ with `--experimental-sqlite`).
5962
*/
6063
// biome-ignore lint/suspicious/noExplicitAny: driver types loaded lazily
61-
const SqliteImpl: any = require("node:sqlite").DatabaseSync;
64+
const SqliteImpl: any = _require("node:sqlite").DatabaseSync;
6265

6366
/**
6467
* SQLite database wrapper.

src/lib/init/ui/ink-ui.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,11 @@
4747
*/
4848

4949
import { openSync } from "node:fs";
50+
import { createRequire } from "node:module";
5051
import { ReadStream } from "node:tty";
52+
53+
const _require = createRequire(import.meta.url);
54+
5155
import { setTag } from "@sentry/node-core/light";
5256
import { CLI_VERSION } from "../../constants.js";
5357
import { stripAnsi } from "../../formatters/plain-detect.js";
@@ -235,7 +239,7 @@ export async function createInkUI(
235239
let isSea = false;
236240
try {
237241
// biome-ignore lint/suspicious/noExplicitAny: node:sea types not yet in @types/node
238-
const sea = require("node:sea") as any;
242+
const sea = _require("node:sea") as any;
239243
isSea = sea.isSea?.() === true;
240244
} catch {
241245
// node:sea not available (older Node or non-SEA context)
@@ -245,7 +249,7 @@ export async function createInkUI(
245249
// Extract the embedded sidecar to a temp file and import it.
246250
// The asset key matches what fossilize registered via --assets.
247251
// biome-ignore lint/suspicious/noExplicitAny: node:sea types not yet in @types/node
248-
const sea = require("node:sea") as any;
252+
const sea = _require("node:sea") as any;
249253
const { writeFileSync, mkdtempSync } = await import("node:fs");
250254
const { join } = await import("node:path");
251255
const { tmpdir } = await import("node:os");

src/lib/list-command.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,12 @@
1313
* buildOrgListCommand
1414
*/
1515

16+
import { createRequire } from "node:module";
1617
import type { Aliases, Command, CommandContext } from "@stricli/core";
1718
import type { SentryContext } from "../context.js";
19+
20+
const _require = createRequire(import.meta.url);
21+
1822
import { parseOrgProjectArg } from "./arg-parsing.js";
1923
import { buildCommand, numberParser } from "./command.js";
2024
import { disableOrgCache } from "./db/regions.js";
@@ -414,7 +418,7 @@ function getSubcommandsForRoute(routeName: string): Set<string> {
414418
_subcommandsByRoute = new Map();
415419

416420
try {
417-
const { routes } = require("../app.js") as {
421+
const { routes } = _require("../app.js") as {
418422
routes: { getAllEntries: () => readonly RouteEntry[] };
419423
};
420424

src/lib/logger.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,12 @@
5656
* @module
5757
*/
5858

59+
import { createRequire } from "node:module";
5960
import type { ConsolaInstance } from "consola";
6061
import { createConsola } from "consola";
62+
63+
const _require = createRequire(import.meta.url);
64+
6165
import { getEnv } from "./env.js";
6266

6367
/**
@@ -209,7 +213,7 @@ export function attachSentryReporter(): void {
209213
// Dynamic import to avoid pulling in Sentry at module load time.
210214
// The reporter is exported from @sentry/node-core/light (via @sentry/node → @sentry/core).
211215
// eslint-disable-next-line @typescript-eslint/no-require-imports
212-
const Sentry = require("@sentry/node-core/light") as {
216+
const Sentry = _require("@sentry/node-core/light") as {
213217
createConsolaReporter: (options?: Record<string, unknown>) => {
214218
log: (logObj: unknown) => void;
215219
};

src/lib/telemetry.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,12 @@
1010
*/
1111

1212
import { chmodSync, statSync } from "node:fs";
13+
import { createRequire } from "node:module";
1314
// biome-ignore lint/performance/noNamespaceImport: Sentry SDK recommends namespace import
1415
import * as Sentry from "@sentry/node-core/light";
16+
17+
const _require = createRequire(import.meta.url);
18+
1519
import { isMusl } from "./binary.js";
1620
import {
1721
CLI_VERSION,
@@ -383,7 +387,7 @@ const LIBRARY_EXCLUDED_INTEGRATIONS = new Set([
383387
const hasGetSystemErrorMap = (() => {
384388
try {
385389
// Dynamic require to avoid bundler issues — the check only matters at runtime
386-
const util = require("node:util") as Record<string, unknown>;
390+
const util = _require("node:util") as Record<string, unknown>;
387391
return typeof util.getSystemErrorMap === "function";
388392
} catch {
389393
return false;
@@ -1017,7 +1021,7 @@ const noop = (): void => {};
10171021
/** Resolves the database path, falling back to a default if the import fails. */
10181022
function resolveDbPath(): string {
10191023
try {
1020-
const { getDbPath } = require("./db/index.js") as {
1024+
const { getDbPath } = _require("./db/index.js") as {
10211025
getDbPath: () => string;
10221026
};
10231027
return getDbPath();
@@ -1102,7 +1106,7 @@ function tryRepairReadonly(): boolean {
11021106
repairAttempted = true;
11031107

11041108
const dbPath = resolveDbPath();
1105-
const { dirname } = require("node:path") as {
1109+
const { dirname } = _require("node:path") as {
11061110
dirname: (p: string) => string;
11071111
};
11081112
const configDir = dirname(dbPath);

0 commit comments

Comments
 (0)