Skip to content

Commit ff61f87

Browse files
authoredOct 7, 2021
fix: simplify plugins:inspect (#345)
* fix: simplify plugins:inspect * fix: use cli.tree
1 parent ece7802 commit ff61f87

File tree

1 file changed

+47
-73
lines changed

1 file changed

+47
-73
lines changed
 

‎src/commands/plugins/inspect.ts

+47-73
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,18 @@
11
import * as path from 'path'
22
import {Command, Flags, Plugin} from '@oclif/core'
33
import * as chalk from 'chalk'
4-
import {exec} from 'child_process'
54
import * as fs from 'fs-extra'
5+
import {cli} from 'cli-ux'
66

77
import Plugins from '../../plugins'
88
import {sortBy} from '../../util'
99

10-
const TAB = ' '
11-
12-
type Dependencies = {
13-
[key: string]: unknown;
14-
from?: string;
15-
version?: string;
16-
name?: string;
17-
dependencies: {
18-
[key: string]: Dependencies;
19-
};
10+
function trimUntil(fsPath: string, part: string): string {
11+
const parts = fsPath.split(path.sep)
12+
const indicies = parts.reduce((a, e, i) => (e === part) ? a.concat([i]) : a, [] as number[])
13+
const partIndex = Math.max(...indicies)
14+
if (partIndex === -1) return fsPath
15+
return parts.slice(0, partIndex + 1).join(path.sep)
2016
}
2117

2218
export default class PluginsInspect extends Command {
@@ -41,13 +37,10 @@ export default class PluginsInspect extends Command {
4137

4238
plugins = new Plugins(this.config);
4339

44-
allDeps: Dependencies = {dependencies: {}};
45-
4640
// In this case we want these operations to happen
4741
// sequentially so the `no-await-in-loop` rule is ugnored
4842
/* eslint-disable no-await-in-loop */
4943
async run() {
50-
this.allDeps = await this.npmList(this.config.root, 3)
5144
const {flags, argv} = await this.parse(PluginsInspect)
5245
if (flags.verbose) this.plugins.verbose = true
5346
const aliases = this.config.pjson.oclif.aliases || {}
@@ -61,7 +54,7 @@ export default class PluginsInspect extends Command {
6154
const pluginName = await this.parsePluginName(name)
6255

6356
try {
64-
await this.inspect(pluginName)
57+
await this.inspect(pluginName, flags.verbose)
6558
} catch (error) {
6659
this.log(chalk.bold.red('failed'))
6760
throw error
@@ -88,77 +81,58 @@ export default class PluginsInspect extends Command {
8881
throw new Error(`${pluginName} not installed`)
8982
}
9083

91-
async inspect(pluginName: string) {
84+
async inspect(pluginName: string, verbose = false) {
9285
const plugin = this.findPlugin(pluginName)
93-
this.log(chalk.bold.cyan(plugin.name))
94-
95-
this.log(`${TAB}version: ${plugin.version}`)
96-
if (plugin.tag) this.log(`${TAB}tag: ${plugin.tag}`)
97-
if (plugin.pjson.homepage) this.log(`${TAB}homepage: ${plugin.pjson.homepage}`)
98-
this.log(`${TAB}location: ${plugin.root}`)
99-
100-
this.log(`${TAB}commands:`)
86+
const tree = cli.tree()
87+
const pluginHeader = chalk.bold.cyan(plugin.name)
88+
tree.insert(pluginHeader)
89+
tree.nodes[pluginHeader].insert(`version ${plugin.version}`)
90+
if (plugin.tag) tree.nodes[pluginHeader].insert(`tag ${plugin.tag}`)
91+
if (plugin.pjson.homepage) tree.nodes[pluginHeader].insert(`homepage ${plugin.pjson.homepage}`)
92+
tree.nodes[pluginHeader].insert(`location ${plugin.root}`)
93+
94+
tree.nodes[pluginHeader].insert('commands')
10195
const commands = sortBy(plugin.commandIDs, c => c)
102-
commands.forEach(cmd => this.log(`${TAB.repeat(2)}${cmd}`))
96+
commands.forEach(cmd => tree.nodes[pluginHeader].nodes.commands.insert(cmd))
10397

104-
const dependencies = plugin.root.includes(this.config.root) ?
105-
this.findDepInTree(plugin).dependencies :
106-
(await this.npmList(plugin.root)).dependencies
98+
const dependencies = Object.assign({}, plugin.pjson.dependencies)
10799

108-
this.log(`${TAB}dependencies:`)
100+
tree.nodes[pluginHeader].insert('dependencies')
109101
const deps = sortBy(Object.keys(dependencies), d => d)
110102
for (const dep of deps) {
111103
// eslint-disable-next-line no-await-in-loop
112-
const version = dependencies[dep].version || await this.findDepInSharedModules(plugin, dep)
113-
const from = dependencies[dep].from ?
114-
dependencies[dep].from!.split('@').reverse()[0] :
115-
null
104+
const {version, pkgPath} = await this.findDep(plugin, dep)
105+
if (!version) continue
116106

117-
if (from) this.log(`${TAB.repeat(2)}${dep}: ${from} => ${version}`)
118-
else this.log(`${TAB.repeat(2)}${dep}: ${version}`)
119-
}
120-
}
107+
const from = dependencies[dep] ?? null
108+
const versionMsg = chalk.dim(from ? `${from} => ${version}` : version)
109+
const msg = verbose ? `${dep} ${versionMsg} ${pkgPath}` : `${dep} ${versionMsg}`
121110

122-
async findDepInSharedModules(plugin: Plugin, dependency: string): Promise<string> {
123-
const sharedModulePath = path.join(plugin.root.replace(plugin.name, ''), ...dependency.split('/'), 'package.json')
124-
const pkgJson = JSON.parse(await fs.readFile(sharedModulePath, 'utf-8'))
125-
return pkgJson.version as string
111+
tree.nodes[pluginHeader].nodes.dependencies.insert(msg)
112+
}
113+
tree.display()
126114
}
127115

128-
findDepInTree(plugin: Plugin): Dependencies {
129-
if (plugin.name === this.allDeps.name) return this.allDeps
130-
const plugins = [plugin.name]
131-
let p = plugin
132-
while (p.parent) {
133-
plugins.push(p.parent.name)
134-
p = p.parent
116+
async findDep(plugin: Plugin, dependency: string): Promise<{ version: string | null; pkgPath: string | null}> {
117+
const dependencyPath = path.join(...dependency.split('/'))
118+
let start = path.join(plugin.root, 'node_modules')
119+
const paths = [start]
120+
while ((start.match(/node_modules/g) || []).length > 1) {
121+
start = trimUntil(path.dirname(start), 'node_modules')
122+
paths.push(start)
135123
}
136124

137-
let dependencies = this.allDeps
138-
for (const plg of plugins.reverse()) {
139-
dependencies = dependencies.dependencies[plg]
125+
for (const p of paths) {
126+
const fullPath = path.join(p, dependencyPath)
127+
const pkgJsonPath = path.join(fullPath, 'package.json')
128+
try {
129+
// eslint-disable-next-line no-await-in-loop
130+
const pkgJson = JSON.parse(await fs.readFile(pkgJsonPath, 'utf-8'))
131+
return {version: pkgJson.version as string, pkgPath: fullPath}
132+
} catch {
133+
// try the next path
134+
}
140135
}
141-
return dependencies
142-
}
143-
144-
async npmList(cwd: string, depth = 0): Promise<Dependencies> {
145-
return new Promise((resolve, reject) => {
146-
exec(`npm list --json --depth ${depth}`, {
147-
cwd,
148-
encoding: 'utf-8',
149-
maxBuffer: 2048 * 2048,
150-
}, (error, stdout) => {
151-
if (error) {
152-
try {
153-
const parsed = JSON.parse(stdout)
154-
if (parsed) resolve(parsed)
155-
} catch {
156-
reject(new Error(`Could not get dependencies for ${cwd}`))
157-
}
158-
} else {
159-
resolve(JSON.parse(stdout))
160-
}
161-
})
162-
})
136+
return {version: null, pkgPath: null}
163137
}
164138
}

0 commit comments

Comments
 (0)