@valbuild/cli 0.97.7 → 0.99.0
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/cli/dist/valbuild-cli-cli.cjs.dev.js +222 -222
- package/cli/dist/valbuild-cli-cli.cjs.prod.js +222 -222
- package/cli/dist/valbuild-cli-cli.esm.js +221 -219
- package/package.json +5 -4
- package/src/__fixtures__/basic/content/basic-gallery-remote-existing.val.ts +20 -0
- package/src/__fixtures__/basic/content/basic-gallery-remote.val.ts +19 -0
- package/src/__fixtures__/basic/public/val/images-remote/image.png +0 -0
- package/src/__fixtures__/basic/public/val/images-remote2/image.png +0 -0
- package/src/__fixtures__/basic/val.config.ts +3 -1
- package/src/__fixtures__/basic/val.modules.ts +2 -0
- package/src/cli.ts +11 -5
- package/src/connect.ts +2 -4
- package/src/debug/context.ts +2 -4
- package/src/listUnusedFiles.ts +2 -4
- package/src/login.ts +29 -94
- package/src/lsp.ts +19 -0
- package/src/runValidation.test.ts +101 -0
- package/src/runValidation.ts +200 -73
- package/src/utils/evalValConfigFile.ts +6 -100
- package/src/validate.ts +2 -4
package/src/runValidation.ts
CHANGED
|
@@ -181,45 +181,17 @@ export async function handleFileMetadata(
|
|
|
181
181
|
return { success: true, shouldApplyPatch: true };
|
|
182
182
|
}
|
|
183
183
|
|
|
184
|
-
|
|
184
|
+
// Shared upload core used by both the single-field (handleRemoteFileUpload)
|
|
185
|
+
// and gallery (handleRemoteGalleryFileUpload) handlers. The two differ only in
|
|
186
|
+
// how they derive the local file ref, metadata and serialized image/file
|
|
187
|
+
// schema; everything from auth through upload is identical.
|
|
188
|
+
async function uploadRemoteFileCore(
|
|
185
189
|
ctx: FixHandlerContext,
|
|
190
|
+
fileRef: string,
|
|
191
|
+
metadata: Record<string, unknown> | undefined,
|
|
192
|
+
schema: SerializedImageSchema | SerializedFileSchema,
|
|
186
193
|
): Promise<FixHandlerResult> {
|
|
187
|
-
|
|
188
|
-
return {
|
|
189
|
-
success: false,
|
|
190
|
-
errorMessage: `Remote file ${ctx.sourcePath} needs to be uploaded (use --fix to upload)`,
|
|
191
|
-
};
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
const [, modulePath] = Internal.splitModuleFilePathAndModulePath(
|
|
195
|
-
ctx.sourcePath,
|
|
196
|
-
);
|
|
197
|
-
|
|
198
|
-
if (!ctx.valModule.source || !ctx.valModule.schema) {
|
|
199
|
-
return {
|
|
200
|
-
success: false,
|
|
201
|
-
errorMessage: `Could not resolve source or schema for ${ctx.sourcePath}`,
|
|
202
|
-
};
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
const resolvedRemoteFileAtSourcePath = Internal.resolvePath(
|
|
206
|
-
modulePath,
|
|
207
|
-
ctx.valModule.source,
|
|
208
|
-
ctx.valModule.schema,
|
|
209
|
-
);
|
|
210
|
-
|
|
211
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
212
|
-
const fileRefProp = (resolvedRemoteFileAtSourcePath.source as any)?.[
|
|
213
|
-
FILE_REF_PROP
|
|
214
|
-
];
|
|
215
|
-
if (!fileRefProp) {
|
|
216
|
-
return {
|
|
217
|
-
success: false,
|
|
218
|
-
errorMessage: `Expected file to be defined at: ${ctx.sourcePath} but no file was found`,
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
const filePath = path.join(ctx.projectRoot, fileRefProp);
|
|
194
|
+
const filePath = path.join(ctx.projectRoot, fileRef);
|
|
223
195
|
if (!ctx.fs.fileExists(filePath)) {
|
|
224
196
|
return {
|
|
225
197
|
success: false,
|
|
@@ -259,26 +231,6 @@ export async function handleRemoteFileUpload(
|
|
|
259
231
|
};
|
|
260
232
|
}
|
|
261
233
|
|
|
262
|
-
if (!resolvedRemoteFileAtSourcePath.schema) {
|
|
263
|
-
return {
|
|
264
|
-
success: false,
|
|
265
|
-
errorMessage: `Cannot upload remote file: schema not found for ${ctx.sourcePath}`,
|
|
266
|
-
};
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
const actualRemoteFileSource = resolvedRemoteFileAtSourcePath.source;
|
|
270
|
-
const fileSourceMetadata = Internal.isFile(actualRemoteFileSource)
|
|
271
|
-
? actualRemoteFileSource.metadata
|
|
272
|
-
: undefined;
|
|
273
|
-
const resolveRemoteFileSchema = resolvedRemoteFileAtSourcePath.schema;
|
|
274
|
-
|
|
275
|
-
if (!resolveRemoteFileSchema) {
|
|
276
|
-
return {
|
|
277
|
-
success: false,
|
|
278
|
-
errorMessage: `Could not resolve schema for remote file: ${ctx.sourcePath}`,
|
|
279
|
-
};
|
|
280
|
-
}
|
|
281
|
-
|
|
282
234
|
const projectName = ctx.project;
|
|
283
235
|
let publicProjectId = ctx.publicProjectId;
|
|
284
236
|
let remoteFileBuckets = ctx.remoteFileBuckets;
|
|
@@ -317,16 +269,6 @@ export async function handleRemoteFileUpload(
|
|
|
317
269
|
};
|
|
318
270
|
}
|
|
319
271
|
|
|
320
|
-
if (
|
|
321
|
-
resolveRemoteFileSchema.type !== "image" &&
|
|
322
|
-
resolveRemoteFileSchema.type !== "file"
|
|
323
|
-
) {
|
|
324
|
-
return {
|
|
325
|
-
success: false,
|
|
326
|
-
errorMessage: `The schema is the remote is neither image nor file: ${ctx.sourcePath}`,
|
|
327
|
-
};
|
|
328
|
-
}
|
|
329
|
-
|
|
330
272
|
remoteFilesCounter += 1;
|
|
331
273
|
const bucket =
|
|
332
274
|
remoteFileBuckets[remoteFilesCounter % remoteFileBuckets.length];
|
|
@@ -361,10 +303,6 @@ export async function handleRemoteFileUpload(
|
|
|
361
303
|
const fileHash = Internal.remote.getFileHash(fileBuffer);
|
|
362
304
|
const coreVersion = Internal.VERSION.core || "unknown";
|
|
363
305
|
const fileExt = getFileExt(filePath);
|
|
364
|
-
const schema = resolveRemoteFileSchema as
|
|
365
|
-
| SerializedImageSchema
|
|
366
|
-
| SerializedFileSchema;
|
|
367
|
-
const metadata = fileSourceMetadata;
|
|
368
306
|
const ref = Internal.remote.createRemoteRef(ctx.remote.remoteHost, {
|
|
369
307
|
publicProjectId,
|
|
370
308
|
coreVersion,
|
|
@@ -399,7 +337,7 @@ export async function handleRemoteFileUpload(
|
|
|
399
337
|
|
|
400
338
|
ctx.remoteFiles[ctx.sourcePath] = {
|
|
401
339
|
ref,
|
|
402
|
-
metadata
|
|
340
|
+
metadata,
|
|
403
341
|
};
|
|
404
342
|
|
|
405
343
|
return {
|
|
@@ -415,6 +353,177 @@ export async function handleRemoteFileUpload(
|
|
|
415
353
|
};
|
|
416
354
|
}
|
|
417
355
|
|
|
356
|
+
export async function handleRemoteFileUpload(
|
|
357
|
+
ctx: FixHandlerContext,
|
|
358
|
+
): Promise<FixHandlerResult> {
|
|
359
|
+
if (!ctx.fix) {
|
|
360
|
+
return {
|
|
361
|
+
success: false,
|
|
362
|
+
// No sourcePath in the message: the reported location already points at it.
|
|
363
|
+
errorMessage: `Remote file needs to be uploaded (use --fix to upload)`,
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
const [, modulePath] = Internal.splitModuleFilePathAndModulePath(
|
|
368
|
+
ctx.sourcePath,
|
|
369
|
+
);
|
|
370
|
+
|
|
371
|
+
if (!ctx.valModule.source || !ctx.valModule.schema) {
|
|
372
|
+
return {
|
|
373
|
+
success: false,
|
|
374
|
+
errorMessage: `Could not resolve source or schema for ${ctx.sourcePath}`,
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
const resolvedRemoteFileAtSourcePath = Internal.resolvePath(
|
|
379
|
+
modulePath,
|
|
380
|
+
ctx.valModule.source,
|
|
381
|
+
ctx.valModule.schema,
|
|
382
|
+
);
|
|
383
|
+
|
|
384
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
385
|
+
const fileRefProp = (resolvedRemoteFileAtSourcePath.source as any)?.[
|
|
386
|
+
FILE_REF_PROP
|
|
387
|
+
];
|
|
388
|
+
if (!fileRefProp) {
|
|
389
|
+
return {
|
|
390
|
+
success: false,
|
|
391
|
+
errorMessage: `Expected file to be defined at: ${ctx.sourcePath} but no file was found`,
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const resolveRemoteFileSchema = resolvedRemoteFileAtSourcePath.schema;
|
|
396
|
+
if (!resolveRemoteFileSchema) {
|
|
397
|
+
return {
|
|
398
|
+
success: false,
|
|
399
|
+
errorMessage: `Cannot upload remote file: schema not found for ${ctx.sourcePath}`,
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
if (
|
|
404
|
+
resolveRemoteFileSchema.type !== "image" &&
|
|
405
|
+
resolveRemoteFileSchema.type !== "file"
|
|
406
|
+
) {
|
|
407
|
+
return {
|
|
408
|
+
success: false,
|
|
409
|
+
errorMessage: `The schema is the remote is neither image nor file: ${ctx.sourcePath}`,
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
const actualRemoteFileSource = resolvedRemoteFileAtSourcePath.source;
|
|
414
|
+
const fileSourceMetadata = Internal.isFile(actualRemoteFileSource)
|
|
415
|
+
? actualRemoteFileSource.metadata
|
|
416
|
+
: undefined;
|
|
417
|
+
|
|
418
|
+
return uploadRemoteFileCore(
|
|
419
|
+
ctx,
|
|
420
|
+
fileRefProp,
|
|
421
|
+
fileSourceMetadata,
|
|
422
|
+
resolveRemoteFileSchema,
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// Gallery (s.images({ remote: true }) / s.files({ remote: true })) upload.
|
|
427
|
+
// Unlike a single image/file field, a gallery entry is keyed by its local file
|
|
428
|
+
// path and the value is bare metadata (no FileSource), so we derive the file
|
|
429
|
+
// ref from the key and synthesize the image/file schema from the record's
|
|
430
|
+
// media options.
|
|
431
|
+
export async function handleRemoteGalleryFileUpload(
|
|
432
|
+
ctx: FixHandlerContext,
|
|
433
|
+
): Promise<FixHandlerResult> {
|
|
434
|
+
if (!ctx.fix) {
|
|
435
|
+
return {
|
|
436
|
+
success: false,
|
|
437
|
+
// No sourcePath in the message: the reported location already points at it.
|
|
438
|
+
errorMessage: `Remote file needs to be uploaded (use --fix to upload)`,
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
const fix = ctx.validationError.fixes?.[0];
|
|
443
|
+
const mediaType = fix === "files:upload-remote" ? "file" : "image";
|
|
444
|
+
|
|
445
|
+
// The gallery entry is keyed by its local file path; validateMediaKey carries
|
|
446
|
+
// that key as the error value.
|
|
447
|
+
const fileRef = ctx.validationError.value;
|
|
448
|
+
if (typeof fileRef !== "string") {
|
|
449
|
+
return {
|
|
450
|
+
success: false,
|
|
451
|
+
errorMessage: `Expected a local file path for gallery entry at ${ctx.sourcePath}`,
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
const [, modulePath] = Internal.splitModuleFilePathAndModulePath(
|
|
456
|
+
ctx.sourcePath,
|
|
457
|
+
);
|
|
458
|
+
|
|
459
|
+
if (!ctx.valModule.source || !ctx.valModule.schema) {
|
|
460
|
+
return {
|
|
461
|
+
success: false,
|
|
462
|
+
errorMessage: `Could not resolve source or schema for ${ctx.sourcePath}`,
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
const resolved = Internal.resolvePath(
|
|
467
|
+
modulePath,
|
|
468
|
+
ctx.valModule.source,
|
|
469
|
+
ctx.valModule.schema,
|
|
470
|
+
);
|
|
471
|
+
const entrySource = resolved.source;
|
|
472
|
+
const metadata =
|
|
473
|
+
entrySource &&
|
|
474
|
+
typeof entrySource === "object" &&
|
|
475
|
+
!Array.isArray(entrySource)
|
|
476
|
+
? (entrySource as Record<string, unknown>)
|
|
477
|
+
: undefined;
|
|
478
|
+
|
|
479
|
+
// The gallery item schema is an ObjectSchema, not an image/file schema, so
|
|
480
|
+
// synthesize the serialized image/file schema (matching how single fields
|
|
481
|
+
// serialize) for the remote ref's validation hash.
|
|
482
|
+
//
|
|
483
|
+
// `accept`/`directory` come from the RECORD that holds the entry, resolved
|
|
484
|
+
// from the entry's PARENT path - not from the module root, which is only the
|
|
485
|
+
// record when the gallery is the whole module. A nested gallery
|
|
486
|
+
// (s.object({ gallery: s.images(...) })) would otherwise synthesize a schema
|
|
487
|
+
// with no options and bake a validation hash into the remote ref that can
|
|
488
|
+
// never validate, so a mismatch fails fast instead of uploading.
|
|
489
|
+
const [, parentModulePath] = Internal.splitModuleFilePathAndModulePath(
|
|
490
|
+
Internal.parentOfSourcePath(ctx.sourcePath),
|
|
491
|
+
);
|
|
492
|
+
const recordSchema = Internal.resolvePath(
|
|
493
|
+
parentModulePath,
|
|
494
|
+
ctx.valModule.source,
|
|
495
|
+
ctx.valModule.schema,
|
|
496
|
+
).schema;
|
|
497
|
+
if (recordSchema?.type !== "record") {
|
|
498
|
+
return {
|
|
499
|
+
success: false,
|
|
500
|
+
errorMessage: `Expected a gallery record at the parent of ${ctx.sourcePath}, got ${
|
|
501
|
+
recordSchema?.type ?? "nothing"
|
|
502
|
+
}`,
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
const { accept, directory } = recordSchema;
|
|
506
|
+
const schema: SerializedImageSchema | SerializedFileSchema =
|
|
507
|
+
mediaType === "image"
|
|
508
|
+
? {
|
|
509
|
+
type: "image",
|
|
510
|
+
opt: false,
|
|
511
|
+
options: {
|
|
512
|
+
...(accept ? { accept } : {}),
|
|
513
|
+
...(directory ? { directory } : {}),
|
|
514
|
+
},
|
|
515
|
+
}
|
|
516
|
+
: {
|
|
517
|
+
type: "file",
|
|
518
|
+
opt: false,
|
|
519
|
+
options: {
|
|
520
|
+
...(accept ? { accept } : {}),
|
|
521
|
+
},
|
|
522
|
+
};
|
|
523
|
+
|
|
524
|
+
return uploadRemoteFileCore(ctx, fileRef, metadata, schema);
|
|
525
|
+
}
|
|
526
|
+
|
|
418
527
|
export async function handleRemoteFileDownload(
|
|
419
528
|
ctx: FixHandlerContext,
|
|
420
529
|
): Promise<FixHandlerResult> {
|
|
@@ -479,6 +588,17 @@ export async function handleUniqueFolderCheck(
|
|
|
479
588
|
return { success: true };
|
|
480
589
|
}
|
|
481
590
|
|
|
591
|
+
// Maps a gallery key to its on-disk local path. Remote galleries key uploaded
|
|
592
|
+
// entries by a remote URL while keeping the file on disk; everything else is
|
|
593
|
+
// already a local path and is returned unchanged.
|
|
594
|
+
function remoteKeyToLocalPath(key: string): string {
|
|
595
|
+
const remoteRefRes = Internal.remote.splitRemoteRef(key);
|
|
596
|
+
if (remoteRefRes.status === "success") {
|
|
597
|
+
return `/${remoteRefRes.filePath}`;
|
|
598
|
+
}
|
|
599
|
+
return key;
|
|
600
|
+
}
|
|
601
|
+
|
|
482
602
|
export async function handleCheckAllFiles(
|
|
483
603
|
ctx: FixHandlerContext,
|
|
484
604
|
): Promise<FixHandlerResult> {
|
|
@@ -500,7 +620,12 @@ export async function handleCheckAllFiles(
|
|
|
500
620
|
errorMessage: `Could not get source for ${ctx.sourcePath}`,
|
|
501
621
|
};
|
|
502
622
|
}
|
|
503
|
-
|
|
623
|
+
// Gallery entries are keyed by their file path. Remote galleries key uploaded
|
|
624
|
+
// entries by a remote URL, but the file is kept on disk at its local path, so
|
|
625
|
+
// normalize remote-URL keys back to that local path for the on-disk checks.
|
|
626
|
+
const trackedFiles = new Set(
|
|
627
|
+
Object.keys(source as Record<string, unknown>).map(remoteKeyToLocalPath),
|
|
628
|
+
);
|
|
504
629
|
|
|
505
630
|
// Check that all tracked files exist on disk
|
|
506
631
|
const missingTrackedFiles = [...trackedFiles].filter((f) => {
|
|
@@ -558,6 +683,8 @@ export const currentFixHandlers: Record<
|
|
|
558
683
|
"file:add-metadata": handleFileMetadata,
|
|
559
684
|
"image:upload-remote": handleRemoteFileUpload,
|
|
560
685
|
"file:upload-remote": handleRemoteFileUpload,
|
|
686
|
+
"images:upload-remote": handleRemoteGalleryFileUpload,
|
|
687
|
+
"files:upload-remote": handleRemoteGalleryFileUpload,
|
|
561
688
|
"image:download-remote": handleRemoteFileDownload,
|
|
562
689
|
"file:download-remote": handleRemoteFileDownload,
|
|
563
690
|
"image:check-remote": handleRemoteFileCheck,
|
|
@@ -1,100 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import { createRequire } from "node:module";
|
|
8
|
-
|
|
9
|
-
const ValConfigSchema = z.object({
|
|
10
|
-
project: z.string().optional(),
|
|
11
|
-
root: z.string().optional(),
|
|
12
|
-
files: z
|
|
13
|
-
.object({
|
|
14
|
-
directory: z.string().refine(
|
|
15
|
-
(val): val is `/public` | `/public/${string}` =>
|
|
16
|
-
(val === "/public" ||
|
|
17
|
-
(val.startsWith("/public/") && !val.endsWith("/"))) &&
|
|
18
|
-
// Reject path traversal so the directory cannot escape /public
|
|
19
|
-
!val
|
|
20
|
-
.split("/")
|
|
21
|
-
.some((segment) => segment === "." || segment === ".."),
|
|
22
|
-
{
|
|
23
|
-
message:
|
|
24
|
-
"files.directory must start with '/public', must not end with '/' and must not contain '.' or '..' segments",
|
|
25
|
-
},
|
|
26
|
-
),
|
|
27
|
-
})
|
|
28
|
-
.optional(),
|
|
29
|
-
gitCommit: z.string().optional(),
|
|
30
|
-
gitBranch: z.string().optional(),
|
|
31
|
-
defaultTheme: z.union([z.literal("light"), z.literal("dark")]).optional(),
|
|
32
|
-
ai: z
|
|
33
|
-
.object({
|
|
34
|
-
commitMessages: z
|
|
35
|
-
.object({
|
|
36
|
-
disabled: z.boolean().optional(),
|
|
37
|
-
})
|
|
38
|
-
.optional(),
|
|
39
|
-
})
|
|
40
|
-
.optional(),
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
export async function evalValConfigFile(
|
|
44
|
-
projectRoot: string,
|
|
45
|
-
configFileName: string,
|
|
46
|
-
): Promise<ValConfig | null> {
|
|
47
|
-
const valConfigPath = path.join(projectRoot, configFileName);
|
|
48
|
-
|
|
49
|
-
let code: string | null = null;
|
|
50
|
-
try {
|
|
51
|
-
code = await fs.readFile(valConfigPath, "utf-8");
|
|
52
|
-
} catch {
|
|
53
|
-
//
|
|
54
|
-
}
|
|
55
|
-
if (!code) {
|
|
56
|
-
return null;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const transpiled = ts.transpileModule(code, {
|
|
60
|
-
compilerOptions: {
|
|
61
|
-
target: ts.ScriptTarget.ES2020,
|
|
62
|
-
module: ts.ModuleKind.CommonJS,
|
|
63
|
-
esModuleInterop: true,
|
|
64
|
-
},
|
|
65
|
-
fileName: valConfigPath,
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
const projectRootRequire = createRequire(valConfigPath);
|
|
69
|
-
const exportsObj = {};
|
|
70
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
71
|
-
const sandbox: Record<string, any> = {
|
|
72
|
-
exports: exportsObj,
|
|
73
|
-
module: { exports: exportsObj },
|
|
74
|
-
require: projectRootRequire, // NOTE: this is a security risk, but this code is running in the users own environment at the CLI level
|
|
75
|
-
__filename: valConfigPath,
|
|
76
|
-
__dirname: projectRoot,
|
|
77
|
-
console,
|
|
78
|
-
process,
|
|
79
|
-
};
|
|
80
|
-
sandbox.global = sandbox;
|
|
81
|
-
|
|
82
|
-
const context = vm.createContext(sandbox);
|
|
83
|
-
const script = new vm.Script(transpiled.outputText, {
|
|
84
|
-
filename: valConfigPath,
|
|
85
|
-
});
|
|
86
|
-
script.runInContext(context);
|
|
87
|
-
const valConfig = sandbox.module.exports.config;
|
|
88
|
-
if (!valConfig) {
|
|
89
|
-
throw Error(
|
|
90
|
-
`Val config file at path: '${valConfigPath}' must export a config object. Got: ${valConfig}`,
|
|
91
|
-
);
|
|
92
|
-
}
|
|
93
|
-
const result = ValConfigSchema.safeParse(valConfig);
|
|
94
|
-
if (!result.success) {
|
|
95
|
-
throw Error(
|
|
96
|
-
`Val config file at path: '${valConfigPath}' has invalid schema: ${result.error.message}`,
|
|
97
|
-
);
|
|
98
|
-
}
|
|
99
|
-
return result.data;
|
|
100
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Moved to `@valbuild/server` so that the CLI and
|
|
3
|
+
* `@valbuild/language-server` resolve project config through the same code.
|
|
4
|
+
* Re-exported here to keep existing CLI imports working.
|
|
5
|
+
*/
|
|
6
|
+
export { evalValConfigFile, findAndEvalValConfigFile } from "@valbuild/server";
|
package/src/validate.ts
CHANGED
|
@@ -4,7 +4,7 @@ import fs from "fs/promises";
|
|
|
4
4
|
import { glob } from "fast-glob";
|
|
5
5
|
import { DEFAULT_CONTENT_HOST, DEFAULT_VAL_REMOTE_HOST } from "@valbuild/core";
|
|
6
6
|
import { getSettings, uploadRemoteFile } from "@valbuild/server";
|
|
7
|
-
import {
|
|
7
|
+
import { findAndEvalValConfigFile } from "./utils/evalValConfigFile";
|
|
8
8
|
import { createDefaultValFSHost, runValidation } from "./runValidation";
|
|
9
9
|
import {
|
|
10
10
|
sourcePathToCodeFrame,
|
|
@@ -49,9 +49,7 @@ export async function validate({
|
|
|
49
49
|
// errors found. Re-reads config and val files each call so it always reflects
|
|
50
50
|
// the latest state on disk (used both for one-shot and watch mode).
|
|
51
51
|
async function runOnce(): Promise<number> {
|
|
52
|
-
const valConfigFile =
|
|
53
|
-
(await evalValConfigFile(projectRoot, "val.config.ts")) ||
|
|
54
|
-
(await evalValConfigFile(projectRoot, "val.config.js"));
|
|
52
|
+
const valConfigFile = await findAndEvalValConfigFile(projectRoot);
|
|
55
53
|
|
|
56
54
|
const resolvedValConfigFile = valConfigFile
|
|
57
55
|
? {
|