eslint-plugin-no-jquery 2.4.0 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/Changelog.md +87 -11
  2. package/README.md +114 -98
  3. package/README.md.template +14 -3
  4. package/package.json +14 -12
  5. package/src/all-methods.js +2 -0
  6. package/src/index.js +174 -53
  7. package/src/rules/no-ajax-events.js +10 -12
  8. package/src/rules/no-and-self.js +1 -1
  9. package/src/rules/no-animate-toggle.js +4 -3
  10. package/src/rules/no-animate.js +1 -1
  11. package/src/rules/no-append-html.js +53 -0
  12. package/src/rules/no-class-state.js +1 -1
  13. package/src/rules/no-constructor-attributes.js +1 -1
  14. package/src/rules/no-deferred.js +1 -1
  15. package/src/rules/no-error.js +7 -1
  16. package/src/rules/no-escape-selector.js +14 -0
  17. package/src/rules/no-event-shorthand.js +7 -7
  18. package/src/rules/no-extend.js +1 -1
  19. package/src/rules/no-global-selector.js +1 -1
  20. package/src/rules/no-html.js +2 -1
  21. package/src/rules/no-is-array.js +1 -1
  22. package/src/rules/no-is-function.js +1 -1
  23. package/src/rules/no-jquery-constructor.js +31 -0
  24. package/src/rules/no-load-shorthand.js +2 -2
  25. package/src/rules/no-noop.js +1 -1
  26. package/src/rules/no-now.js +2 -2
  27. package/src/rules/no-on-ready.js +17 -2
  28. package/src/rules/no-other-methods.js +121 -0
  29. package/src/rules/no-other-utils.js +88 -0
  30. package/src/rules/no-parse-html-literal.js +18 -7
  31. package/src/rules/no-parse-json.js +1 -1
  32. package/src/rules/no-parse-xml.js +12 -1
  33. package/src/rules/no-proxy.js +19 -1
  34. package/src/rules/no-ready-shorthand.js +9 -1
  35. package/src/rules/no-ready.js +1 -1
  36. package/src/rules/no-size.js +2 -2
  37. package/src/rules/no-sizzle.js +2 -2
  38. package/src/rules/no-submit.js +2 -1
  39. package/src/rules/no-unique.js +1 -1
  40. package/src/rules/variable-pattern.js +4 -3
  41. package/src/utils.js +59 -25
