| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621 | /*--------------------------------------------------------------------------------------------- *  Copyright (c) Microsoft Corporation. All rights reserved. *  Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/'use strict';import { createScanner } from './scanner';var ParseOptions;(function (ParseOptions) {    ParseOptions.DEFAULT = {        allowTrailingComma: false    };})(ParseOptions || (ParseOptions = {}));/** * For a given offset, evaluate the location in the JSON document. Each segment in the location path is either a property name or an array index. */export function getLocation(text, position) {    var segments = []; // strings or numbers    var earlyReturnException = new Object();    var previousNode = undefined;    var previousNodeInst = {        value: {},        offset: 0,        length: 0,        type: 'object',        parent: undefined    };    var isAtPropertyKey = false;    function setPreviousNode(value, offset, length, type) {        previousNodeInst.value = value;        previousNodeInst.offset = offset;        previousNodeInst.length = length;        previousNodeInst.type = type;        previousNodeInst.colonOffset = undefined;        previousNode = previousNodeInst;    }    try {        visit(text, {            onObjectBegin: function (offset, length) {                if (position <= offset) {                    throw earlyReturnException;                }                previousNode = undefined;                isAtPropertyKey = position > offset;                segments.push(''); // push a placeholder (will be replaced)            },            onObjectProperty: function (name, offset, length) {                if (position < offset) {                    throw earlyReturnException;                }                setPreviousNode(name, offset, length, 'property');                segments[segments.length - 1] = name;                if (position <= offset + length) {                    throw earlyReturnException;                }            },            onObjectEnd: function (offset, length) {                if (position <= offset) {                    throw earlyReturnException;                }                previousNode = undefined;                segments.pop();            },            onArrayBegin: function (offset, length) {                if (position <= offset) {                    throw earlyReturnException;                }                previousNode = undefined;                segments.push(0);            },            onArrayEnd: function (offset, length) {                if (position <= offset) {                    throw earlyReturnException;                }                previousNode = undefined;                segments.pop();            },            onLiteralValue: function (value, offset, length) {                if (position < offset) {                    throw earlyReturnException;                }                setPreviousNode(value, offset, length, getNodeType(value));                if (position <= offset + length) {                    throw earlyReturnException;                }            },            onSeparator: function (sep, offset, length) {                if (position <= offset) {                    throw earlyReturnException;                }                if (sep === ':' && previousNode && previousNode.type === 'property') {                    previousNode.colonOffset = offset;                    isAtPropertyKey = false;                    previousNode = undefined;                }                else if (sep === ',') {                    var last = segments[segments.length - 1];                    if (typeof last === 'number') {                        segments[segments.length - 1] = last + 1;                    }                    else {                        isAtPropertyKey = true;                        segments[segments.length - 1] = '';                    }                    previousNode = undefined;                }            }        });    }    catch (e) {        if (e !== earlyReturnException) {            throw e;        }    }    return {        path: segments,        previousNode: previousNode,        isAtPropertyKey: isAtPropertyKey,        matches: function (pattern) {            var k = 0;            for (var i = 0; k < pattern.length && i < segments.length; i++) {                if (pattern[k] === segments[i] || pattern[k] === '*') {                    k++;                }                else if (pattern[k] !== '**') {                    return false;                }            }            return k === pattern.length;        }    };}/** * Parses the given text and returns the object the JSON content represents. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result. * Therefore always check the errors list to find out if the input was valid. */export function parse(text, errors, options) {    if (errors === void 0) { errors = []; }    if (options === void 0) { options = ParseOptions.DEFAULT; }    var currentProperty = null;    var currentParent = [];    var previousParents = [];    function onValue(value) {        if (Array.isArray(currentParent)) {            currentParent.push(value);        }        else if (currentProperty !== null) {            currentParent[currentProperty] = value;        }    }    var visitor = {        onObjectBegin: function () {            var object = {};            onValue(object);            previousParents.push(currentParent);            currentParent = object;            currentProperty = null;        },        onObjectProperty: function (name) {            currentProperty = name;        },        onObjectEnd: function () {            currentParent = previousParents.pop();        },        onArrayBegin: function () {            var array = [];            onValue(array);            previousParents.push(currentParent);            currentParent = array;            currentProperty = null;        },        onArrayEnd: function () {            currentParent = previousParents.pop();        },        onLiteralValue: onValue,        onError: function (error, offset, length) {            errors.push({ error: error, offset: offset, length: length });        }    };    visit(text, visitor, options);    return currentParent[0];}/** * Parses the given text and returns a tree representation the JSON content. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result. */export function parseTree(text, errors, options) {    if (errors === void 0) { errors = []; }    if (options === void 0) { options = ParseOptions.DEFAULT; }    var currentParent = { type: 'array', offset: -1, length: -1, children: [], parent: undefined }; // artificial root    function ensurePropertyComplete(endOffset) {        if (currentParent.type === 'property') {            currentParent.length = endOffset - currentParent.offset;            currentParent = currentParent.parent;        }    }    function onValue(valueNode) {        currentParent.children.push(valueNode);        return valueNode;    }    var visitor = {        onObjectBegin: function (offset) {            currentParent = onValue({ type: 'object', offset: offset, length: -1, parent: currentParent, children: [] });        },        onObjectProperty: function (name, offset, length) {            currentParent = onValue({ type: 'property', offset: offset, length: -1, parent: currentParent, children: [] });            currentParent.children.push({ type: 'string', value: name, offset: offset, length: length, parent: currentParent });        },        onObjectEnd: function (offset, length) {            ensurePropertyComplete(offset + length); // in case of a missing value for a property: make sure property is complete            currentParent.length = offset + length - currentParent.offset;            currentParent = currentParent.parent;            ensurePropertyComplete(offset + length);        },        onArrayBegin: function (offset, length) {            currentParent = onValue({ type: 'array', offset: offset, length: -1, parent: currentParent, children: [] });        },        onArrayEnd: function (offset, length) {            currentParent.length = offset + length - currentParent.offset;            currentParent = currentParent.parent;            ensurePropertyComplete(offset + length);        },        onLiteralValue: function (value, offset, length) {            onValue({ type: getNodeType(value), offset: offset, length: length, parent: currentParent, value: value });            ensurePropertyComplete(offset + length);        },        onSeparator: function (sep, offset, length) {            if (currentParent.type === 'property') {                if (sep === ':') {                    currentParent.colonOffset = offset;                }                else if (sep === ',') {                    ensurePropertyComplete(offset);                }            }        },        onError: function (error, offset, length) {            errors.push({ error: error, offset: offset, length: length });        }    };    visit(text, visitor, options);    var result = currentParent.children[0];    if (result) {        delete result.parent;    }    return result;}/** * Finds the node at the given path in a JSON DOM. */export function findNodeAtLocation(root, path) {    if (!root) {        return undefined;    }    var node = root;    for (var _i = 0, path_1 = path; _i < path_1.length; _i++) {        var segment = path_1[_i];        if (typeof segment === 'string') {            if (node.type !== 'object' || !Array.isArray(node.children)) {                return undefined;            }            var found = false;            for (var _a = 0, _b = node.children; _a < _b.length; _a++) {                var propertyNode = _b[_a];                if (Array.isArray(propertyNode.children) && propertyNode.children[0].value === segment) {                    node = propertyNode.children[1];                    found = true;                    break;                }            }            if (!found) {                return undefined;            }        }        else {            var index = segment;            if (node.type !== 'array' || index < 0 || !Array.isArray(node.children) || index >= node.children.length) {                return undefined;            }            node = node.children[index];        }    }    return node;}/** * Gets the JSON path of the given JSON DOM node */export function getNodePath(node) {    if (!node.parent || !node.parent.children) {        return [];    }    var path = getNodePath(node.parent);    if (node.parent.type === 'property') {        var key = node.parent.children[0].value;        path.push(key);    }    else if (node.parent.type === 'array') {        var index = node.parent.children.indexOf(node);        if (index !== -1) {            path.push(index);        }    }    return path;}/** * Evaluates the JavaScript object of the given JSON DOM node */export function getNodeValue(node) {    switch (node.type) {        case 'array':            return node.children.map(getNodeValue);        case 'object':            var obj = Object.create(null);            for (var _i = 0, _a = node.children; _i < _a.length; _i++) {                var prop = _a[_i];                var valueNode = prop.children[1];                if (valueNode) {                    obj[prop.children[0].value] = getNodeValue(valueNode);                }            }            return obj;        case 'null':        case 'string':        case 'number':        case 'boolean':            return node.value;        default:            return undefined;    }}export function contains(node, offset, includeRightBound) {    if (includeRightBound === void 0) { includeRightBound = false; }    return (offset >= node.offset && offset < (node.offset + node.length)) || includeRightBound && (offset === (node.offset + node.length));}/** * Finds the most inner node at the given offset. If includeRightBound is set, also finds nodes that end at the given offset. */export function findNodeAtOffset(node, offset, includeRightBound) {    if (includeRightBound === void 0) { includeRightBound = false; }    if (contains(node, offset, includeRightBound)) {        var children = node.children;        if (Array.isArray(children)) {            for (var i = 0; i < children.length && children[i].offset <= offset; i++) {                var item = findNodeAtOffset(children[i], offset, includeRightBound);                if (item) {                    return item;                }            }        }        return node;    }    return undefined;}/** * Parses the given text and invokes the visitor functions for each object, array and literal reached. */export function visit(text, visitor, options) {    if (options === void 0) { options = ParseOptions.DEFAULT; }    var _scanner = createScanner(text, false);    function toNoArgVisit(visitFunction) {        return visitFunction ? function () { return visitFunction(_scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter()); } : function () { return true; };    }    function toOneArgVisit(visitFunction) {        return visitFunction ? function (arg) { return visitFunction(arg, _scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter()); } : function () { return true; };    }    var onObjectBegin = toNoArgVisit(visitor.onObjectBegin), onObjectProperty = toOneArgVisit(visitor.onObjectProperty), onObjectEnd = toNoArgVisit(visitor.onObjectEnd), onArrayBegin = toNoArgVisit(visitor.onArrayBegin), onArrayEnd = toNoArgVisit(visitor.onArrayEnd), onLiteralValue = toOneArgVisit(visitor.onLiteralValue), onSeparator = toOneArgVisit(visitor.onSeparator), onComment = toNoArgVisit(visitor.onComment), onError = toOneArgVisit(visitor.onError);    var disallowComments = options && options.disallowComments;    var allowTrailingComma = options && options.allowTrailingComma;    function scanNext() {        while (true) {            var token = _scanner.scan();            switch (_scanner.getTokenError()) {                case 4 /* InvalidUnicode */:                    handleError(14 /* InvalidUnicode */);                    break;                case 5 /* InvalidEscapeCharacter */:                    handleError(15 /* InvalidEscapeCharacter */);                    break;                case 3 /* UnexpectedEndOfNumber */:                    handleError(13 /* UnexpectedEndOfNumber */);                    break;                case 1 /* UnexpectedEndOfComment */:                    if (!disallowComments) {                        handleError(11 /* UnexpectedEndOfComment */);                    }                    break;                case 2 /* UnexpectedEndOfString */:                    handleError(12 /* UnexpectedEndOfString */);                    break;                case 6 /* InvalidCharacter */:                    handleError(16 /* InvalidCharacter */);                    break;            }            switch (token) {                case 12 /* LineCommentTrivia */:                case 13 /* BlockCommentTrivia */:                    if (disallowComments) {                        handleError(10 /* InvalidCommentToken */);                    }                    else {                        onComment();                    }                    break;                case 16 /* Unknown */:                    handleError(1 /* InvalidSymbol */);                    break;                case 15 /* Trivia */:                case 14 /* LineBreakTrivia */:                    break;                default:                    return token;            }        }    }    function handleError(error, skipUntilAfter, skipUntil) {        if (skipUntilAfter === void 0) { skipUntilAfter = []; }        if (skipUntil === void 0) { skipUntil = []; }        onError(error);        if (skipUntilAfter.length + skipUntil.length > 0) {            var token = _scanner.getToken();            while (token !== 17 /* EOF */) {                if (skipUntilAfter.indexOf(token) !== -1) {                    scanNext();                    break;                }                else if (skipUntil.indexOf(token) !== -1) {                    break;                }                token = scanNext();            }        }    }    function parseString(isValue) {        var value = _scanner.getTokenValue();        if (isValue) {            onLiteralValue(value);        }        else {            onObjectProperty(value);        }        scanNext();        return true;    }    function parseLiteral() {        switch (_scanner.getToken()) {            case 11 /* NumericLiteral */:                var tokenValue = _scanner.getTokenValue();                var value = Number(tokenValue);                if (isNaN(value)) {                    handleError(2 /* InvalidNumberFormat */);                    value = 0;                }                onLiteralValue(value);                break;            case 7 /* NullKeyword */:                onLiteralValue(null);                break;            case 8 /* TrueKeyword */:                onLiteralValue(true);                break;            case 9 /* FalseKeyword */:                onLiteralValue(false);                break;            default:                return false;        }        scanNext();        return true;    }    function parseProperty() {        if (_scanner.getToken() !== 10 /* StringLiteral */) {            handleError(3 /* PropertyNameExpected */, [], [2 /* CloseBraceToken */, 5 /* CommaToken */]);            return false;        }        parseString(false);        if (_scanner.getToken() === 6 /* ColonToken */) {            onSeparator(':');            scanNext(); // consume colon            if (!parseValue()) {                handleError(4 /* ValueExpected */, [], [2 /* CloseBraceToken */, 5 /* CommaToken */]);            }        }        else {            handleError(5 /* ColonExpected */, [], [2 /* CloseBraceToken */, 5 /* CommaToken */]);        }        return true;    }    function parseObject() {        onObjectBegin();        scanNext(); // consume open brace        var needsComma = false;        while (_scanner.getToken() !== 2 /* CloseBraceToken */ && _scanner.getToken() !== 17 /* EOF */) {            if (_scanner.getToken() === 5 /* CommaToken */) {                if (!needsComma) {                    handleError(4 /* ValueExpected */, [], []);                }                onSeparator(',');                scanNext(); // consume comma                if (_scanner.getToken() === 2 /* CloseBraceToken */ && allowTrailingComma) {                    break;                }            }            else if (needsComma) {                handleError(6 /* CommaExpected */, [], []);            }            if (!parseProperty()) {                handleError(4 /* ValueExpected */, [], [2 /* CloseBraceToken */, 5 /* CommaToken */]);            }            needsComma = true;        }        onObjectEnd();        if (_scanner.getToken() !== 2 /* CloseBraceToken */) {            handleError(7 /* CloseBraceExpected */, [2 /* CloseBraceToken */], []);        }        else {            scanNext(); // consume close brace        }        return true;    }    function parseArray() {        onArrayBegin();        scanNext(); // consume open bracket        var needsComma = false;        while (_scanner.getToken() !== 4 /* CloseBracketToken */ && _scanner.getToken() !== 17 /* EOF */) {            if (_scanner.getToken() === 5 /* CommaToken */) {                if (!needsComma) {                    handleError(4 /* ValueExpected */, [], []);                }                onSeparator(',');                scanNext(); // consume comma                if (_scanner.getToken() === 4 /* CloseBracketToken */ && allowTrailingComma) {                    break;                }            }            else if (needsComma) {                handleError(6 /* CommaExpected */, [], []);            }            if (!parseValue()) {                handleError(4 /* ValueExpected */, [], [4 /* CloseBracketToken */, 5 /* CommaToken */]);            }            needsComma = true;        }        onArrayEnd();        if (_scanner.getToken() !== 4 /* CloseBracketToken */) {            handleError(8 /* CloseBracketExpected */, [4 /* CloseBracketToken */], []);        }        else {            scanNext(); // consume close bracket        }        return true;    }    function parseValue() {        switch (_scanner.getToken()) {            case 3 /* OpenBracketToken */:                return parseArray();            case 1 /* OpenBraceToken */:                return parseObject();            case 10 /* StringLiteral */:                return parseString(true);            default:                return parseLiteral();        }    }    scanNext();    if (_scanner.getToken() === 17 /* EOF */) {        if (options.allowEmptyContent) {            return true;        }        handleError(4 /* ValueExpected */, [], []);        return false;    }    if (!parseValue()) {        handleError(4 /* ValueExpected */, [], []);        return false;    }    if (_scanner.getToken() !== 17 /* EOF */) {        handleError(9 /* EndOfFileExpected */, [], []);    }    return true;}/** * Takes JSON with JavaScript-style comments and remove * them. Optionally replaces every none-newline character * of comments with a replaceCharacter */export function stripComments(text, replaceCh) {    var _scanner = createScanner(text), parts = [], kind, offset = 0, pos;    do {        pos = _scanner.getPosition();        kind = _scanner.scan();        switch (kind) {            case 12 /* LineCommentTrivia */:            case 13 /* BlockCommentTrivia */:            case 17 /* EOF */:                if (offset !== pos) {                    parts.push(text.substring(offset, pos));                }                if (replaceCh !== undefined) {                    parts.push(_scanner.getTokenValue().replace(/[^\r\n]/g, replaceCh));                }                offset = _scanner.getPosition();                break;        }    } while (kind !== 17 /* EOF */);    return parts.join('');}export function getNodeType(value) {    switch (typeof value) {        case 'boolean': return 'boolean';        case 'number': return 'number';        case 'string': return 'string';        case 'object': {            if (!value) {                return 'null';            }            else if (Array.isArray(value)) {                return 'array';            }            return 'object';        }        default: return 'null';    }}
 |