@spinnaker/eslint-plugin 0.0.0-2025.4-4 → 0.0.0-2026.1-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/README.md CHANGED
@@ -5,19 +5,33 @@ This package is an ESLint plugin containing:
5
5
  - A base ESLint config
6
6
  - Parser configured for typescript
7
7
  - A set of default plugins, e.g. `react-hooks` plugin
8
- - Recommended rule sets, e.g. `prettier/@typescript-eslint`
8
+ - Recommended rule sets, e.g. `prettier`, `@typescript-eslint/recommended`
9
9
  - Specific from the recommended rule sets are disabled
10
10
  - Custom ESLint rules specific to Spinnaker
11
11
 
12
12
  ### Use
13
13
 
14
- To use the rules, create a `.eslintrc.js` containing:
14
+ This plugin requires **ESLint 9+** and uses the flat config format.
15
+
16
+ To use the rules, create an `eslint.config.js` containing:
15
17
 
16
18
  ```js
17
- module.exports = {
18
- plugins: ['@spinnaker/eslint-plugin'],
19
- extends: ['plugin:@spinnaker/base'],
20
- };
19
+ const { defineConfig } = require('eslint/config');
20
+ const spinnakerEslintPlugin = require('@spinnaker/eslint-plugin');
21
+ const { FlatCompat } = require('@eslint/eslintrc');
22
+ const js = require('@eslint/js');
23
+
24
+ const compat = new FlatCompat({
25
+ baseDirectory: __dirname,
26
+ recommendedConfig: js.configs.recommended,
27
+ });
28
+
29
+ module.exports = defineConfig([{
30
+ plugins: {
31
+ '@spinnaker': spinnakerEslintPlugin,
32
+ },
33
+ extends: compat.extends('plugin:@spinnaker/base'),
34
+ }]);
21
35
  ```
22
36
 
23
37
  ## Creating a custom lint rule
