Skip to content

Commit

Permalink
feat(nest): init support for nestjs framework (#65)
Browse files Browse the repository at this point in the history
  • Loading branch information
miguelramos authored and NathanWalker committed Jan 7, 2019
1 parent f12c302 commit d49c859
Show file tree
Hide file tree
Showing 17 changed files with 676 additions and 48 deletions.
33 changes: 33 additions & 0 deletions src/app.nest/_files/e2e/app/app.e2e-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import * as request from 'supertest';
import { Test } from '@nestjs/testing';
import { ApplicationModule } from '../../src/app.module';
import { AppService } from '../../src/app.service';
import { INestApplication } from '@nestjs/common';

describe('Application', () => {
let app: INestApplication;
let appService = { get: () => 'Hello world' };

beforeAll(async () => {
const module = await Test.createTestingModule({
imports: [ApplicationModule]
})
.overrideProvider(AppService)
.useValue(appService)
.compile();

app = module.createNestApplication();
await app.init();
});

it(`/GET app`, () => {
return request(app.getHttpServer())
.get('/')
.expect(200)
.expect(appService.get());
});

afterAll(async () => {
await app.close();
});
});
13 changes: 13 additions & 0 deletions src/app.nest/_files/e2e/jest-e2e.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"moduleFileExtensions": ["ts", "tsx", "js", "json"],
"transform": {
"^.+\\.tsx?$": "../../../node_modules/ts-jest/preprocessor.js"
},
"testRegex": "/e2e/.*\\.(e2e-test|e2e-spec).(ts|tsx|js)$",
"collectCoverageFrom": [
"src/**/*.{js,jsx,tsx,ts}",
"!**/node_modules/**",
"!**/vendor/**"
],
"coverageReporters": ["json", "lcov"]
}
13 changes: 13 additions & 0 deletions src/app.nest/_files/jest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"moduleFileExtensions": ["ts", "tsx", "js", "json"],
"transform": {
"^.+\\.tsx?$": "../../node_modules/ts-jest/preprocessor.js"
},
"testRegex": "/src/.*\\.(test|spec).(ts|tsx|js)$",
"collectCoverageFrom": [
"src/**/*.{js,jsx,tsx,ts}",
"!**/node_modules/**",
"!**/vendor/**"
],
"coverageReporters": ["json", "lcov"]
}
15 changes: 15 additions & 0 deletions src/app.nest/_files/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "nest-<%= utils.sanitize(name) %>",
"version": "1.0.0",
"description": "<%= utils.classify(name) %> description.",
"license": "MIT",
"main": "src/main.ts",
"author": {
"name": "Your name",
"email": "name@company.com"
},
"homepage": "https://nstudio.io/xplat",
"repository": {
"url": "https://github.com/nstudio/xplat"
}
}
27 changes: 27 additions & 0 deletions src/app.nest/_files/src/app.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Test } from "@nestjs/testing";
import { AppController } from "./app.controller";
import { AppService } from "./app.service";

describe("App controller", () => {
let appController: AppController;
let appService: AppService;

beforeEach(async () => {
const module = await Test.createTestingModule({
controllers: [AppController],
providers: [AppService]
}).compile();

appService = module.get<AppService>(AppService);
appController = module.get<AppController>(AppController);
});

describe("hello world", () => {
it("should get response", async () => {
const result = "Hello world";
jest.spyOn(appService, "get").mockImplementation(() => result);

expect(await appController.root()).toBe(result);
});
});
});
12 changes: 12 additions & 0 deletions src/app.nest/_files/src/app.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Get, Controller } from "@nestjs/common";
import { AppService } from "./app.service";

@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}

@Get()
root(): string {
return this.appService.get();
}
}
10 changes: 10 additions & 0 deletions src/app.nest/_files/src/app.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Module } from "@nestjs/common";
import { AppController } from "./app.controller";
import { AppService } from "./app.service";

@Module({
imports: [],
controllers: [AppController],
providers: [AppService]
})
export class ApplicationModule {}
8 changes: 8 additions & 0 deletions src/app.nest/_files/src/app.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Injectable } from "@nestjs/common";

@Injectable()
export class AppService {
get() {
return "Hello world!";
}
}
31 changes: 31 additions & 0 deletions src/app.nest/_files/src/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { NestFactory } from "@nestjs/core";
import { ApplicationModule } from "./app.module";

import * as helmet from "helmet";
// import * as csurf from "csurf";
import * as rateLimit from "express-rate-limit";
import * as compression from "compression";

declare const module: any;

async function bootstrap() {
const app = await NestFactory.create(ApplicationModule, { cors: true });

app.use(helmet());
//app.use(csurf());
app.use(
rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100 // limit each IP to 100 requests per windowMs
})
);
app.use(compression());

await app.listen(9000);

if (module.hot) {
module.hot.accept();
module.hot.dispose(() => app.close());
}
}
bootstrap();
23 changes: 23 additions & 0 deletions src/app.nest/_files/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"compilerOptions": {
"module": "commonjs",
"declaration": false,
"noImplicitAny": false,
"removeComments": true,
"noLib": false,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"target": "es6",
"sourceMap": true,
"allowJs": true,
"outDir": "../../dist/apps/nest-<%= utils.sanitize(name) %>",
"typeRoots": ["../../node_modules/@types"],
"types": ["node", "jest"]
},
"include": ["src/**/*"],
"exclude": [
"../../node_modules",
"**/*.spec.ts",
"node_modules/@types/jasmine/**/*"
]
}
117 changes: 117 additions & 0 deletions src/app.nest/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import { Schema as ApplicationOptions } from "./schema";
import {
SchematicsException,
chain,
Tree,
SchematicContext,
branchAndMerge,
mergeWith,
apply,
url,
template,
move,
Rule,
noop
} from "@angular-devkit/schematics";
import { NodePackageInstallTask } from "@angular-devkit/schematics/tasks";
import {
stringUtils,
getNpmScope,
getPrefix,
addRootDeps,
getJsonFromFile,
updatePackageScripts,
addPostinstallers,
formatFiles,
updateNxProjects
} from "../utils";

