Skip to content

Commit

Permalink
Merge pull request #877 from oclif/mdonnalley/core-v4
Browse files Browse the repository at this point in the history
feat: use @oclif/core v4
  • Loading branch information
iowillhoit authored May 31, 2024
2 parents f09e5b7 + 782961e commit 6f6aa3b
Show file tree
Hide file tree
Showing 16 changed files with 202 additions and 228 deletions.
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"author": "Salesforce",
"bugs": "https://github.com/oclif/plugin-plugins/issues",
"dependencies": {
"@oclif/core": "^3.26.6",
"chalk": "^5.3.0",
"@oclif/core": "4.0.0-beta.11",
"ansis": "^3.2.0",
"debug": "^4.3.4",
"npm": "^10.8.0",
"npm-package-arg": "^11.0.2",
Expand All @@ -21,6 +21,7 @@
"@commitlint/config-conventional": "^19",
"@oclif/plugin-help": "^6",
"@oclif/prettier-config": "^0.2.1",
"@oclif/test": "^4.0.2",
"@types/chai": "^4.3.11",
"@types/debug": "^4.1.12",
"@types/mocha": "^10.0.6",
Expand Down Expand Up @@ -68,7 +69,7 @@
"@oclif/plugin-help"
],
"aliases": {
"aliasme": "@oclif/plugin-test-esm-1"
"aliasme": "@oclif/plugin-version"
},
"bin": "mycli",
"flexibleTaxonomy": true,
Expand Down
12 changes: 6 additions & 6 deletions src/commands/plugins/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {Command, Flags, Interfaces, Plugin} from '@oclif/core'
import chalk from 'chalk'
import {dim} from 'ansis'
// @ts-expect-error because object-treeify does not have types: https://github.com/blackflux/object-treeify/issues/1077
import treeify from 'object-treeify'

Expand Down Expand Up @@ -89,17 +89,17 @@ export default class PluginsIndex extends Command {

private displayJitPlugins(jitPlugins: JitPlugin[]) {
if (jitPlugins.length === 0) return
this.log(chalk.dim('\nUninstalled JIT Plugins:'))
this.log(dim('\nUninstalled JIT Plugins:'))
for (const {name, version} of jitPlugins) {
this.log(`${this.plugins.friendlyName(name)} ${chalk.dim(version)}`)
this.log(`${this.plugins.friendlyName(name)} ${dim(version)}`)
}
}

private formatPlugin(plugin: Plugin): string {
let output = `${this.plugins.friendlyName(plugin.name)} ${chalk.dim(plugin.version)}`
if (plugin.type !== 'user') output += chalk.dim(` (${plugin.type})`)
let output = `${this.plugins.friendlyName(plugin.name)} ${dim(plugin.version)}`
if (plugin.type !== 'user') output += dim(` (${plugin.type})`)
if (plugin.type === 'link') output += ` ${plugin.root}`
else if (plugin.tag && plugin.tag !== 'latest') output += chalk.dim(` (${String(plugin.tag)})`)
else if (plugin.tag && plugin.tag !== 'latest') output += dim(` (${String(plugin.tag)})`)
return output
}
}
8 changes: 4 additions & 4 deletions src/commands/plugins/inspect.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {Args, Command, Flags, Plugin} from '@oclif/core'
import chalk from 'chalk'
import {bold, dim} from 'ansis'
import {readFile} from 'node:fs/promises'
import {dirname, join, sep} from 'node:path'
// @ts-expect-error because object-treeify does not have types: https://github.com/blackflux/object-treeify/issues/1077
Expand Down Expand Up @@ -113,15 +113,15 @@ export default class PluginsInspect extends Command {
if (!version) continue

const from = plugin.pjson.dependencies?.[dep]
const versionMsg = chalk.dim(from ? `${from} => ${version}` : version)
const versionMsg = dim(from ? `${from} => ${version}` : version)
const msg = verbose ? `${dep} ${versionMsg} ${pkgPath}` : `${dep} ${versionMsg}`

dependencies[msg] = null
depsJson[dep] = {from, version}
}

