Files
cypress/cli/lib/tasks/cache.ts
Bill Glesias 3481d1acaf chore: refactor cypress/cli to TypeScript (#32063)
* migrate cli scripts to TypeScript

* convert all javascript source files in the CLI to TypeScript

rebase into first

* chore: refactor all tests to TypeScript

rebase into second

* add npmignore for cli for typescript files

* update build process

* fix publically available exports

* Fix cy-in-cy tests

* add ts-expect-error to failing files

* fix projectConfigIpc failures as there are now multiple installs of tsx

* fix after-pack hook

* fix binary script

* chore: update publish binary to account for CLI being an ESModule compiled down to CommonJS

* does this work?

* fix the verify spec by making the listr2 renderer silent as it behaves differently since the refactor and is printing non deterministic outputs into our tests that do not have a large impact on the area we are testing and mostly served to actually test the renders of the listr2 framework itself

* empty commit

* additional refactor to code to remove strange any typing and exporting

* bump cache and build binaries

* fix CLI exports to keep backwards compatibility

* fix unit-tests

* turn on mac jobs

* fix group name rename in CLI

* remove babel deps from cli and explicitly install typescript

* address feedback from code review

* dont just falsy check results and instead explicitly check for null or undefined

* add ts-expect-error

* additional pass on cleaning up dynamic require / import from global lib references

* annotate ts-expect-errors with reason for why error is expected

* add rest of ts-expect-error comments

* removing hardcoded branch to publish binary chore/migrate_cli_to_typescript
2025-09-02 17:52:45 -04:00

170 lines
3.8 KiB
TypeScript

import state from './state'
import logger from '../logger'
import fs from '../fs'
import util from '../util'
import { join } from 'path'
import Table from 'cli-table3'
import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime'
import chalk from 'chalk'
import _ from 'lodash'
import getFolderSize from './get-folder-size'
dayjs.extend(relativeTime)
// output colors for the table
const colors = {
titles: chalk.white,
dates: chalk.cyan,
values: chalk.green,
size: chalk.gray,
}
const logCachePath = (): undefined => {
logger.always(state.getCacheDir())
return undefined
}
const clear = (): Promise<void> => {
return fs.removeAsync(state.getCacheDir())
}
const prune = async (): Promise<void> => {
const cacheDir = state.getCacheDir()
const checkedInBinaryVersion = util.pkgVersion()
let deletedBinary = false
try {
const versions = await fs.readdirAsync(cacheDir)
for (const version of versions) {
if (version !== checkedInBinaryVersion) {
deletedBinary = true
const versionDir = join(cacheDir, version)
await fs.removeAsync(versionDir)
}
}
if (deletedBinary) {
logger.always(`Deleted all binary caches except for the ${checkedInBinaryVersion} binary cache.`)
} else {
logger.always(`No binary caches found to prune.`)
}
} catch (e: any) {
if (e.code === 'ENOENT') {
logger.always(`No Cypress cache was found at ${cacheDir}. Nothing to prune.`)
return
}
throw e
}
}
const fileSizeInMB = (size: number): string => {
return `${(size / 1024 / 1024).toFixed(1)}MB`
}
/**
* Collects all cached versions, finds when each was used
* and prints a table with results to the terminal
*/
const list = (showSize: boolean = false): any => {
return getCachedVersions(showSize)
.then((binaries: any) => {
const head = [colors.titles('version'), colors.titles('last used')]
if (showSize) {
head.push(colors.titles('size'))
}
const table = new Table({
head,
})
binaries.forEach((binary: any) => {
const versionString = colors.values(binary.version)
const lastUsed = binary.accessed ? colors.dates(binary.accessed) : 'unknown'
const row = [versionString, lastUsed]
if (showSize) {
const size = colors.size(fileSizeInMB(binary.size))
row.push(size)
}
return table.push(row)
})
logger.always(table.toString())
})
}
const getCachedVersions = (showSize: boolean): Promise<any> => {
const cacheDir = state.getCacheDir()
return fs
.readdirAsync(cacheDir)
.filter(util.isSemver)
.map((version: any) => {
return {
version,
folderPath: join(cacheDir, version),
}
})
.mapSeries((binary: any) => {
// last access time on the folder is different from last access time
// on the Cypress binary
const binaryDir = state.getBinaryDir(binary.version)
const executable = state.getPathToExecutable(binaryDir)
return fs.statAsync(executable).then((stat: any) => {
const lastAccessedTime = _.get(stat, 'atime')
if (!lastAccessedTime) {
// the test runner has never been opened
// or could be a test simulating missing timestamp
return binary
}
const accessed = dayjs(lastAccessedTime).fromNow()
binary.accessed = accessed
return binary
}, (e: any) => {
// could not find the binary or gets its stats
return binary
})
})
.mapSeries((binary: any) => {
if (showSize) {
const binaryDir = state.getBinaryDir(binary.version)
return getFolderSize(binaryDir).then((size: number) => {
return {
...binary,
size,
}
})
}
return binary
})
}
const cacheModule = {
path: logCachePath,
clear,
prune,
list,
getCachedVersions,
}
export default cacheModule