无用代码去除

This commit is contained in:
JenniferW 2026-01-15 16:08:57 +08:00
parent ed9536f6ab
commit badc8c1cf4
2337 changed files with 600575 additions and 82265 deletions

View File

@ -1,4 +0,0 @@
> 1%
last 2 versions
not dead
not ie 11

View File

@ -1,5 +0,0 @@
# Changesets
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works with multi-package repos, or single-package repos to help you version and publish your code. You can find the full documentation for it [in our repository](https://github.com/changesets/changesets)
We have a quick list of common questions to get you started engaging with this project in [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)

View File

@ -1,18 +0,0 @@
{
"$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json",
"changelog": [
"@changesets/changelog-github",
{ "repo": "aiflowy/aiflowy" }
],
"commit": false,
"fixed": [["@aiflowy-core/*", "@aiflowy/*"]],
"snapshot": {
"prereleaseTemplate": "{tag}-{datetime}"
},
"privatePackages": { "version": true, "tag": true },
"linked": [],
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": []
}

View File

@ -1 +0,0 @@
export { default } from '@aiflowy/commitlint-config';

View File

@ -1,7 +0,0 @@
node_modules
.git
.gitignore
*.md
dist
.turbo
dist.zip

View File

@ -1,18 +0,0 @@
root = true
[*]
charset=utf-8
end_of_line=lf
insert_final_newline=true
indent_style=space
indent_size=2
max_line_length = 100
trim_trailing_whitespace = true
quote_type = single
[*.{yml,yaml,json}]
indent_style = space
indent_size = 2
[*.md]
trim_trailing_whitespace = false

View File

11
.gitattributes vendored
View File

@ -1,11 +0,0 @@
# https://docs.github.com/cn/get-started/getting-started-with-git/configuring-git-to-handle-line-endings
# Automatically normalize line endings (to LF) for all text-based files.
* text=auto eol=lf
# Declare files that will always have CRLF line endings on checkout.
*.{cmd,[cC][mM][dD]} text eol=crlf
*.{bat,[bB][aA][tT]} text eol=crlf
# Denote all files that are truly binary and should not be modified.
*.{ico,png,jpg,jpeg,gif,webp,svg,woff,woff2} binary

View File

@ -1,2 +0,0 @@
[core]
ignorecase = false

52
.gitignore vendored
View File

@ -1,52 +0,0 @@
node_modules
.DS_Store
dist
dist-ssr
dist.zip
dist.tar
dist.war
.nitro
.output
*-dist.zip
*-dist.tar
*-dist.war
coverage
*.local
**/.vitepress/cache
.cache
.turbo
.temp
dev-dist
.stylelintcache
yarn.lock
package-lock.json
.VSCodeCounter
**/backend-mock/data
# local env files
.env.local
.env.*.local
.eslintcache
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
vite.config.mts.*
vite.config.mjs.*
vite.config.js.*
vite.config.ts.*
# Editor directories and files
.idea
# .vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
.history
.cursor

View File

@ -1,6 +0,0 @@
ports:
- port: 5555
onOpen: open-preview
tasks:
- init: npm i -g corepack && pnpm install
command: pnpm run dev:play

View File

@ -1 +0,0 @@
22.1.0

13
.npmrc
View File

@ -1,13 +0,0 @@
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[]=stylelint
public-hoist-pattern[]=*postcss*
public-hoist-pattern[]=@commitlint/*
public-hoist-pattern[]=czg
strict-peer-dependencies=false
auto-install-peers=true
dedupe-peer-dependents=true

View File

@ -1,18 +0,0 @@
dist
dev-dist
.local
.output.js
node_modules
.nvmrc
coverage
CODEOWNERS
.nitro
.output
**/*.svg
**/*.sh
public
.npmrc
*-lock.yaml

View File

@ -1 +0,0 @@
export { default } from '@aiflowy/prettier-config';

View File

@ -1,4 +0,0 @@
dist
public
__tests__
coverage

View File

@ -1,152 +0,0 @@
{
"folders": [
{
"name": "@aiflowy/app",
"path": "app",
},
{
"name": "@aiflowy/commitlint-config",
"path": "internal/lint-configs/commitlint-config",
},
{
"name": "@aiflowy/eslint-config",
"path": "internal/lint-configs/eslint-config",
},
{
"name": "@aiflowy/prettier-config",
"path": "internal/lint-configs/prettier-config",
},
{
"name": "@aiflowy/stylelint-config",
"path": "internal/lint-configs/stylelint-config",
},
{
"name": "@aiflowy/node-utils",
"path": "internal/node-utils",
},
{
"name": "@aiflowy/tailwind-config",
"path": "internal/tailwind-config",
},
{
"name": "@aiflowy/tsconfig",
"path": "internal/tsconfig",
},
{
"name": "@aiflowy/vite-config",
"path": "internal/vite-config",
},
{
"name": "@aiflowy-core/design",
"path": "packages/@core/base/design",
},
{
"name": "@aiflowy-core/icons",
"path": "packages/@core/base/icons",
},
{
"name": "@aiflowy-core/shared",
"path": "packages/@core/base/shared",
},
{
"name": "@aiflowy-core/typings",
"path": "packages/@core/base/typings",
},
{
"name": "@aiflowy-core/composables",
"path": "packages/@core/composables",
},
{
"name": "@aiflowy-core/preferences",
"path": "packages/@core/preferences",
},
{
"name": "@aiflowy-core/form-ui",
"path": "packages/@core/ui-kit/form-ui",
},
{
"name": "@aiflowy-core/layout-ui",
"path": "packages/@core/ui-kit/layout-ui",
},
{
"name": "@aiflowy-core/menu-ui",
"path": "packages/@core/ui-kit/menu-ui",
},
{
"name": "@aiflowy-core/popup-ui",
"path": "packages/@core/ui-kit/popup-ui",
},
{
"name": "@aiflowy-core/shadcn-ui",
"path": "packages/@core/ui-kit/shadcn-ui",
},
{
"name": "@aiflowy-core/tabs-ui",
"path": "packages/@core/ui-kit/tabs-ui",
},
{
"name": "@aiflowy/constants",
"path": "packages/constants",
},
{
"name": "@aiflowy/access",
"path": "packages/effects/access",
},
{
"name": "@aiflowy/common-ui",
"path": "packages/effects/common-ui",
},
{
"name": "@aiflowy/hooks",
"path": "packages/effects/hooks",
},
{
"name": "@aiflowy/layouts",
"path": "packages/effects/layouts",
},
{
"name": "@aiflowy/plugins",
"path": "packages/effects/plugins",
},
{
"name": "@aiflowy/request",
"path": "packages/effects/request",
},
{
"name": "@aiflowy/icons",
"path": "packages/icons",
},
{
"name": "@aiflowy/locales",
"path": "packages/locales",
},
{
"name": "@aiflowy/preferences",
"path": "packages/preferences",
},
{
"name": "@aiflowy/stores",
"path": "packages/stores",
},
{
"name": "@aiflowy/styles",
"path": "packages/styles",
},
{
"name": "@aiflowy/types",
"path": "packages/types",
},
{
"name": "@aiflowy/utils",
"path": "packages/utils",
},
{
"name": "@aiflowy/turbo-run",
"path": "scripts/turbo-run",
},
{
"name": "@aiflowy/vsh",
"path": "scripts/vsh",
},
],
}

View File

@ -1,59 +0,0 @@
{
"name": "@aiflowy/app",
"version": "1.0.0",
"homepage": "https://aiflowy.tech",
"bugs": "https://github.com/aiflowy/aiflowy/issues",
"repository": {
"type": "git",
"url": "git+https://github.com/aiflowy/aiflowy.git",
"directory": "app"
},
"type": "module",
"scripts": {
"build": "pnpm vite build --mode production",
"build:analyze": "pnpm vite build --mode analyze",
"dev": "pnpm vite --mode development",
"preview": "vite preview",
"typecheck": "vue-tsc --noEmit --skipLibCheck"
},
"imports": {
"#/*": "./src/*"
},
"dependencies": {
"@aiflowy-core/shadcn-ui": "workspace:*",
"@aiflowy/access": "workspace:*",
"@aiflowy/common-ui": "workspace:*",
"@aiflowy/constants": "workspace:*",
"@aiflowy/hooks": "workspace:*",
"@aiflowy/icons": "workspace:*",
"@aiflowy/layouts": "workspace:*",
"@aiflowy/locales": "workspace:*",
"@aiflowy/plugins": "workspace:*",
"@aiflowy/preferences": "workspace:*",
"@aiflowy/request": "workspace:*",
"@aiflowy/stores": "workspace:*",
"@aiflowy/styles": "workspace:*",
"@aiflowy/types": "workspace:*",
"@aiflowy/utils": "workspace:*",
"@element-plus/icons-vue": "^2.3.2",
"@tinyflow-ai/vue": "^1.1.10",
"@vueuse/core": "catalog:",
"dayjs": "catalog:",
"dompurify": "^3.3.1",
"element-plus": "catalog:",
"fetch-event-stream": "^0.1.6",
"highlight.js": "^11.11.1",
"markdown-it": "^14.1.0",
"pinia": "catalog:",
"radash": "^12.1.1",
"vue": "catalog:",
"vue-cropper": "^1.1.4",
"vue-element-plus-x": "^1.3.7",
"vue-router": "catalog:",
"vue3-json-viewer": "^2.4.1"
},
"devDependencies": {
"cssnano": "catalog:",
"unplugin-element-plus": "catalog:"
}
}

View File

@ -1,69 +0,0 @@
{
"$schema": "https://raw.githubusercontent.com/streetsidesoftware/cspell/main/cspell.schema.json",
"version": "0.2",
"language": "en,en-US",
"allowCompoundWords": true,
"words": [
"acmr",
"antd",
"antdv",
"astro",
"brotli",
"clsx",
"defu",
"demi",
"echarts",
"ependencies",
"esno",
"etag",
"execa",
"iconify",
"iconoir",
"intlify",
"lockb",
"lucide",
"minh",
"minw",
"mkdist",
"mockjs",
"naiveui",
"nocheck",
"noopener",
"noreferrer",
"nprogress",
"nuxt",
"pinia",
"prefixs",
"publint",
"qrcode",
"reka",
"shadcn",
"sonner",
"sortablejs",
"styl",
"taze",
"ui-kit",
"uicons",
"unplugin",
"unref",
"aiflowy",
"aiflowy",
"vite",
"vitejs",
"vitepress",
"vnode",
"vueuse",
"yxxx"
],
"ignorePaths": [
"**/node_modules/**",
"**/dist/**",
"**/*-dist/**",
"**/icons/**",
"pnpm-lock.yaml",
"**/*.log",
"**/*.test.ts",
"**/*.spec.ts",
"**/__tests__/**"
]
}

