structure.cjs 5.2 KB

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