Skip to content
This repository was archived by the owner on Aug 22, 2023. It is now read-only.

Commit 136e7d4

Browse files
authored
feat: add loading for alternative help classes (#85)
1 parent a686865 commit 136e7d4

File tree

9 files changed

+413
-32
lines changed

9 files changed

+413
-32
lines changed

package.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
"author": "Jeff Dickey @jdxcode",
66
"bugs": "https://github.com/oclif/command/issues",
77
"dependencies": {
8-
"@oclif/config": "^1",
8+
"@oclif/config": "^1.15.1",
99
"@oclif/errors": "^1.2.2",
1010
"@oclif/parser": "^3.8.3",
11-
"@oclif/plugin-help": "^2",
11+
"@oclif/plugin-help": "^3",
1212
"debug": "^4.1.1",
1313
"semver": "^5.6.0"
1414
},
@@ -27,12 +27,12 @@
2727
"fancy-test": "^1.4.3",
2828
"globby": "^9.0.0",
2929
"mocha": "^6.0.2",
30-
"ts-node": "^8.0.3",
31-
"typescript": "^3.3.3333"
30+
"sinon": "^9.0.1",
31+
"ts-node": "^8.8.2",
32+
"typescript": "^3.8.3"
3233
},
3334
"peerDependencies": {
34-
"@oclif/config": "^1",
35-
"@oclif/plugin-help": "^2"
35+
"@oclif/config": "^1"
3636
},
3737
"engines": {
3838
"node": ">=8.0.0"

src/command.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@ const pjson = require('../package.json')
22
import * as Config from '@oclif/config'
33
import * as Errors from '@oclif/errors'
44
import * as Parser from '@oclif/parser'
5-
import Help from '@oclif/plugin-help'
5+
import {HelpBase} from '@oclif/plugin-help'
66
import {format, inspect} from 'util'
77

88
import * as flags from './flags'
99
import {sortBy, uniqBy} from './util'
10+
import {getHelpClass} from '@oclif/plugin-help'
1011

1112
/**
1213
* swallows stdout epipe errors
@@ -186,8 +187,8 @@ export default abstract class Command {
186187
}
187188

188189
protected _help() {
189-
const HHelp: typeof Help = require('@oclif/plugin-help').default
190-
const help = new HHelp(this.config)
190+
const HelpClass = getHelpClass(this.config)
191+
const help: HelpBase = new HelpClass(this.config)
191192
const cmd = Config.Command.toCached(this.ctor as any as Config.Command.Class)
192193
if (!cmd.id) cmd.id = ''
193194
let topics = this.config.topics

src/main.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import * as Config from '@oclif/config'
2-
import Help from '@oclif/plugin-help'
2+
import {HelpBase} from '@oclif/plugin-help'
33

44
import {Command} from '.'
5+
import {getHelpClass} from '@oclif/plugin-help'
56

67
export class Main extends Command {
78
static run(argv = process.argv.slice(2), options?: Config.LoadOptions) {
@@ -36,8 +37,8 @@ export class Main extends Command {
3637
}
3738

3839
protected _help() {
39-
const HHelp: typeof Help = require('@oclif/plugin-help').default
40-
const help = new HHelp(this.config)
40+
const HelpClass = getHelpClass(this.config)
41+
const help: HelpBase = new HelpClass(this.config)
4142
help.showHelp(this.argv)
4243
return this.exit(0)
4344
}

test/command.test.ts

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ import * as Config from '@oclif/config'
22
import {expect, fancy} from 'fancy-test'
33

44
import Base, {flags} from '../src'
5+
import {TestHelpClassConfig} from './helpers/test-help-in-src/src/test-help-plugin'
6+
import * as PluginHelp from '@oclif/plugin-help'
7+
8+
const originalgetHelpClass = PluginHelp.getHelpClass
59

610
// const pjson = require('../package.json')
711

@@ -267,6 +271,144 @@ USAGE
267271
268272
`)
269273
})
274+
275+
fancy
276+
.stdout()
277+
.add('config', async () => {
278+
const config: TestHelpClassConfig = await Config.load()
279+
config.pjson.oclif.helpClass = 'help-class-does-not-exist'
280+
return config
281+
})
282+
.do(async ({config}) => {
283+
class CMD extends Command {
284+
config = config
285+
}
286+
await CMD.run(['-h'])
287+
})
288+
.catch((error: Error) => expect(error.message).to.contain('Unable to load configured help class "help-class-does-not-exist", failed with message:\n'))
289+
.it('shows useful error message when configured help class cannot be loaded')
290+
291+
fancy
292+
.stdout()
293+
.stub(PluginHelp, 'getHelpClass', function (config: any) {
294+
return originalgetHelpClass(config, '')
295+
})
296+
.add('config', async () => {
297+
const config: TestHelpClassConfig = await Config.load()
298+
config.pjson.oclif.helpClass = undefined
299+
return config
300+
})
301+
.do(async ({config}) => {
302+
class CMD extends Command {
303+
config = config
304+
}
305+
await CMD.run(['-h'])
306+
})
307+
.catch((error: Error) => expect(error.message).to.contain('Could not load a help class, consider installing the @oclif/plugin-help package, failed with message:\n'))
308+
.it('shows useful error message when no help class has been configured and the default cannot be loaded')
309+
310+
describe('from a help class', () => {
311+
fancy
312+
.stdout()
313+
.stub(PluginHelp, 'getHelpClass', function (config: Config.IConfig) {
314+
const patchedConfig = {
315+
...config,
316+
root: `${__dirname}/helpers/test-help-in-lib/`,
317+
}
318+
319+
return originalgetHelpClass(patchedConfig)
320+
})
321+
.add('config', async () => {
322+
const config: TestHelpClassConfig = await Config.load()
323+
config.pjson.oclif.helpClass = './lib/test-help-plugin'
324+
return config
325+
})
326+
.do(async ({config}) => {
327+
class CMD extends Command {
328+
static id = 'test-command-for-help-plugin'
329+
330+
config = config
331+
}
332+
await CMD.run(['-h'])
333+
})
334+
.catch(/EEXIT: 0/)
335+
.it('-h via a plugin in lib dir (compiled to js)', ctx => {
336+
expect(ctx.stdout).to.equal('hello from test-help-plugin #showCommandHelp in the lib folder and in compiled javascript\n')
337+
expect(ctx.config.showCommandHelpSpy!.getCalls().length).to.equal(1)
338+
expect(ctx.config.showHelpSpy!.getCalls().length).to.equal(0)
339+
const [Command, Topics] = ctx.config.showCommandHelpSpy!.firstCall.args
340+
expect(Command.id).to.deep.equal('test-command-for-help-plugin')
341+
expect(Topics).to.be.an('array')
342+
})
343+
344+
fancy
345+
.stdout()
346+
.stub(PluginHelp, 'getHelpClass', function (config: Config.IConfig) {
347+
const patchedConfig = {
348+
...config,
349+
root: `${__dirname}/helpers/test-help-in-src/`,
350+
}
351+
352+
return originalgetHelpClass(patchedConfig)
353+
})
354+
.add('config', async () => {
355+
const config: TestHelpClassConfig = await Config.load()
356+
config.pjson.oclif.helpClass = './lib/test-help-plugin'
357+
return config
358+
})
359+
.do(async ({config}) => {
360+
class CMD extends Command {
361+
static id = 'test-command-for-help-plugin'
362+
363+
config = config
364+
}
365+
await CMD.run(['-h'])
366+
})
367+
.catch(/EEXIT: 0/)
368+
.it('-h via a plugin in src dir (source in ts)', ctx => {
369+
expect(ctx.stdout).to.equal('hello from test-help-plugin #showCommandHelp\n')
370+
expect(ctx.config.showCommandHelpSpy!.getCalls().length).to.equal(1)
371+
expect(ctx.config.showHelpSpy!.getCalls().length).to.equal(0)
372+
const [Command, Topics] = ctx.config.showCommandHelpSpy!.firstCall.args
373+
expect(Command.id).to.deep.equal('test-command-for-help-plugin')
374+
expect(Topics).to.be.an('array')
375+
})
376+
377+
fancy
378+
.stdout()
379+
.stub(PluginHelp, 'getHelpClass', function (config: Config.IConfig) {
380+
const patchedConfig = {
381+
...config,
382+
root: `${__dirname}/helpers/test-help-in-src/`,
383+
}
384+
385+
return originalgetHelpClass(patchedConfig)
386+
})
387+
.add('config', async () => {
388+
const config: TestHelpClassConfig = await Config.load()
389+
config.pjson.oclif.helpClass = './lib/test-help-plugin'
390+
return config
391+
})
392+
.do(async ({config}) => {
393+
class CMD extends Command {
394+
static id = 'test-command-for-help-plugin'
395+
396+
config = config
397+
398+
static flags = {help: flags.help()}
399+
}
400+
return CMD.run(['--help'])
401+
})
402+
.catch(/EEXIT: 0/)
403+
.it('--help via a plugin in src dir (source in ts)', ctx => {
404+
expect(ctx.stdout).to.equal('hello from test-help-plugin #showCommandHelp\n')
405+
expect(ctx.config.showCommandHelpSpy!.getCalls().length).to.equal(1)
406+
expect(ctx.config.showHelpSpy!.getCalls().length).to.equal(0)
407+
const [Command, Topics] = ctx.config.showCommandHelpSpy!.firstCall.args
408+
expect(Command.id).to.deep.equal('test-command-for-help-plugin')
409+
expect(Topics).to.be.an('array')
410+
})
411+
})
270412
})
271413

272414
describe('.log()', () => {
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/* eslint-disable */
2+
'use strict';
3+
Object.defineProperty (exports, '__esModule', {value: true});
4+
const sinon_1 = require ('sinon');
5+
class default_1 {
6+
constructor (config, opts) {
7+
this.showCommandHelp = sinon_1.spy (() => {
8+
console.log ('hello from test-help-plugin #showCommandHelp in the lib folder and in compiled javascript');
9+
});
10+
this.showHelp = sinon_1.spy (() => {
11+
console.log ('hello showHelp');
12+
});
13+
config.showCommandHelpSpy = this.showCommandHelp;
14+
config.showHelpSpy = this.showHelp;
15+
}
16+
command () {
17+
throw new Error ('not needed for testing @oclif/command');
18+
}
19+
}
20+
exports.default = default_1;
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import {HelpBase} from '@oclif/plugin-help'
2+
import {spy, SinonSpy} from 'sinon'
3+
import {IConfig} from '@oclif/config'
4+
5+
export type TestHelpClassConfig = IConfig & { showCommandHelpSpy?: SinonSpy; showHelpSpy?: SinonSpy }
6+
7+
export default class extends HelpBase {
8+
constructor(config: any, opts: any) {
9+
super(config, opts)
10+
config.showCommandHelpSpy = this.showCommandHelp
11+
config.showHelpSpy = this.showHelp
12+
}
13+
14+
showCommandHelp = spy(() => {
15+
console.log('hello from test-help-plugin #showCommandHelp')
16+
})
17+
18+
showHelp = spy(() => {
19+
console.log('hello showHelp')
20+
})
21+
22+
getCommandHelpForReadme(): string {
23+
throw new Error('not needed for testing @oclif/command')
24+
}
25+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"compilerOptions": {
3+
"rootDir": "./src",
4+
"outDir": "./lib"
5+
}
6+
}

test/main.test.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
import {expect, fancy} from 'fancy-test'
22

33
import {Main} from '../src/main'
4+
import * as PluginHelp from '@oclif/plugin-help'
5+
import * as Config from '@oclif/config'
6+
import {TestHelpClassConfig} from './helpers/test-help-in-src/src/test-help-plugin'
47

58
const pjson = require('../package.json')
69
const version = `@oclif/command/${pjson.version} ${process.platform}-${process.arch} node-${process.version}`
10+
const originalgetHelpClass = PluginHelp.getHelpClass
711

812
describe('main', () => {
913
fancy
@@ -31,10 +35,71 @@ VERSION
3135
USAGE
3236
$ @oclif/command [COMMAND]
3337
38+
TOPICS
39+
plugins list installed plugins
40+
3441
COMMANDS
3542
help display help for @oclif/command
3643
plugins list installed plugins
3744
3845
`))
3946
.it('runs -h')
47+
48+
describe('with an alternative help class', async () => {
49+
const getMainWithHelpClass = async () => {
50+
const config: TestHelpClassConfig = await Config.load()
51+
config.pjson.oclif.helpClass = './lib/test-help-plugin'
52+
53+
class MainWithHelpClass extends Main {
54+
config = config
55+
}
56+
57+
return MainWithHelpClass
58+
}
59+
60+
fancy
61+
.stdout()
62+
.stub(PluginHelp, 'getHelpClass', function (config: Config.IConfig) {
63+
const patchedConfig = {
64+
...config,
65+
root: `${__dirname}/helpers/test-help-in-src/`,
66+
}
67+
68+
return originalgetHelpClass(patchedConfig)
69+
})
70+
.do(async () => (await getMainWithHelpClass()).run(['-h']))
71+
.catch('EEXIT: 0')
72+
.do(output => expect(output.stdout).to.equal('hello showHelp\n'))
73+
.it('works with -h')
74+
75+
fancy
76+
.stdout()
77+
.stub(PluginHelp, 'getHelpClass', function (config: Config.IConfig) {
78+
const patchedConfig = {
79+
...config,
80+
root: `${__dirname}/helpers/test-help-in-src/`,
81+
}
82+
83+
return originalgetHelpClass(patchedConfig)
84+
})
85+
.do(async () => (await getMainWithHelpClass()).run(['--help']))
86+
.catch('EEXIT: 0')
87+
.do(output => expect(output.stdout).to.equal('hello showHelp\n'))
88+
.it('works with --help')
89+
90+
fancy
91+
.stdout()
92+
.stub(PluginHelp, 'getHelpClass', function (config: Config.IConfig) {
93+
const patchedConfig = {
94+
...config,
95+
root: `${__dirname}/helpers/test-help-in-src/`,
96+
}
97+
98+
return originalgetHelpClass(patchedConfig)
99+
})
100+
.do(async () => (await getMainWithHelpClass()).run(['help']))
101+
.catch('EEXIT: 0')
102+
.do(output => expect(output.stdout).to.equal('hello showHelp\n'))
103+
.it('works with help')
104+
})
40105
})

0 commit comments

Comments
 (0)