@synergyerp/frontend-standards 1.5.9 → 1.6.1
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/.husky/commit-msg +0 -0
- package/.husky/pre-commit +0 -0
- package/.husky/pre-push +0 -0
- package/eslint-rules/require-call-comment.mjs +142 -0
- package/eslint.config.js +48 -0
- package/package.json +13 -12
- package/scripts/check-modularization.mjs +0 -0
- package/scripts/detect-no-verify.sh +0 -0
package/.husky/commit-msg
CHANGED
|
File without changes
|
package/.husky/pre-commit
CHANGED
|
File without changes
|
package/.husky/pre-push
CHANGED
|
File without changes
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
const EXCLUDED_NAMES = new Set([
|
|
2
|
+
'navigate',
|
|
3
|
+
'redirect',
|
|
4
|
+
'dispatch',
|
|
5
|
+
'reload',
|
|
6
|
+
'refresh',
|
|
7
|
+
'toString',
|
|
8
|
+
'toFixed',
|
|
9
|
+
'toLocaleString',
|
|
10
|
+
'preventDefault',
|
|
11
|
+
'stopPropagation',
|
|
12
|
+
]);
|
|
13
|
+
|
|
14
|
+
const ARRAY_METHODS = new Set([
|
|
15
|
+
'map',
|
|
16
|
+
'filter',
|
|
17
|
+
'reduce',
|
|
18
|
+
'forEach',
|
|
19
|
+
'find',
|
|
20
|
+
'some',
|
|
21
|
+
'every',
|
|
22
|
+
'flatMap',
|
|
23
|
+
'flat',
|
|
24
|
+
'includes',
|
|
25
|
+
'indexOf',
|
|
26
|
+
'findIndex',
|
|
27
|
+
]);
|
|
28
|
+
|
|
29
|
+
const TEST_FRAMEWORK = new Set([
|
|
30
|
+
'describe',
|
|
31
|
+
'it',
|
|
32
|
+
'test',
|
|
33
|
+
'expect',
|
|
34
|
+
'beforeEach',
|
|
35
|
+
'afterEach',
|
|
36
|
+
'beforeAll',
|
|
37
|
+
'afterAll',
|
|
38
|
+
'vi',
|
|
39
|
+
'jest',
|
|
40
|
+
]);
|
|
41
|
+
|
|
42
|
+
function isReactHook(name) {
|
|
43
|
+
return /^use[A-Z]/.test(name);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function isReactSetter(name) {
|
|
47
|
+
return /^set[A-Z]/.test(name);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function isTestMethod(name) {
|
|
51
|
+
return TEST_FRAMEWORK.has(name);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function getCallName(callee) {
|
|
55
|
+
if (!callee) return null;
|
|
56
|
+
if (callee.type === 'Identifier') return callee.name;
|
|
57
|
+
if (callee.type === 'MemberExpression' && callee.property.type === 'Identifier') {
|
|
58
|
+
return callee.property.name;
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export default {
|
|
64
|
+
meta: {
|
|
65
|
+
type: 'suggestion',
|
|
66
|
+
docs: {
|
|
67
|
+
description: 'Require an inline comment before function calls that perform complex logic.',
|
|
68
|
+
},
|
|
69
|
+
messages: {
|
|
70
|
+
missingComment: 'Add a brief inline comment describing what this function call does.',
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
create(context) {
|
|
74
|
+
const sourceCode = context.sourceCode || context.getSourceCode();
|
|
75
|
+
|
|
76
|
+
function hasCommentOnLineAboveOrSame(node) {
|
|
77
|
+
const line = node.loc.start.line;
|
|
78
|
+
const comments = sourceCode.getAllComments();
|
|
79
|
+
return comments.some(
|
|
80
|
+
(comment) => comment.loc.end.line === line || comment.loc.end.line === line - 1
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function isExcludedCall(node) {
|
|
85
|
+
if (node.parent?.type === 'JSXExpressionContainer') return true;
|
|
86
|
+
|
|
87
|
+
const callee = node.callee;
|
|
88
|
+
const name = getCallName(callee);
|
|
89
|
+
if (!name) return true;
|
|
90
|
+
|
|
91
|
+
if (isReactHook(name)) return true;
|
|
92
|
+
if (isReactSetter(name)) return true;
|
|
93
|
+
if (isTestMethod(name)) return true;
|
|
94
|
+
if (EXCLUDED_NAMES.has(name)) return true;
|
|
95
|
+
|
|
96
|
+
if (ARRAY_METHODS.has(name)) {
|
|
97
|
+
if (callee.type === 'MemberExpression') return true;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (
|
|
101
|
+
callee.type === 'MemberExpression' &&
|
|
102
|
+
callee.object?.type === 'Identifier' &&
|
|
103
|
+
callee.object.name === 'console'
|
|
104
|
+
) {
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (
|
|
109
|
+
callee.type === 'MemberExpression' &&
|
|
110
|
+
callee.object?.type === 'Identifier' &&
|
|
111
|
+
callee.object.name === 'Math'
|
|
112
|
+
) {
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function checkCall(node) {
|
|
120
|
+
if (!node || node.type !== 'CallExpression') return;
|
|
121
|
+
if (isExcludedCall(node)) return;
|
|
122
|
+
if (hasCommentOnLineAboveOrSame(node)) return;
|
|
123
|
+
|
|
124
|
+
context.report({ node, messageId: 'missingComment' });
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
'ExpressionStatement > CallExpression'(node) {
|
|
129
|
+
checkCall(node);
|
|
130
|
+
},
|
|
131
|
+
'ExpressionStatement > AwaitExpression > CallExpression'(node) {
|
|
132
|
+
checkCall(node);
|
|
133
|
+
},
|
|
134
|
+
'VariableDeclarator > CallExpression'(node) {
|
|
135
|
+
checkCall(node);
|
|
136
|
+
},
|
|
137
|
+
'VariableDeclarator > AwaitExpression > CallExpression'(node) {
|
|
138
|
+
checkCall(node);
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
},
|
|
142
|
+
};
|
package/eslint.config.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import js from '@eslint/js';
|
|
2
2
|
import boundaries from 'eslint-plugin-boundaries';
|
|
3
3
|
import importPlugin from 'eslint-plugin-import';
|
|
4
|
+
import jsdoc from 'eslint-plugin-jsdoc';
|
|
4
5
|
import jsxA11y from 'eslint-plugin-jsx-a11y';
|
|
5
6
|
import prettierPlugin from 'eslint-plugin-prettier/recommended';
|
|
6
7
|
import reactPlugin from 'eslint-plugin-react';
|
|
@@ -9,6 +10,7 @@ import reactRefresh from 'eslint-plugin-react-refresh';
|
|
|
9
10
|
import unicorn from 'eslint-plugin-unicorn';
|
|
10
11
|
import globals from 'globals';
|
|
11
12
|
import tseslint from 'typescript-eslint';
|
|
13
|
+
import requireCallComment from './eslint-rules/require-call-comment.mjs';
|
|
12
14
|
|
|
13
15
|
export default tseslint.config(
|
|
14
16
|
// ===== BASE: Ignore patterns =====
|
|
@@ -45,9 +47,11 @@ export default tseslint.config(
|
|
|
45
47
|
'react-hooks': reactHooks,
|
|
46
48
|
'react-refresh': reactRefresh,
|
|
47
49
|
import: importPlugin,
|
|
50
|
+
jsdoc: jsdoc,
|
|
48
51
|
unicorn: unicorn,
|
|
49
52
|
'jsx-a11y': jsxA11y,
|
|
50
53
|
boundaries: boundaries,
|
|
54
|
+
custom: { rules: { 'require-call-comment': requireCallComment } },
|
|
51
55
|
},
|
|
52
56
|
languageOptions: {
|
|
53
57
|
ecmaVersion: 'latest',
|
|
@@ -204,6 +208,38 @@ export default tseslint.config(
|
|
|
204
208
|
'react-hooks/exhaustive-deps': 'warn',
|
|
205
209
|
'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
|
|
206
210
|
|
|
211
|
+
// ===== JSDOC (FRONTEND_STANDARDS.md Section on Documentation) =====
|
|
212
|
+
'jsdoc/require-jsdoc': [
|
|
213
|
+
'error',
|
|
214
|
+
{
|
|
215
|
+
contexts: [
|
|
216
|
+
'ExportNamedDeclaration > VariableDeclarator > ArrowFunctionExpression',
|
|
217
|
+
'ExportNamedDeclaration > FunctionDeclaration',
|
|
218
|
+
'VariableDeclarator[id.type="Identifier"] > ArrowFunctionExpression',
|
|
219
|
+
'VariableDeclarator[id.type="Identifier"] > FunctionExpression',
|
|
220
|
+
'FunctionDeclaration',
|
|
221
|
+
'MethodDefinition',
|
|
222
|
+
],
|
|
223
|
+
checkConstructors: false,
|
|
224
|
+
enableFixer: false,
|
|
225
|
+
},
|
|
226
|
+
],
|
|
227
|
+
'jsdoc/require-description': ['error', { contexts: ['any'] }],
|
|
228
|
+
'jsdoc/no-missing-syntax': 'off',
|
|
229
|
+
'jsdoc/multiline-blocks': ['error', { noMultilineBlocks: true, noSingleLineBlocks: false }],
|
|
230
|
+
'jsdoc/require-param': ['error', { contexts: ['any'] }],
|
|
231
|
+
'jsdoc/require-param-name': 'error',
|
|
232
|
+
'jsdoc/require-param-type': 'off',
|
|
233
|
+
'jsdoc/require-returns': ['error', { contexts: ['any'] }],
|
|
234
|
+
'jsdoc/require-returns-type': 'off',
|
|
235
|
+
'jsdoc/require-returns-check': 'error',
|
|
236
|
+
'jsdoc/check-tag-names': 'error',
|
|
237
|
+
'jsdoc/check-types': 'error',
|
|
238
|
+
'jsdoc/empty-tags': 'error',
|
|
239
|
+
|
|
240
|
+
// ===== DOCUMENTATION: Inline call comments =====
|
|
241
|
+
'custom/require-call-comment': 'error',
|
|
242
|
+
|
|
207
243
|
// ===== IMPORTS (FRONTEND_STANDARDS.md Section 5.3) =====
|
|
208
244
|
'import/order': [
|
|
209
245
|
'error',
|
|
@@ -333,6 +369,12 @@ export default tseslint.config(
|
|
|
333
369
|
'no-console': 'off',
|
|
334
370
|
'@typescript-eslint/no-explicit-any': 'off',
|
|
335
371
|
'import/no-internal-modules': 'off',
|
|
372
|
+
'custom/require-call-comment': 'off',
|
|
373
|
+
'jsdoc/require-jsdoc': 'off',
|
|
374
|
+
'jsdoc/require-description': 'off',
|
|
375
|
+
'jsdoc/require-param': 'off',
|
|
376
|
+
'jsdoc/require-returns': 'off',
|
|
377
|
+
'jsdoc/require-returns-check': 'off',
|
|
336
378
|
},
|
|
337
379
|
},
|
|
338
380
|
|
|
@@ -342,6 +384,12 @@ export default tseslint.config(
|
|
|
342
384
|
rules: {
|
|
343
385
|
'@typescript-eslint/no-explicit-any': 'off',
|
|
344
386
|
'import/no-internal-modules': 'off',
|
|
387
|
+
'custom/require-call-comment': 'off',
|
|
388
|
+
'jsdoc/require-jsdoc': 'off',
|
|
389
|
+
'jsdoc/require-description': 'off',
|
|
390
|
+
'jsdoc/require-param': 'off',
|
|
391
|
+
'jsdoc/require-returns': 'off',
|
|
392
|
+
'jsdoc/require-returns-check': 'off',
|
|
345
393
|
},
|
|
346
394
|
}
|
|
347
395
|
);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@synergyerp/frontend-standards",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.1",
|
|
4
4
|
"description": "SynergyERP frontend standards — ESLint, Prettier, commitlint, Husky, Vitest, TypeScript configs, modularization enforcement, and PR templates.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
},
|
|
12
12
|
"files": [
|
|
13
13
|
"eslint.config.js",
|
|
14
|
+
"eslint-rules/",
|
|
14
15
|
"prettier.config.js",
|
|
15
16
|
".prettierignore",
|
|
16
17
|
"commitlint.config.js",
|
|
@@ -24,16 +25,6 @@
|
|
|
24
25
|
"FRONTEND_STANDARDS.md",
|
|
25
26
|
"CICD_STANDARDS.md"
|
|
26
27
|
],
|
|
27
|
-
"scripts": {
|
|
28
|
-
"lint": "eslint .",
|
|
29
|
-
"format": "prettier --write .",
|
|
30
|
-
"format:check": "prettier --check .",
|
|
31
|
-
"check-modularization": "node scripts/check-modularization.mjs; exit 0",
|
|
32
|
-
"check-types": "tsc --noEmit; exit 0",
|
|
33
|
-
"test:ci": "exit 0",
|
|
34
|
-
"test": "exit 0",
|
|
35
|
-
"prepare": "echo 'Standards package ready'"
|
|
36
|
-
},
|
|
37
28
|
"repository": {
|
|
38
29
|
"type": "git",
|
|
39
30
|
"url": "git+https://github.com/aoholdings/frontend-standards.git"
|
|
@@ -69,6 +60,7 @@
|
|
|
69
60
|
"eslint-plugin-react": "^7.37.0",
|
|
70
61
|
"eslint-plugin-react-hooks": "^5.0.0",
|
|
71
62
|
"eslint-plugin-react-refresh": "^0.4.0",
|
|
63
|
+
"eslint-plugin-jsdoc": "^50.0.0",
|
|
72
64
|
"eslint-plugin-unicorn": "^56.0.0",
|
|
73
65
|
"globals": "^15.0.0",
|
|
74
66
|
"prettier-plugin-organize-imports": "^4.0.0",
|
|
@@ -81,5 +73,14 @@
|
|
|
81
73
|
"typescript": "^5.5.0",
|
|
82
74
|
"vitest": "^3.0.0",
|
|
83
75
|
"@types/node": "^22.0.0"
|
|
76
|
+
},
|
|
77
|
+
"scripts": {
|
|
78
|
+
"lint": "eslint .",
|
|
79
|
+
"format": "prettier --write .",
|
|
80
|
+
"format:check": "prettier --check .",
|
|
81
|
+
"check-modularization": "node scripts/check-modularization.mjs; exit 0",
|
|
82
|
+
"check-types": "tsc --noEmit; exit 0",
|
|
83
|
+
"test:ci": "exit 0",
|
|
84
|
+
"test": "exit 0"
|
|
84
85
|
}
|
|
85
|
-
}
|
|
86
|
+
}
|
|
File without changes
|
|
File without changes
|