Skip to content
This repository was archived by the owner on Jul 6, 2025. It is now read-only.

Commit 5e6203b

Browse files
committed
Improve atomic css generating
1 parent 6185647 commit 5e6203b

File tree

9 files changed

+87
-65
lines changed

9 files changed

+87
-65
lines changed

lib/helpers.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ export function globalIt<T>(name: string, fn: () => T): T {
7171
return cache;
7272
}
7373
const ret = fn();
74-
Reflect.set(globalThis, name, ret);
74+
if (ret !== undefined) {
75+
Reflect.set(globalThis, name, ret);
76+
}
7577
return ret;
7678
}

server/bundle_css.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@ export type BundleCSSResult = {
2121

2222
export async function bundleCSS(
2323
specifier: string,
24-
rawCode: string,
24+
sourceCode: string,
2525
options: BundleCSSOptions,
2626
tracing = new Set<string>(),
2727
): Promise<BundleCSSResult> {
28-
let { code: css, dependencies, exports } = await transformCSS(specifier, rawCode, {
28+
let { code: css, dependencies, exports } = await transformCSS(specifier, sourceCode, {
2929
...options,
3030
analyzeDependencies: true,
3131
drafts: {

server/config.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ import { basename, dirname, globToRegExp, join } from "https://deno.land/[email protected]
22
import { JSONC } from "https://deno.land/x/[email protected]/src/jsonc.ts";
33
import { findFile } from "../lib/fs.ts";
44
import { globalIt } from "../lib/helpers.ts";
5+
import { createGenerator } from "https://esm.sh/@unocss/[email protected]";
56
import log from "../lib/log.ts";
67
import util from "../lib/util.ts";
78
import { isCanary, VERSION } from "../version.ts";
8-
import type { ImportMap, ModuleLoader } from "./types.ts";
9+
import type { AlephConfig, ImportMap, ModuleLoader } from "./types.ts";
910

1011
export type JSXConfig = {
1112
jsxRuntime?: "react" | "preact";
@@ -25,6 +26,16 @@ export function getAlephPkgUri() {
2526
});
2627
}
2728

29+
export function getUnoGenerator() {
30+
return globalIt("__UNO_GENERATOR", () => {
31+
const config: AlephConfig | undefined = Reflect.get(globalThis, "__ALEPH_CONFIG");
32+
if (config?.atomicCSS?.presets?.length) {
33+
return createGenerator(config.atomicCSS);
34+
}
35+
return null;
36+
});
37+
}
38+
2839
export async function loadJSXConfig(importMap: ImportMap): Promise<JSXConfig> {
2940
const jsxConfig: JSXConfig = {};
3041
const denoConfigFile = await findFile(["deno.jsonc", "deno.json", "tsconfig.json"]);

server/graph.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
export type Module = {
22
readonly specifier: string;
3+
readonly sourceCode: string;
34
readonly version: number;
45
readonly deps?: ReadonlyArray<DependencyDescriptor>;
56
readonly inlineCSS?: string;
7+
readonly atomicCSS?: boolean;
68
};
79

810
export type DependencyDescriptor = {
@@ -48,8 +50,9 @@ export class DependencyGraph {
4850
return prev;
4951
}
5052

51-
const mod = {
53+
const mod: Module = {
5254
specifier,
55+
sourceCode: "",
5356
version: this.#initialVersion,
5457
...props,
5558
};

server/mod.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@ export const serve = (options: ServerOptions = {}) => {
6565
jsxConfig,
6666
buildHash,
6767
buildTarget: config?.build?.target,
68-
atomicCSS: config?.atomicCSS,
6968
});
7069
}
7170

@@ -82,7 +81,6 @@ export const serve = (options: ServerOptions = {}) => {
8281
loaded,
8382
buildHash,
8483
buildTarget: config?.build?.target,
85-
atomicCSS: config?.atomicCSS,
8684
});
8785
} catch (err) {
8886
if (!(err instanceof Deno.errors.NotFound)) {
@@ -328,6 +326,9 @@ export const serve = (options: ServerOptions = {}) => {
328326
// inject global `__ALEPH_CONFIG`
329327
Reflect.set(globalThis, "__ALEPH_CONFIG", Object.assign({}, config));
330328

329+
// delete previous `__UNO_GENERATOR`
330+
Reflect.deleteProperty(globalThis, "__UNO_GENERATOR");
331+
331332
const { hostname, port = 8080, certFile, keyFile, signal } = options;
332333
if (Deno.env.get("ALEPH_CLI")) {
333334
Reflect.set(globalThis, "__ALEPH_SERVER", { hostname, port, certFile, keyFile, handler, signal });

server/proxy_modules.ts

Lines changed: 8 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { createGenerator } from "https://esm.sh/@unocss/[email protected]";
21
import MagicString from "https://esm.sh/[email protected]";
32
import { parseDeps } from "../compiler/mod.ts";
43
import { builtinModuleExts } from "../lib/helpers.ts";
@@ -8,7 +7,7 @@ import { serveDir } from "../lib/serve.ts";
87
import util from "../lib/util.ts";
98
import { bundleCSS } from "./bundle_css.ts";
109
import { DependencyGraph } from "./graph.ts";
11-
import type { AlephConfig, ImportMap, ModuleLoader, ModuleLoaderContent, ModuleLoaderEnv } from "./types.ts";
10+
import type { ImportMap, ModuleLoader, ModuleLoaderContent, ModuleLoaderEnv } from "./types.ts";
1211

1312
const cssModuleLoader = async (pathname: string, env: ModuleLoaderEnv) => {
1413
const specifier = "." + pathname;
@@ -39,32 +38,16 @@ const cssModuleLoader = async (pathname: string, env: ModuleLoaderEnv) => {
3938
};
4039

4140
const esModuleLoader = async (input: { pathname: string } & ModuleLoaderContent, env: ModuleLoaderEnv) => {
42-
const { code: rawCode, pathname, lang } = input;
43-
const config: AlephConfig | undefined = Reflect.get(globalThis, "__ALEPH_CONFIG");
41+
const { code: sourceCode, pathname, lang, inlineCSS } = input;
4442
const specifier = "." + pathname;
45-
const isJSX = lang === "tsx" || lang === "jsx" || pathname.endsWith(".jsx") || pathname.endsWith(".tsx");
43+
const atomicCSS = input.atomicCSS || pathname.endsWith(".jsx") || pathname.endsWith(".tsx");
4644
const contentType = lang ? getContentType(`file.${lang}`) : undefined;
4745
const serverDependencyGraph: DependencyGraph | undefined = Reflect.get(globalThis, "serverDependencyGraph");
4846
if (serverDependencyGraph) {
49-
let inlineCSS = input.inlineCSS;
50-
if (Boolean(config?.atomicCSS?.presets?.length) && isJSX) {
51-
const uno = createGenerator(config?.atomicCSS);
52-
const { css } = await uno.generate(rawCode, {
53-
id: specifier,
54-
minify: !env.isDev,
55-
});
56-
if (css) {
57-
if (inlineCSS) {
58-
inlineCSS = `${inlineCSS}\n${css}`;
59-
} else {
60-
inlineCSS = css;
61-
}
62-
}
63-
}
64-
const deps = await parseDeps(specifier, rawCode, { importMap: JSON.stringify(env.importMap) });
65-
serverDependencyGraph.mark(specifier, { deps, inlineCSS });
47+
const deps = await parseDeps(specifier, sourceCode, { importMap: JSON.stringify(env.importMap) });
48+
serverDependencyGraph.mark(specifier, { sourceCode, deps, inlineCSS, atomicCSS });
6649
if (deps.length) {
67-
const s = new MagicString(rawCode);
50+
const s = new MagicString(sourceCode);
6851
deps.forEach((dep) => {
6952
const { specifier, importUrl, loc } = dep;
7053
if (loc) {
@@ -83,12 +66,12 @@ const esModuleLoader = async (input: { pathname: string } & ModuleLoaderContent,
8366
return { content: s.toString(), contentType };
8467
}
8568
return {
86-
content: rawCode,
69+
content: sourceCode,
8770
contentType,
8871
};
8972
}
9073
return {
91-
content: rawCode,
74+
content: sourceCode,
9275
contentType,
9376
};
9477
};

server/renderer.ts

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ import { builtinModuleExts, FetchError, toLocalPath } from "../lib/helpers.ts";
22
import { type Comment, type Element, HTMLRewriter } from "../lib/html.ts";
33
import log from "../lib/log.ts";
44
import util from "../lib/util.ts";
5-
import { getAlephPkgUri } from "./config.ts";
6-
import type { DependencyGraph } from "./graph.ts";
7-
import { importRouteModule } from "./routing.ts";
85
import type { RouteModule, Routes } from "../lib/route.ts";
96
import { matchRoutes } from "../lib/route.ts";
7+
import { getAlephPkgUri, getUnoGenerator } from "./config.ts";
8+
import type { DependencyGraph } from "./graph.ts";
9+
import { importRouteModule } from "./routing.ts";
1010

1111
export type SSRContext = {
1212
readonly url: URL;
@@ -72,25 +72,45 @@ export default {
7272
const body = await render(ssrContext);
7373
const serverDependencyGraph: DependencyGraph | undefined = Reflect.get(globalThis, "serverDependencyGraph");
7474
if (serverDependencyGraph) {
75-
const styles: string[] = [];
75+
const atomicCSSSource: string[] = [];
7676
for (const { filename } of routeModules) {
7777
serverDependencyGraph.walk(filename, (mod) => {
78+
if (mod.atomicCSS) {
79+
atomicCSSSource.push(mod.sourceCode);
80+
}
7881
if (mod.inlineCSS) {
79-
styles.push(`<style data-module-id="${mod.specifier}">${mod.inlineCSS}</style>`);
82+
headCollection.push(`<style data-module-id="${mod.specifier}">${mod.inlineCSS}</style>`);
8083
}
8184
});
8285
}
8386
for (const serverEntry of builtinModuleExts.map((ext) => `./server.${ext}`)) {
8487
if (serverDependencyGraph.get(serverEntry)) {
8588
serverDependencyGraph.walk(serverEntry, (mod) => {
89+
if (mod.atomicCSS) {
90+
atomicCSSSource.push(mod.sourceCode);
91+
}
8692
if (mod.inlineCSS) {
87-
styles.push(`<style data-module-id="${mod.specifier}">${mod.inlineCSS}</style>`);
93+
headCollection.push(`<style data-module-id="${mod.specifier}">${mod.inlineCSS}</style>`);
8894
}
8995
});
9096
break;
9197
}
9298
}
93-
headCollection.push(...styles);
99+
if (atomicCSSSource.length > 0) {
100+
// todo: cache the atomic CSS in production mode
101+
const unoGenerator = getUnoGenerator();
102+
if (unoGenerator) {
103+
const start = performance.now();
104+
const { css } = await unoGenerator.generate(atomicCSSSource.join("\n"));
105+
if (css) {
106+
headCollection.push(
107+
`<style data-unocss="${unoGenerator.version}" data-build-time="${
108+
performance.now() - start
109+
}ms">${css}</style>`,
110+
);
111+
}
112+
}
113+
}
94114
}
95115
const ttls = routeModules.filter(({ dataCacheTtl }) =>
96116
typeof dataCacheTtl === "number" && !Number.isNaN(dataCacheTtl) && dataCacheTtl > 0

server/routing.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@ const revivedModules: Map<string, Record<string, unknown>> = new Map();
1212

1313
/** revive a route module. */
1414
export function revive(filename: string, module: Record<string, unknown>) {
15-
revivedModules.set(filename, module);
15+
if (!Deno.env.get("ALEPH_CLI")) {
16+
revivedModules.set(filename, module);
17+
}
1618
}
1719

1820
/** import the route module. */
1921
export async function importRouteModule(filename: string) {
2022
let mod: Record<string, unknown>;
21-
if (!Deno.env.get("ALEPH_CLI") && revivedModules.has(filename)) {
23+
if (revivedModules.has(filename)) {
2224
mod = revivedModules.get(filename)!;
2325
} else {
2426
const graph: DependencyGraph | undefined = Reflect.get(globalThis, "serverDependencyGraph");

server/transformer.ts

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { createGenerator } from "https://esm.sh/@unocss/[email protected]";
21
import MagicString from "https://esm.sh/[email protected]";
32
import { parseDeps, transform } from "../compiler/mod.ts";
43
import type { TransformOptions, TransformResult } from "../compiler/types.ts";
@@ -7,18 +6,17 @@ import { restoreUrl, toLocalPath } from "../lib/helpers.ts";
76
import log from "../lib/log.ts";
87
import util from "../lib/util.ts";
98
import { bundleCSS } from "./bundle_css.ts";
10-
import { getAlephPkgUri, type JSXConfig } from "./config.ts";
9+
import { getAlephPkgUri, getUnoGenerator, type JSXConfig } from "./config.ts";
1110
import { isRouteFile } from "./routing.ts";
1211
import { DependencyGraph } from "./graph.ts";
13-
import type { AtomicCSSConfig, ImportMap, ModuleLoaderContent } from "./types.ts";
12+
import type { ImportMap, ModuleLoaderContent } from "./types.ts";
1413

1514
export type TransformerOptions = {
1615
buildHash: string;
1716
buildTarget?: TransformOptions["target"];
1817
importMap: ImportMap;
1918
isDev: boolean;
2019
jsxConfig?: JSXConfig;
21-
atomicCSS?: AtomicCSSConfig;
2220
loaded?: ModuleLoaderContent;
2321
};
2422

@@ -27,28 +25,28 @@ export default {
2725
const { isDev, buildHash, loaded } = options;
2826
const { pathname, searchParams, search } = new URL(req.url);
2927
const specifier = pathname.startsWith("/-/") ? restoreUrl(pathname + search) : `.${pathname}`;
30-
let rawCode: string;
28+
let sourceCode: string;
3129
let mtime: number | undefined;
3230
let lang: string | undefined;
3331
let isCSS: boolean;
3432
let uno: boolean;
3533
if (loaded) {
36-
rawCode = loaded.code;
34+
sourceCode = loaded.code;
3735
mtime = loaded.modtime;
3836
lang = loaded.lang;
3937
isCSS = loaded.lang === "css";
4038
uno = !!loaded.atomicCSS;
4139
} else {
4240
let codeType: string;
43-
[rawCode, mtime, codeType] = await readCode(specifier);
41+
[sourceCode, mtime, codeType] = await readCode(specifier);
4442
isCSS = codeType.startsWith("text/css");
4543
uno = pathname.endsWith(".jsx") || pathname.endsWith(".tsx");
4644
}
4745
const etag = mtime
48-
? `${mtime.toString(16)}-${rawCode.length.toString(16)}-${
49-
rawCode.charCodeAt(Math.floor(rawCode.length / 2)).toString(16)
46+
? `${mtime.toString(16)}-${sourceCode.length.toString(16)}-${
47+
sourceCode.charCodeAt(Math.floor(sourceCode.length / 2)).toString(16)
5048
}${buildHash.slice(0, 8)}`
51-
: await util.computeHash("sha-1", rawCode + buildHash);
49+
: await util.computeHash("sha-1", sourceCode + buildHash);
5250
if (req.headers.get("If-None-Match") === etag) {
5351
return new Response(null, { status: 304 });
5452
}
@@ -66,7 +64,7 @@ export default {
6664

6765
if (isCSS) {
6866
const asJsModule = searchParams.has("module");
69-
const { code, deps } = await bundleCSS(specifier, rawCode, {
67+
const { code, deps } = await bundleCSS(specifier, sourceCode, {
7068
// todo: support borwserslist
7169
targets: {
7270
android: 95,
@@ -87,13 +85,13 @@ export default {
8785
}
8886
} else {
8987
const alephPkgUri = getAlephPkgUri();
90-
const { atomicCSS, jsxConfig, importMap, buildTarget } = options;
88+
const { jsxConfig, importMap, buildTarget } = options;
9189
let ret: TransformResult;
9290
if (/^https?:\/\/(cdn\.)esm\.sh\//.test(specifier)) {
9391
// don't transform modules imported from esm.sh
94-
const deps = await parseDeps(specifier, rawCode, { importMap: JSON.stringify(importMap) });
92+
const deps = await parseDeps(specifier, sourceCode, { importMap: JSON.stringify(importMap) });
9593
if (deps.length > 0) {
96-
const s = new MagicString(rawCode);
94+
const s = new MagicString(sourceCode);
9795
deps.forEach((dep) => {
9896
const { importUrl, loc } = dep;
9997
if (loc) {
@@ -102,7 +100,7 @@ export default {
102100
});
103101
ret = { code: s.toString(), deps };
104102
} else {
105-
ret = { code: rawCode, deps };
103+
ret = { code: sourceCode, deps };
106104
}
107105
} else {
108106
const graphVersions = clientDependencyGraph.modules.filter((mod) =>
@@ -111,7 +109,7 @@ export default {
111109
acc[specifier] = version.toString(16);
112110
return acc;
113111
}, {} as Record<string, string>);
114-
ret = await transform(specifier, rawCode, {
112+
ret = await transform(specifier, sourceCode, {
115113
...jsxConfig,
116114
lang: lang as TransformOptions["lang"],
117115
stripDataExport: isRouteFile(specifier),
@@ -125,13 +123,15 @@ export default {
125123
}
126124
let { code, map, deps } = ret;
127125
let inlineCSS = loaded?.inlineCSS;
128-
if (uno && Boolean(atomicCSS?.presets?.length)) {
129-
const uno = createGenerator(atomicCSS);
130-
const { css } = await uno.generate(rawCode, { id: specifier, minify: !isDev });
131-
if (inlineCSS) {
132-
inlineCSS = `${inlineCSS}\n${css}`;
133-
} else {
134-
inlineCSS = css;
126+
if (uno) {
127+
const unoGenerator = getUnoGenerator();
128+
if (unoGenerator) {
129+
const { css } = await unoGenerator.generate(sourceCode, { id: specifier, minify: !isDev });
130+
if (inlineCSS) {
131+
inlineCSS = `${inlineCSS}\n${css}`;
132+
} else {
133+
inlineCSS = css;
134+
}
135135
}
136136
}
137137
if (inlineCSS) {
@@ -147,7 +147,7 @@ export default {
147147
if (!util.isLikelyHttpURL(specifier)) {
148148
m.sources = [`file://source/${util.trimPrefix(specifier, ".")}`];
149149
}
150-
m.sourcesContent = [rawCode];
150+
m.sourcesContent = [sourceCode];
151151
resBody = code +
152152
`\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,${btoa(JSON.stringify(m))}\n`;
153153
} catch (e) {

0 commit comments

Comments
 (0)