[d565449] | 1 | var List = require('../common/List');
|
---|
| 2 | var hasOwnProperty = Object.prototype.hasOwnProperty;
|
---|
| 3 |
|
---|
| 4 | function isValidNumber(value) {
|
---|
| 5 | // Number.isInteger(value) && value >= 0
|
---|
| 6 | return (
|
---|
| 7 | typeof value === 'number' &&
|
---|
| 8 | isFinite(value) &&
|
---|
| 9 | Math.floor(value) === value &&
|
---|
| 10 | value >= 0
|
---|
| 11 | );
|
---|
| 12 | }
|
---|
| 13 |
|
---|
| 14 | function isValidLocation(loc) {
|
---|
| 15 | return (
|
---|
| 16 | Boolean(loc) &&
|
---|
| 17 | isValidNumber(loc.offset) &&
|
---|
| 18 | isValidNumber(loc.line) &&
|
---|
| 19 | isValidNumber(loc.column)
|
---|
| 20 | );
|
---|
| 21 | }
|
---|
| 22 |
|
---|
| 23 | function createNodeStructureChecker(type, fields) {
|
---|
| 24 | return function checkNode(node, warn) {
|
---|
| 25 | if (!node || node.constructor !== Object) {
|
---|
| 26 | return warn(node, 'Type of node should be an Object');
|
---|
| 27 | }
|
---|
| 28 |
|
---|
| 29 | for (var key in node) {
|
---|
| 30 | var valid = true;
|
---|
| 31 |
|
---|
| 32 | if (hasOwnProperty.call(node, key) === false) {
|
---|
| 33 | continue;
|
---|
| 34 | }
|
---|
| 35 |
|
---|
| 36 | if (key === 'type') {
|
---|
| 37 | if (node.type !== type) {
|
---|
| 38 | warn(node, 'Wrong node type `' + node.type + '`, expected `' + type + '`');
|
---|
| 39 | }
|
---|
| 40 | } else if (key === 'loc') {
|
---|
| 41 | if (node.loc === null) {
|
---|
| 42 | continue;
|
---|
| 43 | } else if (node.loc && node.loc.constructor === Object) {
|
---|
| 44 | if (typeof node.loc.source !== 'string') {
|
---|
| 45 | key += '.source';
|
---|
| 46 | } else if (!isValidLocation(node.loc.start)) {
|
---|
| 47 | key += '.start';
|
---|
| 48 | } else if (!isValidLocation(node.loc.end)) {
|
---|
| 49 | key += '.end';
|
---|
| 50 | } else {
|
---|
| 51 | continue;
|
---|
| 52 | }
|
---|
| 53 | }
|
---|
| 54 |
|
---|
| 55 | valid = false;
|
---|
| 56 | } else if (fields.hasOwnProperty(key)) {
|
---|
| 57 | for (var i = 0, valid = false; !valid && i < fields[key].length; i++) {
|
---|
| 58 | var fieldType = fields[key][i];
|
---|
| 59 |
|
---|
| 60 | switch (fieldType) {
|
---|
| 61 | case String:
|
---|
| 62 | valid = typeof node[key] === 'string';
|
---|
| 63 | break;
|
---|
| 64 |
|
---|
| 65 | case Boolean:
|
---|
| 66 | valid = typeof node[key] === 'boolean';
|
---|
| 67 | break;
|
---|
| 68 |
|
---|
| 69 | case null:
|
---|
| 70 | valid = node[key] === null;
|
---|
| 71 | break;
|
---|
| 72 |
|
---|
| 73 | default:
|
---|
| 74 | if (typeof fieldType === 'string') {
|
---|
| 75 | valid = node[key] && node[key].type === fieldType;
|
---|
| 76 | } else if (Array.isArray(fieldType)) {
|
---|
| 77 | valid = node[key] instanceof List;
|
---|
| 78 | }
|
---|
| 79 | }
|
---|
| 80 | }
|
---|
| 81 | } else {
|
---|
| 82 | warn(node, 'Unknown field `' + key + '` for ' + type + ' node type');
|
---|
| 83 | }
|
---|
| 84 |
|
---|
| 85 | if (!valid) {
|
---|
| 86 | warn(node, 'Bad value for `' + type + '.' + key + '`');
|
---|
| 87 | }
|
---|
| 88 | }
|
---|
| 89 |
|
---|
| 90 | for (var key in fields) {
|
---|
| 91 | if (hasOwnProperty.call(fields, key) &&
|
---|
| 92 | hasOwnProperty.call(node, key) === false) {
|
---|
| 93 | warn(node, 'Field `' + type + '.' + key + '` is missed');
|
---|
| 94 | }
|
---|
| 95 | }
|
---|
| 96 | };
|
---|
| 97 | }
|
---|
| 98 |
|
---|
| 99 | function processStructure(name, nodeType) {
|
---|
| 100 | var structure = nodeType.structure;
|
---|
| 101 | var fields = {
|
---|
| 102 | type: String,
|
---|
| 103 | loc: true
|
---|
| 104 | };
|
---|
| 105 | var docs = {
|
---|
| 106 | type: '"' + name + '"'
|
---|
| 107 | };
|
---|
| 108 |
|
---|
| 109 | for (var key in structure) {
|
---|
| 110 | if (hasOwnProperty.call(structure, key) === false) {
|
---|
| 111 | continue;
|
---|
| 112 | }
|
---|
| 113 |
|
---|
| 114 | var docsTypes = [];
|
---|
| 115 | var fieldTypes = fields[key] = Array.isArray(structure[key])
|
---|
| 116 | ? structure[key].slice()
|
---|
| 117 | : [structure[key]];
|
---|
| 118 |
|
---|
| 119 | for (var i = 0; i < fieldTypes.length; i++) {
|
---|
| 120 | var fieldType = fieldTypes[i];
|
---|
| 121 | if (fieldType === String || fieldType === Boolean) {
|
---|
| 122 | docsTypes.push(fieldType.name);
|
---|
| 123 | } else if (fieldType === null) {
|
---|
| 124 | docsTypes.push('null');
|
---|
| 125 | } else if (typeof fieldType === 'string') {
|
---|
| 126 | docsTypes.push('<' + fieldType + '>');
|
---|
| 127 | } else if (Array.isArray(fieldType)) {
|
---|
| 128 | docsTypes.push('List'); // TODO: use type enum
|
---|
| 129 | } else {
|
---|
| 130 | throw new Error('Wrong value `' + fieldType + '` in `' + name + '.' + key + '` structure definition');
|
---|
| 131 | }
|
---|
| 132 | }
|
---|
| 133 |
|
---|
| 134 | docs[key] = docsTypes.join(' | ');
|
---|
| 135 | }
|
---|
| 136 |
|
---|
| 137 | return {
|
---|
| 138 | docs: docs,
|
---|
| 139 | check: createNodeStructureChecker(name, fields)
|
---|
| 140 | };
|
---|
| 141 | }
|
---|
| 142 |
|
---|
| 143 | module.exports = {
|
---|
| 144 | getStructureFromConfig: function(config) {
|
---|
| 145 | var structure = {};
|
---|
| 146 |
|
---|
| 147 | if (config.node) {
|
---|
| 148 | for (var name in config.node) {
|
---|
| 149 | if (hasOwnProperty.call(config.node, name)) {
|
---|
| 150 | var nodeType = config.node[name];
|
---|
| 151 |
|
---|
| 152 | if (nodeType.structure) {
|
---|
| 153 | structure[name] = processStructure(name, nodeType);
|
---|
| 154 | } else {
|
---|
| 155 | throw new Error('Missed `structure` field in `' + name + '` node type definition');
|
---|
| 156 | }
|
---|
| 157 | }
|
---|
| 158 | }
|
---|
| 159 | }
|
---|
| 160 |
|
---|
| 161 | return structure;
|
---|
| 162 | }
|
---|
| 163 | };
|
---|