diff --git a/.env.production b/.env.production index ec9a400d..827fd3b3 100644 --- a/.env.production +++ b/.env.production @@ -23,5 +23,5 @@ VITE_GLOB_API_URL_PREFIX= # use pwa VITE_USE_PWA = false -# TODO use Cdn -VITE_USE_CDN = true +# Is it compatible with older browsers +VITE_LEGACY = false diff --git a/build/script/changelog.ts b/build/script/changelog.ts deleted file mode 100644 index 6bab0899..00000000 --- a/build/script/changelog.ts +++ /dev/null @@ -1,26 +0,0 @@ -// #!/usr/bin/env node - -import { sh } from 'tasksfile'; -import { errorConsole, successConsole } from '../utils'; - -export const runChangeLog = async () => { - try { - let cmd = `conventional-changelog -p custom-config -i CHANGELOG.md -s -r 0 `; - - await sh(cmd, { - async: true, - nopipe: true, - }); - await sh('prettier --write **/CHANGELOG.md ', { - async: true, - nopipe: true, - }); - successConsole('CHANGE_LOG.md generated successfully!'); - } catch (error) { - errorConsole('CHANGE_LOG.md generated error\n' + error); - - process.exit(1); - } -}; - -runChangeLog(); diff --git a/build/script/postBuild.ts b/build/script/postBuild.ts index 3a9574f0..8c06295f 100644 --- a/build/script/postBuild.ts +++ b/build/script/postBuild.ts @@ -3,7 +3,6 @@ import { argv } from 'yargs'; import { runBuildConfig } from './buildConf'; import { errorConsole, successConsole } from '../utils'; -import { startGzipStyle } from '../vite/plugin/gzip/compress'; export const runBuild = async () => { try { @@ -13,8 +12,6 @@ export const runBuild = async () => { if (!argvList.includes('no-conf')) { await runBuildConfig(); } - // await runUpdateHtml(); - await startGzipStyle(); successConsole('Vite Build successfully!'); } catch (error) { errorConsole('Vite Build Error\n' + error); diff --git a/build/utils.ts b/build/utils.ts index 93d0019b..045e7f6e 100644 --- a/build/utils.ts +++ b/build/utils.ts @@ -63,11 +63,11 @@ export function getIPAddress() { return ''; } -export function isDevFn(mode: 'development' | 'production'): boolean { +export function isDevFn(mode: string): boolean { return mode === 'development'; } -export function isProdFn(mode: 'development' | 'production'): boolean { +export function isProdFn(mode: string): boolean { return mode === 'production'; } @@ -85,13 +85,6 @@ export function isBuildGzip(): boolean { return process.env.VITE_BUILD_GZIP === 'true'; } -/** - * Whether to generate package site - */ -export function isSiteMode(): boolean { - return process.env.SITE === 'true'; -} - export interface ViteEnv { VITE_PORT: number; VITE_USE_MOCK: boolean; @@ -99,10 +92,12 @@ export interface ViteEnv { VITE_PUBLIC_PATH: string; VITE_PROXY: [string, string][]; VITE_GLOB_APP_TITLE: string; + VITE_GLOB_APP_SHORT_NAME: string; VITE_USE_CDN: boolean; VITE_DROP_CONSOLE: boolean; VITE_BUILD_GZIP: boolean; VITE_DYNAMIC_IMPORT: boolean; + VITE_LEGACY: boolean; } // Read all environment variable configuration files to process.env diff --git a/build/vite/hm.ts b/build/vite/hm.ts deleted file mode 100644 index 14abdd8b..00000000 --- a/build/vite/hm.ts +++ /dev/null @@ -1,12 +0,0 @@ -// Baidu statistics code for site deployment -// Only open in build:site -export const hmScript = ` -`; diff --git a/build/vite/plugin/gzip.ts b/build/vite/plugin/gzip.ts new file mode 100644 index 00000000..3cd2716a --- /dev/null +++ b/build/vite/plugin/gzip.ts @@ -0,0 +1,12 @@ +import gzipPlugin from 'rollup-plugin-gzip'; +import { isBuildGzip } from '../../utils'; +import { Plugin } from 'vite'; +export function configGzipPlugin(isBuild: boolean): Plugin | Plugin[] { + const useGzip = isBuild && isBuildGzip; + + if (useGzip) { + return gzipPlugin(); + } + + return []; +} diff --git a/build/vite/plugin/gzip/compress.ts b/build/vite/plugin/gzip/compress.ts deleted file mode 100644 index 119bbe80..00000000 --- a/build/vite/plugin/gzip/compress.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { gzip } from 'zlib'; -import { readFileSync, writeFileSync } from 'fs'; -import { GzipPluginOptions } from './types'; -import { readAllFile, getCwdPath, isBuildGzip, isSiteMode } from '../../../utils'; - -export function startGzip( - fileContent: string | Buffer, - options: GzipPluginOptions = {} -): Promise { - return new Promise((resolve, reject) => { - gzip(fileContent, options.gzipOptions || {}, (err, result) => { - if (err) { - reject(err); - } else { - resolve(result); - } - }); - }); -} - -// 手动压缩css -export async function startGzipStyle() { - if (isBuildGzip() || isSiteMode()) { - const outDir = 'dist'; - const assets = '_assets'; - const allCssFile = readAllFile(getCwdPath(outDir, assets), /\.(css)$/); - for (const path of allCssFile) { - const source = readFileSync(path); - const content = await startGzip(source); - const ds = path.split('/'); - const fileName = ds[ds.length - 1]; - writeFileSync(getCwdPath(outDir, assets, `${fileName}.gz`), content); - } - } -} diff --git a/build/vite/plugin/gzip/index.ts b/build/vite/plugin/gzip/index.ts deleted file mode 100644 index 011b224b..00000000 --- a/build/vite/plugin/gzip/index.ts +++ /dev/null @@ -1,196 +0,0 @@ -// 修改自https://github.com/kryops/rollup-plugin-gzip -// 因为rollup-plugin-gzip不支持vite -// vite对css打包独立的。所以不能在打包的时候顺带打包css -// TODO rc.9会支持 configurBuild 配置项。到时候重新修改 - -import { readFile, writeFile } from 'fs'; -import { basename } from 'path'; -import { promisify } from 'util'; -import { gzip } from 'zlib'; - -import { OutputAsset, OutputChunk, OutputOptions, Plugin } from 'rollup'; -import { GzipPluginOptions } from './types'; - -const isFunction = (arg: unknown): arg is (...args: any[]) => any => typeof arg === 'function'; -const isRegExp = (arg: unknown): arg is RegExp => - Object.prototype.toString.call(arg) === '[object RegExp]'; - -export type StringMappingOption = (originalString: string) => string; -export type CustomCompressionOption = ( - content: string | Buffer -) => string | Buffer | Promise; - -const readFilePromise = promisify(readFile); -const writeFilePromise = promisify(writeFile); - -// functionality partially copied from rollup - -/** - * copied from https://github.com/rollup/rollup/blob/master/src/rollup/index.ts#L450 - */ -function isOutputChunk(file: OutputAsset | OutputChunk): file is OutputChunk { - return typeof (file as OutputChunk).code === 'string'; -} - -/** - * Gets the string/buffer content from a file object. - * Important for adding source map comments - * - * Copied partially from rollup.writeOutputFile - * https://github.com/rollup/rollup/blob/master/src/rollup/index.ts#L454 - */ -function getOutputFileContent( - outputFileName: string, - outputFile: OutputAsset | OutputChunk, - outputOptions: OutputOptions -): string | Buffer { - if (isOutputChunk(outputFile)) { - let source: string | Buffer; - source = outputFile.code; - if (outputOptions.sourcemap && outputFile.map) { - const url = - outputOptions.sourcemap === 'inline' - ? outputFile.map.toUrl() - : `${basename(outputFileName)}.map`; - - // https://github.com/rollup/rollup/blob/master/src/utils/sourceMappingURL.ts#L1 - source += `//# source` + `MappingURL=${url}\n`; - } - return source; - } else { - return typeof outputFile.source === 'string' - ? outputFile.source - : // just to be sure, as it is typed string | Uint8Array in rollup 2.0.0 - Buffer.from(outputFile.source); - } -} - -// actual plugin code - -function gzipPlugin(options: GzipPluginOptions = {}): Plugin { - // check for old options - if ('algorithm' in options) { - console.warn( - '[rollup-plugin-gzip] The "algorithm" option is not supported any more! ' + - 'Use "customCompression" instead to specify a different compression algorithm.' - ); - } - if ('options' in options) { - console.warn('[rollup-plugin-gzip] The "options" option was renamed to "gzipOptions"!'); - } - if ('additional' in options) { - console.warn('[rollup-plugin-gzip] The "additional" option was renamed to "additionalFiles"!'); - } - if ('delay' in options) { - console.warn('[rollup-plugin-gzip] The "delay" option was renamed to "additionalFilesDelay"!'); - } - - const compressGzip: CustomCompressionOption = (fileContent) => { - return new Promise((resolve, reject) => { - gzip(fileContent, options.gzipOptions || {}, (err, result) => { - if (err) { - reject(err); - } else { - resolve(result); - } - }); - }); - }; - - const doCompress = options.customCompression || compressGzip; - - const mapFileName: StringMappingOption = isFunction(options.fileName) - ? (options.fileName as StringMappingOption) - : (fileName: string) => fileName + (options.fileName || '.gz'); - - const plugin: Plugin = { - name: 'gzip', - - generateBundle(outputOptions, bundle) { - return Promise.all( - Object.keys(bundle) - .map((fileName) => { - const fileEntry = bundle[fileName]; - - // file name filter option check - - const fileNameFilter = options.filter || /\.(js|mjs|json|css|html)$/; - - if (isRegExp(fileNameFilter) && !fileName.match(fileNameFilter)) { - return Promise.resolve(); - } - - if ( - isFunction(fileNameFilter) && - !(fileNameFilter as (x: string) => boolean)(fileName) - ) { - return Promise.resolve(); - } - - const fileContent = getOutputFileContent(fileName, fileEntry, outputOptions); - - // minSize option check - if (options.minSize && options.minSize > fileContent.length) { - return Promise.resolve(); - } - - return Promise.resolve(doCompress(fileContent)) - .then((compressedContent) => { - const compressedFileName = mapFileName(fileName); - bundle[compressedFileName] = { - type: 'asset', // Rollup >= 1.21 - name: compressedFileName, - fileName: compressedFileName, - isAsset: true, // Rollup < 1.21 - source: compressedContent, - }; - }) - .catch((err: any) => { - console.error(err); - return Promise.reject('[rollup-plugin-gzip] Error compressing file ' + fileName); - }); - }) - .concat([ - (() => { - if (!options.additionalFiles || !options.additionalFiles.length) - return Promise.resolve(); - - const compressAdditionalFiles = () => - Promise.all( - options.additionalFiles!.map((filePath) => - readFilePromise(filePath) - .then((fileContent) => doCompress(fileContent)) - .then((compressedContent) => { - return writeFilePromise(mapFileName(filePath), compressedContent); - }) - .catch(() => { - return Promise.reject( - '[rollup-plugin-gzip] Error compressing additional file ' + - filePath + - '. Please check the spelling of your configured additionalFiles. ' + - 'You might also have to increase the value of the additionalFilesDelay option.' - ); - }) - ) - ) as Promise; - - // additional files can be processed outside of rollup after a delay - // for older plugins or plugins that write to disk (curcumventing rollup) without awaiting - const additionalFilesDelay = options.additionalFilesDelay || 0; - - if (additionalFilesDelay) { - setTimeout(compressAdditionalFiles, additionalFilesDelay); - return Promise.resolve(); - } else { - return compressAdditionalFiles(); - } - })(), - ]) - ) as Promise; - }, - }; - - return plugin; -} - -export default gzipPlugin; diff --git a/build/vite/plugin/gzip/types.ts b/build/vite/plugin/gzip/types.ts deleted file mode 100644 index 2250bcc9..00000000 --- a/build/vite/plugin/gzip/types.ts +++ /dev/null @@ -1,56 +0,0 @@ -import type { ZlibOptions } from 'zlib'; - -export type StringMappingOption = (originalString: string) => string; -export type CustomCompressionOption = ( - content: string | Buffer -) => string | Buffer | Promise; - -export interface GzipPluginOptions { - /** - * Control which of the output files to compress - * - * Defaults to `/\.(js|mjs|json|css|html)$/` - */ - filter?: RegExp | ((fileName: string) => boolean); - - /** - * GZIP compression options, see https://nodejs.org/api/zlib.html#zlib_class_options - */ - gzipOptions?: ZlibOptions; - - /** - * Specified the minimum size in Bytes for a file to get compressed. - * Files that are smaller than this threshold will not be compressed. - * This does not apply to the files specified through `additionalFiles`! - */ - minSize?: number; - - /** - * This option allows you to compress additional files outside of the main rollup bundling process. - * The processing is delayed to make sure the files are written on disk; the delay is controlled - * through `additionalFilesDelay`. - */ - additionalFiles?: string[]; - - /** - * This options sets a delay (ms) before the plugin compresses the files specified through `additionalFiles`. - * Increase this value if your artifacts take a long time to generate. - * - * Defaults to `2000` - */ - additionalFilesDelay?: number; - - /** - * Set a custom compression algorithm. The function can either return the compressed contents synchronously, - * or otherwise return a promise for asynchronous processing. - */ - customCompression?: CustomCompressionOption; - - /** - * Set a custom file name convention for the compressed files. Can be a suffix string or a function - * returning the file name. - * - * Defaults to `.gz` - */ - fileName?: string | StringMappingOption; -} diff --git a/build/vite/plugin/html.ts b/build/vite/plugin/html.ts index dd1af20e..9a447814 100644 --- a/build/vite/plugin/html.ts +++ b/build/vite/plugin/html.ts @@ -1,37 +1,32 @@ import type { Plugin } from 'vite'; -import ViteHtmlPlugin from 'vite-plugin-html'; -import { isProdFn, isSiteMode, ViteEnv } from '../../utils'; +import html from 'vite-plugin-html'; +import { ViteEnv } from '../../utils'; -import { hmScript } from '../hm'; // @ts-ignore import pkg from '../../../package.json'; import { GLOB_CONFIG_FILE_NAME } from '../../constant'; -export function setupHtmlPlugin( - plugins: Plugin[], - env: ViteEnv, - mode: 'development' | 'production' -) { +export function configHtmlPlugin(env: ViteEnv, isBuild: boolean) { const { VITE_GLOB_APP_TITLE, VITE_PUBLIC_PATH } = env; - - const htmlPlugin = ViteHtmlPlugin({ - // html title - title: VITE_GLOB_APP_TITLE, - minify: isProdFn(mode), - options: { - publicPath: VITE_PUBLIC_PATH, - // Package and insert additional configuration files - injectConfig: isProdFn(mode) - ? `` - : '', - // Insert Baidu statistics code - hmScript: isSiteMode() ? hmScript : '', - title: VITE_GLOB_APP_TITLE, + const htmlPlugin: Plugin[] = html({ + minify: isBuild, + inject: { + injectData: { + title: VITE_GLOB_APP_TITLE, + }, + tags: isBuild + ? [ + { + tag: 'script', + attrs: { + src: `${VITE_PUBLIC_PATH || './'}${GLOB_CONFIG_FILE_NAME}?v=${ + pkg.version + }-${new Date().getTime()}`, + }, + }, + ] + : [], }, }); - - plugins.push(htmlPlugin); - return plugins; + return htmlPlugin; } diff --git a/build/vite/plugin/importContext.ts b/build/vite/plugin/importContext.ts new file mode 100644 index 00000000..96be1b2a --- /dev/null +++ b/build/vite/plugin/importContext.ts @@ -0,0 +1,12 @@ +import dynamicImport from 'vite-plugin-import-context'; +import type { ViteEnv } from '../../utils'; +import type { Plugin } from 'vite'; + +export function configDynamicImport(env: ViteEnv) { + const { VITE_DYNAMIC_IMPORT } = env; + const dynamicImportPlugin: Plugin = dynamicImport({ + include: ['**/*.ts'], + autoImportRoute: VITE_DYNAMIC_IMPORT, + }); + return dynamicImportPlugin; +} diff --git a/build/vite/plugin/index.ts b/build/vite/plugin/index.ts index 5db946ac..a703eb43 100644 --- a/build/vite/plugin/index.ts +++ b/build/vite/plugin/index.ts @@ -1,47 +1,44 @@ -import type { Plugin as VitePlugin } from 'vite'; -import type { Plugin as rollupPlugin } from 'rollup'; +import type { Plugin } from 'vite'; import PurgeIcons from 'vite-plugin-purge-icons'; import visualizer from 'rollup-plugin-visualizer'; -import gzipPlugin from './gzip/index'; // @ts-ignore import pkg from '../../../package.json'; -import { isSiteMode, ViteEnv, isReportMode, isBuildGzip } from '../../utils'; -import { setupHtmlPlugin } from './html'; -import { setupPwaPlugin } from './pwa'; -import { setupMockPlugin } from './mock'; +import { ViteEnv, isReportMode } from '../../utils'; +import { configHtmlPlugin } from './html'; +import { configPwaConfig } from './pwa'; +import { configMockPlugin } from './mock'; +import { configDynamicImport } from './importContext'; +import { configGzipPlugin } from './gzip'; // gen vite plugins -export function createVitePlugins(viteEnv: ViteEnv, mode: 'development' | 'production') { - const vitePlugins: VitePlugin[] = []; +export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean, mode: string) { + const vitePlugins: (Plugin | Plugin[])[] = []; // vite-plugin-html - setupHtmlPlugin(vitePlugins, viteEnv, mode); + vitePlugins.push(configHtmlPlugin(viteEnv, isBuild)); + // vite-plugin-pwa - setupPwaPlugin(vitePlugins, viteEnv, mode); + vitePlugins.push(configPwaConfig(viteEnv, isBuild)); + // vite-plugin-mock - setupMockPlugin(vitePlugins, viteEnv, mode); + vitePlugins.push(configMockPlugin(viteEnv, isBuild)); + + // vite-plugin-import-context + vitePlugins.push(configDynamicImport(viteEnv)); // vite-plugin-purge-icons vitePlugins.push(PurgeIcons()); + // rollup-plugin-gzip + vitePlugins.push(configGzipPlugin(isBuild)); + + // rollup-plugin-visualizer + if (isReportMode()) { + vitePlugins.push(visualizer({ filename: './build/.cache/stats.html', open: true }) as Plugin); + } + return vitePlugins; } - -// gen rollup plugins -export function createRollupPlugin() { - const rollupPlugins: rollupPlugin[] = []; - - if (isReportMode()) { - // rollup-plugin-visualizer - rollupPlugins.push(visualizer({ filename: './build/.cache/stats.html', open: true }) as Plugin); - } - if (isBuildGzip() || isSiteMode()) { - // rollup-plugin-gizp - rollupPlugins.push(gzipPlugin()); - } - - return rollupPlugins; -} diff --git a/build/vite/plugin/mock.ts b/build/vite/plugin/mock.ts index 90537f13..c0f54e55 100644 --- a/build/vite/plugin/mock.ts +++ b/build/vite/plugin/mock.ts @@ -1,24 +1,19 @@ -import { createMockServer } from 'vite-plugin-mock'; -import type { Plugin } from 'vite'; -import { isDevFn, ViteEnv } from '../../utils'; +import { viteMockServe } from 'vite-plugin-mock'; +import { ViteEnv } from '../../utils'; -export function setupMockPlugin( - plugins: Plugin[], - env: ViteEnv, - mode: 'development' | 'production' -) { +export function configMockPlugin(env: ViteEnv, isBuild: boolean) { const { VITE_USE_MOCK } = env; - const useMock = isDevFn(mode) && VITE_USE_MOCK; + const useMock = !isBuild && VITE_USE_MOCK; if (useMock) { - const mockPlugin = createMockServer({ + const mockPlugin = viteMockServe({ ignore: /^\_/, mockPath: 'mock', showTime: true, localEnabled: useMock, }); - plugins.push(mockPlugin); + return mockPlugin; } - return plugins; + return []; } diff --git a/build/vite/plugin/pwa.ts b/build/vite/plugin/pwa.ts index d8e02ed0..6ed4a761 100644 --- a/build/vite/plugin/pwa.ts +++ b/build/vite/plugin/pwa.ts @@ -1,35 +1,31 @@ import { VitePWA } from 'vite-plugin-pwa'; -import type { Plugin } from 'vite'; + import { ViteEnv } from '../../utils'; -export function setupPwaPlugin( - plugins: Plugin[], - env: ViteEnv, - // @ts-ignore - mode: 'development' | 'production' -) { - const { VITE_USE_PWA } = env; +export function configPwaConfig(env: ViteEnv, isBulid: boolean) { + const { VITE_USE_PWA, VITE_GLOB_APP_TITLE, VITE_GLOB_APP_SHORT_NAME } = env; - const pwaPlugin = VitePWA({ - manifest: { - name: 'Vben Admin', - short_name: 'vben_admin', - icons: [ - { - src: './resource/img/pwa-192x192.png', - sizes: '192x192', - type: 'image/png', - }, - { - src: './resource/img/pwa-512x512.png', - sizes: '512x512', - type: 'image/png', - }, - ], - }, - }); - if (VITE_USE_PWA) { - plugins.push(pwaPlugin); + if (VITE_USE_PWA && isBulid) { + // vite-plugin-pwa + const pwaPlugin = VitePWA({ + manifest: { + name: VITE_GLOB_APP_TITLE, + short_name: VITE_GLOB_APP_SHORT_NAME, + icons: [ + { + src: './resource/img/pwa-192x192.png', + sizes: '192x192', + type: 'image/png', + }, + { + src: './resource/img/pwa-512x512.png', + sizes: '512x512', + type: 'image/png', + }, + ], + }, + }); + return pwaPlugin; } - return plugins; + return []; } diff --git a/build/vite/plugin/transform/dynamic-import/index.ts b/build/vite/plugin/transform/dynamic-import/index.ts deleted file mode 100644 index eb60ecca..00000000 --- a/build/vite/plugin/transform/dynamic-import/index.ts +++ /dev/null @@ -1,54 +0,0 @@ -// Used to import all files under `src/views` -// The built-in dynamic import of vite cannot meet the needs of importing all files under views -// Special usage ,Only for this project -import glob from 'glob'; -import { Transform } from 'vite/dist/node/transform.js'; - -function getPath(path: string) { - const lastIndex = path.lastIndexOf('.'); - if (lastIndex !== -1) { - path = path.substring(0, lastIndex); - } - return path.replace('src/views', ''); -} - -const dynamicImportTransform = function (enableDynamicImport: boolean): Transform { - return { - test({ path }) { - // Only convert the file - return ( - path.includes('/src/router/helper/dynamicImport.ts') || - path.includes(`\\src\\router\\helper\\dynamicImport.ts`) - ); - }, - transform({ code }) { - if (!enableDynamicImport) { - return code; - } - - // Only convert the dir - try { - const files = glob.sync('src/views/**/**.{vue,tsx}', { cwd: process.cwd() }); - - return ` - export default function (id) { - switch (id) { - ${files - .map((p) => - ` case '${getPath(p)}': return () => import('${p - .replace('src/views', '/@/views') - .replace(/\/\//g, '/')}');`.replace('.tsx', '') - ) - .join('\n ')} - default: return Promise.reject(new Error("Unknown variable dynamic import: " + id)); - } - }\n\n - `; - } catch (error) { - console.error(error); - return code; - } - }, - }; -}; -export default dynamicImportTransform; diff --git a/build/vite/plugin/transform/globby/index.ts b/build/vite/plugin/transform/globby/index.ts deleted file mode 100644 index 217c0c08..00000000 --- a/build/vite/plugin/transform/globby/index.ts +++ /dev/null @@ -1,222 +0,0 @@ -// Modified from -// https://github.com/luxueyan/vite-transform-globby-import/blob/master/src/index.ts - -// TODO Deleting files requires re-running the project -import { join } from 'path'; -import { lstatSync } from 'fs'; -import glob from 'glob'; -import globrex from 'globrex'; -import dotProp from 'dot-prop'; -import { createResolver, Resolver } from 'vite/dist/node/resolver.js'; -import { Transform } from 'vite/dist/node/transform.js'; - -const modulesDir: string = join(process.cwd(), '/node_modules/'); - -interface SharedConfig { - root?: string; - alias?: Record; - resolvers?: Resolver[]; - - includes?: string[]; -} - -function template(template: string) { - return (data: { [x: string]: any }) => { - return template.replace(/#([^#]+)#/g, (_, g1) => data[g1] || g1); - }; -} - -// TODO support hmr -function hmr(isBuild = false) { - if (isBuild) return ''; - return ` - if (import.meta.hot) { - import.meta.hot.accept(); - }`; -} - -// handle includes -function fileInclude(includes: string | string[] | undefined, filePath: string) { - return !includes || !Array.isArray(includes) - ? true - : includes.some((item) => filePath.startsWith(item)); -} - -// Bare exporter -function compareString(modify: any, data: string[][]) { - return modify ? '\n' + data.map((v) => `${v[0]}._path = ${v[1]}`).join('\n') : ''; -} - -function varTemplate(data: string[][], name: string) { - //prepare deep data (for locales) - let deepData: Record = {}; - let hasDeepData = false; - - //data modify - data.map((v) => { - //check for has deep data - if (v[0].includes('/')) { - hasDeepData = true; - } - - // lastKey is a data - let pathValue = v[0].replace(/\//g, '.').split('.'); - // let scopeKey = ''; - // const len=pathValue.length - // const scope=pathValue[len-2] - let lastKey: string | undefined = pathValue.pop(); - - let deepValue: Record = {}; - if (lastKey) { - // Solve the problem of files with the same name in different folders - const lastKeyList = lastKey.replace('_' + pathValue[0], '').split('_'); - const key = lastKeyList.pop(); - if (key) { - deepValue[key] = lastKey; - } - } - // Set Deep Value - deepValue = Object.assign(deepValue, dotProp.get(deepData, pathValue.join('.'))); - dotProp.set(deepData, pathValue.join('.'), deepValue); - }); - - if (hasDeepData) { - return `const ${name} = ` + JSON.stringify(deepData).replace(/\"|\'/g, ''); - } - - return `const ${name} = { ${data.map((v) => v[0]).join(',')} }`; -} - -const globTransform = function (config: SharedConfig): Transform { - const resolver = createResolver( - config.root || process.cwd(), - config.resolvers || [], - config.alias || {} - ); - const { includes } = config; - const cache = new Map(); - const urlMap = new Map(); - return { - test({ path }) { - const filePath = path.replace('\u0000', ''); // why some path startsWith '\u0000'? - - try { - return ( - !filePath.startsWith(modulesDir) && - /\.(vue|js|jsx|ts|tsx)$/.test(filePath) && - fileInclude(includes, filePath) && - lstatSync(filePath).isFile() - ); - } catch { - return false; - } - }, - transform({ code, path, isBuild }) { - let result = cache.get(path); - if (!result) { - const reg = /import\s+([\w\s{}*]+)\s+from\s+(['"])globby(\?locale)?(\?path)?!([^'"]+)\2/g; - const match = code.match(reg); - if (!match) return code; - const lastImport = urlMap.get(path); - if (lastImport && match) { - code = code.replace(lastImport, match[0]); - } - result = code.replace( - reg, - ( - _, - // variable to export - exportName, - // bare export or not - bareExporter, - // is locale import - isLocale, - // inject _path attr - injectPath, - // path export - globPath - ) => { - const filePath = path.replace('\u0000', ''); // why some path startsWith '\u0000'? - // resolve path - - const resolvedFilePath = globPath.startsWith('.') - ? resolver.resolveRelativeRequest(filePath, globPath) - : { pathname: resolver.requestToFile(globPath) }; - - const files = glob.sync(resolvedFilePath.pathname, { dot: true }); - - let templateStr = 'import #name# from #file#'; // import default - let name = exportName; - const m = exportName.match(/\{\s*(\w+)(\s+as\s+(\w+))?\s*\}/); // import module - const m2 = exportName.match(/\*\s+as\s+(\w+)/); // import * as all module - if (m) { - templateStr = `import { ${m[1]} as #name# } from #file#`; - name = m[3] || m[1]; - } else if (m2) { - templateStr = 'import * as #name# from #file#'; - name = m2[1]; - } - - const templateRender = template(templateStr); - - const groups: Array[] = []; - const replaceFiles = files.map((f, i) => { - const filePath = resolver.fileToRequest(f); - const file = bareExporter + filePath + bareExporter; - - if (isLocale) { - const globrexRes = globrex(globPath, { extended: true, globstar: true }); - - // Get segments for files like an en/system ch/modules for: - // ['en', 'system'] ['ch', 'modules'] - - // TODO The window system and mac system path are inconsistent? - const fileNameWithAlias = filePath.replace(/^(\/src\/)/, '/@/'); - const matchedGroups = globrexRes.regex.exec(fileNameWithAlias); - - if (matchedGroups && matchedGroups.length) { - const matchedSegments = matchedGroups[1]; //first everytime "Full Match" - const matchList = matchedSegments.split('/').filter(Boolean); - const lang = matchList.shift(); - const scope = matchList.pop(); - - // Solve the problem of files with the same name in different folders - const scopeKey = scope ? `${scope}_` : ''; - const fileName = matchedGroups[2]; - const name = scopeKey + fileName + '_' + lang; - - //send deep way like an (en/modules/system/dashboard) into groups - groups.push([matchedSegments + name, file]); - return templateRender({ - name, - file, - }); - } - } else { - groups.push([name + i, file]); - return templateRender({ name: name + i, file }); - } - }); - // save in memory used result - const filesJoined = replaceFiles.join('\n'); - - urlMap.set(path, filesJoined); - - // console.log('======================'); - // console.log(filesJoined, varTemplate(groups, name)); - // console.log('======================'); - return [ - filesJoined, - compareString(injectPath, groups), - varTemplate(groups, name), - '', - ].join('\n'); - } - ); - if (isBuild) cache.set(path, result); - } - return `${result}${hmr(isBuild)}`; - }, - }; -}; -export default globTransform; diff --git a/build/vite/proxy.ts b/build/vite/proxy.ts index f01a0746..0cf1874f 100644 --- a/build/vite/proxy.ts +++ b/build/vite/proxy.ts @@ -1,16 +1,10 @@ +import type { ServerOptions } from 'http-proxy'; + type ProxyItem = [string, string]; type ProxyList = ProxyItem[]; -type ProxyTargetList = Record< - string, - { - target: string; - changeOrigin: boolean; - rewrite: (path: string) => any; - secure?: boolean; - } ->; +type ProxyTargetList = Record string }>; const httpsRE = /^https:\/\//; @@ -23,9 +17,11 @@ export function createProxy(list: ProxyList = []) { for (const [prefix, target] of list) { const isHttps = httpsRE.test(target); + // https://github.com/http-party/node-http-proxy#options ret[prefix] = { target: target, changeOrigin: true, + ws: true, rewrite: (path) => path.replace(new RegExp(`^${prefix}`), ''), // https is require secure=false ...(isHttps ? { secure: false } : {}), diff --git a/index.html b/index.html index 3763dd82..17117612 100644 --- a/index.html +++ b/index.html @@ -1,7 +1,6 @@ - <%= viteHtmlPluginOptions.hmScript %> @@ -10,9 +9,8 @@ content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0" /> - + <%= title %> - <%= viteHtmlPluginOptions.injectConfig %> @@ -137,15 +135,11 @@ - + - <%= viteHtmlPluginOptions.title %> + <%= title %> diff --git a/package.json b/package.json index d0769d9a..b60ba6eb 100644 --- a/package.json +++ b/package.json @@ -1,20 +1,17 @@ { "name": "vben-admin", - "version": "2.0.0-rc.15", + "version": "2.0.0-rc.16", "scripts": { "bootstrap": "yarn install", - "serve": "cross-env vite --mode=development", - "build": "cross-env vite build --mode=production && esno ./build/script/postBuild.ts", - "build:site": "cross-env SITE=true npm run build ", + "serve": "cross-env vite ", + "build": "cross-env vite build && esno ./build/script/postBuild.ts", "build:no-cache": "yarn clean:cache && npm run build", - "typecheck": "vuedx-typecheck .", "report": "cross-env REPORT=true npm run build ", "preview": "npm run build && esno ./build/script/preview.ts", "preview:dist": "esno ./build/script/preview.ts", - "log": "esno ./build/script/changelog.ts", + "log": "conventional-changelog -p custom-config -i CHANGELOG.md -s -r 0", "clean:cache": "rimraf node_modules/.cache/ && rimraf node_modules/.vite_opt_cache", "clean:lib": "npx rimraf node_modules", - "ls-lint": "npx ls-lint", "lint:eslint": "eslint --fix --ext \"src/**/*.{vue,less,css,scss}\"", "lint:prettier": "prettier --write --loglevel warn \"src/**/*.{js,json,tsx,css,less,scss,vue,html,md}\"", "lint:stylelint": "stylelint --fix \"**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/", @@ -22,7 +19,7 @@ }, "dependencies": { "@iconify/iconify": "^2.0.0-rc.5", - "@vueuse/core": "^4.0.2", + "@vueuse/core": "^4.0.5", "ant-design-vue": "^2.0.0-rc.8", "apexcharts": "^3.23.1", "axios": "^0.21.1", @@ -34,26 +31,27 @@ "nprogress": "^0.2.0", "path-to-regexp": "^6.2.0", "qrcode": "^1.4.4", - "sortablejs": "^1.12.0", + "sortablejs": "^1.13.0", "vditor": "^3.7.5", "vue": "^3.0.5", - "vue-i18n": "9.0.0-beta.14", + "vue-i18n": "^9.0.0-rc.1", "vue-router": "^4.0.2", "vue-types": "^3.0.1", "vuex": "^4.0.0-rc.2", "vuex-module-decorators": "^1.0.1", - "xlsx": "^0.16.9", "zxcvbn": "^4.4.2" }, "devDependencies": { + "@babel/core": "^7.12.10", "@commitlint/cli": "^11.0.0", "@commitlint/config-conventional": "^11.0.0", - "@iconify/json": "^1.1.282", + "@iconify/json": "^1.1.283", "@ls-lint/ls-lint": "^1.9.2", - "@purge-icons/generated": "^0.4.1", + "@purge-icons/generated": "^0.5.0", "@types/echarts": "^4.9.3", "@types/fs-extra": "^9.0.6", "@types/globrex": "^0.1.0", + "@types/http-proxy": "^1.17.4", "@types/koa-static": "^4.0.1", "@types/lodash-es": "^4.17.4", "@types/mockjs": "^1.0.3", @@ -65,10 +63,13 @@ "@types/zxcvbn": "^4.4.0", "@typescript-eslint/eslint-plugin": "^4.12.0", "@typescript-eslint/parser": "^4.12.0", + "@vitejs/plugin-legacy": "^1.1.0", + "@vitejs/plugin-vue": "^1.0.4", + "@vitejs/plugin-vue-jsx": "^1.0.1", "@vue/compiler-sfc": "^3.0.5", "@vuedx/typecheck": "^0.4.1", "@vuedx/typescript-plugin-vue": "^0.4.1", - "autoprefixer": "^9.8.6", + "autoprefixer": "^10.2.1", "commitizen": "^4.2.2", "conventional-changelog-cli": "^2.1.1", "conventional-changelog-custom-config": "^0.3.1", @@ -82,14 +83,15 @@ "esno": "^0.4.0", "fs-extra": "^9.0.1", "globrex": "^0.1.2", - "husky": "^4.3.6", + "husky": "^4.3.7", "koa-static": "^5.0.0", "less": "^4.0.0", "lint-staged": "^10.5.3", "portfinder": "^1.0.28", - "postcss-import": "^12.0.1", + "postcss-import": "^14.0.0", "prettier": "^2.2.1", "rimraf": "^3.0.2", + "rollup-plugin-gzip": "^2.5.0", "rollup-plugin-visualizer": "^4.1.2", "stylelint": "^13.8.0", "stylelint-config-prettier": "^8.0.2", @@ -98,11 +100,12 @@ "tasksfile": "^5.1.1", "ts-node": "^9.1.0", "typescript": "^4.1.3", - "vite": "^1.0.0-rc.13", - "vite-plugin-html": "^1.0.0-beta.2", - "vite-plugin-mock": "^1.0.9", - "vite-plugin-purge-icons": "^0.4.5", - "vite-plugin-pwa": "^0.2.1", + "vite": "^2.0.0-beta.15", + "vite-plugin-html": "^2.0.0-beta.5", + "vite-plugin-import-context": "^1.0.0-rc.1", + "vite-plugin-mock": "^2.0.0-beta.1", + "vite-plugin-purge-icons": "^0.5.0", + "vite-plugin-pwa": "^0.3.2", "vue-eslint-parser": "^7.3.0", "yargs": "^16.2.0" }, diff --git a/src/App.vue b/src/App.vue index c0c46bf9..b4bd0ccc 100644 --- a/src/App.vue +++ b/src/App.vue @@ -13,7 +13,7 @@ import { initAppConfigStore } from '/@/setup/App'; import { useLockPage } from '/@/hooks/web/useLockPage'; - import { useLocale } from '/@/hooks/web/useLocale'; + import { useLocale } from '/@/locales/useLocale'; import { AppProvider } from '/@/components/Application'; @@ -21,6 +21,9 @@ name: 'App', components: { ConfigProvider, AppProvider }, setup() { + const { antConfigLocale, setLocale } = useLocale(); + setLocale(); + // Initialize vuex internal system configuration initAppConfigStore(); @@ -28,7 +31,6 @@ const lockEvent = useLockPage(); // support Multi-language - const { antConfigLocale } = useLocale(); return { antConfigLocale, diff --git a/src/components/Application/src/AppLocalePicker.vue b/src/components/Application/src/AppLocalePicker.vue index 3a592ffb..986b709c 100644 --- a/src/components/Application/src/AppLocalePicker.vue +++ b/src/components/Application/src/AppLocalePicker.vue @@ -22,7 +22,7 @@ import { Dropdown, DropMenu } from '/@/components/Dropdown'; import { GlobalOutlined } from '@ant-design/icons-vue'; - import { useLocale } from '/@/hooks/web/useLocale'; + import { useLocale } from '/@/locales/useLocale'; import { useLocaleSetting } from '/@/hooks/setting/useLocaleSetting'; import { LocaleType } from '/@/locales/types'; @@ -75,7 +75,6 @@