haystack-codegen
Version:
Project Haystack Core code generation tools
183 lines (182 loc) • 6.36 kB
JavaScript
;
/*
* Copyright (c) 2021, J2 Innovations. All Rights Reserved
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.resolvePodsNamespace = exports.resolveDefaultNamespace = void 0;
const haystack_core_1 = require("haystack-core");
const fs_1 = require("fs");
const path_1 = __importDefault(require("path"));
const util_1 = require("util");
const safe_1 = __importDefault(require("colors/safe"));
const adm_zip_1 = __importDefault(require("adm-zip"));
const yaml_1 = __importDefault(require("yaml"));
const readFileAsync = (0, util_1.promisify)(fs_1.readFile);
const readdirAsync = (0, util_1.promisify)(fs_1.readdir);
/**
* Resolve the default namespace from defs distributed with the project.
*
* @returns The default namespace.
*/
async function resolveDefaultNamespace() {
const defsBuf = await readFileAsync(path_1.default.join(__dirname, '../rc/defs.zinc'));
const grid = haystack_core_1.ZincReader.readValue(defsBuf.toString('utf-8'));
return new haystack_core_1.HNamespace(grid);
}
exports.resolveDefaultNamespace = resolveDefaultNamespace;
const DEF_FILE_EXTS = ['.trio', '.hayson.yaml', '.hayson.yml', '.hayson.json'];
/**
* Resolve the namespace from some POD files.
*
* @param podDir The directory to read the pod files from.
* @param podFilter If non-empty, used to filter certain POD files.
* @returns The generated namespace.
*/
async function resolvePodsNamespace(podDir, podFilter = '') {
const podsToLibDefs = await getPodToLibDefs(podDir, podFilter);
const pods = [...podsToLibDefs.keys()];
const logger = {
warning(message) {
console.log(' ' + safe_1.default.green(message));
},
error(message) {
console.warn(' ' + safe_1.default.yellow(message));
},
fatal(message) {
console.error(' ' + safe_1.default.red(message));
},
};
if (!pods.length) {
logger.warning('No pods found.');
return new haystack_core_1.HNamespace(haystack_core_1.HGrid.make({}));
}
const loadLib = async (pod) => {
const lib = podsToLibDefs.get(pod);
return {
name: pod.name,
lib,
dicts: readDictsFromPodLibFolder(pod),
};
};
const scanner = () => pods.map(loadLib);
return await new haystack_core_1.HNormalizer(scanner, logger).normalize();
}
exports.resolvePodsNamespace = resolvePodsNamespace;
/**
* Asynchronously load POD to lib defs.
*
* This will filter out any POD files that don't have any defs.
*
* @param podDir The POD file directory.
* @param podFilter If not empty, used to filter the POD files used.
* @returns A map of pods to lib defs dicts.
*/
async function getPodToLibDefs(podDir, podFilter) {
const pods = await getPods(podDir, podFilter);
const map = new Map();
for (const pod of pods) {
for (const ext of DEF_FILE_EXTS) {
const dicts = readDicts(pod, `lib/lib${ext}`);
if (dicts?.length) {
map.set(pod, dicts[0]);
break;
}
}
}
return map;
}
/**
* Return an array of PODs.
*
* @param podDir The POD file directory.
* @param podFilter If non-empty, used to filter the POD files used.
* @returns An array of PODs.
*/
async function getPods(podDir, podFilter) {
let files = await readdirAsync(podDir);
files = files.filter((file) => file.toLowerCase().endsWith('.pod'));
const fileNames = files.map((file) => file.substring(0, file.length - 4));
const zips = files.map((file) => new adm_zip_1.default(path_1.default.join(podDir, file)));
return (fileNames
.map((fileName, i) => ({
name: fileName,
getAsset: (path) => {
try {
return zips[i].readAsText(path);
}
catch (err) {
return undefined;
}
},
listFiles: (path) => zips[i]
.getEntries()
.map((entry) => entry.entryName)
.filter((entryName) => entryName.startsWith(path)),
}))
// If specified, filter the POD files scanned. We have to do this for
// SkySpark since some of the def library creation is dynamic.
.filter((pod) => podFilter ? pod.name.startsWith(podFilter) : true));
}
/**
* Load the dicts from the POD lib folder.
*
* @param pod To load all the dicts from.
* @returns An array of def/defx dicts.
*/
function readDictsFromPodLibFolder(pod) {
const files = pod.listFiles('lib');
return files
.filter((path) => {
const lowerPath = path.toLowerCase();
return DEF_FILE_EXTS.some((ext) => lowerPath.endsWith(ext));
})
.map((path) => readDicts(pod, path) ?? [])
.reduce((dicts, prev) => dicts.concat(prev), []);
}
function readDicts(pod, path) {
const text = pod.getAsset(path);
let dicts;
if (text) {
const lowerPath = path.toLowerCase();
try {
if (lowerPath.endsWith('.trio')) {
dicts = new haystack_core_1.TrioReader(text).readAllDicts();
}
else if (lowerPath.endsWith('.hayson.json')) {
dicts = convertHaysonToDicts(JSON.parse(text));
}
else if (lowerPath.endsWith('.hayson.yaml') ||
lowerPath.endsWith('.hayson.yml')) {
dicts = [];
for (const decoded of yaml_1.default.parseAllDocuments(text).map((doc) => doc.toJSON())) {
dicts = dicts.concat(convertHaysonToDicts(decoded));
}
}
}
catch (error) {
throw new Error(`Error parsing '${pod.name}.pod:/${path}' - ${error}`);
}
}
return dicts;
}
function convertHaysonToDicts(hayson) {
const dicts = [];
function read(value) {
if (value) {
const hval = (0, haystack_core_1.makeValue)(value);
if ((0, haystack_core_1.valueIsKind)(hval, haystack_core_1.Kind.Dict)) {
dicts.push(hval);
}
}
}
if (Array.isArray(hayson)) {
hayson.forEach(read);
}
else {
read(hayson);
}
return dicts;
}