diff --git a/.gitignore b/.gitignore index 2c4eac37d..23784bdab 100644 --- a/.gitignore +++ b/.gitignore @@ -53,3 +53,10 @@ vite.config.ts.* apps/web-antd/types/components.d.ts .history .cursor + +# AI +.agent +.agents +.claude +.codex +skills-lock.json diff --git a/.npmrc b/.npmrc index aeac1ae91..6d28fabf4 100644 --- a/.npmrc +++ b/.npmrc @@ -1,8 +1,8 @@ registry=https://registry.npmmirror.com public-hoist-pattern[]=lefthook public-hoist-pattern[]=eslint -public-hoist-pattern[]=prettier -public-hoist-pattern[]=prettier-plugin-tailwindcss +public-hoist-pattern[]=oxfmt +public-hoist-pattern[]=oxlint public-hoist-pattern[]=stylelint public-hoist-pattern[]=*postcss* public-hoist-pattern[]=@commitlint/* diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index d0b0ca133..000000000 --- a/.prettierignore +++ /dev/null @@ -1,18 +0,0 @@ -dist -dev-dist -.local -.output.js -node_modules -.nvmrc -coverage -CODEOWNERS -.nitro -.output - - -**/*.svg -**/*.sh - -public -.npmrc -*-lock.yaml diff --git a/.prettierrc.mjs b/.prettierrc.mjs deleted file mode 100644 index 3e25d2cfa..000000000 --- a/.prettierrc.mjs +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@vben/prettier-config'; diff --git a/.stylelintignore b/.stylelintignore index f4b2db2c1..3adb33b22 100644 --- a/.stylelintignore +++ b/.stylelintignore @@ -2,3 +2,7 @@ dist public __tests__ coverage +.codex +.claude +.agent +.agents diff --git a/.vscode/extensions.json b/.vscode/extensions.json index f4675d576..5b7db1abe 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -2,14 +2,18 @@ "recommendations": [ // Vue 3 的语言支持 "Vue.volar", - // 将 ESLint JavaScript 集成到 VS Code 中。 + // 将 eslint 集成到 VS Code 中。 "dbaeumer.vscode-eslint", + // 将 oxlint 集成到 VS Code 中。 + "oxc.oxc-vscode", // Visual Studio Code 的官方 Stylelint 扩展 "stylelint.vscode-stylelint", - // 使用 Prettier 的代码格式化程序 - "esbenp.prettier-vscode", + // 使用 oxfmt 的代码格式化程序 + "oxc.oxc-vscode", // 支持 dotenv 文件语法 "mikestead.dotenv", + // YAML 语言支持,供 ESLint 校验 pnpm-workspace.yaml 等文件 + "redhat.vscode-yaml", // 源代码的拼写检查器 "streetsidesoftware.code-spell-checker", // Tailwind CSS 的官方 VS Code 插件 diff --git a/.vscode/settings.json b/.vscode/settings.json index f7ae6c5ba..85b778c0e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,6 @@ { - "tailwindCSS.experimental.configFile": "internal/tailwind-config/src/index.ts", + "tailwindCSS.experimental.configFile": "packages/@core/base/design/src/css/global.css", + "tailwindCSS.lint.suggestCanonicalClasses": "ignore", // workbench "workbench.list.smoothScrolling": true, "workbench.startupEditor": "newUntitledFile", @@ -31,39 +32,51 @@ "editor.autoClosingOvertype": "always", "editor.autoClosingQuotes": "beforeWhitespace", "editor.wordSeparators": "`~!@#%^&*()=+[{]}\\|;:'\",.<>/?", + "editor.quickSuggestions": { + "strings": "on" + }, + + // lint && format + "oxc.enable": true, + "oxc.typeAware": true, + "oxc.configPath": "oxlint.config.ts", + "oxc.fmt.configPath": "oxfmt.config.ts", + "eslint.useFlatConfig": true, "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit", + "source.fixAll.oxc": "explicit", "source.fixAll.stylelint": "explicit", "source.organizeImports": "never" }, - "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.defaultFormatter": "oxc.oxc-vscode", "[html]": { - "editor.defaultFormatter": "vscode.html-language-features" + "editor.defaultFormatter": "oxc.oxc-vscode" }, "[css]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" + "editor.defaultFormatter": "oxc.oxc-vscode" }, "[scss]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" + "editor.defaultFormatter": "oxc.oxc-vscode" }, "[javascript]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" + "editor.defaultFormatter": "oxc.oxc-vscode" }, "[typescript]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" + "editor.defaultFormatter": "oxc.oxc-vscode" }, "[json]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" + "editor.defaultFormatter": "oxc.oxc-vscode" }, "[markdown]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" + "editor.defaultFormatter": "oxc.oxc-vscode" }, "[jsonc]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" + "editor.defaultFormatter": "oxc.oxc-vscode" }, "[vue]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" + "editor.defaultFormatter": "oxc.oxc-vscode" }, + // extensions "extensions.ignoreRecommendations": true, @@ -79,6 +92,7 @@ "files.insertFinalNewline": true, "files.simpleDialog.enable": true, "files.associations": { + "*.css": "tailwindcss", "*.ejs": "html", "*.art": "html", "**/tsconfig.json": "jsonc", @@ -119,7 +133,7 @@ // search "search.searchEditor.singleClickBehaviour": "peekDefinition", "search.followSymlinks": false, - // 在使用搜索功能时,将这些文件夹/文件排除在外 + // 使用搜索功能时,将这些文件和文件夹排除在外 "search.exclude": { "**/node_modules": true, "**/*.log": true, @@ -159,7 +173,7 @@ "emmet.triggerExpansionOnTab": false, "errorLens.enabledDiagnosticLevels": ["warning", "error"], - "errorLens.excludeBySource": ["cSpell", "Grammarly", "eslint"], + "errorLens.excludeBySource": ["cSpell", "Grammarly"], "stylelint.enable": true, "stylelint.packageManager": "pnpm", @@ -196,7 +210,7 @@ "yaml": false }, - "cssVariables.lookupFiles": ["packages/core/base/design/src/**/*.css"], + "cssVariables.lookupFiles": ["packages/@core/base/design/src/**/*.css"], "i18n-ally.localesPaths": [ "packages/locales/src/langs", @@ -220,8 +234,7 @@ "*.env": "$(capture).env.*", "README.md": "README*,CHANGELOG*,LICENSE,CNAME", "package.json": "pnpm-lock.yaml,pnpm-workspace.yaml,.gitattributes,.gitignore,.gitpod.yml,.npmrc,.browserslistrc,.node-version,.git*,.tazerc.json", - "eslint.config.mjs": ".eslintignore,.prettierignore,.stylelintignore,.commitlintrc.*,.prettierrc.*,stylelint.config.*,.lintstagedrc.mjs,cspell.json,lefthook.yml", - "tailwind.config.mjs": "postcss.*" + "oxlint.config.ts": ".eslintignore,.stylelintignore,.commitlintrc.*,stylelint.config.*,.lintstagedrc.mjs,cspell.json,lefthook.yml,oxfmt.config.*,eslint.config.*" }, "commentTranslate.hover.enabled": false, "commentTranslate.multiLineMerge": true, @@ -231,7 +244,6 @@ "editor.linkedEditing": true, // 自动同步更改html标签, "vscodeCustomCodeColor.highlightValue": "v-access", // v-access显示的颜色 "vscodeCustomCodeColor.highlightValueColor": "#CCFFFF", - "oxc.enable": false, "cSpell.words": [ "archiver", "axios", diff --git a/apps/web-antd/package.json b/apps/web-antd/package.json index bacca810d..bf938913f 100644 --- a/apps/web-antd/package.json +++ b/apps/web-antd/package.json @@ -1,6 +1,6 @@ { "name": "@vben/web-antd", - "version": "2.0.0-alpha.3", + "version": "2.0.0-beta.1", "homepage": "https://vben.pro", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", "repository": { diff --git a/apps/web-antd/postcss.config.mjs b/apps/web-antd/postcss.config.mjs deleted file mode 100644 index 3d8070455..000000000 --- a/apps/web-antd/postcss.config.mjs +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@vben/tailwind-config/postcss'; diff --git a/apps/web-antd/src/views/dashboard/analytics/index.vue b/apps/web-antd/src/views/dashboard/analytics/index.vue index e428ac955..49bf4d0b7 100644 --- a/apps/web-antd/src/views/dashboard/analytics/index.vue +++ b/apps/web-antd/src/views/dashboard/analytics/index.vue @@ -120,10 +120,10 @@ function keepOneMessage() {
- + - + diff --git a/apps/web-antd/tailwind.config.mjs b/apps/web-antd/tailwind.config.mjs deleted file mode 100644 index f17f556fa..000000000 --- a/apps/web-antd/tailwind.config.mjs +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@vben/tailwind-config'; diff --git a/apps/web-antd/tsconfig.json b/apps/web-antd/tsconfig.json index 9d43c9297..fb52a54d4 100644 --- a/apps/web-antd/tsconfig.json +++ b/apps/web-antd/tsconfig.json @@ -2,7 +2,6 @@ "$schema": "https://json.schemastore.org/tsconfig", "extends": "@vben/tsconfig/web-app.json", "compilerOptions": { - "baseUrl": ".", "paths": { "#/*": ["./src/*"] } diff --git a/apps/web-antd/tsconfig.node.json b/apps/web-antd/tsconfig.node.json index c2f0d86cc..36e9fb5fb 100644 --- a/apps/web-antd/tsconfig.node.json +++ b/apps/web-antd/tsconfig.node.json @@ -6,5 +6,5 @@ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", "noEmit": false }, - "include": ["vite.config.mts"] + "include": ["vite.config.ts"] } diff --git a/apps/web-antd/vite.config.mts b/apps/web-antd/vite.config.ts similarity index 100% rename from apps/web-antd/vite.config.mts rename to apps/web-antd/vite.config.ts diff --git a/apps/web-ele/src/types/element-plus-style-css.d.ts b/apps/web-ele/src/types/element-plus-style-css.d.ts new file mode 100644 index 000000000..ba7a76672 --- /dev/null +++ b/apps/web-ele/src/types/element-plus-style-css.d.ts @@ -0,0 +1,4 @@ +declare module 'element-plus/es/components/*/style/css' { + const sideEffect: undefined; + export default sideEffect; +} diff --git a/cspell.json b/cspell.json index e5cda71a1..7de2c11c5 100644 --- a/cspell.json +++ b/cspell.json @@ -4,33 +4,37 @@ "language": "en,en-US", "allowCompoundWords": true, "words": [ - "acmr", "Alova", + "Gitee", + "Qqchat", + "acmr", "antd", "antdv", "archiver", "astro", "axios", "brotli", - "clientid", "cascader", + "chatcmpl", + "clientid", "clsx", + "dedup", "defu", "demi", "dotenv", + "echart", "echarts", "ependencies", "esbuild", "esno", "etag", "execa", - "Gitee", "iconify", "iconoir", "intlify", "ipaddr", - "jsencrypt", "isequal", + "jsencrypt", "jspm", "lockb", "logininfor", @@ -49,20 +53,25 @@ "nuxt", "oper", "operlog", + "organisation", + "oxfmt", + "oxlint", "pinia", "prefixs", "publint", - "Qqchat", "qrcode", "reka", - "ruoyi", "rollup", + "ruoyi", "shadcn", "sonner", "sortablejs", "styl", + "tabler", "taze", "tdesign", + "tsgolint", + "turborepo", "ui-kit", "uicons", "unplugin", diff --git a/docs/src/en/components/common-ui/vben-alert.md b/docs/src/en/components/common-ui/vben-alert.md new file mode 100644 index 000000000..fe2a13749 --- /dev/null +++ b/docs/src/en/components/common-ui/vben-alert.md @@ -0,0 +1,76 @@ +--- +outline: deep +--- + +# Vben Alert + +`Alert` provides lightweight JavaScript-driven dialogs for simple `alert`, `confirm`, and `prompt` style interactions. + +## Basic Usage + +Use `alert` for a single confirm button dialog: + + + +Use `confirm` for confirm/cancel interactions: + + + +Use `prompt` when you need simple user input: + + + +## useAlertContext + +If `content`, `footer`, or `icon` is rendered through a custom component, you can call `useAlertContext()` inside that component to access the current dialog actions. + +| Method | Description | Type | +| ----------- | -------------------------- | ------------ | +| `doConfirm` | trigger the confirm action | `() => void` | +| `doCancel` | trigger the cancel action | `() => void` | + +## Core Types + +```ts +export type IconType = 'error' | 'info' | 'question' | 'success' | 'warning'; + +export type BeforeCloseScope = { + isConfirm: boolean; +}; + +export type AlertProps = { + beforeClose?: ( + scope: BeforeCloseScope, + ) => boolean | Promise | undefined; + bordered?: boolean; + buttonAlign?: 'center' | 'end' | 'start'; + cancelText?: string; + centered?: boolean; + confirmText?: string; + containerClass?: string; + content: Component | string; + contentClass?: string; + contentMasking?: boolean; + footer?: Component | string; + icon?: Component | IconType; + overlayBlur?: number; + showCancel?: boolean; + title?: string; +}; + +export type PromptProps = { + beforeClose?: (scope: { + isConfirm: boolean; + value: T | undefined; + }) => boolean | Promise | undefined; + component?: Component; + componentProps?: Recordable; + componentSlots?: + | (() => any) + | Recordable + | VNode + | VNodeArrayChildren; + defaultValue?: T; + modelPropName?: string; +} & Omit; +``` diff --git a/docs/src/en/components/common-ui/vben-api-component.md b/docs/src/en/components/common-ui/vben-api-component.md new file mode 100644 index 000000000..f260a3791 --- /dev/null +++ b/docs/src/en/components/common-ui/vben-api-component.md @@ -0,0 +1,69 @@ +--- +outline: deep +--- + +# Vben ApiComponent + +`ApiComponent` is a wrapper used to attach remote-option loading behavior to an existing component while preserving the original component usage pattern. + +## Common Usage + +The current wrapper flow is: + +- pass the target component through `component` +- fetch remote data through `api` +- transform data through `beforeFetch` and `afterFetch` +- map remote fields through `resultField`, `valueField`, `labelField`, and `childrenField` +- pass normalized options to the target component through `optionsPropName` + +```vue + + + +``` + +## Current Props + +| Prop | Description | Type | +| --- | --- | --- | +| `component` | wrapped target component | `Component` | +| `api` | remote request function | `(arg?: any) => Promise` | +| `params` | extra request params | `Record` | +| `beforeFetch` | hook before request | `AnyPromiseFunction` | +| `afterFetch` | hook after request | `AnyPromiseFunction` | +| `visibleEvent` | event name used to lazy-load data | `string` | +| `loadingSlot` | slot name used to render the loading icon | `string` | +| `modelPropName` | model prop name of the wrapped component | `string` | +| `autoSelect` | auto-pick the first / last / only option, or use a custom function | `'first' \| 'last' \| 'one' \| ((items) => item) \| false` | + +## Exposed Methods + +| Method | Description | +| ------------------------ | -------------------------------------- | +| `getComponentRef()` | returns the wrapped component instance | +| `updateParam(newParams)` | merges and updates request params | +| `getOptions()` | returns loaded options | +| `getValue()` | returns the current bound value | diff --git a/docs/src/en/components/common-ui/vben-count-to-animator.md b/docs/src/en/components/common-ui/vben-count-to-animator.md new file mode 100644 index 000000000..615a417b4 --- /dev/null +++ b/docs/src/en/components/common-ui/vben-count-to-animator.md @@ -0,0 +1,51 @@ +--- +outline: deep +--- + +# Vben CountToAnimator + +`CountToAnimator` renders animated number transitions. + +## Basic Usage + +Use `start-val`, `end-val`, and `duration` to control the animation range and timing. + + + +## Formatting + +Use `prefix`, `suffix`, `separator`, and `decimal` to control how the number is displayed. + + + +## Props + +| Prop | Description | Type | Default | +| --- | --- | --- | --- | +| `startVal` | starting value | `number` | `0` | +| `endVal` | ending value | `number` | `2021` | +| `duration` | animation duration in ms | `number` | `1500` | +| `autoplay` | start automatically | `boolean` | `true` | +| `prefix` | value prefix | `string` | `''` | +| `suffix` | value suffix | `string` | `''` | +| `separator` | thousands separator | `string` | `','` | +| `decimal` | decimal separator | `string` | `'.'` | +| `color` | text color | `string` | `''` | +| `useEasing` | enable transition preset easing | `boolean` | `true` | +| `transition` | transition preset name | `keyof typeof TransitionPresets` | `'linear'` | +| `decimals` | decimal places to keep | `number` | `0` | + +## Events + +| Event | Description | Type | +| ------------ | ------------------------------- | ------------ | +| `started` | fired when the animation starts | `() => void` | +| `finished` | fired when the animation ends | `() => void` | +| `onStarted` | deprecated alias of `started` | `() => void` | +| `onFinished` | deprecated alias of `finished` | `() => void` | + +## Exposed Methods + +| Method | Description | Type | +| ------- | --------------------------------- | ------------ | +| `reset` | reset to `startVal` and run again | `() => void` | diff --git a/docs/src/en/components/common-ui/vben-drawer.md b/docs/src/en/components/common-ui/vben-drawer.md new file mode 100644 index 000000000..20b1d7ddf --- /dev/null +++ b/docs/src/en/components/common-ui/vben-drawer.md @@ -0,0 +1,56 @@ +--- +outline: deep +--- + +# Vben Drawer + +`Vben Drawer` is the shared drawer wrapper used by the framework. It supports auto-height layout, loading state, connected components, and an imperative API similar to the modal API. + +## Basic Usage + +```ts +const [Drawer, drawerApi] = useVbenDrawer({ + // props + // events +}); +``` + + + +## Current Usage Notes + +- If you use `connectedComponent`, the inner and outer components share data through `drawerApi.setData()` and `drawerApi.getData()`. +- Default drawer behavior can be adjusted in `apps//src/bootstrap.ts` through `setDefaultDrawerProps(...)`. +- `setState(...)` works on `DrawerState`, not `ModalState`. + +## Key Props + +| Prop | Description | Type | +| --- | --- | --- | +| `appendToMain` | mount inside the main content area instead of `body` | `boolean` | +| `connectedComponent` | connect an inner component to the drawer wrapper | `Component` | +| `closeIconPlacement` | position of the close icon | `'left' \| 'right'` | +| `placement` | drawer side | `'left' \| 'right' \| 'top' \| 'bottom'` | +| `overlayBlur` | blur amount for the overlay | `number` | +| `submitting` | lock drawer interactions while submitting | `boolean` | + +## Events + +| Event | Description | Type | +| --- | --- | --- | +| `onBeforeClose` | called before close; returning `false` or rejecting prevents close | `() => Promise \| boolean \| undefined` | +| `onOpenChange` | called when open state changes | `(isOpen: boolean) => void` | +| `onOpened` | called after open animation completes | `() => void` | +| `onClosed` | called after close animation completes | `() => void` | + +## drawerApi + +| Method | Description | +| ----------------------- | -------------------------------------- | +| `setState(...)` | updates drawer state | +| `open()` | opens the drawer | +| `close()` | closes the drawer | +| `setData(data)` | stores shared data | +| `getData()` | reads shared data | +| `lock(isLocked = true)` | locks the drawer into submitting state | +| `unlock()` | alias for `lock(false)` | diff --git a/docs/src/en/components/common-ui/vben-ellipsis-text.md b/docs/src/en/components/common-ui/vben-ellipsis-text.md new file mode 100644 index 000000000..602812a07 --- /dev/null +++ b/docs/src/en/components/common-ui/vben-ellipsis-text.md @@ -0,0 +1,42 @@ +--- +outline: deep +--- + +# Vben EllipsisText + +`EllipsisText` displays long text with truncation, tooltip support, and optional expand/collapse behavior. + +## Basic Usage + +Pass the text through the default slot and limit the visual width with `maxWidth`. + + + +## Current Props + +| Prop | Description | Type | Default | +| --- | --- | --- | --- | +| `expand` | allow click-to-expand behavior | `boolean` | `false` | +| `line` | max visible line count | `number` | `1` | +| `maxWidth` | max width of the text area | `number \| string` | `'100%'` | +| `placement` | tooltip placement | `'bottom' \| 'left' \| 'right' \| 'top'` | `'top'` | +| `tooltip` | enable tooltip | `boolean` | `true` | +| `tooltipWhenEllipsis` | only show tooltip when text is actually truncated | `boolean` | `false` | +| `ellipsisThreshold` | pixel threshold used when checking truncation | `number` | `3` | +| `tooltipBackgroundColor` | tooltip background color | `string` | `''` | +| `tooltipColor` | tooltip text color | `string` | `''` | +| `tooltipFontSize` | tooltip font size in px | `number` | `14` | +| `tooltipMaxWidth` | tooltip max width in px | `number` | - | +| `tooltipOverlayStyle` | tooltip content style | `CSSProperties` | `{ textAlign: 'justify' }` | + +## Events + +| Event | Description | Type | +| --- | --- | --- | +| `expandChange` | fired when expand state changes | `(isExpand: boolean) => void` | + +## Slots + +| Slot | Description | +| --------- | ---------------------- | +| `tooltip` | custom tooltip content | diff --git a/docs/src/en/components/common-ui/vben-form.md b/docs/src/en/components/common-ui/vben-form.md new file mode 100644 index 000000000..d23e869bc --- /dev/null +++ b/docs/src/en/components/common-ui/vben-form.md @@ -0,0 +1,203 @@ +--- +outline: deep +--- + +# Vben Form + +`Vben Form` is the shared form abstraction used across different UI-library variants such as `Ant Design Vue`, `Element Plus`, `Naive UI`, and other adapters added inside this repository. + +> If some details are not obvious from the docs, check the live demos as well. + +## Adapter Setup + +Each app keeps its own adapter layer under `src/adapter/form.ts` and `src/adapter/component/index.ts`. + +The current adapter pattern is: + +- initialize the shared component adapter first +- call `setupVbenForm(...)` +- map special `v-model:*` prop names through `modelPropNameMap` +- keep the form empty state aligned with the actual UI library behavior + +### Form Adapter Example + +```ts +import type { + VbenFormSchema as FormSchema, + VbenFormProps, +} from '@vben/common-ui'; + +import type { ComponentType } from './component'; + +import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui'; +import { $t } from '@vben/locales'; + +import { initComponentAdapter } from './component'; + +initComponentAdapter(); +setupVbenForm({ + config: { + baseModelPropName: 'value', + emptyStateValue: null, + modelPropNameMap: { + Checkbox: 'checked', + Radio: 'checked', + Switch: 'checked', + Upload: 'fileList', + }, + }, + defineRules: { + required: (value, _params, ctx) => { + if (value === undefined || value === null || value.length === 0) { + return $t('ui.formRules.required', [ctx.label]); + } + return true; + }, + selectRequired: (value, _params, ctx) => { + if (value === undefined || value === null) { + return $t('ui.formRules.selectRequired', [ctx.label]); + } + return true; + }, + }, +}); + +const useVbenForm = useForm; + +export { useVbenForm, z }; +export type VbenFormSchema = FormSchema; +export type { VbenFormProps }; +``` + +### Component Adapter Example + +```ts +import type { Component, SetupContext } from 'vue'; + +import type { BaseFormComponentType } from '@vben/common-ui'; + +import { h } from 'vue'; + +import { globalShareState } from '@vben/common-ui'; +import { $t } from '@vben/locales'; +import { + AutoComplete, + Button, + Checkbox, + CheckboxGroup, + DatePicker, + Divider, + Input, + InputNumber, + InputPassword, + Mentions, + notification, + Radio, + RadioGroup, + RangePicker, + Rate, + Select, + Space, + Switch, + Textarea, + TimePicker, + TreeSelect, + Upload, +} from 'ant-design-vue'; + +const withDefaultPlaceholder = ( + component: T, + type: 'input' | 'select', +) => { + return (props: any, { attrs, slots }: Omit) => { + const placeholder = props?.placeholder || $t(`ui.placeholder.${type}`); + return h(component, { ...props, ...attrs, placeholder }, slots); + }; +}; + +export type ComponentType = + | 'AutoComplete' + | 'Checkbox' + | 'CheckboxGroup' + | 'DatePicker' + | 'DefaultButton' + | 'Divider' + | 'Input' + | 'InputNumber' + | 'InputPassword' + | 'Mentions' + | 'PrimaryButton' + | 'Radio' + | 'RadioGroup' + | 'RangePicker' + | 'Rate' + | 'Select' + | 'Space' + | 'Switch' + | 'Textarea' + | 'TimePicker' + | 'TreeSelect' + | 'Upload' + | BaseFormComponentType; + +async function initComponentAdapter() { + const components: Partial> = { + AutoComplete, + Checkbox, + CheckboxGroup, + DatePicker, + DefaultButton: (props, { attrs, slots }) => { + return h(Button, { ...props, attrs, type: 'default' }, slots); + }, + Divider, + Input: withDefaultPlaceholder(Input, 'input'), + InputNumber: withDefaultPlaceholder(InputNumber, 'input'), + InputPassword: withDefaultPlaceholder(InputPassword, 'input'), + Mentions: withDefaultPlaceholder(Mentions, 'input'), + PrimaryButton: (props, { attrs, slots }) => { + return h(Button, { ...props, attrs, type: 'primary' }, slots); + }, + Radio, + RadioGroup, + RangePicker, + Rate, + Select: withDefaultPlaceholder(Select, 'select'), + Space, + Switch, + Textarea: withDefaultPlaceholder(Textarea, 'input'), + TimePicker, + TreeSelect: withDefaultPlaceholder(TreeSelect, 'select'), + Upload, + }; + + globalShareState.setComponents(components); + globalShareState.defineMessage({ + copyPreferencesSuccess: (title, content) => { + notification.success({ + description: content, + message: title, + placement: 'bottomRight', + }); + }, + }); +} + +export { initComponentAdapter }; +``` + +## Basic Usage + +Create the form through `useVbenForm`: + + + +## Key API Notes + +- `useVbenForm` returns `[Form, formApi]` +- `formApi.getFieldComponentRef()` and `formApi.getFocusedField()` are available in current versions +- `handleValuesChange(values, fieldsChanged)` includes the second parameter in newer versions +- `fieldMappingTime` and `scrollToFirstError` are part of the current form props + +## Reference + +For the complete Chinese API tables and more examples, see the Chinese component page if you need the full parameter matrix. diff --git a/docs/src/en/components/common-ui/vben-modal.md b/docs/src/en/components/common-ui/vben-modal.md new file mode 100644 index 000000000..0971c4413 --- /dev/null +++ b/docs/src/en/components/common-ui/vben-modal.md @@ -0,0 +1,56 @@ +--- +outline: deep +--- + +# Vben Modal + +`Vben Modal` is the shared modal wrapper used by the framework. It supports draggable behavior, fullscreen mode, auto-height handling, loading state, connected components, and an imperative API. + +## Basic Usage + +```ts +const [Modal, modalApi] = useVbenModal({ + // props + // events +}); +``` + + + +## Current Usage Notes + +- If you use `connectedComponent`, the inner and outer components share data through `modalApi.setData()` and `modalApi.getData()`. +- When `connectedComponent` is present, avoid pushing extra modal props through the connected side. Prefer `useVbenModal(...)` or `modalApi.setState(...)`. +- Default modal behavior can be adjusted in `apps//src/bootstrap.ts` through `setDefaultModalProps(...)`. + +## Key Props + +| Prop | Description | Type | +| --- | --- | --- | +| `appendToMain` | mount inside the main content area instead of `body` | `boolean` | +| `connectedComponent` | connect an inner component to the modal wrapper | `Component` | +| `animationType` | modal enter/leave animation | `'slide' \| 'scale'` | +| `fullscreenButton` | show or hide the fullscreen toggle | `boolean` | +| `overlayBlur` | blur amount for the overlay | `number` | +| `submitting` | lock modal interactions while submitting | `boolean` | + +## Events + +| Event | Description | Type | +| --- | --- | --- | +| `onBeforeClose` | called before close; returning `false` or rejecting prevents close | `() => Promise \| boolean \| undefined` | +| `onOpenChange` | called when open state changes | `(isOpen: boolean) => void` | +| `onOpened` | called after open animation completes | `() => void` | +| `onClosed` | called after close animation completes | `() => void` | + +## modalApi + +| Method | Description | +| ----------------------- | ------------------------------------- | +| `setState(...)` | updates modal state | +| `open()` | opens the modal | +| `close()` | closes the modal | +| `setData(data)` | stores shared data | +| `getData()` | reads shared data | +| `lock(isLocked = true)` | locks the modal into submitting state | +| `unlock()` | alias for `lock(false)` | diff --git a/docs/src/en/components/common-ui/vben-vxe-table.md b/docs/src/en/components/common-ui/vben-vxe-table.md new file mode 100644 index 000000000..2fba1dc0c --- /dev/null +++ b/docs/src/en/components/common-ui/vben-vxe-table.md @@ -0,0 +1,87 @@ +--- +outline: deep +--- + +# Vben Vxe Table + +`Vben Vxe Table` wraps `vxe-table` together with `Vben Form` so you can build searchable data grids with a shared API. + +## Adapter Example + +The current renderer adapter uses `renderTableDefault(...)` for table cell rendering: + +```ts +vxeUI.renderer.add('CellImage', { + renderTableDefault(_renderOpts, params) { + const { column, row } = params; + return h(Image, { src: row[column.field] }); + }, +}); + +vxeUI.renderer.add('CellLink', { + renderTableDefault(renderOpts) { + const { props } = renderOpts; + return h( + Button, + { size: 'small', type: 'link' }, + { default: () => props?.text }, + ); + }, +}); +``` + +## Basic Usage + +```vue + + + +``` + + + +## GridApi + +| Method | Description | Type | +| --- | --- | --- | +| `setLoading` | update loading state | `(loading: boolean) => void` | +| `setGridOptions` | merge new grid options | `(options: Partial) => void` | +| `reload` | reload data and reset pagination | `(params?: Record) => void` | +| `query` | query data while keeping the current page | `(params?: Record) => void` | +| `grid` | `vxe-grid` instance | `VxeGridInstance` | +| `formApi` | search form API | `FormApi` | +| `toggleSearchForm` | toggle or force the search form visible state | `(show?: boolean) => boolean` | + +## Props + +| Prop | Description | Type | +| --- | --- | --- | +| `tableTitle` | table title | `string` | +| `tableTitleHelp` | help text for the table title | `string` | +| `class` | class for the outer container | `string` | +| `gridClass` | class for the `vxe-grid` node | `string` | +| `gridOptions` | `vxe-grid` options | `DeepPartial` | +| `gridEvents` | `vxe-grid` event handlers | `DeepPartial` | +| `formOptions` | search form options | `VbenFormProps` | +| `showSearchForm` | whether the search form is visible | `boolean` | +| `separator` | separator between the search form and table body | `boolean \| SeparatorOptions` | + +## Slots + +| Slot | Description | +| ----------------- | ------------------------------------------------------- | +| `toolbar-actions` | left side of the toolbar, near the title | +| `toolbar-tools` | right side of the toolbar, before built-in tool buttons | +| `table-title` | custom table title | + +All named slots starting with `form-` are forwarded to the search form. diff --git a/docs/src/en/components/introduction.md b/docs/src/en/components/introduction.md new file mode 100644 index 000000000..806ef09ff --- /dev/null +++ b/docs/src/en/components/introduction.md @@ -0,0 +1,15 @@ +# Introduction + +::: info README + +This section documents the framework components, including their usage patterns, configuration points, and major APIs. If the built-in wrappers do not fit your needs, you can always use native components directly or build your own abstractions. + +::: + +## Layout Components + +Layout components are usually used as top-level containers inside the page content area. They provide shared layout styles and some baseline behavior. + +## Common Components + +Common components include frequently used UI building blocks such as modals, drawers, forms, and API-backed selectors. Most of them are implemented on top of shared Tailwind CSS and shadcn-vue based primitives, while still allowing each app to adapt them to its own UI library. diff --git a/docs/src/en/components/layout-ui/page.md b/docs/src/en/components/layout-ui/page.md new file mode 100644 index 000000000..467d30c4c --- /dev/null +++ b/docs/src/en/components/layout-ui/page.md @@ -0,0 +1,29 @@ +--- +outline: deep +--- + +# Page + +`Page` is the standard top-level layout container for business pages. It provides a header area, a content area, and an optional footer area. + +## Props + +| Prop | Description | Type | Default | +| --- | --- | --- | --- | +| `title` | page title | `string \| slot` | - | +| `description` | page description | `string \| slot` | - | +| `contentClass` | class for the content area | `string` | - | +| `headerClass` | class for the header area | `string` | - | +| `footerClass` | class for the footer area | `string` | - | +| `autoContentHeight` | auto-calculate the content area height from the visible layout height | `boolean` | `false` | +| `heightOffset` | extra height offset subtracted from the content area when auto height is enabled | `number` | `0` | + +## Slots + +| Slot | Description | +| ------------- | ------------------------- | +| `default` | page content | +| `title` | custom title | +| `description` | custom description | +| `extra` | right-side header content | +| `footer` | footer content | diff --git a/eslint.config.mjs b/eslint.config.mjs index b29b567fa..63bd9adc3 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,5 +1,3 @@ -// @ts-check - import { defineConfig } from '@vben/eslint-config'; export default defineConfig(); diff --git a/internal/lint-configs/commitlint-config/package.json b/internal/lint-configs/commitlint-config/package.json index 3377803d9..0fef91827 100644 --- a/internal/lint-configs/commitlint-config/package.json +++ b/internal/lint-configs/commitlint-config/package.json @@ -1,6 +1,6 @@ { "name": "@vben/commitlint-config", - "version": "5.6.0", + "version": "5.7.0", "private": true, "homepage": "https://github.com/vbenjs/vue-vben-admin", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", diff --git a/internal/lint-configs/eslint-config/package.json b/internal/lint-configs/eslint-config/package.json index e9ba6a303..db7f699ac 100644 --- a/internal/lint-configs/eslint-config/package.json +++ b/internal/lint-configs/eslint-config/package.json @@ -1,6 +1,6 @@ { "name": "@vben/eslint-config", - "version": "5.6.0", + "version": "5.7.0", "private": true, "homepage": "https://github.com/vbenjs/vue-vben-admin", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", @@ -27,32 +27,22 @@ } }, "dependencies": { - "eslint-config-turbo": "catalog:", - "eslint-plugin-command": "catalog:", - "eslint-plugin-import-x": "catalog:" + "@vben/oxlint-config": "workspace:*" }, "devDependencies": { "@eslint/js": "catalog:", - "@types/eslint": "catalog:", "@typescript-eslint/eslint-plugin": "catalog:", "@typescript-eslint/parser": "catalog:", "eslint": "catalog:", - "eslint-plugin-eslint-comments": "catalog:", - "eslint-plugin-jsdoc": "catalog:", "eslint-plugin-jsonc": "catalog:", "eslint-plugin-n": "catalog:", - "eslint-plugin-no-only-tests": "catalog:", "eslint-plugin-perfectionist": "catalog:", "eslint-plugin-pnpm": "catalog:", - "eslint-plugin-prettier": "catalog:", - "eslint-plugin-regexp": "catalog:", "eslint-plugin-unicorn": "catalog:", "eslint-plugin-unused-imports": "catalog:", - "eslint-plugin-vitest": "catalog:", "eslint-plugin-vue": "catalog:", "eslint-plugin-yml": "catalog:", "globals": "catalog:", - "jsonc-eslint-parser": "catalog:", "vue-eslint-parser": "catalog:", "yaml-eslint-parser": "catalog:" } diff --git a/internal/lint-configs/eslint-config/src/configs/command.ts b/internal/lint-configs/eslint-config/src/configs/command.ts deleted file mode 100644 index d0c902de2..000000000 --- a/internal/lint-configs/eslint-config/src/configs/command.ts +++ /dev/null @@ -1,9 +0,0 @@ -import createCommand from 'eslint-plugin-command/config'; - -export async function command() { - return [ - { - ...createCommand(), - }, - ]; -} diff --git a/internal/lint-configs/eslint-config/src/configs/comments.ts b/internal/lint-configs/eslint-config/src/configs/comments.ts deleted file mode 100644 index 77ccd5dde..000000000 --- a/internal/lint-configs/eslint-config/src/configs/comments.ts +++ /dev/null @@ -1,24 +0,0 @@ -import type { Linter } from 'eslint'; - -import { interopDefault } from '../util'; - -export async function comments(): Promise { - const [pluginComments] = await Promise.all([ - // @ts-expect-error - no types - interopDefault(import('eslint-plugin-eslint-comments')), - ] as const); - - return [ - { - plugins: { - 'eslint-comments': pluginComments, - }, - rules: { - 'eslint-comments/no-aggregating-enable': 'error', - 'eslint-comments/no-duplicate-disable': 'error', - 'eslint-comments/no-unlimited-disable': 'error', - 'eslint-comments/no-unused-enable': 'error', - }, - }, - ]; -} diff --git a/internal/lint-configs/eslint-config/src/configs/disableds.ts b/internal/lint-configs/eslint-config/src/configs/disableds.ts deleted file mode 100644 index 152b84cac..000000000 --- a/internal/lint-configs/eslint-config/src/configs/disableds.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type { Linter } from 'eslint'; - -export async function disableds(): Promise { - return [ - { - files: ['**/__tests__/**/*.?([cm])[jt]s?(x)'], - name: 'disables/test', - rules: { - '@typescript-eslint/ban-ts-comment': 'off', - 'no-console': 'off', - }, - }, - { - files: ['**/*.d.ts'], - name: 'disables/dts', - rules: { - '@typescript-eslint/triple-slash-reference': 'off', - }, - }, - { - files: ['**/*.js', '**/*.mjs', '**/*.cjs'], - name: 'disables/js', - rules: { - '@typescript-eslint/explicit-module-boundary-types': 'off', - }, - }, - ]; -} diff --git a/internal/lint-configs/eslint-config/src/configs/ignores.ts b/internal/lint-configs/eslint-config/src/configs/ignores.ts index 0a05e9e9e..29b16ce6a 100644 --- a/internal/lint-configs/eslint-config/src/configs/ignores.ts +++ b/internal/lint-configs/eslint-config/src/configs/ignores.ts @@ -48,6 +48,12 @@ export async function ignores(): Promise { '**/*.woff', '**/.github', '**/lefthook.yml', + + '**/.agent/**', + '**/.agents/**', + '**/.codex/**', + '**/.claude/**', + '**/.cursor/**', ], }, ]; diff --git a/internal/lint-configs/eslint-config/src/configs/import.ts b/internal/lint-configs/eslint-config/src/configs/import.ts deleted file mode 100644 index ce6cf65d9..000000000 --- a/internal/lint-configs/eslint-config/src/configs/import.ts +++ /dev/null @@ -1,25 +0,0 @@ -import type { Linter } from 'eslint'; - -import * as pluginImport from 'eslint-plugin-import-x'; - -export async function importPluginConfig(): Promise { - return [ - { - plugins: { - // @ts-expect-error - This is a dynamic import - import: pluginImport, - }, - rules: { - 'import/consistent-type-specifier-style': ['error', 'prefer-top-level'], - 'import/first': 'error', - 'import/newline-after-import': 'error', - 'import/no-duplicates': 'error', - 'import/no-mutable-exports': 'error', - 'import/no-named-default': 'error', - 'import/no-self-import': 'error', - 'import/no-unresolved': 'off', - 'import/no-webpack-loader-syntax': 'error', - }, - }, - ]; -} diff --git a/internal/lint-configs/eslint-config/src/configs/index.ts b/internal/lint-configs/eslint-config/src/configs/index.ts index 50f8e1cd5..4ed3ea61e 100644 --- a/internal/lint-configs/eslint-config/src/configs/index.ts +++ b/internal/lint-configs/eslint-config/src/configs/index.ts @@ -1,18 +1,9 @@ -export * from './command'; -export * from './comments'; -export * from './disableds'; export * from './ignores'; -export * from './import'; export * from './javascript'; -export * from './jsdoc'; export * from './jsonc'; export * from './node'; export * from './perfectionist'; export * from './pnpm'; -export * from './prettier'; -export * from './regexp'; -export * from './test'; -export * from './turbo'; export * from './typescript'; export * from './unicorn'; export * from './vue'; diff --git a/internal/lint-configs/eslint-config/src/configs/javascript.ts b/internal/lint-configs/eslint-config/src/configs/javascript.ts index 44cf5b6e6..2019ecdd2 100644 --- a/internal/lint-configs/eslint-config/src/configs/javascript.ts +++ b/internal/lint-configs/eslint-config/src/configs/javascript.ts @@ -4,7 +4,73 @@ import js from '@eslint/js'; import pluginUnusedImports from 'eslint-plugin-unused-imports'; import globals from 'globals'; +const rulesCoveredByOxlint = new Set([ + 'constructor-super', + 'for-direction', + 'getter-return', + 'no-async-promise-executor', + 'no-case-declarations', + 'no-class-assign', + 'no-compare-neg-zero', + 'no-cond-assign', + 'no-const-assign', + 'no-constant-binary-expression', + 'no-constant-condition', + 'no-debugger', + 'no-delete-var', + 'no-dupe-args', + 'no-dupe-class-members', + 'no-dupe-else-if', + 'no-dupe-keys', + 'no-duplicate-case', + 'no-empty', + 'no-empty-character-class', + 'no-empty-pattern', + 'no-empty-static-block', + 'no-ex-assign', + 'no-extra-boolean-cast', + 'no-fallthrough', + 'no-func-assign', + 'no-global-assign', + 'no-import-assign', + 'no-invalid-regexp', + 'no-irregular-whitespace', + 'no-loss-of-precision', + 'no-misleading-character-class', + 'no-new-native-nonconstructor', + 'no-nonoctal-decimal-escape', + 'no-obj-calls', + 'no-prototype-builtins', + 'no-redeclare', + 'no-regex-spaces', + 'no-self-assign', + 'no-setter-return', + 'no-shadow-restricted-names', + 'no-sparse-arrays', + 'no-this-before-super', + 'no-unreachable', + 'no-unsafe-finally', + 'no-unsafe-negation', + 'no-unsafe-optional-chaining', + 'no-unused-labels', + 'no-unused-private-class-members', + 'no-unused-vars', + 'no-useless-backreference', + 'no-useless-catch', + 'no-useless-escape', + 'no-with', + 'require-yield', + 'use-isnan', + 'valid-typeof', +]); + export async function javascript(): Promise { + const recommendedRules = Object.fromEntries( + Object.entries(js.configs.recommended.rules).filter( + ([ruleName]) => !rulesCoveredByOxlint.has(ruleName), + ), + ); + return [ { languageOptions: { @@ -33,79 +99,11 @@ export async function javascript(): Promise { 'unused-imports': pluginUnusedImports, }, rules: { - ...js.configs.recommended.rules, - 'accessor-pairs': [ - 'error', - { enforceForClassMembers: true, setWithoutGet: true }, - ], - 'array-callback-return': 'error', - 'block-scoped-var': 'error', - 'constructor-super': 'error', - 'default-case-last': 'error', + ...recommendedRules, 'dot-notation': ['error', { allowKeywords: true }], - eqeqeq: ['error', 'always'], 'keyword-spacing': 'off', - - 'new-cap': [ - 'error', - { capIsNew: false, newIsCap: true, properties: true }, - ], - 'no-alert': 'error', - 'no-array-constructor': 'error', - 'no-async-promise-executor': 'error', - 'no-caller': 'error', - 'no-case-declarations': 'error', - 'no-class-assign': 'error', - 'no-compare-neg-zero': 'error', - 'no-cond-assign': ['error', 'always'], - 'no-console': ['error', { allow: ['warn', 'error'] }], - 'no-const-assign': 'error', 'no-control-regex': 'error', - 'no-debugger': 'error', - 'no-delete-var': 'error', - 'no-dupe-args': 'error', - 'no-dupe-class-members': 'error', - 'no-dupe-keys': 'error', - 'no-duplicate-case': 'error', - 'no-empty': ['error', { allowEmptyCatch: true }], - 'no-empty-character-class': 'error', 'no-empty-function': 'off', - 'no-empty-pattern': 'error', - 'no-eval': 'error', - 'no-ex-assign': 'error', - 'no-extend-native': 'error', - 'no-extra-bind': 'error', - 'no-extra-boolean-cast': 'error', - 'no-fallthrough': 'error', - 'no-func-assign': 'error', - 'no-global-assign': 'error', - 'no-implied-eval': 'error', - 'no-import-assign': 'error', - 'no-invalid-regexp': 'error', - 'no-irregular-whitespace': 'error', - 'no-iterator': 'error', - 'no-labels': ['error', { allowLoop: false, allowSwitch: false }], - 'no-lone-blocks': 'error', - 'no-loss-of-precision': 'error', - 'no-misleading-character-class': 'error', - 'no-multi-str': 'error', - 'no-new': 'error', - 'no-new-func': 'error', - 'no-new-object': 'error', - 'no-new-symbol': 'error', - 'no-new-wrappers': 'error', - 'no-obj-calls': 'error', - 'no-octal': 'error', - 'no-octal-escape': 'error', - 'no-proto': 'error', - 'no-prototype-builtins': 'error', - 'no-redeclare': ['error', { builtinGlobals: false }], - 'no-regex-spaces': 'error', - 'no-restricted-globals': [ - 'error', - { message: 'Use `globalThis` instead.', name: 'global' }, - { message: 'Use `globalThis` instead.', name: 'self' }, - ], 'no-restricted-properties': [ 'error', { @@ -138,84 +136,9 @@ export async function javascript(): Promise { 'TSEnumDeclaration[const=true]', 'TSExportAssignment', ], - 'no-self-assign': ['error', { props: true }], - 'no-self-compare': 'error', - 'no-sequences': 'error', - 'no-shadow-restricted-names': 'error', - 'no-sparse-arrays': 'error', - 'no-template-curly-in-string': 'error', - 'no-this-before-super': 'error', - 'no-throw-literal': 'error', 'no-undef': 'off', - 'no-undef-init': 'error', - 'no-unexpected-multiline': 'error', - 'no-unmodified-loop-condition': 'error', - 'no-unneeded-ternary': ['error', { defaultAssignment: false }], - 'no-unreachable': 'error', 'no-unreachable-loop': 'error', - 'no-unsafe-finally': 'error', - 'no-unsafe-negation': 'error', - 'no-unused-expressions': [ - 'error', - { - allowShortCircuit: true, - allowTaggedTemplates: true, - allowTernary: true, - }, - ], - 'no-unused-vars': [ - 'error', - { - args: 'none', - caughtErrors: 'none', - ignoreRestSiblings: true, - vars: 'all', - }, - ], - 'no-use-before-define': [ - 'error', - { classes: false, functions: false, variables: false }, - ], - 'no-useless-backreference': 'error', - 'no-useless-call': 'error', - 'no-useless-catch': 'error', - 'no-useless-computed-key': 'error', - 'no-useless-constructor': 'error', - 'no-useless-rename': 'error', - 'no-useless-return': 'error', - 'no-var': 'error', - 'no-with': 'error', - 'object-shorthand': [ - 'error', - 'always', - { avoidQuotes: true, ignoreConstructors: false }, - ], - 'one-var': ['error', { initialized: 'never' }], - 'prefer-arrow-callback': [ - 'error', - { - allowNamedFunctions: false, - allowUnboundThis: true, - }, - ], - 'prefer-const': [ - 'error', - { - destructuring: 'all', - ignoreReadBeforeAssign: true, - }, - ], - 'prefer-exponentiation-operator': 'error', - - 'prefer-promise-reject-errors': 'error', - 'prefer-regex-literals': ['error', { disallowRedundantWrapping: true }], - 'prefer-rest-params': 'error', - 'prefer-spread': 'error', - 'prefer-template': 'error', 'space-before-function-paren': 'off', - 'spaced-comment': 'error', - 'symbol-description': 'error', - 'unicode-bom': ['error', 'never'], 'unused-imports/no-unused-imports': 'error', 'unused-imports/no-unused-vars': [ @@ -227,14 +150,6 @@ export async function javascript(): Promise { varsIgnorePattern: '^_', }, ], - 'use-isnan': [ - 'error', - { enforceForIndexOf: true, enforceForSwitchCase: true }, - ], - 'valid-typeof': ['error', { requireStringLiterals: true }], - - 'vars-on-top': 'error', - yoda: ['error', 'never'], }, }, ]; diff --git a/internal/lint-configs/eslint-config/src/configs/jsdoc.ts b/internal/lint-configs/eslint-config/src/configs/jsdoc.ts deleted file mode 100644 index 136819769..000000000 --- a/internal/lint-configs/eslint-config/src/configs/jsdoc.ts +++ /dev/null @@ -1,34 +0,0 @@ -import type { Linter } from 'eslint'; - -import { interopDefault } from '../util'; - -export async function jsdoc(): Promise { - const [pluginJsdoc] = await Promise.all([ - interopDefault(import('eslint-plugin-jsdoc')), - ] as const); - - return [ - { - plugins: { - jsdoc: pluginJsdoc, - }, - rules: { - 'jsdoc/check-access': 'warn', - 'jsdoc/check-param-names': 'warn', - 'jsdoc/check-property-names': 'warn', - 'jsdoc/check-types': 'warn', - 'jsdoc/empty-tags': 'warn', - 'jsdoc/implements-on-classes': 'warn', - 'jsdoc/no-defaults': 'warn', - 'jsdoc/no-multi-asterisks': 'warn', - 'jsdoc/require-param-name': 'warn', - 'jsdoc/require-property': 'warn', - 'jsdoc/require-property-description': 'warn', - 'jsdoc/require-property-name': 'warn', - 'jsdoc/require-returns-check': 'warn', - 'jsdoc/require-returns-description': 'warn', - 'jsdoc/require-yields-check': 'warn', - }, - }, - ]; -} diff --git a/internal/lint-configs/eslint-config/src/configs/jsonc.ts b/internal/lint-configs/eslint-config/src/configs/jsonc.ts index 4b92ff447..3f33d13ad 100644 --- a/internal/lint-configs/eslint-config/src/configs/jsonc.ts +++ b/internal/lint-configs/eslint-config/src/configs/jsonc.ts @@ -3,17 +3,12 @@ import type { Linter } from 'eslint'; import { interopDefault } from '../util'; export async function jsonc(): Promise { - const [pluginJsonc, parserJsonc] = await Promise.all([ - interopDefault(import('eslint-plugin-jsonc')), - interopDefault(import('jsonc-eslint-parser')), - ] as const); + const pluginJsonc = await interopDefault(import('eslint-plugin-jsonc')); return [ { files: ['**/*.json', '**/*.json5', '**/*.jsonc', '*.code-workspace'], - languageOptions: { - parser: parserJsonc as any, - }, + language: 'jsonc/x', plugins: { jsonc: pluginJsonc as any, }, diff --git a/internal/lint-configs/eslint-config/src/configs/node.ts b/internal/lint-configs/eslint-config/src/configs/node.ts index f8f266436..65f81fd53 100644 --- a/internal/lint-configs/eslint-config/src/configs/node.ts +++ b/internal/lint-configs/eslint-config/src/configs/node.ts @@ -13,7 +13,6 @@ export async function node(): Promise { rules: { 'n/handle-callback-err': ['error', '^(err|error)$'], 'n/no-deprecated-api': 'error', - 'n/no-exports-assign': 'error', 'n/no-extraneous-import': [ 'error', { @@ -23,13 +22,10 @@ export async function node(): Promise { 'vitest', 'vite', '@vue/test-utils', - '@vben/tailwind-config', '@playwright/test', ], }, ], - 'n/no-new-require': 'error', - 'n/no-path-concat': 'error', // 'n/no-unpublished-import': 'off', 'n/no-unsupported-features/es-syntax': [ 'error', @@ -44,6 +40,33 @@ export async function node(): Promise { 'n/process-exit-as-throw': 'error', }, }, + { + files: [ + '**/__tests__/**/*.?([cm])[jt]s?(x)', + '**/*.spec.?([cm])[jt]s?(x)', + '**/*.test.?([cm])[jt]s?(x)', + '**/*.bench.?([cm])[jt]s?(x)', + '**/*.benchmark.?([cm])[jt]s?(x)', + ], + rules: { + 'n/prefer-global/process': 'off', + }, + }, + { + files: ['apps/backend-mock/**/**', 'docs/**/**'], + rules: { + 'n/no-extraneous-import': 'off', + 'n/prefer-global/buffer': 'off', + 'n/prefer-global/process': 'off', + }, + }, + { + files: ['**/**/playwright.config.ts'], + rules: { + 'n/prefer-global/buffer': 'off', + 'n/prefer-global/process': 'off', + }, + }, { files: [ 'scripts/**/*.?([cm])[jt]s?(x)', diff --git a/internal/lint-configs/eslint-config/src/configs/perfectionist.ts b/internal/lint-configs/eslint-config/src/configs/perfectionist.ts index 4a7d12fe9..cb8c98c3a 100644 --- a/internal/lint-configs/eslint-config/src/configs/perfectionist.ts +++ b/internal/lint-configs/eslint-config/src/configs/perfectionist.ts @@ -21,41 +21,58 @@ export async function perfectionist(): Promise { 'perfectionist/sort-imports': [ 'error', { - customGroups: { - type: { - 'vben-core-type': ['^@vben-core/.+'], - 'vben-type': ['^@vben/.+'], - 'vue-type': ['^vue$', '^vue-.+', '^@vue/.+'], + customGroups: [ + { + selector: 'type', + groupName: 'vben-core-type', + elementNamePattern: '^@vben-core/.+', }, - value: { - vben: ['^@vben/.+'], - 'vben-core': ['^@vben-core/.+'], - vue: ['^vue$', '^vue-.+', '^@vue/.+'], + { + selector: 'type', + groupName: 'vben-type', + elementNamePattern: '^@vben/.+', }, - }, + { + selector: 'type', + groupName: 'vue-type', + elementNamePattern: ['^vue$', '^vue-.+', '^@vue/.+'], + }, + { + groupName: 'vben', + elementNamePattern: '^@vben/.+', + }, + { + groupName: 'vben-core', + elementNamePattern: '^@vben-core/.+', + }, + { + groupName: 'vue', + elementNamePattern: ['^vue$', '^vue-.+', '^@vue/.+'], + }, + ], environment: 'node', groups: [ - ['external-type', 'builtin-type', 'type'], + ['type-external', 'type-builtin', 'type-import'], 'vue-type', 'vben-type', 'vben-core-type', - ['parent-type', 'sibling-type', 'index-type'], - ['internal-type'], - 'builtin', + ['type-parent', 'type-sibling', 'type-index'], + ['type-internal'], + 'value-builtin', 'vue', 'vben', 'vben-core', - 'external', - 'internal', - ['parent', 'sibling', 'index'], + 'value-external', + 'value-internal', + ['value-parent', 'value-sibling', 'value-index'], 'side-effect', 'side-effect-style', 'style', - 'object', + 'ts-equals-import', 'unknown', ], internalPattern: ['^#/.+'], - newlinesBetween: 'always', + newlinesBetween: 1, order: 'asc', type: 'natural', }, diff --git a/internal/lint-configs/eslint-config/src/configs/pnpm.ts b/internal/lint-configs/eslint-config/src/configs/pnpm.ts index a3b28a7f6..5fe5afbf0 100644 --- a/internal/lint-configs/eslint-config/src/configs/pnpm.ts +++ b/internal/lint-configs/eslint-config/src/configs/pnpm.ts @@ -3,18 +3,15 @@ import type { Linter } from 'eslint'; import { interopDefault } from '../util'; export async function pnpm(): Promise { - const [pluginPnpm, parserPnpm, parserJsonc] = await Promise.all([ + const [pluginPnpm, parserPnpm] = await Promise.all([ interopDefault(import('eslint-plugin-pnpm')), interopDefault(import('yaml-eslint-parser')), - interopDefault(import('jsonc-eslint-parser')), ] as const); return [ { files: ['package.json', '**/package.json'], - languageOptions: { - parser: parserJsonc, - }, + language: 'jsonc/x', plugins: { pnpm: pluginPnpm, }, diff --git a/internal/lint-configs/eslint-config/src/configs/prettier.ts b/internal/lint-configs/eslint-config/src/configs/prettier.ts deleted file mode 100644 index 3cd7af40e..000000000 --- a/internal/lint-configs/eslint-config/src/configs/prettier.ts +++ /dev/null @@ -1,19 +0,0 @@ -import type { Linter } from 'eslint'; - -import { interopDefault } from '../util'; - -export async function prettier(): Promise { - const [pluginPrettier] = await Promise.all([ - interopDefault(import('eslint-plugin-prettier')), - ] as const); - return [ - { - plugins: { - prettier: pluginPrettier, - }, - rules: { - 'prettier/prettier': 'error', - }, - }, - ]; -} diff --git a/internal/lint-configs/eslint-config/src/configs/regexp.ts b/internal/lint-configs/eslint-config/src/configs/regexp.ts deleted file mode 100644 index c0f4c9f43..000000000 --- a/internal/lint-configs/eslint-config/src/configs/regexp.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { Linter } from 'eslint'; - -import { interopDefault } from '../util'; - -export async function regexp(): Promise { - const [pluginRegexp] = await Promise.all([ - interopDefault(import('eslint-plugin-regexp')), - ] as const); - - return [ - { - plugins: { - regexp: pluginRegexp, - }, - rules: { - ...pluginRegexp.configs.recommended.rules, - }, - }, - ]; -} diff --git a/internal/lint-configs/eslint-config/src/configs/test.ts b/internal/lint-configs/eslint-config/src/configs/test.ts deleted file mode 100644 index ddfde2b87..000000000 --- a/internal/lint-configs/eslint-config/src/configs/test.ts +++ /dev/null @@ -1,45 +0,0 @@ -import type { Linter } from 'eslint'; - -import { interopDefault } from '../util'; - -export async function test(): Promise { - const [pluginTest, pluginNoOnlyTests] = await Promise.all([ - interopDefault(import('eslint-plugin-vitest')), - // @ts-expect-error - no types - interopDefault(import('eslint-plugin-no-only-tests')), - ] as const); - - return [ - { - files: [ - `**/__tests__/**/*.?([cm])[jt]s?(x)`, - `**/*.spec.?([cm])[jt]s?(x)`, - `**/*.test.?([cm])[jt]s?(x)`, - `**/*.bench.?([cm])[jt]s?(x)`, - `**/*.benchmark.?([cm])[jt]s?(x)`, - ], - plugins: { - test: { - ...pluginTest, - rules: { - ...pluginTest.rules, - ...pluginNoOnlyTests.rules, - }, - }, - }, - rules: { - 'no-console': 'off', - 'node/prefer-global/process': 'off', - 'test/consistent-test-it': [ - 'error', - { fn: 'it', withinDescribe: 'it' }, - ], - 'test/no-identical-title': 'error', - 'test/no-import-node-test': 'error', - 'test/no-only-tests': 'error', - 'test/prefer-hooks-in-order': 'error', - 'test/prefer-lowercase-title': 'error', - }, - }, - ]; -} diff --git a/internal/lint-configs/eslint-config/src/configs/turbo.ts b/internal/lint-configs/eslint-config/src/configs/turbo.ts deleted file mode 100644 index bcc27eb69..000000000 --- a/internal/lint-configs/eslint-config/src/configs/turbo.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type { Linter } from 'eslint'; - -import { interopDefault } from '../util'; - -export async function turbo(): Promise { - const [pluginTurbo] = await Promise.all([ - interopDefault(import('eslint-config-turbo')), - ] as const); - - return [ - { - plugins: { - turbo: pluginTurbo, - }, - }, - ]; -} diff --git a/internal/lint-configs/eslint-config/src/configs/typescript.ts b/internal/lint-configs/eslint-config/src/configs/typescript.ts index 2f6f97650..5e4157cf2 100644 --- a/internal/lint-configs/eslint-config/src/configs/typescript.ts +++ b/internal/lint-configs/eslint-config/src/configs/typescript.ts @@ -2,11 +2,24 @@ import type { Linter } from 'eslint'; import { interopDefault } from '../util'; +const rulesCoveredByOxlint = new Set([ + '@typescript-eslint/ban-ts-comment', + '@typescript-eslint/no-non-null-assertion', + '@typescript-eslint/no-unused-expressions', + '@typescript-eslint/no-unused-vars', + '@typescript-eslint/triple-slash-reference', +]); + export async function typescript(): Promise { const [pluginTs, parserTs] = await Promise.all([ interopDefault(import('@typescript-eslint/eslint-plugin')), interopDefault(import('@typescript-eslint/parser')), ] as const); + const strictRules = Object.fromEntries( + Object.entries(pluginTs.configs.strict?.rules ?? {}).filter( + ([ruleName]) => !rulesCoveredByOxlint.has(ruleName), + ), + ); return [ { @@ -30,40 +43,14 @@ export async function typescript(): Promise { }, rules: { ...pluginTs.configs['eslint-recommended']?.overrides?.[0]?.rules, - ...pluginTs.configs.strict?.rules, - '@typescript-eslint/ban-ts-comment': [ - 'error', - { - 'ts-check': false, - 'ts-expect-error': 'allow-with-description', - 'ts-ignore': 'allow-with-description', - 'ts-nocheck': 'allow-with-description', - }, - ], - + ...strictRules, // '@typescript-eslint/consistent-type-definitions': ['warn', 'interface'], '@typescript-eslint/consistent-type-definitions': 'off', '@typescript-eslint/explicit-function-return-type': 'off', '@typescript-eslint/explicit-module-boundary-types': 'off', - '@typescript-eslint/no-empty-function': [ - 'error', - { - allow: ['arrowFunctions', 'functions', 'methods'], - }, - ], '@typescript-eslint/no-explicit-any': 'off', '@typescript-eslint/no-namespace': 'off', - '@typescript-eslint/no-non-null-assertion': 'error', - '@typescript-eslint/no-unused-expressions': 'off', - '@typescript-eslint/no-unused-vars': [ - 'error', - { - argsIgnorePattern: '^_', - varsIgnorePattern: '^_', - }, - ], '@typescript-eslint/no-use-before-define': 'off', - '@typescript-eslint/no-var-requires': 'error', 'unused-imports/no-unused-vars': 'off', }, }, diff --git a/internal/lint-configs/eslint-config/src/configs/unicorn.ts b/internal/lint-configs/eslint-config/src/configs/unicorn.ts index 21b19025d..6e779e3bf 100644 --- a/internal/lint-configs/eslint-config/src/configs/unicorn.ts +++ b/internal/lint-configs/eslint-config/src/configs/unicorn.ts @@ -2,10 +2,20 @@ import type { Linter } from 'eslint'; import { interopDefault } from '../util'; +const rulesCoveredByOxlint = new Set([ + 'unicorn/consistent-function-scoping', + 'unicorn/no-process-exit', + 'unicorn/prefer-global-this', + 'unicorn/prefer-module', +]); + export async function unicorn(): Promise { - const [pluginUnicorn] = await Promise.all([ - interopDefault(import('eslint-plugin-unicorn')), - ] as const); + const pluginUnicorn = await interopDefault(import('eslint-plugin-unicorn')); + const recommendedRules = Object.fromEntries( + Object.entries(pluginUnicorn.configs.recommended.rules ?? {}).filter( + ([ruleName]) => !rulesCoveredByOxlint.has(ruleName), + ), + ); return [ { @@ -13,11 +23,10 @@ export async function unicorn(): Promise { unicorn: pluginUnicorn, }, rules: { - ...pluginUnicorn.configs.recommended.rules, + ...recommendedRules, 'unicorn/better-regex': 'off', 'unicorn/consistent-destructuring': 'off', - 'unicorn/consistent-function-scoping': 'off', 'unicorn/expiring-todo-comments': 'off', 'unicorn/filename-case': 'off', 'unicorn/import-style': 'off', @@ -27,19 +36,9 @@ export async function unicorn(): Promise { 'unicorn/prefer-at': 'off', 'unicorn/prefer-dom-node-text-content': 'off', 'unicorn/prefer-export-from': ['error', { ignoreUsedVariables: true }], - 'unicorn/prefer-global-this': 'off', 'unicorn/prefer-top-level-await': 'off', 'unicorn/prevent-abbreviations': 'off', }, }, - { - files: [ - 'scripts/**/*.?([cm])[jt]s?(x)', - 'internal/**/*.?([cm])[jt]s?(x)', - ], - rules: { - 'unicorn/no-process-exit': 'off', - }, - }, ]; } diff --git a/internal/lint-configs/eslint-config/src/configs/vue.ts b/internal/lint-configs/eslint-config/src/configs/vue.ts index 5db72992d..edd576888 100644 --- a/internal/lint-configs/eslint-config/src/configs/vue.ts +++ b/internal/lint-configs/eslint-config/src/configs/vue.ts @@ -128,7 +128,6 @@ export async function vue(): Promise { }, ], 'vue/one-component-per-file': 'error', - 'vue/prefer-import-from-vue': 'error', 'vue/prefer-separate-static-class': 'error', 'vue/prefer-template': 'error', 'vue/prop-name-casing': ['error', 'camelCase'], diff --git a/internal/lint-configs/eslint-config/src/configs/yaml.ts b/internal/lint-configs/eslint-config/src/configs/yaml.ts index 55aa94d53..889371955 100644 --- a/internal/lint-configs/eslint-config/src/configs/yaml.ts +++ b/internal/lint-configs/eslint-config/src/configs/yaml.ts @@ -12,7 +12,7 @@ export async function yaml(): Promise { { files: ['**/*.y?(a)ml'], plugins: { - yaml: pluginYaml as any, + yaml: pluginYaml, }, languageOptions: { parser: parserYaml, diff --git a/internal/lint-configs/eslint-config/src/custom-config.ts b/internal/lint-configs/eslint-config/src/custom-config.ts index 1b51e5a5a..01a72675c 100644 --- a/internal/lint-configs/eslint-config/src/custom-config.ts +++ b/internal/lint-configs/eslint-config/src/custom-config.ts @@ -1,10 +1,6 @@ import type { Linter } from 'eslint'; -const restrictedImportIgnores = [ - '**/vite.config.mts', - '**/tailwind.config.mjs', - '**/postcss.config.mjs', -]; +const restrictedImportIgnores = ['**/vite.config.mts']; const customConfig: Linter.Config[] = [ // shadcn-ui 内部组件是自动生成的,不做太多限制 @@ -25,14 +21,6 @@ const customConfig: Linter.Config[] = [ ignores: restrictedImportIgnores, rules: { 'perfectionist/sort-interfaces': 'off', - 'perfectionist/sort-objects': 'off', - }, - }, - { - files: ['**/**.vue'], - ignores: restrictedImportIgnores, - rules: { - 'perfectionist/sort-objects': 'off', }, }, { @@ -71,7 +59,6 @@ const customConfig: Linter.Config[] = [ ], }, ], - 'perfectionist/sort-interfaces': 'off', }, }, { @@ -145,19 +132,12 @@ const customConfig: Linter.Config[] = [ { files: ['apps/backend-mock/**/**', 'docs/**/**'], rules: { - '@typescript-eslint/no-extraneous-class': 'off', - 'n/no-extraneous-import': 'off', - 'n/prefer-global/buffer': 'off', - 'n/prefer-global/process': 'off', 'no-console': 'off', - 'unicorn/prefer-module': 'off', }, }, { files: ['**/**/playwright.config.ts'], rules: { - 'n/prefer-global/buffer': 'off', - 'n/prefer-global/process': 'off', 'no-console': 'off', }, }, @@ -167,6 +147,12 @@ const customConfig: Linter.Config[] = [ 'no-console': 'off', }, }, + { + files: ['packages/@core/base/shared/src/utils/inference.ts'], + rules: { + 'vue/prefer-import-from-vue': 'off', + }, + }, ]; export { customConfig }; diff --git a/internal/lint-configs/eslint-config/src/index.ts b/internal/lint-configs/eslint-config/src/index.ts index d3bbf5754..f853781a9 100644 --- a/internal/lint-configs/eslint-config/src/index.ts +++ b/internal/lint-configs/eslint-config/src/index.ts @@ -1,21 +1,12 @@ import type { Linter } from 'eslint'; import { - command, - comments, - disableds, ignores, - importPluginConfig, javascript, - jsdoc, jsonc, node, perfectionist, pnpm, - prettier, - regexp, - test, - turbo, typescript, unicorn, vue, @@ -36,20 +27,11 @@ async function defineConfig(config: FlatConfig[] = []) { vue(), javascript(), ignores(), - prettier(), typescript(), jsonc(), - disableds(), - importPluginConfig(), node(), perfectionist(), - comments(), - jsdoc(), unicorn(), - test(), - regexp(), - command(), - turbo(), yaml(), pnpm(), ...customConfig, diff --git a/internal/tailwind-config/build.config.ts b/internal/lint-configs/oxfmt-config/build.config.ts similarity index 58% rename from internal/tailwind-config/build.config.ts rename to internal/lint-configs/oxfmt-config/build.config.ts index 1f3c3c220..97e572c56 100644 --- a/internal/tailwind-config/build.config.ts +++ b/internal/lint-configs/oxfmt-config/build.config.ts @@ -3,8 +3,5 @@ import { defineBuildConfig } from 'unbuild'; export default defineBuildConfig({ clean: true, declaration: true, - entries: ['src/index', './src/postcss.config'], - rollup: { - emitCJS: true, - }, + entries: ['src/index'], }); diff --git a/internal/lint-configs/prettier-config/package.json b/internal/lint-configs/oxfmt-config/package.json similarity index 51% rename from internal/lint-configs/prettier-config/package.json rename to internal/lint-configs/oxfmt-config/package.json index 5ed785d79..3e016797e 100644 --- a/internal/lint-configs/prettier-config/package.json +++ b/internal/lint-configs/oxfmt-config/package.json @@ -1,28 +1,32 @@ { - "name": "@vben/prettier-config", - "version": "5.6.0", + "name": "@vben/oxfmt-config", + "version": "5.7.0", "private": true, "homepage": "https://github.com/vbenjs/vue-vben-admin", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", "repository": { "type": "git", "url": "git+https://github.com/vbenjs/vue-vben-admin.git", - "directory": "internal/lint-configs/prettier-config" + "directory": "internal/lint-configs/oxfmt-config" }, "license": "MIT", "type": "module", + "scripts": { + "stub": "pnpm unbuild --stub" + }, "files": [ "dist" ], - "main": "./index.mjs", - "module": "./index.mjs", + "main": "./dist/index.mjs", + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", "exports": { ".": { - "default": "./index.mjs" + "types": "./dist/index.d.ts", + "import": "./dist/index.mjs" } }, "dependencies": { - "prettier": "catalog:", - "prettier-plugin-tailwindcss": "catalog:" + "oxfmt": "catalog:" } } diff --git a/internal/lint-configs/oxfmt-config/src/index.ts b/internal/lint-configs/oxfmt-config/src/index.ts new file mode 100644 index 000000000..5f88c1809 --- /dev/null +++ b/internal/lint-configs/oxfmt-config/src/index.ts @@ -0,0 +1,39 @@ +import { defineConfig as defineOxfmtConfig } from 'oxfmt'; + +type OxfmtConfig = Parameters[0]; + +const oxfmtConfig = defineOxfmtConfig({ + printWidth: 80, + proseWrap: 'never', + semi: true, + singleQuote: true, + sortPackageJson: false, + trailingComma: 'all', + overrides: [ + { + files: [ + '*.json', + '*.json5', + '*.jsonc', + '*.code-workspace', + '**/*.json', + '**/*.json5', + '**/*.jsonc', + '**/*.code-workspace', + ], + options: { + trailingComma: 'none', + }, + }, + ], +}); + +function defineConfig(config: OxfmtConfig = {}) { + return defineOxfmtConfig({ + ...oxfmtConfig, + ...config, + }); +} + +export { defineConfig, oxfmtConfig }; +export type { OxfmtConfig }; diff --git a/internal/tailwind-config/tsconfig.json b/internal/lint-configs/oxfmt-config/tsconfig.json similarity index 100% rename from internal/tailwind-config/tsconfig.json rename to internal/lint-configs/oxfmt-config/tsconfig.json diff --git a/internal/lint-configs/oxlint-config/build.config.ts b/internal/lint-configs/oxlint-config/build.config.ts new file mode 100644 index 000000000..97e572c56 --- /dev/null +++ b/internal/lint-configs/oxlint-config/build.config.ts @@ -0,0 +1,7 @@ +import { defineBuildConfig } from 'unbuild'; + +export default defineBuildConfig({ + clean: true, + declaration: true, + entries: ['src/index'], +}); diff --git a/internal/lint-configs/oxlint-config/package.json b/internal/lint-configs/oxlint-config/package.json new file mode 100644 index 000000000..05d3bedfe --- /dev/null +++ b/internal/lint-configs/oxlint-config/package.json @@ -0,0 +1,35 @@ +{ + "name": "@vben/oxlint-config", + "version": "5.7.0", + "private": true, + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "internal/lint-configs/oxlint-config" + }, + "license": "MIT", + "type": "module", + "scripts": { + "stub": "pnpm unbuild --stub" + }, + "files": [ + "dist" + ], + "main": "./dist/index.mjs", + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.mjs" + } + }, + "dependencies": { + "@eslint-community/eslint-plugin-eslint-comments": "catalog:", + "eslint-plugin-better-tailwindcss": "catalog:", + "eslint-plugin-command": "catalog:", + "oxlint": "catalog:" + } +} diff --git a/internal/lint-configs/oxlint-config/src/configs/command.ts b/internal/lint-configs/oxlint-config/src/configs/command.ts new file mode 100644 index 000000000..1a3498cb4 --- /dev/null +++ b/internal/lint-configs/oxlint-config/src/configs/command.ts @@ -0,0 +1,15 @@ +import type { OxlintConfig } from 'oxlint'; + +const command: OxlintConfig = { + jsPlugins: [ + { + name: 'command', + specifier: 'eslint-plugin-command', + }, + ], + rules: { + 'command/command': 'error', + }, +}; + +export { command }; diff --git a/internal/lint-configs/oxlint-config/src/configs/comments.ts b/internal/lint-configs/oxlint-config/src/configs/comments.ts new file mode 100644 index 000000000..09c0d012e --- /dev/null +++ b/internal/lint-configs/oxlint-config/src/configs/comments.ts @@ -0,0 +1,18 @@ +import type { OxlintConfig } from 'oxlint'; + +const comments: OxlintConfig = { + jsPlugins: [ + { + name: 'eslint-comments', + specifier: '@eslint-community/eslint-plugin-eslint-comments', + }, + ], + rules: { + 'eslint-comments/no-aggregating-enable': 'error', + 'eslint-comments/no-duplicate-disable': 'error', + 'eslint-comments/no-unlimited-disable': 'error', + 'eslint-comments/no-unused-enable': 'error', + }, +}; + +export { comments }; diff --git a/internal/lint-configs/oxlint-config/src/configs/ignores.ts b/internal/lint-configs/oxlint-config/src/configs/ignores.ts new file mode 100644 index 000000000..858a3cd73 --- /dev/null +++ b/internal/lint-configs/oxlint-config/src/configs/ignores.ts @@ -0,0 +1,17 @@ +import type { OxlintConfig } from 'oxlint'; + +const ignores: OxlintConfig = { + ignorePatterns: [ + '**/dist/**', + '**/node_modules/**', + 'docs/**', + 'playground/public/**', + '**/*.json', + '**/*.md', + '**/*.svg', + '**/*.yaml', + '**/*.yml', + ], +}; + +export { ignores }; diff --git a/internal/lint-configs/oxlint-config/src/configs/import.ts b/internal/lint-configs/oxlint-config/src/configs/import.ts new file mode 100644 index 000000000..8e719ad48 --- /dev/null +++ b/internal/lint-configs/oxlint-config/src/configs/import.ts @@ -0,0 +1,18 @@ +import type { OxlintConfig } from 'oxlint'; + +const importPluginConfig: OxlintConfig = { + rules: { + 'import/consistent-type-specifier-style': ['error', 'prefer-top-level'], + 'import/first': 'error', + 'import/no-duplicates': 'error', + 'import/no-mutable-exports': 'error', + 'import/no-named-as-default': 'off', + 'import/no-named-as-default-member': 'off', + 'import/no-named-default': 'error', + 'import/no-self-import': 'error', + 'import/no-unassigned-import': 'off', + 'import/no-webpack-loader-syntax': 'error', + }, +}; + +export { importPluginConfig }; diff --git a/internal/lint-configs/oxlint-config/src/configs/index.ts b/internal/lint-configs/oxlint-config/src/configs/index.ts new file mode 100644 index 000000000..361ced9a2 --- /dev/null +++ b/internal/lint-configs/oxlint-config/src/configs/index.ts @@ -0,0 +1,96 @@ +import type { OxlintConfig } from 'oxlint'; + +import { defineConfig as defineOxlintConfig } from 'oxlint'; + +import { command } from './command'; +import { comments } from './comments'; +import { ignores } from './ignores'; +import { importPluginConfig } from './import'; +import { javascript } from './javascript'; +import { node } from './node'; +import { overrides } from './overrides'; +import { plugins } from './plugins'; +import { tailwindcss } from './tailwindcss'; +import { test } from './test'; +import { typescript } from './typescript'; +import { unicorn } from './unicorn'; +import { vue } from './vue'; + +function mergeOxlintConfigs(...configs: OxlintConfig[]): OxlintConfig { + const merged: OxlintConfig = {}; + + for (const config of configs) { + merged.categories = + merged.categories && config.categories + ? { ...merged.categories, ...config.categories } + : (config.categories ?? merged.categories); + merged.env = + merged.env && config.env + ? { ...merged.env, ...config.env } + : (config.env ?? merged.env); + merged.globals = + merged.globals && config.globals + ? { ...merged.globals, ...config.globals } + : (config.globals ?? merged.globals); + merged.ignorePatterns = [ + ...(merged.ignorePatterns ?? []), + ...(config.ignorePatterns ?? []), + ]; + merged.jsPlugins = [ + ...new Set([...(merged.jsPlugins ?? []), ...(config.jsPlugins ?? [])]), + ]; + merged.overrides = [ + ...(merged.overrides ?? []), + ...(config.overrides ?? []), + ]; + merged.plugins = [ + ...new Set([...(merged.plugins ?? []), ...(config.plugins ?? [])]), + ]; + merged.rules = + merged.rules && config.rules + ? { ...merged.rules, ...config.rules } + : (config.rules ?? merged.rules); + merged.settings = + merged.settings && config.settings + ? { ...merged.settings, ...config.settings } + : (config.settings ?? merged.settings); + } + + return merged; +} + +const oxlintConfig = defineOxlintConfig( + mergeOxlintConfigs( + javascript, + command, + comments, + ignores, + plugins, + importPluginConfig, + node, + overrides, + tailwindcss, + test, + typescript, + unicorn, + vue, + ), +); + +export { + command, + comments, + ignores, + importPluginConfig, + javascript, + mergeOxlintConfigs, + node, + overrides, + oxlintConfig, + plugins, + tailwindcss, + test, + typescript, + unicorn, + vue, +}; diff --git a/internal/lint-configs/oxlint-config/src/configs/javascript.ts b/internal/lint-configs/oxlint-config/src/configs/javascript.ts new file mode 100644 index 000000000..4ea37891c --- /dev/null +++ b/internal/lint-configs/oxlint-config/src/configs/javascript.ts @@ -0,0 +1,156 @@ +import type { OxlintConfig } from 'oxlint'; + +const javascript: OxlintConfig = { + categories: { + correctness: 'error', + suspicious: 'warn', + }, + env: { + browser: true, + es2021: true, + node: true, + }, + globals: { + document: 'readonly', + navigator: 'readonly', + window: 'readonly', + }, + rules: { + 'accessor-pairs': [ + 'error', + { + enforceForClassMembers: true, + setWithoutGet: true, + }, + ], + 'array-callback-return': 'error', + 'block-scoped-var': 'error', + 'default-case-last': 'error', + eqeqeq: ['error', 'always'], + 'eslint/no-unreachable': 'error', + 'new-cap': [ + 'error', + { + capIsNew: false, + newIsCap: true, + properties: true, + }, + ], + 'no-alert': 'error', + 'no-array-constructor': 'error', + 'no-caller': 'error', + 'no-case-declarations': 'error', + 'no-console': ['error', { allow: ['warn', 'error'] }], + 'no-control-regex': 'off', + 'no-debugger': 'error', + 'no-empty': ['error', { allowEmptyCatch: true }], + 'no-fallthrough': 'error', + 'no-new-func': 'error', + 'no-new-object': 'error', + 'no-new-symbol': 'error', + 'no-labels': ['error', { allowLoop: false, allowSwitch: false }], + 'no-lone-blocks': 'error', + 'no-multi-str': 'error', + 'no-octal': 'error', + 'no-octal-escape': 'error', + 'no-proto': 'error', + 'no-prototype-builtins': 'error', + 'no-redeclare': ['error', { builtinGlobals: false }], + 'no-regex-spaces': 'error', + 'no-self-compare': 'error', + 'no-sequences': 'error', + 'no-shadow': 'off', + 'no-shadow-restricted-names': 'error', + 'eslint/no-empty-function': [ + 'error', + { + allow: ['arrowFunctions', 'functions', 'methods'], + }, + ], + 'no-template-curly-in-string': 'error', + 'no-throw-literal': 'error', + 'no-undef-init': 'error', + 'no-unused-expressions': [ + 'error', + { + allowShortCircuit: true, + allowTaggedTemplates: true, + allowTernary: true, + }, + ], + 'eslint/no-unused-vars': [ + 'error', + { + argsIgnorePattern: '^_', + varsIgnorePattern: '^_', + }, + ], + 'no-var': 'error', + 'no-eval': 'error', + 'no-iterator': 'error', + 'no-new-wrappers': 'error', + 'no-restricted-globals': [ + 'error', + { message: 'Use `globalThis` instead.', name: 'global' }, + { message: 'Use `globalThis` instead.', name: 'self' }, + ], + 'no-useless-call': 'error', + 'no-useless-computed-key': 'error', + 'no-useless-constructor': 'error', + 'no-useless-return': 'error', + 'object-shorthand': [ + 'error', + 'always', + { + avoidQuotes: true, + ignoreConstructors: false, + }, + ], + 'one-var': ['error', { initialized: 'never' }], + 'prefer-const': [ + 'error', + { + destructuring: 'all', + ignoreReadBeforeAssign: true, + }, + ], + 'eslint/prefer-arrow-callback': [ + 'error', + { + allowNamedFunctions: false, + allowUnboundThis: true, + }, + ], + 'prefer-exponentiation-operator': 'error', + 'prefer-promise-reject-errors': 'error', + 'eslint/prefer-regex-literals': [ + 'error', + { + disallowRedundantWrapping: true, + }, + ], + 'prefer-rest-params': 'error', + 'prefer-spread': 'error', + 'prefer-template': 'error', + 'spaced-comment': 'error', + 'symbol-description': 'error', + 'unicode-bom': ['error', 'never'], + 'use-isnan': [ + 'error', + { + enforceForIndexOf: true, + enforceForSwitchCase: true, + }, + ], + 'valid-typeof': [ + 'error', + { + requireStringLiterals: true, + }, + ], + 'vars-on-top': 'error', + yoda: ['error', 'never'], + }, +}; + +export { javascript }; diff --git a/internal/lint-configs/oxlint-config/src/configs/node.ts b/internal/lint-configs/oxlint-config/src/configs/node.ts new file mode 100644 index 000000000..24aa08dfa --- /dev/null +++ b/internal/lint-configs/oxlint-config/src/configs/node.ts @@ -0,0 +1,11 @@ +import type { OxlintConfig } from 'oxlint'; + +const node: OxlintConfig = { + rules: { + 'node/no-exports-assign': 'error', + 'node/no-new-require': 'error', + 'node/no-path-concat': 'error', + }, +}; + +export { node }; diff --git a/internal/lint-configs/oxlint-config/src/configs/overrides.ts b/internal/lint-configs/oxlint-config/src/configs/overrides.ts new file mode 100644 index 000000000..c8838feb1 --- /dev/null +++ b/internal/lint-configs/oxlint-config/src/configs/overrides.ts @@ -0,0 +1,98 @@ +import type { OxlintConfig } from 'oxlint'; + +const overrides: OxlintConfig = { + overrides: [ + { + files: ['*.d.ts', '**/*.d.ts'], + rules: { + 'import/no-unassigned-import': 'off', + 'typescript/triple-slash-reference': 'off', + }, + }, + { + files: [ + '**/__tests__/**/*.js', + '**/__tests__/**/*.cjs', + '**/__tests__/**/*.mjs', + '**/__tests__/**/*.jsx', + '**/__tests__/**/*.ts', + '**/__tests__/**/*.cts', + '**/__tests__/**/*.mts', + '**/__tests__/**/*.tsx', + '**/*.spec.js', + '**/*.spec.cjs', + '**/*.spec.mjs', + '**/*.spec.jsx', + '**/*.spec.ts', + '**/*.spec.cts', + '**/*.spec.mts', + '**/*.spec.tsx', + '**/*.test.js', + '**/*.test.cjs', + '**/*.test.mjs', + '**/*.test.jsx', + '**/*.test.ts', + '**/*.test.cts', + '**/*.test.mts', + '**/*.test.tsx', + '**/*.bench.js', + '**/*.bench.cjs', + '**/*.bench.mjs', + '**/*.bench.jsx', + '**/*.bench.ts', + '**/*.bench.cts', + '**/*.bench.mts', + '**/*.bench.tsx', + '**/*.benchmark.js', + '**/*.benchmark.cjs', + '**/*.benchmark.mjs', + '**/*.benchmark.jsx', + '**/*.benchmark.ts', + '**/*.benchmark.cts', + '**/*.benchmark.mts', + '**/*.benchmark.tsx', + ], + rules: { + 'no-console': 'off', + }, + }, + { + files: ['packages/@core/base/shared/src/utils/inference.ts'], + rules: { + 'vue/prefer-import-from-vue': 'off', + }, + }, + { + files: ['packages/@core/ui-kit/menu-ui/src/sub-menu.vue'], + rules: { + 'import/no-self-import': 'off', + }, + }, + { + files: [ + 'scripts/**/*.js', + 'scripts/**/*.cjs', + 'scripts/**/*.mjs', + 'scripts/**/*.jsx', + 'scripts/**/*.ts', + 'scripts/**/*.cts', + 'scripts/**/*.mts', + 'scripts/**/*.tsx', + 'internal/**/*.js', + 'internal/**/*.cjs', + 'internal/**/*.mjs', + 'internal/**/*.jsx', + 'internal/**/*.ts', + 'internal/**/*.cts', + 'internal/**/*.mts', + 'internal/**/*.tsx', + ], + rules: { + 'no-console': 'off', + 'unicorn/no-process-exit': 'off', + }, + }, + ], +}; + +export { overrides }; diff --git a/internal/lint-configs/oxlint-config/src/configs/plugins.ts b/internal/lint-configs/oxlint-config/src/configs/plugins.ts new file mode 100644 index 000000000..8b6f3dd17 --- /dev/null +++ b/internal/lint-configs/oxlint-config/src/configs/plugins.ts @@ -0,0 +1,7 @@ +import type { OxlintConfig } from 'oxlint'; + +const plugins: OxlintConfig = { + plugins: ['import', 'node', 'oxc', 'typescript', 'unicorn', 'vitest', 'vue'], +}; + +export { plugins }; diff --git a/internal/lint-configs/oxlint-config/src/configs/tailwindcss.ts b/internal/lint-configs/oxlint-config/src/configs/tailwindcss.ts new file mode 100644 index 000000000..c24e5c865 --- /dev/null +++ b/internal/lint-configs/oxlint-config/src/configs/tailwindcss.ts @@ -0,0 +1,50 @@ +import type { OxlintConfig } from 'oxlint'; + +import eslintPluginBetterTailwindcss from 'eslint-plugin-better-tailwindcss'; +import { getDefaultSelectors } from 'eslint-plugin-better-tailwindcss/defaults'; +import { SelectorKind } from 'eslint-plugin-better-tailwindcss/types'; + +const selectors = [ + ...getDefaultSelectors(), + { + kind: SelectorKind.Attribute, + match: [{ type: 'objectValues' }], + name: '^classNames$', + }, +]; + +const settings = { + entryPoint: 'packages/@core/base/design/src/css/global.css', + selectors, +}; + +const tailwindcss: OxlintConfig = { + // Generated shadcn-ui internals are intentionally left unmanaged. + ignorePatterns: ['packages/@core/ui-kit/shadcn-ui/**/*'], + jsPlugins: [ + { + name: 'better-tailwindcss', + specifier: 'eslint-plugin-better-tailwindcss', + }, + ], + rules: { + ...eslintPluginBetterTailwindcss.configs.recommended.rules, + 'better-tailwindcss/enforce-consistent-class-order': [ + 'error', + { + detectComponentClasses: true, + unknownClassOrder: 'asc', + unknownClassPosition: 'start', + }, + ], + // Let Prettier own wrapping decisions to avoid ping-pong formatting. + 'better-tailwindcss/enforce-consistent-line-wrapping': 'off', + 'better-tailwindcss/no-unknown-classes': 'off', + }, + settings: { + 'better-tailwindcss': settings, + 'eslint-plugin-better-tailwindcss': settings, + }, +}; + +export { tailwindcss }; diff --git a/internal/lint-configs/oxlint-config/src/configs/test.ts b/internal/lint-configs/oxlint-config/src/configs/test.ts new file mode 100644 index 000000000..a7470eb6a --- /dev/null +++ b/internal/lint-configs/oxlint-config/src/configs/test.ts @@ -0,0 +1,23 @@ +import type { OxlintConfig } from 'oxlint'; + +const test: OxlintConfig = { + rules: { + 'jest/no-conditional-expect': 'off', + 'jest/require-to-throw-message': 'off', + 'vitest/consistent-test-it': [ + 'error', + { + fn: 'it', + withinDescribe: 'it', + }, + ], + 'vitest/hoisted-apis-on-top': 'off', + 'vitest/no-focused-tests': 'error', + 'vitest/no-identical-title': 'error', + 'vitest/no-import-node-test': 'error', + 'vitest/prefer-hooks-in-order': 'error', + 'vitest/prefer-lowercase-title': 'error', + }, +}; + +export { test }; diff --git a/internal/lint-configs/oxlint-config/src/configs/typescript.ts b/internal/lint-configs/oxlint-config/src/configs/typescript.ts new file mode 100644 index 000000000..e2db0b892 --- /dev/null +++ b/internal/lint-configs/oxlint-config/src/configs/typescript.ts @@ -0,0 +1,28 @@ +import type { OxlintConfig } from 'oxlint'; + +const typescript: OxlintConfig = { + rules: { + 'typescript/ban-ts-comment': 'error', + // Keep the first type-aware rollout conservative. These rules currently + // produce high-volume diagnostics and need file-by-file cleanup later. + 'typescript/await-thenable': 'off', + 'typescript/no-base-to-string': 'off', + 'typescript/no-duplicate-type-constituents': 'off', + 'typescript/no-floating-promises': 'off', + 'typescript/no-misused-spread': 'off', + 'typescript/no-non-null-assertion': 'error', + 'typescript/no-redundant-type-constituents': 'off', + 'typescript/no-unnecessary-boolean-literal-compare': 'off', + 'typescript/no-unnecessary-type-assertion': 'off', + 'typescript/no-unnecessary-type-arguments': 'off', + 'typescript/no-unnecessary-template-expression': 'off', + 'typescript/no-unsafe-enum-comparison': 'off', + 'typescript/no-unsafe-type-assertion': 'off', + 'typescript/no-var-requires': 'error', + 'typescript/restrict-template-expressions': 'off', + 'typescript/triple-slash-reference': 'error', + 'typescript/unbound-method': 'off', + }, +}; + +export { typescript }; diff --git a/internal/lint-configs/oxlint-config/src/configs/unicorn.ts b/internal/lint-configs/oxlint-config/src/configs/unicorn.ts new file mode 100644 index 000000000..b72af15a0 --- /dev/null +++ b/internal/lint-configs/oxlint-config/src/configs/unicorn.ts @@ -0,0 +1,14 @@ +import type { OxlintConfig } from 'oxlint'; + +const unicorn: OxlintConfig = { + rules: { + 'unicorn/consistent-function-scoping': 'off', + 'unicorn/no-process-exit': 'error', + 'unicorn/no-single-promise-in-promise-methods': 'off', + 'unicorn/no-useless-spread': 'off', + 'unicorn/prefer-global-this': 'off', + 'unicorn/prefer-module': 'error', + }, +}; + +export { unicorn }; diff --git a/internal/lint-configs/oxlint-config/src/configs/vue.ts b/internal/lint-configs/oxlint-config/src/configs/vue.ts new file mode 100644 index 000000000..172507d69 --- /dev/null +++ b/internal/lint-configs/oxlint-config/src/configs/vue.ts @@ -0,0 +1,9 @@ +import type { OxlintConfig } from 'oxlint'; + +const vue: OxlintConfig = { + rules: { + 'vue/prefer-import-from-vue': 'error', + }, +}; + +export { vue }; diff --git a/internal/lint-configs/oxlint-config/src/index.ts b/internal/lint-configs/oxlint-config/src/index.ts new file mode 100644 index 000000000..b890cb0d3 --- /dev/null +++ b/internal/lint-configs/oxlint-config/src/index.ts @@ -0,0 +1,21 @@ +import type { OxlintConfig } from 'oxlint'; + +import { defineConfig as defineOxlintConfig } from 'oxlint'; + +import { mergeOxlintConfigs, oxlintConfig } from './configs'; + +type VbenOxlintConfig = Omit & { + extends?: OxlintConfig[]; +}; + +function defineConfig(config: VbenOxlintConfig = {}) { + const { extends: extendedConfigs = [], ...restConfig } = config; + + return defineOxlintConfig( + mergeOxlintConfigs(oxlintConfig, ...extendedConfigs, restConfig), + ); +} + +export { defineConfig, oxlintConfig }; +export * from './configs'; +export type { OxlintConfig, VbenOxlintConfig }; diff --git a/internal/lint-configs/oxlint-config/tsconfig.json b/internal/lint-configs/oxlint-config/tsconfig.json new file mode 100644 index 000000000..b2ec3b61e --- /dev/null +++ b/internal/lint-configs/oxlint-config/tsconfig.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/node.json", + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/internal/lint-configs/prettier-config/index.mjs b/internal/lint-configs/prettier-config/index.mjs deleted file mode 100644 index f6a20c8b4..000000000 --- a/internal/lint-configs/prettier-config/index.mjs +++ /dev/null @@ -1,18 +0,0 @@ -export default { - endOfLine: 'auto', - overrides: [ - { - files: ['*.json5'], - options: { - quoteProps: 'preserve', - singleQuote: false, - }, - }, - ], - plugins: ['prettier-plugin-tailwindcss'], - printWidth: 80, - proseWrap: 'never', - semi: true, - singleQuote: true, - trailingComma: 'all', -}; diff --git a/internal/lint-configs/stylelint-config/index.mjs b/internal/lint-configs/stylelint-config/index.mjs index 47cd12d81..1395d5ec0 100644 --- a/internal/lint-configs/stylelint-config/index.mjs +++ b/internal/lint-configs/stylelint-config/index.mjs @@ -36,12 +36,7 @@ export default { files: ['*.scss', '**/*.scss'], }, ], - plugins: [ - 'stylelint-order', - '@stylistic/stylelint-plugin', - 'stylelint-prettier', - 'stylelint-scss', - ], + plugins: ['stylelint-order', '@stylistic/stylelint-plugin', 'stylelint-scss'], rules: { 'at-rule-no-deprecated': null, 'at-rule-no-unknown': [ @@ -67,6 +62,12 @@ export default { 'use', 'forward', 'return', + 'reference', + 'plugin', + 'source', + 'theme', + 'utility', + 'custom-variant', ], }, ], @@ -100,7 +101,6 @@ export default { ], { severity: 'error' }, ], - 'prettier/prettier': true, 'rule-empty-line-before': [ 'always', { @@ -130,12 +130,18 @@ export default { 'use', 'forward', 'return', + 'reference', + 'plugin', + 'source', + 'theme', + 'utility', + 'custom-variant', ], }, ], 'scss/operator-no-newline-after': null, 'selector-class-pattern': - '^(?:(?:o|c|u|t|s|is|has|_|js|qa)-)?[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)*(?:__[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)*)?(?:--[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)*)?(?:[.+])?$', + '^-?(?:(?:o|c|u|t|s|is|has|_|js|qa)-)?[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)*(?:__[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)*)?(?:--[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)*)?(?:[.+])?$', 'selector-not-notation': null, }, diff --git a/internal/lint-configs/stylelint-config/package.json b/internal/lint-configs/stylelint-config/package.json index 4d0c26fc6..110795437 100644 --- a/internal/lint-configs/stylelint-config/package.json +++ b/internal/lint-configs/stylelint-config/package.json @@ -1,6 +1,6 @@ { "name": "@vben/stylelint-config", - "version": "5.6.0", + "version": "5.7.0", "private": true, "homepage": "https://github.com/vbenjs/vue-vben-admin", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", @@ -31,13 +31,11 @@ "postcss": "catalog:", "postcss-html": "catalog:", "postcss-scss": "catalog:", - "prettier": "catalog:", "stylelint": "catalog:", "stylelint-config-recommended": "catalog:", "stylelint-config-recommended-scss": "catalog:", "stylelint-config-recommended-vue": "catalog:", "stylelint-config-standard": "catalog:", - "stylelint-order": "catalog:", - "stylelint-prettier": "catalog:" + "stylelint-order": "catalog:" } } diff --git a/internal/node-utils/package.json b/internal/node-utils/package.json index f8add8b4e..40a017f14 100644 --- a/internal/node-utils/package.json +++ b/internal/node-utils/package.json @@ -1,6 +1,6 @@ { "name": "@vben/node-utils", - "version": "5.6.0", + "version": "5.7.0", "private": true, "homepage": "https://github.com/vbenjs/vue-vben-admin", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", @@ -37,7 +37,6 @@ "find-up": "catalog:", "ora": "catalog:", "pkg-types": "catalog:", - "prettier": "catalog:", "rimraf": "catalog:" } } diff --git a/internal/node-utils/src/formatter.ts b/internal/node-utils/src/formatter.ts new file mode 100644 index 000000000..78c6be0b9 --- /dev/null +++ b/internal/node-utils/src/formatter.ts @@ -0,0 +1,13 @@ +import fs from 'node:fs/promises'; + +import { execa } from 'execa'; + +async function formatFile(filepath: string) { + await execa('oxfmt', [filepath], { + stdio: 'inherit', + }); + + return await fs.readFile(filepath, 'utf8'); +} + +export { formatFile }; diff --git a/internal/node-utils/src/index.ts b/internal/node-utils/src/index.ts index 2e39ccff6..ff50289bf 100644 --- a/internal/node-utils/src/index.ts +++ b/internal/node-utils/src/index.ts @@ -1,12 +1,12 @@ export * from './constants'; export * from './date'; +export { formatFile } from './formatter'; export * from './fs'; export * from './git'; export { add as gitAdd, getStagedFiles } from './git'; export { generatorContentHash } from './hash'; export * from './monorepo'; export { toPosixPath } from './path'; -export { prettierFormat } from './prettier'; export * from './spinner'; export type { Package } from '@manypkg/get-packages'; export { default as colors } from 'chalk'; diff --git a/internal/node-utils/src/prettier.ts b/internal/node-utils/src/prettier.ts deleted file mode 100644 index 1e1525db1..000000000 --- a/internal/node-utils/src/prettier.ts +++ /dev/null @@ -1,21 +0,0 @@ -import fs from 'node:fs/promises'; - -import { format, getFileInfo, resolveConfig } from 'prettier'; - -async function prettierFormat(filepath: string) { - const prettierOptions = await resolveConfig(filepath, {}); - - const fileInfo = await getFileInfo(filepath); - - const input = await fs.readFile(filepath, 'utf8'); - const output = await format(input, { - ...prettierOptions, - parser: fileInfo.inferredParser as any, - }); - if (output !== input) { - await fs.writeFile(filepath, output, 'utf8'); - } - return output; -} - -export { prettierFormat }; diff --git a/internal/tailwind-config/package.json b/internal/tailwind-config/package.json deleted file mode 100644 index 16016fd0d..000000000 --- a/internal/tailwind-config/package.json +++ /dev/null @@ -1,67 +0,0 @@ -{ - "name": "@vben/tailwind-config", - "version": "5.6.0", - "private": true, - "homepage": "https://github.com/vbenjs/vue-vben-admin", - "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", - "repository": { - "type": "git", - "url": "git+https://github.com/vbenjs/vue-vben-admin.git", - "directory": "internal/tailwind-config" - }, - "license": "MIT", - "type": "module", - "scripts": { - "stub": "pnpm unbuild --stub" - }, - "files": [ - "dist" - ], - "main": "./dist/index.mjs", - "module": "./dist/index.mjs", - "types": "./dist/index.d.ts", - "typesVersions": { - "*": { - "*": [ - "./dist/*", - "./*" - ] - } - }, - "exports": { - ".": { - "types": "./src/index.ts", - "import": "./dist/index.mjs", - "require": "./dist/index.cjs" - }, - "./postcss": { - "types": "./src/postcss.config.ts", - "import": "./dist/postcss.config.mjs", - "require": "./dist/postcss.config.cjs", - "default": "./dist/postcss.config.mjs" - }, - "./*": "./*" - }, - "peerDependencies": { - "tailwindcss": "^3.4.3" - }, - "dependencies": { - "@iconify/json": "catalog:", - "@iconify/tailwind": "catalog:", - "@manypkg/get-packages": "catalog:", - "@tailwindcss/nesting": "catalog:", - "@tailwindcss/typography": "catalog:", - "autoprefixer": "catalog:", - "cssnano": "catalog:", - "jiti": "catalog:", - "postcss": "catalog:", - "postcss-antd-fixes": "catalog:", - "postcss-import": "catalog:", - "postcss-preset-env": "catalog:", - "tailwindcss": "catalog:", - "tailwindcss-animate": "catalog:" - }, - "devDependencies": { - "@types/postcss-import": "catalog:" - } -} diff --git a/internal/tailwind-config/src/index.ts b/internal/tailwind-config/src/index.ts deleted file mode 100644 index 17fae7dc4..000000000 --- a/internal/tailwind-config/src/index.ts +++ /dev/null @@ -1,266 +0,0 @@ -import type { Config } from 'tailwindcss'; - -import path from 'node:path'; - -import { addDynamicIconSelectors } from '@iconify/tailwind'; -import { getPackagesSync } from '@manypkg/get-packages'; -import typographyPlugin from '@tailwindcss/typography'; -import animate from 'tailwindcss-animate'; - -import { enterAnimationPlugin } from './plugins/entry'; - -// import defaultTheme from 'tailwindcss/defaultTheme'; - -const { packages } = getPackagesSync(process.cwd()); - -const tailwindPackages: string[] = []; - -packages.forEach((pkg) => { - // apps目录下和 @vben-core/tailwind-ui 包需要使用到 tailwindcss ui - // if (fs.existsSync(path.join(pkg.dir, 'tailwind.config.mjs'))) { - tailwindPackages.push(pkg.dir); - // } -}); - -const shadcnUiColors = { - accent: { - DEFAULT: 'hsl(var(--accent))', - foreground: 'hsl(var(--accent-foreground))', - hover: 'hsl(var(--accent-hover))', - lighter: 'has(val(--accent-lighter))', - }, - background: { - deep: 'hsl(var(--background-deep))', - DEFAULT: 'hsl(var(--background))', - }, - border: { - DEFAULT: 'hsl(var(--border))', - }, - card: { - DEFAULT: 'hsl(var(--card))', - foreground: 'hsl(var(--card-foreground))', - }, - destructive: { - ...createColorsPalette('destructive'), - DEFAULT: 'hsl(var(--destructive))', - }, - - foreground: { - DEFAULT: 'hsl(var(--foreground))', - }, - - input: { - background: 'hsl(var(--input-background))', - DEFAULT: 'hsl(var(--input))', - }, - muted: { - DEFAULT: 'hsl(var(--muted))', - foreground: 'hsl(var(--muted-foreground))', - }, - popover: { - DEFAULT: 'hsl(var(--popover))', - foreground: 'hsl(var(--popover-foreground))', - }, - primary: { - ...createColorsPalette('primary'), - DEFAULT: 'hsl(var(--primary))', - }, - - ring: 'hsl(var(--ring))', - secondary: { - DEFAULT: 'hsl(var(--secondary))', - desc: 'hsl(var(--secondary-desc))', - foreground: 'hsl(var(--secondary-foreground))', - }, -}; - -const customColors = { - green: { - ...createColorsPalette('green'), - foreground: 'hsl(var(--success-foreground))', - }, - header: { - DEFAULT: 'hsl(var(--header))', - }, - heavy: { - DEFAULT: 'hsl(var(--heavy))', - foreground: 'hsl(var(--heavy-foreground))', - }, - main: { - DEFAULT: 'hsl(var(--main))', - }, - overlay: { - content: 'hsl(var(--overlay-content))', - DEFAULT: 'hsl(var(--overlay))', - }, - red: { - ...createColorsPalette('red'), - foreground: 'hsl(var(--destructive-foreground))', - }, - sidebar: { - deep: 'hsl(var(--sidebar-deep))', - DEFAULT: 'hsl(var(--sidebar))', - }, - success: { - ...createColorsPalette('success'), - DEFAULT: 'hsl(var(--success))', - }, - warning: { - ...createColorsPalette('warning'), - DEFAULT: 'hsl(var(--warning))', - }, - yellow: { - ...createColorsPalette('yellow'), - foreground: 'hsl(var(--warning-foreground))', - }, -}; - -export default { - content: [ - './index.html', - ...tailwindPackages.map((item) => - path.join(item, 'src/**/*.{vue,js,ts,jsx,tsx,svelte,astro,html}'), - ), - ], - darkMode: 'selector', - plugins: [ - animate, - typographyPlugin, - addDynamicIconSelectors(), - enterAnimationPlugin, - ], - prefix: '', - safelist: ['dark'], - theme: { - container: { - center: true, - padding: '2rem', - screens: { - '2xl': '1400px', - }, - }, - extend: { - animation: { - 'accordion-down': 'accordion-down 0.2s ease-out', - 'accordion-up': 'accordion-up 0.2s ease-out', - 'collapsible-down': 'collapsible-down 0.2s ease-in-out', - 'collapsible-up': 'collapsible-up 0.2s ease-in-out', - float: 'float 5s linear 0ms infinite', - }, - - animationDuration: { - '2000': '2000ms', - '3000': '3000ms', - }, - borderRadius: { - lg: 'var(--radius)', - md: 'calc(var(--radius) - 2px)', - sm: 'calc(var(--radius) - 4px)', - xl: 'calc(var(--radius) + 4px)', - }, - boxShadow: { - float: `0 6px 16px 0 rgb(0 0 0 / 8%), - 0 3px 6px -4px rgb(0 0 0 / 12%), - 0 9px 28px 8px rgb(0 0 0 / 5%)`, - }, - colors: { - ...customColors, - ...shadcnUiColors, - }, - fontFamily: { - sans: [ - 'var(--font-family)', - // ...defaultTheme.fontFamily.sans - ], - }, - keyframes: { - 'accordion-down': { - from: { height: '0' }, - to: { height: 'var(--reka-accordion-content-height)' }, - }, - 'accordion-up': { - from: { height: 'var(--reka-accordion-content-height)' }, - to: { height: '0' }, - }, - 'collapsible-down': { - from: { height: '0' }, - to: { height: 'var(--reka-collapsible-content-height)' }, - }, - 'collapsible-up': { - from: { height: 'var(--reka-collapsible-content-height)' }, - to: { height: '0' }, - }, - float: { - '0%': { transform: 'translateY(0)' }, - '50%': { transform: 'translateY(-20px)' }, - '100%': { transform: 'translateY(0)' }, - }, - }, - zIndex: { - '100': '100', - '1000': '1000', - }, - }, - }, -} as Config; - -function createColorsPalette(name: string) { - // backgroundLightest: '#EFF6FF', // Tailwind CSS 默认的 `blue-50` - // backgroundLighter: '#DBEAFE', // Tailwind CSS 默认的 `blue-100` - // backgroundLight: '#BFDBFE', // Tailwind CSS 默认的 `blue-200` - // borderLight: '#93C5FD', // Tailwind CSS 默认的 `blue-300` - // border: '#60A5FA', // Tailwind CSS 默认的 `blue-400` - // main: '#3B82F6', // Tailwind CSS 默认的 `blue-500` - // hover: '#2563EB', // Tailwind CSS 默认的 `blue-600` - // active: '#1D4ED8', // Tailwind CSS 默认的 `blue-700` - // backgroundDark: '#1E40AF', // Tailwind CSS 默认的 `blue-800` - // backgroundDarker: '#1E3A8A', // Tailwind CSS 默认的 `blue-900` - // backgroundDarkest: '#172554', // Tailwind CSS 默认的 `blue-950` - - // • backgroundLightest (#EFF6FF): 适用于最浅的背景色,可能用于非常轻微的阴影或卡片的背景。 - // • backgroundLighter (#DBEAFE): 适用于略浅的背景色,通常用于次要背景或略浅的区域。 - // • backgroundLight (#BFDBFE): 适用于浅色背景,可能用于输入框或表单区域的背景。 - // • borderLight (#93C5FD): 适用于浅色边框,可能用于输入框或卡片的边框。 - // • border (#60A5FA): 适用于普通边框,可能用于按钮或卡片的边框。 - // • main (#3B82F6): 适用于主要的主题色,通常用于按钮、链接或主要的强调色。 - // • hover (#2563EB): 适用于鼠标悬停状态下的颜色,例如按钮悬停时的背景色或边框色。 - // • active (#1D4ED8): 适用于激活状态下的颜色,例如按钮按下时的背景色或边框色。 - // • backgroundDark (#1E40AF): 适用于深色背景,可能用于主要按钮或深色卡片背景。 - // • backgroundDarker (#1E3A8A): 适用于更深的背景,通常用于头部导航栏或页脚。 - // • backgroundDarkest (#172554): 适用于最深的背景,可能用于非常深色的区域或极端对比色。 - - return { - 50: `hsl(var(--${name}-50))`, - 100: `hsl(var(--${name}-100))`, - 200: `hsl(var(--${name}-200))`, - 300: `hsl(var(--${name}-300))`, - 400: `hsl(var(--${name}-400))`, - 500: `hsl(var(--${name}-500))`, - 600: `hsl(var(--${name}-600))`, - 700: `hsl(var(--${name}-700))`, - // 800: `hsl(var(--${name}-800))`, - // 900: `hsl(var(--${name}-900))`, - // 950: `hsl(var(--${name}-950))`, - // 激活状态下的颜色,适用于按钮按下时的背景色或边框色。 - active: `hsl(var(--${name}-700))`, - // 浅色背景,适用于输入框或表单区域的背景。 - 'background-light': `hsl(var(--${name}-200))`, - // 适用于略浅的背景色,通常用于次要背景或略浅的区域。 - 'background-lighter': `hsl(var(--${name}-100))`, - // 最浅的背景色,适用于非常轻微的阴影或卡片的背景。 - 'background-lightest': `hsl(var(--${name}-50))`, - // 适用于普通边框,可能用于按钮或卡片的边框。 - border: `hsl(var(--${name}-400))`, - // 浅色边框,适用于输入框或卡片的边框。 - 'border-light': `hsl(var(--${name}-300))`, - foreground: `hsl(var(--${name}-foreground))`, - // 鼠标悬停状态下的颜色,适用于按钮悬停时的背景色或边框色。 - hover: `hsl(var(--${name}-600))`, - // 主色文本 - text: `hsl(var(--${name}-500))`, - // 主色文本激活态 - 'text-active': `hsl(var(--${name}-700))`, - // 主色文本悬浮态 - 'text-hover': `hsl(var(--${name}-600))`, - }; -} diff --git a/internal/tailwind-config/src/module.d.ts b/internal/tailwind-config/src/module.d.ts deleted file mode 100644 index a3996533f..000000000 --- a/internal/tailwind-config/src/module.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -declare module '@tailwindcss/nesting' { - export default any; -} diff --git a/internal/tailwind-config/src/plugins/entry.ts b/internal/tailwind-config/src/plugins/entry.ts deleted file mode 100644 index 0d8e8ec80..000000000 --- a/internal/tailwind-config/src/plugins/entry.ts +++ /dev/null @@ -1,53 +0,0 @@ -import plugin from 'tailwindcss/plugin.js'; - -const enterAnimationPlugin = plugin(({ addUtilities }) => { - const maxChild = 5; - const utilities: Record = {}; - for (let i = 1; i <= maxChild; i++) { - const baseDelay = 0.1; - const delay = `${baseDelay * i}s`; - - utilities[`.enter-x:nth-child(${i})`] = { - animation: `enter-x-animation 0.3s ease-in-out ${delay} forwards`, - opacity: '0', - transform: `translateX(50px)`, - }; - - utilities[`.enter-y:nth-child(${i})`] = { - animation: `enter-y-animation 0.3s ease-in-out ${delay} forwards`, - opacity: '0', - transform: `translateY(50px)`, - }; - - utilities[`.-enter-x:nth-child(${i})`] = { - animation: `enter-x-animation 0.3s ease-in-out ${delay} forwards`, - opacity: '0', - transform: `translateX(-50px)`, - }; - - utilities[`.-enter-y:nth-child(${i})`] = { - animation: `enter-y-animation 0.3s ease-in-out ${delay} forwards`, - opacity: '0', - transform: `translateY(-50px)`, - }; - } - - // 添加动画关键帧 - addUtilities(utilities); - addUtilities({ - '@keyframes enter-x-animation': { - to: { - opacity: '1', - transform: 'translateX(0)', - }, - }, - '@keyframes enter-y-animation': { - to: { - opacity: '1', - transform: 'translateY(0)', - }, - }, - }); -}); - -export { enterAnimationPlugin }; diff --git a/internal/tailwind-config/src/postcss.config.ts b/internal/tailwind-config/src/postcss.config.ts deleted file mode 100644 index 43309346b..000000000 --- a/internal/tailwind-config/src/postcss.config.ts +++ /dev/null @@ -1,15 +0,0 @@ -import config from '.'; - -export default { - plugins: { - ...(process.env.NODE_ENV === 'production' ? { cssnano: {} } : {}), - // Specifying the config is not necessary in most cases, but it is included - autoprefixer: {}, - // 修复 element-plus 和 antdv-next 的样式和tailwindcss冲突问题 - 'postcss-antd-fixes': { prefixes: ['ant', 'el'] }, - 'postcss-import': {}, - 'postcss-preset-env': {}, - tailwindcss: { config }, - 'tailwindcss/nesting': {}, - }, -}; diff --git a/internal/tsconfig/base.json b/internal/tsconfig/base.json index 1e45a7843..132806b51 100644 --- a/internal/tsconfig/base.json +++ b/internal/tsconfig/base.json @@ -8,7 +8,6 @@ "moduleDetection": "force", "experimentalDecorators": true, - "baseUrl": ".", "module": "ESNext", "moduleResolution": "node", diff --git a/internal/tsconfig/node.json b/internal/tsconfig/node.json index 01bde7a78..409dd7268 100644 --- a/internal/tsconfig/node.json +++ b/internal/tsconfig/node.json @@ -5,7 +5,6 @@ "compilerOptions": { "composite": false, "lib": ["ESNext"], - "baseUrl": "./", "moduleResolution": "bundler", "types": ["node"], "noImplicitAny": true diff --git a/internal/tsconfig/package.json b/internal/tsconfig/package.json index 59bff7ca1..6370b3aa8 100644 --- a/internal/tsconfig/package.json +++ b/internal/tsconfig/package.json @@ -1,6 +1,6 @@ { "name": "@vben/tsconfig", - "version": "5.6.0", + "version": "5.7.0", "private": true, "homepage": "https://github.com/vbenjs/vue-vben-admin", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", diff --git a/internal/vite-config/package.json b/internal/vite-config/package.json index 445cbf248..d8e87d11b 100644 --- a/internal/vite-config/package.json +++ b/internal/vite-config/package.json @@ -1,6 +1,6 @@ { "name": "@vben/vite-config", - "version": "5.6.0", + "version": "5.7.0", "private": true, "homepage": "https://github.com/vbenjs/vue-vben-admin", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", @@ -29,6 +29,7 @@ "dependencies": { "@intlify/unplugin-vue-i18n": "catalog:", "@jspm/generator": "catalog:", + "@tailwindcss/vite": "catalog:", "archiver": "catalog:", "cheerio": "catalog:", "get-port": "catalog:", @@ -50,6 +51,7 @@ "rollup": "catalog:", "rollup-plugin-visualizer": "catalog:", "sass": "catalog:", + "sass-embedded": "catalog:", "vite": "catalog:", "vite-plugin-compression": "catalog:", "vite-plugin-dts": "catalog:", diff --git a/internal/vite-config/src/config/application.ts b/internal/vite-config/src/config/application.ts index f437082e5..3c10408ad 100644 --- a/internal/vite-config/src/config/application.ts +++ b/internal/vite-config/src/config/application.ts @@ -6,7 +6,7 @@ import path, { relative } from 'node:path'; import { findMonorepoRoot } from '@vben/node-utils'; -import { NodePackageImporter } from 'sass'; +import { NodePackageImporter } from 'sass-embedded'; import { defineConfig, loadEnv, mergeConfig } from 'vite'; import { defaultImportmapOptions, getDefaultPwaOptions } from '../options'; @@ -58,7 +58,7 @@ function defineApplicationConfig(userConfigPromise?: DefineApplicationOptions) { const applicationConfig: UserConfig = { base, build: { - rollupOptions: { + rolldownOptions: { output: { assetFileNames: '[ext]/[name]-[hash].[ext]', chunkFileNames: 'js/[name]-[hash].js', @@ -67,20 +67,18 @@ function defineApplicationConfig(userConfigPromise?: DefineApplicationOptions) { manualChunks: { 'antdv-next': ['antdv-next'], }, + minify: isBuild + ? { + compress: { + dropDebugger: true, + }, + } + : false, }, }, target: 'es2015', }, css: createCssOptions(injectGlobalScss), - esbuild: { - drop: isBuild - ? [ - // 'console', - 'debugger', - ] - : [], - legalComments: 'none', - }, plugins, server: { host: true, @@ -96,10 +94,7 @@ function defineApplicationConfig(userConfigPromise?: DefineApplicationOptions) { }, }; - const mergedCommonConfig = mergeConfig( - await getCommonConfig(), - applicationConfig, - ); + const mergedCommonConfig = mergeConfig(await getCommonConfig(), applicationConfig); return mergeConfig(mergedCommonConfig, vite); }); } diff --git a/internal/vite-config/src/config/library.ts b/internal/vite-config/src/config/library.ts index 08b813520..623229fcd 100644 --- a/internal/vite-config/src/config/library.ts +++ b/internal/vite-config/src/config/library.ts @@ -40,7 +40,7 @@ function defineLibraryConfig(userConfigPromise?: DefineLibraryOptions) { fileName: () => 'index.mjs', formats: ['es'], }, - rollupOptions: { + rolldownOptions: { external: (id) => { return externalPackages.some( (pkg) => id === pkg || id.startsWith(`${pkg}/`), diff --git a/internal/vite-config/src/plugins/index.ts b/internal/vite-config/src/plugins/index.ts index da08db4b8..114d4d837 100644 --- a/internal/vite-config/src/plugins/index.ts +++ b/internal/vite-config/src/plugins/index.ts @@ -8,6 +8,7 @@ import type { } from '../typing'; import viteVueI18nPlugin from '@intlify/unplugin-vue-i18n/vite'; +import tailwindcss from '@tailwindcss/vite'; import viteVue from '@vitejs/plugin-vue'; import viteVueJsx from '@vitejs/plugin-vue-jsx'; import { visualizer as viteVisualizerPlugin } from 'rollup-plugin-visualizer'; @@ -25,6 +26,7 @@ import { viteMetadataPlugin } from './inject-metadata'; import { viteLicensePlugin } from './license'; import { viteNitroMockPlugin } from './nitro-mock'; import { vitePrintPlugin } from './print'; +import { viteTailwindReferencePlugin } from './tailwind-reference'; import { viteVxeTableImportsPlugin } from './vxe-table'; /** @@ -60,6 +62,8 @@ async function loadCommonPlugins( }, }), viteVueJsx(), + viteTailwindReferencePlugin(), + tailwindcss(), ], }, @@ -73,11 +77,13 @@ async function loadCommonPlugins( }, { condition: isBuild && !!visualizer, - plugins: () => [viteVisualizerPlugin({ + plugins: () => [ + viteVisualizerPlugin({ filename: './node_modules/.cache/visualizer/stats.html', gzipSize: true, open: true, - })], + }) as PluginOption, + ], }, ]; } diff --git a/internal/vite-config/src/plugins/tailwind-reference.ts b/internal/vite-config/src/plugins/tailwind-reference.ts new file mode 100644 index 000000000..8c3185125 --- /dev/null +++ b/internal/vite-config/src/plugins/tailwind-reference.ts @@ -0,0 +1,40 @@ +import type { Plugin } from 'vite'; + +const REFERENCE_LINE = '@reference "@vben-core/design/theme";\n'; + +/** + * Auto-inject @reference into Vue SFC diff --git a/packages/@core/ui-kit/menu-ui/src/sub-menu.vue b/packages/@core/ui-kit/menu-ui/src/sub-menu.vue index e4d471fb0..f84e5f818 100644 --- a/packages/@core/ui-kit/menu-ui/src/sub-menu.vue +++ b/packages/@core/ui-kit/menu-ui/src/sub-menu.vue @@ -4,7 +4,6 @@ import type { MenuRecordRaw } from '@vben-core/typings'; import { computed } from 'vue'; import { MenuBadge, MenuItem, SubMenu as SubMenuComp } from './components'; -// eslint-disable-next-line import/no-self-import import SubMenu from './sub-menu.vue'; interface Props { @@ -41,6 +40,7 @@ const hasChildren = computed(() => { :badge-variants="menu.badgeVariants" :icon="menu.icon" :path="menu.path" + :query="menu.query" > diff --git a/packages/@core/ui-kit/tabs-ui/src/components/tabs/tabs.vue b/packages/@core/ui-kit/tabs-ui/src/components/tabs/tabs.vue index a573f4490..fa2712f75 100644 --- a/packages/@core/ui-kit/tabs-ui/src/components/tabs/tabs.vue +++ b/packages/@core/ui-kit/tabs-ui/src/components/tabs/tabs.vue @@ -81,7 +81,7 @@ function onMouseDown(e: MouseEvent, tab: TabConfig) {