Skip to content

Commit

Permalink
feat: Add experimental CLI for LoopBack 4
Browse files Browse the repository at this point in the history
Usage:
npm i -g @loopback/cli
lb4 app <name>
lb4 extension <name>
  • Loading branch information
Raymond Feng authored and raymondfeng committed Nov 6, 2017
1 parent a351932 commit 707f692
Show file tree
Hide file tree
Showing 50 changed files with 1,607 additions and 1 deletion.
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
packages/*/dist
packages/*/dist6
packages/*/api-docs
packages/cli/generators/*/templates
package.json
packages/*/package.json
2 changes: 2 additions & 0 deletions packages/cli/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
coverage
1 change: 1 addition & 0 deletions packages/cli/.npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package-lock=false
6 changes: 6 additions & 0 deletions packages/cli/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"bracketSpacing": false,
"singleQuote": true,
"printWidth": 80,
"trailingComma": "es5"
}
2 changes: 2 additions & 0 deletions packages/cli/.yo-rc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{
}
57 changes: 57 additions & 0 deletions packages/cli/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# @loopback/cli

This module contains the experimental CLI for LoopBack 4.

## Installation

Run the following command to install the CLI.

`npm install -g @loopback/cli`

## Usage

1. To scaffold a LoopBack 4 application

`lb4 app`

```
Usage:
lb4 app [options] [<name>]
Options:
-h, --help # Print the generator's options and usage
--skip-cache # Do not remember prompt answers Default: false
--skip-install # Do not automatically install dependencies Default: false
--applicationName # Application name
--description # Description for the application
--outdir # Project root directory for the application
--tslint # Enable tslint
--prettier # Enable prettier
--mocha # Enable mocha
--loopbackBuild # Use @loopback/build
Arguments:
name # Project name for the application Type: String Required: false
```

2. To scaffold a LoopBack 4 extension

`lb4 extension`

```
Usage:
lb4 extension [options] [<name>]
Options:
-h, --help # Print the generator's options and usage
--skip-cache # Do not remember prompt answers Default: false
--skip-install # Do not automatically install dependencies Default: false
--description # Description for the extension
--outdir # Project root directory for the extension
--tslint # Enable tslint
--prettier # Enable prettier
--mocha # Enable mocha
--loopbackBuild # Use @loopback/build
--componentName # Component name
```
65 changes: 65 additions & 0 deletions packages/cli/bin/cli.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#!/usr/bin/env node
// Copyright IBM Corp. 2017. All Rights Reserved.
// Node module: @loopback/cli
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

'use strict';

const assert = require('assert');
const camelCaseKeys = require('camelcase-keys');
const debug = require('debug')('loopback:cli');
const minimist = require('minimist');
const path = require('path');
const yeoman = require('yeoman-environment');

const opts = minimist(process.argv.slice(2), {
alias: {
help: 'h',
version: 'v',
commands: 'l',
},
});

if (opts.version) {
const ver = require('../package.json').version;
console.log('Version: %s', ver);
return;
}

var env = yeoman.createEnv();

env.register(path.join(__dirname, '../generators/app'), 'loopback4:app');
env.register(path.join(__dirname, '../generators/extension'), 'loopback4:extension');

// list generators
if (opts.commands) {
console.log('Available commands: ');
var list = Object.keys(env.getGeneratorsMeta())
.filter(name => /^loopback4:/.test(name))
.map(name => name.replace(/^loopback4:/, ' lb4 '));
console.log(list.join('\n'));
return;
}

const args = opts._;
const originalCommand = args.shift();
let command = 'loopback4:' + (originalCommand || 'app');
const supportedCommands = env.getGeneratorsMeta();

if (!(command in supportedCommands)) {
command = 'loopback4:app';
args.unshift(originalCommand);
args.unshift(command);
} else {
args.unshift(command);
}

debug('invoking generator', args);

// `yo` is adding flags converted to CamelCase
const options = camelCaseKeys(opts, {exclude: ['--', /^\w$/, 'argv']});
Object.assign(options, opts);

debug('env.run %j %j', args, options);
env.run(args, options);
78 changes: 78 additions & 0 deletions packages/cli/generators/app/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright IBM Corp. 2017. All Rights Reserved.
// Node module: @loopback/cli
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

'use strict';
const ProjectGenerator = require('../../lib/project-generator');
const utils = require('../../lib/utils');

module.exports = class extends ProjectGenerator {
// Note: arguments and options should be defined in the constructor.
constructor(args, opts) {
super(args, opts);
}

_setupGenerator() {
this.projectType = 'application';
this.option('applicationName', {
type: String,
description: 'Application name',
});
return super._setupGenerator();
}

setOptions() {
return super.setOptions();
}

promptProjectName() {
return super.promptProjectName();
}

promptProjectDir() {
return super.promptProjectDir();
}

promptApplication() {
const prompts = [
{
type: 'input',
name: 'applicationName',
message: 'Application class name:',
default: utils.toClassName(this.projectInfo.name) + 'Application',
},
];

return this.prompt(prompts).then(props => {
Object.assign(this.projectInfo, props);
});
}

promptOptions() {
return super.promptOptions();
}

scaffold() {
return super.scaffold();
}

install() {
return super.install();
}

end() {
this.log();
this.log(
'Application %s is now created in %s.',
this.projectInfo.name,
this.projectInfo.outdir
);
this.log();
this.log('Next steps:');
this.log();
this.log('$ cd ' + this.projectInfo.outdir);
this.log('$ npm start');
this.log();
}
};
14 changes: 14 additions & 0 deletions packages/cli/generators/app/templates/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright IBM Corp. 2017. All Rights Reserved.
// Node module: <%= project.name %>
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

const nodeMajorVersion = +process.versions.node.split('.')[0];
const dist = nodeMajorVersion >= 7 ? './dist' : './dist6';

const application = (module.exports = require(dist));

if (require.main === module) {
// Run the application
application.main();
}
27 changes: 27 additions & 0 deletions packages/cli/generators/app/templates/src/application.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright IBM Corp. 2017. All Rights Reserved.
// Node module: <%= project.name %>
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

import {Application, ApplicationConfig} from '@loopback/core';
import {RestComponent} from '@loopback/rest';
import {PingController} from './controllers/ping-controller';

export class <%= project.applicationName %> extends Application {
constructor(options?: ApplicationConfig) {
// Allow options to replace the defined components array, if desired.
options = Object.assign(
{},
{
components: [RestComponent],
},
options,
);
super(options);
this.setupControllers();
}

setupControllers() {
this.controller(PingController);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Controllers

This directory contains source files for the controllers exported by this app.
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright IBM Corp. 2017. All Rights Reserved.
// Node module: <%= project.name %>
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

import {get, ServerRequest} from '@loopback/rest';
import {inject} from '@loopback/context';

/**
* A simple controller to bounce back http requests
*/
export class PingController {
constructor(@inject('rest.http.request') private req: ServerRequest) {}

// Map to `GET /ping`
@get('/ping')
ping(): object {
return {
greeting: 'Hello from LoopBack',
date: new Date(),
url: this.req.url,
headers: Object.assign({}, this.req.headers),
};
}
}
25 changes: 25 additions & 0 deletions packages/cli/generators/app/templates/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright IBM Corp. 2017. All Rights Reserved.
// Node module: <%= project.name %>
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

