@dxos/eslint-plugin-rules 0.8.4-main.9be5663bfe → 0.8.4-main.bbf232bc24
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/index.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import fs from 'node:fs';
|
|
6
6
|
|
|
7
7
|
import comment from './rules/comment.js';
|
|
8
|
-
import
|
|
8
|
+
import consistentUpdateParam from './rules/consistent-update-param.js';
|
|
9
9
|
import effectSubpathImports from './rules/effect-subpath-imports.js';
|
|
10
10
|
import header from './rules/header.js';
|
|
11
11
|
import importAsNamespace from './rules/import-as-namespace.js';
|
|
@@ -24,7 +24,7 @@ const plugin = {
|
|
|
24
24
|
},
|
|
25
25
|
rules: {
|
|
26
26
|
comment,
|
|
27
|
-
'consistent-
|
|
27
|
+
'consistent-update-param': consistentUpdateParam,
|
|
28
28
|
'effect-subpath-imports': effectSubpathImports,
|
|
29
29
|
header,
|
|
30
30
|
'import-as-namespace': importAsNamespace,
|
package/package.json
CHANGED
|
@@ -3,23 +3,23 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* ESLint rule to enforce that the callback parameter name in Obj.
|
|
7
|
-
* Relation.
|
|
6
|
+
* ESLint rule to enforce that the callback parameter name in Obj.update(),
|
|
7
|
+
* Relation.update(), and Entity.update() matches the first argument name.
|
|
8
8
|
*
|
|
9
9
|
* @example
|
|
10
10
|
* // ❌ Bad
|
|
11
|
-
* Obj.
|
|
12
|
-
* Obj.
|
|
11
|
+
* Obj.update(trigger, (t) => { t.enabled = true; });
|
|
12
|
+
* Obj.update(trigger, (mutableTrigger) => { mutableTrigger.enabled = true; });
|
|
13
13
|
*
|
|
14
14
|
* // ✅ Good
|
|
15
|
-
* Obj.
|
|
15
|
+
* Obj.update(trigger, (trigger) => { trigger.enabled = true; });
|
|
16
16
|
*/
|
|
17
17
|
export default {
|
|
18
18
|
meta: {
|
|
19
19
|
type: 'suggestion',
|
|
20
20
|
docs: {
|
|
21
21
|
description:
|
|
22
|
-
'Enforce callback parameter name matches the first argument in Obj.
|
|
22
|
+
'Enforce callback parameter name matches the first argument in Obj.update, Relation.update, and Entity.update.',
|
|
23
23
|
category: 'Best Practices',
|
|
24
24
|
recommended: true,
|
|
25
25
|
},
|
|
@@ -27,7 +27,7 @@ export default {
|
|
|
27
27
|
schema: [],
|
|
28
28
|
messages: {
|
|
29
29
|
mismatchedName:
|
|
30
|
-
'Callback parameter "{{callbackParam}}" should be "{{firstArg}}" to match the first argument of {{caller}}.
|
|
30
|
+
'Callback parameter "{{callbackParam}}" should be "{{firstArg}}" to match the first argument of {{caller}}.update().',
|
|
31
31
|
},
|
|
32
32
|
},
|
|
33
33
|
create(context) {
|
|
@@ -87,7 +87,7 @@ export default {
|
|
|
87
87
|
callee.object.type !== 'Identifier' ||
|
|
88
88
|
!['Obj', 'Relation', 'Entity'].includes(callee.object.name) ||
|
|
89
89
|
callee.property.type !== 'Identifier' ||
|
|
90
|
-
callee.property.name !== '
|
|
90
|
+
callee.property.name !== 'update'
|
|
91
91
|
) {
|
|
92
92
|
return;
|
|
93
93
|
}
|
|
@@ -47,7 +47,9 @@ export default {
|
|
|
47
47
|
const exportsCache = new Map(); // packageName -> Set<segment>
|
|
48
48
|
|
|
49
49
|
const loadExportsForPackage = (pkgName) => {
|
|
50
|
-
if (exportsCache.has(pkgName))
|
|
50
|
+
if (exportsCache.has(pkgName)) {
|
|
51
|
+
return exportsCache.get(pkgName);
|
|
52
|
+
}
|
|
51
53
|
try {
|
|
52
54
|
const pkgJson = requireForFile(`${pkgName}/package.json`);
|
|
53
55
|
const ex = pkgJson && pkgJson.exports;
|
|
@@ -55,8 +57,12 @@ export default {
|
|
|
55
57
|
if (ex && typeof ex === 'object') {
|
|
56
58
|
for (const key of Object.keys(ex)) {
|
|
57
59
|
// Keys like './Schema', './SchemaAST', './Function' (skip '.' and './package.json').
|
|
58
|
-
if (key === '.' || key === './package.json')
|
|
59
|
-
|
|
60
|
+
if (key === '.' || key === './package.json') {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (key.startsWith('./')) {
|
|
64
|
+
segments.add(key.slice(2));
|
|
65
|
+
}
|
|
60
66
|
}
|
|
61
67
|
}
|
|
62
68
|
exportsCache.set(pkgName, segments);
|
|
@@ -74,7 +80,9 @@ export default {
|
|
|
74
80
|
};
|
|
75
81
|
|
|
76
82
|
const resolveExportToSegment = (pkgName, exportName) => {
|
|
77
|
-
if (isValidSubpath(pkgName, exportName))
|
|
83
|
+
if (isValidSubpath(pkgName, exportName)) {
|
|
84
|
+
return exportName;
|
|
85
|
+
}
|
|
78
86
|
if (pkgName === 'effect' && EFFECT_EXPORT_TO_SUBPATH[exportName]) {
|
|
79
87
|
const segment = EFFECT_EXPORT_TO_SUBPATH[exportName];
|
|
80
88
|
return isValidSubpath(pkgName, segment) ? segment : null;
|
|
@@ -110,9 +118,13 @@ export default {
|
|
|
110
118
|
return {
|
|
111
119
|
ImportDeclaration: (node) => {
|
|
112
120
|
const source = String(node.source.value);
|
|
113
|
-
if (!isEffectPackage(source))
|
|
121
|
+
if (!isEffectPackage(source)) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
114
124
|
const basePackage = getBasePackage(source);
|
|
115
|
-
if (shouldSkipEffectPackage(basePackage))
|
|
125
|
+
if (shouldSkipEffectPackage(basePackage)) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
116
128
|
|
|
117
129
|
// If it's a subpath import (e.g., 'effect/Schema'), enforce namespace import except for allowed subpaths.
|
|
118
130
|
if (source.startsWith(basePackage + '/')) {
|
|
@@ -148,10 +160,15 @@ export default {
|
|
|
148
160
|
const typeImports = [];
|
|
149
161
|
const regularImports = [];
|
|
150
162
|
for (const specifier of node.specifiers) {
|
|
151
|
-
if (specifier.type !== 'ImportSpecifier')
|
|
163
|
+
if (specifier.type !== 'ImportSpecifier') {
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
152
166
|
const entry = { imported: specifier.imported.name, local: specifier.local.name };
|
|
153
|
-
if (specifier.importKind === 'type')
|
|
154
|
-
|
|
167
|
+
if (specifier.importKind === 'type') {
|
|
168
|
+
typeImports.push(entry);
|
|
169
|
+
} else {
|
|
170
|
+
regularImports.push(entry);
|
|
171
|
+
}
|
|
155
172
|
}
|
|
156
173
|
|
|
157
174
|
// Partition into resolvable vs unresolved specifiers (resolved entries include segment for fix).
|
|
@@ -162,13 +179,19 @@ export default {
|
|
|
162
179
|
|
|
163
180
|
typeImports.forEach((spec) => {
|
|
164
181
|
const segment = resolveExportToSegment(packageName, spec.imported);
|
|
165
|
-
if (segment)
|
|
166
|
-
|
|
182
|
+
if (segment) {
|
|
183
|
+
resolvedType.push({ ...spec, segment });
|
|
184
|
+
} else {
|
|
185
|
+
unresolvedType.push(spec);
|
|
186
|
+
}
|
|
167
187
|
});
|
|
168
188
|
regularImports.forEach((spec) => {
|
|
169
189
|
const segment = resolveExportToSegment(packageName, spec.imported);
|
|
170
|
-
if (segment)
|
|
171
|
-
|
|
190
|
+
if (segment) {
|
|
191
|
+
resolvedRegular.push({ ...spec, segment });
|
|
192
|
+
} else {
|
|
193
|
+
unresolvedRegular.push(spec);
|
|
194
|
+
}
|
|
172
195
|
});
|
|
173
196
|
|
|
174
197
|
const unresolved = [...unresolvedType, ...unresolvedRegular].map(({ imported }) => imported);
|
|
@@ -214,7 +237,9 @@ export default {
|
|
|
214
237
|
const useNamed = NAMED_IMPORT_ALLOWED_SUBPATHS.has(segment);
|
|
215
238
|
const merged = [...group.regular];
|
|
216
239
|
for (const t of group.type) {
|
|
217
|
-
if (!group.regular.some((r) => r.local === t.local))
|
|
240
|
+
if (!group.regular.some((r) => r.local === t.local)) {
|
|
241
|
+
merged.push(t);
|
|
242
|
+
}
|
|
218
243
|
}
|
|
219
244
|
if (useNamed && merged.length > 0) {
|
|
220
245
|
const specParts = merged.map(({ imported, local }) =>
|
|
@@ -225,7 +250,9 @@ export default {
|
|
|
225
250
|
const seen = new Set();
|
|
226
251
|
for (const { imported, local } of merged) {
|
|
227
252
|
const alias = imported !== local ? local : imported;
|
|
228
|
-
if (seen.has(alias))
|
|
253
|
+
if (seen.has(alias)) {
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
229
256
|
seen.add(alias);
|
|
230
257
|
const isTypeOnly =
|
|
231
258
|
group.type.some((t) => t.imported === imported) &&
|
|
@@ -249,7 +276,9 @@ export default {
|
|
|
249
276
|
});
|
|
250
277
|
unresolvedType.forEach((s) => {
|
|
251
278
|
const entry = byLocal.get(s.local) ?? {};
|
|
252
|
-
if (!entry.value)
|
|
279
|
+
if (!entry.value) {
|
|
280
|
+
entry.type = s;
|
|
281
|
+
} // only keep type if no value for same local
|
|
253
282
|
byLocal.set(s.local, entry);
|
|
254
283
|
});
|
|
255
284
|
|
|
@@ -265,14 +294,18 @@ export default {
|
|
|
265
294
|
specParts.push(part);
|
|
266
295
|
}
|
|
267
296
|
}
|
|
268
|
-
if (specParts.length)
|
|
297
|
+
if (specParts.length) {
|
|
298
|
+
imports.push(`import { ${specParts.join(', ')} } from '${packageName}';`);
|
|
299
|
+
}
|
|
269
300
|
}
|
|
270
301
|
|
|
271
302
|
// Get the original import's indentation.
|
|
272
303
|
const importIndent = sourceCode.text.slice(node.range[0] - node.loc.start.column, node.range[0]);
|
|
273
304
|
|
|
274
305
|
// Join imports with newline and proper indentation.
|
|
275
|
-
if (imports.length === 0)
|
|
306
|
+
if (imports.length === 0) {
|
|
307
|
+
return null;
|
|
308
|
+
} // nothing to change
|
|
276
309
|
const newImports = imports.join('\n' + importIndent);
|
|
277
310
|
|
|
278
311
|
return fixer.replaceText(node, newImports);
|