const tree = {
[chalk.bold.cyan(plugin.name)]: {
[bold.cyan(plugin.name)]: {
[`version ${plugin.version}`]: null,
...(plugin.tag ? {[`tag ${plugin.tag}`]: null} : {}),
...(plugin.pjson.homepage ? {[`homepage ${plugin.pjson.homepage}`]: null} : {}),
Expand Down Expand Up @@ -158,7 +158,7 @@ export default class PluginsInspect extends Command {
try {
plugins.push(await this.inspect(pluginName, flags.verbose))
} catch (error) {
this.log(chalk.bold.red('failed'))
this.log(bold.red('failed'))
throw error
}
}
Expand Down
10 changes: 4 additions & 6 deletions src/commands/plugins/install.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable no-await-in-loop */
import {Args, Command, Errors, Flags, Interfaces, ux} from '@oclif/core'
import chalk from 'chalk'
import {bold, cyan} from 'ansis'
import validate from 'validate-npm-package-name'

import {determineLogLevel} from '../../log-level.js'
Expand Down Expand Up @@ -160,19 +160,17 @@ Use the <%= config.scopedEnvVarKey('NPM_REGISTRY') %> environment variable to se
})
try {
if (p.type === 'npm') {
ux.action.start(
`${this.config.name}: Installing plugin ${chalk.cyan(plugins.friendlyName(p.name) + '@' + p.tag)}`,
)
ux.action.start(`${this.config.name}: Installing plugin ${cyan(plugins.friendlyName(p.name) + '@' + p.tag)}`)
plugin = await plugins.install(p.name, {
force: flags.force,
tag: p.tag,
})
} else {
ux.action.start(`${this.config.name}: Installing plugin ${chalk.cyan(p.url)}`)
ux.action.start(`${this.config.name}: Installing plugin ${cyan(p.url)}`)
plugin = await plugins.install(p.url, {force: flags.force})
}
} catch (error) {
ux.action.stop(chalk.bold.red('failed'))
ux.action.stop(bold.red('failed'))
throw error
}

Expand Down
4 changes: 2 additions & 2 deletions src/commands/plugins/link.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {Args, Command, Flags, ux} from '@oclif/core'
import chalk from 'chalk'
import {cyan} from 'ansis'

import {determineLogLevel} from '../../log-level.js'
import Plugins from '../../plugins.js'
Expand Down Expand Up @@ -35,7 +35,7 @@ e.g. If you have a user-installed or core plugin that has a 'hello' command, ins
logLevel: determineLogLevel(this.config, flags, 'silent'),
})

ux.action.start(`${this.config.name}: Linking plugin ${chalk.cyan(args.path)}`)
ux.action.start(`${this.config.name}: Linking plugin ${cyan(args.path)}`)
await plugins.link(args.path, {install: flags.install})
ux.action.stop()
}
Expand Down
8 changes: 4 additions & 4 deletions src/commands/plugins/reset.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable no-await-in-loop */
import {Command, Flags} from '@oclif/core'
import chalk from 'chalk'
import {dim} from 'ansis'
import {rm} from 'node:fs/promises'
import {join} from 'node:path'

