veryfront 0.1.89 → 0.1.91
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/cli/commands/knowledge/command.d.ts +3 -0
- package/esm/cli/commands/knowledge/command.d.ts.map +1 -1
- package/esm/cli/commands/knowledge/command.js +27 -2
- package/esm/deno.js +1 -1
- package/esm/src/html/styles-builder/candidate-extractor.d.ts +5 -1
- package/esm/src/html/styles-builder/candidate-extractor.d.ts.map +1 -1
- package/esm/src/html/styles-builder/candidate-extractor.js +6 -1
- package/esm/src/html/styles-builder/content-version.d.ts +9 -0
- package/esm/src/html/styles-builder/content-version.d.ts.map +1 -0
- package/esm/src/html/styles-builder/content-version.js +15 -0
- package/esm/src/html/styles-builder/css-pregeneration.d.ts +11 -0
- package/esm/src/html/styles-builder/css-pregeneration.d.ts.map +1 -1
- package/esm/src/html/styles-builder/css-pregeneration.js +15 -10
- package/esm/src/html/styles-builder/prepared-project-css-cache.d.ts +31 -0
- package/esm/src/html/styles-builder/prepared-project-css-cache.d.ts.map +1 -0
- package/esm/src/html/styles-builder/prepared-project-css-cache.js +165 -0
- package/esm/src/html/styles-builder/style-scope-profile.d.ts +14 -0
- package/esm/src/html/styles-builder/style-scope-profile.d.ts.map +1 -0
- package/esm/src/html/styles-builder/style-scope-profile.js +121 -0
- package/esm/src/jobs/schemas.d.ts +30 -30
- package/esm/src/platform/adapters/fs/factory.d.ts.map +1 -1
- package/esm/src/platform/adapters/fs/factory.js +2 -21
- package/esm/src/platform/adapters/fs/veryfront/adapter.d.ts.map +1 -1
- package/esm/src/platform/adapters/fs/veryfront/adapter.js +44 -19
- package/esm/src/platform/adapters/fs/veryfront/default-invalidation-callbacks.d.ts +3 -0
- package/esm/src/platform/adapters/fs/veryfront/default-invalidation-callbacks.d.ts.map +1 -0
- package/esm/src/platform/adapters/fs/veryfront/default-invalidation-callbacks.js +25 -0
- package/esm/src/platform/adapters/fs/veryfront/proxy-manager.d.ts.map +1 -1
- package/esm/src/platform/adapters/fs/veryfront/proxy-manager.js +2 -15
- package/esm/src/platform/adapters/fs/veryfront/websocket-manager.d.ts +4 -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 +2 -0
- package/esm/src/platform/adapters/veryfront-api-client/client.d.ts +3 -1
- package/esm/src/platform/adapters/veryfront-api-client/client.d.ts.map +1 -1
- package/esm/src/platform/adapters/veryfront-api-client/client.js +6 -0
- package/esm/src/platform/adapters/veryfront-api-client/index.d.ts +2 -2
- package/esm/src/platform/adapters/veryfront-api-client/index.d.ts.map +1 -1
- package/esm/src/platform/adapters/veryfront-api-client/index.js +1 -1
- package/esm/src/platform/adapters/veryfront-api-client/operations.d.ts +24 -0
- package/esm/src/platform/adapters/veryfront-api-client/operations.d.ts.map +1 -1
- package/esm/src/platform/adapters/veryfront-api-client/operations.js +65 -3
- package/esm/src/platform/adapters/veryfront-api-client/schemas/api.schema.d.ts +28 -0
- package/esm/src/platform/adapters/veryfront-api-client/schemas/api.schema.d.ts.map +1 -1
- package/esm/src/platform/adapters/veryfront-api-client/schemas/api.schema.js +13 -0
- package/esm/src/platform/adapters/veryfront-api-client/schemas/index.d.ts +1 -1
- package/esm/src/platform/adapters/veryfront-api-client/schemas/index.d.ts.map +1 -1
- package/esm/src/platform/adapters/veryfront-api-client/schemas/index.js +1 -1
- package/esm/src/rendering/orchestrator/css-candidate-manifest.d.ts +3 -0
- package/esm/src/rendering/orchestrator/css-candidate-manifest.d.ts.map +1 -1
- package/esm/src/rendering/orchestrator/css-candidate-manifest.js +10 -5
- package/esm/src/rendering/orchestrator/html.d.ts.map +1 -1
- package/esm/src/rendering/orchestrator/html.js +8 -0
- package/esm/src/sandbox/index.d.ts +1 -1
- package/esm/src/sandbox/index.d.ts.map +1 -1
- package/esm/src/sandbox/index.js +1 -1
- package/esm/src/sandbox/sandbox.d.ts +58 -0
- package/esm/src/sandbox/sandbox.d.ts.map +1 -1
- package/esm/src/sandbox/sandbox.js +111 -0
- package/esm/src/server/handlers/dev/styles-candidate-scanner.d.ts.map +1 -1
- package/esm/src/server/handlers/dev/styles-candidate-scanner.js +14 -16
- package/esm/src/server/handlers/dev/styles-css.handler.d.ts +7 -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 +175 -8
- package/package.json +1 -1
- package/src/cli/commands/knowledge/command.ts +30 -2
- package/src/deno.js +1 -1
- package/src/src/html/styles-builder/candidate-extractor.ts +13 -0
- package/src/src/html/styles-builder/content-version.ts +20 -0
- package/src/src/html/styles-builder/css-pregeneration.ts +49 -12
- package/src/src/html/styles-builder/prepared-project-css-cache.ts +228 -0
- package/src/src/html/styles-builder/style-scope-profile.ts +164 -0
- package/src/src/platform/adapters/fs/factory.ts +2 -27
- package/src/src/platform/adapters/fs/veryfront/adapter.ts +49 -20
- package/src/src/platform/adapters/fs/veryfront/default-invalidation-callbacks.ts +35 -0
- package/src/src/platform/adapters/fs/veryfront/proxy-manager.ts +4 -21
- package/src/src/platform/adapters/fs/veryfront/websocket-manager.ts +3 -0
- package/src/src/platform/adapters/veryfront-api-client/client.ts +17 -0
- package/src/src/platform/adapters/veryfront-api-client/index.ts +6 -0
- package/src/src/platform/adapters/veryfront-api-client/operations.ts +110 -3
- package/src/src/platform/adapters/veryfront-api-client/schemas/api.schema.ts +16 -0
- package/src/src/platform/adapters/veryfront-api-client/schemas/index.ts +2 -0
- package/src/src/rendering/orchestrator/css-candidate-manifest.ts +28 -6
- package/src/src/rendering/orchestrator/html.ts +11 -0
- package/src/src/sandbox/index.ts +13 -1
- package/src/src/sandbox/sandbox.ts +183 -0
- package/src/src/server/handlers/dev/styles-candidate-scanner.ts +18 -15
- package/src/src/server/handlers/dev/styles-css.handler.ts +262 -12
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
* @module server/handlers/dev/styles-candidate-scanner
|
|
9
9
|
*/
|
|
10
10
|
import { extractCandidates } from "../../../html/styles-builder/tailwind-compiler.js";
|
|
11
|
+
import { resolveStyleContentVersion } from "../../../html/styles-builder/content-version.js";
|
|
12
|
+
import { createStyleScopeProfile, shouldIncludeStylePath, shouldTraverseStyleDirectory, } from "../../../html/styles-builder/style-scope-profile.js";
|
|
11
13
|
import { serverLogger } from "../../../utils/index.js";
|
|
12
14
|
import { createFileSystem } from "../../../platform/compat/fs.js";
|
|
13
15
|
import { join } from "../../../platform/compat/path/index.js";
|
|
@@ -15,22 +17,8 @@ import { getProjectCandidates } from "../../../rendering/orchestrator/css-candid
|
|
|
15
17
|
import { FRAMEWORK_CANDIDATES } from "./framework-candidates.generated.js";
|
|
16
18
|
const logger = serverLogger.component("styles-candidate-scanner");
|
|
17
19
|
const SOURCE_EXTENSIONS = [".tsx", ".jsx", ".mdx", ".ts", ".js"];
|
|
18
|
-
const SKIP_DIRS = new Set(["node_modules", ".cache", ".git", "dist", "build", ".vscode"]);
|
|
19
20
|
/** De-duplicated set of framework candidates, computed once at import time. */
|
|
20
21
|
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
|
-
}
|
|
34
22
|
/**
|
|
35
23
|
* Extract Tailwind CSS candidate class names from all project source files.
|
|
36
24
|
*
|
|
@@ -39,6 +27,7 @@ function resolveProjectVersion(ctx, contentContext) {
|
|
|
39
27
|
* method is available (local dev mode).
|
|
40
28
|
*/
|
|
41
29
|
export async function extractProjectCandidates(ctx) {
|
|
30
|
+
const styleProfile = createStyleScopeProfile(ctx.config);
|
|
42
31
|
const wrappedFs = ctx.adapter.fs;
|
|
43
32
|
if (typeof wrappedFs.getUnderlyingAdapter !== "function") {
|
|
44
33
|
logger.debug("[StylesCandidateScanner] No FS adapter wrapper, falling back to local file scanning");
|
|
@@ -57,8 +46,13 @@ export async function extractProjectCandidates(ctx) {
|
|
|
57
46
|
: null;
|
|
58
47
|
for (const cls of getProjectCandidates({
|
|
59
48
|
projectScope: ctx.projectSlug ?? contentContext?.projectSlug ?? ctx.projectDir,
|
|
60
|
-
projectVersion:
|
|
49
|
+
projectVersion: resolveStyleContentVersion(contentContext, {
|
|
50
|
+
releaseId: ctx.releaseId,
|
|
51
|
+
branch: ctx.parsedDomain?.branch,
|
|
52
|
+
environmentName: ctx.environmentName,
|
|
53
|
+
}),
|
|
61
54
|
projectDir: ctx.projectDir,
|
|
55
|
+
styleProfile,
|
|
62
56
|
files,
|
|
63
57
|
developmentMode: contentContext?.sourceType === "branch",
|
|
64
58
|
})) {
|
|
@@ -71,6 +65,7 @@ export async function extractProjectCandidates(ctx) {
|
|
|
71
65
|
* Used in local development mode where projects are read directly from disk.
|
|
72
66
|
*/
|
|
73
67
|
async function scanLocalFiles(projectDir, ctx) {
|
|
68
|
+
const styleProfile = createStyleScopeProfile(ctx.config);
|
|
74
69
|
const candidates = new Set(frameworkCandidates);
|
|
75
70
|
const fs = createFileSystem();
|
|
76
71
|
const scanDir = async (dir) => {
|
|
@@ -85,12 +80,15 @@ async function scanLocalFiles(projectDir, ctx) {
|
|
|
85
80
|
for await (const entry of entries) {
|
|
86
81
|
const fullPath = join(dir, entry.name);
|
|
87
82
|
if (entry.isDirectory) {
|
|
88
|
-
if (
|
|
83
|
+
if (shouldTraverseStyleDirectory(styleProfile, fullPath, projectDir)) {
|
|
89
84
|
await scanDir(fullPath);
|
|
85
|
+
}
|
|
90
86
|
continue;
|
|
91
87
|
}
|
|
92
88
|
if (!entry.isFile)
|
|
93
89
|
continue;
|
|
90
|
+
if (!shouldIncludeStylePath(styleProfile, fullPath, projectDir))
|
|
91
|
+
continue;
|
|
94
92
|
if (!SOURCE_EXTENSIONS.some((ext) => entry.name.endsWith(ext)))
|
|
95
93
|
continue;
|
|
96
94
|
try {
|
|
@@ -12,5 +12,12 @@ export declare class StylesCSSHandler extends BaseHandler {
|
|
|
12
12
|
handle(req: dntShim.Request, ctx: HandlerContext): Promise<HandlerResult>;
|
|
13
13
|
private loadStylesheet;
|
|
14
14
|
private generateStylesheet;
|
|
15
|
+
private getContentContext;
|
|
16
|
+
private getVeryfrontApiClient;
|
|
17
|
+
private createPreparedCSSContext;
|
|
18
|
+
private resolveStyleArtifactSelector;
|
|
19
|
+
private tryResolveRemotePreparedCSS;
|
|
20
|
+
private getPreparedCSSByHash;
|
|
21
|
+
private registerPreparedCSSArtifact;
|
|
15
22
|
}
|
|
16
23
|
//# 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;AA+BnG,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;YAiKjE,cAAc;IAkB5B,OAAO,CAAC,kBAAkB;IAc1B,OAAO,CAAC,iBAAiB;IAWzB,OAAO,CAAC,qBAAqB;IAW7B,OAAO,CAAC,wBAAwB;IA0BhC,OAAO,CAAC,4BAA4B;YA2CtB,2BAA2B;YAiD3B,oBAAoB;YASpB,2BAA2B;CA0B1C"}
|
|
@@ -1,8 +1,11 @@
|
|
|
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,
|
|
4
|
+
import { formatCSSError, getCSSByHashAsync, getProjectCSS, regenerateCSSByHash, } from "../../../html/styles-builder/tailwind-compiler.js";
|
|
5
5
|
import { DEFAULT_STYLESHEET } from "../../../html/styles-builder/css-hash-cache.js";
|
|
6
|
+
import { resolveStyleContentVersion } from "../../../html/styles-builder/content-version.js";
|
|
7
|
+
import { createPreparedProjectCSSContext, storePreparedProjectCSS, tryGetPreparedProjectCSS, } from "../../../html/styles-builder/prepared-project-css-cache.js";
|
|
8
|
+
import { createStyleScopeProfile } from "../../../html/styles-builder/style-scope-profile.js";
|
|
6
9
|
import { serverLogger } from "../../../utils/index.js";
|
|
7
10
|
import { extractProjectCandidates } from "./styles-candidate-scanner.js";
|
|
8
11
|
const logger = serverLogger.component("styles-css-handler");
|
|
@@ -19,6 +22,9 @@ export class StylesCSSHandler extends BaseHandler {
|
|
|
19
22
|
try {
|
|
20
23
|
return await this.withProxyContext(ctx, async () => {
|
|
21
24
|
const responseBuilder = this.createResponseBuilder(ctx).withCache("no-cache");
|
|
25
|
+
const projectScope = ctx.projectSlug ?? ctx.projectDir;
|
|
26
|
+
const styleProfile = createStyleScopeProfile(ctx.config);
|
|
27
|
+
const contentContext = this.getContentContext(ctx);
|
|
22
28
|
let rawCss;
|
|
23
29
|
try {
|
|
24
30
|
rawCss = await this.loadStylesheet(ctx);
|
|
@@ -29,6 +35,28 @@ export class StylesCSSHandler extends BaseHandler {
|
|
|
29
35
|
});
|
|
30
36
|
rawCss = DEFAULT_STYLESHEET;
|
|
31
37
|
}
|
|
38
|
+
const preparedContext = this.createPreparedCSSContext(projectScope, rawCss, styleProfile.hash, contentContext, ctx);
|
|
39
|
+
if (preparedContext) {
|
|
40
|
+
const prepared = await tryGetPreparedProjectCSS(preparedContext);
|
|
41
|
+
if (prepared) {
|
|
42
|
+
logger.debug("Prepared CSS cache hit", {
|
|
43
|
+
projectScope,
|
|
44
|
+
projectVersion: preparedContext.projectVersion,
|
|
45
|
+
styleProfileHash: styleProfile.hash,
|
|
46
|
+
cssHash: prepared.hash,
|
|
47
|
+
});
|
|
48
|
+
return this.respond(responseBuilder.withContentType("text/css; charset=utf-8", prepared.css, HTTP_OK));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const remotePrepared = await this.tryResolveRemotePreparedCSS(ctx, projectScope, styleProfile.hash, contentContext, preparedContext);
|
|
52
|
+
if (remotePrepared) {
|
|
53
|
+
logger.debug("Prepared CSS resolved via style artifact metadata", {
|
|
54
|
+
projectScope,
|
|
55
|
+
styleProfileHash: styleProfile.hash,
|
|
56
|
+
cssHash: remotePrepared.hash,
|
|
57
|
+
});
|
|
58
|
+
return this.respond(responseBuilder.withContentType("text/css; charset=utf-8", remotePrepared.css, HTTP_OK));
|
|
59
|
+
}
|
|
32
60
|
let candidates;
|
|
33
61
|
try {
|
|
34
62
|
candidates = await extractProjectCandidates(ctx);
|
|
@@ -39,9 +67,12 @@ export class StylesCSSHandler extends BaseHandler {
|
|
|
39
67
|
});
|
|
40
68
|
candidates = new Set();
|
|
41
69
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
70
|
+
let result;
|
|
71
|
+
try {
|
|
72
|
+
result = await this.generateStylesheet(ctx, rawCss, candidates);
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
const formatted = formatCSSError(error instanceof Error ? error : String(error));
|
|
45
76
|
logger.error("Tailwind error", {
|
|
46
77
|
error: formatted.message,
|
|
47
78
|
suggestion: formatted.suggestion,
|
|
@@ -78,11 +109,21 @@ body::before {
|
|
|
78
109
|
});
|
|
79
110
|
}
|
|
80
111
|
logger.debug("CSS generated", {
|
|
112
|
+
projectScope,
|
|
81
113
|
candidates: candidates.size,
|
|
82
114
|
cssLength: result.css.length,
|
|
83
115
|
fromCache: "fromCache" in result ? result.fromCache : false,
|
|
84
116
|
cssHash: "hash" in result ? result.hash : undefined,
|
|
85
117
|
});
|
|
118
|
+
if (preparedContext && "hash" in result) {
|
|
119
|
+
await storePreparedProjectCSS(preparedContext, {
|
|
120
|
+
css: result.css,
|
|
121
|
+
hash: result.hash,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
if ("hash" in result) {
|
|
125
|
+
await this.registerPreparedCSSArtifact(ctx, styleProfile.hash, contentContext, result.hash);
|
|
126
|
+
}
|
|
86
127
|
return this.respond(responseBuilder.withContentType("text/css; charset=utf-8", result.css, HTTP_OK));
|
|
87
128
|
});
|
|
88
129
|
}
|
|
@@ -115,13 +156,139 @@ body::before {
|
|
|
115
156
|
}
|
|
116
157
|
}
|
|
117
158
|
generateStylesheet(ctx, rawCss, candidates) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}
|
|
121
|
-
return getProjectCSS(ctx.projectSlug, rawCss, candidates, {
|
|
159
|
+
const projectScope = ctx.projectSlug ?? ctx.projectDir;
|
|
160
|
+
return getProjectCSS(projectScope, rawCss, candidates, {
|
|
122
161
|
minify: true,
|
|
123
162
|
environment: "preview",
|
|
124
163
|
buildMode: "production",
|
|
125
164
|
});
|
|
126
165
|
}
|
|
166
|
+
getContentContext(ctx) {
|
|
167
|
+
const wrappedFs = ctx.adapter.fs;
|
|
168
|
+
if (typeof wrappedFs.getUnderlyingAdapter !== "function")
|
|
169
|
+
return null;
|
|
170
|
+
const fsAdapter = wrappedFs.getUnderlyingAdapter();
|
|
171
|
+
return typeof fsAdapter.getContentContext === "function" ? fsAdapter.getContentContext() : null;
|
|
172
|
+
}
|
|
173
|
+
getVeryfrontApiClient(ctx) {
|
|
174
|
+
const wrappedFs = ctx.adapter.fs;
|
|
175
|
+
if (typeof wrappedFs.getUnderlyingAdapter !== "function")
|
|
176
|
+
return null;
|
|
177
|
+
const fsAdapter = wrappedFs.getUnderlyingAdapter();
|
|
178
|
+
return typeof fsAdapter.getClient === "function" ? fsAdapter.getClient() : null;
|
|
179
|
+
}
|
|
180
|
+
createPreparedCSSContext(projectScope, rawCss, styleProfileHash, contentContext, ctx) {
|
|
181
|
+
if (!projectScope)
|
|
182
|
+
return undefined;
|
|
183
|
+
return createPreparedProjectCSSContext(projectScope, resolveStyleContentVersion(contentContext, {
|
|
184
|
+
releaseId: ctx.releaseId,
|
|
185
|
+
branch: ctx.parsedDomain?.branch,
|
|
186
|
+
environmentName: ctx.environmentName,
|
|
187
|
+
}), rawCss, styleProfileHash, {
|
|
188
|
+
minify: true,
|
|
189
|
+
environment: "preview",
|
|
190
|
+
buildMode: "production",
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
resolveStyleArtifactSelector(contentContext, ctx) {
|
|
194
|
+
if (contentContext?.sourceType === "branch" && contentContext.branch) {
|
|
195
|
+
return {
|
|
196
|
+
branch: contentContext.branch,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
if (contentContext?.sourceType === "environment" && contentContext.environmentName) {
|
|
200
|
+
return {
|
|
201
|
+
environmentName: contentContext.environmentName,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
if (contentContext?.sourceType === "release" && contentContext.releaseId) {
|
|
205
|
+
return {
|
|
206
|
+
releaseId: contentContext.releaseId,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
if (ctx.parsedDomain?.branch) {
|
|
210
|
+
return {
|
|
211
|
+
branch: ctx.parsedDomain.branch,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
if (ctx.environmentName) {
|
|
215
|
+
return {
|
|
216
|
+
environmentName: ctx.environmentName,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
if (ctx.releaseId) {
|
|
220
|
+
return {
|
|
221
|
+
releaseId: ctx.releaseId,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
async tryResolveRemotePreparedCSS(ctx, projectScope, styleProfileHash, contentContext, preparedContext) {
|
|
227
|
+
if (!projectScope)
|
|
228
|
+
return undefined;
|
|
229
|
+
const selector = this.resolveStyleArtifactSelector(contentContext, ctx);
|
|
230
|
+
if (!selector)
|
|
231
|
+
return undefined;
|
|
232
|
+
const client = this.getVeryfrontApiClient(ctx);
|
|
233
|
+
if (!client)
|
|
234
|
+
return undefined;
|
|
235
|
+
try {
|
|
236
|
+
const resolved = await client.resolveStyleArtifact({
|
|
237
|
+
...selector,
|
|
238
|
+
styleProfileHash,
|
|
239
|
+
});
|
|
240
|
+
if (resolved.status !== "ready" || !resolved.artifactHash) {
|
|
241
|
+
return undefined;
|
|
242
|
+
}
|
|
243
|
+
const css = await this.getPreparedCSSByHash(resolved.artifactHash, projectScope);
|
|
244
|
+
if (!css)
|
|
245
|
+
return undefined;
|
|
246
|
+
if (preparedContext) {
|
|
247
|
+
await storePreparedProjectCSS(preparedContext, {
|
|
248
|
+
css,
|
|
249
|
+
hash: resolved.artifactHash,
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
return {
|
|
253
|
+
css,
|
|
254
|
+
hash: resolved.artifactHash,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
catch (error) {
|
|
258
|
+
logger.debug("Failed to resolve prepared CSS via style artifact metadata", {
|
|
259
|
+
projectScope,
|
|
260
|
+
styleProfileHash,
|
|
261
|
+
error: error instanceof Error ? error.message : String(error),
|
|
262
|
+
});
|
|
263
|
+
return undefined;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
async getPreparedCSSByHash(cssHash, projectScope) {
|
|
267
|
+
const cached = await getCSSByHashAsync(cssHash);
|
|
268
|
+
if (cached)
|
|
269
|
+
return cached;
|
|
270
|
+
return regenerateCSSByHash(cssHash, projectScope);
|
|
271
|
+
}
|
|
272
|
+
async registerPreparedCSSArtifact(ctx, styleProfileHash, contentContext, cssHash) {
|
|
273
|
+
const selector = this.resolveStyleArtifactSelector(contentContext, ctx);
|
|
274
|
+
if (!selector)
|
|
275
|
+
return;
|
|
276
|
+
const client = this.getVeryfrontApiClient(ctx);
|
|
277
|
+
if (!client)
|
|
278
|
+
return;
|
|
279
|
+
try {
|
|
280
|
+
await client.upsertStyleArtifact({
|
|
281
|
+
...selector,
|
|
282
|
+
styleProfileHash,
|
|
283
|
+
artifactHash: cssHash,
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
catch (error) {
|
|
287
|
+
logger.debug("Failed to register prepared CSS artifact", {
|
|
288
|
+
cssHash,
|
|
289
|
+
styleProfileHash,
|
|
290
|
+
error: error instanceof Error ? error.message : String(error),
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
}
|
|
127
294
|
}
|
package/package.json
CHANGED
|
@@ -219,6 +219,23 @@ function slugify(value: string): string {
|
|
|
219
219
|
return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "document";
|
|
220
220
|
}
|
|
221
221
|
|
|
222
|
+
/**
|
|
223
|
+
* Strip the chat-upload prefix that Studio prepends to uploaded files.
|
|
224
|
+
*
|
|
225
|
+
* Studio stores uploads with a generated prefix like:
|
|
226
|
+
* chat-<uuid>-<timestamp>-<shortid>-<original-filename>
|
|
227
|
+
*
|
|
228
|
+
* This function removes the prefix so knowledge files use the clean
|
|
229
|
+
* original filename (e.g. "agents" instead of
|
|
230
|
+
* "chat-909d3dbc-5a9a-4156-97e4-bcceb5c2ec0d-1773942180291-fv1qg5-agents").
|
|
231
|
+
*/
|
|
232
|
+
const CHAT_UPLOAD_PREFIX_RE =
|
|
233
|
+
/^chat-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}-\d+-[a-z0-9]+-/i;
|
|
234
|
+
|
|
235
|
+
export function stripChatUploadPrefix(name: string): string {
|
|
236
|
+
return name.replace(CHAT_UPLOAD_PREFIX_RE, "");
|
|
237
|
+
}
|
|
238
|
+
|
|
222
239
|
function defaultOutputRoot(): Promise<string> {
|
|
223
240
|
return dntShim.Deno.makeTempDir({ prefix: "veryfront-knowledge-" });
|
|
224
241
|
}
|
|
@@ -361,7 +378,7 @@ function buildSourceReference(source: KnowledgeSource): string {
|
|
|
361
378
|
: source.localPath;
|
|
362
379
|
}
|
|
363
380
|
|
|
364
|
-
function buildSuggestedSlug(source: KnowledgeSource, index: number): string {
|
|
381
|
+
export function buildSuggestedSlug(source: KnowledgeSource, index: number): string {
|
|
365
382
|
const normalized = normalize(
|
|
366
383
|
source.kind === "upload" ? source.uploadPath : source.localPath,
|
|
367
384
|
).replace(/\\/g, "/");
|
|
@@ -383,9 +400,20 @@ function buildSuggestedSlug(source: KnowledgeSource, index: number): string {
|
|
|
383
400
|
stripped = normalized.replace(/\.[^.]+$/, "");
|
|
384
401
|
}
|
|
385
402
|
|
|
403
|
+
// Strip Studio's chat-upload prefix from the filename portion so that
|
|
404
|
+
// knowledge files use the original clean filename.
|
|
405
|
+
const lastSlash = stripped.lastIndexOf("/");
|
|
406
|
+
if (lastSlash >= 0) {
|
|
407
|
+
const dir = stripped.slice(0, lastSlash + 1);
|
|
408
|
+
const file = stripChatUploadPrefix(stripped.slice(lastSlash + 1));
|
|
409
|
+
stripped = file ? `${dir}${file}` : stripped;
|
|
410
|
+
} else {
|
|
411
|
+
stripped = stripChatUploadPrefix(stripped) || stripped;
|
|
412
|
+
}
|
|
413
|
+
|
|
386
414
|
return slugify(stripped || basename(normalized, extname(normalized)) || `document-${index + 1}`);
|
|
387
415
|
}
|
|
388
|
-
function ensureUniqueSlugs(sources: KnowledgeSource[]): string[] {
|
|
416
|
+
export function ensureUniqueSlugs(sources: KnowledgeSource[]): string[] {
|
|
389
417
|
const counts = new Map<string, number>();
|
|
390
418
|
return sources.map((source, index) => {
|
|
391
419
|
const baseSlug = buildSuggestedSlug(source, index);
|
package/src/deno.js
CHANGED
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
* @module html/styles-builder/candidate-extractor
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import type { StyleScopeProfile } from "./style-scope-profile.js";
|
|
10
|
+
import { shouldIncludeStylePath } from "./style-scope-profile.js";
|
|
11
|
+
|
|
9
12
|
/**
|
|
10
13
|
* Extract potential Tailwind class name candidates from source code content.
|
|
11
14
|
* Uses a comprehensive regex pattern matching Tailwind v4 utility patterns.
|
|
@@ -17,12 +20,22 @@ export function extractCandidates(content: string): string[] {
|
|
|
17
20
|
|
|
18
21
|
export function extractCandidatesFromFiles(
|
|
19
22
|
files: Array<{ path: string; content?: string }>,
|
|
23
|
+
options: {
|
|
24
|
+
projectDir?: string;
|
|
25
|
+
styleProfile?: StyleScopeProfile;
|
|
26
|
+
} = {},
|
|
20
27
|
): Set<string> {
|
|
21
28
|
const candidates = new Set<string>();
|
|
22
29
|
const sourceExtensions = [".tsx", ".jsx", ".ts", ".js", ".mdx"];
|
|
23
30
|
|
|
24
31
|
for (const file of files) {
|
|
25
32
|
if (!file.content) continue;
|
|
33
|
+
if (
|
|
34
|
+
options.styleProfile &&
|
|
35
|
+
!shouldIncludeStylePath(options.styleProfile, file.path, options.projectDir)
|
|
36
|
+
) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
26
39
|
if (!sourceExtensions.some((ext) => file.path.endsWith(ext))) continue;
|
|
27
40
|
|
|
28
41
|
for (const candidate of extractCandidates(file.content)) {
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ResolvedContentContext } from "../../platform/adapters/fs/veryfront/types.js";
|
|
2
|
+
|
|
3
|
+
interface ContentVersionFallback {
|
|
4
|
+
releaseId?: string | null;
|
|
5
|
+
branch?: string | null;
|
|
6
|
+
environmentName?: string | null;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function resolveStyleContentVersion(
|
|
10
|
+
contentContext: ResolvedContentContext | null,
|
|
11
|
+
fallback: ContentVersionFallback = {},
|
|
12
|
+
): string {
|
|
13
|
+
if (contentContext?.releaseId) return `release:${contentContext.releaseId}`;
|
|
14
|
+
if (contentContext?.branch) return `branch:${contentContext.branch}`;
|
|
15
|
+
if (contentContext?.environmentName) return `environment:${contentContext.environmentName}`;
|
|
16
|
+
if (fallback.releaseId) return `release:${fallback.releaseId}`;
|
|
17
|
+
if (fallback.branch) return `branch:${fallback.branch}`;
|
|
18
|
+
if (fallback.environmentName) return `environment:${fallback.environmentName}`;
|
|
19
|
+
return "live";
|
|
20
|
+
}
|
|
@@ -8,20 +8,35 @@
|
|
|
8
8
|
|
|
9
9
|
import { serverLogger } from "../../utils/index.js";
|
|
10
10
|
import { extractCandidatesFromFiles, getProjectCSS } from "./tailwind-compiler.js";
|
|
11
|
+
import {
|
|
12
|
+
createPreparedProjectCSSContext,
|
|
13
|
+
storePreparedProjectCSS,
|
|
14
|
+
} from "./prepared-project-css-cache.js";
|
|
15
|
+
import type { StyleScopeProfile } from "./style-scope-profile.js";
|
|
11
16
|
|
|
12
17
|
const logger = serverLogger.component("css-pregeneration");
|
|
13
18
|
|
|
14
19
|
interface CSSPregenerationOptions {
|
|
15
20
|
/** Project slug for cache keying */
|
|
16
21
|
projectSlug: string;
|
|
22
|
+
/** Current content version for the prepared stylesheet artifact */
|
|
23
|
+
projectVersion: string;
|
|
24
|
+
/** Project root used for style scope filtering */
|
|
25
|
+
projectDir?: string;
|
|
17
26
|
/** List of files with content to extract candidates from */
|
|
18
27
|
files: Array<{ path: string; content?: string }>;
|
|
28
|
+
/** Style scope profile for convention-based filtering */
|
|
29
|
+
styleProfile: StyleScopeProfile;
|
|
19
30
|
/** Optional custom stylesheet (globals.css content) */
|
|
20
31
|
stylesheet?: string;
|
|
21
32
|
/** Optional stylesheet path (from config) to locate content in files */
|
|
22
33
|
stylesheetPath?: string;
|
|
23
34
|
/** Enable minification (default: true) */
|
|
24
35
|
minify?: boolean;
|
|
36
|
+
/** Environment segment used for prepared artifact cache partitioning */
|
|
37
|
+
environment?: string;
|
|
38
|
+
/** Build mode recorded in the prepared artifact profile */
|
|
39
|
+
buildMode?: "development" | "production";
|
|
25
40
|
}
|
|
26
41
|
|
|
27
42
|
/**
|
|
@@ -39,34 +54,56 @@ interface CSSPregenerationOptions {
|
|
|
39
54
|
export async function pregenerateCSSFromFiles(
|
|
40
55
|
options: CSSPregenerationOptions,
|
|
41
56
|
): Promise<void> {
|
|
42
|
-
const {
|
|
57
|
+
const {
|
|
58
|
+
projectSlug,
|
|
59
|
+
projectVersion,
|
|
60
|
+
projectDir,
|
|
61
|
+
files,
|
|
62
|
+
styleProfile,
|
|
63
|
+
stylesheet,
|
|
64
|
+
stylesheetPath,
|
|
65
|
+
minify = true,
|
|
66
|
+
environment = "preview",
|
|
67
|
+
buildMode = "production",
|
|
68
|
+
} = options;
|
|
43
69
|
const startTime = performance.now();
|
|
44
70
|
|
|
45
71
|
try {
|
|
46
|
-
const candidates = extractCandidatesFromFiles(files);
|
|
47
|
-
|
|
48
|
-
if (candidates.size === 0) {
|
|
49
|
-
logger.debug("No candidates found, skipping", {
|
|
50
|
-
projectSlug,
|
|
51
|
-
fileCount: files.length,
|
|
52
|
-
});
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
72
|
const resolvedStylesheet = stylesheet ?? findStylesheetFromFiles(files, stylesheetPath);
|
|
73
|
+
const candidates = extractCandidatesFromFiles(files, {
|
|
74
|
+
projectDir,
|
|
75
|
+
styleProfile,
|
|
76
|
+
});
|
|
57
77
|
|
|
58
78
|
logger.debug("Starting", {
|
|
59
79
|
projectSlug,
|
|
80
|
+
projectVersion,
|
|
60
81
|
fileCount: files.length,
|
|
61
82
|
candidateCount: candidates.size,
|
|
62
83
|
hasStylesheet: Boolean(resolvedStylesheet),
|
|
84
|
+
styleProfileHash: styleProfile.hash,
|
|
63
85
|
});
|
|
64
86
|
|
|
65
|
-
const result = await getProjectCSS(projectSlug, resolvedStylesheet, candidates, {
|
|
87
|
+
const result = await getProjectCSS(projectSlug, resolvedStylesheet, candidates, {
|
|
88
|
+
minify,
|
|
89
|
+
environment,
|
|
90
|
+
buildMode,
|
|
91
|
+
});
|
|
92
|
+
await storePreparedProjectCSS(
|
|
93
|
+
createPreparedProjectCSSContext(
|
|
94
|
+
projectSlug,
|
|
95
|
+
projectVersion,
|
|
96
|
+
resolvedStylesheet,
|
|
97
|
+
styleProfile.hash,
|
|
98
|
+
{ minify, environment, buildMode },
|
|
99
|
+
),
|
|
100
|
+
{ css: result.css, hash: result.hash },
|
|
101
|
+
);
|
|
66
102
|
const duration = performance.now() - startTime;
|
|
67
103
|
|
|
68
104
|
logger.debug("Complete", {
|
|
69
105
|
projectSlug,
|
|
106
|
+
projectVersion,
|
|
70
107
|
candidateCount: candidates.size,
|
|
71
108
|
cssLength: result.css.length,
|
|
72
109
|
cssHash: result.hash,
|