@lark-apaas/fullstack-presets 1.1.5-alpha.20 → 1.1.5-alpha.21
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/custom-stylelint-rules/hsl-variable.d.ts +41 -0
- package/lib/custom-stylelint-rules/hsl-variable.js +205 -0
- package/lib/recommend/eslint/eslint-client.js +1 -27
- package/lib/recommend/stylelint/index.d.ts +2 -0
- package/lib/recommend/stylelint/index.js +37 -0
- package/lib/simple/recommend/eslint/eslint-client.d.ts +1 -1
- package/lib/simple/recommend/eslint/eslint-client.js +18 -25
- package/lib/simple/recommend/eslint/index.d.ts +1 -1
- package/lib/simple/recommend/stylelint/index.d.ts +2 -0
- package/lib/simple/recommend/stylelint/index.js +37 -0
- package/package.json +6 -2
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 自定义 Stylelint 规则:校验 CSS 声明中非法的 hsl() 写法
|
|
3
|
+
*
|
|
4
|
+
* 遍历所有 CSS 声明(包括自定义属性 --*),当值以 hsl()/hsla() 开头时,
|
|
5
|
+
* 使用 css-tree 的词法器验证其是否符合 CSS color 规范。
|
|
6
|
+
*
|
|
7
|
+
* 注意:只处理纯颜色值声明,不处理复合值(如 box-shadow、background 等)中的 hsl()。
|
|
8
|
+
*
|
|
9
|
+
* 非法示例:
|
|
10
|
+
* --color: hsl(100%, 50%, 26%); // hue 不能是百分比
|
|
11
|
+
* --color: hsl(200 50% 26% 0.5); // alpha 缺少斜杠
|
|
12
|
+
* --color: hsl(200 50%); // 缺少 lightness
|
|
13
|
+
* color: hsl(); // 空参数
|
|
14
|
+
*
|
|
15
|
+
* 合法示例:
|
|
16
|
+
* --color: hsl(200, 50%, 26%); // CSS3 逗号写法
|
|
17
|
+
* --color: hsl(200 50% 26%); // CSS4 空格写法
|
|
18
|
+
* --color: hsl(200deg 50% 26%); // 带单位的 hue
|
|
19
|
+
* --color: hsl(200 50% 26% / 0.5); // CSS4 带 alpha
|
|
20
|
+
*
|
|
21
|
+
* 跳过(不检查)的示例:
|
|
22
|
+
* --shadow: 0px 2px 12px hsl(0 0% 0% / 0.01); // 复合值中的 hsl
|
|
23
|
+
* background: linear-gradient(hsl(...), ...); // 复合值中的 hsl
|
|
24
|
+
* --border: hsl(from hsl(217 80% 55%) h s calc(l + var(--x))); // CSS Color Level 5 相对颜色语法
|
|
25
|
+
* --color: hsl(none 50% 26%); // none 关键字(CSS Color Level 4)
|
|
26
|
+
*
|
|
27
|
+
* var() 通道的处理:
|
|
28
|
+
* 对每个 var() 独立尝试占位符 0 和 0%(hue 位置需要数字,sat/light 位置需要百分比),
|
|
29
|
+
* 穷举所有 2^n 组合,只要存在一种合法结构就认为该写法合法。
|
|
30
|
+
* 这样可以在不误报合法写法的前提下,仍然捕获结构性错误(如缺少参数)。
|
|
31
|
+
*/
|
|
32
|
+
import stylelint from 'stylelint';
|
|
33
|
+
export declare const ruleName = "custom/hsl-valid-value";
|
|
34
|
+
export declare const messages: {
|
|
35
|
+
invalidHsl: (value: string) => string;
|
|
36
|
+
};
|
|
37
|
+
export declare const meta: {
|
|
38
|
+
url: string;
|
|
39
|
+
};
|
|
40
|
+
declare const _default: stylelint.Plugin;
|
|
41
|
+
export default _default;
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.meta = exports.messages = exports.ruleName = void 0;
|
|
40
|
+
/**
|
|
41
|
+
* 自定义 Stylelint 规则:校验 CSS 声明中非法的 hsl() 写法
|
|
42
|
+
*
|
|
43
|
+
* 遍历所有 CSS 声明(包括自定义属性 --*),当值以 hsl()/hsla() 开头时,
|
|
44
|
+
* 使用 css-tree 的词法器验证其是否符合 CSS color 规范。
|
|
45
|
+
*
|
|
46
|
+
* 注意:只处理纯颜色值声明,不处理复合值(如 box-shadow、background 等)中的 hsl()。
|
|
47
|
+
*
|
|
48
|
+
* 非法示例:
|
|
49
|
+
* --color: hsl(100%, 50%, 26%); // hue 不能是百分比
|
|
50
|
+
* --color: hsl(200 50% 26% 0.5); // alpha 缺少斜杠
|
|
51
|
+
* --color: hsl(200 50%); // 缺少 lightness
|
|
52
|
+
* color: hsl(); // 空参数
|
|
53
|
+
*
|
|
54
|
+
* 合法示例:
|
|
55
|
+
* --color: hsl(200, 50%, 26%); // CSS3 逗号写法
|
|
56
|
+
* --color: hsl(200 50% 26%); // CSS4 空格写法
|
|
57
|
+
* --color: hsl(200deg 50% 26%); // 带单位的 hue
|
|
58
|
+
* --color: hsl(200 50% 26% / 0.5); // CSS4 带 alpha
|
|
59
|
+
*
|
|
60
|
+
* 跳过(不检查)的示例:
|
|
61
|
+
* --shadow: 0px 2px 12px hsl(0 0% 0% / 0.01); // 复合值中的 hsl
|
|
62
|
+
* background: linear-gradient(hsl(...), ...); // 复合值中的 hsl
|
|
63
|
+
* --border: hsl(from hsl(217 80% 55%) h s calc(l + var(--x))); // CSS Color Level 5 相对颜色语法
|
|
64
|
+
* --color: hsl(none 50% 26%); // none 关键字(CSS Color Level 4)
|
|
65
|
+
*
|
|
66
|
+
* var() 通道的处理:
|
|
67
|
+
* 对每个 var() 独立尝试占位符 0 和 0%(hue 位置需要数字,sat/light 位置需要百分比),
|
|
68
|
+
* 穷举所有 2^n 组合,只要存在一种合法结构就认为该写法合法。
|
|
69
|
+
* 这样可以在不误报合法写法的前提下,仍然捕获结构性错误(如缺少参数)。
|
|
70
|
+
*/
|
|
71
|
+
const stylelint_1 = __importDefault(require("stylelint"));
|
|
72
|
+
const csstree = __importStar(require("css-tree"));
|
|
73
|
+
exports.ruleName = 'custom/hsl-valid-value';
|
|
74
|
+
const { report, ruleMessages, validateOptions } = stylelint_1.default.utils;
|
|
75
|
+
exports.messages = ruleMessages(exports.ruleName, {
|
|
76
|
+
invalidHsl: (value) => `Invalid hsl() value: "${value}". Refer to https://developer.mozilla.org/docs/Web/CSS/color_value/hsl`,
|
|
77
|
+
});
|
|
78
|
+
exports.meta = {
|
|
79
|
+
url: 'https://github.com/csstree/stylelint-validator',
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* 提取字符串中所有顶层 var() 的位置和长度,使用括号计数支持任意深度嵌套。
|
|
83
|
+
* 例如 var(--a, var(--b, var(--c, 0))) 会被作为一个整体提取。
|
|
84
|
+
*/
|
|
85
|
+
function extractVarRanges(value) {
|
|
86
|
+
const ranges = [];
|
|
87
|
+
const varStart = /var\s*\(/gi;
|
|
88
|
+
let m;
|
|
89
|
+
while ((m = varStart.exec(value)) !== null) {
|
|
90
|
+
let depth = 1;
|
|
91
|
+
let j = m.index + m[0].length;
|
|
92
|
+
while (j < value.length && depth > 0) {
|
|
93
|
+
if (value[j] === '(')
|
|
94
|
+
depth++;
|
|
95
|
+
else if (value[j] === ')')
|
|
96
|
+
depth--;
|
|
97
|
+
j++;
|
|
98
|
+
}
|
|
99
|
+
ranges.push({ index: m.index, length: j - m.index });
|
|
100
|
+
varStart.lastIndex = j; // 跳过已处理的 var(),避免重复匹配内层
|
|
101
|
+
}
|
|
102
|
+
return ranges;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* 对 hsl() 值中的每个 var() 分别尝试占位符 "0" 和 "0%",
|
|
106
|
+
* 穷举 2^n 种组合,只要任意一种通过 css-tree 词法验证就返回 true。
|
|
107
|
+
*
|
|
108
|
+
* 背景:css-tree 不能解析 var(),但不同通道对占位符类型要求不同:
|
|
109
|
+
* - hue 位置需要数字(0),saturation/lightness 位置需要百分比(0%)。
|
|
110
|
+
* 穷举组合避免了需要感知通道位置的复杂解析。
|
|
111
|
+
*/
|
|
112
|
+
function isHslValidWithVarPlaceholders(value, ranges) {
|
|
113
|
+
const isValidCssColor = (v) => {
|
|
114
|
+
try {
|
|
115
|
+
const ast = csstree.parse(v, { context: 'value' });
|
|
116
|
+
return !csstree.lexer.matchType('color', ast).error;
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
const n = ranges.length;
|
|
123
|
+
const total = 1 << n; // 2^n 种组合
|
|
124
|
+
for (let mask = 0; mask < total; mask++) {
|
|
125
|
+
let replaced = value;
|
|
126
|
+
let offset = 0;
|
|
127
|
+
for (let i = 0; i < n; i++) {
|
|
128
|
+
const placeholder = (mask >> i) & 1 ? '0%' : '0';
|
|
129
|
+
const m = ranges[i];
|
|
130
|
+
const start = m.index + offset;
|
|
131
|
+
const end = start + m.length;
|
|
132
|
+
replaced = replaced.slice(0, start) + placeholder + replaced.slice(end);
|
|
133
|
+
offset += placeholder.length - m.length;
|
|
134
|
+
}
|
|
135
|
+
if (isValidCssColor(replaced))
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
return false; // 所有组合均非法,视为结构性错误
|
|
139
|
+
}
|
|
140
|
+
const rule = primary => {
|
|
141
|
+
return (root, result) => {
|
|
142
|
+
const validOptions = validateOptions(result, exports.ruleName, {
|
|
143
|
+
actual: primary,
|
|
144
|
+
possible: [true],
|
|
145
|
+
});
|
|
146
|
+
if (!validOptions || !primary)
|
|
147
|
+
return;
|
|
148
|
+
root.walkDecls(decl => {
|
|
149
|
+
const value = decl.value.trim();
|
|
150
|
+
// 只处理纯 hsl/hsla 颜色值,跳过复合值(如 box-shadow)
|
|
151
|
+
if (!/^hsla?\(/i.test(value))
|
|
152
|
+
return;
|
|
153
|
+
// 跳过 CSS Color Level 5 相对颜色语法:hsl(from <color> h s l)
|
|
154
|
+
// css-tree 词法器尚不支持该语法,直接跳过避免误报
|
|
155
|
+
if (/^hsla?\(\s*from\s+/i.test(value))
|
|
156
|
+
return;
|
|
157
|
+
// none 关键字只在现代空格语法中合法(CSS Color Level 4),逗号语法不支持
|
|
158
|
+
// 通过是否含逗号区分语法:现代语法跳过,逗号语法交由 css-tree 验证
|
|
159
|
+
const isLegacySyntax = /^hsla?\s*\([^)]*,/i.test(value);
|
|
160
|
+
if (!isLegacySyntax && /\bnone\b/i.test(value))
|
|
161
|
+
return;
|
|
162
|
+
// 包含 var() 时,穷举占位符组合进行结构性校验
|
|
163
|
+
const varRanges = extractVarRanges(value);
|
|
164
|
+
if (varRanges.length > 0) {
|
|
165
|
+
if (!isHslValidWithVarPlaceholders(value, varRanges)) {
|
|
166
|
+
report({
|
|
167
|
+
message: exports.messages.invalidHsl(value),
|
|
168
|
+
node: decl,
|
|
169
|
+
result,
|
|
170
|
+
ruleName: exports.ruleName,
|
|
171
|
+
word: value,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
try {
|
|
177
|
+
const ast = csstree.parse(value, { context: 'value' });
|
|
178
|
+
const matchResult = csstree.lexer.matchType('color', ast);
|
|
179
|
+
if (matchResult.error) {
|
|
180
|
+
report({
|
|
181
|
+
message: exports.messages.invalidHsl(value),
|
|
182
|
+
node: decl,
|
|
183
|
+
result,
|
|
184
|
+
ruleName: exports.ruleName,
|
|
185
|
+
word: value,
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
// css-tree parse 失败也视为非法值
|
|
191
|
+
report({
|
|
192
|
+
message: exports.messages.invalidHsl(value),
|
|
193
|
+
node: decl,
|
|
194
|
+
result,
|
|
195
|
+
ruleName: exports.ruleName,
|
|
196
|
+
word: value,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
};
|
|
201
|
+
};
|
|
202
|
+
rule.ruleName = exports.ruleName;
|
|
203
|
+
rule.messages = exports.messages;
|
|
204
|
+
rule.meta = exports.meta;
|
|
205
|
+
exports.default = stylelint_1.default.createPlugin(exports.ruleName, rule);
|
|
@@ -59,7 +59,7 @@ exports.default = {
|
|
|
59
59
|
'no-case-declarations': 'off', // 允许在 case 块中使用词法声明
|
|
60
60
|
'no-empty': 'off', // 允许空的 catch 块
|
|
61
61
|
// Import 规则
|
|
62
|
-
'import/no-unresolved': 'error', //
|
|
62
|
+
'import/no-unresolved': ['error', { ignore: ['\\?raw$'] }], // 检查导入路径是否存在,忽略 ?raw 查询参数
|
|
63
63
|
// 其他规则
|
|
64
64
|
'no-constant-binary-expression': 'off', // 不强制使用常量二进制表达式
|
|
65
65
|
// React Refresh 相关 - 开发时不影响渲染的检测
|
|
@@ -119,32 +119,6 @@ exports.default = {
|
|
|
119
119
|
selector: "JSXOpeningElement[name.name='a']:has(JSXAttribute[name.name='href'][value.value=/^(?!https?:|\\u002F\\u002F|mailto:|tel:|#).+/])",
|
|
120
120
|
message: "Please don't use relative paths in <a> tags. Use NavLink from 'react-router-dom' instead.",
|
|
121
121
|
},
|
|
122
|
-
// 禁止 variant 为 outline|link|ghost 的 Button 使用 text-white
|
|
123
|
-
// {
|
|
124
|
-
// selector:
|
|
125
|
-
// 'JSXElement[openingElement.name.name="Button"]' +
|
|
126
|
-
// ':has(JSXAttribute[name.name="variant"][value.value=/^(outline|link|ghost)$/])' +
|
|
127
|
-
// ':has(JSXAttribute[name.name="className"][value.value=/text-white/])',
|
|
128
|
-
// message:
|
|
129
|
-
// 'Button with variant="outline|link|ghost" should not use "text-white" className. This causes visibility issues. Consider using proper semantic color tokens from `client/src/tailwind-theme.css`',
|
|
130
|
-
// },
|
|
131
|
-
// // 禁止在 Button 上组合 text-primary-foreground 与 bg-background 并用的情况
|
|
132
|
-
// {
|
|
133
|
-
// selector:
|
|
134
|
-
// 'JSXElement[openingElement.name.name="Button"]' +
|
|
135
|
-
// ':has(JSXAttribute[name.name="className"][value.value=/text-primary-foreground/])' +
|
|
136
|
-
// ':has(JSXAttribute[name.name="className"][value.value=/bg-background/])',
|
|
137
|
-
// message:
|
|
138
|
-
// 'Button should not use "text-primary-foreground" and "bg-background" className. This causes visibility issues. Consider using proper semantic color tokens from `client/src/tailwind-theme.css`',
|
|
139
|
-
// },
|
|
140
|
-
// // 禁止使用 text-accent
|
|
141
|
-
// {
|
|
142
|
-
// selector:
|
|
143
|
-
// 'JSXAttribute[name.name="className"][value.value=/(^|\\s)text-accent(\\s|$)/]',
|
|
144
|
-
// message:
|
|
145
|
-
// 'Classname "text-accent" would cause visibility issues. Consider using proper semantic color tokens from `client/src/tailwind-theme.css`',
|
|
146
|
-
// },
|
|
147
|
-
// 禁止在 Tailwind 任意值语法中使用包含空格的 hsl/rgb 值
|
|
148
122
|
{
|
|
149
123
|
selector: 'JSXAttribute[name.name="className"][value.value=/\\[hsl\\([^\\]]*\\s[^\\]]*\\)/]',
|
|
150
124
|
message: 'Tailwind 4 arbitrary values cannot contain spaces. Replace spaces with underscores in hsl() values. Example: from-[hsl(215_60%_18%)] instead of from-[hsl(215 60% 18%)]',
|
|
@@ -1,8 +1,45 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
36
|
exports.stylelintPresets = void 0;
|
|
37
|
+
const hsl_variable_1 = __importStar(require("../../custom-stylelint-rules/hsl-variable"));
|
|
38
|
+
const isLooseMode = process.env.FORCE_FRAMEWORK_LINT_LOOSE_MODE === 'true';
|
|
4
39
|
exports.stylelintPresets = {
|
|
40
|
+
plugins: [hsl_variable_1.default],
|
|
5
41
|
rules: {
|
|
6
42
|
'declaration-block-no-duplicate-custom-properties': true,
|
|
43
|
+
[hsl_variable_1.ruleName]: isLooseMode || null,
|
|
7
44
|
},
|
|
8
45
|
};
|
|
@@ -28,11 +28,6 @@ const projectFlags = getProjectFlags();
|
|
|
28
28
|
const supportScrollReveal = projectFlags.supportScrollReveal === true;
|
|
29
29
|
// 基础语法规则:所有模式都启用(包括 loose 模式)
|
|
30
30
|
const baseSyntaxRules = [
|
|
31
|
-
// SelectItem组件的value属性值不能为空字符串
|
|
32
|
-
{
|
|
33
|
-
message: 'The `value` attribute of the `SelectItem` component cannot be an empty string.',
|
|
34
|
-
selector: 'JSXOpeningElement[name.name="SelectItem"]:has(JSXAttribute[name.name="value"][value.value=""]), JSXOpeningElement[name.name="SelectItem"]:has(JSXAttribute[name.name="value"][value.expression.value=""])',
|
|
35
|
-
},
|
|
36
31
|
// 禁用`window.location.href`赋值使用,可以读取
|
|
37
32
|
{
|
|
38
33
|
selector: 'AssignmentExpression[left.object.object.name="window"][left.object.property.name="location"][left.property.name="href"]',
|
|
@@ -71,6 +66,23 @@ exports.looseRestrictSyntaxRules = [
|
|
|
71
66
|
selector: 'JSXAttribute[name.name="className"][value.value=/\\[rgb\\([^\\]]*\\s[^\\]]*\\)/]',
|
|
72
67
|
message: 'Tailwind 4 arbitrary values cannot contain spaces. Replace spaces with underscores in rgb() values. Example: bg-[rgb(255_255_255)] instead of bg-[rgb(255 255 255)]',
|
|
73
68
|
},
|
|
69
|
+
// 阻止模型引用 @shared/static 与 shared/static 的字符串
|
|
70
|
+
{
|
|
71
|
+
selector: 'Literal[value=/^@shared\\u002Fstatic/]:not(ImportDeclaration > Literal):not(ExportAllDeclaration > Literal):not(ExportNamedDeclaration > Literal):not(ImportExpression > Literal)',
|
|
72
|
+
message: "Do not use '@shared/static/*' or 'shared/static/*' as a raw string (e.g. in fetch, image src, etc.). Shared static assets must be accessed via import statements.",
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
selector: 'TemplateLiteral[expressions.length=0] > TemplateElement[value.raw=/^@shared\\u002Fstatic/]',
|
|
76
|
+
message: "Do not use '@shared/static/*' or 'shared/static/*' as a raw string (e.g. in fetch, image src, etc.). Shared static assets must be accessed via import statements.",
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
selector: 'Literal[value=/^shared\\u002Fstatic/]:not(ImportDeclaration > Literal):not(ExportAllDeclaration > Literal):not(ExportNamedDeclaration > Literal):not(ImportExpression > Literal)',
|
|
80
|
+
message: "Do not use '@shared/static/*' or 'shared/static/*' as a raw string (e.g. in fetch, image src, etc.). Shared static assets must be accessed via import statements.",
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
selector: 'TemplateLiteral[expressions.length=0] > TemplateElement[value.raw=/^shared\\u002Fstatic/]',
|
|
84
|
+
message: "Do not use '@shared/static/*' or 'shared/static/*' as a raw string (e.g. in fetch, image src, etc.). Shared static assets must be accessed via import statements.",
|
|
85
|
+
},
|
|
74
86
|
];
|
|
75
87
|
// 严格语法规则:仅正常模式启用,loose 模式下不启用
|
|
76
88
|
const strictSyntaxRules = [
|
|
@@ -101,25 +113,6 @@ const strictSyntaxRules = [
|
|
|
101
113
|
message: "Please don't use confirm. It may conflict with window.confirm BOM method, use `Dialog` component instead for better user experience and consistency",
|
|
102
114
|
selector: "CallExpression[callee.name='confirm']",
|
|
103
115
|
},
|
|
104
|
-
// 禁止 variant 为 outline|link|ghost 的 Button 使用 text-white
|
|
105
|
-
{
|
|
106
|
-
selector: 'JSXElement[openingElement.name.name="Button"]' +
|
|
107
|
-
':has(JSXAttribute[name.name="variant"][value.value=/^(outline|link|ghost)$/])' +
|
|
108
|
-
':has(JSXAttribute[name.name="className"][value.value=/text-white/])',
|
|
109
|
-
message: 'Button with variant="outline|link|ghost" should not use "text-white" className. This causes visibility issues. Consider using proper semantic color tokens from `client/src/tailwind-theme.css`',
|
|
110
|
-
},
|
|
111
|
-
// 禁止在 Button 上组合 text-primary-foreground 与 bg-background 并用的情况
|
|
112
|
-
{
|
|
113
|
-
selector: 'JSXElement[openingElement.name.name="Button"]' +
|
|
114
|
-
':has(JSXAttribute[name.name="className"][value.value=/text-primary-foreground/])' +
|
|
115
|
-
':has(JSXAttribute[name.name="className"][value.value=/bg-background/])',
|
|
116
|
-
message: 'Button should not use "text-primary-foreground" and "bg-background" className. This causes visibility issues. Consider using proper semantic color tokens from `client/src/tailwind-theme.css`',
|
|
117
|
-
},
|
|
118
|
-
// 禁止使用 text-accent
|
|
119
|
-
{
|
|
120
|
-
selector: 'JSXAttribute[name.name="className"][value.value=/(^|\\s)text-accent(\\s|$)/]',
|
|
121
|
-
message: 'Classname "text-accent" would cause visibility issues. Consider using proper semantic color tokens from `client/src/tailwind-theme.css`',
|
|
122
|
-
},
|
|
123
116
|
];
|
|
124
117
|
const looseSpecificPlugins = {
|
|
125
118
|
'@lark-apaas': { rules: custom_eslint_rules_1.customRules },
|
|
@@ -193,7 +186,7 @@ const baseConfig = {
|
|
|
193
186
|
'no-useless-escape': 'off',
|
|
194
187
|
'no-case-declarations': 'off',
|
|
195
188
|
// Import 规则
|
|
196
|
-
'import/no-unresolved': 'error', //
|
|
189
|
+
'import/no-unresolved': ['error', { ignore: ['\\?raw$'] }], // 检查导入路径是否存在,忽略 ?raw 查询参数
|
|
197
190
|
// 其他规则
|
|
198
191
|
'no-constant-binary-expression': 'off', // 不强制使用常量二进制表达式
|
|
199
192
|
// React Refresh 相关 - 开发时不影响渲染的检测
|
|
@@ -1,8 +1,45 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
36
|
exports.stylelintPresets = void 0;
|
|
37
|
+
const hsl_variable_1 = __importStar(require("../../../custom-stylelint-rules/hsl-variable"));
|
|
38
|
+
const isLooseMode = process.env.FORCE_FRAMEWORK_LINT_LOOSE_MODE === 'true';
|
|
4
39
|
exports.stylelintPresets = {
|
|
40
|
+
plugins: [hsl_variable_1.default],
|
|
5
41
|
rules: {
|
|
6
42
|
'declaration-block-no-duplicate-custom-properties': true,
|
|
43
|
+
[hsl_variable_1.ruleName]: isLooseMode || null,
|
|
7
44
|
},
|
|
8
45
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lark-apaas/fullstack-presets",
|
|
3
|
-
"version": "1.1.5-alpha.
|
|
3
|
+
"version": "1.1.5-alpha.21",
|
|
4
4
|
"files": [
|
|
5
5
|
"lib"
|
|
6
6
|
],
|
|
@@ -26,21 +26,25 @@
|
|
|
26
26
|
"@eslint/js": "^9.35.0",
|
|
27
27
|
"@types/node": "^22.19.1",
|
|
28
28
|
"@types/styled-jsx": "^2.2.9",
|
|
29
|
+
"css-tree": "^3.1.0",
|
|
29
30
|
"eslint-import-resolver-alias": "^1.1.2",
|
|
30
31
|
"eslint-plugin-import": "^2.32.0",
|
|
31
32
|
"eslint-plugin-react": "^7.37.5",
|
|
32
33
|
"eslint-plugin-react-hooks": "^5.2.0",
|
|
33
34
|
"globals": "^16.4.0",
|
|
35
|
+
"stylelint": "^17.3.0",
|
|
34
36
|
"tailwindcss-animate": "^1.0.7"
|
|
35
37
|
},
|
|
36
38
|
"devDependencies": {
|
|
37
39
|
"@babel/core": "^7.24.0",
|
|
38
40
|
"@babel/preset-react": "^7.24.0",
|
|
41
|
+
"@types/css-tree": "^2.3.11",
|
|
39
42
|
"@types/eslint": "^9.6.0",
|
|
40
43
|
"eslint": "^9.35.0",
|
|
41
44
|
"styled-jsx": "^5.1.6",
|
|
42
45
|
"typescript": "^5.9.2",
|
|
43
|
-
"typescript-eslint": "^8.44.0"
|
|
46
|
+
"typescript-eslint": "^8.44.0",
|
|
47
|
+
"vitest": "^2.0.0"
|
|
44
48
|
},
|
|
45
49
|
"peerDependencies": {
|
|
46
50
|
"eslint": "^9.0.0",
|