UNPKG

yini-parser

Version:

Node.js parser for YINI — a clean, structured INI alternative with types, simple section nesting, comments, and strict mode.

186 lines (185 loc) 9.48 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.constructFinalObject = void 0; const env_1 = require("../config/env"); const print_1 = require("../utils/print"); /** * Construct the final result of a JavaScript Object. */ const constructFinalObject = (syntaxTreeC, errorHandler) => { (0, print_1.debugPrint)('-> constructFinalObject(..)'); const bulder = new Builder(syntaxTreeC, errorHandler); if ((0, env_1.isDebug)()) { console.log('Argument, syntaxTreeC:'); (0, print_1.printObject)(syntaxTreeC); } const jsObject = bulder.doCheckAndBuild(); (0, print_1.debugPrint)('<- About to leave constructFinalObject(..)'); if ((0, env_1.isDebug)()) { console.log('Returning, jsObject:'); (0, print_1.printObject)(syntaxTreeC); } return jsObject; }; exports.constructFinalObject = constructFinalObject; class Builder { constructor(syntaxTreeC, errorHandler) { (0, print_1.debugPrint)('-> Builder: constructor(..)'); this.syntaxTreeC = syntaxTreeC; this.errorHandler = errorHandler; } doCheckAndBuild() { (0, print_1.debugPrint)('-> Builder: doCheckAndBuild(..)'); if (!this.syntaxTreeC) { // Note, after pushing processing may continue or exit, depending on the error and/or the bail threshold. this.errorHandler.pushOrBail(null, 'Fatal-Error', 'SyntaxTreeC is undefined', 'This is most likely caused by an internal error somewhere. The process cannot recover fully from this, sorry.'); } const fullSubTreeList = this.buildFullSubTrees(this.syntaxTreeC); const jsObject = this.buildObjectFromList(fullSubTreeList); return jsObject; } buildFullSubTrees(syntaxTreeC) { (0, print_1.debugPrint)('-> Builder: buildFullSubTrees(..)'); (0, env_1.isDebug)() && console.log(); const fullSubTreeList = []; // List of FULL sub-trees. // Current Working Full Sub-Tree (starting at level 1). let workingFullSubTree = syntaxTreeC._syntaxTree[0]; // (!) Any tree MUST START at level 1. (0, print_1.debugPrint)(`Setted new workingFullSubTree, from syntaxTreeC._syntaxTree[0]`); const len = syntaxTreeC._syntaxTree.length; for (let i = 1; i < len; i++) { const currentChainC = syntaxTreeC._syntaxTree[i]; const level = currentChainC.originLevel; const nestingIndex = level - 1; // For debugging purposes. const chain = currentChainC.chain; // For debugging purposes. (0, print_1.debugPrint)(`Got new chain from syntaxTreeC._syntaxTree[${i}] to be mounted onto parent...`); (0, print_1.debugPrint)('* level: ' + level + ' (i=' + i + '), chain: ' + chain); if (level === 1) { (0, print_1.debugPrint)('HIT - Detected that currentChain starts with level 1'); fullSubTreeList.push(workingFullSubTree); (0, print_1.debugPrint)('The workingFullSubTree is finished, pushed it to the list.'); workingFullSubTree = syntaxTreeC._syntaxTree[i]; // (!) The tree MUST START at level 1. (0, print_1.debugPrint)(`Setted new workingFullSubTree, from syntaxTreeC._syntaxTree[${i}]`); } else { (0, print_1.debugPrint)('About to mount currentChain onto workingFullSubTree at correct level...'); workingFullSubTree = this.mountChainOntoLevel(currentChainC, workingFullSubTree); } (0, print_1.debugPrint)(); } fullSubTreeList.push(workingFullSubTree); if ((0, env_1.isDebug)()) { console.log(); console.log('--- fullSubTreeList: (list of FULL sub-trees.) -------'); (0, print_1.printObject)(fullSubTreeList); console.log(); } return fullSubTreeList; } mountChainOntoLevel(chainC, workingSubTree) { (0, print_1.debugPrint)('-> Builder: mountChainOntoLevel(..)'); if ((0, env_1.isDebug)()) { (0, print_1.printObject)(chainC); } if (chainC.originLevel > 1) { // NOP } else { // Note, after pushing processing may continue or exit, depending on the error and/or the bail threshold. this.errorHandler.pushOrBail(null, 'Fatal-Error', 'Internal-Error: Detected incorrect chain in mountChainOntoLevel(..), start section has level: ' + chainC.originLevel, 'The (chain) must start with a section level higher than 1', '' + (0, print_1.printObject)(chainC)); } if (workingSubTree.originLevel != 1) { // Note, after pushing processing may continue or exit, depending on the error and/or the bail threshold. this.errorHandler.pushOrBail(null, 'Fatal-Error', 'Internal-Error: Detected incorrect full sub-tree in mountChainOntoLevel(..), start section has level: ' + chainC.originLevel, 'A full sub-tree (chain) must start with a section at level 1', '' + (0, print_1.printObject)(chainC)); } const chain = chainC.chain; const targetLevel = chainC.originLevel; if ((0, env_1.isDebug)()) { (0, print_1.debugPrint)('Target level = ' + targetLevel); (0, print_1.debugPrint)(`The chain to mount: (onto level: ${targetLevel})`); (0, print_1.printObject)(chain); (0, print_1.debugPrint)('--- workingFullSubTree: -------'); (0, print_1.debugPrint)('Before mounting onto workingSubTree.chain:'); (0, print_1.printObject)(workingSubTree.chain); } (0, print_1.debugPrint)('Mount currentChain onto workingFullSubTree.'); workingSubTree.chain = mountObjectAtLevel(workingSubTree.chain, chain, targetLevel, this.errorHandler); if ((0, env_1.isDebug)()) { (0, print_1.debugPrint)('After mounting onto workingSubTree.chain:'); (0, print_1.printObject)(workingSubTree.chain); (0, print_1.debugPrint)('----------'); } (0, print_1.debugPrint)('<- Builder: mountChainOntoLevel(..)'); return workingSubTree; } // Contruct the final JS object from the list of full sub-trees. buildObjectFromList(fullSubTreeList) { (0, print_1.debugPrint)('-> Builder: buildObjectFromList(..)'); const jsObject = {}; for (const chainC of fullSubTreeList) { if (chainC.originLevel === 1) { if ((0, env_1.isDebug)()) { console.log('About to assign chainC.chain:'); console.log(chainC.chain); } Object.assign(jsObject, chainC.chain); if ((0, env_1.isDebug)()) { console.log('After, jsObject:'); console.log(jsObject); } } else { // Note, after pushing processing may continue or exit, depending on the error and/or the bail threshold. this.errorHandler.pushOrBail(null, 'Fatal-Error', 'Internal-Error: Detected incorrect full sub-tree in buildObjectFromList(..), start section has level: ' + chainC.originLevel, 'A full sub-tree (chain) must start with a section at level 1', '' + (0, print_1.printObject)(chainC)); } } return jsObject; } } /** * Mounts objectDest onto the first object at the given depth (level is * 1-based) in objectSrc. * @return Returns a new object without mutating input objects. */ const mountObjectAtLevel = (objectSrc, objectDest, level, errorHandler) => { // Deep copy to avoid mutating the input. const result = JSON.parse(JSON.stringify(objectSrc)); let current = result; let currentLevel = 1; // Traverse down the first object path until the desired level. while (currentLevel < level) { // Get all keys in current object. const keys = Object.keys(current); // Find first key where value is a plain object (not array). const nextKey = keys.find((key) => current[key] && typeof current[key] === 'object' && !Array.isArray(current[key])); if (!nextKey) { // Could not reach the specified depth. return result; } current = current[nextKey]; currentLevel++; } (0, print_1.debugPrint)('--------'); (0, print_1.debugPrint)(' current = ' + (0, print_1.toPrettyJSON)(current)); const [firstKey] = Object.keys(objectDest); (0, print_1.debugPrint)('objectDest = ' + firstKey); if (Object.prototype.hasOwnProperty.call(current, firstKey)) { //@todo Add metadata with line number, onto chainC, so can use line number in error reporting (0, print_1.debugPrint)(`(!) sectionName already exist, name: "${firstKey}", in: `); (0, print_1.debugPrint)((0, print_1.toPrettyJSON)(current)); // Note, after pushing processing may continue or exit, depending on the error and/or the bail threshold. errorHandler.pushOrBail(null, 'Syntax-Error', 'Section name already exists', 'Cannot redefine section name: "' + firstKey + '" at level ' + currentLevel + '.'); return current; } // Mount objectDest onto the object at the required level. Object.assign(current, objectDest); return result; };