requireValidFileAnnotation.js 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports["default"] = void 0;
  6. var _lodash = _interopRequireDefault(require("lodash"));
  7. var _utilities = require("../utilities");
  8. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
  9. var defaults = {
  10. annotationStyle: 'none',
  11. strict: false
  12. };
  13. var looksLikeFlowFileAnnotation = function looksLikeFlowFileAnnotation(comment) {
  14. return /@(?:no)?flo/i.test(comment);
  15. };
  16. var isValidAnnotationStyle = function isValidAnnotationStyle(node, style) {
  17. if (style === 'none') {
  18. return true;
  19. }
  20. return style === node.type.toLowerCase();
  21. };
  22. var checkAnnotationSpelling = function checkAnnotationSpelling(comment) {
  23. return /@[a-z]+\b/.test(comment) && (0, _utilities.fuzzyStringMatch)(comment.replace(/no/i, ''), '@flow', 0.2);
  24. };
  25. var isFlowStrict = function isFlowStrict(comment) {
  26. return /^@flow[\t-\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]strict\b/.test(comment);
  27. };
  28. var noFlowAnnotation = function noFlowAnnotation(comment) {
  29. return /^@noflow\b/.test(comment);
  30. };
  31. var schema = [{
  32. "enum": ['always', 'never'],
  33. type: 'string'
  34. }, {
  35. additionalProperties: false,
  36. properties: {
  37. annotationStyle: {
  38. "enum": ['none', 'line', 'block'],
  39. type: 'string'
  40. },
  41. strict: {
  42. "enum": [true, false],
  43. type: 'boolean'
  44. }
  45. },
  46. type: 'object'
  47. }];
  48. var create = function create(context) {
  49. var always = context.options[0] === 'always';
  50. var style = _lodash["default"].get(context, 'options[1].annotationStyle', defaults.annotationStyle);
  51. var flowStrict = _lodash["default"].get(context, 'options[1].strict', defaults.strict);
  52. return {
  53. Program: function Program(node) {
  54. var firstToken = node.tokens[0];
  55. var potentialFlowFileAnnotation = _lodash["default"].find(context.getSourceCode().getAllComments(), function (comment) {
  56. return looksLikeFlowFileAnnotation(comment.value);
  57. });
  58. if (potentialFlowFileAnnotation) {
  59. if (firstToken && firstToken.range[0] < potentialFlowFileAnnotation.range[0]) {
  60. context.report({
  61. message: 'Flow file annotation not at the top of the file.',
  62. node: potentialFlowFileAnnotation
  63. });
  64. }
  65. var annotationValue = potentialFlowFileAnnotation.value.trim();
  66. if ((0, _utilities.isFlowFileAnnotation)(annotationValue)) {
  67. if (!isValidAnnotationStyle(potentialFlowFileAnnotation, style)) {
  68. var annotation = style === 'line' ? "// ".concat(annotationValue) : "/* ".concat(annotationValue, " */");
  69. context.report({
  70. fix: function fix(fixer) {
  71. return fixer.replaceTextRange([potentialFlowFileAnnotation.range[0], potentialFlowFileAnnotation.range[1]], annotation);
  72. },
  73. message: "Flow file annotation style must be `".concat(annotation, "`"),
  74. node: potentialFlowFileAnnotation
  75. });
  76. }
  77. if (!noFlowAnnotation(annotationValue) && flowStrict && !isFlowStrict(annotationValue)) {
  78. var str = style === 'line' ? '`// @flow strict`' : '`/* @flow strict */`';
  79. context.report({
  80. fix: function fix(fixer) {
  81. var annotation = ['line', 'none'].includes(style) ? '// @flow strict' : '/* @flow strict */';
  82. return fixer.replaceTextRange([potentialFlowFileAnnotation.range[0], potentialFlowFileAnnotation.range[1]], annotation);
  83. },
  84. message: "Strict Flow file annotation is required, must be ".concat(str),
  85. node: node
  86. });
  87. }
  88. } else if (checkAnnotationSpelling(annotationValue)) {
  89. context.report({
  90. message: 'Misspelled or malformed Flow file annotation.',
  91. node: potentialFlowFileAnnotation
  92. });
  93. } else {
  94. context.report({
  95. message: 'Malformed Flow file annotation.',
  96. node: potentialFlowFileAnnotation
  97. });
  98. }
  99. } else if (always && !_lodash["default"].get(context, 'settings[\'ft-flow\'].onlyFilesWithFlowAnnotation')) {
  100. context.report({
  101. fix: function fix(fixer) {
  102. var annotation;
  103. if (flowStrict) {
  104. annotation = ['line', 'none'].includes(style) ? '// @flow strict\n' : '/* @flow strict */\n';
  105. } else {
  106. annotation = ['line', 'none'].includes(style) ? '// @flow\n' : '/* @flow */\n';
  107. }
  108. var firstComment = node.comments[0];
  109. if (firstComment && firstComment.type === 'Shebang') {
  110. return fixer.replaceTextRange([firstComment.range[1], firstComment.range[1]], "\n".concat(annotation.trim()));
  111. }
  112. return fixer.replaceTextRange([node.range[0], node.range[0]], annotation);
  113. },
  114. message: 'Flow file annotation is missing.',
  115. node: node
  116. });
  117. }
  118. }
  119. };
  120. };
  121. var _default = {
  122. create: create,
  123. meta: {
  124. fixable: 'code'
  125. },
  126. schema: schema
  127. };
  128. exports["default"] = _default;
  129. module.exports = exports.default;