|  | 
|  | 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