Skip to content

Commit e4c1852

Browse files
committed
Fix ast issue with re-exports
1 parent 774d804 commit e4c1852

File tree

6 files changed

+444
-38
lines changed

6 files changed

+444
-38
lines changed

src/vite/utils/data-functions-augment.test.ts

Lines changed: 169 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,30 @@ describe("transform", () => {
9292
)
9393
const expected = removeWhitespace(`
9494
import { withLoaderWrapper as _withLoaderWrapper } from "react-router-devtools/server";
95-
import { loader } from "./loader.js";
96-
export { loader as _loader };
95+
import { loader as _loader } from "./loader.js";
96+
export const loader = _withLoaderWrapper(_loader, "test");
97+
`)
98+
expect(removeWhitespace(result.code)).toStrictEqual(expected)
99+
})
100+
101+
it("should wrap the loader export when it's imported from another file and exported and used", () => {
102+
const result = augmentDataFetchingFunctions(
103+
`
104+
import { loader } from "./loader.js";
105+
const test = () => {
106+
const data = loader();
107+
}
108+
export { loader };
109+
`,
110+
"test",
111+
"/file/path"
112+
)
113+
const expected = removeWhitespace(`
114+
import { withLoaderWrapper as _withLoaderWrapper } from "react-router-devtools/server";
115+
import { loader as _loader } from "./loader.js";
116+
const test = () => {
117+
const data = _loader();
118+
};
97119
export const loader = _withLoaderWrapper(_loader, "test");
98120
`)
99121
expect(removeWhitespace(result.code)).toStrictEqual(expected)
@@ -217,8 +239,30 @@ describe("transform", () => {
217239
)
218240
const expected = removeWhitespace(`
219241
import { withClientLoaderWrapper as _withClientLoaderWrapper } from "react-router-devtools/client";
220-
import { clientLoader } from "./client-loader.js";
221-
export { clientLoader as _clientLoader };
242+
import { clientLoader as _clientLoader } from "./client-loader.js";
243+
export const clientLoader = _withClientLoaderWrapper(_clientLoader, "test");
244+
`)
245+
expect(removeWhitespace(result.code)).toStrictEqual(expected)
246+
})
247+
248+
it("should wrap the client loader export when it's re-exported from another file and used by other code", () => {
249+
const result = augmentDataFetchingFunctions(
250+
`
251+
import { clientLoader } from "./client-loader.js";
252+
const test = () => {
253+
const data = clientLoader();
254+
}
255+
export { clientLoader };
256+
`,
257+
"test",
258+
"/file/path"
259+
)
260+
const expected = removeWhitespace(`
261+
import { withClientLoaderWrapper as _withClientLoaderWrapper } from "react-router-devtools/client";
262+
import { clientLoader as _clientLoader } from "./client-loader.js";
263+
const test = () => {
264+
const data = _clientLoader();
265+
};
222266
export const clientLoader = _withClientLoaderWrapper(_clientLoader, "test");
223267
`)
224268
expect(removeWhitespace(result.code)).toStrictEqual(expected)
@@ -235,8 +279,30 @@ describe("transform", () => {
235279
)
236280
const expected = removeWhitespace(`
237281
import { withClientLoaderWrapper as _withClientLoaderWrapper } from "react-router-devtools/client";
238-
import { clientLoader } from "./client-loader.js";
239-
export { clientLoader as _clientLoader };
282+
import { clientLoader as _clientLoader } from "./client-loader.js";
283+
export const clientLoader = _withClientLoaderWrapper(_clientLoader, "test");
284+
`)
285+
expect(removeWhitespace(result.code)).toStrictEqual(expected)
286+
})
287+
288+
it("should wrap the client loader export when it's imported from another file and exported and used by other code", () => {
289+
const result = augmentDataFetchingFunctions(
290+
`
291+
import { clientLoader } from "./client-loader.js";
292+
const test = () => {
293+
const data = clientLoader();
294+
}
295+
export { clientLoader };
296+
`,
297+
"test",
298+
"/file/path"
299+
)
300+
const expected = removeWhitespace(`
301+
import { withClientLoaderWrapper as _withClientLoaderWrapper } from "react-router-devtools/client";
302+
import { clientLoader as _clientLoader } from "./client-loader.js";
303+
const test = () => {
304+
const data = _clientLoader();
305+
};
240306
export const clientLoader = _withClientLoaderWrapper(_clientLoader, "test");
241307
`)
242308
expect(removeWhitespace(result.code)).toStrictEqual(expected)
@@ -373,8 +439,30 @@ describe("transform", () => {
373439
)
374440
const expected = removeWhitespace(`
375441
import { withActionWrapper as _withActionWrapper } from "react-router-devtools/server";
376-
import { action } from "./action.js";
377-
export { action as _action };
442+
import { action as _action } from "./action.js";
443+
export const action = _withActionWrapper(_action, "test");
444+
`)
445+
expect(removeWhitespace(result.code)).toStrictEqual(expected)
446+
})
447+
448+
it("should wrap the action export when it's imported from another file and exported and used by other code", () => {
449+
const result = augmentDataFetchingFunctions(
450+
`
451+
import { action } from "./action.js";
452+
const test = () => {
453+
const data = action();
454+
}
455+
export { action };
456+
`,
457+
"test",
458+
"/file/path"
459+
)
460+
const expected = removeWhitespace(`
461+
import { withActionWrapper as _withActionWrapper } from "react-router-devtools/server";
462+
import { action as _action } from "./action.js";
463+
const test = () => {
464+
const data = _action();
465+
};
378466
export const action = _withActionWrapper(_action, "test");
379467
`)
380468
expect(removeWhitespace(result.code)).toStrictEqual(expected)
@@ -478,8 +566,31 @@ describe("transform", () => {
478566
)
479567
const expected = removeWhitespace(`
480568
import { withClientActionWrapper as _withClientActionWrapper } from "react-router-devtools/client";
481-
import { clientAction } from "./client-action.js";
482-
export { clientAction as _clientAction };
569+
import { clientAction as _clientAction } from "./client-action.js";
570+
export const clientAction = _withClientActionWrapper(_clientAction, "test");
571+
`)
572+
expect(removeWhitespace(result.code)).toStrictEqual(expected)
573+
})
574+
575+
it("should transform the client action export when it's re-exported from another file and keep it working if used somewhere", () => {
576+
const result = augmentDataFetchingFunctions(
577+
`
578+
import { clientAction } from "./client-action.js";
579+
580+
const test = () => {
581+
const data = clientAction();
582+
}
583+
export { clientAction };
584+
`,
585+
"test",
586+
"/file/path"
587+
)
588+
const expected = removeWhitespace(`
589+
import { withClientActionWrapper as _withClientActionWrapper } from "react-router-devtools/client";
590+
import { clientAction as _clientAction } from "./client-action.js";
591+
const test = () => {
592+
const data = _clientAction();
593+
};
483594
export const clientAction = _withClientActionWrapper(_clientAction, "test");
484595
`)
485596
expect(removeWhitespace(result.code)).toStrictEqual(expected)
@@ -512,8 +623,54 @@ describe("transform", () => {
512623
)
513624
const expected = removeWhitespace(`
514625
import { withClientActionWrapper as _withClientActionWrapper } from "react-router-devtools/client";
515-
import { clientAction } from "./client-action.js";
516-
export { clientAction as _clientAction };
626+
import { clientAction as _clientAction } from "./client-action.js";
627+
export const clientAction = _withClientActionWrapper(_clientAction, "test");
628+
`)
629+
expect(removeWhitespace(result.code)).toStrictEqual(expected)
630+
})
631+
632+
it("should transform the client action export when it's imported from another file and exported and used by other code", () => {
633+
const result = augmentDataFetchingFunctions(
634+
`
635+
import { clientAction } from "./client-action.js";
636+
const test = () => {
637+
const data = clientAction();
638+
}
639+
export { clientAction };
640+
`,
641+
"test",
642+
"/file/path"
643+
)
644+
const expected = removeWhitespace(`
645+
import { withClientActionWrapper as _withClientActionWrapper } from "react-router-devtools/client";
646+
import { clientAction as _clientAction } from "./client-action.js";
647+
const test = () => {
648+
const data = _clientAction();
649+
};
650+
export const clientAction = _withClientActionWrapper(_clientAction, "test");
651+
`)
652+
expect(removeWhitespace(result.code)).toStrictEqual(expected)
653+
})
654+
655+
it("should transform the client action export when it's imported from another file and exported and keep the export around if more things are exported", () => {
656+
const result = augmentDataFetchingFunctions(
657+
`
658+
import { clientAction } from "./client-action.js";
659+
const test = () => {
660+
const data = clientAction();
661+
}
662+
export { clientAction, test };
663+
`,
664+
"test",
665+
"/file/path"
666+
)
667+
const expected = removeWhitespace(`
668+
import { withClientActionWrapper as _withClientActionWrapper } from "react-router-devtools/client";
669+
import { clientAction as _clientAction } from "./client-action.js";
670+
const test = () => {
671+
const data = _clientAction();
672+
};
673+
export { test };
517674
export const clientAction = _withClientActionWrapper(_clientAction, "test");
518675
`)
519676
expect(removeWhitespace(result.code)).toStrictEqual(expected)

src/vite/utils/data-functions-augment.ts

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ const ALL_EXPORTS = [...SERVER_COMPONENT_EXPORTS, ...CLIENT_COMPONENT_EXPORTS]
1010
const transform = (ast: ParseResult<Babel.File>, routeId: string) => {
1111
const serverHocs: Array<[string, Babel.Identifier]> = []
1212
const clientHocs: Array<[string, Babel.Identifier]> = []
13+
const imports: Array<[string, Babel.Identifier]> = []
14+
1315
function getServerHocId(path: NodePath, hocName: string) {
1416
const uid = path.scope.generateUidIdentifier(hocName)
1517
const hasHoc = serverHocs.find(([name]) => name === hocName)
@@ -35,6 +37,23 @@ const transform = (ast: ParseResult<Babel.File>, routeId: string) => {
3537

3638
const importDeclarations: Babel.ImportDeclaration[] = []
3739
trav(ast, {
40+
ImportDeclaration(path) {
41+
const specifiers = path.node.specifiers
42+
for (const specifier of specifiers) {
43+
if (!t.isImportSpecifier(specifier) || !t.isIdentifier(specifier.imported)) {
44+
continue
45+
}
46+
const name = specifier.imported.name
47+
if (!ALL_EXPORTS.includes(name)) {
48+
continue
49+
}
50+
const uniqueName = path.scope.generateUidIdentifier(name)
51+
imports.push([name, uniqueName])
52+
specifier.local = uniqueName
53+
// Replace the import specifier with a new one
54+
path.scope.rename(name, uniqueName.name)
55+
}
56+
},
3857
ExportDeclaration(path) {
3958
if (path.isExportNamedDeclaration()) {
4059
const decl = path.get("declaration")
@@ -148,14 +167,13 @@ const transform = (ast: ParseResult<Babel.File>, routeId: string) => {
148167
}
149168
} else {
150169
transformations.push(() => {
151-
const uniqueName = path.scope.generateUidIdentifier(name).name
152-
path.replaceWith(
153-
t.exportNamedDeclaration(
154-
null,
155-
[t.exportSpecifier(t.identifier(name), t.identifier(uniqueName))],
156-
path.node.source
157-
)
170+
const existingImport = imports.find(([existingName]) => existingName === name)
171+
const uniqueName = existingImport?.[1].name ?? path.scope.generateUidIdentifier(name).name
172+
173+
const remainingSpecifiers = path.node.specifiers.filter(
174+
(exportSpecifier) => !(t.isIdentifier(exportSpecifier.exported) && exportSpecifier.exported.name === name)
158175
)
176+
path.replaceWith(t.exportNamedDeclaration(null, remainingSpecifiers, path.node.source))
159177

160178
// Insert the wrapped export after the modified export statement
161179
path.insertAfter(
@@ -169,6 +187,10 @@ const transform = (ast: ParseResult<Babel.File>, routeId: string) => {
169187
[]
170188
)
171189
)
190+
const newRemainingSpecifiers = path.node.specifiers.length
191+
if (newRemainingSpecifiers === 0) {
192+
path.remove()
193+
}
172194
})
173195
}
174196
}

0 commit comments

Comments
 (0)