@pjts/eslint-style-rules 0.0.8 → 0.0.9
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/LICENSE +21 -0
- package/README.md +38 -8
- package/dist/index.js +35 -4
- package/dist/index.js.map +1 -1
- package/dist/lib/ast.d.ts +27 -13
- package/dist/lib/ast.js +33 -7
- package/dist/lib/ast.js.map +1 -1
- package/dist/lib/db.d.ts +5 -4
- package/dist/lib/db.js +2 -2
- package/dist/lib/db.js.map +1 -1
- package/dist/lib/rule.d.ts +8 -6
- package/dist/lib/rule.js +3 -3
- package/dist/lib/rule.js.map +1 -1
- package/dist/lib/types.d.ts +69 -0
- package/dist/lib/types.js +270 -0
- package/dist/lib/types.js.map +1 -0
- package/dist/lib/wiring.d.ts +5 -3
- package/dist/lib/wiring.js +8 -4
- package/dist/lib/wiring.js.map +1 -1
- package/dist/rules/app-class-suffix.d.ts +1 -1
- package/dist/rules/app-class-suffix.js +1 -1
- package/dist/rules/app-class-suffix.js.map +1 -1
- package/dist/rules/app-service-endpoints.d.ts +3 -1
- package/dist/rules/app-service-endpoints.js +9 -8
- package/dist/rules/app-service-endpoints.js.map +1 -1
- package/dist/rules/auth-access.d.ts +2 -2
- package/dist/rules/auth-access.js +11 -10
- package/dist/rules/auth-access.js.map +1 -1
- package/dist/rules/config-provider-only.d.ts +1 -1
- package/dist/rules/config-provider-only.js +5 -3
- package/dist/rules/config-provider-only.js.map +1 -1
- package/dist/rules/db-client-dependency.d.ts +1 -1
- package/dist/rules/db-client-dependency.js +11 -7
- package/dist/rules/db-client-dependency.js.map +1 -1
- package/dist/rules/db-writes-in-repositories.d.ts +1 -1
- package/dist/rules/db-writes-in-repositories.js +12 -8
- package/dist/rules/db-writes-in-repositories.js.map +1 -1
- package/dist/rules/decorated-class-contract.d.ts +1 -1
- package/dist/rules/decorated-class-contract.js +1 -1
- package/dist/rules/decorated-class-contract.js.map +1 -1
- package/dist/rules/domain-primitive-params.d.ts +17 -0
- package/dist/rules/domain-primitive-params.js +116 -0
- package/dist/rules/domain-primitive-params.js.map +1 -0
- package/dist/rules/entity-constructor-data.d.ts +1 -1
- package/dist/rules/entity-constructor-data.js +4 -3
- package/dist/rules/entity-constructor-data.js.map +1 -1
- package/dist/rules/event-sourced-aggregate.d.ts +2 -2
- package/dist/rules/event-sourced-aggregate.js +8 -8
- package/dist/rules/event-sourced-aggregate.js.map +1 -1
- package/dist/rules/find-one-naming.d.ts +3 -2
- package/dist/rules/find-one-naming.js +35 -8
- package/dist/rules/find-one-naming.js.map +1 -1
- package/dist/rules/implements-ports-only.d.ts +10 -0
- package/dist/rules/implements-ports-only.js +74 -0
- package/dist/rules/implements-ports-only.js.map +1 -0
- package/dist/rules/import-only-in.d.ts +1 -1
- package/dist/rules/import-only-in.js +7 -3
- package/dist/rules/import-only-in.js.map +1 -1
- package/dist/rules/knex-first.d.ts +2 -1
- package/dist/rules/knex-first.js +11 -4
- package/dist/rules/knex-first.js.map +1 -1
- package/dist/rules/message-file.d.ts +1 -1
- package/dist/rules/message-file.js +3 -2
- package/dist/rules/message-file.js.map +1 -1
- package/dist/rules/message-handler.d.ts +1 -1
- package/dist/rules/message-handler.js +7 -6
- package/dist/rules/message-handler.js.map +1 -1
- package/dist/rules/message-name-reference.d.ts +2 -1
- package/dist/rules/message-name-reference.js +24 -5
- package/dist/rules/message-name-reference.js.map +1 -1
- package/dist/rules/message-names.d.ts +1 -1
- package/dist/rules/message-names.js +2 -2
- package/dist/rules/message-names.js.map +1 -1
- package/dist/rules/module-layout.d.ts +1 -1
- package/dist/rules/module-layout.js +1 -1
- package/dist/rules/module-layout.js.map +1 -1
- package/dist/rules/module-service-getters.d.ts +1 -1
- package/dist/rules/module-service-getters.js +11 -9
- package/dist/rules/module-service-getters.js.map +1 -1
- package/dist/rules/module-shape.d.ts +1 -1
- package/dist/rules/module-shape.js +6 -7
- package/dist/rules/module-shape.js.map +1 -1
- package/dist/rules/no-async-in-domain.d.ts +1 -1
- package/dist/rules/no-async-in-domain.js +2 -2
- package/dist/rules/no-async-in-domain.js.map +1 -1
- package/dist/rules/no-duplicate-sdk-types.d.ts +1 -1
- package/dist/rules/no-duplicate-sdk-types.js +1 -1
- package/dist/rules/no-duplicate-sdk-types.js.map +1 -1
- package/dist/rules/no-entity-assembly-in-app.d.ts +4 -2
- package/dist/rules/no-entity-assembly-in-app.js +24 -8
- package/dist/rules/no-entity-assembly-in-app.js.map +1 -1
- package/dist/rules/no-entity-mutation-outside-domain.d.ts +1 -1
- package/dist/rules/no-entity-mutation-outside-domain.js +1 -1
- package/dist/rules/no-entity-mutation-outside-domain.js.map +1 -1
- package/dist/rules/no-manual-wiring.d.ts +1 -1
- package/dist/rules/no-manual-wiring.js +5 -2
- package/dist/rules/no-manual-wiring.js.map +1 -1
- package/dist/rules/no-message-construction-outside-domain.d.ts +1 -1
- package/dist/rules/no-message-construction-outside-domain.js +1 -1
- package/dist/rules/no-message-construction-outside-domain.js.map +1 -1
- package/dist/rules/no-reconstitute.d.ts +1 -1
- package/dist/rules/no-reconstitute.js +1 -1
- package/dist/rules/no-reconstitute.js.map +1 -1
- package/dist/rules/one-class-per-file.d.ts +7 -0
- package/dist/rules/one-class-per-file.js +55 -0
- package/dist/rules/one-class-per-file.js.map +1 -0
- package/dist/rules/port-file.d.ts +1 -1
- package/dist/rules/port-file.js +3 -2
- package/dist/rules/port-file.js.map +1 -1
- package/dist/rules/port-has-adapters.d.ts +1 -1
- package/dist/rules/port-has-adapters.js +1 -1
- package/dist/rules/port-has-adapters.js.map +1 -1
- package/dist/rules/repository-naming.d.ts +1 -1
- package/dist/rules/repository-naming.js +2 -2
- package/dist/rules/repository-naming.js.map +1 -1
- package/dist/rules/repository-return-types.d.ts +10 -0
- package/dist/rules/repository-return-types.js +67 -0
- package/dist/rules/repository-return-types.js.map +1 -0
- package/dist/rules/repository-serialization.d.ts +13 -3
- package/dist/rules/repository-serialization.js +108 -12
- package/dist/rules/repository-serialization.js.map +1 -1
- package/dist/rules/return-entities.d.ts +1 -1
- package/dist/rules/return-entities.js +3 -3
- package/dist/rules/return-entities.js.map +1 -1
- package/dist/rules/save-contract.d.ts +5 -3
- package/dist/rules/save-contract.js +18 -5
- package/dist/rules/save-contract.js.map +1 -1
- package/dist/rules/subclass-declares-constructor.d.ts +1 -1
- package/dist/rules/subclass-declares-constructor.js +1 -1
- package/dist/rules/subclass-declares-constructor.js.map +1 -1
- package/dist/rules/test-location.d.ts +1 -1
- package/dist/rules/test-location.js +1 -1
- package/dist/rules/test-location.js.map +1 -1
- package/dist/rules/throw-app-error.d.ts +1 -1
- package/dist/rules/throw-app-error.js +4 -3
- package/dist/rules/throw-app-error.js.map +1 -1
- package/dist/rules/trackable-repository.d.ts +1 -1
- package/dist/rules/trackable-repository.js +4 -4
- package/dist/rules/trackable-repository.js.map +1 -1
- package/dist/rules/unique-class-names.d.ts +1 -1
- package/dist/rules/unique-class-names.js +5 -4
- package/dist/rules/unique-class-names.js.map +1 -1
- package/package.json +18 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-provider-only.js","sourceRoot":"","sources":["../../src/rules/config-provider-only.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"config-provider-only.js","sourceRoot":"","sources":["../../src/rules/config-provider-only.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,5 +7,5 @@
|
|
|
7
7
|
* Options: `allowedClasses` — name patterns of the classes allowed to depend on it
|
|
8
8
|
* (default `['^([A-Z][a-z]+)*Pg[A-Z]\\w*Repository$']` — `Pg` anywhere in a combined prefix: PgX, PgNatsX, NatsPgX), `clients` — `module#ImportedName` specs.
|
|
9
9
|
*/
|
|
10
|
-
declare const _default: import("eslint").
|
|
10
|
+
declare const _default: import("@typescript-eslint/utils/dist/ts-eslint").RuleModule<string, any[], unknown, import("@typescript-eslint/utils/dist/ts-eslint").RuleListener>;
|
|
11
11
|
export default _default;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const db_1 = require("../lib/db");
|
|
4
|
+
const ast_1 = require("../lib/ast");
|
|
4
5
|
const rule_1 = require("../lib/rule");
|
|
5
6
|
/**
|
|
6
7
|
* The database client (`PostgresDbClient`, knex, pg) is a dependency of `…Pg…Repository` classes only.
|
|
@@ -14,7 +15,7 @@ const rule_1 = require("../lib/rule");
|
|
|
14
15
|
exports.default = (0, rule_1.defineRule)({
|
|
15
16
|
meta: {
|
|
16
17
|
type: 'problem',
|
|
17
|
-
docs: { description: 'Only …Pg…Repository classes may depend on the database client' },
|
|
18
|
+
docs: { description: 'Only …Pg…Repository classes may depend on the database client', url: 'https://github.com/PiotrJozefow/pjx/tree/master/modules/eslint-style-rules#db-client-dependency' },
|
|
18
19
|
messages: {
|
|
19
20
|
classDependency: '"{{name}}" depends on {{client}} — only {{allowed}} classes may. Go through a repository port, or make this a {{allowed}} in infrastructure/repositories/pg/.',
|
|
20
21
|
functionDependency: 'Function "{{name}}" takes {{client}} — only {{allowed}} classes may depend on the database client.',
|
|
@@ -34,8 +35,9 @@ exports.default = (0, rule_1.defineRule)({
|
|
|
34
35
|
const allowedLabel = context.options[0]?.allowedClasses ? allowedClasses.join(' / ') : '…Pg…Repository';
|
|
35
36
|
const tracker = (0, db_1.clientTypeTracker)(clients);
|
|
36
37
|
const clientName = (annotation) => context.sourceCode.getText(annotation.typeAnnotation ?? annotation);
|
|
38
|
+
const annotationOf = (node) => ('typeAnnotation' in node ? node.typeAnnotation : undefined);
|
|
37
39
|
/** First client-typed parameter of a function, if any. */
|
|
38
|
-
const clientParam = (fn) => fn.params.map(db_1.paramBinding).find((binding) => tracker.usesClient(binding
|
|
40
|
+
const clientParam = (fn) => fn.params.map(db_1.paramBinding).find((binding) => tracker.usesClient(annotationOf(binding)));
|
|
39
41
|
const checkClass = (classNode) => {
|
|
40
42
|
const name = classNode.id?.name ?? '(anonymous class)';
|
|
41
43
|
if (allowed.some((pattern) => pattern.test(name)))
|
|
@@ -44,10 +46,12 @@ exports.default = (0, rule_1.defineRule)({
|
|
|
44
46
|
let annotation = null;
|
|
45
47
|
if (member.type === 'PropertyDefinition' && tracker.usesClient(member.typeAnnotation))
|
|
46
48
|
annotation = member.typeAnnotation;
|
|
47
|
-
if (member.type === 'MethodDefinition')
|
|
48
|
-
|
|
49
|
+
if (member.type === 'MethodDefinition') {
|
|
50
|
+
const param = member.value.type === 'FunctionExpression' ? clientParam(member.value) : undefined;
|
|
51
|
+
annotation = param ? annotationOf(param) ?? null : null;
|
|
52
|
+
}
|
|
49
53
|
if (annotation) {
|
|
50
|
-
context.report({ node: member
|
|
54
|
+
context.report({ node: (0, ast_1.memberKey)(member) ?? member, messageId: 'classDependency', data: { name, client: clientName(annotation), allowed: allowedLabel } });
|
|
51
55
|
return; // one report per class is enough
|
|
52
56
|
}
|
|
53
57
|
}
|
|
@@ -58,8 +62,8 @@ exports.default = (0, rule_1.defineRule)({
|
|
|
58
62
|
const binding = clientParam(fn);
|
|
59
63
|
if (!binding)
|
|
60
64
|
return;
|
|
61
|
-
const name = fn.id?.name ?? (fn.parent.type === 'VariableDeclarator' ? fn.parent.id.name : '(anonymous)');
|
|
62
|
-
context.report({ node: binding, messageId: 'functionDependency', data: { name, client: clientName(binding
|
|
65
|
+
const name = fn.id?.name ?? (fn.parent.type === 'VariableDeclarator' && fn.parent.id.type === 'Identifier' ? fn.parent.id.name : '(anonymous)');
|
|
66
|
+
context.report({ node: binding, messageId: 'functionDependency', data: { name, client: clientName(annotationOf(binding)), allowed: allowedLabel } });
|
|
63
67
|
};
|
|
64
68
|
return {
|
|
65
69
|
ImportDeclaration: (node) => tracker.onImport(node),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db-client-dependency.js","sourceRoot":"","sources":["../../src/rules/db-client-dependency.ts"],"names":[],"mappings":";;AAAA,kCAA6E;
|
|
1
|
+
{"version":3,"file":"db-client-dependency.js","sourceRoot":"","sources":["../../src/rules/db-client-dependency.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,5 +8,5 @@
|
|
|
8
8
|
* Options: `allowedClasses` (default `['^([A-Z][a-z]+)*Pg[A-Z]\\w*Repository$']` — `Pg` anywhere in a combined prefix: PgX, PgNatsX, NatsPgX), `allowedPath` (default
|
|
9
9
|
* `/infrastructure/`), `clients`.
|
|
10
10
|
*/
|
|
11
|
-
declare const _default: import("eslint").
|
|
11
|
+
declare const _default: import("@typescript-eslint/utils/dist/ts-eslint").RuleModule<string, any[], unknown, import("@typescript-eslint/utils/dist/ts-eslint").RuleListener>;
|
|
12
12
|
export default _default;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const db_1 = require("../lib/db");
|
|
4
4
|
const ast_1 = require("../lib/ast");
|
|
5
|
+
const ast_2 = require("../lib/ast");
|
|
5
6
|
const rule_1 = require("../lib/rule");
|
|
6
7
|
const WRITE_METHODS = new Set(['insert', 'update', 'del', 'delete', 'truncate', 'upsert', 'increment', 'decrement', 'onConflict', 'transaction']);
|
|
7
8
|
const WRITE_SQL = /^\s*(insert|update|delete|truncate|merge|upsert|alter|drop|create|grant|revoke|copy)\b/i;
|
|
@@ -18,7 +19,7 @@ const WRITE_SQL = /^\s*(insert|update|delete|truncate|merge|upsert|alter|drop|cr
|
|
|
18
19
|
exports.default = (0, rule_1.defineRule)({
|
|
19
20
|
meta: {
|
|
20
21
|
type: 'problem',
|
|
21
|
-
docs: { description: 'Allow database writes only in infrastructure …Pg…Repository classes' },
|
|
22
|
+
docs: { description: 'Allow database writes only in infrastructure …Pg…Repository classes', url: 'https://github.com/PiotrJozefow/pjx/tree/master/modules/eslint-style-rules#db-writes-in-repositories' },
|
|
22
23
|
messages: {
|
|
23
24
|
write: '`{{method}}` writes to the database — writes belong in a …Pg…Repository in infrastructure/{{where}}.',
|
|
24
25
|
rawWrite: 'This raw SQL writes to the database — writes belong in a …Pg…Repository in infrastructure/{{where}}.',
|
|
@@ -53,13 +54,16 @@ exports.default = (0, rule_1.defineRule)({
|
|
|
53
54
|
for (;;) {
|
|
54
55
|
if (current.type === 'CallExpression')
|
|
55
56
|
current = current.callee;
|
|
56
|
-
else if (current.type === 'AwaitExpression'
|
|
57
|
-
current = current.argument
|
|
57
|
+
else if (current.type === 'AwaitExpression')
|
|
58
|
+
current = current.argument;
|
|
59
|
+
else if (current.type === 'TSNonNullExpression')
|
|
60
|
+
current = current.expression;
|
|
58
61
|
else if (current.type === 'MemberExpression') {
|
|
59
|
-
|
|
62
|
+
const property = (0, ast_2.keyName)(current.property);
|
|
63
|
+
if (!current.computed && property === 'postgresDbClient')
|
|
60
64
|
return true;
|
|
61
65
|
if (current.object.type === 'ThisExpression')
|
|
62
|
-
return !current.computed && handles.has(
|
|
66
|
+
return !current.computed && property !== null && handles.has(property);
|
|
63
67
|
current = current.object;
|
|
64
68
|
}
|
|
65
69
|
else
|
|
@@ -83,9 +87,9 @@ exports.default = (0, rule_1.defineRule)({
|
|
|
83
87
|
handles.add(node.id.name);
|
|
84
88
|
},
|
|
85
89
|
'CallExpression[callee.type="MemberExpression"][callee.computed=false]'(node) {
|
|
86
|
-
if (!isHandleRoot(node.callee.object))
|
|
90
|
+
if (node.callee.type !== 'MemberExpression' || !isHandleRoot(node.callee.object))
|
|
87
91
|
return;
|
|
88
|
-
const method = node.callee.property
|
|
92
|
+
const method = (0, ast_2.keyName)(node.callee.property) ?? '';
|
|
89
93
|
if (WRITE_METHODS.has(method)) {
|
|
90
94
|
const where = misplaced(node);
|
|
91
95
|
if (where !== null)
|
|
@@ -93,7 +97,7 @@ exports.default = (0, rule_1.defineRule)({
|
|
|
93
97
|
}
|
|
94
98
|
else if (method === 'raw') {
|
|
95
99
|
const sql = node.arguments[0];
|
|
96
|
-
const text = sql?.type === 'Literal' ? String(sql.value) : sql?.type === 'TemplateLiteral' ? sql.quasis[0].value.cooked : '';
|
|
100
|
+
const text = sql?.type === 'Literal' ? String(sql.value) : sql?.type === 'TemplateLiteral' ? sql.quasis[0].value.cooked ?? '' : '';
|
|
97
101
|
const where = WRITE_SQL.test(text) ? misplaced(node) : null;
|
|
98
102
|
if (where !== null)
|
|
99
103
|
context.report({ node: sql, messageId: 'rawWrite', data: { where } });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db-writes-in-repositories.js","sourceRoot":"","sources":["../../src/rules/db-writes-in-repositories.ts"],"names":[],"mappings":";;AAAA,kCAA6E;AAC7E,oCAAgD;
|
|
1
|
+
{"version":3,"file":"db-writes-in-repositories.js","sourceRoot":"","sources":["../../src/rules/db-writes-in-repositories.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
* Classes carrying a configured decorator must implement the listed methods.
|
|
3
3
|
* Options: `{ contracts: { StringDomainPrimitive: ['toJSON', 'static fromUnknown'] } }`.
|
|
4
4
|
*/
|
|
5
|
-
declare const _default: import("eslint").
|
|
5
|
+
declare const _default: import("@typescript-eslint/utils/dist/ts-eslint").RuleModule<string, any[], unknown, import("@typescript-eslint/utils/dist/ts-eslint").RuleListener>;
|
|
6
6
|
export default _default;
|
|
@@ -9,7 +9,7 @@ const rule_1 = require("../lib/rule");
|
|
|
9
9
|
exports.default = (0, rule_1.defineRule)({
|
|
10
10
|
meta: {
|
|
11
11
|
type: 'problem',
|
|
12
|
-
docs: { description: 'Require methods on classes that carry a given decorator' },
|
|
12
|
+
docs: { description: 'Require methods on classes that carry a given decorator', url: 'https://github.com/PiotrJozefow/pjx/tree/master/modules/eslint-style-rules#decorated-class-contract' },
|
|
13
13
|
messages: { missing: '@{{decorator}} class "{{name}}" must implement `{{method}}`.' },
|
|
14
14
|
schema: [{
|
|
15
15
|
type: 'object',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decorated-class-contract.js","sourceRoot":"","sources":["../../src/rules/decorated-class-contract.ts"],"names":[],"mappings":";;AAAA,oCAAwD;AACxD,sCAAyC;AAEzC;;;GAGG;AACH,kBAAe,IAAA,iBAAU,EAAC;IACxB,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,IAAI,EAAE,EAAE,WAAW,EAAE,yDAAyD,EAAE;
|
|
1
|
+
{"version":3,"file":"decorated-class-contract.js","sourceRoot":"","sources":["../../src/rules/decorated-class-contract.ts"],"names":[],"mappings":";;AAAA,oCAAwD;AACxD,sCAAyC;AAEzC;;;GAGG;AACH,kBAAe,IAAA,iBAAU,EAAC;IACxB,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,IAAI,EAAE,EAAE,WAAW,EAAE,yDAAyD,EAAE,GAAG,EAAE,qGAAqG,EAAE;QAC5L,QAAQ,EAAE,EAAE,OAAO,EAAE,8DAA8D,EAAE;QACrF,MAAM,EAAE,CAAC;gBACP,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE,EAAE,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,oBAAoB,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE;gBACjH,oBAAoB,EAAE,KAAK;aAC5B,CAAC;KACH;IACD,MAAM,CAAC,OAAO;QACZ,MAAM,SAAS,GAA6B,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,SAAS,IAAI,EAAE,CAAC;QAChF,OAAO;YACL,gBAAgB,CAAC,IAAI;gBACnB,KAAK,MAAM,SAAS,IAAI,IAAA,oBAAc,EAAC,IAAI,CAAC,EAAE,CAAC;oBAC7C,KAAK,MAAM,IAAI,IAAI,SAAS,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,CAAC;wBAC9C,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;wBAC5C,MAAM,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;wBAC9D,IAAI,CAAC,IAAA,gBAAU,EAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,CAAC;4BACxC,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;wBAC1H,CAAC;oBACH,CAAC;gBACH,CAAC;YACH,CAAC;SACF,CAAC;IACJ,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parameters crossing a function boundary are domain classes, never native or plain types:
|
|
3
|
+
* no `string` / `number` / `boolean` / `bigint`, literals and literal unions, inline object types,
|
|
4
|
+
* `Date`, `Record<…>`, plain `interface` / `type` objects or enums (arrays and unions of those too).
|
|
5
|
+
*
|
|
6
|
+
* Checked: class methods and constructors (incl. parameter properties), class-field arrow
|
|
7
|
+
* functions, function declarations and module-level function constants, interface method
|
|
8
|
+
* signatures. Not checked: callbacks and arrows inside function bodies, constructors and static
|
|
9
|
+
* methods of classes under `…/values/` (domain primitives build themselves from raw values), private
|
|
10
|
+
* `deserialize…` methods under `infrastructure/` (they take the row).
|
|
11
|
+
*
|
|
12
|
+
* Accepted: classes, generics, function types, names ending with an `allowTypeSuffixes` entry
|
|
13
|
+
* (`…Port`, `…Config`, `…Data`), types declared under `allowTypesFrom` (message bodies and
|
|
14
|
+
* `MessageNames`), and names that don't resolve to a project file (package types).
|
|
15
|
+
*/
|
|
16
|
+
declare const _default: import("@typescript-eslint/utils/dist/ts-eslint").RuleModule<string, any[], unknown, import("@typescript-eslint/utils/dist/ts-eslint").RuleListener>;
|
|
17
|
+
export default _default;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const ast_1 = require("../lib/ast");
|
|
4
|
+
const rule_1 = require("../lib/rule");
|
|
5
|
+
const types_1 = require("../lib/types");
|
|
6
|
+
const FUNCTION_TYPES = new Set(['FunctionDeclaration', 'FunctionExpression', 'ArrowFunctionExpression', 'TSDeclareFunction', 'TSEmptyBodyFunctionExpression']);
|
|
7
|
+
/**
|
|
8
|
+
* Parameters crossing a function boundary are domain classes, never native or plain types:
|
|
9
|
+
* no `string` / `number` / `boolean` / `bigint`, literals and literal unions, inline object types,
|
|
10
|
+
* `Date`, `Record<…>`, plain `interface` / `type` objects or enums (arrays and unions of those too).
|
|
11
|
+
*
|
|
12
|
+
* Checked: class methods and constructors (incl. parameter properties), class-field arrow
|
|
13
|
+
* functions, function declarations and module-level function constants, interface method
|
|
14
|
+
* signatures. Not checked: callbacks and arrows inside function bodies, constructors and static
|
|
15
|
+
* methods of classes under `…/values/` (domain primitives build themselves from raw values), private
|
|
16
|
+
* `deserialize…` methods under `infrastructure/` (they take the row).
|
|
17
|
+
*
|
|
18
|
+
* Accepted: classes, generics, function types, names ending with an `allowTypeSuffixes` entry
|
|
19
|
+
* (`…Port`, `…Config`, `…Data`), types declared under `allowTypesFrom` (message bodies and
|
|
20
|
+
* `MessageNames`), and names that don't resolve to a project file (package types).
|
|
21
|
+
*/
|
|
22
|
+
exports.default = (0, rule_1.defineRule)({
|
|
23
|
+
meta: {
|
|
24
|
+
type: 'suggestion',
|
|
25
|
+
docs: { description: 'Require domain classes, not native or plain types, as parameter types', url: 'https://github.com/PiotrJozefow/pjx/tree/master/modules/eslint-style-rules#domain-primitive-params' },
|
|
26
|
+
messages: {
|
|
27
|
+
plain: 'Parameter "{{name}}" of "{{fn}}": use a domain primitive (…/values/) instead of `{{type}}`.',
|
|
28
|
+
},
|
|
29
|
+
schema: [{
|
|
30
|
+
type: 'object',
|
|
31
|
+
properties: { ...types_1.typePolicySchema, valuesFolder: { type: 'string' } },
|
|
32
|
+
additionalProperties: false,
|
|
33
|
+
}],
|
|
34
|
+
},
|
|
35
|
+
create(context) {
|
|
36
|
+
const options = context.options[0] ?? {};
|
|
37
|
+
const classifier = (0, types_1.createTypeClassifier)(context, (0, types_1.typePolicy)(options, { allowTypeSuffixes: ['Port', 'Config', 'Data'] }));
|
|
38
|
+
const file = (0, ast_1.filePath)(context);
|
|
39
|
+
const valuesFolder = options.valuesFolder ?? 'values';
|
|
40
|
+
const inValues = file.split('/').slice(0, -1).includes(valuesFolder);
|
|
41
|
+
const inInfrastructure = file.split('/').includes('infrastructure');
|
|
42
|
+
const source = context.sourceCode;
|
|
43
|
+
const insideFunctionBody = (node) => {
|
|
44
|
+
for (let current = node.parent; current; current = current.parent) {
|
|
45
|
+
if (FUNCTION_TYPES.has(current.type))
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
return false;
|
|
49
|
+
};
|
|
50
|
+
const paramName = (binding) => {
|
|
51
|
+
if (binding.type === 'Identifier')
|
|
52
|
+
return binding.name;
|
|
53
|
+
const text = source.getText(binding).replace(/:[\s\S]*$/, '').replace(/\s+/g, ' ');
|
|
54
|
+
return text.length > 40 ? `${text.slice(0, 37)}…` : text;
|
|
55
|
+
};
|
|
56
|
+
/** `x = 10` without an annotation is a `number`. */
|
|
57
|
+
const defaultType = (param) => {
|
|
58
|
+
if (param.type !== 'AssignmentPattern')
|
|
59
|
+
return null;
|
|
60
|
+
const value = param.right;
|
|
61
|
+
if (value.type === 'Literal' && ['string', 'number', 'boolean', 'bigint'].includes(typeof value.value))
|
|
62
|
+
return typeof value.value;
|
|
63
|
+
if (value.type === 'TemplateLiteral')
|
|
64
|
+
return 'string';
|
|
65
|
+
return null;
|
|
66
|
+
};
|
|
67
|
+
const checkParams = (fn, fnName) => {
|
|
68
|
+
for (const raw of fn.params) {
|
|
69
|
+
const param = raw.type === 'TSParameterProperty' ? raw.parameter : raw;
|
|
70
|
+
const binding = param.type === 'AssignmentPattern' ? param.left : param.type === 'RestElement' ? param.argument : param;
|
|
71
|
+
if (binding.type === 'Identifier' && binding.name === 'this')
|
|
72
|
+
continue;
|
|
73
|
+
const annotation = (param.typeAnnotation ?? binding.typeAnnotation)?.typeAnnotation;
|
|
74
|
+
const type = annotation ? classifier.plainReason(annotation) : defaultType(param);
|
|
75
|
+
if (type)
|
|
76
|
+
context.report({ node: raw, messageId: 'plain', data: { name: paramName(binding), fn: fnName, type } });
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
const checkMember = (member, fn) => {
|
|
80
|
+
if (insideFunctionBody(member))
|
|
81
|
+
return;
|
|
82
|
+
const name = (0, ast_1.memberName)(member) ?? '[computed]';
|
|
83
|
+
const isConstructor = member.kind === 'constructor';
|
|
84
|
+
if (inValues && (isConstructor || member.static))
|
|
85
|
+
return;
|
|
86
|
+
const isPrivate = member.accessibility === 'private' || member.key?.type === 'PrivateIdentifier';
|
|
87
|
+
if (inInfrastructure && isPrivate && /^deserialize/.test(name))
|
|
88
|
+
return;
|
|
89
|
+
checkParams(fn, isConstructor ? 'constructor' : name);
|
|
90
|
+
};
|
|
91
|
+
const isFunctionValue = (value) => value?.type === 'ArrowFunctionExpression' || value?.type === 'FunctionExpression';
|
|
92
|
+
return {
|
|
93
|
+
'MethodDefinition, TSAbstractMethodDefinition'(member) {
|
|
94
|
+
checkMember(member, member.value);
|
|
95
|
+
},
|
|
96
|
+
'PropertyDefinition, TSAbstractPropertyDefinition'(member) {
|
|
97
|
+
if (isFunctionValue(member.value))
|
|
98
|
+
checkMember(member, member.value);
|
|
99
|
+
},
|
|
100
|
+
'FunctionDeclaration, TSDeclareFunction'(fn) {
|
|
101
|
+
if (!insideFunctionBody(fn))
|
|
102
|
+
checkParams(fn, fn.id?.name ?? 'default');
|
|
103
|
+
},
|
|
104
|
+
VariableDeclarator(declarator) {
|
|
105
|
+
if (!isFunctionValue(declarator.init) || insideFunctionBody(declarator) || declarator.id.type !== 'Identifier')
|
|
106
|
+
return;
|
|
107
|
+
checkParams(declarator.init, declarator.id.name);
|
|
108
|
+
},
|
|
109
|
+
TSMethodSignature(signature) {
|
|
110
|
+
if (!insideFunctionBody(signature))
|
|
111
|
+
checkParams(signature, (0, ast_1.memberName)(signature) ?? '[computed]');
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
//# sourceMappingURL=domain-primitive-params.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domain-primitive-params.js","sourceRoot":"","sources":["../../src/rules/domain-primitive-params.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
* entity constructors take one `<Entity>Data` object, never positional parameters.
|
|
3
3
|
* Decorated classes (domain primitives) and `…Error` subclasses are not entities and are skipped.
|
|
4
4
|
*/
|
|
5
|
-
declare const _default: import("eslint").
|
|
5
|
+
declare const _default: import("@typescript-eslint/utils/dist/ts-eslint").RuleModule<string, any[], unknown, import("@typescript-eslint/utils/dist/ts-eslint").RuleListener>;
|
|
6
6
|
export default _default;
|
|
@@ -9,7 +9,7 @@ const rule_1 = require("../lib/rule");
|
|
|
9
9
|
exports.default = (0, rule_1.defineRule)({
|
|
10
10
|
meta: {
|
|
11
11
|
type: 'suggestion',
|
|
12
|
-
docs: { description: 'Require entity constructors to take a single `<Entity>Data` parameter' },
|
|
12
|
+
docs: { description: 'Require entity constructors to take a single `<Entity>Data` parameter', url: 'https://github.com/PiotrJozefow/pjx/tree/master/modules/eslint-style-rules#entity-constructor-data' },
|
|
13
13
|
messages: { data: 'Constructor of "{{name}}" must take a single `data: {{name}}Data` parameter.' },
|
|
14
14
|
schema: [],
|
|
15
15
|
},
|
|
@@ -19,13 +19,14 @@ exports.default = (0, rule_1.defineRule)({
|
|
|
19
19
|
const classNode = constructor.parent.parent;
|
|
20
20
|
if (!classNode.id || (0, ast_1.decoratorNames)(classNode).length > 0)
|
|
21
21
|
return;
|
|
22
|
-
if (/Error$/.test(classNode.superClass?.name
|
|
22
|
+
if (/Error$/.test(classNode.superClass?.type === 'Identifier' ? classNode.superClass.name : ''))
|
|
23
23
|
return;
|
|
24
24
|
const params = constructor.value.params.map((param) => (param.type === 'TSParameterProperty' ? param.parameter : param));
|
|
25
25
|
if (params.length === 0)
|
|
26
26
|
return;
|
|
27
27
|
const name = classNode.id.name;
|
|
28
|
-
|
|
28
|
+
const first = params[0];
|
|
29
|
+
if (params.length === 1 && (0, ast_1.annotationTypeName)('typeAnnotation' in first ? first.typeAnnotation : undefined) === `${name}Data`)
|
|
29
30
|
return;
|
|
30
31
|
context.report({ node: constructor.key, messageId: 'data', data: { name } });
|
|
31
32
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entity-constructor-data.js","sourceRoot":"","sources":["../../src/rules/entity-constructor-data.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"entity-constructor-data.js","sourceRoot":"","sources":["../../src/rules/entity-constructor-data.ts"],"names":[],"mappings":";;AACA,oCAAgE;AAChE,sCAAyC;AAEzC;;;GAGG;AACH,kBAAe,IAAA,iBAAU,EAAC;IACxB,IAAI,EAAE;QACJ,IAAI,EAAE,YAAY;QAClB,IAAI,EAAE,EAAE,WAAW,EAAE,uEAAuE,EAAE,GAAG,EAAE,oGAAoG,EAAE;QACzM,QAAQ,EAAE,EAAE,IAAI,EAAE,8EAA8E,EAAE;QAClG,MAAM,EAAE,EAAE;KACX;IACD,MAAM,CAAC,OAAO;QACZ,OAAO;YACL,qEAAqE,CAAC,WAAsC;gBAC1G,MAAM,SAAS,GAAG,WAAW,CAAC,MAAM,CAAC,MAAmC,CAAC;gBACzE,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,IAAA,oBAAc,EAAC,SAAS,CAAC,CAAC,MAAM,GAAG,CAAC;oBAAE,OAAO;gBAClE,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,IAAI,KAAK,YAAY,CAAC,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;oBAAE,OAAO;gBAExG,MAAM,MAAM,GAAG,WAAW,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,KAAK,qBAAqB,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;gBACzH,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;oBAAE,OAAO;gBAChC,MAAM,IAAI,GAAG,SAAS,CAAC,EAAE,CAAC,IAAI,CAAC;gBAC/B,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;gBACxB,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,IAAA,wBAAkB,EAAC,gBAAgB,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,GAAG,IAAI,MAAM;oBAAE,OAAO;gBACtI,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,WAAW,CAAC,GAAG,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;YAC/E,CAAC;SACF,CAAC;IACJ,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Event-sourced aggregates (`extends EventSourceable<PaymentEvent
|
|
2
|
+
* Event-sourced aggregates (`extends EventSourceable<PaymentEvent>`):
|
|
3
3
|
*
|
|
4
4
|
* - decorated `@trackable` — the repository persists changed properties and the emitted events;
|
|
5
5
|
* - typed with their event union (`EventSourceable<PaymentEvent>`), declared in the messages barrel;
|
|
@@ -11,5 +11,5 @@
|
|
|
11
11
|
* - events are emitted with `name: MessageNames.X`.
|
|
12
12
|
* Option `stateChangesInApply` (off by default): `this.x = …` only in the constructor and `apply`.
|
|
13
13
|
*/
|
|
14
|
-
declare const _default: import("eslint").
|
|
14
|
+
declare const _default: import("@typescript-eslint/utils/dist/ts-eslint").RuleModule<string, any[], unknown, import("@typescript-eslint/utils/dist/ts-eslint").RuleListener>;
|
|
15
15
|
export default _default;
|
|
@@ -9,7 +9,7 @@ const NON_DETERMINISTIC_CALLS = [
|
|
|
9
9
|
{ object: 'Math', property: 'random' },
|
|
10
10
|
];
|
|
11
11
|
/**
|
|
12
|
-
* Event-sourced aggregates (`extends EventSourceable<PaymentEvent
|
|
12
|
+
* Event-sourced aggregates (`extends EventSourceable<PaymentEvent>`):
|
|
13
13
|
*
|
|
14
14
|
* - decorated `@trackable` — the repository persists changed properties and the emitted events;
|
|
15
15
|
* - typed with their event union (`EventSourceable<PaymentEvent>`), declared in the messages barrel;
|
|
@@ -24,7 +24,7 @@ const NON_DETERMINISTIC_CALLS = [
|
|
|
24
24
|
exports.default = (0, rule_1.defineRule)({
|
|
25
25
|
meta: {
|
|
26
26
|
type: 'problem',
|
|
27
|
-
docs: { description: 'Enforce the event-sourced aggregate contract' },
|
|
27
|
+
docs: { description: 'Enforce the event-sourced aggregate contract', url: 'https://github.com/PiotrJozefow/pjx/tree/master/modules/eslint-style-rules#event-sourced-aggregate' },
|
|
28
28
|
messages: {
|
|
29
29
|
trackable: 'Event-sourced aggregate "{{name}}" must be decorated @{{decorator}}.',
|
|
30
30
|
eventType: '"{{name}}" must declare its event union: `extends {{base}}<{{name}}Event>`.',
|
|
@@ -50,13 +50,13 @@ exports.default = (0, rule_1.defineRule)({
|
|
|
50
50
|
const isThisCall = (node, method) => node.type === 'CallExpression' && node.callee.type === 'MemberExpression' && node.callee.object.type === 'ThisExpression' && node.callee.property.name === method;
|
|
51
51
|
return {
|
|
52
52
|
ClassDeclaration(node) {
|
|
53
|
-
if (node.superClass?.name !== base)
|
|
53
|
+
if (node.superClass?.type !== 'Identifier' || node.superClass.name !== base)
|
|
54
54
|
return;
|
|
55
55
|
const name = node.id?.name ?? '(anonymous)';
|
|
56
56
|
const at = node.id ?? node;
|
|
57
57
|
if (!(0, ast_1.decoratorNames)(node).includes(decorator))
|
|
58
58
|
context.report({ node: at, messageId: 'trackable', data: { name, decorator } });
|
|
59
|
-
const typeArguments = node.superTypeArguments
|
|
59
|
+
const typeArguments = node.superTypeArguments;
|
|
60
60
|
if (!typeArguments?.params?.length)
|
|
61
61
|
context.report({ node: node.superClass, messageId: 'eventType', data: { name, base } });
|
|
62
62
|
const apply = (0, ast_1.findMethod)(node, 'apply', false);
|
|
@@ -94,12 +94,12 @@ exports.default = (0, rule_1.defineRule)({
|
|
|
94
94
|
context.report({ node: inner, messageId: 'replay', data: { what: 'emitting' } });
|
|
95
95
|
if (inner.type === 'ThrowStatement')
|
|
96
96
|
context.report({ node: inner, messageId: 'replay', data: { what: 'throwing' } });
|
|
97
|
-
if (inner.type === 'NewExpression' && inner.callee.name === 'Date' && inner.arguments.length === 0) {
|
|
97
|
+
if (inner.type === 'NewExpression' && inner.callee.type === 'Identifier' && inner.callee.name === 'Date' && inner.arguments.length === 0) {
|
|
98
98
|
context.report({ node: inner, messageId: 'replay', data: { what: 'reading the clock (new Date())' } });
|
|
99
99
|
}
|
|
100
100
|
if (inner.type === 'CallExpression' && inner.callee.type === 'MemberExpression') {
|
|
101
|
-
const object = inner.callee.object.name;
|
|
102
|
-
const property = inner.callee.property
|
|
101
|
+
const object = inner.callee.object.type === 'Identifier' ? inner.callee.object.name : undefined;
|
|
102
|
+
const property = (0, ast_1.keyName)(inner.callee.property);
|
|
103
103
|
if (NON_DETERMINISTIC_CALLS.some((call) => call.object === object && call.property === property)) {
|
|
104
104
|
context.report({ node: inner, messageId: 'replay', data: { what: `${object}.${property}()` } });
|
|
105
105
|
}
|
|
@@ -122,7 +122,7 @@ exports.default = (0, rule_1.defineRule)({
|
|
|
122
122
|
(0, messages_1.walkOwnBody)(member.value.body, (inner) => {
|
|
123
123
|
const target = inner.type === 'AssignmentExpression' ? inner.left : inner.type === 'UpdateExpression' ? inner.argument : null;
|
|
124
124
|
if (target?.type === 'MemberExpression' && target.object.type === 'ThisExpression' && !target.computed) {
|
|
125
|
-
context.report({ node: inner, messageId: 'stateOutsideApply', data: { name, property: target.property
|
|
125
|
+
context.report({ node: inner, messageId: 'stateOutsideApply', data: { name, property: (0, ast_1.keyName)(target.property) ?? '', method: (0, ast_1.memberName)(member) ?? '' } });
|
|
126
126
|
}
|
|
127
127
|
});
|
|
128
128
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"event-sourced-aggregate.js","sourceRoot":"","sources":["../../src/rules/event-sourced-aggregate.ts"],"names":[],"mappings":";;AAAA,sCAAyC;AACzC,
|
|
1
|
+
{"version":3,"file":"event-sourced-aggregate.js","sourceRoot":"","sources":["../../src/rules/event-sourced-aggregate.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* the private helper that loads one owner-scoped entity and throws not-found is `findOne`.
|
|
3
3
|
* Detected as: private method, first parameter typed `AuthToken`, returns `Promise<Entity>`,
|
|
4
|
-
* body throws `AppError.notFound(...)
|
|
4
|
+
* body throws, and every `throw` is `AppError.notFound(...)` — any other throw (403, 409, …) marks an
|
|
5
|
+
* eligibility guard, not a fetch. `findOne<Thing>` is accepted when a class needs several.
|
|
5
6
|
*/
|
|
6
|
-
declare const _default: import("eslint").
|
|
7
|
+
declare const _default: import("@typescript-eslint/utils/dist/ts-eslint").RuleModule<string, any[], unknown, import("@typescript-eslint/utils/dist/ts-eslint").RuleListener>;
|
|
7
8
|
export default _default;
|
|
@@ -5,32 +5,59 @@ const rule_1 = require("../lib/rule");
|
|
|
5
5
|
/**
|
|
6
6
|
* the private helper that loads one owner-scoped entity and throws not-found is `findOne`.
|
|
7
7
|
* Detected as: private method, first parameter typed `AuthToken`, returns `Promise<Entity>`,
|
|
8
|
-
* body throws `AppError.notFound(...)
|
|
8
|
+
* body throws, and every `throw` is `AppError.notFound(...)` — any other throw (403, 409, …) marks an
|
|
9
|
+
* eligibility guard, not a fetch. `findOne<Thing>` is accepted when a class needs several.
|
|
9
10
|
*/
|
|
10
11
|
exports.default = (0, rule_1.defineRule)({
|
|
11
12
|
meta: {
|
|
12
13
|
type: 'suggestion',
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
hasSuggestions: true,
|
|
15
|
+
docs: { description: 'Name owner-scoped single-entity fetch helpers `findOne`', url: 'https://github.com/PiotrJozefow/pjx/tree/master/modules/eslint-style-rules#find-one-naming' },
|
|
16
|
+
messages: { name: 'Owner-scoped fetch helper "{{name}}" must be named `findOne`.', rename: 'Rename the declaration to `findOne` (update its call sites too).' },
|
|
15
17
|
schema: [],
|
|
16
18
|
},
|
|
17
19
|
create(context) {
|
|
18
|
-
const
|
|
20
|
+
const isNotFound = (argument) => argument?.type === 'CallExpression' && argument.callee.type === 'MemberExpression' && !argument.callee.computed &&
|
|
21
|
+
argument.callee.object.type === 'Identifier' && argument.callee.object.name === 'AppError' && argument.callee.property.type === 'Identifier' && argument.callee.property.name === 'notFound';
|
|
22
|
+
// FIXME: generic AST walk over untyped child keys; typed as a record since TSESTree has no child index.
|
|
23
|
+
const throwsOf = (node, found = []) => {
|
|
24
|
+
if (!node || typeof node.type !== 'string')
|
|
25
|
+
return found;
|
|
26
|
+
if (node.type === 'ThrowStatement')
|
|
27
|
+
found.push(node.argument);
|
|
28
|
+
for (const key of Object.keys(node)) {
|
|
29
|
+
if (key === 'parent')
|
|
30
|
+
continue;
|
|
31
|
+
const value = node[key];
|
|
32
|
+
if (Array.isArray(value))
|
|
33
|
+
value.forEach((child) => throwsOf(child, found));
|
|
34
|
+
else if (value && typeof value.type === 'string')
|
|
35
|
+
throwsOf(value, found);
|
|
36
|
+
}
|
|
37
|
+
return found;
|
|
38
|
+
};
|
|
19
39
|
return {
|
|
20
40
|
'MethodDefinition[accessibility="private"][kind="method"]'(method) {
|
|
21
|
-
const name = method
|
|
41
|
+
const name = (0, ast_1.memberName)(method);
|
|
22
42
|
if (!name || /^findOne([A-Z]\w*)?$/.test(name))
|
|
23
43
|
return;
|
|
24
44
|
const fn = method.value;
|
|
25
45
|
const firstParam = fn.params[0]?.type === 'TSParameterProperty' ? fn.params[0].parameter : fn.params[0];
|
|
26
|
-
if ((0, ast_1.annotationTypeName)(firstParam
|
|
46
|
+
if ((0, ast_1.annotationTypeName)(firstParam && 'typeAnnotation' in firstParam ? firstParam.typeAnnotation : undefined) !== 'AuthToken')
|
|
27
47
|
return;
|
|
28
48
|
const returned = (0, ast_1.promiseTypeArgument)(fn.returnType);
|
|
29
49
|
if (returned?.type !== 'TSTypeReference')
|
|
30
50
|
return;
|
|
31
|
-
|
|
51
|
+
const thrown = throwsOf(fn.body);
|
|
52
|
+
if (thrown.length === 0 || !thrown.every(isNotFound))
|
|
32
53
|
return;
|
|
33
|
-
|
|
54
|
+
const taken = method.parent.body.some((member) => member !== method && (0, ast_1.memberName)(member) === 'findOne');
|
|
55
|
+
context.report({
|
|
56
|
+
node: method.key,
|
|
57
|
+
messageId: 'name',
|
|
58
|
+
data: { name },
|
|
59
|
+
suggest: taken ? [] : [{ messageId: 'rename', fix: (fixer) => fixer.replaceText(method.key, 'findOne') }],
|
|
60
|
+
});
|
|
34
61
|
},
|
|
35
62
|
};
|
|
36
63
|
},
|