From cb037e681757bca53544434eef393f11fc734f3a Mon Sep 17 00:00:00 2001 From: Mark Dalgleish Date: Wed, 2 Oct 2024 16:12:16 +1000 Subject: [PATCH] Support shared imports in route chunks --- .../vite/route-chunks-test.ts | 20 +++++++++ .../react-router-dev/vite/route-chunks.ts | 41 ++++--------------- 2 files changed, 29 insertions(+), 32 deletions(-) diff --git a/packages/react-router-dev/vite/route-chunks-test.ts b/packages/react-router-dev/vite/route-chunks-test.ts index 93ce850f33..17dc9f280c 100644 --- a/packages/react-router-dev/vite/route-chunks-test.ts +++ b/packages/react-router-dev/vite/route-chunks-test.ts @@ -165,6 +165,26 @@ describe("route chunks", () => { `); }); + test("shared imports", () => { + const code = dedent` + import { shared } from "./shared"; + export const chunk = shared("chunk"); + export const main = shared("main"); + `; + expect(hasChunkableExport(code, "chunk", ...cache)).toBe(true); + expect(hasChunkableExport(code, "main", ...cache)).toBe(true); + expect(getChunkedExport(code, "chunk", {}, ...cache)?.code) + .toMatchInlineSnapshot(` + "import { shared } from "./shared"; + export const chunk = shared("chunk");" + `); + expect(getChunkedExport(code, "main", {}, ...cache)?.code) + .toMatchInlineSnapshot(` + "import { shared } from "./shared"; + export const main = shared("main");" + `); + }); + test("re-exports using shared statement", () => { const code = dedent` export { chunk1, chunk2, main } from "./shared"; diff --git a/packages/react-router-dev/vite/route-chunks.ts b/packages/react-router-dev/vite/route-chunks.ts index 810bb735f2..058d737de2 100644 --- a/packages/react-router-dev/vite/route-chunks.ts +++ b/packages/react-router-dev/vite/route-chunks.ts @@ -53,6 +53,7 @@ function assertNodePathIsStatement( }` ); } + function assertNodePathIsVariableDeclarator( path: NodePath | NodePath[] | null | undefined ): asserts path is NodePath { @@ -469,11 +470,6 @@ export function hasChunkableExport( return false; } - invariant( - dependencies.topLevelStatements.size > 0, - `Expected export "${exportName}" to have top level statements if the set exists` - ); - // Loop through all other exports to see if they have top level non-import // statements in common with the export we're trying to chunk. for (let [currentExportName, currentDependencies] of exportDependencies) { @@ -498,35 +494,16 @@ export function hasChunkableExport( } } - // Loop through all other exports to see if they have imported identifiers - // in common with the export we're trying to chunk. - if (dependencies.importedIdentifierNames.size > 0) { - for (let [ - currentExportName, - currentDependencies, - ] of exportDependencies) { - if (currentExportName === exportName) { - continue; - } - - // As soon as we find any imported identifiers in common with another - // export, we know this export cannot be placed in its own chunk. Note - // that the chunk can still share top level import statements with - // other exports because we filter out all unused imports, so we can - // treat each imported identifier as a separate entity in this check. - if ( - setsIntersect( - currentDependencies.importedIdentifierNames, - dependencies.importedIdentifierNames - ) - ) { - return false; - } - } + // If the export we're trying to chunk depends on more than one exported + // variable declarator (where one of them might be the chunked export + // itself), it means it must depend on other exports and can't be chunked, + // so we can bail out early before comparing against other exports. + if (dependencies.exportedVariableDeclarators.size > 1) { + return false; } - // Loop through all other exports to see if they have exported variable - // declarators in common with the export we're trying to chunk. + // Loop through all other exports to see if they depend on the export + // we're trying to chunk. if (dependencies.exportedVariableDeclarators.size > 0) { for (let [ currentExportName,