chore: init project

This commit is contained in:
vben
2024-05-19 21:20:42 +08:00
commit 399334ac57
630 changed files with 45623 additions and 0 deletions

View File

@@ -0,0 +1,7 @@
import { defineBuildConfig } from 'unbuild';
export default defineBuildConfig({
clean: true,
declaration: true,
entries: ['src/index'],
});

View File

@@ -0,0 +1,42 @@
{
"name": "@vben/commitlint-config",
"version": "1.0.0",
"private": true,
"type": "module",
"license": "MIT",
"homepage": "https://github.com/vbenjs/vue-vben-admin",
"repository": {
"type": "git",
"url": "git+https://github.com/vbenjs/vue-vben-admin.git",
"directory": "internal/lint-configs/commitlint-config"
},
"bugs": {
"url": "https://github.com/vbenjs/vue-vben-admin/issues"
},
"scripts": {
"stub": "pnpm unbuild --stub"
},
"files": [
"dist"
],
"main": "./dist/index.mjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"imports": {
"#*": "./src/*"
},
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"default": "./dist/index.mjs"
}
},
"dependencies": {
"@commitlint/cli": "^19.3.0",
"@commitlint/config-conventional": "^19.2.2",
"@vben/node-utils": "workspace:*",
"cz-git": "^1.9.1",
"czg": "^1.9.1"
}
}

View File

@@ -0,0 +1,145 @@
import type { UserConfig } from 'cz-git';
import { execSync } from 'node:child_process';
import { getPackagesSync } from '@vben/node-utils';
const { packages } = getPackagesSync();
const pkgs = packages.map((pkg) => {
return pkg.packageJson.name?.replace('@vben-core/', '');
});
const scopes = [
...pkgs,
'project',
'style',
'lint',
'ci',
'dev',
'deploy',
'other',
];
// precomputed scope
const scopeComplete = execSync('git status --porcelain || true')
.toString()
.trim()
.split('\n')
.find((r) => ~r.indexOf('M src'))
?.replace(/(\/)/g, '%%')
?.match(/src%%((\w|-)*)/)?.[1]
?.replace(/s$/, '');
const userConfig: UserConfig = {
extends: ['@commitlint/config-conventional'],
prompt: {
/** @use `pnpm commit :f` */
alias: {
b: 'build: bump dependencies',
c: 'chore: update config',
f: 'docs: fix typos',
r: 'docs: update README',
s: 'style: update code format',
},
allowCustomIssuePrefixs: false,
// scopes: [...scopes, 'mock'],
allowEmptyIssuePrefixs: false,
customScopesAlign: scopeComplete ? 'bottom' : 'top',
defaultScope: scopeComplete,
// English
typesAppend: [
{ name: 'wip: work in process', value: 'wip' },
{ name: 'workflow: workflow improvements', value: 'workflow' },
{ name: 'types: type definition file changes', value: 'types' },
],
// 中英文对照版
// messages: {
// type: '选择你要提交的类型 :',
// scope: '选择一个提交范围 (可选):',
// customScope: '请输入自定义的提交范围 :',
// subject: '填写简短精炼的变更描述 :\n',
// body: '填写更加详细的变更描述 (可选)。使用 "|" 换行 :\n',
// breaking: '列举非兼容性重大的变更 (可选)。使用 "|" 换行 :\n',
// footerPrefixsSelect: '选择关联issue前缀 (可选):',
// customFooterPrefixs: '输入自定义issue前缀 :',
// footer: '列举关联issue (可选) 例如: #31, #I3244 :\n',
// confirmCommit: '是否提交或修改commit ?',
// },
// types: [
// { value: 'feat', name: 'feat: 新增功能' },
// { value: 'fix', name: 'fix: 修复缺陷' },
// { value: 'docs', name: 'docs: 文档变更' },
// { value: 'style', name: 'style: 代码格式' },
// { value: 'refactor', name: 'refactor: 代码重构' },
// { value: 'perf', name: 'perf: 性能优化' },
// { value: 'test', name: 'test: 添加疏漏测试或已有测试改动' },
// { value: 'build', name: 'build: 构建流程、外部依赖变更 (如升级 npm 包、修改打包配置等)' },
// { value: 'ci', name: 'ci: 修改 CI 配置、脚本' },
// { value: 'revert', name: 'revert: 回滚 commit' },
// { value: 'chore', name: 'chore: 对构建过程或辅助工具和库的更改 (不影响源文件、测试用例)' },
// { value: 'wip', name: 'wip: 正在开发中' },
// { value: 'workflow', name: 'workflow: 工作流程改进' },
// { value: 'types', name: 'types: 类型定义文件修改' },
// ],
// emptyScopesAlias: 'empty: 不填写',
// customScopesAlias: 'custom: 自定义',
},
rules: {
/**
* type[scope]: [function] description
*
* ^^^^^^^^^^^^^^ empty line.
* - Something here
*/
'body-leading-blank': [2, 'always'],
/**
* type[scope]: [function] description
*
* - something here
*
* ^^^^^^^^^^^^^^
*/
'footer-leading-blank': [1, 'always'],
/**
* type[scope]: [function] description [No more than 108 characters]
* ^^^^^
*/
'header-max-length': [2, 'always', 108],
/**
* type[scope]: [function] description
* ^^^^^
*/
'scope-enum': [2, 'always', scopes],
'subject-case': [0],
'subject-empty': [2, 'never'],
'type-empty': [2, 'never'],
/**
* type[scope]: [function] description
* ^^^^
*/
'type-enum': [
2,
'always',
[
'feat',
'fix',
'perf',
'style',
'docs',
'test',
'refactor',
'build',
'ci',
'chore',
'revert',
'types',
'release',
'improvement',
],
],
},
};
export default userConfig;

