veryfront 0.1.92 → 0.1.94
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/styles/command-help.d.ts +3 -0
- package/esm/cli/commands/styles/command-help.d.ts.map +1 -0
- package/esm/cli/commands/styles/command-help.js +19 -0
- package/esm/cli/commands/styles/command.d.ts +3 -0
- package/esm/cli/commands/styles/command.d.ts.map +1 -0
- package/esm/cli/commands/styles/command.js +198 -0
- package/esm/cli/commands/styles/handler.d.ts +24 -0
- package/esm/cli/commands/styles/handler.d.ts.map +1 -0
- package/esm/cli/commands/styles/handler.js +17 -0
- package/esm/cli/help/command-definitions.d.ts.map +1 -1
- package/esm/cli/help/command-definitions.js +2 -0
- package/esm/cli/router.d.ts.map +1 -1
- package/esm/cli/router.js +2 -0
- package/esm/deno.d.ts +1 -0
- package/esm/deno.js +2 -1
- package/esm/src/html/styles-builder/css-pregeneration.d.ts +9 -0
- package/esm/src/html/styles-builder/css-pregeneration.d.ts.map +1 -1
- package/esm/src/html/styles-builder/css-pregeneration.js +26 -15
- package/esm/src/jobs/schemas.d.ts +40 -40
- package/esm/src/platform/adapters/fs/veryfront/adapter.d.ts.map +1 -1
- package/esm/src/platform/adapters/fs/veryfront/adapter.js +15 -3
- package/esm/src/platform/adapters/fs/veryfront/types.d.ts +2 -0
- package/esm/src/platform/adapters/fs/veryfront/types.d.ts.map +1 -1
- package/esm/src/platform/adapters/fs/veryfront/websocket-manager.d.ts +5 -1
- package/esm/src/platform/adapters/fs/veryfront/websocket-manager.d.ts.map +1 -1
- package/esm/src/platform/adapters/fs/veryfront/websocket-manager.js +10 -2
- package/esm/src/platform/adapters/veryfront-api-client/client.d.ts +2 -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 +3 -0
- package/esm/src/platform/adapters/veryfront-api-client/index.d.ts +1 -1
- package/esm/src/platform/adapters/veryfront-api-client/index.d.ts.map +1 -1
- package/esm/src/platform/adapters/veryfront-api-client/operations.d.ts +11 -2
- package/esm/src/platform/adapters/veryfront-api-client/operations.d.ts.map +1 -1
- package/esm/src/platform/adapters/veryfront-api-client/operations.js +30 -0
- package/esm/src/platform/adapters/veryfront-api-client/schemas/api.schema.d.ts +14 -3
- 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 +8 -1
- package/esm/src/rendering/styles.d.ts +9 -0
- package/esm/src/rendering/styles.d.ts.map +1 -0
- package/esm/src/rendering/styles.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/sandbox.d.ts +14 -3
- package/esm/src/sandbox/sandbox.d.ts.map +1 -1
- package/esm/src/sandbox/sandbox.js +20 -6
- package/esm/src/security/http/base-handler.d.ts.map +1 -1
- package/esm/src/security/http/base-handler.js +5 -2
- package/esm/src/server/context/request-context.d.ts.map +1 -1
- package/esm/src/server/context/request-context.js +4 -2
- package/esm/src/server/handlers/dev/scripts/hmr-scripts.d.ts.map +1 -1
- package/esm/src/server/handlers/dev/scripts/hmr-scripts.js +91 -4
- package/esm/src/server/handlers/dev/styles-css.handler.d.ts +2 -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 +23 -0
- package/esm/src/server/handlers/preview/hmr-message-router.d.ts +2 -1
- package/esm/src/server/handlers/preview/hmr-message-router.d.ts.map +1 -1
- package/esm/src/server/handlers/preview/hmr-message-router.js +15 -5
- package/esm/src/server/handlers/preview/hmr.handler.js +1 -1
- package/esm/src/server/handlers/preview/markdown-preview.handler.d.ts.map +1 -1
- package/esm/src/server/handlers/preview/markdown-preview.handler.js +4 -2
- package/esm/src/server/handlers/request/css.handler.d.ts.map +1 -1
- package/esm/src/server/handlers/request/css.handler.js +4 -2
- package/esm/src/server/handlers/request/ssr/ssr.handler.d.ts.map +1 -1
- package/esm/src/server/handlers/request/ssr/ssr.handler.js +4 -2
- package/esm/src/server/reload-notifier.d.ts +3 -1
- package/esm/src/server/reload-notifier.d.ts.map +1 -1
- package/esm/src/utils/version.d.ts +1 -1
- package/esm/src/utils/version.js +1 -1
- package/package.json +1 -1
- package/src/cli/commands/styles/command-help.ts +21 -0
- package/src/cli/commands/styles/command.ts +296 -0
- package/src/cli/commands/styles/handler.ts +23 -0
- package/src/cli/help/command-definitions.ts +2 -0
- package/src/cli/router.ts +2 -0
- package/src/deno.js +2 -1
- package/src/src/html/styles-builder/css-pregeneration.ts +57 -29
- package/src/src/platform/adapters/fs/veryfront/adapter.ts +18 -4
- package/src/src/platform/adapters/fs/veryfront/types.ts +2 -0
- package/src/src/platform/adapters/fs/veryfront/websocket-manager.ts +18 -3
- package/src/src/platform/adapters/veryfront-api-client/client.ts +8 -0
- package/src/src/platform/adapters/veryfront-api-client/index.ts +1 -0
- package/src/src/platform/adapters/veryfront-api-client/operations.ts +48 -2
- package/src/src/platform/adapters/veryfront-api-client/schemas/api.schema.ts +9 -1
- package/src/src/rendering/styles.ts +15 -0
- package/src/src/sandbox/index.ts +1 -0
- package/src/src/sandbox/sandbox.ts +33 -6
- package/src/src/security/http/base-handler.ts +5 -2
- package/src/src/server/context/request-context.ts +4 -2
- package/src/src/server/handlers/dev/scripts/hmr-scripts.ts +93 -4
- package/src/src/server/handlers/dev/styles-css.handler.ts +31 -0
- package/src/src/server/handlers/preview/hmr-message-router.ts +15 -5
- package/src/src/server/handlers/preview/hmr.handler.ts +1 -1
- package/src/src/server/handlers/preview/markdown-preview.handler.ts +4 -2
- package/src/src/server/handlers/request/css.handler.ts +4 -2
- package/src/src/server/handlers/request/ssr/ssr.handler.ts +4 -2
- package/src/src/server/reload-notifier.ts +3 -1
- package/src/src/utils/version.ts +1 -1
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
import * as dntShim from "../../../_dnt.shims.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { getConfig } from "../../../src/config/index.js";
|
|
4
|
+
import {
|
|
5
|
+
enhanceAdapterWithFS,
|
|
6
|
+
getEnv,
|
|
7
|
+
isExtendedFSAdapter,
|
|
8
|
+
runtime,
|
|
9
|
+
type VeryfrontApiClient,
|
|
10
|
+
} from "../../../src/platform/index.js";
|
|
11
|
+
import {
|
|
12
|
+
buildPreparedCSSArtifactFromFiles,
|
|
13
|
+
createStyleScopeProfile,
|
|
14
|
+
resolveStyleContentVersion,
|
|
15
|
+
} from "../../../src/rendering/styles.js";
|
|
16
|
+
import { cliLogger, exitProcess } from "../../utils/index.js";
|
|
17
|
+
import type { StylesArgs } from "./handler.js";
|
|
18
|
+
import { writeJobResultIfConfigured } from "../../utils/write-job-result.js";
|
|
19
|
+
|
|
20
|
+
const StyleArtifactBuildConfigSchema = z.object({
|
|
21
|
+
style_profile_hash: z.string().min(1).optional(),
|
|
22
|
+
branch: z.string().min(1).optional(),
|
|
23
|
+
environment_name: z.string().min(1).optional(),
|
|
24
|
+
release_id: z.string().min(1).optional(),
|
|
25
|
+
}).superRefine((value, ctx) => {
|
|
26
|
+
const selectorCount = [value.branch, value.environment_name, value.release_id].filter(Boolean)
|
|
27
|
+
.length;
|
|
28
|
+
if (selectorCount !== 1) {
|
|
29
|
+
ctx.addIssue({
|
|
30
|
+
code: z.ZodIssueCode.custom,
|
|
31
|
+
message: "Exactly one of branch, environment_name, or release_id is required.",
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
type StyleArtifactBuildConfig = z.infer<typeof StyleArtifactBuildConfigSchema>;
|
|
37
|
+
|
|
38
|
+
interface StyleBuildContentContext {
|
|
39
|
+
branch?: string;
|
|
40
|
+
environmentName?: string;
|
|
41
|
+
releaseId?: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface StyleArtifactBuildResult {
|
|
45
|
+
kind: "style_artifact";
|
|
46
|
+
status: "ready";
|
|
47
|
+
style_profile_hash: string;
|
|
48
|
+
artifact_hash: string;
|
|
49
|
+
asset_path: string;
|
|
50
|
+
content_type: string;
|
|
51
|
+
etag: string;
|
|
52
|
+
selector: {
|
|
53
|
+
type: "branch" | "environment" | "release";
|
|
54
|
+
value: string;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
type StyleArtifactSelector =
|
|
59
|
+
| { branch: string; environmentName?: never; releaseId?: never; type: "branch"; value: string }
|
|
60
|
+
| {
|
|
61
|
+
branch?: never;
|
|
62
|
+
environmentName: string;
|
|
63
|
+
releaseId?: never;
|
|
64
|
+
type: "environment";
|
|
65
|
+
value: string;
|
|
66
|
+
}
|
|
67
|
+
| { branch?: never; environmentName?: never; releaseId: string; type: "release"; value: string };
|
|
68
|
+
|
|
69
|
+
function parseStyleArtifactBuildConfig(rawConfig: string | undefined): StyleArtifactBuildConfig {
|
|
70
|
+
if (!rawConfig) {
|
|
71
|
+
throw new Error("Missing --config JSON");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
let parsed: unknown;
|
|
75
|
+
try {
|
|
76
|
+
parsed = JSON.parse(rawConfig);
|
|
77
|
+
} catch {
|
|
78
|
+
throw new Error("Invalid --config JSON");
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return StyleArtifactBuildConfigSchema.parse(parsed);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function resolveStyleArtifactSelector(
|
|
85
|
+
config: StyleArtifactBuildConfig,
|
|
86
|
+
): StyleArtifactSelector {
|
|
87
|
+
if (config.branch) {
|
|
88
|
+
return {
|
|
89
|
+
branch: config.branch,
|
|
90
|
+
type: "branch",
|
|
91
|
+
value: config.branch,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (config.environment_name) {
|
|
96
|
+
return {
|
|
97
|
+
environmentName: config.environment_name,
|
|
98
|
+
type: "environment",
|
|
99
|
+
value: config.environment_name,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
releaseId: config.release_id!,
|
|
105
|
+
type: "release",
|
|
106
|
+
value: config.release_id!,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function resolveContentContextSelector(
|
|
111
|
+
selector: StyleArtifactSelector,
|
|
112
|
+
): { type: "branch"; branch: string } | { type: "environment"; name: string } | {
|
|
113
|
+
type: "release";
|
|
114
|
+
releaseId: string;
|
|
115
|
+
} {
|
|
116
|
+
if (selector.type === "branch") {
|
|
117
|
+
return { type: "branch", branch: selector.branch };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (selector.type === "environment") {
|
|
121
|
+
return { type: "environment", name: selector.environmentName };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return { type: "release", releaseId: selector.releaseId };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function requireEnv(name: string): string {
|
|
128
|
+
const value = getEnv(name)?.trim();
|
|
129
|
+
if (!value) {
|
|
130
|
+
throw new Error(`${name} is required`);
|
|
131
|
+
}
|
|
132
|
+
return value;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function getUnderlyingVeryfrontClient(adapter: Awaited<ReturnType<typeof enhanceAdapterWithFS>>): {
|
|
136
|
+
client: VeryfrontApiClient;
|
|
137
|
+
contentContext: StyleBuildContentContext | null;
|
|
138
|
+
getAllSourceFiles: () => Promise<Array<{ path: string; content?: string }>>;
|
|
139
|
+
} {
|
|
140
|
+
if (!isExtendedFSAdapter(adapter.fs)) {
|
|
141
|
+
throw new Error("Styles build requires a Veryfront FS adapter");
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const fsAdapter = adapter.fs.getUnderlyingAdapter() as {
|
|
145
|
+
getAllSourceFiles?: () => Promise<Array<{ path: string; content?: string }>>;
|
|
146
|
+
getContentContext?: () => StyleBuildContentContext | null;
|
|
147
|
+
getClient?: () => VeryfrontApiClient;
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
if (
|
|
151
|
+
typeof fsAdapter.getAllSourceFiles !== "function" ||
|
|
152
|
+
typeof fsAdapter.getClient !== "function"
|
|
153
|
+
) {
|
|
154
|
+
throw new Error("Styles build requires a Veryfront source adapter");
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
client: fsAdapter.getClient(),
|
|
159
|
+
contentContext: typeof fsAdapter.getContentContext === "function"
|
|
160
|
+
? fsAdapter.getContentContext()
|
|
161
|
+
: null,
|
|
162
|
+
getAllSourceFiles: fsAdapter.getAllSourceFiles,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async function markStyleArtifactFailed(
|
|
167
|
+
client: VeryfrontApiClient | null,
|
|
168
|
+
selector: StyleArtifactSelector,
|
|
169
|
+
styleProfileHash: string | null | undefined,
|
|
170
|
+
error: string,
|
|
171
|
+
): Promise<void> {
|
|
172
|
+
if (!client || !styleProfileHash) return;
|
|
173
|
+
|
|
174
|
+
try {
|
|
175
|
+
await client.upsertStyleArtifact({
|
|
176
|
+
...(selector.type === "branch" ? { branch: selector.branch } : {}),
|
|
177
|
+
...(selector.type === "environment" ? { environmentName: selector.environmentName } : {}),
|
|
178
|
+
...(selector.type === "release" ? { releaseId: selector.releaseId } : {}),
|
|
179
|
+
styleProfileHash,
|
|
180
|
+
status: "failed",
|
|
181
|
+
failureReason: error,
|
|
182
|
+
});
|
|
183
|
+
} catch (updateError) {
|
|
184
|
+
cliLogger.warn("Failed to mark style artifact as failed", updateError);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export async function stylesCommand(options: StylesArgs): Promise<void> {
|
|
189
|
+
const buildConfig = parseStyleArtifactBuildConfig(options.config);
|
|
190
|
+
const selector = resolveStyleArtifactSelector(buildConfig);
|
|
191
|
+
const projectSlug = requireEnv("VERYFRONT_PROJECT_SLUG");
|
|
192
|
+
const apiToken = requireEnv("VERYFRONT_API_TOKEN");
|
|
193
|
+
const apiBaseUrl = requireEnv("VERYFRONT_API_BASE_URL");
|
|
194
|
+
const projectId = getEnv("VERYFRONT_PROJECT_ID")?.trim();
|
|
195
|
+
const projectDir = dntShim.Deno.cwd();
|
|
196
|
+
|
|
197
|
+
const baseAdapter = await runtime.get();
|
|
198
|
+
const adapter = await enhanceAdapterWithFS(
|
|
199
|
+
baseAdapter,
|
|
200
|
+
{
|
|
201
|
+
fs: {
|
|
202
|
+
type: "veryfront-api",
|
|
203
|
+
projectDir,
|
|
204
|
+
veryfront: {
|
|
205
|
+
apiBaseUrl,
|
|
206
|
+
apiToken,
|
|
207
|
+
projectSlug,
|
|
208
|
+
projectId,
|
|
209
|
+
contentSource: resolveContentContextSelector(selector),
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
projectDir,
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
let client: VeryfrontApiClient | null = null;
|
|
217
|
+
let resolvedStyleProfileHash: string | null = buildConfig.style_profile_hash ?? null;
|
|
218
|
+
|
|
219
|
+
try {
|
|
220
|
+
const sourceAdapter = getUnderlyingVeryfrontClient(adapter);
|
|
221
|
+
client = sourceAdapter.client;
|
|
222
|
+
const cacheKey = projectId || projectSlug;
|
|
223
|
+
const config = await getConfig(projectDir, adapter, { cacheKey });
|
|
224
|
+
const styleProfile = createStyleScopeProfile(config);
|
|
225
|
+
resolvedStyleProfileHash = styleProfile.hash;
|
|
226
|
+
|
|
227
|
+
if (buildConfig.style_profile_hash && styleProfile.hash !== buildConfig.style_profile_hash) {
|
|
228
|
+
const message =
|
|
229
|
+
`Style profile hash mismatch: expected ${buildConfig.style_profile_hash}, got ${styleProfile.hash}`;
|
|
230
|
+
await markStyleArtifactFailed(client, selector, buildConfig.style_profile_hash, message);
|
|
231
|
+
throw new Error(message);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const files = await sourceAdapter.getAllSourceFiles();
|
|
235
|
+
if (files.length === 0) {
|
|
236
|
+
throw new Error("No project files were available to build the style artifact");
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const projectVersion = resolveStyleContentVersion(null, {
|
|
240
|
+
branch: sourceAdapter.contentContext?.branch ?? null,
|
|
241
|
+
releaseId: sourceAdapter.contentContext?.releaseId ?? null,
|
|
242
|
+
environmentName: sourceAdapter.contentContext?.environmentName ?? null,
|
|
243
|
+
});
|
|
244
|
+
const build = await buildPreparedCSSArtifactFromFiles({
|
|
245
|
+
projectSlug,
|
|
246
|
+
projectVersion,
|
|
247
|
+
projectDir,
|
|
248
|
+
files,
|
|
249
|
+
styleProfile,
|
|
250
|
+
stylesheetPath: config?.tailwind?.stylesheet,
|
|
251
|
+
minify: true,
|
|
252
|
+
environment: "preview",
|
|
253
|
+
buildMode: "production",
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
const resolution = await client.upsertStyleArtifact({
|
|
257
|
+
...(selector.type === "branch" ? { branch: selector.branch } : {}),
|
|
258
|
+
...(selector.type === "environment" ? { environmentName: selector.environmentName } : {}),
|
|
259
|
+
...(selector.type === "release" ? { releaseId: selector.releaseId } : {}),
|
|
260
|
+
styleProfileHash: styleProfile.hash,
|
|
261
|
+
status: "ready",
|
|
262
|
+
artifactHash: build.hash,
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
const result: StyleArtifactBuildResult = {
|
|
266
|
+
kind: "style_artifact",
|
|
267
|
+
status: "ready",
|
|
268
|
+
style_profile_hash: styleProfile.hash,
|
|
269
|
+
artifact_hash: build.hash,
|
|
270
|
+
asset_path: resolution.assetPath ?? `/_vf/css/${build.hash}.css`,
|
|
271
|
+
content_type: resolution.contentType ?? "text/css; charset=utf-8",
|
|
272
|
+
etag: resolution.etag ?? `"${build.hash}"`,
|
|
273
|
+
selector: {
|
|
274
|
+
type: selector.type,
|
|
275
|
+
value: selector.value,
|
|
276
|
+
},
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
await writeJobResultIfConfigured(result);
|
|
280
|
+
cliLogger.info(`Built style artifact ${build.hash}`);
|
|
281
|
+
} catch (error) {
|
|
282
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
283
|
+
await markStyleArtifactFailed(
|
|
284
|
+
client,
|
|
285
|
+
selector,
|
|
286
|
+
resolvedStyleProfileHash ?? buildConfig.style_profile_hash,
|
|
287
|
+
message,
|
|
288
|
+
);
|
|
289
|
+
cliLogger.error(message);
|
|
290
|
+
exitProcess(1);
|
|
291
|
+
} finally {
|
|
292
|
+
if (isExtendedFSAdapter(adapter.fs)) {
|
|
293
|
+
await adapter.fs.shutdown();
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { createArgParser, parseArgsOrThrow } from "../../shared/args.js";
|
|
3
|
+
import type { ParsedArgs } from "../../shared/types.js";
|
|
4
|
+
|
|
5
|
+
const StylesArgsSchema = z.object({
|
|
6
|
+
subcommand: z.literal("build-artifact"),
|
|
7
|
+
config: z.string().optional(),
|
|
8
|
+
debug: z.boolean().default(false),
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
export type StylesArgs = z.infer<typeof StylesArgsSchema>;
|
|
12
|
+
|
|
13
|
+
export const parseStylesArgs = createArgParser(StylesArgsSchema, {
|
|
14
|
+
subcommand: { keys: ["subcommand"], type: "string", positional: 0 },
|
|
15
|
+
config: { keys: ["config"], type: "string" },
|
|
16
|
+
debug: { keys: ["debug"], type: "boolean" },
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
export async function handleStylesCommand(args: ParsedArgs): Promise<void> {
|
|
20
|
+
const opts = parseArgsOrThrow(parseStylesArgs, "styles", args);
|
|
21
|
+
const { stylesCommand } = await import("./command.js");
|
|
22
|
+
await stylesCommand(opts);
|
|
23
|
+
}
|
|
@@ -16,6 +16,7 @@ import { doctorHelp } from "../commands/doctor/command-help.js";
|
|
|
16
16
|
import { cleanHelp } from "../commands/clean/command-help.js";
|
|
17
17
|
import { routesHelp } from "../commands/routes/command-help.js";
|
|
18
18
|
import { studioHelp } from "../commands/studio/command-help.js";
|
|
19
|
+
import { stylesHelp } from "../commands/styles/command-help.js";
|
|
19
20
|
import { lockHelp } from "../commands/lock/command-help.js";
|
|
20
21
|
import { analyzeChunksHelp } from "../commands/analyze-chunks/command-help.js";
|
|
21
22
|
import { generateHelp } from "../commands/generate/command-help.js";
|
|
@@ -51,6 +52,7 @@ export const COMMANDS: CommandRegistry = {
|
|
|
51
52
|
clean: cleanHelp,
|
|
52
53
|
routes: routesHelp,
|
|
53
54
|
studio: studioHelp,
|
|
55
|
+
styles: stylesHelp,
|
|
54
56
|
lock: lockHelp,
|
|
55
57
|
"analyze-chunks": analyzeChunksHelp,
|
|
56
58
|
generate: generateHelp,
|
package/src/cli/router.ts
CHANGED
|
@@ -29,6 +29,7 @@ import { handleRoutesCommand } from "./commands/routes/handler.js";
|
|
|
29
29
|
import { handleServeCommand } from "./commands/serve/handler.js";
|
|
30
30
|
import { handleStartCommand } from "./commands/start/handler.js";
|
|
31
31
|
import { handleStudioCommand } from "./commands/studio/handler.js";
|
|
32
|
+
import { handleStylesCommand } from "./commands/styles/handler.js";
|
|
32
33
|
import { handleUpCommand } from "./commands/up/index.js";
|
|
33
34
|
import { handleTaskCommand } from "./commands/task/handler.js";
|
|
34
35
|
import { handleWorkflowCommand } from "./commands/workflow/handler.js";
|
|
@@ -55,6 +56,7 @@ const commands: Record<string, (args: ParsedArgs) => Promise<void>> = {
|
|
|
55
56
|
"analyze-chunks": handleAnalyzeChunksCommand,
|
|
56
57
|
"routes": handleRoutesCommand,
|
|
57
58
|
"studio": handleStudioCommand,
|
|
59
|
+
"styles": handleStylesCommand,
|
|
58
60
|
"lock": handleLockCommand,
|
|
59
61
|
"generate": handleGenerateCommand,
|
|
60
62
|
"g": handleGenerateCommand,
|
package/src/deno.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
"name": "veryfront",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.94",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"nodeModulesDir": "auto",
|
|
6
6
|
"exclude": [
|
|
@@ -97,6 +97,7 @@ export default {
|
|
|
97
97
|
"veryfront/config": "./src/config/index.ts",
|
|
98
98
|
"veryfront/build": "./src/build/index.ts",
|
|
99
99
|
"veryfront/rendering": "./src/rendering/index.ts",
|
|
100
|
+
"veryfront/rendering/styles": "./src/rendering/styles.ts",
|
|
100
101
|
"veryfront/cache": "./src/cache/index.ts",
|
|
101
102
|
"veryfront/security": "./src/security/index.ts",
|
|
102
103
|
"veryfront/errors": "./src/errors/index.ts",
|
|
@@ -39,6 +39,60 @@ interface CSSPregenerationOptions {
|
|
|
39
39
|
buildMode?: "development" | "production";
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
export interface PreparedCSSArtifactBuildResult {
|
|
43
|
+
css: string;
|
|
44
|
+
hash: string;
|
|
45
|
+
candidateCount: number;
|
|
46
|
+
fromCache: boolean;
|
|
47
|
+
context: ReturnType<typeof createPreparedProjectCSSContext>;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export async function buildPreparedCSSArtifactFromFiles(
|
|
51
|
+
options: CSSPregenerationOptions,
|
|
52
|
+
): Promise<PreparedCSSArtifactBuildResult> {
|
|
53
|
+
const {
|
|
54
|
+
projectSlug,
|
|
55
|
+
projectVersion,
|
|
56
|
+
projectDir,
|
|
57
|
+
files,
|
|
58
|
+
styleProfile,
|
|
59
|
+
stylesheet,
|
|
60
|
+
stylesheetPath,
|
|
61
|
+
minify = true,
|
|
62
|
+
environment = "preview",
|
|
63
|
+
buildMode = "production",
|
|
64
|
+
} = options;
|
|
65
|
+
|
|
66
|
+
const resolvedStylesheet = stylesheet ?? findStylesheetFromFiles(files, stylesheetPath);
|
|
67
|
+
const candidates = extractCandidatesFromFiles(files, {
|
|
68
|
+
projectDir,
|
|
69
|
+
styleProfile,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const result = await getProjectCSS(projectSlug, resolvedStylesheet, candidates, {
|
|
73
|
+
minify,
|
|
74
|
+
environment,
|
|
75
|
+
buildMode,
|
|
76
|
+
});
|
|
77
|
+
const context = createPreparedProjectCSSContext(
|
|
78
|
+
projectSlug,
|
|
79
|
+
projectVersion,
|
|
80
|
+
resolvedStylesheet,
|
|
81
|
+
styleProfile.hash,
|
|
82
|
+
{ minify, environment, buildMode },
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
await storePreparedProjectCSS(context, { css: result.css, hash: result.hash });
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
css: result.css,
|
|
89
|
+
hash: result.hash,
|
|
90
|
+
candidateCount: candidates.size,
|
|
91
|
+
fromCache: result.fromCache,
|
|
92
|
+
context,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
42
96
|
/**
|
|
43
97
|
* Pre-generate and cache CSS from file list.
|
|
44
98
|
*
|
|
@@ -57,54 +111,28 @@ export async function pregenerateCSSFromFiles(
|
|
|
57
111
|
const {
|
|
58
112
|
projectSlug,
|
|
59
113
|
projectVersion,
|
|
60
|
-
projectDir,
|
|
61
114
|
files,
|
|
62
115
|
styleProfile,
|
|
63
116
|
stylesheet,
|
|
64
|
-
stylesheetPath,
|
|
65
|
-
minify = true,
|
|
66
|
-
environment = "preview",
|
|
67
|
-
buildMode = "production",
|
|
68
117
|
} = options;
|
|
69
118
|
const startTime = performance.now();
|
|
70
119
|
|
|
71
120
|
try {
|
|
72
|
-
const resolvedStylesheet = stylesheet ?? findStylesheetFromFiles(files, stylesheetPath);
|
|
73
|
-
const candidates = extractCandidatesFromFiles(files, {
|
|
74
|
-
projectDir,
|
|
75
|
-
styleProfile,
|
|
76
|
-
});
|
|
77
|
-
|
|
78
121
|
logger.debug("Starting", {
|
|
79
122
|
projectSlug,
|
|
80
123
|
projectVersion,
|
|
81
124
|
fileCount: files.length,
|
|
82
|
-
|
|
83
|
-
hasStylesheet: Boolean(resolvedStylesheet),
|
|
125
|
+
hasStylesheet: Boolean(stylesheet),
|
|
84
126
|
styleProfileHash: styleProfile.hash,
|
|
85
127
|
});
|
|
86
128
|
|
|
87
|
-
const result = await
|
|
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
|
-
);
|
|
129
|
+
const result = await buildPreparedCSSArtifactFromFiles(options);
|
|
102
130
|
const duration = performance.now() - startTime;
|
|
103
131
|
|
|
104
132
|
logger.debug("Complete", {
|
|
105
133
|
projectSlug,
|
|
106
134
|
projectVersion,
|
|
107
|
-
candidateCount:
|
|
135
|
+
candidateCount: result.candidateCount,
|
|
108
136
|
cssLength: result.css.length,
|
|
109
137
|
cssHash: result.hash,
|
|
110
138
|
fromCache: result.fromCache,
|
|
@@ -724,9 +724,9 @@ export class VeryfrontFSAdapter implements FSAdapter {
|
|
|
724
724
|
*/
|
|
725
725
|
private async triggerCSSPregeneration(
|
|
726
726
|
files: Array<{ path: string; content?: string }>,
|
|
727
|
-
): Promise<
|
|
727
|
+
): Promise<{ hash: string; assetPath: string } | undefined> {
|
|
728
728
|
try {
|
|
729
|
-
const {
|
|
729
|
+
const { buildPreparedCSSArtifactFromFiles, findStylesheetFromFiles } = await import(
|
|
730
730
|
"../../../../html/styles-builder/css-pregeneration.js"
|
|
731
731
|
);
|
|
732
732
|
const { resolveStyleContentVersion } = await import(
|
|
@@ -744,7 +744,7 @@ export class VeryfrontFSAdapter implements FSAdapter {
|
|
|
744
744
|
logger.debug("Skipping CSS pre-generation without projectDir", {
|
|
745
745
|
projectSlug: this.projectSlug,
|
|
746
746
|
});
|
|
747
|
-
return;
|
|
747
|
+
return undefined;
|
|
748
748
|
}
|
|
749
749
|
|
|
750
750
|
try {
|
|
@@ -769,7 +769,7 @@ export class VeryfrontFSAdapter implements FSAdapter {
|
|
|
769
769
|
environmentName: this.contentContext?.environmentName ?? null,
|
|
770
770
|
});
|
|
771
771
|
|
|
772
|
-
await
|
|
772
|
+
const result = await buildPreparedCSSArtifactFromFiles({
|
|
773
773
|
projectSlug: this.projectSlug,
|
|
774
774
|
projectVersion,
|
|
775
775
|
projectDir,
|
|
@@ -781,11 +781,25 @@ export class VeryfrontFSAdapter implements FSAdapter {
|
|
|
781
781
|
environment: "preview",
|
|
782
782
|
buildMode: "production",
|
|
783
783
|
});
|
|
784
|
+
|
|
785
|
+
logger.debug("CSS pre-generation complete", {
|
|
786
|
+
projectSlug: this.projectSlug,
|
|
787
|
+
projectVersion,
|
|
788
|
+
cssHash: result.hash,
|
|
789
|
+
candidateCount: result.candidateCount,
|
|
790
|
+
fromCache: result.fromCache,
|
|
791
|
+
});
|
|
792
|
+
|
|
793
|
+
return {
|
|
794
|
+
hash: result.hash,
|
|
795
|
+
assetPath: `/_vf/css/${result.hash}.css`,
|
|
796
|
+
};
|
|
784
797
|
} catch (error) {
|
|
785
798
|
logger.warn("CSS pre-generation failed", {
|
|
786
799
|
projectSlug: this.projectSlug,
|
|
787
800
|
error: error instanceof Error ? error.message : String(error),
|
|
788
801
|
});
|
|
802
|
+
return undefined;
|
|
789
803
|
}
|
|
790
804
|
}
|
|
791
805
|
}
|
|
@@ -120,6 +120,8 @@ export interface InvalidationProjectContext {
|
|
|
120
120
|
environment?: "preview" | "production";
|
|
121
121
|
branch?: string | null;
|
|
122
122
|
releaseId?: string | null;
|
|
123
|
+
styleArtifactHash?: string;
|
|
124
|
+
styleAssetPath?: string;
|
|
123
125
|
}
|
|
124
126
|
|
|
125
127
|
export interface InvalidationCallbacks {
|
|
@@ -26,6 +26,11 @@ const WS_RECONNECT_MAX_FAILURES = 10;
|
|
|
26
26
|
const WS_HEARTBEAT_INTERVAL_MS = 60000;
|
|
27
27
|
const WS_HEARTBEAT_TIMEOUT_MS = 300000;
|
|
28
28
|
|
|
29
|
+
interface PreviewStyleArtifactInfo {
|
|
30
|
+
hash: string;
|
|
31
|
+
assetPath: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
29
34
|
interface WebSocketDeps {
|
|
30
35
|
apiBaseUrl: string;
|
|
31
36
|
apiToken: string;
|
|
@@ -43,7 +48,9 @@ interface WebSocketDeps {
|
|
|
43
48
|
cacheKey: string,
|
|
44
49
|
files: Array<{ path: string; content?: string }>,
|
|
45
50
|
) => Promise<void>;
|
|
46
|
-
pregenerateStyles?: (
|
|
51
|
+
pregenerateStyles?: (
|
|
52
|
+
files: Array<{ path: string; content?: string }>,
|
|
53
|
+
) => Promise<PreviewStyleArtifactInfo | undefined>;
|
|
47
54
|
}
|
|
48
55
|
|
|
49
56
|
export class WebSocketManager {
|
|
@@ -557,6 +564,7 @@ export class WebSocketManager {
|
|
|
557
564
|
const contentContext = this.deps.getContentContext();
|
|
558
565
|
const previewInvalidationPrefixes = this.getPreviewInvalidationPrefixes(contentContext);
|
|
559
566
|
const previewInvalidationVersion = this.previewInvalidationVersion;
|
|
567
|
+
let preparedStyleArtifact: PreviewStyleArtifactInfo | undefined;
|
|
560
568
|
let succeeded = false;
|
|
561
569
|
|
|
562
570
|
try {
|
|
@@ -630,11 +638,12 @@ export class WebSocketManager {
|
|
|
630
638
|
const cacheKey = buildFileListCacheKey(contentContext);
|
|
631
639
|
await this.deps.setFileListCache(cacheKey, files);
|
|
632
640
|
this.deps.clearFileListIndex();
|
|
633
|
-
await this.deps.pregenerateStyles?.(files);
|
|
641
|
+
preparedStyleArtifact = await this.deps.pregenerateStyles?.(files);
|
|
634
642
|
|
|
635
643
|
logger.debug("Fresh files cached (memory + Redis)", {
|
|
636
644
|
cacheKey,
|
|
637
645
|
fileCount: files.length,
|
|
646
|
+
styleAssetPath: preparedStyleArtifact?.assetPath,
|
|
638
647
|
});
|
|
639
648
|
} catch (error) {
|
|
640
649
|
logger.warn("Failed to fetch files during selective invalidation", {
|
|
@@ -664,6 +673,8 @@ export class WebSocketManager {
|
|
|
664
673
|
environment,
|
|
665
674
|
branch: contentContext?.branch ?? null,
|
|
666
675
|
releaseId: contentContext?.releaseId ?? null,
|
|
676
|
+
styleArtifactHash: preparedStyleArtifact?.hash,
|
|
677
|
+
styleAssetPath: preparedStyleArtifact?.assetPath,
|
|
667
678
|
};
|
|
668
679
|
|
|
669
680
|
this.deps.invalidationCallbacks.triggerReload?.(changedPaths, projectContext);
|
|
@@ -691,6 +702,7 @@ export class WebSocketManager {
|
|
|
691
702
|
const contentContext = this.deps.getContentContext();
|
|
692
703
|
const previewInvalidationPrefixes = this.getPreviewInvalidationPrefixes(contentContext);
|
|
693
704
|
const previewInvalidationVersion = this.previewInvalidationVersion;
|
|
705
|
+
let preparedStyleArtifact: PreviewStyleArtifactInfo | undefined;
|
|
694
706
|
let succeeded = false;
|
|
695
707
|
|
|
696
708
|
try {
|
|
@@ -773,11 +785,12 @@ export class WebSocketManager {
|
|
|
773
785
|
const cacheKey = buildFileListCacheKey(contentContext);
|
|
774
786
|
await this.deps.setFileListCache(cacheKey, files);
|
|
775
787
|
this.deps.clearFileListIndex();
|
|
776
|
-
await this.deps.pregenerateStyles?.(files);
|
|
788
|
+
preparedStyleArtifact = await this.deps.pregenerateStyles?.(files);
|
|
777
789
|
|
|
778
790
|
logger.debug("FRESH FILES FETCHED", {
|
|
779
791
|
cacheKey,
|
|
780
792
|
fileCount: files.length,
|
|
793
|
+
styleAssetPath: preparedStyleArtifact?.assetPath,
|
|
781
794
|
});
|
|
782
795
|
} catch (error) {
|
|
783
796
|
logger.warn("Failed to fetch files during invalidation", { error });
|
|
@@ -801,6 +814,8 @@ export class WebSocketManager {
|
|
|
801
814
|
environment,
|
|
802
815
|
branch: contentContext?.branch ?? null,
|
|
803
816
|
releaseId: contentContext?.releaseId ?? null,
|
|
817
|
+
styleArtifactHash: preparedStyleArtifact?.hash,
|
|
818
|
+
styleAssetPath: preparedStyleArtifact?.assetPath,
|
|
804
819
|
};
|
|
805
820
|
|
|
806
821
|
this.deps.invalidationCallbacks.triggerReload?.(undefined, projectContext);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { logger as baseLogger } from "../../../utils/index.js";
|
|
2
2
|
import {
|
|
3
|
+
type EnsureStyleArtifactBuildInput,
|
|
3
4
|
type FileDetail,
|
|
4
5
|
type FileListResult,
|
|
5
6
|
type ListFilesOptions,
|
|
@@ -350,6 +351,13 @@ export class VeryfrontApiClient {
|
|
|
350
351
|
return this.operations.resolveStyleArtifact(projectRef, input);
|
|
351
352
|
}
|
|
352
353
|
|
|
354
|
+
ensureStyleArtifactBuild(
|
|
355
|
+
input: EnsureStyleArtifactBuildInput,
|
|
356
|
+
projectRef = this.getProjectSlug()!,
|
|
357
|
+
): Promise<ProjectStyleArtifactResolution> {
|
|
358
|
+
return this.operations.ensureStyleArtifactBuild(projectRef, input);
|
|
359
|
+
}
|
|
360
|
+
|
|
353
361
|
upsertStyleArtifact(
|
|
354
362
|
input: UpsertStyleArtifactInput,
|
|
355
363
|
projectRef = this.getProjectSlug()!,
|