diff --git a/package.json b/package.json index f8927ef9..9a9ea8c5 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "react-router-devtools", "description": "Devtools for React Router - debug, trace, find hydration errors, catch bugs and inspect server/client data with react-router-devtools", "author": "Alem Tuzlak", - "version": "1.1.9", + "version": "1.1.10", "license": "MIT", "keywords": [ "react-router", diff --git a/src/vite/utils/inject-client.test.ts b/src/vite/utils/inject-client.test.ts index 083799bd..70581c10 100644 --- a/src/vite/utils/inject-client.test.ts +++ b/src/vite/utils/inject-client.test.ts @@ -137,6 +137,56 @@ describe("transform", () => { expect(removeWhitespace(result.code)).toStrictEqual(expected) }) + it("should wrap the default export properly even if it's declared as a variable and then exported via export { name as default }", () => { + const result = injectRdtClient( + ` + const App = () => {}; + export { App as default }; + `, + '{ "config": { }, "plugins": "[]" }', + "", + "/file/path" + ) + const expected = removeWhitespace(` + import { withViteDevTools as _withViteDevTools } from "react-router-devtools/client"; + import rdtStylesheet from "react-router-devtools/client.css?url"; + const App = () => {}; + export default _withViteDevTools(App, { + config: { }, + plugins: [] + }); + export const links = () => [{ rel: "stylesheet", href: rdtStylesheet }]; + `) + expect(removeWhitespace(result.code)).toStrictEqual(expected) + }) + + it("should wrap the default export properly even if it's declared as a variable and then exported via export { name as default } and has other exports as well", () => { + const result = injectRdtClient( + ` + import { test } from "./file/path"; + const App = () => {}; + export { App as default, test }; + `, + '{ "config": { }, "plugins": "[]" }', + "", + "/file/path" + ) + const expected = removeWhitespace(` + import { withViteDevTools as _withViteDevTools } from "react-router-devtools/client"; + import rdtStylesheet from "react-router-devtools/client.css?url"; + import { test } from "./file/path"; + const App = () => {}; + export default _withViteDevTools(App, { + config: { }, + plugins: [] + }); + export { test }; + export const links = () => [{ rel: "stylesheet", href: rdtStylesheet }]; + + `) + expect(removeWhitespace(result.code)).toStrictEqual(expected) + }) + it("should wrap the default export properly even if it's declared as a function and then exported", () => { const result = injectRdtClient( ` diff --git a/src/vite/utils/inject-client.ts b/src/vite/utils/inject-client.ts index b984de37..4f232970 100644 --- a/src/vite/utils/inject-client.ts +++ b/src/vite/utils/inject-client.ts @@ -30,6 +30,7 @@ const transform = (ast: ParseResult, clientConfig: string) => { function uppercaseFirstLetter(str: string) { return str.charAt(0).toUpperCase() + str.slice(1) } + const transformations: Array<() => void> = [] trav(ast, { ExportDeclaration(path) { if (path.isExportDefaultDeclaration()) { @@ -83,10 +84,43 @@ const transform = (ast: ParseResult, clientConfig: string) => { ]) ) } + + // Handle `export { App as default };` + const specifiers = path.node.specifiers + for (const specifier of specifiers) { + if ( + t.isExportSpecifier(specifier) && + t.isIdentifier(specifier.exported) && + specifier.exported.name === "default" && + t.isIdentifier(specifier.local) + ) { + const localName = specifier.local.name + const uid = getHocUid(path, "withViteDevTools") + + // Insert the wrapped default export + transformations.push(() => { + path.insertBefore( + t.exportDefaultDeclaration(t.callExpression(uid, [t.identifier(localName), clientConfigExpression])) + ) + + // Remove the original export specifier + const remainingSpecifiers = path.node.specifiers.filter( + (s) => !(t.isExportSpecifier(s) && t.isIdentifier(s.exported) && s.exported.name === "default") + ) + if (remainingSpecifiers.length > 0) { + path.replaceWith(t.exportNamedDeclaration(null, remainingSpecifiers, path.node.source)) + } else { + path.remove() + } + }) + } + } } }, }) - + for (const transformation of transformations) { + transformation() + } if (hocs.length > 0) { ast.program.body.unshift( t.importDeclaration( diff --git a/test-apps/react-router-vite/app/root.tsx b/test-apps/react-router-vite/app/root.tsx index a488c0dc..956163c4 100644 --- a/test-apps/react-router-vite/app/root.tsx +++ b/test-apps/react-router-vite/app/root.tsx @@ -42,7 +42,7 @@ export const action =async ({devTools}: ActionFunctionArgs) => { return ({ message: "Hello World", bigInt: BigInt(10) }); } -export default function App() { +function App() { return ( @@ -66,3 +66,5 @@ export default function App() { ); } + +export { App as default } \ No newline at end of file