@af-mobile/eslint-plugin 2.0.0 → 2.0.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/index.js CHANGED
@@ -1,4 +1,4 @@
1
- // AIFlow UI —— eslint-plugin-aiflow 入口(ESLint 9 flat config 兼容)
1
+ // af-mobile UI —— eslint-plugin-af-mobile 入口(ESLint 9 flat config 兼容)
2
2
  // 20 条规则:L1(2) + L2(7) + L3(6) + L3.5(5)
3
3
  // (definePage 消费端 7 条规则已随 definePage 全局单例移除)
4
4
  import noTokenModification from './rules/no-token-modification.js';
@@ -23,7 +23,7 @@ import wcBlockStates from './rules/wc-block-states.js';
23
23
  import wcBlockVariantEnum from './rules/wc-block-variant-enum.js';
24
24
 
25
25
  const plugin = {
26
- meta: { name: 'eslint-plugin-aiflow', version: '2.0.0' },
26
+ meta: { name: 'eslint-plugin-af-mobile', version: '2.0.1' },
27
27
  rules: {
28
28
  // L1(2 条,全部 error;tokens-css-locked 由 CODEOWNERS + 分支保护,非 ESLint 规则)
29
29
  'no-token-modification': noTokenModification,
@@ -57,37 +57,37 @@ const plugin = {
57
57
  // recommended 配置:按设计文档 L4 §3.2-3.4 的 severity 启用
58
58
  Object.assign(plugin.configs, {
59
59
  recommended: {
60
- plugins: { aiflow: plugin },
60
+ plugins: { 'af-mobile': plugin },
61
61
  rules: {
62
62
  // L1 error
63
- 'aiflow/no-token-modification': 'error',
64
- 'aiflow/no-inline-style': 'error',
63
+ 'af-mobile/no-token-modification': 'error',
64
+ 'af-mobile/no-inline-style': 'error',
65
65
  // L2 error
66
66
  // extraComponents: af-data 属 L3.5 Block 层数据源元素(非 L3 注册组件,不在 whitelist-v1.json),
67
67
  // 但 AI 生成的页面代码(createPage/:bind 体系)需要能使用 <af-data>,故在规则级放行
68
- 'aiflow/token-whitelist': ['error', { extraComponents: ['af-data'] }],
69
- 'aiflow/no-recipe-break': 'error',
70
- 'aiflow/no-arbitrary-value': 'error',
71
- 'aiflow/no-tailwind-syntax': 'error',
68
+ 'af-mobile/token-whitelist': ['error', { extraComponents: ['af-data'] }],
69
+ 'af-mobile/no-recipe-break': 'error',
70
+ 'af-mobile/no-arbitrary-value': 'error',
71
+ 'af-mobile/no-tailwind-syntax': 'error',
72
72
  // L2 warn
73
- 'aiflow/no-variant-conflict': 'warn',
74
- 'aiflow/prefer-component': 'warn',
75
- 'aiflow/atomic-duplicate': 'warn',
73
+ 'af-mobile/no-variant-conflict': 'warn',
74
+ 'af-mobile/prefer-component': 'warn',
75
+ 'af-mobile/atomic-duplicate': 'warn',
76
76
  // L3 error
77
- 'aiflow/wc-light-no-style': 'error',
78
- 'aiflow/wc-shadow-use-token': 'error',
79
- 'aiflow/wc-event-naming': 'error',
80
- 'aiflow/wc-aria-required': 'error',
77
+ 'af-mobile/wc-light-no-style': 'error',
78
+ 'af-mobile/wc-shadow-use-token': 'error',
79
+ 'af-mobile/wc-event-naming': 'error',
80
+ 'af-mobile/wc-aria-required': 'error',
81
81
  // L3 warn
82
- 'aiflow/wc-part-naming': 'warn',
83
- 'aiflow/wc-cleanup': 'warn',
82
+ 'af-mobile/wc-part-naming': 'warn',
83
+ 'af-mobile/wc-cleanup': 'warn',
84
84
  // L3.5 error(Block 层约束)
85
- 'aiflow/wc-block-no-internal-ref': 'error',
86
- 'aiflow/wc-block-props-count': 'error',
87
- 'aiflow/wc-block-states': 'error',
88
- // L3.5 warn(引导性/启发式)
89
- 'aiflow/no-register-all': 'warn',
90
- 'aiflow/wc-block-variant-enum': 'warn',
85
+ 'af-mobile/wc-block-no-internal-ref': 'error',
86
+ 'af-mobile/wc-block-props-count': 'error',
87
+ 'af-mobile/wc-block-states': 'error',
88
+ // L3.5 error(按需引入铁律:registerAll 全量注册 = 全局引入,一律禁止)
89
+ 'af-mobile/no-register-all': 'error',
90
+ 'af-mobile/wc-block-variant-enum': 'warn',
91
91
  },
92
92
  },
93
93
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@af-mobile/eslint-plugin",
3
- "version": "2.0.0",
4
- "description": "AIFlow UI ESLint plugin — 27 rules (L1/L2/L3/L3.5) enforcing design-system + Web Component constraints",
3
+ "version": "2.0.2",
4
+ "description": "af-mobile UI ESLint plugin — 27 rules (L1/L2/L3/L3.5) enforcing design-system + Web Component constraints",
5
5
  "type": "module",
6
6
  "main": "./index.js",
7
7
  "module": "./index.js",
@@ -25,7 +25,7 @@
25
25
  "eslint-plugin",
26
26
  "web-components",
27
27
  "design-system",
28
- "aiflow"
28
+ "af-mobile"
29
29
  ],
30
30
  "license": "MIT"
31
31
  }
@@ -1,4 +1,4 @@
1
- // L2-7 aiflow/atomic-duplicate(warn,可自动修)
1
+ // L2-7 af-mobile/atomic-duplicate(warn,可自动修)
2
2
  // 检测:同一 class 属性内出现两个同属性原子(如 p-4 p-2),保留最后一个
3
3
  import { extractAllClassLists } from '../utils/helpers.js';
4
4
 
@@ -34,15 +34,15 @@ export default {
34
34
  messageId: 'duplicate',
35
35
  data: { prop: PROP_LABELS[prefix] || prefix, a: prev.cls, b: cls },
36
36
  fix(fixer) {
37
+ // 删除被覆盖的 class + 其后的空格(保留的类在后面,必有空格可吃)
37
38
  const sourceCode = context.sourceCode || context.getSourceCode();
38
39
  const text = sourceCode.getText(node);
39
40
  const idx = text.indexOf(raw);
40
41
  if (idx < 0) return null;
41
42
  const classIdx = idx + raw.indexOf(prev.cls);
42
43
  const before = text.slice(0, classIdx);
43
- const after = text.slice(classIdx + prev.cls.length);
44
- const trimmedBefore = before.replace(/\s+$/, '');
45
- return fixer.replaceText(node, trimmedBefore + after);
44
+ const after = text.slice(classIdx + prev.cls.length).replace(/^\s+/, '');
45
+ return fixer.replaceText(node, before + after);
46
46
  },
47
47
  });
48
48
  }
@@ -1,10 +1,10 @@
1
- // L2-4 aiflow/no-arbitrary-value(error,部分可自动修)
2
- // 检测:class 含 [xxx] 任意值语法 / 档位越界(如 p-7
1
+ // L2-4 af-mobile/no-arbitrary-value(error,部分可自动修)
2
+ // 检测:class 含 [xxx] 任意值语法 / 档位越界(如 p-9
3
3
  import { extractAllClassLists, ALL_CLASSES } from '../utils/helpers.js';
4
4
 
5
5
  // 合法档位
6
6
  const RANGES = {
7
- 'p': ['0', '1', '2', '3', '4', '5', '6', '8', '10'],
7
+ 'p': ['0', '1', '2', '3', '4', '5', '6', '7', '8', '10'],
8
8
  'm': ['0', '1', '2', '3', '4', '5', '6'],
9
9
  't': ['xs', 'sm', 'md', 'lg', 'xl'],
10
10
  'r': ['s', 'm', 'l', 'f'],
@@ -1,4 +1,4 @@
1
- // L1-2 aiflow/no-inline-style(error,部分可自动修)
1
+ // L1-2 af-mobile/no-inline-style(error,部分可自动修)
2
2
  // 检测:style="" 中设置 forbiddenInlineStyle 列表内的属性
3
3
  // 例外:display/transform/z-index/width/height 等布局属性放行;skeleton class 元素内 width/height 放行
4
4
  // 适用:HTML/JS 文件中所有字符串字面量(含 template literal),覆盖组件 innerHTML 赋值场景
@@ -1,12 +1,14 @@
1
- // L2-2 aiflow/no-recipe-break(error,3 子规则)
2
- // a: .cell + f/fc 原子 → 布局冲突
1
+ // L2-2 af-mobile/no-recipe-break(error,4 子规则)
2
+ // a: .cell + f/fi/fc 原子 → 布局冲突
3
3
  // b: .btn(非 .btn-ghost)+ text-brand/text-danger/text-success → 对比度问题
4
4
  // c: .input + t-sm/t-xs → iOS 焦点缩放
5
+ // d: .btn(非 .btn-ghost)+ bg-card/bg-muted → 背景覆盖破坏 onbrand 对比度
5
6
  import { extractAllClassLists } from '../utils/helpers.js';
6
7
 
7
- const FLEX_ATOMS = new Set(['f', 'fc']);
8
+ const FLEX_ATOMS = new Set(['f', 'fi', 'fc']);
8
9
  const COLORED_TEXT = new Set(['text-brand', 'text-danger', 'text-success']);
9
10
  const SMALL_FONT = new Set(['t-sm', 't-xs']);
11
+ const BROKEN_BG = new Set(['bg-card', 'bg-muted']);
10
12
 
11
13
  export default {
12
14
  meta: {
@@ -17,12 +19,13 @@ export default {
17
19
  cellFlex: ".cell has built-in display:flex, adding 'f'/'fc' may break layout",
18
20
  btnColor: ".btn background uses --c-brand, text color must keep --c-onbrand contrast. Use .btn-ghost instead for colored text",
19
21
  inputFont: ".input font-size must be 16px (--t-md) to prevent iOS focus zoom. Put help text in <label> or .form-err instead",
22
+ btnBg: ".btn background is --c-brand with white text; overriding background with 'bg-card'/'bg-muted' breaks contrast. Use .btn-ghost instead",
20
23
  },
21
24
  },
22
25
  create(context) {
23
26
  function checkClasses(classes, node) {
24
27
  const set = new Set(classes);
25
- // a: .cell + f/fc
28
+ // a: .cell + f/fi/fc
26
29
  if (set.has('cell')) {
27
30
  for (const a of FLEX_ATOMS) {
28
31
  if (set.has(a)) { context.report({ node, messageId: 'cellFlex' }); break; }
@@ -33,6 +36,10 @@ export default {
33
36
  for (const a of COLORED_TEXT) {
34
37
  if (set.has(a)) { context.report({ node, messageId: 'btnColor' }); break; }
35
38
  }
39
+ // d: 背景覆盖同样破坏 onbrand 对比度
40
+ for (const a of BROKEN_BG) {
41
+ if (set.has(a)) { context.report({ node, messageId: 'btnBg' }); break; }
42
+ }
36
43
  }
37
44
  // c: .input + t-sm/t-xs
38
45
  if (set.has('input')) {
@@ -1,5 +1,5 @@
1
- // L3 aiflow/no-register-all(error)
2
- // 检测:registerAll() 调用(已废弃,诱导全量加载,失去 Tree Shaking)
1
+ // L3 af-mobile/no-register-all(error)
2
+ // 检测:registerAll() 调用(已废弃/已移除,诱导全量加载,失去 Tree Shaking)
3
3
  // 正确:register('af-list', 'af-dialog') 显式列名,配合 Tree Shaking 按需打包
4
4
  export default {
5
5
  meta: {
@@ -7,13 +7,15 @@ export default {
7
7
  docs: { description: '禁止 registerAll(),改用 register(...names) 显式列名', fixable: 'ai-rewrite' },
8
8
  schema: [],
9
9
  messages: {
10
- registerAll: "registerAll() 已废弃(诱导全量加载,失去 Tree Shaking),改用 register(...names) 显式列名",
10
+ registerAll: "registerAll() 已废弃/已移除(全量注册 = 全局引入),改用 register(...names) 显式列名",
11
11
  },
12
12
  },
13
13
  create(context) {
14
14
  const filename = context.filename || context.getFilename();
15
- // 非消费端放行:库源码(index.js 导出 registerAll)/单元测试/构建脚本(build.mjs 全量入口)
16
- if (/src[\\/]|test[\\/]|scripts[\\/]/.test(filename)) return {};
15
+ // 非消费端放行:单元测试夹具 / 构建脚本
16
+ // 注意:原正则含 `src[\\/]` 会误伤消费端 src/main.js——消费端恰恰是要约束的对象。
17
+ // 库源码 src/ 不在此 skip 也安全:库源码已移除 registerAll(按需引入铁律)。
18
+ if (/test[\\/]|scripts[\\/]/.test(filename)) return {};
17
19
  return {
18
20
  CallExpression(node) {
19
21
  const callee = node.callee;
@@ -1,4 +1,4 @@
1
- // L2-5 aiflow/no-tailwind-syntax(error)
1
+ // L2-5 af-mobile/no-tailwind-syntax(error)
2
2
  // 检测:class 含 sm:/md:/lg:/xl:/hover:/focus:/active:/dark: 前缀
3
3
  import { extractAllClassLists } from '../utils/helpers.js';
4
4
 
@@ -1,4 +1,4 @@
1
- // L1-1 aiflow/no-token-modification(error)
1
+ // L1-1 af-mobile/no-token-modification(error)
2
2
  // 检测:非 tokens.css / tokens.project.css 文件内,重定义 --(c|s|r|t|lh|fw|shadow|z|ease|dur)-* 变量
3
3
  // 例外:路径匹配 **/tokens.css 或 **/tokens.project.css
4
4
  import postcss from 'postcss';
@@ -1,4 +1,4 @@
1
- // L2-3 aiflow/no-variant-conflict(warn,可自动修)
1
+ // L2-3 af-mobile/no-variant-conflict(warn,可自动修)
2
2
  // 检测:同属性互斥变体对同时出现,自动修:保留最后一个
3
3
  import { extractAllClassLists } from '../utils/helpers.js';
4
4
 
@@ -9,7 +9,13 @@ const CONFLICT_GROUPS = [
9
9
  // 圆角类
10
10
  ['r-s', 'r-m', 'r-l', 'r-f'],
11
11
  // padding 类
12
- ['p-0', 'p-1', 'p-2', 'p-3', 'p-4', 'p-5', 'p-6', 'p-8', 'p-10'],
12
+ ['p-0', 'p-1', 'p-2', 'p-3', 'p-4', 'p-5', 'p-6', 'p-7', 'p-8', 'p-10'],
13
+ // display 类(同为 display 属性)
14
+ ['f', 'fi', 'fc'],
15
+ // 行高类
16
+ ['lh-tight', 'lh-normal'],
17
+ // 背景类
18
+ ['bg-brand', 'bg-card', 'bg-muted'],
13
19
  ];
14
20
 
15
21
  export default {
@@ -39,19 +45,15 @@ export default {
39
45
  messageId: 'conflict',
40
46
  data: { a: r, b: keep },
41
47
  fix(fixer) {
42
- // 删除 class 名(含前导空格)
48
+ // 删除 class 名 + 其后的一个空格(保留的类在后面,必有空格可吃)
43
49
  const sourceCode = context.sourceCode || context.getSourceCode();
44
50
  const text = sourceCode.getText(node);
45
51
  const idx = text.indexOf(raw);
46
52
  if (idx < 0) return null;
47
53
  const classIdx = idx + raw.indexOf(r);
48
- // 删除 " r" 或 "r " 或 "r"
49
54
  const before = text.slice(0, classIdx);
50
- const after = text.slice(classIdx + r.length);
51
- // 如果前面有空格,连空格一起删
52
- const trimmedBefore = before.replace(/\s+$/, '');
53
- const newText = trimmedBefore + after;
54
- return fixer.replaceText(node, newText);
55
+ const after = text.slice(classIdx + r.length).replace(/^\s+/, '');
56
+ return fixer.replaceText(node, before + after);
55
57
  },
56
58
  });
57
59
  }
@@ -1,4 +1,4 @@
1
- // L2-6 aiflow/prefer-component(warn)
1
+ // L2-6 af-mobile/prefer-component(warn)
2
2
  // 检测:(a) .toast class + setTimeout / (b) .sheet class vs <af-action-sheet> / (c) .list class + scroll 监听
3
3
  import { extractAllClassLists } from '../utils/helpers.js';
4
4
 
@@ -1,4 +1,4 @@
1
- // L2-1 aiflow/token-whitelist(error)
1
+ // L2-1 af-mobile/token-whitelist(error)
2
2
  // 检测:class="" 中的 class / 自定义元素 tagName → 与 whitelist + extraClass/extraComponents 对比
3
3
  import { ALL_CLASSES, ALL_COMPONENTS, extractAllClassLists, extractCustomElements } from '../utils/helpers.js';
4
4
 
@@ -15,8 +15,8 @@ export default {
15
15
  additionalProperties: false,
16
16
  }],
17
17
  messages: {
18
- unknownClass: "Class '{{name}}' not in whitelist. Use recipe/atomic or register in 'aiflow/token-whitelist' rule's extraClass",
19
- unknownComponent: "Component '{{name}}' not in whitelist. Register in 'aiflow/token-whitelist' rule's extraComponents",
18
+ unknownClass: "Class '{{name}}' not in whitelist. Use recipe/atomic or register in 'af-mobile/token-whitelist' rule's extraClass",
19
+ unknownComponent: "Component '{{name}}' not in whitelist. Register in 'af-mobile/token-whitelist' rule's extraComponents",
20
20
  },
21
21
  },
22
22
  create(context) {
@@ -1,4 +1,4 @@
1
- // L3-5 aiflow/wc-aria-required(error)
1
+ // L3-5 af-mobile/wc-aria-required(error)
2
2
  // 检测:组件 render 输出的 DOM 缺少必需的 ARIA 角色/属性
3
3
  import { readFileSync } from 'node:fs';
4
4
  import { fileURLToPath } from 'node:url';
@@ -1,4 +1,4 @@
1
- // L3.5 aiflow/wc-block-no-internal-ref(error)
1
+ // L3.5 af-mobile/wc-block-no-internal-ref(error)
2
2
  // 检测:消费端代码(非 src/)禁止穿透 Block 边界访问内部
3
3
  // 反例:querySelector('af-auth-form > div') / blockInstance.shadowRoot.querySelector(...)
4
4
  // 正例:document.querySelector('af-auth-form').setAttribute('loading', 'true')
@@ -18,7 +18,8 @@ export default {
18
18
  if (/src[\\/]|test[\\/]|scripts[\\/]/.test(filename)) return {};
19
19
 
20
20
  // af-* 标签选择器(含子代/后代穿透):af-xxx > 或 af-xxx 空格 后跟其他选择器
21
- const PENETRATE_RE = /af-[a-z]+(?:[>+~]|\s+[^,)\]]+)/;
21
+ // 标签名含连字符(af-auth-form),字符类须含 - 否则匹配不到组合器
22
+ const PENETRATE_RE = /af-[a-z][a-z0-9-]*(?:[>+~]|\s+[^,)\]]+)/;
22
23
 
23
24
  return {
24
25
  CallExpression(node) {
@@ -1,4 +1,4 @@
1
- // L3.5 aiflow/wc-block-props-count(error)
1
+ // L3.5 af-mobile/wc-block-props-count(error)
2
2
  // 检测:Block 类的 AfElement.defineProp 调用数必须在 2-5 之间
3
3
  // 适用:src/blocks/**/*.js
4
4
  export default {
@@ -1,4 +1,4 @@
1
- // L3.5 aiflow/wc-block-states(error)
1
+ // L3.5 af-mobile/wc-block-states(error)
2
2
  // 检测:Block 类的 mounted/render/_render 方法必须包含 loading/error/empty 分支
3
3
  // 五态:idle(默认)/ loading / error / empty / success
4
4
  // 检测策略:源码文本扫描,找 loading / error / empty 三个关键词至少各出现一次(在条件分支中)
@@ -1,4 +1,4 @@
1
- // L3.5 aiflow/wc-block-variant-enum(warn)
1
+ // L3.5 af-mobile/wc-block-variant-enum(warn)
2
2
  // 检测:Block 类的 variant 属性必须在 defineProp 时声明枚举值,或在 onAttributeChange 中校验
3
3
  // 策略:检测 defineProp(proto, 'variant', { ... }) 调用,检查是否有 enum / values / 一行注释枚举
4
4
  // 或:onAttributeChange 内有 variant 的 if/switch 校验
@@ -64,6 +64,15 @@ export default {
64
64
  variantEnumDeclared = true;
65
65
  }
66
66
  },
67
+ // 赋值形式:AfFoo.prototype.onAttributeChange = function (name) { ... }
68
+ AssignmentExpression(node) {
69
+ const left = node.left;
70
+ if (left?.type !== 'MemberExpression' || left.property?.name !== 'onAttributeChange') return;
71
+ const bodyText = sourceCode.getText(node.right);
72
+ if (/variant/.test(bodyText) && /(if|switch)/.test(bodyText)) {
73
+ variantEnumDeclared = true;
74
+ }
75
+ },
67
76
  'Program:exit'() {
68
77
  if (hasVariantProp && !variantEnumDeclared) {
69
78
  context.report({ loc: { line: 1, column: 0 }, messageId: 'noEnum' });
@@ -1,4 +1,4 @@
1
- // L3-6 aiflow/wc-cleanup(warn)
1
+ // L3-6 af-mobile/wc-cleanup(warn)
2
2
  // 检测两类资源泄漏(覆盖 src/components 与 src/blocks):
3
3
  // 1) addEventListener 未通过 this._listen() 登记(传 signal 选项的原生自动解绑除外)
4
4
  // —— 基类 disconnectedCallback 统一解绑 _listeners,绕过登记即泄漏
@@ -1,4 +1,4 @@
1
- // L3-4 aiflow/wc-event-naming(error,可自动修)
1
+ // L3-4 af-mobile/wc-event-naming(error,可自动修)
2
2
  // 检测:emit('xxx') 调用名不匹配 /^af-[a-z0-9]+:[a-z]+$/
3
3
  const EVENT_RE = /^af-[a-z0-9-]+:[a-z]+$/;
4
4
 
@@ -32,13 +32,18 @@ export default {
32
32
  .toLowerCase();
33
33
  // 如果不含冒号,尝试在组件名后插入冒号
34
34
  if (!fixed.includes(':')) {
35
- // 检测 af- 前缀
36
35
  if (fixed.startsWith('af-')) {
37
- // af-xxx 后插入 : (xxx 是第一个单词)
38
- fixed = fixed.replace(/^af-([a-z0-9]+)/, 'af-$1:');
36
+ // af-xxx-yyy af-xxx:yyy(首个 - 起视为动作;动作段的 - 去除,保证 [a-z]+ 格式)
37
+ const rest = fixed.slice(3);
38
+ const dash = rest.indexOf('-');
39
+ if (dash > 0) {
40
+ fixed = `af-${rest.slice(0, dash)}:${rest.slice(dash + 1).replace(/-/g, '')}`;
41
+ } else {
42
+ fixed = 'af-component:' + rest;
43
+ }
39
44
  } else {
40
45
  // 无 af- 前缀,添加 af- 前缀 + 冒号
41
- fixed = 'af-component:' + fixed;
46
+ fixed = 'af-component:' + fixed.replace(/-/g, '');
42
47
  }
43
48
  }
44
49
  context.report({
@@ -1,4 +1,4 @@
1
- // L3-1 aiflow/wc-light-no-style(error)
1
+ // L3-1 af-mobile/wc-light-no-style(error)
2
2
  // 检测:Light 组件(useShadow=false)中 .style.xxx 赋值 / innerHTML 含 <style> 标签 / innerHTML 含 style="..." 属性
3
3
  export default {
4
4
  meta: {
@@ -1,4 +1,4 @@
1
- // L3-3 aiflow/wc-part-naming(warn)
1
+ // L3-3 af-mobile/wc-part-naming(warn)
2
2
  // 检测:part="xxx" 属性名非 kebab-case
3
3
  const KEBAB_RE = /^[a-z]+(-[a-z0-9]+)*$/;
4
4
 
@@ -1,4 +1,4 @@
1
- // L3-2 aiflow/wc-shadow-use-token(error)
1
+ // L3-2 af-mobile/wc-shadow-use-token(error)
2
2
  // 检测:Shadow 组件 CSS 字符串中颜色/间距/字号/圆角硬编码(非 var(--*))
3
3
  // 例外:backdrop 遮罩半透明黑
4
4
  import postcss from 'postcss';
package/utils/helpers.js CHANGED
@@ -1,4 +1,4 @@
1
- // AIFlow UI —— ESLint 规则共享工具
1
+ // af-mobile UI —— ESLint 规则共享工具
2
2
  import { readFileSync } from 'node:fs';
3
3
  import { fileURLToPath } from 'node:url';
4
4
  import { resolve, dirname } from 'node:path';
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": "v1",
3
- "aiflowVersion": "1.0.0",
3
+ "af-mobileVersion": "1.0.0",
4
4
  "classes": {
5
5
  "recipe": [
6
6
  "actions",
@@ -24,8 +24,10 @@
24
24
  "collapse",
25
25
  "collapse-content",
26
26
  "collapse-summary",
27
+ "display",
27
28
  "divider",
28
29
  "empty",
30
+ "eyebrow",
29
31
  "form-err",
30
32
  "form-row",
31
33
  "form-row-h",
@@ -44,6 +46,7 @@
44
46
  "notice-scroll",
45
47
  "notice-text",
46
48
  "page",
49
+ "page-col",
47
50
  "price",
48
51
  "price-del",
49
52
  "progress",
@@ -58,10 +61,13 @@
58
61
  "rate-readonly",
59
62
  "rate-sm",
60
63
  "rate-star",
64
+ "scroll-y",
61
65
  "search-bar-clear",
62
66
  "search-bar-icon",
63
67
  "search-bar-wrap",
64
68
  "search-input",
69
+ "section",
70
+ "section-title",
65
71
  "segmented",
66
72
  "segmented-block",
67
73
  "segmented-item",
@@ -103,15 +109,21 @@
103
109
  "thumb",
104
110
  "title",
105
111
  "toast",
112
+ "toast-error",
113
+ "toast-success",
114
+ "toast-warning",
106
115
  "upload-grid",
107
116
  "upload-trigger"
108
117
  ],
109
118
  "atomic": [
110
119
  "aic",
111
120
  "bg-brand",
121
+ "bg-card",
112
122
  "bg-muted",
123
+ "ellipsis",
113
124
  "f",
114
125
  "fc",
126
+ "fi",
115
127
  "flex-1",
116
128
  "g-0",
117
129
  "g-1",
@@ -121,11 +133,14 @@
121
133
  "jcc",
122
134
  "jce",
123
135
  "jcsb",
136
+ "lh-normal",
137
+ "lh-tight",
124
138
  "m-0",
125
139
  "m-1",
126
140
  "m-2",
127
141
  "m-3",
128
142
  "m-4",
143
+ "m-5",
129
144
  "p-0",
130
145
  "p-1",
131
146
  "p-10",
@@ -134,6 +149,7 @@
134
149
  "p-4",
135
150
  "p-5",
136
151
  "p-6",
152
+ "p-7",
137
153
  "p-8",
138
154
  "r-0",
139
155
  "r-f",
@@ -143,13 +159,16 @@
143
159
  "shadow-lg",
144
160
  "shadow-md",
145
161
  "shadow-sm",
162
+ "shrink-0",
146
163
  "t-b",
147
164
  "t-center",
165
+ "t-display",
148
166
  "t-left",
149
167
  "t-lg",
150
168
  "t-m",
151
169
  "t-md",
152
170
  "t-right",
171
+ "t-semibold",
153
172
  "t-sm",
154
173
  "t-xl",
155
174
  "t-xs",
@@ -200,6 +219,8 @@
200
219
  "--c-bg",
201
220
  "--c-border",
202
221
  "--c-brand",
222
+ "--c-brand-soft",
223
+ "--c-brand-strong",
203
224
  "--c-card",
204
225
  "--c-danger",
205
226
  "--c-muted",
@@ -216,11 +237,17 @@
216
237
  "--fw-bold",
217
238
  "--fw-medium",
218
239
  "--fw-normal",
240
+ "--fw-semibold",
241
+ "--lh-display",
219
242
  "--lh-normal",
220
243
  "--lh-tight",
244
+ "--ls-caption",
245
+ "--ls-display",
221
246
  "--palette-bg",
222
247
  "--palette-border",
223
248
  "--palette-brand",
249
+ "--palette-brand-soft",
250
+ "--palette-brand-strong",
224
251
  "--palette-card",
225
252
  "--palette-color-scheme",
226
253
  "--palette-danger",
@@ -237,15 +264,21 @@
237
264
  "--r-l",
238
265
  "--r-m",
239
266
  "--r-s",
267
+ "--r-xs",
268
+ "--ring-focus",
240
269
  "--s-1",
241
270
  "--s-2",
242
271
  "--s-3",
243
272
  "--s-4",
244
273
  "--s-5",
245
274
  "--s-6",
275
+ "--s-7",
276
+ "--s-8",
277
+ "--shadow-brand",
246
278
  "--shadow-lg",
247
279
  "--shadow-md",
248
280
  "--shadow-sm",
281
+ "--t-display",
249
282
  "--t-lg",
250
283
  "--t-md",
251
284
  "--t-sm",