eslint-plugin-stamhoofd 2.133.0 → 2.135.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-stamhoofd",
3
- "version": "2.133.0",
3
+ "version": "2.135.0",
4
4
  "type": "module",
5
5
  "main": "./src/index.js",
6
6
  "sideEffects": false,
@@ -30,5 +30,5 @@
30
30
  "publishConfig": {
31
31
  "access": "public"
32
32
  },
33
- "gitHead": "65d5f0a31f87b8e3b7a23349f08cf7943ef15bda"
33
+ "gitHead": "df20ecb0e4b10af877d2e37e92e39100afe342e8"
34
34
  }
package/src/index.js CHANGED
@@ -13,10 +13,12 @@ import node from './configs/node.js';
13
13
  import typescript from './configs/typescript.js';
14
14
  import asyncComponentWithProperties from './rules/async-component-with-properties.js';
15
15
  import asyncRouteComponents from './rules/async-route-components.js';
16
+ import preferDefineRoute from './rules/prefer-define-route.js';
16
17
 
17
18
  const rules = {
18
19
  'async-component-with-properties': asyncComponentWithProperties,
19
20
  'async-route-components': asyncRouteComponents,
21
+ 'prefer-define-route': preferDefineRoute,
20
22
  };
21
23
  const stamhoofdPlugin = {
22
24
  rules,
@@ -163,6 +165,7 @@ export default {
163
165
  ],
164
166
  }],
165
167
  'stamhoofd/async-route-components': 'error',
168
+ 'stamhoofd/prefer-define-route': 'warn',
166
169
  // TODO: restore to 'error' once the existing dependency cycles are resolved.
167
170
  'import/no-cycle': ['warn', { maxDepth: 100, ignoreExternal: false, allowUnsafeDynamicCyclicDependency: true }],
168
171
  // Disallow importing from barrel files (index files that only re-export).
@@ -0,0 +1,79 @@
1
+ const navigationPackage = '@simonbackx/vue-app-navigation';
2
+
3
+ function unwrapExpression(node) {
4
+ while (
5
+ node?.type === 'TSAsExpression'
6
+ || node?.type === 'TSNonNullExpression'
7
+ || node?.type === 'TSSatisfiesExpression'
8
+ || node?.type === 'TSTypeAssertion'
9
+ || node?.type === 'ChainExpression'
10
+ ) {
11
+ node = node.expression;
12
+ }
13
+
14
+ return node;
15
+ }
16
+
17
+ function findVariable(sourceCode, identifier) {
18
+ let scope = sourceCode.getScope(identifier);
19
+
20
+ while (scope) {
21
+ const variable = scope.set.get(identifier.name);
22
+ if (variable) {
23
+ return variable;
24
+ }
25
+ scope = scope.upper;
26
+ }
27
+
28
+ return null;
29
+ }
30
+
31
+ function getImportedName(variable) {
32
+ const definition = variable?.defs.find(def => def.type === 'ImportBinding');
33
+ const importDeclaration = definition?.parent;
34
+
35
+ if (importDeclaration?.type !== 'ImportDeclaration' || importDeclaration.source.value !== navigationPackage) {
36
+ return null;
37
+ }
38
+
39
+ if (definition.node.type !== 'ImportSpecifier') {
40
+ return null;
41
+ }
42
+
43
+ return definition.node.imported.name ?? definition.node.imported.value;
44
+ }
45
+
46
+ function isDefineRoutes(sourceCode, node) {
47
+ if (node.type !== 'Identifier') {
48
+ return false;
49
+ }
50
+
51
+ return getImportedName(findVariable(sourceCode, node)) === 'defineRoutes';
52
+ }
53
+
54
+ export default {
55
+ meta: {
56
+ type: 'suggestion',
57
+ docs: {
58
+ description: 'Prefer defineRoute over defineRoutes for better type safety.',
59
+ },
60
+ schema: [],
61
+ messages: {
62
+ preferDefineRoute: 'Prefer defineRoute over defineRoutes: defineRoutes is typed as Route<any>[] and loses type safety. Call defineRoute for each route instead.',
63
+ },
64
+ },
65
+ create(context) {
66
+ const sourceCode = context.sourceCode;
67
+
68
+ return {
69
+ CallExpression(node) {
70
+ if (isDefineRoutes(sourceCode, unwrapExpression(node.callee))) {
71
+ context.report({
72
+ node: node.callee,
73
+ messageId: 'preferDefineRoute',
74
+ });
75
+ }
76
+ },
77
+ };
78
+ },
79
+ };