@angular-devkit/core
Version:
Angular DevKit - Core Utility Library
147 lines (146 loc) • 6.5 kB
JavaScript
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.visitJson = visitJson;
exports.visitJsonSchema = visitJsonSchema;
const rxjs_1 = require("rxjs");
const pointer_1 = require("./pointer");
function _getObjectSubSchema(schema, key) {
if (typeof schema !== 'object' || schema === null) {
return undefined;
}
// Is it an object schema?
if (typeof schema.properties == 'object' || schema.type == 'object') {
if (typeof schema.properties == 'object' &&
typeof schema.properties[key] == 'object') {
return schema.properties[key];
}
if (typeof schema.additionalProperties == 'object') {
return schema.additionalProperties;
}
return undefined;
}
// Is it an array schema?
if (typeof schema.items == 'object' || schema.type == 'array') {
return typeof schema.items == 'object' ? schema.items : undefined;
}
return undefined;
}
function _visitJsonRecursive(json, visitor, ptr, schema, refResolver, context, root) {
if (schema === true || schema === false) {
// There's no schema definition, so just visit the JSON recursively.
schema = undefined;
}
// eslint-disable-next-line no-prototype-builtins
if (schema && schema.hasOwnProperty('$ref') && typeof schema['$ref'] == 'string') {
if (refResolver) {
const resolved = refResolver(schema['$ref'], context);
schema = resolved.schema;
context = resolved.context;
}
}
const value = visitor(json, ptr, schema, root);
return ((0, rxjs_1.isObservable)(value) ? value : (0, rxjs_1.of)(value)).pipe((0, rxjs_1.concatMap)((value) => {
if (Array.isArray(value)) {
return (0, rxjs_1.concat)((0, rxjs_1.from)(value).pipe((0, rxjs_1.mergeMap)((item, i) => {
return _visitJsonRecursive(item, visitor, (0, pointer_1.joinJsonPointer)(ptr, '' + i), _getObjectSubSchema(schema, '' + i), refResolver, context, root || value).pipe((0, rxjs_1.tap)((x) => (value[i] = x)));
}), (0, rxjs_1.ignoreElements)()), (0, rxjs_1.of)(value));
}
else if (typeof value == 'object' && value !== null) {
return (0, rxjs_1.concat)((0, rxjs_1.from)(Object.getOwnPropertyNames(value)).pipe((0, rxjs_1.mergeMap)((key) => {
return _visitJsonRecursive(value[key], visitor, (0, pointer_1.joinJsonPointer)(ptr, key), _getObjectSubSchema(schema, key), refResolver, context, root || value).pipe((0, rxjs_1.tap)((x) => {
const descriptor = Object.getOwnPropertyDescriptor(value, key);
if (descriptor && descriptor.writable && value[key] !== x) {
value[key] = x;
}
}));
}), (0, rxjs_1.ignoreElements)()), (0, rxjs_1.of)(value));
}
else {
return (0, rxjs_1.of)(value);
}
}));
}
/**
* Visit all the properties in a JSON object, allowing to transform them. It supports calling
* properties synchronously or asynchronously (through Observables).
* The original object can be mutated or replaced entirely. In case where it's replaced, the new
* value is returned. When it's mutated though the original object will be changed.
*
* Please note it is possible to have an infinite loop here (which will result in a stack overflow)
* if you return 2 objects that references each others (or the same object all the time).
*
* @param {JsonValue} json The Json value to visit.
* @param {JsonVisitor} visitor A function that will be called on every items.
* @param {JsonObject} schema A JSON schema to pass through to the visitor (where possible).
* @param refResolver a function to resolve references in the schema.
* @returns {Observable< | undefined>} The observable of the new root, if the root changed.
*/
function visitJson(json, visitor, schema, refResolver, context) {
return _visitJsonRecursive(json, visitor, (0, pointer_1.buildJsonPointer)([]), schema, refResolver, context);
}
function visitJsonSchema(schema, visitor) {
if (schema === false || schema === true) {
// Nothing to visit.
return;
}
const keywords = {
additionalItems: true,
items: true,
contains: true,
additionalProperties: true,
propertyNames: true,
not: true,
};
const arrayKeywords = {
items: true,
allOf: true,
anyOf: true,
oneOf: true,
};
const propsKeywords = {
definitions: true,
properties: true,
patternProperties: true,
additionalProperties: true,
dependencies: true,
items: true,
};
function _traverse(schema, jsonPtr, rootSchema, parentSchema, keyIndex) {
if (schema && typeof schema == 'object' && !Array.isArray(schema)) {
visitor(schema, jsonPtr, parentSchema, keyIndex);
for (const key of Object.keys(schema)) {
const sch = schema[key];
if (key in propsKeywords) {
if (sch && typeof sch == 'object') {
for (const prop of Object.keys(sch)) {
_traverse(sch[prop], (0, pointer_1.joinJsonPointer)(jsonPtr, key, prop), rootSchema, schema, prop);
}
}
}
else if (key in keywords) {
_traverse(sch, (0, pointer_1.joinJsonPointer)(jsonPtr, key), rootSchema, schema, key);
}
else if (key in arrayKeywords) {
if (Array.isArray(sch)) {
for (let i = 0; i < sch.length; i++) {
_traverse(sch[i], (0, pointer_1.joinJsonPointer)(jsonPtr, key, '' + i), rootSchema, sch, '' + i);
}
}
}
else if (Array.isArray(sch)) {
for (let i = 0; i < sch.length; i++) {
_traverse(sch[i], (0, pointer_1.joinJsonPointer)(jsonPtr, key, '' + i), rootSchema, sch, '' + i);
}
}
}
}
}
_traverse(schema, (0, pointer_1.buildJsonPointer)([]), schema);
}
;