structure.js 5.1 KB

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