View File

@@ -0,0 +1,5 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "@vben/tsconfig/node.json",
"include": ["src"]
}

View File

@@ -0,0 +1,7 @@
import { defineBuildConfig } from 'unbuild';
export default defineBuildConfig({
clean: true,
declaration: true,
entries: ['src/index'],
});

View File

@@ -0,0 +1,61 @@
{
"name": "@vben/eslint-config",
"version": "1.0.0",
"private": true,
"type": "module",
"license": "MIT",
"homepage": "https://github.com/vbenjs/vue-vben-admin",
"repository": {
"type": "git",
"url": "git+https://github.com/vbenjs/vue-vben-admin.git",
"directory": "internal/lint-configs/eslint-config"
},
"bugs": {
"url": "https://github.com/vbenjs/vue-vben-admin/issues"
},
"scripts": {
"stub": "pnpm unbuild --stub"
},
"files": [
"dist"
],
"main": "./dist/index.mjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"imports": {
"#*": "./src/*"
},
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs"
}
},
"dependencies": {
"eslint-plugin-command": "^0.2.3"
},
"devDependencies": {
"@eslint/js": "^9.3.0",
"@types/eslint": "^8.56.10",
"@typescript-eslint/eslint-plugin": "^7.9.0",
"@typescript-eslint/parser": "^7.9.0",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-eslint-comments": "^3.2.0",
"eslint-plugin-i": "^2.29.1",
"eslint-plugin-jsdoc": "^48.2.5",
"eslint-plugin-jsonc": "^2.15.1",
"eslint-plugin-n": "^17.7.0",
"eslint-plugin-no-only-tests": "^3.1.0",
"eslint-plugin-perfectionist": "^2.10.0",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-regexp": "^2.5.0",
"eslint-plugin-unicorn": "^53.0.0",
"eslint-plugin-unused-imports": "^3.2.0",
"eslint-plugin-vitest": "^0.5.4",
"eslint-plugin-vue": "^9.26.0",
"globals": "^15.2.0",
"jsonc-eslint-parser": "^2.4.0",
"vue-eslint-parser": "^9.4.2"
}
}

View File

@@ -0,0 +1,10 @@
import createCommand from 'eslint-plugin-command/config';
export async function command() {
return [
{
// @ts-expect-error - no types
...createCommand(),
},
];
}

View File

@@ -0,0 +1,22 @@
import type { Linter } from 'eslint';
export async function comments(): Promise<Linter.FlatConfig[]> {
const [pluginComments] = await Promise.all([
// @ts-expect-error - no types
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',
},
},
];
}

View File

@@ -0,0 +1,46 @@
import type { Linter } from 'eslint';
export async function ignores(): Promise<Linter.FlatConfig[]> {
return [
{
ignores: [
'**/node_modules',
'**/dist',
'**/.husky',
'**/Dockerfile',
'**/package-lock.json',
'**/yarn.lock',
'**/pnpm-lock.yaml',
'**/bun.lockb',
'**/output',
'**/coverage',
'**/temp',
'**/.temp',
'**/tmp',
'**/.tmp',
'**/.history',
'**/.vitepress/cache',
'**/.nuxt',
'**/.next',
'**/.vercel',
'**/.changeset',
'**/.idea',
'**/.cache',
'**/.output',
'**/.vite-inspect',
'**/CHANGELOG*.md',
'**/*.min.*',
'**/LICENSE*',
'**/__snapshots__',
'**/auto-import?(s).d.ts',
'**/components.d.ts',
'**/vite.config.mts.*',
'**/*.sh',
'**/*.ttf',
'**/*.woff',
],
},
];
}

View File

