mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-06-06 05:04:22 +00:00
chore(frontend): add react+typescript toolchain alongside vue
Step 0 of the planned vue->react migration. React 19, antd 5, i18next
+ react-i18next, typescript 5, and @vitejs/plugin-react 6 are added as
dev/runtime deps alongside the existing vue stack. Both frameworks
coexist in the build until the last entry flips.
* vite.config.js: react() plugin runs next to vue(); new manualChunks
for vendor-react / vendor-antd-react / vendor-icons-react /
vendor-i18next. Existing vue chunks unchanged.
* eslint.config.js: typescript-eslint + eslint-plugin-react-hooks
rules scoped to *.{ts,tsx}; vue config untouched for *.{js,vue}.
* tsconfig.json: strict, jsx: react-jsx, moduleResolution: bundler,
allowJs: true (lets .tsx files import the remaining .js modules
during incremental migration), @/* path alias.
* env.d.ts: Vite client types + window.X_UI_BASE_PATH typing +
SubPageData shape consumed by the subscription page.
Vite stays pinned at 8.0.13 per the existing project policy. No
existing .vue/.js source files touched in this step.
eslint-plugin-react (not -hooks) is not included because its latest
release does not yet support ESLint 10. react-hooks/purity covers
the safety-critical case; revisit when the plugin updates.
This commit is contained in:
parent
237b7c898d
commit
8c20bde1da
6 changed files with 2107 additions and 3 deletions
|
|
@ -1,6 +1,8 @@
|
||||||
import js from '@eslint/js';
|
import js from '@eslint/js';
|
||||||
import vue from 'eslint-plugin-vue';
|
import vue from 'eslint-plugin-vue';
|
||||||
import vueParser from 'vue-eslint-parser';
|
import vueParser from 'vue-eslint-parser';
|
||||||
|
import tseslint from 'typescript-eslint';
|
||||||
|
import reactHooks from 'eslint-plugin-react-hooks';
|
||||||
import globals from 'globals';
|
import globals from 'globals';
|
||||||
|
|
||||||
export default [
|
export default [
|
||||||
|
|
@ -55,4 +57,30 @@ export default [
|
||||||
'vue/no-mutating-props': 'off',
|
'vue/no-mutating-props': 'off',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
...tseslint.configs.recommended.map((config) => ({
|
||||||
|
...config,
|
||||||
|
files: ['**/*.{ts,tsx}'],
|
||||||
|
})),
|
||||||
|
{
|
||||||
|
files: ['**/*.{ts,tsx}'],
|
||||||
|
plugins: {
|
||||||
|
'react-hooks': reactHooks,
|
||||||
|
},
|
||||||
|
languageOptions: {
|
||||||
|
ecmaVersion: 2022,
|
||||||
|
sourceType: 'module',
|
||||||
|
globals: {
|
||||||
|
...globals.browser,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
...reactHooks.configs.recommended.rules,
|
||||||
|
'@typescript-eslint/no-unused-vars': ['warn', {
|
||||||
|
argsIgnorePattern: '^_',
|
||||||
|
varsIgnorePattern: '^_',
|
||||||
|
caughtErrorsIgnorePattern: '^_',
|
||||||
|
}],
|
||||||
|
'no-empty': ['error', { allowEmptyCatch: true }],
|
||||||
|
},
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
|
||||||
1994
frontend/package-lock.json
generated
1994
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -12,28 +12,41 @@
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"lint": "eslint src"
|
"lint": "eslint src",
|
||||||
|
"typecheck": "tsc --noEmit"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@ant-design/icons": "^6.2.3",
|
||||||
"@ant-design/icons-vue": "^7.0.1",
|
"@ant-design/icons-vue": "^7.0.1",
|
||||||
"@codemirror/lang-json": "^6.0.2",
|
"@codemirror/lang-json": "^6.0.2",
|
||||||
"@codemirror/theme-one-dark": "^6.1.3",
|
"@codemirror/theme-one-dark": "^6.1.3",
|
||||||
"ant-design-vue": "^4.2.6",
|
"ant-design-vue": "^4.2.6",
|
||||||
|
"antd": "^5.29.3",
|
||||||
"axios": "^1.7.9",
|
"axios": "^1.7.9",
|
||||||
"codemirror": "^6.0.2",
|
"codemirror": "^6.0.2",
|
||||||
"dayjs": "^1.11.20",
|
"dayjs": "^1.11.20",
|
||||||
|
"i18next": "^25.10.10",
|
||||||
"otpauth": "^9.5.1",
|
"otpauth": "^9.5.1",
|
||||||
"qs": "^6.13.1",
|
"qs": "^6.13.1",
|
||||||
|
"react": "^19.2.6",
|
||||||
|
"react-dom": "^19.2.6",
|
||||||
|
"react-i18next": "^16.6.6",
|
||||||
"vue": "^3.5.34",
|
"vue": "^3.5.34",
|
||||||
"vue-i18n": "^11.1.4",
|
"vue-i18n": "^11.1.4",
|
||||||
"vue3-persian-datetime-picker": "^1.2.2"
|
"vue3-persian-datetime-picker": "^1.2.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^10.0.1",
|
"@eslint/js": "^10.0.1",
|
||||||
|
"@types/react": "^19.2.15",
|
||||||
|
"@types/react-dom": "^19.2.3",
|
||||||
|
"@vitejs/plugin-react": "^6.0.2",
|
||||||
"@vitejs/plugin-vue": "^6.0.6",
|
"@vitejs/plugin-vue": "^6.0.6",
|
||||||
"eslint": "^10.3.0",
|
"eslint": "^10.3.0",
|
||||||
|
"eslint-plugin-react-hooks": "^7.1.1",
|
||||||
"eslint-plugin-vue": "^10.9.1",
|
"eslint-plugin-vue": "^10.9.1",
|
||||||
"globals": "^17.6.0",
|
"globals": "^17.6.0",
|
||||||
|
"typescript": "^5.9.3",
|
||||||
|
"typescript-eslint": "^8.59.4",
|
||||||
"vite": "8.0.13",
|
"vite": "8.0.13",
|
||||||
"vue-eslint-parser": "^10.4.0"
|
"vue-eslint-parser": "^10.4.0"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
28
frontend/src/env.d.ts
vendored
Normal file
28
frontend/src/env.d.ts
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
/// <reference types="vite/client" />
|
||||||
|
|
||||||
|
interface SubPageData {
|
||||||
|
sId?: string;
|
||||||
|
enabled?: boolean;
|
||||||
|
download?: string;
|
||||||
|
upload?: string;
|
||||||
|
total?: string;
|
||||||
|
used?: string;
|
||||||
|
remained?: string;
|
||||||
|
totalByte?: string | number;
|
||||||
|
expire?: string | number;
|
||||||
|
lastOnline?: string | number;
|
||||||
|
subUrl?: string;
|
||||||
|
subJsonUrl?: string;
|
||||||
|
subClashUrl?: string;
|
||||||
|
subTitle?: string;
|
||||||
|
links?: string[];
|
||||||
|
datepicker?: 'gregorian' | 'jalalian';
|
||||||
|
downloadByte?: string | number;
|
||||||
|
uploadByte?: string | number;
|
||||||
|
usedByte?: string | number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Window {
|
||||||
|
X_UI_BASE_PATH?: string;
|
||||||
|
__SUB_PAGE_DATA__?: SubPageData;
|
||||||
|
}
|
||||||
29
frontend/tsconfig.json
Normal file
29
frontend/tsconfig.json
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2022",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": false,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"noUncheckedSideEffectImports": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"allowJs": true,
|
||||||
|
"checkJs": false,
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./src/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["src/**/*.ts", "src/**/*.tsx"],
|
||||||
|
"exclude": ["node_modules", "../web/dist"]
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { defineConfig } from 'vite';
|
import { defineConfig } from 'vite';
|
||||||
import vue from '@vitejs/plugin-vue';
|
import vue from '@vitejs/plugin-vue';
|
||||||
|
import react from '@vitejs/plugin-react';
|
||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import { DatabaseSync } from 'node:sqlite';
|
import { DatabaseSync } from 'node:sqlite';
|
||||||
|
|
@ -136,7 +137,7 @@ function makeBackendProxy(target) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [vue(), injectBasePathPlugin()],
|
plugins: [vue(), react(), injectBasePathPlugin()],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
'@': path.resolve(__dirname, 'src'),
|
'@': path.resolve(__dirname, 'src'),
|
||||||
|
|
@ -170,6 +171,17 @@ export default defineConfig({
|
||||||
id.includes('/node_modules/vue/')
|
id.includes('/node_modules/vue/')
|
||||||
|| id.includes('/node_modules/@vue/')
|
|| id.includes('/node_modules/@vue/')
|
||||||
) return 'vendor-vue';
|
) return 'vendor-vue';
|
||||||
|
if (id.includes('/node_modules/antd/')) return 'vendor-antd-react';
|
||||||
|
if (id.includes('/@ant-design/icons/')) return 'vendor-icons-react';
|
||||||
|
if (
|
||||||
|
id.includes('/node_modules/react-i18next/')
|
||||||
|
|| id.includes('/node_modules/i18next/')
|
||||||
|
) return 'vendor-i18next';
|
||||||
|
if (
|
||||||
|
id.includes('/node_modules/react/')
|
||||||
|
|| id.includes('/node_modules/react-dom/')
|
||||||
|
|| id.includes('/node_modules/scheduler/')
|
||||||
|
) return 'vendor-react';
|
||||||
if (id.includes('dayjs')) return 'vendor-dayjs';
|
if (id.includes('dayjs')) return 'vendor-dayjs';
|
||||||
if (id.includes('axios')) return 'vendor-axios';
|
if (id.includes('axios')) return 'vendor-axios';
|
||||||
if (
|
if (
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue