eslint-plugin-constructors 1.0.0 → 1.0.2

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.
package/README.md CHANGED
@@ -1,148 +1,150 @@
1
- # eslint-plugin-constructors
2
-
3
- [![](https://img.shields.io/npm/v/eslint-plugin-constructors?style=for-the-badge&label=Latest%20Build)](https://www.npmjs.com/package/eslint-plugin-constructors)
4
- [![](https://img.shields.io/badge/Requires-ESLint_%3E%3D9.0.0-4B32C3?style=for-the-badge&logo=eslint)](https://eslint.org/)
5
-
6
- ESLint plugin to enforce best practices for constructor usage.
7
-
8
- ## Installation
9
-
10
- ```bash
11
- npm i -D eslint-plugin-constructors
12
- ```
13
-
14
- ## Usage
15
-
16
- Add `constructors` to your flat config:
17
-
18
- ```js
19
- import constructors from 'eslint-plugin-constructors'
20
-
21
- export default [
22
- {
23
- files: ['**/*.js'],
24
- plugins: { constructors },
25
- rules: {
26
- 'constructors/no-explicit-return': 'error',
27
- 'constructors/require-error-subclass-name': 'error',
28
- 'constructors/require-new': 'error'
29
- }
30
- }
31
- ]
32
- ```
33
-
34
- Or use the bundled recommended config:
35
-
36
- ```js
37
- import constructors from 'eslint-plugin-constructors'
38
-
39
- export default [
40
- {
41
- files: ['**/*.js'],
42
- ...constructors.configs.recommended
43
- }
44
- ];
45
- ```
46
-
47
- ## Rules
48
-
49
- The plugin has 3 rules.
50
-
51
- ### `require-new`
52
-
53
- Requires `new` when calling error constructors (and optionally other built‑in constructors).
54
-
55
- #### Options
56
-
57
- - `"error-only"` (default) – only error constructors (`Error`, `TypeError`, `RangeError`, `SyntaxError`, `ReferenceError`, `URIError`, `EvalError`, `AggregateError`)
58
- - `"builtins"` – also includes `Array`, `Boolean`, `Date`, `Function`, `Number`, `Object`, `RegExp`, `String`, `Symbol`, `BigInt`, `Map`, `Set`, `WeakMap`, `WeakSet`
59
-
60
- #### Example incorrect code
61
-
62
- ```js
63
- throw Error('boom')
64
- throw TypeError('bad')
65
-
66
- // w/ builtins:
67
- const arr = Array(),
68
- m = Map()
69
- ```
70
-
71
- #### Example correct code
72
-
73
- ```js
74
- throw new Error('boom')
75
- throw new TypeError('bad')
76
-
77
- // w/ builtins:
78
- const arr = new Array(),
79
- m = new Map()
80
- ```
81
-
82
- #### When Not To Use It
83
-
84
- If you prefer the convenience of calling built‑ins without `new` (which is allowed by the spec).
85
-
86
- ---
87
-
88
- ### `require-error-subclass-name`
89
-
90
- Requires classes extending `Error` have a name ending in `Error`.
91
-
92
- #### Example incorrect Code
93
-
94
- ```js
95
- class My extends Error {}
96
- const Custom = class extends Error {}
97
- ```
98
-
99
- #### Example correct Code
100
-
101
- ```js
102
- class MyError extends Error {}
103
- const CustomError = class extends Error {}
104
- ```
105
-
106
- #### When Not To Use It
107
-
108
- If you don't follow the common convention of suffixing custom error classes w/ `Error`.
109
-
110
- ---
111
-
112
- ### `no-explicit-return`
113
-
114
- Disallows explicit `return` statements inside class constructors.
115
-
116
- #### Incorrect Code
117
-
118
- ```js
119
- class A {
120
- constructor() {
121
- return
122
- }
123
- }
124
-
125
- class B {
126
- constructor() {
127
- return this
128
- }
129
- }
130
- ```
131
-
132
- #### Correct Code
133
-
134
- ```js
135
- class A {
136
- constructor() {
137
- this.x = 1
138
- }
139
- }
140
- ```
141
-
142
- #### When Not To Use It
143
-
144
- If you need to return a different object from a constructor (a rare but valid pattern).
145
-
146
- ## License
147
-
148
- Copyright © 2026 [Adam Lui](https://codeberg.org/adamlui).
1
+ # eslint-plugin-constructors
2
+
3
+ [![](https://img.shields.io/npm/v/eslint-plugin-constructors?style=for-the-badge&label=Latest%20Build&color=32fcee)](https://www.npmjs.com/package/eslint-plugin-constructors)
4
+ <a href="./#license">
5
+ <img src="https://img.shields.io/badge/License-MIT-f99b27.svg?logo=internetarchive&logoColor=white&labelColor=464646&style=for-the-badge"></a>
6
+ [![](https://img.shields.io/badge/Requires-ESLint_%3E%3D9.0.0-4B32C3?style=for-the-badge&logo=eslint)](https://eslint.org/)
7
+
8
+ ESLint rules to enforce best practices for constructor usage.
9
+
10
+ ## Installation
11
+
12
+ ```bash
13
+ npm i -D eslint-plugin-constructors
14
+ ```
15
+
16
+ ## Usage
17
+
18
+ Add `constructors` to your flat config:
19
+
20
+ ```js
21
+ import constructors from 'eslint-plugin-constructors'
22
+
23
+ export default [
24
+ {
25
+ files: ['**/*.js'],
26
+ plugins: { constructors },
27
+ rules: {
28
+ 'constructors/no-explicit-return': 'error',
29
+ 'constructors/require-error-subclass-name': 'error',
30
+ 'constructors/require-new': 'error'
31
+ }
32
+ }
33
+ ]
34
+ ```
35
+
36
+ Or use the bundled recommended config:
37
+
38
+ ```js
39
+ import constructors from 'eslint-plugin-constructors'
40
+
41
+ export default [
42
+ {
43
+ files: ['**/*.js'],
44
+ ...constructors.configs.recommended
45
+ }
46
+ ];
47
+ ```
48
+
49
+ ## Rules
50
+
51
+ The plugin has 3 rules:
52
+
53
+ ### `require-new`
54
+
55
+ Requires `new` when calling error constructors (and optionally other built‑in constructors).
56
+
57
+ #### Options
58
+
59
+ - `"error-only"` (default) – only error constructors (`Error`, `TypeError`, `RangeError`, `SyntaxError`, `ReferenceError`, `URIError`, `EvalError`, `AggregateError`)
60
+ - `"builtins"` – also includes `Array`, `Boolean`, `Date`, `Function`, `Number`, `Object`, `RegExp`, `String`, `Symbol`, `BigInt`, `Map`, `Set`, `WeakMap`, `WeakSet`
61
+
62
+ #### Example incorrect code
63
+
64
+ ```js
65
+ throw Error('boom')
66
+ throw TypeError('bad')
67
+
68
+ // w/ builtins:
69
+ const arr = Array(),
70
+ m = Map()
71
+ ```
72
+
73
+ #### Example correct code
74
+
75
+ ```js
76
+ throw new Error('boom')
77
+ throw new TypeError('bad')
78
+
79
+ // w/ builtins:
80
+ const arr = new Array(),
81
+ m = new Map()
82
+ ```
83
+
84
+ #### When not to use it
85
+
86
+ If you prefer the convenience of calling built‑ins without `new` (which is allowed by the spec).
87
+
88
+ ---
89
+
90
+ ### `require-error-subclass-name`
91
+
92
+ Requires classes extending `Error` have a name ending in `Error`.
93
+
94
+ #### Example incorrect code
95
+
96
+ ```js
97
+ class My extends Error {}
98
+ const Custom = class extends Error {}
99
+ ```
100
+
101
+ #### Example correct code
102
+
103
+ ```js
104
+ class MyError extends Error {}
105
+ const CustomError = class extends Error {}
106
+ ```
107
+
108
+ #### When not to use it
109
+
110
+ If you don't follow the convention of suffixing custom error classes w/ `Error`.
111
+
112
+ ---
113
+
114
+ ### `no-explicit-return`
115
+
116
+ Disallows explicit `return` statements inside class constructors.
117
+
118
+ #### Incorrect Code
119
+
120
+ ```js
121
+ class A {
122
+ constructor() {
123
+ return
124
+ }
125
+ }
126
+
127
+ class B {
128
+ constructor() {
129
+ return this
130
+ }
131
+ }
132
+ ```
133
+
134
+ #### Correct Code
135
+
136
+ ```js
137
+ class A {
138
+ constructor() {
139
+ this.x = 1
140
+ }
141
+ }
142
+ ```
143
+
144
+ #### When not to use it
145
+
146
+ If you need to return a different object from a constructor.
147
+
148
+ ## License
149
+
150
+ Copyright © 2026 [Adam Lui](https://codeberg.org/adamlui).
@@ -0,0 +1,9 @@
1
+ # 🏛️ MIT License
2
+
3
+ **Copyright © 2026 [Adam Lui](https://codeberg.org/adamlui).**
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
@@ -0,0 +1,3 @@
1
+ # Security Policy
2
+
3
+ If you find a vulnerability, please e-mail <security@kudoai.org>.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-constructors",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "ESLint rules for constructor usage",
5
5
  "type": "module",
6
6
  "engines": {
@@ -15,16 +15,21 @@
15
15
  "license": "MIT",
16
16
  "main": "src/index.js",
17
17
  "files": [
18
- "src"
18
+ "docs/",
19
+ "src/"
19
20
  ],
20
21
  "scripts": {
21
22
  "prepare": "husky",
22
- "lint": "eslint . --cache",
23
- "lint:all": "eslint .",
24
- "lint:fix": "eslint . --fix --cache",
25
- "lint:fix-all": "eslint . --fix",
23
+ "lint": "eslint . --cache && yamllint .",
24
+ "lint:all": "eslint . && yamllint .",
25
+ "lint:fix": "eslint . --fix --cache && yamllint .",
26
+ "lint:fix-all": "eslint . --fix && yamllint .",
26
27
  "test": "node tests/run.js",
27
- "demo": "eslint --config demo/eslint.config.js demo/buggy-script.js"
28
+ "demo": "eslint --config demo/eslint.config.js demo/buggy-script.js",
29
+ "bump:patch": "bash utils/bump.sh patch",
30
+ "bump:minor": "bash utils/bump.sh minor",
31
+ "bump:feat": "npm run bump:minor",
32
+ "bump:major": "bash utils/bump.sh major"
28
33
  },
29
34
  "keywords": [
30
35
  "code-quality",
@@ -42,13 +47,15 @@
42
47
  "@eslint/markdown": "^8.0.3",
43
48
  "@stylistic/eslint-plugin": "^5.10.0",
44
49
  "eslint": "^10.10.0",
50
+ "eslint-plugin-eslint-plugin": "^7.6.2",
45
51
  "eslint-plugin-import-x": "^4.17.1",
46
52
  "eslint-plugin-no-secrets": "^2.3.3",
47
53
  "eslint-plugin-no-unsanitized": "^4.1.5",
48
54
  "eslint-plugin-promise": "^7.3.0",
49
55
  "eslint-plugin-regexp": "^3.3.0",
50
56
  "globals": "^17.12.0",
51
- "husky": "^9.1.7"
57
+ "husky": "^9.1.7",
58
+ "yamllint-js": "^0.2.4"
52
59
  },
53
60
  "peerDependencies": {
54
61
  "eslint": ">=9.0.0"
package/src/index.js CHANGED
@@ -1,26 +1,24 @@
1
- import requireNew from './rules/require-new.js'
2
- import requireErrorSubclassName from './rules/require-error-subclass-name.js'
3
- import noExplicitReturn from './rules/no-explicit-return.js'
4
-
5
- const plugin = {
6
- rules: {
7
- 'require-new': requireNew,
8
- 'require-error-subclass-name': requireErrorSubclassName,
9
- 'no-explicit-return': noExplicitReturn
10
- }
11
- }
12
-
13
- plugin.configs = {
14
- recommended: [
15
- {
16
- plugins: { constructors: plugin },
17
- rules: {
18
- 'constructors/require-new': 'error',
19
- 'constructors/require-error-subclass-name': 'error',
20
- 'constructors/no-explicit-return': 'error'
21
- }
22
- }
23
- ]
24
- }
25
-
26
- export default plugin
1
+ import requireNew from './rules/require-new.js'
2
+ import requireErrorSubclassName from './rules/require-error-subclass-name.js'
3
+ import noExplicitReturn from './rules/no-explicit-return.js'
4
+
5
+ const plugin = {
6
+ rules: {
7
+ 'require-new': requireNew,
8
+ 'require-error-subclass-name': requireErrorSubclassName,
9
+ 'no-explicit-return': noExplicitReturn
10
+ }
11
+ }
12
+
13
+ plugin.configs = {
14
+ recommended: {
15
+ plugins: { constructors: plugin },
16
+ rules: {
17
+ 'constructors/require-new': 'error',
18
+ 'constructors/require-error-subclass-name': 'error',
19
+ 'constructors/no-explicit-return': 'error'
20
+ }
21
+ }
22
+ }
23
+
24
+ export default plugin
@@ -1,37 +1,37 @@
1
- const reFuncNodeType = /^(?:Arrow)?Function(?:Declaration|Expression)$/
2
-
3
- export default {
4
- meta: {
5
- type: 'problem',
6
- docs: {
7
- description: 'Disallow explicit return statements in class constructors',
8
- category: 'Best Practices',
9
- recommended: true
10
- },
11
- schema: [],
12
- messages: { unexpectedReturn: 'Unexpected return statement in constructor' }
13
- },
14
-
15
- create(context) {
16
- function isInsideConstructorFunc(node) {
17
- let current = node.parent
18
- while (current) {
19
- if (reFuncNodeType.test(current.type))
20
- return ( // whether this func is constructor's val
21
- current.parent?.type == 'MethodDefinition'
22
- && current.parent.kind == 'constructor'
23
- && !current.parent.static
24
- )
25
- current = current.parent
26
- }
27
- return false
28
- }
29
-
30
- return {
31
- ReturnStatement(node) {
32
- if (isInsideConstructorFunc(node))
33
- context.report({ node, messageId: 'unexpectedReturn' })
34
- }
35
- }
36
- }
37
- }
1
+ const reFuncNodeType = /^(?:Arrow)?Function(?:Declaration|Expression)$/
2
+
3
+ function isInsideConstructorFunc(node) {
4
+ let current = node.parent
5
+ while (current) {
6
+ if (reFuncNodeType.test(current.type))
7
+ return ( // whether this func is constructor's val
8
+ current.parent?.type == 'MethodDefinition'
9
+ && current.parent.kind == 'constructor'
10
+ && !current.parent.static
11
+ )
12
+ current = current.parent
13
+ }
14
+ return false
15
+ }
16
+
17
+ export default {
18
+ meta: {
19
+ type: 'problem',
20
+ docs: {
21
+ description: 'Disallow explicit return statements in class constructors',
22
+ category: 'Best Practices',
23
+ recommended: true
24
+ },
25
+ schema: [],
26
+ messages: { unexpectedReturn: 'Unexpected return statement in constructor' }
27
+ },
28
+
29
+ create(context) {
30
+ return {
31
+ ReturnStatement(node) {
32
+ if (isInsideConstructorFunc(node))
33
+ context.report({ node, messageId: 'unexpectedReturn' })
34
+ }
35
+ }
36
+ }
37
+ }
@@ -1,39 +1,39 @@
1
- export default {
2
- meta: {
3
- type: 'suggestion',
4
- docs: {
5
- description: 'Require that classes extending Error have names ending in "Error"',
6
- category: 'Best Practices',
7
- recommended: true
8
- },
9
- schema: [],
10
- messages: { endWithError: 'Class extending Error must have a name ending in "Error"' }
11
- },
12
-
13
- create(context) {
14
- function checkName(name, node) {
15
- if (!name || name.endsWith('Error')) return
16
- context.report({ node, messageId: 'endWithError' })
17
- }
18
-
19
- function checkClassNode(node) {
20
- if (!node.id || !node.superClass?.name?.endsWith('Error')) return
21
- checkName(node.id.name, node.id)
22
- }
23
-
24
- function checkVariableDeclarator(node) {
25
- const classExpr = node.init
26
- if (classExpr?.type != 'ClassExpression'
27
- || !classExpr.superClass?.name?.endsWith('Error')
28
- || classExpr.id // named class expression, already checked
29
- ) return
30
- checkName(node.id.name, node.id)
31
- }
32
-
33
- return {
34
- ClassDeclaration: checkClassNode,
35
- ClassExpression: checkClassNode,
36
- VariableDeclarator: checkVariableDeclarator
37
- }
38
- }
39
- }
1
+ export default {
2
+ meta: {
3
+ type: 'suggestion',
4
+ docs: {
5
+ description: 'Require that classes extending Error have names ending in "Error"',
6
+ category: 'Best Practices',
7
+ recommended: true
8
+ },
9
+ schema: [],
10
+ messages: { endWithError: 'Class extending Error must have a name ending in "Error"' }
11
+ },
12
+
13
+ create(context) {
14
+ function checkName(name, node) {
15
+ if (!name || name.endsWith('Error')) return
16
+ context.report({ node, messageId: 'endWithError' })
17
+ }
18
+
19
+ function checkClassNode(node) {
20
+ if (!node.id || !node.superClass?.name?.endsWith('Error')) return
21
+ checkName(node.id.name, node.id)
22
+ }
23
+
24
+ function checkVariableDeclarator(node) {
25
+ const classExpr = node.init
26
+ if (classExpr?.type != 'ClassExpression'
27
+ || !classExpr.superClass?.name?.endsWith('Error')
28
+ || classExpr.id // named class expression, already checked
29
+ ) return
30
+ checkName(node.id.name, node.id)
31
+ }
32
+
33
+ return {
34
+ ClassDeclaration: checkClassNode,
35
+ ClassExpression: checkClassNode,
36
+ VariableDeclarator: checkVariableDeclarator
37
+ }
38
+ }
39
+ }
@@ -1,49 +1,45 @@
1
- const ERR_CONSTRUCTORS = new Set([
2
- 'Error',
3
- 'TypeError',
4
- 'RangeError',
5
- 'SyntaxError',
6
- 'ReferenceError',
7
- 'URIError',
8
- 'EvalError',
9
- 'AggregateError'
10
- ])
11
-
12
- const BUILTIN_CONSTRUCTORS = new Set([
13
- 'Array', 'Boolean', 'Date', 'Function', 'Number', 'Object',
14
- 'RegExp', 'String', 'Symbol', 'BigInt', 'Map', 'Set',
15
- 'WeakMap', 'WeakSet'
16
- ])
17
-
18
- function isErrConstructor(name) { return ERR_CONSTRUCTORS.has(name) }
19
-
20
- export default {
21
- meta: {
22
- type: 'problem',
23
- docs: {
24
- description: 'Require `new` when calling constructor functions',
25
- category: 'Best Practices',
26
- recommended: true
27
- },
28
- fixable: 'code',
29
- schema: [{ enum: ['error-only', 'builtins'] }],
30
- messages: { requireNew: 'Use `new {{name}}()` instead of `{{name}}()`' }
31
- },
32
-
33
- create(context) {
34
- const mode = context.options[0] || 'error-only'
35
- return {
36
- CallExpression(node) {
37
- if (node.new) return
38
- const calleeName = node.callee.name ; if (!calleeName) return
39
- const shouldReport = mode == 'error-only' ? isErrConstructor(calleeName)
40
- : isErrConstructor(calleeName) || BUILTIN_CONSTRUCTORS.has(calleeName)
41
- if (shouldReport)
42
- context.report({
43
- node, messageId: 'requireNew', data: { name: calleeName },
44
- fix(fixer) { return fixer.insertTextBefore(node, 'new ') }
45
- })
46
- }
47
- }
48
- }
49
- }
1
+ const ERR_CONSTRUCTORS = new Set([
2
+ 'AggregateError', 'Error', 'EvalError', 'RangeError', 'ReferenceError', 'SyntaxError', 'TypeError', 'URIError'])
3
+
4
+ const BUILTIN_CONSTRUCTORS = new Set([
5
+ 'Array', 'BigInt', 'Boolean', 'Date', 'Function', 'Number', 'Object', 'RegExp', 'String', 'Symbol', 'Map', 'Set',
6
+ 'WeakMap', 'WeakSet'
7
+ ])
8
+
9
+ function isErrConstructor(name) { return ERR_CONSTRUCTORS.has(name) }
10
+
11
+ export default {
12
+ meta: {
13
+ type: 'problem',
14
+ docs: {
15
+ description: 'Require `new` when calling constructor functions',
16
+ category: 'Best Practices',
17
+ recommended: true
18
+ },
19
+ fixable: 'code',
20
+ schema: [{
21
+ type: 'string',
22
+ enum: ['error-only', 'builtins'],
23
+ description: 'Constructor categories to check: "error-only" (default) or "builtins".'
24
+ }],
25
+ defaultOptions: ['error-only'],
26
+ messages: { requireNew: 'Use `new {{name}}()` instead of `{{name}}()`' }
27
+ },
28
+
29
+ create(context) {
30
+ const mode = context.options[0] || 'error-only'
31
+ return {
32
+ CallExpression(node) {
33
+ if (node.new) return
34
+ const calleeName = node.callee.name ; if (!calleeName) return
35
+ const shouldReport = mode == 'error-only' ? isErrConstructor(calleeName)
36
+ : isErrConstructor(calleeName) || BUILTIN_CONSTRUCTORS.has(calleeName)
37
+ if (shouldReport)
38
+ context.report({
39
+ node, messageId: 'requireNew', data: { name: calleeName },
40
+ fix(fixer) { return fixer.insertTextBefore(node, 'new ') }
41
+ })
42
+ }
43
+ }
44
+ }
45
+ }