@akinon/eslint-plugin-projectzero 1.14.0 → 1.14.1
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/.prettierrc +13 -13
- package/CHANGELOG.md +6 -0
- package/configs/core.ts +20 -20
- package/configs/index.ts +4 -4
- package/configs/recommended.ts +23 -23
- package/dist/rules/router-import.js +37 -37
- package/dist/rules/urls-without-slash.js +40 -40
- package/package.json +1 -1
- package/rules/check-locale.ts +78 -78
- package/rules/check-middleware-order.ts +64 -64
- package/rules/client-url.ts +44 -44
- package/rules/image-import.ts +44 -44
- package/rules/index.ts +23 -23
- package/rules/link-import.ts +44 -44
- package/rules/meta-data-import.ts +50 -50
- package/rules/skip-trailing-slash-redirect.ts +60 -60
package/.prettierrc
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
{
|
|
2
|
-
"bracketSameLine": false,
|
|
3
|
-
"tabWidth": 2,
|
|
4
|
-
"singleQuote": true,
|
|
5
|
-
"jsxSingleQuote": false,
|
|
6
|
-
"bracketSpacing": true,
|
|
7
|
-
"semi": true,
|
|
8
|
-
"useTabs": false,
|
|
9
|
-
"arrowParens": "always",
|
|
10
|
-
"endOfLine": "lf",
|
|
11
|
-
"proseWrap": "never",
|
|
12
|
-
"trailingComma": "none"
|
|
13
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"bracketSameLine": false,
|
|
3
|
+
"tabWidth": 2,
|
|
4
|
+
"singleQuote": true,
|
|
5
|
+
"jsxSingleQuote": false,
|
|
6
|
+
"bracketSpacing": true,
|
|
7
|
+
"semi": true,
|
|
8
|
+
"useTabs": false,
|
|
9
|
+
"arrowParens": "always",
|
|
10
|
+
"endOfLine": "lf",
|
|
11
|
+
"proseWrap": "never",
|
|
12
|
+
"trailingComma": "none"
|
|
13
|
+
}
|
package/CHANGELOG.md
CHANGED
package/configs/core.ts
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
export default {
|
|
2
|
-
rules: {
|
|
3
|
-
'@akinon/projectzero/image-import': 'error',
|
|
4
|
-
'@akinon/projectzero/client-url': 'error',
|
|
5
|
-
'@akinon/projectzero/link-import': 'error',
|
|
6
|
-
'@akinon/projectzero/router-import': 'error',
|
|
7
|
-
'@akinon/projectzero/skip-trailing-slash-redirect': 'error',
|
|
8
|
-
'@akinon/projectzero/check-locale': 'error',
|
|
9
|
-
'@akinon/projectzero/meta-data-import': 'error',
|
|
10
|
-
'@akinon/projectzero/urls-without-slash': 'error',
|
|
11
|
-
'no-restricted-syntax': [
|
|
12
|
-
'error',
|
|
13
|
-
{
|
|
14
|
-
selector:
|
|
15
|
-
"MemberExpression[property.name='COMMERCE_URL'][object.property.name='env'][object.object.name='process']",
|
|
16
|
-
message: "You should use it as 'Settings.commerceUrl'"
|
|
17
|
-
}
|
|
18
|
-
]
|
|
19
|
-
}
|
|
20
|
-
};
|
|
1
|
+
export default {
|
|
2
|
+
rules: {
|
|
3
|
+
'@akinon/projectzero/image-import': 'error',
|
|
4
|
+
'@akinon/projectzero/client-url': 'error',
|
|
5
|
+
'@akinon/projectzero/link-import': 'error',
|
|
6
|
+
'@akinon/projectzero/router-import': 'error',
|
|
7
|
+
'@akinon/projectzero/skip-trailing-slash-redirect': 'error',
|
|
8
|
+
'@akinon/projectzero/check-locale': 'error',
|
|
9
|
+
'@akinon/projectzero/meta-data-import': 'error',
|
|
10
|
+
'@akinon/projectzero/urls-without-slash': 'error',
|
|
11
|
+
'no-restricted-syntax': [
|
|
12
|
+
'error',
|
|
13
|
+
{
|
|
14
|
+
selector:
|
|
15
|
+
"MemberExpression[property.name='COMMERCE_URL'][object.property.name='env'][object.object.name='process']",
|
|
16
|
+
message: "You should use it as 'Settings.commerceUrl'"
|
|
17
|
+
}
|
|
18
|
+
]
|
|
19
|
+
}
|
|
20
|
+
};
|
package/configs/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import core from './core';
|
|
2
|
-
import recommended from './recommended';
|
|
3
|
-
|
|
4
|
-
export { core, recommended };
|
|
1
|
+
import core from './core';
|
|
2
|
+
import recommended from './recommended';
|
|
3
|
+
|
|
4
|
+
export { core, recommended };
|
package/configs/recommended.ts
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
export default {
|
|
2
|
-
rules: {
|
|
3
|
-
'@next/next/no-html-link-for-pages': 'off',
|
|
4
|
-
'@next/next/no-server-import-in-page': 'off',
|
|
5
|
-
'@typescript-eslint/no-var-requires': 0,
|
|
6
|
-
'@typescript-eslint/no-empty-interface': [
|
|
7
|
-
'error',
|
|
8
|
-
{
|
|
9
|
-
allowSingleExtends: false
|
|
10
|
-
}
|
|
11
|
-
],
|
|
12
|
-
'no-console': ['error', { allow: ['error', 'warn'] }],
|
|
13
|
-
'no-empty-function': [
|
|
14
|
-
'error',
|
|
15
|
-
{ allow: ['arrowFunctions', 'functions', 'methods'] }
|
|
16
|
-
],
|
|
17
|
-
'object-shorthand': [
|
|
18
|
-
'error',
|
|
19
|
-
'always',
|
|
20
|
-
{ ignoreConstructors: false, avoidQuotes: true }
|
|
21
|
-
]
|
|
22
|
-
}
|
|
23
|
-
};
|
|
1
|
+
export default {
|
|
2
|
+
rules: {
|
|
3
|
+
'@next/next/no-html-link-for-pages': 'off',
|
|
4
|
+
'@next/next/no-server-import-in-page': 'off',
|
|
5
|
+
'@typescript-eslint/no-var-requires': 0,
|
|
6
|
+
'@typescript-eslint/no-empty-interface': [
|
|
7
|
+
'error',
|
|
8
|
+
{
|
|
9
|
+
allowSingleExtends: false
|
|
10
|
+
}
|
|
11
|
+
],
|
|
12
|
+
'no-console': ['error', { allow: ['error', 'warn'] }],
|
|
13
|
+
'no-empty-function': [
|
|
14
|
+
'error',
|
|
15
|
+
{ allow: ['arrowFunctions', 'functions', 'methods'] }
|
|
16
|
+
],
|
|
17
|
+
'object-shorthand': [
|
|
18
|
+
'error',
|
|
19
|
+
'always',
|
|
20
|
+
{ ignoreConstructors: false, avoidQuotes: true }
|
|
21
|
+
]
|
|
22
|
+
}
|
|
23
|
+
};
|
|
@@ -1,37 +1,37 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const utils_1 = require("@typescript-eslint/utils");
|
|
4
|
-
exports.default = utils_1.ESLintUtils.RuleCreator.withoutDocs({
|
|
5
|
-
create(context) {
|
|
6
|
-
if (context.getFilename().includes('@akinon/next/hooks/use-router.ts')) {
|
|
7
|
-
return {};
|
|
8
|
-
}
|
|
9
|
-
return {
|
|
10
|
-
ImportDeclaration(node) {
|
|
11
|
-
if (node.source.value === 'next/router' || node.source.value === 'next/navigation' && node.specifiers.find((s) => s.local.name === 'useRouter')) {
|
|
12
|
-
context.report({
|
|
13
|
-
messageId: 'routerImport',
|
|
14
|
-
node,
|
|
15
|
-
fix: (fixer) => {
|
|
16
|
-
if (node.specifiers.find((s) => s.type === 'ImportSpecifier')) {
|
|
17
|
-
return null;
|
|
18
|
-
}
|
|
19
|
-
return fixer.replaceText(node, node.specifiers
|
|
20
|
-
.map((specifier) => `import { ${specifier.local.name} } from '@akinon/next/hooks';`)
|
|
21
|
-
.join('\n'));
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
};
|
|
27
|
-
},
|
|
28
|
-
meta: {
|
|
29
|
-
messages: {
|
|
30
|
-
routerImport: "Import 'useRouter' hooks from '@akinon/next/hooks'."
|
|
31
|
-
},
|
|
32
|
-
type: 'problem',
|
|
33
|
-
fixable: 'code',
|
|
34
|
-
schema: []
|
|
35
|
-
},
|
|
36
|
-
defaultOptions: []
|
|
37
|
-
});
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
4
|
+
exports.default = utils_1.ESLintUtils.RuleCreator.withoutDocs({
|
|
5
|
+
create(context) {
|
|
6
|
+
if (context.getFilename().includes('@akinon/next/hooks/use-router.ts')) {
|
|
7
|
+
return {};
|
|
8
|
+
}
|
|
9
|
+
return {
|
|
10
|
+
ImportDeclaration(node) {
|
|
11
|
+
if (node.source.value === 'next/router' || node.source.value === 'next/navigation' && node.specifiers.find((s) => s.local.name === 'useRouter')) {
|
|
12
|
+
context.report({
|
|
13
|
+
messageId: 'routerImport',
|
|
14
|
+
node,
|
|
15
|
+
fix: (fixer) => {
|
|
16
|
+
if (node.specifiers.find((s) => s.type === 'ImportSpecifier')) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
return fixer.replaceText(node, node.specifiers
|
|
20
|
+
.map((specifier) => `import { ${specifier.local.name} } from '@akinon/next/hooks';`)
|
|
21
|
+
.join('\n'));
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
},
|
|
28
|
+
meta: {
|
|
29
|
+
messages: {
|
|
30
|
+
routerImport: "Import 'useRouter' hooks from '@akinon/next/hooks'."
|
|
31
|
+
},
|
|
32
|
+
type: 'problem',
|
|
33
|
+
fixable: 'code',
|
|
34
|
+
schema: []
|
|
35
|
+
},
|
|
36
|
+
defaultOptions: []
|
|
37
|
+
});
|
|
@@ -1,40 +1,40 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const utils_1 = require("@typescript-eslint/utils");
|
|
4
|
-
const URLS = [
|
|
5
|
-
'/users/logout/'
|
|
6
|
-
];
|
|
7
|
-
exports.default = utils_1.ESLintUtils.RuleCreator.withoutDocs({
|
|
8
|
-
create(context) {
|
|
9
|
-
if (!context.getFilename().includes('urls.ts')) {
|
|
10
|
-
return {};
|
|
11
|
-
}
|
|
12
|
-
return {
|
|
13
|
-
Literal(node) {
|
|
14
|
-
var _a;
|
|
15
|
-
const value = (_a = node.value) === null || _a === void 0 ? void 0 : _a.toString();
|
|
16
|
-
if (!value) {
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
19
|
-
URLS.map((url) => {
|
|
20
|
-
if (value.endsWith(url)) {
|
|
21
|
-
context.report({
|
|
22
|
-
messageId: 'UrlWithoutSlash',
|
|
23
|
-
node,
|
|
24
|
-
fix: (fixer) => fixer.replaceText(node, `\`${value.slice(0, -1)}\``)
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
};
|
|
30
|
-
},
|
|
31
|
-
meta: {
|
|
32
|
-
messages: {
|
|
33
|
-
UrlWithoutSlash: 'It must not end with a forward slash (/).'
|
|
34
|
-
},
|
|
35
|
-
type: 'problem',
|
|
36
|
-
fixable: 'code',
|
|
37
|
-
schema: []
|
|
38
|
-
},
|
|
39
|
-
defaultOptions: []
|
|
40
|
-
});
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
4
|
+
const URLS = [
|
|
5
|
+
'/users/logout/'
|
|
6
|
+
];
|
|
7
|
+
exports.default = utils_1.ESLintUtils.RuleCreator.withoutDocs({
|
|
8
|
+
create(context) {
|
|
9
|
+
if (!context.getFilename().includes('urls.ts')) {
|
|
10
|
+
return {};
|
|
11
|
+
}
|
|
12
|
+
return {
|
|
13
|
+
Literal(node) {
|
|
14
|
+
var _a;
|
|
15
|
+
const value = (_a = node.value) === null || _a === void 0 ? void 0 : _a.toString();
|
|
16
|
+
if (!value) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
URLS.map((url) => {
|
|
20
|
+
if (value.endsWith(url)) {
|
|
21
|
+
context.report({
|
|
22
|
+
messageId: 'UrlWithoutSlash',
|
|
23
|
+
node,
|
|
24
|
+
fix: (fixer) => fixer.replaceText(node, `\`${value.slice(0, -1)}\``)
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
},
|
|
31
|
+
meta: {
|
|
32
|
+
messages: {
|
|
33
|
+
UrlWithoutSlash: 'It must not end with a forward slash (/).'
|
|
34
|
+
},
|
|
35
|
+
type: 'problem',
|
|
36
|
+
fixable: 'code',
|
|
37
|
+
schema: []
|
|
38
|
+
},
|
|
39
|
+
defaultOptions: []
|
|
40
|
+
});
|
package/package.json
CHANGED
package/rules/check-locale.ts
CHANGED
|
@@ -1,78 +1,78 @@
|
|
|
1
|
-
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
-
import * as path from 'path';
|
|
3
|
-
import * as fs from 'fs';
|
|
4
|
-
|
|
5
|
-
const MONOREPO_LOCALES_DIR = path.resolve(
|
|
6
|
-
__dirname,
|
|
7
|
-
'../../../../apps/projectzeropwa/public/locales/en'
|
|
8
|
-
);
|
|
9
|
-
|
|
10
|
-
const STANDARD_REPO_LOCALES_DIR = path.join(process.cwd(), 'public/locales/en');
|
|
11
|
-
|
|
12
|
-
const LOCALES_DIR = fs.existsSync(MONOREPO_LOCALES_DIR)
|
|
13
|
-
? MONOREPO_LOCALES_DIR
|
|
14
|
-
: STANDARD_REPO_LOCALES_DIR;
|
|
15
|
-
|
|
16
|
-
const getAllKeysFromObject = (obj: any, prefix = ''): string[] => {
|
|
17
|
-
return Object.keys(obj).reduce((result, key) => {
|
|
18
|
-
if (typeof obj[key] === 'object' && !Array.isArray(obj[key])) {
|
|
19
|
-
return result.concat(getAllKeysFromObject(obj[key], `${prefix}${key}.`));
|
|
20
|
-
}
|
|
21
|
-
return result.concat(`${prefix}${key}`);
|
|
22
|
-
}, [] as string[]);
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
export default ESLintUtils.RuleCreator.withoutDocs({
|
|
26
|
-
create(context: any) {
|
|
27
|
-
return {
|
|
28
|
-
CallExpression(node: any) {
|
|
29
|
-
if (node.callee.type !== 'Identifier' || node.callee.name !== 't') {
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const [firstArg] = node.arguments;
|
|
34
|
-
|
|
35
|
-
if (
|
|
36
|
-
firstArg &&
|
|
37
|
-
firstArg.type === 'Literal' &&
|
|
38
|
-
typeof firstArg.value === 'string'
|
|
39
|
-
) {
|
|
40
|
-
const key = firstArg.value;
|
|
41
|
-
|
|
42
|
-
const filename = `${key.split('.')[0]}.json`;
|
|
43
|
-
const filepath = path.join(LOCALES_DIR, filename);
|
|
44
|
-
|
|
45
|
-
if (fs.existsSync(filepath)) {
|
|
46
|
-
const jsonContent = JSON.parse(fs.readFileSync(filepath, 'utf-8'));
|
|
47
|
-
const allKeys = getAllKeysFromObject(
|
|
48
|
-
jsonContent,
|
|
49
|
-
filename.replace('.json', '.')
|
|
50
|
-
);
|
|
51
|
-
|
|
52
|
-
if (!allKeys.includes(key)) {
|
|
53
|
-
context.report({
|
|
54
|
-
node: firstArg,
|
|
55
|
-
messageId: 'missingLocalizationKey'
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
} else {
|
|
59
|
-
context.report({
|
|
60
|
-
node: firstArg,
|
|
61
|
-
messageId: 'missingLocalizationFile'
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
},
|
|
68
|
-
meta: {
|
|
69
|
-
messages: {
|
|
70
|
-
missingLocalizationKey:
|
|
71
|
-
'The localization key does not exist in the JSON files.',
|
|
72
|
-
missingLocalizationFile: 'The localization file does not exist.'
|
|
73
|
-
},
|
|
74
|
-
type: 'problem',
|
|
75
|
-
schema: []
|
|
76
|
-
},
|
|
77
|
-
defaultOptions: []
|
|
78
|
-
});
|
|
1
|
+
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as fs from 'fs';
|
|
4
|
+
|
|
5
|
+
const MONOREPO_LOCALES_DIR = path.resolve(
|
|
6
|
+
__dirname,
|
|
7
|
+
'../../../../apps/projectzeropwa/public/locales/en'
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
const STANDARD_REPO_LOCALES_DIR = path.join(process.cwd(), 'public/locales/en');
|
|
11
|
+
|
|
12
|
+
const LOCALES_DIR = fs.existsSync(MONOREPO_LOCALES_DIR)
|
|
13
|
+
? MONOREPO_LOCALES_DIR
|
|
14
|
+
: STANDARD_REPO_LOCALES_DIR;
|
|
15
|
+
|
|
16
|
+
const getAllKeysFromObject = (obj: any, prefix = ''): string[] => {
|
|
17
|
+
return Object.keys(obj).reduce((result, key) => {
|
|
18
|
+
if (typeof obj[key] === 'object' && !Array.isArray(obj[key])) {
|
|
19
|
+
return result.concat(getAllKeysFromObject(obj[key], `${prefix}${key}.`));
|
|
20
|
+
}
|
|
21
|
+
return result.concat(`${prefix}${key}`);
|
|
22
|
+
}, [] as string[]);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export default ESLintUtils.RuleCreator.withoutDocs({
|
|
26
|
+
create(context: any) {
|
|
27
|
+
return {
|
|
28
|
+
CallExpression(node: any) {
|
|
29
|
+
if (node.callee.type !== 'Identifier' || node.callee.name !== 't') {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const [firstArg] = node.arguments;
|
|
34
|
+
|
|
35
|
+
if (
|
|
36
|
+
firstArg &&
|
|
37
|
+
firstArg.type === 'Literal' &&
|
|
38
|
+
typeof firstArg.value === 'string'
|
|
39
|
+
) {
|
|
40
|
+
const key = firstArg.value;
|
|
41
|
+
|
|
42
|
+
const filename = `${key.split('.')[0]}.json`;
|
|
43
|
+
const filepath = path.join(LOCALES_DIR, filename);
|
|
44
|
+
|
|
45
|
+
if (fs.existsSync(filepath)) {
|
|
46
|
+
const jsonContent = JSON.parse(fs.readFileSync(filepath, 'utf-8'));
|
|
47
|
+
const allKeys = getAllKeysFromObject(
|
|
48
|
+
jsonContent,
|
|
49
|
+
filename.replace('.json', '.')
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
if (!allKeys.includes(key)) {
|
|
53
|
+
context.report({
|
|
54
|
+
node: firstArg,
|
|
55
|
+
messageId: 'missingLocalizationKey'
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
} else {
|
|
59
|
+
context.report({
|
|
60
|
+
node: firstArg,
|
|
61
|
+
messageId: 'missingLocalizationFile'
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
},
|
|
68
|
+
meta: {
|
|
69
|
+
messages: {
|
|
70
|
+
missingLocalizationKey:
|
|
71
|
+
'The localization key does not exist in the JSON files.',
|
|
72
|
+
missingLocalizationFile: 'The localization file does not exist.'
|
|
73
|
+
},
|
|
74
|
+
type: 'problem',
|
|
75
|
+
schema: []
|
|
76
|
+
},
|
|
77
|
+
defaultOptions: []
|
|
78
|
+
});
|
|
@@ -1,64 +1,64 @@
|
|
|
1
|
-
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
-
|
|
3
|
-
const correctOrder = [
|
|
4
|
-
'withOauthLogin',
|
|
5
|
-
'withLocale',
|
|
6
|
-
'withCurrency',
|
|
7
|
-
'withPrettyUrl',
|
|
8
|
-
'withRedirectionPayment',
|
|
9
|
-
'withThreeDRedirection',
|
|
10
|
-
'withUrlRedirection'
|
|
11
|
-
];
|
|
12
|
-
|
|
13
|
-
export default ESLintUtils.RuleCreator.withoutDocs({
|
|
14
|
-
create(context: any) {
|
|
15
|
-
return {
|
|
16
|
-
CallExpression(node: any) {
|
|
17
|
-
if (
|
|
18
|
-
node.callee.type === 'Identifier' &&
|
|
19
|
-
correctOrder.includes(node.callee.name)
|
|
20
|
-
) {
|
|
21
|
-
let current = node;
|
|
22
|
-
const callSequence = [];
|
|
23
|
-
|
|
24
|
-
while (
|
|
25
|
-
current.type === 'CallExpression' &&
|
|
26
|
-
current.callee.type === 'Identifier'
|
|
27
|
-
) {
|
|
28
|
-
if (correctOrder.includes(current.callee.name)) {
|
|
29
|
-
callSequence.unshift(current.callee.name);
|
|
30
|
-
}
|
|
31
|
-
current = current.parent;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
let isValidOrder = true;
|
|
35
|
-
for (let i = 0; i < callSequence.length; i++) {
|
|
36
|
-
if (callSequence[i] !== correctOrder[i]) {
|
|
37
|
-
isValidOrder = false;
|
|
38
|
-
break;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Report an error if the order is incorrect
|
|
43
|
-
if (!isValidOrder) {
|
|
44
|
-
context.report({
|
|
45
|
-
node,
|
|
46
|
-
message: `Incorrect order of middleware functions. Expected order: ${correctOrder.join(
|
|
47
|
-
', '
|
|
48
|
-
)}. Found order: ${callSequence.join(', ')}.`
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
};
|
|
54
|
-
},
|
|
55
|
-
meta: {
|
|
56
|
-
type: 'problem',
|
|
57
|
-
schema: [],
|
|
58
|
-
messages: {
|
|
59
|
-
incorrectMiddlewareOrder:
|
|
60
|
-
'Incorrect middleware order. Expected {{expected}}, found {{found}}.'
|
|
61
|
-
}
|
|
62
|
-
},
|
|
63
|
-
defaultOptions: []
|
|
64
|
-
});
|
|
1
|
+
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
+
|
|
3
|
+
const correctOrder = [
|
|
4
|
+
'withOauthLogin',
|
|
5
|
+
'withLocale',
|
|
6
|
+
'withCurrency',
|
|
7
|
+
'withPrettyUrl',
|
|
8
|
+
'withRedirectionPayment',
|
|
9
|
+
'withThreeDRedirection',
|
|
10
|
+
'withUrlRedirection'
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
export default ESLintUtils.RuleCreator.withoutDocs({
|
|
14
|
+
create(context: any) {
|
|
15
|
+
return {
|
|
16
|
+
CallExpression(node: any) {
|
|
17
|
+
if (
|
|
18
|
+
node.callee.type === 'Identifier' &&
|
|
19
|
+
correctOrder.includes(node.callee.name)
|
|
20
|
+
) {
|
|
21
|
+
let current = node;
|
|
22
|
+
const callSequence = [];
|
|
23
|
+
|
|
24
|
+
while (
|
|
25
|
+
current.type === 'CallExpression' &&
|
|
26
|
+
current.callee.type === 'Identifier'
|
|
27
|
+
) {
|
|
28
|
+
if (correctOrder.includes(current.callee.name)) {
|
|
29
|
+
callSequence.unshift(current.callee.name);
|
|
30
|
+
}
|
|
31
|
+
current = current.parent;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let isValidOrder = true;
|
|
35
|
+
for (let i = 0; i < callSequence.length; i++) {
|
|
36
|
+
if (callSequence[i] !== correctOrder[i]) {
|
|
37
|
+
isValidOrder = false;
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Report an error if the order is incorrect
|
|
43
|
+
if (!isValidOrder) {
|
|
44
|
+
context.report({
|
|
45
|
+
node,
|
|
46
|
+
message: `Incorrect order of middleware functions. Expected order: ${correctOrder.join(
|
|
47
|
+
', '
|
|
48
|
+
)}. Found order: ${callSequence.join(', ')}.`
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
},
|
|
55
|
+
meta: {
|
|
56
|
+
type: 'problem',
|
|
57
|
+
schema: [],
|
|
58
|
+
messages: {
|
|
59
|
+
incorrectMiddlewareOrder:
|
|
60
|
+
'Incorrect middleware order. Expected {{expected}}, found {{found}}.'
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
defaultOptions: []
|
|
64
|
+
});
|
package/rules/client-url.ts
CHANGED
|
@@ -1,44 +1,44 @@
|
|
|
1
|
-
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
-
|
|
3
|
-
export default ESLintUtils.RuleCreator.withoutDocs({
|
|
4
|
-
create(context) {
|
|
5
|
-
if (!context.getFilename().includes('urls.ts')) {
|
|
6
|
-
return {};
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
return {
|
|
10
|
-
Literal(node) {
|
|
11
|
-
const value = node.value?.toString();
|
|
12
|
-
const isStrLiteral = value?.includes('${');
|
|
13
|
-
|
|
14
|
-
if (!value) {
|
|
15
|
-
return;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
if (value.endsWith('/')) {
|
|
19
|
-
context.report({
|
|
20
|
-
messageId: 'clientUrl',
|
|
21
|
-
node,
|
|
22
|
-
fix: (fixer) =>
|
|
23
|
-
fixer.replaceText(
|
|
24
|
-
node,
|
|
25
|
-
`${isStrLiteral ? '`' : `'`}${value.substring(
|
|
26
|
-
0,
|
|
27
|
-
value.length - 1
|
|
28
|
-
)}${isStrLiteral ? '`' : `'`}`
|
|
29
|
-
)
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
};
|
|
34
|
-
},
|
|
35
|
-
meta: {
|
|
36
|
-
messages: {
|
|
37
|
-
clientUrl: 'Client URLs must not end with slash(/).'
|
|
38
|
-
},
|
|
39
|
-
type: 'problem',
|
|
40
|
-
fixable: 'code',
|
|
41
|
-
schema: []
|
|
42
|
-
},
|
|
43
|
-
defaultOptions: []
|
|
44
|
-
});
|
|
1
|
+
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
+
|
|
3
|
+
export default ESLintUtils.RuleCreator.withoutDocs({
|
|
4
|
+
create(context) {
|
|
5
|
+
if (!context.getFilename().includes('urls.ts')) {
|
|
6
|
+
return {};
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
return {
|
|
10
|
+
Literal(node) {
|
|
11
|
+
const value = node.value?.toString();
|
|
12
|
+
const isStrLiteral = value?.includes('${');
|
|
13
|
+
|
|
14
|
+
if (!value) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (value.endsWith('/')) {
|
|
19
|
+
context.report({
|
|
20
|
+
messageId: 'clientUrl',
|
|
21
|
+
node,
|
|
22
|
+
fix: (fixer) =>
|
|
23
|
+
fixer.replaceText(
|
|
24
|
+
node,
|
|
25
|
+
`${isStrLiteral ? '`' : `'`}${value.substring(
|
|
26
|
+
0,
|
|
27
|
+
value.length - 1
|
|
28
|
+
)}${isStrLiteral ? '`' : `'`}`
|
|
29
|
+
)
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
},
|
|
35
|
+
meta: {
|
|
36
|
+
messages: {
|
|
37
|
+
clientUrl: 'Client URLs must not end with slash(/).'
|
|
38
|
+
},
|
|
39
|
+
type: 'problem',
|
|
40
|
+
fixable: 'code',
|
|
41
|
+
schema: []
|
|
42
|
+
},
|
|
43
|
+
defaultOptions: []
|
|
44
|
+
});
|
package/rules/image-import.ts
CHANGED
|
@@ -1,44 +1,44 @@
|
|
|
1
|
-
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
-
|
|
3
|
-
export default ESLintUtils.RuleCreator.withoutDocs({
|
|
4
|
-
create(context) {
|
|
5
|
-
if (context.getFilename().includes('components/image.tsx')) {
|
|
6
|
-
return {};
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
return {
|
|
10
|
-
ImportDeclaration(node) {
|
|
11
|
-
if (node.source.value === 'next/image') {
|
|
12
|
-
context.report({
|
|
13
|
-
messageId: 'imageImport',
|
|
14
|
-
node,
|
|
15
|
-
fix: (fixer) => {
|
|
16
|
-
if (node.specifiers.find((s) => s.type === 'ImportSpecifier')) {
|
|
17
|
-
return null;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
return fixer.replaceText(
|
|
21
|
-
node,
|
|
22
|
-
node.specifiers
|
|
23
|
-
.map(
|
|
24
|
-
(specifier) =>
|
|
25
|
-
`import { ${specifier.local.name} } from 'components';`
|
|
26
|
-
)
|
|
27
|
-
.join('\n')
|
|
28
|
-
);
|
|
29
|
-
}
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
};
|
|
34
|
-
},
|
|
35
|
-
meta: {
|
|
36
|
-
messages: {
|
|
37
|
-
imageImport: "Import 'Image' component from 'components'."
|
|
38
|
-
},
|
|
39
|
-
type: 'problem',
|
|
40
|
-
fixable: 'code',
|
|
41
|
-
schema: []
|
|
42
|
-
},
|
|
43
|
-
defaultOptions: []
|
|
44
|
-
});
|
|
1
|
+
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
+
|
|
3
|
+
export default ESLintUtils.RuleCreator.withoutDocs({
|
|
4
|
+
create(context) {
|
|
5
|
+
if (context.getFilename().includes('components/image.tsx')) {
|
|
6
|
+
return {};
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
return {
|
|
10
|
+
ImportDeclaration(node) {
|
|
11
|
+
if (node.source.value === 'next/image') {
|
|
12
|
+
context.report({
|
|
13
|
+
messageId: 'imageImport',
|
|
14
|
+
node,
|
|
15
|
+
fix: (fixer) => {
|
|
16
|
+
if (node.specifiers.find((s) => s.type === 'ImportSpecifier')) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return fixer.replaceText(
|
|
21
|
+
node,
|
|
22
|
+
node.specifiers
|
|
23
|
+
.map(
|
|
24
|
+
(specifier) =>
|
|
25
|
+
`import { ${specifier.local.name} } from 'components';`
|
|
26
|
+
)
|
|
27
|
+
.join('\n')
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
},
|
|
35
|
+
meta: {
|
|
36
|
+
messages: {
|
|
37
|
+
imageImport: "Import 'Image' component from 'components'."
|
|
38
|
+
},
|
|
39
|
+
type: 'problem',
|
|
40
|
+
fixable: 'code',
|
|
41
|
+
schema: []
|
|
42
|
+
},
|
|
43
|
+
defaultOptions: []
|
|
44
|
+
});
|
package/rules/index.ts
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
import clientUrl from './client-url';
|
|
2
|
-
import imageImport from './image-import';
|
|
3
|
-
import linkImport from './link-import';
|
|
4
|
-
import routerImport from './router-import';
|
|
5
|
-
import skipTrailingSlashRedirect from './skip-trailing-slash-redirect';
|
|
6
|
-
import checkLocale from './check-locale';
|
|
7
|
-
import metaDataImport from './meta-data-import';
|
|
8
|
-
import checkMiddlewareOrder from './check-middleware-order';
|
|
9
|
-
import urlsWithoutSlash from './urls-without-slash';
|
|
10
|
-
|
|
11
|
-
const rules = {
|
|
12
|
-
'client-url': clientUrl,
|
|
13
|
-
'image-import': imageImport,
|
|
14
|
-
'link-import': linkImport,
|
|
15
|
-
'router-import': routerImport,
|
|
16
|
-
'skip-trailing-slash-redirect': skipTrailingSlashRedirect,
|
|
17
|
-
'check-locale': checkLocale,
|
|
18
|
-
'meta-data-import': metaDataImport,
|
|
19
|
-
'check-middleware-order': checkMiddlewareOrder,
|
|
20
|
-
'urls-without-slash': urlsWithoutSlash
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
export { rules };
|
|
1
|
+
import clientUrl from './client-url';
|
|
2
|
+
import imageImport from './image-import';
|
|
3
|
+
import linkImport from './link-import';
|
|
4
|
+
import routerImport from './router-import';
|
|
5
|
+
import skipTrailingSlashRedirect from './skip-trailing-slash-redirect';
|
|
6
|
+
import checkLocale from './check-locale';
|
|
7
|
+
import metaDataImport from './meta-data-import';
|
|
8
|
+
import checkMiddlewareOrder from './check-middleware-order';
|
|
9
|
+
import urlsWithoutSlash from './urls-without-slash';
|
|
10
|
+
|
|
11
|
+
const rules = {
|
|
12
|
+
'client-url': clientUrl,
|
|
13
|
+
'image-import': imageImport,
|
|
14
|
+
'link-import': linkImport,
|
|
15
|
+
'router-import': routerImport,
|
|
16
|
+
'skip-trailing-slash-redirect': skipTrailingSlashRedirect,
|
|
17
|
+
'check-locale': checkLocale,
|
|
18
|
+
'meta-data-import': metaDataImport,
|
|
19
|
+
'check-middleware-order': checkMiddlewareOrder,
|
|
20
|
+
'urls-without-slash': urlsWithoutSlash
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export { rules };
|
package/rules/link-import.ts
CHANGED
|
@@ -1,44 +1,44 @@
|
|
|
1
|
-
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
-
|
|
3
|
-
export default ESLintUtils.RuleCreator.withoutDocs({
|
|
4
|
-
create(context) {
|
|
5
|
-
if (context.getFilename().includes('components/link.tsx')) {
|
|
6
|
-
return {};
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
return {
|
|
10
|
-
ImportDeclaration(node) {
|
|
11
|
-
if (node.source.value === 'next/link') {
|
|
12
|
-
context.report({
|
|
13
|
-
messageId: 'linkImport',
|
|
14
|
-
node,
|
|
15
|
-
fix: (fixer) => {
|
|
16
|
-
if (node.specifiers.find((s) => s.type === 'ImportSpecifier')) {
|
|
17
|
-
return null;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
return fixer.replaceText(
|
|
21
|
-
node,
|
|
22
|
-
node.specifiers
|
|
23
|
-
.map(
|
|
24
|
-
(specifier) =>
|
|
25
|
-
`import { ${specifier.local.name} } from 'components';`
|
|
26
|
-
)
|
|
27
|
-
.join('\n')
|
|
28
|
-
);
|
|
29
|
-
}
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
};
|
|
34
|
-
},
|
|
35
|
-
meta: {
|
|
36
|
-
messages: {
|
|
37
|
-
linkImport: "Import 'Link' component from 'components'."
|
|
38
|
-
},
|
|
39
|
-
type: 'problem',
|
|
40
|
-
fixable: 'code',
|
|
41
|
-
schema: []
|
|
42
|
-
},
|
|
43
|
-
defaultOptions: []
|
|
44
|
-
});
|
|
1
|
+
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
+
|
|
3
|
+
export default ESLintUtils.RuleCreator.withoutDocs({
|
|
4
|
+
create(context) {
|
|
5
|
+
if (context.getFilename().includes('components/link.tsx')) {
|
|
6
|
+
return {};
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
return {
|
|
10
|
+
ImportDeclaration(node) {
|
|
11
|
+
if (node.source.value === 'next/link') {
|
|
12
|
+
context.report({
|
|
13
|
+
messageId: 'linkImport',
|
|
14
|
+
node,
|
|
15
|
+
fix: (fixer) => {
|
|
16
|
+
if (node.specifiers.find((s) => s.type === 'ImportSpecifier')) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return fixer.replaceText(
|
|
21
|
+
node,
|
|
22
|
+
node.specifiers
|
|
23
|
+
.map(
|
|
24
|
+
(specifier) =>
|
|
25
|
+
`import { ${specifier.local.name} } from 'components';`
|
|
26
|
+
)
|
|
27
|
+
.join('\n')
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
},
|
|
35
|
+
meta: {
|
|
36
|
+
messages: {
|
|
37
|
+
linkImport: "Import 'Link' component from 'components'."
|
|
38
|
+
},
|
|
39
|
+
type: 'problem',
|
|
40
|
+
fixable: 'code',
|
|
41
|
+
schema: []
|
|
42
|
+
},
|
|
43
|
+
defaultOptions: []
|
|
44
|
+
});
|
|
@@ -1,50 +1,50 @@
|
|
|
1
|
-
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
-
|
|
3
|
-
export default ESLintUtils.RuleCreator.withoutDocs({
|
|
4
|
-
create(context) {
|
|
5
|
-
return {
|
|
6
|
-
ImportDeclaration(node) {
|
|
7
|
-
const targetImportName = 'Metadata';
|
|
8
|
-
|
|
9
|
-
const importSpecifiers = node.specifiers
|
|
10
|
-
.filter((specifier) => specifier.type === 'ImportSpecifier')
|
|
11
|
-
.map((specifier) => specifier.local.name);
|
|
12
|
-
|
|
13
|
-
if (
|
|
14
|
-
importSpecifiers.includes(targetImportName) &&
|
|
15
|
-
node.source.value === 'next'
|
|
16
|
-
) {
|
|
17
|
-
context.report({
|
|
18
|
-
messageId: 'metaDataImport',
|
|
19
|
-
node,
|
|
20
|
-
fix: (fixer) => {
|
|
21
|
-
if (node.specifiers.find((s) => s.type === 'ImportSpecifier')) {
|
|
22
|
-
return null;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return fixer.replaceText(
|
|
26
|
-
node,
|
|
27
|
-
node.specifiers
|
|
28
|
-
.map(
|
|
29
|
-
(specifier) =>
|
|
30
|
-
`import { ${specifier.local.name} } from '@akinon/next/types';`
|
|
31
|
-
)
|
|
32
|
-
.join('\n')
|
|
33
|
-
);
|
|
34
|
-
}
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
};
|
|
39
|
-
},
|
|
40
|
-
meta: {
|
|
41
|
-
messages: {
|
|
42
|
-
metaDataImport:
|
|
43
|
-
"Import '{ Metadata }' component from '@akinon/next/types'."
|
|
44
|
-
},
|
|
45
|
-
type: 'problem',
|
|
46
|
-
fixable: 'code',
|
|
47
|
-
schema: []
|
|
48
|
-
},
|
|
49
|
-
defaultOptions: []
|
|
50
|
-
});
|
|
1
|
+
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
+
|
|
3
|
+
export default ESLintUtils.RuleCreator.withoutDocs({
|
|
4
|
+
create(context) {
|
|
5
|
+
return {
|
|
6
|
+
ImportDeclaration(node) {
|
|
7
|
+
const targetImportName = 'Metadata';
|
|
8
|
+
|
|
9
|
+
const importSpecifiers = node.specifiers
|
|
10
|
+
.filter((specifier) => specifier.type === 'ImportSpecifier')
|
|
11
|
+
.map((specifier) => specifier.local.name);
|
|
12
|
+
|
|
13
|
+
if (
|
|
14
|
+
importSpecifiers.includes(targetImportName) &&
|
|
15
|
+
node.source.value === 'next'
|
|
16
|
+
) {
|
|
17
|
+
context.report({
|
|
18
|
+
messageId: 'metaDataImport',
|
|
19
|
+
node,
|
|
20
|
+
fix: (fixer) => {
|
|
21
|
+
if (node.specifiers.find((s) => s.type === 'ImportSpecifier')) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return fixer.replaceText(
|
|
26
|
+
node,
|
|
27
|
+
node.specifiers
|
|
28
|
+
.map(
|
|
29
|
+
(specifier) =>
|
|
30
|
+
`import { ${specifier.local.name} } from '@akinon/next/types';`
|
|
31
|
+
)
|
|
32
|
+
.join('\n')
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
},
|
|
40
|
+
meta: {
|
|
41
|
+
messages: {
|
|
42
|
+
metaDataImport:
|
|
43
|
+
"Import '{ Metadata }' component from '@akinon/next/types'."
|
|
44
|
+
},
|
|
45
|
+
type: 'problem',
|
|
46
|
+
fixable: 'code',
|
|
47
|
+
schema: []
|
|
48
|
+
},
|
|
49
|
+
defaultOptions: []
|
|
50
|
+
});
|
|
@@ -1,60 +1,60 @@
|
|
|
1
|
-
import { AST_NODE_TYPES } from '@typescript-eslint/types';
|
|
2
|
-
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
3
|
-
|
|
4
|
-
export default ESLintUtils.RuleCreator.withoutDocs({
|
|
5
|
-
create(context) {
|
|
6
|
-
return {
|
|
7
|
-
VariableDeclarator(node) {
|
|
8
|
-
if (node.id.type !== AST_NODE_TYPES.Identifier) {
|
|
9
|
-
return;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
if (node.id.name !== 'nextConfig') {
|
|
13
|
-
return;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
if (node.init?.type !== AST_NODE_TYPES.ObjectExpression) {
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const skipTrailingSlashRedirectNode = node.init.properties.find(
|
|
21
|
-
(property) =>
|
|
22
|
-
'key' in property &&
|
|
23
|
-
property.key.type === AST_NODE_TYPES.Identifier &&
|
|
24
|
-
property.key.name === 'skipTrailingSlashRedirect'
|
|
25
|
-
);
|
|
26
|
-
|
|
27
|
-
if (
|
|
28
|
-
!skipTrailingSlashRedirectNode ||
|
|
29
|
-
skipTrailingSlashRedirectNode.type !== AST_NODE_TYPES.Property
|
|
30
|
-
) {
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
if (
|
|
35
|
-
skipTrailingSlashRedirectNode.value.type === AST_NODE_TYPES.Literal &&
|
|
36
|
-
skipTrailingSlashRedirectNode.value.value === true
|
|
37
|
-
) {
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
context.report({
|
|
42
|
-
node: skipTrailingSlashRedirectNode,
|
|
43
|
-
messageId: 'skipTrailingSlashRedirect',
|
|
44
|
-
fix: (fixer) =>
|
|
45
|
-
fixer.replaceText(skipTrailingSlashRedirectNode.value, 'true')
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
|
-
},
|
|
50
|
-
meta: {
|
|
51
|
-
messages: {
|
|
52
|
-
skipTrailingSlashRedirect:
|
|
53
|
-
'The `skipTrailingSlashRedirect` value must be true.'
|
|
54
|
-
},
|
|
55
|
-
type: 'problem',
|
|
56
|
-
fixable: 'code',
|
|
57
|
-
schema: []
|
|
58
|
-
},
|
|
59
|
-
defaultOptions: []
|
|
60
|
-
});
|
|
1
|
+
import { AST_NODE_TYPES } from '@typescript-eslint/types';
|
|
2
|
+
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
3
|
+
|
|
4
|
+
export default ESLintUtils.RuleCreator.withoutDocs({
|
|
5
|
+
create(context) {
|
|
6
|
+
return {
|
|
7
|
+
VariableDeclarator(node) {
|
|
8
|
+
if (node.id.type !== AST_NODE_TYPES.Identifier) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (node.id.name !== 'nextConfig') {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (node.init?.type !== AST_NODE_TYPES.ObjectExpression) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const skipTrailingSlashRedirectNode = node.init.properties.find(
|
|
21
|
+
(property) =>
|
|
22
|
+
'key' in property &&
|
|
23
|
+
property.key.type === AST_NODE_TYPES.Identifier &&
|
|
24
|
+
property.key.name === 'skipTrailingSlashRedirect'
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
if (
|
|
28
|
+
!skipTrailingSlashRedirectNode ||
|
|
29
|
+
skipTrailingSlashRedirectNode.type !== AST_NODE_TYPES.Property
|
|
30
|
+
) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (
|
|
35
|
+
skipTrailingSlashRedirectNode.value.type === AST_NODE_TYPES.Literal &&
|
|
36
|
+
skipTrailingSlashRedirectNode.value.value === true
|
|
37
|
+
) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
context.report({
|
|
42
|
+
node: skipTrailingSlashRedirectNode,
|
|
43
|
+
messageId: 'skipTrailingSlashRedirect',
|
|
44
|
+
fix: (fixer) =>
|
|
45
|
+
fixer.replaceText(skipTrailingSlashRedirectNode.value, 'true')
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
},
|
|
50
|
+
meta: {
|
|
51
|
+
messages: {
|
|
52
|
+
skipTrailingSlashRedirect:
|
|
53
|
+
'The `skipTrailingSlashRedirect` value must be true.'
|
|
54
|
+
},
|
|
55
|
+
type: 'problem',
|
|
56
|
+
fixable: 'code',
|
|
57
|
+
schema: []
|
|
58
|
+
},
|
|
59
|
+
defaultOptions: []
|
|
60
|
+
});
|