veryfront 0.1.84 → 0.1.85
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/css-candidate-manifest.d.ts +11 -0
- package/esm/src/rendering/orchestrator/css-candidate-manifest.d.ts.map +1 -1
- package/esm/src/rendering/orchestrator/css-candidate-manifest.js +23 -12
- package/esm/src/server/handlers/dev/styles-candidate-scanner.d.ts.map +1 -1
- package/esm/src/server/handlers/dev/styles-candidate-scanner.js +25 -8
- package/esm/src/server/handlers/dev/styles-css.handler.d.ts +1 -0
- package/esm/src/server/handlers/dev/styles-css.handler.d.ts.map +1 -1
- package/esm/src/server/handlers/dev/styles-css.handler.js +15 -5
- package/package.json +1 -1
- package/src/deno.js +1 -1
- package/src/src/rendering/orchestrator/css-candidate-manifest.ts +40 -14
- package/src/src/server/handlers/dev/styles-candidate-scanner.ts +37 -11
- package/src/src/server/handlers/dev/styles-css.handler.ts +25 -4
package/esm/deno.js
CHANGED
|
@@ -11,10 +11,21 @@ interface RouteCandidateOptions {
|
|
|
11
11
|
files: SourceFileLike[];
|
|
12
12
|
developmentMode: boolean;
|
|
13
13
|
}
|
|
14
|
+
interface ProjectCandidateOptions {
|
|
15
|
+
projectScope: string;
|
|
16
|
+
projectVersion: string;
|
|
17
|
+
projectDir: string;
|
|
18
|
+
files: SourceFileLike[];
|
|
19
|
+
developmentMode: boolean;
|
|
20
|
+
}
|
|
14
21
|
/**
|
|
15
22
|
* Resolve route-scoped Tailwind candidates from a precomputed per-project manifest.
|
|
16
23
|
*/
|
|
17
24
|
export declare function getRouteCandidates(options: RouteCandidateOptions): Set<string>;
|
|
25
|
+
/**
|
|
26
|
+
* Resolve full-project Tailwind candidates from a precomputed per-project manifest.
|
|
27
|
+
*/
|
|
28
|
+
export declare function getProjectCandidates(options: ProjectCandidateOptions): Set<string>;
|
|
18
29
|
/**
|
|
19
30
|
* Invalidate cached candidate manifests for one project scope (or all scopes).
|
|
20
31
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"css-candidate-manifest.d.ts","sourceRoot":"","sources":["../../../../src/src/rendering/orchestrator/css-candidate-manifest.ts"],"names":[],"mappings":"AAIA,UAAU,cAAc;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAQD,UAAU,qBAAqB;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,eAAe,EAAE,OAAO,CAAC;CAC1B;
|
|
1
|
+
{"version":3,"file":"css-candidate-manifest.d.ts","sourceRoot":"","sources":["../../../../src/src/rendering/orchestrator/css-candidate-manifest.ts"],"names":[],"mappings":"AAIA,UAAU,cAAc;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAQD,UAAU,qBAAqB;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,UAAU,uBAAuB;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,eAAe,EAAE,OAAO,CAAC;CAC1B;AA0GD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,qBAAqB,GAAG,GAAG,CAAC,MAAM,CAAC,CAkC9E;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,uBAAuB,GAAG,GAAG,CAAC,MAAM,CAAC,CAGlF;AAED;;GAEG;AACH,wBAAgB,mCAAmC,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAc/E"}
|
|
@@ -58,6 +58,21 @@ function buildCandidateManifest(files, projectDir) {
|
|
|
58
58
|
}
|
|
59
59
|
return { fileCandidates, allCandidates, builtAt: Date.now() };
|
|
60
60
|
}
|
|
61
|
+
function getOrBuildManifest(options) {
|
|
62
|
+
const manifestKey = buildManifestCacheKey(options.projectScope, options.projectVersion);
|
|
63
|
+
const existingManifest = manifestCache.get(manifestKey);
|
|
64
|
+
const manifest = shouldRebuildManifest(existingManifest, options.developmentMode)
|
|
65
|
+
? buildCandidateManifest(options.files, options.projectDir)
|
|
66
|
+
: existingManifest;
|
|
67
|
+
if (manifest !== existingManifest) {
|
|
68
|
+
manifestCache.set(manifestKey, manifest);
|
|
69
|
+
for (const key of routeCandidateCache.keys()) {
|
|
70
|
+
if (key.startsWith(`${manifestKey}:`))
|
|
71
|
+
routeCandidateCache.delete(key);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return manifest;
|
|
75
|
+
}
|
|
61
76
|
function addCandidatesForPath(target, manifest, path, projectDir) {
|
|
62
77
|
const absolutePath = normalizePath(path);
|
|
63
78
|
const relativePath = toRelativeProjectPath(path, projectDir);
|
|
@@ -73,18 +88,7 @@ function addCandidatesForPath(target, manifest, path, projectDir) {
|
|
|
73
88
|
*/
|
|
74
89
|
export function getRouteCandidates(options) {
|
|
75
90
|
const manifestKey = buildManifestCacheKey(options.projectScope, options.projectVersion);
|
|
76
|
-
const
|
|
77
|
-
const manifest = shouldRebuildManifest(existingManifest, options.developmentMode)
|
|
78
|
-
? buildCandidateManifest(options.files, options.projectDir)
|
|
79
|
-
: existingManifest;
|
|
80
|
-
if (manifest !== existingManifest) {
|
|
81
|
-
manifestCache.set(manifestKey, manifest);
|
|
82
|
-
// Clear route subsets when project-level file manifest is rebuilt.
|
|
83
|
-
for (const key of routeCandidateCache.keys()) {
|
|
84
|
-
if (key.startsWith(`${manifestKey}:`))
|
|
85
|
-
routeCandidateCache.delete(key);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
91
|
+
const manifest = getOrBuildManifest(options);
|
|
88
92
|
const routeCacheKey = `${manifestKey}:${options.routeKey}`;
|
|
89
93
|
const cachedRoute = routeCandidateCache.get(routeCacheKey);
|
|
90
94
|
if (cachedRoute)
|
|
@@ -112,6 +116,13 @@ export function getRouteCandidates(options) {
|
|
|
112
116
|
});
|
|
113
117
|
return new Set(routeCandidates);
|
|
114
118
|
}
|
|
119
|
+
/**
|
|
120
|
+
* Resolve full-project Tailwind candidates from a precomputed per-project manifest.
|
|
121
|
+
*/
|
|
122
|
+
export function getProjectCandidates(options) {
|
|
123
|
+
const manifest = getOrBuildManifest(options);
|
|
124
|
+
return new Set(manifest.allCandidates);
|
|
125
|
+
}
|
|
115
126
|
/**
|
|
116
127
|
* Invalidate cached candidate manifests for one project scope (or all scopes).
|
|
117
128
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"styles-candidate-scanner.d.ts","sourceRoot":"","sources":["../../../../../src/src/server/handlers/dev/styles-candidate-scanner.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"styles-candidate-scanner.d.ts","sourceRoot":"","sources":["../../../../../src/src/server/handlers/dev/styles-candidate-scanner.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAQH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AA8BlD;;;;;;GAMG;AACH,wBAAsB,wBAAwB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CA0CxF"}
|
|
@@ -11,12 +11,26 @@ import { extractCandidates } from "../../../html/styles-builder/tailwind-compile
|
|
|
11
11
|
import { serverLogger } from "../../../utils/index.js";
|
|
12
12
|
import { createFileSystem } from "../../../platform/compat/fs.js";
|
|
13
13
|
import { join } from "../../../platform/compat/path/index.js";
|
|
14
|
+
import { getProjectCandidates } from "../../../rendering/orchestrator/css-candidate-manifest.js";
|
|
14
15
|
import { FRAMEWORK_CANDIDATES } from "./framework-candidates.generated.js";
|
|
15
16
|
const logger = serverLogger.component("styles-candidate-scanner");
|
|
16
17
|
const SOURCE_EXTENSIONS = [".tsx", ".jsx", ".mdx", ".ts", ".js"];
|
|
17
18
|
const SKIP_DIRS = new Set(["node_modules", ".cache", ".git", "dist", "build", ".vscode"]);
|
|
18
19
|
/** De-duplicated set of framework candidates, computed once at import time. */
|
|
19
20
|
const frameworkCandidates = new Set(FRAMEWORK_CANDIDATES);
|
|
21
|
+
function resolveProjectVersion(ctx, contentContext) {
|
|
22
|
+
if (contentContext?.releaseId)
|
|
23
|
+
return `release:${contentContext.releaseId}`;
|
|
24
|
+
if (contentContext?.branch)
|
|
25
|
+
return `branch:${contentContext.branch}`;
|
|
26
|
+
if (contentContext?.environmentName)
|
|
27
|
+
return `environment:${contentContext.environmentName}`;
|
|
28
|
+
if (ctx.releaseId)
|
|
29
|
+
return `release:${ctx.releaseId}`;
|
|
30
|
+
if (ctx.parsedDomain?.branch)
|
|
31
|
+
return `branch:${ctx.parsedDomain.branch}`;
|
|
32
|
+
return "live";
|
|
33
|
+
}
|
|
20
34
|
/**
|
|
21
35
|
* Extract Tailwind CSS candidate class names from all project source files.
|
|
22
36
|
*
|
|
@@ -38,14 +52,17 @@ export async function extractProjectCandidates(ctx) {
|
|
|
38
52
|
}
|
|
39
53
|
const candidates = new Set(frameworkCandidates);
|
|
40
54
|
const files = await fsAdapter.getAllSourceFiles();
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
55
|
+
const contentContext = typeof fsAdapter.getContentContext === "function"
|
|
56
|
+
? fsAdapter.getContentContext()
|
|
57
|
+
: null;
|
|
58
|
+
for (const cls of getProjectCandidates({
|
|
59
|
+
projectScope: ctx.projectSlug ?? contentContext?.projectSlug ?? ctx.projectDir,
|
|
60
|
+
projectVersion: resolveProjectVersion(ctx, contentContext),
|
|
61
|
+
projectDir: ctx.projectDir,
|
|
62
|
+
files,
|
|
63
|
+
developmentMode: contentContext?.sourceType === "branch",
|
|
64
|
+
})) {
|
|
65
|
+
candidates.add(cls);
|
|
49
66
|
}
|
|
50
67
|
return candidates;
|
|
51
68
|
}
|
|
@@ -11,5 +11,6 @@ export declare class StylesCSSHandler extends BaseHandler {
|
|
|
11
11
|
metadata: HandlerMetadata;
|
|
12
12
|
handle(req: dntShim.Request, ctx: HandlerContext): Promise<HandlerResult>;
|
|
13
13
|
private loadStylesheet;
|
|
14
|
+
private generateStylesheet;
|
|
14
15
|
}
|
|
15
16
|
//# sourceMappingURL=styles-css.handler.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"styles-css.handler.d.ts","sourceRoot":"","sources":["../../../../../src/src/server/handlers/dev/styles-css.handler.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,OAAO,MAAM,2BAA2B,CAAC;AAGrD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAmB,aAAa,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"styles-css.handler.d.ts","sourceRoot":"","sources":["../../../../../src/src/server/handlers/dev/styles-css.handler.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,OAAO,MAAM,2BAA2B,CAAC;AAGrD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAmB,aAAa,EAAE,MAAM,aAAa,CAAC;AAkBnG,qBAAa,gBAAiB,SAAQ,WAAW;IAC/C,QAAQ,EAAE,eAAe,CAKvB;IAEI,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;YAkGjE,cAAc;IAkB5B,OAAO,CAAC,kBAAkB;CAe3B"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BaseHandler } from "../response/base.js";
|
|
2
2
|
import { HTTP_OK, PRIORITY_HIGH_DEV } from "../../../utils/constants/index.js";
|
|
3
3
|
import { joinPath } from "../../../utils/path-utils.js";
|
|
4
|
-
import { formatCSSError, generateTailwindCSS, } from "../../../html/styles-builder/tailwind-compiler.js";
|
|
4
|
+
import { formatCSSError, generateTailwindCSS, getProjectCSS, } from "../../../html/styles-builder/tailwind-compiler.js";
|
|
5
5
|
import { DEFAULT_STYLESHEET } from "../../../html/styles-builder/css-hash-cache.js";
|
|
6
6
|
import { serverLogger } from "../../../utils/index.js";
|
|
7
7
|
import { extractProjectCandidates } from "./styles-candidate-scanner.js";
|
|
@@ -39,10 +39,8 @@ export class StylesCSSHandler extends BaseHandler {
|
|
|
39
39
|
});
|
|
40
40
|
candidates = new Set();
|
|
41
41
|
}
|
|
42
|
-
const result = await
|
|
43
|
-
|
|
44
|
-
});
|
|
45
|
-
if (result.error) {
|
|
42
|
+
const result = await this.generateStylesheet(ctx, rawCss, candidates);
|
|
43
|
+
if ("error" in result && result.error) {
|
|
46
44
|
const formatted = formatCSSError(result.error);
|
|
47
45
|
logger.error("Tailwind error", {
|
|
48
46
|
error: formatted.message,
|
|
@@ -82,6 +80,8 @@ body::before {
|
|
|
82
80
|
logger.debug("CSS generated", {
|
|
83
81
|
candidates: candidates.size,
|
|
84
82
|
cssLength: result.css.length,
|
|
83
|
+
fromCache: "fromCache" in result ? result.fromCache : false,
|
|
84
|
+
cssHash: "hash" in result ? result.hash : undefined,
|
|
85
85
|
});
|
|
86
86
|
return this.respond(responseBuilder.withContentType("text/css; charset=utf-8", result.css, HTTP_OK));
|
|
87
87
|
});
|
|
@@ -114,4 +114,14 @@ body::before {
|
|
|
114
114
|
return DEFAULT_STYLESHEET;
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
|
+
generateStylesheet(ctx, rawCss, candidates) {
|
|
118
|
+
if (!ctx.projectSlug) {
|
|
119
|
+
return generateTailwindCSS(rawCss, candidates);
|
|
120
|
+
}
|
|
121
|
+
return getProjectCSS(ctx.projectSlug, rawCss, candidates, {
|
|
122
|
+
minify: true,
|
|
123
|
+
environment: "preview",
|
|
124
|
+
buildMode: "production",
|
|
125
|
+
});
|
|
126
|
+
}
|
|
117
127
|
}
|
package/package.json
CHANGED
package/src/deno.js
CHANGED
|
@@ -23,6 +23,14 @@ interface RouteCandidateOptions {
|
|
|
23
23
|
developmentMode: boolean;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
interface ProjectCandidateOptions {
|
|
27
|
+
projectScope: string;
|
|
28
|
+
projectVersion: string;
|
|
29
|
+
projectDir: string;
|
|
30
|
+
files: SourceFileLike[];
|
|
31
|
+
developmentMode: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
26
34
|
const logger = rendererLogger.component("css-candidate-manifest");
|
|
27
35
|
const SOURCE_EXTENSIONS = [".tsx", ".jsx", ".mdx", ".ts", ".js"];
|
|
28
36
|
const DEV_MANIFEST_TTL_MS = 2_000;
|
|
@@ -90,6 +98,29 @@ function buildCandidateManifest(files: SourceFileLike[], projectDir: string): Ca
|
|
|
90
98
|
return { fileCandidates, allCandidates, builtAt: Date.now() };
|
|
91
99
|
}
|
|
92
100
|
|
|
101
|
+
function getOrBuildManifest(
|
|
102
|
+
options: Pick<
|
|
103
|
+
ProjectCandidateOptions,
|
|
104
|
+
"projectScope" | "projectVersion" | "projectDir" | "files" | "developmentMode"
|
|
105
|
+
>,
|
|
106
|
+
): CandidateManifest {
|
|
107
|
+
const manifestKey = buildManifestCacheKey(options.projectScope, options.projectVersion);
|
|
108
|
+
const existingManifest = manifestCache.get(manifestKey);
|
|
109
|
+
const manifest = shouldRebuildManifest(existingManifest, options.developmentMode)
|
|
110
|
+
? buildCandidateManifest(options.files, options.projectDir)
|
|
111
|
+
: existingManifest!;
|
|
112
|
+
|
|
113
|
+
if (manifest !== existingManifest) {
|
|
114
|
+
manifestCache.set(manifestKey, manifest);
|
|
115
|
+
|
|
116
|
+
for (const key of routeCandidateCache.keys()) {
|
|
117
|
+
if (key.startsWith(`${manifestKey}:`)) routeCandidateCache.delete(key);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return manifest;
|
|
122
|
+
}
|
|
123
|
+
|
|
93
124
|
function addCandidatesForPath(
|
|
94
125
|
target: Set<string>,
|
|
95
126
|
manifest: CandidateManifest,
|
|
@@ -109,20 +140,7 @@ function addCandidatesForPath(
|
|
|
109
140
|
*/
|
|
110
141
|
export function getRouteCandidates(options: RouteCandidateOptions): Set<string> {
|
|
111
142
|
const manifestKey = buildManifestCacheKey(options.projectScope, options.projectVersion);
|
|
112
|
-
const
|
|
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
|
-
|
|
143
|
+
const manifest = getOrBuildManifest(options);
|
|
126
144
|
const routeCacheKey = `${manifestKey}:${options.routeKey}`;
|
|
127
145
|
const cachedRoute = routeCandidateCache.get(routeCacheKey);
|
|
128
146
|
if (cachedRoute) return new Set(cachedRoute);
|
|
@@ -156,6 +174,14 @@ export function getRouteCandidates(options: RouteCandidateOptions): Set<string>
|
|
|
156
174
|
return new Set(routeCandidates);
|
|
157
175
|
}
|
|
158
176
|
|
|
177
|
+
/**
|
|
178
|
+
* Resolve full-project Tailwind candidates from a precomputed per-project manifest.
|
|
179
|
+
*/
|
|
180
|
+
export function getProjectCandidates(options: ProjectCandidateOptions): Set<string> {
|
|
181
|
+
const manifest = getOrBuildManifest(options);
|
|
182
|
+
return new Set(manifest.allCandidates);
|
|
183
|
+
}
|
|
184
|
+
|
|
159
185
|
/**
|
|
160
186
|
* Invalidate cached candidate manifests for one project scope (or all scopes).
|
|
161
187
|
*/
|
|
@@ -12,6 +12,8 @@ import { extractCandidates } from "../../../html/styles-builder/tailwind-compile
|
|
|
12
12
|
import { serverLogger } from "../../../utils/index.js";
|
|
13
13
|
import { createFileSystem } from "../../../platform/compat/fs.js";
|
|
14
14
|
import { join } from "../../../platform/compat/path/index.js";
|
|
15
|
+
import { getProjectCandidates } from "../../../rendering/orchestrator/css-candidate-manifest.js";
|
|
16
|
+
import type { ResolvedContentContext } from "../../../platform/adapters/fs/veryfront/types.js";
|
|
15
17
|
import type { HandlerContext } from "../types.js";
|
|
16
18
|
import { FRAMEWORK_CANDIDATES } from "./framework-candidates.generated.js";
|
|
17
19
|
|
|
@@ -23,6 +25,25 @@ const SKIP_DIRS = new Set(["node_modules", ".cache", ".git", "dist", "build", ".
|
|
|
23
25
|
/** De-duplicated set of framework candidates, computed once at import time. */
|
|
24
26
|
const frameworkCandidates = new Set<string>(FRAMEWORK_CANDIDATES);
|
|
25
27
|
|
|
28
|
+
interface SourceFileProvider {
|
|
29
|
+
getAllSourceFiles?: () =>
|
|
30
|
+
| Array<{ path: string; content?: string }>
|
|
31
|
+
| Promise<Array<{ path: string; content?: string }>>;
|
|
32
|
+
getContentContext?: () => ResolvedContentContext | null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function resolveProjectVersion(
|
|
36
|
+
ctx: HandlerContext,
|
|
37
|
+
contentContext: ResolvedContentContext | null,
|
|
38
|
+
): string {
|
|
39
|
+
if (contentContext?.releaseId) return `release:${contentContext.releaseId}`;
|
|
40
|
+
if (contentContext?.branch) return `branch:${contentContext.branch}`;
|
|
41
|
+
if (contentContext?.environmentName) return `environment:${contentContext.environmentName}`;
|
|
42
|
+
if (ctx.releaseId) return `release:${ctx.releaseId}`;
|
|
43
|
+
if (ctx.parsedDomain?.branch) return `branch:${ctx.parsedDomain.branch}`;
|
|
44
|
+
return "live";
|
|
45
|
+
}
|
|
46
|
+
|
|
26
47
|
/**
|
|
27
48
|
* Extract Tailwind CSS candidate class names from all project source files.
|
|
28
49
|
*
|
|
@@ -42,9 +63,8 @@ export async function extractProjectCandidates(ctx: HandlerContext): Promise<Set
|
|
|
42
63
|
|
|
43
64
|
// Call method directly on wrappedFs to preserve 'this' context
|
|
44
65
|
const fsAdapter = wrappedFs.getUnderlyingAdapter() as {
|
|
45
|
-
getAllSourceFiles?:
|
|
46
|
-
|
|
47
|
-
| Promise<Array<{ path: string; content?: string }>>;
|
|
66
|
+
getAllSourceFiles?: SourceFileProvider["getAllSourceFiles"];
|
|
67
|
+
getContentContext?: SourceFileProvider["getContentContext"];
|
|
48
68
|
};
|
|
49
69
|
|
|
50
70
|
if (typeof fsAdapter.getAllSourceFiles !== "function") {
|
|
@@ -56,14 +76,20 @@ export async function extractProjectCandidates(ctx: HandlerContext): Promise<Set
|
|
|
56
76
|
|
|
57
77
|
const candidates = new Set<string>(frameworkCandidates);
|
|
58
78
|
const files = await fsAdapter.getAllSourceFiles();
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
79
|
+
const contentContext = typeof fsAdapter.getContentContext === "function"
|
|
80
|
+
? fsAdapter.getContentContext()
|
|
81
|
+
: null;
|
|
82
|
+
|
|
83
|
+
for (
|
|
84
|
+
const cls of getProjectCandidates({
|
|
85
|
+
projectScope: ctx.projectSlug ?? contentContext?.projectSlug ?? ctx.projectDir,
|
|
86
|
+
projectVersion: resolveProjectVersion(ctx, contentContext),
|
|
87
|
+
projectDir: ctx.projectDir,
|
|
88
|
+
files,
|
|
89
|
+
developmentMode: contentContext?.sourceType === "branch",
|
|
90
|
+
})
|
|
91
|
+
) {
|
|
92
|
+
candidates.add(cls);
|
|
67
93
|
}
|
|
68
94
|
|
|
69
95
|
return candidates;
|
|
@@ -14,6 +14,7 @@ import { joinPath } from "../../../utils/path-utils.js";
|
|
|
14
14
|
import {
|
|
15
15
|
formatCSSError,
|
|
16
16
|
generateTailwindCSS,
|
|
17
|
+
getProjectCSS,
|
|
17
18
|
} from "../../../html/styles-builder/tailwind-compiler.js";
|
|
18
19
|
import { DEFAULT_STYLESHEET } from "../../../html/styles-builder/css-hash-cache.js";
|
|
19
20
|
import { serverLogger } from "../../../utils/index.js";
|
|
@@ -21,6 +22,10 @@ import { extractProjectCandidates } from "./styles-candidate-scanner.js";
|
|
|
21
22
|
|
|
22
23
|
const logger = serverLogger.component("styles-css-handler");
|
|
23
24
|
|
|
25
|
+
type GeneratedStylesResult =
|
|
26
|
+
| Awaited<ReturnType<typeof generateTailwindCSS>>
|
|
27
|
+
| Awaited<ReturnType<typeof getProjectCSS>>;
|
|
28
|
+
|
|
24
29
|
export class StylesCSSHandler extends BaseHandler {
|
|
25
30
|
metadata: HandlerMetadata = {
|
|
26
31
|
name: "StylesCSSHandler",
|
|
@@ -54,11 +59,9 @@ export class StylesCSSHandler extends BaseHandler {
|
|
|
54
59
|
});
|
|
55
60
|
candidates = new Set<string>();
|
|
56
61
|
}
|
|
57
|
-
const result = await
|
|
58
|
-
projectSlug: ctx.projectSlug,
|
|
59
|
-
});
|
|
62
|
+
const result = await this.generateStylesheet(ctx, rawCss, candidates);
|
|
60
63
|
|
|
61
|
-
if (result.error) {
|
|
64
|
+
if ("error" in result && result.error) {
|
|
62
65
|
const formatted = formatCSSError(result.error);
|
|
63
66
|
logger.error("Tailwind error", {
|
|
64
67
|
error: formatted.message,
|
|
@@ -104,6 +107,8 @@ body::before {
|
|
|
104
107
|
logger.debug("CSS generated", {
|
|
105
108
|
candidates: candidates.size,
|
|
106
109
|
cssLength: result.css.length,
|
|
110
|
+
fromCache: "fromCache" in result ? result.fromCache : false,
|
|
111
|
+
cssHash: "hash" in result ? result.hash : undefined,
|
|
107
112
|
});
|
|
108
113
|
|
|
109
114
|
return this.respond(
|
|
@@ -144,4 +149,20 @@ body::before {
|
|
|
144
149
|
return DEFAULT_STYLESHEET;
|
|
145
150
|
}
|
|
146
151
|
}
|
|
152
|
+
|
|
153
|
+
private generateStylesheet(
|
|
154
|
+
ctx: HandlerContext,
|
|
155
|
+
rawCss: string,
|
|
156
|
+
candidates: Set<string>,
|
|
157
|
+
): Promise<GeneratedStylesResult> {
|
|
158
|
+
if (!ctx.projectSlug) {
|
|
159
|
+
return generateTailwindCSS(rawCss, candidates);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return getProjectCSS(ctx.projectSlug, rawCss, candidates, {
|
|
163
|
+
minify: true,
|
|
164
|
+
environment: "preview",
|
|
165
|
+
buildMode: "production",
|
|
166
|
+
});
|
|
167
|
+
}
|
|
147
168
|
}
|