diff --git a/.changeset/twenty-toys-laugh.md b/.changeset/twenty-toys-laugh.md new file mode 100644 index 00000000000..55a4b81a3c6 --- /dev/null +++ b/.changeset/twenty-toys-laugh.md @@ -0,0 +1,5 @@ +--- +"@remix-run/dev": patch +--- + +cancel previous build when rebuild is kicked off to prevent rebuilds from hanging diff --git a/packages/remix-dev/compiler/cancel.ts b/packages/remix-dev/compiler/cancel.ts new file mode 100644 index 00000000000..cf6b026d5c2 --- /dev/null +++ b/packages/remix-dev/compiler/cancel.ts @@ -0,0 +1,7 @@ +export const CANCEL_PREFIX = "remix-compile-cancel"; + +export class Cancel extends Error { + constructor(message: string) { + super(`${CANCEL_PREFIX}: ${message}`); + } +} diff --git a/packages/remix-dev/compiler/js/plugins/cssBundleUpdate.ts b/packages/remix-dev/compiler/js/plugins/cssBundleUpdate.ts index e5ca772c7a7..e61f46a8115 100644 --- a/packages/remix-dev/compiler/js/plugins/cssBundleUpdate.ts +++ b/packages/remix-dev/compiler/js/plugins/cssBundleUpdate.ts @@ -2,6 +2,7 @@ import type { Plugin } from "esbuild"; import { readFile } from "fs-extra"; import type * as Channel from "../../../channel"; +import { Cancel } from "../../cancel"; const pluginName = "css-bundle-update-plugin"; const namespace = `${pluginName}-ns`; @@ -53,7 +54,7 @@ export function cssBundleUpdatePlugin(channels: { build.onLoad({ filter: /.*/, namespace }, async (args) => { let cssBundleHref = await channels.cssBundleHref.result; - if (!cssBundleHref.ok) throw Error("canceled"); + if (!cssBundleHref.ok) throw new Cancel("js"); let contents = await readFile(args.path, "utf8"); if (cssBundleHref.value) { diff --git a/packages/remix-dev/compiler/server/plugins/manifest.ts b/packages/remix-dev/compiler/server/plugins/manifest.ts index 5bf2522e915..cf83e2a91ae 100644 --- a/packages/remix-dev/compiler/server/plugins/manifest.ts +++ b/packages/remix-dev/compiler/server/plugins/manifest.ts @@ -4,6 +4,7 @@ import jsesc from "jsesc"; import type * as Channel from "../../../channel"; import { type Manifest } from "../../../manifest"; import { assetsManifestVirtualModule } from "../virtualModules"; +import { Cancel } from "../../cancel"; /** * Creates a virtual module called `@remix-run/dev/assets-manifest` that exports @@ -27,7 +28,7 @@ export function serverAssetsManifestPlugin(channels: { build.onLoad({ filter }, async () => { let manifest = await channels.manifest.result; - if (!manifest.ok) throw Error("canceled"); + if (!manifest.ok) throw new Cancel("server"); return { contents: `export default ${jsesc(manifest.value, { es6: true, diff --git a/packages/remix-dev/compiler/utils/log.ts b/packages/remix-dev/compiler/utils/log.ts index 3c8769c234f..38d3b3f666c 100644 --- a/packages/remix-dev/compiler/utils/log.ts +++ b/packages/remix-dev/compiler/utils/log.ts @@ -1,5 +1,7 @@ import esbuild from "esbuild"; +import { CANCEL_PREFIX } from "../cancel"; + let toError = (thrown: unknown): Error => { if (thrown instanceof Error) return thrown; try { @@ -21,10 +23,14 @@ let logEsbuildError = (error: esbuild.BuildFailure) => { color: true, }); warnings.forEach((w) => console.warn(w)); - let errors = esbuild.formatMessagesSync(error.errors, { - kind: "error", - color: true, - }); + let errors = esbuild.formatMessagesSync( + // Filter out cancelation errors + error.errors.filter((e) => !e.text.startsWith(CANCEL_PREFIX)), + { + kind: "error", + color: true, + } + ); errors.forEach((e) => console.error(e)); }; diff --git a/packages/remix-dev/compiler/watch.ts b/packages/remix-dev/compiler/watch.ts index 3e4fb3a9d0e..d8723aebed5 100644 --- a/packages/remix-dev/compiler/watch.ts +++ b/packages/remix-dev/compiler/watch.ts @@ -74,6 +74,7 @@ export async function watch( }, 500); let rebuild = debounce(async () => { + await compiler.cancel(); onBuildStart?.(ctx); let start = Date.now(); let manifest = await compile();