Expand Down Expand Up @@ -28,7 +28,7 @@ export default class Reset extends Command {
this.log(`Found ${userPlugins.length} plugin${userPlugins.length === 0 ? '' : 's'}:`)
for (const plugin of userPlugins) {
this.log(
`- ${plugin.name} ${chalk.dim(this.config.plugins.get(plugin.name)?.version)} ${chalk.dim(`(${plugin.type})`)}`,
`- ${plugin.name} ${dim(this.config.plugins.get(plugin.name)?.version ?? '')} ${dim(`(${plugin.type})`)}`,
)
}

Expand Down Expand Up @@ -68,7 +68,7 @@ export default class Reset extends Command {
if (plugin.type === 'link') {
try {
const newPlugin = await plugins.link(plugin.root, {install: false})
const newVersion = chalk.dim(`-> ${newPlugin.version}`)
const newVersion = dim(`-> ${newPlugin.version}`)
this.log(`✅ Relinked ${plugin.name} ${newVersion}`)
} catch {
this.warn(`Failed to relink ${plugin.name}`)
Expand All @@ -80,7 +80,7 @@ export default class Reset extends Command {
const newPlugin = plugin.url
? await plugins.install(plugin.url)
: await plugins.install(plugin.name, {tag: plugin.tag})
const newVersion = chalk.dim(`-> ${newPlugin.version}`)
const newVersion = dim(`-> ${newPlugin.version}`)
const tag = plugin.tag ? `@${plugin.tag}` : plugin.url ? ` (${plugin.url})` : ''
this.log(`✅ Reinstalled ${plugin.name}${tag} ${newVersion}`)
} catch {
Expand Down
4 changes: 2 additions & 2 deletions src/commands/plugins/uninstall.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable no-await-in-loop */
import {Args, Command, Flags, ux} from '@oclif/core'
import chalk from 'chalk'
import {bold} from 'ansis'

import {determineLogLevel} from '../../log-level.js'
import Plugins from '../../plugins.js'
Expand Down Expand Up @@ -68,7 +68,7 @@ export default class PluginsUninstall extends Command {
ux.action.start(`${this.config.name}: Uninstalling ${displayName}`)
await plugins.uninstall(name)
} catch (error) {
ux.action.stop(chalk.bold.red('failed'))
ux.action.stop(bold.red('failed'))
throw error
}

Expand Down
2 changes: 1 addition & 1 deletion src/npm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export class NPM {
if (this.config.npmRegistry) args.push(`--registry=${this.config.npmRegistry}`)

if (options.logLevel !== 'notice' && options.logLevel !== 'silent') {
ux.logToStderr(`${options.cwd}: ${bin} ${args.join(' ')}`)
ux.stderr(`${options.cwd}: ${bin} ${args.join(' ')}`)
}

debug(`${options.cwd}: ${bin} ${args.join(' ')}`)
Expand Down
73 changes: 37 additions & 36 deletions src/plugins.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {Config, Errors, Interfaces, ux} from '@oclif/core'
import chalk from 'chalk'
import {bold} from 'ansis'
import makeDebug from 'debug'
import {spawn} from 'node:child_process'
import {access, mkdir, readFile, rename, rm, writeFile} from 'node:fs/promises'
Expand All @@ -13,10 +13,21 @@ import {Output} from './spawn.js'
import {uniqWith} from './util.js'
import {Yarn} from './yarn.js'

type Plugin = Interfaces.LinkedPlugin | Interfaces.UserPlugin

type UserPJSON = {
dependencies: Record<string, string>
oclif: {
plugins: Array<Interfaces.PJSON.PluginTypes.Link | Interfaces.PJSON.PluginTypes.User>
plugins: Plugin[]
schema: number
}
private: boolean
}

type NormalizedUserPJSON = {
dependencies: Record<string, string>
oclif: {
plugins: Plugin[]
schema: number
}
private: boolean
Expand All @@ -37,14 +48,8 @@ async function fileExists(filePath: string): Promise<boolean> {
}
}

function dedupePlugins(
plugins: Interfaces.PJSON.PluginTypes[],
): (Interfaces.PJSON.PluginTypes.Link | Interfaces.PJSON.PluginTypes.User)[] {
return uniqWith(
plugins,
// @ts-expect-error because typescript doesn't think it's possible for a plugin to have the `link` type here
(a, b) => a.name === b.name || (a.type === 'link' && b.type === 'link' && a.root === b.root),
) as (Interfaces.PJSON.PluginTypes.Link | Interfaces.PJSON.PluginTypes.User)[]
function dedupePlugins(plugins: Plugin[]): Plugin[] {
return uniqWith(plugins, (a, b) => a.name === b.name || (a.type === 'link' && b.type === 'link' && a.root === b.root))
}

function extractIssuesLocation(
Expand All @@ -63,10 +68,10 @@ function extractIssuesLocation(
function notifyUser(plugin: Config, output: Output): void {
const containsWarnings = [...output.stdout, ...output.stderr].some((l) => l.includes('npm WARN'))
if (containsWarnings) {
ux.logToStderr(chalk.bold.yellow(`\nThese warnings can only be addressed by the owner(s) of ${plugin.name}.`))
ux.stderr(bold.yellow(`\nThese warnings can only be addressed by the owner(s) of ${plugin.name}.`))

if (plugin.pjson.bugs || plugin.pjson.repository) {
ux.logToStderr(
ux.stderr(
`We suggest that you create an issue at ${extractIssuesLocation(
plugin.pjson.bugs,
plugin.pjson.repository,
Expand All @@ -93,7 +98,7 @@ export default class Plugins {
})
}

public async add(...plugins: Interfaces.PJSON.PluginTypes[]): Promise<void> {
public async add(...plugins: Plugin[]): Promise<void> {
const pjson = await this.pjson()
const mergedPlugins = [...(pjson.oclif.plugins || []), ...plugins] as typeof pjson.oclif.plugins
await this.savePJSON({
Expand All @@ -112,9 +117,7 @@ export default class Plugins {
return match?.[1] ?? name
}

public async hasPlugin(
name: string,
): Promise<Interfaces.PJSON.PluginTypes.Link | Interfaces.PJSON.PluginTypes.User | false> {
public async hasPlugin(name: string): Promise<Plugin | false> {
const list = await this.list()
const friendlyName = this.friendlyName(name)
const unfriendlyName = this.unfriendlyName(name) ?? name
Expand Down Expand Up @@ -263,7 +266,7 @@ export default class Plugins {
return c
}

public async list(): Promise<(Interfaces.PJSON.PluginTypes.Link | Interfaces.PJSON.PluginTypes.User)[]> {
public async list(): Promise<Plugin[]> {
const pjson = await this.pjson()
return pjson.oclif.plugins
}
Expand All @@ -280,7 +283,7 @@ export default class Plugins {
return name
}

public async pjson(): Promise<UserPJSON> {
public async pjson(): Promise<NormalizedUserPJSON> {
const pjson = await this.readPJSON()
const plugins = pjson ? normalizePlugins(pjson.oclif.plugins) : []
return {
Expand Down Expand Up @@ -330,7 +333,7 @@ export default class Plugins {
}

public async update(): Promise<void> {
let plugins = (await this.list()).filter((p): p is Interfaces.PJSON.PluginTypes.User => p.type === 'user')
let plugins = (await this.list()).filter((p): p is Interfaces.UserPlugin => p.type === 'user')
if (plugins.length === 0) return

await this.maybeCleanUp()
Expand Down Expand Up @@ -360,7 +363,7 @@ export default class Plugins {

const npmPlugins = plugins.filter((p) => !p.url)
const jitPlugins = this.config.pjson.oclif.jitPlugins ?? {}
const modifiedPlugins: Interfaces.PJSON.PluginTypes[] = []
const modifiedPlugins: Plugin[] = []
if (npmPlugins.length > 0) {
await this.npm.install(
npmPlugins.map((p) => {
Expand Down Expand Up @@ -455,9 +458,9 @@ export default class Plugins {
return join(this.config.dataDir, 'package.json')
}

private async readPJSON(): Promise<Interfaces.PJSON.User | undefined> {
private async readPJSON(): Promise<UserPJSON | undefined> {
try {
return JSON.parse(await readFile(this.pjsonPath, 'utf8')) as Interfaces.PJSON.User
return JSON.parse(await readFile(this.pjsonPath, 'utf8')) as UserPJSON
} catch (error: unknown) {
this.debug(error)
const err = error as {code?: string} & Error
Expand All @@ -466,24 +469,22 @@ export default class Plugins {
}

private async savePJSON(pjson: UserPJSON) {
this.debug(`saving pjson at ${this.pjsonPath}`, JSON.stringify(pjson, null, 2))
await mkdir(dirname(this.pjsonPath), {recursive: true})
await writeFile(this.pjsonPath, JSON.stringify({name: this.config.name, ...pjson}, null, 2))
}
}

// if the plugin is a simple string, convert it to an object
const normalizePlugins = (
input: Interfaces.PJSON.User['oclif']['plugins'],
): (Interfaces.PJSON.PluginTypes.Link | Interfaces.PJSON.PluginTypes.User)[] =>
dedupePlugins(
(input ?? []).map((p) =>
typeof p === 'string'
? {
name: p,
tag: 'latest',
type: 'user' as const,
}
: p,
),
const normalizePlugins = (input: Plugin[]): Plugin[] => {
const normalized = (input ?? []).map((p) =>
typeof p === 'string'
? {
name: p,
tag: 'latest',
type: 'user' as const,
}
: p,
)

return dedupePlugins(normalized)
}
4 changes: 2 additions & 2 deletions src/spawn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export async function spawn(modulePath: string, args: string[] = [], {cwd, logLe
stderr.push(output)
if (shouldPrint(output)) {
loggedStderr.push(output)
ux.log(output)
ux.stdout(output)
} else debug(output)
})

Expand All @@ -74,7 +74,7 @@ export async function spawn(modulePath: string, args: string[] = [], {cwd, logLe
stdout.push(output)
if (shouldPrint(output)) {
loggedStdout.push(output)
ux.log(output)
ux.stdout(output)
} else debug(output)
})

Expand Down
2 changes: 1 addition & 1 deletion src/yarn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export class Yarn {
if (this.config.npmRegistry) args.push(`--registry=${this.config.npmRegistry}`)

if (options.logLevel !== 'notice' && options.logLevel !== 'silent') {
ux.logToStderr(`${options.cwd}: ${bin} ${args.join(' ')}`)
ux.stderr(`${options.cwd}: ${bin} ${args.join(' ')}`)
}

debug(`${options.cwd}: ${bin} ${args.join(' ')}`)
Expand Down
Loading

0 comments on commit 6f6aa3b

Please sign in to comment.