new-for-builtins.js 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. 'use strict';
  2. const {GlobalReferenceTracker} = require('./utils/global-reference-tracker.js');
  3. const builtins = require('./utils/builtins.js');
  4. const {
  5. switchCallExpressionToNewExpression,
  6. switchNewExpressionToCallExpression,
  7. } = require('./fix/index.js');
  8. const messages = {
  9. enforce: 'Use `new {{name}}()` instead of `{{name}}()`.',
  10. disallow: 'Use `{{name}}()` instead of `new {{name}}()`.',
  11. };
  12. function enforceNewExpression({node, path: [name]}, sourceCode) {
  13. if (name === 'Object') {
  14. const {parent} = node;
  15. if (
  16. parent.type === 'BinaryExpression'
  17. && (parent.operator === '===' || parent.operator === '!==')
  18. && (parent.left === node || parent.right === node)
  19. ) {
  20. return;
  21. }
  22. }
  23. return {
  24. node,
  25. messageId: 'enforce',
  26. data: {name},
  27. fix: fixer => switchCallExpressionToNewExpression(node, sourceCode, fixer),
  28. };
  29. }
  30. function enforceCallExpression({node, path: [name]}, sourceCode) {
  31. const problem = {
  32. node,
  33. messageId: 'disallow',
  34. data: {name},
  35. };
  36. if (name !== 'String' && name !== 'Boolean' && name !== 'Number') {
  37. problem.fix = function * (fixer) {
  38. yield * switchNewExpressionToCallExpression(node, sourceCode, fixer);
  39. };
  40. }
  41. return problem;
  42. }
  43. /** @param {import('eslint').Rule.RuleContext} context */
  44. const create = context => {
  45. const sourceCode = context.getSourceCode();
  46. const newExpressionTracker = new GlobalReferenceTracker({
  47. objects: builtins.disallowNew,
  48. type: GlobalReferenceTracker.CONSTRUCT,
  49. handle: reference => enforceCallExpression(reference, sourceCode),
  50. });
  51. const callExpressionTracker = new GlobalReferenceTracker({
  52. objects: builtins.enforceNew,
  53. type: GlobalReferenceTracker.CALL,
  54. handle: reference => enforceNewExpression(reference, sourceCode),
  55. });
  56. return {
  57. * 'Program:exit'() {
  58. const scope = context.getScope();
  59. yield * newExpressionTracker.track(scope);
  60. yield * callExpressionTracker.track(scope);
  61. },
  62. };
  63. };
  64. /** @type {import('eslint').Rule.RuleModule} */
  65. module.exports = {
  66. create,
  67. meta: {
  68. type: 'suggestion',
  69. docs: {
  70. description: 'Enforce the use of `new` for all builtins, except `String`, `Number`, `Boolean`, `Symbol` and `BigInt`.',
  71. },
  72. fixable: 'code',
  73. messages,
  74. },
  75. };