Skip to content
Merged
34 changes: 17 additions & 17 deletions AGENTS.md

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions script/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import { build as esbuild } from "esbuild";
import pkg from "../package.json";
import { uploadSourcemaps } from "../src/lib/api/sourcemaps.js";
import { injectDebugId, PLACEHOLDER_DEBUG_ID } from "./debug-id.js";
import { textImportPlugin } from "./text-import-plugin.js";

const gzipAsync = promisify(gzip);

Expand Down Expand Up @@ -134,6 +135,7 @@ async function bundleJs(): Promise<boolean> {
"process.env.NODE_ENV": JSON.stringify("production"),
__SENTRY_DEBUG_ID__: JSON.stringify(PLACEHOLDER_DEBUG_ID),
},
plugins: [textImportPlugin],
});

const output = result.metafile?.outputs[BUNDLE_JS];
Expand Down
7 changes: 6 additions & 1 deletion script/bundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { build, type Plugin } from "esbuild";
import pkg from "../package.json";
import { uploadSourcemaps } from "../src/lib/api/sourcemaps.js";
import { injectDebugId, PLACEHOLDER_DEBUG_ID } from "./debug-id.js";
import { textImportPlugin } from "./text-import-plugin.js";

const VERSION = pkg.version;
const SENTRY_CLIENT_ID = process.env.SENTRY_CLIENT_ID ?? "";
Expand Down Expand Up @@ -179,7 +180,11 @@ const sentrySourcemapPlugin: Plugin = {
};

// Always inject debug IDs (even without auth token); upload is gated inside the plugin
const plugins: Plugin[] = [bunSqlitePlugin, sentrySourcemapPlugin];
const plugins: Plugin[] = [
bunSqlitePlugin,
sentrySourcemapPlugin,
textImportPlugin,
];

if (process.env.SENTRY_AUTH_TOKEN) {
console.log(" Sentry auth token found, source maps will be uploaded");
Expand Down
51 changes: 51 additions & 0 deletions script/text-import-plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**
* esbuild plugin that polyfills Bun's `with { type: "text" }` import
* attribute.
*
* esbuild doesn't natively support the `text` import attribute (only
* `json`), but Bun does. Our CLI code uses it to load the grep worker
* source as a string at bundle time (see
* `src/lib/scan/worker-pool.ts`). Without this plugin, esbuild errors
* with `Importing with a type attribute of "text" is not supported`
* on any file that imports a sibling `.js` as text.
*
* The plugin intercepts imports whose `with` attribute matches
* `{ type: "text" }`, reads the file from disk, and emits it as a JS
* module that default-exports the file's contents as a string.
* Runtime behavior matches Bun's native handling, so the same source
* works in dev (via `bun run`) and in compiled binaries (esbuild +
* `bun build --compile` two-step).
*
* Used by both `script/build.ts` (single-file executable) and
* `script/bundle.ts` (CJS library bundle for npm).
*/

import { readFileSync } from "node:fs";
import { resolve as resolvePath } from "node:path";
import type { Plugin } from "esbuild";

const TEXT_IMPORT_NS = "text-import";
/** Match-any filter for esbuild's plugin API. Hoisted for top-level-regex lint. */
const ANY_FILTER = /.*/;

export const textImportPlugin: Plugin = {
name: "text-import",
setup(build) {
build.onResolve({ filter: ANY_FILTER }, (args) => {
if (args.with?.type !== "text") {
return null;
}
return {
path: resolvePath(args.resolveDir, args.path),
namespace: TEXT_IMPORT_NS,
};
});
build.onLoad({ filter: ANY_FILTER, namespace: TEXT_IMPORT_NS }, (args) => {
const content = readFileSync(args.path, "utf-8");
return {
contents: `export default ${JSON.stringify(content)};`,
loader: "js",
};
});
},
};
Loading
Loading