View File

@ -1,5 +0,0 @@
// @ts-check
import { defineConfig } from '@aiflowy/eslint-config';
export default defineConfig();

View File

@ -1,153 +0,0 @@
import { execSync } from 'node:child_process';
import { getPackagesSync } from '@aiflowy/node-utils';
const { packages } = getPackagesSync();
const allowedScopes = [
...packages.map((pkg) => pkg.packageJson.name),
'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$/, '');
/**
* @type {import('cz-git').UserConfig}
*/
const userConfig = {
extends: ['@commitlint/config-conventional'],
plugins: ['commitlint-plugin-function-rules'],
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: '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
* ^^^^^
*/
'function-rules/scope-enum': [
2, // level: error
'always',
(parsed) => {
if (!parsed.scope || allowedScopes.includes(parsed.scope)) {
return [true];
}
return [false, `scope must be one of ${allowedScopes.join(', ')}`];
},
],
/**
* type[scope]: [function] description [No more than 108 characters]
* ^^^^^
*/
'header-max-length': [2, 'always', 108],
'scope-enum': [0],
'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',
],
],
},
};
export default userConfig;

View File

@ -1,33 +0,0 @@
{
"name": "@aiflowy/commitlint-config",
"version": "1.0.0",
"private": true,
"homepage": "https://github.com/aiflowy/aiflowy",
"bugs": "https://github.com/aiflowy/aiflowy/issues",
"repository": {
"type": "git",
"url": "git+https://github.com/aiflowy/aiflowy.git",
"directory": "internal/lint-configs/commitlint-config"
},
"license": "MIT",
"type": "module",
"files": [
"dist"
],
"main": "./index.mjs",
"module": "./index.mjs",
"exports": {
".": {
"import": "./index.mjs",
"default": "./index.mjs"
}
},
"dependencies": {
"@commitlint/cli": "catalog:",
"@commitlint/config-conventional": "catalog:",
"@aiflowy/node-utils": "workspace:*",
"commitlint-plugin-function-rules": "catalog:",
"cz-git": "catalog:",
"czg": "catalog:"
}
}

View File

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

View File

@ -1,56 +0,0 @@
{
"name": "@aiflowy/eslint-config",
"version": "5.0.0",
"private": true,
"homepage": "https://github.com/aiflowy/aiflowy",
"bugs": "https://github.com/aiflowy/aiflowy/issues",
"repository": {
"type": "git",
"url": "git+https://github.com/aiflowy/aiflowy.git",
"directory": "internal/lint-configs/eslint-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-config-turbo": "catalog:",
"eslint-plugin-command": "catalog:",
"eslint-plugin-import-x": "catalog:"
},
"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-prettier": "catalog:",
"eslint-plugin-regexp": "catalog:",
"eslint-plugin-unicorn": "catalog:",
"eslint-plugin-unused-imports": "catalog:",
"eslint-plugin-vitest": "catalog:",
"eslint-plugin-vue": "catalog:",
"globals": "catalog:",
"jsonc-eslint-parser": "catalog:",
"vue-eslint-parser": "catalog:"
}
}

View File

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

View File

@ -1,24 +0,0 @@
import type { Linter } from 'eslint';
import { interopDefault } from '../util';
export async function comments(): Promise<Linter.Config[]> {
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',
},
},
];
}

View File

@ -1,28 +0,0 @@
import type { Linter } from 'eslint';
export async function disableds(): Promise<Linter.Config[]> {
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',
},
},
];
}

View File

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

View File

@ -1,25 +0,0 @@
import type { Linter } from 'eslint';
import * as pluginImport from 'eslint-plugin-import-x';
export async function importPluginConfig(): Promise<Linter.Config[]> {
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',
},
},
];
}

View File

@ -1,17 +0,0 @@
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 './prettier';
export * from './regexp';
export * from './test';
export * from './turbo';
export * from './typescript';
export * from './unicorn';
export * from './vue';

View File

@ -1,241 +0,0 @@
import type { Linter } from 'eslint';
import js from '@eslint/js';
import pluginUnusedImports from 'eslint-plugin-unused-imports';
import globals from 'globals';
export async function javascript(): Promise<Linter.Config[]> {
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': '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': [
'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

@ -1,34 +0,0 @@
import type { Linter } from 'eslint';
import { interopDefault } from '../util';
export async function jsdoc(): Promise<Linter.Config[]> {
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',
},
},
];
}

View File