package/base.config.js CHANGED
@@ -1,86 +1,137 @@
1
- module.exports = {
2
- parser: '@typescript-eslint/parser',
3
- parserOptions: { sourceType: 'module' },
4
- plugins: ['@typescript-eslint', '@spinnaker/eslint-plugin', 'react-hooks'],
5
- extends: ['eslint:recommended', 'prettier', 'prettier/@typescript-eslint', 'plugin:@typescript-eslint/recommended'],
6
- rules: {
7
- '@spinnaker/import-sort': 1,
8
- '@spinnaker/api-deprecation': 2,
9
- '@spinnaker/api-no-slashes': 2,
10
- '@spinnaker/api-no-unused-chaining': 2,
11
- '@spinnaker/import-from-alias-not-npm': 2,
12
- '@spinnaker/import-from-npm-not-alias': 2,
13
- '@spinnaker/import-from-npm-not-relative': 2,
14
- '@spinnaker/import-from-presentation-not-core': 2,
15
- '@spinnaker/import-relative-within-subpackage': 2,
16
- '@spinnaker/migrate-to-mock-http-client': 2,
17
- '@spinnaker/ng-no-component-class': 2,
18
- '@spinnaker/ng-no-module-export': 2,
19
- '@spinnaker/ng-no-require-angularjs': 2,
20
- '@spinnaker/ng-no-require-module-deps': 2,
21
- '@spinnaker/ng-strictdi': 'off', // TODO: this rule seems to be broken, needs to be rewritten to avoid an error
22
- '@spinnaker/prefer-promise-like': 1,
23
- '@spinnaker/react2angular-with-error-boundary': 2,
24
- '@spinnaker/rest-prefer-static-strings-in-initializer': 2,
25
- indent: 'off',
26
- 'member-ordering': 'off',
27
- 'no-console': ['error', { allow: ['warn', 'error'] }],
28
- 'no-extra-boolean-cast': 'off',
29
- 'no-prototype-builtins': 'off',
30
- 'one-var': ['error', { initialized: 'never' }],
31
- 'prefer-rest-params': 'off',
32
- 'prefer-spread': 'off',
33
- // turn back on if https://github.com/eslint/eslint/issues/11899 fixes false positives
34
- 'require-atomic-updates': 'off',
35
- 'react-hooks/rules-of-hooks': 'error',
36
- // turn back on after addressing all violations
37
- // 'react-hooks/exhaustive-deps': 'warn',
38
- '@typescript-eslint/array-type': ['error', { default: 'array-simple' }],
39
- '@typescript-eslint/ban-ts-ignore': 'off',
40
- '@typescript-eslint/consistent-type-imports': ['error', { prefer: 'type-imports' }],
41
- '@typescript-eslint/explicit-function-return-type': 'off',
42
- '@typescript-eslint/explicit-member-accessibility': 'off',
43
- '@typescript-eslint/indent': 'off',
44
- '@typescript-eslint/interface-name-prefix': 'off',
45
- '@typescript-eslint/no-case-declarations': 'off',
46
- '@typescript-eslint/no-empty-function': 'off',
47
- '@typescript-eslint/no-empty-interface': 'off',
48
- '@typescript-eslint/no-explicit-any': 'off',
49
- '@typescript-eslint/no-object-literal-type-assertion': 'off',
50
- '@typescript-eslint/no-parameter-properties': 'off',
51
- '@typescript-eslint/no-this-alias': 'off',
52
- '@typescript-eslint/no-triple-slash-reference': 'off',
53
- '@typescript-eslint/no-unused-vars': 'off',
54
- '@typescript-eslint/no-use-before-define': 'off',
55
- '@typescript-eslint/no-var-requires': 'off', // TODO: turn on once all code is using ES6 imports
56
- '@typescript-eslint/triple-slash-reference': 'off',
57
- '@typescript-eslint/explicit-module-boundary-types': 'off',
58
- '@typescript-eslint/ban-types': 'off',
59
- '@typescript-eslint/ban-ts-comment': 'off',
60
- },
61
- overrides: [
62
- {
63
- files: ['*.js', '*.jsx'],
64
- rules: {
65
- '@typescript-eslint/no-use-before-define': 'off',
1
+ const { globalIgnores } = require('eslint/config');
2
+
3
+ const tsParser = require('@typescript-eslint/parser');
4
+ const typescriptEslint = require('@typescript-eslint/eslint-plugin');
5
+ const reactHooks = require('eslint-plugin-react-hooks');
6
+
7
+ // Import rules directly to avoid circular dependency when this config is bundled in the plugin
8
+ // Use a function to get rules lazily
9
+ const getSpinnakerRules = () => require('./index.js').rules;
10
+
11
+ const globals = require('globals');
12
+ const js = require('@eslint/js');
13
+
14
+ const { FlatCompat } = require('@eslint/eslintrc');
15
+
16
+ const compat = new FlatCompat({
17
+ baseDirectory: __dirname,
18
+ recommendedConfig: js.configs.recommended,
19
+ allConfig: js.configs.all,
20
+ });
21
+
22
+ module.exports = [
23
+ js.configs.recommended,
24
+ ...compat.extends('prettier', 'plugin:@typescript-eslint/recommended'),
25
+ {
26
+ languageOptions: {
27
+ parser: tsParser,
28
+ sourceType: 'module',
29
+ parserOptions: {},
30
+
31
+ globals: {
32
+ ...globals.browser,
33
+ ...globals.node,
34
+ ...globals.jasmine,
35
+ angular: true,
36
+ $: true,
37
+ _: true,
66
38
  },
67
39
  },
68
- {
69
- files: ['*.ts', '*.tsx'],
70
- rules: {
71
- 'no-undef': 'off', // typescript already checks this
72
- },
40
+
41
+ plugins: {
42
+ '@typescript-eslint': typescriptEslint,
43
+ '@spinnaker': { rules: getSpinnakerRules() },
44
+ 'react-hooks': { rules: reactHooks.rules },
45
+ },
46
+
47
+ rules: {
48
+ '@spinnaker/import-sort': 1,
49
+ '@spinnaker/api-deprecation': 2,
50
+ '@spinnaker/api-no-slashes': 2,
51
+ '@spinnaker/api-no-unused-chaining': 2,
52
+ '@spinnaker/import-from-alias-not-npm': 2,
53
+ '@spinnaker/import-from-npm-not-alias': 2,
54
+ '@spinnaker/import-from-npm-not-relative': 2,
55
+ '@spinnaker/import-from-presentation-not-core': 2,
56
+ '@spinnaker/import-relative-within-subpackage': 2,
57
+ '@spinnaker/migrate-to-mock-http-client': 2,
58
+ '@spinnaker/ng-no-component-class': 2,
59
+ '@spinnaker/ng-no-module-export': 2,
60
+ '@spinnaker/ng-no-require-angularjs': 2,
61
+ '@spinnaker/ng-no-require-module-deps': 2,
62
+ '@spinnaker/ng-strictdi': 'off',
63
+ '@spinnaker/prefer-promise-like': 1,
64
+ '@spinnaker/react2angular-with-error-boundary': 2,
65
+ '@spinnaker/rest-prefer-static-strings-in-initializer': 2,
66
+ indent: 'off',
67
+ 'member-ordering': 'off',
68
+ 'no-console': [
69
+ 'error',
70
+ {
71
+ allow: ['warn', 'error'],
72
+ },
73
+ ],
74
+ 'no-extra-boolean-cast': 'off',
75
+ 'no-prototype-builtins': 'off',
76
+ 'one-var': [
77
+ 'error',
78
+ {
79
+ initialized: 'never',
80
+ },
81
+ ],
82
+ 'prefer-rest-params': 'off',
83
+ 'prefer-spread': 'off',
84
+ 'require-atomic-updates': 'off',
85
+ 'react-hooks/rules-of-hooks': 'error',
86
+ '@typescript-eslint/array-type': [
87
+ 'error',
88
+ {
89
+ default: 'array-simple',
90
+ },
91
+ ],
92
+ '@typescript-eslint/ban-ts-ignore': 'off',
93
+ '@typescript-eslint/consistent-type-imports': [
94
+ 'error',
95
+ {
96
+ prefer: 'type-imports',
97
+ },
98
+ ],
99
+ '@typescript-eslint/explicit-function-return-type': 'off',
100
+ '@typescript-eslint/explicit-member-accessibility': 'off',
101
+ '@typescript-eslint/indent': 'off',
102
+ '@typescript-eslint/interface-name-prefix': 'off',
103
+ '@typescript-eslint/no-case-declarations': 'off',
104
+ '@typescript-eslint/no-empty-function': 'off',
105
+ '@typescript-eslint/no-empty-interface': 'off',
106
+ '@typescript-eslint/no-explicit-any': 'off',
107
+ '@typescript-eslint/no-object-literal-type-assertion': 'off',
108
+ '@typescript-eslint/no-parameter-properties': 'off',
109
+ '@typescript-eslint/no-this-alias': 'off',
110
+ '@typescript-eslint/no-triple-slash-reference': 'off',
111
+ '@typescript-eslint/no-unused-vars': 'off',
112
+ '@typescript-eslint/no-use-before-define': 'off',
113
+ '@typescript-eslint/no-var-requires': 'off',
114
+ '@typescript-eslint/triple-slash-reference': 'off',
115
+ '@typescript-eslint/explicit-module-boundary-types': 'off',
116
+ '@typescript-eslint/ban-types': 'off',
117
+ '@typescript-eslint/ban-ts-comment': 'off',
118
+ '@typescript-eslint/no-require-imports': 'off',
119
+ '@typescript-eslint/no-unused-expressions': 'off',
120
+ '@typescript-eslint/no-empty-object-type': 'off',
121
+ '@typescript-eslint/no-unsafe-function-type': 'off',
73
122
  },
74
- ],
75
- env: {
76
- browser: true,
77
- node: true,
78
- es6: true,
79
- jasmine: true,
80
123
  },
81
- globals: {
82
- angular: true,
83
- $: true,
84
- _: true,
124
+ {
125
+ files: ['**/*.js', '**/*.jsx'],
126
+ rules: {
127
+ '@typescript-eslint/no-use-before-define': 'off',
128
+ },
129
+ },
130
+ {
131
+ files: ['**/*.ts', '**/*.tsx'],
132
+ rules: {
133
+ 'no-undef': 'off',
134
+ },
85
135
  },
86
- };
136
+ globalIgnores(['**/*.spec.*', './template/**/*']),
137
+ ];
package/eslint-plugin.ts CHANGED
@@ -17,30 +17,35 @@ import preferPromiseLike from './rules/prefer-promise-like';
17
17
  import react2angularWithErrorBoundary from './rules/react2angular-with-error-boundary';
18
18
  import restPreferStaticStringsInInitializer from './rules/rest-prefer-static-strings-in-initializer';
19
19
 
20
+ const rules = {
21
+ 'api-deprecation': apiDeprecation,
22
+ 'api-no-slashes': apiNoSlashes,
23
+ 'api-no-unused-chaining': apiNoUnusedChaining,
24
+ 'import-from-alias-not-npm': importFromAliasNotNpm,
25
+ 'import-from-npm-not-alias': importFromNpmNotAlias,
26
+ 'import-from-npm-not-relative': importFromNpmNotRelative,
27
+ 'import-from-presentation-not-core': importFromPresentationNotCore,
28
+ 'import-relative-within-subpackage': importRelativeWithinSubpackage,
29
+ 'import-sort': importSort,
30
+ 'migrate-to-mock-http-client': migrateToMockHttpClient,
31
+ 'ng-no-component-class': ngNoComponentClass,
32
+ 'ng-no-module-export': ngNoModuleExport,
33
+ 'ng-no-require-angularjs': ngNoRequireAngularJS,
34
+ 'ng-no-require-module-deps': ngNoRequireModuleDeps,
35
+ 'ng-strictdi': ngStrictDI,
36
+ 'prefer-promise-like': preferPromiseLike,
37
+ 'react2angular-with-error-boundary': react2angularWithErrorBoundary,
38
+ 'rest-prefer-static-strings-in-initializer': restPreferStaticStringsInInitializer,
39
+ };
40
+
20
41
  const plugin = {
21
- configs: {
22
- base: require('./base.config.js'),
23
- none: require('./none.config.js'),
24
- },
25
- rules: {
26
- 'api-deprecation': apiDeprecation,
27
- 'api-no-slashes': apiNoSlashes,
28
- 'api-no-unused-chaining': apiNoUnusedChaining,
29
- 'import-from-alias-not-npm': importFromAliasNotNpm,
30
- 'import-from-npm-not-alias': importFromNpmNotAlias,
31
- 'import-from-npm-not-relative': importFromNpmNotRelative,
32
- 'import-from-presentation-not-core': importFromPresentationNotCore,
33
- 'import-relative-within-subpackage': importRelativeWithinSubpackage,
34
- 'import-sort': importSort,
35
- 'migrate-to-mock-http-client': migrateToMockHttpClient,
36
- 'ng-no-component-class': ngNoComponentClass,
37
- 'ng-no-module-export': ngNoModuleExport,
38
- 'ng-no-require-angularjs': ngNoRequireAngularJS,
39
- 'ng-no-require-module-deps': ngNoRequireModuleDeps,
40
- 'ng-strictdi': ngStrictDI,
41
- 'prefer-promise-like': preferPromiseLike,
42
- 'react2angular-with-error-boundary': react2angularWithErrorBoundary,
43
- 'rest-prefer-static-strings-in-initializer': restPreferStaticStringsInInitializer,
42
+ rules,
43
+ // Configs are loaded lazily to avoid circular dependency
44
+ get configs() {
45
+ return {
46
+ base: require('./base.config.js'),
47
+ none: require('./none.config.js'),
48
+ };
44
49
  },
45
50
  };
46
51
 
package/none.config.js CHANGED
@@ -1,18 +1,36 @@
1
- module.exports = {
2
- parser: '@typescript-eslint/parser',
3
- parserOptions: { sourceType: 'module' },
4
- plugins: ['@typescript-eslint', '@spinnaker/eslint-plugin'],
5
- extends: [],
6
- rules: {},
7
- env: {
8
- browser: true,
9
- node: true,
10
- es6: true,
11
- jasmine: true,
1
+ const { globalIgnores } = require('eslint/config');
2
+
3
+ const tsParser = require('@typescript-eslint/parser');
4
+ const typescriptEslint = require('@typescript-eslint/eslint-plugin');
5
+
6
+ // Import rules directly to avoid circular dependency when this config is bundled in the plugin
7
+ // Use a function to get rules lazily
8
+ const getSpinnakerRules = () => require('./index.js').rules;
9
+ const globals = require('globals');
10
+
11
+ module.exports = [
12
+ {
13
+ languageOptions: {
14
+ parser: tsParser,
15
+ sourceType: 'module',
16
+ parserOptions: {},
17
+
18
+ globals: {
19
+ ...globals.browser,
20
+ ...globals.node,
21
+ ...globals.jasmine,
22
+ angular: true,
23
+ $: true,
24
+ _: true,
25
+ },
26
+ },
27
+
28
+ plugins: {
29
+ '@typescript-eslint': typescriptEslint,
30
+ '@spinnaker': { rules: getSpinnakerRules() },
31
+ },
32
+
33
+ rules: {},
12
34
  },
13
- globals: {
14
- angular: true,
15
- $: true,
16
- _: true,
17
- },
18
- };
35
+ globalIgnores(['**/*.spec.*', './template/**/*']),
36
+ ];
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "type": "git",
5
5
  "url": "https://github.com/spinnaker/spinnaker.git"
6
6
  },
7
- "version": "0.0.0-2025.4-4",
7
+ "version": "0.0.0-2026.1-0",
8
8
  "main": "index.js",
9
9
  "license": "Apache-2.0",
10
10
  "publishConfig": {
@@ -23,25 +23,29 @@
23
23
  },
24
24
  "devDependencies": {
25
25
  "@babel/preset-typescript": "^7.15.0",
26
- "@types/eslint": "^7.28.0",
26
+ "@eslint/compat": "^2.0.2",
27
+ "@eslint/eslintrc": "^3.3.4",
28
+ "@eslint/js": "^9.39.2",
29
+ "@types/eslint": "9.6.1",
27
30
  "@types/estree": "*",
28
- "@types/jest": "^26.0.24",
31
+ "@types/jest": "^29.0.0",
29
32
  "@types/lodash": "^4.14.165",
30
33
  "@types/node": "^16.4.13",
31
- "@typescript-eslint/parser": "5.59.8",
32
- "@typescript-eslint/types": "5.59.8",
33
- "eslint": "7.32.0",
34
+ "@typescript-eslint/parser": "8.54.0",
35
+ "@typescript-eslint/types": "8.54.0",
36
+ "eslint": "9.39.2",
34
37
  "fast-glob": "^3.2.7",
35
- "jest": "^27.0.6",
38
+ "globals": "^17.2.0",
39
+ "jest": "^29.0.0",
36
40
  "prettier": "*",
37
41
  "typescript": "5.0.4"
38
42
  },
39
43
  "peerDependencies": {
40
- "@typescript-eslint/eslint-plugin": "5.59.8",
41
- "@typescript-eslint/parser": "5.59.8",
42
- "eslint": "7.32.0",
43
- "eslint-config-prettier": "6.12.0",
44
- "eslint-plugin-react-hooks": "4.1.2"
44
+ "@typescript-eslint/eslint-plugin": "^8.0.0",
45
+ "@typescript-eslint/parser": "^8.0.0",
46
+ "eslint": "^9.0.0",
47
+ "eslint-config-prettier": "^10.0.0",
48
+ "eslint-plugin-react-hooks": "^5.0.0 || ^7.0.0"
45
49
  },
46
50
  "peerDevDependencies": [
47
51
  "@typescript-eslint/eslint-plugin",
@@ -54,7 +58,8 @@
54
58
  "testPathIgnorePatterns": [
55
59
  "<rootDir>/template",
56
60
  "<rootDir>/rules/ng-strictdi.spec.ts"
57
- ]
61
+ ],
62
+ "testEnvironment": "node"
58
63
  },
59
- "gitHead": "de92b34ca585a04c23170cd6ba1495dbe4a56edb"
64
+ "gitHead": "81101959188c6307f7b15e2f62ecca4aa2f53df7"
60
65
  }
@@ -1,5 +1,11 @@
1
1
  import type { Rule } from 'eslint';
2
- import type { CallExpression, ImportDeclaration, ImportSpecifier, MemberExpression, Node } from 'estree';
2
+ import type * as ESTree from 'estree';
3
+
4
+ type CallExpression = ESTree.CallExpression;
5
+ type ImportDeclaration = ESTree.ImportDeclaration;
6
+ type ImportSpecifier = ESTree.ImportSpecifier;
7
+ type MemberExpression = ESTree.MemberExpression;
8
+ type Node = ESTree.Node;
3
9
  import * as _ from 'lodash/fp';
4
10
 
5
11
  import { getImportName } from '../utils/ast';
@@ -202,7 +208,8 @@ const rule: Rule.RuleModule = {
202
208
  * - part of an xyz() call chained off a variable, e.g.: var foo = API.xyz(); foo.get()
203
209
  * @param node {CallExpression}
204
210
  */
205
- CallExpression(node) {
211
+ CallExpression(_node: any) {
212
+ const node = _node as CallExpression & Rule.NodeParentExtension;
206
213
  if (node.parent.type === 'MemberExpression' || !isAPICall(context, node)) {
207
214
  return undefined;
208
215
  }
@@ -1,5 +1,9 @@
1
1
  import type { Rule } from 'eslint';
2
- import type { CallExpression, Identifier, Literal } from 'estree';
2
+ import type * as ESTree from 'estree';
3
+
4
+ type CallExpression = ESTree.CallExpression;
5
+ type Identifier = ESTree.Identifier;
6
+ type Literal = ESTree.Literal;
3
7
  import { get } from 'lodash';
4
8
 
5
9
  import { getCallingIdentifier, getVariableInScope, isMemberExpression } from '../utils/utils';
@@ -12,7 +16,8 @@ import { getCallingIdentifier, getVariableInScope, isMemberExpression } from '..
12
16
  */
13
17
  const rule = function (context: Rule.RuleContext) {
14
18
  return {
15
- CallExpression: function (node: CallExpression) {
19
+ CallExpression: function (_node: any) {
20
+ const node = _node as CallExpression;
16
21
  const callee = node.callee;
17
22
  const args = node.arguments;
18
23
 
@@ -1,5 +1,4 @@
1
1
  import type { Rule } from 'eslint';
2
- import type { ImportDeclaration } from 'estree';
3
2
 
4
3
  import { getImportFromNpm, getSourceFileDetails } from '../utils/import-aliases';
5
4
 
@@ -18,7 +17,8 @@ const rule = function (context: Rule.RuleContext) {
18
17
  }
19
18
 
20
19
  return {
21
- ImportDeclaration(node: ImportDeclaration) {
20
+ ImportDeclaration(_node: any) {
21
+ const node = _node;
22
22
  if (node.source.type !== 'Literal' || !node.source.value) {
23
23
  return;
24
24
  }
@@ -1,5 +1,4 @@
1
1
  import type { Rule } from 'eslint';
2
- import type { ImportDeclaration } from 'estree';
3
2
 
4
3
  import { getAliasImport, getAllSpinnakerPackages, getSourceFileDetails } from '../utils/import-aliases';
5
4
 
@@ -21,7 +20,8 @@ const rule = function (context: Rule.RuleContext) {
21
20
  const allSpinnakerPackages = getAllSpinnakerPackages(modulesPath);
22
21
 
23
22
  return {
24
- ImportDeclaration: function (node: ImportDeclaration & Rule.NodeParentExtension) {
23
+ ImportDeclaration: function (_node: any) {
24
+ const node = _node;
25
25
  if (node.source.type !== 'Literal' || !node.source.value) {
26
26
  return;
27
27
  }
@@ -1,5 +1,4 @@
1
1
  import type { Rule } from 'eslint';
2
- import type { ImportDeclaration } from 'estree';
3
2
 
4
3
  import { getRelativeImport, getSourceFileDetails } from '../utils/import-aliases';
5
4
 
@@ -20,7 +19,8 @@ const rule = function (context: Rule.RuleContext) {
20
19
  }
21
20
 
22
21
  return {
23
- ImportDeclaration: function (node: ImportDeclaration) {
22
+ ImportDeclaration: function (_node: any) {
23
+ const node = _node;
24
24
  if (node.source.type !== 'Literal' || !node.source.value) {
25
25
  return;
26
26
  }
@@ -1,5 +1,4 @@
1
1
  import type { Rule } from 'eslint';
2
- import type { ImportDeclaration } from 'estree';
3
2
  import path from 'path';
4
3
 
5
4
  import { getAliasImport, getAllSpinnakerPackages, getSourceFileDetails } from '../utils/import-aliases';
@@ -23,7 +22,8 @@ const rule = function (context: Rule.RuleContext) {
23
22
  const allSpinnakerPackages = getAllSpinnakerPackages(modulesPath);
24
23
 
25
24
  return {
26
- ImportDeclaration: function (node: ImportDeclaration & Rule.NodeParentExtension) {
25
+ ImportDeclaration: function (_node: any) {
26
+ const node = _node;
27
27
  if (node.source.type !== 'Literal' || !node.source.value) {
28
28
  return;
29
29
  }
@@ -1,5 +1,4 @@
1
1
  import type { Rule } from 'eslint';
2
- import type { FunctionExpression, ImportDeclaration, ImportSpecifier } from 'estree';
3
2
 
4
3
  import { getImportName } from '../utils/ast';
5
4
  import { getProgram } from '../utils/utils';
@@ -9,13 +8,14 @@ const ruleModule: Rule.RuleModule = {
9
8
  const text = (node) => context.getSourceCode().getText(node);
10
9
 
11
10
  return {
12
- CallExpression(node) {
11
+ CallExpression(_node: any) {
12
+ const node = _node;
13
13
  /** it(() => {}) */
14
14
  const isItBlock = node.callee.type === 'Identifier' && node.callee.name === 'it';
15
15
 
16
16
  if (isItBlock) {
17
17
  const itBlockText = text(node);
18
- const testFunction = node.arguments[1] as FunctionExpression;
18
+ const testFunction = node.arguments[1] as any;
19
19
 
20
20
  const doesFunctionIncludeHttpBackend = !!testFunction && itBlockText.includes('$httpBackend');
21
21
 
@@ -38,12 +38,10 @@ const ruleModule: Rule.RuleModule = {
38
38
  !text(testFunction.body.body[0]).includes('mockHttpClient')
39
39
  ) {
40
40
  const program = getProgram(node);
41
- const allImports = program.body.filter(
42
- (item) => item.type === 'ImportDeclaration',
43
- ) as ImportDeclaration[];
41
+ const allImports = program.body.filter((item) => item.type === 'ImportDeclaration') as any[];
44
42
 
45
43
  const importSpecifiers = allImports
46
- .map((decl) => decl.specifiers as ImportSpecifier[])
44
+ .map((decl) => decl.specifiers as any[])
47
45
  .reduce((acc, x) => acc.concat(x), []);
48
46
 
49
47
  const mockHttpClientImport = importSpecifiers.find((specifier) => {
@@ -12,14 +12,13 @@ const findParentNodeByType = (node: Rule.Node, type: string) =>
12
12
  * @category conventions
13
13
  */
14
14
  import angularRule from '../utils/angular-rule/angular-rule';
15
- import { isNewExpression } from '../utils/utils';
16
15
 
17
16
  const useObjectLiteral = function (context: Rule.RuleContext) {
18
17
  return {
19
18
  'angular?component': function (callee, thisGuy) {
20
19
  const node: Rule.Node = thisGuy.node;
21
20
  const scope: Scope.Scope = thisGuy.scope;
22
- if (isNewExpression(node)) {
21
+ if (node.type === 'NewExpression') {
23
22
  const calleeName = 'name' in node.callee ? node.callee.name : undefined;
24
23
  const fix = (fixer: Rule.RuleFixer) => {
25
24
  const variable = scope.variables.find((x) => x.name === calleeName);
@@ -3,7 +3,6 @@
3
3
  */
4
4
 
5
5
  import type { AST, Rule } from 'eslint';
6
- import type { AssignmentExpression, CallExpression, MemberExpression } from 'estree';
7
6
  import { isCallExpression, isIdentifier, isMemberExpression } from '../utils/utils';
8
7
 
9
8
  const rule = function (context: Rule.RuleContext) {
@@ -20,7 +19,8 @@ const rule = function (context: Rule.RuleContext) {
20
19
  }
21
20
 
22
21
  return {
23
- AssignmentExpression: function (node: AssignmentExpression) {
22
+ AssignmentExpression: function (_node: any) {
23
+ const node = _node;
24
24
  const left = node.left;
25
25
  const right = node.right;
26
26
  const isModuleExports = isModuleExportMemberExpression(left);
@@ -65,13 +65,13 @@ function getAngularModuleNameNode(node) {
65
65
  if (!isCallExpression(node)) return false;
66
66
  const callee = node.callee as Rule.Node;
67
67
 
68
- function angularModuleNameNode(callExpression: CallExpression) {
68
+ function angularModuleNameNode(callExpression: any) {
69
69
  const isLiteral =
70
70
  callExpression.arguments && callExpression.arguments[0] && callExpression.arguments[0].type === 'Literal';
71
71
  return isLiteral ? callExpression.arguments[0] : undefined;
72
72
  }
73
73
 
74
- function isChainedCallExpression(_callee: Rule.Node): _callee is MemberExpression & Rule.NodeParentExtension {
74
+ function isChainedCallExpression(_callee: any): boolean {
75
75
  if (isMemberExpression(_callee)) {
76
76
  return _callee.object && _callee.object.type === 'CallExpression';
77
77
  }
@@ -95,7 +95,7 @@ function getAngularModuleNameNode(node) {
95
95
  }
96
96
 
97
97
  if (isChainedCallExpression(callee)) {
98
- return getAngularModuleNameNode(callee.object);
98
+ return getAngularModuleNameNode((callee as any).object);
99
99
  } else if (isRawModuleCall(callee)) {
100
100
  if (node.arguments && node.arguments[0] && node.arguments[0].type === 'Literal') return angularModuleNameNode(node);
101
101
  } else if (isAngularModuleCall(callee)) {
@@ -8,19 +8,17 @@
8
8
  * angular.module('mymodule', [])
9
9
  */
10
10
  import type { Rule, Scope } from 'eslint';
11
- import type { ImportDeclaration, MemberExpression } from 'estree';
12
11
  import { getProgram, isMemberExpression } from '../utils/utils';
13
12
 
14
13
  const rule = function (context: Rule.RuleContext) {
15
14
  return {
16
- 'MemberExpression[object.name="angular"][property.name="module"]': function (
17
- node: MemberExpression & Rule.NodeParentExtension,
18
- ) {
15
+ 'MemberExpression[object.name="angular"][property.name="module"]': function (_node: any) {
16
+ const node = _node;
19
17
  const angularVar = findAngularVariable(node, context);
20
18
  const angularImport = findAngularImportStatement(node);
21
19
  // Double check that there is only a single use of 'angular' variable and that it's 'angular.module()')
22
20
  if (angularImport && angularVar && angularVar.references.length === 1) {
23
- const { parent } = angularVar.references[0].identifier as Rule.Node;
21
+ const { parent } = angularVar.references[0].identifier as any;
24
22
  if (isMemberExpression(parent)) {
25
23
  if (
26
24
  'name' in parent.object &&
@@ -46,7 +44,7 @@ function findAngularVariable(_node, context): Scope.Variable {
46
44
  return moduleScope && moduleScope.variables.find((v) => v.name === 'angular');
47
45
  }
48
46
 
49
- function findAngularImportStatement(_node): ImportDeclaration {
47
+ function findAngularImportStatement(_node): any {
50
48
  let program = _node;
51
49
  while (program && program.parent) {
52
50
  program = program.parent;
@@ -59,7 +57,7 @@ function findAngularImportStatement(_node): ImportDeclaration {
59
57
  node.source.type === 'Literal' &&
60
58
  node.source.value === 'angular'
61
59
  );
62
- }) as ImportDeclaration;
60
+ });
63
61
  }
64
62
 
65
63
  /*
@@ -71,7 +69,7 @@ import { module } from 'angular';
71
69
 
72
70
  module('module', ['dep']);
73
71
  */
74
- function getFixForAngularModule(angularDotModuleNode: Rule.Node, importStatement: ImportDeclaration) {
72
+ function getFixForAngularModule(angularDotModuleNode: Rule.Node, importStatement: any) {
75
73
  return function (fixer: Rule.RuleFixer) {
76
74
  return [
77
75
  fixer.replaceText(angularDotModuleNode, 'module'),
@@ -1,5 +1,4 @@
1
1
  import type { Rule } from 'eslint';
2
- import type { ArrayExpression, Node } from 'estree';
3
2
  import fs from 'fs';
4
3
  import path from 'path';
5
4
 
@@ -12,7 +11,8 @@ import path from 'path';
12
11
  */
13
12
  const rule = function (context: Rule.RuleContext) {
14
13
  return {
15
- ArrayExpression: function (node: ArrayExpression) {
14
+ ArrayExpression: function (_node: any) {
15
+ const node = _node;
16
16
  if (isInAngularModuleCall(node)) {
17
17
  const requireDotNames = node.elements.map((element) => getRequireDotNameNode(element)).filter((x) => !!x);
18
18
  requireDotNames.forEach(([_node, relativePath]) => {
@@ -54,7 +54,7 @@ angular.module('module', [
54
54
  ANGULAR_UI_BOOTSTRAP
55
55
  ]);
56
56
  */
57
- function getFixForBareRequire(node: Node, requiredString: string) {
57
+ function getFixForBareRequire(node: any, requiredString: string) {
58
58
  return function (fixer: Rule.RuleFixer) {
59
59
  const variableName = requiredString.replace(/[^\w_]/g, '_').toUpperCase();
60
60
  const lastImport = findLastImportStatement(node);
@@ -80,7 +80,7 @@ angular.module('module', [
80
80
  SOME_REQUIRE_STRING
81
81
  ]);
82
82
  */
83
- function getFixForRequireDotAnything(node: Node, requiredString: string, property: string) {
83
+ function getFixForRequireDotAnything(node: any, requiredString: string, property: string) {
84
84
  return function (fixer: Rule.RuleFixer) {
85
85
  const variableName = requiredString
86
86
  .replace(/^[^\w_]*/g, '')
@@ -109,7 +109,7 @@ angular.module('module', [
109
109
  DEPENDENCY_SYMBOL
110
110
  ]);
111
111
  */
112
- function getFixForRequireDotName(node: Node, filename: string, relativePath: string) {
112
+ function getFixForRequireDotName(node: any, filename: string, relativePath: string) {
113
113
  const modulesPath = filename.replace(/modules\/.*/, 'modules/');
114
114
 
115
115
  const path1 = path.resolve(filename, '..', relativePath);
@@ -138,7 +138,7 @@ function getFixForRequireDotName(node: Node, filename: string, relativePath: str
138
138
  }
139
139
  }
140
140
 
141
- function findLastImportStatement(_node: Node) {
141
+ function findLastImportStatement(_node: any) {
142
142
  let program = _node as Rule.Node;
143
143
  while (program && program.parent) {
144
144
  program = program.parent;
@@ -153,7 +153,7 @@ function findLastImportStatement(_node: Node) {
153
153
  }
154
154
 
155
155
  // require('./some/nested/angularjs/module').name
156
- function getRequireDotNameNode(node: Node): [Node, string] {
156
+ function getRequireDotNameNode(node: any): [any, string] {
157
157
  if (node.type !== 'MemberExpression') return undefined;
158
158
  if (node.property.type !== 'Identifier' || node.property.name !== 'name') return undefined;
159
159
  if (node.object.type !== 'CallExpression' || (node.object.callee as any).name !== 'require') return undefined;
@@ -164,7 +164,7 @@ function getRequireDotNameNode(node: Node): [Node, string] {
164
164
  }
165
165
 
166
166
  // require('something').anything
167
- function getRequireDotAnythingNode(node: Node): [Node, string, string] {
167
+ function getRequireDotAnythingNode(node: any): [any, string, string] {
168
168
  if (node.type !== 'MemberExpression') return undefined;
169
169
  if (node.property.type !== 'Identifier') return undefined;
170
170
  if (node.object.type !== 'CallExpression' || (node.object.callee as any).name !== 'require') return undefined;
@@ -175,7 +175,7 @@ function getRequireDotAnythingNode(node: Node): [Node, string, string] {
175
175
  }
176
176
 
177
177
  // require('something')
178
- function getBareRequireNode(node): [Node, string] {
178
+ function getBareRequireNode(node): [any, string] {
179
179
  if (node.type !== 'CallExpression' || node.callee.name !== 'require') return undefined;
180
180
  if (node.arguments.length !== 1 || node.arguments[0].type !== 'Literal') return undefined;
181
181
 
@@ -272,7 +272,7 @@ const rule = function (context: Rule.RuleContext) {
272
272
  'CallExpression:exit': function (node) {
273
273
  const { object, property } = node.callee;
274
274
  if (object && object.name === '$provide' && property && property.name === 'decorator') {
275
- checkDi(null, { node: node.arguments[1], scope: context.getScope() });
275
+ checkDi(null, { node: node.arguments[1], scope: context.sourceCode.getScope(node) });
276
276
  }
277
277
  },
278
278
  AssignmentExpression: function (node) {
@@ -1,6 +1,5 @@
1
1
  import type { TSESTree } from '@typescript-eslint/types';
2
2
  import type { Rule } from 'eslint';
3
- import type { ImportDeclaration } from 'estree';
4
3
  import _ from 'lodash';
5
4
 
6
5
  /**
@@ -50,7 +49,8 @@ const rule = function (context: Rule.RuleContext) {
50
49
  },
51
50
 
52
51
  // If there are any unused IPromise imports, remove them
53
- ImportDeclaration: function (node: ImportDeclaration) {
52
+ ImportDeclaration: function (_node: any) {
53
+ const node = _node;
54
54
  const importIPromise = {
55
55
  type: 'ImportSpecifier',
56
56
  imported: {
@@ -66,7 +66,7 @@ const rule = function (context: Rule.RuleContext) {
66
66
  const specifiers = node.specifiers || [];
67
67
  const foundIPromiseImport = specifiers.find((s) => _.isMatch(s, importIPromise));
68
68
 
69
- const variables = context.getScope().variables;
69
+ const variables = context.sourceCode.getScope(node).variables;
70
70
  const variable = variables.find((x) => x.defs.some((def) => def.node === foundIPromiseImport));
71
71
  const unused = variable && variable.references.length === 0;
72
72
 
@@ -1,5 +1,4 @@
1
1
  import type { Rule } from 'eslint';
2
- import type { CallExpression, ImportDeclaration } from 'estree';
3
2
  import _ from 'lodash';
4
3
  import { isLiteral } from '../utils/utils';
5
4
 
@@ -9,12 +8,13 @@ import { isLiteral } from '../utils/utils';
9
8
  * @version 0.1.0
10
9
  */
11
10
  const rule = function (context: Rule.RuleContext) {
12
- let coreImport: ImportDeclaration;
11
+ let coreImport: any;
13
12
 
14
13
  return {
15
14
  // Find an import from @spinnaker/core or core/presentation
16
15
  // This will be used to add the import for withErrorBoundary
17
- ImportDeclaration: function (node: ImportDeclaration) {
16
+ ImportDeclaration: function (_node: any) {
17
+ const node = _node;
18
18
  // import { foo, bar } from 'package';
19
19
  // ^^^^^^^
20
20
  const from = node.source.value || '';
@@ -22,7 +22,8 @@ const rule = function (context: Rule.RuleContext) {
22
22
  coreImport = node;
23
23
  }
24
24
  },
25
- CallExpression: function (node: CallExpression & Rule.NodeParentExtension) {
25
+ CallExpression: function (_node: any) {
26
+ const node = _node;
26
27
  // Find:
27
28
  // react2angular(SomeComponent, ...)
28
29
  const match = {
@@ -69,7 +70,7 @@ const rule = function (context: Rule.RuleContext) {
69
70
  },
70
71
  },
71
72
  };
72
- const isComponentCallExpression = (node): node is CallExpression => _.isMatch(node, parentMatch);
73
+ const isComponentCallExpression = (node): node is any => _.isMatch(node, parentMatch);
73
74
 
74
75
  let componentName = `'react2angular component'`;
75
76
  const parentNode = node.parent;
@@ -24,11 +24,5 @@ ruleTester.run('rest-prefer-static-strings-in-initializer', rule, {
24
24
  output: "REST('foo/bar').path('baz').get()",
25
25
  errors: ["Prefer REST('/foo/bar') over REST().path('foo', 'bar')"],
26
26
  },
27
- {
28
- // Process one path arg at a time
29
- code: "REST('foo').path('bar', 'baz').get()",
30
- output: "REST('foo/bar').path('baz').get()",
31
- errors: ["Prefer REST('/foo/bar') over REST().path('foo', 'bar')"],
32
- },
33
27
  ],
34
28
  });
@@ -3,7 +3,6 @@
3
3
  */
4
4
 
5
5
  import type { Rule } from 'eslint';
6
- import type { CallExpression, Literal } from 'estree';
7
6
  import * as _ from 'lodash/fp';
8
7
 
9
8
  import { getCallChain, getCallingIdentifierName, isLiteral } from '../utils/utils';
@@ -15,7 +14,8 @@ const ruleModule: Rule.RuleModule = {
15
14
  /**
16
15
  * Look for chains of CallExpressions that are part of a REST().path() call
17
16
  */
18
- CallExpression(node: CallExpression & Rule.NodeParentExtension) {
17
+ CallExpression(_node: any) {
18
+ const node = _node;
19
19
  const callingIdentifierName = getCallingIdentifierName(node);
20
20
  if (node.parent.type === 'MemberExpression' || callingIdentifierName !== 'REST') {
21
21
  return undefined;
@@ -32,8 +32,8 @@ const ruleModule: Rule.RuleModule = {
32
32
  const restCall = callChain[0];
33
33
  const pathCall = callChain[1];
34
34
 
35
- const restArg = restCall.arguments[0] as Literal;
36
- const firstPathArg = pathCall.arguments[0] as Literal;
35
+ const restArg = restCall.arguments[0] as any;
36
+ const firstPathArg = pathCall.arguments[0] as any;
37
37
 
38
38
  // Only REST('literal').path('literal', ...)
39
39
  // Ignores: REST(variable) and REST().path(variable)
@@ -131,7 +131,7 @@ function angularRule(ruleDefinition) {
131
131
  return {
132
132
  callExpression: callExpressionNode,
133
133
  node: findInjectedArgument(callExpressionNode),
134
- scope: context.getScope(),
134
+ scope: context.sourceCode.getScope(callExpressionNode),
135
135
  };
136
136
  }
137
137
 
@@ -182,7 +182,7 @@ function angularRule(ruleDefinition) {
182
182
  } else if (callee.object.type === 'Identifier') {
183
183
  // var app = angular.module(); app.factory()
184
184
  // ^^^^^^^
185
- var scope = context.getScope();
185
+ var scope = context.sourceCode.getScope(callExpressionNode);
186
186
  var isAngularModule = scope.variables.some(function (variable) {
187
187
  if (callee.object.name !== variable.name) {
188
188
  return false;
@@ -559,7 +559,8 @@ function isUIRouterStateDefinition(node) {
559
559
  */
560
560
  function findIdentiferInScope(context, identifier) {
561
561
  var identifierNode = null;
562
- context.getScope().variables.forEach(function (variable) {
562
+ var scope = context.sourceCode ? context.sourceCode.getScope(identifier) : context.getScope();
563
+ scope.variables.forEach(function (variable) {
563
564
  if (variable.name === identifier.name) {
564
565
  identifierNode = variable.defs[0].node;
565
566
  if (identifierNode.type === 'VariableDeclarator') {
package/utils/ast.ts CHANGED
@@ -1,6 +1,4 @@
1
- import type { Identifier, Literal } from 'estree';
2
-
3
- export function getImportName(node: Identifier | Literal): string {
1
+ export function getImportName(node: any): string {
4
2
  if (node.type === 'Identifier') {
5
3
  return node.name;
6
4
  }
@@ -1,8 +1,8 @@
1
1
  const RuleTester = require('eslint').RuleTester;
2
2
  module.exports = new RuleTester({
3
- parserOptions: {
4
- ecmaVersion: 8,
3
+ languageOptions: {
4
+ ecmaVersion: 2017,
5
5
  sourceType: 'module',
6
+ parser: require('@typescript-eslint/parser'),
6
7
  },
7
- parser: require.resolve('@typescript-eslint/parser'),
8
8
  });
package/utils/utils.ts CHANGED
@@ -1,17 +1,17 @@
1
1
  import type { Rule, Scope } from 'eslint';
2
- import type {
3
- CallExpression,
4
- Expression,
5
- Identifier,
6
- Literal,
7
- MemberExpression,
8
- NewExpression,
9
- Node,
10
- Program,
11
- SpreadElement,
12
- } from 'estree';
2
+ import type * as ESTree from 'estree';
13
3
  import * as _ from 'lodash/fp';
14
4
 
5
+ type Node = ESTree.Node;
6
+ type CallExpression = ESTree.CallExpression;
7
+ type Expression = ESTree.Expression;
8
+ type Identifier = ESTree.Identifier;
9
+ type Literal = ESTree.Literal;
10
+ type MemberExpression = ESTree.MemberExpression;
11
+ type NewExpression = ESTree.NewExpression;
12
+ type Program = ESTree.Program;
13
+ type SpreadElement = ESTree.SpreadElement;
14
+
15
15
  export const getNodeType = (obj: Node) => obj?.type;
16
16
  export const isType = <T extends Node>(type: string) => (obj: Node): obj is T => getNodeType(obj) === type;
17
17
  export const isIdentifier = isType<Identifier>('Identifier');
@@ -53,7 +53,7 @@ export function getVariableInScope(context: Rule.RuleContext, identifier: Identi
53
53
  return undefined;
54
54
  }
55
55
 
56
- const { references } = context.getScope();
56
+ const { references } = context.sourceCode.getScope((identifier as unknown) as Rule.Node);
57
57
  const ref = references.find((r) => r.identifier.name === identifier.name);
58
58
  return ref ? ref.resolved : undefined;
59
59
  }
@@ -61,10 +61,10 @@ export function getVariableInScope(context: Rule.RuleContext, identifier: Identi
61
61
  export const getVariableInitializer = _.get('defs[0].node.init');
62
62
 
63
63
  export function getProgram(node: Node): Program {
64
- let _node = node as Node & Rule.NodeParentExtension;
64
+ let _node = node as any;
65
65
  while (_node.parent) {
66
66
  if (_node.parent.type === 'Program') {
67
- return _node.parent;
67
+ return _node.parent as Program;
68
68
  }
69
69
  _node = _node.parent;
70
70
  }