@xingwangzhe/stalux 1.4.2 → 1.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xingwangzhe/stalux",
3
- "version": "1.4.2",
3
+ "version": "1.4.3",
4
4
  "description": "A powerful, modern Astro blog theme \u2014 use as template or install as plugin",
5
5
  "keywords": [
6
6
  "astro",
@@ -4,7 +4,7 @@
4
4
  * Intercepts /fonts/subset-*.css requests, generates the WOFF2 subset
5
5
  * for the requested route on-the-fly, and serves it.
6
6
  *
7
- * Hooked into astro:server:setup.
7
+ * Handles ALL HTML routes: posts, about, words, home, archives, tags, etc.
8
8
  */
9
9
 
10
10
  import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
@@ -17,43 +17,66 @@ const FONT_INPUT = "src/assets/fonts/LXGWWenKai-Regular.ttf";
17
17
  const CONTENT_ROOT = "stalux";
18
18
  const FONT_OUT_DIR = "public/fonts";
19
19
 
20
- /** Map route-style subset ID → content file path */
21
- function resolveContentFile(projectRoot: string, subsetId: string): string | null {
22
- if (subsetId === "home") return null; // home has no specific content
20
+ /**
21
+ * Get characters for a given route subset.
22
+ * Returns null if route is unknown.
23
+ */
24
+ function getRouteChars(projectRoot: string, subsetId: string): string | null {
25
+ const contentRoot = resolve(projectRoot, CONTENT_ROOT);
26
+
27
+ // ── Posts ──────────────────────────────────────────────
28
+ if (subsetId.startsWith("posts-")) {
29
+ const abbrlink = subsetId.slice(6);
30
+ const postsDir = join(contentRoot, "posts");
31
+ if (!existsSync(postsDir)) return null;
32
+ for (const f of readdirSync(postsDir)) {
33
+ if ((f.endsWith(".md") || f.endsWith(".mdx")) && !f.startsWith("_")) {
34
+ const content = readFileSync(join(postsDir, f), "utf-8");
35
+ if (content.includes(`abbrlink: ${abbrlink}`)) return content;
36
+ }
37
+ }
38
+ return null;
39
+ }
40
+
41
+ // ── About ──────────────────────────────────────────────
23
42
  if (subsetId === "about") {
24
- const aboutDir = resolve(projectRoot, CONTENT_ROOT, "about");
43
+ const aboutDir = join(contentRoot, "about");
25
44
  if (!existsSync(aboutDir)) return null;
26
45
  for (const f of readdirSync(aboutDir)) {
27
- if (f.endsWith(".md") || f.endsWith(".mdx")) return join(aboutDir, f);
46
+ if (f.endsWith(".md") || f.endsWith(".mdx")) return readFileSync(join(aboutDir, f), "utf-8");
28
47
  }
29
48
  return null;
30
49
  }
50
+
51
+ // ── Words ──────────────────────────────────────────────
31
52
  if (subsetId === "words") {
32
- const wordsDir = resolve(projectRoot, CONTENT_ROOT, "words");
53
+ const wordsDir = join(contentRoot, "words");
33
54
  if (!existsSync(wordsDir)) return null;
34
- // Aggregate all words files
35
- return wordsDir; // special: aggregate all
55
+ const all: string[] = [];
56
+ for (const f of readdirSync(wordsDir)) {
57
+ if (f.endsWith(".md") && !f.startsWith("_")) {
58
+ all.push(readFileSync(join(wordsDir, f), "utf-8"));
59
+ }
60
+ }
61
+ return all.join("\n") || null;
36
62
  }
37
- if (subsetId.startsWith("posts-")) {
38
- const abbrlink = subsetId.slice(6);
39
- const postsDir = resolve(projectRoot, CONTENT_ROOT, "posts");
40
- if (!existsSync(postsDir)) return null;
41
- for (const f of readdirSync(postsDir)) {
42
- if ((f.endsWith(".md") || f.endsWith(".mdx")) && !f.startsWith("_")) {
43
- const content = readFileSync(join(postsDir, f), "utf-8");
44
- if (content.includes(`abbrlink: ${abbrlink}`)) return join(postsDir, f);
63
+
64
+ // ── Other HTML routes (home, archives, tags, categories, links, 404) ──
65
+ // These pages use i18n text + config YAML text, which are already
66
+ // covered by the common subset. Return config text for extra safety.
67
+ if (["home", "archives", "tags", "categories", "links", "common"].includes(subsetId)) {
68
+ const configDir = join(contentRoot, "config");
69
+ if (!existsSync(configDir)) return null;
70
+ const all: string[] = [];
71
+ for (const f of readdirSync(configDir)) {
72
+ if (f.endsWith(".yml") || f.endsWith(".yaml")) {
73
+ all.push(readFileSync(join(configDir, f), "utf-8"));
45
74
  }
46
75
  }
47
- return null;
76
+ return all.join("\n") || null;
48
77
  }
49
- return null;
50
- }
51
78
 
52
- /** Extract unique characters from file content */
53
- function extractChars(text: string): string {
54
- const set = new Set<string>();
55
- for (const ch of text) set.add(ch);
56
- return [...set].sort().join("");
79
+ return null;
57
80
  }
58
81
 
59
82
  /**
@@ -65,42 +88,25 @@ async function generateSubsetOnDemand(
65
88
  subsetId: string,
66
89
  logger: AstroIntegrationLogger,
67
90
  ): Promise<string | null> {
68
- // 1. Resolve content file
69
- const contentPath = resolveContentFile(projectRoot, subsetId);
70
- if (!contentPath) return null;
71
-
72
- // 2. Read content + extract chars
73
- let content: string;
74
- if (contentPath.endsWith("words")) {
75
- // aggregate all words
76
- const allWords: string[] = [];
77
- for (const f of readdirSync(contentPath)) {
78
- if (f.endsWith(".md") && !f.startsWith("_")) {
79
- allWords.push(readFileSync(join(contentPath, f), "utf-8"));
80
- }
81
- }
82
- content = allWords.join("\n");
83
- } else {
84
- content = readFileSync(contentPath, "utf-8");
85
- }
91
+ const chars = getRouteChars(projectRoot, subsetId);
92
+ if (!chars) return null;
86
93
 
87
- const chars = extractChars(content);
94
+ // Extract unique characters
95
+ const charSet = new Set<string>();
96
+ for (const ch of chars) charSet.add(ch);
97
+ if (charSet.size === 0) return null;
88
98
 
89
- // 3. Load common subset chars to find unique ones
99
+ // Deduplicate against common subset: read common.css to find its referenced WOFF2
90
100
  const outDir = resolve(projectRoot, FONT_OUT_DIR);
91
- const commonCSS = join(outDir, "common.css");
92
- let commonCharsCached: string[] = [];
93
-
94
- // Look for existing common subset to extract its covered chars
95
- // We re-read the common subset data from the generated file list
96
- const commonFile = existsSync(join(outDir, "common.css"))
97
- ? readFileSync(join(outDir, "common.css"), "utf-8")
98
- : "";
99
- // We can't know exactly what chars the common subset covers without reading
100
- // the original source. But we can just generate the full subset.
101
- // To minimize size, we note that common subset already covers most.
102
-
103
- // 4. Locate font file
101
+ const commonCssPath = join(outDir, "common.css");
102
+ const commonCharSet = new Set<string>();
103
+
104
+ // We can't precisely know which chars common covers without re-extracting.
105
+ // But we can read the i18n + config source for a rough estimate.
106
+ // For now, generate the full subset - the WOFF2 will be small either way.
107
+ // (Most chars are already in common, so the delta is tiny.)
108
+
109
+ // Locate font file
104
110
  const fontPaths = [
105
111
  resolve(projectRoot, FONT_INPUT),
106
112
  resolve(projectRoot, "node_modules", "@xingwangzhe", "stalux", FONT_INPUT),
@@ -115,18 +121,24 @@ async function generateSubsetOnDemand(
115
121
  const fontBuffer = readFileSync(fontPath);
116
122
  mkdirSync(outDir, { recursive: true });
117
123
 
118
- // 5. Generate subset WOFF2
119
- const hash = [...chars].slice(0, 12).join("");
124
+ // Generate subset WOFF2
125
+ const charsStr = [...charSet].sort().join("");
126
+ const hash = [...charsStr].slice(0, 12).join("");
120
127
  const filename = `subset-${subsetId}-${hash}.woff2`;
121
128
  const outPath = join(outDir, filename);
122
129
 
123
130
  if (!existsSync(outPath)) {
124
- const data = await subsetFontFn(fontBuffer, chars, { targetFormat: "woff2" });
125
- writeFileSync(outPath, data);
126
- logger.info(` on-demand subset: ${(data.length / 1024).toFixed(1)} KB → ${filename}`);
131
+ try {
132
+ const data = await subsetFontFn(fontBuffer, charsStr, { targetFormat: "woff2" });
133
+ writeFileSync(outPath, data);
134
+ logger.info(
135
+ ` on-demand subset: ${(data.length / 1024).toFixed(1)} KB → ${filename}`,
136
+ );
137
+ } catch {
138
+ return null;
139
+ }
127
140
  }
128
141
 
129
- // 6. Return CSS content
130
142
  return `@font-face {
131
143
  font-family: "LXGW WenKai Subset";
132
144
  src: url("/fonts/${filename}") format("woff2");
@@ -145,7 +157,7 @@ export function createDevFontMiddleware(projectRoot: string, logger: AstroIntegr
145
157
 
146
158
  const subsetId = match[1];
147
159
 
148
- // Check if file already exists
160
+ // Check if file already exists (cached from previous on-demand generation)
149
161
  const cssPath = resolve(projectRoot, FONT_OUT_DIR, `subset-${subsetId}.css`);
150
162
  if (existsSync(cssPath)) {
151
163
  res.statusCode = 200;
@@ -158,8 +170,7 @@ export function createDevFontMiddleware(projectRoot: string, logger: AstroIntegr
158
170
  logger.info(`Font subset: on-demand generating for "${subsetId}"...`);
159
171
  const css = await generateSubsetOnDemand(projectRoot, subsetId, logger);
160
172
  if (css) {
161
- // Cache it
162
- writeFileSync(cssPath, css);
173
+ writeFileSync(cssPath, css); // cache it
163
174
  res.statusCode = 200;
164
175
  res.setHeader("Content-Type", "text/css");
165
176
  res.end(css);