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 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) |
|||
? `<script src='${VITE_PUBLIC_PATH || './'}${GLOB_CONFIG_FILE_NAME}?v=${ |
|||
pkg.version |
|||
}-${new Date().getTime()}'></script>` |
|||
: '', |
|||
// 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; |
|||
} |
|||
|
|||
@ -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 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()); |
|||
|
|||
return vitePlugins; |
|||
} |
|||
|
|||
// gen rollup plugins
|
|||
export function createRollupPlugin() { |
|||
const rollupPlugins: rollupPlugin[] = []; |
|||
// rollup-plugin-gzip
|
|||
vitePlugins.push(configGzipPlugin(isBuild)); |
|||
|
|||
// rollup-plugin-visualizer
|
|||
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()); |
|||
vitePlugins.push(visualizer({ filename: './build/.cache/stats.html', open: true }) as Plugin); |
|||
} |
|||
|
|||
return rollupPlugins; |
|||
return vitePlugins; |
|||
} |
|||
|
|||
@ -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 []; |
|||
} |
|||
|
|||
@ -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 []; |
|||
} |
|||
|
|||
@ -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 ImagePreview = createAsyncComponent(() => import('./src/index.vue')); |
|||
export const createImgPreview = () => {}; |
|||
|
|||
// 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 { 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'; |
|||
const { setupLocale } = useLocale(); |
|||
|
|||
import messages from './getMessage'; |
|||
|
|||
const { lang, availableLocales, fallback } = projectSetting?.locale; |
|||
|
|||
const localeData: I18nOptions = { |
|||
legacy: false, |
|||
locale: lang, |
|||
fallbackLocale: fallback, |
|||
messages: localeMessages, |
|||
messages, |
|||
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.
|
|||
silentTranslationWarn: true, // true - warning off
|
|||
missingWarn: false, |
|||
silentFallbackWarn: true, |
|||
}; |
|||
|
|||
let i18n: I18n; |
|||
export let i18n: I18n; |
|||
|
|||
// setup i18n instance with glob
|
|||
export function setupI18n(app: App) { |
|||
i18n = createI18n(localeData) as I18n; |
|||
setupLocale(); |
|||
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