@discord/eslint-plugin-discord-intl 0.29.1 → 0.31.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/index.js +2 -0
- package/package.json +2 -2
- package/rules/no-invalid-meta.js +65 -0
- package/rules/no-invalid-meta.test.js +126 -0
package/index.js
CHANGED
|
@@ -16,6 +16,7 @@ module.exports = {
|
|
|
16
16
|
|
|
17
17
|
'use-static-access': require('./rules/use-static-access'),
|
|
18
18
|
'no-opaque-messages-objects': require('./rules/no-opaque-messages-objects'),
|
|
19
|
+
'no-invalid-meta': require('./rules/no-invalid-meta'),
|
|
19
20
|
},
|
|
20
21
|
configs: {
|
|
21
22
|
recommended: {
|
|
@@ -38,6 +39,7 @@ module.exports = {
|
|
|
38
39
|
// JS rules
|
|
39
40
|
'@discord/discord-intl/use-static-access': 'error',
|
|
40
41
|
'@discord/discord-intl/no-opaque-messages-objects': 'error',
|
|
42
|
+
// '@discord/discord-intl/no-invalid-meta': 'error',
|
|
41
43
|
},
|
|
42
44
|
},
|
|
43
45
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@discord/eslint-plugin-discord-intl",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.31.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"description": "ESLint plugin for validating and linting messages and translations using @discord/intl",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
13
|
"@typescript-eslint/scope-manager": "^8.8.1",
|
|
14
|
-
"@discord/intl-message-database": "0.
|
|
14
|
+
"@discord/intl-message-database": "0.31.0"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
17
17
|
"@types/eslint": "^8.56.12",
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
const { isDefinitionsFile } = require('../lib/traverse');
|
|
2
|
+
|
|
3
|
+
module.exports = /** @type {import('eslint').Rule.RuleModule} */ ({
|
|
4
|
+
meta: {
|
|
5
|
+
docs: {
|
|
6
|
+
description:
|
|
7
|
+
"Check that meta information is defined correctly and won't be silently ignored while processing.",
|
|
8
|
+
category: 'Correctness',
|
|
9
|
+
},
|
|
10
|
+
messages: {
|
|
11
|
+
metaMustBeInitialized: '`meta` must have an initializer',
|
|
12
|
+
metaMustBeObjectLiteral: '`meta` must be defined using an object literal',
|
|
13
|
+
disallowSpread: 'meta properties must be defined inline',
|
|
14
|
+
disallowComputedKey: 'meta properties cannot be defined using computed keys',
|
|
15
|
+
invalidTagsValue:
|
|
16
|
+
'`tags` must be initialized using an array literal containing only string literals',
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
create(context) {
|
|
20
|
+
if (!isDefinitionsFile(context.filename, context.sourceCode.text)) return {};
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
/**
|
|
24
|
+
* @param {import('estree').VariableDeclarator} node
|
|
25
|
+
*/
|
|
26
|
+
'VariableDeclarator[id.name="meta"]'(node) {
|
|
27
|
+
if (node.init == null) {
|
|
28
|
+
context.report({ node, messageId: 'metaMustBeInitialized' });
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (node.init.type !== 'ObjectExpression') {
|
|
32
|
+
context.report({ node, messageId: 'metaMustBeObjectLiteral' });
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const initializer = node.init;
|
|
37
|
+
|
|
38
|
+
for (const property of initializer.properties) {
|
|
39
|
+
if (property.type === 'SpreadElement') {
|
|
40
|
+
context.report({ node: property, messageId: 'disallowSpread' });
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (property.computed || property.key.type !== 'Identifier') {
|
|
44
|
+
context.report({ node: property, messageId: 'disallowComputedKey' });
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
switch (property.key.name) {
|
|
48
|
+
// Tags are more than just a single literal. Every element of the array must also be a
|
|
49
|
+
// string literal for it to be valid.
|
|
50
|
+
case 'tags':
|
|
51
|
+
if (property.value.type !== 'ArrayExpression') {
|
|
52
|
+
context.report({ node: property.value, messageId: 'invalidTagsValue' });
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
for (const tag of property.value.elements) {
|
|
56
|
+
if (tag == null || tag.type !== 'Literal' || typeof tag.value !== 'string') {
|
|
57
|
+
context.report({ node: tag ?? property.value, messageId: 'invalidTagsValue' });
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
},
|
|
65
|
+
});
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
const { RuleTester } = require('eslint');
|
|
2
|
+
const noInvalidMeta = require('./no-invalid-meta');
|
|
3
|
+
|
|
4
|
+
const ruleTester = new RuleTester({
|
|
5
|
+
// Must use at least ecmaVersion 2015 because
|
|
6
|
+
// that's when `const` variables were introduced.
|
|
7
|
+
// Using 2022 to make sure the spread operator is supported.
|
|
8
|
+
parserOptions: { ecmaVersion: 2022, sourceType: 'module' },
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
// @ts-ignore
|
|
12
|
+
const errorMessages = noInvalidMeta.meta.messages ?? {};
|
|
13
|
+
|
|
14
|
+
ruleTester.run('no-invalid-meta', noInvalidMeta, {
|
|
15
|
+
valid: [
|
|
16
|
+
{
|
|
17
|
+
name: 'no meta defined',
|
|
18
|
+
code: `
|
|
19
|
+
import {defineMessages} from '@discord/intl';
|
|
20
|
+
export default defineMessages({HELLO: "world"});
|
|
21
|
+
`,
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: 'not a messages file',
|
|
25
|
+
code: `
|
|
26
|
+
export const meta = {foobar: thisWouldBeInvalid};
|
|
27
|
+
`,
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: 'all literals',
|
|
31
|
+
code: `
|
|
32
|
+
import {defineMessages} from '@discord/intl';
|
|
33
|
+
export const meta = {
|
|
34
|
+
project: 'some-feature',
|
|
35
|
+
secret: false,
|
|
36
|
+
translate: true,
|
|
37
|
+
translationsPath: './translations',
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export default defineMessages({HELLO: "world"});
|
|
41
|
+
`,
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
invalid: [
|
|
45
|
+
{
|
|
46
|
+
name: 'no spread',
|
|
47
|
+
code: `
|
|
48
|
+
import {defineMessages} from '@discord/intl';
|
|
49
|
+
export const meta = {
|
|
50
|
+
...something,
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export default defineMessages({HELLO: "world"});
|
|
54
|
+
`,
|
|
55
|
+
errors: [errorMessages.disallowSpread],
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
name: 'no computed key',
|
|
59
|
+
code: `
|
|
60
|
+
import {defineMessages} from '@discord/intl';
|
|
61
|
+
export const meta = {
|
|
62
|
+
[computed]: true,
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export default defineMessages({HELLO: "world"});
|
|
66
|
+
`,
|
|
67
|
+
errors: [errorMessages.disallowComputedKey],
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
name: 'no empty tags',
|
|
71
|
+
code: `
|
|
72
|
+
import {defineMessages} from '@discord/intl';
|
|
73
|
+
export const meta = {
|
|
74
|
+
tags: [,'foo']
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export default defineMessages({HELLO: "world"});
|
|
78
|
+
`,
|
|
79
|
+
errors: [errorMessages.invalidTagsValue],
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: 'no variable tags',
|
|
83
|
+
code: `
|
|
84
|
+
import {defineMessages} from '@discord/intl';
|
|
85
|
+
export const meta = {
|
|
86
|
+
tags: [aVariable]
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export default defineMessages({HELLO: "world"});
|
|
90
|
+
`,
|
|
91
|
+
errors: [errorMessages.invalidTagsValue],
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
name: 'no missing meta initializer',
|
|
95
|
+
code: `
|
|
96
|
+
import {defineMessages} from '@discord/intl';
|
|
97
|
+
export let meta;
|
|
98
|
+
|
|
99
|
+
export default defineMessages({HELLO: "world"});
|
|
100
|
+
`,
|
|
101
|
+
errors: [errorMessages.metaMustBeInitialized],
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
name: 'require object literal initializer',
|
|
105
|
+
code: `
|
|
106
|
+
import {defineMessages} from '@discord/intl';
|
|
107
|
+
export const meta = false;
|
|
108
|
+
|
|
109
|
+
export default defineMessages({HELLO: "world"});
|
|
110
|
+
`,
|
|
111
|
+
errors: [errorMessages.metaMustBeObjectLiteral],
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
name: 'require object literal initializer',
|
|
115
|
+
code: `
|
|
116
|
+
import {defineMessages} from '@discord/intl';
|
|
117
|
+
export const meta = someObject;
|
|
118
|
+
|
|
119
|
+
export default defineMessages({HELLO: "world"});
|
|
120
|
+
`,
|
|
121
|
+
errors: [errorMessages.metaMustBeObjectLiteral],
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
console.log('All tests passed!');
|