UNPKG

json-merger

Version:

Merge JSON (or YAML) files and objects with indicators like $import $remove $replace $merge etc

308 lines 12.8 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.SourceType = void 0; const path_1 = __importDefault(require("path")); const jsonpath_1 = __importDefault(require("jsonpath")); const json_ptr_1 = require("json-ptr"); const types_1 = require("./utils/types"); const Scope_1 = require("./Scope"); class Processor { constructor(_config, _dataLoader) { this._config = _config; this._dataLoader = _dataLoader; this._cache = []; this._enabledOperationNames = []; this._nameOperationMap = {}; this._operationNames = []; this._enabledOperationNames = this._operationNames; } merge(sources) { const scopeVariables = { $params: this._config.params, }; const scope = new Scope_1.GlobalScope(); this._enterScope(scope); let result = sources.reduce((target, source) => { if (source.type === 0) { target = this.mergeObject(source.object, target, scopeVariables); } else if (source.type === 1) { target = this.mergeFile(source.uri, target, scopeVariables); } return target; }, undefined); if (scope.hasRegisteredPhase("afterMerges")) { result = this.mergeObject(result, undefined, scopeVariables, "afterMerges"); } this._leaveScope(); return result; } mergeFile(uri, target, scopeVariables) { return this.loadAndProcessFileByRef(uri, target, scopeVariables, true); } mergeObject(source, target, scopeVariables, phase) { const scope = new Scope_1.RootMergeObjectScope(source, target, this.currentScope, scopeVariables, phase); this._enterScope(scope); let result = this.processSource(source, target); this._leaveScope(); if (scope.hasRegisteredPhase("afterMerge")) { result = this.mergeObject(result, target, scopeVariables, "afterMerge"); } return result; } addOperation(operation) { const name = operation.name(); this._nameOperationMap[name] = operation; this._operationNames.push(name); } addOperations(operations) { operations.forEach((operation) => this.addOperation(operation)); } enableOperations() { this._enabledOperationNames = this._operationNames; } disableOperations() { this._enabledOperationNames = []; } getKeyword(operationName) { return this._config.operationPrefix + operationName; } isKeyword(input) { if (!this.startsWithOperationPrefix(input)) { return false; } const name = this.stripOperationPrefix(input); return this._nameOperationMap[name] !== undefined; } isEscapedKeyword(input) { return this.isKeyword(this.stripOperationPrefix(input)); } stripOperationPrefix(input) { return input.substr(this._config.operationPrefix.length); } startsWithOperationPrefix(input) { const prefix = this._config.operationPrefix; return input.substr(0, prefix.length) === prefix; } getCurrentUri() { const scope = this.currentScope; if (scope.root && scope.root.sourceFilePath) { return scope.root.sourceFilePath; } else if (this._config.cwd !== "") { return path_1.default.join(this._config.cwd, "object.json"); } return path_1.default.join(process.cwd(), "object.json"); } loadFile(uri) { return this._dataLoader.load(uri, this.getCurrentUri()); } loadFileByRef(ref) { const [uri, pointer] = ref.split("#"); let result = this.loadFile(uri); if (pointer !== undefined) { result = this.resolveJsonPointer(result, pointer); } return result; } loadAndProcessFile(uri, target, scopeVariables, isRoot = false) { const currentUri = this.getCurrentUri(); const absoluteUri = this._dataLoader.toAbsoluteUri(uri, currentUri); let usedScopeVariables = scopeVariables; if (usedScopeVariables === undefined && this.currentScope) { usedScopeVariables = this.currentScope.localVariables; } const hashedScopeVariables = JSON.stringify(usedScopeVariables); const cacheItem = this._cache.filter((x) => x.absoluteUri === absoluteUri && x.target === target && x.hashedScopeVariables === hashedScopeVariables)[0]; if (cacheItem) { if (cacheItem.executeAfterMergesPhase) { this.currentScope.registerPhase("afterMerges"); } return cacheItem.result; } const source = this._dataLoader.load(absoluteUri, currentUri); scopeVariables = scopeVariables || {}; if (!scopeVariables.$params) { scopeVariables.$params = this.currentScope.scopeVariables.$params; } let scope; if (isRoot) { scope = new Scope_1.RootMergeFileScope(absoluteUri, source, target, this.currentScope, scopeVariables, this.currentScope.phase); } else { scope = new Scope_1.MergeFileScope(absoluteUri, source, target, this.currentScope, scopeVariables, this.currentScope.phase); } this._enterScope(scope); let result = this.processSource(source, target); if (scope.hasRegisteredPhase("afterMerge")) { const mergeObjectScope = new Scope_1.MergeObjectScope(result, undefined, scope, scopeVariables, "afterMerge"); this._enterScope(mergeObjectScope); result = this.processSource(result); this._leaveScope(); } this._leaveScope(); const executeAfterMergesPhase = scope.hasRegisteredPhase("afterMerges"); this._cache.push({ absoluteUri, target, hashedScopeVariables, result, executeAfterMergesPhase, }); return result; } loadAndProcessFileByRef(ref, target, scopeVariables, isRoot = false) { const [uri, pointer] = ref.split("#"); let result = this.loadAndProcessFile(uri, target, scopeVariables, isRoot); if (pointer !== undefined) { result = this.resolveJsonPointer(result, pointer); } return result; } processSourcePropertyInNewMergeObjectScope(sourceProperty, sourcePropertyName, targetProperty, scopeVariables) { const scope = new Scope_1.MergeObjectScope(sourceProperty, targetProperty, this.currentScope, scopeVariables); this._enterScope(scope); const result = this.processSourceProperty(sourceProperty, sourcePropertyName, targetProperty); this._leaveScope(); return result; } processSourcePropertyInNewScope(sourceProperty, sourcePropertyName, targetProperty, scopeVariables) { const scope = new Scope_1.Scope(this.currentScope, scopeVariables); this._enterScope(scope); const result = this.processSourceProperty(sourceProperty, sourcePropertyName, targetProperty); this._leaveScope(); return result; } processSourceProperty(sourceProperty, sourcePropertyName, targetProperty) { this.currentScope.enterProperty(sourcePropertyName); const modifiedSourceProperty = this.addDefaultArrayMergeStrategy(sourceProperty, targetProperty); const result = this.processSource(modifiedSourceProperty, targetProperty); this.currentScope.leaveProperty(); return result; } addDefaultArrayMergeStrategy(sourceProperty, targetProperty) { const isBothArray = Array.isArray(sourceProperty) && Array.isArray(targetProperty); if (isBothArray) { const keyword = this.getKeyword(this._config.defaultArrayMergeOperation); return { [keyword]: sourceProperty }; } return sourceProperty; } processSource(source, target) { if ((0, types_1.isObject)(source)) { return this._processObject(source, target); } else if (Array.isArray(source)) { return this._processArray(source, target); } return source; } _processObject(source, target) { for (let i = 0; i < this._enabledOperationNames.length; i++) { const name = this._enabledOperationNames[i]; const operation = this._nameOperationMap[name]; const keyword = this.getKeyword(name); if (source[keyword] !== undefined) { this.currentScope.enterProperty(keyword); const result = operation.processInObject(keyword, source, target); this.currentScope.leaveProperty(); return result; } } if (!(0, types_1.isObject)(target)) { target = {}; } const result = Object.assign({}, target); Object.keys(source).forEach((key) => { if (this.stripOperationPrefix(key) === "comment") { return; } const targetKey = this.isEscapedKeyword(key) ? this.stripOperationPrefix(key) : key; result[targetKey] = this.processSourceProperty(source[key], key, target[key]); if (typeof result[targetKey] === "undefined") { delete result[targetKey]; } }); return result; } _processArray(source, target) { target = (Array.isArray(target) ? target : []); let processResult = { resultArray: target.slice(), resultArrayIndex: -1, }; source.forEach((sourceItem, sourceItemIndex) => { this.currentScope.enterProperty(sourceItemIndex); processResult = this.processArrayItem(sourceItem, source, sourceItemIndex, processResult.resultArray, processResult.resultArrayIndex + 1, target); this.currentScope.leaveProperty(); }); return processResult.resultArray; } processArrayItem(source, sourceArray, sourceArrayIndex, resultArray, resultArrayIndex, target) { if ((0, types_1.isObject)(source)) { for (let i = 0; i < this._enabledOperationNames.length; i++) { const name = this._enabledOperationNames[i]; const operation = this._nameOperationMap[name]; const keyword = this.getKeyword(name); if (source[keyword] !== undefined) { this.currentScope.enterProperty(keyword); const result = operation.processInArray(keyword, source, sourceArray, sourceArrayIndex, resultArray, resultArrayIndex, target); this.currentScope.leaveProperty(); return result; } } } resultArray[resultArrayIndex] = this.processSource(source, resultArray[resultArrayIndex]); return { resultArray, resultArrayIndex }; } resolveJsonPointer(target, pointer) { let result; if (pointer === undefined || pointer === "/") { result = target; } else { result = json_ptr_1.JsonPointer.get(target, pointer); } if (result === undefined && this._config.errorOnRefNotFound) { throw new Error(`The JSON pointer "${pointer}" resolves to undefined. Set Config.errorOnRefNotFound to false to suppress this message`); } return result; } resolveJsonPath(target, path) { let result; if (path === undefined) { result = target; } else if ((0, types_1.isObject)(target) || Array.isArray(target)) { result = jsonpath_1.default.query(target, path); } if (this._config.errorOnRefNotFound && (result === undefined || result.length === 0)) { throw new Error(`The JSON path "${path}" resolves to undefined. Set Config.errorOnRefNotFound to false to suppress this message`); } return result; } _enterScope(scope) { this.currentScope = scope; return this.currentScope; } _leaveScope() { const currentScope = this.currentScope; this.currentScope = this.currentScope.parent; return currentScope; } } exports.default = Processor; var SourceType; (function (SourceType) { SourceType[SourceType["Object"] = 0] = "Object"; SourceType[SourceType["Uri"] = 1] = "Uri"; })(SourceType || (exports.SourceType = SourceType = {})); //# sourceMappingURL=Processor.js.map