@@ -0,0 +1,27 @@
import type { Linter } from 'eslint';
export async function importPluginConfig(): Promise<Linter.FlatConfig[]> {
const [pluginImport] = await Promise.all([
// @ts-expect-error - no types
import('eslint-plugin-i'),
] as const);
return [
{
plugins: {
import: pluginImport,
},
rules: {
'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',
},
},
];
}

View File

@@ -0,0 +1,15 @@
export * from './command';
export * from './comments';
export * from './ignores';
export * from './import';
export * from './javascript';
export * from './jsdoc';
export * from './jsonc';
export * from './node';
export * from './perfectionist';
export * from './prettier';
export * from './regexp';
export * from './test';
export * from './typescript';
export * from './unicorn';
export * from './vue';

View File

@@ -0,0 +1,243 @@
import type { Linter } from 'eslint';
// @ts-expect-error - no types
import js from '@eslint/js';
// @ts-expect-error - no types
import pluginUnusedImports from 'eslint-plugin-unused-imports';
import globals from 'globals';
export async function javascript(): Promise<Linter.FlatConfig[]> {
return [
{
languageOptions: {
ecmaVersion: 'latest',
globals: {
...globals.browser,
...globals.es2021,
...globals.node,
document: 'readonly',
navigator: 'readonly',
window: 'readonly',
},
parserOptions: {
ecmaFeatures: {
jsx: true,
},
ecmaVersion: 'latest',
sourceType: 'module',
},
sourceType: 'module',
},
linterOptions: {
reportUnusedDisableDirectives: true,
},
plugins: {
'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',
'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',
{
message:
'Use `Object.getPrototypeOf` or `Object.setPrototypeOf` instead.',
property: '__proto__',
},
{
message: 'Use `Object.defineProperty` instead.',
property: '__defineGetter__',
},
{
message: 'Use `Object.defineProperty` instead.',
property: '__defineSetter__',
},
{
message: 'Use `Object.getOwnPropertyDescriptor` instead.',
property: '__lookupGetter__',
},
{
message: 'Use `Object.getOwnPropertyDescriptor` instead.',
property: '__lookupSetter__',
},
],
'no-restricted-syntax': [
'error',
'DebuggerStatement',
'LabeledStatement',
'WithStatement',
'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': 'error',
'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: true },
],
'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': [
'error',
{
args: 'after-used',
argsIgnorePattern: '^_',
vars: 'all',
varsIgnorePattern: '^_',
},
],
'use-isnan': [
'error',
{ enforceForIndexOf: true, enforceForSwitchCase: true },
],
'valid-typeof': ['error', { requireStringLiterals: true }],
'vars-on-top': 'error',
yoda: ['error', 'never'],
},
},
];
}

View File

@@ -0,0 +1,33 @@
import type { Linter } from 'eslint';
export async function jsdoc(): Promise<Linter.FlatConfig[]> {
const [pluginJsdoc] = await Promise.all([
import('eslint-plugin-jsdoc'),
] as const);
return [
{
plugins: {
// @ts-expect-error - no types
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',
},
},
];
}

View File

