@gitlab/eslint-plugin 9.4.0 → 10.0.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/CHANGELOG.md +15 -0
- package/docs/rules/require-valid-i18n-helpers.md +41 -0
- package/docs/rules/vue-require-valid-i18n-helpers.md +42 -0
- package/docs/rules.md +2 -2
- package/lib/configs/i18n.js +2 -2
- package/lib/index.js +2 -2
- package/lib/rules/require-i18n-strings.js +5 -11
- package/lib/rules/{require-string-literal-i18n-helpers.js → require-valid-i18n-helpers.js} +2 -3
- package/lib/rules/{vue-require-string-literal-i18n-helpers.js → vue-require-valid-i18n-helpers.js} +2 -3
- package/lib/utils/i18n-utils.js +48 -13
- package/lib/utils/rule-utils.js +25 -0
- package/package.json +1 -1
- package/docs/rules/require-string-literal-i18n-helpers.md +0 -26
- package/docs/rules/vue-require-string-literal-i18n-helpers.md +0 -34
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
# [10.0.0](https://gitlab.com/gitlab-org/frontend/eslint-plugin/compare/v9.4.0...v10.0.0) (2021-11-03)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* lint against missing translation namespaces ([db00d87](https://gitlab.com/gitlab-org/frontend/eslint-plugin/commit/db00d87e3877fa0fe538775280a54fd820c89572))
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### BREAKING CHANGES
|
|
10
|
+
|
|
11
|
+
* require-string-literal-i18n-helpers and
|
|
12
|
+
vue-require-string-literal-i18n-helpers rules have been renamed to
|
|
13
|
+
require-valid-i18n-helpers and vue-require-valid-i18n-helpers
|
|
14
|
+
respectively.
|
|
15
|
+
|
|
1
16
|
# [9.4.0](https://gitlab.com/gitlab-org/frontend/eslint-plugin/compare/v9.3.0...v9.4.0) (2021-09-21)
|
|
2
17
|
|
|
3
18
|
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# @gitlab/require-valid-i18n-helpers
|
|
2
|
+
|
|
3
|
+
This rule enforces valid usages of the translation helpers (`__`, `s__` and `n__`). It requires the
|
|
4
|
+
following:
|
|
5
|
+
|
|
6
|
+
- All helpers should be called with string-literal arguments to ensure the strings are properly
|
|
7
|
+
collected by the `gettext:regenerate` script.
|
|
8
|
+
- `s__` requires a translation namespace to be provided.
|
|
9
|
+
|
|
10
|
+
## Rule Details
|
|
11
|
+
|
|
12
|
+
### Examples of **incorrect** code for this rule
|
|
13
|
+
|
|
14
|
+
```js
|
|
15
|
+
// Invalid argument types
|
|
16
|
+
const label = __(LABEL);
|
|
17
|
+
const description = __(getDescription());
|
|
18
|
+
|
|
19
|
+
// Missing translation namespace
|
|
20
|
+
const namespacedTranslation = s__('NamespacedTranslation');
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Examples of **correct** code for this rule
|
|
24
|
+
|
|
25
|
+
```js
|
|
26
|
+
// Valid argument type
|
|
27
|
+
const label = __('Some label');
|
|
28
|
+
|
|
29
|
+
// Valid translation namespace
|
|
30
|
+
const namespacedTranslation = s__('Namespace|Translation');
|
|
31
|
+
// or
|
|
32
|
+
const otherNamespacedTranslation = s__('Namespace', 'Other translation');
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Options
|
|
36
|
+
|
|
37
|
+
Nothing
|
|
38
|
+
|
|
39
|
+
## Related rules
|
|
40
|
+
|
|
41
|
+
- TBA
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# @gitlab/vue-require-valid-i18n-helpers
|
|
2
|
+
|
|
3
|
+
This rule is like [require-valid-i18n-helpers](./require-valid-i18n-helpers.md) for Vue templates.
|
|
4
|
+
|
|
5
|
+
## Rule Details
|
|
6
|
+
|
|
7
|
+
### Examples of **incorrect** code for this rule
|
|
8
|
+
|
|
9
|
+
```html
|
|
10
|
+
<template>
|
|
11
|
+
<div>
|
|
12
|
+
<!-- Invalid argument types -->
|
|
13
|
+
{{ __(LABEL) }}
|
|
14
|
+
{{ __(getDescription()) }}
|
|
15
|
+
|
|
16
|
+
<!-- Missing translation namespace -->
|
|
17
|
+
{{ s__('NamespacedTranslation') }}
|
|
18
|
+
</div>
|
|
19
|
+
</template>
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Examples of **correct** code for this rule
|
|
23
|
+
|
|
24
|
+
```html
|
|
25
|
+
<template>
|
|
26
|
+
<div>
|
|
27
|
+
<!-- Valid argument type -->
|
|
28
|
+
{{ __('Some label') }}
|
|
29
|
+
|
|
30
|
+
<!-- Valid translation namespace -->
|
|
31
|
+
{{ s__('Namespace|Translation') }}
|
|
32
|
+
</div>
|
|
33
|
+
</template>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Options
|
|
37
|
+
|
|
38
|
+
Nothing
|
|
39
|
+
|
|
40
|
+
## Related rules
|
|
41
|
+
|
|
42
|
+
- TBA
|
package/docs/rules.md
CHANGED
|
@@ -7,12 +7,12 @@ Available rules:
|
|
|
7
7
|
- [no-global-event-off](./rules/no-global-event-off.md): Throw an error when globally deregistering all event listeners.
|
|
8
8
|
- [no-runtime-template-compiler](./rules/no-runtime-template-compiler.md): Disallow components which rely on a runtime template compiler.
|
|
9
9
|
- [require-i18n-strings](./rules/require-i18n-strings.md): Detect a string which has been hard coded and requires externalization.
|
|
10
|
-
- [require-
|
|
10
|
+
- [require-valid-i18n-helpers](./rules/require-valid-i18n-helpers.md): Enforces valid usage of translation helpers in JavaScript.
|
|
11
11
|
- [vue-emit-events-with-two-args](./rules/vue-emit-events-with-two-args.md): Throw an error when emit is called with more than two arguments.
|
|
12
12
|
- [vue-no-data-toggle](./rules/vue-no-data-toggle.md): Restrict the use of `data-toggle` bootstrap behaviors within Vue templates
|
|
13
13
|
- [vue-no-new-non-primitive-in-template](./rules/vue-no-new-non-primitive-in-template.md): Prevents non-primitive values from being declared in templates
|
|
14
14
|
- [vue-require-i18n-attribute-strings](./rules/vue-require-i18n-attribute-strings.md): Detect non externalized strings in vue `<template>` attributes
|
|
15
15
|
- [vue-require-i18n-strings](./rules/vue-require-i18n-strings.md): enforce no bare strings in vue `<template>`
|
|
16
16
|
- [vue-require-required-key](./rules/vue-require-required-key.md): Require the required key to be set
|
|
17
|
-
- [vue-require-
|
|
17
|
+
- [vue-require-valid-i18n-helpers](./rules/vue-require-valid-i18n-helpers.md): Enforces valid usage of translation helpers in Vue templates.
|
|
18
18
|
- [vue-slot-name-casing](./rules/vue-slot-name-casing.md): enforce specific casing for slot naming style in template
|
package/lib/configs/i18n.js
CHANGED
|
@@ -4,7 +4,7 @@ module.exports = {
|
|
|
4
4
|
'@gitlab/require-i18n-strings': 'error',
|
|
5
5
|
'@gitlab/vue-require-i18n-attribute-strings': 'error',
|
|
6
6
|
'@gitlab/vue-require-i18n-strings': 'error',
|
|
7
|
-
'@gitlab/require-
|
|
8
|
-
'@gitlab/vue-require-
|
|
7
|
+
'@gitlab/require-valid-i18n-helpers': 'error',
|
|
8
|
+
'@gitlab/vue-require-valid-i18n-helpers': 'error',
|
|
9
9
|
},
|
|
10
10
|
};
|
package/lib/index.js
CHANGED
|
@@ -6,14 +6,14 @@ module.exports = {
|
|
|
6
6
|
'no-global-event-off': require('./rules/no-global-event-off.js'),
|
|
7
7
|
'no-runtime-template-compiler': require('./rules/no-runtime-template-compiler.js'),
|
|
8
8
|
'require-i18n-strings': require('./rules/require-i18n-strings.js'),
|
|
9
|
-
'require-
|
|
9
|
+
'require-valid-i18n-helpers': require('./rules/require-valid-i18n-helpers.js'),
|
|
10
10
|
'vue-emit-events-with-two-args': require('./rules/vue-emit-events-with-two-args.js'),
|
|
11
11
|
'vue-no-data-toggle': require('./rules/vue-no-data-toggle.js'),
|
|
12
12
|
'vue-no-new-non-primitive-in-template': require('./rules/vue-no-new-non-primitive-in-template.js'),
|
|
13
13
|
'vue-require-i18n-attribute-strings': require('./rules/vue-require-i18n-attribute-strings.js'),
|
|
14
14
|
'vue-require-i18n-strings': require('./rules/vue-require-i18n-strings.js'),
|
|
15
15
|
'vue-require-required-key': require('./rules/vue-require-required-key.js'),
|
|
16
|
-
'vue-require-
|
|
16
|
+
'vue-require-valid-i18n-helpers': require('./rules/vue-require-valid-i18n-helpers.js'),
|
|
17
17
|
'vue-slot-name-casing': require('./rules/vue-slot-name-casing.js'),
|
|
18
18
|
},
|
|
19
19
|
configs: {
|
|
@@ -4,6 +4,11 @@
|
|
|
4
4
|
*/
|
|
5
5
|
'use strict';
|
|
6
6
|
|
|
7
|
+
// ------------------------------------------------------------------------------
|
|
8
|
+
// Requirements
|
|
9
|
+
// ------------------------------------------------------------------------------
|
|
10
|
+
const { isTemplateLiteral, getTemplateLiteralString } = require('../utils/rule-utils');
|
|
11
|
+
|
|
7
12
|
//------------------------------------------------------------------------------
|
|
8
13
|
// Rule Definition
|
|
9
14
|
//------------------------------------------------------------------------------
|
|
@@ -269,21 +274,10 @@ module.exports = {
|
|
|
269
274
|
return node.type === 'Literal';
|
|
270
275
|
}
|
|
271
276
|
|
|
272
|
-
function isTemplateLiteral(node) {
|
|
273
|
-
return node.type === 'TemplateLiteral';
|
|
274
|
-
}
|
|
275
|
-
|
|
276
277
|
function stripVariables(value) {
|
|
277
278
|
return value ? value.replace(/(\%{[\w-]+})/g, '') : '';
|
|
278
279
|
}
|
|
279
280
|
|
|
280
|
-
function getTemplateLiteralString(node) {
|
|
281
|
-
return node.quasis
|
|
282
|
-
.filter((quasi) => quasi.type === 'TemplateElement')
|
|
283
|
-
.map((quasi) => quasi.value.raw)
|
|
284
|
-
.join('');
|
|
285
|
-
}
|
|
286
|
-
|
|
287
281
|
function getCallExpressionString(node, callee) {
|
|
288
282
|
if (callee.name === 'sprintf') {
|
|
289
283
|
return stripVariables(node.value);
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
// Requirements
|
|
3
3
|
// ------------------------------------------------------------------------------
|
|
4
4
|
const { DOCS_BASE_URL } = require('../constants');
|
|
5
|
-
const { getFunctionName, isString } = require('../utils/rule-utils');
|
|
6
5
|
const { validateI18nHelperCallFactory } = require('../utils/i18n-utils');
|
|
7
6
|
|
|
8
7
|
// ------------------------------------------------------------------------------
|
|
@@ -13,9 +12,9 @@ module.exports = {
|
|
|
13
12
|
meta: {
|
|
14
13
|
type: 'error',
|
|
15
14
|
docs: {
|
|
16
|
-
description: '
|
|
15
|
+
description: 'Enforces valid usage of translation helpers in JavaScript.',
|
|
17
16
|
category: 'i18n',
|
|
18
|
-
url: DOCS_BASE_URL + '/require-
|
|
17
|
+
url: DOCS_BASE_URL + '/require-valid-i18n-helpers.md',
|
|
19
18
|
},
|
|
20
19
|
},
|
|
21
20
|
create(context) {
|
package/lib/rules/{vue-require-string-literal-i18n-helpers.js → vue-require-valid-i18n-helpers.js}
RENAMED
|
@@ -13,10 +13,9 @@ module.exports = {
|
|
|
13
13
|
meta: {
|
|
14
14
|
type: 'error',
|
|
15
15
|
docs: {
|
|
16
|
-
description:
|
|
17
|
-
'Prevents translation helpers from being called with non-string-literal arguments in Vue templates.',
|
|
16
|
+
description: 'Enforces valid usage of translation helpers in Vue templates.',
|
|
18
17
|
category: 'i18n',
|
|
19
|
-
url: DOCS_BASE_URL + '/vue-require-
|
|
18
|
+
url: DOCS_BASE_URL + '/vue-require-valid-i18n-helpers.md',
|
|
20
19
|
},
|
|
21
20
|
},
|
|
22
21
|
create(context) {
|
package/lib/utils/i18n-utils.js
CHANGED
|
@@ -1,21 +1,22 @@
|
|
|
1
|
-
const {
|
|
1
|
+
const {
|
|
2
|
+
getFunctionName,
|
|
3
|
+
isString,
|
|
4
|
+
isTemplateLiteral,
|
|
5
|
+
getTemplateLiteralString,
|
|
6
|
+
} = require('../utils/rule-utils');
|
|
2
7
|
|
|
3
|
-
const
|
|
4
|
-
const
|
|
8
|
+
const GETTEXT = '__';
|
|
9
|
+
const P_GETTEXT = 's__';
|
|
10
|
+
const N_GETTEXT = 'n__';
|
|
5
11
|
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
return functionName && I18N_HELPERS.has(functionName);
|
|
9
|
-
};
|
|
12
|
+
const ERROR_MESSAGE_STRING_LITERAL = 'Translation helpers must be called with a string literal.';
|
|
13
|
+
const ERROR_MESSAGE_MISSING_NAMESPACE = 's__ requires a translation namespace to be provided.';
|
|
10
14
|
|
|
11
|
-
const
|
|
12
|
-
if (!isI18nCall(node)) {
|
|
13
|
-
return;
|
|
14
|
-
}
|
|
15
|
+
const mustBeCalledWithStringLiteralArguments = (context, node) => {
|
|
15
16
|
const report = (node) => {
|
|
16
17
|
context.report({
|
|
17
18
|
node,
|
|
18
|
-
message:
|
|
19
|
+
message: ERROR_MESSAGE_STRING_LITERAL,
|
|
19
20
|
});
|
|
20
21
|
};
|
|
21
22
|
|
|
@@ -28,6 +29,40 @@ const validateI18nHelperCallFactory = (context) => (node) => {
|
|
|
28
29
|
}
|
|
29
30
|
};
|
|
30
31
|
|
|
32
|
+
const mustHaveNamespace = (context, node) => {
|
|
33
|
+
if (node.arguments.length === 2) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const [arg] = node.arguments;
|
|
38
|
+
const value = isTemplateLiteral(arg) ? getTemplateLiteralString(arg).trim() : arg.value;
|
|
39
|
+
if (!/^\w.+\|/.test(value)) {
|
|
40
|
+
context.report({
|
|
41
|
+
node,
|
|
42
|
+
message: ERROR_MESSAGE_MISSING_NAMESPACE,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const VALIDATORS = {
|
|
48
|
+
[GETTEXT]: [mustBeCalledWithStringLiteralArguments],
|
|
49
|
+
[P_GETTEXT]: [mustBeCalledWithStringLiteralArguments, mustHaveNamespace],
|
|
50
|
+
[N_GETTEXT]: [mustBeCalledWithStringLiteralArguments],
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const validateI18nHelperCallFactory = (context) => (node) => {
|
|
54
|
+
const functionName = getFunctionName(node);
|
|
55
|
+
if (!Object.keys(VALIDATORS).includes(functionName)) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
VALIDATORS[functionName].forEach((validator) => {
|
|
60
|
+
validator(context, node);
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
|
|
31
64
|
module.exports = {
|
|
32
65
|
validateI18nHelperCallFactory,
|
|
33
|
-
|
|
66
|
+
ERROR_MESSAGE_STRING_LITERAL,
|
|
67
|
+
ERROR_MESSAGE_MISSING_NAMESPACE,
|
|
68
|
+
};
|
package/lib/utils/rule-utils.js
CHANGED
|
@@ -41,6 +41,29 @@ function isString(node) {
|
|
|
41
41
|
return [LITERAL, TEMPLATE_LITERAL].includes(node.type);
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Checks whether a node is a template literal.
|
|
46
|
+
*
|
|
47
|
+
* @param {ASTNode} node The node to type check
|
|
48
|
+
* @returns {boolean} `true` if node is a template literal.
|
|
49
|
+
*/
|
|
50
|
+
function isTemplateLiteral(node) {
|
|
51
|
+
return node.type === TEMPLATE_LITERAL;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Extracts the string out of a template literal node.
|
|
56
|
+
*
|
|
57
|
+
* @param {ASTNode} node The template literal node
|
|
58
|
+
* @returns {string} The extracted string.
|
|
59
|
+
*/
|
|
60
|
+
function getTemplateLiteralString(node) {
|
|
61
|
+
return node.quasis
|
|
62
|
+
.filter((quasi) => quasi.type === 'TemplateElement')
|
|
63
|
+
.map((quasi) => quasi.value.raw)
|
|
64
|
+
.join('');
|
|
65
|
+
}
|
|
66
|
+
|
|
44
67
|
/**
|
|
45
68
|
* Check that a node is a `VLiteral type`
|
|
46
69
|
* @param {ASTNode} node The node to type check
|
|
@@ -172,6 +195,8 @@ module.exports = {
|
|
|
172
195
|
clearSplitLines,
|
|
173
196
|
closest,
|
|
174
197
|
isString,
|
|
198
|
+
isTemplateLiteral,
|
|
199
|
+
getTemplateLiteralString,
|
|
175
200
|
isVLiteral,
|
|
176
201
|
removeNewLines,
|
|
177
202
|
selectFixFunction,
|
package/package.json
CHANGED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
# @gitlab/require-string-literal-i18n-helpers
|
|
2
|
-
|
|
3
|
-
Prevents translation helpers from being called with non-string-literal arguments.
|
|
4
|
-
|
|
5
|
-
## Rule Details
|
|
6
|
-
|
|
7
|
-
### Examples of **incorrect** code for this rule
|
|
8
|
-
|
|
9
|
-
```js
|
|
10
|
-
const label = __(LABEL);
|
|
11
|
-
const description = __(getDescription());
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
### Examples of **correct** code for this rule
|
|
15
|
-
|
|
16
|
-
```js
|
|
17
|
-
const label = __('Some label');
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
## Options
|
|
21
|
-
|
|
22
|
-
Nothing
|
|
23
|
-
|
|
24
|
-
## Related rules
|
|
25
|
-
|
|
26
|
-
- TBA
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
# @gitlab/vue-require-string-literal-i18n-helpers
|
|
2
|
-
|
|
3
|
-
Prevents translation helpers from being called with non-string-literal arguments in Vue templates.
|
|
4
|
-
|
|
5
|
-
## Rule Details
|
|
6
|
-
|
|
7
|
-
### Examples of **incorrect** code for this rule
|
|
8
|
-
|
|
9
|
-
```html
|
|
10
|
-
<template>
|
|
11
|
-
<div>
|
|
12
|
-
{{ __(LABEL) }}
|
|
13
|
-
{{ __(getDescription()) }}
|
|
14
|
-
</div>
|
|
15
|
-
</template>
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
### Examples of **correct** code for this rule
|
|
19
|
-
|
|
20
|
-
```html
|
|
21
|
-
<template>
|
|
22
|
-
<div>
|
|
23
|
-
{{ __('Some label') }}
|
|
24
|
-
</div>
|
|
25
|
-
</template>
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
## Options
|
|
29
|
-
|
|
30
|
-
Nothing
|
|
31
|
-
|
|
32
|
-
## Related rules
|
|
33
|
-
|
|
34
|
-
- TBA
|