veryfront 0.1.26 → 0.1.28
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/README.md +3 -11
- package/esm/cli/app/shell.d.ts.map +1 -1
- package/esm/cli/app/shell.js +9 -5
- package/esm/cli/commands/demo/demo.js +1 -1
- package/esm/cli/commands/init/catalog.d.ts.map +1 -1
- package/esm/cli/commands/init/catalog.js +13 -5
- package/esm/cli/commands/init/command-help.js +4 -4
- package/esm/cli/commands/init/types.d.ts +1 -1
- package/esm/cli/commands/init/types.d.ts.map +1 -1
- package/esm/cli/commands/serve/command.d.ts.map +1 -1
- package/esm/cli/commands/serve/command.js +0 -4
- package/esm/cli/commands/start/command.d.ts.map +1 -1
- package/esm/cli/commands/start/command.js +16 -9
- package/esm/cli/help/tips.js +6 -6
- package/esm/cli/mcp/remote-file-tools.js +1 -1
- package/esm/cli/mcp/tools/catalog-tools.d.ts +3 -3
- package/esm/cli/mcp/tools/catalog-tools.d.ts.map +1 -1
- package/esm/cli/mcp/tools/catalog-tools.js +21 -13
- package/esm/cli/mcp/tools/project-tools.js +1 -1
- package/esm/cli/templates/index.js +11 -11
- package/esm/cli/templates/manifest.d.ts +22 -15
- package/esm/cli/templates/manifest.js +24 -17
- package/esm/cli/templates/types.d.ts +1 -1
- package/esm/cli/templates/types.d.ts.map +1 -1
- package/esm/cli/utils/index.d.ts.map +1 -1
- package/esm/cli/utils/index.js +13 -1
- package/esm/deno.js +1 -1
- package/esm/src/html/html-shell-generator.d.ts.map +1 -1
- package/esm/src/html/html-shell-generator.js +2 -0
- package/esm/src/html/styles-builder/project-css-cache.d.ts +8 -1
- package/esm/src/html/styles-builder/project-css-cache.d.ts.map +1 -1
- package/esm/src/html/styles-builder/project-css-cache.js +13 -2
- package/esm/src/html/styles-builder/tailwind-compiler.d.ts +2 -0
- package/esm/src/html/styles-builder/tailwind-compiler.d.ts.map +1 -1
- package/esm/src/html/styles-builder/tailwind-compiler.js +52 -19
- package/esm/src/modules/react-loader/css-import-collector.d.ts +29 -0
- package/esm/src/modules/react-loader/css-import-collector.d.ts.map +1 -0
- package/esm/src/modules/react-loader/css-import-collector.js +41 -0
- package/esm/src/modules/react-loader/ssr-module-loader/loader.d.ts.map +1 -1
- package/esm/src/modules/react-loader/ssr-module-loader/loader.js +6 -0
- package/esm/src/modules/react-loader/ssr-module-loader/ssr-dependency-validator.d.ts.map +1 -1
- package/esm/src/modules/react-loader/ssr-module-loader/ssr-dependency-validator.js +5 -0
- package/esm/src/platform/adapters/fs/factory.d.ts.map +1 -1
- package/esm/src/platform/adapters/fs/factory.js +5 -1
- package/esm/src/platform/adapters/fs/veryfront/websocket-manager.d.ts +1 -0
- package/esm/src/platform/adapters/fs/veryfront/websocket-manager.d.ts.map +1 -1
- package/esm/src/platform/adapters/fs/veryfront/websocket-manager.js +19 -5
- package/esm/src/platform/compat/process.d.ts.map +1 -1
- package/esm/src/platform/compat/process.js +20 -3
- package/esm/src/proxy/main.js +31 -12
- package/esm/src/proxy/token-manager.d.ts +2 -0
- package/esm/src/proxy/token-manager.d.ts.map +1 -1
- package/esm/src/proxy/token-manager.js +47 -8
- package/esm/src/rendering/orchestrator/css-candidate-manifest.d.ts +23 -0
- package/esm/src/rendering/orchestrator/css-candidate-manifest.d.ts.map +1 -0
- package/esm/src/rendering/orchestrator/css-candidate-manifest.js +132 -0
- package/esm/src/rendering/orchestrator/html.d.ts +11 -1
- package/esm/src/rendering/orchestrator/html.d.ts.map +1 -1
- package/esm/src/rendering/orchestrator/html.js +103 -18
- package/esm/src/rendering/orchestrator/pipeline.d.ts.map +1 -1
- package/esm/src/rendering/orchestrator/pipeline.js +14 -2
- package/esm/src/server/bootstrap.d.ts +2 -0
- package/esm/src/server/bootstrap.d.ts.map +1 -1
- package/esm/src/server/bootstrap.js +10 -0
- package/esm/src/server/handlers/preview/markdown-html-generator.d.ts.map +1 -1
- package/esm/src/server/handlers/preview/markdown-html-generator.js +11 -5
- package/esm/src/server/production-server.js +10 -2
- package/esm/src/studio/bridge-template.d.ts +2 -0
- package/esm/src/studio/bridge-template.d.ts.map +1 -1
- package/esm/src/studio/bridge-template.js +3390 -52
- package/esm/src/transforms/css-modules/naming.d.ts +33 -0
- package/esm/src/transforms/css-modules/naming.d.ts.map +1 -0
- package/esm/src/transforms/css-modules/naming.js +128 -0
- package/esm/src/transforms/esm/import-parser.d.ts +1 -0
- package/esm/src/transforms/esm/import-parser.d.ts.map +1 -1
- package/esm/src/transforms/esm/import-parser.js +16 -5
- package/esm/src/transforms/pipeline/index.d.ts.map +1 -1
- package/esm/src/transforms/pipeline/index.js +3 -1
- package/esm/src/transforms/pipeline/stages/index.d.ts +1 -0
- package/esm/src/transforms/pipeline/stages/index.d.ts.map +1 -1
- package/esm/src/transforms/pipeline/stages/index.js +1 -0
- package/esm/src/transforms/pipeline/stages/ssr-css-strip.d.ts +18 -0
- package/esm/src/transforms/pipeline/stages/ssr-css-strip.d.ts.map +1 -0
- package/esm/src/transforms/pipeline/stages/ssr-css-strip.js +168 -0
- package/package.json +1 -1
- package/src/cli/app/shell.ts +9 -5
- package/src/cli/commands/demo/demo.ts +1 -1
- package/src/cli/commands/init/catalog.ts +13 -5
- package/src/cli/commands/init/command-help.ts +4 -4
- package/src/cli/commands/init/types.ts +5 -5
- package/src/cli/commands/serve/command.ts +0 -5
- package/src/cli/commands/start/command.ts +15 -10
- package/src/cli/help/tips.ts +6 -6
- package/src/cli/mcp/remote-file-tools.ts +1 -1
- package/src/cli/mcp/tools/catalog-tools.ts +21 -13
- package/src/cli/mcp/tools/project-tools.ts +1 -1
- package/src/cli/templates/index.ts +11 -11
- package/src/cli/templates/manifest.js +24 -17
- package/src/cli/templates/types.ts +5 -5
- package/src/cli/utils/index.ts +12 -1
- package/src/deno.js +1 -1
- package/src/src/html/html-shell-generator.ts +2 -0
- package/src/src/html/styles-builder/project-css-cache.ts +24 -1
- package/src/src/html/styles-builder/tailwind-compiler.ts +67 -26
- package/src/src/modules/react-loader/css-import-collector.ts +50 -0
- package/src/src/modules/react-loader/ssr-module-loader/loader.ts +7 -0
- package/src/src/modules/react-loader/ssr-module-loader/ssr-dependency-validator.ts +6 -0
- package/src/src/platform/adapters/fs/factory.ts +5 -1
- package/src/src/platform/adapters/fs/veryfront/websocket-manager.ts +21 -5
- package/src/src/platform/compat/process.ts +28 -4
- package/src/src/proxy/main.ts +32 -12
- package/src/src/proxy/token-manager.ts +54 -8
- package/src/src/rendering/orchestrator/css-candidate-manifest.ts +176 -0
- package/src/src/rendering/orchestrator/html.ts +128 -16
- package/src/src/rendering/orchestrator/pipeline.ts +183 -165
- package/src/src/server/bootstrap.ts +16 -0
- package/src/src/server/handlers/preview/markdown-html-generator.ts +12 -5
- package/src/src/server/production-server.ts +12 -2
- package/src/src/studio/bridge-template.ts +3392 -52
- package/src/src/transforms/css-modules/naming.ts +152 -0
- package/src/src/transforms/esm/import-parser.ts +15 -5
- package/src/src/transforms/pipeline/index.ts +3 -0
- package/src/src/transforms/pipeline/stages/index.ts +1 -0
- package/src/src/transforms/pipeline/stages/ssr-css-strip.ts +201 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { extractCandidates } from "../../html/styles-builder/tailwind-compiler.js";
|
|
2
|
+
import { getRouteModulePaths } from "../../modules/manifest/route-module-manifest.js";
|
|
3
|
+
import { rendererLogger } from "../../utils/index.js";
|
|
4
|
+
|
|
5
|
+
interface SourceFileLike {
|
|
6
|
+
path: string;
|
|
7
|
+
content?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface CandidateManifest {
|
|
11
|
+
fileCandidates: Map<string, Set<string>>;
|
|
12
|
+
allCandidates: Set<string>;
|
|
13
|
+
builtAt: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface RouteCandidateOptions {
|
|
17
|
+
projectScope: string;
|
|
18
|
+
projectVersion: string;
|
|
19
|
+
projectDir: string;
|
|
20
|
+
routeKey: string;
|
|
21
|
+
routeFilePaths: string[];
|
|
22
|
+
files: SourceFileLike[];
|
|
23
|
+
developmentMode: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const logger = rendererLogger.component("css-candidate-manifest");
|
|
27
|
+
const SOURCE_EXTENSIONS = [".tsx", ".jsx", ".mdx", ".ts", ".js"];
|
|
28
|
+
const DEV_MANIFEST_TTL_MS = 2000;
|
|
29
|
+
|
|
30
|
+
const manifestCache = new Map<string, CandidateManifest>();
|
|
31
|
+
const routeCandidateCache = new Map<string, Set<string>>();
|
|
32
|
+
|
|
33
|
+
function normalizePath(path: string): string {
|
|
34
|
+
return path.replace(/\\/g, "/").replace(/\/{2,}/g, "/");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function toRelativeProjectPath(path: string, projectDir: string): string {
|
|
38
|
+
const normalized = normalizePath(path);
|
|
39
|
+
const normalizedProjectDir = normalizePath(projectDir).replace(/\/+$/, "");
|
|
40
|
+
if (normalized.startsWith(normalizedProjectDir)) {
|
|
41
|
+
return normalized.slice(normalizedProjectDir.length).replace(/^\/+/, "");
|
|
42
|
+
}
|
|
43
|
+
return normalized.replace(/^\/+/, "");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function buildManifestCacheKey(projectScope: string, projectVersion: string): string {
|
|
47
|
+
return `${projectScope}:${projectVersion}`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function shouldRebuildManifest(
|
|
51
|
+
existing: CandidateManifest | undefined,
|
|
52
|
+
developmentMode: boolean,
|
|
53
|
+
): boolean {
|
|
54
|
+
if (!existing) return true;
|
|
55
|
+
if (!developmentMode) return false;
|
|
56
|
+
return (Date.now() - existing.builtAt) > DEV_MANIFEST_TTL_MS;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function buildSourceCandidatePaths(modulePath: string): string[] {
|
|
60
|
+
const normalized = normalizePath(modulePath).replace(/^\/+/, "").replace(/^_vf_modules\//, "");
|
|
61
|
+
if (!normalized.endsWith(".js")) return [normalized];
|
|
62
|
+
const withoutJs = normalized.slice(0, -3);
|
|
63
|
+
return [
|
|
64
|
+
`${withoutJs}.tsx`,
|
|
65
|
+
`${withoutJs}.ts`,
|
|
66
|
+
`${withoutJs}.jsx`,
|
|
67
|
+
`${withoutJs}.mdx`,
|
|
68
|
+
`${withoutJs}.js`,
|
|
69
|
+
];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function buildCandidateManifest(files: SourceFileLike[], projectDir: string): CandidateManifest {
|
|
73
|
+
const fileCandidates = new Map<string, Set<string>>();
|
|
74
|
+
const allCandidates = new Set<string>();
|
|
75
|
+
|
|
76
|
+
for (const file of files) {
|
|
77
|
+
if (!file.content) continue;
|
|
78
|
+
if (!SOURCE_EXTENSIONS.some((ext) => file.path.endsWith(ext))) continue;
|
|
79
|
+
|
|
80
|
+
const candidates = new Set(extractCandidates(file.content));
|
|
81
|
+
const relativePath = toRelativeProjectPath(file.path, projectDir);
|
|
82
|
+
const absolutePath = normalizePath(file.path);
|
|
83
|
+
|
|
84
|
+
fileCandidates.set(relativePath, candidates);
|
|
85
|
+
fileCandidates.set(absolutePath, candidates);
|
|
86
|
+
|
|
87
|
+
for (const cls of candidates) allCandidates.add(cls);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return { fileCandidates, allCandidates, builtAt: Date.now() };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function addCandidatesForPath(
|
|
94
|
+
target: Set<string>,
|
|
95
|
+
manifest: CandidateManifest,
|
|
96
|
+
path: string,
|
|
97
|
+
projectDir: string,
|
|
98
|
+
): void {
|
|
99
|
+
const absolutePath = normalizePath(path);
|
|
100
|
+
const relativePath = toRelativeProjectPath(path, projectDir);
|
|
101
|
+
const candidates = manifest.fileCandidates.get(absolutePath) ??
|
|
102
|
+
manifest.fileCandidates.get(relativePath);
|
|
103
|
+
if (!candidates) return;
|
|
104
|
+
for (const cls of candidates) target.add(cls);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Resolve route-scoped Tailwind candidates from a precomputed per-project manifest.
|
|
109
|
+
*/
|
|
110
|
+
export function getRouteCandidates(options: RouteCandidateOptions): Set<string> {
|
|
111
|
+
const manifestKey = buildManifestCacheKey(options.projectScope, options.projectVersion);
|
|
112
|
+
const existingManifest = manifestCache.get(manifestKey);
|
|
113
|
+
const manifest = shouldRebuildManifest(existingManifest, options.developmentMode)
|
|
114
|
+
? buildCandidateManifest(options.files, options.projectDir)
|
|
115
|
+
: existingManifest!;
|
|
116
|
+
|
|
117
|
+
if (manifest !== existingManifest) {
|
|
118
|
+
manifestCache.set(manifestKey, manifest);
|
|
119
|
+
|
|
120
|
+
// Clear route subsets when project-level file manifest is rebuilt.
|
|
121
|
+
for (const key of routeCandidateCache.keys()) {
|
|
122
|
+
if (key.startsWith(`${manifestKey}:`)) routeCandidateCache.delete(key);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const routeCacheKey = `${manifestKey}:${options.routeKey}`;
|
|
127
|
+
const cachedRoute = routeCandidateCache.get(routeCacheKey);
|
|
128
|
+
if (cachedRoute) return new Set(cachedRoute);
|
|
129
|
+
|
|
130
|
+
const routeCandidates = new Set<string>();
|
|
131
|
+
|
|
132
|
+
for (const path of options.routeFilePaths) {
|
|
133
|
+
addCandidatesForPath(routeCandidates, manifest, path, options.projectDir);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
for (const modulePath of getRouteModulePaths(options.projectScope, options.routeKey)) {
|
|
137
|
+
for (const sourcePath of buildSourceCandidatePaths(modulePath)) {
|
|
138
|
+
addCandidatesForPath(routeCandidates, manifest, sourcePath, options.projectDir);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Fallback to full-project candidates for correctness if route manifest is incomplete.
|
|
143
|
+
if (routeCandidates.size === 0) {
|
|
144
|
+
for (const cls of manifest.allCandidates) routeCandidates.add(cls);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
routeCandidateCache.set(routeCacheKey, routeCandidates);
|
|
148
|
+
|
|
149
|
+
logger.debug("Resolved route candidates", {
|
|
150
|
+
projectScope: options.projectScope,
|
|
151
|
+
projectVersion: options.projectVersion,
|
|
152
|
+
route: options.routeKey,
|
|
153
|
+
count: routeCandidates.size,
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
return new Set(routeCandidates);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Invalidate cached candidate manifests for one project scope (or all scopes).
|
|
161
|
+
*/
|
|
162
|
+
export function invalidateProjectCandidateManifests(projectScope?: string): void {
|
|
163
|
+
if (!projectScope) {
|
|
164
|
+
manifestCache.clear();
|
|
165
|
+
routeCandidateCache.clear();
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
for (const key of manifestCache.keys()) {
|
|
170
|
+
if (key.startsWith(`${projectScope}:`)) manifestCache.delete(key);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
for (const key of routeCandidateCache.keys()) {
|
|
174
|
+
if (key.startsWith(`${projectScope}:`)) routeCandidateCache.delete(key);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
@@ -8,7 +8,6 @@ import {
|
|
|
8
8
|
injectHTMLContent,
|
|
9
9
|
isFullHTMLDocument,
|
|
10
10
|
} from "../../html/index.js";
|
|
11
|
-
import { extractCandidates } from "../../html/styles-builder/tailwind-compiler.js";
|
|
12
11
|
import type { CollectedHead } from "../../react/head-collector.js";
|
|
13
12
|
import type { RuntimeAdapter } from "../../platform/adapters/base.js";
|
|
14
13
|
import type {
|
|
@@ -25,6 +24,11 @@ import { computeSourceHash } from "../../studio/hash-utils.js";
|
|
|
25
24
|
import { extractRelativePath } from "../../utils/route-path-utils.js";
|
|
26
25
|
import { resolveAppComponentPath } from "../layouts/utils/app-resolver.js";
|
|
27
26
|
import { StreamTimeoutError, streamToString } from "../utils/stream-utils.js";
|
|
27
|
+
import {
|
|
28
|
+
normalizeCssModuleKey,
|
|
29
|
+
rewriteCssModuleContent,
|
|
30
|
+
} from "../../transforms/css-modules/naming.js";
|
|
31
|
+
import { getRouteCandidates } from "./css-candidate-manifest.js";
|
|
28
32
|
|
|
29
33
|
const logger = rendererLogger.component("html-generator");
|
|
30
34
|
|
|
@@ -46,6 +50,8 @@ export interface HTMLGenerationContext {
|
|
|
46
50
|
ssrHash: string;
|
|
47
51
|
options?: RenderOptions;
|
|
48
52
|
collectedHead?: CollectedHead;
|
|
53
|
+
/** Absolute paths to CSS files imported by components (collected during module loading) */
|
|
54
|
+
cssImports?: string[];
|
|
49
55
|
}
|
|
50
56
|
|
|
51
57
|
export class HTMLGenerator {
|
|
@@ -306,11 +312,15 @@ export class HTMLGenerator {
|
|
|
306
312
|
mergedFrontmatter: MDXFrontmatter,
|
|
307
313
|
): Promise<HTMLGenerationOptions> {
|
|
308
314
|
const stylesheetPath = this.config.config?.tailwind?.stylesheet || "globals.css";
|
|
309
|
-
const [appComponentPath, globalCSS
|
|
315
|
+
const [appComponentPath, globalCSS] = await Promise.all([
|
|
310
316
|
this.resolveAppPath().then((p) => p ?? undefined),
|
|
311
317
|
this.loadProjectFile(stylesheetPath),
|
|
312
|
-
this.extractProjectClasses(),
|
|
313
318
|
]);
|
|
319
|
+
const projectClasses = await this.extractProjectClassesForRoute(context, appComponentPath);
|
|
320
|
+
|
|
321
|
+
// Load CSS imported by components and merge with globalCSS.
|
|
322
|
+
// Deduplicate against the configured stylesheet to avoid double-loading.
|
|
323
|
+
const combinedCSS = await this.mergeImportedCSS(globalCSS, context.cssImports, stylesheetPath);
|
|
314
324
|
|
|
315
325
|
logger.debug("App component resolution", {
|
|
316
326
|
appComponentPath,
|
|
@@ -351,7 +361,7 @@ export class HTMLGenerator {
|
|
|
351
361
|
pagePath,
|
|
352
362
|
pageType,
|
|
353
363
|
nonce: context.options?.nonce,
|
|
354
|
-
globalCSS,
|
|
364
|
+
globalCSS: combinedCSS,
|
|
355
365
|
frontmatter: mergedFrontmatter,
|
|
356
366
|
studioEmbed: context.options?.studioEmbed,
|
|
357
367
|
projectId: context.options?.projectId,
|
|
@@ -368,8 +378,94 @@ export class HTMLGenerator {
|
|
|
368
378
|
};
|
|
369
379
|
}
|
|
370
380
|
|
|
371
|
-
|
|
372
|
-
|
|
381
|
+
/**
|
|
382
|
+
* Load CSS files imported by components and merge with the global stylesheet.
|
|
383
|
+
* Deduplicates against the configured Tailwind stylesheet path to avoid
|
|
384
|
+
* double-loading globals.css when it's both auto-discovered and explicitly imported.
|
|
385
|
+
*/
|
|
386
|
+
private async mergeImportedCSS(
|
|
387
|
+
globalCSS: string | undefined,
|
|
388
|
+
cssImports: string[] | undefined,
|
|
389
|
+
stylesheetPath: string,
|
|
390
|
+
): Promise<string | undefined> {
|
|
391
|
+
if (!cssImports || cssImports.length === 0) return globalCSS;
|
|
392
|
+
|
|
393
|
+
const normalizedStylesheetPath = stylesheetPath.replace(/^\/+/, "");
|
|
394
|
+
const configuredStylesheetAbsolute = normalizeCssModuleKey(
|
|
395
|
+
join(this.config.projectDir, normalizedStylesheetPath),
|
|
396
|
+
);
|
|
397
|
+
const uniqueImports = new Map<string, string>();
|
|
398
|
+
for (const cssPath of cssImports) {
|
|
399
|
+
const normalized = normalizeCssModuleKey(cssPath);
|
|
400
|
+
if (!uniqueImports.has(normalized)) {
|
|
401
|
+
uniqueImports.set(normalized, cssPath);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const sortedImports = [...uniqueImports.entries()].sort((a, b) => a[0].localeCompare(b[0]));
|
|
406
|
+
const regularCssSegments: string[] = [];
|
|
407
|
+
const moduleCssSegments: string[] = [];
|
|
408
|
+
|
|
409
|
+
for (const [normalizedCssPath, cssPath] of sortedImports) {
|
|
410
|
+
// Deduplicate only exact path matches to avoid skipping unrelated files
|
|
411
|
+
// like /styles/globals.css when the configured stylesheet is /globals.css.
|
|
412
|
+
if (normalizedCssPath === configuredStylesheetAbsolute) {
|
|
413
|
+
continue;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
try {
|
|
417
|
+
const content = await this.config.adapter.fs.readFile(cssPath);
|
|
418
|
+
if (!content) continue;
|
|
419
|
+
|
|
420
|
+
if (normalizedCssPath.endsWith(".module.css")) {
|
|
421
|
+
moduleCssSegments.push(rewriteCssModuleContent(content, normalizedCssPath));
|
|
422
|
+
} else {
|
|
423
|
+
regularCssSegments.push(content);
|
|
424
|
+
}
|
|
425
|
+
} catch {
|
|
426
|
+
logger.debug("Could not load imported CSS file", { cssPath });
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
if (regularCssSegments.length === 0 && moduleCssSegments.length === 0) return globalCSS;
|
|
431
|
+
|
|
432
|
+
const combined = [globalCSS, ...regularCssSegments, ...moduleCssSegments]
|
|
433
|
+
.filter(Boolean)
|
|
434
|
+
.join("\n");
|
|
435
|
+
logger.debug("Merged imported CSS with global stylesheet", {
|
|
436
|
+
importedCount: regularCssSegments.length + moduleCssSegments.length,
|
|
437
|
+
regularCount: regularCssSegments.length,
|
|
438
|
+
moduleCount: moduleCssSegments.length,
|
|
439
|
+
totalLength: combined.length,
|
|
440
|
+
});
|
|
441
|
+
return combined;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
private getProjectContentVersion(): string | undefined {
|
|
445
|
+
const wrappedFs = this.config.adapter.fs as unknown as {
|
|
446
|
+
getUnderlyingAdapter?: () => unknown;
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
if (typeof wrappedFs.getUnderlyingAdapter !== "function") return undefined;
|
|
450
|
+
|
|
451
|
+
const fsAdapter = wrappedFs.getUnderlyingAdapter() as {
|
|
452
|
+
getProjectData?: () => { updated_at?: string } | undefined;
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
return fsAdapter.getProjectData?.()?.updated_at;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
private buildRouteManifestKey(pagePath: string): string {
|
|
459
|
+
const relativePagePath = extractRelativePath(pagePath, this.config.projectDir);
|
|
460
|
+
return relativePagePath
|
|
461
|
+
.replace(/\.(tsx|ts|jsx|mdx|md|js)$/, "")
|
|
462
|
+
.replace(/^pages\//, "");
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
private async extractProjectClassesForRoute(
|
|
466
|
+
context: HTMLGenerationContext,
|
|
467
|
+
appComponentPath?: string,
|
|
468
|
+
): Promise<Set<string>> {
|
|
373
469
|
const classes = new Set<string>();
|
|
374
470
|
|
|
375
471
|
const wrappedFs = this.config.adapter.fs as unknown as {
|
|
@@ -387,20 +483,36 @@ export class HTMLGenerator {
|
|
|
387
483
|
if (typeof fsAdapter.getAllSourceFiles !== "function") return classes;
|
|
388
484
|
|
|
389
485
|
const files = await fsAdapter.getAllSourceFiles();
|
|
486
|
+
const projectScope = context.options?.projectSlug || context.options?.projectId ||
|
|
487
|
+
this.config.projectDir;
|
|
488
|
+
const projectVersion = this.getProjectContentVersion() ??
|
|
489
|
+
(this.config.mode === "development" ? "dev" : "unknown");
|
|
490
|
+
const routeKey = this.buildRouteManifestKey(context.pageInfo.entity.path);
|
|
491
|
+
const routeLayoutPaths = context.nestedLayouts
|
|
492
|
+
.map((layout) => layout.componentPath || layout.path)
|
|
493
|
+
.filter((path): path is string => Boolean(path));
|
|
494
|
+
const routeFilePaths = [
|
|
495
|
+
context.pageInfo.entity.path,
|
|
496
|
+
...routeLayoutPaths,
|
|
497
|
+
...(appComponentPath ? [appComponentPath] : []),
|
|
498
|
+
];
|
|
390
499
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
500
|
+
const routeCandidates = getRouteCandidates({
|
|
501
|
+
projectScope,
|
|
502
|
+
projectVersion,
|
|
503
|
+
projectDir: this.config.projectDir,
|
|
504
|
+
routeKey,
|
|
505
|
+
routeFilePaths,
|
|
506
|
+
files,
|
|
507
|
+
developmentMode: this.config.mode === "development",
|
|
508
|
+
});
|
|
395
509
|
|
|
396
|
-
|
|
397
|
-
for (const cls of extractCandidates(file.content)) {
|
|
398
|
-
classes.add(cls);
|
|
399
|
-
}
|
|
400
|
-
}
|
|
510
|
+
for (const cls of routeCandidates) classes.add(cls);
|
|
401
511
|
|
|
402
512
|
logger.debug("extractProjectClasses", {
|
|
403
|
-
filesProcessed,
|
|
513
|
+
filesProcessed: files.length,
|
|
514
|
+
routeKey,
|
|
515
|
+
routeFileCount: routeFilePaths.length,
|
|
404
516
|
totalClasses: classes.size,
|
|
405
517
|
});
|
|
406
518
|
|