Skip to content

Commit

Permalink
[search] Support bun for the source code out of the box.
Browse files Browse the repository at this point in the history
For example: `bun run src/bin/scramble.ts -- 333`

Thanks to oven-sh/bun#3645 and
oven-sh/bun#3669, we can use `bun` directly on
our source code (except the WASM parts). This requires a few changes:

- Move around the source code to account for the fact that `esbuild`
  does not have understand relative `new URL(…, import.meta.url)` or
  `import.meta.resolve(…)` references yet:
  evanw/esbuild#312 (comment)
  - This has the unfortunate side effect that some files have to move to
    the source code root. This isn't *bad* per se, but it breaks some
    assumptions while still relying on some other assumptions. I hope we
    can move the code back some time soon.
- Avoid using the trampoline workaround when we seem to be in a browser environment.
- Avoid assuming that the output of `await import.meta.resolve(…)` can
  be passed to `new URL(…)` (`bun` returns a bath without the `file:`
  protocol).
  • Loading branch information
lgarron committed Jul 24, 2023
1 parent 27dcd8c commit c146874
Show file tree
Hide file tree
Showing 7 changed files with 55 additions and 35 deletions.
6 changes: 6 additions & 0 deletions script/build/targets.js
Original file line number Diff line number Diff line change
Expand Up @@ -277,13 +277,19 @@ export const binTarget = {
"src/bin/order.ts",
"src/bin/puzzle-geometry-bin.ts",
"src/bin/import-restrictions-mermaid-diagram.ts",
"src/bin/scramble.ts",
{
in: "src/cubing/search-worker-entry.js",
out: "search-worker-entry.js",
},
],
outdir: "dist/bin/",
format: "esm",
target: "es2020",
bundle: true,
logLevel: "info",
sourcemap: dev,
splitting: true, // We need this so that `search-worker-entry.js` exists in the output and can be used by other binaries without importing duplicate copies of some code.
//
external,
supported: {
Expand Down
4 changes: 1 addition & 3 deletions script/lib/packages.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,4 @@ export const packageEntryPoints = packageNames.map((p) =>
);

export const packageEntryPointsWithSearchWorkerEntry =
packageEntryPoints.concat([
"./src/cubing/search/worker-workarounds/search-worker-entry.js",
]);
packageEntryPoints.concat(["./src/cubing/search-worker-entry.js"]);
10 changes: 4 additions & 6 deletions src/bin/scramble.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,15 @@

import { randomScrambleForEvent } from "../cubing/scramble";
import { setSearchDebug } from "../cubing/search";
import { searchOutsideDebugGlobals } from "../cubing/search/outside";

const eventID = process.argv[2];

if (!eventID) {
console.log("Usage: scramble <event-id>");
console.log("");
console.log("Example: scramble 333");
console.log(`Usage: scramble <event-id>
Example: scramble 333`);
process.exit(0);
}

setSearchDebug({ logPerf: false });
console.log(searchOutsideDebugGlobals);
setSearchDebug({ logPerf: false }); // TODO: why doesn't this work?
console.log((await randomScrambleForEvent(eventID)).toString());
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { nodeEndpointPort } from "../../vendor/apache/comlink-everywhere/inside/index";
import { exposeAPI } from "./worker-guard";
import { nodeEndpointPort } from "./vendor/apache/comlink-everywhere/inside/index";
import { exposeAPI } from "./search/worker-guard";

if (exposeAPI.expose) {
(async () => {
await import("../inside");
await import("./search/inside");

// Workaround for `node`
const messagePort = globalThis.postMessage
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { exposeAPI } from "./worker-guard";
import { exposeAPI } from "./search/worker-guard";

export async function searchWorkerURLImportMetaResolve(): Promise<string> {
// Note:
Expand All @@ -8,20 +8,15 @@ export async function searchWorkerURLImportMetaResolve(): Promise<string> {
// - `import.meta.resolve(…)` returns a sync result in every environment except `bun`: https://loadeverything.net/#compatibility-dashboard
// - We assume that it's `async`, just in case.
// @ts-ignore
return import.meta.resolve(
"./search/worker-workarounds/search-worker-entry.js",
);
return import.meta.resolve("./search-worker-entry.js");
}

export function searchWorkerURLNewURLImportMetaURL(): URL {
// Note:
// - We have to hardcode the expected path of the entry file in the ESM build, due to lack of `esbuild` support: https://github.com/evanw/esbuild/issues/795
// - This URL is based on the assumption that the code from this file ends up in a shared chunk in the `esm` build. This is not guaranteed by `esbuild`, but it consistently happens for our codebase.
// - We inline the value (instead of using a constant), to maximize compatibility for hardcoded syntax detection in bundlers.
return new URL(
"./search/worker-workarounds/search-worker-entry.js",
import.meta.url,
);
return new URL("./search-worker-entry.js", import.meta.url);
}

// Workaround for `esbuild`: https://github.com/evanw/esbuild/issues/312#issuecomment-1092195778
Expand All @@ -31,11 +26,7 @@ export async function searchWorkerURLEsbuildWorkaround(): Promise<string> {
}

export function instantiateSearchWorkerURLNewURLImportMetaURL(): Worker {
return new Worker(
new URL(
"./search/worker-workarounds/search-worker-entry.js",
import.meta.url,
),
{ type: "module" },
);
return new Worker(new URL("./search-worker-entry.js", import.meta.url), {
type: "module",
});
}
43 changes: 35 additions & 8 deletions src/cubing/search/instantiator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
searchWorkerURLEsbuildWorkaround,
searchWorkerURLImportMetaResolve,
searchWorkerURLNewURLImportMetaURL,
} from "./worker-workarounds";
} from "../search-worker-workarounds";

export interface WorkerOutsideAPI {
terminate: () => void; // `node` can return a `Promise` with an exit code, but we match the web worker API.
Expand All @@ -19,28 +19,55 @@ export interface InsideOutsideAPI {
outsideAPI: WorkerOutsideAPI;
}

export async function instantiateModuleWorker(
function probablyCrossOrigin(workerEntryFileURL: URL): boolean {
try {
const scriptOrigin = globalThis.location?.origin;
const workerOrigin = workerEntryFileURL.origin;
return (
!!scriptOrigin &&
scriptOrigin !== null &&
!!workerOrigin &&
workerOrigin !== null &&
scriptOrigin !== workerOrigin
);
} catch {
return false;
}
}

async function instantiateModuleWorker(
workerEntryFileURL: string | URL,
): Promise<InsideOutsideAPI> {
// We need the `import.meta.url` base for `bun`.
const url = new URL(workerEntryFileURL, import.meta.url);
const tryTrampolineFirst = probablyCrossOrigin(url);
try {
return instantiateModuleWorkerAttempt(url, tryTrampolineFirst);
} catch {
return instantiateModuleWorkerAttempt(url, !tryTrampolineFirst);
}
}

async function instantiateModuleWorkerAttempt(
workerEntryFileURL: URL,
crossOriginTrampoline: boolean,
): Promise<InsideOutsideAPI> {
// rome-ignore lint/suspicious/noAsyncPromiseExecutor: TODO
return new Promise<InsideOutsideAPI>(async (resolve, reject) => {
try {
if (!workerEntryFileURL) {
reject(new Error("Could not get worker entry file URL."));
}
let url: string | URL;
if (globalThis.Worker) {
let url: URL = workerEntryFileURL;
if (crossOriginTrampoline) {
// Standard browser-like environment.
const importSrc = `import ${JSON.stringify(
workerEntryFileURL.toString(),
)};`;
const blob = new Blob([importSrc], {
type: "text/javascript",
});
url = URL.createObjectURL(blob);
} else {
// We need to keep the original entry file URL, but we have to wrap it in the `URL` class.
url = new URL(workerEntryFileURL);
url = new URL(URL.createObjectURL(blob));
}

const worker = (await constructWorker(url, {
Expand Down
File renamed without changes.

0 comments on commit c146874

Please sign in to comment.