export default function(options: ApplicationOptions) {
if (!options.name) {
throw new SchematicsException(
`Missing name argument. Provide a name for your Nest app. Example: ng g app.nest sample`
);
}

const appPath = `nest-${options.name}`;

return chain([
// create app files
(tree: Tree, context: SchematicContext) =>
addAppFiles(options, appPath)(tree, context),
// add root package dependencies
(tree: Tree) => addRootDeps(tree, { nest: true }),
// add npm scripts
(tree: Tree) => {
const packageConfig = getJsonFromFile(tree, "package.json");
const scripts = packageConfig.scripts || {};

scripts[`serve.nest.${options.name}`] = `ts-node -P apps/nest-${
options.name
}/tsconfig.json apps/nest-${options.name}/src/main.ts`;
scripts[`start.nest.${options.name}`] = `npm-run-all -p serve.nest.${
options.name
}`;
scripts[`build.nest.${options.name}`] = `tsc -p apps/nest-${
options.name
}`;
scripts[`test.nest.${options.name}`] = `jest --config=apps/nest-${
options.name
}/jest.json`;
scripts[
`test.nest.${options.name}.coverage`
] = `jest --config=apps/nest-${
options.name
}/jest.json --coverage --coverageDirectory=coverage`;
scripts[`test.nest.${options.name}.watch`] = `jest --config=apps/nest-${
options.name
}/jest.json --watch`;
scripts[`test.nest.${options.name}.e2e`] = `jest --config=apps/nest-${
options.name
}/e2e/jest-e2e.json --forceExit`;
scripts[
`test.nest.${options.name}.e2e.watch`
] = `jest --config=apps/nest-${options.name}/e2e/jest-e2e.json --watch`;

return updatePackageScripts(tree, scripts);
},
// nx.json
(tree: Tree) => {
const projects = {};
projects[`nest-${options.name}`] = {
tags: []
};
return updateNxProjects(tree, projects);
},
addInstall,
addPostinstallers(),
options.skipFormat ? noop() : formatFiles(options)
]);
}

function addInstall(host: Tree, context: SchematicContext) {
context.addTask(new NodePackageInstallTask());
return host;
}

function addAppFiles(
options: ApplicationOptions,
appPath: string,
sample: string = ""
): Rule {
sample = "";
return branchAndMerge(
mergeWith(
apply(url(`./_${sample}files`), [
template({
...(options as any),
utils: stringUtils,
npmScope: getNpmScope(),
prefix: getPrefix(),
dot: "."
}),
move(`apps/${appPath}`)
])
)
);
}
65 changes: 65 additions & 0 deletions src/app.nest/index_spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { Tree, VirtualTree } from "@angular-devkit/schematics";
import { Schema as ApplicationOptions } from "./schema";
import { SchematicTestRunner } from "@angular-devkit/schematics/testing";

import * as path from "path";
import { createEmptyWorkspace, getFileContent } from "../utils";

describe("app.nest schematic", () => {
const schematicRunner = new SchematicTestRunner(
"@nstudio/schematics",
path.join(__dirname, "../collection.json")
);
const defaultOptions: ApplicationOptions = {
name: "foo",
npmScope: "testing"
};

let appTree: Tree;

beforeEach(() => {
appTree = new VirtualTree();
appTree = createEmptyWorkspace(appTree);
});

it("should create all files for node app", () => {
const options: ApplicationOptions = { ...defaultOptions };
const tree = schematicRunner.runSchematic("app.nest", options, appTree);
const files = tree.files;

expect(
files.indexOf("/apps/nest-foo/src/main.ts")
).toBeGreaterThanOrEqual(0);
expect(
files.indexOf("/apps/nest-foo/src/app.service.ts")
).toBeGreaterThanOrEqual(0);
expect(
files.indexOf("/apps/nest-foo/src/app.module.ts")
).toBeGreaterThanOrEqual(0);
expect(
files.indexOf("/apps/nest-foo/src/app.controller.ts")
).toBeGreaterThanOrEqual(0);

let checkPath = "/apps/nest-foo/package.json";
expect(files.indexOf(checkPath)).toBeGreaterThanOrEqual(0);

let checkFile = getFileContent(tree, checkPath);
expect(checkFile.indexOf(`"name": "nest-foo"`)).toBeGreaterThanOrEqual(0);

expect(
files.indexOf("/tools/electron/postinstall.js")
).toBeGreaterThanOrEqual(0);
expect(files.indexOf("/tools/web/postinstall.js")).toBeGreaterThanOrEqual(
0
);

checkPath = "/package.json";
expect(files.indexOf(checkPath)).toBeGreaterThanOrEqual(0);

checkFile = getFileContent(tree, checkPath);

const packageData: any = JSON.parse(checkFile);
expect(packageData.scripts["serve.nest.foo"]).toBeDefined();
expect(packageData.scripts["start.nest.foo"]).toBeDefined();
});
});
18 changes: 18 additions & 0 deletions src/app.nest/schema.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export interface Schema {
/**
* The name of the app.
*/
name: string;
/**
* npm scope - auto detected from nx.json but can specify your own name
*/
npmScope?: string;
/**
* Skip installing dependencies
*/
skipInstall?: boolean;
/**
* Skip formatting files
*/
skipFormat?: boolean;
}
Loading

0 comments on commit d49c859

Please sign in to comment.