@onecx/angular-linter-rules 8.0.0-rc.1 → 8.0.0-rc.12
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 +0 -1
- package/package.json +6 -2
- package/src/index.d.ts +18 -0
- package/src/index.js +12 -0
- package/src/index.js.map +1 -0
- package/src/lib/configs.d.ts +2 -0
- package/src/lib/configs.js +16 -0
- package/src/lib/configs.js.map +1 -0
- package/src/lib/rules/index.d.ts +11 -0
- package/src/lib/rules/index.js +12 -0
- package/src/lib/rules/index.js.map +1 -0
- package/src/lib/rules/no-subscribe-assignment.rule.d.ts +4 -0
- package/src/lib/rules/no-subscribe-assignment.rule.js +132 -0
- package/src/lib/rules/no-subscribe-assignment.rule.js.map +1 -0
- package/src/lib/rules/no-translate-instant.rule.d.ts +4 -0
- package/src/lib/rules/no-translate-instant.rule.js +82 -0
- package/src/lib/rules/no-translate-instant.rule.js.map +1 -0
- package/src/lib/rules/prefer-translate-params.rule.d.ts +4 -0
- package/src/lib/rules/prefer-translate-params.rule.js +128 -0
- package/src/lib/rules/prefer-translate-params.rule.js.map +1 -0
- package/src/lib/types.d.ts +5 -0
- package/src/lib/types.js +3 -0
- package/src/lib/types.js.map +1 -0
- package/src/lib/utils/type-utils.d.ts +2 -0
- package/src/lib/utils/type-utils.js +17 -0
- package/src/lib/utils/type-utils.js.map +1 -0
- package/src/version.d.ts +2 -0
- package/src/version.js +6 -0
- package/src/version.js.map +1 -0
- package/eslint.config.cjs +0 -19
- package/jest.config.ts +0 -10
- package/project.json +0 -28
- package/src/index.ts +0 -11
- package/src/lib/configs.ts +0 -16
- package/src/lib/rules/index.ts +0 -9
- package/src/lib/rules/no-subscribe-assignment.rule.spec.ts +0 -118
- package/src/lib/rules/no-subscribe-assignment.rule.ts +0 -149
- package/src/lib/rules/no-translate-instant.rule.spec.ts +0 -122
- package/src/lib/rules/no-translate-instant.rule.ts +0 -98
- package/src/lib/rules/prefer-translate-params.rule.spec.ts +0 -97
- package/src/lib/rules/prefer-translate-params.rule.ts +0 -133
- package/src/lib/types.ts +0 -6
- package/src/lib/utils/type-utils.ts +0 -15
- package/src/types/rule-tester.d.ts +0 -3
- package/src/version.ts +0 -2
- package/tsconfig.json +0 -23
- package/tsconfig.lib.json +0 -10
- package/tsconfig.rule-tester.json +0 -5
- package/tsconfig.spec.json +0 -10
package/src/index.ts
DELETED
package/src/lib/configs.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { TSESLint } from '@typescript-eslint/utils'
|
|
2
|
-
import { rules } from './rules'
|
|
3
|
-
|
|
4
|
-
const pluginName = '@onecx/angular-linter-rules'
|
|
5
|
-
|
|
6
|
-
const recommendedRules = Object.keys(rules).reduce<Record<string, TSESLint.Linter.RuleEntry>>((acc, ruleName) => {
|
|
7
|
-
acc[`${pluginName}/${ruleName}`] = 'warn'
|
|
8
|
-
return acc
|
|
9
|
-
}, {})
|
|
10
|
-
|
|
11
|
-
export const configs: Record<string, TSESLint.Linter.Config> = {
|
|
12
|
-
recommended: {
|
|
13
|
-
plugins: [pluginName],
|
|
14
|
-
rules: recommendedRules,
|
|
15
|
-
},
|
|
16
|
-
}
|
package/src/lib/rules/index.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { noSubscribeAssignment } from './no-subscribe-assignment.rule'
|
|
2
|
-
import { noTranslateInstant } from './no-translate-instant.rule'
|
|
3
|
-
import { preferTranslateParams } from './prefer-translate-params.rule'
|
|
4
|
-
|
|
5
|
-
export const rules = {
|
|
6
|
-
'no-subscribe-assignment': noSubscribeAssignment,
|
|
7
|
-
'no-translate-instant': noTranslateInstant,
|
|
8
|
-
'prefer-translate-params': preferTranslateParams,
|
|
9
|
-
} as const
|
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
import { RuleTester } from '@typescript-eslint/rule-tester'
|
|
2
|
-
import { noSubscribeAssignment } from './no-subscribe-assignment.rule'
|
|
3
|
-
|
|
4
|
-
RuleTester.afterAll = afterAll
|
|
5
|
-
|
|
6
|
-
const ruleTester = new RuleTester({
|
|
7
|
-
languageOptions: {
|
|
8
|
-
parser: require('@typescript-eslint/parser'),
|
|
9
|
-
parserOptions: {
|
|
10
|
-
ecmaVersion: 2022,
|
|
11
|
-
sourceType: 'module',
|
|
12
|
-
},
|
|
13
|
-
},
|
|
14
|
-
} as unknown as any)
|
|
15
|
-
|
|
16
|
-
const messageId = 'assignmentOutside'
|
|
17
|
-
|
|
18
|
-
ruleTester.run('no-subscribe-assignment', noSubscribeAssignment, {
|
|
19
|
-
valid: [
|
|
20
|
-
{
|
|
21
|
-
code: `
|
|
22
|
-
import { of } from 'rxjs'
|
|
23
|
-
|
|
24
|
-
function test() {
|
|
25
|
-
of(1).subscribe((value) => {
|
|
26
|
-
const local = value
|
|
27
|
-
console.log(local)
|
|
28
|
-
})
|
|
29
|
-
}
|
|
30
|
-
`,
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
code: `
|
|
34
|
-
import { of } from 'rxjs'
|
|
35
|
-
|
|
36
|
-
function test() {
|
|
37
|
-
let outer = 0
|
|
38
|
-
of(1).subscribe((value) => {
|
|
39
|
-
const outer = value
|
|
40
|
-
console.log(outer)
|
|
41
|
-
})
|
|
42
|
-
console.log(outer)
|
|
43
|
-
}
|
|
44
|
-
`,
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
code: `
|
|
48
|
-
import { of } from 'rxjs'
|
|
49
|
-
|
|
50
|
-
function test() {
|
|
51
|
-
of({ a: 1 }).subscribe((value) => {
|
|
52
|
-
const { a } = value
|
|
53
|
-
console.log(a)
|
|
54
|
-
})
|
|
55
|
-
}
|
|
56
|
-
`,
|
|
57
|
-
},
|
|
58
|
-
],
|
|
59
|
-
invalid: [
|
|
60
|
-
{
|
|
61
|
-
code: `
|
|
62
|
-
import { of } from 'rxjs'
|
|
63
|
-
|
|
64
|
-
function test() {
|
|
65
|
-
let outer: number | undefined
|
|
66
|
-
of(1).subscribe((value) => {
|
|
67
|
-
outer = value
|
|
68
|
-
})
|
|
69
|
-
console.log(outer)
|
|
70
|
-
}
|
|
71
|
-
`,
|
|
72
|
-
errors: [{ messageId }],
|
|
73
|
-
},
|
|
74
|
-
{
|
|
75
|
-
code: `
|
|
76
|
-
import { of } from 'rxjs'
|
|
77
|
-
|
|
78
|
-
class A {
|
|
79
|
-
value?: number
|
|
80
|
-
test() {
|
|
81
|
-
of(1).subscribe((value) => {
|
|
82
|
-
this.value = value
|
|
83
|
-
})
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
`,
|
|
87
|
-
errors: [{ messageId }],
|
|
88
|
-
},
|
|
89
|
-
{
|
|
90
|
-
code: `
|
|
91
|
-
import { of } from 'rxjs'
|
|
92
|
-
|
|
93
|
-
function test() {
|
|
94
|
-
let outer = 0
|
|
95
|
-
of(1).subscribe(function (value) {
|
|
96
|
-
outer++
|
|
97
|
-
})
|
|
98
|
-
console.log(outer)
|
|
99
|
-
}
|
|
100
|
-
`,
|
|
101
|
-
errors: [{ messageId }],
|
|
102
|
-
},
|
|
103
|
-
{
|
|
104
|
-
code: `
|
|
105
|
-
import { of } from 'rxjs'
|
|
106
|
-
|
|
107
|
-
function test() {
|
|
108
|
-
let outer = 0
|
|
109
|
-
of(1).subscribe((value) => {
|
|
110
|
-
;({ outer } = { outer: value })
|
|
111
|
-
})
|
|
112
|
-
console.log(outer)
|
|
113
|
-
}
|
|
114
|
-
`,
|
|
115
|
-
errors: [{ messageId }],
|
|
116
|
-
},
|
|
117
|
-
],
|
|
118
|
-
})
|
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
import { AST_NODE_TYPES, ESLintUtils, TSESTree } from '@typescript-eslint/utils'
|
|
2
|
-
|
|
3
|
-
const createRule = ESLintUtils.RuleCreator((name) => `https://github.com/onecx/onecx-portal-ui-libs/tree/main/libs/angular-linter-rules#${name}`)
|
|
4
|
-
|
|
5
|
-
type Options = []
|
|
6
|
-
type MessageIds = 'assignmentOutside'
|
|
7
|
-
|
|
8
|
-
function isSubscribeCall(node: TSESTree.CallExpression): boolean {
|
|
9
|
-
return node.callee.type === AST_NODE_TYPES.MemberExpression &&
|
|
10
|
-
node.callee.property.type === AST_NODE_TYPES.Identifier &&
|
|
11
|
-
node.callee.property.name === 'subscribe'
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function getFunctionArg(node: TSESTree.CallExpression): TSESTree.FunctionExpression | TSESTree.ArrowFunctionExpression | undefined {
|
|
15
|
-
for (const arg of node.arguments) {
|
|
16
|
-
if (arg.type === AST_NODE_TYPES.ArrowFunctionExpression || arg.type === AST_NODE_TYPES.FunctionExpression) {
|
|
17
|
-
return arg
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
return undefined
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
type PatternLike =
|
|
24
|
-
| TSESTree.Identifier
|
|
25
|
-
| TSESTree.ArrayPattern
|
|
26
|
-
| TSESTree.ObjectPattern
|
|
27
|
-
| TSESTree.RestElement
|
|
28
|
-
| TSESTree.AssignmentPattern
|
|
29
|
-
|
|
30
|
-
function getAssignedIdentifiers(pattern: PatternLike): TSESTree.Identifier[] {
|
|
31
|
-
switch (pattern.type) {
|
|
32
|
-
case AST_NODE_TYPES.Identifier:
|
|
33
|
-
return [pattern]
|
|
34
|
-
case AST_NODE_TYPES.ArrayPattern:
|
|
35
|
-
return pattern.elements.flatMap((el) => (el ? getAssignedIdentifiers(el as PatternLike) : []))
|
|
36
|
-
case AST_NODE_TYPES.ObjectPattern:
|
|
37
|
-
return pattern.properties.flatMap((p) => {
|
|
38
|
-
if (p.type === AST_NODE_TYPES.Property) return getAssignedIdentifiers(p.value as PatternLike)
|
|
39
|
-
if (p.type === AST_NODE_TYPES.RestElement) return getAssignedIdentifiers(p.argument as PatternLike)
|
|
40
|
-
return []
|
|
41
|
-
})
|
|
42
|
-
case AST_NODE_TYPES.RestElement:
|
|
43
|
-
return getAssignedIdentifiers(pattern.argument as PatternLike)
|
|
44
|
-
case AST_NODE_TYPES.AssignmentPattern:
|
|
45
|
-
return getAssignedIdentifiers(pattern.left as PatternLike)
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function getDeclaredVariablesInFunctionBody(
|
|
50
|
-
body: TSESTree.BlockStatement | TSESTree.Expression,
|
|
51
|
-
): Set<string> {
|
|
52
|
-
const names = new Set<string>()
|
|
53
|
-
if (body.type !== AST_NODE_TYPES.BlockStatement) return names
|
|
54
|
-
|
|
55
|
-
for (const stmt of body.body) {
|
|
56
|
-
if (stmt.type !== AST_NODE_TYPES.VariableDeclaration) continue
|
|
57
|
-
for (const decl of stmt.declarations) {
|
|
58
|
-
for (const id of getAssignedIdentifiers(decl.id as any)) {
|
|
59
|
-
names.add(id.name)
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return names
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export const noSubscribeAssignment = createRule<Options, MessageIds>({
|
|
68
|
-
name: 'no-subscribe-assignment',
|
|
69
|
-
meta: {
|
|
70
|
-
type: 'problem',
|
|
71
|
-
docs: {
|
|
72
|
-
description:
|
|
73
|
-
'Warn when assigning inside an Observable subscribe callback to variables declared outside the callback (including class members).',
|
|
74
|
-
},
|
|
75
|
-
schema: [],
|
|
76
|
-
messages: {
|
|
77
|
-
assignmentOutside: 'Avoid assigning to outer-scope variables inside `subscribe`. Prefer `map/tap` + `async` pipe or return the value.',
|
|
78
|
-
},
|
|
79
|
-
},
|
|
80
|
-
defaultOptions: [],
|
|
81
|
-
create(context) {
|
|
82
|
-
return {
|
|
83
|
-
CallExpression(node) {
|
|
84
|
-
if (!isSubscribeCall(node)) return
|
|
85
|
-
|
|
86
|
-
const callback = getFunctionArg(node)
|
|
87
|
-
if (!callback) return
|
|
88
|
-
|
|
89
|
-
const callbackBody = callback.body
|
|
90
|
-
const localDeclarations = getDeclaredVariablesInFunctionBody(callbackBody)
|
|
91
|
-
|
|
92
|
-
const reportIfOuter = (identifier: TSESTree.Identifier) => {
|
|
93
|
-
if (localDeclarations.has(identifier.name)) return
|
|
94
|
-
context.report({
|
|
95
|
-
node: identifier,
|
|
96
|
-
messageId: 'assignmentOutside',
|
|
97
|
-
})
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const sourceCode = context.sourceCode
|
|
101
|
-
const visitor = (n: TSESTree.Node) => {
|
|
102
|
-
if (n.type === AST_NODE_TYPES.AssignmentExpression) {
|
|
103
|
-
if (n.left.type === AST_NODE_TYPES.MemberExpression) {
|
|
104
|
-
if (n.left.object.type === AST_NODE_TYPES.ThisExpression) {
|
|
105
|
-
context.report({ node: n.left, messageId: 'assignmentOutside' })
|
|
106
|
-
}
|
|
107
|
-
return
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
if (n.left.type === AST_NODE_TYPES.Identifier) {
|
|
111
|
-
reportIfOuter(n.left)
|
|
112
|
-
return
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
if (n.left.type === AST_NODE_TYPES.ObjectPattern || n.left.type === AST_NODE_TYPES.ArrayPattern) {
|
|
116
|
-
for (const id of getAssignedIdentifiers(n.left)) {
|
|
117
|
-
reportIfOuter(id)
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
return
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
if (n.type === AST_NODE_TYPES.UpdateExpression) {
|
|
124
|
-
if (n.argument.type === AST_NODE_TYPES.Identifier) {
|
|
125
|
-
reportIfOuter(n.argument)
|
|
126
|
-
}
|
|
127
|
-
if (n.argument.type === AST_NODE_TYPES.MemberExpression && n.argument.object.type === AST_NODE_TYPES.ThisExpression) {
|
|
128
|
-
context.report({ node: n.argument, messageId: 'assignmentOutside' })
|
|
129
|
-
}
|
|
130
|
-
return
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
for (const child of sourceCode.visitorKeys[n.type] ?? []) {
|
|
134
|
-
const val = (n as any)[child]
|
|
135
|
-
if (Array.isArray(val)) {
|
|
136
|
-
for (const item of val) {
|
|
137
|
-
if (item && typeof item.type === 'string') visitor(item)
|
|
138
|
-
}
|
|
139
|
-
} else if (val && typeof val.type === 'string') {
|
|
140
|
-
visitor(val)
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
visitor(callbackBody)
|
|
146
|
-
},
|
|
147
|
-
}
|
|
148
|
-
},
|
|
149
|
-
})
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
import { RuleTester } from '@typescript-eslint/rule-tester'
|
|
2
|
-
import { noTranslateInstant } from './no-translate-instant.rule'
|
|
3
|
-
|
|
4
|
-
RuleTester.afterAll = afterAll
|
|
5
|
-
|
|
6
|
-
const ruleTester = new RuleTester({
|
|
7
|
-
languageOptions: {
|
|
8
|
-
parser: require('@typescript-eslint/parser'),
|
|
9
|
-
parserOptions: {
|
|
10
|
-
ecmaVersion: 2022,
|
|
11
|
-
sourceType: 'module',
|
|
12
|
-
},
|
|
13
|
-
},
|
|
14
|
-
} as unknown as any)
|
|
15
|
-
|
|
16
|
-
const messageId = 'noTranslateInstant'
|
|
17
|
-
|
|
18
|
-
ruleTester.run('no-translate-instant', noTranslateInstant, {
|
|
19
|
-
valid: [
|
|
20
|
-
{
|
|
21
|
-
filename: `${process.cwd()}/libs/angular-linter-rules/src/some.spec.ts`,
|
|
22
|
-
code: `
|
|
23
|
-
class A {
|
|
24
|
-
constructor(private translate: any) {}
|
|
25
|
-
test() {
|
|
26
|
-
return this.translate.instant('KEY')
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
`,
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
filename: `${process.cwd()}/libs/angular-linter-rules/src/testing/test-helper.ts`,
|
|
33
|
-
code: `
|
|
34
|
-
const translateService: any = { instant: (k: string) => k }
|
|
35
|
-
translateService.instant('KEY')
|
|
36
|
-
`,
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
filename: `${process.cwd()}/libs/angular-linter-rules/src/mocks/translate.mock.ts`,
|
|
40
|
-
code: `
|
|
41
|
-
const translate: any = { instant: (k: string) => k }
|
|
42
|
-
translate.instant('KEY')
|
|
43
|
-
`,
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
filename: `${process.cwd()}/libs/angular-linter-rules/src/app/a.ts`,
|
|
47
|
-
code: `
|
|
48
|
-
const other = { instant: (k: string) => k }
|
|
49
|
-
other.instant('KEY')
|
|
50
|
-
`,
|
|
51
|
-
},
|
|
52
|
-
],
|
|
53
|
-
invalid: [
|
|
54
|
-
{
|
|
55
|
-
filename: `${process.cwd()}/libs/angular-linter-rules/src/app/a.ts`,
|
|
56
|
-
code: `
|
|
57
|
-
class A {
|
|
58
|
-
constructor(private translate: any) {}
|
|
59
|
-
test() {
|
|
60
|
-
return this.translate.instant('KEY')
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
`,
|
|
64
|
-
errors: [{ messageId }],
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
filename: `${process.cwd()}/libs/angular-linter-rules/src/app/b.ts`,
|
|
68
|
-
code: `
|
|
69
|
-
function f(translate: any) {
|
|
70
|
-
return translate.instant('KEY')
|
|
71
|
-
}
|
|
72
|
-
`,
|
|
73
|
-
errors: [{ messageId }],
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
filename: `${process.cwd()}/libs/angular-linter-rules/src/app/c.ts`,
|
|
77
|
-
code: `
|
|
78
|
-
function f(translateService: any) {
|
|
79
|
-
return translateService.instant('KEY')
|
|
80
|
-
}
|
|
81
|
-
`,
|
|
82
|
-
errors: [{ messageId }],
|
|
83
|
-
},
|
|
84
|
-
{
|
|
85
|
-
filename: `${process.cwd()}/libs/angular-linter-rules/src/app/d.ts`,
|
|
86
|
-
code: `
|
|
87
|
-
class A {
|
|
88
|
-
private translateService: any
|
|
89
|
-
test() {
|
|
90
|
-
return this.translateService.instant('KEY')
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
`,
|
|
94
|
-
errors: [{ messageId }],
|
|
95
|
-
},
|
|
96
|
-
{
|
|
97
|
-
filename: `${process.cwd()}/libs/angular-linter-rules/src/app/e.ts`,
|
|
98
|
-
code: `
|
|
99
|
-
import { TranslateService } from '@ngx-translate/core'
|
|
100
|
-
|
|
101
|
-
class A {
|
|
102
|
-
constructor(private translate: TranslateService) {}
|
|
103
|
-
test() {
|
|
104
|
-
return this.translate.instant('KEY')
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
`,
|
|
108
|
-
errors: [{ messageId }],
|
|
109
|
-
},
|
|
110
|
-
{
|
|
111
|
-
filename: `${process.cwd()}/libs/angular-linter-rules/src/app/f.ts`,
|
|
112
|
-
code: `
|
|
113
|
-
import { TranslateService } from '@ngx-translate/core'
|
|
114
|
-
|
|
115
|
-
function f(translateService: TranslateService) {
|
|
116
|
-
return translateService.instant('KEY')
|
|
117
|
-
}
|
|
118
|
-
`,
|
|
119
|
-
errors: [{ messageId }],
|
|
120
|
-
},
|
|
121
|
-
],
|
|
122
|
-
})
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import { AST_NODE_TYPES, ESLintUtils, TSESTree } from '@typescript-eslint/utils'
|
|
2
|
-
import { isTranslateServiceType } from '../utils/type-utils'
|
|
3
|
-
|
|
4
|
-
const createRule = ESLintUtils.RuleCreator(
|
|
5
|
-
(name) => `https://github.com/onecx/onecx-portal-ui-libs/tree/main/libs/angular-linter-rules#${name}`,
|
|
6
|
-
)
|
|
7
|
-
|
|
8
|
-
type Options = []
|
|
9
|
-
type MessageIds = 'noTranslateInstant'
|
|
10
|
-
|
|
11
|
-
const defaultAllowedFilePatterns = [/\.spec\.ts$/i, /\.test\.ts$/i, /\/testing\//i, /\/mocks\//i]
|
|
12
|
-
|
|
13
|
-
function isAllowedTestFile(filename: string): boolean {
|
|
14
|
-
if (!filename || filename === '<input>' || filename === '<text>') return false
|
|
15
|
-
return defaultAllowedFilePatterns.some((re) => re.test(filename))
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function isInstantMemberCall(node: TSESTree.CallExpression): boolean {
|
|
19
|
-
if (node.callee.type !== AST_NODE_TYPES.MemberExpression) return false
|
|
20
|
-
const property = node.callee.property
|
|
21
|
-
if (property.type !== AST_NODE_TYPES.Identifier || property.name !== 'instant') return false
|
|
22
|
-
|
|
23
|
-
const obj = node.callee.object
|
|
24
|
-
if (obj.type === AST_NODE_TYPES.Identifier) {
|
|
25
|
-
return obj.name === 'translate' || obj.name === 'translateService'
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (obj.type === AST_NODE_TYPES.MemberExpression) {
|
|
29
|
-
if (obj.object.type !== AST_NODE_TYPES.ThisExpression) return false
|
|
30
|
-
if (obj.property.type !== AST_NODE_TYPES.Identifier) return false
|
|
31
|
-
return obj.property.name === 'translate' || obj.property.name === 'translateService'
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return false
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
type ContextWithTypeInfo = {
|
|
38
|
-
sourceCode: {
|
|
39
|
-
parserServices?: {
|
|
40
|
-
program?: unknown
|
|
41
|
-
esTreeNodeToTSNodeMap?: unknown
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function isTranslateServiceReceiver(context: ContextWithTypeInfo, node: TSESTree.CallExpression): boolean {
|
|
47
|
-
if (node.callee.type !== AST_NODE_TYPES.MemberExpression) return false
|
|
48
|
-
if (!context.sourceCode.parserServices?.program) return false
|
|
49
|
-
|
|
50
|
-
const { esTreeNodeToTSNodeMap, program } = context.sourceCode.parserServices
|
|
51
|
-
if (!esTreeNodeToTSNodeMap) return false
|
|
52
|
-
const checker = (program as any).getTypeChecker()
|
|
53
|
-
|
|
54
|
-
const receiverTsNode = (esTreeNodeToTSNodeMap as any).get(node.callee.object as any)
|
|
55
|
-
const receiverType = checker.getTypeAtLocation(receiverTsNode)
|
|
56
|
-
if (isTranslateServiceType(checker, receiverType)) return true
|
|
57
|
-
|
|
58
|
-
const receiverTypeText = checker.typeToString(receiverType)
|
|
59
|
-
return receiverTypeText === 'TranslateService'
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export const noTranslateInstant = createRule<Options, MessageIds>({
|
|
63
|
-
name: 'no-translate-instant',
|
|
64
|
-
meta: {
|
|
65
|
-
type: 'problem',
|
|
66
|
-
docs: {
|
|
67
|
-
description: 'Disallow ngx-translate TranslateService.instant outside of tests.',
|
|
68
|
-
},
|
|
69
|
-
schema: [],
|
|
70
|
-
messages: {
|
|
71
|
-
noTranslateInstant:
|
|
72
|
-
'Avoid `TranslateService.instant(...)` in production code. Use stream-based translation (`get`/`stream`) or the translate pipe instead.',
|
|
73
|
-
},
|
|
74
|
-
},
|
|
75
|
-
defaultOptions: [],
|
|
76
|
-
create(context) {
|
|
77
|
-
const filename = context.filename
|
|
78
|
-
const allowed = isAllowedTestFile(filename)
|
|
79
|
-
|
|
80
|
-
return {
|
|
81
|
-
CallExpression(node) {
|
|
82
|
-
if (allowed) return
|
|
83
|
-
|
|
84
|
-
if (isTranslateServiceReceiver(context as unknown as ContextWithTypeInfo, node)) {
|
|
85
|
-
context.report({ node: node.callee, messageId: 'noTranslateInstant' })
|
|
86
|
-
return
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (!isInstantMemberCall(node)) return
|
|
90
|
-
|
|
91
|
-
context.report({
|
|
92
|
-
node: node.callee,
|
|
93
|
-
messageId: 'noTranslateInstant',
|
|
94
|
-
})
|
|
95
|
-
},
|
|
96
|
-
}
|
|
97
|
-
},
|
|
98
|
-
})
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import { RuleTester } from '@typescript-eslint/rule-tester'
|
|
2
|
-
import { preferTranslateParams } from './prefer-translate-params.rule'
|
|
3
|
-
|
|
4
|
-
RuleTester.afterAll = afterAll
|
|
5
|
-
|
|
6
|
-
const ruleTester = new RuleTester({
|
|
7
|
-
languageOptions: {
|
|
8
|
-
parser: require('@typescript-eslint/parser'),
|
|
9
|
-
parserOptions: {
|
|
10
|
-
ecmaVersion: 2022,
|
|
11
|
-
sourceType: 'module',
|
|
12
|
-
},
|
|
13
|
-
},
|
|
14
|
-
} as unknown as any)
|
|
15
|
-
|
|
16
|
-
const messageId = 'preferTranslateParams'
|
|
17
|
-
|
|
18
|
-
ruleTester.run('prefer-translate-params', preferTranslateParams, {
|
|
19
|
-
valid: [
|
|
20
|
-
{
|
|
21
|
-
filename: `${process.cwd()}/libs/angular-linter-rules/src/app/a.ts`,
|
|
22
|
-
code: `
|
|
23
|
-
function f(translate: any, name: string) {
|
|
24
|
-
return translate.get('HELLO_NAME', { name })
|
|
25
|
-
}
|
|
26
|
-
`,
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
filename: `${process.cwd()}/libs/angular-linter-rules/src/app/b.ts`,
|
|
30
|
-
code: `
|
|
31
|
-
function f(translate: any, name: string) {
|
|
32
|
-
return translate.get('HELLO_' + name)
|
|
33
|
-
}
|
|
34
|
-
`,
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
filename: `${process.cwd()}/libs/angular-linter-rules/src/app/b2.ts`,
|
|
38
|
-
code: `
|
|
39
|
-
function f(translate: any, name: string) {
|
|
40
|
-
return translate.stream('HELLO_' + name)
|
|
41
|
-
}
|
|
42
|
-
`,
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
filename: `${process.cwd()}/libs/angular-linter-rules/src/some.spec.ts`,
|
|
46
|
-
code: `
|
|
47
|
-
function f(translate: any, name: string) {
|
|
48
|
-
return translate.get('HELLO')
|
|
49
|
-
}
|
|
50
|
-
`,
|
|
51
|
-
},
|
|
52
|
-
],
|
|
53
|
-
invalid: [
|
|
54
|
-
{
|
|
55
|
-
filename: `${process.cwd()}/libs/angular-linter-rules/src/app/d.ts`,
|
|
56
|
-
code: `
|
|
57
|
-
import { map } from 'rxjs'
|
|
58
|
-
|
|
59
|
-
function f(translateService: any, name: string) {
|
|
60
|
-
return translate.get('HELLO').pipe(map((t: string) => 'Hi ' + t))
|
|
61
|
-
}
|
|
62
|
-
`,
|
|
63
|
-
errors: [{ messageId }],
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
filename: `${process.cwd()}/libs/angular-linter-rules/src/app/rx.ts`,
|
|
67
|
-
code: `
|
|
68
|
-
import { map } from 'rxjs'
|
|
69
|
-
|
|
70
|
-
function f(translate: any, name: string) {
|
|
71
|
-
return translate.get('HELLO').pipe(map((t: string) => t + name))
|
|
72
|
-
}
|
|
73
|
-
`,
|
|
74
|
-
errors: [{ messageId }],
|
|
75
|
-
},
|
|
76
|
-
{
|
|
77
|
-
filename: `${process.cwd()}/libs/angular-linter-rules/src/app/rx2.ts`,
|
|
78
|
-
code: `
|
|
79
|
-
import { map } from 'rxjs'
|
|
80
|
-
|
|
81
|
-
function f(translate: any, name: string) {
|
|
82
|
-
return translate.stream('HELLO').pipe(map((t: string) => t + name))
|
|
83
|
-
}
|
|
84
|
-
`,
|
|
85
|
-
errors: [{ messageId }],
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
filename: `${process.cwd()}/libs/angular-linter-rules/src/app/e.ts`,
|
|
89
|
-
code: `
|
|
90
|
-
function f(translate: any, name: string) {
|
|
91
|
-
return ` + "`" + '${translate.instant(\'HELLO\')} ${name}' + "`" + `
|
|
92
|
-
}
|
|
93
|
-
`,
|
|
94
|
-
errors: [{ messageId }],
|
|
95
|
-
},
|
|
96
|
-
],
|
|
97
|
-
})
|