@@ -0,0 +1,257 @@
import type { Linter } from 'eslint';
export async function jsonc(): Promise<Linter.FlatConfig[]> {
const [pluginJsonc, parserJsonc] = await Promise.all([
import('eslint-plugin-jsonc'),
import('jsonc-eslint-parser'),
] as const);
return [
{
files: ['**/*.json', '**/*.json5', '**/*.jsonc', '*.code-workspace'],
languageOptions: {
parser: parserJsonc as any,
},
plugins: {
jsonc: pluginJsonc as any,
},
rules: {
'jsonc/no-bigint-literals': 'error',
'jsonc/no-binary-expression': 'error',
'jsonc/no-binary-numeric-literals': 'error',
'jsonc/no-dupe-keys': 'error',
'jsonc/no-escape-sequence-in-identifier': 'error',
'jsonc/no-floating-decimal': 'error',
'jsonc/no-hexadecimal-numeric-literals': 'error',
'jsonc/no-infinity': 'error',
'jsonc/no-multi-str': 'error',
'jsonc/no-nan': 'error',
'jsonc/no-number-props': 'error',
'jsonc/no-numeric-separators': 'error',
'jsonc/no-octal': 'error',
'jsonc/no-octal-escape': 'error',
'jsonc/no-octal-numeric-literals': 'error',
'jsonc/no-parenthesized': 'error',
'jsonc/no-plus-sign': 'error',
'jsonc/no-regexp-literals': 'error',
'jsonc/no-sparse-arrays': 'error',
'jsonc/no-template-literals': 'error',
'jsonc/no-undefined-value': 'error',
'jsonc/no-unicode-codepoint-escapes': 'error',
'jsonc/no-useless-escape': 'error',
'jsonc/space-unary-ops': 'error',
'jsonc/valid-json-number': 'error',
'jsonc/vue-custom-block/no-parsing-error': 'error',
},
},
sortTsconfig(),
sortPackageJson(),
];
}
function sortPackageJson(): Linter.FlatConfig {
return {
files: ['**/package.json'],
rules: {
'jsonc/sort-array-values': [
'error',
{
order: { type: 'asc' },
pathPattern: '^files$|^pnpm.neverBuiltDependencies$',
},
],
'jsonc/sort-keys': [
'error',
{
order: [
'publisher',
'name',
'version',
'private',
'description',
'displayName',
'type',
'author',
'license',
'funding',
'homepage',
'repository',
'bugs',
'keywords',
'categories',
'scripts',
'files',
'sideEffects',
'bin',
'main',
'module',
'unpkg',
'jsdelivr',
'types',
'typesVersions',
'imports',
'exports',
'publishConfig',
'icon',
'activationEvents',
'contributes',
'peerDependencies',
'peerDependenciesMeta',
'dependencies',
'optionalDependencies',
'devDependencies',
'engines',
'packageManager',
'pnpm',
'overrides',
'resolutions',
'husky',
'simple-git-hooks',
'lint-staged',
'eslintConfig',
],
pathPattern: '^$',
},
{
order: { type: 'asc' },
pathPattern: '^(?:dev|peer|optional|bundled)?[Dd]ependencies(Meta)?$',
},
{
order: { type: 'asc' },
pathPattern: '^(?:resolutions|overrides|pnpm.overrides)$',
},
{
order: ['types', 'import', 'require', 'default'],
pathPattern: '^exports.*$',
},
],
},
};
}
function sortTsconfig(): Linter.FlatConfig {
return {
files: [
'**/tsconfig.json',
'**/tsconfig.*.json',
'internal/tsconfig/*.json',
],
rules: {
'jsonc/sort-keys': [
'error',
{
order: [
'extends',
'compilerOptions',
'references',
'files',
'include',
'exclude',
],
pathPattern: '^$',
},
{
order: [
/* Projects */
'incremental',
'composite',
'tsBuildInfoFile',
'disableSourceOfProjectReferenceRedirect',
'disableSolutionSearching',
'disableReferencedProjectLoad',
/* Language and Environment */
'target',
'jsx',
'jsxFactory',
'jsxFragmentFactory',
'jsxImportSource',
'lib',
'moduleDetection',
'noLib',
'reactNamespace',
'useDefineForClassFields',
'emitDecoratorMetadata',
'experimentalDecorators',
/* Modules */
'baseUrl',
'rootDir',
'rootDirs',
'customConditions',
'module',
'moduleResolution',
'moduleSuffixes',
'noResolve',
'paths',
'resolveJsonModule',
'resolvePackageJsonExports',
'resolvePackageJsonImports',
'typeRoots',
'types',
'allowArbitraryExtensions',
'allowImportingTsExtensions',
'allowUmdGlobalAccess',
/* JavaScript Support */
'allowJs',
'checkJs',
'maxNodeModuleJsDepth',
/* Type Checking */
'strict',
'strictBindCallApply',
'strictFunctionTypes',
'strictNullChecks',
'strictPropertyInitialization',
'allowUnreachableCode',
'allowUnusedLabels',
'alwaysStrict',
'exactOptionalPropertyTypes',
'noFallthroughCasesInSwitch',
'noImplicitAny',
'noImplicitOverride',
'noImplicitReturns',
'noImplicitThis',
'noPropertyAccessFromIndexSignature',
'noUncheckedIndexedAccess',
'noUnusedLocals',
'noUnusedParameters',
'useUnknownInCatchVariables',
/* Emit */
'declaration',
'declarationDir',
'declarationMap',
'downlevelIteration',
'emitBOM',
'emitDeclarationOnly',
'importHelpers',
'importsNotUsedAsValues',
'inlineSourceMap',
'inlineSources',
'mapRoot',
'newLine',
'noEmit',
'noEmitHelpers',
'noEmitOnError',
'outDir',
'outFile',
'preserveConstEnums',
'preserveValueImports',
'removeComments',
'sourceMap',
'sourceRoot',
'stripInternal',
/* Interop Constraints */
'allowSyntheticDefaultImports',
'esModuleInterop',
'forceConsistentCasingInFileNames',
'isolatedModules',
'preserveSymlinks',
'verbatimModuleSyntax',
/* Completeness */
'skipDefaultLibCheck',
'skipLibCheck',
],
pathPattern: '^compilerOptions$',
},
],
},
};
}

View File

