npm package: @qiun/eslint-plugin-ucharts
Description: ESLint plugin enforcing Safe TS Subset v4.0 rules for uCharts cross-platform compatibility (Swift/Kotlin/ArkTS/UTS)
Author: 秋云
Latest version: 2.0.0

# eslint-plugin-ucharts

[![npm version](https://img.shields.io/npm/v/@qiun/eslint-plugin-ucharts.svg)](https://www.npmjs.com/package/@qiun/eslint-plugin-ucharts)
[![license](https://img.shields.io/npm/l/@qiun/eslint-plugin-ucharts.svg)](https://github.com/qiun/ucharts/blob/main/LICENSE)

> ESLint plugin enforcing **Safe TS Subset v4.0** rules for uCharts cross-platform compatibility (Swift / Kotlin / ArkTS / UTS)

## Background

uCharts v4 的 `core/` 目录代码会被自动转译为 **Swift(iOS)**、**Kotlin(Android)**、**ArkTS(HarmonyOS)**、**UTS(uni-app x)** 原生代码。为了确保转译成功，`core/` 中的 TypeScript 代码必须遵守 **Safe TS Subset v4.0** 规范。

本插件提供 **18 条 ESLint 规则**（v2.0），在编码阶段自动检测违反 Safe TS Subset v4.0 的语法，避免转译时出错。

> 📖 完整规范文档：[Safe TS Subset v4.0](../docs/safe-ts-subset-v4.0.md)

## Installation

```bash
npm install -D @qiun/eslint-plugin-ucharts
```

## Usage

### Quick Start: Recommended Config

```javascript
// eslint.config.mjs (Flat Config)
import uchartsPlugin from '@qiun/eslint-plugin-ucharts';

export default [
  {
    plugins: {
      '@qiun/ucharts': uchartsPlugin
    },
    rules: {
      '@qiun/ucharts/recommended': 'error'
    }
  }
];
```

### Strict Mode (All Rules as Error)

```javascript
{
  extends: ['plugin:@qiun/ucharts/strict']
}
```

### v4-Production (Production-Ready)

最严格的配置，适用于生产环境发布前最终检查：

```javascript
{
  extends: ['plugin:@qiun/ucharts/v4-production']
}
```

### Manual Configuration

```javascript
{
  plugins: ['@qiun/ucharts'],
  rules: {
    '@qiun/ucharts/no-destructuring': 'error',
    '@qiun/ucharts/no-object-spread': 'error',
    '@qiun/ucharts/no-typeof-runtime-check': 'error',    // 🆕 v2.0
    '@qiun/ucharts/no-object-as-map': 'error',            // 🆕 v2.0
    // ... see Rules below
  }
}
```

## Rules (v2.0)

### F-Level: Forbidden (Compile-Blocking) — 14 rules

| Rule | ID | Default | Description |
|------|----|---------|-------------|
| `no-destructuring` | F-03~F-05 | error | 禁止解构赋值/解构参数 |
| `no-object-spread` | F-06 | error | 禁止对象展开 `{...obj}` |
| `no-dynamic-property-access` | F-08~F-09 | error | 禁止动态属性访问 `obj[key]` |
| `no-index-signature` | F-07 | error | 禁止 Index Signature `[key: string]: T` |
| `no-for-in` | F-17 | error | 禁止 `for...in` 循环 |
| `no-with-statement` | F-18 | error | 禁止 `with` 语句 |
| `no-nested-function` | F-21 | error | 禁止嵌套函数声明 |
| `require-null-over-undefined` | M-01 | error | 要求用 `null` 替代 `undefined` |
| `no-web-api-direct` | M-07~M-12 | error | 禁止直接调用 Web API |
| `no-weakmap-proxy` | M-04~M-05 | warn | 禁止 WeakMap / Proxy |
| `no-typeof-runtime-check` | **F-29** | error | 禁止 typeof 运行时类型检查 🆕 |
| `no-object-as-map` | **F-30** | error | 禁止对象作为 Map 使用 🆕 |
| `require-explicit-return-type` | **F-31** | warn | 要求显式返回类型注解 🆕 |
| `no-double-type-cast` | **F-32** | error | 禁止双重类型转换 `as Any as T` 🆕 |

### M-Level: Medium (Replace with Alternative) — 1 rule

| Rule | ID | Default | Description |
|------|----|---------|-------------|
| `no-js-builtin-api` | **M-17, M-20~M-22** | error | 禁止直接调用 JS 内置 API (Object/Array/String/Math) 🆕 |

### C-Level: Code Style (Best Practices) — 3 rules

| Rule | ID | Default | Description |
|------|----|---------|-------------|
| `prefer-template-literal` | C-04, R-14 | warn | 优先使用模板字符串拼接 🆕 |
| `no-nested-ternary` | F-28, C-10 | error | 禁止嵌套三元表达式 🆕 |
| `class-member-order` | R-12, C-09 | warn | 类成员遵循标准顺序 (Swift/Kotlin) 🆕 |

## Rule Details

### F-03~F-05: no-destructuring

检测所有形式的解构：

```typescript
const { a, b } = obj;        // ❌ Object destructuring
const [x, y] = arr;          // ❌ Array destructuring
function foo({ name }) {}    // ❌ Parameter destructuring
```

**Fix:** 使用显式属性访问：
```typescript
const a = obj.a;
const b = obj.b;
const x = arr[0];
function foo(param) { const name = param.name; }
```

---

### F-06: no-object-spread

```typescript
const merged = { ...obj1, ...obj2 };   // ❌
```

**Fix:** 使用 merge 工具函数：
```typescript
const merged = merge(obj1, obj2);
```

---

### F-07: no-index-signature

```typescript
interface Dict {
  [key: string]: any;    // ❌ Index Signature
}
```

**Fix:** 使用显式字段或 Map：
```typescript
interface Dict {
  name: string;
  value: number;
}
```

---

### F-08~F-09: no-dynamic-property-access

```typescript
const key = 'name';
obj[key] = value;       // ❌ dynamic key
```

**Fix:** 使用 Map 存储动态键值对：
```typescript
const map = new Map<string, any>();
map.set(key, value);
```

---

### F-21: no-nested-function

```typescript
function outer() {
  function inner() {}     // ❌ nested function declaration
}
```

**Fix:** 提取到模块级别或使用箭头函数。

---

### M-01: require-null-over-undefined

```typescript
let x = undefined;        // ❌
if (x === undefined) {}   // ❌
return undefined;         // ❌
```

**Fix:** 统一使用 `null`：
```typescript
let x = null;
if (x == null) {}
return null;
```

---

### M-07~M-12: no-web-api-direct

禁止直接调用必须通过抽象层的平台特定 API：

```typescript
console.log('msg');              // ❌ use Logger from core/platform/logger.ts
requestAnimationFrame(cb);        // ❌ use getAnimationScheduler()
performance.now();                // ❌ use getAnimationScheduler().now()
setTimeout(fn, 100);              // ❌ use getAnimationScheduler().setTimeout()
```

**Fix:** 从 `core/platform/` 导入平台抽象模块。

---

### M-04~M-05: no-weakmap-proxy

```typescript
const wm = new WeakMap();        // ⚠️ warn: 各平台行为不一致
const p = new Proxy(target, {}); // ⚠️ warn: ArkTS 不支持
```

**Fix:** 使用 `Map` 替代 `WeakMap`；使用显式适配器类替代 `Proxy`。

---

### 🆕 F-29: no-typeof-runtime-check

**v2.0 新增规则** — 禁止使用 typeof 进行运行时类型检查：

```typescript
if (typeof item === 'string') { ... }   // ❌
if (typeof obj === 'object') { ... }    // ❌
```

**Fix:** 使用类型守卫函数：
```typescript
function isString(value: unknown): value is string {
  return typeof value === 'string';
}

function isObject(value: unknown): value is Record<string, unknown> {
  return typeof value === 'object' && value !== null;
}

if (isString(item)) { ... }    // ✅
if (isObject(obj)) { ... }    // ✅
```

**Auto-fix:** ESLint 可自动将 `typeof x === 'string'` 转换为 `isString(x)`。

---

### 🆕 F-30: no-object-as-map

**v2.0 新增规则** — 禁止使用 JS 对象作为 Map：

```typescript
const entries = Object.entries(obj);      // ❌ Kotlin/Swift 不存在
const newObj = Object.fromEntries(entries); // ❌ Kotlin/Swift 不存在
const keys = Object.keys(obj);            // ❌ 应使用 Map.keys()
const values = Object.values(obj);         // ❌ 应使用 Map.values()
```

**Fix:** 使用 Map 替代对象存储键值对：
```typescript
const map = new Map<string, any>();
map.set('key', 'value');
const entries = Array.from(map.entries());
```

---

### 🆕 F-31: require-explicit-return-type

**v2.0 新增规则** — 要求所有公开函数必须有显式返回类型注解：

```typescript
function getData() {              // ❌ 隐式返回类型
  return { items: [] };
}
```

**Fix:** 添加显式返回类型：
```typescript
function getData(): { items: Item[] } {  // ✅
  return { items: [] };
}
```

**Strict Mode:** 在 v4-production 配置中，箭头函数也必须显式声明返回类型。

---

### 🆕 F-32: no-double-type-cast

**v2.0 新增规则** — 禁止双重类型转换：

```typescript
const result = obj as Any as T;          // ❌ 完全丢失类型安全
const value = data as unknown as MyType;  // ❌
```

**Fix:** 使用单次安全转换或类型检查：
```typescript
const result = obj as T;                  // ✅ 单次转换
const result2 = obj as? T;                 // ✅ 可选链转换
if (result !== null) { ... }               // ✅ 带检查
```

---

### 🆕 M-17, M-20~M-22: no-js-builtin-api

**v2.0 新增规则** — 禁止直接调用需要平台映射的 JS 内置 API：

```typescript
Object.entries(obj);           // ❌ 需映射为 map.entries()
Array.prototype.push.call(arr, item);  // ❌
str.includes(sub);             // ⚠️ 部分平台名称不同
Math.floor(x);                 // ⚠️ 平台差异
```

**Fix:** 参考 [Safe TS Subset v4.0 API映射表](../docs/safe-ts-subset-v4.0.md#四完整api映射规范js--目标语言)

| JS API | Kotlin | Swift |
|--------|--------|-------|
| `arr.push(item)` | `arr.add(item)` | `arr.append(item)` |
| `arr.pop()` | `arr.removeAt(arr.size - 1)` | `arr.removeLast()` |
| `Object.keys(obj)` | `(obj as Map<String, Any>).keys` | `(obj as! [String: Any]).keys` |

---

### 🆕 C-04: prefer-template-literal

**v2.0 新增规则** — 优先使用模板字符串拼接：

```typescript
const str = "prefix_" + index + "_suffix";  // ❌
```

**Fix:**
```typescript
const str = `prefix_${index}_suffix`;        // ✅
```

**Auto-fix:** ESLint 可自动将字符串拼接转换为模板字符串。

---

### 🆕 F-28: no-nested-ternary

**v2.0 新增规则** — 禁止嵌套三元表达式：

```typescript
const result = condition1 ? (condition2 ? value1 : value2) : value3;  // ❌
```

**Fix:** 使用 if-else 或提取变量：
```typescript
let result: string;
if (condition1) {
  result = condition2 ? value1 : value2;
} else {
  result = value3;
}
```

---

### 🆕 C-09: class-member-order

**v2.0 新增规则** — 强制类成员遵循 Swift/Kotlin 惯例顺序：

```typescript
class BadOrder {
  render() { }           // ❌ 应在 constructor 后
  private _state;        // ❌ 应在最前
  constructor() { }      // ❌ 应在属性后
  
  dispose() { }          // ❌ 应在 constructor 后
  static VERSION = '1';  // ❌ 应在最前
}
```

**Fix:** 遵循标准顺序：
```typescript
class GoodOrder {
  static readonly VERSION = '1.0.0';  // 1. 静态属性
  private _state;                    // 2. 实例属性
  
  constructor() { }                  // 3. 构造函数
  dispose() { }                      // 4. 生命周期方法
  
  render() { }                       // 5. 公共方法
  protected beforeRender() { }        // 6. 受保护方法
  private createInitialState() { }    // 7. 私有方法
}
```

---

## Config Presets

### recommended

平衡的严格度，适合大多数项目。所有规则为 `error`，部分规则为 `warn`：

```javascript
{
  extends: ['plugin:@qiun/ucharts/recommended']
}
```

### strict

所有规则均为 `error`，适用于**必须**通过跨平台转译的代码：

```javascript
{
  extends: ['plugin:@qiun/ucharts/strict']
}
```

### v4-production 🆕

**生产级最严格配置**，适用于发布前最终质量门禁：

```javascript
{
  extends: ['plugin:@qiun/ucharts/v4-production']
}
```

特点：
- 扩展了 `no-web-api-direct` 的禁止列表（增加 setTimeout/setInterval/performance.measure）
- 扩展了 `no-js-builtin-api` 的禁止列表（完整覆盖 Object/Array/String/Math API）
- `require-explicit-return-type` 排除私有方法和箭头函数

---

## Integration with VSCode

### Settings

```json
{
  "eslint.enable": true,
  "eslint.run": "onType",
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": "explicit"
  }
}
```

### Auto-Fix Support

以下规则支持自动修复：

| Rule | Fix Example |
|------|-------------|
| `no-typeof-runtime-check` | `typeof x === 'string'` → `isString(x)` |
| `no-double-type-cast` | `obj as Any as T` → `obj as T` |
| `prefer-template-literal` | `"a" + b` → `` `a${b}` `` |

---

## Integration with uCharts Project

### Package.json Scripts

```json
{
  "scripts": {
    "lint": "eslint . --ext .ts,.js",
    "lint:fix": "eslint . --fix",
    "lint:subset": "eslint core/ --ext .ts --rule 'plugin:@qiun/ucharts/strict'",
    "lint:v4": "eslint core/ --ext .ts --rule 'plugin:@qiun/ucharts/v4-production'",
    "check:subset": "npm run lint:v4 && echo '✅ Safe TS Subset v4.0 check passed!'"
  }
}
```

### File Exemptions

对需要特殊处理的文件使用 overrides：

```javascript
{
  files: ['core/util/merge.ts'],
  rules: {
    '@qiun/ucharts/no-dynamic-property-access': 'off',
    '@qiun/ucharts/no-index-signature': 'off',
    '@qiun/ucharts/no-object-as-map': 'off',
    '@qiun/ucharts/no-js-builtin-api': 'off'
  }
},
{
  files: ['core/util/type-guards.ts'],
  rules: {
    '@qiun/ucharts/no-typeof-runtime-check': 'off'
  }
},
{
  files: ['core/platform/**/*.ts'],
  rules: {
    '@qiun-ucharts/no-web-api-direct': 'off',
    '@qiun-ucharts/require-null-over-undefined': 'off',
    '@qiun-ucharts/no-js-builtin-api': 'off'
  }
}
```

---

## Development

```bash
# Clone the uCharts repo
git clone https://github.com/qiun/ucharts.git
cd uCharts-v4

# Install dependencies
npm install

# Lint core/ with v4-production config
npm run lint:v4

# Generate violation report
npm run lint:subset:report

# Run with verbose output
npx eslint core/ --ext .ts -f stylish
```

---

## Migration Guide (v1.0 → v2.0)

### Breaking Changes

v2.0 完全向后兼容 v1.0。主要变化：

1. **规则数量增加**：10 → 18 条
2. **新增配置预设**：`v4-production`
3. **更严格的检查**：新增 8 条规则覆盖 v4.0 规范

### Quick Migration

```bash
# Update plugin
npm install @qiun/eslint-plugin-ucharts@latest

# Check violations
npm run lint:subset:report
```

---

## Related Documents

- [Safe TS Subset v4.0](../docs/safe-ts-subset-v4.0.md) — 完整规范文档
- [ESLint v2.0 Upgrade Guide](../docs/eslint-v2-upgrade-guide.md) — 详细升级方案
- [Safe TS Subset v3.0 Quality Assessment](../docs/safe-ts-subset-v3.0-quality-assessment.md) — 转译质量分析报告

---

## License

[Apache-2.0](../LICENSE)