@lark-apaas/fullstack-presets 1.1.2 → 1.1.3-beta.2
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/README.md +58 -1
- package/lib/custom-eslint-rules/index.d.ts +3 -0
- package/lib/custom-eslint-rules/index.js +10 -0
- package/lib/custom-eslint-rules/no-nested-styled-jsx.d.ts +13 -0
- package/lib/custom-eslint-rules/no-nested-styled-jsx.js +73 -0
- package/lib/simple/recommend/eslint/eslint-client.d.ts +9 -0
- package/lib/simple/recommend/eslint/eslint-client.js +29 -1
- package/lib/simple/recommend/eslint/index.d.ts +5 -0
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -1,2 +1,59 @@
|
|
|
1
1
|
# 全栈技术栈 Presets
|
|
2
|
-
独立拆分 simple 目录,属于全栈精简版,目前妙搭场景专用。
|
|
2
|
+
独立拆分 simple 目录,属于全栈精简版,目前妙搭场景专用。
|
|
3
|
+
|
|
4
|
+
## ESLint 自定义规则
|
|
5
|
+
|
|
6
|
+
### `no-nested-styled-jsx`
|
|
7
|
+
|
|
8
|
+
检测嵌套的 styled-jsx 标签,防止编译错误。https://nextjs.org/docs/messages/nested-styled-jsx-tags
|
|
9
|
+
|
|
10
|
+
该规则镜像了 styled-jsx babel 插件中的验证逻辑:
|
|
11
|
+
- [babel.js#L215](https://github.com/vercel/styled-jsx/blob/d7a59379134d73afaeb98177387cd62d54d746be/src/babel.js#L215)
|
|
12
|
+
- [babel.js#L222](https://github.com/vercel/styled-jsx/blob/d7a59379134d73afaeb98177387cd62d54d746be/src/babel.js#L222)
|
|
13
|
+
|
|
14
|
+
#### 使用方式
|
|
15
|
+
|
|
16
|
+
该规则已在 `eslint-client.ts` 配置中默认启用,无需额外配置:
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
import clientConfig from '@lark-apaas/fullstack-presets/recommend/eslint/eslint-client';
|
|
20
|
+
|
|
21
|
+
export default [
|
|
22
|
+
clientConfig,
|
|
23
|
+
// 其他配置...
|
|
24
|
+
];
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
如需单独使用该规则:
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import { customRules } from '@lark-apaas/fullstack-presets/custom-eslint-rules';
|
|
31
|
+
|
|
32
|
+
export default [
|
|
33
|
+
{
|
|
34
|
+
plugins: {
|
|
35
|
+
'@lark-apaas': { rules: customRules },
|
|
36
|
+
},
|
|
37
|
+
rules: {
|
|
38
|
+
'@lark-apaas/no-nested-styled-jsx': 'error',
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
];
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
#### 自定义错误信息
|
|
45
|
+
|
|
46
|
+
可通过配置自定义报错信息:
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
rules: {
|
|
50
|
+
'@lark-apaas/no-nested-styled-jsx': ['error', {
|
|
51
|
+
message: '禁止嵌套 styled-jsx 标签,请将 <style jsx> 移到组件根级别'
|
|
52
|
+
}],
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
#### 参考资料
|
|
57
|
+
|
|
58
|
+
- [Next.js 错误说明](https://nextjs.org/docs/messages/nested-styled-jsx-tags)
|
|
59
|
+
- [styled-jsx GitHub Issue #42](https://github.com/vercel/styled-jsx/issues/42)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.customRules = void 0;
|
|
7
|
+
const no_nested_styled_jsx_1 = __importDefault(require("./no-nested-styled-jsx"));
|
|
8
|
+
exports.customRules = {
|
|
9
|
+
'no-nested-styled-jsx': no_nested_styled_jsx_1.default,
|
|
10
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ESLint rule to detect nested styled-jsx tags.
|
|
3
|
+
*
|
|
4
|
+
* This rule mirrors the validation logic in styled-jsx's babel plugin:
|
|
5
|
+
* @see https://github.com/vercel/styled-jsx/blob/d7a59379134d73afaeb98177387cd62d54d746be/src/babel.js#L215
|
|
6
|
+
* @see https://github.com/vercel/styled-jsx/blob/d7a59379134d73afaeb98177387cd62d54d746be/src/babel.js#L222
|
|
7
|
+
*
|
|
8
|
+
* Test fixture:
|
|
9
|
+
* @see https://github.com/vercel/styled-jsx/blob/d7a59379134d73afaeb98177387cd62d54d746be/test/fixtures/nested-style-tags.js
|
|
10
|
+
*/
|
|
11
|
+
import type { Rule } from 'eslint';
|
|
12
|
+
declare const rule: Rule.RuleModule;
|
|
13
|
+
export default rule;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const DEFAULT_MESSAGE = 'Detected nested styled-jsx tag. Read more: https://nextjs.org/docs/messages/nested-styled-jsx-tags';
|
|
4
|
+
const rule = {
|
|
5
|
+
meta: {
|
|
6
|
+
type: 'problem',
|
|
7
|
+
docs: {
|
|
8
|
+
description: 'Disallow nested styled-jsx tags',
|
|
9
|
+
category: 'Possible Errors',
|
|
10
|
+
recommended: true,
|
|
11
|
+
url: 'https://nextjs.org/docs/messages/nested-styled-jsx-tags',
|
|
12
|
+
},
|
|
13
|
+
schema: [
|
|
14
|
+
{
|
|
15
|
+
type: 'object',
|
|
16
|
+
properties: {
|
|
17
|
+
message: {
|
|
18
|
+
type: 'string',
|
|
19
|
+
description: 'Custom error message',
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
additionalProperties: false,
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
messages: {
|
|
26
|
+
nestedStyleJsx: DEFAULT_MESSAGE,
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
create(context) {
|
|
30
|
+
const options = context.options[0];
|
|
31
|
+
const customMessage = options?.message;
|
|
32
|
+
return {
|
|
33
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
34
|
+
JSXElement(node) {
|
|
35
|
+
const openingElement = node.openingElement;
|
|
36
|
+
if (!openingElement)
|
|
37
|
+
return;
|
|
38
|
+
const elementName = openingElement.name;
|
|
39
|
+
// Check if this is a <style> element with jsx attribute
|
|
40
|
+
if (elementName.type !== 'JSXIdentifier' || elementName.name !== 'style') {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
44
|
+
const hasJsxAttribute = openingElement.attributes.some((attr) => attr.type === 'JSXAttribute' &&
|
|
45
|
+
attr.name?.type === 'JSXIdentifier' &&
|
|
46
|
+
attr.name?.name === 'jsx');
|
|
47
|
+
if (!hasJsxAttribute) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
// Count JSXElement ancestors
|
|
51
|
+
// styled-jsx uses ignoreClosing counter that increments on JSXOpeningElement enter
|
|
52
|
+
// and checks if ignoreClosing > 1 on style tag exit
|
|
53
|
+
// @see https://github.com/vercel/styled-jsx/blob/d7a59379134d73afaeb98177387cd62d54d746be/src/babel.js#L222
|
|
54
|
+
let jsxElementCount = 0;
|
|
55
|
+
let current = node.parent;
|
|
56
|
+
while (current) {
|
|
57
|
+
if (current.type === 'JSXElement') {
|
|
58
|
+
jsxElementCount++;
|
|
59
|
+
}
|
|
60
|
+
current = current.parent;
|
|
61
|
+
}
|
|
62
|
+
// If there's more than 1 JSXElement ancestor, the style tag is nested
|
|
63
|
+
if (jsxElementCount > 1) {
|
|
64
|
+
context.report({
|
|
65
|
+
node,
|
|
66
|
+
...(customMessage ? { message: customMessage } : { messageId: 'nestedStyleJsx' }),
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
exports.default = rule;
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
export declare const looseRestrictSyntaxRules: {
|
|
2
|
+
selector: string;
|
|
3
|
+
message: string;
|
|
4
|
+
}[];
|
|
1
5
|
declare const _default: {
|
|
2
6
|
name: string;
|
|
3
7
|
languageOptions: {
|
|
@@ -11,6 +15,11 @@ declare const _default: {
|
|
|
11
15
|
globals: any;
|
|
12
16
|
};
|
|
13
17
|
plugins: {
|
|
18
|
+
'@lark-apaas'?: {
|
|
19
|
+
rules: {
|
|
20
|
+
'no-nested-styled-jsx': import("eslint").Rule.RuleModule;
|
|
21
|
+
};
|
|
22
|
+
} | undefined;
|
|
14
23
|
'react-hooks': any;
|
|
15
24
|
import: any;
|
|
16
25
|
};
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.looseRestrictSyntaxRules = void 0;
|
|
3
4
|
const globals = require('globals');
|
|
4
5
|
const reactHooks = require('eslint-plugin-react-hooks');
|
|
5
6
|
const tseslint = require('typescript-eslint');
|
|
6
7
|
const importPlugin = require('eslint-plugin-import');
|
|
8
|
+
const custom_eslint_rules_1 = require("../../../custom-eslint-rules");
|
|
7
9
|
// 检查是否启用宽松 lint 模式
|
|
8
10
|
const isLooseMode = process.env.FORCE_FRAMEWORK_LINT_LOOSE_MODE === 'true';
|
|
9
11
|
// 基础语法规则:所有模式都启用(包括 loose 模式)
|
|
@@ -29,6 +31,20 @@ const baseSyntaxRules = [
|
|
|
29
31
|
message: "Please don't use relative paths in <a> tags. Use NavLink from 'react-router-dom' instead.",
|
|
30
32
|
},
|
|
31
33
|
];
|
|
34
|
+
// loose模式特化的规则
|
|
35
|
+
exports.looseRestrictSyntaxRules = [
|
|
36
|
+
// 约束RoutesComponent路由组件 dom规则
|
|
37
|
+
{
|
|
38
|
+
// 箭头函数 block body
|
|
39
|
+
selector: "VariableDeclarator[id.name='RoutesComponent'] > ArrowFunctionExpression > BlockStatement > ReturnStatement[argument.type='JSXElement'][argument.openingElement.name.name!='Routes']",
|
|
40
|
+
message: 'RoutesComponent must return <Routes> directly without any wrapper',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
// 箭头函数 expression body
|
|
44
|
+
selector: "VariableDeclarator[id.name='RoutesComponent'] > ArrowFunctionExpression > JSXElement:not([openingElement.name.name='Routes'])",
|
|
45
|
+
message: 'RoutesComponent must return <Routes> directly without any wrapper',
|
|
46
|
+
},
|
|
47
|
+
];
|
|
32
48
|
// 严格语法规则:仅正常模式启用,loose 模式下不启用
|
|
33
49
|
const strictSyntaxRules = [
|
|
34
50
|
// 限制使用 fetch(推荐使用生成的 API Client)
|
|
@@ -78,9 +94,16 @@ const strictSyntaxRules = [
|
|
|
78
94
|
message: 'Classname "text-accent" would cause visibility issues. Consider using proper semantic color tokens from `client/src/tailwind-theme.css`',
|
|
79
95
|
},
|
|
80
96
|
];
|
|
97
|
+
const looseSpecificPlugins = {
|
|
98
|
+
'@lark-apaas': { rules: custom_eslint_rules_1.customRules }
|
|
99
|
+
};
|
|
100
|
+
const looseSpecificRules = {
|
|
101
|
+
'@lark-apaas/no-nested-styled-jsx': 'error'
|
|
102
|
+
};
|
|
81
103
|
// 宽松模式下覆盖的规则(关闭非关键规则)
|
|
82
104
|
const looseOverrideRules = isLooseMode
|
|
83
105
|
? {
|
|
106
|
+
...looseSpecificRules,
|
|
84
107
|
'@typescript-eslint/no-unsafe-function-type': 'off',
|
|
85
108
|
'@typescript-eslint/no-unused-expressions': 'off',
|
|
86
109
|
'no-useless-escape': 'off', // 允许不必要的转义字符
|
|
@@ -104,6 +127,7 @@ exports.default = {
|
|
|
104
127
|
plugins: {
|
|
105
128
|
'react-hooks': reactHooks,
|
|
106
129
|
import: importPlugin,
|
|
130
|
+
...(isLooseMode ? looseSpecificPlugins : {}),
|
|
107
131
|
},
|
|
108
132
|
settings: {
|
|
109
133
|
'import/resolver': {
|
|
@@ -149,7 +173,11 @@ exports.default = {
|
|
|
149
173
|
],
|
|
150
174
|
},
|
|
151
175
|
],
|
|
152
|
-
'no-restricted-syntax': [
|
|
176
|
+
'no-restricted-syntax': [
|
|
177
|
+
'error',
|
|
178
|
+
...baseSyntaxRules,
|
|
179
|
+
...(isLooseMode ? exports.looseRestrictSyntaxRules : strictSyntaxRules),
|
|
180
|
+
],
|
|
153
181
|
// 宽松模式下覆盖上述规则
|
|
154
182
|
...looseOverrideRules,
|
|
155
183
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lark-apaas/fullstack-presets",
|
|
3
|
-
"version": "1.1.2",
|
|
3
|
+
"version": "1.1.3-beta.2",
|
|
4
4
|
"files": [
|
|
5
5
|
"lib"
|
|
6
6
|
],
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"build": "tsc && npm run copy:json",
|
|
19
19
|
"copy:json": "node scripts/copy-json.js",
|
|
20
20
|
"watch": "tsc --watch",
|
|
21
|
+
"test": "vitest",
|
|
21
22
|
"prepublishOnly": "npm run build"
|
|
22
23
|
},
|
|
23
24
|
"dependencies": {
|
|
@@ -33,8 +34,11 @@
|
|
|
33
34
|
"tailwindcss-animate": "^1.0.7"
|
|
34
35
|
},
|
|
35
36
|
"devDependencies": {
|
|
37
|
+
"@babel/core": "^7.24.0",
|
|
38
|
+
"@babel/preset-react": "^7.24.0",
|
|
36
39
|
"@types/eslint": "^9.6.0",
|
|
37
40
|
"eslint": "^9.35.0",
|
|
41
|
+
"styled-jsx": "^5.1.6",
|
|
38
42
|
"typescript": "^5.9.2",
|
|
39
43
|
"typescript-eslint": "^8.44.0"
|
|
40
44
|
},
|