@sprucelabs/spruce-cli
Version:
Command line interface for building Spruce skills.
162 lines (137 loc) • 5.61 kB
text/typescript
import { buildSchema, SchemaValues } from '@sprucelabs/schema'
import { diskUtil } from '@sprucelabs/spruce-skill-utils'
import { uniqBy } from 'lodash'
import InFlightEntertainment from '../../../InFlightEntertainment'
import PkgService from '../../../services/PkgService'
import { NpmPackage } from '../../../types/cli.types'
import AbstractAction from '../../AbstractAction'
import { FeatureActionResponse } from '../../features.types'
const optionsSchema = buildSchema({
id: 'updateDependenciesAction',
description:
'Clear lock files and node_modules and then reinstalls all modules.',
fields: {},
})
type OptionsSchema = typeof optionsSchema
type Options = SchemaValues<OptionsSchema>
export default class UpdateDependenciesAction extends AbstractAction<OptionsSchema> {
public code = 'updateDependencies'
public optionsSchema = optionsSchema
public commandAliases = ['update.dependencies', 'upgrade.dependencies']
public invocationMessage = 'Updating dependencies... 💪'
public async execute(_options: Options): Promise<FeatureActionResponse> {
const files = ['package-lock.json', 'yarn.lock', 'node_modules']
for (const file of files) {
diskUtil.deleteFile(diskUtil.resolvePath(this.cwd, file))
}
try {
InFlightEntertainment.start(['Here we go!', 'Good luck!'])
const { totalDependencies, totalDevDependencies } =
await this.installDependencies()
return {
headline: 'Update Complete!',
summaryLines: [
`${totalDependencies} dependencie${
totalDependencies === 1 ? '' : 's'
} updated! 💪`,
`${totalDevDependencies} dev dependencie${
totalDevDependencies === 1 ? '' : 's'
} updated! 💪`,
],
}
} finally {
InFlightEntertainment.stop()
}
}
private async installDependencies() {
const features = this.features.isInSpruceModule()
? await this.features.getInstalledFeatures()
: []
const pkg = this.Service('pkg')
pkg.unset(['dependencies', '@sprucelabs/resolve-path-aliases'])
const pkgContents = pkg.readPackage()
let dependencies: { stripped: string; name: string }[] =
Object.keys(pkgContents.dependencies ?? {}).map((d) => ({
stripped: d,
name: d,
})) ?? []
let devDependencies: { stripped: string; name: string }[] =
Object.keys(pkgContents.devDependencies ?? {}).map((d) => ({
stripped: d,
name: d,
})) ?? []
for (const feature of features) {
for (const dep of feature.packageDependencies as NpmPackage[]) {
const stripped = pkg.stripLatest(dep.name)
const name = pkg.buildPackageName(dep)
const isDev =
(dep.isDev ||
devDependencies.find((d) => d.stripped === stripped)) &&
!dependencies.find((d) => d.stripped === stripped)
if (isDev) {
devDependencies.unshift({
stripped,
name,
})
} else {
dependencies.unshift({
stripped,
name,
})
}
}
}
dependencies = uniqBy(dependencies, 'stripped').filter(
(d) => !this.isBlockedFromUpgrade(d.stripped, pkg)
)
devDependencies = uniqBy(devDependencies, 'stripped').filter(
(d) => !this.isBlockedFromUpgrade(d.stripped, pkg)
)
pkg.deleteLockFile()
const namespace = pkg.getSkillNamespace()
if (dependencies.length > 0) {
InFlightEntertainment.writeStatus(
`Installing ${dependencies.length} dependenc${
dependencies.length === 1 ? 'y' : 'ies'
} for ${namespace}.`
)
await pkg.install(
dependencies.map((d) => d.name),
{
shouldForceInstall: true,
shouldCleanupLockFiles: false,
}
)
}
if (devDependencies.length > 0) {
InFlightEntertainment.writeStatus(
`Installing ${devDependencies.length} dev dependenc${
dependencies.length === 1 ? 'y' : 'ies'
} for ${namespace}.`
)
await pkg.install(
devDependencies.map((d) => d.name),
{
shouldForceInstall: true,
isDev: true,
shouldCleanupLockFiles: false,
}
)
}
pkg.deleteLockFile()
return {
totalDependencies: dependencies.length,
totalDevDependencies: devDependencies.length,
}
}
public blockUpgrade(name: string, pkg: PkgService) {
const content = pkg.get('skill.upgradeIgnoreList') ?? []
content.push(name)
pkg.set({ path: 'skill.upgradeIgnoreList', value: content })
}
public isBlockedFromUpgrade(name: string, pkg: PkgService) {
const content = pkg.get('skill.upgradeIgnoreList') ?? []
const isBlocked = content.indexOf(name) > -1
return isBlocked
}
}