Skip to content

Commit 20da1f1

Browse files
committed
feat: kernel now entertains manifest as a first class citizen
1 parent 786e1f1 commit 20da1f1

File tree

3 files changed

+96
-6
lines changed

3 files changed

+96
-6
lines changed

src/Kernel/index.ts

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,16 @@
1010
import * as getopts from 'getopts'
1111

1212
import { Parser } from '../Parser'
13+
import { Manifest } from '../Manifest'
1314
import { validateCommand } from '../utils/validateCommand'
1415
import { printHelp, printHelpFor } from '../utils/help'
15-
import { CommandConstructorContract, CommandFlag, GlobalFlagHandler } from '../Contracts'
16+
import {
17+
CommandConstructorContract,
18+
CommandFlag,
19+
GlobalFlagHandler,
20+
ManifestCommand,
21+
ManifestNode,
22+
} from '../Contracts'
1623

1724
/**
1825
* Ace kernel class is used to register, find and invoke commands by
@@ -24,11 +31,24 @@ export class Kernel {
2431
*/
2532
public commands: { [name: string]: CommandConstructorContract } = {}
2633

34+
/**
35+
* Reference to commands defined inside the manifest file. This only exists
36+
* when you call [[Kernel.useManifest]].
37+
*/
38+
public manifestCommands?: ManifestNode
39+
2740
/**
2841
* List of registered flags
2942
*/
3043
public flags: { [name: string]: CommandFlag & { handler: GlobalFlagHandler } } = {}
3144

45+
/**
46+
* Reference to the manifest instance. When this exists, the kernel
47+
* will give prefrence to the manifest file over the registered
48+
* commands
49+
*/
50+
private _manifest?: Manifest
51+
3252
/**
3353
* Executing global flag handlers. The global flag handlers are
3454
* not async as of now, but later we can look into making them
@@ -95,6 +115,7 @@ export class Kernel {
95115
*/
96116
public find (argv: string[]): CommandConstructorContract | null {
97117
/**
118+
* ----------------------------------------------------------------------------
98119
* Even though in `Unix` the command name may appear in between or at last, with
99120
* ace we always want the command name to be the first argument. However, the
100121
* arguments to the command itself can appear in any sequence. For example:
@@ -105,6 +126,19 @@ export class Kernel {
105126
*
106127
* Doesn't work
107128
* - node ace foo make:controller
129+
* ----------------------------------------------------------------------------
130+
*/
131+
132+
/**
133+
* Manifest commands gets preference over manually registered commands.
134+
*/
135+
if (this.manifestCommands && this.manifestCommands[argv[0]]) {
136+
return this._manifest!.loadCommand(this.manifestCommands[argv[0]].commandPath)
137+
}
138+
139+
/**
140+
* Try to find command inside manually registered command or fallback
141+
* to null
108142
*/
109143
return this.commands[argv[0]] || null
110144
}
@@ -165,6 +199,15 @@ export class Kernel {
165199
return
166200
}
167201

202+
/**
203+
* Load manifest commands when instance of manifest exists. From here the
204+
* kernel will give preference to the `manifest` file vs manually
205+
* registered commands.
206+
*/
207+
if (this._manifest) {
208+
this.manifestCommands = await this._manifest.load()
209+
}
210+
168211
const hasMentionedCommand = !argv[0].startsWith('-')
169212

170213
/**
@@ -179,22 +222,40 @@ export class Kernel {
179222
/**
180223
* If command doesn't exists, then raise an error for same
181224
*/
182-
const command = this.find(argv)
225+
let command = this.find(argv)
183226
if (!command) {
184227
throw new Error(`${argv[0]} is not a registered command`)
185228
}
186229

187230
return this.runCommand(argv.splice(1), command)
188231
}
189232

233+
/**
234+
* Use manifest instance to lazy load commands
235+
*/
236+
public useManifest (manifest: Manifest): this {
237+
this._manifest = manifest
238+
return this
239+
}
240+
190241
/**
191242
* Print the help screen for a given command or all commands/flags
192243
*/
193244
public printHelp (command?: CommandConstructorContract) {
194245
if (command) {
195246
printHelpFor(command)
196247
} else {
197-
const commands = Object.keys(this.commands).map((name) => this.commands[name])
248+
let commands: ManifestCommand[] | CommandConstructorContract[]
249+
250+
/**
251+
* Using manifest commands over registered commands
252+
*/
253+
if (this.manifestCommands) {
254+
commands = Object.keys(this.manifestCommands).map((name) => this.commands[name])
255+
} else {
256+
commands = Object.keys(this.commands).map((name) => this.commands[name])
257+
}
258+
198259
const flags = Object.keys(this.flags).map((name) => this.flags[name])
199260
printHelp(commands, flags)
200261
}

src/Manifest/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,12 @@ export class Manifest {
2727
/**
2828
* Require and return command
2929
*/
30-
private _getCommand (commandPath: string): CommandConstructorContract {
30+
public loadCommand (commandPath: string): CommandConstructorContract {
3131
const command = esmRequire(join(this._appRoot, commandPath))
3232
if (!command.name) {
3333
throw CommandValidationException.invalidManifestExport(commandPath)
3434
}
3535

36-
validateCommand(command)
3736
return command
3837
}
3938

@@ -57,7 +56,8 @@ export class Manifest {
5756
*/
5857
public async generate (commandPaths: string[]) {
5958
const manifest = commandPaths.reduce((manifest: ManifestNode, commandPath) => {
60-
const command = this._getCommand(commandPath)
59+
const command = this.loadCommand(commandPath)
60+
validateCommand(command)
6161

6262
manifest[command.commandName] = {
6363
commandPath: commandPath,

test/kernel.spec.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ import { Kernel } from '../src/Kernel'
1212
import { BaseCommand } from '../src/BaseCommand'
1313
import { args } from '../src/Decorators/args'
1414
import { flags } from '../src/Decorators/flags'
15+
import { Manifest } from '../src/Manifest'
16+
import { Filesystem } from '@adonisjs/dev-utils'
17+
import { join } from 'path'
18+
19+
const fs = new Filesystem(join(__dirname, '__app'))
1520

1621
test.group('Kernel | register', () => {
1722
test('raise error when required argument comes after optional argument', (assert) => {
@@ -87,6 +92,30 @@ test.group('Kernel | find', () => {
8792
const kernel = new Kernel()
8893
assert.isNull(kernel.find(['greet']))
8994
})
95+
96+
test('find command from manifest when manifestCommands exists', async (assert) => {
97+
const kernel = new Kernel()
98+
const manifest = new Manifest(fs.basePath)
99+
100+
await fs.add(`ace-manifest.json`, JSON.stringify({
101+
greet: {
102+
commandName: 'greet',
103+
commandPath: 'Commands/Greet.ts',
104+
},
105+
}))
106+
107+
await fs.add('Commands/Greet.ts', `export default class Greet {
108+
public static commandName = 'greet'
109+
}`)
110+
111+
kernel.useManifest(manifest)
112+
kernel.manifestCommands = await manifest.load()
113+
114+
const greet = kernel.find(['greet'])
115+
assert.equal(greet!.name, 'Greet')
116+
117+
await fs.cleanup()
118+
})
90119
})
91120

92121
test.group('Kernel | handle', () => {

0 commit comments

Comments
 (0)