diff --git a/scripts/optimize-media.mjs b/scripts/optimize-media.mjs index 894a6f036..77cad714f 100644 --- a/scripts/optimize-media.mjs +++ b/scripts/optimize-media.mjs @@ -21,14 +21,14 @@ const MP4_AUDIO_BITRATE = process.env.MP4_AUDIO_BITRATE || '128k' const MIN_SAVINGS_PCT = Number(process.env.MEDIA_MIN_SAVINGS_PCT || '5') const SKIP_SMALL_KB = Number(process.env.MEDIA_SKIP_SMALL_KB || '256') -function bytesToMiB(bytes) { +function bytesToMiB (bytes) { return (bytes / 1024 / 1024).toFixed(2) } -function runFfmpeg(args) { +function runFfmpeg (args) { const result = spawnSync('ffmpeg', args, { stdio: 'inherit', - windowsHide: true, + windowsHide: true }) if (result.error) { throw result.error @@ -38,7 +38,7 @@ function runFfmpeg(args) { } } -function isFileExists(filePath) { +function isFileExists (filePath) { try { fsSync.accessSync(filePath) return true @@ -47,29 +47,29 @@ function isFileExists(filePath) { } } -async function ensureDir(dirPath) { +async function ensureDir (dirPath) { await fs.mkdir(dirPath, { recursive: true }) } -async function cleanDir(dirPath) { +async function cleanDir (dirPath) { await fs.rm(dirPath, { recursive: true, force: true }) await ensureDir(dirPath) } -async function* walkFiles(dirPath) { +async function * walkFiles (dirPath) { if (!isFileExists(dirPath)) return const entries = await fs.readdir(dirPath, { withFileTypes: true }) for (const entry of entries) { const fullPath = path.join(dirPath, entry.name) if (entry.isDirectory()) { - yield* walkFiles(fullPath) + yield * walkFiles(fullPath) } else if (entry.isFile()) { yield fullPath } } } -async function optimizeOne({ inputPath, inputBaseDir, outputBaseDir }) { +async function optimizeOne ({ inputPath, inputBaseDir, outputBaseDir }) { const relPath = path.relative(inputBaseDir, inputPath) const outputPath = path.join(outputBaseDir, relPath) @@ -111,7 +111,7 @@ async function optimizeOne({ inputPath, inputBaseDir, outputBaseDir }) { MP3_BITRATE, '-id3v2_version', '3', - tmpPath, + tmpPath ]) } else { // Re-encode MP4 (H.264 + AAC) and enable faststart @@ -133,7 +133,7 @@ async function optimizeOne({ inputPath, inputBaseDir, outputBaseDir }) { MP4_AUDIO_BITRATE, '-movflags', '+faststart', - tmpPath, + tmpPath ]) } @@ -151,12 +151,12 @@ async function optimizeOne({ inputPath, inputBaseDir, outputBaseDir }) { return { changed: true, reason: 'optimized', before: stat.size, after: outStat.size, outputPath } } -function checkFfmpegAvailable() { +function checkFfmpegAvailable () { const result = spawnSync('ffmpeg', ['-version'], { stdio: 'ignore', windowsHide: true }) return result.status === 0 } -async function main() { +async function main () { if (!checkFfmpegAvailable()) { console.error('[optimize:media] ffmpeg not found in PATH.') console.error('Install ffmpeg and ensure `ffmpeg` is available in your terminal, then re-run.') @@ -167,21 +167,15 @@ async function main() { const targets = [ { inputDir: SRC_AUDIO_DIR, outputDir: OUT_AUDIO_DIR }, { inputDir: SRC_VIDEO_DIR, outputDir: OUT_VIDEO_DIR }, - { inputDir: SRC_PUBLIC_AUDIO_DIR, outputDir: OUT_PUBLIC_AUDIO_DIR }, + { inputDir: SRC_PUBLIC_AUDIO_DIR, outputDir: OUT_PUBLIC_AUDIO_DIR } ] let changedCount = 0 let scannedCount = 0 let savedBytes = 0 - console.log( - '[optimize:media] scanning:', - targets.map((t) => path.relative(projectRoot, t.inputDir)).join(', ') - ) - console.log( - '[optimize:media] output:', - targets.map((t) => path.relative(projectRoot, t.outputDir)).join(', ') - ) + console.log('[optimize:media] scanning:', targets.map((t) => path.relative(projectRoot, t.inputDir)).join(', ')) + console.log('[optimize:media] output:', targets.map((t) => path.relative(projectRoot, t.outputDir)).join(', ')) console.log( `[optimize:media] settings: MP3_BITRATE=${MP3_BITRATE}, MP4_CRF=${MP4_CRF}, MP4_PRESET=${MP4_PRESET}, MIN_SAVINGS_PCT=${MIN_SAVINGS_PCT}%, SKIP_SMALL_KB=${SKIP_SMALL_KB}KB` ) @@ -194,7 +188,7 @@ async function main() { const result = await optimizeOne({ inputPath, inputBaseDir: target.inputDir, - outputBaseDir: target.outputDir, + outputBaseDir: target.outputDir }) if (result.changed) { changedCount += 1 diff --git a/webpack/webpack.common.js b/webpack/webpack.common.js index 9a94e346a..5c215e490 100644 --- a/webpack/webpack.common.js +++ b/webpack/webpack.common.js @@ -38,7 +38,7 @@ switch (process.env.BUILD_GOAL) { const config = { entry: { - app: `${paths.src}/index.tsx`, + app: `${paths.src}/index.tsx` }, output: { path: paths.build, @@ -48,7 +48,7 @@ const config = { // library: '', // libraryTarget: 'umd', // chunkLoadingGlobal: '', - clean: true, + clean: true // contentBase: path.join(__dirname, "public"), // 配置额外的静态文件内容的访问路径 }, resolve: { @@ -67,16 +67,16 @@ const config = { '@pages': path.resolve('./src/pages'), '@routers': path.resolve('./src/routers'), '@utils': path.resolve('./src/utils'), - '@theme': path.resolve('./src/theme'), + '@theme': path.resolve('./src/theme') }, - symlinks: false, + symlinks: false }, plugins: [ new Dotenv({ - path: path.resolve(__dirname, '..', dotEnv), + path: path.resolve(__dirname, '..', dotEnv) }), codeInspectorPlugin({ - bundler: 'webpack', + bundler: 'webpack' }), new HtmlWebpackPlugin({ title: isDev ? 'Pro React Dev' : 'Pro React Admin', @@ -100,8 +100,8 @@ const config = { minifyCSS: true, minifyJS: true, minifyURLs: true, - useShortDoctype: true, - }, + useShortDoctype: true + } }), new AntdDayjsWebpackPlugin(), new CaseSensitivePathsPlugin(), @@ -115,7 +115,7 @@ const config = { new NodePolyfillPlugin(), new WebpackBar(), new ForkTsCheckerWebpackPlugin({ - async: true, + async: true }), new ESLintWebpackPlugin({ // 指定检查文件的根目录 @@ -123,8 +123,8 @@ const config = { exclude: 'node_modules', // 默认值 cache: true, // 开启缓存 // 缓存目录 - cacheLocation: path.resolve(__dirname, '../node_modules/.cache/.eslintcache'), - }), + cacheLocation: path.resolve(__dirname, '../node_modules/.cache/.eslintcache') + }) ], module: { // 将缺失的导出提示成错误而不是警告 @@ -133,12 +133,12 @@ const config = { { test: /\.m?js$/, resolve: { - fullySpecified: false, - }, + fullySpecified: false + } }, { test: /\.css$/, - use: ['style-loader', 'css-loader', 'postcss-loader'], + use: ['style-loader', 'css-loader', 'postcss-loader'] }, { test: /\.less$/i, @@ -155,10 +155,10 @@ const config = { localIdentName: isDev ? '[path][name]__[local]--[hash:base64:5]' : '[local]--[hash:base64:5]', localIdentContext: paths.src, namedExport: false, - exportLocalsConvention: 'camelCase', + exportLocalsConvention: 'camelCase' }, - importLoaders: 2, - }, + importLoaders: 2 + } }, { loader: require.resolve('postcss-loader'), @@ -172,18 +172,18 @@ const config = { 'postcss-preset-env', { autoprefixer: { - flexbox: 'no-2009', + flexbox: 'no-2009' }, - stage: 3, - }, + stage: 3 + } ], - 'postcss-normalize', - ], + 'postcss-normalize' + ] }, - sourceMap: true, - }, - }, - ], + sourceMap: true + } + } + ] }, { test: /\.(js|jsx|ts|tsx)$/, @@ -193,17 +193,17 @@ const config = { loader: 'esbuild-loader', options: { // loader: 'tsx', - target: 'es2020', - }, + target: 'es2020' + } }, { loader: 'babel-loader', options: { presets: [['@babel/preset-env', { modules: false }], '@babel/preset-react'], - plugins: ['@babel/plugin-transform-object-rest-spread', '@babel/plugin-transform-runtime'], - }, - }, - ], + plugins: ['@babel/plugin-transform-object-rest-spread', '@babel/plugin-transform-runtime'] + } + } + ] }, { test: /\.(png|jpe?g|gif|webp|eot|ttf|woff|woff2|mp4|mp3|mkv|pdf)$/i, @@ -211,29 +211,29 @@ const config = { parser: { // Conditions for converting to base64 dataUrlCondition: { - maxSize: 25 * 1024, // 25kb - }, + maxSize: 25 * 1024 // 25kb + } }, generator: { - filename: 'images/[contenthash][ext][query]', - }, + filename: 'images/[contenthash][ext][query]' + } }, { test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, use: [ { - loader: 'babel-loader', + loader: 'babel-loader' }, { loader: '@svgr/webpack', options: { babel: false, - icon: true, - }, - }, - ], - }, - ], + icon: true + } + } + ] + } + ] }, stats: { ...(isAnalyze @@ -250,7 +250,7 @@ const config = { dependentModules: true, reasons: true, ids: true, - errorDetails: true, + errorDetails: true } : { all: false, @@ -258,15 +258,15 @@ const config = { warnings: true, errorDetails: true, moduleTrace: true, - excludeAssets: /node_modules/, - }), + excludeAssets: /node_modules/ + }) }, // 性能提示 performance: { hints: isDev ? false : 'warning', maxEntrypointSize: 512000, - maxAssetSize: 512000, - }, + maxAssetSize: 512000 + } } if (USE_ANALYZE) { diff --git a/webpack/webpack.dev.js b/webpack/webpack.dev.js index b78835667..0cd323fa0 100644 --- a/webpack/webpack.dev.js +++ b/webpack/webpack.dev.js @@ -19,16 +19,16 @@ const devWebpackConfig = merge(common, { progress: true, overlay: { errors: true, - warnings: false, - }, + warnings: false + } }, static: { - directory: path.join(__dirname, '../public'), + directory: path.join(__dirname, '../public') }, compress: true, // open: true, hot: true, - proxy: devProxy, + proxy: devProxy }, // watch: true, // watchOptions: { @@ -46,17 +46,17 @@ const devWebpackConfig = merge(common, { loader: 'babel-loader', options: { presets: [['@babel/preset-env', { modules: false }]], - plugins: [require.resolve('react-refresh/babel')].filter(Boolean), - }, - }, - ], - }, - ], + plugins: [require.resolve('react-refresh/babel')].filter(Boolean) + } + } + ] + } + ] }, plugins: [ new ReactRefreshWebpackPlugin({ - overlay: false, - }), + overlay: false + }) // new webpack.debug.ProfilingPlugin({ // outputPath: path.join(__dirname, 'profiling/profileEvents.json'), // }), @@ -71,20 +71,20 @@ const devWebpackConfig = merge(common, { test: /[\\/]node_modules[\\/]/, name: 'vendors', chunks: 'all', - priority: 10, - }, - }, + priority: 10 + } + } }, minimize: false, concatenateModules: false, - usedExports: false, - }, + usedExports: false + } }) module.exports = new Promise((resolve, reject) => { portfinder.getPort( { port: 8080, // 默认8080端口,若被占用,重复+1,直到找到可用端口或到stopPort才停止 - stopPort: 65535, // maximum port + stopPort: 65535 // maximum port }, (err, port) => { if (err) { diff --git a/webpack/webpack.prod.js b/webpack/webpack.prod.js index 4d7c140ab..d6291eebe 100644 --- a/webpack/webpack.prod.js +++ b/webpack/webpack.prod.js @@ -40,8 +40,8 @@ const prodWebpackConfig = merge(common, { ? { alias: { ...(hasOptimizedAudio ? { '@assets/audio': optimizedAudioDir } : {}), - ...(hasOptimizedVideo ? { '@assets/video': optimizedVideoDir } : {}), - }, + ...(hasOptimizedVideo ? { '@assets/video': optimizedVideoDir } : {}) + } } : undefined, // 使用文件缓存 @@ -52,44 +52,44 @@ const prodWebpackConfig = merge(common, { new MiniCssExtractPlugin({ filename: 'static/css/[name].[contenthash].css', chunkFilename: 'static/css/[name].[contenthash].css', - ignoreOrder: true, + ignoreOrder: true }), new PurgeCSSPlugin({ paths: glob.sync(`${path.join(__dirname, '../src')}/**/*`, { nodir: true }), only: ['bundle', 'vendor', 'dist'], safelist: { - standard: [/^ant-/], - }, + standard: [/^ant-/] + } }), new CompressionWebpackPlugin({ algorithm: 'gzip', test: /\.(js|css|html|svg)$/, threshold: 8192, - minRatio: 0.8, + minRatio: 0.8 }), new CopyWebpackPlugin({ patterns: [ { from: path.resolve(__dirname, '../public-optimized/audio'), to: path.resolve(__dirname, '../dist/audio'), - noErrorOnMissing: true, + noErrorOnMissing: true }, { from: path.resolve(__dirname, '../public'), to: path.resolve(__dirname, '../dist'), globOptions: { - ignore: ['**/index.html', '**/audio/**'], - }, - }, - ], - }), + ignore: ['**/index.html', '**/audio/**'] + } + } + ] + }) ], optimization: { minimize: true, minimizer: [ new CssMinimizerPlugin(), new EsbuildPlugin({ - target: 'es2015', + target: 'es2015' }), new HtmlMinimizerPlugin(), new ImageMinimizerPlugin({ @@ -103,11 +103,11 @@ const prodWebpackConfig = merge(common, { jpeg: { quality: 78, mozjpeg: true }, png: { compressionLevel: 9, palette: true }, webp: { quality: 80 }, - avif: { quality: 50 }, - }, - }, - }, - }), + avif: { quality: 50 } + } + } + } + }) ], splitChunks: { chunks: 'all', @@ -122,35 +122,35 @@ const prodWebpackConfig = merge(common, { minChunks: 1, priority: 10, enforce: true, - chunks: 'all', + chunks: 'all' }, react: { - test(module) { + test (module) { return module.resource && module.resource.includes('node_modules/react') }, chunks: 'initial', filename: 'react.[contenthash].js', priority: 1, maxInitialRequests: 2, - minChunks: 1, - }, + minChunks: 1 + } // commons: { // name: 'commons', // minChunks: 2, // chunks: 'all', // priority: 5, // }, - }, + } }, runtimeChunk: { - name: 'runtime', - }, + name: 'runtime' + } }, performance: { hints: 'warning', maxEntrypointSize: 800000, - maxAssetSize: 400000, - }, + maxAssetSize: 400000 + } }) if (useSentryMap) { @@ -162,7 +162,7 @@ if (useSentryMap) { authToken: process.env.SENTRY_AUTH_TOKEN, org: process.env.SENTRY_ORG, project: process.env.SENTRY_PROJECT, - telemetry: false, + telemetry: false }) ) } @@ -177,11 +177,11 @@ if (process.env.DIST_ZIP === '1' || process.env.DIST_ZIP === 'true') { archive: [ { source: path.resolve(__dirname, '../dist'), - destination: path.resolve(__dirname, '../dist-zip/pro-react-admin.zip'), - }, - ], - }, - }, + destination: path.resolve(__dirname, '../dist-zip/pro-react-admin.zip') + } + ] + } + } }) ) }