@@ -0,0 +1,54 @@
import type { Linter } from 'eslint';
export async function node(): Promise<Linter.FlatConfig[]> {
const [pluginNode] = await Promise.all([import('eslint-plugin-n')] as const);
return [
{
plugins: {
n: pluginNode,
},
rules: {
'n/handle-callback-err': ['error', '^(err|error)$'],
'n/no-deprecated-api': 'error',
'n/no-exports-assign': 'error',
'n/no-extraneous-import': [
'error',
{
allowModules: [
'unbuild',
'@vben/vite-config',
'vitest',
'vite',
'@vue/test-utils',
'@vben/tailwind-config',
],
},
],
'n/no-new-require': 'error',
'n/no-path-concat': 'error',
// 'n/no-unpublished-import': 'off',
'n/no-unsupported-features/es-syntax': [
'error',
{
ignores: [],
version: '>=18.0.0',
},
],
'n/prefer-global/buffer': ['error', 'never'],
// 'n/no-missing-import': 'off',
'n/prefer-global/process': ['error', 'never'],
'n/process-exit-as-throw': 'error',
},
},
{
files: [
'scripts/**/*.?([cm])[jt]s?(x)',
'internal/**/*.?([cm])[jt]s?(x)',
],
rules: {
'n/prefer-global/process': 'off',
},
},
];
}

View File

@@ -0,0 +1,81 @@
import type { Linter } from 'eslint';
export async function perfectionist(): Promise<Linter.FlatConfig[]> {
const [perfectionistNatural] = await Promise.all([
// @ts-expect-error - no types
import('eslint-plugin-perfectionist/configs/recommended-natural'),
] as const);
return [
perfectionistNatural,
{
rules: {
'perfectionist/sort-exports': [
'error',
{
order: 'asc',
type: 'natural',
},
],
'perfectionist/sort-imports': [
'error',
{
'custom-groups': {
type: {
vben: 'vue',
vue: ['vue', 'vue-*', '@vue*'],
},
value: {
vben: 'vben',
vue: ['@vben-*', '@vben-core/*'],
},
},
groups: [
'side-effect',
'type',
'vue',
'builtin',
'vben',
'external',
'internal-type',
'internal',
['parent', 'sibling', 'index'],
'style',
'object',
'unknown',
'type',
['parent-type', 'sibling-type', 'index-type'],
],
'internal-pattern': ['@/layouts/**', '@/router/**', '@/views/**'],
'newlines-between': 'always',
order: 'asc',
type: 'natural',
},
],
'perfectionist/sort-named-exports': [
'error',
{
order: 'asc',
type: 'natural',
},
],
'perfectionist/sort-objects': [
'error',
{
'custom-groups': {
items: 'items',
list: 'list',
children: 'children',
},
groups: ['unknown', 'items', 'list', 'children'],
'ignore-pattern': ['children'],
order: 'asc',
'partition-by-comment': 'Part:**',
type: 'natural',
},
],
'perfectionist/sort-vue-attributes': 'off',
},
},
];
}

View File

@@ -0,0 +1,17 @@
import type { Linter } from 'eslint';
export async function prettier(): Promise<Linter.FlatConfig[]> {
const [pluginPrettier] = await Promise.all([
import('eslint-plugin-prettier'),
] as const);
return [
{
plugins: {
prettier: pluginPrettier,
},
rules: {
'prettier/prettier': 'error',
},
},
];
}

View File

@@ -0,0 +1,18 @@
import type { Linter } from 'eslint';
export async function regexp(): Promise<Linter.FlatConfig[]> {
const [pluginRegexp] = await Promise.all([
import('eslint-plugin-regexp'),
] as const);
return [
{
plugins: {
regexp: pluginRegexp,
},
rules: {
...pluginRegexp.configs.recommended.rules,
},
},
];
}

View File

@@ -0,0 +1,44 @@
import type { Linter } from 'eslint';
export async function test(): Promise<Linter.FlatConfig[]> {
const [pluginTest, pluginNoOnlyTests] = await Promise.all([
import('eslint-plugin-vitest'),
// @ts-expect-error - no types
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: {
// @ts-expect-error - no types
...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',
},
},
];
}

View File

@@ -0,0 +1,81 @@
import type { Linter } from 'eslint';
export async function typescript(): Promise<Linter.FlatConfig[]> {
const [pluginTs, parserTs] = await Promise.all([
import('@typescript-eslint/eslint-plugin'),
// @ts-expect-error missing types
import('@typescript-eslint/parser'),
] as const);
return [
{
files: ['**/*.?([cm])[jt]s?(x)'],
languageOptions: {
parser: parserTs,
parserOptions: {
createDefaultProgram: false,
ecmaFeatures: {
jsx: true,
},
ecmaVersion: 'latest',
extraFileExtensions: ['.vue'],
jsxPragma: 'React',
project: './tsconfig.*?.json',
sourceType: 'module',
},
},
plugins: {
'@typescript-eslint': pluginTs,
},
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',
},
],
'@typescript-eslint/ban-types': 'error',
'@typescript-eslint/consistent-type-definitions': ['warn', 'interface'],
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/keyword-spacing': [
'error',
{
after: true,
before: true,
overrides: {
case: { after: true },
return: { after: true },
throw: { after: true },
},
},
],
'@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-vars': [
'error',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
},
],
'@typescript-eslint/no-use-before-define': 'off',
'@typescript-eslint/no-var-requires': 'error',
'unused-imports/no-unused-vars': 'off',
},
},
];
}

