@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
@@ -55,6 +55,7 @@ module.exports = {
55
55
  ],
56
56
  },
57
57
  ],
58
+ 'no-restricted-syntax': 'off',
58
59
  'no-sequences': [
59
60
  'error',
60
61
  {
@@ -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 = isTemplateLiteral(arg) ? getTemplateLiteralString(arg).trim() : arg.value;
39
- if (!/^\w.+\|/.test(value)) {
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
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitlab/eslint-plugin",
3
- "version": "18.2.0",
3
+ "version": "18.3.0",
4
4
  "description": "GitLab package for our custom eslint rules",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {