web 3d图形渲染器
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

265 lines
8.3 KiB

  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.default = void 0;
  6. var _experimentalUtils = require("@typescript-eslint/experimental-utils");
  7. var _utils = require("./utils");
  8. /*
  9. * This implementation is ported from from eslint-plugin-jasmine.
  10. * MIT license, Tom Vincent.
  11. */
  12. /**
  13. * Async assertions might be called in Promise
  14. * methods like `Promise.x(expect1)` or `Promise.x([expect1, expect2])`.
  15. * If that's the case, Promise node have to be awaited or returned.
  16. *
  17. * @Returns CallExpressionNode
  18. */
  19. const getPromiseCallExpressionNode = node => {
  20. if (node.type === _experimentalUtils.AST_NODE_TYPES.ArrayExpression && node.parent && node.parent.type === _experimentalUtils.AST_NODE_TYPES.CallExpression) {
  21. node = node.parent;
  22. }
  23. if (node.type === _experimentalUtils.AST_NODE_TYPES.CallExpression && node.callee && node.callee.type === _experimentalUtils.AST_NODE_TYPES.MemberExpression && (0, _utils.isSupportedAccessor)(node.callee.object) && (0, _utils.getAccessorValue)(node.callee.object) === 'Promise' && node.parent) {
  24. return node;
  25. }
  26. return null;
  27. };
  28. const findPromiseCallExpressionNode = node => node.parent && node.parent.parent && [_experimentalUtils.AST_NODE_TYPES.CallExpression, _experimentalUtils.AST_NODE_TYPES.ArrayExpression].includes(node.parent.type) ? getPromiseCallExpressionNode(node.parent) : null;
  29. const getParentIfThenified = node => {
  30. const grandParentNode = node.parent && node.parent.parent;
  31. if (grandParentNode && grandParentNode.type === _experimentalUtils.AST_NODE_TYPES.CallExpression && grandParentNode.callee && (0, _utils.isExpectMember)(grandParentNode.callee) && ['then', 'catch'].includes((0, _utils.getAccessorValue)(grandParentNode.callee.property)) && grandParentNode.parent) {
  32. // Just in case `then`s are chained look one above.
  33. return getParentIfThenified(grandParentNode);
  34. }
  35. return node;
  36. };
  37. const isAcceptableReturnNode = (node, allowReturn) => allowReturn && node.type === _experimentalUtils.AST_NODE_TYPES.ReturnStatement || [_experimentalUtils.AST_NODE_TYPES.ArrowFunctionExpression, _experimentalUtils.AST_NODE_TYPES.AwaitExpression].includes(node.type);
  38. const isNoAssertionsParentNode = node => node.type === _experimentalUtils.AST_NODE_TYPES.ExpressionStatement || node.type === _experimentalUtils.AST_NODE_TYPES.AwaitExpression && node.parent !== undefined && node.parent.type === _experimentalUtils.AST_NODE_TYPES.ExpressionStatement;
  39. const promiseArrayExceptionKey = ({
  40. start,
  41. end
  42. }) => `${start.line}:${start.column}-${end.line}:${end.column}`;
  43. var _default = (0, _utils.createRule)({
  44. name: __filename,
  45. meta: {
  46. docs: {
  47. category: 'Best Practices',
  48. description: 'Enforce valid `expect()` usage',
  49. recommended: 'error'
  50. },
  51. messages: {
  52. tooManyArgs: 'Expect takes at most {{ amount }} argument{{ s }}.',
  53. notEnoughArgs: 'Expect requires at least {{ amount }} argument{{ s }}.',
  54. modifierUnknown: 'Expect has no modifier named "{{ modifierName }}".',
  55. matcherNotFound: 'Expect must have a corresponding matcher call.',
  56. matcherNotCalled: 'Matchers must be called to assert.',
  57. asyncMustBeAwaited: 'Async assertions must be awaited{{ orReturned }}.',
  58. promisesWithAsyncAssertionsMustBeAwaited: 'Promises which return async assertions must be awaited{{ orReturned }}.'
  59. },
  60. type: 'suggestion',
  61. schema: [{
  62. type: 'object',
  63. properties: {
  64. alwaysAwait: {
  65. type: 'boolean',
  66. default: false
  67. },
  68. minArgs: {
  69. type: 'number',
  70. minimum: 1
  71. },
  72. maxArgs: {
  73. type: 'number',
  74. minimum: 1
  75. }
  76. },
  77. additionalProperties: false
  78. }]
  79. },
  80. defaultOptions: [{
  81. alwaysAwait: false,
  82. minArgs: 1,
  83. maxArgs: 1
  84. }],
  85. create(context, [{
  86. alwaysAwait,
  87. minArgs = 1,
  88. maxArgs = 1
  89. }]) {
  90. // Context state
  91. const arrayExceptions = new Set();
  92. const pushPromiseArrayException = loc => arrayExceptions.add(promiseArrayExceptionKey(loc));
  93. /**
  94. * Promise method that accepts an array of promises,
  95. * (eg. Promise.all), will throw warnings for the each
  96. * unawaited or non-returned promise. To avoid throwing
  97. * multiple warnings, we check if there is a warning in
  98. * the given location.
  99. */
  100. const promiseArrayExceptionExists = loc => arrayExceptions.has(promiseArrayExceptionKey(loc));
  101. return {
  102. CallExpression(node) {
  103. if (!(0, _utils.isExpectCall)(node)) {
  104. return;
  105. }
  106. const {
  107. expect,
  108. modifier,
  109. matcher
  110. } = (0, _utils.parseExpectCall)(node);
  111. if (expect.arguments.length < minArgs) {
  112. const expectLength = (0, _utils.getAccessorValue)(expect.callee).length;
  113. const loc = {
  114. start: {
  115. column: node.loc.start.column + expectLength,
  116. line: node.loc.start.line
  117. },
  118. end: {
  119. column: node.loc.start.column + expectLength + 1,
  120. line: node.loc.start.line
  121. }
  122. };
  123. context.report({
  124. messageId: 'notEnoughArgs',
  125. data: {
  126. amount: minArgs,
  127. s: minArgs === 1 ? '' : 's'
  128. },
  129. node,
  130. loc
  131. });
  132. }
  133. if (expect.arguments.length > maxArgs) {
  134. const {
  135. start
  136. } = expect.arguments[maxArgs].loc;
  137. const {
  138. end
  139. } = expect.arguments[node.arguments.length - 1].loc;
  140. const loc = {
  141. start,
  142. end: {
  143. column: end.column - 1,
  144. line: end.line
  145. }
  146. };
  147. context.report({
  148. messageId: 'tooManyArgs',
  149. data: {
  150. amount: maxArgs,
  151. s: maxArgs === 1 ? '' : 's'
  152. },
  153. node,
  154. loc
  155. });
  156. } // something was called on `expect()`
  157. if (!matcher) {
  158. if (modifier) {
  159. context.report({
  160. messageId: 'matcherNotFound',
  161. node: modifier.node.property
  162. });
  163. }
  164. return;
  165. }
  166. if ((0, _utils.isExpectMember)(matcher.node.parent)) {
  167. context.report({
  168. messageId: 'modifierUnknown',
  169. data: {
  170. modifierName: matcher.name
  171. },
  172. node: matcher.node.property
  173. });
  174. return;
  175. }
  176. if (!matcher.arguments) {
  177. context.report({
  178. messageId: 'matcherNotCalled',
  179. node: matcher.node.property
  180. });
  181. }
  182. const parentNode = matcher.node.parent;
  183. if (!parentNode.parent || !modifier || modifier.name === _utils.ModifierName.not) {
  184. return;
  185. }
  186. /**
  187. * If parent node is an array expression, we'll report the warning,
  188. * for the array object, not for each individual assertion.
  189. */
  190. const isParentArrayExpression = parentNode.parent.type === _experimentalUtils.AST_NODE_TYPES.ArrayExpression;
  191. const orReturned = alwaysAwait ? '' : ' or returned';
  192. /**
  193. * An async assertion can be chained with `then` or `catch` statements.
  194. * In that case our target CallExpression node is the one with
  195. * the last `then` or `catch` statement.
  196. */
  197. const targetNode = getParentIfThenified(parentNode);
  198. const finalNode = findPromiseCallExpressionNode(targetNode) || targetNode;
  199. if (finalNode.parent && // If node is not awaited or returned
  200. !isAcceptableReturnNode(finalNode.parent, !alwaysAwait) && // if we didn't warn user already
  201. !promiseArrayExceptionExists(finalNode.loc)) {
  202. context.report({
  203. loc: finalNode.loc,
  204. data: {
  205. orReturned
  206. },
  207. messageId: finalNode === targetNode ? 'asyncMustBeAwaited' : 'promisesWithAsyncAssertionsMustBeAwaited',
  208. node
  209. });
  210. if (isParentArrayExpression) {
  211. pushPromiseArrayException(finalNode.loc);
  212. }
  213. }
  214. },
  215. // nothing called on "expect()"
  216. 'CallExpression:exit'(node) {
  217. if ((0, _utils.isExpectCall)(node) && isNoAssertionsParentNode(node.parent)) {
  218. context.report({
  219. messageId: 'matcherNotFound',
  220. node
  221. });
  222. }
  223. }
  224. };
  225. }
  226. });
  227. exports.default = _default;