diff --git a/package.json b/package.json index 5286115a9..d03ea09f8 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "build:app": "next build", "build:app:analyze": "cross-env ANALYZE=true next build", "build:core": "turbo build --filter \"!@payloadcms/plugin-*\"", + "build:core:force": "pnpm clean:build && turbo build --filter \"!@payloadcms/plugin-*\" --no-cache --force", "build:create-payload-app": "turbo build --filter create-payload-app", "build:db-mongodb": "turbo build --filter db-mongodb", "build:db-postgres": "turbo build --filter db-postgres", @@ -42,21 +43,21 @@ "build:translations": "turbo build --filter translations", "build:ui": "turbo build --filter ui", "clean": "turbo clean", - "clean:all": "find . \\( -type d \\( -name node_modules -o -name dist -o -name .cache -o -name .next -o -name .turbo \\) -o -type f -name tsconfig.tsbuildinfo \\) -exec rm -rf {} +", - "clean:build": "find . \\( -type d \\( -name dist -o -name .cache -o -name .next -o -name .turbo \\) -o -type f -name tsconfig.tsbuildinfo \\) -not -path '*/node_modules/*' -exec rm -rf {} +", - "clean:cache": "rimraf node_modules/.cache && rimraf packages/payload/node_modules/.cache && rimraf .next", + "clean:all": "node ./scripts/delete-recursively.js '@node_modules' 'media' '**/dist' '**/.cache' '**/.next' '**/.turbo' '**/tsconfig.tsbuildinfo' '**/payload*.tgz'", + "clean:build": "node ./scripts/delete-recursively.js 'media' '**/dist' '**/.cache' '**/.next' '**/.turbo' '**/tsconfig.tsbuildinfo' '**/payload*.tgz'", + "clean:cache": "node ./scripts/delete-recursively.js node_modules/.cache! packages/payload/node_modules/.cache! .next", "dev": "cross-env NODE_OPTIONS=--no-deprecation node ./test/dev.js", "dev:generate-graphql-schema": "cross-env NODE_OPTIONS=--no-deprecation tsx ./test/generateGraphQLSchema.ts", "dev:generate-types": "cross-env NODE_OPTIONS=--no-deprecation tsx ./test/generateTypes.ts", "dev:postgres": "cross-env NODE_OPTIONS=--no-deprecation PAYLOAD_DATABASE=postgres node ./test/dev.js", - "devsafe": "rimraf .next && pnpm dev", + "devsafe": "node ./scripts/delete-recursively.js '**/.next' && pnpm dev", "docker:restart": "pnpm docker:stop --remove-orphans && pnpm docker:start", "docker:start": "docker compose -f packages/plugin-cloud-storage/docker-compose.yml up -d", "docker:stop": "docker compose -f packages/plugin-cloud-storage/docker-compose.yml down", "fix": "eslint \"packages/**/*.ts\" --fix", "lint": "eslint \"packages/**/*.ts\"", "lint-staged": "lint-staged", - "obliterate-playwright-cache": "rm -rf ~/Library/Caches/ms-playwright && find /System/Volumes/Data/private/var/folders -type d -name 'playwright*' -exec rm -rf {} +", + "obliterate-playwright-cache-macos": "rm -rf ~/Library/Caches/ms-playwright && find /System/Volumes/Data/private/var/folders -type d -name 'playwright*' -exec rm -rf {} +", "prepare": "husky install", "reinstall": "pnpm clean:all && pnpm install", "release:alpha": "tsx ./scripts/release.ts --bump prerelease --tag alpha", diff --git a/scripts/delete-recursively.js b/scripts/delete-recursively.js new file mode 100644 index 000000000..e06d91543 --- /dev/null +++ b/scripts/delete-recursively.js @@ -0,0 +1,157 @@ +import { promises as fs, existsSync } from 'fs' +import path, { join } from 'path' +import glob from 'glob' +import process from 'process' +import chalk from 'chalk' + +// Helper function to format size appropriately in KB or MB +function formatSize(sizeInBytes) { + const sizeInKB = sizeInBytes / 1024 + if (sizeInKB < 1024) { + return `${sizeInKB.toFixed(2)} KB` + } else { + return `${(sizeInKB / 1024).toFixed(2)} MB` + } +} + +// Function to calculate the size of a directory +async function calculateSize(targetPath) { + let totalSize = 0 + const stats = await fs.lstat(targetPath) + if (stats.isDirectory()) { + const files = await fs.readdir(targetPath) + for (const file of files) { + const filePath = join(targetPath, file) + totalSize += await calculateSize(filePath) + } + } else { + totalSize = stats.size + } + return totalSize +} + +// Function to delete a file or directory recursively +async function deleteRecursively(targetPath, fullDelete = false) { + try { + if (fullDelete && existsSync(targetPath)) { + const size = await calculateSize(targetPath) + await fs.rmdir(targetPath, { recursive: true }) // Use async version of rmdir + return size + } + + const stats = await fs.lstat(targetPath) + let size = 0 + if (stats.isDirectory()) { + const files = await fs.readdir(targetPath) + for (const file of files) { + const curPath = join(targetPath, file) + size += await deleteRecursively(curPath) + } + await fs.rmdir(targetPath) + } else { + size = stats.size + await fs.unlink(targetPath) + } + return size + } catch (error) { + console.error(chalk.red(`Error deleting ${targetPath}: ${error.message}`)) + return 0 // Return 0 size if there's an error + } +} + +// Function to clean directories based on provided patterns +async function cleanDirectories(patterns) { + const deletedCounts = {} + let totalSize = 0 + + for (let entry of patterns) { + const ignoreNodeModules = !entry.endsWith('!') + let pattern = ignoreNodeModules ? entry : entry.slice(0, -1) + + let files = [] + let fulleDelete = false + if (pattern === '@node_modules') { + pattern = '**/node_modules' + fulleDelete = true + files = await new Promise((resolve, reject) => { + glob(pattern, { nodir: false }, (err, files) => { + if (err) { + reject(err) + } else { + // Filter out node_modules within other node_modules + const topNodeModules = files.filter((file) => { + const parentDir = path.dirname(file) + return !parentDir.includes('node_modules') + }) + resolve(topNodeModules) + } + }) + }) + } else { + const options = { + ignore: ignoreNodeModules ? '**/node_modules/**' : '', + nodir: false, + } + + files = await new Promise((resolve, reject) => { + glob(pattern, options, (err, files) => { + if (err) { + reject(err) + } else { + resolve(files) + } + }) + }) + } + + let count = 0 + let patternSize = 0 + for (const file of files) { + const fileSize = await deleteRecursively(file, fulleDelete) + count++ + patternSize += fileSize + } + deletedCounts[pattern] = { count, size: patternSize } + totalSize += patternSize + } + + // Determine the maximum lengths needed for even spacing + const maxPatternLength = Math.max(...Object.keys(deletedCounts).map((pattern) => pattern.length)) + const maxCountLength = Math.max( + ...Object.values(deletedCounts).map( + (item) => `${item.count} item${item.count !== 1 ? 's' : ''} deleted`.length, + ), + ) + const maxSizeLength = Math.max( + ...Object.values(deletedCounts).map((item) => formatSize(item.size).length), + ) + + // Print details for each pattern with colors, formatted for alignment + console.log(chalk.blue('Summary of deleted items:')) + Object.keys(deletedCounts).forEach((pattern) => { + const itemCount = + `${deletedCounts[pattern].count} item${deletedCounts[pattern].count !== 1 ? 's' : ''} deleted`.padEnd( + maxCountLength, + ) + console.log( + `${chalk.green(pattern.padEnd(maxPatternLength))} ${chalk.red(itemCount)} ${chalk.yellow(formatSize(deletedCounts[pattern].size).padStart(maxSizeLength))}`, + ) + }) + + // Calculate total deleted items and size + console.log( + chalk.magenta( + `Total deleted items: ${Object.values(deletedCounts).reduce((acc, { count }) => acc + count, 0)}`, + ), + ) + console.log(chalk.cyan(`Total size of deleted items: ${formatSize(totalSize)}`)) +} + +// Get patterns from command-line arguments +const patterns = process.argv.slice(2) + +if (patterns.length > 0) { + void cleanDirectories(patterns) +} else { + console.log(chalk.red('No patterns provided. Usage: node script.js [patterns]')) +}