Skip to content

Commit 08c7db1

Browse files
committed
feature: dev-call validate-yml
1 parent 57e32ce commit 08c7db1

File tree

2 files changed

+90
-1
lines changed

2 files changed

+90
-1
lines changed

src/command/dev-call/cmd.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Command } from "cliffy/command/mod.ts";
22
import { quartoConfig } from "../../core/quarto.ts";
33
import { commands } from "../command.ts";
44
import { buildJsCommand } from "./build-artifacts/cmd.ts";
5-
import { hidden } from "../../core/lib/external/colors.ts";
5+
import { validateYamlCommand } from "./validate-yaml/cmd.ts";
66

77
type CommandOptionInfo = {
88
name: string;
@@ -70,4 +70,5 @@ export const devCallCommand = new Command()
7070
Deno.exit(1);
7171
})
7272
.command("cli-info", generateCliInfoCommand)
73+
.command("validate-yaml", validateYamlCommand)
7374
.command("build-artifacts", buildJsCommand);
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
* cmd.ts
3+
*
4+
* Copyright (C) 2025 Posit Software, PBC
5+
*/
6+
7+
import { Command } from "cliffy/command/mod.ts";
8+
import { initYamlIntelligenceResourcesFromFilesystem } from "../../../core/schema/utils.ts";
9+
import { readAndValidateYamlFromMappedString } from "../../../core/lib/yaml-schema/validated-yaml.ts";
10+
import { mappedStringFromFile } from "../../../core/mapped-text.ts";
11+
import {
12+
getSchemaDefinition,
13+
setSchemaDefinition,
14+
} from "../../../core/lib/yaml-validation/schema.ts";
15+
import { error } from "../../../deno_ral/log.ts";
16+
import { tidyverseFormatError } from "../../../core/lib/errors.ts";
17+
import {
18+
convertFromYaml,
19+
getSchemaSchemas,
20+
} from "../../../core/lib/yaml-schema/from-yaml.ts";
21+
22+
const getSchema = async (schemaNameOrFile: string) => {
23+
if (schemaNameOrFile.endsWith(".yml")) {
24+
getSchemaSchemas();
25+
// it's a file, we load it, validate it against the schema schema
26+
// and then return it
27+
const file = mappedStringFromFile(schemaNameOrFile);
28+
const schema = getSchemaDefinition("schema/schema");
29+
const result = await readAndValidateYamlFromMappedString(
30+
file,
31+
schema,
32+
);
33+
if (result.yamlValidationErrors.length) {
34+
error("Schema file is not valid");
35+
for (const err of result.yamlValidationErrors) {
36+
error(tidyverseFormatError(err.niceError), { colorize: false });
37+
}
38+
Deno.exit(1);
39+
}
40+
const schemaName = `user-schema-${schemaNameOrFile}`;
41+
const newSchema = convertFromYaml(result.yaml);
42+
newSchema.$id = schemaName;
43+
setSchemaDefinition(newSchema);
44+
return getSchemaDefinition(schemaName);
45+
} else {
46+
// it's a schema name, we get it from the schema registry
47+
// and return it
48+
return getSchemaDefinition(schemaNameOrFile);
49+
}
50+
};
51+
52+
export const validateYamlCommand = new Command()
53+
.name("validate-yaml")
54+
.hidden()
55+
.arguments("<input:string>")
56+
.option(
57+
"-s, --schema [schema:string]",
58+
"Name of schema in Quarto's definitions.yml. If string ends with .yml, it is treated as a file name for a new schema, which is validated, loaded, and then used.",
59+
)
60+
.option(
61+
"--json",
62+
"If set, output error messages in JSON format.",
63+
)
64+
.description(
65+
"Validates a YAML file against Quarto's schemas.\n\n",
66+
)
67+
.action(async (options: any, input: string) => {
68+
await initYamlIntelligenceResourcesFromFilesystem();
69+
if (!options.schema) {
70+
throw new Error("Schema name or file is required");
71+
}
72+
const file = mappedStringFromFile(input);
73+
const schema = await getSchema(options.schema);
74+
const result = await readAndValidateYamlFromMappedString(
75+
file,
76+
schema,
77+
);
78+
if (options.json) {
79+
console.log(JSON.stringify(result.yamlValidationErrors, null, 2));
80+
} else {
81+
for (const err of result.yamlValidationErrors) {
82+
error(tidyverseFormatError(err.niceError), { colorize: false });
83+
}
84+
}
85+
if (result.yamlValidationErrors.length) {
86+
Deno.exit(1);
87+
}
88+
});

0 commit comments

Comments
 (0)