@spinnaker/eslint-plugin 2026.2.2 → 2026.3.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/base.config.js CHANGED
@@ -32,7 +32,6 @@ module.exports = [
32
32
  ...globals.browser,
33
33
  ...globals.node,
34
34
  ...globals.jasmine,
35
- angular: true,
36
35
  $: true,
37
36
  _: true,
38
37
  },
@@ -54,14 +53,6 @@ module.exports = [
54
53
  '@spinnaker/import-from-npm-not-relative': 2,
55
54
  '@spinnaker/import-from-presentation-not-core': 2,
56
55
  '@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
56
  '@spinnaker/rest-prefer-static-strings-in-initializer': 2,
66
57
  indent: 'off',
67
58
  'member-ordering': 'off',
@@ -0,0 +1,34 @@
1
+ import plugin from './eslint-plugin';
2
+
3
+ describe('ESLint config contracts', () => {
4
+ test('provides flat and legacy base configs', () => {
5
+ const configs = plugin.configs as Record<string, unknown>;
6
+
7
+ expect(Array.isArray(configs.base)).toBe(true);
8
+ expect(Array.isArray(configs['legacy-base'])).toBe(false);
9
+ expect(configs['legacy-base']).toEqual(
10
+ expect.objectContaining({
11
+ plugins: expect.arrayContaining(['@spinnaker/eslint-plugin']),
12
+ rules: expect.objectContaining({
13
+ '@typescript-eslint/consistent-type-imports': ['error', { prefer: 'type-imports' }],
14
+ }),
15
+ }),
16
+ );
17
+ });
18
+
19
+ test('omits the retired migration rule while preserving the generic API chaining rule', () => {
20
+ const configs = plugin.configs as Record<string, unknown>;
21
+ const flatRules = (configs.base as Array<{ rules?: Record<string, unknown> }>)
22
+ .map((config) => config.rules)
23
+ .filter((rules): rules is Record<string, unknown> => Boolean(rules));
24
+ const legacyRules = (configs['legacy-base'] as { rules: Record<string, unknown> }).rules;
25
+
26
+ expect(plugin.rules).not.toHaveProperty('migrate-to-mock-http-client');
27
+ expect(flatRules.some((rules) => '@spinnaker/migrate-to-mock-http-client' in rules)).toBe(false);
28
+ expect(legacyRules).not.toHaveProperty('@spinnaker/migrate-to-mock-http-client');
29
+
30
+ expect(plugin.rules).toHaveProperty('api-no-unused-chaining');
31
+ expect(flatRules.some((rules) => '@spinnaker/api-no-unused-chaining' in rules)).toBe(true);
32
+ expect(legacyRules).toHaveProperty('@spinnaker/api-no-unused-chaining', 2);
33
+ });
34
+ });
package/eslint-plugin.ts CHANGED
@@ -7,14 +7,6 @@ import importFromNpmNotRelative from './rules/import-from-npm-not-relative';
7
7
  import importFromPresentationNotCore from './rules/import-from-presentation-not-core';
8
8
  import importRelativeWithinSubpackage from './rules/import-relative-within-subpackage';
9
9
  import importSort from './rules/import-sort';
10
- import migrateToMockHttpClient from './rules/migrate-to-mock-http-client';
11
- import ngNoComponentClass from './rules/ng-no-component-class';
12
- import ngNoModuleExport from './rules/ng-no-module-export';
13
- import ngNoRequireAngularJS from './rules/ng-no-require-angularjs';
14
- import ngNoRequireModuleDeps from './rules/ng-no-require-module-deps';
15
- import ngStrictDI from './rules/ng-strictdi';
16
- import preferPromiseLike from './rules/prefer-promise-like';
17
- import react2angularWithErrorBoundary from './rules/react2angular-with-error-boundary';
18
10
  import restPreferStaticStringsInInitializer from './rules/rest-prefer-static-strings-in-initializer';
19
11
 
20
12
  const rules = {
@@ -27,14 +19,6 @@ const rules = {
27
19
  'import-from-presentation-not-core': importFromPresentationNotCore,
28
20
  'import-relative-within-subpackage': importRelativeWithinSubpackage,
29
21
  '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
22
  'rest-prefer-static-strings-in-initializer': restPreferStaticStringsInInitializer,
39
23
  };
40
24
 
@@ -44,6 +28,7 @@ const plugin = {
44
28
  get configs() {
45
29
  return {
46
30
  base: require('./base.config.js'),
31
+ 'legacy-base': require('./legacy.config.js'),
47
32
  none: require('./none.config.js'),
48
33
  };
49
34
  },
@@ -0,0 +1,72 @@
1
+ module.exports = {
2
+ env: {
3
+ browser: true,
4
+ jasmine: true,
5
+ node: true,
6
+ },
7
+ extends: ['eslint:recommended', 'prettier', 'plugin:@typescript-eslint/recommended'],
8
+ globals: {
9
+ $: 'readonly',
10
+ _: 'readonly',
11
+ },
12
+ ignorePatterns: ['**/*.spec.*', 'template/**/*'],
13
+ parser: '@typescript-eslint/parser',
14
+ parserOptions: {
15
+ sourceType: 'module',
16
+ },
17
+ plugins: ['@spinnaker/eslint-plugin', 'react-hooks'],
18
+ rules: {
19
+ '@spinnaker/api-deprecation': 2,
20
+ '@spinnaker/api-no-slashes': 2,
21
+ '@spinnaker/api-no-unused-chaining': 2,
22
+ '@spinnaker/import-from-alias-not-npm': 2,
23
+ '@spinnaker/import-from-npm-not-alias': 2,
24
+ '@spinnaker/import-from-npm-not-relative': 2,
25
+ '@spinnaker/import-from-presentation-not-core': 2,
26
+ '@spinnaker/import-relative-within-subpackage': 2,
27
+ '@spinnaker/import-sort': 1,
28
+ '@spinnaker/rest-prefer-static-strings-in-initializer': 2,
29
+ '@typescript-eslint/array-type': ['error', { default: 'array-simple' }],
30
+ '@typescript-eslint/ban-ts-comment': 'off',
31
+ '@typescript-eslint/ban-types': 'off',
32
+ '@typescript-eslint/consistent-type-imports': ['error', { prefer: 'type-imports' }],
33
+ '@typescript-eslint/explicit-function-return-type': 'off',
34
+ '@typescript-eslint/explicit-member-accessibility': 'off',
35
+ '@typescript-eslint/explicit-module-boundary-types': 'off',
36
+ '@typescript-eslint/indent': 'off',
37
+ '@typescript-eslint/no-empty-function': 'off',
38
+ '@typescript-eslint/no-empty-interface': 'off',
39
+ '@typescript-eslint/no-explicit-any': 'off',
40
+ '@typescript-eslint/no-parameter-properties': 'off',
41
+ '@typescript-eslint/no-require-imports': 'off',
42
+ '@typescript-eslint/no-this-alias': 'off',
43
+ '@typescript-eslint/no-unused-expressions': 'off',
44
+ '@typescript-eslint/no-unused-vars': 'off',
45
+ '@typescript-eslint/no-use-before-define': 'off',
46
+ '@typescript-eslint/no-var-requires': 'off',
47
+ '@typescript-eslint/triple-slash-reference': 'off',
48
+ indent: 'off',
49
+ 'no-console': ['error', { allow: ['warn', 'error'] }],
50
+ 'no-extra-boolean-cast': 'off',
51
+ 'no-prototype-builtins': 'off',
52
+ 'one-var': ['error', { initialized: 'never' }],
53
+ 'prefer-rest-params': 'off',
54
+ 'prefer-spread': 'off',
55
+ 'react-hooks/rules-of-hooks': 'error',
56
+ 'require-atomic-updates': 'off',
57
+ },
58
+ overrides: [
59
+ {
60
+ files: ['**/*.js', '**/*.jsx'],
61
+ rules: {
62
+ '@typescript-eslint/no-use-before-define': 'off',
63
+ },
64
+ },
65
+ {
66
+ files: ['**/*.ts', '**/*.tsx'],
67
+ rules: {
68
+ 'no-undef': 'off',
69
+ },
70
+ },
71
+ ],
72
+ };
package/none.config.js CHANGED
@@ -19,7 +19,6 @@ module.exports = [
19
19
  ...globals.browser,
20
20
  ...globals.node,
21
21
  ...globals.jasmine,
22
- angular: true,
23
22
  $: true,
24
23
  _: true,
25
24
  },
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": "2026.2.2",
7
+ "version": "2026.3.0",
8
8
  "main": "index.js",
9
9
  "license": "Apache-2.0",
10
10
  "publishConfig": {
@@ -22,9 +22,10 @@
22
22
  "ts-node": "*"
23
23
  },
24
24
  "devDependencies": {
25
+ "@babel/preset-env": "^7.15.0",
25
26
  "@babel/preset-typescript": "^7.15.0",
26
27
  "@eslint/compat": "^2.0.3",
27
- "@eslint/eslintrc": "^3.3.5",
28
+ "@eslint/eslintrc": "^3.3.6",
28
29
  "@eslint/js": "^9.39.2",
29
30
  "@types/eslint": "9.6.1",
30
31
  "@types/estree": "*",
@@ -57,10 +58,9 @@
57
58
  ],
58
59
  "jest": {
59
60
  "testPathIgnorePatterns": [
60
- "<rootDir>/template",
61
- "<rootDir>/rules/ng-strictdi.spec.ts"
61
+ "<rootDir>/template"
62
62
  ],
63
63
  "testEnvironment": "node"
64
64
  },
65
- "gitHead": "db086c61529c9421e8c0c2d4a2b8eca88c675ac8"
65
+ "gitHead": "0f7d75f5d4b42c1675e2ecc69a53f8eb73090eb9"
66
66
  }
@@ -2,7 +2,7 @@ import rule from '../rules/api-no-unused-chaining';
2
2
  import ruleTester from '../utils/ruleTester';
3
3
  const errorMessage = (text) => `Unused API.xyz() method chaining no longer works. Re-assign the result of: ${text}`;
4
4
 
5
- ruleTester.run('api-no-slashes', rule, {
5
+ ruleTester.run('api-no-unused-chaining', rule, {
6
6
  valid: [
7
7
  { code: `const fooBar = API.one('foo', 'bar');` },
8
8
  { code: `let fooBar = API.one('foo', 'bar'); fooBar = fooBar.useCache()` },
@@ -22,5 +22,9 @@ ruleTester.run('api-no-slashes', rule, {
22
22
  code: `var foo = API.one('foo/bad'); foo.withParams({});`,
23
23
  errors: [errorMessage(`foo.withParams({});`)],
24
24
  },
25
+ {
26
+ code: `var foo = API.one('foo/bad'); foo.data({});`,
27
+ errors: [errorMessage(`foo.data({});`)],
28
+ },
25
29
  ],
26
30
  });
@@ -9,21 +9,10 @@ const isApiConfigCall = _.overSome([
9
9
  _.matches({ property: { type: 'Identifier', name: 'data' } }),
10
10
  ]);
11
11
 
12
- const falsePostitives = _.overSome([
13
- _.matches({
14
- property: { type: 'Identifier', name: 'data' },
15
- object: { type: 'Identifier', name: '$element' },
16
- }),
17
- ]);
18
-
19
12
  const create = function (context) {
20
13
  return {
21
14
  ExpressionStatement(node) {
22
- if (
23
- node.expression.type === 'CallExpression' &&
24
- isApiConfigCall(node.expression.callee) &&
25
- !falsePostitives(node.expression.callee)
26
- ) {
15
+ if (node.expression.type === 'CallExpression' && isApiConfigCall(node.expression.callee)) {
27
16
  const text = context.getSourceCode().getText(node);
28
17
  context.report({
29
18
  node,
@@ -5,7 +5,7 @@ ruleTester.run('import-sort', rule, {
5
5
  valid: [
6
6
  {
7
7
  code: `
8
- import angular from 'angular';
8
+ import axios from 'axios';
9
9
  import 'jquery';
10
10
  import React, { useCallback, useState } from 'react';
11
11
  import * as Select from 'react-select';
@@ -29,11 +29,11 @@ import './styles.less';
29
29
  {
30
30
  code: `
31
31
  import React, {useState, useCallback} from 'react';
32
- import angular from 'angular';
32
+ import axios from 'axios';
33
33
  import * as Select from 'react-select';
34
34
  `,
35
35
  output: `
36
- import angular from 'angular';
36
+ import axios from 'axios';
37
37
  import React, { useCallback, useState } from 'react';
38
38
  import * as Select from 'react-select';
39
39
  `,
@@ -47,7 +47,7 @@ const {useState, useCallback} = React;
47
47
 
48
48
  import { Application } from 'core/application';
49
49
  import Bar from "./bar";
50
- import angular from 'angular';
50
+ import axios from 'axios';
51
51
  // Some comment about react-select
52
52
  import * as Select from 'react-select';
53
53
  import {
@@ -62,7 +62,7 @@ import 'bootstrap.less';
62
62
  `,
63
63
  // For some strange reason eslint fixer writes additional newline characters
64
64
  output: `
65
- import angular from 'angular';
65
+ import axios from 'axios';
66
66
  import 'jquery';
67
67
  import React from 'react';
68
68
  // Some comment about react-select
@@ -142,9 +142,9 @@ const printImportDeclaration = (context, importDeclaration) => {
142
142
  * Returns a custom textual representation of import declarations which will be used to verify if they are already
143
143
  * sorted. For example
144
144
  *
145
- * `import React, {useState, useCallback} from 'react';\nimport angular from 'angular';`
145
+ * `import React, {useState, useCallback} from 'react';\nimport axios from 'axios';`
146
146
  * will be written as
147
- * `react: React, useState, useCallback\nangular: angular`
147
+ * `react: React, useState, useCallback\naxios: axios`
148
148
  */
149
149
  const getText = (importDeclarations) => {
150
150
  return importDeclarations.reduce((output, importDeclaration) => {
package/test.eslintrc CHANGED
@@ -1,20 +1,18 @@
1
1
  {
2
2
  parser: '@typescript-eslint/parser',
3
- "rules": {
3
+ rules: {},
4
+ parserOptions: {
5
+ ecmaVersion: 8,
6
+ sourceType: 'module',
4
7
  },
5
- "parserOptions": {
6
- "ecmaVersion": 8,
7
- "sourceType": "module"
8
+ env: {
9
+ browser: true,
10
+ node: true,
11
+ es6: true,
12
+ jasmine: true,
8
13
  },
9
- "env": {
10
- "browser": true,
11
- "node": true,
12
- "es6": true,
13
- "jasmine": true,
14
+ globals: {
15
+ $: true,
16
+ _: true,
14
17
  },
15
- "globals": {
16
- "angular": true,
17
- "$": true,
18
- "_": true,
19
- }
20
18
  }
@@ -0,0 +1,16 @@
1
+ import type { Rule, Scope } from 'eslint';
2
+
3
+ import { getVariableInScope } from './utils';
4
+
5
+ describe('getVariableInScope', () => {
6
+ test('uses the legacy context scope API when SourceCode does not provide one', () => {
7
+ const variable = {} as Scope.Variable;
8
+ const context = ({
9
+ getScope: () => ({
10
+ references: [{ identifier: { name: 'value' }, resolved: variable }],
11
+ }),
12
+ } as unknown) as Rule.RuleContext;
13
+
14
+ expect(getVariableInScope(context, { name: 'value', type: 'Identifier' })).toBe(variable);
15
+ });
16
+ });
package/utils/utils.ts CHANGED
@@ -53,7 +53,9 @@ export function getVariableInScope(context: Rule.RuleContext, identifier: Identi
53
53
  return undefined;
54
54
  }
55
55
 
56
- const { references } = context.sourceCode.getScope((identifier as unknown) as Rule.Node);
56
+ const { references } = context.sourceCode?.getScope
57
+ ? context.sourceCode.getScope((identifier as unknown) as Rule.Node)
58
+ : ((context as unknown) as { getScope: () => Pick<Scope.Scope, 'references'> }).getScope();
57
59
  const ref = references.find((r) => r.identifier.name === identifier.name);
58
60
  return ref ? ref.resolved : undefined;
59
61
  }
@@ -1,78 +0,0 @@
1
- import rule from './migrate-to-mock-http-client';
2
- import ruleTester from '../utils/ruleTester';
3
-
4
- ruleTester.run('migrate-to-mock-http-client', rule, {
5
- valid: [
6
- {
7
- code: 'it(() => { const http = mockHttpClient(); })',
8
- },
9
- ],
10
- invalid: [
11
- {
12
- code: `it('does things', () => { $httpBackend.flush() })`,
13
- output: `it('does things', async () => { $httpBackend.flush() })`,
14
- errors: ['Migrate to MockHttpClient (step 1): make test function async'],
15
- },
16
-
17
- // Step 1 make async
18
- {
19
- code: `
20
- describe('foo bar', () => {
21
- it('does things', () => {
22
- $httpBackend.flush()
23
- })
24
- })`,
25
- output: `
26
- describe('foo bar', () => {
27
- it('does things', async () => {
28
- $httpBackend.flush()
29
- })
30
- })`,
31
- errors: ['Migrate to MockHttpClient (step 1): make test function async'],
32
- },
33
-
34
- // Step 2 create mock
35
- {
36
- code: `
37
- describe('foo bar', () => {
38
- it('does things', async () => {
39
- $httpBackend.flush()
40
- })
41
- })`,
42
- output: `import { mockHttpClient } from 'core/api/mock/jasmine';
43
-
44
- describe('foo bar', () => {
45
- it('does things', async () => {
46
- const http = mockHttpClient();
47
- $httpBackend.flush()
48
- })
49
- })`,
50
- errors: ['Migrate to MockHttpClient (step 2): Create a MockHttpClient named "http"'],
51
- },
52
-
53
- // Step 3 change variables
54
- {
55
- code: `
56
- import { mockHttpClient } from 'core/api/mock/jasmine';
57
- describe('foo bar', () => {
58
- it('does things', async () => {
59
- const http = mockHttpClient();
60
- $httpBackend.expectGET('/foo/bar').respond(200, { bar: 15 });
61
- service.fetchBars();
62
- $httpBackend.flush()
63
- })
64
- })`,
65
- output: `
66
- import { mockHttpClient } from 'core/api/mock/jasmine';
67
- describe('foo bar', () => {
68
- it('does things', async () => {
69
- const http = mockHttpClient();
70
- http.expectGET('/foo/bar').respond(200, { bar: 15 });
71
- service.fetchBars();
72
- await http.flush()
73
- })
74
- })`,
75
- errors: ['Migrate to MockHttpClient (step 3): replace $httpBackend with http'],
76
- },
77
- ],
78
- });
@@ -1,121 +0,0 @@
1
- import type { Rule } from 'eslint';
2
-
3
- import { getImportName } from '../utils/ast';
4
- import { getProgram } from '../utils/utils';
5
-
6
- const ruleModule: Rule.RuleModule = {
7
- create(context) {
8
- const text = (node) => context.getSourceCode().getText(node);
9
-
10
- return {
11
- CallExpression(_node: any) {
12
- const node = _node;
13
- /** it(() => {}) */
14
- const isItBlock = node.callee.type === 'Identifier' && node.callee.name === 'it';
15
-
16
- if (isItBlock) {
17
- const itBlockText = text(node);
18
- const testFunction = node.arguments[1] as any;
19
-
20
- const doesFunctionIncludeHttpBackend = !!testFunction && itBlockText.includes('$httpBackend');
21
-
22
- if (doesFunctionIncludeHttpBackend) {
23
- const isFirstArgAFunction = ['FunctionExpression', 'ArrowFunctionExpression'].includes(testFunction.type);
24
-
25
- if (isFirstArgAFunction) {
26
- // Fix 1: make the test 'async'
27
- if (testFunction.async !== true) {
28
- return context.report({
29
- node,
30
- message: 'Migrate to MockHttpClient (step 1): make test function async',
31
- fix: (fixer) => fixer.insertTextBefore(testFunction, 'async '),
32
- });
33
- }
34
-
35
- // Fix 2: Add a 'http' variable
36
- if (
37
- testFunction.body.type === 'BlockStatement' &&
38
- !text(testFunction.body.body[0]).includes('mockHttpClient')
39
- ) {
40
- const program = getProgram(node);
41
- const allImports = program.body.filter((item) => item.type === 'ImportDeclaration') as any[];
42
-
43
- const importSpecifiers = allImports
44
- .map((decl) => decl.specifiers as any[])
45
- .reduce((acc, x) => acc.concat(x), []);
46
-
47
- const mockHttpClientImport = importSpecifiers.find((specifier) => {
48
- return specifier.imported && getImportName(specifier.imported) === 'mockHttpClient';
49
- });
50
-
51
- return context.report({
52
- node,
53
- message: 'Migrate to MockHttpClient (step 2): Create a MockHttpClient named "http"',
54
- fix: (fixer) => {
55
- const insertHttp = fixer.insertTextBefore(
56
- testFunction.body.body[0],
57
- 'const http = mockHttpClient();\n',
58
- );
59
-
60
- let insertImport = fixer.insertTextBeforeRange(
61
- [0, 0],
62
- `import { mockHttpClient } from 'core/api/mock/jasmine';\n`,
63
- );
64
-
65
- // Put after 'use strict'
66
- const sourcecode = text(program);
67
- const [preamble] = /^['"]use strict['"];?/.exec(sourcecode) || [];
68
- if (preamble) {
69
- const insertPos = preamble.length;
70
- insertImport = fixer.insertTextAfterRange(
71
- [insertPos, insertPos],
72
- `\nimport { mockHttpClient } from 'core/api/mock/jasmine';`,
73
- );
74
- }
75
-
76
- if (mockHttpClientImport) {
77
- return insertHttp;
78
- } else {
79
- return [insertHttp, insertImport];
80
- }
81
- },
82
- });
83
- }
84
-
85
- // Fix 3:
86
- // - replace "$httpBackend.when('GET'" with "$httpBackend.expectGET("
87
- // - replace "$httpBackend.whenGET" with "$httpBackend.expectGET"
88
- // - replace "$httpBackend" with "http"
89
- return context.report({
90
- node,
91
- message: 'Migrate to MockHttpClient (step 3): replace $httpBackend with http',
92
- fix: (fixer) => {
93
- const newItBlockText = itBlockText
94
- .replace(/(this\.)?\$httpBackend\.when(GET|POST|PUT|PATCH|DELETE)/g, '$httpBackend.expect$2')
95
- .replace(
96
- /(this\.)?\$httpBackend\.when\(['"](GET|POST|PUT|PATCH|DELETE)['"], /g,
97
- '$httpBackend.expect$2(',
98
- )
99
- .replace(/(this\.)?\$httpBackend/g, 'http')
100
- .replace(/http.flush/g, 'await http.flush')
101
- .replace(/await await /g, 'await ');
102
-
103
- return fixer.replaceText(node, newItBlockText);
104
- },
105
- });
106
- }
107
- }
108
- }
109
- },
110
- };
111
- },
112
- meta: {
113
- fixable: 'code',
114
- type: 'problem',
115
- docs: {
116
- description: 'Do not import API',
117
- },
118
- },
119
- };
120
-
121
- export default ruleModule;
@@ -1,45 +0,0 @@
1
- import rule from './ng-no-component-class';
2
- import ruleTester from '../utils/ruleTester';
3
-
4
- ruleTester.run('ng-no-component-class', rule, {
5
- valid: [
6
- {
7
- code: `
8
- const angular = require('angular');
9
- angular.module('foo', [])
10
- .component('componentName', componentObject);
11
-
12
- const componentObject = {
13
- controller: function() {},
14
- template: 'a template'
15
- }
16
- `,
17
- },
18
- ],
19
-
20
- invalid: [
21
- {
22
- errors: [{ message: 'Use .component("foo", {}) instead of .component("foo", new FooComponentClass())' }],
23
- code: `
24
- import angular from 'angular';
25
- angular.module('foo', [])
26
- .component('componentName', new ComponentClass());
27
-
28
- class ComponentClass {
29
- controller = function() {};
30
- template = 'a template';
31
- }
32
- `,
33
- output: `
34
- import angular from 'angular';
35
- angular.module('foo', [])
36
- .component('componentName', componentClass);
37
-
38
- const componentClass = {
39
- controller: function() {},
40
- template: 'a template'
41
- };
42
- `,
43
- },
44
- ],
45
- });