View File

@@ -0,0 +1,40 @@
import type { Linter } from 'eslint';
export async function unicorn(): Promise<Linter.FlatConfig[]> {
const [pluginUnicorn] = await Promise.all([
// @ts-expect-error - missing types
import('eslint-plugin-unicorn'),
] as const);
return [
{
plugins: {
unicorn: pluginUnicorn,
},
rules: {
...pluginUnicorn.configs.recommended.rules,
'unicorn/consistent-destructuring': 'off',
'unicorn/consistent-function-scoping': 'off',
'unicorn/filename-case': 'off',
'unicorn/import-style': 'off',
'unicorn/no-array-for-each': 'off',
'unicorn/no-null': 'off',
'unicorn/prefer-at': 'off',
'unicorn/prefer-dom-node-text-content': 'off',
'unicorn/prefer-export-from': ['error', { ignoreUsedVariables: true }],
'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',
},
},
];
}

View File

@@ -0,0 +1,136 @@
import type { Linter } from 'eslint';
export async function vue(): Promise<Linter.FlatConfig[]> {
const [pluginVue, parserVue, parserTs] = await Promise.all([
// @ts-expect-error missing types
import('eslint-plugin-vue'),
import('vue-eslint-parser'),
// @ts-expect-error missing types
import('@typescript-eslint/parser'),
] as const);
return [
{
files: ['**/*.vue'],
languageOptions: {
globals: {
// TODO: 等待插件正式支持后删除
defineModel: true,
},
parser: parserVue,
parserOptions: {
ecmaFeatures: {
jsx: true,
},
extraFileExtensions: ['.vue'],
parser: parserTs,
sourceType: 'module',
},
},
plugins: {
vue: pluginVue,
},
processor: pluginVue.processors['.vue'],
rules: {
...pluginVue.configs.base.rules,
...pluginVue.configs['vue3-essential'].rules,
...pluginVue.configs['vue3-strongly-recommended'].rules,
...pluginVue.configs['vue3-recommended'].rules,
'vue/attribute-hyphenation': [
'error',
'always',
{
ignore: [],
},
],
'vue/attributes-order': 'error',
'vue/block-order': [
'error',
{
order: ['script', 'template', 'style'],
},
],
'vue/component-name-in-template-casing': ['error', 'PascalCase'],
'vue/component-options-name-casing': ['error', 'PascalCase'],
'vue/custom-event-name-casing': ['error', 'camelCase'],
'vue/define-macros-order': [
'error',
{
order: [
'defineOptions',
'defineProps',
'defineEmits',
'defineSlots',
],
},
],
'vue/dot-location': ['error', 'property'],
'vue/dot-notation': ['error', { allowKeywords: true }],
'vue/eqeqeq': ['error', 'smart'],
'vue/html-closing-bracket-newline': 'error',
'vue/html-indent': 'off',
// 'vue/html-indent': ['error', 2],
'vue/html-quotes': ['error', 'double'],
'vue/html-self-closing': [
'error',
{
html: {
component: 'always',
normal: 'never',
void: 'always',
},
math: 'always',
svg: 'always',
},
],
'vue/max-attributes-per-line': 'off',
'vue/multi-word-component-names': 'off',
'vue/multiline-html-element-content-newline': 'error',
'vue/no-empty-pattern': 'error',
'vue/no-extra-parens': ['error', 'functions'],
'vue/no-irregular-whitespace': 'error',
'vue/no-loss-of-precision': 'error',
'vue/no-reserved-component-names': 'off',
'vue/no-restricted-syntax': [
'error',
'DebuggerStatement',
'LabeledStatement',
'WithStatement',
],
'vue/no-restricted-v-bind': ['error', '/^v-/'],
'vue/no-sparse-arrays': 'error',
'vue/no-unused-refs': 'error',
'vue/no-useless-v-bind': 'error',
'vue/object-shorthand': [
'error',
'always',
{
avoidQuotes: true,
ignoreConstructors: false,
},
],
'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'],
'vue/require-default-prop': 'error',
'vue/require-explicit-emits': 'error',
'vue/require-prop-types': 'off',
'vue/script-setup-uses-vars': 'error',
'vue/singleline-html-element-content-newline': 'off',
'vue/space-infix-ops': 'error',
'vue/space-unary-ops': ['error', { nonwords: false, words: true }],
'vue/v-on-event-hyphenation': [
'error',
'always',
{
autofix: true,
ignore: [],
},
],
},
},
];
}

