@shuvi/eslint-plugin-shuvi 1.0.23 → 1.0.25
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/lib/index.js +6 -2
- package/lib/rules/no-head-element.d.ts +1 -0
- package/lib/rules/no-head-element.js +5 -2
- package/lib/rules/no-html-link-for-pages.d.ts +1 -0
- package/lib/rules/no-html-link-for-pages.js +5 -2
- package/lib/rules/no-typos-custom-app.js +67 -0
- package/lib/rules/no-typos-custom-server.d.ts +2 -0
- package/lib/rules/no-typos-custom-server.js +72 -0
- package/lib/rules/no-typos-page.d.ts +2 -0
- package/lib/rules/{no-typos.js → no-typos-page.js} +3 -30
- package/lib/utils/url.d.ts +2 -0
- package/lib/utils/url.js +29 -1
- package/package.json +3 -3
- /package/lib/rules/{no-typos.d.ts → no-typos-custom-app.d.ts} +0 -0
package/lib/index.js
CHANGED
|
@@ -3,15 +3,19 @@ module.exports = {
|
|
|
3
3
|
rules: {
|
|
4
4
|
'no-head-element': require('./rules/no-head-element').default,
|
|
5
5
|
'no-html-link-for-pages': require('./rules/no-html-link-for-pages').default,
|
|
6
|
-
'no-typos': require('./rules/no-typos').default
|
|
6
|
+
'no-typos-page': require('./rules/no-typos-page').default,
|
|
7
|
+
'no-typos-custom-app': require('./rules/no-typos-custom-app').default,
|
|
8
|
+
'no-typos-custom-server': require('./rules/no-typos-custom-server').default
|
|
7
9
|
},
|
|
8
10
|
configs: {
|
|
9
11
|
recommended: {
|
|
10
12
|
rules: {
|
|
11
13
|
// warnings
|
|
12
14
|
'@shuvi/shuvi/no-head-element': 'warn',
|
|
13
|
-
'@shuvi/shuvi/no-typos': 'error',
|
|
14
15
|
// errors
|
|
16
|
+
'@shuvi/shuvi/no-typos-page': 'error',
|
|
17
|
+
'@shuvi/shuvi/no-typos-custom-app': 'error',
|
|
18
|
+
'@shuvi/shuvi/no-typos-custom-server': 'error',
|
|
15
19
|
'@shuvi/shuvi/no-html-link-for-pages': 'error'
|
|
16
20
|
}
|
|
17
21
|
}
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.url = void 0;
|
|
3
4
|
const define_rule_1 = require("../utils/define-rule");
|
|
5
|
+
exports.url = 'https://shuvijs.github.io/shuvijs.org/docs/guides/rules/no-html-link-for-pages';
|
|
4
6
|
exports.default = (0, define_rule_1.defineRule)({
|
|
5
7
|
meta: {
|
|
6
8
|
docs: {
|
|
7
9
|
description: 'Prevent usage of `<head>` element.',
|
|
8
10
|
category: 'HTML',
|
|
9
|
-
recommended: true
|
|
11
|
+
recommended: true,
|
|
12
|
+
url: exports.url
|
|
10
13
|
},
|
|
11
14
|
type: 'problem',
|
|
12
15
|
schema: []
|
|
@@ -24,7 +27,7 @@ exports.default = (0, define_rule_1.defineRule)({
|
|
|
24
27
|
}
|
|
25
28
|
context.report({
|
|
26
29
|
node,
|
|
27
|
-
message: `Do not use \`<head>\` element. Use \`<Head />\` from \`shuvi/runtime\` instead
|
|
30
|
+
message: `Do not use \`<head>\` element. Use \`<Head />\` from \`shuvi/runtime\` instead. See: ${exports.url}`
|
|
28
31
|
});
|
|
29
32
|
}
|
|
30
33
|
};
|
|
@@ -23,6 +23,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
23
23
|
return result;
|
|
24
24
|
};
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.url = void 0;
|
|
26
27
|
const define_rule_1 = require("../utils/define-rule");
|
|
27
28
|
const path = __importStar(require("path"));
|
|
28
29
|
const fs = __importStar(require("fs"));
|
|
@@ -35,12 +36,14 @@ const pagesDirWarning = (0, url_1.execOnce)(pagesDirs => {
|
|
|
35
36
|
// Cache for fs.existsSync lookup.
|
|
36
37
|
// Prevent multiple blocking IO requests that have already been calculated.
|
|
37
38
|
const fsExistsSyncCache = {};
|
|
39
|
+
exports.url = 'https://shuvijs.github.io/shuvijs.org/docs/guides/rules/no-html-link-for-pages';
|
|
38
40
|
exports.default = (0, define_rule_1.defineRule)({
|
|
39
41
|
meta: {
|
|
40
42
|
docs: {
|
|
41
43
|
description: 'Prevent usage of `<a>` elements to navigate to internal Shuvi.js pages.',
|
|
42
44
|
category: 'HTML',
|
|
43
|
-
recommended: true
|
|
45
|
+
recommended: true,
|
|
46
|
+
url: exports.url
|
|
44
47
|
},
|
|
45
48
|
type: 'problem',
|
|
46
49
|
schema: [
|
|
@@ -115,7 +118,7 @@ exports.default = (0, define_rule_1.defineRule)({
|
|
|
115
118
|
if (match) {
|
|
116
119
|
context.report({
|
|
117
120
|
node,
|
|
118
|
-
message: `Do not use an \`<a>\` element to navigate to \`${hrefPath}\`. Use \`<Link />\` from \`shuvi/runtime\` instead
|
|
121
|
+
message: `Do not use an \`<a>\` element to navigate to \`${hrefPath}\`. Use \`<Link />\` from \`shuvi/runtime\` instead. See: ${exports.url}`
|
|
119
122
|
});
|
|
120
123
|
}
|
|
121
124
|
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const define_rule_1 = require("../utils/define-rule");
|
|
4
|
+
const url_1 = require("../utils/url");
|
|
5
|
+
const EXPORT_FUNCTIONS = ['init', 'appContext', 'appComponent', 'dispose'];
|
|
6
|
+
const FILEREG = /src\/app\.(j|t)s?$/;
|
|
7
|
+
exports.default = (0, define_rule_1.defineRule)({
|
|
8
|
+
meta: {
|
|
9
|
+
docs: {
|
|
10
|
+
description: 'Prevent common typos in custom app.',
|
|
11
|
+
recommended: true
|
|
12
|
+
},
|
|
13
|
+
type: 'problem',
|
|
14
|
+
schema: []
|
|
15
|
+
},
|
|
16
|
+
create(context) {
|
|
17
|
+
function checkTypos(node, name) {
|
|
18
|
+
if (EXPORT_FUNCTIONS.includes(name)) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const potentialTypos = EXPORT_FUNCTIONS.map(o => ({
|
|
22
|
+
option: o,
|
|
23
|
+
distance: (0, url_1.minDistance)(o, name)
|
|
24
|
+
}))
|
|
25
|
+
.filter(({ distance }) => distance <= url_1.THRESHOLD && distance > 0)
|
|
26
|
+
.sort((a, b) => a.distance - b.distance);
|
|
27
|
+
if (potentialTypos.length) {
|
|
28
|
+
context.report({
|
|
29
|
+
node,
|
|
30
|
+
message: `${name} may be a typo. Did you mean ${potentialTypos[0].option}?`
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
ExportNamedDeclaration(node) {
|
|
36
|
+
var _a;
|
|
37
|
+
const fileName = context.getFilename();
|
|
38
|
+
if (!fileName || !FILEREG.test(fileName)) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const decl = node.declaration;
|
|
42
|
+
if (!decl) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
switch (decl.type) {
|
|
46
|
+
case 'FunctionDeclaration': {
|
|
47
|
+
checkTypos(node, (_a = decl.id) === null || _a === void 0 ? void 0 : _a.name);
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
case 'VariableDeclaration': {
|
|
51
|
+
decl.declarations.forEach(d => {
|
|
52
|
+
if (d.id.type !== 'Identifier') {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
checkTypos(node, d.id.name);
|
|
56
|
+
});
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
default: {
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
});
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const define_rule_1 = require("../utils/define-rule");
|
|
4
|
+
const url_1 = require("../utils/url");
|
|
5
|
+
const EXPORT_FUNCTIONS = [
|
|
6
|
+
'getPageData',
|
|
7
|
+
'handlePageRequest',
|
|
8
|
+
'modifyHtml',
|
|
9
|
+
'sendHtml'
|
|
10
|
+
];
|
|
11
|
+
const FILEREG = /src\/server\.(j|t)s?$/;
|
|
12
|
+
exports.default = (0, define_rule_1.defineRule)({
|
|
13
|
+
meta: {
|
|
14
|
+
docs: {
|
|
15
|
+
description: 'Prevent common typos in custom server',
|
|
16
|
+
recommended: true
|
|
17
|
+
},
|
|
18
|
+
type: 'problem',
|
|
19
|
+
schema: []
|
|
20
|
+
},
|
|
21
|
+
create(context) {
|
|
22
|
+
function checkTypos(node, name) {
|
|
23
|
+
if (EXPORT_FUNCTIONS.includes(name)) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const potentialTypos = EXPORT_FUNCTIONS.map(o => ({
|
|
27
|
+
option: o,
|
|
28
|
+
distance: (0, url_1.minDistance)(o, name)
|
|
29
|
+
}))
|
|
30
|
+
.filter(({ distance }) => distance <= url_1.THRESHOLD && distance > 0)
|
|
31
|
+
.sort((a, b) => a.distance - b.distance);
|
|
32
|
+
if (potentialTypos.length) {
|
|
33
|
+
context.report({
|
|
34
|
+
node,
|
|
35
|
+
message: `${name} may be a typo. Did you mean ${potentialTypos[0].option}?`
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
ExportNamedDeclaration(node) {
|
|
41
|
+
var _a;
|
|
42
|
+
const fileName = context.getFilename();
|
|
43
|
+
if (!fileName || !FILEREG.test(fileName)) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const decl = node.declaration;
|
|
47
|
+
if (!decl) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
switch (decl.type) {
|
|
51
|
+
case 'FunctionDeclaration': {
|
|
52
|
+
checkTypos(node, (_a = decl.id) === null || _a === void 0 ? void 0 : _a.name);
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
case 'VariableDeclaration': {
|
|
56
|
+
decl.declarations.forEach(d => {
|
|
57
|
+
if (d.id.type !== 'Identifier') {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
checkTypos(node, d.id.name);
|
|
61
|
+
});
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
default: {
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
});
|
|
@@ -1,36 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const define_rule_1 = require("../utils/define-rule");
|
|
4
|
+
const url_1 = require("../utils/url");
|
|
4
5
|
const EXPORT_FUNCTIONS = ['loader'];
|
|
5
6
|
const PAGEREG = /routes\/.*page\.(j|t)sx?$/;
|
|
6
|
-
// 0 is the exact match
|
|
7
|
-
const THRESHOLD = 1;
|
|
8
|
-
// the minimum number of operations required to convert string a to string b.
|
|
9
|
-
function minDistance(a, b) {
|
|
10
|
-
const m = a.length;
|
|
11
|
-
const n = b.length;
|
|
12
|
-
if (m < n) {
|
|
13
|
-
return minDistance(b, a);
|
|
14
|
-
}
|
|
15
|
-
if (n === 0) {
|
|
16
|
-
return m;
|
|
17
|
-
}
|
|
18
|
-
let previousRow = Array.from({ length: n + 1 }, (_, i) => i);
|
|
19
|
-
for (let i = 0; i < m; i++) {
|
|
20
|
-
const s1 = a[i];
|
|
21
|
-
let currentRow = [i + 1];
|
|
22
|
-
for (let j = 0; j < n; j++) {
|
|
23
|
-
const s2 = b[j];
|
|
24
|
-
const insertions = previousRow[j + 1] + 1;
|
|
25
|
-
const deletions = currentRow[j] + 1;
|
|
26
|
-
const substitutions = previousRow[j] + Number(s1 !== s2);
|
|
27
|
-
currentRow.push(Math.min(insertions, deletions, substitutions));
|
|
28
|
-
}
|
|
29
|
-
previousRow = currentRow;
|
|
30
|
-
}
|
|
31
|
-
return previousRow[previousRow.length - 1];
|
|
32
|
-
}
|
|
33
|
-
/* eslint-disable eslint-plugin/require-meta-docs-url */
|
|
34
7
|
exports.default = (0, define_rule_1.defineRule)({
|
|
35
8
|
meta: {
|
|
36
9
|
docs: {
|
|
@@ -47,9 +20,9 @@ exports.default = (0, define_rule_1.defineRule)({
|
|
|
47
20
|
}
|
|
48
21
|
const potentialTypos = EXPORT_FUNCTIONS.map(o => ({
|
|
49
22
|
option: o,
|
|
50
|
-
distance: minDistance(o, name)
|
|
23
|
+
distance: (0, url_1.minDistance)(o, name)
|
|
51
24
|
}))
|
|
52
|
-
.filter(({ distance }) => distance <= THRESHOLD && distance > 0)
|
|
25
|
+
.filter(({ distance }) => distance <= url_1.THRESHOLD && distance > 0)
|
|
53
26
|
.sort((a, b) => a.distance - b.distance);
|
|
54
27
|
if (potentialTypos.length) {
|
|
55
28
|
context.report({
|
package/lib/utils/url.d.ts
CHANGED
|
@@ -12,3 +12,5 @@ export declare function getUrlFromPagesDirectories(urlPrefix: string, directorie
|
|
|
12
12
|
path: string;
|
|
13
13
|
}[];
|
|
14
14
|
export declare function execOnce<TArgs extends any[], TResult extends unknown>(fn: (...args: TArgs) => TResult): (...args: TArgs) => TResult;
|
|
15
|
+
export declare const THRESHOLD = 1;
|
|
16
|
+
export declare function minDistance(a: string, b: string): number;
|
package/lib/utils/url.js
CHANGED
|
@@ -23,7 +23,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
23
23
|
return result;
|
|
24
24
|
};
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.execOnce = exports.getUrlFromPagesDirectories = exports.normalizeURL = void 0;
|
|
26
|
+
exports.minDistance = exports.THRESHOLD = exports.execOnce = exports.getUrlFromPagesDirectories = exports.normalizeURL = void 0;
|
|
27
27
|
const path = __importStar(require("path"));
|
|
28
28
|
const fs = __importStar(require("fs"));
|
|
29
29
|
const helpers_1 = require("@shuvi/platform-shared/node/route/helpers");
|
|
@@ -124,3 +124,31 @@ function execOnce(fn) {
|
|
|
124
124
|
};
|
|
125
125
|
}
|
|
126
126
|
exports.execOnce = execOnce;
|
|
127
|
+
// 0 is the exact match
|
|
128
|
+
exports.THRESHOLD = 1;
|
|
129
|
+
// the minimum number of operations required to convert string a to string b.
|
|
130
|
+
function minDistance(a, b) {
|
|
131
|
+
const m = a.length;
|
|
132
|
+
const n = b.length;
|
|
133
|
+
if (m < n) {
|
|
134
|
+
return minDistance(b, a);
|
|
135
|
+
}
|
|
136
|
+
if (n === 0) {
|
|
137
|
+
return m;
|
|
138
|
+
}
|
|
139
|
+
let previousRow = Array.from({ length: n + 1 }, (_, i) => i);
|
|
140
|
+
for (let i = 0; i < m; i++) {
|
|
141
|
+
const s1 = a[i];
|
|
142
|
+
let currentRow = [i + 1];
|
|
143
|
+
for (let j = 0; j < n; j++) {
|
|
144
|
+
const s2 = b[j];
|
|
145
|
+
const insertions = previousRow[j + 1] + 1;
|
|
146
|
+
const deletions = currentRow[j] + 1;
|
|
147
|
+
const substitutions = previousRow[j] + Number(s1 !== s2);
|
|
148
|
+
currentRow.push(Math.min(insertions, deletions, substitutions));
|
|
149
|
+
}
|
|
150
|
+
previousRow = currentRow;
|
|
151
|
+
}
|
|
152
|
+
return previousRow[previousRow.length - 1];
|
|
153
|
+
}
|
|
154
|
+
exports.minDistance = minDistance;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shuvi/eslint-plugin-shuvi",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.25",
|
|
4
4
|
"description": "ESLint plugin for Shuvi.",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"license": "MIT",
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
"build": "tsc -p tsconfig.json"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@shuvi/router": "1.0.
|
|
22
|
-
"@shuvi/platform-shared": "1.0.
|
|
21
|
+
"@shuvi/router": "1.0.25",
|
|
22
|
+
"@shuvi/platform-shared": "1.0.25"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"@types/eslint": "7.28.0",
|
|
File without changes
|