@gitlab/eslint-plugin 18.2.0 → 18.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/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
# [18.3.0](https://gitlab.com/gitlab-org/frontend/eslint-plugin/compare/v18.2.1...v18.3.0) (2023-03-23)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* **i18n:** lint against namespaces in gettext ([f3f3579](https://gitlab.com/gitlab-org/frontend/eslint-plugin/commit/f3f35799f85be89ac31b8785036d9f28b97c60e9))
|
|
7
|
+
* **i18n:** require matching namespaces in n_gettext ([63fcbb5](https://gitlab.com/gitlab-org/frontend/eslint-plugin/commit/63fcbb5483cda54e85244ecd716b1fea94e8ae11))
|
|
8
|
+
|
|
9
|
+
## [18.2.1](https://gitlab.com/gitlab-org/frontend/eslint-plugin/compare/v18.2.0...v18.2.1) (2023-03-22)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
### Bug Fixes
|
|
13
|
+
|
|
14
|
+
* disable no-restricted-syntax rules ([cf65279](https://gitlab.com/gitlab-org/frontend/eslint-plugin/commit/cf652794d8d8a87a775af5bf0805f5194a599e75)), closes [/github.com/airbnb/javascript/blob/eslint-config-airbnb-v15.0.0/packages/eslint-config-airbnb-base/rules/style.js#L257-L275](https://gitlab.com//github.com/airbnb/javascript/blob/eslint-config-airbnb-v15.0.0/packages/eslint-config-airbnb-base/rules/style.js/issues/L257-L275)
|
|
15
|
+
|
|
1
16
|
# [18.2.0](https://gitlab.com/gitlab-org/frontend/eslint-plugin/compare/v18.1.0...v18.2.0) (2023-03-03)
|
|
2
17
|
|
|
3
18
|
|
|
@@ -5,7 +5,9 @@ following:
|
|
|
5
5
|
|
|
6
6
|
- All helpers should be called with string-literal arguments to ensure the strings are properly
|
|
7
7
|
collected by the `gettext:regenerate` script.
|
|
8
|
+
- `__` should not be called with a namespace.
|
|
8
9
|
- `s__` requires a translation namespace to be provided.
|
|
10
|
+
- `n__` should use the same namespace, or no namespace at all, for both the singular and plural forms.
|
|
9
11
|
|
|
10
12
|
## Rule Details
|
|
11
13
|
|
|
@@ -16,8 +18,15 @@ following:
|
|
|
16
18
|
const label = __(LABEL);
|
|
17
19
|
const description = __(getDescription());
|
|
18
20
|
|
|
21
|
+
// Extraneous namespace
|
|
22
|
+
const globalTranslation = __('Global|Translation');
|
|
23
|
+
|
|
19
24
|
// Missing translation namespace
|
|
20
25
|
const namespacedTranslation = s__('NamespacedTranslation');
|
|
26
|
+
|
|
27
|
+
// Mismatching namespaces when pluralizing
|
|
28
|
+
const namespaceMismatch1 = n__('%d apple', 'Fruits|%d apples', 1);
|
|
29
|
+
const namespaceMismatch2 =n__('Vegetables|%d apple', 'Fruits|%d apples', 3);
|
|
21
30
|
```
|
|
22
31
|
|
|
23
32
|
### Examples of **correct** code for this rule
|
|
@@ -26,10 +35,17 @@ const namespacedTranslation = s__('NamespacedTranslation');
|
|
|
26
35
|
// Valid argument type
|
|
27
36
|
const label = __('Some label');
|
|
28
37
|
|
|
38
|
+
// Valid gettext usage
|
|
39
|
+
const globalTranslation = __('Translation');
|
|
40
|
+
|
|
29
41
|
// Valid translation namespace
|
|
30
42
|
const namespacedTranslation = s__('Namespace|Translation');
|
|
31
43
|
// or
|
|
32
44
|
const otherNamespacedTranslation = s__('Namespace', 'Other translation');
|
|
45
|
+
|
|
46
|
+
// Matching namespaces
|
|
47
|
+
const namespacesMatch1 = n__('%d apple', '%d apples', 1);
|
|
48
|
+
const namespacesMatch2 =n__('Fruits|%d apple', 'Fruits|%d apples', 3);
|
|
33
49
|
```
|
|
34
50
|
|
|
35
51
|
## Options
|
package/lib/configs/default.js
CHANGED
package/lib/utils/i18n-utils.js
CHANGED
|
@@ -10,7 +10,16 @@ const P_GETTEXT = 's__';
|
|
|
10
10
|
const N_GETTEXT = 'n__';
|
|
11
11
|
|
|
12
12
|
const ERROR_MESSAGE_STRING_LITERAL = 'Translation helpers must be called with a string literal.';
|
|
13
|
+
const ERROR_MESSAGE_EXTRANEOUS_NAMESPACE =
|
|
14
|
+
'__ should not be used with a translation namespace, consider using s__ instead.';
|
|
13
15
|
const ERROR_MESSAGE_MISSING_NAMESPACE = 's__ requires a translation namespace to be provided.';
|
|
16
|
+
const ERROR_MESSAGE_NAMESPACES_MISMATCH =
|
|
17
|
+
'Both strings should use the same namesapce, or no namespace at all, when using n__.';
|
|
18
|
+
|
|
19
|
+
const NAMESPACE_REGEX = /^\w.+\|/;
|
|
20
|
+
|
|
21
|
+
const getArgAsString = (arg) =>
|
|
22
|
+
isTemplateLiteral(arg) ? getTemplateLiteralString(arg).trim() : arg.value;
|
|
14
23
|
|
|
15
24
|
const mustBeCalledWithStringLiteralArguments = (context, node) => {
|
|
16
25
|
const report = (node) => {
|
|
@@ -35,8 +44,8 @@ const mustHaveNamespace = (context, node) => {
|
|
|
35
44
|
}
|
|
36
45
|
|
|
37
46
|
const [arg] = node.arguments;
|
|
38
|
-
const value =
|
|
39
|
-
if (
|
|
47
|
+
const value = getArgAsString(arg);
|
|
48
|
+
if (!NAMESPACE_REGEX.test(value)) {
|
|
40
49
|
context.report({
|
|
41
50
|
node,
|
|
42
51
|
message: ERROR_MESSAGE_MISSING_NAMESPACE,
|
|
@@ -44,10 +53,50 @@ const mustHaveNamespace = (context, node) => {
|
|
|
44
53
|
}
|
|
45
54
|
};
|
|
46
55
|
|
|
56
|
+
const mustNotHaveNamespace = (context, node) => {
|
|
57
|
+
const [arg] = node.arguments;
|
|
58
|
+
|
|
59
|
+
// We can ignore cases where no argument is provided as they will
|
|
60
|
+
// be caught by mustBeCalledWithStringLiteralArguments
|
|
61
|
+
if (arg === undefined) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const value = getArgAsString(arg);
|
|
66
|
+
if (NAMESPACE_REGEX.test(value)) {
|
|
67
|
+
context.report({
|
|
68
|
+
node,
|
|
69
|
+
message: ERROR_MESSAGE_EXTRANEOUS_NAMESPACE,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const namespacesMustMatch = (context, node) => {
|
|
75
|
+
const [arg1, arg2] = node.arguments;
|
|
76
|
+
const [value1, value2] = [arg1, arg2].map((arg) => getArgAsString(arg));
|
|
77
|
+
|
|
78
|
+
// Escape early if either value isn't defined, mustBeCalledWithStringLiteralArguments will
|
|
79
|
+
// handle those.
|
|
80
|
+
if (!value1 || !value2) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const [namespace1, namespace2] = [value1, value2].map((value) => {
|
|
85
|
+
const splitString = value.split('|');
|
|
86
|
+
return splitString.length > 1 ? splitString[0] : '';
|
|
87
|
+
});
|
|
88
|
+
if (namespace1 !== namespace2) {
|
|
89
|
+
context.report({
|
|
90
|
+
node,
|
|
91
|
+
message: ERROR_MESSAGE_NAMESPACES_MISMATCH,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
|
|
47
96
|
const VALIDATORS = {
|
|
48
|
-
[GETTEXT]: [mustBeCalledWithStringLiteralArguments],
|
|
97
|
+
[GETTEXT]: [mustBeCalledWithStringLiteralArguments, mustNotHaveNamespace],
|
|
49
98
|
[P_GETTEXT]: [mustBeCalledWithStringLiteralArguments, mustHaveNamespace],
|
|
50
|
-
[N_GETTEXT]: [mustBeCalledWithStringLiteralArguments],
|
|
99
|
+
[N_GETTEXT]: [mustBeCalledWithStringLiteralArguments, namespacesMustMatch],
|
|
51
100
|
};
|
|
52
101
|
|
|
53
102
|
const validateI18nHelperCallFactory = (context) => (node) => {
|
|
@@ -64,5 +113,7 @@ const validateI18nHelperCallFactory = (context) => (node) => {
|
|
|
64
113
|
module.exports = {
|
|
65
114
|
validateI18nHelperCallFactory,
|
|
66
115
|
ERROR_MESSAGE_STRING_LITERAL,
|
|
116
|
+
ERROR_MESSAGE_EXTRANEOUS_NAMESPACE,
|
|
67
117
|
ERROR_MESSAGE_MISSING_NAMESPACE,
|
|
118
|
+
ERROR_MESSAGE_NAMESPACES_MISMATCH,
|
|
68
119
|
};
|