View File

@@ -0,0 +1,12 @@
import type { Linter } from 'eslint';
const customConfig: Linter.FlatConfig[] = [
{
files: ['packages/@vben-core/uikit/shadcn-ui/**/**'],
rules: {
'vue/require-default-prop': 'off',
},
},
];
export { customConfig };

View File

@@ -0,0 +1,56 @@
import type { Linter } from 'eslint';
import {
command,
comments,
ignores,
importPluginConfig,
javascript,
jsdoc,
jsonc,
node,
perfectionist,
prettier,
regexp,
test,
typescript,
unicorn,
vue,
} from './configs';
import { customConfig } from './custom-config';
type FlatConfig = Linter.FlatConfig;
type FlatConfigPromise =
| FlatConfig
| FlatConfig[]
| Promise<FlatConfig>
| Promise<FlatConfig[]>;
async function defineConfig(config: FlatConfig[] = []) {
const configs: FlatConfigPromise[] = [
vue(),
javascript(),
ignores(),
prettier(),
typescript(),
jsonc(),
importPluginConfig(),
node(),
perfectionist(),
comments(),
jsdoc(),
unicorn(),
test(),
regexp(),
command(),
...customConfig,
...config,
];
const resolved = await Promise.all(configs);
return resolved.flat();
}
export { defineConfig };

View File

@@ -0,0 +1,5 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "@vben/tsconfig/node.json",
"include": ["src"]
}

View File

@@ -0,0 +1,7 @@
import { defineBuildConfig } from 'unbuild';
export default defineBuildConfig({
clean: true,
declaration: true,
entries: ['src/index'],
});

View File

@@ -0,0 +1,38 @@
{
"name": "@vben/lint-staged-config",
"version": "1.0.0",
"private": true,
"type": "module",
"license": "MIT",
"homepage": "https://github.com/vbenjs/vue-vben-admin",
"repository": {
"type": "git",
"url": "git+https://github.com/vbenjs/vue-vben-admin.git",
"directory": "internal/lint-configs/lint-staged-config"
},
"bugs": {
"url": "https://github.com/vbenjs/vue-vben-admin/issues"
},
"scripts": {
"stub": "pnpm unbuild --stub"
},
"files": [
"dist"
],
"main": "./dist/index.mjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"imports": {
"#*": "./src/*"
},
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"default": "./dist/index.mjs"
}
},
"dependencies": {
"lint-staged": "15.2.2"
}
}

View File

@@ -0,0 +1,16 @@
export default {
'*.{js,jsx,ts,tsx}': [
'prettier --cache --ignore-unknown --write',
'eslint --cache --fix',
],
'*.{scss,less,styl,html,vue,css}': [
'prettier --cache --ignore-unknown --write',
'stylelint --fix',
],
'*.md': ['prettier --cache --ignore-unknown --write'],
'*.vue': ['prettier --write', 'eslint --cache --fix', 'stylelint --fix'],
'{!(package)*.json,*.code-snippets,.!(browserslist)*rc}': [
'prettier --cache --write--parser json',
],
'package.json': ['prettier --cache --write'],
};

View File

@@ -0,0 +1,5 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "@vben/tsconfig/node.json",
"include": ["src"]
}

View File

@@ -0,0 +1,7 @@
import { defineBuildConfig } from 'unbuild';
export default defineBuildConfig({
clean: true,
declaration: true,
entries: ['src/index'],
});

View File

@@ -0,0 +1,39 @@
{
"name": "@vben/prettier-config",
"version": "1.0.0",
"private": true,
"type": "module",
"license": "MIT",
"homepage": "https://github.com/vbenjs/vue-vben-admin",
"repository": {
"type": "git",
"url": "git+https://github.com/vbenjs/vue-vben-admin.git",
"directory": "internal/lint-configs/prettier-config"
},
"bugs": {
"url": "https://github.com/vbenjs/vue-vben-admin/issues"
},
"scripts": {
"stub": "pnpm unbuild --stub"
},
"files": [
"dist"
],
"main": "./dist/index.mjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"imports": {
"#*": "./src/*"
},
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"default": "./dist/index.mjs"
}
},
"dependencies": {
"prettier": "^3.2.5",
"prettier-plugin-tailwindcss": "^0.5.14"
}
}

View File

