diff --git a/CHANGELOG.md b/CHANGELOG.md index 317cf30..f12bf22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to this project will be documented in this file. +## 0.6.2 - 2024-01-?? + +- CLI: recommend additional installation on help + ## 0.6.1 - 2024-01-14 - Support setting record type(s) diff --git a/bin/avram.js b/bin/avram.js index d2f99e8..35faf14 100755 --- a/bin/avram.js +++ b/bin/avram.js @@ -1,8 +1,17 @@ #!/usr/bin/env node import cli from "../lib/cli.js" -import formats from "../lib/formats.js" +import { formats, missing } from "../lib/formats.js" import avramAction from "../lib/action.js" +console.log(missing) + +import ajv from "../lib/ajv.js" +var details = ajv ? "" : ` +Install ajv and ajv-formats for better validation of schemas.` + +if (missing.size) details += ` +Install ${[...missing.keys()].join(", ")} for additional input formats.` + cli.usage("avram [options] [validation options] []") .description("Validate file(s) with an Avram schema") .option(`-f, --format [name] input format (${Object.keys(formats).join("|")})`) @@ -11,9 +20,7 @@ cli.usage("avram [options] [validation options] []") .option("-p, --print print all input records (in JSON)") .option("-v, --verbose verbose error messages") .option("-l, --list list supported validation options") - .details(` -An empty string schema argument uses the empty schema. Combining -n and -v -emits parsed records. See supported validation options with --list.`) + .details(details) .action(avramAction) .parse(process.argv) .catch(e => { diff --git a/lib/action.js b/lib/action.js index 59e6d55..6aed36d 100644 --- a/lib/action.js +++ b/lib/action.js @@ -3,7 +3,7 @@ import { Validator } from "./validate.js" import { SchemaValidator } from "./schema-validator.js" import { optionHelp, optionDefault } from "./options.js" import cli from "./cli.js" -import formats from "../lib/formats.js" +import { formats } from "../lib/formats.js" function loadSchema(file, opt) { const schema = JSON.parse(fs.readFileSync(file)) diff --git a/lib/ajv.js b/lib/ajv.js new file mode 100644 index 0000000..ecaeb66 --- /dev/null +++ b/lib/ajv.js @@ -0,0 +1,15 @@ +import { createRequire } from "node:module" +const require = createRequire(import.meta.url) + +const { default: Ajv } = await import("ajv").catch(() => ({})) +const { default: addFormats } = await import("ajv-formats").catch(() => ({})) + +// export initialized ajv, if available +export default (() => { + if (Ajv && addFormats) { + const ajv = new Ajv({strictTypes: false}) + addFormats(ajv) + ajv.addMetaSchema(require("ajv/lib/refs/json-schema-draft-06.json")) + return ajv + } +})() diff --git a/lib/formats.js b/lib/formats.js index e093bfd..4bfb032 100644 --- a/lib/formats.js +++ b/lib/formats.js @@ -1,5 +1,4 @@ import { Transform } from "stream" - import { Record } from "./record.js" const recordTransform = mapping => new Transform({ @@ -14,7 +13,7 @@ const marcTransform = recordTransform(Record.fromMarcjs) const picaTransform = recordTransform(Record.fromPicajson) const flatTransform = recordTransform(Record.fromObject) -const formats = {} +export const formats = {} const plugins = { marcjs: module => { @@ -49,15 +48,17 @@ const plugins = { // TODO: add split2 to support, tsv... ndjson... } +export const missing = new Set(Object.keys(plugins)) + // dynamically load optional modules in parallel const modules = Object.keys(plugins) await Promise.allSettled(modules.map(m => import(m))) .then(imports => { imports.forEach(({value}, i) => { if (value) { - plugins[modules[i]](value) + const module = modules[i] + plugins[module](value) + missing.delete(module) } }) }) - -export default formats diff --git a/lib/schema-validator.js b/lib/schema-validator.js index ac826c3..67224dd 100644 --- a/lib/schema-validator.js +++ b/lib/schema-validator.js @@ -1,14 +1,8 @@ -import { readFileSync } from "fs" +import ajv from "./ajv.js" +import { readFileSync } from "fs" const jsonSchema = JSON.parse(readFileSync(new URL("../avram-schema.json", import.meta.url))) -// load ajv, if available -import { createRequire } from "node:module" -const require = createRequire(import.meta.url) - -const { default: Ajv } = await import("ajv").catch(() => ({})) -const { default: addFormats } = await import("ajv-formats").catch(() => ({})) - const families = { flat: { id: /./, disallow: ["occurrence", "counter", "indicator1", "indicator2", "subfields"] }, marc: { id: /^([0-9]{3}|LDR)$/, disallow: ["occurrence", "counter"] }, @@ -18,10 +12,7 @@ const families = { export class SchemaValidator { constructor() { - if (Ajv && addFormats) { - const ajv = new Ajv({strictTypes: false}) - ajv.addMetaSchema(require("ajv/lib/refs/json-schema-draft-06.json")) - addFormats(ajv) + if (ajv) { this.validator = ajv.compile(jsonSchema) } else { console.warn("ajv and/or ajv-formats is not installed so Avram schema validation is very limited!")