| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | import fs from 'fs' |
| | import path from 'path' |
| | import { program } from 'commander' |
| | import walk from 'walk-sync' |
| |
|
| | import walkFiles from '@/workflows/walk-files' |
| | import languages from '@/languages/lib/languages-server' |
| |
|
| | const EXCEPTIONS = new Set([ |
| | 'assets/images/site/favicon.ico', |
| | 'assets/images/site/favicon.svg', |
| | 'assets/images/site/apple-touch-icon.png', |
| | 'assets/images/site/apple-touch-icon-114x114.png', |
| | 'assets/images/site/apple-touch-icon-120x120.png', |
| | 'assets/images/site/apple-touch-icon-144x144.png', |
| | 'assets/images/site/apple-touch-icon-152x152.png', |
| | 'assets/images/site/apple-touch-icon-180x180.png', |
| | 'assets/images/site/apple-touch-icon-192x192.png', |
| | 'assets/images/site/apple-touch-icon-512x512.png', |
| | 'assets/images/site/apple-touch-icon-57x57.png', |
| | 'assets/images/site/apple-touch-icon-60x60.png', |
| | 'assets/images/site/apple-touch-icon-72x72.png', |
| | 'assets/images/site/apple-touch-icon-76x76.png', |
| | 'assets/images/site/evergreens/balsam.png', |
| | 'assets/images/site/evergreens/boxwood.png', |
| | 'assets/images/site/evergreens/cedar.png', |
| | 'assets/images/site/evergreens/cypress.png', |
| | 'assets/images/site/evergreens/fir.png', |
| | 'assets/images/site/evergreens/hemlock.png', |
| | 'assets/images/site/evergreens/hinoki.png', |
| | 'assets/images/site/evergreens/holly.png', |
| | 'assets/images/site/evergreens/juniper.png', |
| | 'assets/images/site/evergreens/laurel.png', |
| | 'assets/images/site/evergreens/pine.png', |
| | 'assets/images/site/evergreens/redwood.png', |
| | 'assets/images/site/evergreens/sequoia.png', |
| | 'assets/images/site/evergreens/spruce.png', |
| | 'assets/images/site/evergreens/yew.png', |
| | 'assets/images/social-cards/actions.png', |
| | 'assets/images/social-cards/copilot.png', |
| | 'assets/images/social-cards/default.png', |
| | 'assets/images/social-cards/issues.png', |
| | 'assets/images/social-cards/code-security.png', |
| | |
| | 'assets/images/banner-images/hero-1.png', |
| | 'assets/images/banner-images/hero-2.png', |
| | 'assets/images/banner-images/hero-3.png', |
| | 'assets/images/banner-images/hero-4.png', |
| | 'assets/images/banner-images/hero-5.png', |
| | 'assets/images/banner-images/hero-6.png', |
| | ]) |
| |
|
| | function isExceptionPath(imagePath: string) { |
| | |
| | |
| | |
| | |
| | |
| | return ( |
| | EXCEPTIONS.has(imagePath) || |
| | path.basename(imagePath) === '.DS_Store' || |
| | imagePath.split(path.sep).includes('early-access') |
| | ) |
| | } |
| |
|
| | program |
| | .description('Print all images that are in ./assets/ but not found in any source files') |
| | .option('-e, --exit', 'Exit script by count of orphans (useful for CI)') |
| | .option('-v, --verbose', 'Verbose outputs') |
| | .option('--json', 'Output in JSON format') |
| | .option('--exclude-translations', "Don't search in translations/") |
| | .parse(process.argv) |
| |
|
| | type MainOptions = { |
| | json: boolean |
| | verbose: boolean |
| | exit: boolean |
| | excludeTranslations: boolean |
| | } |
| | main(program.opts()) |
| |
|
| | async function main(opts: MainOptions) { |
| | const { json, verbose, exit, excludeTranslations } = opts |
| |
|
| | const walkOptions = { |
| | directories: false, |
| | includeBasePath: true, |
| | } |
| | const sourceFiles = [] |
| | const englishFiles = [] |
| | englishFiles.push(...walkFiles(path.join(languages.en.dir, 'content'), ['.md'])) |
| | englishFiles.push(...walkFiles(path.join(languages.en.dir, 'data'), ['.md', '.yml'])) |
| | sourceFiles.push(...englishFiles) |
| |
|
| | if (!excludeTranslations) { |
| | |
| | |
| | |
| | |
| | const englishRelativeFiles = new Set( |
| | englishFiles.map((englishFile) => path.relative(languages.en.dir, englishFile)), |
| | ) |
| | for (const [language, { dir }] of Object.entries(languages)) { |
| | if (language !== 'en') { |
| | if (!fs.existsSync(dir)) { |
| | throw new Error( |
| | `${dir} does not exist. ` + |
| | 'Get around this by using the flag `--exclude-translations`. Or set up the TRANSLATION_ROOT.', |
| | ) |
| | } |
| | const languageFiles = [] |
| | languageFiles.push(...walkFiles(path.join(dir, 'content'), ['.md'])) |
| | languageFiles.push(...walkFiles(path.join(dir, 'data'), ['.md', '.yml'])) |
| | sourceFiles.push( |
| | ...languageFiles.filter((languageFile) => |
| | englishRelativeFiles.has(path.relative(dir, languageFile)), |
| | ), |
| | ) |
| | } |
| | } |
| | } |
| |
|
| | const roots = ['contributing', 'src', 'assets'] |
| |
|
| | for (const root of roots) { |
| | sourceFiles.push( |
| | ...walk( |
| | root, |
| | Object.assign( |
| | { |
| | globs: ['!**/*.+(png|jpe?g|csv|graphql|json|svg)'], |
| | }, |
| | walkOptions, |
| | ), |
| | ), |
| | ) |
| | } |
| | |
| | sourceFiles.push('.github/CONTRIBUTING.md') |
| | sourceFiles.push('README.md') |
| | if (verbose) { |
| | console.log(`${sourceFiles.length.toLocaleString()} source files found in total.`) |
| | } |
| |
|
| | const allImages = new Set( |
| | walk( |
| | 'assets', |
| | Object.assign( |
| | { |
| | globs: ['!**/*.+(md)'], |
| | }, |
| | walkOptions, |
| | ), |
| | ).filter((filePath) => !filePath.endsWith('.md')), |
| | ) |
| |
|
| | if (verbose) { |
| | console.log(`${allImages.size.toLocaleString()} images found in total.`) |
| | } |
| |
|
| | for (const sourceFile of sourceFiles) { |
| | const content = fs.readFileSync(sourceFile, 'utf-8') |
| | for (const imagePath of allImages) { |
| | const needle = imagePath.split(path.sep).slice(-2).join('/') |
| | if (content.includes(needle) || isExceptionPath(imagePath)) { |
| | allImages.delete(imagePath) |
| | } |
| | } |
| | } |
| |
|
| | if (verbose && allImages.size) { |
| | console.log('The following files are not mentioned anywhere in any source file') |
| | } |
| | if (json) { |
| | console.log(JSON.stringify([...allImages], undefined, 2)) |
| | } else { |
| | for (const imagePath of [...allImages].sort((a, b) => a.localeCompare(b))) { |
| | |
| | |
| | console.log(`"${imagePath}"`) |
| | } |
| | } |
| |
|
| | if (verbose) { |
| | console.log(`${allImages.size.toLocaleString()} orphans left.`) |
| | const totalDiskSize = getTotalDiskSize(allImages) |
| | console.log(`Total disk size of all of these: ${(totalDiskSize / 1024 / 1024).toFixed(1)}MB`) |
| | } |
| |
|
| | if (exit) { |
| | process.exit(allImages.size) |
| | } |
| | } |
| |
|
| | function getTotalDiskSize(filePaths: Set<string>) { |
| | let sum = 0 |
| | for (const filePath of filePaths) { |
| | sum += fs.statSync(filePath).size |
| | } |
| | return sum |
| | } |
| |
|