From d40e59205eeabf112af3dcd5780b7943d454e8d6 Mon Sep 17 00:00:00 2001 From: Rodney Chen <rodney.chen@hotmail.com> Date: 星期二, 18 六月 2024 20:57:08 +0800 Subject: [PATCH] 干净版internal --- internal/vite-config/src/plugins/appConfig.ts | 104 +++++ internal/eslint-config/src/index.ts | 91 ++++ internal/vite-config/src/plugins/compress.ts | 38 ++ .gitignore | 2 internal/stylelint-config/src/index.ts | 92 ++++ internal/eslint-config/build.config.ts | 10 internal/vite-config/package.json | 59 +++ internal/stylelint-config/.eslintignore | 9 internal/stylelint-config/build.config.ts | 10 internal/vite-config/build.config.ts | 10 internal/vite-config/.eslintrc.cjs | 4 internal/vite-config/src/plugins/svgSprite.ts | 17 internal/vite-config/src/plugins/visualizer.ts | 14 internal/eslint-config/package.json | 50 ++ internal/ts-config/node.json | 12 internal/ts-config/vue-app.json | 10 internal/vite-config/src/utils/modifyVars.ts | 47 ++ internal/stylelint-config/package.json | 49 ++ internal/ts-config/node-server.json | 18 internal/ts-config/package.json | 26 + internal/eslint-config/tsconfig.json | 5 internal/vite-config/src/utils/hash.ts | 16 internal/eslint-config/.eslintignore | 9 internal/vite-config/src/config/package.ts | 42 ++ internal/vite-config/src/plugins/index.ts | 62 +++ internal/ts-config/base.json | 27 + internal/stylelint-config/.eslintrc.cjs | 4 internal/eslint-config/src/strict.ts | 57 +++ internal/vite-config/src/plugins/html.ts | 13 internal/vite-config/src/utils/env.ts | 49 ++ internal/vite-config/src/config/application.ts | 109 +++++ internal/stylelint-config/tsconfig.json | 5 internal/eslint-config/.eslintrc.cjs | 4 internal/vite-config/src/plugins/mock.ts | 19 + internal/vite-config/src/index.ts | 2 internal/vite-config/.eslintignore | 9 internal/vite-config/src/config/common.ts | 22 + internal/vite-config/tsconfig.json | 5 38 files changed, 1,130 insertions(+), 1 deletions(-) diff --git a/.gitignore b/.gitignore index e1ca94c..5ed19af 100644 --- a/.gitignore +++ b/.gitignore @@ -43,4 +43,4 @@ .history .vscode/extensions.json .vscode/settings.json -internal/ + diff --git a/internal/eslint-config/.eslintignore b/internal/eslint-config/.eslintignore new file mode 100644 index 0000000..cef44b3 --- /dev/null +++ b/internal/eslint-config/.eslintignore @@ -0,0 +1,9 @@ + +*.sh +node_modules +*.md +*.woff +*.ttf +.turbo +dist +package.json diff --git a/internal/eslint-config/.eslintrc.cjs b/internal/eslint-config/.eslintrc.cjs new file mode 100644 index 0000000..cd27a19 --- /dev/null +++ b/internal/eslint-config/.eslintrc.cjs @@ -0,0 +1,4 @@ +module.exports = { + root: true, + extends: ['@vben/eslint-config/strict'], +}; diff --git a/internal/eslint-config/build.config.ts b/internal/eslint-config/build.config.ts new file mode 100644 index 0000000..08301e5 --- /dev/null +++ b/internal/eslint-config/build.config.ts @@ -0,0 +1,10 @@ +import { defineBuildConfig } from 'unbuild'; + +export default defineBuildConfig({ + clean: true, + entries: ['src/index', 'src/strict'], + declaration: true, + rollup: { + emitCJS: true, + }, +}); diff --git a/internal/eslint-config/package.json b/internal/eslint-config/package.json new file mode 100644 index 0000000..2e075f8 --- /dev/null +++ b/internal/eslint-config/package.json @@ -0,0 +1,50 @@ +{ + "name": "@vben/eslint-config", + "version": "1.0.0", + "private": true, + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": { + "url": "https://github.com/vbenjs/vue-vben-admin/issues" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "internal/eslint-config" + }, + "license": "MIT", + "type": "module", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.mjs", + "require": "./dist/index.cjs" + }, + "./strict": { + "types": "./dist/strict.d.ts", + "import": "./dist/strict.mjs", + "require": "./dist/strict.cjs" + } + }, + "main": "./dist/index.cjs", + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", + "files": [ + "dist" + ], + "scripts": { + "clean": "pnpm rimraf .turbo node_modules dist", + "lint": "pnpm eslint .", + "stub": "pnpm unbuild --stub" + }, + "devDependencies": { + "@typescript-eslint/eslint-plugin": "^7.0.1", + "@typescript-eslint/parser": "^7.0.1", + "eslint": "^8.56.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-import": "^2.29.1", + "eslint-plugin-prettier": "^5.1.3", + "eslint-plugin-simple-import-sort": "^12.0.0", + "eslint-plugin-vue": "^9.21.1", + "vue-eslint-parser": "^9.4.2" + } +} diff --git a/internal/eslint-config/src/index.ts b/internal/eslint-config/src/index.ts new file mode 100644 index 0000000..1138bb3 --- /dev/null +++ b/internal/eslint-config/src/index.ts @@ -0,0 +1,91 @@ +export default { + env: { + browser: true, + node: true, + es6: true, + }, + parser: 'vue-eslint-parser', + parserOptions: { + parser: '@typescript-eslint/parser', + ecmaVersion: 2020, + sourceType: 'module', + jsxPragma: 'React', + ecmaFeatures: { + jsx: true, + }, + project: './tsconfig.*?.json', + createDefaultProgram: false, + extraFileExtensions: ['.vue'], + }, + plugins: ['vue', '@typescript-eslint', 'import'], + extends: [ + 'eslint:recommended', + 'plugin:vue/vue3-recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:prettier/recommended', + ], + rules: { + 'no-unused-vars': 'off', + 'no-case-declarations': 'off', + 'no-use-before-define': 'off', + 'space-before-function-paren': 'off', + + 'import/first': 'error', + 'import/newline-after-import': 'error', + 'import/no-duplicates': 'error', + + '@typescript-eslint/no-unused-vars': [ + 'error', + { + argsIgnorePattern: '^_', + varsIgnorePattern: '^_', + }, + ], + '@typescript-eslint/ban-ts-ignore': 'off', + '@typescript-eslint/ban-ts-comment': 'off', + '@typescript-eslint/ban-types': 'off', + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-var-requires': 'off', + '@typescript-eslint/no-empty-function': 'off', + '@typescript-eslint/no-use-before-define': 'off', + '@typescript-eslint/no-non-null-assertion': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + 'vue/script-setup-uses-vars': 'error', + 'vue/no-reserved-component-names': 'off', + 'vue/custom-event-name-casing': 'off', + 'vue/attributes-order': 'off', + 'vue/one-component-per-file': 'off', + 'vue/html-closing-bracket-newline': 'off', + 'vue/max-attributes-per-line': 'off', + 'vue/multiline-html-element-content-newline': 'off', + 'vue/singleline-html-element-content-newline': 'off', + 'vue/attribute-hyphenation': 'off', + 'vue/require-default-prop': 'off', + 'vue/require-explicit-emits': 'off', + 'vue/html-self-closing': [ + 'error', + { + html: { + void: 'always', + normal: 'never', + component: 'always', + }, + svg: 'always', + math: 'always', + }, + ], + 'vue/multi-word-component-names': 'off', + // 'sort-imports': [ + // 'error', + // { + // ignoreCase: true, + // ignoreDeclarationSort: false, + // ignoreMemberSort: false, + // memberSyntaxSortOrder: ['none', 'all', 'multiple', 'single'], + // allowSeparatedGroups: false, + // }, + // ], + }, + globals: { defineOptions: 'readonly' }, +}; diff --git a/internal/eslint-config/src/strict.ts b/internal/eslint-config/src/strict.ts new file mode 100644 index 0000000..5dbf5b7 --- /dev/null +++ b/internal/eslint-config/src/strict.ts @@ -0,0 +1,57 @@ +export default { + extends: ['@vben'], + plugins: ['simple-import-sort'], + rules: { + 'simple-import-sort/imports': 'error', + 'simple-import-sort/exports': 'error', + + '@typescript-eslint/ban-ts-comment': [ + 'error', + { + 'ts-expect-error': 'allow-with-description', + 'ts-ignore': 'allow-with-description', + 'ts-nocheck': 'allow-with-description', + 'ts-check': false, + }, + ], + + /** + * 銆愬己鍒躲�戝叧閿瓧鍓嶅悗鏈変竴涓┖鏍� + * @link https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/keyword-spacing.md + */ + 'keyword-spacing': 'off', + '@typescript-eslint/keyword-spacing': [ + 'error', + { + before: true, + after: true, + overrides: { + return: { after: true }, + throw: { after: true }, + case: { after: true }, + }, + }, + ], + + /** + * 绂佹鍑虹幇绌哄嚱鏁帮紝鏅�氬嚱鏁帮紙闈� async/await/generator锛夈�佺澶村嚱鏁般�佺被涓婄殑鏂规硶闄ゅ + * @link https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-empty-function.md + */ + 'no-empty-function': 'off', + '@typescript-eslint/no-empty-function': [ + 'error', + { + allow: ['arrowFunctions', 'functions', 'methods'], + }, + ], + + /** + * 浼樺厛浣跨敤 interface 鑰屼笉鏄� type 瀹氫箟瀵硅薄绫诲瀷 + * @link https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/consistent-type-definitions.md + */ + '@typescript-eslint/consistent-type-definitions': ['warn', 'interface'], + + 'vue/attributes-order': 'error', + 'vue/require-default-prop': 'error', + }, +}; diff --git a/internal/eslint-config/tsconfig.json b/internal/eslint-config/tsconfig.json new file mode 100644 index 0000000..cd27063 --- /dev/null +++ b/internal/eslint-config/tsconfig.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/ts-config/node.json", + "include": ["src"] +} diff --git a/internal/stylelint-config/.eslintignore b/internal/stylelint-config/.eslintignore new file mode 100644 index 0000000..cef44b3 --- /dev/null +++ b/internal/stylelint-config/.eslintignore @@ -0,0 +1,9 @@ + +*.sh +node_modules +*.md +*.woff +*.ttf +.turbo +dist +package.json diff --git a/internal/stylelint-config/.eslintrc.cjs b/internal/stylelint-config/.eslintrc.cjs new file mode 100644 index 0000000..cd27a19 --- /dev/null +++ b/internal/stylelint-config/.eslintrc.cjs @@ -0,0 +1,4 @@ +module.exports = { + root: true, + extends: ['@vben/eslint-config/strict'], +}; diff --git a/internal/stylelint-config/build.config.ts b/internal/stylelint-config/build.config.ts new file mode 100644 index 0000000..20c8b54 --- /dev/null +++ b/internal/stylelint-config/build.config.ts @@ -0,0 +1,10 @@ +import { defineBuildConfig } from 'unbuild'; + +export default defineBuildConfig({ + clean: true, + entries: ['src/index'], + declaration: true, + rollup: { + emitCJS: true, + }, +}); diff --git a/internal/stylelint-config/package.json b/internal/stylelint-config/package.json new file mode 100644 index 0000000..18dd5d2 --- /dev/null +++ b/internal/stylelint-config/package.json @@ -0,0 +1,49 @@ +{ + "name": "@vben/stylelint-config", + "version": "1.0.0", + "private": true, + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": { + "url": "https://github.com/vbenjs/vue-vben-admin/issues" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "internal/stylelint-config" + }, + "license": "MIT", + "type": "module", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.mjs", + "require": "./dist/index.cjs" + } + }, + "main": "./dist/index.cjs", + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", + "files": [ + "dist" + ], + "scripts": { + "clean": "pnpm rimraf .turbo node_modules dist", + "lint": "pnpm eslint .", + "stub": "pnpm unbuild --stub" + }, + "devDependencies": { + "postcss": "^8.4.38", + "postcss-html": "^1.6.0", + "postcss-less": "^6.0.0", + "postcss-scss": "^4.0.9", + "prettier": "^3.2.5", + "stylelint": "^16.4.0", + "stylelint-config-property-sort-order-smacss": "^10.0.0", + "stylelint-config-recommended-scss": "^14.0.0", + "stylelint-config-recommended-vue": "^1.5.0", + "stylelint-config-standard": "^36.0.0", + "stylelint-config-standard-scss": "^13.1.0", + "stylelint-order": "^6.0.4", + "stylelint-prettier": "^5.0.0" + } +} diff --git a/internal/stylelint-config/src/index.ts b/internal/stylelint-config/src/index.ts new file mode 100644 index 0000000..8b15456 --- /dev/null +++ b/internal/stylelint-config/src/index.ts @@ -0,0 +1,92 @@ +export default { + extends: ['stylelint-config-standard', 'stylelint-config-property-sort-order-smacss'], + plugins: ['stylelint-order', 'stylelint-prettier'], + // customSyntax: 'postcss-html', + overrides: [ + { + files: ['**/*.(css|html|vue)'], + customSyntax: 'postcss-html', + }, + { + files: ['*.less', '**/*.less'], + customSyntax: 'postcss-less', + extends: ['stylelint-config-standard', 'stylelint-config-recommended-vue'], + }, + { + files: ['*.scss', '**/*.scss'], + customSyntax: 'postcss-scss', + extends: ['stylelint-config-standard-scss', 'stylelint-config-recommended-vue/scss'], + rule: { + 'scss/percent-placeholder-pattern': null, + }, + }, + ], + rules: { + 'prettier/prettier': true, + 'media-feature-range-notation': null, + 'selector-not-notation': null, + 'import-notation': null, + 'function-no-unknown': null, + 'selector-class-pattern': null, + 'selector-pseudo-class-no-unknown': [ + true, + { + ignorePseudoClasses: ['global', 'deep'], + }, + ], + 'selector-pseudo-element-no-unknown': [ + true, + { + ignorePseudoElements: ['v-deep'], + }, + ], + 'at-rule-no-unknown': [ + true, + { + ignoreAtRules: [ + 'tailwind', + 'apply', + 'variants', + 'responsive', + 'screen', + 'function', + 'if', + 'each', + 'include', + 'mixin', + 'extend', + ], + }, + ], + 'no-empty-source': null, + 'named-grid-areas-no-invalid': null, + 'no-descending-specificity': null, + 'font-family-no-missing-generic-family-keyword': null, + 'rule-empty-line-before': [ + 'always', + { + ignore: ['after-comment', 'first-nested'], + }, + ], + 'unit-no-unknown': [true, { ignoreUnits: ['rpx'] }], + 'order/order': [ + [ + 'dollar-variables', + 'custom-properties', + 'at-rules', + 'declarations', + { + type: 'at-rule', + name: 'supports', + }, + { + type: 'at-rule', + name: 'media', + }, + 'rules', + ], + { severity: 'error' }, + ], + }, + ignoreFiles: ['**/*.js', '**/*.jsx', '**/*.tsx', '**/*.ts'], +}; diff --git a/internal/stylelint-config/tsconfig.json b/internal/stylelint-config/tsconfig.json new file mode 100644 index 0000000..cd27063 --- /dev/null +++ b/internal/stylelint-config/tsconfig.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/ts-config/node.json", + "include": ["src"] +} diff --git a/internal/ts-config/base.json b/internal/ts-config/base.json new file mode 100644 index 0000000..8b90054 --- /dev/null +++ b/internal/ts-config/base.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "display": "Base", + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "bundler", + "strict": true, + "declaration": true, + "noImplicitOverride": true, + "noUnusedLocals": true, + "esModuleInterop": true, + "useUnknownInCatchVariables": false, + "composite": false, + "declarationMap": false, + "forceConsistentCasingInFileNames": true, + "inlineSources": false, + "isolatedModules": true, + "skipLibCheck": true, + "noUnusedParameters": false, + "preserveWatchOutput": true, + "experimentalDecorators": true, + "resolveJsonModule": true, + "removeComments": true + }, + "exclude": ["**/node_modules/**", "**/dist/**"] +} diff --git a/internal/ts-config/node-server.json b/internal/ts-config/node-server.json new file mode 100644 index 0000000..e27374a --- /dev/null +++ b/internal/ts-config/node-server.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "display": "Node Server Config", + "extends": "./base.json", + "compilerOptions": { + "module": "commonjs", + "declaration": false, + "removeComments": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "target": "es6", + "sourceMap": false, + "esModuleInterop": true, + "outDir": "./dist", + "baseUrl": "./" + }, + "exclude": ["node_modules"] +} diff --git a/internal/ts-config/node.json b/internal/ts-config/node.json new file mode 100644 index 0000000..cdd365f --- /dev/null +++ b/internal/ts-config/node.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "display": "Node Config", + "extends": "./base.json", + "compilerOptions": { + "lib": ["ESNext"], + "noImplicitAny": true, + "sourceMap": true, + "noEmit": true, + "baseUrl": "./" + } +} diff --git a/internal/ts-config/package.json b/internal/ts-config/package.json new file mode 100644 index 0000000..dd968c8 --- /dev/null +++ b/internal/ts-config/package.json @@ -0,0 +1,26 @@ +{ + "name": "@vben/ts-config", + "version": "1.0.0", + "private": true, + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": { + "url": "https://github.com/vbenjs/vue-vben-admin/issues" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "internal/ts-config" + }, + "license": "MIT", + "type": "module", + "files": [ + "base.json", + "node.json", + "vue-app.json", + "node-server.json" + ], + "dependencies": { + "@types/node": "^20.12.7", + "vite": "^5.2.10" + } +} diff --git a/internal/ts-config/vue-app.json b/internal/ts-config/vue-app.json new file mode 100644 index 0000000..02f3fd2 --- /dev/null +++ b/internal/ts-config/vue-app.json @@ -0,0 +1,10 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "display": "Vue Application", + "extends": "./base.json", + "compilerOptions": { + "jsx": "preserve", + "lib": ["ESNext", "DOM"], + "noImplicitAny": false + } +} diff --git a/internal/vite-config/.eslintignore b/internal/vite-config/.eslintignore new file mode 100644 index 0000000..cef44b3 --- /dev/null +++ b/internal/vite-config/.eslintignore @@ -0,0 +1,9 @@ + +*.sh +node_modules +*.md +*.woff +*.ttf +.turbo +dist +package.json diff --git a/internal/vite-config/.eslintrc.cjs b/internal/vite-config/.eslintrc.cjs new file mode 100644 index 0000000..cd27a19 --- /dev/null +++ b/internal/vite-config/.eslintrc.cjs @@ -0,0 +1,4 @@ +module.exports = { + root: true, + extends: ['@vben/eslint-config/strict'], +}; diff --git a/internal/vite-config/build.config.ts b/internal/vite-config/build.config.ts new file mode 100644 index 0000000..20c8b54 --- /dev/null +++ b/internal/vite-config/build.config.ts @@ -0,0 +1,10 @@ +import { defineBuildConfig } from 'unbuild'; + +export default defineBuildConfig({ + clean: true, + entries: ['src/index'], + declaration: true, + rollup: { + emitCJS: true, + }, +}); diff --git a/internal/vite-config/package.json b/internal/vite-config/package.json new file mode 100644 index 0000000..86d44e2 --- /dev/null +++ b/internal/vite-config/package.json @@ -0,0 +1,59 @@ +{ + "name": "@vben/vite-config", + "version": "1.0.0", + "private": true, + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": { + "url": "https://github.com/vbenjs/vue-vben-admin/issues" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "internal/vite-config" + }, + "license": "MIT", + "type": "module", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.mjs", + "require": "./dist/index.cjs" + } + }, + "main": "./dist/index.cjs", + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", + "files": [ + "dist" + ], + "scripts": { + "clean": "pnpm rimraf .turbo node_modules dist", + "lint": "pnpm eslint .", + "stub": "pnpm unbuild --stub" + }, + "dependencies": { + "@ant-design/colors": "^7.0.2", + "vite": "^5.2.10" + }, + "devDependencies": { + "@types/fs-extra": "^11.0.4", + "@vitejs/plugin-vue": "^5.0.4", + "@vitejs/plugin-vue-jsx": "^3.1.0", + "ant-design-vue": "^4.2.1", + "dayjs": "^1.11.10", + "dotenv": "^16.4.5", + "fs-extra": "^11.2.0", + "less": "^4.2.0", + "picocolors": "^1.0.0", + "pkg-types": "^1.1.0", + "rollup-plugin-visualizer": "^5.12.0", + "sass": "^1.75.0", + "unocss": "0.59.4", + "vite-plugin-compression": "^0.5.1", + "vite-plugin-dts": "^3.9.0", + "vite-plugin-html": "^3.2.2", + "vite-plugin-mock": "^2.9.6", + "vite-plugin-purge-icons": "^0.10.0", + "vite-plugin-svg-icons": "^2.0.1" + } +} diff --git a/internal/vite-config/src/config/application.ts b/internal/vite-config/src/config/application.ts new file mode 100644 index 0000000..7ee5ef1 --- /dev/null +++ b/internal/vite-config/src/config/application.ts @@ -0,0 +1,109 @@ +import { resolve } from 'node:path'; + +import dayjs from 'dayjs'; +import { readPackageJSON } from 'pkg-types'; +import { defineConfig, loadEnv, mergeConfig, type UserConfig } from 'vite'; + +import { createPlugins } from '../plugins'; +import { generateModifyVars } from '../utils/modifyVars'; +import { commonConfig } from './common'; + +interface DefineOptions { + overrides?: UserConfig; + options?: { + // + }; +} + +function defineApplicationConfig(defineOptions: DefineOptions = {}) { + const { overrides = {} } = defineOptions; + + return defineConfig(async ({ command, mode }) => { + const root = process.cwd(); + const isBuild = command === 'build'; + const { VITE_PUBLIC_PATH, VITE_USE_MOCK, VITE_BUILD_COMPRESS, VITE_ENABLE_ANALYZE } = loadEnv( + mode, + root, + ); + + const defineData = await createDefineData(root); + const plugins = await createPlugins({ + isBuild, + root, + enableAnalyze: VITE_ENABLE_ANALYZE === 'true', + enableMock: VITE_USE_MOCK === 'true', + compress: VITE_BUILD_COMPRESS, + }); + + const pathResolve = (pathname: string) => resolve(root, '.', pathname); + + const applicationConfig: UserConfig = { + base: VITE_PUBLIC_PATH, + resolve: { + alias: [ + { + find: 'vue-i18n', + replacement: 'vue-i18n/dist/vue-i18n.cjs.js', + }, + // @/xxxx => src/xxxx + { + find: /@\//, + replacement: pathResolve('src') + '/', + }, + // #/xxxx => types/xxxx + { + find: /#\//, + replacement: pathResolve('types') + '/', + }, + ], + }, + define: defineData, + build: { + target: 'es2015', + cssTarget: 'chrome80', + rollupOptions: { + output: { + // 鍏ュ彛鏂囦欢鍚� + entryFileNames: 'assets/entry/[name]-[hash].js', + manualChunks: { + vue: ['vue', 'pinia', 'vue-router'], + antd: ['ant-design-vue', '@ant-design/icons-vue'], + }, + }, + }, + }, + css: { + preprocessorOptions: { + less: { + modifyVars: generateModifyVars(), + javascriptEnabled: true, + }, + }, + }, + plugins, + }; + + const mergedConfig = mergeConfig(commonConfig(mode), applicationConfig); + + return mergeConfig(mergedConfig, overrides); + }); +} + +async function createDefineData(root: string) { + try { + const pkgJson = await readPackageJSON(root); + const { dependencies, devDependencies, name, version } = pkgJson; + + const __APP_INFO__ = { + pkg: { dependencies, devDependencies, name, version }, + lastBuildTime: dayjs().format('YYYY-MM-DD HH:mm:ss'), + }; + return { + __APP_INFO__: JSON.stringify(__APP_INFO__), + }; + } catch (error) { + return {}; + } +} + +export { defineApplicationConfig }; diff --git a/internal/vite-config/src/config/common.ts b/internal/vite-config/src/config/common.ts new file mode 100644 index 0000000..559896e --- /dev/null +++ b/internal/vite-config/src/config/common.ts @@ -0,0 +1,22 @@ +import UnoCSS from 'unocss/vite'; +import { type UserConfig } from 'vite'; + +const commonConfig: (mode: string) => UserConfig = (mode) => ({ + server: { + host: true, + }, + esbuild: { + drop: mode === 'production' ? ['console', 'debugger'] : [], + }, + build: { + reportCompressedSize: false, + chunkSizeWarningLimit: 1500, + rollupOptions: { + // TODO: Prevent memory overflow + maxParallelFileOps: 3, + }, + }, + plugins: [UnoCSS()], +}); + +export { commonConfig }; diff --git a/internal/vite-config/src/config/package.ts b/internal/vite-config/src/config/package.ts new file mode 100644 index 0000000..ab83852 --- /dev/null +++ b/internal/vite-config/src/config/package.ts @@ -0,0 +1,42 @@ +import { readPackageJSON } from 'pkg-types'; +import { defineConfig, mergeConfig, type UserConfig } from 'vite'; +import dts from 'vite-plugin-dts'; + +import { commonConfig } from './common'; + +interface DefineOptions { + overrides?: UserConfig; + options?: { + // + }; +} + +function definePackageConfig(defineOptions: DefineOptions = {}) { + const { overrides = {} } = defineOptions; + const root = process.cwd(); + return defineConfig(async ({ mode }) => { + const { dependencies = {}, peerDependencies = {} } = await readPackageJSON(root); + const packageConfig: UserConfig = { + build: { + lib: { + entry: 'src/index.ts', + formats: ['es'], + fileName: () => 'index.mjs', + }, + rollupOptions: { + external: [...Object.keys(dependencies), ...Object.keys(peerDependencies)], + }, + }, + plugins: [ + dts({ + logLevel: 'error', + }), + ], + }; + const mergedConfig = mergeConfig(commonConfig(mode), packageConfig); + + return mergeConfig(mergedConfig, overrides); + }); +} + +export { definePackageConfig }; diff --git a/internal/vite-config/src/index.ts b/internal/vite-config/src/index.ts new file mode 100644 index 0000000..9ef1e80 --- /dev/null +++ b/internal/vite-config/src/index.ts @@ -0,0 +1,2 @@ +export * from './config/application'; +export * from './config/package'; diff --git a/internal/vite-config/src/plugins/appConfig.ts b/internal/vite-config/src/plugins/appConfig.ts new file mode 100644 index 0000000..7d50662 --- /dev/null +++ b/internal/vite-config/src/plugins/appConfig.ts @@ -0,0 +1,104 @@ +import colors from 'picocolors'; +import { readPackageJSON } from 'pkg-types'; +import { type PluginOption } from 'vite'; + +import { getEnvConfig } from '../utils/env'; +import { createContentHash } from '../utils/hash'; + +const GLOBAL_CONFIG_FILE_NAME = '_app.config.js'; +const PLUGIN_NAME = 'app-config'; + +async function createAppConfigPlugin({ + root, + isBuild, +}: { + root: string; + isBuild: boolean; +}): Promise<PluginOption> { + let publicPath: string; + let source: string; + if (!isBuild) { + return { + name: PLUGIN_NAME, + }; + } + const { version = '' } = await readPackageJSON(root); + + return { + name: PLUGIN_NAME, + async configResolved(_config) { + const appTitle = _config?.env?.VITE_GLOB_APP_TITLE ?? ''; + // appTitle = appTitle.replace(/\s/g, '_').replace(/-/g, '_'); + publicPath = _config.base; + source = await getConfigSource(appTitle); + }, + async transformIndexHtml(html) { + publicPath = publicPath.endsWith('/') ? publicPath : `${publicPath}/`; + + const appConfigSrc = `${ + publicPath || '/' + }${GLOBAL_CONFIG_FILE_NAME}?v=${version}-${createContentHash(source)}`; + + return { + html, + tags: [ + { + tag: 'script', + attrs: { + src: appConfigSrc, + }, + }, + ], + }; + }, + async generateBundle() { + try { + this.emitFile({ + type: 'asset', + fileName: GLOBAL_CONFIG_FILE_NAME, + source, + }); + + console.log(colors.cyan(`鉁╟onfiguration file is build successfully!`)); + } catch (error) { + console.log( + colors.red('configuration file configuration file failed to package:\n' + error), + ); + } + }, + }; +} + +/** + * Get the configuration file variable name + * @param env + */ +const getVariableName = (title: string) => { + function strToHex(str: string) { + const result: string[] = []; + for (let i = 0; i < str.length; ++i) { + const hex = str.charCodeAt(i).toString(16); + result.push(('000' + hex).slice(-4)); + } + return result.join('').toUpperCase(); + } + return `__PRODUCTION__${strToHex(title) || '__APP'}__CONF__`.toUpperCase().replace(/\s/g, ''); +}; + +async function getConfigSource(appTitle: string) { + const config = await getEnvConfig(); + const variableName = getVariableName(appTitle); + const windowVariable = `window.${variableName}`; + // Ensure that the variable will not be modified + let source = `${windowVariable}=${JSON.stringify(config)};`; + source += ` + Object.freeze(${windowVariable}); + Object.defineProperty(window, "${variableName}", { + configurable: false, + writable: false, + }); + `.replace(/\s/g, ''); + return source; +} + +export { createAppConfigPlugin }; diff --git a/internal/vite-config/src/plugins/compress.ts b/internal/vite-config/src/plugins/compress.ts new file mode 100644 index 0000000..8fc1397 --- /dev/null +++ b/internal/vite-config/src/plugins/compress.ts @@ -0,0 +1,38 @@ +/** + * Used to package and output gzip. Note that this does not work properly in Vite, the specific reason is still being investigated + * https://github.com/anncwb/vite-plugin-compression + */ +import type { PluginOption } from 'vite'; +import compressPlugin from 'vite-plugin-compression'; + +export function configCompressPlugin({ + compress, + deleteOriginFile = false, +}: { + compress: string; + deleteOriginFile?: boolean; +}): PluginOption[] { + const compressList = compress.split(','); + + const plugins: PluginOption[] = []; + + if (compressList.includes('gzip')) { + plugins.push( + compressPlugin({ + ext: '.gz', + deleteOriginFile, + }), + ); + } + + if (compressList.includes('brotli')) { + plugins.push( + compressPlugin({ + ext: '.br', + algorithm: 'brotliCompress', + deleteOriginFile, + }), + ); + } + return plugins; +} diff --git a/internal/vite-config/src/plugins/html.ts b/internal/vite-config/src/plugins/html.ts new file mode 100644 index 0000000..a01b43a --- /dev/null +++ b/internal/vite-config/src/plugins/html.ts @@ -0,0 +1,13 @@ +/** + * Plugin to minimize and use ejs template syntax in index.html. + * https://github.com/anncwb/vite-plugin-html + */ +import type { PluginOption } from 'vite'; +import { createHtmlPlugin } from 'vite-plugin-html'; + +export function configHtmlPlugin({ isBuild }: { isBuild: boolean }) { + const htmlPlugin: PluginOption[] = createHtmlPlugin({ + minify: isBuild, + }); + return htmlPlugin; +} diff --git a/internal/vite-config/src/plugins/index.ts b/internal/vite-config/src/plugins/index.ts new file mode 100644 index 0000000..77b7466 --- /dev/null +++ b/internal/vite-config/src/plugins/index.ts @@ -0,0 +1,62 @@ +import vue from '@vitejs/plugin-vue'; +import vueJsx from '@vitejs/plugin-vue-jsx'; +import { type PluginOption } from 'vite'; +import purgeIcons from 'vite-plugin-purge-icons'; +import DevTools from 'vite-plugin-vue-devtools'; + +import { createAppConfigPlugin } from './appConfig'; +import { configCompressPlugin } from './compress'; +import { configHtmlPlugin } from './html'; +import { configMockPlugin } from './mock'; +import { configSvgIconsPlugin } from './svgSprite'; +import { configVisualizerConfig } from './visualizer'; + +interface Options { + isBuild: boolean; + root: string; + compress: string; + enableMock?: boolean; + enableAnalyze?: boolean; +} + +async function createPlugins({ isBuild, root, enableMock, compress, enableAnalyze }: Options) { + const vitePlugins: (PluginOption | PluginOption[])[] = [vue(), vueJsx()]; + + const appConfigPlugin = await createAppConfigPlugin({ root, isBuild }); + vitePlugins.push(appConfigPlugin); + + vitePlugins.push(DevTools()); + + // vite-plugin-html + vitePlugins.push(configHtmlPlugin({ isBuild })); + + // vite-plugin-svg-icons + vitePlugins.push(configSvgIconsPlugin({ isBuild })); + + // vite-plugin-purge-icons + vitePlugins.push(purgeIcons()); + + // The following plugins only work in the production environment + if (isBuild) { + // rollup-plugin-gzip + vitePlugins.push( + configCompressPlugin({ + compress, + }), + ); + } + + // rollup-plugin-visualizer + if (enableAnalyze) { + vitePlugins.push(configVisualizerConfig()); + } + + // vite-plugin-mock + if (enableMock) { + vitePlugins.push(configMockPlugin({ isBuild })); + } + + return vitePlugins; +} + +export { createPlugins }; diff --git a/internal/vite-config/src/plugins/mock.ts b/internal/vite-config/src/plugins/mock.ts new file mode 100644 index 0000000..b47899c --- /dev/null +++ b/internal/vite-config/src/plugins/mock.ts @@ -0,0 +1,19 @@ +/** + * Mock plugin for development and production. + * https://github.com/anncwb/vite-plugin-mock + */ +import { viteMockServe } from 'vite-plugin-mock'; + +export function configMockPlugin({ isBuild }: { isBuild: boolean }) { + return viteMockServe({ + ignore: /^_/, + mockPath: 'mock', + localEnabled: !isBuild, + prodEnabled: isBuild, + injectCode: ` + import { setupProdMockServer } from '../mock/_createProductionServer'; + + setupProdMockServer(); + `, + }); +} diff --git a/internal/vite-config/src/plugins/svgSprite.ts b/internal/vite-config/src/plugins/svgSprite.ts new file mode 100644 index 0000000..659e5af --- /dev/null +++ b/internal/vite-config/src/plugins/svgSprite.ts @@ -0,0 +1,17 @@ +/** + * Vite Plugin for fast creating SVG sprites. + * https://github.com/anncwb/vite-plugin-svg-icons + */ + +import { resolve } from 'node:path'; + +import type { PluginOption } from 'vite'; +import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'; + +export function configSvgIconsPlugin({ isBuild }: { isBuild: boolean }) { + const svgIconsPlugin = createSvgIconsPlugin({ + iconDirs: [resolve(process.cwd(), 'src/assets/icons')], + svgoOptions: isBuild, + }); + return svgIconsPlugin as PluginOption; +} diff --git a/internal/vite-config/src/plugins/visualizer.ts b/internal/vite-config/src/plugins/visualizer.ts new file mode 100644 index 0000000..0b6ba62 --- /dev/null +++ b/internal/vite-config/src/plugins/visualizer.ts @@ -0,0 +1,14 @@ +/** + * Package file volume analysis + */ +import visualizer from 'rollup-plugin-visualizer'; +import { type PluginOption } from 'vite'; + +export function configVisualizerConfig() { + return visualizer({ + filename: './node_modules/.cache/visualizer/stats.html', + open: true, + gzipSize: true, + brotliSize: true, + }) as PluginOption; +} diff --git a/internal/vite-config/src/utils/env.ts b/internal/vite-config/src/utils/env.ts new file mode 100644 index 0000000..c84ea94 --- /dev/null +++ b/internal/vite-config/src/utils/env.ts @@ -0,0 +1,49 @@ +import { join } from 'node:path'; + +import dotenv from 'dotenv'; +import { readFile } from 'fs-extra'; + +/** + * 鑾峰彇褰撳墠鐜涓嬬敓鏁堢殑閰嶇疆鏂囦欢鍚� + */ +function getConfFiles() { + const script = process.env.npm_lifecycle_script as string; + const reg = new RegExp('--mode ([a-z_\\d]+)'); + const result = reg.exec(script); + if (result) { + const mode = result[1]; + return ['.env', `.env.${mode}`]; + } + return ['.env', '.env.production']; +} + +/** + * Get the environment variables starting with the specified prefix + * @param match prefix + * @param confFiles ext + */ +export async function getEnvConfig( + match = 'VITE_GLOB_', + confFiles = getConfFiles(), +): Promise<{ + [key: string]: string; +}> { + let envConfig = {}; + + for (const confFile of confFiles) { + try { + const envPath = await readFile(join(process.cwd(), confFile), { encoding: 'utf8' }); + const env = dotenv.parse(envPath); + envConfig = { ...envConfig, ...env }; + } catch (e) { + console.error(`Error in parsing ${confFile}`, e); + } + } + const reg = new RegExp(`^(${match})`); + Object.keys(envConfig).forEach((key) => { + if (!reg.test(key)) { + Reflect.deleteProperty(envConfig, key); + } + }); + return envConfig; +} diff --git a/internal/vite-config/src/utils/hash.ts b/internal/vite-config/src/utils/hash.ts new file mode 100644 index 0000000..0b5a7c9 --- /dev/null +++ b/internal/vite-config/src/utils/hash.ts @@ -0,0 +1,16 @@ +import { createHash } from 'node:crypto'; + +function createContentHash(content: string, hashLSize = 12) { + const hash = createHash('sha256').update(content); + return hash.digest('hex').slice(0, hashLSize); +} +function strToHex(str: string) { + const result: string[] = []; + for (let i = 0; i < str.length; ++i) { + const hex = str.charCodeAt(i).toString(16); + result.push(('000' + hex).slice(-4)); + } + return result.join('').toUpperCase(); +} + +export { createContentHash, strToHex }; diff --git a/internal/vite-config/src/utils/modifyVars.ts b/internal/vite-config/src/utils/modifyVars.ts new file mode 100644 index 0000000..0554343 --- /dev/null +++ b/internal/vite-config/src/utils/modifyVars.ts @@ -0,0 +1,47 @@ +import { resolve } from 'node:path'; + +import { generate } from '@ant-design/colors'; +// @ts-ignore: typo +/* import { getThemeVariables } from 'ant-design-vue/dist/theme'; */ +import { theme } from 'ant-design-vue/lib'; +import convertLegacyToken from 'ant-design-vue/lib/theme/convertLegacyToken'; + +const { defaultAlgorithm, defaultSeed } = theme; +const primaryColor = '#0960bd'; + +function generateAntColors(color: string, theme: 'default' | 'dark' = 'default') { + return generate(color, { + theme, + }); +} + +/** + * less global variable + */ +export function generateModifyVars() { + const palettes = generateAntColors(primaryColor); + const primary = palettes[5]; + const primaryColorObj: Record<string, string> = {}; + + for (let index = 0; index < 10; index++) { + primaryColorObj[`primary-${index + 1}`] = palettes[index]; + } + // const modifyVars = getThemeVariables(); + const mapToken = defaultAlgorithm(defaultSeed); + const v3Token = convertLegacyToken(mapToken); + return { + ...v3Token, + // reference: Avoid repeated references + hack: `true; @import (reference) "${resolve('src/design/config.less')}";`, + 'primary-color': primary, + ...primaryColorObj, + 'info-color': primary, + 'processing-color': primary, + 'success-color': '#55D187', // Success color + 'error-color': '#ED6F6F', // False color + 'warning-color': '#EFBD47', // Warning color + 'font-size-base': '14px', // Main font size + 'border-radius-base': '2px', // Component/float fillet + 'link-color': primary, // Link color + }; +} diff --git a/internal/vite-config/tsconfig.json b/internal/vite-config/tsconfig.json new file mode 100644 index 0000000..cd27063 --- /dev/null +++ b/internal/vite-config/tsconfig.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/ts-config/node.json", + "include": ["src"] +} -- Gitblit v1.9.3