From 904289bb1d363427b6c55c50b023cdeb98fa8305 Mon Sep 17 00:00:00 2001 From: Stephen Zhou Date: Wed, 11 Mar 2026 11:36:09 +0800 Subject: [PATCH] chore: remove optimize standalone script (#33256) Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- web/eslint-suppressions.json | 8 -- web/eslint.config.mjs | 3 + web/package.json | 39 ++++--- web/scripts/README.md | 38 ------- web/scripts/optimize-standalone.js | 163 ----------------------------- 5 files changed, 21 insertions(+), 230 deletions(-) delete mode 100644 web/scripts/README.md delete mode 100644 web/scripts/optimize-standalone.js diff --git a/web/eslint-suppressions.json b/web/eslint-suppressions.json index b7f18c3d3c..937ac6a0ed 100644 --- a/web/eslint-suppressions.json +++ b/web/eslint-suppressions.json @@ -12898,14 +12898,6 @@ "count": 2 } }, - "scripts/optimize-standalone.js": { - "e18e/prefer-static-regex": { - "count": 1 - }, - "unused-imports/no-unused-vars": { - "count": 2 - } - }, "scripts/refactor-component.js": { "e18e/prefer-static-regex": { "count": 14 diff --git a/web/eslint.config.mjs b/web/eslint.config.mjs index 145df1484e..de78e90548 100644 --- a/web/eslint.config.mjs +++ b/web/eslint.config.mjs @@ -218,3 +218,6 @@ export default antfu( }, }, ) + .disableRulesFix([ + 'e18e/prefer-array-at', + ]) diff --git a/web/package.json b/web/package.json index dcb060e6bd..30434e8707 100644 --- a/web/package.json +++ b/web/package.json @@ -26,39 +26,36 @@ "node": "^22.22.1" }, "scripts": { + "analyze": "next experimental-analyze", + "analyze-component": "node ./scripts/analyze-component.js", + "build": "next build", + "build:vinext": "vinext build", "dev": "next dev", "dev:inspect": "next dev --inspect", "dev:vinext": "vinext dev", - "build": "next build", - "build:docker": "next build && node scripts/optimize-standalone.js", - "build:vinext": "vinext build", - "start": "node ./scripts/copy-and-start.mjs", - "start:vinext": "vinext start", + "gen-doc-paths": "tsx ./scripts/gen-doc-paths.ts", + "gen-icons": "node ./scripts/gen-icons.mjs && eslint --fix app/components/base/icons/src/", + "i18n:check": "tsx ./scripts/check-i18n.js", + "knip": "knip", "lint": "eslint --cache --concurrency=auto", "lint:ci": "eslint --cache --concurrency 2", "lint:fix": "pnpm lint --fix", "lint:quiet": "pnpm lint --quiet", - "lint:complexity": "pnpm lint --rule 'complexity: [error, {max: 15}]' --quiet", - "lint:report": "pnpm lint --output-file eslint_report.json --format json", "lint:tss": "tsslint --project tsconfig.json", - "type-check": "tsc --noEmit", - "type-check:tsgo": "tsgo --noEmit", + "preinstall": "npx only-allow pnpm", "prepare": "cd ../ && node -e \"if (process.env.NODE_ENV !== 'production'){process.exit(1)} \" || husky ./web/.husky", - "gen-icons": "node ./scripts/gen-icons.mjs && eslint --fix app/components/base/icons/src/", - "gen-doc-paths": "tsx ./scripts/gen-doc-paths.ts", - "uglify-embed": "node ./bin/uglify-embed", - "i18n:check": "tsx ./scripts/check-i18n.js", - "test": "vitest run", - "test:coverage": "vitest run --coverage", - "test:ci": "vitest run --coverage --silent=passed-only", - "test:watch": "vitest --watch", - "analyze-component": "node ./scripts/analyze-component.js", "refactor-component": "node ./scripts/refactor-component.js", + "start": "node ./scripts/copy-and-start.mjs", + "start:vinext": "vinext start", "storybook": "storybook dev -p 6006", "storybook:build": "storybook build", - "preinstall": "npx only-allow pnpm", - "analyze": "next experimental-analyze", - "knip": "knip" + "test": "vitest run", + "test:ci": "vitest run --coverage --silent=passed-only", + "test:coverage": "vitest run --coverage", + "test:watch": "vitest --watch", + "type-check": "tsc --noEmit", + "type-check:tsgo": "tsgo --noEmit", + "uglify-embed": "node ./bin/uglify-embed" }, "dependencies": { "@amplitude/analytics-browser": "2.36.3", diff --git a/web/scripts/README.md b/web/scripts/README.md deleted file mode 100644 index 2c575a244c..0000000000 --- a/web/scripts/README.md +++ /dev/null @@ -1,38 +0,0 @@ -# Production Build Optimization Scripts - -## optimize-standalone.js - -This script removes unnecessary development dependencies from the Next.js standalone build output to reduce the production Docker image size. - -### What it does - -The script specifically targets and removes `jest-worker` packages that are bundled with Next.js but not needed in production. These packages are included because: - -1. Next.js includes jest-worker in its compiled dependencies -1. terser-webpack-plugin (used by Next.js for minification) depends on jest-worker -1. pnpm's dependency resolution creates symlinks to jest-worker in various locations - -### Usage - -The script is automatically run during Docker builds via the `build:docker` npm script: - -```bash -# Docker build (removes jest-worker after build) -pnpm build:docker -``` - -To run the optimization manually: - -```bash -node scripts/optimize-standalone.js -``` - -### What gets removed - -- `node_modules/.pnpm/next@*/node_modules/next/dist/compiled/jest-worker` -- `node_modules/.pnpm/terser-webpack-plugin@*/node_modules/jest-worker` (symlinks) -- `node_modules/.pnpm/jest-worker@*` (actual packages) - -### Impact - -Removing jest-worker saves approximately 36KB per instance from the production image. While this may seem small, it helps ensure production images only contain necessary runtime dependencies. diff --git a/web/scripts/optimize-standalone.js b/web/scripts/optimize-standalone.js deleted file mode 100644 index b73667eac6..0000000000 --- a/web/scripts/optimize-standalone.js +++ /dev/null @@ -1,163 +0,0 @@ -/** - * Script to optimize Next.js standalone output for production - * Removes unnecessary files like jest-worker that are bundled with Next.js - */ - -import fs from 'node:fs' -import path from 'node:path' -import { fileURLToPath } from 'node:url' - -const __filename = fileURLToPath(import.meta.url) -const __dirname = path.dirname(__filename) - -console.log('šŸ”§ Optimizing standalone output...') - -const standaloneDir = path.join(__dirname, '..', '.next', 'standalone') - -// Check if standalone directory exists -if (!fs.existsSync(standaloneDir)) { - console.error('āŒ Standalone directory not found. Please run "next build" first.') - process.exit(1) -} - -// List of paths to remove (relative to standalone directory) -const pathsToRemove = [ - // Remove jest-worker from Next.js compiled dependencies - 'node_modules/.pnpm/next@*/node_modules/next/dist/compiled/jest-worker', - // Remove jest-worker symlinks from terser-webpack-plugin - 'node_modules/.pnpm/terser-webpack-plugin@*/node_modules/jest-worker', - // Remove actual jest-worker packages (directories only, not symlinks) - 'node_modules/.pnpm/jest-worker@*', -] - -// Function to safely remove a path -function removePath(basePath, relativePath) { - const fullPath = path.join(basePath, relativePath) - - // Handle wildcard patterns - if (relativePath.includes('*')) { - const parts = relativePath.split('/') - let currentPath = basePath - - for (let i = 0; i < parts.length; i++) { - const part = parts[i] - if (part.includes('*')) { - // Find matching directories - if (fs.existsSync(currentPath)) { - const entries = fs.readdirSync(currentPath) - - // replace '*' with '.*' - const regexPattern = part.replace(/\*/g, '.*') - - const regex = new RegExp(`^${regexPattern}$`) - - for (const entry of entries) { - if (regex.test(entry)) { - const remainingPath = parts.slice(i + 1).join('/') - const matchedPath = path.join(currentPath, entry, remainingPath) - - try { - // Use lstatSync to check if path exists (works for both files and symlinks) - const stats = fs.lstatSync(matchedPath) - - if (stats.isSymbolicLink()) { - // Remove symlink - fs.unlinkSync(matchedPath) - console.log(`āœ… Removed symlink: ${path.relative(basePath, matchedPath)}`) - } - else { - // Remove directory/file - fs.rmSync(matchedPath, { recursive: true, force: true }) - console.log(`āœ… Removed: ${path.relative(basePath, matchedPath)}`) - } - } - catch (error) { - // Silently ignore ENOENT (path not found) errors - if (error.code !== 'ENOENT') { - console.error(`āŒ Failed to remove ${matchedPath}: ${error.message}`) - } - } - } - } - } - return - } - else { - currentPath = path.join(currentPath, part) - } - } - } - else { - // Direct path removal - if (fs.existsSync(fullPath)) { - try { - fs.rmSync(fullPath, { recursive: true, force: true }) - console.log(`āœ… Removed: ${relativePath}`) - } - catch (error) { - console.error(`āŒ Failed to remove ${fullPath}: ${error.message}`) - } - } - } -} - -// Remove unnecessary paths -console.log('šŸ—‘ļø Removing unnecessary files...') -for (const pathToRemove of pathsToRemove) { - removePath(standaloneDir, pathToRemove) -} - -// Calculate size reduction -console.log('\nšŸ“Š Optimization complete!') - -// Optional: Display the size of remaining jest-related files (if any) -const checkForJest = (dir) => { - const jestFiles = [] - - function walk(currentPath) { - if (!fs.existsSync(currentPath)) - return - - try { - const entries = fs.readdirSync(currentPath) - for (const entry of entries) { - const fullPath = path.join(currentPath, entry) - - try { - const stat = fs.lstatSync(fullPath) // Use lstatSync to handle symlinks - - if (stat.isDirectory() && !stat.isSymbolicLink()) { - // Skip node_modules subdirectories to avoid deep traversal - if (entry === 'node_modules' && currentPath !== standaloneDir) { - continue - } - walk(fullPath) - } - else if (stat.isFile() && entry.includes('jest')) { - jestFiles.push(path.relative(standaloneDir, fullPath)) - } - } - catch (err) { - // Skip files that can't be accessed - continue - } - } - } - catch (err) { - // Skip directories that can't be read - - } - } - - walk(dir) - return jestFiles -} - -const remainingJestFiles = checkForJest(standaloneDir) -if (remainingJestFiles.length > 0) { - console.log('\nāš ļø Warning: Some jest-related files still remain:') - remainingJestFiles.forEach(file => console.log(` - ${file}`)) -} -else { - console.log('\n✨ No jest-related files found in standalone output!') -}