@@ -0,0 +1,24 @@
export default {
endOfLine: 'auto',
overrides: [
{
files: ['*.json5'],
options: {
quoteProps: 'preserve',
singleQuote: false,
},
},
{
files: ['*.yaml', '*.yml'],
options: {
singleQuote: false,
},
},
],
plugins: ['prettier-plugin-tailwindcss'],
printWidth: 80,
proseWrap: 'never',
semi: true,
singleQuote: true,
trailingComma: 'all',
};

View File

@@ -0,0 +1,5 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "@vben/tsconfig/node.json",
"include": ["src"]
}

View File

@@ -0,0 +1,7 @@
import { defineBuildConfig } from 'unbuild';
export default defineBuildConfig({
clean: true,
declaration: true,
entries: ['src/index'],
});

View File

@@ -0,0 +1,53 @@
{
"name": "@vben/stylelint-config",
"version": "1.0.0",
"private": true,
"type": "module",
"license": "MIT",
"homepage": "https://github.com/vbenjs/vue-vben-admin",
"repository": {
"type": "git",
"url": "git+https://github.com/vbenjs/vue-vben-admin.git",
"directory": "internal/lint-configs/stylelint-config"
},
"bugs": {
"url": "https://github.com/vbenjs/vue-vben-admin/issues"
},
"scripts": {
"stub": "pnpm unbuild --stub"
},
"files": [
"dist"
],
"main": "./dist/index.mjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"imports": {
"#*": "./src/*"
},
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"default": "./dist/index.mjs"
}
},
"dependencies": {
"@stylistic/stylelint-plugin": "^2.1.2",
"stylelint-config-recess-order": "^5.0.1",
"stylelint-scss": "^6.3.0"
},
"devDependencies": {
"postcss": "^8.4.38",
"postcss-html": "^1.7.0",
"postcss-scss": "^4.0.9",
"prettier": "^3.2.5",
"stylelint": "^16.5.0",
"stylelint-config-recommended": "^14.0.0",
"stylelint-config-recommended-scss": "^14.0.0",
"stylelint-config-recommended-vue": "^1.5.0",
"stylelint-config-standard": "^36.0.0",
"stylelint-order": "^6.0.4",
"stylelint-prettier": "^5.0.0"
}
}

View File

@@ -0,0 +1,140 @@
export default {
extends: ['stylelint-config-standard', 'stylelint-config-recess-order'],
ignoreFiles: [
'**/*.js',
'**/*.jsx',
'**/*.tsx',
'**/*.ts',
'**/*.json',
'**/*.md',
],
overrides: [
{
customSyntax: 'postcss-html',
files: ['**/*.(css|html|vue)'],
rules: {
'selector-pseudo-class-no-unknown': [
true,
{
ignorePseudoClasses: ['global', 'deep'],
},
],
'selector-pseudo-element-no-unknown': [
true,
{
ignorePseudoElements: ['v-deep', 'v-global', 'v-slotted'],
},
],
},
},
{
customSyntax: 'postcss-scss',
extends: [
'stylelint-config-recommended-scss',
'stylelint-config-recommended-vue/scss',
],
files: ['*.scss', '**/*.scss'],
},
],
plugins: [
'stylelint-order',
'@stylistic/stylelint-plugin',
'stylelint-prettier',
'stylelint-scss',
],
rules: {
'at-rule-no-unknown': [
true,
{
ignoreAtRules: [
'extends',
'ignores',
'include',
'mixin',
'if',
'else',
'media',
'for',
'at-root',
'tailwind',
'apply',
'variants',
'responsive',
'screen',
'function',
'each',
'use',
'forward',
'return',
],
},
],
'font-family-no-missing-generic-family-keyword': null,
'function-no-unknown': null,
'import-notation': null,
'media-feature-range-notation': null,
'named-grid-areas-no-invalid': null,
'no-descending-specificity': null,
'no-empty-source': null,
'order/order': [
[
'dollar-variables',
'custom-properties',
'at-rules',
'declarations',
{
name: 'supports',
type: 'at-rule',
},
{
name: 'media',
type: 'at-rule',
},
{
name: 'include',
type: 'at-rule',
},
'rules',
],
{ severity: 'error' },
],
'prettier/prettier': true,
'rule-empty-line-before': [
'always',
{
ignore: ['after-comment', 'first-nested'],
},
],
'scss/at-rule-no-unknown': [
true,
{
ignoreAtRules: [
'extends',
'ignores',
'include',
'mixin',
'if',
'else',
'media',
'for',
'at-root',
'tailwind',
'apply',
'variants',
'responsive',
'screen',
'function',
'each',
'use',
'forward',
'return',
],
},
],
'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]+)*)?(?:[.+])?$',
'selector-not-notation': null,
},
};

View File

@@ -0,0 +1,5 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "@vben/tsconfig/node.json",
"include": ["src"]
}