#!/usr/bin/env node "use strict"; const fs = require("fs"); const jsyaml = require("js-yaml"); const ExampleValidator = require("openapi-examples-validator"); const SwaggerParser = require("swagger-parser"); function checkRefSiblings(file, path, data) { let ok = true; if (typeof data === "object" && data !== null) { if ( "allOf" in data && Object.values(data.allOf).filter((subschema) => !("$ref" in subschema)).length !== 1 ) { console.error( `${file}: Too many inline allOf subschemas at ${JSON.stringify( path, )}: ${JSON.stringify(data, undefined, 2)}`, ); ok = false; } if ("$ref" in data && Object.entries(data).length !== 1) { console.error( `${file}: Siblings of $ref have no effect at ${JSON.stringify( path, )}: ${JSON.stringify(data, undefined, 2)}`, ); ok = false; } for (const [key, child] of Array.isArray(data) ? data.entries() : Object.entries(data)) { if (!checkRefSiblings(file, [...path, key], child)) { ok = false; } } } return ok; } (async () => { // Iterate through the changed files, passed in the arguments. // The two first arguments are the call to the Node interpreter and this // script, hence the starting point at 2. for (const file of process.argv.slice(2)) { try { const data = jsyaml.load(await fs.promises.readFile(file, "utf8"), { filename: file, }); if (data.openapi !== undefined) { if (!checkRefSiblings(file, [], data)) { process.exitCode = 1; } await SwaggerParser.validate(file); const res = await ExampleValidator.validateFile(file); if (!res.valid) { for (const error of res.errors) { console.error(error); } process.exitCode = 1; } } } catch (error) { if (error instanceof jsyaml.YAMLException) { console.error(error.message); } else if (error instanceof SyntaxError) { console.error(`${file}: ${error.message}`); } else { throw error; } process.exitCode = 1; } } })().catch((error) => { console.error(error); process.exit(1); });