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.

examples/hello/package.json
 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.

examples/hello/.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.

examples/hello/src/message.view.js
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).

examples/hello/src/hello.service.js
 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.

examples/hello/src/hello.controller.js
 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.

examples/hello/src/index.js
 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!