Comparing version 0.5.0 to 0.6.0
# Changelog | ||
## 📦 [0.6.0](https://www.npmjs.com/package/v8r/v/0.6.0) - 2021-07-28 | ||
* Add the ability to search custom schema catalogs using the `--catalogs` param | ||
## 📦 [0.5.0](https://www.npmjs.com/package/v8r/v/0.5.0) - 2021-01-13 | ||
@@ -4,0 +8,0 @@ |
{ | ||
"name": "v8r", | ||
"version": "0.5.0", | ||
"version": "0.6.0", | ||
"description": "A command-line JSON and YAML validator that's on your wavelength", | ||
@@ -8,3 +8,3 @@ "scripts": { | ||
"lint": "eslint \"src/**/*.js\"", | ||
"coverage": "nyc report --reporter=text-lcov > coverage.lcov", | ||
"coverage": "nyc report --reporter=cobertura", | ||
"prettier": "prettier --write \"**/*.js\"", | ||
@@ -32,3 +32,3 @@ "prettier:check": "prettier --check \"**/*.js\"", | ||
"minimatch": "^3.0.4", | ||
"yargs": "^16.1.0" | ||
"yargs": "^17.0.1" | ||
}, | ||
@@ -39,6 +39,6 @@ "devDependencies": { | ||
"eslint": "^7.16.0", | ||
"eslint-config-prettier": "^7.1.0", | ||
"eslint-plugin-mocha": "^8.0.0", | ||
"eslint-config-prettier": "^8.1.0", | ||
"eslint-plugin-mocha": "^9.0.0", | ||
"eslint-plugin-prettier": "^3.3.0", | ||
"mocha": "^8.2.1", | ||
"mocha": "^9.0.0", | ||
"nock": "^13.0.4", | ||
@@ -45,0 +45,0 @@ "nyc": "^15.1.0", |
# v8r | ||
![Build status](https://github.com/chris48s/v8r/workflows/Run%20tests/badge.svg?branch=main) | ||
[![codecov](https://codecov.io/gh/chris48s/v8r/branch/main/graph/badge.svg?token=KL998A5CJH)](https://codecov.io/gh/chris48s/v8r) | ||
![NPM version](https://img.shields.io/npm/v/v8r.svg) | ||
![License](https://img.shields.io/npm/l/v8r.svg) | ||
![Node Compatibility](https://img.shields.io/node/v/v8r.svg) | ||
![build](https://raw.githubusercontent.com/chris48s/v8r/badges/.badges/main/build-status.svg) | ||
![coverage](https://raw.githubusercontent.com/chris48s/v8r/badges/.badges/main/coverage.svg) | ||
![version](https://raw.githubusercontent.com/chris48s/v8r/badges/.badges/main/package-version.svg) | ||
![license](https://raw.githubusercontent.com/chris48s/v8r/badges/.badges/main/package-license.svg) | ||
![node](https://raw.githubusercontent.com/chris48s/v8r/badges/.badges/main/package-node-version.svg) | ||
@@ -53,6 +53,20 @@ A command-line JSON and YAML validator that's on your wavelength. | ||
# ..you can specify one | ||
# ..you can specify a schema | ||
$ v8r feature.geojson -s https://json.schemastore.org/geojson | ||
Validating feature.geojson against schema from https://json.schemastore.org/geojson ... | ||
✅ feature.geojson is valid | ||
# ..or use a custom catalog | ||
$ cat > my-catalog.json <<EOF | ||
{ "$schema": "https://json.schemastore.org/schema-catalog.json", | ||
"version": 1, | ||
"schemas": [ { "name": "geojson", | ||
"description": "geojson", | ||
"url": "https://json.schemastore.org/geojson.json", | ||
"fileMatch": ["*.geojson"] } ] } | ||
EOF | ||
$ v8r feature.geojson -c my-catalog.json | ||
ℹ️ Found schema in my-catalog.json ... | ||
Validating feature.geojson against schema from https://json.schemastore.org/geojson ... | ||
✅ feature.geojson is valid | ||
``` | ||
@@ -59,0 +73,0 @@ |
"use strict"; | ||
const got = require("got"); | ||
const callCounter = {}; | ||
const callLimit = 10; | ||
let callCounter = {}; | ||
@@ -66,2 +66,6 @@ function expire(cache, ttl) { | ||
module.exports = { cachedFetch, expire }; | ||
function resetCallCounter() { | ||
callCounter = {}; | ||
} | ||
module.exports = { cachedFetch, expire, resetCallCounter }; |
@@ -18,7 +18,53 @@ "use strict"; | ||
"https://www.schemastore.org/api/json/catalog.json"; | ||
const SCHEMASTORE_CATALOG_SCHEMA_URL = | ||
"https://json.schemastore.org/schema-catalog.json"; | ||
const CACHE_DIR = path.join(os.tmpdir(), "flat-cache"); | ||
async function getSchemaUrlForFilename(filename, cache, ttl) { | ||
const { schemas } = await cachedFetch(SCHEMASTORE_CATALOG_URL, cache, ttl); | ||
async function getFromUrlOrFile(location, cache, ttl) { | ||
return isUrl(location) | ||
? await cachedFetch(location, cache, ttl) | ||
: JSON.parse(fs.readFileSync(location, "utf8").toString()); | ||
} | ||
async function getSchemaUrlForFilename(catalogs, filename, cache, ttl) { | ||
for (const [i, catalogLocation] of catalogs.entries()) { | ||
const catalog = await getFromUrlOrFile(catalogLocation, cache, ttl); | ||
const catalogSchema = await getFromUrlOrFile( | ||
SCHEMASTORE_CATALOG_SCHEMA_URL, | ||
cache, | ||
ttl | ||
); | ||
// Validate the catalog | ||
const valid = await validate(catalog, catalogSchema, cache, ttl); | ||
if (!valid || catalog.schemas === undefined) { | ||
throw new Error(`❌ Malformed catalog at ${catalogLocation}`); | ||
} | ||
const { schemas } = catalog; | ||
const matches = getSchemaMatchesForFilename(schemas, filename); | ||
if (matches.length === 1) { | ||
console.log(`ℹ️ Found schema in ${catalogLocation} ...`); | ||
return matches[0].url; // Match found. We're done. | ||
} | ||
if (matches.length === 0 && i < catalogs.length - 1) { | ||
continue; // No match found. Try the next catalog in the array. | ||
} | ||
if (matches.length > 1) { | ||
// We found >1 matches in the same catalog. This is always a hard error. | ||
console.log( | ||
`Found multiple possible schemas for ${filename}. Possible matches:` | ||
); | ||
matches.forEach(function (match) { | ||
console.log(`${match.description}: ${match.url}`); | ||
}); | ||
} | ||
// Either we found >1 matches in the same catalog or we found 0 matches | ||
// in the last catalog and there are no more catalogs left to try. | ||
throw new Error(`❌ Could not find a schema to validate ${filename}`); | ||
} | ||
} | ||
function getSchemaMatchesForFilename(schemas, filename) { | ||
const matches = []; | ||
@@ -39,18 +85,7 @@ schemas.forEach(function (schema) { | ||
}); | ||
if (matches.length === 1) { | ||
return matches[0].url; | ||
} | ||
if (matches.length > 1) { | ||
console.log( | ||
`Found multiple possible schemas for ${filename}. Possible matches:` | ||
); | ||
matches.forEach(function (match) { | ||
console.log(`${match.description}: ${match.url}`); | ||
}); | ||
} | ||
throw new Error(`❌ Could not find a schema to validate ${filename}`); | ||
return matches; | ||
} | ||
async function validate(data, schema, resolver) { | ||
async function validate(data, schema, cache, ttl) { | ||
const resolver = (url) => cachedFetch(url, cache, ttl); | ||
const ajv = new Ajv({ schemaId: "auto", loadSchema: resolver }); | ||
@@ -105,7 +140,12 @@ ajv.addMetaSchema(require("ajv/lib/refs/json-schema-draft-04.json")); | ||
); | ||
const schemaLocation = | ||
args.schema || (await getSchemaUrlForFilename(filename, cache, ttl)); | ||
const schema = isUrl(schemaLocation) | ||
? await cachedFetch(schemaLocation, cache, ttl) | ||
: JSON.parse(fs.readFileSync(schemaLocation, "utf8").toString()); | ||
args.schema || | ||
(await getSchemaUrlForFilename( | ||
(args.catalogs || []).concat([SCHEMASTORE_CATALOG_URL]), | ||
filename, | ||
cache, | ||
ttl | ||
)); | ||
const schema = await getFromUrlOrFile(schemaLocation, cache, ttl); | ||
if ( | ||
@@ -121,6 +161,3 @@ "$schema" in schema && | ||
const resolver = function (url) { | ||
return cachedFetch(url, cache, ttl); | ||
}; | ||
const valid = await validate(data, schema, resolver); | ||
const valid = await validate(data, schema, cache, ttl); | ||
if (valid) { | ||
@@ -176,2 +213,10 @@ console.log(`✅ ${filename} is valid`); | ||
}) | ||
.option("catalogs", { | ||
type: "string", | ||
alias: "c", | ||
array: true, | ||
describe: | ||
"Local path or URL of custom catalogs to use prior to schemastore.org", | ||
}) | ||
.conflicts("schema", "catalogs") | ||
.option("ignore-errors", { | ||
@@ -178,0 +223,0 @@ type: "boolean", |
17291
299
115
+ Addedcliui@8.0.1(transitive)
+ Addedyargs@17.7.2(transitive)
+ Addedyargs-parser@21.1.1(transitive)
- Removedcliui@7.0.4(transitive)
- Removedyargs@16.2.0(transitive)
- Removedyargs-parser@20.2.9(transitive)
Updatedyargs@^17.0.1