@@ -0,0 +1,31 @@
1
+ 'use strict';
2
+
3
+ const utils = require( '../utils.js' );
4
+
5
+ module.exports = {
6
+ meta: {
7
+ type: 'suggestion',
8
+ docs: {
9
+ description: 'Disallows the jQuery constructor `$()`.'
10
+ },
11
+ schema: []
12
+ },
13
+
14
+ create: function ( context ) {
15
+ return {
16
+ 'CallExpression:exit': function ( node ) {
17
+ if (
18
+ node.callee.type !== 'Identifier' ||
19
+ !utils.isjQueryConstructor( context, node.callee.name )
20
+ ) {
21
+ return;
22
+ }
23
+
24
+ context.report( {
25
+ node: node,
26
+ message: 'The jQuery constructor is not allowed'
27
+ } );
28
+ }
29
+ };
30
+ }
31
+ };
@@ -14,7 +14,7 @@ module.exports = {
14
14
 
15
15
  create: function ( context ) {
16
16
  return {
17
- CallExpression: function ( node ) {
17
+ 'CallExpression:exit': function ( node ) {
18
18
  if ( !(
19
19
  node.callee.type === 'MemberExpression' &&
20
20
  !utils.isjQueryConstructor( context, node.callee.object.name ) &&
@@ -30,7 +30,7 @@ module.exports = {
30
30
  context.report( {
31
31
  node: node,
32
32
  message: 'Prefer .on or .trigger to .load',
33
- fix: utils.eventShorthandFixer.bind( this, node )
33
+ fix: utils.eventShorthandFixer.bind( this, node, context )
34
34
  } );
35
35
  }
36
36
  }
@@ -7,7 +7,7 @@ module.exports = utils.createUtilPropertyRule(
7
7
  'Prefer `function(){}` to `$.noop`',
8
8
  {
9
9
  fixable: 'code',
10
- fix: function ( node, fixer ) {
10
+ fix: function ( node, context, fixer ) {
11
11
  return fixer.replaceText( node, '(function(){})' );
12
12
  }
13
13
  }
@@ -4,10 +4,10 @@ const utils = require( '../utils.js' );
4
4
 
5
5
  module.exports = utils.createUtilMethodRule(
6
6
  'now',
7
- 'Prefer `(new Date).getTime()` to `$.now`',
7
+ 'Prefer `Date.now` to `$.now`',
8
8
  {
9
9
  fixable: 'code',
10
- fix: function ( node, fixer ) {
10
+ fix: function ( node, context, fixer ) {
11
11
  return fixer.replaceText( node.callee, 'Date.now' );
12
12
  }
13
13
  }
@@ -8,12 +8,13 @@ module.exports = {
8
8
  docs: {
9
9
  description: 'Disallows using the ready event on the document.'
10
10
  },
11
+ fixable: 'code',
11
12
  schema: []
12
13
  },
13
14
 
14
15
  create: function ( context ) {
15
16
  return {
16
- CallExpression: function ( node ) {
17
+ 'CallExpression:exit': function ( node ) {
17
18
  if (
18
19
  node.callee.type !== 'MemberExpression' ||
19
20
  node.callee.property.name !== 'on'
@@ -28,7 +29,21 @@ module.exports = {
28
29
  if ( utils.isjQuery( context, node.callee ) ) {
29
30
  context.report( {
30
31
  node: node,
31
- message: '.on("ready") is not allowed'
32
+ message: '.on("ready") is not allowed',
33
+ fix: function ( fixer ) {
34
+ if ( node.arguments.length > 1 ) {
35
+ return [
36
+ fixer.replaceText( node.callee.property, 'ready' ),
37
+ fixer.replaceTextRange(
38
+ [
39
+ node.arguments[ 0 ].range[ 0 ],
40
+ node.arguments[ 1 ].range[ 0 ]
41
+ ],
42
+ ''
43
+ )
44
+ ];
45
+ }
46
+ }
32
47
  } );
33
48
  }
34
49
  }
@@ -0,0 +1,121 @@
1
+ 'use strict';
2
+
3
+ const utils = require( '../utils.js' );
4
+
5
+ const methodsWithRules = [
6
+ 'addClass',
7
+ 'ajaxComplete',
8
+ 'ajaxError',
9
+ 'ajaxSend',
10
+ 'ajaxStart',
11
+ 'ajaxStop',
12
+ 'ajaxSuccess',
13
+ 'animate',
14
+ 'attr',
15
+ 'bind',
16
+ 'blur',
17
+ 'change',
18
+ 'click',
19
+ 'clone',
20
+ 'closest',
21
+ 'contextmenu',
22
+ 'css',
23
+ 'data',
24
+ 'dblclick',
25
+ 'delegate',
26
+ 'die',
27
+ 'each',
28
+ 'fadeIn',
29
+ 'fadeOut',
30
+ 'fadeTo',
31
+ 'fadeToggle',
32
+ 'filter',
33
+ 'find',
34
+ 'focus',
35
+ 'focusin',
36
+ 'focusout',
37
+ 'has',
38
+ 'hasClass',
39
+ 'hide',
40
+ 'hover',
41
+ 'html',
42
+ 'is',
43
+ 'keydown',
44
+ 'keypress',
45
+ 'keyup',
46
+ 'live',
47
+ 'load',
48
+ 'map',
49
+ 'mousedown',
50
+ 'mouseenter',
51
+ 'mouseleave',
52
+ 'mousemove',
53
+ 'mouseout',
54
+ 'mouseover',
55
+ 'mouseup',
56
+ 'parent',
57
+ 'parents',
58
+ 'prop',
59
+ 'ready',
60
+ 'removeAttr',
61
+ 'removeClass',
62
+ 'removeData',
63
+ 'removeProp',
64
+ 'resize',
65
+ 'scroll',
66
+ 'select',
67
+ 'serialize',
68
+ 'serializeArray',
69
+ 'show',
70
+ 'size',
71
+ 'slideDown',
72
+ 'slideToggle',
73
+ 'slideUp',
74
+ 'submit',
75
+ 'text',
76
+ 'toggle',
77
+ 'toggleClass',
78
+ 'trigger',
79
+ 'unbind',
80
+ 'undelegate',
81
+ 'unwrap',
82
+ 'val',
83
+ 'wrap',
84
+ 'wrapAll',
85
+ 'wrapInner'
86
+ ];
87
+
88
+ module.exports = {
89
+ meta: {
90
+ type: 'suggestion',
91
+ docs: {
92
+ description: 'Disallows all methods not covered by more specific rules.'
93
+ },
94
+ schema: []
95
+ },
96
+
97
+ create: function ( context ) {
98
+ return {
99
+ 'CallExpression:exit': function ( node ) {
100
+ if ( node.callee.type !== 'MemberExpression' ) {
101
+ return;
102
+ }
103
+ const name = node.callee.property.name;
104
+ if (
105
+ !name ||
106
+ methodsWithRules.includes( name ) ||
107
+ utils.isjQueryConstructor( context, node.callee.object.name )
108
+ ) {
109
+ return;
110
+ }
111
+ if ( utils.isjQuery( context, node.callee ) ) {
112
+ context.report( {
113
+ node: node,
114
+ message: '.{{name}} is not allowed',
115
+ data: { name: name }
116
+ } );
117
+ }
118
+ }
119
+ };
120
+ }
121
+ };
@@ -0,0 +1,88 @@
1
+ 'use strict';
2
+
3
+ const utils = require( '../utils.js' );
4
+
5
+ const utilsWithRules = [
6
+ 'ajax',
7
+ 'attr',
8
+ 'camelCase',
9
+ 'clone',
10
+ 'contains',
11
+ 'css',
12
+ 'data',
13
+ 'Deferred',
14
+ 'each',
15
+ 'error',
16
+ 'escapeSelector',
17
+ 'extend',
18
+ 'filter',
19
+ 'find',
20
+ 'get',
21
+ 'getJSON',
22
+ 'getScript',
23
+ 'globalEval',
24
+ 'grep',
25
+ 'hasData',
26
+ 'holdReady',
27
+ 'inArray',
28
+ 'isArray',
29
+ 'isEmptyObject',
30
+ 'isFunction',
31
+ 'isNumeric',
32
+ 'isPlainObject',
33
+ 'isWindow',
34
+ 'map',
35
+ 'merge',
36
+ 'nodeName',
37
+ 'noop',
38
+ 'now',
39
+ 'param',
40
+ 'parseHTML',
41
+ 'parseJSON',
42
+ 'parseXML',
43
+ 'post',
44
+ 'prop',
45
+ 'proxy',
46
+ 'removeAttr',
47
+ 'removeData',
48
+ 'sub',
49
+ 'text',
50
+ 'trim',
51
+ 'type',
52
+ 'unique',
53
+ 'when'
54
+ ];
55
+
56
+ module.exports = {
57
+ meta: {
58
+ type: 'suggestion',
59
+ docs: {
60
+ description: 'Disallows all utilities not covered by more specific rules.'
61
+ },
62
+ schema: []
63
+ },
64
+
65
+ create: function ( context ) {
66
+ return {
67
+ 'CallExpression:exit': function ( node ) {
68
+ if ( node.callee.type !== 'MemberExpression' ) {
69
+ return;
70
+ }
71
+ const name = node.callee.property.name;
72
+ if (
73
+ !name ||
74
+ utilsWithRules.includes( name ) ||
75
+ !utils.isjQueryConstructor( context, node.callee.object.name )
76
+ ) {
77
+ return;
78
+ }
79
+
80
+ context.report( {
81
+ node: node,
82
+ message: '$.{{name}} is not allowed',
83
+ data: { name: name }
84
+ } );
85
+ }
86
+ };
87
+ }
88
+ };
@@ -40,8 +40,9 @@ module.exports = {
40
40
  'The format of single tags can be specified using the `singleTagStyle` option:\n' +
41
41
  '* `"minimal"` (default) no whitespace or self-closing i.e. `<div>`\n' +
42
42
  '* `"self-closing"` no whitespace and self-closing i.e. `<div/>`\n' +
43
- '* `"any"` no style enforced\n'
43
+ '* `"any"` no style enforced'
44
44
  },
45
+ fixable: 'code',
45
46
  schema: [
46
47
  {
47
48
  type: 'object',
@@ -57,7 +58,7 @@ module.exports = {
57
58
 
58
59
  create: function ( context ) {
59
60
  return {
60
- CallExpression: function ( node ) {
61
+ 'CallExpression:exit': function ( node ) {
61
62
  let allowSingle,
62
63
  message = 'Prefer DOM building to parsing HTML literals';
63
64
 
@@ -80,7 +81,7 @@ module.exports = {
80
81
  ) {
81
82
  allowSingle = false;
82
83
  } else if (
83
- [ 'html', 'append', 'add' ].indexOf( node.callee.property.name ) !== -1 &&
84
+ [ 'html', 'append', 'add' ].includes( node.callee.property.name ) &&
84
85
  utils.isjQuery( context, node )
85
86
  ) {
86
87
  allowSingle = true;
@@ -91,6 +92,7 @@ module.exports = {
91
92
  return;
92
93
  }
93
94
 
95
+ let expectedTag;
94
96
  const arg = node.arguments[ 0 ];
95
97
  if ( allowSingle ) {
96
98
  const value = arg && allLiteral( arg ) && joinLiterals( arg );
@@ -98,18 +100,21 @@ module.exports = {
98
100
  // Empty or non-string, or non-HTML
99
101
  return;
100
102
  }
101
- if ( rsingleTag.exec( value ) ) {
103
+ let match;
104
+ if ( ( match = rsingleTag.exec( value ) ) ) {
102
105
  // Single tag
103
106
  const singleTagStyle = ( context.options[ 0 ] && context.options[ 0 ].singleTagStyle ) || 'minimal';
104
107
  if ( singleTagStyle === 'minimal' ) {
105
108
  if ( !rsingleTagMinimal.exec( value ) ) {
106
- message = 'Single tag must use the format: <tagname>';
109
+ expectedTag = '<' + match[ 1 ] + '>';
110
+ message = 'Single tag must use the format: ' + expectedTag;
107
111
  } else {
108
112
  return;
109
113
  }
110
114
  } else if ( singleTagStyle === 'self-closing' ) {
111
115
  if ( !rsingleTagSelfClosing.exec( value ) ) {
112
- message = 'Single tag must use the format: <tagname/>';
116
+ expectedTag = '<' + match[ 1 ] + '/>';
117
+ message = 'Single tag must use the format: ' + expectedTag;
113
118
  } else {
114
119
  return;
115
120
  }
@@ -125,7 +130,13 @@ module.exports = {
125
130
 
126
131
  context.report( {
127
132
  node: node,
128
- message: message
133
+ message: message,
134
+ fix: function ( fixer ) {
135
+ if ( expectedTag ) {
136
+ return fixer.replaceText( arg, '"' + expectedTag + '"' );
137
+ }
138
+ return null;
139
+ }
129
140
  } );
130
141
  }
131
142
  };
@@ -7,7 +7,7 @@ module.exports = utils.createUtilMethodRule(
7
7
  'Prefer `JSON.parse` to `$.parseJSON`',
8
8
  {
9
9
  fixable: 'code',
10
- fix: function ( node, fixer ) {
10
+ fix: function ( node, context, fixer ) {
11
11
  return fixer.replaceText( node.callee, 'JSON.parse' );
12
12
  }
13
13
  }
@@ -4,5 +4,16 @@ const utils = require( '../utils.js' );
4
4
 
5
5
  module.exports = utils.createUtilMethodRule(
6
6
  'parseXML',
7
- 'Prefer `DOMParser#parseFromString` to `$.parseXML`'
7
+ 'Prefer `DOMParser#parseFromString` to `$.parseXML`',
8
+ {
9
+ fixable: 'code',
10
+ fix: function ( node, context, fixer ) {
11
+ if ( node.arguments.length ) {
12
+ return [
13
+ fixer.replaceText( node.callee, '( new window.DOMParser() ).parseFromString' ),
14
+ fixer.insertTextAfterRange( node.arguments[ 0 ].range, ', "text/xml"' )
15
+ ];
16
+ }
17
+ }
18
+ }
8
19
  );
@@ -4,5 +4,23 @@ const utils = require( '../utils.js' );
4
4
 
5
5
  module.exports = utils.createUtilMethodRule(
6
6
  'proxy',
7
- 'Prefer `Function#bind` to `$.proxy`'
7
+ 'Prefer `Function#bind` to `$.proxy`',
8
+ {
9
+ fixable: 'code',
10
+ fix: function ( node, context, fixer ) {
11
+ if (
12
+ node.arguments.length >= 2 &&
13
+ node.arguments[ 1 ].type !== 'Literal'
14
+ ) {
15
+ const fnText = context.getSourceCode().getText( node.arguments[ 0 ] );
16
+ return [
17
+ fixer.replaceText( node.callee, fnText + '.bind' ),
18
+ fixer.removeRange( [
19
+ node.arguments[ 0 ].range[ 0 ],
20
+ node.arguments[ 1 ].range[ 0 ]
21
+ ] )
22
+ ];
23
+ }
24
+ }
25
+ }
8
26
  );
@@ -4,5 +4,13 @@ const utils = require( '../utils.js' );
4
4
 
5
5
  module.exports = utils.createCollectionMethodRule(
6
6
  'ready',
7
- 'Prefer `$()` to `.ready`'
7
+ 'Prefer `$()` to `.ready`',
8
+ {
9
+ fixable: 'code',
10
+ fix: function ( node, context, fixer ) {
11
+ if ( node.parent.type === 'ExpressionStatement' ) {
12
+ return fixer.replaceText( node.callee, '$' );
13
+ }
14
+ }
15
+ }
8
16
  );
@@ -32,7 +32,7 @@ module.exports = {
32
32
 
33
33
  create: function ( context ) {
34
34
  return {
35
- CallExpression: function ( node ) {
35
+ 'CallExpression:exit': function ( node ) {
36
36
  if ( isDirect( context, node ) || isChained( context, node ) ) {
37
37
  context.report( {
38
38
  node: node,
@@ -7,8 +7,8 @@ module.exports = utils.createCollectionMethodRule(
7
7
  'Prefer `.length` to `.size`',
8
8
  {
9
9
  fixable: 'code',
10
- fix: function ( node, fixer ) {
11
- return fixer.replaceTextRange( [ node.callee.property.start, node.end ], 'length' );
10
+ fix: function ( node, context, fixer ) {
11
+ return fixer.replaceTextRange( [ node.callee.property.range[ 0 ], node.range[ 1 ] ], 'length' );
12
12
  }
13
13
  }
14
14
  );
@@ -63,13 +63,13 @@ module.exports = {
63
63
  ];
64
64
 
65
65
  return {
66
- CallExpression: function ( node ) {
66
+ 'CallExpression:exit': function ( node ) {
67
67
  if (
68
68
  !node.arguments[ 0 ] ||
69
69
  !utils.isjQuery( context, node.callee ) ||
70
70
  (
71
71
  node.callee.type === 'MemberExpression' &&
72
- traversals.indexOf( node.callee.property.name ) === -1
72
+ !traversals.includes( node.callee.property.name )
73
73
  )
74
74
  ) {
75
75
  return;
@@ -4,5 +4,6 @@ const utils = require( '../utils.js' );
4
4
 
5
5
  module.exports = utils.createCollectionMethodRule(
6
6
  'submit',
7
- 'Prefer `EventTarget#dispatchEvent` + `HTMLFormElement#submit` to `.submit`'
7
+ 'Prefer `EventTarget#dispatchEvent` + `HTMLFormElement#submit` to `.submit`',
8
+ { deprecated: [ 'no-event-shorthand' ] }
8
9
  );
@@ -7,7 +7,7 @@ module.exports = utils.createUtilMethodRule(
7
7
  'Prefer `$.uniqueSort` to `$.unique`',
8
8
  {
9
9
  fixable: 'code',
10
- fix: function ( node, fixer ) {
10
+ fix: function ( node, context, fixer ) {
11
11
  return fixer.replaceText( node.callee.property, 'uniqueSort' );
12
12
  }
13
13
  }
@@ -18,7 +18,8 @@ module.exports = {
18
18
  // If the variable name is computed (e.g. foo[bar]) we
19
19
  // can't be sure this is not correctly named.
20
20
  !left.computed &&
21
- utils.isjQuery( context, right )
21
+ // right can be null, e.g. `var x;`
22
+ right && utils.isjQuery( context, right )
22
23
  ) {
23
24
  context.report( {
24
25
  node: node,
@@ -28,10 +29,10 @@ module.exports = {
28
29
  }
29
30
 
30
31
  return {
31
- AssignmentExpression: function ( node ) {
32
+ 'AssignmentExpression:exit': function ( node ) {
32
33
  test( node, node.left, node.right );
33
34
  },
34
- VariableDeclarator: function ( node ) {
35
+ 'VariableDeclarator:exit': function ( node ) {
35
36
  test( node, node.id, node.init );
36
37
  }
37
38
  };