@valbuild/cli 0.147.0 → 0.148.1
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/CHANGELOG.md +36 -0
- package/cli/dist/valbuild-cli-cli.cjs.dev.js +295 -230
- package/cli/dist/valbuild-cli-cli.cjs.prod.js +295 -230
- package/cli/dist/valbuild-cli-cli.esm.js +297 -232
- package/package.json +8 -8
- package/src/cli.ts +6 -0
- package/src/publish.test.ts +84 -0
- package/src/publish.ts +37 -0
- package/src/runValidation.imageset.test.ts +23 -0
- package/src/runValidation.ts +15 -1
- package/src/validate.ts +325 -303
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,41 @@
|
|
|
1
1
|
# @valbuild/cli
|
|
2
2
|
|
|
3
|
+
## 0.148.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies []:
|
|
8
|
+
- @valbuild/server@0.148.1
|
|
9
|
+
- @valbuild/language-server@0.148.1
|
|
10
|
+
|
|
11
|
+
## 0.148.0
|
|
12
|
+
|
|
13
|
+
### Minor Changes
|
|
14
|
+
|
|
15
|
+
- [#818](https://github.com/valbuild/val/pull/818) [`562d032`](https://github.com/valbuild/val/commit/562d03280c3fcf97a80562e8b017dd2125ac4ad1) Thanks [@freekh](https://github.com/freekh)! - `val publish` now runs `val validate` first, and does not publish content that
|
|
16
|
+
has validation errors the Studio's Publish would also refuse. What the Studio
|
|
17
|
+
repairs by itself on save -- a local image in a project with remote files, or
|
|
18
|
+
metadata still to be filled in -- is reported and published, as it is from the
|
|
19
|
+
Studio.
|
|
20
|
+
|
|
21
|
+
A connected project's CI publishes with `val publish`, so from this release a
|
|
22
|
+
push whose content does not validate fails its run instead of going live. The
|
|
23
|
+
run prints the same report `val validate` does. To fix it, run
|
|
24
|
+
`val validate --fix` in the project, commit what it changes, push, and fix by
|
|
25
|
+
hand whatever it reports that it cannot fix.
|
|
26
|
+
|
|
27
|
+
The check never fixes anything itself — CI does not rewrite what it was asked
|
|
28
|
+
to publish. `--skip-validation` publishes without it.
|
|
29
|
+
|
|
30
|
+
### Patch Changes
|
|
31
|
+
|
|
32
|
+
- Updated dependencies [[`df5544c`](https://github.com/valbuild/val/commit/df5544c6e92a4c3a52765630c9de191e854ee723), [`562d032`](https://github.com/valbuild/val/commit/562d03280c3fcf97a80562e8b017dd2125ac4ad1), [`4176757`](https://github.com/valbuild/val/commit/41767579b01849473a94779122fd39c07dd99234), [`562d032`](https://github.com/valbuild/val/commit/562d03280c3fcf97a80562e8b017dd2125ac4ad1), [`562d032`](https://github.com/valbuild/val/commit/562d03280c3fcf97a80562e8b017dd2125ac4ad1), [`562d032`](https://github.com/valbuild/val/commit/562d03280c3fcf97a80562e8b017dd2125ac4ad1), [`562d032`](https://github.com/valbuild/val/commit/562d03280c3fcf97a80562e8b017dd2125ac4ad1), [`562d032`](https://github.com/valbuild/val/commit/562d03280c3fcf97a80562e8b017dd2125ac4ad1), [`562d032`](https://github.com/valbuild/val/commit/562d03280c3fcf97a80562e8b017dd2125ac4ad1), [`562d032`](https://github.com/valbuild/val/commit/562d03280c3fcf97a80562e8b017dd2125ac4ad1)]:
|
|
33
|
+
- @valbuild/server@0.148.0
|
|
34
|
+
- @valbuild/tanstack-build@0.148.0
|
|
35
|
+
- @valbuild/shared@0.148.0
|
|
36
|
+
- @valbuild/core@0.148.0
|
|
37
|
+
- @valbuild/language-server@0.148.0
|
|
38
|
+
|
|
3
39
|
## 0.147.0
|
|
4
40
|
|
|
5
41
|
### Patch Changes
|
|
@@ -131,6 +131,10 @@ async function* runValidation({
|
|
|
131
131
|
fs
|
|
132
132
|
}) {
|
|
133
133
|
const projectRoot = path__default["default"].resolve(root);
|
|
134
|
+
// One question per platform host for the whole run: each error is fixed by
|
|
135
|
+
// its own createFixPatch call, and a host that does not answer would
|
|
136
|
+
// otherwise cost a timeout per ref.
|
|
137
|
+
const askHostProject = server.askOncePerHost();
|
|
134
138
|
const service = await server.createService(projectRoot, fs);
|
|
135
139
|
|
|
136
140
|
// Modules registered in the project's val.modules. Files found on disk that
|
|
@@ -327,6 +331,9 @@ async function* runValidation({
|
|
|
327
331
|
message: result.errorMessage ?? "Unknown error",
|
|
328
332
|
...(v.keyError ? {
|
|
329
333
|
keyError: true
|
|
334
|
+
} : {}),
|
|
335
|
+
...(internal.isRepairedOnSave(v) ? {
|
|
336
|
+
repairedOnSave: true
|
|
330
337
|
} : {})
|
|
331
338
|
};
|
|
332
339
|
fileErrors += 1;
|
|
@@ -348,6 +355,9 @@ async function* runValidation({
|
|
|
348
355
|
fixable: true,
|
|
349
356
|
...(v.keyError ? {
|
|
350
357
|
keyError: true
|
|
358
|
+
} : {}),
|
|
359
|
+
...(internal.isRepairedOnSave(v) ? {
|
|
360
|
+
repairedOnSave: true
|
|
351
361
|
} : {})
|
|
352
362
|
};
|
|
353
363
|
}
|
|
@@ -356,7 +366,8 @@ async function* runValidation({
|
|
|
356
366
|
if (result.shouldApplyPatch) {
|
|
357
367
|
const fixPatch = await server.createFixPatch({
|
|
358
368
|
projectRoot,
|
|
359
|
-
remoteHost: remote.remoteHost
|
|
369
|
+
remoteHost: remote.remoteHost,
|
|
370
|
+
askHostProject
|
|
360
371
|
}, !!fix, sourcePath, v, remoteFiles, valModule.source, valModule.schema);
|
|
361
372
|
if (fix && fixPatch !== null && fixPatch !== void 0 && fixPatch.patch && (fixPatch === null || fixPatch === void 0 ? void 0 : fixPatch.patch.length) > 0) {
|
|
362
373
|
await service.patch(moduleFilePath, fixPatch.patch);
|
|
@@ -386,6 +397,9 @@ async function* runValidation({
|
|
|
386
397
|
fixable: true,
|
|
387
398
|
...(v.keyError ? {
|
|
388
399
|
keyError: true
|
|
400
|
+
} : {}),
|
|
401
|
+
...(internal.isRepairedOnSave(v) ? {
|
|
402
|
+
repairedOnSave: true
|
|
389
403
|
} : {})
|
|
390
404
|
};
|
|
391
405
|
}
|
|
@@ -400,6 +414,9 @@ async function* runValidation({
|
|
|
400
414
|
fixable: !!(e.fixes && e.fixes.length),
|
|
401
415
|
...(e.keyError ? {
|
|
402
416
|
keyError: true
|
|
417
|
+
} : {}),
|
|
418
|
+
...(internal.isRepairedOnSave(e) ? {
|
|
419
|
+
repairedOnSave: true
|
|
403
420
|
} : {})
|
|
404
421
|
};
|
|
405
422
|
}
|
|
@@ -678,255 +695,273 @@ async function validate({
|
|
|
678
695
|
} catch {
|
|
679
696
|
console.log("Prettier not found, skipping formatting");
|
|
680
697
|
}
|
|
698
|
+
const runOnce = () => validateOnce({
|
|
699
|
+
projectRoot,
|
|
700
|
+
fix: !!fix,
|
|
701
|
+
prettier
|
|
702
|
+
});
|
|
703
|
+
if (!watch) {
|
|
704
|
+
const totalErrors = await runOnce();
|
|
705
|
+
if (totalErrors > 0) {
|
|
706
|
+
process.exit(1);
|
|
707
|
+
}
|
|
708
|
+
return;
|
|
709
|
+
}
|
|
710
|
+
await watchAndValidate(projectRoot, runOnce);
|
|
711
|
+
}
|
|
681
712
|
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
713
|
+
/**
|
|
714
|
+
* One validation pass over the project, printed as `val validate` prints it,
|
|
715
|
+
* returning the number of errors found.
|
|
716
|
+
*
|
|
717
|
+
* Re-reads config and val files on each call so it always reflects the latest
|
|
718
|
+
* state on disk: `val validate` calls it once or on every change (`--watch`),
|
|
719
|
+
* and `val publish` calls it before it builds anything.
|
|
720
|
+
*/
|
|
721
|
+
async function validateOnce({
|
|
722
|
+
projectRoot,
|
|
723
|
+
fix,
|
|
724
|
+
prettier,
|
|
725
|
+
blockingOnly = false
|
|
726
|
+
}) {
|
|
727
|
+
const valConfigFile = await server.findAndEvalValConfigFile(projectRoot);
|
|
728
|
+
const resolvedValConfigFile = valConfigFile ? {
|
|
729
|
+
...valConfigFile,
|
|
730
|
+
project: process.env.VAL_PROJECT || valConfigFile.project
|
|
731
|
+
} : process.env.VAL_PROJECT ? {
|
|
732
|
+
project: process.env.VAL_PROJECT
|
|
733
|
+
} : undefined;
|
|
734
|
+
console.log(pc__default["default"].greenBright(`Validating project${resolvedValConfigFile !== null && resolvedValConfigFile !== void 0 && resolvedValConfigFile.project ? ` '${pc__default["default"].inverse(resolvedValConfigFile.project)}'` : ""}...`));
|
|
735
|
+
const valFiles = await fastGlob.glob("**/*.val.{js,ts}", {
|
|
736
|
+
ignore: ["node_modules/**"],
|
|
737
|
+
cwd: projectRoot
|
|
738
|
+
});
|
|
739
|
+
console.log(pc__default["default"].greenBright(`Found ${valFiles.length} files...`));
|
|
740
|
+
const fixedFiles = new Set();
|
|
741
|
+
let totalErrors = 0;
|
|
742
|
+
let repairedOnSave = 0;
|
|
701
743
|
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
744
|
+
// Caches each val file's parsed source so files are read/parsed at most once
|
|
745
|
+
// per pass when resolving sourcePaths to file locations and code frames.
|
|
746
|
+
const sourceFileCache = new Map();
|
|
705
747
|
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
748
|
+
// Diagnostics are buffered per module (keyed by relative file path) so we can
|
|
749
|
+
// render them grouped and prioritised after the run, rather than streaming
|
|
750
|
+
// them out interleaved. Transient progress (remote/fix-applied) still streams
|
|
751
|
+
// live below.
|
|
752
|
+
const reports = new Map();
|
|
753
|
+
const valid = [];
|
|
754
|
+
const skipped = [];
|
|
713
755
|
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
}
|
|
730
|
-
return report;
|
|
731
|
-
};
|
|
732
|
-
for await (const event of runValidation({
|
|
733
|
-
root: projectRoot,
|
|
734
|
-
fix: !!fix,
|
|
735
|
-
valFiles,
|
|
736
|
-
project: resolvedValConfigFile === null || resolvedValConfigFile === void 0 ? void 0 : resolvedValConfigFile.project,
|
|
737
|
-
remote: {
|
|
738
|
-
remoteHost: process.env.VAL_REMOTE_HOST || core.DEFAULT_VAL_REMOTE_HOST,
|
|
739
|
-
getSettings: (projectName, options) => server.getSettings(projectName, options),
|
|
740
|
-
uploadFile: (project, bucket, fileHash, fileExt, fileBuffer, options) => server.uploadRemoteFile(process.env.VAL_CONTENT_URL || core.DEFAULT_CONTENT_HOST, project, bucket, fileHash, fileExt ?? "", fileBuffer, options)
|
|
741
|
-
},
|
|
742
|
-
fs: server.createDefaultValFSHost()
|
|
743
|
-
})) {
|
|
744
|
-
switch (event.type) {
|
|
745
|
-
case "file-valid":
|
|
746
|
-
valid.push({
|
|
747
|
-
file: relFile(event.file),
|
|
748
|
-
durationMs: event.durationMs
|
|
749
|
-
});
|
|
750
|
-
break;
|
|
751
|
-
case "file-error-count":
|
|
752
|
-
reportFor(event.file).durationMs = event.durationMs;
|
|
753
|
-
totalErrors += event.errorCount;
|
|
754
|
-
break;
|
|
755
|
-
case "validation-error":
|
|
756
|
-
reportFor(moduleOf(event.sourcePath)).diagnostics.push({
|
|
757
|
-
severity: "error",
|
|
758
|
-
sourcePath: event.sourcePath,
|
|
759
|
-
message: event.message,
|
|
760
|
-
...(event.keyError ? {
|
|
761
|
-
keyError: true
|
|
762
|
-
} : {})
|
|
763
|
-
});
|
|
764
|
-
break;
|
|
765
|
-
case "validation-fixable-error":
|
|
766
|
-
reportFor(moduleOf(event.sourcePath)).diagnostics.push({
|
|
767
|
-
severity: event.fixable ? "fixable" : "error",
|
|
768
|
-
sourcePath: event.sourcePath,
|
|
769
|
-
message: event.message,
|
|
770
|
-
...(event.keyError ? {
|
|
771
|
-
keyError: true
|
|
772
|
-
} : {})
|
|
773
|
-
});
|
|
774
|
-
break;
|
|
775
|
-
case "unknown-fix":
|
|
776
|
-
reportFor(moduleOf(event.sourcePath)).diagnostics.push({
|
|
777
|
-
severity: "error",
|
|
778
|
-
sourcePath: event.sourcePath,
|
|
779
|
-
message: `Unknown fix: ${event.fixes.join(", ")}`,
|
|
780
|
-
...(event.keyError ? {
|
|
781
|
-
keyError: true
|
|
782
|
-
} : {})
|
|
783
|
-
});
|
|
784
|
-
break;
|
|
785
|
-
case "unregistered-module":
|
|
786
|
-
skipped.push(event.file);
|
|
787
|
-
break;
|
|
788
|
-
case "fatal-error":
|
|
789
|
-
// No sourcePath for fatal errors; group by file, render message only.
|
|
790
|
-
reportFor(event.file).diagnostics.push({
|
|
791
|
-
severity: "error",
|
|
792
|
-
sourcePath: event.file,
|
|
793
|
-
message: event.message
|
|
794
|
-
});
|
|
795
|
-
break;
|
|
796
|
-
case "fix-applied":
|
|
797
|
-
console.log(pc__default["default"].yellow("⚠"), "Applied fix for", event.sourcePath);
|
|
798
|
-
fixedFiles.add(event.file);
|
|
799
|
-
break;
|
|
800
|
-
case "remote-uploading":
|
|
801
|
-
console.log(pc__default["default"].yellow("⚠"), `Uploading remote file: '${event.ref}'...`);
|
|
802
|
-
break;
|
|
803
|
-
case "remote-uploaded":
|
|
804
|
-
console.log(pc__default["default"].green("✔"), `Completed upload of remote file: '${event.ref}'`);
|
|
805
|
-
break;
|
|
806
|
-
case "remote-already-uploaded":
|
|
807
|
-
console.log(pc__default["default"].yellow("⚠"), `Remote file ${event.filePath} already uploaded`);
|
|
808
|
-
break;
|
|
809
|
-
case "remote-downloading":
|
|
810
|
-
console.log(pc__default["default"].yellow("⚠"), `Downloading remote file in ${event.sourcePath}...`);
|
|
811
|
-
break;
|
|
812
|
-
}
|
|
756
|
+
// Relative file path (no leading slash), matching the code frame's
|
|
757
|
+
// relativeFile so headers, diagnostics and frames all agree.
|
|
758
|
+
const relFile = file => file.replace(/^\//, "");
|
|
759
|
+
// The module a sourcePath/file belongs to is the part before the `?p=...`.
|
|
760
|
+
const moduleOf = sourcePathOrFile => relFile(sourcePathOrFile.split("?")[0]);
|
|
761
|
+
const reportFor = file => {
|
|
762
|
+
const key = relFile(file);
|
|
763
|
+
let report = reports.get(key);
|
|
764
|
+
if (!report) {
|
|
765
|
+
report = {
|
|
766
|
+
file: key,
|
|
767
|
+
durationMs: 0,
|
|
768
|
+
diagnostics: []
|
|
769
|
+
};
|
|
770
|
+
reports.set(key, report);
|
|
813
771
|
}
|
|
772
|
+
return report;
|
|
773
|
+
};
|
|
774
|
+
for await (const event of runValidation({
|
|
775
|
+
root: projectRoot,
|
|
776
|
+
fix,
|
|
777
|
+
valFiles,
|
|
778
|
+
project: resolvedValConfigFile === null || resolvedValConfigFile === void 0 ? void 0 : resolvedValConfigFile.project,
|
|
779
|
+
remote: {
|
|
780
|
+
remoteHost: process.env.VAL_REMOTE_HOST || core.DEFAULT_VAL_REMOTE_HOST,
|
|
781
|
+
getSettings: (projectName, options) => server.getSettings(projectName, options),
|
|
782
|
+
uploadFile: (project, bucket, fileHash, fileExt, fileBuffer, options) => server.uploadRemoteFile(process.env.VAL_CONTENT_URL || core.DEFAULT_CONTENT_HOST, project, bucket, fileHash, fileExt ?? "", fileBuffer, options)
|
|
783
|
+
},
|
|
784
|
+
fs: server.createDefaultValFSHost()
|
|
785
|
+
})) {
|
|
786
|
+
switch (event.type) {
|
|
787
|
+
case "file-valid":
|
|
788
|
+
valid.push({
|
|
789
|
+
file: relFile(event.file),
|
|
790
|
+
durationMs: event.durationMs
|
|
791
|
+
});
|
|
792
|
+
break;
|
|
793
|
+
case "file-error-count":
|
|
794
|
+
reportFor(event.file).durationMs = event.durationMs;
|
|
795
|
+
totalErrors += event.errorCount;
|
|
796
|
+
break;
|
|
797
|
+
case "validation-error":
|
|
798
|
+
if (event.repairedOnSave) repairedOnSave += 1;
|
|
799
|
+
reportFor(moduleOf(event.sourcePath)).diagnostics.push({
|
|
800
|
+
severity: "error",
|
|
801
|
+
sourcePath: event.sourcePath,
|
|
802
|
+
message: event.message,
|
|
803
|
+
...(event.keyError ? {
|
|
804
|
+
keyError: true
|
|
805
|
+
} : {})
|
|
806
|
+
});
|
|
807
|
+
break;
|
|
808
|
+
case "validation-fixable-error":
|
|
809
|
+
if (event.repairedOnSave) repairedOnSave += 1;
|
|
810
|
+
reportFor(moduleOf(event.sourcePath)).diagnostics.push({
|
|
811
|
+
severity: event.fixable ? "fixable" : "error",
|
|
812
|
+
sourcePath: event.sourcePath,
|
|
813
|
+
message: event.message,
|
|
814
|
+
...(event.keyError ? {
|
|
815
|
+
keyError: true
|
|
816
|
+
} : {})
|
|
817
|
+
});
|
|
818
|
+
break;
|
|
819
|
+
case "unknown-fix":
|
|
820
|
+
reportFor(moduleOf(event.sourcePath)).diagnostics.push({
|
|
821
|
+
severity: "error",
|
|
822
|
+
sourcePath: event.sourcePath,
|
|
823
|
+
message: `Unknown fix: ${event.fixes.join(", ")}`,
|
|
824
|
+
...(event.keyError ? {
|
|
825
|
+
keyError: true
|
|
826
|
+
} : {})
|
|
827
|
+
});
|
|
828
|
+
break;
|
|
829
|
+
case "unregistered-module":
|
|
830
|
+
skipped.push(event.file);
|
|
831
|
+
break;
|
|
832
|
+
case "fatal-error":
|
|
833
|
+
// No sourcePath for fatal errors; group by file, render message only.
|
|
834
|
+
reportFor(event.file).diagnostics.push({
|
|
835
|
+
severity: "error",
|
|
836
|
+
sourcePath: event.file,
|
|
837
|
+
message: event.message
|
|
838
|
+
});
|
|
839
|
+
break;
|
|
840
|
+
case "fix-applied":
|
|
841
|
+
console.log(pc__default["default"].yellow("⚠"), "Applied fix for", event.sourcePath);
|
|
842
|
+
fixedFiles.add(event.file);
|
|
843
|
+
break;
|
|
844
|
+
case "remote-uploading":
|
|
845
|
+
console.log(pc__default["default"].yellow("⚠"), `Uploading remote file: '${event.ref}'...`);
|
|
846
|
+
break;
|
|
847
|
+
case "remote-uploaded":
|
|
848
|
+
console.log(pc__default["default"].green("✔"), `Completed upload of remote file: '${event.ref}'`);
|
|
849
|
+
break;
|
|
850
|
+
case "remote-already-uploaded":
|
|
851
|
+
console.log(pc__default["default"].yellow("⚠"), `Remote file ${event.filePath} already uploaded`);
|
|
852
|
+
break;
|
|
853
|
+
case "remote-downloading":
|
|
854
|
+
console.log(pc__default["default"].yellow("⚠"), `Downloading remote file in ${event.sourcePath}...`);
|
|
855
|
+
break;
|
|
856
|
+
}
|
|
857
|
+
}
|
|
814
858
|
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
859
|
+
// Renders a module's diagnostics under a single left "│" gutter bar, with the
|
|
860
|
+
// file name on top and blank gutter lines for air. Output flows least- to
|
|
861
|
+
// most-actionable top-to-bottom, so fixable diagnostics are shown last (at the
|
|
862
|
+
// bottom, nearest the prompt).
|
|
863
|
+
const renderModule = report => {
|
|
864
|
+
const bar = pc__default["default"].dim("│");
|
|
865
|
+
const diagnostics = [...report.diagnostics].sort((a, b) => a.severity === b.severity ? 0 : a.severity === "fixable" ? 1 : -1);
|
|
866
|
+
const fixableCount = diagnostics.filter(d => d.severity === "fixable").length;
|
|
867
|
+
const total = diagnostics.length;
|
|
868
|
+
const hasError = fixableCount < total;
|
|
869
|
+
const symbol = hasError ? pc__default["default"].red("✘") : pc__default["default"].yellow("⚠");
|
|
870
|
+
let label;
|
|
871
|
+
if (fixableCount === total) {
|
|
872
|
+
label = `${fixableCount} fixable`;
|
|
873
|
+
} else if (fixableCount > 0) {
|
|
874
|
+
label = `${total} error${total > 1 ? "s" : ""} (${fixableCount} fixable)`;
|
|
875
|
+
} else {
|
|
876
|
+
label = `${total} error${total > 1 ? "s" : ""}`;
|
|
877
|
+
}
|
|
878
|
+
console.log(`${pc__default["default"].bold(report.file)} ${symbol} ${label} ${pc__default["default"].dim(`(${report.durationMs}ms)`)}`);
|
|
879
|
+
for (const d of diagnostics) {
|
|
880
|
+
const target = d.keyError ? "key" : "value";
|
|
881
|
+
const dsym = d.severity === "fixable" ? pc__default["default"].yellow("⚠") : pc__default["default"].red("✘");
|
|
882
|
+
const parts = sourcePathToLocationParts(d.sourcePath, projectRoot, sourceFileCache, target);
|
|
883
|
+
console.log(bar);
|
|
884
|
+
if (parts) {
|
|
885
|
+
// `file:line:col` (no key/value label) so VS Code's terminal links it.
|
|
886
|
+
console.log(`${bar} ${dsym} ${parts.relativeFile}:${parts.line}:${parts.character}`);
|
|
887
|
+
console.log(`${bar} ${d.message}`);
|
|
831
888
|
} else {
|
|
832
|
-
|
|
889
|
+
console.log(`${bar} ${dsym} ${d.message}`);
|
|
833
890
|
}
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
const target = d.keyError ? "key" : "value";
|
|
837
|
-
const dsym = d.severity === "fixable" ? pc__default["default"].yellow("⚠") : pc__default["default"].red("✘");
|
|
838
|
-
const parts = sourcePathToLocationParts(d.sourcePath, projectRoot, sourceFileCache, target);
|
|
891
|
+
const frame = sourcePathToCodeFrame(d.sourcePath, projectRoot, sourceFileCache, target);
|
|
892
|
+
if (frame !== undefined) {
|
|
839
893
|
console.log(bar);
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
console.log(`${bar} ${dsym} ${parts.relativeFile}:${parts.line}:${parts.character}`);
|
|
843
|
-
console.log(`${bar} ${d.message}`);
|
|
844
|
-
} else {
|
|
845
|
-
console.log(`${bar} ${dsym} ${d.message}`);
|
|
846
|
-
}
|
|
847
|
-
const frame = sourcePathToCodeFrame(d.sourcePath, projectRoot, sourceFileCache, target);
|
|
848
|
-
if (frame !== undefined) {
|
|
849
|
-
console.log(bar);
|
|
850
|
-
for (const frameLine of frame.split("\n")) {
|
|
851
|
-
console.log(`${bar} ${frameLine}`);
|
|
852
|
-
}
|
|
853
|
-
}
|
|
854
|
-
if (d.severity === "fixable") {
|
|
855
|
-
console.log(`${bar} ${pc__default["default"].dim("→ run with --fix to apply")}`);
|
|
894
|
+
for (const frameLine of frame.split("\n")) {
|
|
895
|
+
console.log(`${bar} ${frameLine}`);
|
|
856
896
|
}
|
|
857
897
|
}
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
};
|
|
861
|
-
|
|
862
|
-
// Format the files that had fixes applied, the way the project formats
|
|
863
|
-
// everything else. See `formatFixedFiles` for why this cannot simply be
|
|
864
|
-
// `prettier.format(code, { filepath })`.
|
|
865
|
-
//
|
|
866
|
-
// A file that could not be formatted is reported and skipped rather than
|
|
867
|
-
// thrown: the fix itself landed, and aborting here would leave the fix
|
|
868
|
-
// applied and the report below unwritten.
|
|
869
|
-
if (prettier) {
|
|
870
|
-
const {
|
|
871
|
-
failures
|
|
872
|
-
} = await formatFixedFiles(prettier, projectRoot, fixedFiles);
|
|
873
|
-
for (const failure of failures) {
|
|
874
|
-
console.log(pc__default["default"].yellow("⚠"), `Could not format ${relFile(failure.file)}: ${failure.message}`);
|
|
898
|
+
if (d.severity === "fixable") {
|
|
899
|
+
console.log(`${bar} ${pc__default["default"].dim("→ run with --fix to apply")}`);
|
|
875
900
|
}
|
|
876
901
|
}
|
|
902
|
+
console.log(bar);
|
|
903
|
+
console.log("");
|
|
904
|
+
};
|
|
877
905
|
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
906
|
+
// Format the files that had fixes applied, the way the project formats
|
|
907
|
+
// everything else. See `formatFixedFiles` for why this cannot simply be
|
|
908
|
+
// `prettier.format(code, { filepath })`.
|
|
909
|
+
//
|
|
910
|
+
// A file that could not be formatted is reported and skipped rather than
|
|
911
|
+
// thrown: the fix itself landed, and aborting here would leave the fix
|
|
912
|
+
// applied and the report below unwritten.
|
|
913
|
+
if (prettier) {
|
|
914
|
+
const {
|
|
915
|
+
failures
|
|
916
|
+
} = await formatFixedFiles(prettier, projectRoot, fixedFiles);
|
|
917
|
+
for (const failure of failures) {
|
|
918
|
+
console.log(pc__default["default"].yellow("⚠"), `Could not format ${relFile(failure.file)}: ${failure.message}`);
|
|
886
919
|
}
|
|
887
|
-
|
|
888
|
-
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
// Render the grouped report least- to most-actionable, top-to-bottom, so the
|
|
923
|
+
// most important things end up at the bottom nearest the prompt: valid files
|
|
924
|
+
// and skipped modules first, then error-only modules, then fixable modules.
|
|
925
|
+
const allReports = [...reports.values()];
|
|
926
|
+
const fixableModules = allReports.filter(r => r.diagnostics.some(d => d.severity === "fixable"));
|
|
927
|
+
const errorModules = allReports.filter(r => !r.diagnostics.some(d => d.severity === "fixable"));
|
|
928
|
+
for (const v of valid) {
|
|
929
|
+
console.log(pc__default["default"].green("✔"), pc__default["default"].dim(`${v.file} valid (${v.durationMs}ms)`));
|
|
930
|
+
}
|
|
931
|
+
for (const file of skipped) {
|
|
932
|
+
console.log(pc__default["default"].yellow("⚠"), pc__default["default"].dim(`/${file} is not registered in val.modules - skipping`));
|
|
933
|
+
}
|
|
934
|
+
if (allReports.length > 0) {
|
|
935
|
+
console.log("");
|
|
936
|
+
}
|
|
937
|
+
for (const report of [...errorModules, ...fixableModules]) {
|
|
938
|
+
renderModule(report);
|
|
939
|
+
}
|
|
940
|
+
const fixableTotal = allReports.reduce((n, r) => n + r.diagnostics.filter(d => d.severity === "fixable").length, 0);
|
|
941
|
+
if (totalErrors > 0) {
|
|
942
|
+
let summary = `${totalErrors} error${totalErrors > 1 ? "s" : ""}`;
|
|
943
|
+
if (fixableTotal > 0) {
|
|
944
|
+
summary += ` (${fixableTotal} fixable)`;
|
|
889
945
|
}
|
|
890
|
-
|
|
891
|
-
|
|
946
|
+
summary += ` across ${allReports.length} file${allReports.length > 1 ? "s" : ""}`;
|
|
947
|
+
if (valid.length > 0) {
|
|
948
|
+
summary += ` · ${valid.length} valid`;
|
|
892
949
|
}
|
|
893
|
-
|
|
894
|
-
|
|
950
|
+
if (skipped.length > 0) {
|
|
951
|
+
summary += ` · ${skipped.length} skipped`;
|
|
895
952
|
}
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
}
|
|
902
|
-
summary += ` across ${allReports.length} file${allReports.length > 1 ? "s" : ""}`;
|
|
903
|
-
if (valid.length > 0) {
|
|
904
|
-
summary += ` · ${valid.length} valid`;
|
|
905
|
-
}
|
|
906
|
-
if (skipped.length > 0) {
|
|
907
|
-
summary += ` · ${skipped.length} skipped`;
|
|
908
|
-
}
|
|
909
|
-
console.log(pc__default["default"].red("✘"), summary);
|
|
910
|
-
} else {
|
|
911
|
-
let summary = "No validation errors found";
|
|
912
|
-
if (valid.length > 0) {
|
|
913
|
-
summary += ` · ${valid.length} valid`;
|
|
914
|
-
}
|
|
915
|
-
if (skipped.length > 0) {
|
|
916
|
-
summary += ` · ${skipped.length} skipped`;
|
|
917
|
-
}
|
|
918
|
-
console.log(pc__default["default"].green("✔"), summary);
|
|
953
|
+
console.log(pc__default["default"].red("✘"), summary);
|
|
954
|
+
} else {
|
|
955
|
+
let summary = "No validation errors found";
|
|
956
|
+
if (valid.length > 0) {
|
|
957
|
+
summary += ` · ${valid.length} valid`;
|
|
919
958
|
}
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
if (!watch) {
|
|
923
|
-
const totalErrors = await runOnce();
|
|
924
|
-
if (totalErrors > 0) {
|
|
925
|
-
process.exit(1);
|
|
959
|
+
if (skipped.length > 0) {
|
|
960
|
+
summary += ` · ${skipped.length} skipped`;
|
|
926
961
|
}
|
|
927
|
-
|
|
962
|
+
console.log(pc__default["default"].green("✔"), summary);
|
|
928
963
|
}
|
|
929
|
-
|
|
964
|
+
return blockingOnly ? totalErrors - repairedOnSave : totalErrors;
|
|
930
965
|
}
|
|
931
966
|
|
|
932
967
|
// Directory names anywhere in the path that should never be watched.
|
|
@@ -3922,6 +3957,30 @@ function remoteFilesRefusal(read, {
|
|
|
3922
3957
|
* person reads the lines.
|
|
3923
3958
|
*/
|
|
3924
3959
|
async function publish(options) {
|
|
3960
|
+
// Content that does not validate is not published. This is the check that
|
|
3961
|
+
// lets the Studio trust a connected project's published content without
|
|
3962
|
+
// re-checking every file itself: whatever reached the site went through
|
|
3963
|
+
// here. Without `--fix`, so CI never rewrites what it was asked to publish.
|
|
3964
|
+
//
|
|
3965
|
+
// Refused on what the Studio's Publish refuses on, and no more: an error
|
|
3966
|
+
// the Studio leaves to the server to repair (a local image in a remote
|
|
3967
|
+
// project, metadata to fill in) is printed and published, as it would be
|
|
3968
|
+
// from the Studio. Every site made on /new starts with its template's
|
|
3969
|
+
// images local, so counting those would refuse its first publish.
|
|
3970
|
+
if (!options.skipValidation) {
|
|
3971
|
+
const projectRoot = options.root ? path__default["default"].resolve(options.root) : process.cwd();
|
|
3972
|
+
const errorCount = await validateOnce({
|
|
3973
|
+
projectRoot,
|
|
3974
|
+
fix: false,
|
|
3975
|
+
blockingOnly: true
|
|
3976
|
+
});
|
|
3977
|
+
if (errorCount > 0) {
|
|
3978
|
+
error(`Not published: ${errorCount} validation error${errorCount === 1 ? "" : "s"}.`);
|
|
3979
|
+
console.error(pc__default["default"].dim(` Run "val validate --fix" in the project, commit what it changes and push, ` + `and fix by hand what it cannot. --skip-validation publishes anyway.`));
|
|
3980
|
+
process.exitCode = 1;
|
|
3981
|
+
return;
|
|
3982
|
+
}
|
|
3983
|
+
}
|
|
3925
3984
|
const result = await runPublish({
|
|
3926
3985
|
...(options.root ? {
|
|
3927
3986
|
root: options.root
|
|
@@ -4216,6 +4275,8 @@ async function main() {
|
|
|
4216
4275
|
--links-own-css The app links its own CSS (--no-links-own-css for the
|
|
4217
4276
|
opposite; omit it when the build did not say)
|
|
4218
4277
|
--dry-run Verify, and stop before the site changes
|
|
4278
|
+
--skip-validation Publish without running "val validate" first. It runs by
|
|
4279
|
+
default, and an error stops the publish
|
|
4219
4280
|
|
|
4220
4281
|
Command: list-unused-files
|
|
4221
4282
|
Description: EXPERIMENTAL.
|
|
@@ -4307,6 +4368,9 @@ async function main() {
|
|
|
4307
4368
|
dryRun: {
|
|
4308
4369
|
type: "boolean"
|
|
4309
4370
|
},
|
|
4371
|
+
skipValidation: {
|
|
4372
|
+
type: "boolean"
|
|
4373
|
+
},
|
|
4310
4374
|
yes: {
|
|
4311
4375
|
type: "boolean"
|
|
4312
4376
|
},
|
|
@@ -4378,7 +4442,8 @@ async function main() {
|
|
|
4378
4442
|
layerRev: flags.layerRev,
|
|
4379
4443
|
buildHash: flags.buildHash,
|
|
4380
4444
|
linksOwnCss: flags.linksOwnCss,
|
|
4381
|
-
dryRun: flags.dryRun
|
|
4445
|
+
dryRun: flags.dryRun,
|
|
4446
|
+
skipValidation: flags.skipValidation
|
|
4382
4447
|
});
|
|
4383
4448
|
case "login":
|
|
4384
4449
|
return login({
|