Skip to content

Commit c223670

Browse files
committed
extract styles
1 parent 0a0c7ce commit c223670

File tree

10 files changed

+122
-41
lines changed

10 files changed

+122
-41
lines changed

bun.lockb

-9.05 KB
Binary file not shown.

packages/gemi/server/dev.ts

+6-13
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
import { Serve } from "bun";
12
import { join } from "path";
3+
24
import type { App } from "../app/App";
3-
import { createStyles } from "./styles";
5+
import { createDevStyles } from "./styles";
46
import { renderErrorPage } from "./renderErrorPage";
5-
import { Serve } from "bun";
67

78
const rootDir = process.cwd();
89
const appDir = join(rootDir, "app");
@@ -126,18 +127,10 @@ export async function startDevServer() {
126127
}
127128

128129
const loaders = `{${templates.join(",")}}`;
129-
const { default: css } = await vite.ssrLoadModule(
130-
`${appDir}/app.css`,
131-
);
132-
133-
const styles = [];
134-
styles.push({
135-
isDev: true,
136-
id: `${appDir}/app.css`,
137-
content: css,
138-
});
130+
139131
return await result({
140-
getStyles: () => createStyles(styles),
132+
getStyles: async (currentViews: string[]) =>
133+
await createDevStyles(appDir, vite, currentViews),
141134
bootstrapModules: [
142135
"/refresh.js",
143136
"/app/client.tsx",

packages/gemi/server/styles.tsx

+86-13
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,90 @@
1-
export function createStyles(styles) {
2-
return styles.map((style, i) => {
3-
if (style.isDev) {
4-
return (
5-
<style key={style.id} type="text/css" data-vite-dev-id={style.id}>
6-
{style.content}
7-
</style>
8-
);
9-
} else {
10-
return (
11-
<style key={i} id={style?.id} type="text/css">
12-
{style.content}
13-
</style>
1+
import { ModuleNode, type ViteDevServer } from "vite";
2+
3+
function replaceStrings(text: string, record: Record<string, string>): string {
4+
const escapedKeys = Object.keys(record)
5+
.sort((a, b) => b.length - a.length)
6+
.map((key) => key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
7+
8+
// Create a single regex with all keys
9+
const regex = new RegExp(escapedKeys.join("|"), "g");
10+
11+
return text.replace(regex, (match) => record[match]);
12+
}
13+
14+
export async function createDevStyles(
15+
appDir: string,
16+
vite: ViteDevServer,
17+
currentViews: string[],
18+
) {
19+
const views = [
20+
...currentViews.map((view) => `${appDir}/views/${view}.tsx`),
21+
`${appDir}/views/RootLayout.tsx`,
22+
];
23+
24+
let modules = new Set<ModuleNode>();
25+
for (const view of views) {
26+
const mod = vite.moduleGraph.getModulesByFile(view);
27+
if (mod) {
28+
modules = modules.union(mod);
29+
}
30+
}
31+
32+
const styles = [];
33+
const cssModules = [];
34+
const cssModuleContent: Record<string, string> = {};
35+
for (const mod of modules as any) {
36+
if (mod) {
37+
for (const imported of mod.importedModules) {
38+
if (imported.file.includes("module.css")) {
39+
cssModuleContent[imported.file] =
40+
imported.ssrTransformResult.map.sourcesContent.join("");
41+
}
42+
if (imported.file.includes(".css")) {
43+
console.log(imported.file);
44+
cssModules.push(imported.file);
45+
}
46+
}
47+
}
48+
}
49+
50+
for (const cssModulePath of cssModules) {
51+
const transform = await vite.ssrLoadModule(cssModulePath, {
52+
fixStacktrace: true,
53+
});
54+
55+
const isCssModule = cssModulePath.includes("module.css");
56+
57+
let transformedCssModule = "";
58+
59+
if (isCssModule) {
60+
transformedCssModule = replaceStrings(
61+
cssModuleContent[cssModulePath],
62+
transform.default,
1463
);
1564
}
65+
66+
styles.push({
67+
isDev: true,
68+
id: cssModulePath,
69+
content: isCssModule ? transformedCssModule : transform.default,
70+
});
71+
}
72+
73+
return styles.map((style, i) => {
74+
return (
75+
<style key={i} type="text/css" data-vite-dev-id={style.id}>
76+
{style.content}
77+
</style>
78+
);
79+
});
80+
}
81+
82+
export async function createStyles(styles = []) {
83+
return styles.map((style, i) => {
84+
return (
85+
<style key={i} id={style?.id} type="text/css">
86+
{style.content}
87+
</style>
88+
);
1689
});
1790
}

packages/gemi/services/router/ViewRouterServiceContainer.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -255,10 +255,11 @@ export class ViewRouterServiceContainer extends ServiceContainer {
255255
const stream = await renderToReadableStream(
256256
createElement(Fragment, {
257257
children: [
258-
await getStyles(currentViews),
258+
...(await getStyles(currentViews)),
259259
createElement(Root, {
260260
data: result.data,
261261
viewImportMap,
262+
key: "root",
262263
}),
263264
],
264265
}),

templates/saas-starter/app/http/routes/view.ts

+1-4
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,7 @@ export default class extends ViewRouter {
1515
middlewares = ["cache:public,12840,must-revalidate"];
1616

1717
override routes = {
18-
"/": this.layout("PublicLayout", {
19-
"/": this.view("Home", [HomeController, "index"]),
20-
"/:testId": this.view("Test"),
21-
}),
18+
"/": this.view("Home", [HomeController, "index"]),
2219
"/auth": AuthViewRouter,
2320
};
2421
}
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
11
import { QueueServiceProvider } from "gemi/services";
2-
import { TestJob } from "../jobs/TestJob";
32

4-
export default class extends QueueServiceProvider {
5-
jobs = [TestJob];
6-
}
3+
export default class extends QueueServiceProvider {}

templates/saas-starter/app/views/Dashboard.tsx

-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ export default function Dashboard() {
22
return (
33
<div className="p-4">
44
<h1>Dashboard</h1>
5-
<div>Hello</div>
65
</div>
76
);
87
}
+16-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
import { Link } from "gemi/client";
2+
import styles from "./test.module.css";
3+
14
export default function Home() {
2-
return <div>Home</div>;
5+
return (
6+
<div>
7+
<div>
8+
<h1>Home</h1>
9+
</div>
10+
<div className={styles.awesome}>Hidden</div>
11+
<div>
12+
<Link className={styles.bgRed} href="/auth/sign-in">
13+
Sign In
14+
</Link>
15+
</div>
16+
</div>
17+
);
318
}

templates/saas-starter/app/views/RootLayout.tsx

+2-4
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ interface Props {
44
children: React.ReactNode;
55
}
66

7-
const RootLayout = (props: Props) => {
7+
export default function RootLayout(props: Props) {
88
return (
99
<html>
1010
<head>
@@ -15,6 +15,4 @@ const RootLayout = (props: Props) => {
1515
<body>{props.children}</body>
1616
</html>
1717
);
18-
};
19-
20-
export default RootLayout;
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
.bgRed {
3+
background: red;
4+
}
5+
6+
.awesome {
7+
visibility: visible;
8+
}

0 commit comments

Comments
 (0)