├── monorepo-example-www
├── src
│ ├── assets
│ │ └── .gitkeep
│ ├── app
│ │ ├── app.component.scss
│ │ ├── containers
│ │ │ ├── home
│ │ │ │ ├── home.component.css
│ │ │ │ ├── index.ts
│ │ │ │ ├── home.module.ts
│ │ │ │ ├── home.component.ts
│ │ │ │ ├── home.component.spec.ts
│ │ │ │ └── home.component.html
│ │ │ ├── sign-in
│ │ │ │ ├── sign-in.component.css
│ │ │ │ ├── index.ts
│ │ │ │ ├── sign-in.module.ts
│ │ │ │ ├── sign-in.component.spec.ts
│ │ │ │ ├── sign-in.component.ts
│ │ │ │ └── sign-in.component.html
│ │ │ ├── page-not-found
│ │ │ │ ├── page-not-found.component.css
│ │ │ │ ├── page-not-found.component.html
│ │ │ │ ├── index.ts
│ │ │ │ ├── page-not-found.module.ts
│ │ │ │ ├── page-not-found.component.ts
│ │ │ │ └── page-not-found.component.spec.ts
│ │ │ └── index.ts
│ │ ├── components
│ │ │ └── toolbar
│ │ │ │ ├── toolbar.component.scss
│ │ │ │ ├── toolbar.component.ts
│ │ │ │ ├── toolbar.component.html
│ │ │ │ ├── toolbar-component.module.ts
│ │ │ │ └── toolbar.component.spec.ts
│ │ ├── effects
│ │ │ ├── index.ts
│ │ │ ├── app.effects.spec.ts
│ │ │ └── app.effects.ts
│ │ ├── services
│ │ │ ├── index.ts
│ │ │ ├── window.service.ts
│ │ │ ├── location.service.ts
│ │ │ ├── user.service.spec.ts
│ │ │ ├── window.service.spec.ts
│ │ │ ├── location.service.spec.ts
│ │ │ ├── app-config.service.spec.ts
│ │ │ ├── route-guard.service.spec.ts
│ │ │ ├── user.service.ts
│ │ │ ├── route-guard.service.ts
│ │ │ └── app-config.service.ts
│ │ ├── reducers
│ │ │ ├── app.reducer.spec.ts
│ │ │ ├── index.ts
│ │ │ └── app.reducer.ts
│ │ ├── app.component.html
│ │ ├── app-store.module.ts
│ │ ├── actions
│ │ │ └── app.actions.ts
│ │ ├── angular-material.module.ts
│ │ ├── app.component.spec.ts
│ │ ├── app-routing.module.ts
│ │ ├── app.component.ts
│ │ └── app.module.ts
│ ├── favicon.ico
│ ├── styles.scss
│ ├── tsconfig.app.json
│ ├── environments
│ │ ├── environment.prod.ts
│ │ └── environment.ts
│ ├── tsconfig.spec.json
│ ├── tslint.json
│ ├── browserslist
│ ├── main.ts
│ ├── index.html
│ ├── test.ts
│ ├── karma.conf.js
│ └── polyfills.ts
├── e2e
│ ├── src
│ │ ├── app.po.ts
│ │ └── app.e2e-spec.ts
│ ├── tsconfig.e2e.json
│ └── protractor.conf.js
├── .editorconfig
├── tsconfig.json
├── .gitignore
├── README.md
├── package.json
├── tslint.json
└── angular.json
├── shared
├── src
│ ├── public_api.ts
│ ├── models
│ │ ├── models.ts
│ │ ├── index.ts
│ │ ├── app-config.model.ts
│ │ ├── session.model.ts
│ │ └── user.model.ts
│ └── adapters
│ │ ├── adapter.ts
│ │ ├── index.ts
│ │ ├── app-config.adapter.ts
│ │ ├── session.adapter.ts
│ │ └── user.adapter.ts
├── package.json
├── tsconfig.json
├── .gitignore
└── yarn.lock
├── monorepo-example-api
├── .prettierrc
├── src
│ ├── config
│ │ ├── index.ts
│ │ ├── local-config.service.ts
│ │ └── config.module.ts
│ ├── controllers
│ │ ├── index.ts
│ │ ├── app.controller.ts
│ │ ├── api.controller.spec.ts
│ │ ├── app.controller.spec.ts
│ │ └── api.controller.ts
│ ├── storage
│ │ ├── index.ts
│ │ ├── memory-storage.service.spec.ts
│ │ ├── memory-storage.service.ts
│ │ └── storage.module.ts
│ ├── middlewares
│ │ ├── index.ts
│ │ ├── cors.middleware.spec.ts
│ │ ├── jwt-session.middleware.spec.ts
│ │ ├── cors.middleware.ts
│ │ └── jwt-session.middleware.ts
│ ├── services
│ │ ├── index.ts
│ │ ├── app.service.ts
│ │ ├── app.service.spec.ts
│ │ ├── user.service.spec.ts
│ │ ├── oauth.service.spec.ts
│ │ ├── user.service.ts
│ │ └── oauth.service.ts
│ ├── main.ts
│ ├── main.hmr.ts
│ └── app.module.ts
├── nest-cli.json
├── nodemon.json
├── tsconfig.spec.json
├── test
│ ├── jest-e2e.json
│ └── app.e2e-spec.ts
├── tsconfig.json
├── .env-template
├── .gitignore
├── webpack.config.js
├── tslint.json
├── package.json
└── README.md
├── README-screenshot.png
├── .gitignore
└── README.md
/monorepo-example-www/src/assets/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/app.component.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/containers/home/home.component.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/components/toolbar/toolbar.component.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/containers/sign-in/sign-in.component.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/containers/page-not-found/page-not-found.component.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/effects/index.ts:
--------------------------------------------------------------------------------
1 | export * from './app.effects';
2 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/services/index.ts:
--------------------------------------------------------------------------------
1 | export * from './app-config.service';
2 |
--------------------------------------------------------------------------------
/shared/src/public_api.ts:
--------------------------------------------------------------------------------
1 | export * from './adapters';
2 | export * from './models';
3 |
--------------------------------------------------------------------------------
/monorepo-example-api/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "trailingComma": "all"
4 | }
--------------------------------------------------------------------------------
/README-screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/METACEO/monorepo-example/HEAD/README-screenshot.png
--------------------------------------------------------------------------------
/monorepo-example-api/src/config/index.ts:
--------------------------------------------------------------------------------
1 | export * from './config.module';
2 | export * from './local-config.service';
3 |
--------------------------------------------------------------------------------
/monorepo-example-api/src/controllers/index.ts:
--------------------------------------------------------------------------------
1 | export * from './api.controller';
2 | export * from './app.controller';
3 |
--------------------------------------------------------------------------------
/shared/src/models/models.ts:
--------------------------------------------------------------------------------
1 | export class BaseModel {
2 | constructor(public readonly id: string) {
3 | }
4 | }
5 |
--------------------------------------------------------------------------------
/monorepo-example-api/src/storage/index.ts:
--------------------------------------------------------------------------------
1 | export * from './memory-storage.service';
2 | export * from './storage.module';
3 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/containers/home/index.ts:
--------------------------------------------------------------------------------
1 | export * from './home.component';
2 | export * from './home.module';
3 |
--------------------------------------------------------------------------------
/monorepo-example-api/src/middlewares/index.ts:
--------------------------------------------------------------------------------
1 | export * from './cors.middleware';
2 | export * from './jwt-session.middleware';
3 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/containers/page-not-found/page-not-found.component.html:
--------------------------------------------------------------------------------
1 |
2 | page-not-found works!
3 |
4 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/METACEO/monorepo-example/HEAD/monorepo-example-www/src/favicon.ico
--------------------------------------------------------------------------------
/shared/src/adapters/adapter.ts:
--------------------------------------------------------------------------------
1 | export interface Adapter {
2 | adapt(item: any): T;
3 | partition(model: T): any;
4 | }
5 |
--------------------------------------------------------------------------------
/monorepo-example-api/nest-cli.json:
--------------------------------------------------------------------------------
1 | {
2 | "language": "ts",
3 | "collection": "@nestjs/schematics",
4 | "sourceRoot": "src"
5 | }
6 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/containers/sign-in/index.ts:
--------------------------------------------------------------------------------
1 | export * from './sign-in.component';
2 | export * from './sign-in.module';
3 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/containers/index.ts:
--------------------------------------------------------------------------------
1 | export * from './home';
2 | export * from './page-not-found';
3 | export * from './sign-in';
4 |
--------------------------------------------------------------------------------
/monorepo-example-api/src/services/index.ts:
--------------------------------------------------------------------------------
1 | export * from './app.service';
2 | export * from './oauth.service';
3 | export * from './user.service';
4 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/containers/page-not-found/index.ts:
--------------------------------------------------------------------------------
1 | export * from './page-not-found.component';
2 | export * from './page-not-found.module';
3 |
--------------------------------------------------------------------------------
/shared/src/models/index.ts:
--------------------------------------------------------------------------------
1 | export * from './app-config.model';
2 | export * from './models';
3 | export * from './session.model';
4 | export * from './user.model';
5 |
--------------------------------------------------------------------------------
/shared/src/adapters/index.ts:
--------------------------------------------------------------------------------
1 | export * from './adapter';
2 | export * from './app-config.adapter';
3 | export * from './session.adapter';
4 | export * from './user.adapter';
5 |
--------------------------------------------------------------------------------
/monorepo-example-api/nodemon.json:
--------------------------------------------------------------------------------
1 | {
2 | "watch": ["src"],
3 | "ext": "ts",
4 | "ignore": ["src/**/*.spec.ts"],
5 | "exec": "ts-node -r tsconfig-paths/register src/main.ts"
6 | }
7 |
--------------------------------------------------------------------------------
/monorepo-example-api/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "tsconfig.json",
3 | "compilerOptions": {
4 | "types": ["jest", "node"]
5 | },
6 | "include": ["**/*.spec.ts", "**/*.d.ts"]
7 | }
8 |
--------------------------------------------------------------------------------
/monorepo-example-api/src/services/app.service.ts:
--------------------------------------------------------------------------------
1 | import {Injectable} from '@nestjs/common';
2 |
3 | @Injectable()
4 | export class AppService {
5 | root(): string {
6 | return 'Hello World!';
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/styles.scss:
--------------------------------------------------------------------------------
1 | /* You can add global styles to this file, and also import other style files */
2 |
3 | html, body { height: 100%; }
4 | body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
5 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/services/window.service.ts:
--------------------------------------------------------------------------------
1 | import {Injectable} from '@angular/core';
2 |
3 | @Injectable({
4 | providedIn: 'root'
5 | })
6 | export class WindowService {
7 |
8 | constructor() {
9 | }
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/shared/src/models/app-config.model.ts:
--------------------------------------------------------------------------------
1 | import {SessionModel} from './session.model';
2 |
3 | export class AppConfigModel {
4 | constructor(public readonly features: string[],
5 | public readonly session: SessionModel) {
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/monorepo-example-api/test/jest-e2e.json:
--------------------------------------------------------------------------------
1 | {
2 | "moduleFileExtensions": ["js", "json", "ts"],
3 | "rootDir": ".",
4 | "testEnvironment": "node",
5 | "testRegex": ".e2e-spec.ts$",
6 | "transform": {
7 | "^.+\\.(t|j)s$": "ts-jest"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/app",
5 | "types": []
6 | },
7 | "exclude": [
8 | "test.ts",
9 | "**/*.spec.ts"
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/monorepo-example-api/src/main.ts:
--------------------------------------------------------------------------------
1 | import { NestFactory } from '@nestjs/core';
2 | import { AppModule } from './app.module';
3 |
4 | async function bootstrap() {
5 | const app = await NestFactory.create(AppModule);
6 | await app.listen(3000);
7 | }
8 | bootstrap();
9 |
--------------------------------------------------------------------------------
/monorepo-example-api/src/middlewares/cors.middleware.spec.ts:
--------------------------------------------------------------------------------
1 | import {CorsMiddleware} from './cors.middleware';
2 |
3 | describe('CorsMiddleware', () => {
4 | it('should be defined', () => {
5 | expect(new CorsMiddleware()).toBeTruthy();
6 | });
7 | });
8 |
--------------------------------------------------------------------------------
/monorepo-example-api/src/middlewares/jwt-session.middleware.spec.ts:
--------------------------------------------------------------------------------
1 | import { JwtSessionMiddleware } from './jwt-session.middleware';
2 |
3 | describe('JwtSessionMiddleware', () => {
4 | it('should be defined', () => {
5 | expect(new JwtSessionMiddleware()).toBeTruthy();
6 | });
7 | });
8 |
--------------------------------------------------------------------------------
/monorepo-example-www/e2e/src/app.po.ts:
--------------------------------------------------------------------------------
1 | import { browser, by, element } from 'protractor';
2 |
3 | export class AppPage {
4 | navigateTo() {
5 | return browser.get('/');
6 | }
7 |
8 | getParagraphText() {
9 | return element(by.css('app-root h1')).getText();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/monorepo-example-www/e2e/tsconfig.e2e.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/app",
5 | "module": "commonjs",
6 | "target": "es5",
7 | "types": [
8 | "jasmine",
9 | "jasminewd2",
10 | "node"
11 | ]
12 | }
13 | }
--------------------------------------------------------------------------------
/monorepo-example-www/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | max_line_length = off
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/monorepo-example-api/src/middlewares/cors.middleware.ts:
--------------------------------------------------------------------------------
1 | import {Injectable, MiddlewareFunction, NestMiddleware} from '@nestjs/common';
2 | import * as cors from 'cors';
3 |
4 | @Injectable()
5 | export class CorsMiddleware implements NestMiddleware {
6 | resolve(corsOptions): MiddlewareFunction {
7 | return cors(corsOptions);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/shared/src/models/session.model.ts:
--------------------------------------------------------------------------------
1 | import {BaseModel} from './models';
2 | import {UserModel} from './user.model';
3 |
4 | export class SessionModel extends BaseModel {
5 | constructor(public readonly id: string,
6 | public readonly user: UserModel,
7 | public readonly userId: string) {
8 | super(id);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: false,
3 | url: {
4 | appConfig: 'https://monorepo-example/v1/app-config',
5 | home: 'https://monorepo-example/',
6 | signinGithub: 'https://monorepo-example/v1/sign-in/github',
7 | signout: 'https://monorepo-example/v1/api/sign-out',
8 | },
9 | };
10 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/spec",
5 | "types": [
6 | "jasmine",
7 | "node"
8 | ]
9 | },
10 | "files": [
11 | "test.ts",
12 | "polyfills.ts"
13 | ],
14 | "include": [
15 | "**/*.spec.ts",
16 | "**/*.d.ts"
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/services/location.service.ts:
--------------------------------------------------------------------------------
1 | import {Injectable} from '@angular/core';
2 |
3 | import {environment} from '../../environments/environment';
4 |
5 | @Injectable({
6 | providedIn: 'root'
7 | })
8 | export class LocationService {
9 |
10 | constructor() {
11 | }
12 |
13 | goHome(): void {
14 | location.assign(environment.url.home);
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/monorepo-example-api/src/controllers/app.controller.ts:
--------------------------------------------------------------------------------
1 | import {Get, Controller} from '@nestjs/common';
2 |
3 | import * as servicesRoot from '../services';
4 |
5 | @Controller()
6 | export class AppController {
7 | constructor(private readonly appService: servicesRoot.AppService) {
8 | }
9 |
10 | @Get()
11 | root(): string {
12 | return this.appService.root();
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # IDEs and editors
4 | /.idea
5 | .project
6 | .classpath
7 | .c9/
8 | *.launch
9 | .settings/
10 | *.sublime-workspace
11 |
12 | # IDE - VSCode
13 | .vscode/*
14 | !.vscode/settings.json
15 | !.vscode/tasks.json
16 | !.vscode/launch.json
17 | !.vscode/extensions.json
18 |
19 | # System Files
20 | .DS_Store
21 | Thumbs.db
22 |
--------------------------------------------------------------------------------
/monorepo-example-www/e2e/src/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { AppPage } from './app.po';
2 |
3 | describe('workspace-project App', () => {
4 | let page: AppPage;
5 |
6 | beforeEach(() => {
7 | page = new AppPage();
8 | });
9 |
10 | it('should display welcome message', () => {
11 | page.navigateTo();
12 | expect(page.getParagraphText()).toEqual('Welcome to monorepo-example-www!');
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/services/user.service.spec.ts:
--------------------------------------------------------------------------------
1 | import {TestBed} from '@angular/core/testing';
2 |
3 | import {UserService} from './user.service';
4 |
5 | describe('UserService', () => {
6 | beforeEach(() => TestBed.configureTestingModule({}));
7 |
8 | it('should be created', () => {
9 | const service: UserService = TestBed.get(UserService);
10 | expect(service).toBeTruthy();
11 | });
12 | });
13 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tslint.json",
3 | "rules": {
4 | "directive-selector": [
5 | true,
6 | "attribute",
7 | "app",
8 | "camelCase"
9 | ],
10 | "component-selector": [
11 | true,
12 | "element",
13 | "app",
14 | "kebab-case"
15 | ]
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/monorepo-example-api/src/main.hmr.ts:
--------------------------------------------------------------------------------
1 | import { NestFactory } from '@nestjs/core';
2 | import { AppModule } from './app.module';
3 |
4 | declare const module: any;
5 |
6 | async function bootstrap() {
7 | const app = await NestFactory.create(AppModule);
8 | await app.listen(3000);
9 |
10 | if (module.hot) {
11 | module.hot.accept();
12 | module.hot.dispose(() => app.close());
13 | }
14 | }
15 | bootstrap();
16 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/components/toolbar/toolbar.component.ts:
--------------------------------------------------------------------------------
1 | import {Component, Input} from '@angular/core';
2 | import {SessionModel} from '@monorepo-example/shared';
3 |
4 | @Component({
5 | selector: 'app-toolbar',
6 | templateUrl: './toolbar.component.html',
7 | styleUrls: ['./toolbar.component.scss']
8 | })
9 | export class ToolbarComponent {
10 |
11 | @Input() session: SessionModel = null;
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/reducers/app.reducer.spec.ts:
--------------------------------------------------------------------------------
1 | import {reducer, initialState} from './app.reducer';
2 |
3 | describe('App Reducer', () => {
4 | describe('unknown action', () => {
5 | it('should return the initial state', () => {
6 | const action = {} as any;
7 |
8 | const result = reducer(initialState, action);
9 |
10 | expect(result).toBe(initialState);
11 | });
12 | });
13 | });
14 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/services/window.service.spec.ts:
--------------------------------------------------------------------------------
1 | import {TestBed} from '@angular/core/testing';
2 |
3 | import {WindowService} from './window.service';
4 |
5 | describe('WindowService', () => {
6 | beforeEach(() => TestBed.configureTestingModule({}));
7 |
8 | it('should be created', () => {
9 | const service: WindowService = TestBed.get(WindowService);
10 | expect(service).toBeTruthy();
11 | });
12 | });
13 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/containers/home/home.module.ts:
--------------------------------------------------------------------------------
1 | import {NgModule} from '@angular/core';
2 | import {CommonModule} from '@angular/common';
3 |
4 | import {AppStoreModule} from '../../app-store.module';
5 | import {HomeComponent} from './home.component';
6 |
7 | @NgModule({
8 | declarations: [HomeComponent],
9 | imports: [
10 | CommonModule,
11 | AppStoreModule,
12 | ],
13 | })
14 | export class HomeModule {
15 | }
16 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/services/location.service.spec.ts:
--------------------------------------------------------------------------------
1 | import {TestBed} from '@angular/core/testing';
2 |
3 | import {LocationService} from './location.service';
4 |
5 | describe('LocationService', () => {
6 | beforeEach(() => TestBed.configureTestingModule({}));
7 |
8 | it('should be created', () => {
9 | const service: LocationService = TestBed.get(LocationService);
10 | expect(service).toBeTruthy();
11 | });
12 | });
13 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/services/app-config.service.spec.ts:
--------------------------------------------------------------------------------
1 | import {TestBed} from '@angular/core/testing';
2 |
3 | import {AppConfigService} from './app-config.service';
4 |
5 | describe('AppConfigService', () => {
6 | beforeEach(() => TestBed.configureTestingModule({}));
7 |
8 | it('should be created', () => {
9 | const service: AppConfigService = TestBed.get(AppConfigService);
10 | expect(service).toBeTruthy();
11 | });
12 | });
13 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/containers/home/home.component.ts:
--------------------------------------------------------------------------------
1 | import {Component, OnInit} from '@angular/core';
2 | import {Store} from '@ngrx/store';
3 |
4 | @Component({
5 | selector: 'app-home',
6 | templateUrl: './home.component.html',
7 | styleUrls: ['./home.component.css']
8 | })
9 | export class HomeComponent implements OnInit {
10 |
11 | constructor(private readonly store: Store) {
12 | }
13 |
14 | ngOnInit() {
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/services/route-guard.service.spec.ts:
--------------------------------------------------------------------------------
1 | import {TestBed} from '@angular/core/testing';
2 |
3 | import {RouteGuardService} from './route-guard.service';
4 |
5 | describe('RouteGuardService', () => {
6 | beforeEach(() => TestBed.configureTestingModule({}));
7 |
8 | it('should be created', () => {
9 | const service: RouteGuardService = TestBed.get(RouteGuardService);
10 | expect(service).toBeTruthy();
11 | });
12 | });
13 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/browserslist:
--------------------------------------------------------------------------------
1 | # This file is currently used by autoprefixer to adjust CSS to support the below specified browsers
2 | # For additional information regarding the format and rule options, please see:
3 | # https://github.com/browserslist/browserslist#queries
4 | #
5 | # For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed
6 |
7 | > 0.5%
8 | last 2 versions
9 | Firefox ESR
10 | not dead
11 | not IE 9-11
--------------------------------------------------------------------------------
/shared/src/models/user.model.ts:
--------------------------------------------------------------------------------
1 | import {BaseModel} from './models';
2 |
3 | export class UserModel extends BaseModel {
4 | constructor(public readonly id: string,
5 | public readonly auth: {
6 | oauth?: {
7 | github?: {
8 | id: string;
9 | };
10 | };
11 | }) {
12 | super(id);
13 | }
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/main.ts:
--------------------------------------------------------------------------------
1 | import 'hammerjs';
2 | import { enableProdMode } from '@angular/core';
3 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
4 |
5 | import { AppModule } from './app/app.module';
6 | import { environment } from './environments/environment';
7 |
8 | if (environment.production) {
9 | enableProdMode();
10 | }
11 |
12 | platformBrowserDynamic().bootstrapModule(AppModule)
13 | .catch(err => console.error(err));
14 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/components/toolbar/toolbar.component.html:
--------------------------------------------------------------------------------
1 |
2 |
5 |
monorepo-example
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/containers/page-not-found/page-not-found.module.ts:
--------------------------------------------------------------------------------
1 | import {NgModule} from '@angular/core';
2 | import {CommonModule} from '@angular/common';
3 |
4 | import {AppStoreModule} from '../../app-store.module';
5 | import {PageNotFoundComponent} from './page-not-found.component';
6 |
7 | @NgModule({
8 | declarations: [PageNotFoundComponent],
9 | imports: [
10 | CommonModule,
11 | AppStoreModule,
12 | ],
13 | })
14 | export class PageNotFoundModule {
15 | }
16 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/containers/page-not-found/page-not-found.component.ts:
--------------------------------------------------------------------------------
1 | import {Component, OnInit} from '@angular/core';
2 | import {Store} from '@ngrx/store';
3 |
4 | @Component({
5 | selector: 'app-page-not-found',
6 | templateUrl: './page-not-found.component.html',
7 | styleUrls: ['./page-not-found.component.css']
8 | })
9 | export class PageNotFoundComponent implements OnInit {
10 |
11 | constructor(private readonly store: Store) {
12 | }
13 |
14 | ngOnInit() {
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/monorepo-example-api/src/services/app.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { Test, TestingModule } from '@nestjs/testing';
2 | import { AppService } from './app.service';
3 |
4 | describe('AppService', () => {
5 | let service: AppService;
6 |
7 | beforeAll(async () => {
8 | const module: TestingModule = await Test.createTestingModule({
9 | providers: [AppService],
10 | }).compile();
11 | service = module.get(AppService);
12 | });
13 | it('should be defined', () => {
14 | expect(service).toBeDefined();
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/containers/sign-in/sign-in.module.ts:
--------------------------------------------------------------------------------
1 | import {NgModule} from '@angular/core';
2 | import {CommonModule} from '@angular/common';
3 |
4 | import {AngularMaterialModule} from '../../angular-material.module';
5 | import {AppStoreModule} from '../../app-store.module';
6 | import {SignInComponent} from './sign-in.component';
7 |
8 | @NgModule({
9 | declarations: [SignInComponent],
10 | imports: [
11 | CommonModule,
12 | AngularMaterialModule,
13 | AppStoreModule,
14 | ]
15 | })
16 | export class SignInModule {
17 | }
18 |
--------------------------------------------------------------------------------
/monorepo-example-api/src/services/user.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { Test, TestingModule } from '@nestjs/testing';
2 | import { UserService } from './user.service';
3 |
4 | describe('UserService', () => {
5 | let service: UserService;
6 |
7 | beforeAll(async () => {
8 | const module: TestingModule = await Test.createTestingModule({
9 | providers: [UserService],
10 | }).compile();
11 | service = module.get(UserService);
12 | });
13 | it('should be defined', () => {
14 | expect(service).toBeDefined();
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/monorepo-example-api/src/controllers/api.controller.spec.ts:
--------------------------------------------------------------------------------
1 | import { Test, TestingModule } from '@nestjs/testing';
2 | import { ApiController } from './api.controller';
3 |
4 | describe('Api Controller', () => {
5 | let module: TestingModule;
6 |
7 | beforeAll(async () => {
8 | module = await Test.createTestingModule({
9 | controllers: [ApiController],
10 | }).compile();
11 | });
12 | it('should be defined', () => {
13 | const controller: ApiController = module.get(ApiController);
14 | expect(controller).toBeDefined();
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/monorepo-example-api/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "declaration": true,
5 | "noImplicitAny": false,
6 | "removeComments": true,
7 | "noLib": false,
8 | "allowSyntheticDefaultImports": true,
9 | "emitDecoratorMetadata": true,
10 | "experimentalDecorators": true,
11 | "target": "es6",
12 | "sourceMap": true,
13 | "outDir": "./dist",
14 | "baseUrl": "./src"
15 | },
16 | "include": [
17 | "src/**/*"
18 | ],
19 | "exclude": [
20 | "node_modules",
21 | "**/*.spec.ts"
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Home
4 |
5 |
6 | Sign Up
7 | Sign In
8 |
9 |
10 | Settings
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/monorepo-example-api/src/services/oauth.service.spec.ts:
--------------------------------------------------------------------------------
1 | import {Test, TestingModule} from '@nestjs/testing';
2 | import {OauthService} from './oauth.service';
3 |
4 | describe('OauthService', () => {
5 | let service: OauthService;
6 |
7 | beforeAll(async () => {
8 | const module: TestingModule = await Test.createTestingModule({
9 | providers: [OauthService],
10 | }).compile();
11 | service = module.get(OauthService);
12 | });
13 | it('should be defined', () => {
14 | expect(service).toBeDefined();
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | MonorepoExampleWww
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | Loading screen here.
15 |
16 |
17 |
--------------------------------------------------------------------------------
/shared/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@monorepo-example/shared",
3 | "version": "1.0.0",
4 | "description": "Shared resources for the monorepo-example project.",
5 | "main": "./dist/public_api.js",
6 | "types": "./dist/public_api.d.ts",
7 | "scripts": {
8 | "tsc": "tsc",
9 | "build": "rimraf dist && tsc"
10 | },
11 | "author": "James Allen",
12 | "license": "UNLICENSED",
13 | "dependencies": {
14 | "lodash": "^4.17.11",
15 | "typescript": "^3.1.6"
16 | },
17 | "devDependencies": {
18 | "@types/lodash": "^4.14.116",
19 | "rimraf": "^2.6.2"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/shared/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "baseUrl": "./",
5 | "declaration": true,
6 | "emitDecoratorMetadata": true,
7 | "experimentalDecorators": true,
8 | "module": "commonjs",
9 | "moduleResolution": "node",
10 | "outDir": "./dist",
11 | "rootDir": "./src",
12 | "sourceMap": true,
13 | "target": "es5",
14 | "lib": [
15 | "es2015",
16 | "es2016",
17 | "es2017",
18 | "dom"
19 | ]
20 | },
21 | "files": [
22 | "./src/public_api.ts"
23 | ]
24 | }
25 |
--------------------------------------------------------------------------------
/monorepo-example-api/src/storage/memory-storage.service.spec.ts:
--------------------------------------------------------------------------------
1 | import {Test, TestingModule} from '@nestjs/testing';
2 |
3 | import {MemoryStorageService} from './memory-storage.service';
4 |
5 | describe('MemoryStorage', () => {
6 | let service: MemoryStorageService;
7 |
8 | beforeAll(async () => {
9 | const module: TestingModule = await Test.createTestingModule({
10 | providers: [MemoryStorageService],
11 | }).compile();
12 | service = module.get(MemoryStorageService);
13 | });
14 | it('should be defined', () => {
15 | expect(service).toBeDefined();
16 | });
17 | });
18 |
--------------------------------------------------------------------------------
/monorepo-example-www/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "baseUrl": "./",
5 | "outDir": "./dist/out-tsc",
6 | "sourceMap": true,
7 | "declaration": false,
8 | "module": "es2015",
9 | "moduleResolution": "node",
10 | "emitDecoratorMetadata": true,
11 | "experimentalDecorators": true,
12 | "target": "es5",
13 | "typeRoots": [
14 | "node_modules/@types"
15 | ],
16 | "lib": [
17 | "es2018",
18 | "dom"
19 | ],
20 | "paths": {
21 | "@monorepo-example/shared": [
22 | "../shared/src/public_api.ts"
23 | ]
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/monorepo-example-api/.env-template:
--------------------------------------------------------------------------------
1 | MONOREPO_EXAMPLE_JWT_SIGNINGKEY=have a good secret here
2 | MONOREPO_EXAMPLE_OAUTH_GITHUB_ACCESSTOKENURL=https://github.com/login/oauth/access_token
3 | MONOREPO_EXAMPLE_OAUTH_GITHUB_AUTHORIZEURL=https://github.com/login/oauth/authorize
4 | MONOREPO_EXAMPLE_OAUTH_GITHUB_CLIENTID=Your Github app's client id here
5 | MONOREPO_EXAMPLE_OAUTH_GITHUB_CLIENTSECRET=Your Github app's client secret here
6 | MONOREPO_EXAMPLE_OAUTH_GITHUB_REDIRECTURI=http://localhost:3000/api/oauth/github
7 | MONOREPO_EXAMPLE_OAUTH_GITHUB_USERAGENTHEADER=Your Name Here
8 | MONOREPO_EXAMPLE_OAUTH_GITHUB_USERPROFILEURL=https://api.github.com/user
9 | MONOREPO_EXAMPLE_STORAGE_TYPE=memory
10 |
--------------------------------------------------------------------------------
/shared/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | /dist
5 | /tmp
6 | /out-tsc
7 |
8 | # dependencies
9 | /node_modules
10 |
11 | # IDEs and editors
12 | /.idea
13 | .project
14 | .classpath
15 | .c9/
16 | *.launch
17 | .settings/
18 | *.sublime-workspace
19 |
20 | # IDE - VSCode
21 | .vscode/*
22 | !.vscode/settings.json
23 | !.vscode/tasks.json
24 | !.vscode/launch.json
25 | !.vscode/extensions.json
26 |
27 | # misc
28 | /.sass-cache
29 | /connect.lock
30 | /coverage
31 | /libpeerconnection.log
32 | npm-debug.log
33 | yarn-error.log
34 | testem.log
35 | /typings
36 |
37 | # System Files
38 | .DS_Store
39 | Thumbs.db
40 |
--------------------------------------------------------------------------------
/monorepo-example-www/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | /dist
5 | /tmp
6 | /out-tsc
7 |
8 | # dependencies
9 | /node_modules
10 |
11 | # IDEs and editors
12 | /.idea
13 | .project
14 | .classpath
15 | .c9/
16 | *.launch
17 | .settings/
18 | *.sublime-workspace
19 |
20 | # IDE - VSCode
21 | .vscode/*
22 | !.vscode/settings.json
23 | !.vscode/tasks.json
24 | !.vscode/launch.json
25 | !.vscode/extensions.json
26 |
27 | # misc
28 | /.sass-cache
29 | /connect.lock
30 | /coverage
31 | /libpeerconnection.log
32 | npm-debug.log
33 | yarn-error.log
34 | testem.log
35 | /typings
36 |
37 | # System Files
38 | .DS_Store
39 | Thumbs.db
40 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/app-store.module.ts:
--------------------------------------------------------------------------------
1 | import {NgModule} from '@angular/core';
2 | import {EffectsModule} from '@ngrx/effects';
3 | import {StoreModule} from '@ngrx/store';
4 | import {StoreDevtoolsModule} from '@ngrx/store-devtools';
5 |
6 | import {environment} from '../environments/environment';
7 | import * as effectsRoot from './effects';
8 | import {metaReducers, reducers} from './reducers';
9 |
10 | @NgModule({
11 | imports: [
12 | StoreModule.forRoot(reducers, {metaReducers}),
13 | !environment.production ? StoreDevtoolsModule.instrument() : [],
14 | EffectsModule.forRoot([
15 | effectsRoot.AppEffects,
16 | ]),
17 | ],
18 | })
19 | export class AppStoreModule {
20 | }
21 |
--------------------------------------------------------------------------------
/monorepo-example-api/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | /dist
5 | /tmp
6 | /out-tsc
7 |
8 | # dependencies
9 | /node_modules
10 |
11 | # IDEs and editors
12 | /.idea
13 | .project
14 | .classpath
15 | .c9/
16 | *.launch
17 | .settings/
18 | *.sublime-workspace
19 |
20 | # IDE - VSCode
21 | .vscode/*
22 | !.vscode/settings.json
23 | !.vscode/tasks.json
24 | !.vscode/launch.json
25 | !.vscode/extensions.json
26 |
27 | # misc
28 | /.sass-cache
29 | /connect.lock
30 | /coverage
31 | /libpeerconnection.log
32 | npm-debug.log
33 | yarn-error.log
34 | testem.log
35 | /typings
36 |
37 | # System Files
38 | .DS_Store
39 | Thumbs.db
40 | .env
41 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/effects/app.effects.spec.ts:
--------------------------------------------------------------------------------
1 | import {TestBed, inject} from '@angular/core/testing';
2 | import {provideMockActions} from '@ngrx/effects/testing';
3 | import {Observable} from 'rxjs';
4 |
5 | import {AppEffects} from './app.effects';
6 |
7 | describe('AppEffects', () => {
8 | let actions$: Observable;
9 | let effects: AppEffects;
10 |
11 | beforeEach(() => {
12 | TestBed.configureTestingModule({
13 | providers: [
14 | AppEffects,
15 | provideMockActions(() => actions$)
16 | ]
17 | });
18 |
19 | effects = TestBed.get(AppEffects);
20 | });
21 |
22 | it('should be created', () => {
23 | expect(effects).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/components/toolbar/toolbar-component.module.ts:
--------------------------------------------------------------------------------
1 | import {CommonModule} from '@angular/common';
2 | import {NgModule} from '@angular/core';
3 | import {FlexLayoutModule} from '@angular/flex-layout';
4 | import {MatButtonModule} from '@angular/material/button';
5 | import {MatToolbarModule} from '@angular/material/toolbar';
6 |
7 | import {ToolbarComponent} from './toolbar.component';
8 |
9 | @NgModule({
10 | declarations: [
11 | ToolbarComponent,
12 | ],
13 | imports: [
14 | CommonModule,
15 | MatButtonModule,
16 | MatToolbarModule,
17 | FlexLayoutModule,
18 | ],
19 | exports: [
20 | ToolbarComponent,
21 | ],
22 | })
23 | export class ToolbarComponentModule {
24 | }
25 |
--------------------------------------------------------------------------------
/monorepo-example-api/test/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { INestApplication } from '@nestjs/common';
2 | import { Test } from '@nestjs/testing';
3 | import * as request from 'supertest';
4 | import { AppModule } from './../src/app.module';
5 |
6 | describe('AppController (e2e)', () => {
7 | let app: INestApplication;
8 |
9 | beforeAll(async () => {
10 | const moduleFixture = await Test.createTestingModule({
11 | imports: [AppModule],
12 | }).compile();
13 |
14 | app = moduleFixture.createNestApplication();
15 | await app.init();
16 | });
17 |
18 | it('/ (GET)', () => {
19 | return request(app.getHttpServer())
20 | .get('/')
21 | .expect(200)
22 | .expect('Hello World!');
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import 'zone.js/dist/zone-testing';
4 | import { getTestBed } from '@angular/core/testing';
5 | import {
6 | BrowserDynamicTestingModule,
7 | platformBrowserDynamicTesting
8 | } from '@angular/platform-browser-dynamic/testing';
9 |
10 | declare const require: any;
11 |
12 | // First, initialize the Angular testing environment.
13 | getTestBed().initTestEnvironment(
14 | BrowserDynamicTestingModule,
15 | platformBrowserDynamicTesting()
16 | );
17 | // Then we find all the tests.
18 | const context = require.context('./', true, /\.spec\.ts$/);
19 | // And load the modules.
20 | context.keys().map(context);
21 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/actions/app.actions.ts:
--------------------------------------------------------------------------------
1 | import {AppConfigModel} from '@monorepo-example/shared';
2 | import {Action} from '@ngrx/store';
3 |
4 | export enum AppActionTypes {
5 | LoadInitialAppConfig = '[App] Load Initial App Config',
6 | UserClickedToolbarToSignout = '[App] User Clicked Toolbar To Signout',
7 | }
8 |
9 | export class LoadInitialAppConfig implements Action {
10 | readonly type = AppActionTypes.LoadInitialAppConfig;
11 |
12 | constructor(public readonly payload: AppConfigModel) {
13 | }
14 | }
15 |
16 | export class UserClickedToolbarToSignout implements Action {
17 | readonly type = AppActionTypes.UserClickedToolbarToSignout;
18 | }
19 |
20 | export type AppActions =
21 | | LoadInitialAppConfig
22 | | UserClickedToolbarToSignout;
23 |
--------------------------------------------------------------------------------
/monorepo-example-api/src/controllers/app.controller.spec.ts:
--------------------------------------------------------------------------------
1 | import { Test, TestingModule } from '@nestjs/testing';
2 | import { INestApplication } from '@nestjs/common';
3 | import { AppController } from './app.controller';
4 | import { AppService } from '../app.service';
5 |
6 | describe('AppController', () => {
7 | let app: TestingModule;
8 |
9 | beforeAll(async () => {
10 | app = await Test.createTestingModule({
11 | controllers: [AppController],
12 | providers: [AppService],
13 | }).compile();
14 | });
15 |
16 | describe('root', () => {
17 | it('should return "Hello World!"', () => {
18 | const appController = app.get(AppController);
19 | expect(appController.root()).toBe('Hello World!');
20 | });
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/components/toolbar/toolbar.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { ToolbarComponent } from './toolbar.component';
4 |
5 | describe('ToolbarComponent', () => {
6 | let component: ToolbarComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ ToolbarComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(ToolbarComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/effects/app.effects.ts:
--------------------------------------------------------------------------------
1 | import {Injectable} from '@angular/core';
2 | import {Actions, Effect, ofType} from '@ngrx/effects';
3 | import {Action} from '@ngrx/store';
4 | import {Observable} from 'rxjs';
5 | import {tap} from 'rxjs/operators';
6 |
7 | import {AppActionTypes} from '../actions/app.actions';
8 | import {UserService} from "../services/user.service";
9 |
10 | @Injectable()
11 | export class AppEffects {
12 |
13 | @Effect({dispatch: false})
14 | userClickedToolbarDoSignout$: Observable = this.actions$.pipe(
15 | ofType(AppActionTypes.UserClickedToolbarToSignout),
16 | tap(() => this.userService.signOut()),
17 | );
18 |
19 | constructor(private readonly actions$: Actions,
20 | private readonly userService: UserService) {
21 | }
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/monorepo-example-api/webpack.config.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack');
2 | const path = require('path');
3 | const nodeExternals = require('webpack-node-externals');
4 |
5 | module.exports = {
6 | entry: ['webpack/hot/poll?1000', './src/main.hmr.ts'],
7 | watch: true,
8 | target: 'node',
9 | externals: [
10 | nodeExternals({
11 | whitelist: ['webpack/hot/poll?1000'],
12 | }),
13 | ],
14 | module: {
15 | rules: [
16 | {
17 | test: /\.tsx?$/,
18 | use: 'ts-loader',
19 | exclude: /node_modules/,
20 | },
21 | ],
22 | },
23 | mode: "development",
24 | resolve: {
25 | extensions: ['.tsx', '.ts', '.js'],
26 | },
27 | plugins: [
28 | new webpack.HotModuleReplacementPlugin(),
29 | ],
30 | output: {
31 | path: path.join(__dirname, 'dist'),
32 | filename: 'server.js',
33 | },
34 | };
35 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/angular-material.module.ts:
--------------------------------------------------------------------------------
1 | import {NgModule} from '@angular/core';
2 | import {FlexLayoutModule} from '@angular/flex-layout';
3 | import {FormsModule, ReactiveFormsModule} from '@angular/forms';
4 | import {MatButtonModule} from '@angular/material/button';
5 | import {MatCardModule} from '@angular/material/card';
6 | import {MatFormFieldModule} from '@angular/material/form-field';
7 | import {MatInputModule} from '@angular/material/input';
8 | import {MatToolbarModule} from '@angular/material/toolbar';
9 |
10 | const modules = [
11 | FlexLayoutModule,
12 | FormsModule,
13 | MatButtonModule,
14 | MatCardModule,
15 | MatFormFieldModule,
16 | MatInputModule,
17 | MatToolbarModule,
18 | ReactiveFormsModule,
19 | ];
20 |
21 | @NgModule({
22 | imports: modules,
23 | exports: modules,
24 | })
25 | export class AngularMaterialModule {
26 | }
27 |
--------------------------------------------------------------------------------
/monorepo-example-www/e2e/protractor.conf.js:
--------------------------------------------------------------------------------
1 | // Protractor configuration file, see link for more information
2 | // https://github.com/angular/protractor/blob/master/lib/config.ts
3 |
4 | const { SpecReporter } = require('jasmine-spec-reporter');
5 |
6 | exports.config = {
7 | allScriptsTimeout: 11000,
8 | specs: [
9 | './src/**/*.e2e-spec.ts'
10 | ],
11 | capabilities: {
12 | 'browserName': 'chrome'
13 | },
14 | directConnect: true,
15 | baseUrl: 'http://localhost:4200/',
16 | framework: 'jasmine',
17 | jasmineNodeOpts: {
18 | showColors: true,
19 | defaultTimeoutInterval: 30000,
20 | print: function() {}
21 | },
22 | onPrepare() {
23 | require('ts-node').register({
24 | project: require('path').join(__dirname, './tsconfig.e2e.json')
25 | });
26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
27 | }
28 | };
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/app.component.spec.ts:
--------------------------------------------------------------------------------
1 | import {ComponentFixture, TestBed} from '@angular/core/testing';
2 |
3 | import {AppComponent} from './app.component';
4 | import {Store, StoreModule} from '@ngrx/store';
5 |
6 | describe('AppComponent', () => {
7 | let component: AppComponent;
8 | let fixture: ComponentFixture;
9 | let store: Store;
10 |
11 | beforeEach(async () => {
12 | TestBed.configureTestingModule({
13 | imports: [StoreModule.forRoot({})],
14 | declarations: [AppComponent]
15 | });
16 |
17 | await TestBed.compileComponents();
18 | });
19 |
20 | beforeEach(() => {
21 | fixture = TestBed.createComponent(AppComponent);
22 | component = fixture.componentInstance;
23 | store = TestBed.get(Store);
24 |
25 | spyOn(store, 'dispatch').and.callThrough();
26 | fixture.detectChanges();
27 | });
28 |
29 | it('should create', () => {
30 | expect(component).toBeTruthy();
31 | });
32 | });
33 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/containers/home/home.component.spec.ts:
--------------------------------------------------------------------------------
1 | import {ComponentFixture, TestBed} from '@angular/core/testing';
2 |
3 | import {HomeComponent} from './home.component';
4 | import {Store, StoreModule} from '@ngrx/store';
5 |
6 | describe('HomeComponent', () => {
7 | let component: HomeComponent;
8 | let fixture: ComponentFixture;
9 | let store: Store;
10 |
11 | beforeEach(async () => {
12 | TestBed.configureTestingModule({
13 | imports: [StoreModule.forRoot({})],
14 | declarations: [HomeComponent]
15 | });
16 |
17 | await TestBed.compileComponents();
18 | });
19 |
20 | beforeEach(() => {
21 | fixture = TestBed.createComponent(HomeComponent);
22 | component = fixture.componentInstance;
23 | store = TestBed.get(Store);
24 |
25 | spyOn(store, 'dispatch').and.callThrough();
26 | fixture.detectChanges();
27 | });
28 |
29 | it('should create', () => {
30 | expect(component).toBeTruthy();
31 | });
32 | });
33 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // This file can be replaced during build by using the `fileReplacements` array.
2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
3 | // The list of file replacements can be found in `angular.json`.
4 |
5 | export const environment = {
6 | production: false,
7 | url: {
8 | appConfig: 'http://localhost:3000/api/app-config',
9 | home: 'http://localhost:4200/',
10 | signinGithub: 'http://localhost:3000/api/sign-in/github',
11 | signout: 'http://localhost:3000/api/sign-out',
12 | },
13 | };
14 |
15 | /*
16 | * For easier debugging in development mode, you can import the following file
17 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
18 | *
19 | * This import should be commented out in production mode because it will have a negative impact
20 | * on performance if an error is thrown.
21 | */
22 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI.
23 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/containers/sign-in/sign-in.component.spec.ts:
--------------------------------------------------------------------------------
1 | import {ComponentFixture, TestBed} from '@angular/core/testing';
2 | import {Store, StoreModule} from '@ngrx/store';
3 |
4 | import {SignInComponent} from './sign-in.component';
5 |
6 | describe('SignInComponent', () => {
7 | let component: SignInComponent;
8 | let fixture: ComponentFixture;
9 | let store: Store;
10 |
11 | beforeEach(async () => {
12 | TestBed.configureTestingModule({
13 | imports: [StoreModule.forRoot({})],
14 | declarations: [SignInComponent]
15 | });
16 |
17 | await TestBed.compileComponents();
18 | });
19 |
20 | beforeEach(() => {
21 | fixture = TestBed.createComponent(SignInComponent);
22 | component = fixture.componentInstance;
23 | store = TestBed.get(Store);
24 |
25 | spyOn(store, 'dispatch').and.callThrough();
26 | fixture.detectChanges();
27 | });
28 |
29 | it('should create', () => {
30 | expect(component).toBeTruthy();
31 | });
32 | });
33 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/reducers/index.ts:
--------------------------------------------------------------------------------
1 | import {
2 | ActionReducer,
3 | ActionReducerMap,
4 | createFeatureSelector,
5 | createSelector,
6 | MetaReducer
7 | } from '@ngrx/store';
8 |
9 | import {environment} from '../../environments/environment';
10 | import * as fromApp from './app.reducer';
11 |
12 | export interface State {
13 | app: fromApp.State;
14 | }
15 |
16 | export const reducers: ActionReducerMap = {
17 | app: fromApp.reducer,
18 | };
19 |
20 | export const metaReducers: MetaReducer[] = !environment.production ? [] : [];
21 |
22 | export const selectApp = createFeatureSelector('app');
23 | export const selectAppError = createSelector(selectApp, (state: fromApp.State) => state.error);
24 | export const selectAppFeatures = createSelector(selectApp, (state: fromApp.State) => state.features);
25 | export const selectAppInstantiated = createSelector(selectApp, (state: fromApp.State) => state.instantiated);
26 | export const selectAppSession = createSelector(selectApp, (state: fromApp.State) => state.session);
27 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/services/user.service.ts:
--------------------------------------------------------------------------------
1 | import {HttpClient} from '@angular/common/http';
2 | import {Injectable} from '@angular/core';
3 | import {SessionModel} from '@monorepo-example/shared';
4 | import {Store} from '@ngrx/store';
5 |
6 | import {environment} from '../../environments/environment';
7 | import * as reducersRoot from '../reducers';
8 | import {LocationService} from './location.service';
9 |
10 | @Injectable({
11 | providedIn: 'root'
12 | })
13 | export class UserService {
14 |
15 | session: SessionModel = null;
16 |
17 | constructor(private readonly httpClient: HttpClient,
18 | private readonly locationService: LocationService,
19 | private readonly store: Store) {
20 | this.store.select(reducersRoot.selectAppSession).subscribe(session => this.session = session);
21 | }
22 |
23 | public signOut(): void {
24 | this.httpClient.post(environment.url.signout, {id: this.session.id}, {withCredentials: true})
25 | .subscribe(() => this.locationService.goHome())
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/containers/page-not-found/page-not-found.component.spec.ts:
--------------------------------------------------------------------------------
1 | import {ComponentFixture, TestBed} from '@angular/core/testing';
2 |
3 | import {PageNotFoundComponent} from './page-not-found.component';
4 | import {Store, StoreModule} from '@ngrx/store';
5 |
6 | describe('PageNotFoundComponent', () => {
7 | let component: PageNotFoundComponent;
8 | let fixture: ComponentFixture;
9 | let store: Store;
10 |
11 | beforeEach(async () => {
12 | TestBed.configureTestingModule({
13 | imports: [StoreModule.forRoot({})],
14 | declarations: [PageNotFoundComponent]
15 | });
16 |
17 | await TestBed.compileComponents();
18 | });
19 |
20 | beforeEach(() => {
21 | fixture = TestBed.createComponent(PageNotFoundComponent);
22 | component = fixture.componentInstance;
23 | store = TestBed.get(Store);
24 |
25 | spyOn(store, 'dispatch').and.callThrough();
26 | fixture.detectChanges();
27 | });
28 |
29 | it('should create', () => {
30 | expect(component).toBeTruthy();
31 | });
32 | });
33 |
--------------------------------------------------------------------------------
/shared/src/adapters/app-config.adapter.ts:
--------------------------------------------------------------------------------
1 | import {get} from 'lodash';
2 |
3 | import {AppConfigModel} from '../models';
4 | import {Adapter} from './adapter';
5 |
6 | export class AppConfigAdapter implements Adapter {
7 |
8 | public static adapt(appConfig: any): AppConfigModel {
9 | if (!appConfig) {
10 | return null;
11 | }
12 | return new AppConfigModel(
13 | get(appConfig, 'features', []),
14 | get(appConfig, 'session', null),
15 | );
16 | }
17 |
18 | public static partition(appConfig: AppConfigModel): any {
19 | if (!appConfig) {
20 | return null;
21 | }
22 | return {
23 | features: get(appConfig, 'features', []),
24 | session: get(appConfig, 'session', null),
25 | };
26 | }
27 |
28 | public adapt(item: any): AppConfigModel {
29 | return AppConfigAdapter.adapt(item);
30 | }
31 |
32 | public partition(appConfig: AppConfigModel): any {
33 | return AppConfigAdapter.partition(appConfig);
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/services/route-guard.service.ts:
--------------------------------------------------------------------------------
1 | import {Injectable} from '@angular/core';
2 | import {Router, CanActivate, ActivatedRouteSnapshot} from '@angular/router';
3 |
4 | import {UserService} from './user.service';
5 |
6 | @Injectable({
7 | providedIn: 'root'
8 | })
9 | export class RouteGuardService implements CanActivate {
10 |
11 | constructor(private readonly router: Router,
12 | private readonly userService: UserService) {
13 | }
14 |
15 | static readonly GUEST = '[RouteGuardService] Guest';
16 | static readonly USER = '[RouteGuardService] User';
17 |
18 | canActivate(route: ActivatedRouteSnapshot): boolean {
19 |
20 | const isSession = this.userService.session !== null;
21 |
22 | if (isSession && route.data.allow === RouteGuardService.GUEST) {
23 | this.router.navigate(route.data.redirect);
24 | return false;
25 | }
26 |
27 | if (!isSession && route.data.allow === RouteGuardService.USER) {
28 | this.router.navigate(route.data.redirect);
29 | return false;
30 | }
31 |
32 | return true;
33 |
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/1.0/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular-devkit/build-angular'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-coverage-istanbul-reporter'),
13 | require('@angular-devkit/build-angular/plugins/karma')
14 | ],
15 | client: {
16 | clearContext: false // leave Jasmine Spec Runner output visible in browser
17 | },
18 | coverageIstanbulReporter: {
19 | dir: require('path').join(__dirname, '../coverage'),
20 | reports: ['html', 'lcovonly'],
21 | fixWebpackSourcePaths: true
22 | },
23 | reporters: ['progress', 'kjhtml'],
24 | port: 9876,
25 | colors: true,
26 | logLevel: config.LOG_INFO,
27 | autoWatch: true,
28 | browsers: ['Chrome'],
29 | singleRun: false
30 | });
31 | };
--------------------------------------------------------------------------------
/monorepo-example-api/src/config/local-config.service.ts:
--------------------------------------------------------------------------------
1 | import {Injectable} from '@nestjs/common';
2 |
3 | import {BaseConfigService} from './config.module';
4 |
5 | @Injectable()
6 | export class LocalConfigService implements BaseConfigService {
7 | public jwt = {
8 | signing_key: process.env.MONOREPO_EXAMPLE_JWT_SIGNINGKEY,
9 | };
10 | public oauth = {
11 | github: {
12 | access_token_url: process.env.MONOREPO_EXAMPLE_OAUTH_GITHUB_ACCESSTOKENURL,
13 | authorize_url: process.env.MONOREPO_EXAMPLE_OAUTH_GITHUB_AUTHORIZEURL,
14 | client_id: process.env.MONOREPO_EXAMPLE_OAUTH_GITHUB_CLIENTID,
15 | client_secret: process.env.MONOREPO_EXAMPLE_OAUTH_GITHUB_CLIENTSECRET,
16 | redirect_uri: process.env.MONOREPO_EXAMPLE_OAUTH_GITHUB_REDIRECTURI,
17 | user_agent_header: process.env.MONOREPO_EXAMPLE_OAUTH_GITHUB_USERAGENTHEADER,
18 | user_profile_url: process.env.MONOREPO_EXAMPLE_OAUTH_GITHUB_USERPROFILEURL,
19 | },
20 | };
21 | public storage = {
22 | type: process.env.MONOREPO_EXAMPLE_STORAGE_TYPE,
23 | };
24 | }
25 |
--------------------------------------------------------------------------------
/shared/src/adapters/session.adapter.ts:
--------------------------------------------------------------------------------
1 | import {get} from 'lodash';
2 |
3 | import {SessionModel} from '../models';
4 | import {Adapter} from './adapter';
5 |
6 | export class SessionAdapter implements Adapter {
7 |
8 | public static adapt(session: any): SessionModel {
9 | if (!session) {
10 | return null;
11 | }
12 | return new SessionModel(
13 | get(session, 'id', null),
14 | get(session, 'user', null),
15 | get(session, 'userId', null),
16 | );
17 | }
18 |
19 | public static partition(session: SessionModel): any {
20 | if (!session) {
21 | return null;
22 | }
23 | return {
24 | id: get(session, 'id', null),
25 | user: get(session, 'user', null),
26 | userId: get(session, 'userId', null),
27 | };
28 | }
29 |
30 | public adapt(item: any): SessionModel {
31 | return SessionAdapter.adapt(item);
32 | }
33 |
34 | public partition(session: SessionModel): any {
35 | return SessionAdapter.partition(session);
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/app-routing.module.ts:
--------------------------------------------------------------------------------
1 | import {NgModule} from '@angular/core';
2 | import {Routes, RouterModule} from '@angular/router';
3 |
4 | import * as containersRoot from './containers';
5 | import {RouteGuardService} from './services/route-guard.service';
6 |
7 | const routes: Routes = [
8 | {
9 | path: '',
10 | pathMatch: 'full',
11 | component: containersRoot.HomeComponent,
12 | },
13 | {
14 | path: 'sign-in',
15 | component: containersRoot.SignInComponent,
16 | canActivate: [RouteGuardService],
17 | data: {
18 | allow: RouteGuardService.GUEST,
19 | redirect: ['/'],
20 | },
21 | },
22 | {
23 | path: 'settings',
24 | component: containersRoot.PageNotFoundComponent,
25 | canActivate: [RouteGuardService],
26 | data: {
27 | allow: RouteGuardService.USER,
28 | redirect: ['/sign-in'],
29 | },
30 | },
31 | {
32 | path: '**',
33 | component: containersRoot.PageNotFoundComponent,
34 | },
35 | ];
36 |
37 | @NgModule({
38 | imports: [RouterModule.forRoot(routes)],
39 | exports: [RouterModule]
40 | })
41 | export class AppRoutingModule {
42 | }
43 |
--------------------------------------------------------------------------------
/monorepo-example-www/README.md:
--------------------------------------------------------------------------------
1 | # MonorepoExampleWww
2 |
3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 7.0.4.
4 |
5 | ## Development server
6 |
7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
8 |
9 | ## Code scaffolding
10 |
11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
12 |
13 | ## Build
14 |
15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
16 |
17 | ## Running unit tests
18 |
19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
20 |
21 | ## Running end-to-end tests
22 |
23 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
24 |
25 | ## Further help
26 |
27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
28 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import {Component} from '@angular/core';
2 | import {SessionModel} from '@monorepo-example/shared';
3 | import {Store} from '@ngrx/store';
4 | import {Observable} from 'rxjs';
5 |
6 | import * as appActions from './actions/app.actions';
7 | import * as fromStore from './reducers';
8 |
9 | @Component({
10 | selector: 'app-app',
11 | templateUrl: './app.component.html',
12 | styleUrls: ['./app.component.scss']
13 | })
14 | export class AppComponent {
15 |
16 | appError$: Observable;
17 | appInstantiated$: Observable;
18 | appFeatures$: Observable;
19 | appSession$: Observable;
20 |
21 | constructor(private readonly store: Store) {
22 | this.appError$ = this.store.select(fromStore.selectAppError);
23 | this.appFeatures$ = this.store.select(fromStore.selectAppFeatures);
24 | this.appInstantiated$ = this.store.select(fromStore.selectAppInstantiated);
25 | this.appSession$ = this.store.select(fromStore.selectAppSession);
26 | }
27 |
28 | public doSignout(): void {
29 | this.store.dispatch(new appActions.UserClickedToolbarToSignout());
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/containers/home/home.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |

5 |
6 | Here are some links to help you start:
7 |
8 | -
9 |
10 |
11 | -
12 |
13 |
14 | -
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # monorepo-example
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | ## Build instructions
10 |
11 | (Instructions written within a Linux environment!)
12 |
13 | 1. First run `yarn` and install all dependencies in each project: `monorepo-example-api`, `monorepo-example-www` and `shared`
14 | 2. Build our local dependencies first by running `yarn build` in the `shared` project.
15 | 3. Create [an OAuth GitHub application](https://github.com/settings/applications/new) to get a `client_id` and a `client_secret` to use in the next step.
16 | 4. Make a file called `.env` based off of the `.env-template` file in the `monorepo-example-api` project (use your new app's client info from the previous step!) The `.env` file is ignored by git and should not be checked in.
17 | 5. Run `yarn start:dev` in the `monorepo-example-api` project.
18 | 6. (In another terminal) run `yarn start` in the `monorepo-example-www` project.
19 | 7. Visit `http://localhost:4200` in your browser.
20 | 8. Sign into your application via GitHub and see the 'Sign In' button turn to a 'Sign Out' button.
21 |
--------------------------------------------------------------------------------
/monorepo-example-api/src/app.module.ts:
--------------------------------------------------------------------------------
1 | import {MiddlewareConsumer, Module} from '@nestjs/common';
2 | import {ConfigModule} from './config';
3 | import {StorageModule} from './storage';
4 | import * as cookieParser from 'cookie-parser';
5 | require('dotenv').config();
6 |
7 | import * as controllersRoot from './controllers';
8 | import * as middlewaresRoot from './middlewares';
9 | import * as servicesRoot from './services';
10 |
11 | @Module({
12 | imports: [
13 | ConfigModule,
14 | StorageModule,
15 | ],
16 | controllers: [
17 | controllersRoot.AppController,
18 | controllersRoot.ApiController,
19 | ],
20 | providers: [
21 | servicesRoot.AppService,
22 | servicesRoot.UserService,
23 | servicesRoot.OauthService,
24 | ],
25 | })
26 | export class AppModule {
27 | configure(consumer: MiddlewareConsumer) {
28 | consumer
29 | .apply(middlewaresRoot.CorsMiddleware)
30 | .with({credentials: true, origin: 'http://localhost:4200', preflightContinue: true, methods: 'GET,POST,PUT,DELETE,OPTIONS'})
31 | .forRoutes(controllersRoot.ApiController);
32 | consumer
33 | .apply(cookieParser(), middlewaresRoot.JwtSessionMiddleware)
34 | .forRoutes(controllersRoot.ApiController);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/shared/src/adapters/user.adapter.ts:
--------------------------------------------------------------------------------
1 | import {get} from 'lodash';
2 |
3 | import {UserModel} from '../models';
4 | import {Adapter} from './adapter';
5 |
6 | export class UserAdapter implements Adapter {
7 |
8 | public static adapt(user: any): UserModel {
9 | if (!user) {
10 | return null;
11 | }
12 | return new UserModel(
13 | get(user,'id', null),
14 | {
15 | oauth: {
16 | github: {
17 | id: get(user, 'auth.oauth.github.id', null),
18 | },
19 | },
20 | },
21 | );
22 | }
23 |
24 | public static partition(user: UserModel): any {
25 | if (!user) {
26 | return null;
27 | }
28 | return {
29 | id: get(user, 'id', null),
30 | auth: {
31 | oauth: {
32 | github: {
33 | id: get(user, 'auth.oauth.github.id', null),
34 | },
35 | },
36 | },
37 | };
38 | }
39 |
40 | public adapt(item: any): UserModel {
41 | return UserAdapter.adapt(item);
42 | }
43 |
44 | public partition(user: UserModel): any {
45 | return UserAdapter.partition(user);
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/reducers/app.reducer.ts:
--------------------------------------------------------------------------------
1 | import {SessionModel} from '@monorepo-example/shared';
2 |
3 | import {AppActions, AppActionTypes} from '../actions/app.actions';
4 |
5 | export interface State {
6 | error: string;
7 | features: string[];
8 | instantiated: boolean;
9 | session: SessionModel;
10 | }
11 |
12 | export const initialState: State = {
13 | error: null,
14 | features: null,
15 | instantiated: null,
16 | session: null,
17 | };
18 |
19 | export function reducer(state = initialState, action: AppActions): State {
20 |
21 | switch (action.type) {
22 |
23 | case AppActionTypes.LoadInitialAppConfig: {
24 | if (action.payload.session === undefined) {
25 | return {
26 | ...state,
27 | error: 'Session information was not provided.',
28 | instantiated: true,
29 | };
30 | }
31 | if (action.payload.features === undefined) {
32 | return {
33 | ...state,
34 | error: 'Features were not provided.',
35 | instantiated: true,
36 | };
37 | }
38 | return {
39 | ...state,
40 | features: action.payload.features,
41 | instantiated: true,
42 | session: action.payload.session,
43 | };
44 | }
45 |
46 | default:
47 | return state;
48 | }
49 | }
50 |
51 |
52 |
--------------------------------------------------------------------------------
/monorepo-example-api/src/config/config.module.ts:
--------------------------------------------------------------------------------
1 | import {Module} from '@nestjs/common';
2 |
3 | import {LocalConfigService} from './local-config.service';
4 |
5 | export abstract class BaseConfigService {
6 | public jwt: {
7 | signing_key: string;
8 | };
9 | public oauth: {
10 | github: {
11 | access_token_url: string;
12 | authorize_url: string;
13 | client_id: string;
14 | client_secret: string;
15 | redirect_uri: string;
16 | user_agent_header: string;
17 | user_profile_url: string;
18 | };
19 | };
20 | public storage: {
21 | type: string;
22 | };
23 | }
24 |
25 | @Module({
26 | providers: [
27 | {
28 | provide: BaseConfigService,
29 | useFactory: ConfigModule.Factory,
30 | }
31 | ],
32 | exports: [
33 | BaseConfigService,
34 | ],
35 | })
36 | export class ConfigModule {
37 | public static Factory() {
38 | switch (process.env['monorepoexampleCONFIG']) {
39 | case 'environment': {
40 | return null;
41 | }
42 | case 'dotenv': {
43 | return null;
44 | }
45 | default: {
46 | return new LocalConfigService();
47 | }
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/monorepo-example-api/src/storage/memory-storage.service.ts:
--------------------------------------------------------------------------------
1 | import {UserModel} from '@monorepo-example/shared';
2 | import {Injectable} from '@nestjs/common';
3 | import {Observable, of} from 'rxjs';
4 |
5 | import {BaseStorageService} from './storage.module';
6 |
7 | @Injectable()
8 | export class MemoryStorageService implements BaseStorageService {
9 |
10 | private users: UserModel[] = [];
11 |
12 | public createUser(user: UserModel): Observable {
13 | this.users.push(user);
14 | return of(user);
15 | }
16 |
17 | public getUser(id: string): Observable {
18 | for (const user of this.users) {
19 | if (user.id === id) {
20 | return of(user);
21 | }
22 | }
23 | return of(null);
24 | }
25 |
26 | public getUserByGithubId(githubId: string): Observable {
27 | for (const user of this.users) {
28 | if (!user.auth) {
29 | continue;
30 | }
31 | if (!user.auth.oauth) {
32 | continue;
33 | }
34 | if (!user.auth.oauth.github) {
35 | continue;
36 | }
37 | if (user.auth.oauth.github.id === githubId) {
38 | return of(user);
39 | }
40 | }
41 | return of(null);
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/monorepo-example-api/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "defaultSeverity": "error",
3 | "extends": [
4 | "tslint:recommended"
5 | ],
6 | "jsRules": {
7 | "no-unused-expression": true
8 | },
9 | "rules": {
10 | "eofline": false,
11 | "quotemark": [
12 | true,
13 | "single"
14 | ],
15 | "indent": false,
16 | "member-access": [
17 | false
18 | ],
19 | "ordered-imports": [
20 | false
21 | ],
22 | "max-line-length": [
23 | true,
24 | 150
25 | ],
26 | "member-ordering": [
27 | false
28 | ],
29 | "curly": false,
30 | "interface-name": [
31 | false
32 | ],
33 | "array-type": [
34 | false
35 | ],
36 | "no-empty-interface": false,
37 | "no-empty": false,
38 | "arrow-parens": false,
39 | "object-literal-sort-keys": false,
40 | "no-unused-expression": false,
41 | "max-classes-per-file": [
42 | false
43 | ],
44 | "variable-name": [
45 | false
46 | ],
47 | "one-line": [
48 | false
49 | ],
50 | "one-variable-per-declaration": [
51 | false
52 | ]
53 | },
54 | "rulesDirectory": []
55 | }
56 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/services/app-config.service.ts:
--------------------------------------------------------------------------------
1 | import {HttpClient} from '@angular/common/http';
2 | import {Injectable} from '@angular/core';
3 | import {AppConfigModel} from '@monorepo-example/shared';
4 | import {Store} from '@ngrx/store';
5 | import {of} from 'rxjs';
6 | import {catchError} from 'rxjs/operators';
7 |
8 | import {environment} from '../../environments/environment';
9 | import * as appActions from '../actions/app.actions';
10 | import * as reducersRoot from '../reducers';
11 |
12 | @Injectable({
13 | providedIn: 'root'
14 | })
15 | export class AppConfigService {
16 |
17 | constructor(private readonly store: Store) {
18 | }
19 |
20 | public static Initialize(httpClient: HttpClient, appConfigService: AppConfigService): () => Promise {
21 | return () => new Promise(resolve => {
22 | httpClient.get(environment.url.appConfig, {withCredentials: true})
23 | .pipe(
24 | catchError(err => {
25 | console.error({err});
26 | return of(null);
27 | })
28 | )
29 | .subscribe(appConfig => {
30 | appConfigService.load(appConfig);
31 | resolve();
32 | });
33 | });
34 | }
35 |
36 | public load(appConfig: AppConfigModel): void {
37 | this.store.dispatch(new appActions.LoadInitialAppConfig(appConfig));
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/monorepo-example-api/src/storage/storage.module.ts:
--------------------------------------------------------------------------------
1 | import {UserModel} from '@monorepo-example/shared';
2 | import {Module} from '@nestjs/common';
3 | import {Observable} from 'rxjs';
4 |
5 | import {ConfigModule, BaseConfigService} from '../config';
6 | import {MemoryStorageService} from './memory-storage.service';
7 |
8 | export abstract class BaseStorageService {
9 | abstract createUser(user: UserModel): Observable;
10 | abstract getUser(id: string): Observable;
11 | abstract getUserByGithubId(githubId: string): Observable;
12 | }
13 |
14 | @Module({
15 | imports: [
16 | ConfigModule,
17 | ],
18 | providers: [
19 | {
20 | provide: BaseStorageService,
21 | useFactory: StorageModule.Factory,
22 | inject: [BaseConfigService]
23 | }
24 | ],
25 | exports: [
26 | BaseStorageService,
27 | ],
28 | })
29 | export class StorageModule {
30 | public static Factory(baseConfigService: BaseConfigService) {
31 | switch (baseConfigService.storage.type) {
32 | case 'aws-dynamodb': {
33 | return null;
34 | }
35 | case 'azure-cosmosdb': {
36 | return null;
37 | }
38 | case 'local-sqlite': {
39 | return null;
40 | }
41 | default: {
42 | return new MemoryStorageService();
43 | }
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/monorepo-example-api/src/middlewares/jwt-session.middleware.ts:
--------------------------------------------------------------------------------
1 | import {Injectable, MiddlewareFunction, NestMiddleware} from '@nestjs/common';
2 | import {Request} from 'express';
3 | import {verify} from 'jsonwebtoken';
4 |
5 | import {BaseConfigService} from '../config';
6 |
7 | @Injectable()
8 | export class JwtSessionMiddleware implements NestMiddleware {
9 |
10 | constructor(private readonly baseConfigService: BaseConfigService) {
11 | }
12 |
13 | public static getToken(req: Request): string {
14 | if (req.headers.authorization) {
15 | const authorizationPair = req.headers.authorization.split(' ');
16 | if (authorizationPair[0] === 'Bearer') {
17 | return authorizationPair[1];
18 | }
19 | }
20 | if (req.cookies && req.cookies.token) {
21 | return req.cookies.token;
22 | }
23 | if (req.query && req.query.token) {
24 | return req.query.token;
25 | }
26 | }
27 |
28 | resolve(...args: any[]): MiddlewareFunction {
29 | return (req, res, next) => {
30 | const access_token = JwtSessionMiddleware.getToken(req);
31 | if (access_token) {
32 | try {
33 | (req as any).session = verify(access_token, this.baseConfigService.jwt.signing_key);
34 | } catch(err) {
35 | (req as any).session = null;
36 | }
37 | }
38 | next()
39 | };
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import {HttpClient, HttpClientModule} from '@angular/common/http';
2 | import {APP_INITIALIZER, NgModule} from '@angular/core';
3 | import {BrowserModule} from '@angular/platform-browser';
4 | import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
5 | import {Store} from '@ngrx/store';
6 |
7 | import * as containersRoot from './containers';
8 | import * as servicesRoot from './services';
9 |
10 | /**
11 | * The initial application entry point:
12 | */
13 | import {AngularMaterialModule} from './angular-material.module';
14 | import {AppComponent} from './app.component';
15 | import {AppRoutingModule} from './app-routing.module';
16 | import {AppStoreModule} from './app-store.module';
17 | import {ToolbarComponentModule} from './components/toolbar/toolbar-component.module';
18 |
19 | @NgModule({
20 | imports: [
21 | BrowserModule,
22 | HttpClientModule,
23 | BrowserAnimationsModule,
24 | AppStoreModule,
25 | /**
26 | * The initial application entry point:
27 | */
28 | AngularMaterialModule,
29 | AppRoutingModule,
30 | ToolbarComponentModule,
31 | /**
32 | * View Containers
33 | */
34 | containersRoot.HomeModule,
35 | containersRoot.PageNotFoundModule,
36 | containersRoot.SignInModule,
37 | ],
38 | declarations: [AppComponent],
39 | bootstrap: [AppComponent],
40 | providers: [
41 | {
42 | provide: APP_INITIALIZER,
43 | useFactory: servicesRoot.AppConfigService.Initialize,
44 | deps: [HttpClient, servicesRoot.AppConfigService, Store],
45 | multi: true,
46 | },
47 | ],
48 | })
49 | export class AppModule {
50 | }
51 |
--------------------------------------------------------------------------------
/monorepo-example-api/src/services/user.service.ts:
--------------------------------------------------------------------------------
1 | import {UserAdapter, UserModel} from '@monorepo-example/shared';
2 | import {Injectable} from '@nestjs/common';
3 | import {Observable, of} from 'rxjs';
4 | import {switchMap} from 'rxjs/operators';
5 | import * as uuid from 'uuid/v4';
6 |
7 | import {BaseStorageService} from '../storage';
8 |
9 | @Injectable()
10 | export class UserService {
11 |
12 | constructor(private readonly baseStorageService: BaseStorageService) {
13 | }
14 |
15 | public createUser(user: any = {}): Observable {
16 | return this.baseStorageService.createUser(UserAdapter.adapt({
17 | id: uuid(),
18 | ...user,
19 | }));
20 | }
21 |
22 | public getUserById(id: string): Observable {
23 | return this.baseStorageService.getUser(id);
24 | }
25 |
26 | public getUserByGithubId(githubId: string, createIfNotExists?: boolean): Observable {
27 | return this.baseStorageService.getUserByGithubId(githubId)
28 | .pipe(
29 | switchMap((user: UserModel) => {
30 | if (user) {
31 | return of(user);
32 | }
33 | return this.createUser({
34 | auth: {
35 | oauth: {
36 | github: {
37 | id: githubId,
38 | },
39 | },
40 | },
41 | });
42 | }),
43 | );
44 | }
45 |
46 | public updateUser(user: UserModel, updates: UserModel): Observable {
47 | return of(user);
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/containers/sign-in/sign-in.component.ts:
--------------------------------------------------------------------------------
1 | import {Component} from '@angular/core';
2 | import {FormControl, Validators} from '@angular/forms';
3 | import {SessionModel} from '@monorepo-example/shared';
4 | import {Store} from '@ngrx/store';
5 | import {Observable} from 'rxjs';
6 |
7 | import {environment} from '../../../environments/environment';
8 | import * as fromStore from '../../reducers';
9 |
10 | @Component({
11 | selector: 'app-sign-in',
12 | templateUrl: './sign-in.component.html',
13 | styleUrls: ['./sign-in.component.css']
14 | })
15 | export class SignInComponent {
16 |
17 | appError$: Observable;
18 | appInstantiated$: Observable;
19 | appFeatures$: Observable;
20 | appSession$: Observable;
21 | urlSigninGithub = environment.url.signinGithub;
22 |
23 | email = new FormControl('', [Validators.required, Validators.email]);
24 | password = new FormControl('', [Validators.required]);
25 |
26 | constructor(private readonly store: Store) {
27 | this.appError$ = this.store.select(fromStore.selectAppError);
28 | this.appFeatures$ = this.store.select(fromStore.selectAppFeatures);
29 | this.appInstantiated$ = this.store.select(fromStore.selectAppInstantiated);
30 | this.appSession$ = this.store.select(fromStore.selectAppSession);
31 | }
32 |
33 | getEmailErrorMessage() {
34 | return this.email.hasError('required') ? 'You must enter a value'
35 | : this.email.hasError('email') ? 'Not a valid email'
36 | : '';
37 | }
38 |
39 | getPasswordErrorMessage() {
40 | return this.email.hasError('required') ? 'You must enter a value'
41 | : this.email.hasError('email') ? 'Not a valid email'
42 | : '';
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/app/containers/sign-in/sign-in.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
54 |
--------------------------------------------------------------------------------
/monorepo-example-www/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "monorepo-example-www",
3 | "version": "0.0.0",
4 | "scripts": {
5 | "ng": "ng",
6 | "start": "ng serve --live-reload false",
7 | "build": "ng build",
8 | "test": "ng test",
9 | "lint": "ng lint",
10 | "e2e": "ng e2e"
11 | },
12 | "private": true,
13 | "dependencies": {
14 | "@angular/animations": "^7.0.2",
15 | "@angular/cdk": "^7.0.2",
16 | "@angular/common": "~7.0.0",
17 | "@angular/compiler": "~7.0.0",
18 | "@angular/core": "~7.0.0",
19 | "@angular/flex-layout": "^7.0.0-beta.19",
20 | "@angular/forms": "~7.0.0",
21 | "@angular/http": "~7.0.0",
22 | "@angular/material": "^7.0.2",
23 | "@angular/platform-browser": "~7.0.0",
24 | "@angular/platform-browser-dynamic": "~7.0.0",
25 | "@angular/router": "~7.0.0",
26 | "@monorepo-example/shared": "../shared",
27 | "@ngrx/effects": "^6.1.2",
28 | "@ngrx/entity": "^6.1.2",
29 | "@ngrx/store": "^6.1.2",
30 | "@ngrx/store-devtools": "^6.1.2",
31 | "core-js": "^2.5.4",
32 | "hammerjs": "^2.0.8",
33 | "rxjs": "~6.3.3",
34 | "zone.js": "~0.8.26"
35 | },
36 | "devDependencies": {
37 | "@angular-devkit/build-angular": "~0.10.0",
38 | "@angular/cli": "~7.0.4",
39 | "@angular/compiler-cli": "~7.0.0",
40 | "@angular/language-service": "~7.0.0",
41 | "@ngrx/schematics": "^6.1.2",
42 | "@types/jasmine": "~2.8.8",
43 | "@types/jasminewd2": "~2.0.3",
44 | "@types/node": "~8.9.4",
45 | "codelyzer": "~4.5.0",
46 | "jasmine-core": "~2.99.1",
47 | "jasmine-spec-reporter": "~4.2.1",
48 | "karma": "~3.0.0",
49 | "karma-chrome-launcher": "~2.2.0",
50 | "karma-coverage-istanbul-reporter": "~2.0.1",
51 | "karma-jasmine": "~1.1.2",
52 | "karma-jasmine-html-reporter": "^0.2.2",
53 | "protractor": "~5.4.0",
54 | "ts-node": "~7.0.0",
55 | "tslint": "~5.11.0",
56 | "typescript": "~3.1.1"
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/monorepo-example-api/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "monorepo-example-api",
3 | "version": "0.0.0",
4 | "description": "The backend API code for the monorepo-example website.",
5 | "author": "James Allen",
6 | "license": "UNLICENSED",
7 | "scripts": {
8 | "nest": "nest",
9 | "format": "prettier --write \"src/**/*.ts\"",
10 | "start": "ts-node -r tsconfig-paths/register src/main.ts",
11 | "start:dev": "nodemon",
12 | "start:debug": "nodemon --config nodemon-debug.json",
13 | "prestart:prod": "rimraf dist && tsc",
14 | "start:prod": "node dist/main.js",
15 | "start:hmr": "node dist/server",
16 | "lint": "tslint -p tsconfig.json -c tslint.json",
17 | "test": "jest",
18 | "test:watch": "jest --watch",
19 | "test:cov": "jest --coverage",
20 | "test:e2e": "jest --config ./test/jest-e2e.json",
21 | "webpack": "webpack --config webpack.config.js"
22 | },
23 | "dependencies": {
24 | "@monorepo-example/shared": "../shared",
25 | "@nestjs/cli": "^5.6.2",
26 | "@nestjs/common": "^5.1.0",
27 | "@nestjs/core": "^5.1.0",
28 | "async": "^2.6.1",
29 | "cookie-parser": "^1.4.3",
30 | "cors": "^2.8.5",
31 | "dotenv": "^6.1.0",
32 | "jsonwebtoken": "^8.4.0",
33 | "lodash": "^4.17.11",
34 | "reflect-metadata": "^0.1.12",
35 | "request": "^2.88.0",
36 | "rxjs": "^6.2.2",
37 | "typescript": "^3.0.1",
38 | "uuid": "^3.3.2"
39 | },
40 | "devDependencies": {
41 | "@nestjs/testing": "^5.1.0",
42 | "@types/async": "^2.0.50",
43 | "@types/cookie-parser": "^1.4.1",
44 | "@types/cors": "^2.8.4",
45 | "@types/express": "^4.16.0",
46 | "@types/jest": "^23.3.1",
47 | "@types/jsonwebtoken": "^8.3.0",
48 | "@types/lodash": "^4.14.118",
49 | "@types/node": "^10.7.1",
50 | "@types/request": "^2.48.1",
51 | "@types/supertest": "^2.0.5",
52 | "@types/uuid": "^3.4.4",
53 | "jest": "^23.5.0",
54 | "nodemon": "^1.18.3",
55 | "prettier": "^1.14.2",
56 | "rimraf": "^2.6.2",
57 | "supertest": "^3.1.0",
58 | "ts-jest": "^23.1.3",
59 | "ts-loader": "^4.4.2",
60 | "ts-node": "^7.0.1",
61 | "tsconfig-paths": "^3.5.0",
62 | "tslint": "5.11.0",
63 | "webpack": "^4.16.5",
64 | "webpack-cli": "^3.1.0",
65 | "webpack-node-externals": "^1.7.2"
66 | },
67 | "jest": {
68 | "moduleFileExtensions": [
69 | "js",
70 | "json",
71 | "ts"
72 | ],
73 | "rootDir": "src",
74 | "testRegex": ".spec.ts$",
75 | "transform": {
76 | "^.+\\.(t|j)s$": "ts-jest"
77 | },
78 | "coverageDirectory": "../coverage",
79 | "testEnvironment": "node"
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/monorepo-example-api/src/controllers/api.controller.ts:
--------------------------------------------------------------------------------
1 | import {AppConfigAdapter, AppConfigModel, SessionAdapter, SessionModel} from '@monorepo-example/shared';
2 | import {Body, Controller, Get, Options, Post, Query, Res, Session} from '@nestjs/common';
3 | import {Response} from 'express';
4 | import {sign} from 'jsonwebtoken';
5 | import {get} from 'lodash';
6 | import {of}from 'rxjs';
7 | import {catchError, switchMap} from 'rxjs/operators';
8 | import * as uuid from 'uuid/v4';
9 |
10 | import {BaseConfigService} from '../config';
11 | import {OauthService, UserService} from '../services';
12 |
13 | @Controller('api')
14 | export class ApiController {
15 |
16 | constructor(private readonly baseConfigService: BaseConfigService,
17 | private readonly oauthService: OauthService,
18 | private readonly userService: UserService) {
19 | }
20 |
21 | @Options('*')
22 | apiOptions(): void {
23 | }
24 |
25 | @Get('app-config')
26 | apiGetAppConfig(@Session() session: SessionModel = null): AppConfigModel {
27 | if (!session) {
28 | return AppConfigAdapter.adapt({});
29 | }
30 | return AppConfigAdapter.adapt({
31 | session: SessionAdapter.adapt({
32 | id: session.id,
33 | userId: session.userId,
34 | }),
35 | });
36 | }
37 |
38 | @Post('sign-out')
39 | apiPostSignOut(@Body() body,
40 | @Res() res: Response,
41 | @Session() session: SessionModel = null): any {
42 | if (get(body, 'id') === get(session, 'id')) {
43 | res.clearCookie('token');
44 | res.send({error: null});
45 | } else {
46 | res.send({error: true});
47 | }
48 | }
49 |
50 | @Get('sign-in/github')
51 | apiGetSignInGithub(@Res() res: Response): void {
52 | const state = Date.now().toString();
53 | this.oauthService.githubRedirectUrl(state)
54 | .subscribe((redirect_url) => res.redirect(redirect_url));
55 | }
56 |
57 | @Get('oauth/github')
58 | apiGetOauthGithub(@Session() session: SessionModel = null,
59 | @Query('code') code: string,
60 | @Query('state') state: string,
61 | @Res() res: Response): void {
62 | this.oauthService.githubAuthenticate(code)
63 | .pipe(
64 | catchError((err) => {
65 | console.log({err});
66 | return of(null);
67 | }),
68 | switchMap((githubId: string) => this.userService.getUserByGithubId(githubId, true)),
69 | )
70 | .subscribe(
71 | (user) => {
72 | const userId = user.id;
73 | const encode = String;
74 | const httpOnly = true;
75 | const id = uuid();
76 | const jwtToken = sign({userId, id}, this.baseConfigService.jwt.signing_key);
77 | res.cookie('token', jwtToken, {encode, httpOnly});
78 | res.redirect('http://localhost:4200/');
79 | }
80 | );
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/monorepo-example-www/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "rulesDirectory": [
3 | "node_modules/codelyzer"
4 | ],
5 | "rules": {
6 | "arrow-return-shorthand": true,
7 | "callable-types": true,
8 | "class-name": true,
9 | "comment-format": [
10 | true,
11 | "check-space"
12 | ],
13 | "curly": true,
14 | "deprecation": {
15 | "severity": "warn"
16 | },
17 | "eofline": true,
18 | "forin": true,
19 | "import-blacklist": [
20 | true,
21 | "rxjs/Rx"
22 | ],
23 | "import-spacing": true,
24 | "indent": [
25 | true,
26 | "spaces"
27 | ],
28 | "interface-over-type-literal": true,
29 | "label-position": true,
30 | "max-line-length": [
31 | true,
32 | 140
33 | ],
34 | "member-access": false,
35 | "member-ordering": [
36 | true,
37 | {
38 | "order": [
39 | "static-field",
40 | "instance-field",
41 | "static-method",
42 | "instance-method"
43 | ]
44 | }
45 | ],
46 | "no-arg": true,
47 | "no-bitwise": true,
48 | "no-console": [
49 | true,
50 | "debug",
51 | "info",
52 | "time",
53 | "timeEnd",
54 | "trace"
55 | ],
56 | "no-construct": true,
57 | "no-debugger": true,
58 | "no-duplicate-super": true,
59 | "no-empty": false,
60 | "no-empty-interface": true,
61 | "no-eval": true,
62 | "no-inferrable-types": [
63 | true,
64 | "ignore-params"
65 | ],
66 | "no-misused-new": true,
67 | "no-non-null-assertion": true,
68 | "no-redundant-jsdoc": true,
69 | "no-shadowed-variable": true,
70 | "no-string-literal": false,
71 | "no-string-throw": true,
72 | "no-switch-case-fall-through": true,
73 | "no-trailing-whitespace": true,
74 | "no-unnecessary-initializer": true,
75 | "no-unused-expression": true,
76 | "no-use-before-declare": true,
77 | "no-var-keyword": true,
78 | "object-literal-sort-keys": false,
79 | "one-line": [
80 | true,
81 | "check-open-brace",
82 | "check-catch",
83 | "check-else",
84 | "check-whitespace"
85 | ],
86 | "prefer-const": true,
87 | "quotemark": [
88 | true,
89 | "single"
90 | ],
91 | "radix": true,
92 | "semicolon": [
93 | true,
94 | "always"
95 | ],
96 | "triple-equals": [
97 | true,
98 | "allow-null-check"
99 | ],
100 | "typedef-whitespace": [
101 | true,
102 | {
103 | "call-signature": "nospace",
104 | "index-signature": "nospace",
105 | "parameter": "nospace",
106 | "property-declaration": "nospace",
107 | "variable-declaration": "nospace"
108 | }
109 | ],
110 | "unified-signatures": true,
111 | "variable-name": false,
112 | "whitespace": [
113 | true,
114 | "check-branch",
115 | "check-decl",
116 | "check-operator",
117 | "check-separator",
118 | "check-type"
119 | ],
120 | "no-output-on-prefix": true,
121 | "use-input-property-decorator": true,
122 | "use-output-property-decorator": true,
123 | "use-host-property-decorator": true,
124 | "no-input-rename": true,
125 | "no-output-rename": true,
126 | "use-life-cycle-interface": true,
127 | "use-pipe-transform-interface": true,
128 | "component-class-suffix": true,
129 | "directive-class-suffix": true
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/monorepo-example-www/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This file includes polyfills needed by Angular and is loaded before the app.
3 | * You can add your own extra polyfills to this file.
4 | *
5 | * This file is divided into 2 sections:
6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
8 | * file.
9 | *
10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
13 | *
14 | * Learn more in https://angular.io/guide/browser-support
15 | */
16 |
17 | /***************************************************************************************************
18 | * BROWSER POLYFILLS
19 | */
20 |
21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/
22 | // import 'core-js/es6/symbol';
23 | // import 'core-js/es6/object';
24 | // import 'core-js/es6/function';
25 | // import 'core-js/es6/parse-int';
26 | // import 'core-js/es6/parse-float';
27 | // import 'core-js/es6/number';
28 | // import 'core-js/es6/math';
29 | // import 'core-js/es6/string';
30 | // import 'core-js/es6/date';
31 | // import 'core-js/es6/array';
32 | // import 'core-js/es6/regexp';
33 | // import 'core-js/es6/map';
34 | // import 'core-js/es6/weak-map';
35 | // import 'core-js/es6/set';
36 |
37 | /**
38 | * If the application will be indexed by Google Search, the following is required.
39 | * Googlebot uses a renderer based on Chrome 41.
40 | * https://developers.google.com/search/docs/guides/rendering
41 | **/
42 | // import 'core-js/es6/array';
43 |
44 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */
45 | // import 'classlist.js'; // Run `npm install --save classlist.js`.
46 |
47 | /** IE10 and IE11 requires the following for the Reflect API. */
48 | // import 'core-js/es6/reflect';
49 |
50 | /**
51 | * Web Animations `@angular/platform-browser/animations`
52 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
53 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
54 | **/
55 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`.
56 |
57 | /**
58 | * By default, zone.js will patch all possible macroTask and DomEvents
59 | * user can disable parts of macroTask/DomEvents patch by setting following flags
60 | */
61 |
62 | // (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
63 | // (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
64 | // (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
65 |
66 | /*
67 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
68 | * with the following flag, it will bypass `zone.js` patch for IE/Edge
69 | */
70 | // (window as any).__Zone_enable_cross_context_check = true;
71 |
72 | /***************************************************************************************************
73 | * Zone JS is required by default for Angular itself.
74 | */
75 | import 'zone.js/dist/zone'; // Included with Angular CLI.
76 |
77 |
78 | /***************************************************************************************************
79 | * APPLICATION IMPORTS
80 | */
81 |
--------------------------------------------------------------------------------
/monorepo-example-api/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | [travis-image]: https://api.travis-ci.org/nestjs/nest.svg?branch=master
6 | [travis-url]: https://travis-ci.org/nestjs/nest
7 | [linux-image]: https://img.shields.io/travis/nestjs/nest/master.svg?label=linux
8 | [linux-url]: https://travis-ci.org/nestjs/nest
9 |
10 | A progressive Node.js framework for building efficient and scalable server-side applications, heavily inspired by Angular.
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
26 |
27 | ## Description
28 |
29 | [Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
30 |
31 | ## Installation
32 |
33 | ```bash
34 | $ npm install
35 | ```
36 |
37 | ## Running the app
38 |
39 | ```bash
40 | # development
41 | $ npm run start
42 |
43 | # watch mode
44 | $ npm run start:dev
45 |
46 | # incremental rebuild (webpack)
47 | $ npm run webpack
48 | $ npm run start:hmr
49 |
50 | # production mode
51 | $ npm run start:prod
52 | ```
53 |
54 | ## Test
55 |
56 | ```bash
57 | # unit tests
58 | $ npm run test
59 |
60 | # e2e tests
61 | $ npm run test:e2e
62 |
63 | # test coverage
64 | $ npm run test:cov
65 | ```
66 |
67 | ## Support
68 |
69 | Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
70 |
71 | ## Stay in touch
72 |
73 | - Author - [Kamil Myśliwiec](https://kamilmysliwiec.com)
74 | - Website - [https://nestjs.com](https://nestjs.com/)
75 | - Twitter - [@nestframework](https://twitter.com/nestframework)
76 |
77 | ## License
78 |
79 | Nest is [MIT licensed](LICENSE).
80 |
--------------------------------------------------------------------------------
/monorepo-example-api/src/services/oauth.service.ts:
--------------------------------------------------------------------------------
1 | import {Injectable} from '@nestjs/common';
2 | import {waterfall} from 'async';
3 | import * as request from 'request';
4 | import {Observable, of, Subscriber} from 'rxjs';
5 | import {URL} from 'url';
6 |
7 | import {BaseConfigService} from '../config';
8 |
9 | @Injectable()
10 | export class OauthService {
11 |
12 | constructor(private readonly baseConfigService: BaseConfigService) {
13 | }
14 |
15 | public githubRedirectUrl(state?: string): Observable {
16 | const url = new URL(this.baseConfigService.oauth.github.authorize_url);
17 | url.searchParams.set('client_id', this.baseConfigService.oauth.github.client_id);
18 | url.searchParams.set('redirect_uri', this.baseConfigService.oauth.github.redirect_uri);
19 | if (state) {
20 | url.searchParams.set('state', state);
21 | }
22 | return of(url.toString());
23 | }
24 |
25 | public githubAuthenticate(code: string): Observable {
26 | return Observable.create((observer: Subscriber) => {
27 | waterfall(
28 | [
29 | (next) => {
30 | request.post({
31 | url: this.baseConfigService.oauth.github.access_token_url,
32 | json: {
33 | client_id: this.baseConfigService.oauth.github.client_id,
34 | client_secret: this.baseConfigService.oauth.github.client_secret,
35 | code,
36 | },
37 | }, (error, r, body: any) => {
38 | if (error) {
39 | next(error);
40 | } else if (!body) {
41 | next(new Error('oAuth: Bad access token response from GitHub'));
42 | } else if (!body.access_token) {
43 | next(new Error('oAuth: No access token from GitHub.'));
44 | } else {
45 | next(null, body.access_token);
46 | }
47 | })
48 | },
49 | (access_token: string, next) => {
50 | request.get({
51 | url: this.baseConfigService.oauth.github.user_profile_url,
52 | json: true,
53 | headers: {
54 | 'User-Agent': this.baseConfigService.oauth.github.user_agent_header,
55 | Authorization: `token ${access_token}`,
56 | },
57 | }, (error, r, body) => {
58 | if (error) {
59 | next(error);
60 | } else if (!body) {
61 | next(new Error('oAuth: Bad profile response from GitHub.'))
62 | } else if (!body.id) {
63 | next(new Error('oAuth: No profile from GitHub profile.'))
64 | } else {
65 | next(null, body.id.toString());
66 | }
67 | });
68 | },
69 | ],
70 | (error, github_id: string) => {
71 | if (error) {
72 | observer.error(error);
73 | } else {
74 | observer.next(github_id);
75 | observer.complete();
76 | }
77 | }
78 | );
79 | });
80 | }
81 |
82 | }
83 |
--------------------------------------------------------------------------------
/shared/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 | "@types/lodash@^4.14.116":
6 | version "4.14.118"
7 | resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.118.tgz#247bab39bfcc6d910d4927c6e06cbc70ec376f27"
8 | integrity sha512-iiJbKLZbhSa6FYRip/9ZDX6HXhayXLDGY2Fqws9cOkEQ6XeKfaxB0sC541mowZJueYyMnVUmmG+al5/4fCDrgw==
9 |
10 | balanced-match@^1.0.0:
11 | version "1.0.0"
12 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
13 | integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
14 |
15 | brace-expansion@^1.1.7:
16 | version "1.1.11"
17 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
18 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
19 | dependencies:
20 | balanced-match "^1.0.0"
21 | concat-map "0.0.1"
22 |
23 | concat-map@0.0.1:
24 | version "0.0.1"
25 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
26 | integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
27 |
28 | fs.realpath@^1.0.0:
29 | version "1.0.0"
30 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
31 | integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
32 |
33 | glob@^7.0.5:
34 | version "7.1.3"
35 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1"
36 | integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==
37 | dependencies:
38 | fs.realpath "^1.0.0"
39 | inflight "^1.0.4"
40 | inherits "2"
41 | minimatch "^3.0.4"
42 | once "^1.3.0"
43 | path-is-absolute "^1.0.0"
44 |
45 | inflight@^1.0.4:
46 | version "1.0.6"
47 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
48 | integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
49 | dependencies:
50 | once "^1.3.0"
51 | wrappy "1"
52 |
53 | inherits@2:
54 | version "2.0.3"
55 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
56 | integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
57 |
58 | lodash@^4.17.11:
59 | version "4.17.11"
60 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
61 | integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==
62 |
63 | minimatch@^3.0.4:
64 | version "3.0.4"
65 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
66 | integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
67 | dependencies:
68 | brace-expansion "^1.1.7"
69 |
70 | once@^1.3.0:
71 | version "1.4.0"
72 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
73 | integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
74 | dependencies:
75 | wrappy "1"
76 |
77 | path-is-absolute@^1.0.0:
78 | version "1.0.1"
79 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
80 | integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
81 |
82 | rimraf@^2.6.2:
83 | version "2.6.2"
84 | resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36"
85 | integrity sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==
86 | dependencies:
87 | glob "^7.0.5"
88 |
89 | typescript@^3.1.6:
90 | version "3.1.6"
91 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.1.6.tgz#b6543a83cfc8c2befb3f4c8fba6896f5b0c9be68"
92 | integrity sha512-tDMYfVtvpb96msS1lDX9MEdHrW4yOuZ4Kdc4Him9oU796XldPYF/t2+uKoX0BBa0hXXwDlqYQbXY5Rzjzc5hBA==
93 |
94 | wrappy@1:
95 | version "1.0.2"
96 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
97 | integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
98 |
--------------------------------------------------------------------------------
/monorepo-example-www/angular.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "version": 1,
4 | "newProjectRoot": "projects",
5 | "projects": {
6 | "monorepo-example-www": {
7 | "root": "",
8 | "sourceRoot": "src",
9 | "projectType": "application",
10 | "prefix": "app",
11 | "schematics": {
12 | "@schematics/angular:component": {
13 | "styleext": "scss"
14 | }
15 | },
16 | "architect": {
17 | "build": {
18 | "builder": "@angular-devkit/build-angular:browser",
19 | "options": {
20 | "outputPath": "dist/monorepo-example-www",
21 | "index": "src/index.html",
22 | "main": "src/main.ts",
23 | "polyfills": "src/polyfills.ts",
24 | "tsConfig": "src/tsconfig.app.json",
25 | "assets": [
26 | "src/favicon.ico",
27 | "src/assets"
28 | ],
29 | "styles": [
30 | "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
31 | "src/styles.scss"
32 | ],
33 | "scripts": []
34 | },
35 | "configurations": {
36 | "production": {
37 | "fileReplacements": [
38 | {
39 | "replace": "src/environments/environment.ts",
40 | "with": "src/environments/environment.prod.ts"
41 | }
42 | ],
43 | "optimization": true,
44 | "outputHashing": "all",
45 | "sourceMap": false,
46 | "extractCss": true,
47 | "namedChunks": false,
48 | "aot": true,
49 | "extractLicenses": true,
50 | "vendorChunk": false,
51 | "buildOptimizer": true,
52 | "budgets": [
53 | {
54 | "type": "initial",
55 | "maximumWarning": "2mb",
56 | "maximumError": "5mb"
57 | }
58 | ]
59 | }
60 | }
61 | },
62 | "serve": {
63 | "builder": "@angular-devkit/build-angular:dev-server",
64 | "options": {
65 | "browserTarget": "monorepo-example-www:build"
66 | },
67 | "configurations": {
68 | "production": {
69 | "browserTarget": "monorepo-example-www:build:production"
70 | }
71 | }
72 | },
73 | "extract-i18n": {
74 | "builder": "@angular-devkit/build-angular:extract-i18n",
75 | "options": {
76 | "browserTarget": "monorepo-example-www:build"
77 | }
78 | },
79 | "test": {
80 | "builder": "@angular-devkit/build-angular:karma",
81 | "options": {
82 | "main": "src/test.ts",
83 | "polyfills": "src/polyfills.ts",
84 | "tsConfig": "src/tsconfig.spec.json",
85 | "karmaConfig": "src/karma.conf.js",
86 | "styles": [
87 | "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
88 | "src/styles.scss"
89 | ],
90 | "scripts": [],
91 | "assets": [
92 | "src/favicon.ico",
93 | "src/assets"
94 | ]
95 | }
96 | },
97 | "lint": {
98 | "builder": "@angular-devkit/build-angular:tslint",
99 | "options": {
100 | "tsConfig": [
101 | "src/tsconfig.app.json",
102 | "src/tsconfig.spec.json"
103 | ],
104 | "exclude": [
105 | "**/node_modules/**"
106 | ]
107 | }
108 | }
109 | }
110 | },
111 | "monorepo-example-www-e2e": {
112 | "root": "e2e/",
113 | "projectType": "application",
114 | "prefix": "",
115 | "architect": {
116 | "e2e": {
117 | "builder": "@angular-devkit/build-angular:protractor",
118 | "options": {
119 | "protractorConfig": "e2e/protractor.conf.js",
120 | "devServerTarget": "monorepo-example-www:serve"
121 | },
122 | "configurations": {
123 | "production": {
124 | "devServerTarget": "monorepo-example-www:serve:production"
125 | }
126 | }
127 | },
128 | "lint": {
129 | "builder": "@angular-devkit/build-angular:tslint",
130 | "options": {
131 | "tsConfig": "e2e/tsconfig.e2e.json",
132 | "exclude": [
133 | "**/node_modules/**"
134 | ]
135 | }
136 | }
137 | }
138 | }
139 | },
140 | "defaultProject": "monorepo-example-www"
141 | }
142 |
--------------------------------------------------------------------------------