105 changed files with 950 additions and 2166 deletions
@ -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(); |
|
||||
@ -1,12 +0,0 @@ |
|||||
// Baidu statistics code for site deployment
|
|
||||
// Only open in build:site
|
|
||||
export const hmScript = `<script>
|
|
||||
var _hmt = _hmt || []; |
|
||||
(function() { |
|
||||
var hm = document.createElement("script"); |
|
||||
hm.src = "https://hm.baidu.com/hm.js?384d6046e02f6ac4ea075357bd0e9b43"; |
|
||||
var s = document.getElementsByTagName("script")[0]; |
|
||||
s.parentNode.insertBefore(hm, s); |
|
||||
})(); |
|
||||
</script> |
|
||||
`;
|
|
||||
@ -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 []; |
||||
|
} |
||||
@ -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<Buffer> { |
|
||||
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); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -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<string | Buffer>; |
|
||||
|
|
||||
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<any>; |
|
||||
|
|
||||
// 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<any>; |
|
||||
}, |
|
||||
}; |
|
||||
|
|
||||
return plugin; |
|
||||
} |
|
||||
|
|
||||
export default gzipPlugin; |
|
||||
@ -1,56 +0,0 @@ |
|||||
import type { ZlibOptions } from 'zlib'; |
|
||||
|
|
||||
export type StringMappingOption = (originalString: string) => string; |
|
||||
export type CustomCompressionOption = ( |
|
||||
content: string | Buffer |
|
||||
) => string | Buffer | Promise<string | Buffer>; |
|
||||
|
|
||||
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; |
|
||||
} |
|
||||
@ -1,37 +1,32 @@ |
|||||
import type { Plugin } from 'vite'; |
import type { Plugin } from 'vite'; |
||||
import ViteHtmlPlugin from 'vite-plugin-html'; |
import html from 'vite-plugin-html'; |
||||
import { isProdFn, isSiteMode, ViteEnv } from '../../utils'; |
import { ViteEnv } from '../../utils'; |
||||
|
|
||||
import { hmScript } from '../hm'; |
|
||||
// @ts-ignore
|
// @ts-ignore
|
||||
import pkg from '../../../package.json'; |
import pkg from '../../../package.json'; |
||||
import { GLOB_CONFIG_FILE_NAME } from '../../constant'; |
import { GLOB_CONFIG_FILE_NAME } from '../../constant'; |
||||
|
|
||||
export function setupHtmlPlugin( |
export function configHtmlPlugin(env: ViteEnv, isBuild: boolean) { |
||||
plugins: Plugin[], |
|
||||
env: ViteEnv, |
|
||||
mode: 'development' | 'production' |
|
||||
) { |
|
||||
const { VITE_GLOB_APP_TITLE, VITE_PUBLIC_PATH } = env; |
const { VITE_GLOB_APP_TITLE, VITE_PUBLIC_PATH } = env; |
||||
|
const htmlPlugin: Plugin[] = html({ |
||||
const htmlPlugin = ViteHtmlPlugin({ |
minify: isBuild, |
||||
// html title
|
inject: { |
||||
title: VITE_GLOB_APP_TITLE, |
injectData: { |
||||
minify: isProdFn(mode), |
title: VITE_GLOB_APP_TITLE, |
||||
options: { |
}, |
||||
publicPath: VITE_PUBLIC_PATH, |
tags: isBuild |
||||
// Package and insert additional configuration files
|
? [ |
||||
injectConfig: isProdFn(mode) |
{ |
||||
? `<script src='${VITE_PUBLIC_PATH || './'}${GLOB_CONFIG_FILE_NAME}?v=${ |
tag: 'script', |
||||
pkg.version |
attrs: { |
||||
}-${new Date().getTime()}'></script>` |
src: `${VITE_PUBLIC_PATH || './'}${GLOB_CONFIG_FILE_NAME}?v=${ |
||||
: '', |
pkg.version |
||||
// Insert Baidu statistics code
|
}-${new Date().getTime()}`,
|
||||
hmScript: isSiteMode() ? hmScript : '', |
}, |
||||
title: VITE_GLOB_APP_TITLE, |
}, |
||||
|
] |
||||
|
: [], |
||||
}, |
}, |
||||
}); |
}); |
||||
|
return htmlPlugin; |
||||
plugins.push(htmlPlugin); |
|
||||
return plugins; |
|
||||
} |
} |
||||
|
|||||
@ -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; |
||||
|
} |
||||
@ -1,47 +1,44 @@ |
|||||
import type { Plugin as VitePlugin } from 'vite'; |
import type { Plugin } from 'vite'; |
||||
import type { Plugin as rollupPlugin } from 'rollup'; |
|
||||
|
|
||||
import PurgeIcons from 'vite-plugin-purge-icons'; |
import PurgeIcons from 'vite-plugin-purge-icons'; |
||||
|
|
||||
import visualizer from 'rollup-plugin-visualizer'; |
import visualizer from 'rollup-plugin-visualizer'; |
||||
import gzipPlugin from './gzip/index'; |
|
||||
|
|
||||
// @ts-ignore
|
// @ts-ignore
|
||||
import pkg from '../../../package.json'; |
import pkg from '../../../package.json'; |
||||
import { isSiteMode, ViteEnv, isReportMode, isBuildGzip } from '../../utils'; |
import { ViteEnv, isReportMode } from '../../utils'; |
||||
import { setupHtmlPlugin } from './html'; |
import { configHtmlPlugin } from './html'; |
||||
import { setupPwaPlugin } from './pwa'; |
import { configPwaConfig } from './pwa'; |
||||
import { setupMockPlugin } from './mock'; |
import { configMockPlugin } from './mock'; |
||||
|
import { configDynamicImport } from './importContext'; |
||||
|
import { configGzipPlugin } from './gzip'; |
||||
|
|
||||
// gen vite plugins
|
// gen vite plugins
|
||||
export function createVitePlugins(viteEnv: ViteEnv, mode: 'development' | 'production') { |
export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean, mode: string) { |
||||
const vitePlugins: VitePlugin[] = []; |
const vitePlugins: (Plugin | Plugin[])[] = []; |
||||
|
|
||||
// vite-plugin-html
|
// vite-plugin-html
|
||||
setupHtmlPlugin(vitePlugins, viteEnv, mode); |
vitePlugins.push(configHtmlPlugin(viteEnv, isBuild)); |
||||
|
|
||||
// vite-plugin-pwa
|
// vite-plugin-pwa
|
||||
setupPwaPlugin(vitePlugins, viteEnv, mode); |
vitePlugins.push(configPwaConfig(viteEnv, isBuild)); |
||||
|
|
||||
// vite-plugin-mock
|
// vite-plugin-mock
|
||||
setupMockPlugin(vitePlugins, viteEnv, mode); |
vitePlugins.push(configMockPlugin(viteEnv, isBuild)); |
||||
|
|
||||
|
// vite-plugin-import-context
|
||||
|
vitePlugins.push(configDynamicImport(viteEnv)); |
||||
|
|
||||
// vite-plugin-purge-icons
|
// vite-plugin-purge-icons
|
||||
vitePlugins.push(PurgeIcons()); |
vitePlugins.push(PurgeIcons()); |
||||
|
|
||||
return vitePlugins; |
// rollup-plugin-gzip
|
||||
} |
vitePlugins.push(configGzipPlugin(isBuild)); |
||||
|
|
||||
// gen rollup plugins
|
|
||||
export function createRollupPlugin() { |
|
||||
const rollupPlugins: rollupPlugin[] = []; |
|
||||
|
|
||||
|
// rollup-plugin-visualizer
|
||||
if (isReportMode()) { |
if (isReportMode()) { |
||||
// rollup-plugin-visualizer
|
vitePlugins.push(visualizer({ filename: './build/.cache/stats.html', open: true }) as Plugin); |
||||
rollupPlugins.push(visualizer({ filename: './build/.cache/stats.html', open: true }) as Plugin); |
|
||||
} |
|
||||
if (isBuildGzip() || isSiteMode()) { |
|
||||
// rollup-plugin-gizp
|
|
||||
rollupPlugins.push(gzipPlugin()); |
|
||||
} |
} |
||||
|
|
||||
return rollupPlugins; |
return vitePlugins; |
||||
} |
} |
||||
|
|||||
@ -1,24 +1,19 @@ |
|||||
import { createMockServer } from 'vite-plugin-mock'; |
import { viteMockServe } from 'vite-plugin-mock'; |
||||
import type { Plugin } from 'vite'; |
import { ViteEnv } from '../../utils'; |
||||
import { isDevFn, ViteEnv } from '../../utils'; |
|
||||
|
|
||||
export function setupMockPlugin( |
export function configMockPlugin(env: ViteEnv, isBuild: boolean) { |
||||
plugins: Plugin[], |
|
||||
env: ViteEnv, |
|
||||
mode: 'development' | 'production' |
|
||||
) { |
|
||||
const { VITE_USE_MOCK } = env; |
const { VITE_USE_MOCK } = env; |
||||
|
|
||||
const useMock = isDevFn(mode) && VITE_USE_MOCK; |
const useMock = !isBuild && VITE_USE_MOCK; |
||||
|
|
||||
if (useMock) { |
if (useMock) { |
||||
const mockPlugin = createMockServer({ |
const mockPlugin = viteMockServe({ |
||||
ignore: /^\_/, |
ignore: /^\_/, |
||||
mockPath: 'mock', |
mockPath: 'mock', |
||||
showTime: true, |
showTime: true, |
||||
localEnabled: useMock, |
localEnabled: useMock, |
||||
}); |
}); |
||||
plugins.push(mockPlugin); |
return mockPlugin; |
||||
} |
} |
||||
return plugins; |
return []; |
||||
} |
} |
||||
|
|||||
@ -1,35 +1,31 @@ |
|||||
import { VitePWA } from 'vite-plugin-pwa'; |
import { VitePWA } from 'vite-plugin-pwa'; |
||||
import type { Plugin } from 'vite'; |
|
||||
import { ViteEnv } from '../../utils'; |
import { ViteEnv } from '../../utils'; |
||||
|
|
||||
export function setupPwaPlugin( |
export function configPwaConfig(env: ViteEnv, isBulid: boolean) { |
||||
plugins: Plugin[], |
const { VITE_USE_PWA, VITE_GLOB_APP_TITLE, VITE_GLOB_APP_SHORT_NAME } = env; |
||||
env: ViteEnv, |
|
||||
// @ts-ignore
|
|
||||
mode: 'development' | 'production' |
|
||||
) { |
|
||||
const { VITE_USE_PWA } = env; |
|
||||
|
|
||||
const pwaPlugin = VitePWA({ |
if (VITE_USE_PWA && isBulid) { |
||||
manifest: { |
// vite-plugin-pwa
|
||||
name: 'Vben Admin', |
const pwaPlugin = VitePWA({ |
||||
short_name: 'vben_admin', |
manifest: { |
||||
icons: [ |
name: VITE_GLOB_APP_TITLE, |
||||
{ |
short_name: VITE_GLOB_APP_SHORT_NAME, |
||||
src: './resource/img/pwa-192x192.png', |
icons: [ |
||||
sizes: '192x192', |
{ |
||||
type: 'image/png', |
src: './resource/img/pwa-192x192.png', |
||||
}, |
sizes: '192x192', |
||||
{ |
type: 'image/png', |
||||
src: './resource/img/pwa-512x512.png', |
}, |
||||
sizes: '512x512', |
{ |
||||
type: 'image/png', |
src: './resource/img/pwa-512x512.png', |
||||
}, |
sizes: '512x512', |
||||
], |
type: 'image/png', |
||||
}, |
}, |
||||
}); |
], |
||||
if (VITE_USE_PWA) { |
}, |
||||
plugins.push(pwaPlugin); |
}); |
||||
|
return pwaPlugin; |
||||
} |
} |
||||
return plugins; |
return []; |
||||
} |
} |
||||
|
|||||
@ -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; |
|
||||
@ -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<string, string>; |
|
||||
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<string, object | string> = {}; |
|
||||
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<any, any> = {}; |
|
||||
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<string>[] = []; |
|
||||
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; |
|
||||
@ -1,4 +1,8 @@ |
|||||
export { createImgPreview } from './src/functional'; |
// export { createImgPreview } from './src/functional';
|
||||
|
|
||||
import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent'; |
export const createImgPreview = () => {}; |
||||
export const ImagePreview = createAsyncComponent(() => import('./src/index.vue')); |
|
||||
|
// import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
|
||||
|
// export const ImagePreview = createAsyncComponent(() => import('./src/index.vue'));
|
||||
|
|
||||
|
export { default as ImagePreview } from './src/index.vue'; |
||||
|
|||||
@ -1,72 +0,0 @@ |
|||||
import type { DefaultContext } from './transition'; |
|
||||
|
|
||||
import { computed, defineComponent, unref, Transition, KeepAlive } from 'vue'; |
|
||||
import { RouterView } from 'vue-router'; |
|
||||
|
|
||||
import FrameLayout from '/@/layouts/iframe/index.vue'; |
|
||||
|
|
||||
import { useRootSetting } from '/@/hooks/setting/useRootSetting'; |
|
||||
|
|
||||
import { useTransitionSetting } from '/@/hooks/setting/useTransitionSetting'; |
|
||||
import { useCache } from './useCache'; |
|
||||
import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting'; |
|
||||
import { getTransitionName } from './transition'; |
|
||||
|
|
||||
export default defineComponent({ |
|
||||
name: 'PageLayout', |
|
||||
setup() { |
|
||||
const { getCaches } = useCache(true); |
|
||||
const { getShowMultipleTab } = useMultipleTabSetting(); |
|
||||
|
|
||||
const { getOpenKeepAlive, getCanEmbedIFramePage } = useRootSetting(); |
|
||||
|
|
||||
const { getBasicTransition, getEnableTransition } = useTransitionSetting(); |
|
||||
|
|
||||
const openCache = computed(() => unref(getOpenKeepAlive) && unref(getShowMultipleTab)); |
|
||||
|
|
||||
return () => { |
|
||||
return ( |
|
||||
<> |
|
||||
<RouterView> |
|
||||
{{ |
|
||||
default: ({ Component, route }: DefaultContext) => { |
|
||||
// No longer show animations that are already in the tab
|
|
||||
const cacheTabs = unref(getCaches); |
|
||||
|
|
||||
const name = getTransitionName({ |
|
||||
route, |
|
||||
openCache: unref(openCache), |
|
||||
enableTransition: unref(getEnableTransition), |
|
||||
cacheTabs, |
|
||||
def: unref(getBasicTransition), |
|
||||
}); |
|
||||
|
|
||||
// When the child element is the parentView, adding the key will cause the component to be executed multiple times. When it is not parentView, you need to add a key, because it needs to be compatible with the same route carrying different parameters
|
|
||||
const isParentView = Component?.type.parentView; |
|
||||
const componentKey = isParentView ? {} : { key: route.fullPath }; |
|
||||
|
|
||||
const renderComp = () => <Component {...componentKey} />; |
|
||||
|
|
||||
const PageContent = unref(openCache) ? ( |
|
||||
<KeepAlive include={cacheTabs}>{renderComp()}</KeepAlive> |
|
||||
) : ( |
|
||||
renderComp() |
|
||||
); |
|
||||
|
|
||||
if (!unref(getEnableTransition)) { |
|
||||
return PageContent; |
|
||||
} |
|
||||
return ( |
|
||||
<Transition name={name} mode="out-in" appear={true}> |
|
||||
{() => PageContent} |
|
||||
</Transition> |
|
||||
); |
|
||||
}, |
|
||||
}} |
|
||||
</RouterView> |
|
||||
{unref(getCanEmbedIFramePage) && <FrameLayout />} |
|
||||
</> |
|
||||
); |
|
||||
}; |
|
||||
}, |
|
||||
}); |
|
||||
@ -0,0 +1,72 @@ |
|||||
|
<template> |
||||
|
<div> |
||||
|
<router-view> |
||||
|
<template v-slot="{ Component, route }"> |
||||
|
<transition |
||||
|
:name=" |
||||
|
getTransitionName({ |
||||
|
route, |
||||
|
openCache, |
||||
|
enableTransition: getEnableTransition, |
||||
|
cacheTabs: getCaches, |
||||
|
def: getBasicTransition, |
||||
|
}) |
||||
|
" |
||||
|
mode="out-in" |
||||
|
appear |
||||
|
> |
||||
|
<keep-alive v-if="openCache" :include="getCaches"> |
||||
|
<component :is="Component" v-bind="getKey(Component, route)" /> |
||||
|
</keep-alive> |
||||
|
<component v-else :is="Component" v-bind="getKey(Component, route)" /> |
||||
|
</transition> |
||||
|
</template> |
||||
|
</router-view> |
||||
|
<FrameLayout v-if="getCanEmbedIFramePage" /> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script lang="ts"> |
||||
|
import type { FunctionalComponent } from 'vue'; |
||||
|
import type { RouteLocation } from 'vue-router'; |
||||
|
|
||||
|
import { computed, defineComponent, unref } from 'vue'; |
||||
|
|
||||
|
import FrameLayout from '/@/layouts/iframe/index.vue'; |
||||
|
|
||||
|
import { useRootSetting } from '/@/hooks/setting/useRootSetting'; |
||||
|
|
||||
|
import { useTransitionSetting } from '/@/hooks/setting/useTransitionSetting'; |
||||
|
import { useCache } from './useCache'; |
||||
|
import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting'; |
||||
|
import { getTransitionName } from './transition'; |
||||
|
|
||||
|
export default defineComponent({ |
||||
|
name: 'PageLayout', |
||||
|
components: { FrameLayout }, |
||||
|
setup() { |
||||
|
const { getCaches } = useCache(true); |
||||
|
const { getShowMultipleTab } = useMultipleTabSetting(); |
||||
|
|
||||
|
const { getOpenKeepAlive, getCanEmbedIFramePage } = useRootSetting(); |
||||
|
|
||||
|
const { getBasicTransition, getEnableTransition } = useTransitionSetting(); |
||||
|
|
||||
|
const openCache = computed(() => unref(getOpenKeepAlive) && unref(getShowMultipleTab)); |
||||
|
|
||||
|
function getKey(component: FunctionalComponent & { type: Indexable }, route: RouteLocation) { |
||||
|
return !!component?.type.parentView ? {} : { key: route.fullPath }; |
||||
|
} |
||||
|
|
||||
|
return { |
||||
|
getTransitionName, |
||||
|
openCache, |
||||
|
getEnableTransition, |
||||
|
getBasicTransition, |
||||
|
getCaches, |
||||
|
getCanEmbedIFramePage, |
||||
|
getKey, |
||||
|
}; |
||||
|
}, |
||||
|
}); |
||||
|
</script> |
||||
@ -0,0 +1,4 @@ |
|||||
|
import { genMessage } from './helper'; |
||||
|
import modules from 'glob:./lang/**/*.ts'; |
||||
|
|
||||
|
export default genMessage(modules); |
||||
@ -0,0 +1,20 @@ |
|||||
|
import { set } from 'lodash-es'; |
||||
|
|
||||
|
export function genMessage(langs: Record<string, Record<string, any>>, prefix = 'lang') { |
||||
|
const obj: Recordable = {}; |
||||
|
|
||||
|
Object.keys(langs).forEach((key) => { |
||||
|
const mod = langs[key].default; |
||||
|
let k = key.replace(`./${prefix}/`, '').replace(/^\.\//, ''); |
||||
|
const lastIndex = k.lastIndexOf('.'); |
||||
|
k = k.substring(0, lastIndex); |
||||
|
const keyList = k.split('/'); |
||||
|
const lang = keyList.shift(); |
||||
|
const objKey = keyList.join('.'); |
||||
|
if (lang) { |
||||
|
set(obj, lang, obj[lang] || {}); |
||||
|
set(obj[lang], objKey, mod); |
||||
|
} |
||||
|
}); |
||||
|
return obj; |
||||
|
} |
||||
@ -1,34 +1,31 @@ |
|||||
import { App } from 'vue'; |
import type { App } from 'vue'; |
||||
import type { I18n, I18nOptions } from 'vue-i18n'; |
import type { I18n, I18nOptions } from 'vue-i18n'; |
||||
|
|
||||
import { createI18n } from 'vue-i18n'; |
import { createI18n } from 'vue-i18n'; |
||||
import localeMessages from '/@/locales'; |
|
||||
import { useLocale } from '/@/hooks/web/useLocale'; |
import 'moment/dist/locale/zh-cn'; |
||||
|
|
||||
import projectSetting from '/@/settings/projectSetting'; |
import projectSetting from '/@/settings/projectSetting'; |
||||
const { setupLocale } = useLocale(); |
|
||||
|
import messages from './getMessage'; |
||||
|
|
||||
const { lang, availableLocales, fallback } = projectSetting?.locale; |
const { lang, availableLocales, fallback } = projectSetting?.locale; |
||||
|
|
||||
const localeData: I18nOptions = { |
const localeData: I18nOptions = { |
||||
legacy: false, |
legacy: false, |
||||
locale: lang, |
locale: lang, |
||||
fallbackLocale: fallback, |
fallbackLocale: fallback, |
||||
messages: localeMessages, |
messages, |
||||
availableLocales: availableLocales, |
availableLocales: availableLocales, |
||||
sync: true, //If you don’t want to inherit locale from global scope, you need to set sync of i18n component option to false.
|
sync: true, //If you don’t want to inherit locale from global scope, you need to set sync of i18n component option to false.
|
||||
silentTranslationWarn: true, // true - warning off
|
silentTranslationWarn: true, // true - warning off
|
||||
missingWarn: false, |
missingWarn: false, |
||||
silentFallbackWarn: true, |
silentFallbackWarn: true, |
||||
}; |
}; |
||||
|
export let i18n: I18n; |
||||
let i18n: I18n; |
|
||||
|
|
||||
// setup i18n instance with glob
|
// setup i18n instance with glob
|
||||
export function setupI18n(app: App) { |
export function setupI18n(app: App) { |
||||
i18n = createI18n(localeData) as I18n; |
i18n = createI18n(localeData) as I18n; |
||||
setupLocale(); |
|
||||
app.use(i18n); |
app.use(i18n); |
||||
} |
} |
||||
|
|
||||
export function getI18n(): I18n { |
|
||||
return i18n; |
|
||||
} |
|
||||
@ -1,5 +0,0 @@ |
|||||
// The content here is just for type approval. The actual file content is overwritten by transform
|
|
||||
// For specific coverage, see build/vite/plugin/transform/dynamic-import/index.ts
|
|
||||
export default function (name: string) { |
|
||||
return name as any; |
|
||||
} |
|
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue