Skip to content

Commit 4b53fc2

Browse files
committed
feat: create vite-null-export package
1 parent f08084f commit 4b53fc2

File tree

9 files changed

+121
-38
lines changed

9 files changed

+121
-38
lines changed

packages/demo/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
"@hiogawa/utils-hattip": "0.0.1-pre.1",
3232
"@hiogawa/vite-glob-routes": "workspace:*",
3333
"@hiogawa/vite-import-dev-server": "workspace:*",
34+
"@hiogawa/vite-null-export": "workspace:*",
3435
"@iconify-json/ri": "^1.1.9",
3536
"@tanstack/react-query": "^4.29.14",
3637
"@tanstack/react-query-devtools": "^4.29.14",

packages/demo/src/routes/layout.tsx

+4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
useNavigation,
1010
useRouteError,
1111
} from "react-router-dom";
12+
import { getRequestContext } from "../server/request-context";
1213
import { useEffectNoStrict } from "../utils/misc-react";
1314
import { ReactQueryWrapper } from "../utils/react-query-utils";
1415

@@ -17,6 +18,9 @@ export const handle = "root-handle";
1718
export function Component() {
1819
useTopProgressBar();
1920

21+
// test viteNullExportPlugin
22+
import.meta.env.SSR && console.log(getRequestContext().url);
23+
2024
return (
2125
<ReactQueryWrapper>
2226
<Toaster

packages/demo/vite.config.ts

+3-37
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import process from "node:process";
22
import globRoutesPlugin from "@hiogawa/vite-glob-routes";
33
import { importDevServerPlugin } from "@hiogawa/vite-import-dev-server";
4+
import { viteNullExportPlugin } from "@hiogawa/vite-null-export";
45
import vaviteConnect from "@vavite/connect";
56
import react from "@vitejs/plugin-react";
67
import unocss from "unocss/vite";
7-
import { FilterPattern, type Plugin, createFilter, defineConfig } from "vite";
8+
import { defineConfig } from "vite";
89

910
export default defineConfig((ctx) => ({
1011
plugins: [
@@ -18,7 +19,7 @@ export default defineConfig((ctx) => ({
1819
handlerEntry:
1920
process.env["SERVER_ENTRY"] ?? "./src/server/adapter-connect.ts",
2021
}),
21-
viteEmptifyModulePlugin(),
22+
viteNullExportPlugin(),
2223
],
2324
build: {
2425
outDir: ctx.ssrBuild ? "dist/server" : "dist/client",
@@ -33,38 +34,3 @@ export default defineConfig((ctx) => ({
3334
: undefined,
3435
clearScreen: false,
3536
}));
36-
37-
// similar to https://github.com/remix-run/remix/blob/80c6842f547b7e83b58f1963894b07ad18c2dfe2/packages/remix-dev/compiler/plugins/emptyModules.ts#L10
38-
// but for vite, we needs to fake esm exports so we use es-module-lexer to extract export names.
39-
function viteEmptifyModulePlugin(pluginOptions?: {
40-
clientOnly?: FilterPattern;
41-
serverOnly?: FilterPattern;
42-
exclude?: FilterPattern;
43-
}): Plugin {
44-
const exclude = pluginOptions?.exclude ?? ["**/node_modules/**"];
45-
const serverOnly = createFilter(
46-
pluginOptions?.serverOnly ?? ["**/server/**", "**/*.server.*"],
47-
exclude
48-
);
49-
const clientOnly = createFilter(
50-
pluginOptions?.clientOnly ?? ["**/client/**", "**/*.client.*"],
51-
exclude
52-
);
53-
return {
54-
name: viteEmptifyModulePlugin.name,
55-
enforce: "pre",
56-
async transform(code, id, options) {
57-
if (options?.ssr ? clientOnly(id) : serverOnly(id)) {
58-
const lib = await import("es-module-lexer");
59-
await lib.init;
60-
const [_import, exports] = lib.parse(code);
61-
return exports
62-
.map((e) =>
63-
e.n === "default" ? `export default {}` : `export var ${e.n} = {}`
64-
)
65-
.join("\n");
66-
}
67-
return undefined;
68-
},
69-
};
70-
}

packages/vite-null-export/README.md

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# vite-null-export
2+
3+
Vite port of remix's server/client-only file convention plugin https://github.com/remix-run/remix/blob/80c6842f547b7e83b58f1963894b07ad18c2dfe2/packages/remix-dev/compiler/plugins/emptyModules.ts#L10
4+
5+
```sh
6+
pnpm build
7+
pnpm release
8+
```
+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"name": "@hiogawa/vite-null-export",
3+
"version": "0.0.0",
4+
"homepage": "https://github.com/hi-ogawa/vite-plugins/tree/main/packages/vite-null-export",
5+
"repository": {
6+
"type": "git",
7+
"url": "https://github.com/hi-ogawa/vite-plugins",
8+
"directory": "packages/vite-null-export"
9+
},
10+
"license": "MIT",
11+
"type": "module",
12+
"exports": {
13+
".": {
14+
"import": "./dist/index.js",
15+
"require": "./dist/index.cjs",
16+
"types": "./dist/index.d.ts"
17+
}
18+
},
19+
"main": "./dist/index.js",
20+
"module": "./dist/index.js",
21+
"types": "./dist/index.d.ts",
22+
"files": [
23+
"dist"
24+
],
25+
"scripts": {
26+
"build": "tsup",
27+
"release": "pnpm publish --no-git-checks --access public"
28+
},
29+
"dependencies": {
30+
"es-module-lexer": "^1.3.1"
31+
},
32+
"devDependencies": {
33+
"vite": "^4.4.4"
34+
},
35+
"peerDependencies": {
36+
"vite": "*"
37+
}
38+
}
+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import * as esModuleLexer from "es-module-lexer";
2+
import { type FilterPattern, type Plugin, createFilter } from "vite";
3+
import { name as packageName } from "../package.json";
4+
5+
// similar to remix's https://github.com/remix-run/remix/blob/80c6842f547b7e83b58f1963894b07ad18c2dfe2/packages/remix-dev/compiler/plugins/emptyModules.ts#L10
6+
// but for vite, we needs to fake esm exports so we use es-module-lexer to extract export names.
7+
8+
export function viteNullExportPlugin(pluginOptions?: {
9+
clientOnly?: FilterPattern;
10+
serverOnly?: FilterPattern;
11+
exclude?: FilterPattern;
12+
}): Plugin {
13+
const exclude = pluginOptions?.exclude ?? ["**/node_modules/**"];
14+
const serverOnly = createFilter(
15+
pluginOptions?.serverOnly ?? ["**/server/**", "**/*.server.*"],
16+
exclude
17+
);
18+
const clientOnly = createFilter(
19+
pluginOptions?.clientOnly ?? ["**/client/**", "**/*.client.*"],
20+
exclude
21+
);
22+
return {
23+
name: packageName,
24+
25+
// we don't enforce "pre" since es-module-lexer cannot reliably parse typescript file.
26+
// so we rely on vite's default typescript transpilation.
27+
28+
async transform(code, id, options) {
29+
if (options?.ssr ? clientOnly(id) : serverOnly(id)) {
30+
await esModuleLexer.init;
31+
const [_import, exports] = esModuleLexer.parse(code);
32+
return exports
33+
.map((e) =>
34+
e.n === "default"
35+
? `export default null;\n`
36+
: `export var ${e.n} = null;\n`
37+
)
38+
.join("");
39+
}
40+
return undefined;
41+
},
42+
};
43+
}
+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"extends": "../../tsconfig.base.json",
3+
"include": ["src"]
4+
}
+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { defineConfig } from "tsup";
2+
3+
export default defineConfig({
4+
entry: ["src/index.ts"],
5+
format: ["esm", "cjs"],
6+
dts: true,
7+
});

pnpm-lock.yaml

+13-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)