Skip to content

Commit 1906ec3

Browse files
committed
feat: implement css aware lazy and migrate routes
1 parent c95e3cd commit 1906ec3

13 files changed

Lines changed: 218 additions & 875 deletions

File tree

packages/start/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
"dependencies": {
3838
"@babel/core": "^7.28.3",
3939
"@babel/traverse": "^7.28.3",
40+
"@babel/types": "^7.28.5",
4041
"@solidjs/meta": "^0.29.4",
4142
"@tanstack/server-functions-plugin": "^1.133.11",
4243
"@types/babel__traverse": "^7.28.0",

packages/start/src/config/index.ts

Lines changed: 5 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1+
import { TanStackServerFnPlugin } from "@tanstack/server-functions-plugin";
2+
import { defu } from "defu";
13
import { globSync } from "node:fs";
24
import { extname, isAbsolute, join, normalize } from "node:path";
35
import { fileURLToPath } from "node:url";
4-
import { TanStackServerFnPlugin } from "@tanstack/server-functions-plugin";
5-
import { defu } from "defu";
6-
import { type PluginOption, type ViteDevServer } from "vite";
6+
import { type PluginOption } from "vite";
77
import solid, { type Options as SolidOptions } from "vite-plugin-solid";
88

9-
import { isCssModulesFile } from "../server/collect-styles.ts";
109
import {
1110
DEFAULT_EXTENSIONS,
1211
VIRTUAL_MODULES,
@@ -19,6 +18,7 @@ import {
1918
} from "./fs-router.ts";
2019
import { fsRoutes } from "./fs-routes/index.ts";
2120
import type { BaseFileSystemRouter } from "./fs-routes/router.ts";
21+
import lazy from "./lazy.ts";
2222
import { manifest } from "./manifest.ts";
2323
import { parseIdQuery } from "./utils.ts";
2424

@@ -158,7 +158,6 @@ export function solidStart(options?: SolidStartOptions): Array<PluginOption> {
158158
},
159159
},
160160
manifest(start),
161-
css(),
162161
fsRoutes({
163162
routers: {
164163
client: new SolidStartClientFileRouter({
@@ -172,6 +171,7 @@ export function solidStart(options?: SolidStartOptions): Array<PluginOption> {
172171
}),
173172
},
174173
}),
174+
lazy(),
175175
// Must be placed after fsRoutes, as treeShake will remove the
176176
// server fn exports added in by this plugin
177177
TanStackServerFnPlugin({
@@ -236,38 +236,3 @@ export function solidStart(options?: SolidStartOptions): Array<PluginOption> {
236236
}),
237237
];
238238
}
239-
240-
function css(): PluginOption {
241-
let viteServer!: ViteDevServer;
242-
const cssModules: Record<string, any> = {};
243-
return {
244-
name: "solid-start:css-hmr",
245-
configureServer(dev) {
246-
viteServer = dev;
247-
},
248-
async handleHotUpdate({ file, server }) {
249-
if (file.endsWith(".css")) {
250-
const resp = await server.transformRequest(file);
251-
if (!resp) return;
252-
const json = resp.code
253-
.match(/const __vite__css = .*\n/)?.[0]
254-
?.slice("const __vite__css = ".length);
255-
if (!json) return;
256-
resp.code = JSON.parse(json);
257-
viteServer.ws.send({
258-
type: "custom",
259-
event: "css-update",
260-
data: {
261-
file,
262-
contents: resp.code,
263-
},
264-
});
265-
}
266-
},
267-
transform(code, id) {
268-
if (isCssModulesFile(id)) {
269-
cssModules[id] = code;
270-
}
271-
},
272-
};
273-
}

packages/start/src/config/lazy.ts

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import type { PluginItem } from "@babel/core";
2+
import babel from "@babel/core";
3+
import * as t from "@babel/types";
4+
import { sep as osSep } from "path";
5+
import { basename, relative, sep } from "path/posix";
6+
import type { PluginOption } from "vite";
7+
import { VITE_ENVIRONMENTS } from "./constants.ts";
8+
9+
const idTransform = (id: string): PluginItem => {
10+
return {
11+
visitor: {
12+
Program(path) {
13+
path.node.body.unshift(
14+
t.exportNamedDeclaration(
15+
t.variableDeclaration("const", [
16+
t.variableDeclarator(t.identifier("id$$"), t.stringLiteral(id))
17+
])
18+
)
19+
);
20+
}
21+
}
22+
};
23+
};
24+
25+
const importTransform = (): PluginItem => {
26+
return {
27+
visitor: {
28+
ImportDeclaration(path) {
29+
if (path.node.source.value !== "solid-js") return;
30+
path.traverse({
31+
ImportSpecifier(subPath) {
32+
if (subPath.node.local.name !== "lazy") return;
33+
subPath.remove();
34+
path.insertAfter(
35+
t.importDeclaration(
36+
[t.importSpecifier(t.identifier("lazy"), t.identifier("lazy"))],
37+
t.stringLiteral("@solidjs/start/server")
38+
)
39+
);
40+
}
41+
});
42+
}
43+
}
44+
};
45+
};
46+
47+
const fileEndingRegex = /(ts|js)x(\?.*)?$/;
48+
49+
const lazy = (): PluginOption => {
50+
const cwd = process.cwd().replaceAll(osSep, sep);
51+
return {
52+
name: "solid-lazy-css",
53+
enforce: "pre",
54+
applyToEnvironment(env) {
55+
return env.name === VITE_ENVIRONMENTS.server;
56+
},
57+
async transform(src, id) {
58+
if (!id.match(fileEndingRegex)) return;
59+
60+
// The transformed files either import "lazy" or css files
61+
// Therefore we skip, if the src doesn't have any import
62+
if (src.indexOf("import") === -1) return;
63+
64+
const plugins: PluginItem[] = [];
65+
66+
const hasDefaultExport = src.indexOf("export default") !== -1;
67+
if (hasDefaultExport) {
68+
const localId = relative(cwd, id);
69+
plugins.push(idTransform(localId));
70+
}
71+
72+
const hasLazy = src.indexOf("lazy(") !== -1;
73+
if (hasLazy) plugins.push(importTransform());
74+
75+
if (!plugins.length) {
76+
return;
77+
}
78+
79+
const transformed = await babel.transformAsync(src, {
80+
plugins,
81+
parserOpts: {
82+
plugins: ["jsx", "typescript"]
83+
},
84+
filename: basename(id),
85+
ast: false,
86+
sourceMaps: true,
87+
configFile: false,
88+
babelrc: false,
89+
sourceFileName: id
90+
});
91+
92+
if (!transformed?.code) return;
93+
94+
const { code, map } = transformed;
95+
return { code, map };
96+
}
97+
};
98+
};
99+
100+
export default lazy;

packages/start/src/router.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,23 @@
1-
import { getManifest } from "solid-start:get-manifest";
1+
import { lazy, type Component } from "solid-js";
22
import { getRequestEvent, isServer } from "solid-js/web";
33

4-
import lazyRoute from "./server/lazyRoute.tsx";
54
import { pageRoutes as routeConfigs } from "./server/routes.ts";
65
import type { PageEvent } from "./server/types.ts";
76

7+
const components: Record<string, Component> = {};
8+
89
export function createRoutes() {
910
function createRoute(route: any) {
11+
const component = route.$component && (components[route.$component.src] ??= lazy(route.$component.import));
12+
1013
return {
1114
...route,
1215
...(route.$$route ? route.$$route.require().route : undefined),
1316
info: {
1417
...(route.$$route ? route.$$route.require().route.info : {}),
1518
filesystem: true
1619
},
17-
component:
18-
route.$component && lazyRoute(route.$component, getManifest("client"), getManifest("ssr")),
20+
component,
1921
children: route.children ? route.children.map(createRoute) : undefined
2022
};
2123
}

packages/start/src/server/handler.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,6 @@ export async function createPageEvent(ctx: FetchEvent) {
164164
// : [])
165165
];
166166
const pageEvent: PageEvent = Object.assign(ctx, {
167-
manifest: "json" in manifest ? await manifest.json() : {},
168167
assets,
169168
router: {
170169
submission: initFromFlash(ctx) as any,

packages/start/src/server/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
export { default as lazy } from "../shared/lazy.ts";
12
export { getServerFunctionMeta } from "../shared/serverFunction.ts";
23
export { StartServer } from "./StartServer.tsx";
34
export { createHandler } from "./handler.ts";

packages/start/src/server/lazyRoute.tsx

Lines changed: 0 additions & 139 deletions
This file was deleted.

packages/start/src/server/manifest/prod-ssr-manifest.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export function getSsrProdManifest() {
2828
const json: Record<string, any> = {};
2929

3030
const entryKeys = Object.keys(viteManifest)
31-
.filter((id) => viteManifest[id]?.isEntry)
31+
.filter((id) => viteManifest[id]?.isEntry || viteManifest[id]?.isDynamicEntry)
3232
.map((id) => id);
3333

3434
for (const entryKey of entryKeys) {

packages/start/src/server/spa/StartServer.tsx

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,6 @@ export function StartServer(props: { document: Component<DocumentComponentProps>
2727
<props.document
2828
scripts={
2929
<>
30-
<script
31-
nonce={nonce}
32-
innerHTML={`window.manifest = ${JSON.stringify(context.manifest)}`}
33-
/>
3430
<script
3531
type="module"
3632
src={getSsrManifest("client").path(import.meta.env.START_CLIENT_ENTRY)}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// @refresh skip
2+
export { default as lazy } from "../../shared/lazy.ts";
23
export * from "../types.ts";
34
export { StartServer } from "./StartServer.tsx";
45
export { createHandler } from "./handler.ts";

0 commit comments

Comments
 (0)