@ -1,258 +0,0 @@
import type { Linter } from 'eslint';
import { interopDefault } from '../util';
export async function jsonc(): Promise<Linter.Config[]> {
const [pluginJsonc, parserJsonc] = await Promise.all([
interopDefault(import('eslint-plugin-jsonc')),
interopDefault(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.Config {
return {
files: ['**/package.json'],
rules: {
'jsonc/sort-array-values': [
'error',
{
order: { type: 'asc' },
pathPattern: '^files$|^pnpm.neverBuiltDependencies$',
},
],
'jsonc/sort-keys': [
'error',
{
order: [
'name',
'version',
'description',
'private',
'keywords',
'homepage',
'bugs',
'repository',
'license',
'author',
'contributors',
'categories',
'funding',
'type',
'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.Config {
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

@ -1,57 +0,0 @@
import type { Linter } from 'eslint';
import { interopDefault } from '../util';
export async function node(): Promise<Linter.Config[]> {
const pluginNode = await interopDefault(import('eslint-plugin-n'));
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',
'@aiflowy/vite-config',
'vitest',
'vite',
'@vue/test-utils',
'@aiflowy/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',
{
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

@ -1,89 +0,0 @@
import type { Linter } from 'eslint';
import { interopDefault } from '../util';
export async function perfectionist(): Promise<Linter.Config[]> {
const perfectionistPlugin = await interopDefault(
// @ts-expect-error - no types
import('eslint-plugin-perfectionist'),
);
return [
perfectionistPlugin.configs['recommended-natural'],
{
rules: {
'perfectionist/sort-exports': [
'error',
{
order: 'asc',
type: 'natural',
},
],
'perfectionist/sort-imports': [
'error',
{
customGroups: {
type: {
'aiflowy-core-type': ['^@aiflowy-core/.+'],
'aiflowy-type': ['^@aiflowy/.+'],
'vue-type': ['^vue$', '^vue-.+', '^@vue/.+'],
},
value: {
aiflowy: ['^@aiflowy/.+'],
'aiflowy-core': ['^@aiflowy-core/.+'],
vue: ['^vue$', '^vue-.+', '^@vue/.+'],
},
},
environment: 'node',
groups: [
['external-type', 'builtin-type', 'type'],
'vue-type',
'aiflowy-type',
'aiflowy-core-type',
['parent-type', 'sibling-type', 'index-type'],
['internal-type'],
'builtin',
'vue',
'aiflowy',
'aiflowy-core',
'external',
'internal',
['parent', 'sibling', 'index'],
'side-effect',
'side-effect-style',
'style',
'object',
'unknown',
],
internalPattern: ['^#/.+'],
newlinesBetween: 'always',
order: 'asc',
type: 'natural',
},
],
'perfectionist/sort-modules': 'off',
'perfectionist/sort-named-exports': [
'error',
{
order: 'asc',
type: 'natural',
},
],
'perfectionist/sort-objects': [
'off',
{
customGroups: {
items: 'items',
list: 'list',
children: 'children',
},
groups: ['unknown', 'items', 'list', 'children'],
ignorePattern: ['children'],
order: 'asc',
type: 'natural',
},
],
},
},
];
}

View File

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

View File

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

View File

@ -1,45 +0,0 @@
import type { Linter } from 'eslint';
import { interopDefault } from '../util';
export async function test(): Promise<Linter.Config[]> {
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',
},
},
];
}

View File

@ -1,18 +0,0 @@
import type { Linter } from 'eslint';
import { interopDefault } from '../util';
export async function turbo(): Promise<Linter.Config[]> {
const [pluginTurbo] = await Promise.all([
// @ts-expect-error - no types
interopDefault(import('eslint-config-turbo')),
] as const);
return [
{
plugins: {
turbo: pluginTurbo,
},
},
];
}

View File

@ -1,72 +0,0 @@
import type { Linter } from 'eslint';
import { interopDefault } from '../util';
export async function typescript(): Promise<Linter.Config[]> {
const [pluginTs, parserTs] = await Promise.all([
interopDefault(import('@typescript-eslint/eslint-plugin')),
// @ts-expect-error missing types
interopDefault(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/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',
},
},
];
}

View File

@ -1,45 +0,0 @@
import type { Linter } from 'eslint';
import { interopDefault } from '../util';
export async function unicorn(): Promise<Linter.Config[]> {
const [pluginUnicorn] = await Promise.all([
interopDefault(import('eslint-plugin-unicorn')),
] as const);
return [
{
plugins: {
unicorn: pluginUnicorn,
},
rules: {
...pluginUnicorn.configs.recommended.rules,
'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',
'unicorn/no-array-for-each': 'off',
'unicorn/no-null': 'off',
'unicorn/no-useless-undefined': 'off',
'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',
},
},
];
}

View File

@ -1,153 +0,0 @@
import type { Linter } from 'eslint';
import { interopDefault } from '../util';
export async function vue(): Promise<Linter.Config[]> {
const [pluginVue, parserVue, parserTs] = await Promise.all([
interopDefault(import('eslint-plugin-vue')),
interopDefault(import('vue-eslint-parser')),
// @ts-expect-error missing types
interopDefault(import('@typescript-eslint/parser')),
] as const);
const flatEssential = pluginVue.configs?.['flat/essential'] || [];
const flatStronglyRecommended =
pluginVue.configs?.['flat/strongly-recommended'] || [];
const flatRecommended = pluginVue.configs?.['flat/recommended'] || [];
return [
...flatEssential,
...flatStronglyRecommended,
...flatRecommended,
{
files: ['**/*.vue'],
languageOptions: {
// globals: {
// computed: 'readonly',
// defineEmits: 'readonly',
// defineExpose: 'readonly',
// defineProps: 'readonly',
// onMounted: 'readonly',
// onUnmounted: 'readonly',
// reactive: 'readonly',
// ref: 'readonly',
// shallowReactive: 'readonly',
// shallowRef: 'readonly',
// toRef: 'readonly',
// toRefs: 'readonly',
// watch: 'readonly',
// watchEffect: 'readonly',
// },
parser: parserVue,
parserOptions: {
ecmaFeatures: {
jsx: true,
},
extraFileExtensions: ['.vue'],
parser: parserTs,
sourceType: 'module',
},
},
plugins: {
vue: pluginVue,
},
processor: pluginVue.processors?.['.vue'],
rules: {
...pluginVue.configs?.base?.rules,
'vue/attribute-hyphenation': [
'error',
'always',
{
ignore: [],
},
],
'vue/attributes-order': 'off',
'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/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

@ -1,156 +0,0 @@
import type { Linter } from 'eslint';
const restrictedImportIgnores = [
'**/vite.config.mts',
'**/tailwind.config.mjs',
'**/postcss.config.mjs',
];
const customConfig: Linter.Config[] = [
// shadcn-ui 内部组件是自动生成的,不做太多限制
{
files: ['packages/@core/ui-kit/shadcn-ui/**/**'],
rules: {
'vue/require-default-prop': 'off',
},
},
{
files: [
'app/**',
'packages/effects/**/**',
'packages/utils/**/**',
'packages/types/**/**',
'packages/locales/**/**',
],
ignores: restrictedImportIgnores,
rules: {
'perfectionist/sort-interfaces': 'off',
'perfectionist/sort-objects': 'off',
},
},
{
files: ['**/**.vue'],
ignores: restrictedImportIgnores,
rules: {
'perfectionist/sort-objects': 'off',
},
},
{
// apps内部的一些基础规则
files: ['app/**'],
ignores: restrictedImportIgnores,
rules: {
'no-restricted-imports': [
'error',
{
patterns: [
{
group: ['#/api/*'],
message:
'The #/api package cannot be imported, please use the @core package itself',
},
{
group: ['#/layouts/*'],
message:
'The #/layouts package cannot be imported, please use the @core package itself',
},
{
group: ['#/locales/*'],
message:
'The #/locales package cannot be imported, please use the @core package itself',
},
{
group: ['#/stores/*'],
message:
'The #/stores package cannot be imported, please use the @core package itself',
},
],
},
],
'perfectionist/sort-interfaces': 'off',
},
},
{
// @core内部组件不能引入@aiflowy/* 里面的包
files: ['packages/@core/**/**'],
ignores: restrictedImportIgnores,
rules: {
'no-restricted-imports': [
'error',
{
patterns: [
{
group: ['@aiflowy/*'],
message:
'The @core package cannot import the @aiflowy package, please use the @core package itself',
},
],
},
],
},
},
{
// @core/shared内部组件不能引入@aiflowy/* 或者 @aiflowy-core/* 里面的包
files: ['packages/@core/base/**/**'],
ignores: restrictedImportIgnores,
rules: {
'no-restricted-imports': [
'error',
{
patterns: [
{
group: ['@aiflowy/*', '@aiflowy-core/*'],
message:
'The @aiflowy-core/shared package cannot import the @aiflowy package, please use the @core/shared package itself',
},
],
},
],
},
},
{
// 不能引入@aiflowy/*里面的包
files: [
'packages/types/**/**',
'packages/utils/**/**',
'packages/icons/**/**',
'packages/constants/**/**',
'packages/styles/**/**',
'packages/stores/**/**',
'packages/preferences/**/**',
'packages/locales/**/**',
],
ignores: restrictedImportIgnores,
rules: {
'no-restricted-imports': [
'error',
{
patterns: [
{
group: ['@aiflowy/*'],
message:
'The @aiflowy package cannot be imported, please use the @core package itself',
},
],
},
],
},
},
{
files: ['**/**/playwright.config.ts'],
rules: {
'n/prefer-global/buffer': 'off',
'n/prefer-global/process': 'off',
'no-console': 'off',
},
},
{
files: ['internal/**/**', 'scripts/**/**'],
rules: {
'no-console': 'off',
},
},
];
export { customConfig };

View File

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

View File

@ -1,8 +0,0 @@
export type Awaitable<T> = Promise<T> | T;
export async function interopDefault<T>(
m: Awaitable<T>,
): Promise<T extends { default: infer U } ? U : T> {
const resolved = await m;
return (resolved as any).default || resolved;
}

View File

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

View File

@ -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',
};

View File

@ -1,28 +0,0 @@
{
"name": "@aiflowy/prettier-config",
"version": "5.0.0",
"private": true,
"homepage": "https://github.com/aiflowy/aiflowy",
"bugs": "https://github.com/aiflowy/aiflowy/issues",
"repository": {
"type": "git",
"url": "git+https://github.com/aiflowy/aiflowy.git",
"directory": "internal/lint-configs/prettier-config"
},
"license": "MIT",
"type": "module",
"files": [
"dist"
],
"main": "./index.mjs",
"module": "./index.mjs",
"exports": {
".": {
"default": "./index.mjs"
}
},
"dependencies": {
"prettier": "catalog:",
"prettier-plugin-tailwindcss": "catalog:"
}
}

View File

@ -1,141 +0,0 @@
export default {
extends: ['stylelint-config-standard', 'stylelint-config-recess-order'],
ignoreFiles: [
'**/*.js',
'**/*.jsx',
'**/*.tsx',
'**/*.ts',
'**/*.json',
'**/*.md',
],
overrides: [
{
customSyntax: 'postcss-html',
files: ['*.(html|vue)', '**/*.(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-deprecated': null,
'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

@ -1,43 +0,0 @@
{
"name": "@aiflowy/stylelint-config",
"version": "1.0.0",
"private": true,
"homepage": "https://github.com/aiflowy/aiflowy",
"bugs": "https://github.com/aiflowy/aiflowy/issues",
"repository": {
"type": "git",
"url": "git+https://github.com/aiflowy/aiflowy.git",
"directory": "internal/lint-configs/stylelint-config"
},
"license": "MIT",
"type": "module",
"files": [
"dist"
],
"main": "./index.mjs",
"module": "./index.mjs",
"exports": {
".": {
"import": "./index.mjs",
"default": "./index.mjs"
}
},
"dependencies": {
"@stylistic/stylelint-plugin": "catalog:",
"stylelint-config-recess-order": "catalog:",
"stylelint-scss": "catalog:"
},
"devDependencies": {
"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:"
}
}

View File

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

View File

@ -1,43 +0,0 @@
{
"name": "@aiflowy/node-utils",
"version": "1.0.0",
"private": true,
"homepage": "https://github.com/aiflowy/aiflowy",
"bugs": "https://github.com/aiflowy/aiflowy/issues",
"repository": {
"type": "git",
"url": "git+https://github.com/aiflowy/aiflowy.git",
"directory": "internal/node-utils"
},
"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": "./src/index.ts",
"import": "./dist/index.mjs",
"default": "./dist/index.mjs"
}
},
"dependencies": {
"@changesets/git": "catalog:",
"@manypkg/get-packages": "catalog:",
"chalk": "catalog:",
"consola": "catalog:",
"dayjs": "catalog:",
"execa": "catalog:",
"find-up": "catalog:",
"ora": "catalog:",
"pkg-types": "catalog:",
"prettier": "catalog:",
"rimraf": "catalog:"
}
}

View File

@ -1,52 +0,0 @@
import { createHash } from 'node:crypto';
import { describe, expect, it } from 'vitest';
import { generatorContentHash } from '../hash';
describe('generatorContentHash', () => {
it('should generate an MD5 hash for the content', () => {
const content = 'example content';
const expectedHash = createHash('md5')
.update(content, 'utf8')
.digest('hex');
const actualHash = generatorContentHash(content);
expect(actualHash).toBe(expectedHash);
});
it('should generate an MD5 hash with specified length', () => {
const content = 'example content';
const hashLength = 10;
const generatedHash = generatorContentHash(content, hashLength);
expect(generatedHash).toHaveLength(hashLength);
});
it('should correctly generate the hash with specified length', () => {
const content = 'example content';
const hashLength = 8;
const expectedHash = createHash('md5')
.update(content, 'utf8')
.digest('hex')
.slice(0, hashLength);
const generatedHash = generatorContentHash(content, hashLength);
expect(generatedHash).toBe(expectedHash);
});
it('should return full hash if hash length parameter is not provided', () => {
const content = 'example content';
const expectedHash = createHash('md5')
.update(content, 'utf8')
.digest('hex');
const actualHash = generatorContentHash(content);
expect(actualHash).toBe(expectedHash);
});
it('should handle empty content', () => {
const content = '';
const expectedHash = createHash('md5')
.update(content, 'utf8')
.digest('hex');
const actualHash = generatorContentHash(content);
expect(actualHash).toBe(expectedHash);
});
});

View File

@ -1,67 +0,0 @@
// pathUtils.test.ts
import { describe, expect, it } from 'vitest';
import { toPosixPath } from '../path';
describe('toPosixPath', () => {
// 测试 Windows 风格路径到 POSIX 风格路径的转换
it('converts Windows-style paths to POSIX paths', () => {
const windowsPath = String.raw`C:\Users\Example\file.txt`;
const expectedPosixPath = 'C:/Users/Example/file.txt';
expect(toPosixPath(windowsPath)).toBe(expectedPosixPath);
});
// 确认 POSIX 风格路径不会被改变
it('leaves POSIX-style paths unchanged', () => {
const posixPath = '/home/user/file.txt';
expect(toPosixPath(posixPath)).toBe(posixPath);
});
// 测试带有多个分隔符的路径
it('converts paths with mixed separators', () => {
const mixedPath = String.raw`C:/Users\Example\file.txt`;
const expectedPosixPath = 'C:/Users/Example/file.txt';
expect(toPosixPath(mixedPath)).toBe(expectedPosixPath);
});
// 测试空字符串
it('handles empty strings', () => {
const emptyPath = '';
expect(toPosixPath(emptyPath)).toBe('');
});
// 测试仅包含分隔符的路径
it('handles path with only separators', () => {
const separatorsPath = '\\\\\\';
const expectedPosixPath = '///';
expect(toPosixPath(separatorsPath)).toBe(expectedPosixPath);
});
// 测试不包含任何分隔符的路径
it('handles path without separators', () => {
const noSeparatorPath = 'file.txt';
expect(toPosixPath(noSeparatorPath)).toBe('file.txt');
});
// 测试以分隔符结尾的路径
it('handles path ending with a separator', () => {
const endingSeparatorPath = 'C:\\Users\\Example\\';
const expectedPosixPath = 'C:/Users/Example/';
expect(toPosixPath(endingSeparatorPath)).toBe(expectedPosixPath);
});
// 测试以分隔符开头的路径
it('handles path starting with a separator', () => {
const startingSeparatorPath = String.raw`\Users\Example`;
const expectedPosixPath = '/Users/Example';
expect(toPosixPath(startingSeparatorPath)).toBe(expectedPosixPath);
});
// 测试包含非法字符的路径
it('handles path with invalid characters', () => {
const invalidCharsPath = String.raw`C:\Us*?ers\Ex<ample>|file.txt`;
const expectedPosixPath = 'C:/Us*?ers/Ex<ample>|file.txt';
expect(toPosixPath(invalidCharsPath)).toBe(expectedPosixPath);
});
});

View File

@ -1,6 +0,0 @@
enum UNICODE {
FAILURE = '\u2716', // ✖
SUCCESS = '\u2714', // ✔
}
export { UNICODE };

View File

@ -1,12 +0,0 @@
import dayjs from 'dayjs';
import timezone from 'dayjs/plugin/timezone';
import utc from 'dayjs/plugin/utc';
dayjs.extend(utc);
dayjs.extend(timezone);
dayjs.tz.setDefault('Asia/Shanghai');
const dateUtil = dayjs;
export { dateUtil };

View File

@ -1,39 +0,0 @@
import { promises as fs } from 'node:fs';
import { dirname } from 'node:path';
export async function outputJSON(
filePath: string,
data: any,
spaces: number = 2,
) {
try {
const dir = dirname(filePath);
await fs.mkdir(dir, { recursive: true });
const jsonData = JSON.stringify(data, null, spaces);
await fs.writeFile(filePath, jsonData, 'utf8');
} catch (error) {
console.error('Error writing JSON file:', error);
throw error;
}
}
export async function ensureFile(filePath: string) {
try {
const dir = dirname(filePath);
await fs.mkdir(dir, { recursive: true });
await fs.writeFile(filePath, '', { flag: 'a' });
} catch (error) {
console.error('Error ensuring file:', error);
throw error;
}
}
export async function readJSON(filePath: string) {
try {
const data = await fs.readFile(filePath, 'utf8');
return JSON.parse(data);
} catch (error) {
console.error('Error reading JSON file:', error);
throw error;
}
}

View File

@ -1,34 +0,0 @@
import path from 'node:path';
import { execa } from 'execa';
export * from '@changesets/git';
/**
*
*/
async function getStagedFiles(): Promise<string[]> {
try {
const { stdout } = await execa('git', [
'-c',
'submodule.recurse=false',
'diff',
'--staged',
'--diff-filter=ACMR',
'--name-only',
'--ignore-submodules',
'-z',
]);
let changedList = stdout ? stdout.replace(/\0$/, '').split('\0') : [];
changedList = changedList.map((item) => path.resolve(process.cwd(), item));
const changedSet = new Set(changedList);
changedSet.delete('');
return [...changedSet];
} catch (error) {
console.error('Failed to get staged files:', error);
return [];
}
}
export { getStagedFiles };

View File

@ -1,18 +0,0 @@
import { createHash } from 'node:crypto';
/**
* hash
* @param content
* @param hashLSize
*/
function generatorContentHash(content: string, hashLSize?: number) {
const hash = createHash('md5').update(content, 'utf8').digest('hex');
if (hashLSize) {
return hash.slice(0, hashLSize);
}
return hash;
}
export { generatorContentHash };

View File

@ -1,19 +0,0 @@
export * from './constants';
export * from './date';
export * from './fs';
export * from './git';
export { getStagedFiles, add as gitAdd } 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';
export { consola } from 'consola';
export * from 'execa';
export { default as fs } from 'node:fs/promises';
export { type PackageJson, readPackageJSON } from 'pkg-types';
export { rimraf } from 'rimraf';

View File

@ -1,46 +0,0 @@
import { dirname } from 'node:path';
import {
getPackages as getPackagesFunc,
getPackagesSync as getPackagesSyncFunc,
} from '@manypkg/get-packages';
import { findUpSync } from 'find-up';
/**
*
* @param cwd
*/
function findMonorepoRoot(cwd: string = process.cwd()) {
const lockFile = findUpSync('pnpm-lock.yaml', {
cwd,
type: 'file',
});
return dirname(lockFile || '');
}
/**
*
*/
function getPackagesSync() {
const root = findMonorepoRoot();
return getPackagesSyncFunc(root);
}
/**
*
*/
async function getPackages() {
const root = findMonorepoRoot();
return await getPackagesFunc(root);
}
/**
*
*/
async function getPackage(pkgName: string) {
const { packages } = await getPackages();
return packages.find((pkg) => pkg.packageJson.name === pkgName);
}
export { findMonorepoRoot, getPackage, getPackages, getPackagesSync };

View File

@ -1,11 +0,0 @@
import { posix } from 'node:path';
/**
* POSIX
* @param {string} pathname -
*/
function toPosixPath(pathname: string) {
return pathname.split(`\\`).join(posix.sep);
}
export { toPosixPath };

View File

@ -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 };

View File

@ -1,26 +0,0 @@
import type { Ora } from 'ora';
import ora from 'ora';
interface SpinnerOptions {
failedText?: string;
successText?: string;
title: string;
}
export async function spinner<T>(
{ failedText, successText, title }: SpinnerOptions,
callback: () => Promise<T>,
): Promise<T> {
const loading: Ora = ora(title).start();
try {
const result = await callback();
loading.succeed(successText || 'Success!');
return result;
} catch (error) {
loading.fail(failedText || 'Failed!');
throw error;
} finally {
loading.stop();
}
}

View File

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

View File

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

View File

@ -1,66 +0,0 @@
{
"name": "@aiflowy/tailwind-config",
"version": "1.0.0",
"private": true,
"homepage": "https://github.com/aiflowy/aiflowy",
"bugs": "https://github.com/aiflowy/aiflowy/issues",
"repository": {
"type": "git",
"url": "git+https://github.com/aiflowy/aiflowy.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:",
"postcss": "catalog:",
"postcss-antd-fixes": "catalog:",
"postcss-import": "catalog:",
"postcss-preset-env": "catalog:",
"tailwindcss": "catalog:",
"tailwindcss-animate": "catalog:"
},
"devDependencies": {
"@types/postcss-import": "catalog:"
}
}

View File

@ -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目录下和 @aiflowy-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: '',
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',
},
},
},
safelist: ['dark'],
} 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))`,
};
}

View File

@ -1,3 +0,0 @@
declare module '@tailwindcss/nesting' {
export default any;
}

View File

@ -1,53 +0,0 @@
import plugin from 'tailwindcss/plugin.js';
const enterAnimationPlugin = plugin(({ addUtilities }) => {
const maxChild = 5;
const utilities: Record<string, any> = {};
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 };

View File

@ -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 和 ant-design-vue 的样式和tailwindcss冲突问题
'postcss-antd-fixes': { prefixes: ['ant', 'el'] },
'postcss-import': {},
'postcss-preset-env': {},
tailwindcss: { config },
'tailwindcss/nesting': {},
},
};

View File

@ -1,9 +0,0 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "@aiflowy/tsconfig/node.json",
"compilerOptions": {
"moduleResolution": "bundler"
},
"include": ["src"],
"exclude": ["node_modules"]
}

View File

@ -1,40 +0,0 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"display": "Base",
"compilerOptions": {
"composite": false,
"target": "ESNext",
"moduleDetection": "force",
"experimentalDecorators": true,
"baseUrl": ".",
"module": "ESNext",
"moduleResolution": "node",
"resolveJsonModule": true,
"strict": true,
"strictNullChecks": true,
"noFallthroughCasesInSwitch": true,
"noImplicitAny": true,
"noImplicitOverride": true,
"noImplicitThis": true,
"noUncheckedIndexedAccess": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"inlineSources": false,
"noEmit": true,
"removeComments": true,
"sourceMap": false,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"isolatedModules": true,
"verbatimModuleSyntax": true,
"skipLibCheck": true,
"preserveWatchOutput": true
},
"exclude": ["**/node_modules/**", "**/dist/**", "**/.turbo/**"]
}

View File

@ -1,13 +0,0 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"display": "Web Application",
"extends": "./base.json",
"compilerOptions": {
"jsx": "preserve",
"lib": ["ESNext", "DOM", "DOM.Iterable"],
"useDefineForClassFields": true,
"moduleResolution": "bundler",
"declaration": true,
"noEmit": false
}
}

View File

@ -1,12 +0,0 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"display": "Node Config",
"extends": "./base.json",
"compilerOptions": {
"composite": false,
"lib": ["ESNext"],
"baseUrl": "./",
"types": ["node"],
"noImplicitAny": true
}
}

View File

@ -1,25 +0,0 @@
{
"name": "@aiflowy/tsconfig",
"version": "1.0.0",
"private": true,
"homepage": "https://github.com/aiflowy/aiflowy",
"bugs": "https://github.com/aiflowy/aiflowy/issues",
"repository": {
"type": "git",
"url": "git+https://github.com/aiflowy/aiflowy.git",
"directory": "internal/tsconfig"
},
"license": "MIT",
"type": "module",
"files": [
"base.json",
"library.json",
"node.json",
"web-app.json",
"web.json"
],
"dependencies": {
"@aiflowy/types": "workspace:*",
"vite": "catalog:"
}
}

View File

@ -1,8 +0,0 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"display": "Web Application",
"extends": "./web.json",
"compilerOptions": {
"types": ["vite/client", "@aiflowy/types/global"]
}
}

View File

@ -1,14 +0,0 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"display": "Web Package",
"extends": "./base.json",
"compilerOptions": {
"jsx": "preserve",
"jsxImportSource": "vue",
"lib": ["ESNext", "DOM", "DOM.Iterable"],
"useDefineForClassFields": true,
"moduleResolution": "bundler",
"types": ["vite/client"],
"declaration": false
}
}

View File

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

View File

@ -1,59 +0,0 @@
{
"name": "@aiflowy/vite-config",
"version": "1.0.0",
"private": true,
"homepage": "https://github.com/aiflowy/aiflowy",
"bugs": "https://github.com/aiflowy/aiflowy/issues",
"repository": {
"type": "git",
"url": "git+https://github.com/aiflowy/aiflowy.git",
"directory": "internal/vite-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": "./src/index.ts",
"default": "./dist/index.mjs"
}
},
"dependencies": {
"@intlify/unplugin-vue-i18n": "catalog:",
"@jspm/generator": "catalog:",
"archiver": "catalog:",
"cheerio": "catalog:",
"get-port": "catalog:",
"html-minifier-terser": "catalog:",
"nitropack": "catalog:",
"resolve.exports": "catalog:",
"vite-plugin-pwa": "catalog:",
"vite-plugin-vue-devtools": "catalog:"
},
"devDependencies": {
"@pnpm/workspace.read-manifest": "catalog:",
"@types/archiver": "catalog:",
"@types/html-minifier-terser": "catalog:",
"@aiflowy/node-utils": "workspace:*",
"@vitejs/plugin-vue": "catalog:",
"@vitejs/plugin-vue-jsx": "catalog:",
"dayjs": "catalog:",
"dotenv": "catalog:",
"rollup": "catalog:",
"rollup-plugin-visualizer": "catalog:",
"sass": "catalog:",
"vite": "catalog:",
"vite-plugin-compression": "catalog:",
"vite-plugin-dts": "catalog:",
"vite-plugin-html": "catalog:",
"vite-plugin-lazy-import": "catalog:"
}
}

View File

@ -1,125 +0,0 @@
import type { CSSOptions, UserConfig } from 'vite';
import type { DefineApplicationOptions } from '../typing';
import path, { relative } from 'node:path';
import { findMonorepoRoot } from '@aiflowy/node-utils';
import { NodePackageImporter } from 'sass';
import { defineConfig, loadEnv, mergeConfig } from 'vite';
import { defaultImportmapOptions, getDefaultPwaOptions } from '../options';
import { loadApplicationPlugins } from '../plugins';
import { loadAndConvertEnv } from '../utils/env';
import { getCommonConfig } from './common';
function defineApplicationConfig(userConfigPromise?: DefineApplicationOptions) {
return defineConfig(async (config) => {
const options = await userConfigPromise?.(config);
const { appTitle, base, port, ...envConfig } = await loadAndConvertEnv();
const { command, mode } = config;
const { application = {}, vite = {} } = options || {};
const root = process.cwd();
const isBuild = command === 'build';
const env = loadEnv(mode, root);
const plugins = await loadApplicationPlugins({
archiver: true,
archiverPluginOptions: {},
compress: false,
compressTypes: ['brotli', 'gzip'],
devtools: true,
env,
extraAppConfig: true,
html: true,
i18n: true,
importmapOptions: defaultImportmapOptions,
injectAppLoading: true,
injectMetadata: true,
isBuild,
license: true,
mode,
nitroMock: !isBuild,
nitroMockOptions: {},
print: !isBuild,
printInfoMap: {
'AIFlowy Docs': 'https://aiflowy.tech',
},
pwa: true,
pwaOptions: getDefaultPwaOptions(appTitle),
vxeTableLazyImport: true,
...envConfig,
...application,
});
const { injectGlobalScss = true } = application;
const applicationConfig: UserConfig = {
base,
build: {
rollupOptions: {
output: {
assetFileNames: '[ext]/[name]-[hash].[ext]',
chunkFileNames: 'js/[name]-[hash].js',
entryFileNames: 'jse/index-[name]-[hash].js',
},
},
target: 'es2015',
},
css: createCssOptions(injectGlobalScss),
esbuild: {
drop: isBuild
? [
// 'console',
'debugger',
]
: [],
legalComments: 'none',
},
plugins,
server: {
host: true,
port,
warmup: {
// 预热文件
clientFiles: [
'./index.html',
'./src/bootstrap.ts',
'./src/{views,layouts,router,store,api,adapter}/*',
],
},
},
};
const mergedCommonConfig = mergeConfig(
await getCommonConfig(),
applicationConfig,
);
return mergeConfig(mergedCommonConfig, vite);
});
}
function createCssOptions(injectGlobalScss = true): CSSOptions {
const root = findMonorepoRoot();
return {
preprocessorOptions: injectGlobalScss
? {
scss: {
additionalData: (content: string, filepath: string) => {
const relativePath = relative(root, filepath);
// apps下的包注入全局样式
if (relativePath.startsWith(`apps${path.sep}`)) {
return `@use "@aiflowy/styles/global" as *;\n${content}`;
}
return content;
},
api: 'modern',
importers: [new NodePackageImporter()],
},
}
: {},
};
}
export { defineApplicationConfig };

View File

@ -1,13 +0,0 @@
import type { UserConfig } from 'vite';
async function getCommonConfig(): Promise<UserConfig> {
return {
build: {
chunkSizeWarningLimit: 2000,
reportCompressedSize: false,
sourcemap: false,
},
};
}
export { getCommonConfig };

View File

@ -1,37 +0,0 @@
import type { DefineConfig } from '../typing';
import { existsSync } from 'node:fs';
import { join } from 'node:path';
import { defineApplicationConfig } from './application';
import { defineLibraryConfig } from './library';
export * from './application';
export * from './library';
function defineConfig(
userConfigPromise?: DefineConfig,
type: 'application' | 'auto' | 'library' = 'auto',
) {
let projectType = type;
// 根据包是否存在 index.html,自动判断类型
if (projectType === 'auto') {
const htmlPath = join(process.cwd(), 'index.html');
projectType = existsSync(htmlPath) ? 'application' : 'library';
}
switch (projectType) {
case 'application': {
return defineApplicationConfig(userConfigPromise);
}
case 'library': {
return defineLibraryConfig(userConfigPromise);
}
default: {
throw new Error(`Unsupported project type: ${projectType}`);
}
}
}
export { defineConfig };

View File

@ -1,59 +0,0 @@
import type { ConfigEnv, UserConfig } from 'vite';
import type { DefineLibraryOptions } from '../typing';
import { readPackageJSON } from '@aiflowy/node-utils';
import { defineConfig, mergeConfig } from 'vite';
import { loadLibraryPlugins } from '../plugins';
import { getCommonConfig } from './common';
function defineLibraryConfig(userConfigPromise?: DefineLibraryOptions) {
return defineConfig(async (config: ConfigEnv) => {
const options = await userConfigPromise?.(config);
const { command, mode } = config;
const { library = {}, vite = {} } = options || {};
const root = process.cwd();
const isBuild = command === 'build';
const plugins = await loadLibraryPlugins({
dts: false,
injectMetadata: true,
isBuild,
mode,
...library,
});
const { dependencies = {}, peerDependencies = {} } =
await readPackageJSON(root);
const externalPackages = [
...Object.keys(dependencies),
...Object.keys(peerDependencies),
];
const packageConfig: UserConfig = {
build: {
lib: {
entry: 'src/index.ts',
fileName: () => 'index.mjs',
formats: ['es'],
},
rollupOptions: {
external: (id) => {
return externalPackages.some(
(pkg) => id === pkg || id.startsWith(`${pkg}/`),
);
},
},
},
plugins,
};
const commonConfig = await getCommonConfig();
const mergedConmonConfig = mergeConfig(commonConfig, packageConfig);
return mergeConfig(mergedConmonConfig, vite);
});
}
export { defineLibraryConfig };

View File

@ -1,4 +0,0 @@
export * from './config';
export * from './options';
export * from './plugins';
export { loadAndConvertEnv } from './utils/env';

View File

@ -1,45 +0,0 @@
import type { Options as PwaPluginOptions } from 'vite-plugin-pwa';
import type { ImportmapPluginOptions } from './typing';
const isDevelopment = process.env.NODE_ENV === 'development';
const getDefaultPwaOptions = (name: string): Partial<PwaPluginOptions> => ({
manifest: {
description:
'AIFlowy Admin is a modern admin dashboard template based on Vue 3. ',
icons: [
{
sizes: '192x192',
src: 'https://unpkg.com/@aiflowy/static-source@0.1.7/source/pwa-icon-192.png',
type: 'image/png',
},
{
sizes: '512x512',
src: 'https://unpkg.com/@aiflowy/static-source@0.1.7/source/pwa-icon-512.png',
type: 'image/png',
},
],
name: `${name}${isDevelopment ? ' dev' : ''}`,
short_name: `${name}${isDevelopment ? ' dev' : ''}`,
},
});
/**
* importmap CDN
*/
const defaultImportmapOptions: ImportmapPluginOptions = {
// 通过 Importmap CDN 方式引入,
// 目前只有esm.sh源兼容性好一点jspm.io对于 esm 入口要求高
defaultProvider: 'esm.sh',
importmap: [
{ name: 'vue' },
{ name: 'pinia' },
{ name: 'vue-router' },
// { name: 'vue-i18n' },
{ name: 'dayjs' },
{ name: 'vue-demi' },
],
};
export { defaultImportmapOptions, getDefaultPwaOptions };

View File

@ -1,75 +0,0 @@
import type { PluginOption } from 'vite';
import type { ArchiverPluginOptions } from '../typing';
import fs from 'node:fs';
import fsp from 'node:fs/promises';
import { join } from 'node:path';
import archiver from 'archiver';
export const viteArchiverPlugin = (
options: ArchiverPluginOptions = {},
): PluginOption => {
return {
apply: 'build',
closeBundle: {
handler() {
const { name = 'dist', outputDir = '.' } = options;
setTimeout(async () => {
const folderToZip = 'dist';
const zipOutputDir = join(process.cwd(), outputDir);
const zipOutputPath = join(zipOutputDir, `${name}.zip`);
try {
await fsp.mkdir(zipOutputDir, { recursive: true });
} catch {
// ignore
}
try {
await zipFolder(folderToZip, zipOutputPath);
console.log(`Folder has been zipped to: ${zipOutputPath}`);
} catch (error) {
console.error('Error zipping folder:', error);
}
}, 0);
},
order: 'post',
},
enforce: 'post',
name: 'vite:archiver',
};
};
async function zipFolder(
folderPath: string,
outputPath: string,
): Promise<void> {
return new Promise((resolve, reject) => {
const output = fs.createWriteStream(outputPath);
const archive = archiver('zip', {
zlib: { level: 9 }, // 设置压缩级别为 9 以实现最高压缩率
});
output.on('close', () => {
console.log(
`ZIP file created: ${outputPath} (${archive.pointer()} total bytes)`,
);
resolve();
});
archive.on('error', (err) => {
reject(err);
});
archive.pipe(output);
// 使用 directory 方法以流的方式压缩文件夹,减少内存消耗
archive.directory(folderPath, false);
// 流式处理完成
archive.finalize();
});
}

View File

@ -1,92 +0,0 @@
import type { PluginOption } from 'vite';
import {
colors,
generatorContentHash,
readPackageJSON,
} from '@aiflowy/node-utils';
import { loadEnv } from '../utils/env';
interface PluginOptions {
isBuild: boolean;
root: string;
}
const GLOBAL_CONFIG_FILE_NAME = '_app.config.js';
const APP_ADMIN_PRO_APP_CONF = '_APP_ADMIN_PRO_APP_CONF_';
/**
*
* @returns
*/
async function viteExtraAppConfigPlugin({
isBuild,
root,
}: PluginOptions): Promise<PluginOption | undefined> {
let publicPath: string;
let source: string;
if (!isBuild) {
return;
}
const { version = '' } = await readPackageJSON(root);
return {
async configResolved(config) {
publicPath = ensureTrailingSlash(config.base);
source = await getConfigSource();
},
async generateBundle() {
try {
this.emitFile({
fileName: GLOBAL_CONFIG_FILE_NAME,
source,
type: 'asset',
});
console.log(colors.cyan(`✨configuration file is build successfully!`));
} catch (error) {
console.log(
colors.red(
`configuration file configuration file failed to package:\n${error}`,
),
);
}
},
name: 'vite:extra-app-config',
async transformIndexHtml(html) {
const hash = `v=${version}-${generatorContentHash(source, 8)}`;
const appConfigSrc = `${publicPath}${GLOBAL_CONFIG_FILE_NAME}?${hash}`;
return {
html,
tags: [{ attrs: { src: appConfigSrc }, tag: 'script' }],
};
},
};
}
async function getConfigSource() {
const config = await loadEnv();
const windowVariable = `window.${APP_ADMIN_PRO_APP_CONF}`;
// 确保变量不会被修改
let source = `${windowVariable}=${JSON.stringify(config)};`;
source += `
Object.freeze(${windowVariable});
Object.defineProperty(window, "${APP_ADMIN_PRO_APP_CONF}", {
configurable: false,
writable: false,
});
`.replaceAll(/\s/g, '');
return source;
}
function ensureTrailingSlash(path: string) {
return path.endsWith('/') ? path : `${path}/`;
}
export { viteExtraAppConfigPlugin };

View File

@ -1,245 +0,0 @@
/**
* https://github.com/jspm/vite-plugin-jspm调整为需要的功能
*/
import type { GeneratorOptions } from '@jspm/generator';
import type { Plugin } from 'vite';
import { Generator } from '@jspm/generator';
import { load } from 'cheerio';
import { minify } from 'html-minifier-terser';
const DEFAULT_PROVIDER = 'jspm.io';
type pluginOptions = GeneratorOptions & {
debug?: boolean;
defaultProvider?: 'esm.sh' | 'jsdelivr' | 'jspm.io';
importmap?: Array<{ name: string; range?: string }>;
};
// async function getLatestVersionOfShims() {
// const result = await fetch('https://ga.jspm.io/npm:es-module-shims');
// const version = result.text();
// return version;
// }
async function getShimsUrl(provide: string) {
// const version = await getLatestVersionOfShims();
const version = '1.10.0';
const shimsSubpath = `dist/es-module-shims.js`;
const providerShimsMap: Record<string, string> = {
'esm.sh': `https://esm.sh/es-module-shims@${version}/${shimsSubpath}`,
// unpkg: `https://unpkg.com/es-module-shims@${version}/${shimsSubpath}`,
jsdelivr: `https://cdn.jsdelivr.net/npm/es-module-shims@${version}/${shimsSubpath}`,
// 下面两个CDN不稳定暂时不用
'jspm.io': `https://ga.jspm.io/npm:es-module-shims@${version}/${shimsSubpath}`,
};
return providerShimsMap[provide] || providerShimsMap[DEFAULT_PROVIDER];
}
let generator: Generator;
async function viteImportMapPlugin(
pluginOptions?: pluginOptions,
): Promise<Plugin[]> {
const { importmap } = pluginOptions || {};
let isSSR = false;
let isBuild = false;
let installed = false;
let installError: Error | null = null;
const options: pluginOptions = Object.assign(
{},
{
debug: false,
defaultProvider: 'jspm.io',
env: ['production', 'browser', 'module'],
importmap: [],
},
pluginOptions,
);
generator = new Generator({
...options,
baseUrl: process.cwd(),
});
if (options?.debug) {
(async () => {
for await (const { message, type } of generator.logStream()) {
console.log(`${type}: ${message}`);
}
})();
}
const imports = options.inputMap?.imports ?? {};
const scopes = options.inputMap?.scopes ?? {};
const firstLayerKeys = Object.keys(scopes);
const inputMapScopes: string[] = [];
firstLayerKeys.forEach((key) => {
inputMapScopes.push(...Object.keys(scopes[key] || {}));
});
const inputMapImports = Object.keys(imports);
const allDepNames: string[] = [
...(importmap?.map((item) => item.name) || []),
...inputMapImports,
...inputMapScopes,
];
const depNames = new Set<string>(allDepNames);
const installDeps = importmap?.map((item) => ({
range: item.range,
target: item.name,
}));
return [
{
async config(_, { command, isSsrBuild }) {
isBuild = command === 'build';
isSSR = !!isSsrBuild;
},
enforce: 'pre',
name: 'importmap:external',
resolveId(id) {
if (isSSR || !isBuild) {
return null;
}
if (!depNames.has(id)) {
return null;
}
return { external: true, id };
},
},
{
enforce: 'post',
name: 'importmap:install',
async resolveId() {
if (isSSR || !isBuild || installed) {
return null;
}
try {
installed = true;
await Promise.allSettled(
(installDeps || []).map((dep) => generator.install(dep)),
);
} catch (error: any) {
installError = error;
installed = false;
}
return null;
},
},
{
buildEnd() {
// 未生成importmap时抛出错误防止被turbo缓存
if (!installed && !isSSR) {
installError && console.error(installError);
throw new Error('Importmap installation failed.');
}
},
enforce: 'post',
name: 'importmap:html',
transformIndexHtml: {
async handler(html) {
if (isSSR || !isBuild) {
return html;
}
const importmapJson = generator.getMap();
if (!importmapJson) {
return html;
}
const esModuleShimsSrc = await getShimsUrl(
options.defaultProvider || DEFAULT_PROVIDER,
);
const resultHtml = await injectShimsToHtml(
html,
esModuleShimsSrc || '',
);
html = await minify(resultHtml || html, {
collapseWhitespace: true,
minifyCSS: true,
minifyJS: true,
removeComments: false,
});
return {
html,
tags: [
{
attrs: {
type: 'importmap',
},
injectTo: 'head-prepend',
tag: 'script',
children: `${JSON.stringify(importmapJson)}`,
},
],
};
},
order: 'post',
},
},
];
}
async function injectShimsToHtml(html: string, esModuleShimUrl: string) {
const $ = load(html);
const $script = $(`script[type='module']`);
if (!$script) {
return;
}
const entry = $script.attr('src');
$script.removeAttr('type');
$script.removeAttr('crossorigin');
$script.removeAttr('src');
$script.html(`
if (!HTMLScriptElement.supports || !HTMLScriptElement.supports('importmap')) {
self.importShim = function () {
const promise = new Promise((resolve, reject) => {
document.head.appendChild(
Object.assign(document.createElement('script'), {
src: '${esModuleShimUrl}',
crossorigin: 'anonymous',
async: true,
onload() {
if (!importShim.$proxy) {
resolve(importShim);
} else {
reject(new Error('No globalThis.importShim found:' + esModuleShimUrl));
}
},
onerror(error) {
reject(error);
},
}),
);
});
importShim.$proxy = true;
return promise.then((importShim) => importShim(...arguments));
};
}
var modules = ['${entry}'];
typeof importShim === 'function'
? modules.forEach((moduleName) => importShim(moduleName))
: modules.forEach((moduleName) => import(moduleName));
`);
$('body').after($script);
$('head').remove(`script[type='module']`);
return $.html();
}
export { viteImportMapPlugin };

View File

@ -1,247 +0,0 @@
import type { PluginOption } from 'vite';
import type {
ApplicationPluginOptions,
CommonPluginOptions,
ConditionPlugin,
LibraryPluginOptions,
} from '../typing';
import viteVueI18nPlugin from '@intlify/unplugin-vue-i18n/vite';
import viteVue from '@vitejs/plugin-vue';
import viteVueJsx from '@vitejs/plugin-vue-jsx';
import { visualizer as viteVisualizerPlugin } from 'rollup-plugin-visualizer';
import viteCompressPlugin from 'vite-plugin-compression';
import viteDtsPlugin from 'vite-plugin-dts';
import { createHtmlPlugin as viteHtmlPlugin } from 'vite-plugin-html';
import { VitePWA } from 'vite-plugin-pwa';
import viteVueDevTools from 'vite-plugin-vue-devtools';
import { viteArchiverPlugin } from './archiver';
import { viteExtraAppConfigPlugin } from './extra-app-config';
import { viteImportMapPlugin } from './importmap';
import { viteInjectAppLoadingPlugin } from './inject-app-loading';
import { viteMetadataPlugin } from './inject-metadata';
import { viteLicensePlugin } from './license';
import { viteNitroMockPlugin } from './nitro-mock';
import { vitePrintPlugin } from './print';
import { viteVxeTableImportsPlugin } from './vxe-table';
/**
* vite
* @param conditionPlugins
*/
async function loadConditionPlugins(conditionPlugins: ConditionPlugin[]) {
const plugins: PluginOption[] = [];
for (const conditionPlugin of conditionPlugins) {
if (conditionPlugin.condition) {
const realPlugins = await conditionPlugin.plugins();
plugins.push(...realPlugins);
}
}
return plugins.flat();
}
/**
* vite插件
*/
async function loadCommonPlugins(
options: CommonPluginOptions,
): Promise<ConditionPlugin[]> {
const { devtools, injectMetadata, isBuild, visualizer } = options;
return [
{
condition: true,
plugins: () => [
viteVue({
script: {
defineModel: true,
// propsDestructure: true,
},
}),
viteVueJsx(),
],
},
{
condition: !isBuild && devtools,
plugins: () => [viteVueDevTools()],
},
{
condition: injectMetadata,
plugins: async () => [await viteMetadataPlugin()],
},
{
condition: isBuild && !!visualizer,
plugins: () => [<PluginOption>viteVisualizerPlugin({
filename: './node_modules/.cache/visualizer/stats.html',
gzipSize: true,
open: true,
})],
},
];
}
/**
* vite插件
*/
async function loadApplicationPlugins(
options: ApplicationPluginOptions,
): Promise<PluginOption[]> {
// 单独取否则commonOptions拿不到
const isBuild = options.isBuild;
const env = options.env;
const {
archiver,
archiverPluginOptions,
compress,
compressTypes,
extraAppConfig,
html,
i18n,
importmap,
importmapOptions,
injectAppLoading,
license,
nitroMock,
nitroMockOptions,
print,
printInfoMap,
pwa,
pwaOptions,
vxeTableLazyImport,
...commonOptions
} = options;
const commonPlugins = await loadCommonPlugins(commonOptions);
return await loadConditionPlugins([
...commonPlugins,
{
condition: i18n,
plugins: async () => {
return [
viteVueI18nPlugin({
compositionOnly: true,
fullInstall: true,
runtimeOnly: true,
}),
];
},
},
{
condition: print,
plugins: async () => {
return [await vitePrintPlugin({ infoMap: printInfoMap })];
},
},
{
condition: vxeTableLazyImport,
plugins: async () => {
return [await viteVxeTableImportsPlugin()];
},
},
{
condition: nitroMock,
plugins: async () => {
return [await viteNitroMockPlugin(nitroMockOptions)];
},
},
{
condition: injectAppLoading,
plugins: async () => [await viteInjectAppLoadingPlugin(!!isBuild, env)],
},
{
condition: license,
plugins: async () => [await viteLicensePlugin()],
},
{
condition: pwa,
plugins: () =>
VitePWA({
injectRegister: false,
workbox: {
globPatterns: [],
},
...pwaOptions,
manifest: {
display: 'standalone',
start_url: '/',
theme_color: '#ffffff',
...pwaOptions?.manifest,
},
}),
},
{
condition: isBuild && !!compress,
plugins: () => {
const compressPlugins: PluginOption[] = [];
if (compressTypes?.includes('brotli')) {
compressPlugins.push(
viteCompressPlugin({ deleteOriginFile: false, ext: '.br' }),
);
}
if (compressTypes?.includes('gzip')) {
compressPlugins.push(
viteCompressPlugin({ deleteOriginFile: false, ext: '.gz' }),
);
}
return compressPlugins;
},
},
{
condition: !!html,
plugins: () => [viteHtmlPlugin({ minify: true })],
},
{
condition: isBuild && importmap,
plugins: () => {
return [viteImportMapPlugin(importmapOptions)];
},
},
{
condition: isBuild && extraAppConfig,
plugins: async () => [
await viteExtraAppConfigPlugin({ isBuild: true, root: process.cwd() }),
],
},
{
condition: archiver,
plugins: async () => {
return [await viteArchiverPlugin(archiverPluginOptions)];
},
},
]);
}
/**
* vite插件
*/
async function loadLibraryPlugins(
options: LibraryPluginOptions,
): Promise<PluginOption[]> {
// 单独取否则commonOptions拿不到
const isBuild = options.isBuild;
const { dts, ...commonOptions } = options;
const commonPlugins = await loadCommonPlugins(commonOptions);
return await loadConditionPlugins([
...commonPlugins,
{
condition: isBuild && !!dts,
plugins: () => [viteDtsPlugin({ logLevel: 'error' })],
},
]);
}
export {
loadApplicationPlugins,
loadLibraryPlugins,
viteArchiverPlugin,
viteCompressPlugin,
viteDtsPlugin,
viteHtmlPlugin,
viteVisualizerPlugin,
viteVxeTableImportsPlugin,
};

View File

@ -1,3 +0,0 @@
# inject-app-loading
用于在应用加载时显示加载动画的插件,可自行选择加载动画的样式。

View File

@ -1,107 +0,0 @@
<style data-app-loading="inject-css">
html {
/* same as ant-design-vue/dist/reset.css setting, avoid the title line-height changed */
line-height: 1.15;
}
.dark .loading {
background-color: #0d0d10;
}
.dark .loading .title {
color: rgb(255 255 255 / 85%);
}
.loading {
position: fixed;
top: 0;
left: 0;
z-index: 9999;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
overflow: hidden;
pointer-events: none;
background-color: #f4f7f9;
}
.loading.hidden {
visibility: hidden;
opacity: 0;
transition: all 0.6s ease-out;
}
.loading .title {
margin-top: 36px;
font-size: 30px;
font-weight: 600;
color: rgb(0 0 0 / 85%);
}
.dot {
position: relative;
box-sizing: border-box;
display: inline-block;
width: 48px;
height: 48px;
margin-top: 30px;
font-size: 32px;
transform: rotate(45deg);
animation: rotate-ani 1.2s infinite linear;
}
.dot i {
position: absolute;
display: block;
width: 20px;
height: 20px;
background-color: hsl(var(--primary, 210 100% 50%));
border-radius: 100%;
opacity: 0.3;
transform: scale(0.75);
transform-origin: 50% 50%;
animation: spin-move-ani 1s infinite linear alternate;
}
.dot i:nth-child(1) {
top: 0;
left: 0;
}
.dot i:nth-child(2) {
top: 0;
right: 0;
animation-delay: 0.4s;
}
.dot i:nth-child(3) {
right: 0;
bottom: 0;
animation-delay: 0.8s;
}
.dot i:nth-child(4) {
bottom: 0;
left: 0;
animation-delay: 1.2s;
}
@keyframes rotate-ani {
to {
transform: rotate(405deg);
}
}
@keyframes spin-move-ani {
to {
opacity: 1;
}
}
</style>
<div class="loading" id="__app-loading__">
<span class="dot"><i></i><i></i><i></i><i></i></span>
<div class="title"><%= VITE_APP_TITLE %></div>
</div>

View File

@ -1,113 +0,0 @@
<style data-app-loading="inject-css">
html {
/* same as ant-design-vue/dist/reset.css setting, avoid the title line-height changed */
line-height: 1.15;
}
.loading {
position: fixed;
top: 0;
left: 0;
z-index: 9999;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
overflow: hidden;
background-color: #f4f7f9;
/* transition: all 0.8s ease-out; */
}
.loading.hidden {
pointer-events: none;
visibility: hidden;
opacity: 0;
transition: all 0.8s ease-out;
}
.dark .loading {
background: #0d0d10;
}
.title {
margin-top: 66px;
font-size: 28px;
font-weight: 600;
color: rgb(0 0 0 / 85%);
}
.dark .title {
color: #fff;
}
.loader {
position: relative;
width: 48px;
height: 48px;
}
.loader::before {
position: absolute;
top: 60px;
left: 0;
width: 48px;
height: 5px;
content: '';
background: hsl(var(--primary, 210 100% 50%) / 50%);
border-radius: 50%;
animation: shadow-ani 0.5s linear infinite;
}
.loader::after {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
content: '';
background: hsl(var(--primary, 210 100% 50%));
border-radius: 4px;
animation: jump-ani 0.5s linear infinite;
}
@keyframes jump-ani {
15% {
border-bottom-right-radius: 3px;
}
25% {
transform: translateY(9px) rotate(22.5deg);
}
50% {
border-bottom-right-radius: 40px;
transform: translateY(18px) scale(1, 0.9) rotate(45deg);
}
75% {
transform: translateY(9px) rotate(67.5deg);
}
100% {
transform: translateY(0) rotate(90deg);
}
}
@keyframes shadow-ani {
0%,
100% {
transform: scale(1, 1);
}
50% {
transform: scale(1.2, 1);
}
}
</style>
<div class="loading" id="__app-loading__">
<div class="loader"></div>
<div class="title"><%= VITE_APP_TITLE %></div>
</div>

View File

@ -1,66 +0,0 @@
import type { PluginOption } from 'vite';
import fs from 'node:fs';
import fsp from 'node:fs/promises';
import { join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { readPackageJSON } from '@aiflowy/node-utils';
/**
* loading样式注入到项目中
* app提供loading样式 app -> index.html单独引入
*/
async function viteInjectAppLoadingPlugin(
isBuild: boolean,
env: Record<string, any> = {},
loadingTemplate = 'loading.html',
): Promise<PluginOption | undefined> {
const loadingHtml = await getLoadingRawByHtmlTemplate(loadingTemplate);
const { version } = await readPackageJSON(process.cwd());
const envRaw = isBuild ? 'prod' : 'dev';
const cacheName = `'${env.VITE_APP_NAMESPACE}-${version}-${envRaw}-preferences-theme'`;
// 获取缓存的主题
// 保证黑暗主题下刷新页面时loading也是黑暗主题
const injectScript = `
<script data-app-loading="inject-js">
var theme = localStorage.getItem(${cacheName});
document.documentElement.classList.toggle('dark', /dark/.test(theme));
</script>
`;
if (!loadingHtml) {
return;
}
return {
enforce: 'pre',
name: 'vite:inject-app-loading',
transformIndexHtml: {
handler(html) {
const re = /<body\s*>/;
html = html.replace(re, `<body>${injectScript}${loadingHtml}`);
return html;
},
order: 'pre',
},
};
}
/**
* loading的html模板
*/
async function getLoadingRawByHtmlTemplate(loadingTemplate: string) {
// 支持在app内自定义loading模板模版参考default-loading.html即可
let appLoadingPath = join(process.cwd(), loadingTemplate);
if (!fs.existsSync(appLoadingPath)) {
const __dirname = fileURLToPath(new URL('.', import.meta.url));
appLoadingPath = join(__dirname, './default-loading.html');
}
return await fsp.readFile(appLoadingPath, 'utf8');
}
export { viteInjectAppLoadingPlugin };

View File

@ -1,111 +0,0 @@
import type { PluginOption } from 'vite';
import {
dateUtil,
findMonorepoRoot,
getPackages,
readPackageJSON,
} from '@aiflowy/node-utils';
import { readWorkspaceManifest } from '@pnpm/workspace.read-manifest';
function resolvePackageVersion(
pkgsMeta: Record<string, string>,
name: string,
value: string,
catalog: Record<string, string>,
) {
if (value.includes('catalog:')) {
return catalog[name];
}
if (value.includes('workspace')) {
return pkgsMeta[name];
}
return value;
}
async function resolveMonorepoDependencies() {
const { packages } = await getPackages();
const manifest = await readWorkspaceManifest(findMonorepoRoot());
const catalog = manifest?.catalog || {};
const resultDevDependencies: Record<string, string | undefined> = {};
const resultDependencies: Record<string, string | undefined> = {};
const pkgsMeta: Record<string, string> = {};
for (const { packageJson } of packages) {
pkgsMeta[packageJson.name] = packageJson.version;
}
for (const { packageJson } of packages) {
const { dependencies = {}, devDependencies = {} } = packageJson;
for (const [key, value] of Object.entries(dependencies)) {
resultDependencies[key] = resolvePackageVersion(
pkgsMeta,
key,
value,
catalog,
);
}
for (const [key, value] of Object.entries(devDependencies)) {
resultDevDependencies[key] = resolvePackageVersion(
pkgsMeta,
key,
value,
catalog,
);
}
}
return {
dependencies: resultDependencies,
devDependencies: resultDevDependencies,
};
}
/**
*
*/
async function viteMetadataPlugin(
root = process.cwd(),
): Promise<PluginOption | undefined> {
const { author, description, homepage, license, version } =
await readPackageJSON(root);
const buildTime = dateUtil().format('YYYY-MM-DD HH:mm:ss');
return {
async config() {
const { dependencies, devDependencies } =
await resolveMonorepoDependencies();
const isAuthorObject = typeof author === 'object';
const authorName = isAuthorObject ? author.name : author;
const authorEmail = isAuthorObject ? author.email : null;
const authorUrl = isAuthorObject ? author.url : null;
return {
define: {
__APP_ADMIN_METADATA__: JSON.stringify({
authorEmail,
authorName,
authorUrl,
buildTime,
dependencies,
description,
devDependencies,
homepage,
license,
version,
}),
'import.meta.env.VITE_APP_VERSION': JSON.stringify(version),
},
};
},
enforce: 'post',
name: 'vite:inject-metadata',
};
}
export { viteMetadataPlugin };

View File

@ -1,63 +0,0 @@
import type {
NormalizedOutputOptions,
OutputBundle,
OutputChunk,
} from 'rollup';
import type { PluginOption } from 'vite';
import { EOL } from 'node:os';
import { dateUtil, readPackageJSON } from '@aiflowy/node-utils';
/**
*
* @returns
*/
async function viteLicensePlugin(
root = process.cwd(),
): Promise<PluginOption | undefined> {
const {
description = '',
homepage = '',
version = '',
} = await readPackageJSON(root);
return {
apply: 'build',
enforce: 'post',
generateBundle: {
handler: (_options: NormalizedOutputOptions, bundle: OutputBundle) => {
const date = dateUtil().format('YYYY-MM-DD ');
const copyrightText = `/*!
* AIFlowy Admin
* Version: ${version}
* Author: aiflowy
* Copyright (C) 2026 AIFlowy
* License: MIT License
* Description: ${description}
* Date Created: ${date}
* Homepage: ${homepage}
* Contact: fuhai999@gmail.com
*/
`.trim();
for (const [, fileContent] of Object.entries(bundle)) {
if (fileContent.type === 'chunk' && fileContent.isEntry) {
const chunkContent = fileContent as OutputChunk;
// 插入版权信息
const content = chunkContent.code;
const updatedContent = `${copyrightText}${EOL}${content}`;
// 更新bundle
(fileContent as OutputChunk).code = updatedContent;
}
}
},
order: 'post',
},
name: 'vite:license',
};
}
export { viteLicensePlugin };

Some files were not shown because too many files have changed in this diff Show More