Getting started¶
Installation¶
Save the module to your application’s node dependencies.
$ npm install --save commander-mvc
Example Project¶
Note
This example will utilize Babel and ES2017. However you can just as well use typescript for your own project. Since commander-mvc is written in typescript you will not have to install separate type definitions.
For this example, we will create a simple hello world project. The commandline interface (CLI) we will create will print “Hello, world!” as a default message, but will print “Hello, <name>” if the user specifies a name instead of the default ‘world.’
To get started, first create our project directory, and initialize it with npm.
$ mkdir commander-mvc-hello
$ cd commander-mvc-hello
$ npm init -y
Then let’s install all our dependencies.
$ npm install --save @babel/cli @babel/core @babel/plugin-proposal-decorators @babel/polyfill @babel/preset-env commander-mvc
Setup the package.json
so that it looks like below. The important fields to note are bin
and script
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | {
"name": "commander-mvc-hello",
"version": "1.0.0",
"bin": "dist/index.js",
"scripts": {
"build": "babel src -d dist"
},
"license": "MIT",
"dependencies": {
"@babel/cli": "^7.0.0",
"@babel/core": "^7.0.1",
"@babel/plugin-proposal-decorators": "^7.0.0",
"@babel/polyfill": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"commander-mvc": "^1.3.1"
}
}
|
Note
Some of these snippets (like the one above) are captioned with filenames such that you can find them in the docs directory of the main repository.
Now, you will also need to add a .babelrc
.
1 2 3 4 5 6 7 8 | {
"presets": ["@babel/preset-env"],
"plugins": [
["@babel/plugin-proposal-decorators", {
"legacy": true
}]
]
}
|
View¶
Next, we will create our view: message.view.js
. Create a folder
called src
, and put message.view.js
inside it.
1 2 3 4 5 | export class MessageView {
print(message) {
console.log(message)
}
}
|
Note
Views must implement the print method. To learn more, see the View section.
Your project directory should now look like this:
src/
message.view.js
.babelrc
package-lock.json
package.json
Service¶
Now, create a service called hello.service.js
where we will put our
program’s main logic (determining whether we are saying hello to the world or a
specific person).
1 2 3 4 5 6 7 8 9 10 11 | import { Injectable } from 'commander-mvc'
@Injectable()
export class HelloService {
sayHello(name) {
if (name) {
return `Hello, ${name}!`
}
return 'Hello, world!'
}
}
|
Note the Injectable()
decorator. This decorator is what tells commander-mvc
that our class should be available to our controllers via dependency
injection (DI) through the constructor.
Note
Technically it is also available to views via DI, but I think the view should just be concerned with printing simple messages. To learn more, see the Injectable section.
Controller¶
The hello.controller.js
which we will be creating next will tie the view
and service together.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | import { Controller, Action } from 'commander-mvc'
import { MessageView } from './message.view'
@Controller({
command: 'hello [name]'
})
export class HelloController {
constructor({ helloService }) {
this.helloService = helloService
}
takeArg(name) {
this.name = name
}
@Action({
view: MessageView
})
sayHello() {
return this.helloService.sayHello(this.name)
}
}
|
The controller is decorated with Controller()
, which tells commander-mvc to
route the hello
command to our controller with an optional name
argument and no additional options. For more information about commands and
options, see commander.js.
The sayHello
method is decorated with
Action()
, which tells commander-mvc that this method should be called to
handle those commands routed to our controller, and that it should render the
return value using the MessageView
we created earlier.
Note
You can specify on the controller options for the command. Then on each action, you can indicate when the controller should route the command to it depending on which options with which the command was invoked. To learn more, see the Controller and Action section.
Pulling it together¶
The last file to create is index.js
which will create the commander-mvc
context and run our CLI application.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #!/usr/bin/env node
import { initializeContext } from 'commander-mvc'
import { HelloController } from './hello.controller'
import { HelloService } from './hello.service'
import pjson from '../package.json'
const { run } = initializeContext({
name: 'commander-mvc-hello',
version: pjson.version,
providers: [
HelloController,
HelloService
]
})
run(process.argv)
|
Note
Behind the scenes, the context brings all of the command and option definitions together to construct the commander program definition. The context also uses a default entry point which calls parse on the commander instance when run is called. To learn more see the Context and EntryPoint section.
Finally we can build and install our CLI. First cd
into the root of the
project, and run the commands:
$ npm run build
$ npm install -g
Now you can run it like this:
$ commander-mvc-hello --version
1.0.0
$ commander-mvc-hello --help
Usage: commander-mvc-hello [options] [command]
Options:
-V, --version output the version number
-h, --help output usage information
Commands:
hello [name]
$ commander-mvc-hello hello
Hello, world!
$ commander-mvc-hello hello Martin
Hello, Martin!
Congratulations on your first commander-mvc application!