import {<%= project.applicationName %>} from './application';
import {RestServer} from '@loopback/rest';
import {ApplicationConfig} from '@loopback/core';

export {<%= project.applicationName %>};

export async function main(options?: ApplicationConfig) {
const app = new <%= project.applicationName %>(options);

try {
await app.start();
const server = await app.getServer(RestServer);
const port = await server.get('rest.port');
console.log(`Server is running at http://127.0.0.1:${port}`);
console.log(`Try http://127.0.0.1:${port}/ping`);
} catch (err) {
console.error(`Unable to start application: ${err}`);
}
return app;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Repositories

This directory contains code for repositories provided by this app.
4 changes: 4 additions & 0 deletions packages/cli/generators/app/templates/test/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Tests

Please place your tests in this folder.

46 changes: 46 additions & 0 deletions packages/cli/generators/app/templates/test/ping-controller.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright IBM Corp. 2017. All Rights Reserved.
// Node module: <%= project.name %>
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

import {createClientForHandler, supertest} from '@loopback/testlab';
import {RestServer} from '@loopback/rest';
import {<%= project.applicationName %>} from '../';

describe('PingController', () => {
let app: <%= project.applicationName %>;
let server: RestServer;
let client: supertest.SuperTest<supertest.Test>;

before(givenAnApplication);

before(givenARestServer);

before(async () => {
await app.start();
});

before(() => {
client = createClientForHandler(server.handleHttp);
});

after(async () => {
await app.stop();
});

it('invokes GET /ping', async () => {
await client.get('/ping?msg=world').expect(200);
});

function givenAnApplication() {
app = new <%= project.applicationName %>({
rest: {
port: 0,
},
});
}

async function givenARestServer() {
server = await app.getServer(RestServer);
}
});
Loading

0 comments on commit 707f692

Please sign in to comment.