@rmlio/yarrrml-parser
Version:
Parse YARRRML descriptions into RML RDF statements
153 lines (129 loc) • 4.83 kB
JavaScript
/**
* author: Pieter Heyvaert (pheyvaer.heyvaert@ugent.be)
* Ghent University - imec - IDLab
*/
const { program } = require('commander');
const path = require('path');
const fs = require('fs');
const Y2R = require('../lib/rml-generator.js');
const Y2R2 = require('../lib/r2rml-generator.js');
const { canonicalize } = require('../lib/tools');
const namespaces = require('../lib/namespaces').asMap();
const pkginfo = require('pkginfo');
const N3 = require('n3');
const watch = require('../lib/watcher.js');
const glob = require('glob');
const Logger = require('../lib/logger');
pkginfo(module, 'version');
/**
* This method collect all values when an option is used multiple times.
* @param val A single value.
* @param memo The current array of values.
* @returns {*} The updated array with the new value.
*/
function collect(val, memo) {
memo.push(val);
return memo;
}
program.version(module.exports.version);
program.option('-i, --input <file>', 'input file (can be used multiple times)', collect, []); // We support multiple uses of this option.
program.option('-c, --class', 'use rr:class when appropriate');
program.option('-o, --output <file>', 'output file (default: stdout)');
program.option('-f, --format <format>', 'RML or R2RML (default: RML)');
program.option('-w, --watch', 'watch for file changes');
program.option('-p, --pretty', 'output prettified triples');
program.option('-e, --external <value>', 'external references (key=value, can be used multiple times', collect, []); // We support multiple uses of this option.
program.option('-m, --skip-metadata', 'skip metadata in generated rules');
program.parse(process.argv);
const options = program.opts();
if (!options.input) {
Logger.error('Please provide an input file using -i| --input.');
} else {
let inputPaths = [];
for (let input of options.input) {
// Check if the input is a regex, e.g., *.yarrrml
if (glob.hasMagic(input)) {
const foundFiles = glob.sync(input).map(file => path.join(process.cwd(), file));
inputPaths = inputPaths.concat(foundFiles);
} else {
if (!path.isAbsolute(input)) {
input = path.join(process.cwd(), input);
}
inputPaths.push(input);
}
}
if (!options.watch) {
try {
const inputData = [];
for (const p of inputPaths) {
const yarrrml = fs.readFileSync(p, 'utf8');
inputData.push({ yarrrml, file: p });
}
if (options.format) {
options.format = options.format.toUpperCase();
}
let triples;
let prefixes = {
rr: namespaces.rr,
rdf: namespaces.rdf,
rdfs: namespaces.rdfs,
fnml: namespaces.fnml,
fno: namespaces.fno,
d2rq: namespaces.d2rq,
void: namespaces.void,
dc: namespaces.dc,
foaf: namespaces.foaf
};
const externalReferences = {};
for (const e of options.external) {
const keyValue = e.split('=');
externalReferences[keyValue[0]] = keyValue[1];
}
const includeMetadata = !(!!options.skipMetadata);
if (!options.format || options.format === 'RML') {
const y2r = new Y2R({ class: !!options.class, externalReferences, includeMetadata });
triples = y2r.convert(inputData);
prefixes.rml = namespaces.rml;
prefixes.ql = namespaces.ql;
prefixes[''] = y2r.getBaseIRI();
prefixes = Object.assign({}, prefixes, y2r.getPrefixes());
} else {
const y2r = new Y2R2({ class: !!options.class, externalReferences, includeMetadata });
triples = y2r.convert(inputData);
prefixes[''] = y2r.getBaseIRI();
prefixes = Object.assign({}, prefixes, y2r.getPrefixes());
}
const writer = new N3.Writer({ prefixes });
writer.addQuads(triples);
writer.end(async (error, result) => {
if (options.pretty) {
result = await canonicalize(result);
}
if (options.output) {
if (!path.isAbsolute(options.output)) {
options.output = path.join(process.cwd(), options.output);
}
try {
await fs.promises.writeFile(options.output, result);
} catch (e) {
Logger.error(`The RML could not be written to the output file ${options.output}`);
}
} else {
Logger.log(result);
}
});
} catch (e) {
if (e.code === 'ENOENT') {
Logger.error(`The input file ${options.input} is not found.`);
} else if (e.code === 'INVALID_YAML') {
Logger.error(`The input file contains invalid YAML.`);
Logger.error(`line ${e.parsedLine}: ${e.message}`);
} else {
Logger.error(e);
}
}
} else {
watch(options.input, options.output, options.format);
}
}