no-new-array.js 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. 'use strict';
  2. const {isParenthesized, getStaticValue} = require('@eslint-community/eslint-utils');
  3. const needsSemicolon = require('./utils/needs-semicolon.js');
  4. const {newExpressionSelector} = require('./selectors/index.js');
  5. const isNumber = require('./utils/is-number.js');
  6. const MESSAGE_ID_ERROR = 'error';
  7. const MESSAGE_ID_LENGTH = 'array-length';
  8. const MESSAGE_ID_ONLY_ELEMENT = 'only-element';
  9. const MESSAGE_ID_SPREAD = 'spread';
  10. const messages = {
  11. [MESSAGE_ID_ERROR]: 'Do not use `new Array()`.',
  12. [MESSAGE_ID_LENGTH]: 'The argument is the length of array.',
  13. [MESSAGE_ID_ONLY_ELEMENT]: 'The argument is the only element of array.',
  14. [MESSAGE_ID_SPREAD]: 'Spread the argument.',
  15. };
  16. const newArraySelector = newExpressionSelector({
  17. name: 'Array',
  18. argumentsLength: 1,
  19. allowSpreadElement: true,
  20. });
  21. function getProblem(context, node) {
  22. const problem = {
  23. node,
  24. messageId: MESSAGE_ID_ERROR,
  25. };
  26. const [argumentNode] = node.arguments;
  27. const sourceCode = context.getSourceCode();
  28. let text = sourceCode.getText(argumentNode);
  29. if (isParenthesized(argumentNode, sourceCode)) {
  30. text = `(${text})`;
  31. }
  32. const maybeSemiColon = needsSemicolon(sourceCode.getTokenBefore(node), sourceCode, '[')
  33. ? ';'
  34. : '';
  35. // We are not sure how many `arguments` passed
  36. if (argumentNode.type === 'SpreadElement') {
  37. problem.suggest = [
  38. {
  39. messageId: MESSAGE_ID_SPREAD,
  40. fix: fixer => fixer.replaceText(node, `${maybeSemiColon}[${text}]`),
  41. },
  42. ];
  43. return problem;
  44. }
  45. const fromLengthText = `Array.from(${text === 'length' ? '{length}' : `{length: ${text}}`})`;
  46. if (isNumber(argumentNode, context.getScope())) {
  47. problem.fix = fixer => fixer.replaceText(node, fromLengthText);
  48. return problem;
  49. }
  50. const onlyElementText = `${maybeSemiColon}[${text}]`;
  51. const result = getStaticValue(argumentNode, context.getScope());
  52. if (result !== null && typeof result.value !== 'number') {
  53. problem.fix = fixer => fixer.replaceText(node, onlyElementText);
  54. return problem;
  55. }
  56. // We don't know the argument is number or not
  57. problem.suggest = [
  58. {
  59. messageId: MESSAGE_ID_LENGTH,
  60. fix: fixer => fixer.replaceText(node, fromLengthText),
  61. },
  62. {
  63. messageId: MESSAGE_ID_ONLY_ELEMENT,
  64. fix: fixer => fixer.replaceText(node, onlyElementText),
  65. },
  66. ];
  67. return problem;
  68. }
  69. /** @param {import('eslint').Rule.RuleContext} context */
  70. const create = context => ({
  71. [newArraySelector](node) {
  72. return getProblem(context, node);
  73. },
  74. });
  75. /** @type {import('eslint').Rule.RuleModule} */
  76. module.exports = {
  77. create,
  78. meta: {
  79. type: 'suggestion',
  80. docs: {
  81. description: 'Disallow `new Array()`.',
  82. },
  83. fixable: 'code',
  84. hasSuggestions: true,
  85. messages,
  86. },
  87. };