| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163 | var List = require('../common/List');var hasOwnProperty = Object.prototype.hasOwnProperty;function isValidNumber(value) {    // Number.isInteger(value) && value >= 0    return (        typeof value === 'number' &&        isFinite(value) &&        Math.floor(value) === value &&        value >= 0    );}function isValidLocation(loc) {    return (        Boolean(loc) &&        isValidNumber(loc.offset) &&        isValidNumber(loc.line) &&        isValidNumber(loc.column)    );}function createNodeStructureChecker(type, fields) {    return function checkNode(node, warn) {        if (!node || node.constructor !== Object) {            return warn(node, 'Type of node should be an Object');        }        for (var key in node) {            var valid = true;            if (hasOwnProperty.call(node, key) === false) {                continue;            }            if (key === 'type') {                if (node.type !== type) {                    warn(node, 'Wrong node type `' + node.type + '`, expected `' + type + '`');                }            } else if (key === 'loc') {                if (node.loc === null) {                    continue;                } else if (node.loc && node.loc.constructor === Object) {                    if (typeof node.loc.source !== 'string') {                        key += '.source';                    } else if (!isValidLocation(node.loc.start)) {                        key += '.start';                    } else if (!isValidLocation(node.loc.end)) {                        key += '.end';                    } else {                        continue;                    }                }                valid = false;            } else if (fields.hasOwnProperty(key)) {                for (var i = 0, valid = false; !valid && i < fields[key].length; i++) {                    var fieldType = fields[key][i];                    switch (fieldType) {                        case String:                            valid = typeof node[key] === 'string';                            break;                        case Boolean:                            valid = typeof node[key] === 'boolean';                            break;                        case null:                            valid = node[key] === null;                            break;                        default:                            if (typeof fieldType === 'string') {                                valid = node[key] && node[key].type === fieldType;                            } else if (Array.isArray(fieldType)) {                                valid = node[key] instanceof List;                            }                    }                }            } else {                warn(node, 'Unknown field `' + key + '` for ' + type + ' node type');            }            if (!valid) {                warn(node, 'Bad value for `' + type + '.' + key + '`');            }        }        for (var key in fields) {            if (hasOwnProperty.call(fields, key) &&                hasOwnProperty.call(node, key) === false) {                warn(node, 'Field `' + type + '.' + key + '` is missed');            }        }    };}function processStructure(name, nodeType) {    var structure = nodeType.structure;    var fields = {        type: String,        loc: true    };    var docs = {        type: '"' + name + '"'    };    for (var key in structure) {        if (hasOwnProperty.call(structure, key) === false) {            continue;        }        var docsTypes = [];        var fieldTypes = fields[key] = Array.isArray(structure[key])            ? structure[key].slice()            : [structure[key]];        for (var i = 0; i < fieldTypes.length; i++) {            var fieldType = fieldTypes[i];            if (fieldType === String || fieldType === Boolean) {                docsTypes.push(fieldType.name);            } else if (fieldType === null) {                docsTypes.push('null');            } else if (typeof fieldType === 'string') {                docsTypes.push('<' + fieldType + '>');            } else if (Array.isArray(fieldType)) {                docsTypes.push('List'); // TODO: use type enum            } else {                throw new Error('Wrong value `' + fieldType + '` in `' + name + '.' + key + '` structure definition');            }        }        docs[key] = docsTypes.join(' | ');    }    return {        docs: docs,        check: createNodeStructureChecker(name, fields)    };}module.exports = {    getStructureFromConfig: function(config) {        var structure = {};        if (config.node) {            for (var name in config.node) {                if (hasOwnProperty.call(config.node, name)) {                    var nodeType = config.node[name];                    if (nodeType.structure) {                        structure[name] = processStructure(name, nodeType);                    } else {                        throw new Error('Missed `structure` field in `' + name + '` node type definition');                    }                }            }        }        return structure;    }};
 |