From 354924c856f7b39c54f484a5ecd775e5378bdc5a Mon Sep 17 00:00:00 2001 From: Jon Jensen Date: Mon, 8 Aug 2022 14:57:33 -0600 Subject: [PATCH] fix(remix-dev): make MDX builds deterministic This is a fix along the lines of #2027; because the MDX virtual modules resolve to absolute paths, the manifest/hashes/etc. differ based on the parent directory structure. Testing notes: 1. Expanded existing integration test to cover this scenario (fails without the fix). Also add other virtual module scenarios that weren't already covered 2. Validated in a real MDX Remix app that has split client / server builds (applied fix via patch-package) --- .changeset/purple-ligers-drive.md | 5 ++++ .../deterministic-build-output-test.ts | 30 +++++++++++++++++-- packages/remix-dev/compiler/plugins/mdx.ts | 7 +++-- 3 files changed, 36 insertions(+), 6 deletions(-) create mode 100644 .changeset/purple-ligers-drive.md diff --git a/.changeset/purple-ligers-drive.md b/.changeset/purple-ligers-drive.md new file mode 100644 index 00000000000..51ac8ce48df --- /dev/null +++ b/.changeset/purple-ligers-drive.md @@ -0,0 +1,5 @@ +--- +"@remix-run/dev": patch +--- + +Make MDX builds deterministic diff --git a/integration/deterministic-build-output-test.ts b/integration/deterministic-build-output-test.ts index f713717491d..8ecf888bb16 100644 --- a/integration/deterministic-build-output-test.ts +++ b/integration/deterministic-build-output-test.ts @@ -3,11 +3,35 @@ import globby from "globby"; import fs from "fs"; import path from "path"; -import { createFixtureProject } from "./helpers/create-fixture"; +import { createFixtureProject, js } from "./helpers/create-fixture"; test("builds deterministically under different paths", async () => { - let dir1 = await createFixtureProject(); - let dir2 = await createFixtureProject(); + // This test validates various flavors of remix virtual modules to ensure + // we get identical builds regardless of the parent paths. If a virtual + // module resolves or imports from absolute paths (e.g. via `path.resolve`), + // the build hashes may change even though the output is identical. This + // can cause broken apps (i.e. manifest mismatch) if the server and client + // are built separately. + + // Virtual modules tested: + // * browserRouteModulesPlugin (implicitly tested by root route) + // * emptyModulesPlugin (via app/routes/foo.tsx' server import) + // * mdx (via app/routes/index.mdx) + // * serverAssetsManifestPlugin (implicitly tested by build) + // * serverEntryModulePlugin (implicitly tested by build) + // * serverRouteModulesPlugin (implicitly tested by build) + let init = { + files: { + "app/routes/index.mdx": "# hello world", + "app/routes/foo.tsx": js` + export * from "~/foo/bar.server"; + export default () => "YAY"; + `, + "app/foo/bar.server.ts": "export const meta = () => []", + }, + }; + let dir1 = await createFixtureProject(init); + let dir2 = await createFixtureProject(init); expect(dir1).not.toEqual(dir2); diff --git a/packages/remix-dev/compiler/plugins/mdx.ts b/packages/remix-dev/compiler/plugins/mdx.ts index 2b0ec22e86a..4ff210ec9f4 100644 --- a/packages/remix-dev/compiler/plugins/mdx.ts +++ b/packages/remix-dev/compiler/plugins/mdx.ts @@ -39,14 +39,15 @@ export function mdxPlugin(config: RemixConfig): esbuild.Plugin { let resolved = path.resolve(args.resolveDir, resolvedPath); return { - path: resolved, + path: path.relative(config.appDirectory, resolved), namespace: "mdx", }; }); build.onLoad({ filter: /\.mdx?$/ }, async (args) => { + let absolutePath = path.join(config.appDirectory, args.path); try { - let fileContents = await fsp.readFile(args.path, "utf-8"); + let fileContents = await fsp.readFile(absolutePath, "utf-8"); let rehypePlugins = []; let remarkPlugins = [ @@ -115,7 +116,7 @@ ${remixExports}`; errors: errors.length ? errors : undefined, warnings: warnings.length ? warnings : undefined, contents, - resolveDir: path.dirname(args.path), + resolveDir: path.dirname(absolutePath), loader: getLoaderForFile(args.path), }; } catch (err: any) {