build: 引入 ESLint 防回潮基建 + 清理存量未使用 import
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
# Gitea Actions CI —— 防回潮检查。
|
||||
#
|
||||
# 注意:本文件需 Gitea 服务端【启用 Actions】并【配置 act_runner】后才会执行。
|
||||
# 未配置 runner 时本文件无副作用(不影响本地开发与 husky 钩子)。
|
||||
# 启用方式:Gitea 站点管理 → 启用 Actions;在 runner 主机注册 act_runner 并打 label。
|
||||
#
|
||||
# 本地已有 husky 钩子兜底:pre-commit 跑 tsc+eslint(增量),pre-push 跑 css:audit。
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, "main-merge-work"]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
verify:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "20"
|
||||
cache: "npm"
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Type check
|
||||
run: npm run type-check
|
||||
|
||||
- name: Tests
|
||||
run: npm run test:run
|
||||
|
||||
- name: Lint (error 阻断,warning 放行)
|
||||
run: npm run lint
|
||||
@@ -0,0 +1,75 @@
|
||||
// ESLint flat config(ESLint 9)。防回潮基建:锁定去重/抽取/合规成果,约束新代码。
|
||||
// 策略:warn 基线——历史问题(如 exhaustive-deps)设 warn 不阻断提交,
|
||||
// 新代码的 error 类问题(unused vars 等)强制清零。CI/pre-commit 只拦 error。
|
||||
import js from "@eslint/js";
|
||||
import tseslint from "typescript-eslint";
|
||||
import reactHooks from "eslint-plugin-react-hooks";
|
||||
import reactRefresh from "eslint-plugin-react-refresh";
|
||||
import unusedImports from "eslint-plugin-unused-imports";
|
||||
import globals from "globals";
|
||||
|
||||
export default tseslint.config(
|
||||
{
|
||||
// 忽略构建产物、依赖、配置脚本、JS shim。
|
||||
ignores: [
|
||||
"dist/**",
|
||||
"node_modules/**",
|
||||
"coverage/**",
|
||||
"**/*.config.{js,ts,mjs,cjs}",
|
||||
"scripts/**",
|
||||
"src/data/ossAssets.js",
|
||||
],
|
||||
},
|
||||
js.configs.recommended,
|
||||
...tseslint.configs.recommended,
|
||||
{
|
||||
files: ["**/*.{ts,tsx}"],
|
||||
languageOptions: {
|
||||
ecmaVersion: 2022,
|
||||
sourceType: "module",
|
||||
globals: {
|
||||
...globals.browser,
|
||||
...globals.es2022,
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
"react-hooks": reactHooks,
|
||||
"react-refresh": reactRefresh,
|
||||
"unused-imports": unusedImports,
|
||||
},
|
||||
rules: {
|
||||
...reactHooks.configs.recommended.rules,
|
||||
// 历史问题:warn 不阻断,渐进清理。
|
||||
"react-hooks/exhaustive-deps": "warn",
|
||||
"@typescript-eslint/no-explicit-any": "warn",
|
||||
// 未使用 import:error 且可 autofix 自动删除(unused-imports 插件专长)。
|
||||
"@typescript-eslint/no-unused-vars": "off",
|
||||
"unused-imports/no-unused-imports": "error",
|
||||
// 未使用局部变量:warn 基线(不自动删,避免误删 dead code 有副作用的赋值)。
|
||||
"unused-imports/no-unused-vars": [
|
||||
"warn",
|
||||
{ vars: "all", varsIgnorePattern: "^_", args: "after-used", argsIgnorePattern: "^_" },
|
||||
],
|
||||
// 允许 warn/error(现有 console.warn/error 是有意的诊断输出)。
|
||||
"no-console": ["warn", { allow: ["warn", "error"] }],
|
||||
"react-refresh/only-export-components": ["warn", { allowConstantExport: true }],
|
||||
},
|
||||
},
|
||||
{
|
||||
// 测试文件:补 vitest 全局,避免 describe/it/expect 误报未定义。
|
||||
files: ["**/*.{test,spec}.{ts,tsx}"],
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.node,
|
||||
describe: "readonly",
|
||||
it: "readonly",
|
||||
expect: "readonly",
|
||||
vi: "readonly",
|
||||
beforeEach: "readonly",
|
||||
afterEach: "readonly",
|
||||
beforeAll: "readonly",
|
||||
afterAll: "readonly",
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
@@ -1,10 +1,7 @@
|
||||
// lint-staged 配置 —— 配合 husky pre-commit 使用
|
||||
//
|
||||
// 当前只运行 tsc 全量类型检查(tsc 不接受单文件增量检查),
|
||||
// 未来可扩展 ESLint / Prettier / stylelint 等按文件的检查。
|
||||
//
|
||||
// 函数语法返回原始命令字符串,lint-staged 不会追加文件名。
|
||||
|
||||
// lint-staged:pre-commit 时对暂存文件运行检查。
|
||||
// - tsc --noEmit:全量类型检查(函数语法返回命令,不追加文件名)。
|
||||
// - eslint --fix:仅对暂存的改动文件增量检查(新代码强制 error=0,
|
||||
// warning 不阻断提交)。存量历史问题不会因此被卡住。
|
||||
export default {
|
||||
"*.{ts,tsx}": () => "tsc --noEmit",
|
||||
"*.{ts,tsx}": [() => "tsc --noEmit", "eslint --fix"],
|
||||
};
|
||||
|
||||
Generated
+1446
File diff suppressed because it is too large
Load Diff
@@ -8,6 +8,9 @@
|
||||
"build": "vite build",
|
||||
"preview": "vite preview --host 127.0.0.1",
|
||||
"type-check": "tsc -p tsconfig.json --noEmit",
|
||||
"lint": "eslint .",
|
||||
"lint:fix": "eslint . --fix",
|
||||
"lint:strict": "eslint . --max-warnings=0",
|
||||
"test": "vitest",
|
||||
"test:run": "vitest run",
|
||||
"test:coverage": "vitest run --coverage",
|
||||
@@ -23,13 +26,20 @@
|
||||
"zustand": "5.0.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.18.0",
|
||||
"@types/react": "18.2.55",
|
||||
"@types/react-dom": "18.2.18",
|
||||
"@vitejs/plugin-react": "4.2.1",
|
||||
"@vitest/coverage-v8": "^1.6.0",
|
||||
"eslint": "^9.18.0",
|
||||
"eslint-plugin-react-hooks": "^5.1.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.18",
|
||||
"eslint-plugin-unused-imports": "^4.1.4",
|
||||
"globals": "^15.14.0",
|
||||
"husky": "^9.1.7",
|
||||
"lint-staged": "^17.0.7",
|
||||
"typescript": "5.3.3",
|
||||
"typescript-eslint": "^8.20.0",
|
||||
"vite": "5.1.0",
|
||||
"vite-plugin-compression2": "2.5.3",
|
||||
"vitest": "^1.6.0"
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { lazy, Suspense, useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { lazy, Suspense, useCallback, useEffect, useState } from "react";
|
||||
import {
|
||||
BugOutlined,
|
||||
CheckCircleFilled,
|
||||
|
||||
@@ -108,7 +108,7 @@ export interface ComplianceCheck {
|
||||
}
|
||||
|
||||
function findJsonSlice(raw: string): string {
|
||||
const start = raw.search(/[\[{]/);
|
||||
const start = raw.search(/[{[]/);
|
||||
if (start < 0) return raw;
|
||||
|
||||
const stack: string[] = [];
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export type ToastType = "success" | "error" | "info";
|
||||
|
||||
|
||||
@@ -26,15 +26,8 @@ import {
|
||||
} from "@ant-design/icons";
|
||||
import {
|
||||
ArrowsCounterClockwise,
|
||||
Fire,
|
||||
FrameCorners,
|
||||
Gift,
|
||||
MagicWand,
|
||||
Mountains,
|
||||
PaperPlaneRight,
|
||||
ShoppingBag,
|
||||
User,
|
||||
VideoCamera,
|
||||
} from "@phosphor-icons/react";
|
||||
import { Fragment, useEffect, useMemo, useRef, useState, type CSSProperties, type ChangeEvent, type DragEvent, type KeyboardEvent as ReactKeyboardEvent, type MouseEvent as ReactMouseEvent, type PointerEvent as ReactPointerEvent, type ReactNode } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
@@ -58,43 +51,25 @@ import EcommerceCopywritingPanel from "./panels/EcommerceCopywritingPanel";
|
||||
import { ecommerceOssScopes, saveUnifiedEcommerceGenerationRecord, deleteEcommerceGenerationRecord } from "./ecommerceGenerationPersistence";
|
||||
import { downloadResultAsset } from "../workbench/workbenchDownload";
|
||||
import {
|
||||
appendEnglish,
|
||||
defaultCloneOutput,
|
||||
defaultEcommercePlatform,
|
||||
defaultMarketLanguageOption,
|
||||
defaultPlatformSpec,
|
||||
defaultProductSetOutput,
|
||||
domesticPlatformLabels,
|
||||
domesticPlatformLanguages,
|
||||
formatUploadedImageRatio,
|
||||
getMarketLanguageOptions,
|
||||
getPlatformDefaultLanguage,
|
||||
getPlatformDefaultRatio,
|
||||
getPlatformLanguageOptions,
|
||||
getPlatformRatioGroup,
|
||||
getPlatformRatioOptions,
|
||||
getPlatformSpec,
|
||||
getUniqueRatioOptions,
|
||||
isDomesticPlatform,
|
||||
languageAliases,
|
||||
legacyPlatformAliases,
|
||||
marketLanguageOptions,
|
||||
marketOptions,
|
||||
normalizeLanguage,
|
||||
normalizeLanguageForPlatform,
|
||||
normalizeMarket,
|
||||
normalizePlatform,
|
||||
normalizeRatioForPlatform,
|
||||
platformOptions,
|
||||
platformSpecOptions,
|
||||
uniqueLanguages,
|
||||
} from "./utils/platformRules";
|
||||
import type {
|
||||
CloneOutputKey,
|
||||
PlatformRatioModeKey,
|
||||
ProductSetOutputKey,
|
||||
} from "./utils/platformRules";
|
||||
import type { EcommercePlatformSpec, PlatformRatioGroup } from "./utils/platformRules";
|
||||
import {
|
||||
buildHistoryTurnFromRecord,
|
||||
cloneLatestSettingStorageKey,
|
||||
@@ -102,15 +77,9 @@ import {
|
||||
defaultCloneSetCounts,
|
||||
ecommerceHistoryStorageKey,
|
||||
getTurnResults,
|
||||
isCloneImageItem,
|
||||
isCloneResult,
|
||||
isCloneSavedSetting,
|
||||
isEcommerceHistoryRecord,
|
||||
normalizeEcommerceHistoryRecord,
|
||||
normalizeEcommerceHistoryTurn,
|
||||
readCloneLatestSetting,
|
||||
readEcommerceHistoryRecords,
|
||||
removeFilePayloadFromImages,
|
||||
writeCloneLatestSetting,
|
||||
writeEcommerceHistoryRecords,
|
||||
} from "./utils/clonePersistence";
|
||||
@@ -275,18 +244,13 @@ import {
|
||||
rgbToHex,
|
||||
} from "./utils/colorUtils";
|
||||
import {
|
||||
formatAspectRatio,
|
||||
formatRatioDisplayValue,
|
||||
getQuickSetRatioValue,
|
||||
getRatioDisplayParts,
|
||||
greatestCommonDivisor,
|
||||
normalizeRatioForApi,
|
||||
normalizeRatioToken,
|
||||
parseRatioToAspectCss,
|
||||
quickSetRatioOptions,
|
||||
supportedImageApiRatios,
|
||||
toSupportedImageApiRatio,
|
||||
type SupportedImageApiRatio,
|
||||
} from "./utils/ratioUtils";
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Fragment, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import "../../styles/pages/ecommerce-video.css";
|
||||
import {
|
||||
CloseOutlined,
|
||||
@@ -33,7 +33,6 @@ import { useGenerationTasks } from "../../hooks/useGenerationTasks";
|
||||
import {
|
||||
saveEcommerceVideoState,
|
||||
loadEcommerceVideoState,
|
||||
clearEcommerceVideoState,
|
||||
} from "./ecommerceVideoKeepalive";
|
||||
import { saveUnifiedEcommerceGenerationRecord } from "./ecommerceGenerationPersistence";
|
||||
import { useVideoSceneRunner } from "./useVideoSceneRunner";
|
||||
|
||||
@@ -4,7 +4,6 @@ import {
|
||||
CopyOutlined,
|
||||
EditOutlined,
|
||||
FileTextOutlined,
|
||||
FireOutlined,
|
||||
GlobalOutlined,
|
||||
MessageOutlined,
|
||||
SmileOutlined,
|
||||
|
||||
@@ -13,8 +13,6 @@ import {
|
||||
} from "./ecommerceVideoService";
|
||||
import { waitForTask } from "../../api/taskSubscription";
|
||||
import { ServerRequestError } from "../../api/serverConnection";
|
||||
import { ENTERPRISE_DEFAULT_VIDEO_MODEL } from "../../utils/enterpriseVideoPolicy";
|
||||
import { resolveVideoRequestModel } from "../../utils/resolveVideoModel";
|
||||
import {
|
||||
saveEcommerceVideoState,
|
||||
} from "./ecommerceVideoKeepalive";
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { create } from "zustand";
|
||||
import type { WebGenerationPreviewTask } from "../types";
|
||||
|
||||
export type QueueItemStatus = "pending" | "running" | "completed" | "failed" | "cancelled";
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
export type WebViewKey =
|
||||
| "home"
|
||||
|
||||
@@ -10,7 +10,7 @@ interface ErrorReport {
|
||||
sessionId?: string;
|
||||
}
|
||||
|
||||
let reportQueue: ErrorReport[] = [];
|
||||
const reportQueue: ErrorReport[] = [];
|
||||
let flushTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
function getSessionId(): string | undefined {
|
||||
|
||||
Reference in New Issue
Block a user