veryfront 0.1.162 → 0.1.163
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/esm/deno.js +1 -1
- package/esm/src/rendering/orchestrator/html-project-css.d.ts +31 -0
- package/esm/src/rendering/orchestrator/html-project-css.d.ts.map +1 -0
- package/esm/src/rendering/orchestrator/html-project-css.js +126 -0
- package/esm/src/rendering/orchestrator/html.d.ts +0 -5
- package/esm/src/rendering/orchestrator/html.d.ts.map +1 -1
- package/esm/src/rendering/orchestrator/html.js +7 -119
- package/esm/src/utils/version-constant.d.ts +1 -1
- package/esm/src/utils/version-constant.js +1 -1
- package/package.json +1 -1
- package/src/deno.js +1 -1
- package/src/src/rendering/orchestrator/html-project-css.ts +203 -0
- package/src/src/rendering/orchestrator/html.ts +12 -169
- package/src/src/utils/version-constant.ts +1 -1
package/esm/deno.js
CHANGED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { VeryfrontConfig } from "../../config/index.js";
|
|
2
|
+
import type { HTMLGenerationOptions } from "../../html/index.js";
|
|
3
|
+
import { getProjectCSS } from "../../html/styles-builder/index.js";
|
|
4
|
+
import { warmPreparedCSSArtifactFromFiles } from "../../html/styles-builder/css-pregeneration.js";
|
|
5
|
+
import { resolveStyleContentVersion } from "../../html/styles-builder/content-version.js";
|
|
6
|
+
import { createStyleScopeProfile } from "../../html/styles-builder/style-scope-profile.js";
|
|
7
|
+
import type { RuntimeAdapter } from "../../platform/adapters/base.js";
|
|
8
|
+
import { getRouteCandidates } from "./css-candidate-manifest.js";
|
|
9
|
+
import type { HTMLGenerationContext } from "./html.js";
|
|
10
|
+
export type ProjectCSSResult = Awaited<ReturnType<typeof getProjectCSS>> | null;
|
|
11
|
+
interface ProjectCssConfig {
|
|
12
|
+
projectDir: string;
|
|
13
|
+
adapter: RuntimeAdapter;
|
|
14
|
+
config: VeryfrontConfig;
|
|
15
|
+
mode: "development" | "production";
|
|
16
|
+
}
|
|
17
|
+
interface ProjectCssDeps {
|
|
18
|
+
createStyleScopeProfile?: typeof createStyleScopeProfile;
|
|
19
|
+
getProjectCSS?: typeof getProjectCSS;
|
|
20
|
+
getProjectContentVersion?: (config: Pick<ProjectCssConfig, "adapter" | "mode">) => string | undefined;
|
|
21
|
+
getRouteCandidates?: typeof getRouteCandidates;
|
|
22
|
+
resolveStyleContentVersion?: typeof resolveStyleContentVersion;
|
|
23
|
+
warmPreparedCSSArtifactFromFiles?: typeof warmPreparedCSSArtifactFromFiles;
|
|
24
|
+
}
|
|
25
|
+
export declare function buildRouteManifestKey(pagePath: string, projectDir: string): string;
|
|
26
|
+
export declare function getProjectContentVersion(config: Pick<ProjectCssConfig, "adapter" | "mode">, deps?: Pick<ProjectCssDeps, "resolveStyleContentVersion">): string | undefined;
|
|
27
|
+
export declare function startProjectCSSPreparation(context: HTMLGenerationContext, htmlOptions: HTMLGenerationOptions, deps?: Pick<ProjectCssDeps, "getProjectCSS">): Promise<ProjectCSSResult> | undefined;
|
|
28
|
+
export declare function startPreparedCSSWarmup(config: ProjectCssConfig, context: HTMLGenerationContext, htmlOptions: HTMLGenerationOptions, deps?: Pick<ProjectCssDeps, "createStyleScopeProfile" | "getProjectContentVersion" | "warmPreparedCSSArtifactFromFiles">): void;
|
|
29
|
+
export declare function extractProjectClassesForRoute(config: ProjectCssConfig, context: HTMLGenerationContext, appComponentPath?: string, deps?: Pick<ProjectCssDeps, "createStyleScopeProfile" | "getProjectContentVersion" | "getRouteCandidates">): Promise<Set<string>>;
|
|
30
|
+
export {};
|
|
31
|
+
//# sourceMappingURL=html-project-css.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"html-project-css.d.ts","sourceRoot":"","sources":["../../../../src/src/rendering/orchestrator/html-project-css.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AACjE,OAAO,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AACnE,OAAO,EAAE,gCAAgC,EAAE,MAAM,gDAAgD,CAAC;AAClG,OAAO,EAAE,0BAA0B,EAAE,MAAM,8CAA8C,CAAC;AAC1F,OAAO,EAAE,uBAAuB,EAAE,MAAM,kDAAkD,CAAC;AAC3F,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAItE,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAIvD,MAAM,MAAM,gBAAgB,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC,GAAG,IAAI,CAAC;AAEhF,UAAU,gBAAgB;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,cAAc,CAAC;IACxB,MAAM,EAAE,eAAe,CAAC;IACxB,IAAI,EAAE,aAAa,GAAG,YAAY,CAAC;CACpC;AAED,UAAU,cAAc;IACtB,uBAAuB,CAAC,EAAE,OAAO,uBAAuB,CAAC;IACzD,aAAa,CAAC,EAAE,OAAO,aAAa,CAAC;IACrC,wBAAwB,CAAC,EAAE,CACzB,MAAM,EAAE,IAAI,CAAC,gBAAgB,EAAE,SAAS,GAAG,MAAM,CAAC,KAC/C,MAAM,GAAG,SAAS,CAAC;IACxB,kBAAkB,CAAC,EAAE,OAAO,kBAAkB,CAAC;IAC/C,0BAA0B,CAAC,EAAE,OAAO,0BAA0B,CAAC;IAC/D,gCAAgC,CAAC,EAAE,OAAO,gCAAgC,CAAC;CAC5E;AAUD,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAKlF;AAED,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,IAAI,CAAC,gBAAgB,EAAE,SAAS,GAAG,MAAM,CAAC,EAClD,IAAI,GAAE,IAAI,CAAC,cAAc,EAAE,4BAA4B,CAAM,GAC5D,MAAM,GAAG,SAAS,CAiBpB;AAED,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,qBAAqB,EAC9B,WAAW,EAAE,qBAAqB,EAClC,IAAI,GAAE,IAAI,CAAC,cAAc,EAAE,eAAe,CAAM,GAC/C,OAAO,CAAC,gBAAgB,CAAC,GAAG,SAAS,CAkBvC;AAED,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,gBAAgB,EACxB,OAAO,EAAE,qBAAqB,EAC9B,WAAW,EAAE,qBAAqB,EAClC,IAAI,GAAE,IAAI,CACR,cAAc,EACd,yBAAyB,GAAG,0BAA0B,GAAG,kCAAkC,CACvF,GACL,IAAI,CAyCN;AAED,wBAAsB,6BAA6B,CACjD,MAAM,EAAE,gBAAgB,EACxB,OAAO,EAAE,qBAAqB,EAC9B,gBAAgB,CAAC,EAAE,MAAM,EACzB,IAAI,GAAE,IAAI,CACR,cAAc,EACd,yBAAyB,GAAG,0BAA0B,GAAG,oBAAoB,CACzE,GACL,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CA+CtB"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { getProjectCSS } from "../../html/styles-builder/index.js";
|
|
2
|
+
import { warmPreparedCSSArtifactFromFiles } from "../../html/styles-builder/css-pregeneration.js";
|
|
3
|
+
import { resolveStyleContentVersion } from "../../html/styles-builder/content-version.js";
|
|
4
|
+
import { createStyleScopeProfile } from "../../html/styles-builder/style-scope-profile.js";
|
|
5
|
+
import { rendererLogger } from "../../utils/index.js";
|
|
6
|
+
import { extractRelativePath } from "../../utils/route-path-utils.js";
|
|
7
|
+
import { getRouteCandidates } from "./css-candidate-manifest.js";
|
|
8
|
+
const logger = rendererLogger.component("html-project-css");
|
|
9
|
+
function getUnderlyingFsAdapter(adapter) {
|
|
10
|
+
const wrappedFs = adapter.fs;
|
|
11
|
+
if (typeof wrappedFs.getUnderlyingAdapter !== "function")
|
|
12
|
+
return undefined;
|
|
13
|
+
return wrappedFs.getUnderlyingAdapter();
|
|
14
|
+
}
|
|
15
|
+
export function buildRouteManifestKey(pagePath, projectDir) {
|
|
16
|
+
const relativePagePath = extractRelativePath(pagePath, projectDir);
|
|
17
|
+
return relativePagePath
|
|
18
|
+
.replace(/\.(tsx|ts|jsx|mdx|md|js)$/, "")
|
|
19
|
+
.replace(/^pages\//, "");
|
|
20
|
+
}
|
|
21
|
+
export function getProjectContentVersion(config, deps = {}) {
|
|
22
|
+
const fsAdapter = getUnderlyingFsAdapter(config.adapter);
|
|
23
|
+
if (!fsAdapter)
|
|
24
|
+
return undefined;
|
|
25
|
+
const contentContext = typeof fsAdapter.getContentContext === "function"
|
|
26
|
+
? fsAdapter.getContentContext()
|
|
27
|
+
: null;
|
|
28
|
+
if (contentContext) {
|
|
29
|
+
const resolveContentVersion = deps.resolveStyleContentVersion ?? resolveStyleContentVersion;
|
|
30
|
+
return resolveContentVersion(contentContext);
|
|
31
|
+
}
|
|
32
|
+
return fsAdapter.getProjectData?.()?.updated_at;
|
|
33
|
+
}
|
|
34
|
+
export function startProjectCSSPreparation(context, htmlOptions, deps = {}) {
|
|
35
|
+
const isLocalProject = htmlOptions.isLocalProject ?? false;
|
|
36
|
+
if (isLocalProject || htmlOptions.environment !== "production")
|
|
37
|
+
return undefined;
|
|
38
|
+
const projectScope = htmlOptions.projectSlug || htmlOptions.projectId || context.slug;
|
|
39
|
+
if (!projectScope || projectScope === "default")
|
|
40
|
+
return undefined;
|
|
41
|
+
const getProjectCss = deps.getProjectCSS ?? getProjectCSS;
|
|
42
|
+
return getProjectCss(projectScope, htmlOptions.globalCSS, new Set([...(htmlOptions.projectClasses ?? [])]), {
|
|
43
|
+
minify: true,
|
|
44
|
+
environment: htmlOptions.environment,
|
|
45
|
+
buildMode: htmlOptions.mode,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
export function startPreparedCSSWarmup(config, context, htmlOptions, deps = {}) {
|
|
49
|
+
const isLocalProject = htmlOptions.isLocalProject ?? false;
|
|
50
|
+
const usesPreviewStylesheet = isLocalProject || htmlOptions.environment !== "production";
|
|
51
|
+
if (!usesPreviewStylesheet)
|
|
52
|
+
return;
|
|
53
|
+
const fsAdapter = getUnderlyingFsAdapter(config.adapter);
|
|
54
|
+
if (typeof fsAdapter?.getAllSourceFiles !== "function")
|
|
55
|
+
return;
|
|
56
|
+
const projectScope = htmlOptions.projectSlug || htmlOptions.projectId || context.slug;
|
|
57
|
+
if (!projectScope || projectScope === "default")
|
|
58
|
+
return;
|
|
59
|
+
const resolveProjectContentVersion = deps.getProjectContentVersion ?? getProjectContentVersion;
|
|
60
|
+
const projectVersion = resolveProjectContentVersion(config) ??
|
|
61
|
+
(config.mode === "development" ? "dev" : "unknown");
|
|
62
|
+
const createStyleProfile = deps.createStyleScopeProfile ?? createStyleScopeProfile;
|
|
63
|
+
const warmPreparedCss = deps.warmPreparedCSSArtifactFromFiles ?? warmPreparedCSSArtifactFromFiles;
|
|
64
|
+
const styleProfile = createStyleProfile(config.config);
|
|
65
|
+
const stylesheetPath = config.config?.tailwind?.stylesheet;
|
|
66
|
+
Promise.resolve(fsAdapter.getAllSourceFiles())
|
|
67
|
+
.then((files) => warmPreparedCss({
|
|
68
|
+
projectSlug: projectScope,
|
|
69
|
+
projectVersion,
|
|
70
|
+
projectDir: config.projectDir,
|
|
71
|
+
files,
|
|
72
|
+
styleProfile,
|
|
73
|
+
stylesheetPath,
|
|
74
|
+
minify: true,
|
|
75
|
+
environment: "preview",
|
|
76
|
+
buildMode: "production",
|
|
77
|
+
}))
|
|
78
|
+
.catch((error) => {
|
|
79
|
+
logger.debug("Prepared CSS warmup skipped after source scan failure", {
|
|
80
|
+
projectScope,
|
|
81
|
+
error: error instanceof Error ? error.message : String(error),
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
export async function extractProjectClassesForRoute(config, context, appComponentPath, deps = {}) {
|
|
86
|
+
const classes = new Set();
|
|
87
|
+
const fsAdapter = getUnderlyingFsAdapter(config.adapter);
|
|
88
|
+
if (typeof fsAdapter?.getAllSourceFiles !== "function")
|
|
89
|
+
return classes;
|
|
90
|
+
const files = await fsAdapter.getAllSourceFiles();
|
|
91
|
+
const projectScope = context.options?.projectSlug || context.options?.projectId ||
|
|
92
|
+
config.projectDir;
|
|
93
|
+
const resolveProjectContentVersion = deps.getProjectContentVersion ?? getProjectContentVersion;
|
|
94
|
+
const projectVersion = resolveProjectContentVersion(config) ??
|
|
95
|
+
(config.mode === "development" ? "dev" : "unknown");
|
|
96
|
+
const routeKey = buildRouteManifestKey(context.pageInfo.entity.path, config.projectDir);
|
|
97
|
+
const routeLayoutPaths = context.nestedLayouts
|
|
98
|
+
.map((layout) => layout.componentPath || layout.path)
|
|
99
|
+
.filter((path) => Boolean(path));
|
|
100
|
+
const routeFilePaths = [
|
|
101
|
+
context.pageInfo.entity.path,
|
|
102
|
+
...routeLayoutPaths,
|
|
103
|
+
...(appComponentPath ? [appComponentPath] : []),
|
|
104
|
+
];
|
|
105
|
+
const createStyleProfile = deps.createStyleScopeProfile ?? createStyleScopeProfile;
|
|
106
|
+
const getRouteCssCandidates = deps.getRouteCandidates ?? getRouteCandidates;
|
|
107
|
+
const routeCandidates = getRouteCssCandidates({
|
|
108
|
+
projectScope,
|
|
109
|
+
projectVersion,
|
|
110
|
+
projectDir: config.projectDir,
|
|
111
|
+
styleProfile: createStyleProfile(config.config),
|
|
112
|
+
routeKey,
|
|
113
|
+
routeFilePaths,
|
|
114
|
+
files,
|
|
115
|
+
developmentMode: config.mode === "development",
|
|
116
|
+
});
|
|
117
|
+
for (const cls of routeCandidates)
|
|
118
|
+
classes.add(cls);
|
|
119
|
+
logger.debug("extractProjectClasses", {
|
|
120
|
+
filesProcessed: files.length,
|
|
121
|
+
routeKey,
|
|
122
|
+
routeFileCount: routeFilePaths.length,
|
|
123
|
+
totalClasses: classes.size,
|
|
124
|
+
});
|
|
125
|
+
return classes;
|
|
126
|
+
}
|
|
@@ -33,8 +33,6 @@ export declare class HTMLGenerator {
|
|
|
33
33
|
private detectUseClientDirective;
|
|
34
34
|
private wrapHTMLFragment;
|
|
35
35
|
private generateShellParts;
|
|
36
|
-
private startProjectCSSPreparation;
|
|
37
|
-
private startPreparedCSSWarmup;
|
|
38
36
|
private buildHeadElements;
|
|
39
37
|
private mergeFrontmatter;
|
|
40
38
|
private resolveAppPath;
|
|
@@ -46,8 +44,5 @@ export declare class HTMLGenerator {
|
|
|
46
44
|
* double-loading globals.css when it's both auto-discovered and explicitly imported.
|
|
47
45
|
*/
|
|
48
46
|
private mergeImportedCSS;
|
|
49
|
-
private getProjectContentVersion;
|
|
50
|
-
private buildRouteManifestKey;
|
|
51
|
-
private extractProjectClassesForRoute;
|
|
52
47
|
}
|
|
53
48
|
//# sourceMappingURL=html.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../../../../src/src/rendering/orchestrator/html.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAS7D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,KAAK,EACV,UAAU,EACV,UAAU,EACV,SAAS,EAET,UAAU,EACX,MAAM,sBAAsB,CAAC;AAG9B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../../../../src/src/rendering/orchestrator/html.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAS7D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,KAAK,EACV,UAAU,EACV,UAAU,EACV,SAAS,EAET,UAAU,EACX,MAAM,sBAAsB,CAAC;AAG9B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AA4EhD,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,cAAc,CAAC;IACxB,MAAM,EAAE,eAAe,CAAC;IACxB,IAAI,EAAE,aAAa,GAAG,YAAY,CAAC;CACpC;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,UAAU,CAAC;IACrB,UAAU,EAAE,UAAU,CAAC;IACvB,YAAY,EAAE,SAAS,GAAG,SAAS,CAAC;IACpC,aAAa,EAAE,UAAU,EAAE,CAAC;IAC5B,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,2FAA2F;IAC3F,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAsB;gBAExB,MAAM,EAAE,mBAAmB;IAIjC,gBAAgB,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,MAAM,CAAC;IA0BjE,kBAAkB,CACtB,WAAW,EAAE,cAAc,EAC3B,OAAO,EAAE,IAAI,CAAC,qBAAqB,EAAE,MAAM,CAAC,GAC3C,OAAO,CAAC,cAAc,CAAC;YAqEZ,sBAAsB;YAqDtB,4BAA4B;YAiB5B,wBAAwB;YAmBxB,gBAAgB;YAyBhB,kBAAkB;IAwDhC,OAAO,CAAC,iBAAiB;IA4DzB,OAAO,CAAC,gBAAgB;IAQxB,OAAO,CAAC,cAAc;YAQR,eAAe;YAaf,gBAAgB;IAgF9B;;;;OAIG;YACW,gBAAgB;CA0D/B"}
|
|
@@ -10,11 +10,7 @@ import { resolveAppComponentPath } from "../layouts/utils/app-resolver.js";
|
|
|
10
10
|
import { StreamTimeoutError, streamToString } from "../utils/stream-utils.js";
|
|
11
11
|
import { profilePhase, profileSyncPhase } from "../../observability/request-profiler.js";
|
|
12
12
|
import { normalizeCssModuleKey, rewriteCssModuleContent, } from "../../transforms/css-modules/naming.js";
|
|
13
|
-
import {
|
|
14
|
-
import { warmPreparedCSSArtifactFromFiles } from "../../html/styles-builder/css-pregeneration.js";
|
|
15
|
-
import { getRouteCandidates } from "./css-candidate-manifest.js";
|
|
16
|
-
import { resolveStyleContentVersion } from "../../html/styles-builder/content-version.js";
|
|
17
|
-
import { createStyleScopeProfile } from "../../html/styles-builder/style-scope-profile.js";
|
|
13
|
+
import { extractProjectClassesForRoute, startPreparedCSSWarmup, startProjectCSSPreparation, } from "./html-project-css.js";
|
|
18
14
|
const logger = rendererLogger.component("html-generator");
|
|
19
15
|
function applyExplicitThemeToDocument(html, colorScheme, enabled) {
|
|
20
16
|
if (!enabled || !colorScheme)
|
|
@@ -67,7 +63,7 @@ export class HTMLGenerator {
|
|
|
67
63
|
if (this.config.mode === "production" && context.options?.environment === "production") {
|
|
68
64
|
const mergedFrontmatter = this.mergeFrontmatter(context);
|
|
69
65
|
const htmlOptions = await profilePhase("html.build_options", () => this.buildHTMLOptions(context, mergedFrontmatter));
|
|
70
|
-
projectCSSPromise =
|
|
66
|
+
projectCSSPromise = startProjectCSSPreparation(context, htmlOptions);
|
|
71
67
|
}
|
|
72
68
|
html = await this.handleFullHTMLDocument(context, projectCSSPromise);
|
|
73
69
|
}
|
|
@@ -84,8 +80,8 @@ export class HTMLGenerator {
|
|
|
84
80
|
const fullContext = context;
|
|
85
81
|
const mergedFrontmatter = this.mergeFrontmatter(fullContext);
|
|
86
82
|
const htmlOptions = await profilePhase("html.build_options", () => this.buildHTMLOptions(fullContext, mergedFrontmatter));
|
|
87
|
-
const projectCSSPromise =
|
|
88
|
-
this.
|
|
83
|
+
const projectCSSPromise = startProjectCSSPreparation(fullContext, htmlOptions);
|
|
84
|
+
startPreparedCSSWarmup(this.config, fullContext, htmlOptions);
|
|
89
85
|
let reactContent;
|
|
90
86
|
try {
|
|
91
87
|
reactContent = (await streamToString(reactStream)).trim();
|
|
@@ -181,8 +177,8 @@ export class HTMLGenerator {
|
|
|
181
177
|
async wrapHTMLFragment(context) {
|
|
182
178
|
const mergedFrontmatter = this.mergeFrontmatter(context);
|
|
183
179
|
const htmlOptions = await profilePhase("html.build_options", () => this.buildHTMLOptions(context, mergedFrontmatter));
|
|
184
|
-
const projectCSSPromise =
|
|
185
|
-
this.
|
|
180
|
+
const projectCSSPromise = startProjectCSSPreparation(context, htmlOptions);
|
|
181
|
+
startPreparedCSSWarmup(this.config, context, htmlOptions);
|
|
186
182
|
const reactContent = context.html.trim();
|
|
187
183
|
const { start, end } = await profilePhase("html.generate_shell_parts", () => this.generateShellParts(context, mergedFrontmatter, htmlOptions, reactContent, projectCSSPromise));
|
|
188
184
|
return `${start}${reactContent}${end}`;
|
|
@@ -224,54 +220,6 @@ export class HTMLGenerator {
|
|
|
224
220
|
}
|
|
225
221
|
return { start: modifiedStart, end };
|
|
226
222
|
}
|
|
227
|
-
startProjectCSSPreparation(context, htmlOptions) {
|
|
228
|
-
const isLocalProject = htmlOptions.isLocalProject ?? false;
|
|
229
|
-
if (isLocalProject || htmlOptions.environment !== "production")
|
|
230
|
-
return undefined;
|
|
231
|
-
const projectScope = htmlOptions.projectSlug || htmlOptions.projectId || context.slug;
|
|
232
|
-
if (!projectScope || projectScope === "default")
|
|
233
|
-
return undefined;
|
|
234
|
-
return getProjectCSS(projectScope, htmlOptions.globalCSS, new Set([...(htmlOptions.projectClasses ?? [])]), {
|
|
235
|
-
minify: true,
|
|
236
|
-
environment: htmlOptions.environment,
|
|
237
|
-
buildMode: htmlOptions.mode,
|
|
238
|
-
});
|
|
239
|
-
}
|
|
240
|
-
startPreparedCSSWarmup(context, htmlOptions) {
|
|
241
|
-
const isLocalProject = htmlOptions.isLocalProject ?? false;
|
|
242
|
-
const usesPreviewStylesheet = isLocalProject || htmlOptions.environment !== "production";
|
|
243
|
-
if (!usesPreviewStylesheet)
|
|
244
|
-
return;
|
|
245
|
-
const wrappedFs = this.config.adapter.fs;
|
|
246
|
-
if (typeof wrappedFs.getUnderlyingAdapter !== "function")
|
|
247
|
-
return;
|
|
248
|
-
const fsAdapter = wrappedFs.getUnderlyingAdapter();
|
|
249
|
-
if (typeof fsAdapter.getAllSourceFiles !== "function")
|
|
250
|
-
return;
|
|
251
|
-
const projectScope = htmlOptions.projectSlug || htmlOptions.projectId || context.slug;
|
|
252
|
-
if (!projectScope || projectScope === "default")
|
|
253
|
-
return;
|
|
254
|
-
const projectVersion = this.getProjectContentVersion() ??
|
|
255
|
-
(this.config.mode === "development" ? "dev" : "unknown");
|
|
256
|
-
const styleProfile = createStyleScopeProfile(this.config.config);
|
|
257
|
-
const stylesheetPath = this.config.config?.tailwind?.stylesheet;
|
|
258
|
-
Promise.resolve(fsAdapter.getAllSourceFiles()).then((files) => warmPreparedCSSArtifactFromFiles({
|
|
259
|
-
projectSlug: projectScope,
|
|
260
|
-
projectVersion,
|
|
261
|
-
projectDir: this.config.projectDir,
|
|
262
|
-
files,
|
|
263
|
-
styleProfile,
|
|
264
|
-
stylesheetPath,
|
|
265
|
-
minify: true,
|
|
266
|
-
environment: "preview",
|
|
267
|
-
buildMode: "production",
|
|
268
|
-
})).catch((error) => {
|
|
269
|
-
logger.debug("Prepared CSS warmup skipped after source scan failure", {
|
|
270
|
-
projectScope,
|
|
271
|
-
error: error instanceof Error ? error.message : String(error),
|
|
272
|
-
});
|
|
273
|
-
});
|
|
274
|
-
}
|
|
275
223
|
buildHeadElements(head) {
|
|
276
224
|
if (!head)
|
|
277
225
|
return { scripts: "", other: "" };
|
|
@@ -360,7 +308,7 @@ export class HTMLGenerator {
|
|
|
360
308
|
profilePhase("html.load_global_css", () => this.loadProjectFile(stylesheetPath)),
|
|
361
309
|
]);
|
|
362
310
|
const appComponentPath = appComponentPathOrNull ?? undefined;
|
|
363
|
-
const projectClasses = await profilePhase("html.route_candidates", () => this.
|
|
311
|
+
const projectClasses = await profilePhase("html.route_candidates", () => extractProjectClassesForRoute(this.config, context, appComponentPath));
|
|
364
312
|
// Load CSS imported by components and merge with globalCSS.
|
|
365
313
|
// Deduplicate against the configured stylesheet to avoid double-loading.
|
|
366
314
|
const combinedCSS = await profilePhase("html.merge_imported_css", () => this.mergeImportedCSS(globalCSS, context.cssImports, stylesheetPath));
|
|
@@ -462,64 +410,4 @@ export class HTMLGenerator {
|
|
|
462
410
|
});
|
|
463
411
|
return combined;
|
|
464
412
|
}
|
|
465
|
-
getProjectContentVersion() {
|
|
466
|
-
const wrappedFs = this.config.adapter.fs;
|
|
467
|
-
if (typeof wrappedFs.getUnderlyingAdapter !== "function")
|
|
468
|
-
return undefined;
|
|
469
|
-
const fsAdapter = wrappedFs.getUnderlyingAdapter();
|
|
470
|
-
const contentContext = typeof fsAdapter.getContentContext === "function"
|
|
471
|
-
? fsAdapter.getContentContext()
|
|
472
|
-
: null;
|
|
473
|
-
if (contentContext)
|
|
474
|
-
return resolveStyleContentVersion(contentContext);
|
|
475
|
-
return fsAdapter.getProjectData?.()?.updated_at;
|
|
476
|
-
}
|
|
477
|
-
buildRouteManifestKey(pagePath) {
|
|
478
|
-
const relativePagePath = extractRelativePath(pagePath, this.config.projectDir);
|
|
479
|
-
return relativePagePath
|
|
480
|
-
.replace(/\.(tsx|ts|jsx|mdx|md|js)$/, "")
|
|
481
|
-
.replace(/^pages\//, "");
|
|
482
|
-
}
|
|
483
|
-
async extractProjectClassesForRoute(context, appComponentPath) {
|
|
484
|
-
const classes = new Set();
|
|
485
|
-
const wrappedFs = this.config.adapter.fs;
|
|
486
|
-
if (typeof wrappedFs.getUnderlyingAdapter !== "function")
|
|
487
|
-
return classes;
|
|
488
|
-
const fsAdapter = wrappedFs.getUnderlyingAdapter();
|
|
489
|
-
if (typeof fsAdapter.getAllSourceFiles !== "function")
|
|
490
|
-
return classes;
|
|
491
|
-
const files = await fsAdapter.getAllSourceFiles();
|
|
492
|
-
const projectScope = context.options?.projectSlug || context.options?.projectId ||
|
|
493
|
-
this.config.projectDir;
|
|
494
|
-
const projectVersion = this.getProjectContentVersion() ??
|
|
495
|
-
(this.config.mode === "development" ? "dev" : "unknown");
|
|
496
|
-
const routeKey = this.buildRouteManifestKey(context.pageInfo.entity.path);
|
|
497
|
-
const routeLayoutPaths = context.nestedLayouts
|
|
498
|
-
.map((layout) => layout.componentPath || layout.path)
|
|
499
|
-
.filter((path) => Boolean(path));
|
|
500
|
-
const routeFilePaths = [
|
|
501
|
-
context.pageInfo.entity.path,
|
|
502
|
-
...routeLayoutPaths,
|
|
503
|
-
...(appComponentPath ? [appComponentPath] : []),
|
|
504
|
-
];
|
|
505
|
-
const routeCandidates = getRouteCandidates({
|
|
506
|
-
projectScope,
|
|
507
|
-
projectVersion,
|
|
508
|
-
projectDir: this.config.projectDir,
|
|
509
|
-
styleProfile: createStyleScopeProfile(this.config.config),
|
|
510
|
-
routeKey,
|
|
511
|
-
routeFilePaths,
|
|
512
|
-
files,
|
|
513
|
-
developmentMode: this.config.mode === "development",
|
|
514
|
-
});
|
|
515
|
-
for (const cls of routeCandidates)
|
|
516
|
-
classes.add(cls);
|
|
517
|
-
logger.debug("extractProjectClasses", {
|
|
518
|
-
filesProcessed: files.length,
|
|
519
|
-
routeKey,
|
|
520
|
-
routeFileCount: routeFilePaths.length,
|
|
521
|
-
totalClasses: classes.size,
|
|
522
|
-
});
|
|
523
|
-
return classes;
|
|
524
|
-
}
|
|
525
413
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const VERSION = "0.1.
|
|
1
|
+
export declare const VERSION = "0.1.163";
|
|
2
2
|
//# sourceMappingURL=version-constant.d.ts.map
|
package/package.json
CHANGED
package/src/deno.js
CHANGED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import type { VeryfrontConfig } from "../../config/index.js";
|
|
2
|
+
import type { HTMLGenerationOptions } from "../../html/index.js";
|
|
3
|
+
import { getProjectCSS } from "../../html/styles-builder/index.js";
|
|
4
|
+
import { warmPreparedCSSArtifactFromFiles } from "../../html/styles-builder/css-pregeneration.js";
|
|
5
|
+
import { resolveStyleContentVersion } from "../../html/styles-builder/content-version.js";
|
|
6
|
+
import { createStyleScopeProfile } from "../../html/styles-builder/style-scope-profile.js";
|
|
7
|
+
import type { RuntimeAdapter } from "../../platform/adapters/base.js";
|
|
8
|
+
import type { ResolvedContentContext } from "../../platform/adapters/fs/veryfront/types.js";
|
|
9
|
+
import { rendererLogger } from "../../utils/index.js";
|
|
10
|
+
import { extractRelativePath } from "../../utils/route-path-utils.js";
|
|
11
|
+
import { getRouteCandidates } from "./css-candidate-manifest.js";
|
|
12
|
+
import type { HTMLGenerationContext } from "./html.js";
|
|
13
|
+
|
|
14
|
+
const logger = rendererLogger.component("html-project-css");
|
|
15
|
+
|
|
16
|
+
export type ProjectCSSResult = Awaited<ReturnType<typeof getProjectCSS>> | null;
|
|
17
|
+
|
|
18
|
+
interface ProjectCssConfig {
|
|
19
|
+
projectDir: string;
|
|
20
|
+
adapter: RuntimeAdapter;
|
|
21
|
+
config: VeryfrontConfig;
|
|
22
|
+
mode: "development" | "production";
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface ProjectCssDeps {
|
|
26
|
+
createStyleScopeProfile?: typeof createStyleScopeProfile;
|
|
27
|
+
getProjectCSS?: typeof getProjectCSS;
|
|
28
|
+
getProjectContentVersion?: (
|
|
29
|
+
config: Pick<ProjectCssConfig, "adapter" | "mode">,
|
|
30
|
+
) => string | undefined;
|
|
31
|
+
getRouteCandidates?: typeof getRouteCandidates;
|
|
32
|
+
resolveStyleContentVersion?: typeof resolveStyleContentVersion;
|
|
33
|
+
warmPreparedCSSArtifactFromFiles?: typeof warmPreparedCSSArtifactFromFiles;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
type SourceFileEntry = { path: string; content?: string };
|
|
37
|
+
|
|
38
|
+
function getUnderlyingFsAdapter(adapter: RuntimeAdapter): unknown {
|
|
39
|
+
const wrappedFs = adapter.fs as unknown as { getUnderlyingAdapter?: () => unknown };
|
|
40
|
+
if (typeof wrappedFs.getUnderlyingAdapter !== "function") return undefined;
|
|
41
|
+
return wrappedFs.getUnderlyingAdapter();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function buildRouteManifestKey(pagePath: string, projectDir: string): string {
|
|
45
|
+
const relativePagePath = extractRelativePath(pagePath, projectDir);
|
|
46
|
+
return relativePagePath
|
|
47
|
+
.replace(/\.(tsx|ts|jsx|mdx|md|js)$/, "")
|
|
48
|
+
.replace(/^pages\//, "");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function getProjectContentVersion(
|
|
52
|
+
config: Pick<ProjectCssConfig, "adapter" | "mode">,
|
|
53
|
+
deps: Pick<ProjectCssDeps, "resolveStyleContentVersion"> = {},
|
|
54
|
+
): string | undefined {
|
|
55
|
+
const fsAdapter = getUnderlyingFsAdapter(config.adapter) as {
|
|
56
|
+
getContentContext?: () => ResolvedContentContext | null;
|
|
57
|
+
getProjectData?: () => { updated_at?: string } | undefined;
|
|
58
|
+
} | undefined;
|
|
59
|
+
|
|
60
|
+
if (!fsAdapter) return undefined;
|
|
61
|
+
|
|
62
|
+
const contentContext = typeof fsAdapter.getContentContext === "function"
|
|
63
|
+
? fsAdapter.getContentContext()
|
|
64
|
+
: null;
|
|
65
|
+
if (contentContext) {
|
|
66
|
+
const resolveContentVersion = deps.resolveStyleContentVersion ?? resolveStyleContentVersion;
|
|
67
|
+
return resolveContentVersion(contentContext);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return fsAdapter.getProjectData?.()?.updated_at;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function startProjectCSSPreparation(
|
|
74
|
+
context: HTMLGenerationContext,
|
|
75
|
+
htmlOptions: HTMLGenerationOptions,
|
|
76
|
+
deps: Pick<ProjectCssDeps, "getProjectCSS"> = {},
|
|
77
|
+
): Promise<ProjectCSSResult> | undefined {
|
|
78
|
+
const isLocalProject = htmlOptions.isLocalProject ?? false;
|
|
79
|
+
if (isLocalProject || htmlOptions.environment !== "production") return undefined;
|
|
80
|
+
|
|
81
|
+
const projectScope = htmlOptions.projectSlug || htmlOptions.projectId || context.slug;
|
|
82
|
+
if (!projectScope || projectScope === "default") return undefined;
|
|
83
|
+
|
|
84
|
+
const getProjectCss = deps.getProjectCSS ?? getProjectCSS;
|
|
85
|
+
return getProjectCss(
|
|
86
|
+
projectScope,
|
|
87
|
+
htmlOptions.globalCSS,
|
|
88
|
+
new Set([...(htmlOptions.projectClasses ?? [])]),
|
|
89
|
+
{
|
|
90
|
+
minify: true,
|
|
91
|
+
environment: htmlOptions.environment,
|
|
92
|
+
buildMode: htmlOptions.mode,
|
|
93
|
+
},
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function startPreparedCSSWarmup(
|
|
98
|
+
config: ProjectCssConfig,
|
|
99
|
+
context: HTMLGenerationContext,
|
|
100
|
+
htmlOptions: HTMLGenerationOptions,
|
|
101
|
+
deps: Pick<
|
|
102
|
+
ProjectCssDeps,
|
|
103
|
+
"createStyleScopeProfile" | "getProjectContentVersion" | "warmPreparedCSSArtifactFromFiles"
|
|
104
|
+
> = {},
|
|
105
|
+
): void {
|
|
106
|
+
const isLocalProject = htmlOptions.isLocalProject ?? false;
|
|
107
|
+
const usesPreviewStylesheet = isLocalProject || htmlOptions.environment !== "production";
|
|
108
|
+
if (!usesPreviewStylesheet) return;
|
|
109
|
+
|
|
110
|
+
const fsAdapter = getUnderlyingFsAdapter(config.adapter) as {
|
|
111
|
+
getAllSourceFiles?: () => SourceFileEntry[] | Promise<SourceFileEntry[]>;
|
|
112
|
+
} | undefined;
|
|
113
|
+
if (typeof fsAdapter?.getAllSourceFiles !== "function") return;
|
|
114
|
+
|
|
115
|
+
const projectScope = htmlOptions.projectSlug || htmlOptions.projectId || context.slug;
|
|
116
|
+
if (!projectScope || projectScope === "default") return;
|
|
117
|
+
|
|
118
|
+
const resolveProjectContentVersion = deps.getProjectContentVersion ?? getProjectContentVersion;
|
|
119
|
+
const projectVersion = resolveProjectContentVersion(config) ??
|
|
120
|
+
(config.mode === "development" ? "dev" : "unknown");
|
|
121
|
+
const createStyleProfile = deps.createStyleScopeProfile ?? createStyleScopeProfile;
|
|
122
|
+
const warmPreparedCss = deps.warmPreparedCSSArtifactFromFiles ?? warmPreparedCSSArtifactFromFiles;
|
|
123
|
+
const styleProfile = createStyleProfile(config.config);
|
|
124
|
+
const stylesheetPath = config.config?.tailwind?.stylesheet;
|
|
125
|
+
|
|
126
|
+
Promise.resolve(fsAdapter.getAllSourceFiles())
|
|
127
|
+
.then((files) =>
|
|
128
|
+
warmPreparedCss({
|
|
129
|
+
projectSlug: projectScope,
|
|
130
|
+
projectVersion,
|
|
131
|
+
projectDir: config.projectDir,
|
|
132
|
+
files,
|
|
133
|
+
styleProfile,
|
|
134
|
+
stylesheetPath,
|
|
135
|
+
minify: true,
|
|
136
|
+
environment: "preview",
|
|
137
|
+
buildMode: "production",
|
|
138
|
+
})
|
|
139
|
+
)
|
|
140
|
+
.catch((error) => {
|
|
141
|
+
logger.debug("Prepared CSS warmup skipped after source scan failure", {
|
|
142
|
+
projectScope,
|
|
143
|
+
error: error instanceof Error ? error.message : String(error),
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export async function extractProjectClassesForRoute(
|
|
149
|
+
config: ProjectCssConfig,
|
|
150
|
+
context: HTMLGenerationContext,
|
|
151
|
+
appComponentPath?: string,
|
|
152
|
+
deps: Pick<
|
|
153
|
+
ProjectCssDeps,
|
|
154
|
+
"createStyleScopeProfile" | "getProjectContentVersion" | "getRouteCandidates"
|
|
155
|
+
> = {},
|
|
156
|
+
): Promise<Set<string>> {
|
|
157
|
+
const classes = new Set<string>();
|
|
158
|
+
const fsAdapter = getUnderlyingFsAdapter(config.adapter) as {
|
|
159
|
+
getAllSourceFiles?: () => SourceFileEntry[] | Promise<SourceFileEntry[]>;
|
|
160
|
+
} | undefined;
|
|
161
|
+
|
|
162
|
+
if (typeof fsAdapter?.getAllSourceFiles !== "function") return classes;
|
|
163
|
+
|
|
164
|
+
const files = await fsAdapter.getAllSourceFiles();
|
|
165
|
+
const projectScope = context.options?.projectSlug || context.options?.projectId ||
|
|
166
|
+
config.projectDir;
|
|
167
|
+
const resolveProjectContentVersion = deps.getProjectContentVersion ?? getProjectContentVersion;
|
|
168
|
+
const projectVersion = resolveProjectContentVersion(config) ??
|
|
169
|
+
(config.mode === "development" ? "dev" : "unknown");
|
|
170
|
+
const routeKey = buildRouteManifestKey(context.pageInfo.entity.path, config.projectDir);
|
|
171
|
+
const routeLayoutPaths = context.nestedLayouts
|
|
172
|
+
.map((layout) => layout.componentPath || layout.path)
|
|
173
|
+
.filter((path): path is string => Boolean(path));
|
|
174
|
+
const routeFilePaths = [
|
|
175
|
+
context.pageInfo.entity.path,
|
|
176
|
+
...routeLayoutPaths,
|
|
177
|
+
...(appComponentPath ? [appComponentPath] : []),
|
|
178
|
+
];
|
|
179
|
+
|
|
180
|
+
const createStyleProfile = deps.createStyleScopeProfile ?? createStyleScopeProfile;
|
|
181
|
+
const getRouteCssCandidates = deps.getRouteCandidates ?? getRouteCandidates;
|
|
182
|
+
const routeCandidates = getRouteCssCandidates({
|
|
183
|
+
projectScope,
|
|
184
|
+
projectVersion,
|
|
185
|
+
projectDir: config.projectDir,
|
|
186
|
+
styleProfile: createStyleProfile(config.config),
|
|
187
|
+
routeKey,
|
|
188
|
+
routeFilePaths,
|
|
189
|
+
files,
|
|
190
|
+
developmentMode: config.mode === "development",
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
for (const cls of routeCandidates) classes.add(cls);
|
|
194
|
+
|
|
195
|
+
logger.debug("extractProjectClasses", {
|
|
196
|
+
filesProcessed: files.length,
|
|
197
|
+
routeKey,
|
|
198
|
+
routeFileCount: routeFilePaths.length,
|
|
199
|
+
totalClasses: classes.size,
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
return classes;
|
|
203
|
+
}
|
|
@@ -31,15 +31,14 @@ import {
|
|
|
31
31
|
normalizeCssModuleKey,
|
|
32
32
|
rewriteCssModuleContent,
|
|
33
33
|
} from "../../transforms/css-modules/naming.js";
|
|
34
|
-
import {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
34
|
+
import {
|
|
35
|
+
extractProjectClassesForRoute,
|
|
36
|
+
type ProjectCSSResult,
|
|
37
|
+
startPreparedCSSWarmup,
|
|
38
|
+
startProjectCSSPreparation,
|
|
39
|
+
} from "./html-project-css.js";
|
|
40
40
|
|
|
41
41
|
const logger = rendererLogger.component("html-generator");
|
|
42
|
-
type ProjectCSSResult = Awaited<ReturnType<typeof getProjectCSS>> | null;
|
|
43
42
|
|
|
44
43
|
function applyExplicitThemeToDocument(
|
|
45
44
|
html: string,
|
|
@@ -136,7 +135,7 @@ export class HTMLGenerator {
|
|
|
136
135
|
"html.build_options",
|
|
137
136
|
() => this.buildHTMLOptions(context, mergedFrontmatter),
|
|
138
137
|
);
|
|
139
|
-
projectCSSPromise =
|
|
138
|
+
projectCSSPromise = startProjectCSSPreparation(context, htmlOptions);
|
|
140
139
|
}
|
|
141
140
|
|
|
142
141
|
html = await this.handleFullHTMLDocument(context, projectCSSPromise);
|
|
@@ -162,8 +161,8 @@ export class HTMLGenerator {
|
|
|
162
161
|
"html.build_options",
|
|
163
162
|
() => this.buildHTMLOptions(fullContext, mergedFrontmatter),
|
|
164
163
|
);
|
|
165
|
-
const projectCSSPromise =
|
|
166
|
-
this.
|
|
164
|
+
const projectCSSPromise = startProjectCSSPreparation(fullContext, htmlOptions);
|
|
165
|
+
startPreparedCSSWarmup(this.config, fullContext, htmlOptions);
|
|
167
166
|
|
|
168
167
|
let reactContent: string;
|
|
169
168
|
try {
|
|
@@ -319,8 +318,8 @@ export class HTMLGenerator {
|
|
|
319
318
|
"html.build_options",
|
|
320
319
|
() => this.buildHTMLOptions(context, mergedFrontmatter),
|
|
321
320
|
);
|
|
322
|
-
const projectCSSPromise =
|
|
323
|
-
this.
|
|
321
|
+
const projectCSSPromise = startProjectCSSPreparation(context, htmlOptions);
|
|
322
|
+
startPreparedCSSWarmup(this.config, context, htmlOptions);
|
|
324
323
|
const reactContent = context.html.trim();
|
|
325
324
|
|
|
326
325
|
const { start, end } = await profilePhase(
|
|
@@ -394,76 +393,6 @@ export class HTMLGenerator {
|
|
|
394
393
|
return { start: modifiedStart, end };
|
|
395
394
|
}
|
|
396
395
|
|
|
397
|
-
private startProjectCSSPreparation(
|
|
398
|
-
context: HTMLGenerationContext,
|
|
399
|
-
htmlOptions: HTMLGenerationOptions,
|
|
400
|
-
): Promise<ProjectCSSResult> | undefined {
|
|
401
|
-
const isLocalProject = htmlOptions.isLocalProject ?? false;
|
|
402
|
-
if (isLocalProject || htmlOptions.environment !== "production") return undefined;
|
|
403
|
-
|
|
404
|
-
const projectScope = htmlOptions.projectSlug || htmlOptions.projectId || context.slug;
|
|
405
|
-
if (!projectScope || projectScope === "default") return undefined;
|
|
406
|
-
|
|
407
|
-
return getProjectCSS(
|
|
408
|
-
projectScope,
|
|
409
|
-
htmlOptions.globalCSS,
|
|
410
|
-
new Set([...(htmlOptions.projectClasses ?? [])]),
|
|
411
|
-
{
|
|
412
|
-
minify: true,
|
|
413
|
-
environment: htmlOptions.environment,
|
|
414
|
-
buildMode: htmlOptions.mode,
|
|
415
|
-
},
|
|
416
|
-
);
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
private startPreparedCSSWarmup(
|
|
420
|
-
context: HTMLGenerationContext,
|
|
421
|
-
htmlOptions: HTMLGenerationOptions,
|
|
422
|
-
): void {
|
|
423
|
-
const isLocalProject = htmlOptions.isLocalProject ?? false;
|
|
424
|
-
const usesPreviewStylesheet = isLocalProject || htmlOptions.environment !== "production";
|
|
425
|
-
if (!usesPreviewStylesheet) return;
|
|
426
|
-
|
|
427
|
-
const wrappedFs = this.config.adapter.fs as unknown as {
|
|
428
|
-
getUnderlyingAdapter?: () => unknown;
|
|
429
|
-
};
|
|
430
|
-
if (typeof wrappedFs.getUnderlyingAdapter !== "function") return;
|
|
431
|
-
|
|
432
|
-
const fsAdapter = wrappedFs.getUnderlyingAdapter() as {
|
|
433
|
-
getAllSourceFiles?: () =>
|
|
434
|
-
| Array<{ path: string; content?: string }>
|
|
435
|
-
| Promise<Array<{ path: string; content?: string }>>;
|
|
436
|
-
};
|
|
437
|
-
if (typeof fsAdapter.getAllSourceFiles !== "function") return;
|
|
438
|
-
|
|
439
|
-
const projectScope = htmlOptions.projectSlug || htmlOptions.projectId || context.slug;
|
|
440
|
-
if (!projectScope || projectScope === "default") return;
|
|
441
|
-
|
|
442
|
-
const projectVersion = this.getProjectContentVersion() ??
|
|
443
|
-
(this.config.mode === "development" ? "dev" : "unknown");
|
|
444
|
-
const styleProfile = createStyleScopeProfile(this.config.config);
|
|
445
|
-
const stylesheetPath = this.config.config?.tailwind?.stylesheet;
|
|
446
|
-
|
|
447
|
-
Promise.resolve(fsAdapter.getAllSourceFiles()).then((files) =>
|
|
448
|
-
warmPreparedCSSArtifactFromFiles({
|
|
449
|
-
projectSlug: projectScope,
|
|
450
|
-
projectVersion,
|
|
451
|
-
projectDir: this.config.projectDir,
|
|
452
|
-
files,
|
|
453
|
-
styleProfile,
|
|
454
|
-
stylesheetPath,
|
|
455
|
-
minify: true,
|
|
456
|
-
environment: "preview",
|
|
457
|
-
buildMode: "production",
|
|
458
|
-
})
|
|
459
|
-
).catch((error) => {
|
|
460
|
-
logger.debug("Prepared CSS warmup skipped after source scan failure", {
|
|
461
|
-
projectScope,
|
|
462
|
-
error: error instanceof Error ? error.message : String(error),
|
|
463
|
-
});
|
|
464
|
-
});
|
|
465
|
-
}
|
|
466
|
-
|
|
467
396
|
private buildHeadElements(head?: CollectedHead): { scripts: string; other: string } {
|
|
468
397
|
if (!head) return { scripts: "", other: "" };
|
|
469
398
|
|
|
@@ -565,7 +494,7 @@ export class HTMLGenerator {
|
|
|
565
494
|
const appComponentPath = appComponentPathOrNull ?? undefined;
|
|
566
495
|
const projectClasses = await profilePhase(
|
|
567
496
|
"html.route_candidates",
|
|
568
|
-
() => this.
|
|
497
|
+
() => extractProjectClassesForRoute(this.config, context, appComponentPath),
|
|
569
498
|
);
|
|
570
499
|
|
|
571
500
|
// Load CSS imported by components and merge with globalCSS.
|
|
@@ -696,90 +625,4 @@ export class HTMLGenerator {
|
|
|
696
625
|
});
|
|
697
626
|
return combined;
|
|
698
627
|
}
|
|
699
|
-
|
|
700
|
-
private getProjectContentVersion(): string | undefined {
|
|
701
|
-
const wrappedFs = this.config.adapter.fs as unknown as {
|
|
702
|
-
getUnderlyingAdapter?: () => unknown;
|
|
703
|
-
};
|
|
704
|
-
|
|
705
|
-
if (typeof wrappedFs.getUnderlyingAdapter !== "function") return undefined;
|
|
706
|
-
|
|
707
|
-
const fsAdapter = wrappedFs.getUnderlyingAdapter() as {
|
|
708
|
-
getContentContext?: () => ResolvedContentContext | null;
|
|
709
|
-
getProjectData?: () => { updated_at?: string } | undefined;
|
|
710
|
-
};
|
|
711
|
-
|
|
712
|
-
const contentContext = typeof fsAdapter.getContentContext === "function"
|
|
713
|
-
? fsAdapter.getContentContext()
|
|
714
|
-
: null;
|
|
715
|
-
|
|
716
|
-
if (contentContext) return resolveStyleContentVersion(contentContext);
|
|
717
|
-
|
|
718
|
-
return fsAdapter.getProjectData?.()?.updated_at;
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
private buildRouteManifestKey(pagePath: string): string {
|
|
722
|
-
const relativePagePath = extractRelativePath(pagePath, this.config.projectDir);
|
|
723
|
-
return relativePagePath
|
|
724
|
-
.replace(/\.(tsx|ts|jsx|mdx|md|js)$/, "")
|
|
725
|
-
.replace(/^pages\//, "");
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
private async extractProjectClassesForRoute(
|
|
729
|
-
context: HTMLGenerationContext,
|
|
730
|
-
appComponentPath?: string,
|
|
731
|
-
): Promise<Set<string>> {
|
|
732
|
-
const classes = new Set<string>();
|
|
733
|
-
|
|
734
|
-
const wrappedFs = this.config.adapter.fs as unknown as {
|
|
735
|
-
getUnderlyingAdapter?: () => unknown;
|
|
736
|
-
};
|
|
737
|
-
|
|
738
|
-
if (typeof wrappedFs.getUnderlyingAdapter !== "function") return classes;
|
|
739
|
-
|
|
740
|
-
const fsAdapter = wrappedFs.getUnderlyingAdapter() as {
|
|
741
|
-
getAllSourceFiles?: () =>
|
|
742
|
-
| Array<{ path: string; content?: string }>
|
|
743
|
-
| Promise<Array<{ path: string; content?: string }>>;
|
|
744
|
-
};
|
|
745
|
-
|
|
746
|
-
if (typeof fsAdapter.getAllSourceFiles !== "function") return classes;
|
|
747
|
-
|
|
748
|
-
const files = await fsAdapter.getAllSourceFiles();
|
|
749
|
-
const projectScope = context.options?.projectSlug || context.options?.projectId ||
|
|
750
|
-
this.config.projectDir;
|
|
751
|
-
const projectVersion = this.getProjectContentVersion() ??
|
|
752
|
-
(this.config.mode === "development" ? "dev" : "unknown");
|
|
753
|
-
const routeKey = this.buildRouteManifestKey(context.pageInfo.entity.path);
|
|
754
|
-
const routeLayoutPaths = context.nestedLayouts
|
|
755
|
-
.map((layout) => layout.componentPath || layout.path)
|
|
756
|
-
.filter((path): path is string => Boolean(path));
|
|
757
|
-
const routeFilePaths = [
|
|
758
|
-
context.pageInfo.entity.path,
|
|
759
|
-
...routeLayoutPaths,
|
|
760
|
-
...(appComponentPath ? [appComponentPath] : []),
|
|
761
|
-
];
|
|
762
|
-
|
|
763
|
-
const routeCandidates = getRouteCandidates({
|
|
764
|
-
projectScope,
|
|
765
|
-
projectVersion,
|
|
766
|
-
projectDir: this.config.projectDir,
|
|
767
|
-
styleProfile: createStyleScopeProfile(this.config.config),
|
|
768
|
-
routeKey,
|
|
769
|
-
routeFilePaths,
|
|
770
|
-
files,
|
|
771
|
-
developmentMode: this.config.mode === "development",
|
|
772
|
-
});
|
|
773
|
-
|
|
774
|
-
for (const cls of routeCandidates) classes.add(cls);
|
|
775
|
-
|
|
776
|
-
logger.debug("extractProjectClasses", {
|
|
777
|
-
filesProcessed: files.length,
|
|
778
|
-
routeKey,
|
|
779
|
-
routeFileCount: routeFilePaths.length,
|
|
780
|
-
totalClasses: classes.size,
|
|
781
|
-
});
|
|
782
|
-
|
|
783
|
-
return classes;
|
|
784
|
-
}
|
|
785
628
|
}
|