Skip to content

Commit 3205687

Browse files
authored
Vite: client references to .server code result in build-time error (#8184)
1 parent a9e0102 commit 3205687

File tree

3 files changed

+89
-5
lines changed

3 files changed

+89
-5
lines changed

.changeset/wise-pumas-thank.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
"@remix-run/dev": patch
3+
---
4+
5+
Vite: Errors at build-time when client imports .server default export
6+
7+
Remix already stripped .server file code before ensuring that server code never makes it into the client.
8+
That results in errors when client code tries to import server code, which is exactly what we want!
9+
But those errors were happening at runtime for default imports.
10+
A better experience is to have those errors happen at build-time so that you guarantee that your users won't hit them.

integration/vite-dot-server-test.ts

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,15 @@ import { createProject, viteBuild } from "./helpers/vite.js";
88
let files = {
99
"app/utils.server.ts": String.raw`
1010
export const dotServerFile = "SERVER_ONLY_FILE";
11+
export default dotServerFile;
1112
`,
1213
"app/.server/utils.ts": String.raw`
1314
export const dotServerDir = "SERVER_ONLY_DIR";
15+
export default dotServerDir;
1416
`,
1517
};
1618

17-
test("Vite / build / .server file in client fails with expected error", async () => {
19+
test("Vite / .server file / named import in client fails with expected error", async () => {
1820
let cwd = await createProject({
1921
...files,
2022
"app/routes/fail-server-file-in-client.tsx": String.raw`
@@ -33,7 +35,43 @@ test("Vite / build / .server file in client fails with expected error", async ()
3335
);
3436
});
3537

36-
test("Vite / build / .server dir in client fails with expected error", async () => {
38+
test("Vite / .server file / namespace import in client fails with expected error", async () => {
39+
let cwd = await createProject({
40+
...files,
41+
"app/routes/fail-server-file-in-client.tsx": String.raw`
42+
import * as utils from "~/utils.server";
43+
44+
export default function() {
45+
console.log(utils.dotServerFile);
46+
return <h1>Fail: Server file included in client</h1>
47+
}
48+
`,
49+
});
50+
let client = viteBuild({ cwd })[0];
51+
let stderr = client.stderr.toString("utf8");
52+
expect(stderr).toMatch(
53+
`"dotServerFile" is not exported by "app/utils.server.ts"`
54+
);
55+
});
56+
57+
test("Vite / .server file / default import in client fails with expected error", async () => {
58+
let cwd = await createProject({
59+
...files,
60+
"app/routes/fail-server-file-in-client.tsx": String.raw`
61+
import dotServerFile from "~/utils.server";
62+
63+
export default function() {
64+
console.log(dotServerFile);
65+
return <h1>Fail: Server file included in client</h1>
66+
}
67+
`,
68+
});
69+
let client = viteBuild({ cwd })[0];
70+
let stderr = client.stderr.toString("utf8");
71+
expect(stderr).toMatch(`"default" is not exported by "app/utils.server.ts"`);
72+
});
73+
74+
test("Vite / .server dir / named import in client fails with expected error", async () => {
3775
let cwd = await createProject({
3876
...files,
3977
"app/routes/fail-server-dir-in-client.tsx": String.raw`
@@ -52,7 +90,43 @@ test("Vite / build / .server dir in client fails with expected error", async ()
5290
);
5391
});
5492

55-
test("Vite / build / dead-code elimination for server exports", async () => {
93+
test("Vite / .server dir / namespace import in client fails with expected error", async () => {
94+
let cwd = await createProject({
95+
...files,
96+
"app/routes/fail-server-dir-in-client.tsx": String.raw`
97+
import * as utils from "~/.server/utils";
98+
99+
export default function() {
100+
console.log(utils.dotServerDir);
101+
return <h1>Fail: Server directory included in client</h1>
102+
}
103+
`,
104+
});
105+
let client = viteBuild({ cwd })[0];
106+
let stderr = client.stderr.toString("utf8");
107+
expect(stderr).toMatch(
108+
`"dotServerDir" is not exported by "app/.server/utils.ts"`
109+
);
110+
});
111+
112+
test("Vite / .server dir / default import in client fails with expected error", async () => {
113+
let cwd = await createProject({
114+
...files,
115+
"app/routes/fail-server-dir-in-client.tsx": String.raw`
116+
import dotServerDir from "~/.server/utils";
117+
118+
export default function() {
119+
console.log(dotServerDir);
120+
return <h1>Fail: Server directory included in client</h1>
121+
}
122+
`,
123+
});
124+
let client = viteBuild({ cwd })[0];
125+
let stderr = client.stderr.toString("utf8");
126+
expect(stderr).toMatch(`"default" is not exported by "app/.server/utils.ts"`);
127+
});
128+
129+
test("Vite / dead-code elimination for server exports", async () => {
56130
let cwd = await createProject({
57131
...files,
58132
"app/routes/remove-server-exports-and-dce.tsx": String.raw`

packages/remix-dev/vite/plugin.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -912,7 +912,7 @@ export const remixVitePlugin: RemixVitePlugin = (options = {}) => {
912912
let serverDirRE = /\/\.server\//;
913913
if (serverFileRE.test(id) || serverDirRE.test(id)) {
914914
return {
915-
code: "export default {}",
915+
code: "export {}",
916916
map: null,
917917
};
918918
}
@@ -927,7 +927,7 @@ export const remixVitePlugin: RemixVitePlugin = (options = {}) => {
927927
let clientDirRE = /\/\.client\//;
928928
if (clientFileRE.test(id) || clientDirRE.test(id)) {
929929
return {
930-
code: "export default {}",
930+
code: "export {}",
931931
map: null,
932932
};
933933
}

0 commit comments

Comments
 (0)