├── .husky ├── .gitignore ├── commit-msg └── pre-commit ├── projects ├── demo-integrations │ ├── cypress │ │ ├── fixtures │ │ │ └── example.json │ │ ├── support │ │ │ ├── e2e.ts │ │ │ └── command.ts │ │ ├── tests │ │ │ └── ssr │ │ │ │ └── ssr.cy.ts │ │ └── plugins │ │ │ └── index.js │ ├── tsconfig.json │ └── cypress.config.ts └── demo │ ├── src │ ├── test-setup.ts │ ├── app │ │ ├── modules │ │ │ ├── lazy │ │ │ │ ├── lazy.template.html │ │ │ │ ├── lazy.component.ts │ │ │ │ └── lazy.module.ts │ │ │ └── static │ │ │ │ ├── static.template.html │ │ │ │ ├── static.module.ts │ │ │ │ └── static.component.ts │ │ ├── app.component.ts │ │ ├── app.component.html │ │ ├── app.server.module.ts │ │ ├── app.component.spec.ts │ │ ├── app.browser.module.ts │ │ └── app.routes.ts │ ├── environments │ │ ├── environment.ts │ │ └── environment.prod.ts │ ├── polyfills.ts │ ├── favicon.ico │ ├── styles.css │ ├── main.server.ts │ ├── main.browser.ts │ └── index.html │ ├── tsconfig.json │ ├── tsconfig.spec.json │ ├── tsconfig.app.json │ ├── tsconfig.server.json │ ├── jest.config.js │ ├── .gitignore │ ├── server.ts │ └── project.json ├── .prettierignore ├── commitlint.config.js ├── jest.preset.js ├── .npmrc ├── .browserslistrc ├── angular.json ├── scripts ├── helpers │ ├── get-last-major-version.ts │ ├── check-prettier-changes.ts │ ├── get-all-versions.ts │ ├── execute.ts │ ├── colored-log.ts │ └── argv.ts ├── postchangelog.ts ├── syncVersions.js └── npm-publish.ts ├── tsconfig.json ├── CHANGELOG.md ├── stylelint.config.js ├── tsconfig.spec.json ├── .editorconfig ├── tsconfig.eslint.json ├── .github ├── workflows │ ├── build.yml │ ├── lint.yml │ ├── test.yml │ ├── deploy.yml │ └── e2e.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md └── actions │ └── nodejs │ └── action.yml ├── prettier.config.js ├── .eslintrc.js ├── codecov.yml ├── .gitignore ├── CONTRIBUTING.md ├── tsconfig.base.json ├── nx.json ├── CODE_OF_CONDUCT.md ├── README.md ├── renovate.json ├── package.json └── LICENSE /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /projects/demo-integrations/cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /projects/demo-integrations/cypress/support/e2e.ts: -------------------------------------------------------------------------------- 1 | import './command'; 2 | -------------------------------------------------------------------------------- /projects/demo/src/test-setup.ts: -------------------------------------------------------------------------------- 1 | import 'jest-preset-angular/setup-jest'; 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | **/dist/** 2 | **/coverage/** 3 | package-lock.json 4 | .husky 5 | -------------------------------------------------------------------------------- /projects/demo/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /projects/demo/src/app/modules/lazy/lazy.template.html: -------------------------------------------------------------------------------- 1 |

This is a lazy route

2 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = {extends: ['@commitlint/config-conventional']}; 2 | -------------------------------------------------------------------------------- /projects/demo/src/app/modules/static/static.template.html: -------------------------------------------------------------------------------- 1 |

This is a static route

2 | -------------------------------------------------------------------------------- /jest.preset.js: -------------------------------------------------------------------------------- 1 | const nxPreset = require('@nrwl/jest/preset'); 2 | 3 | module.exports = {...nxPreset}; 4 | -------------------------------------------------------------------------------- /projects/demo/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: false, 3 | }; 4 | -------------------------------------------------------------------------------- /projects/demo/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | }; 4 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | legacy-peer-deps=true 2 | engine-strict=false 3 | lockfileVersion=2 4 | ignore-engines=true 5 | loglevel=error 6 | -------------------------------------------------------------------------------- /projects/demo/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | import 'core-js/es6/reflect'; 2 | import 'core-js/es7/reflect'; 3 | import 'zone.js'; 4 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # shellcheck disable=SC1090 3 | . "$(dirname "$0")/_/husky.sh" 4 | 5 | npx commitlint --edit $1 6 | -------------------------------------------------------------------------------- /projects/demo/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tinkoff/angular-open-source-starter/HEAD/projects/demo/src/favicon.ico -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # shellcheck disable=SC1090 3 | . "$(dirname "$0")/_/husky.sh" 4 | 5 | npx lint-staged 6 | npm run typecheck 7 | -------------------------------------------------------------------------------- /.browserslistrc: -------------------------------------------------------------------------------- 1 | # we only want es2015 compliant browsers https://caniuse.com/#feat=es6 2 | # just use one as representative for all 3 | 4 | supports es6-module 5 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 2, 4 | "projects": { 5 | "demo": "projects/demo" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /projects/demo/src/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: -apple-system, 'BlinkMacSystemFont', 'Segoe UI', system-ui, 'Roboto', 'Helvetica Neue', sans-serif; 3 | color: #333; 4 | margin: 0; 5 | } 6 | -------------------------------------------------------------------------------- /scripts/helpers/get-last-major-version.ts: -------------------------------------------------------------------------------- 1 | export function getLastMajorVersion(versions: string[], currentMajor: number): number { 2 | return Math.max(...versions.map(x => parseInt(x)), currentMajor); 3 | } 4 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | // TODO after update all "@nrwl/*"-packages to 13.8.6+: 2 | // 1. Drop this file 3 | // 2. Rename "tsconfig.base.json" => "tsconfig.json" 4 | { 5 | "extends": "./tsconfig.base.json" 6 | } 7 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See 4 | [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 5 | -------------------------------------------------------------------------------- /projects/demo/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import {Component} from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'my-app', 5 | templateUrl: './app.component.html', 6 | }) 7 | export class AppComponent {} 8 | -------------------------------------------------------------------------------- /projects/demo/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.spec.json", 3 | "include": ["**/*.spec.ts", "**/*.d.ts"], 4 | "compilerOptions": { 5 | "esModuleInterop": true 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /projects/demo/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app" 5 | }, 6 | "files": ["./src/main.browser.ts", "./src/polyfills.ts"] 7 | } 8 | -------------------------------------------------------------------------------- /stylelint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@tinkoff/stylelint-config/less', '@tinkoff/stylelint-config/angular'], 3 | ignoreFiles: ['**/demo/**', '**/dist/**', '**/coverage/**', '**/node_modules/**'], 4 | }; 5 | -------------------------------------------------------------------------------- /scripts/helpers/check-prettier-changes.ts: -------------------------------------------------------------------------------- 1 | import {execute} from './execute'; 2 | 3 | export function checkPrettierChanges(pattern = `**/*.{json,md}`): void { 4 | execute(`prettier '${pattern}' --write`); 5 | execute(`git add .`, {stdio: `inherit`}); 6 | } 7 | -------------------------------------------------------------------------------- /projects/demo/src/app/modules/static/static.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | 3 | import {StaticComponent} from './static.component'; 4 | 5 | @NgModule({declarations: [StaticComponent], exports: [StaticComponent]}) 6 | export class StaticModule {} 7 | -------------------------------------------------------------------------------- /tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/spec", 5 | "module": "esnext", 6 | "types": ["jest"] 7 | }, 8 | "exclude": ["**/schematics/**/*", "projects/demo-integrations"] 9 | } 10 | -------------------------------------------------------------------------------- /projects/demo/src/app/modules/lazy/lazy.component.ts: -------------------------------------------------------------------------------- 1 | import {ChangeDetectionStrategy, Component} from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'lazy', 5 | templateUrl: './lazy.template.html', 6 | changeDetection: ChangeDetectionStrategy.OnPush, 7 | }) 8 | export class LazyComponent {} 9 | -------------------------------------------------------------------------------- /projects/demo/src/app/modules/static/static.component.ts: -------------------------------------------------------------------------------- 1 | import {ChangeDetectionStrategy, Component} from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'static', 5 | templateUrl: './static.template.html', 6 | changeDetection: ChangeDetectionStrategy.OnPush, 7 | }) 8 | export class StaticComponent {} 9 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 4 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /projects/demo-integrations/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "typeRoots": ["../../node_modules/@types", "../../node_modules/cypress/types"], 5 | "types": ["cypress", "node"] 6 | }, 7 | "include": ["./cypress/**/*.ts"], 8 | "exclude": [] 9 | } 10 | -------------------------------------------------------------------------------- /tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": ".", 5 | "baseUrl": ".", 6 | "strict": false, 7 | "incremental": true 8 | }, 9 | "include": ["projects", "scripts"], 10 | "exclude": ["**/node_modules", "**/schematics/**", "**/.*/", "*.js"] 11 | } 12 | -------------------------------------------------------------------------------- /projects/demo/src/main.server.ts: -------------------------------------------------------------------------------- 1 | import {enableProdMode} from '@angular/core'; 2 | 3 | import {environment} from './environments/environment'; 4 | 5 | if (environment.production) { 6 | enableProdMode(); 7 | } 8 | 9 | export {AppServerModule} from './app/app.server.module'; 10 | export {renderModule, renderModuleFactory} from '@angular/platform-server'; 11 | -------------------------------------------------------------------------------- /projects/demo/tsconfig.server.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./dist/out-tsc-server", 5 | "target": "es2016", 6 | "types": ["node"] 7 | }, 8 | "files": ["src/main.server.ts", "server.ts"], 9 | "angularCompilerOptions": { 10 | "entryModule": "./src/app/app.server.module#AppServerModule" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: [pull_request] 3 | 4 | jobs: 5 | build: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v2 9 | - name: Setup Node.js and Cache 10 | uses: ./.github/actions/nodejs 11 | 12 | - name: Build demo 13 | run: npm run build 14 | 15 | concurrency: 16 | group: build-${{ github.head_ref }} 17 | cancel-in-progress: true 18 | -------------------------------------------------------------------------------- /scripts/helpers/get-all-versions.ts: -------------------------------------------------------------------------------- 1 | import {execute} from './execute'; 2 | 3 | export function getAllVersions(name: string): string[] { 4 | try { 5 | const versions: string[] | string = JSON.parse( 6 | execute(`npm view ${name} versions --json || echo "[]"`, {}), 7 | ); 8 | 9 | return Array.isArray(versions) ? versions : [versions]; 10 | } catch { 11 | return []; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /scripts/postchangelog.ts: -------------------------------------------------------------------------------- 1 | import {checkPrettierChanges} from './helpers/check-prettier-changes'; 2 | 3 | (function main(): void { 4 | /** 5 | * We need to automatically format changes by prettier 6 | * after the CHANGELOG is generated before commit and push 7 | * 8 | * postbump lifecycle script is executed just 9 | * before information is written to CHANGELOG.md 10 | */ 11 | checkPrettierChanges(); 12 | })(); 13 | -------------------------------------------------------------------------------- /projects/demo/src/app/app.component.html: -------------------------------------------------------------------------------- 1 |
2 | Import from your library in this demo project as if you were using it as an 3 | NPM package 4 |
5 |
6 | 10 | Static component 11 | 12 | Lazy component 13 |
14 |
15 | 16 |
17 | -------------------------------------------------------------------------------- /scripts/helpers/execute.ts: -------------------------------------------------------------------------------- 1 | import {CommonExecOptions, execSync} from 'child_process'; 2 | 3 | import {infoLog} from './colored-log'; 4 | 5 | export function execute(shell: string, options?: Partial): string { 6 | infoLog(`ᐅ ${shell}`); 7 | 8 | return execSync( 9 | shell, 10 | options ?? { 11 | stdio: `inherit`, 12 | encoding: `utf8`, 13 | }, 14 | ) 15 | ?.toString() 16 | .trim(); 17 | } 18 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | const base = require('@tinkoff/prettier-config/angular'); 2 | 3 | module.exports = { 4 | ...base, 5 | singleAttributePerLine: true, 6 | overrides: [ 7 | ...base.overrides, 8 | { 9 | files: ['*.js', '*.ts'], 10 | options: {printWidth: 90, parser: 'typescript'}, 11 | }, 12 | { 13 | files: '*.html', 14 | options: {printWidth: 80, parser: 'angular'}, 15 | }, 16 | ], 17 | }; 18 | -------------------------------------------------------------------------------- /projects/demo/src/app/app.server.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | import {ServerModule} from '@angular/platform-server'; 3 | import {UNIVERSAL_PROVIDERS} from '@ng-web-apis/universal'; 4 | 5 | import {AppBrowserModule} from './app.browser.module'; 6 | import {AppComponent} from './app.component'; 7 | 8 | @NgModule({ 9 | imports: [AppBrowserModule, ServerModule], 10 | bootstrap: [AppComponent], 11 | providers: UNIVERSAL_PROVIDERS, 12 | }) 13 | export class AppServerModule {} 14 | -------------------------------------------------------------------------------- /projects/demo/src/app/modules/lazy/lazy.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | import {RouterModule, Routes} from '@angular/router'; 3 | 4 | import {LazyComponent} from './lazy.component'; 5 | 6 | export const routes: Routes = [ 7 | { 8 | path: '', 9 | component: LazyComponent, 10 | }, 11 | ]; 12 | 13 | @NgModule({ 14 | imports: [RouterModule.forChild(routes)], 15 | declarations: [LazyComponent], 16 | exports: [LazyComponent], 17 | }) 18 | export class LazyModule {} 19 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | on: [pull_request] 3 | 4 | jobs: 5 | lint: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v2 9 | - name: Setup Node.js and Cache 10 | uses: ./.github/actions/nodejs 11 | 12 | - name: Lint check 13 | run: | 14 | npm run typecheck 15 | npm run format -- --check 16 | npm run stylelint 17 | npm run lint 18 | 19 | concurrency: 20 | group: lint-${{ github.head_ref }} 21 | cancel-in-progress: true 22 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @type {import('eslint').Linter.Config} 3 | */ 4 | module.exports = { 5 | root: true, 6 | extends: [ 7 | '@tinkoff/eslint-config-angular', 8 | '@tinkoff/eslint-config-angular/html', 9 | '@tinkoff/eslint-config-angular/rxjs', 10 | ], 11 | ignorePatterns: ['projects/**/test.ts', '*.json', '*.less', '*.md', '*.js'], 12 | parser: '@typescript-eslint/parser', 13 | parserOptions: { 14 | ecmaVersion: 2020, 15 | sourceType: 'module', 16 | project: [require.resolve('./tsconfig.eslint.json')], 17 | }, 18 | }; 19 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | branch: main 3 | notify: 4 | require_ci_to_pass: no 5 | 6 | coverage: 7 | # This value is used to customize the visible color range in Codecov. 8 | # The first number represents the red, and the second represents green. 9 | # You can change the range of colors by adjusting this configuration. 10 | range: 50..100 # by default 70..100 11 | round: down 12 | precision: 2 13 | 14 | # Disable codecov/patch check 15 | status: 16 | project: 17 | default: 18 | enabled: false 19 | patch: 20 | default: 21 | enabled: false 22 | -------------------------------------------------------------------------------- /scripts/helpers/colored-log.ts: -------------------------------------------------------------------------------- 1 | export function processLog(message: string): void { 2 | console.info('\x1b[36m%s\x1b[0m', message); 3 | } 4 | 5 | export function errorLog(message: string): void { 6 | console.info('\x1B[31m%s\x1B[0m', message); 7 | } 8 | 9 | export function successLog(message: string): void { 10 | console.info('\x1B[32m%s\x1B[0m', message); 11 | } 12 | 13 | export function infoLog(message: string): void { 14 | console.info('\x1B[34m%s\x1B[0m', message); 15 | } 16 | 17 | export function titleLog(message: string): void { 18 | console.info('\x1b[35m', message); 19 | } 20 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | on: 3 | pull_request: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | tests: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - name: Setup Node.js and Cache 14 | uses: ./.github/actions/nodejs 15 | 16 | - name: Run tests 17 | run: npm run test -- --coverage 18 | 19 | - uses: codecov/codecov-action@v2 20 | with: 21 | directory: ./coverage 22 | flags: summary 23 | name: demo 24 | 25 | concurrency: 26 | group: test-${{ github.head_ref }} 27 | cancel-in-progress: true 28 | -------------------------------------------------------------------------------- /projects/demo-integrations/cypress/tests/ssr/ssr.cy.ts: -------------------------------------------------------------------------------- 1 | describe('Server side rendering', () => { 2 | const baseUrl: string = Cypress.config('baseUrl') ?? '/'; 3 | 4 | it('should serve statics and favicon.ico', () => { 5 | cy.request(`${baseUrl}/favicon.ico`).its('status').should('equal', 200); 6 | }); 7 | 8 | it('should successfully render static url', () => { 9 | cy.request(baseUrl).its('body').should('include', 'This is a static route'); 10 | }); 11 | 12 | it('should successfully render lazy url', () => { 13 | cy.request(`${baseUrl}/lazy`) 14 | .its('body') 15 | .should('include', 'This is a lazy route'); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy 2 | on: 3 | push: 4 | branches: 5 | - main 6 | 7 | jobs: 8 | deploy: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - name: Setup Node.js and Cache 13 | uses: ./.github/actions/nodejs 14 | 15 | - name: Build demo 16 | run: npx nx build-gh-pages 17 | 18 | - name: Deploy 19 | uses: JamesIves/github-pages-deploy-action@v4.2.5 20 | with: 21 | branch: gh-pages 22 | folder: dist/demo/browser 23 | silent: false 24 | clean: true 25 | 26 | concurrency: 27 | group: deploy-${{ github.head_ref }} 28 | cancel-in-progress: true 29 | -------------------------------------------------------------------------------- /projects/demo/src/main.browser.ts: -------------------------------------------------------------------------------- 1 | import './polyfills'; 2 | 3 | import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; 4 | 5 | import {AppBrowserModule} from './app/app.browser.module'; 6 | 7 | document.addEventListener('DOMContentLoaded', () => { 8 | platformBrowserDynamic() 9 | .bootstrapModule(AppBrowserModule) 10 | .then(ref => { 11 | const windowRef: any = window; 12 | 13 | // Ensure Angular destroys itself on hot reloads for Stackblitz 14 | if (windowRef['ngRef']) { 15 | windowRef['ngRef'].destroy(); 16 | } 17 | 18 | windowRef['ngRef'] = ref; 19 | }) 20 | .catch(console.error); 21 | }); 22 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🐞 Bug report 3 | about: Create a report to help us improve 4 | title: '[BUG] ' 5 | labels: '' 6 | --- 7 | 8 | # 🐞 Bug report 9 | 10 | ### Description 11 | 12 | 13 | 14 | ### Reproduction 15 | 16 | 17 | 18 | http://www.stackblitz.com/... 19 | 20 | ### Expected behavior 21 | 22 | 23 | 24 | ### Versions 25 | 26 | - OS: [e.g. iOS] 27 | - Browser [e.g. chrome, safari] 28 | - Angular [e.g. 8] 29 | 30 | ### Additional context 31 | 32 | 33 | -------------------------------------------------------------------------------- /projects/demo/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | displayName: 'demo', 3 | preset: '../../jest.preset.js', 4 | setupFilesAfterEnv: ['/src/test-setup.ts'], 5 | globals: { 6 | 'ts-jest': { 7 | tsconfig: '/tsconfig.spec.json', 8 | stringifyContentPathRegex: '\\.(html|svg)$', 9 | }, 10 | }, 11 | coverageDirectory: '../../coverage/projects/demo', 12 | transform: { 13 | '^.+\\.(ts|js|html)$': 'jest-preset-angular', 14 | }, 15 | snapshotSerializers: [ 16 | 'jest-preset-angular/build/serializers/no-ng-attributes', 17 | 'jest-preset-angular/build/serializers/ng-snapshot', 18 | 'jest-preset-angular/build/serializers/html-comment', 19 | ], 20 | }; 21 | -------------------------------------------------------------------------------- /projects/demo/.gitignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist 3 | /tmp 4 | /out-tsc 5 | # Only exists if Bazel was run 6 | /bazel-out 7 | 8 | # dependencies 9 | /node_modules 10 | 11 | # profiling files 12 | chrome-profiler-events.json 13 | speed-measure-plugin.json 14 | 15 | # IDEs and editors 16 | /.idea 17 | .project 18 | .classpath 19 | .c9/ 20 | *.launch 21 | .settings/ 22 | *.sublime-workspace 23 | 24 | # IDE - VSCode 25 | .vscode/* 26 | !.vscode/settings.json 27 | !.vscode/tasks.json 28 | !.vscode/launch.json 29 | !.vscode/extensions.json 30 | .history/* 31 | 32 | # misc 33 | /.sass-cache 34 | /connect.lock 35 | /coverage 36 | /libpeerconnection.log 37 | npm-debug.log 38 | yarn-error.log 39 | testem.log 40 | /typings 41 | 42 | # System Files 43 | .DS_Store 44 | Thumbs.db 45 | -------------------------------------------------------------------------------- /projects/demo-integrations/cypress.config.ts: -------------------------------------------------------------------------------- 1 | import {defineConfig} from 'cypress'; 2 | 3 | export const CYPRESS_CONFIG: Cypress.ConfigOptions = { 4 | video: false, 5 | fixturesFolder: 'cypress/fixtures', 6 | viewportWidth: 1440, 7 | viewportHeight: 900, 8 | responseTimeout: 60000, 9 | pageLoadTimeout: 120000, 10 | defaultCommandTimeout: 10000, 11 | e2e: { 12 | // We've imported your old cypress plugins here. 13 | // You may want to clean this up later by importing these. 14 | setupNodeEvents(on, config) { 15 | return require('./cypress/plugins/index.js')(on, config); 16 | }, 17 | baseUrl: 'http://localhost:4200', 18 | specPattern: 'cypress/tests/**/*.cy.ts', 19 | }, 20 | }; 21 | 22 | export default defineConfig(CYPRESS_CONFIG); 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist 3 | /tmp 4 | /out-tsc 5 | # Only exists if Bazel was run 6 | /bazel-out 7 | 8 | # dependencies 9 | /node_modules 10 | 11 | # profiling files 12 | chrome-profiler-events.json 13 | speed-measure-plugin.json 14 | 15 | # IDEs and editors 16 | /.idea 17 | .project 18 | .classpath 19 | .c9/ 20 | *.launch 21 | .settings/ 22 | *.sublime-workspace 23 | 24 | # IDE - VSCode 25 | .vscode/* 26 | !.vscode/settings.json 27 | !.vscode/tasks.json 28 | !.vscode/launch.json 29 | !.vscode/extensions.json 30 | .history/* 31 | 32 | # misc 33 | /.sass-cache 34 | /connect.lock 35 | /coverage 36 | /libpeerconnection.log 37 | npm-debug.log 38 | yarn-error.log 39 | testem.log 40 | /typings 41 | 42 | # System Files 43 | .DS_Store 44 | Thumbs.db 45 | 46 | # cypress 47 | **/cypress/**/screenshots/** 48 | 49 | .ssl 50 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## PR Checklist 2 | 3 | Please check if your PR fulfills the following requirements: 4 | 5 | - [ ] The commit message follows [Conventional Commits](https://www.conventionalcommits.org/en/) 6 | - [ ] Tests for the changes have been added (for bug fixes / features) 7 | - [ ] Docs have been added / updated (for bug fixes / features) 8 | 9 | ## PR Type 10 | 11 | What kind of change does this PR introduce? 12 | 13 | - [ ] Bugfix 14 | - [ ] Feature 15 | - [ ] Refactoring 16 | - [ ] Code style update 17 | - [ ] Build or CI related changes 18 | - [ ] Documentation content changes 19 | 20 | ## What is the current behavior? 21 | 22 | Closes # 23 | 24 | ## What is the new behavior? 25 | 26 | ## Does this PR introduce a breaking change? 27 | 28 | - [ ] Yes 29 | - [ ] No 30 | -------------------------------------------------------------------------------- /projects/demo/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import {APP_BASE_HREF} from '@angular/common'; 2 | import {TestBed} from '@angular/core/testing'; 3 | import {Router} from '@angular/router'; 4 | 5 | import {AppBrowserModule} from './app.browser.module'; 6 | import {AppComponent} from './app.component'; 7 | 8 | describe('Test dummy', () => { 9 | let component: AppComponent; 10 | 11 | beforeEach(() => { 12 | TestBed.configureTestingModule({ 13 | imports: [AppBrowserModule], 14 | providers: [{provide: APP_BASE_HREF, useValue: '/'}], 15 | }); 16 | 17 | component = TestBed.createComponent(AppComponent); 18 | }); 19 | 20 | it('AppComponent compiles properly', () => { 21 | TestBed.inject(Router).navigate(['/lazy']); 22 | 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🚀 Feature request 3 | about: Suggest an idea for this project 4 | title: '[FEATURE]' 5 | labels: '' 6 | --- 7 | 8 | # 🚀 Feature request 9 | 10 | ### Is your feature request related to a problem? 11 | 12 | 13 | I'm always frustrated when... 14 | 15 | ### Describe the solution you'd like 16 | 17 | 18 | 19 | 20 | ### Describe alternatives you've considered 21 | 22 | 23 | 24 | 25 | ### Additional context 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /projects/demo/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 13 | 17 | 21 | 25 | 26 | Angular open source starter 27 | 28 | 29 | Loading... 30 | 31 | 32 | -------------------------------------------------------------------------------- /projects/demo-integrations/cypress/plugins/index.js: -------------------------------------------------------------------------------- 1 | const {CYPRESS_CONFIG} = require('./../../cypress.config'); 2 | 3 | module.exports = on => { 4 | on('before:browser:launch', (browser, launchOptions) => { 5 | if (browser.name === 'chrome') { 6 | launchOptions.args.push( 7 | `--window-size=${CYPRESS_CONFIG.viewportWidth},${CYPRESS_CONFIG.viewportHeight}`, 8 | ); 9 | launchOptions.args.push('--force-device-scale-factor=2'); 10 | launchOptions.args.push('--high-dpi-support=1'); 11 | launchOptions.args.push('--disable-dev-shm-usage'); 12 | launchOptions.args.push('--incognito'); 13 | } 14 | 15 | if (browser.isHeadless) { 16 | launchOptions.args.push('--disable-gpu'); 17 | } 18 | 19 | return launchOptions; 20 | }); 21 | }; 22 | -------------------------------------------------------------------------------- /projects/demo/src/app/app.browser.module.ts: -------------------------------------------------------------------------------- 1 | import {LocationStrategy, PathLocationStrategy} from '@angular/common'; 2 | import {NgModule} from '@angular/core'; 3 | import {BrowserModule} from '@angular/platform-browser'; 4 | 5 | import {AppComponent} from './app.component'; 6 | import {AppRoutingModule} from './app.routes'; 7 | import {StaticModule} from './modules/static/static.module'; 8 | 9 | @NgModule({ 10 | bootstrap: [AppComponent], 11 | imports: [ 12 | BrowserModule.withServerTransition({ 13 | appId: 'demo', 14 | }), 15 | AppRoutingModule, 16 | StaticModule, 17 | ], 18 | declarations: [AppComponent], 19 | providers: [ 20 | { 21 | provide: LocationStrategy, 22 | useClass: PathLocationStrategy, 23 | }, 24 | ], 25 | }) 26 | export class AppBrowserModule {} 27 | -------------------------------------------------------------------------------- /scripts/syncVersions.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const glob = require('glob'); 3 | const JSON_INDENTATION_LEVEL = 4; 4 | const {version} = require('../package.json'); 5 | 6 | // Sync libraries package.json versions with main package.json 7 | syncVersions('projects'); 8 | 9 | function syncVersions(root) { 10 | glob(root + '/**/package.json', (_, files) => { 11 | files.forEach(file => { 12 | const packageJson = JSON.parse(fs.readFileSync(file)); 13 | 14 | fs.writeFileSync( 15 | file, 16 | JSON.stringify( 17 | { 18 | ...packageJson, 19 | version, 20 | }, 21 | null, 22 | JSON_INDENTATION_LEVEL, 23 | ), 24 | ); 25 | }); 26 | }); 27 | } 28 | -------------------------------------------------------------------------------- /projects/demo/src/app/app.routes.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | import {RouterModule, Routes} from '@angular/router'; 3 | 4 | import {StaticComponent} from './modules/static/static.component'; 5 | 6 | export const appRoutes: Routes = [ 7 | { 8 | path: '', 9 | component: StaticComponent, 10 | }, 11 | { 12 | path: 'lazy', 13 | loadChildren: () => import(`./modules/lazy/lazy.module`).then(m => m.LazyModule), 14 | }, 15 | { 16 | path: '**', 17 | redirectTo: '', 18 | }, 19 | ]; 20 | 21 | @NgModule({ 22 | imports: [ 23 | RouterModule.forRoot(appRoutes, { 24 | initialNavigation: 'enabledBlocking', 25 | relativeLinkResolution: 'corrected', 26 | scrollPositionRestoration: 'enabled', 27 | }), 28 | ], 29 | exports: [RouterModule], 30 | }) 31 | export class AppRoutingModule {} 32 | -------------------------------------------------------------------------------- /projects/demo-integrations/cypress/support/command.ts: -------------------------------------------------------------------------------- 1 | // *********************************************** 2 | // This example commands.js shows you how to 3 | // create various custom commands and overwrite 4 | // existing commands. 5 | // 6 | // For more comprehensive examples of custom 7 | // commands please read more here: 8 | // https://on.cypress.io/custom-commands 9 | // *********************************************** 10 | // 11 | // 12 | // -- This is a parent command -- 13 | // Cypress.Commands.add('login', (email, password) => { ... }) 14 | // 15 | // 16 | // -- This is a child command -- 17 | // Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) 18 | // 19 | // 20 | // -- This is a dual command -- 21 | // Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) 22 | // 23 | // 24 | // -- This will overwrite an existing command -- 25 | // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) 26 | -------------------------------------------------------------------------------- /scripts/helpers/argv.ts: -------------------------------------------------------------------------------- 1 | import * as process from 'process'; 2 | 3 | import {processLog} from './colored-log'; 4 | 5 | export function getValueByFlag(flag: string, fallback: T): T { 6 | const index = findIndexFlag(flag); 7 | 8 | if (index === -1) { 9 | return fallback; 10 | } 11 | 12 | const [parsedFlag, parsedValue] = process.argv[index].split(`=`) ?? []; 13 | const value = 14 | stringifier(parsedValue) ?? 15 | (process.argv[index + 1].startsWith(`-`) 16 | ? fallback 17 | : stringifier(process.argv[index + 1]) ?? fallback); 18 | 19 | processLog(`parsed flags: \n${[parsedFlag, value].join(`=`)}`); 20 | 21 | return value as T; 22 | } 23 | 24 | export function hasFlag(flag: string): boolean { 25 | return findIndexFlag(flag) !== -1; 26 | } 27 | 28 | export function findIndexFlag(flag: string): number { 29 | return process.argv.findIndex(arg => arg === flag || arg.split(`=`)[0] === flag); 30 | } 31 | 32 | export function stringifier(value?: string): string | undefined { 33 | return value === `undefined` || value === `null` ? undefined : value; 34 | } 35 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | > Thank you for considering contributing to our project. Your help if very welcome! 4 | 5 | When contributing, it's better to first discuss the change you wish to make via issue, email, or any other method with 6 | the owners of this repository before making a change. 7 | 8 | All members of our community are expected to follow our [Code of Conduct](CODE_OF_CONDUCT.md). Please make sure you are 9 | welcoming and friendly in all of our spaces. 10 | 11 | ## Getting started 12 | 13 | In order to make your contribution please make a fork of the repository. After you've pulled the code, follow these 14 | steps to kick-start the development: 15 | 16 | 1. Run `npm ci` to install dependencies 17 | 2. Run `npm start` to launch demo project where you could test your changes 18 | 3. Use following commands to ensure code quality 19 | 20 | ``` 21 | npm run lint 22 | npm run build 23 | npm run test 24 | ``` 25 | 26 | ## Pull Request Process 27 | 28 | 1. We follow [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0-beta.4/) in our commit messages, i.e. 29 | `feat(core): improve typing` 30 | 2. Update [README.md](README.md) to reflect changes related to public API and everything relevant 31 | 3. Make sure you cover all code changes with unit tests 32 | 4. When you are ready, create Pull Request of your fork into original repository 33 | -------------------------------------------------------------------------------- /tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "angularCompilerOptions": { 4 | "enableIvy": true, 5 | "preserveWhitespaces": false, 6 | "annotateForClosureCompiler": false, 7 | "skipTemplateCodegen": true, 8 | "strictMetadataEmit": true, 9 | "fullTemplateTypeCheck": true, 10 | "strictInjectionParameters": true, 11 | "enableResourceInlining": true, 12 | "strictLiteralTypes": true, 13 | "strictInputTypes": true 14 | }, 15 | "compilerOptions": { 16 | "baseUrl": "./", 17 | "outDir": "./dist/out-tsc", 18 | "sourceMap": true, 19 | "declaration": false, 20 | "module": "es2020", 21 | "moduleResolution": "node", 22 | "experimentalDecorators": true, 23 | "importHelpers": true, 24 | "strict": true, 25 | "noFallthroughCasesInSwitch": true, 26 | "noImplicitReturns": true, 27 | "noUnusedParameters": true, 28 | "noUnusedLocals": true, 29 | "target": "es2015", 30 | "typeRoots": ["node_modules/@types"], 31 | "lib": ["es2018", "dom"], 32 | "paths": {} 33 | }, 34 | "ts-node": { 35 | "files": true, 36 | "transpileOnly": true, 37 | "compilerOptions": { 38 | "types": ["node"], 39 | "module": "commonjs" 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /.github/workflows/e2e.yml: -------------------------------------------------------------------------------- 1 | name: E2E 2 | on: [pull_request] 3 | 4 | jobs: 5 | e2e: 6 | runs-on: ubuntu-latest 7 | env: 8 | CYPRESS_CACHE_FOLDER: ./node_modules/cache-cypress 9 | UNIVERSAL_SERVER: http://localhost:4000 10 | STATIC_SERVER: http://localhost:8080 11 | steps: 12 | - uses: actions/checkout@v2 13 | - name: Setup Node.js and Cache 14 | uses: ./.github/actions/nodejs 15 | 16 | - name: Build demo 17 | run: npm run build 18 | 19 | - name: Prerender demo 20 | run: npm run build:prerender 21 | 22 | - name: Serving static files 23 | run: | 24 | npm run serve:prerender & sleep 5 25 | curl -X GET -I -f "${{ env.STATIC_SERVER }}" 26 | 27 | - name: Run cypress / ssr / prerender 28 | run: npm run cy:run -- --spec "**/ssr/*.cy.ts" --config baseUrl="${{ env.STATIC_SERVER }}" 29 | 30 | - name: Build ssr server 31 | run: npm run build:ssr 32 | 33 | - name: Serving ssr server 34 | run: | 35 | npm run serve:ssr & sleep 5 36 | curl -X GET -I -f "${{ env.UNIVERSAL_SERVER }}" 37 | 38 | - name: Run cypress / ssr / universal 39 | run: npm run cy:run -- --spec "**/ssr/*.cy.ts" --config baseUrl="${{ env.UNIVERSAL_SERVER }}" 40 | 41 | concurrency: 42 | group: integration-e2e-${{ github.head_ref }} 43 | cancel-in-progress: true 44 | -------------------------------------------------------------------------------- /nx.json: -------------------------------------------------------------------------------- 1 | { 2 | "implicitDependencies": { 3 | "angular.json": "*", 4 | "package.json": { 5 | "dependencies": "*", 6 | "devDependencies": "*" 7 | }, 8 | ".eslintrc.js": "*", 9 | "nx.json": "*", 10 | "karma.*.js": "*", 11 | "tsconfig.*.json": "*", 12 | "tsconfig.json": "*" 13 | }, 14 | "npmScope": "angular-open-source-starter", 15 | "tasksRunnerOptions": { 16 | "default": { 17 | "runner": "@nrwl/nx-cloud", 18 | "options": { 19 | "cacheableOperations": ["build", "test", "lint"], 20 | "parallel": 1 21 | } 22 | } 23 | }, 24 | "affected": { 25 | "defaultBase": "origin/main" 26 | }, 27 | "workspaceLayout": { 28 | "libsDir": "projects", 29 | "appsDir": "projects" 30 | }, 31 | "defaultProject": "demo", 32 | "generators": { 33 | "@nrwl/js:library": { 34 | "buildable": true, 35 | "strict": true, 36 | "linter": "none", 37 | "unitTestRunner": "jest", 38 | "config": "project" 39 | }, 40 | "@nrwl/angular:library": { 41 | "buildable": true, 42 | "compilationMode": "partial", 43 | "linter": "none", 44 | "unitTestRunner": "jest", 45 | "strict": true, 46 | "skipModule": true, 47 | "standaloneConfig": true 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /scripts/npm-publish.ts: -------------------------------------------------------------------------------- 1 | import {execSync} from 'child_process'; 2 | import {resolve} from 'path'; 3 | 4 | import {getValueByFlag} from './helpers/argv'; 5 | import {errorLog, infoLog, processLog, successLog} from './helpers/colored-log'; 6 | import {getAllVersions} from './helpers/get-all-versions'; 7 | import {getLastMajorVersion} from './helpers/get-last-major-version'; 8 | 9 | const isDryRun = 10 | getValueByFlag<'true' | 'false' | 'undefined'>(`--dry-run`, `false`) === `true`; 11 | const path = getValueByFlag(`--path`, ``); 12 | 13 | (async function main(): Promise { 14 | const packageJson = await import(resolve(path, `package.json`)); 15 | const versions: string[] = getAllVersions(packageJson.name); 16 | 17 | if (versions.includes(packageJson.version) && !isDryRun) { 18 | errorLog(`${packageJson.name}@${packageJson.version} is already published`); 19 | 20 | return; 21 | } 22 | 23 | infoLog(`name: ${packageJson.name}`); 24 | infoLog(`version: ${packageJson.version}`); 25 | 26 | const dry = isDryRun ? `--dry-run` : ``; 27 | const tag = makeTag(packageJson.version, versions); 28 | const command = `npm publish ${path} ${tag} ${dry} --access public`; 29 | 30 | processLog(command); 31 | execSync(command, {stdio: `inherit`, encoding: `utf8`}); 32 | successLog(`+${packageJson.name}@${packageJson.version} is published successfully`); 33 | })(); 34 | 35 | function makeTag(version: string, versions: string[]): string { 36 | const currentMajor = parseInt(version); 37 | const maxMajorVersion = getLastMajorVersion(versions, currentMajor); 38 | const tagFlag = maxMajorVersion > currentMajor ? `--tag v${currentMajor}-lts` : ``; 39 | 40 | return version.includes(`rc`) ? `--tag next` : tagFlag; 41 | } 42 | -------------------------------------------------------------------------------- /.github/actions/nodejs/action.yml: -------------------------------------------------------------------------------- 1 | name: Action for Node.js 2 | description: Node.js setup cache 3 | 4 | inputs: 5 | node-version: 6 | description: Node.js version 7 | required: false 8 | default: 16.x 9 | 10 | runs: 11 | using: composite 12 | steps: 13 | - name: Use Node.js ${{ inputs.node-version }} 14 | uses: actions/setup-node@v3 15 | with: 16 | node-version: ${{ inputs.node-version }} 17 | registry-url: 'https://registry.npmjs.org' 18 | 19 | - name: Restore node_modules from cache 20 | id: cache-node-modules 21 | uses: actions/cache@v3 22 | with: 23 | path: | 24 | **/node_modules 25 | !**/node_modules/.cache 26 | key: modules-cache__nodejs-${{ inputs.node-version }}__${{ hashfiles('**/package-lock.json') }} 27 | 28 | - name: Restore from cache of builds 29 | id: build-cache 30 | if: steps.cache-node-modules.outputs.cache-hit == 'true' 31 | uses: actions/cache@v3 32 | with: 33 | path: | 34 | **/node_modules/.cache 35 | key: builds-cache-hash__${{ hashFiles('**/package-lock.json') }}-${{ github.ref }} 36 | restore-keys: builds-cache__nodejs-${{ inputs.node-version }}__${{ hashFiles('**/package-lock.json') }} 37 | 38 | - name: Restore from global NPM cache 39 | if: steps.cache-node-modules.outputs.cache-hit != 'true' 40 | uses: actions/cache@v3 41 | with: 42 | path: ~/.npm 43 | key: npm-cache__nodejs-${{ inputs.node-version }}__${{ hashFiles('**/package-lock.json') }} 44 | restore-keys: npm-cache-hash__ 45 | 46 | - run: npm ci 47 | env: 48 | CYPRESS_CACHE_FOLDER: ./node_modules/cache-cypress 49 | if: steps.cache-node-modules.outputs.cache-hit != 'true' 50 | shell: bash 51 | 52 | - name: environment 53 | shell: bash 54 | run: | 55 | node -v 56 | npm -v 57 | -------------------------------------------------------------------------------- /projects/demo/server.ts: -------------------------------------------------------------------------------- 1 | import '@ng-web-apis/universal/mocks'; 2 | import 'zone.js/node'; 3 | 4 | import {APP_BASE_HREF} from '@angular/common'; 5 | import {provideLocation, provideUserAgent} from '@ng-web-apis/universal'; 6 | import {ngExpressEngine} from '@nguniversal/express-engine'; 7 | import * as express from 'express'; 8 | import {Express} from 'express'; 9 | import {existsSync} from 'fs'; 10 | import {join} from 'path'; 11 | 12 | import {AppServerModule} from './src/main.server'; 13 | 14 | // The Express app is exported so that it can be used by serverless Functions. 15 | export function app(): Express { 16 | const server = express(); 17 | const distFolder = join(process.cwd(), 'dist/demo/browser'); 18 | const indexHtml = existsSync(join(distFolder, 'index.original.html')) 19 | ? 'index.original.html' 20 | : 'index'; 21 | 22 | // Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine) 23 | server.engine( 24 | 'html', 25 | ngExpressEngine({ 26 | bootstrap: AppServerModule, 27 | }) as any, 28 | ); 29 | 30 | server.set('view engine', 'html'); 31 | server.set('views', distFolder); 32 | 33 | // Example Express Rest API endpoints 34 | // server.get('/api/**', (req, res) => { }); 35 | // Serve static files from /browser 36 | server.get( 37 | '*.*', 38 | express.static(distFolder, { 39 | maxAge: '1y', 40 | }), 41 | ); 42 | 43 | // All regular routes use the Universal engine 44 | server.get('*', (req, res) => { 45 | res.render(indexHtml, { 46 | req, 47 | providers: [ 48 | {provide: APP_BASE_HREF, useValue: req.baseUrl}, 49 | provideLocation(req), 50 | provideUserAgent(req), 51 | ], 52 | }); 53 | }); 54 | 55 | return server; 56 | } 57 | 58 | function run() { 59 | const port = process.env.PORT || 4000; 60 | 61 | // Start up the Node server 62 | const server = app(); 63 | 64 | server.listen(port, () => { 65 | console.info(`Node Express server listening on http://localhost:${port}`); 66 | }); 67 | } 68 | 69 | // Webpack will replace 'require' with '__webpack_require__' 70 | // '__non_webpack_require__' is a proxy to Node 'require' 71 | // The below code is to ensure that the server is run only when not requiring the bundle. 72 | declare const __non_webpack_require__: NodeRequire; 73 | const mainModule = __non_webpack_require__.main; 74 | const moduleFilename = (mainModule && mainModule.filename) || ''; 75 | 76 | if (moduleFilename === __filename || moduleFilename.includes('iisnode')) { 77 | run(); 78 | } 79 | 80 | export * from './src/main.server'; 81 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making 6 | participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, 7 | disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, 8 | socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. 9 | 10 | ## Our Standards 11 | 12 | Examples of behavior that contributes to creating a positive environment include: 13 | 14 | - Using welcoming and inclusive language 15 | - Being respectful of differing viewpoints and experiences 16 | - Gracefully accepting constructive criticism 17 | - Focusing on what is best for the community 18 | - Showing empathy towards other community members 19 | 20 | Examples of unacceptable behavior by participants include: 21 | 22 | - The use of sexualized language or imagery and unwelcome sexual attention or advances 23 | - Trolling, insulting/derogatory comments, and personal or political attacks 24 | - Public or private harassment 25 | - Publishing others' private information, such as a physical or electronic address, without explicit permission 26 | - Other conduct which could reasonably be considered inappropriate in a professional setting 27 | 28 | ## Our Responsibilities 29 | 30 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take 31 | appropriate and fair corrective action in response to any instances of unacceptable behavior. 32 | 33 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, 34 | issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any 35 | contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 36 | 37 | ## Scope 38 | 39 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the 40 | project or its community. Examples of representing a project or community include using an official project e-mail 41 | address, posting via an official social media account, or acting as an appointed representative at an online or offline 42 | event. Representation of a project may be further defined and clarified by project maintainers. 43 | 44 | ## Enforcement 45 | 46 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at 47 | [INSERT YOUR EMAIL HERE]. All complaints will be reviewed and investigated and will result in a response that is deemed 48 | necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to 49 | the reporter of an incident. Further details of specific enforcement policies may be posted separately. 50 | 51 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent 52 | repercussions as determined by other members of the project's leadership. 53 | 54 | ## Attribution 55 | 56 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at 57 | https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 58 | 59 | [homepage]: https://www.contributor-covenant.org 60 | 61 | For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Angular Open-source Starter 2 | 3 | [![Deploy](https://github.com/Tinkoff/angular-open-source-starter/actions/workflows/deploy.yml/badge.svg)](https://github.com/Tinkoff/angular-open-source-starter/actions/workflows/deploy.yml) 4 | [![Unit tests](https://github.com/Tinkoff/angular-open-source-starter/actions/workflows/test.yml/badge.svg)](https://github.com/Tinkoff/angular-open-source-starter/actions/workflows/test.yml) 5 | [![codecov](https://codecov.io/gh/Tinkoff/angular-open-source-starter/branch/main/graph/badge.svg?token=KfV90T6KkK)](https://codecov.io/gh/Tinkoff/angular-open-source-starter) 6 | [![Demo](https://img.shields.io/badge/demo-website-green)](https://tinkoff.github.io/angular-open-source-starter/) 7 | 8 | ## How to use 9 | 10 | > This is a starter project for creating open-source libraries for Angular. It is a full-fledged Angular workspace with 11 | > demo application and easy library addition. It is designed to be used for open-sourcing libraries on GitHub and has 12 | > everything you'd need ready for CI, code coverage, SSR testing, StackBlitz demo deployment and more. 13 | 14 | 1. Run `npm ci` to install everything 15 | 16 | 2. Use search to replace all mentions of `angular-open-source-starter` with your new library name (`npmScope` inside 17 | `nx.json`, matadata of the root `package.json`, root `README.md` etc.) 18 | 19 | 3. Create basic file structure via [Nx-generators](https://nx.dev/plugin-features/use-code-generators): 20 | 21 | - `nx g @nrwl/js:library [your-library-name]` - create Typescript library. See 22 | [available options](https://nx.dev/packages/js/generators/library#options). 23 | - `nx g @nrwl/angular:library [your-library-name]` - create Angular library. See 24 | [available options](https://nx.dev/packages/angular/generators/library#options). 25 | 26 | 4. Fill in `projects/[your-library-name]/package.json` metadata for your newly generated library 27 | 28 | 5. Add your email at [INSERT YOUR EMAIL HERE] in `CODE_OF_CONDUCT.md` 29 | 30 | 6. Update `LICENSE` file according to your preferences 31 | 32 | 7. Update root `package.json` metadata to represent your project 33 | 34 | 8. Code your library and create demo for it 35 | 36 | ## Cool features 37 | 38 | - [x] Versioning is ready for you with following simple commands: 39 | 40 | ```json 41 | { 42 | "release": "standard-version", 43 | "release:patch": "npm run release -- --release-as patch", 44 | "release:minor": "npm run release -- --release-as minor", 45 | "release:major": "npm run release -- --release-as major", 46 | "publish": "nx run-many --target build --all --exclude=demo && nx run-many --target publish --all" 47 | } 48 | ``` 49 | 50 | Just use [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0-beta.4/) format and `CHANGELOG.md` will be 51 | automatically generated on releases by 52 | [Standard Version](https://github.com/conventional-changelog/standard-version#standard-version). 53 | 54 | - [x] This project has Angular Universal — you can easily test your library with SSR and Prerender: 55 | 56 | `npm run serve:dev:ssr` or `npm run build:prerender && npm run serve:prerender` 57 | 58 | - [x] CI and code coverage are ready, GitHub Action checks that packages build, test and lint correctly. It also sends 59 | test results to [Codecov](https://about.codecov.io). 60 | 61 | - [x] Precommit checks, prettier, linter and all that jazz is there. 62 | 63 | - [x] You can also deploy your demo to [StackBlitz](https://stackblitz.com) with no hustle, just use link in the 64 | following format: 65 | 66 | https://stackblitz.com/github/[User|Organization]/[Repository]/tree/main/projects/demo 67 | 68 | - [x] You can add more libraries using the same [Nx-generators](https://nx.dev/plugin-features/use-code-generators) to 69 | create a whole Angular Workspace with multiple libraries. Versioning and publishing is configured that they are 70 | released simultaneously like Angular packages. 71 | 72 | ## Infrastructure 73 | 74 | - [x] GitHub Actions 75 | - [x] Node.js 16+, npm 8+ 76 | - [x] Angular 12+ 77 | - [x] TypeScript 4+ 78 | - [x] Prettier 2.6+ 79 | - [x] Stylelint 14+ 80 | - [x] ESLint 7+ 81 | - [x] Husky 7+ 82 | - [x] Cypress 10+ 83 | - [x] Jest 27+ 84 | - [x] Nx 13+ 85 | 86 | ## Badge 87 | 88 | Show that your project is based off of our starter 89 | 90 | [![angular-open-source-starter](https://img.shields.io/badge/made%20with-angular--open--source--starter-d81676?logo=angular)](https://github.com/Tinkoff/angular-open-source-starter) 91 | 92 | ```md 93 | [![angular-open-source-starter](https://img.shields.io/badge/made%20with-angular--open--source--starter-d81676?logo=angular)](https://github.com/Tinkoff/angular-open-source-starter) 94 | ``` 95 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "enabled": true, 4 | "automerge": true, 5 | "pinVersions": true, 6 | "rangeStrategy": "bump", 7 | "branchPrefix": "renovate/", 8 | "timezone": "Europe/Moscow", 9 | "semanticCommits": "enabled", 10 | "masterIssueApproval": false, 11 | "separateMajorMinor": true, 12 | "separateMinorPatch": false, 13 | "dependencyDashboard": false, 14 | "dependencyDashboardApproval": false, 15 | "semanticPrefix": "build", 16 | "rebaseWhen": "auto", 17 | "labels": ["renovate"], 18 | "baseBranches": ["master"], 19 | "digest": {"enabled": false}, 20 | "pinDigests": false, 21 | "includePaths": ["package.json", "**/package.json", "!node_modules/**"], 22 | "packageRules": [ 23 | { 24 | "matchUpdateTypes": ["minor", "patch"], 25 | "matchPackagePatterns": ["*"], 26 | "groupName": "all dependencies (minor/patch)", 27 | "groupSlug": "all-minor-patch" 28 | }, 29 | { 30 | "matchUpdateTypes": ["major"], 31 | "labels": ["renovate", "major"], 32 | "automerge": false 33 | }, 34 | { 35 | "matchUpdateTypes": ["minor", "patch"], 36 | "labels": ["renovate", "minor/patch"], 37 | "automerge": true 38 | }, 39 | { 40 | "depTypeList": ["engines", "peerDependencies"], 41 | "enabled": false 42 | }, 43 | { 44 | "matchPackagePatterns": ["^rxjs.*"], 45 | "groupSlug": "rxjs", 46 | "groupName": "rxjs dependencies" 47 | }, 48 | { 49 | "matchPackagePatterns": ["^prettier"], 50 | "groupSlug": "prettier", 51 | "groupName": "prettier dependencies" 52 | }, 53 | { 54 | "matchPackagePatterns": [ 55 | "^@angular/.*", 56 | "^@angular-devkit/.*", 57 | "^@schematics/.*", 58 | "^@angular/cdk$", 59 | "^@angular/cli$", 60 | "^@angular/cdk-experimental$", 61 | "^@angular/flex-layout$", 62 | "^@angular/material$", 63 | "^@angular-builders/.*", 64 | "^ng-packagr$", 65 | "^zone.js" 66 | ], 67 | "groupSlug": "angular", 68 | "groupName": "angular dependencies" 69 | }, 70 | { 71 | "matchPackagePatterns": ["^class-transformer$"], 72 | "groupSlug": "class-transformer", 73 | "groupName": "class-transformer dependencies" 74 | }, 75 | { 76 | "matchPackagePatterns": ["^@nrwl/.*"], 77 | "groupSlug": "nrwl", 78 | "groupName": "nrwl dependencies" 79 | }, 80 | { 81 | "matchPackagePatterns": ["^@commitlint/.*"], 82 | "groupSlug": "commitlint", 83 | "groupName": "commitlint dependencies" 84 | }, 85 | { 86 | "matchPackagePatterns": ["^@sentry/.*"], 87 | "groupSlug": "sentry", 88 | "groupName": "sentry dependencies" 89 | }, 90 | { 91 | "matchPackagePatterns": ["^@ngx-translate/.*"], 92 | "groupSlug": "ngx-translate", 93 | "groupName": "ngx-translate dependencies" 94 | }, 95 | { 96 | "matchPackagePatterns": [ 97 | "^@types/eslint$", 98 | "^@angular-eslint/.*", 99 | "^@angular-ru/eslint.*", 100 | "^@typescript-eslint/.*", 101 | "^eslint.*" 102 | ], 103 | "groupSlug": "eslint", 104 | "groupName": "eslint dependencies" 105 | }, 106 | { 107 | "matchPackagePatterns": ["^@types/jest$", "^jest.*", "^ts-jest$", "^jest-preset-angular$"], 108 | "groupSlug": "jest", 109 | "groupName": "jest dependencies" 110 | }, 111 | { 112 | "matchPackagePatterns": ["@types/karma.*", "^karma.*"], 113 | "groupSlug": "karma", 114 | "groupName": "karma dependencies" 115 | }, 116 | { 117 | "matchPackagePatterns": ["@types/jasmine", "@types/jasmine*", "^jasmine.*"], 118 | "groupSlug": "jasmine", 119 | "groupName": "jasmine dependencies" 120 | }, 121 | { 122 | "matchPackagePatterns": ["@types/node"], 123 | "groupSlug": "nodejs", 124 | "groupName": "nodejs dependencies" 125 | }, 126 | { 127 | "matchPackagePatterns": ["^typescript", "^tslib"], 128 | "groupSlug": "typescript", 129 | "groupName": "typescript dependencies" 130 | } 131 | ] 132 | } 133 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@tinkoff/angular-open-source-starter", 3 | "version": "0.0.0", 4 | "description": "This is a template project to develop, create and open-source Angular libraries with demos", 5 | "keywords": [ 6 | "angular", 7 | "ng", 8 | "workspace", 9 | "library", 10 | "starter", 11 | "tinkoff" 12 | ], 13 | "homepage": "https://github.com/tinkoff/angular-open-source-starter#README", 14 | "bugs": "https://github.com/tinkoff/angular-open-source-starter/issues", 15 | "repository": "https://github.com/tinkoff/angular-open-source-starter", 16 | "license": "Apache-2.0", 17 | "author": { 18 | "name": "Roman Sedov", 19 | "email": "79601794011@ya.ru", 20 | "url": "http://marsibarsi.me/" 21 | }, 22 | "contributors": [ 23 | "Alex Inkin ", 24 | "Maksim Ivanov " 25 | ], 26 | "scripts": { 27 | "postinstall": "husky install && ngcc --async", 28 | "*** Workflow ***": "", 29 | "start": "nx run demo:serve", 30 | "build": "nx run demo:build:production", 31 | "test": "nx test", 32 | "*** Format ***": "", 33 | "lint": "eslint .", 34 | "format": "prettier '**/*.{svg,yml,js,ts,html,md,less,json}'", 35 | "stylelint": "stylelint '**/*.{less,css}'", 36 | "typecheck": "tsc --noEmit --skipLibCheck --incremental false --tsBuildInfoFile null --project tsconfig.spec.json", 37 | "*** SSR ***": "", 38 | "serve:dev:ssr": "nx run demo:serve-ssr", 39 | "serve:ssr": "node dist/demo/server/main.js", 40 | "serve:prerender": "http-server dist/demo/browser", 41 | "build:ssr": "nx run demo:server:production", 42 | "build:prerender": "nx run demo:prerender:production", 43 | "*** Cypress ***": "", 44 | "cy:open": "cypress open --project ./projects/demo-integrations/", 45 | "cy:run": "cypress run --project ./projects/demo-integrations/", 46 | "*** Release ***": "", 47 | "release": "standard-version -a --no-verify", 48 | "release:patch": "npm run release -- --release-as patch", 49 | "release:minor": "npm run release -- --release-as minor", 50 | "release:major": "npm run release -- --release-as major", 51 | "publish": "nx run-many --target build --all --exclude=demo && nx run-many --target publish --all" 52 | }, 53 | "lint-staged": { 54 | "*.{js,ts,html,md,less,json}": [ 55 | "npm run lint -- --fix", 56 | "prettier --write", 57 | "git add" 58 | ], 59 | "*.less": [ 60 | "stylelint --fix", 61 | "git add" 62 | ] 63 | }, 64 | "dependencies": { 65 | "@angular/animations": "^12.2.16", 66 | "@angular/common": "^12.2.16", 67 | "@angular/compiler": "^12.2.16", 68 | "@angular/core": "^12.2.16", 69 | "@angular/forms": "^12.2.16", 70 | "@angular/platform-browser": "^12.2.16", 71 | "@angular/platform-browser-dynamic": "^12.2.16", 72 | "@angular/platform-server": "^12.2.16", 73 | "@angular/router": "^12.2.16", 74 | "@ng-web-apis/common": "^2.0.0", 75 | "@ng-web-apis/universal": "^2.0.0", 76 | "@nguniversal/express-engine": "^12.1.3", 77 | "core-js": "^2.6.12", 78 | "express": "^4.17.3", 79 | "rxjs": "~6.6.7", 80 | "tslib": "^2.0.0", 81 | "zone.js": "~0.11.5" 82 | }, 83 | "devDependencies": { 84 | "@angular-devkit/build-angular": "~12.2.17", 85 | "@angular-devkit/core": "^12.2.17", 86 | "@angular-devkit/schematics": "^12.2.17", 87 | "@angular-devkit/schematics-cli": "~0.901.13", 88 | "@angular/cli": "^12.2.17", 89 | "@angular/compiler-cli": "^12.2.16", 90 | "@angular/language-service": "^12.2.16", 91 | "@commitlint/cli": "^16.2.3", 92 | "@commitlint/config-conventional": "^16.2.1", 93 | "@nguniversal/builders": "^12.1.3", 94 | "@nrwl/angular": "13.1.4", 95 | "@nrwl/cli": "13.1.4", 96 | "@nrwl/eslint-plugin-nx": "13.1.4", 97 | "@nrwl/jest": "13.1.4", 98 | "@nrwl/js": "13.3.0", 99 | "@nrwl/node": "13.1.4", 100 | "@nrwl/nx-cloud": "13.1.4", 101 | "@nrwl/tao": "13.1.4", 102 | "@nrwl/workspace": "13.1.4", 103 | "@tinkoff/eslint-config": "^1.36.1", 104 | "@tinkoff/eslint-config-angular": "^1.36.1", 105 | "@tinkoff/prettier-config": "^1.32.1", 106 | "@tinkoff/stylelint-config": "^1.30.0", 107 | "@types/estree": "^0.0.51", 108 | "@types/express": "^4.17.13", 109 | "@types/jest": "^27.4.1", 110 | "@types/node": "17.0.23", 111 | "cypress": "^10.11.0", 112 | "http-server": "^14.1.0", 113 | "husky": "^7.0.4", 114 | "jest": "^27.5.1", 115 | "jest-preset-angular": "^11.1.1", 116 | "lint-staged": "^12.3.7", 117 | "ng-packagr": "^12.2.7", 118 | "prettier": "^2.6.2", 119 | "standard-version": "^9.3.2", 120 | "ts-jest": "27.0.5", 121 | "ts-node": "^10.7.0", 122 | "tsutils": "^3.21.0", 123 | "typescript": "~4.3.5" 124 | }, 125 | "engines": { 126 | "node": ">= 14", 127 | "npm": ">= 7", 128 | "yarn": "Please use npm instead of yarn to install dependencies" 129 | }, 130 | "standard-version": { 131 | "scripts": { 132 | "postchangelog": "ts-node ./scripts/postchangelog.ts", 133 | "postbump": "node scripts/syncVersions.js && git add **/package.json" 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /projects/demo/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "projectType": "application", 3 | "generators": {}, 4 | "root": "projects/demo", 5 | "sourceRoot": "projects/demo/src", 6 | "prefix": "app", 7 | "targets": { 8 | "build": { 9 | "executor": "@angular-devkit/build-angular:browser", 10 | "options": { 11 | "baseHref": "/", 12 | "outputPath": "dist/demo/browser", 13 | "index": "projects/demo/src/index.html", 14 | "main": "projects/demo/src/main.browser.ts", 15 | "polyfills": "projects/demo/src/polyfills.ts", 16 | "tsConfig": "projects/demo/tsconfig.app.json", 17 | "aot": false, 18 | "assets": ["projects/demo/src/favicon.ico"], 19 | "styles": ["projects/demo/src/styles.css"], 20 | "scripts": [], 21 | "vendorChunk": true, 22 | "extractLicenses": false, 23 | "buildOptimizer": false, 24 | "sourceMap": true, 25 | "optimization": false, 26 | "namedChunks": true 27 | }, 28 | "configurations": { 29 | "production": { 30 | "fileReplacements": [ 31 | { 32 | "replace": "projects/demo/src/environments/environment.ts", 33 | "with": "projects/demo/src/environments/environment.prod.ts" 34 | } 35 | ], 36 | "optimization": true, 37 | "outputHashing": "all", 38 | "sourceMap": false, 39 | "namedChunks": false, 40 | "aot": true, 41 | "extractLicenses": true, 42 | "vendorChunk": false, 43 | "buildOptimizer": true, 44 | "budgets": [ 45 | { 46 | "type": "initial", 47 | "maximumWarning": "2mb", 48 | "maximumError": "5mb" 49 | } 50 | ] 51 | }, 52 | "development": {} 53 | }, 54 | "defaultConfiguration": "production" 55 | }, 56 | "build-gh-pages": { 57 | "executor": "@nrwl/workspace:run-commands", 58 | "options": { 59 | "parallel": false, 60 | "commands": [ 61 | "echo 'Github pages require special baseHref + 404.html'", 62 | "echo 'Read more: https://angular.io/guide/deployment#deploy-to-github-pages'", 63 | "echo ------", 64 | "nx build --base-href='/angular-open-source-starter/'", 65 | "cp dist/demo/browser/index.html dist/demo/browser/404.html" 66 | ] 67 | } 68 | }, 69 | "serve": { 70 | "executor": "@angular-devkit/build-angular:dev-server", 71 | "options": { 72 | "browserTarget": "demo:build", 73 | "sslCert": ".ssl/localhost.pem", 74 | "sslKey": ".ssl/localhost-key.pem" 75 | }, 76 | "configurations": { 77 | "production": { 78 | "browserTarget": "demo:build:production" 79 | }, 80 | "development": { 81 | "browserTarget": "demo:build:development" 82 | } 83 | }, 84 | "defaultConfiguration": "development" 85 | }, 86 | "serve-ip": { 87 | "builder": "@nrwl/workspace:run-commands", 88 | "options": { 89 | "command": "nx serve --open --host 0.0.0.0 --disable-host-check" 90 | } 91 | }, 92 | "serve-ssl": { 93 | "builder": "@nrwl/workspace:run-commands", 94 | "options": { 95 | "parallel": false, 96 | "commands": [ 97 | "echo \"mkcert is a simple tool for making locally-trusted development certificates\"", 98 | "echo \"Read about installation and more: https://github.com/FiloSottile/mkcert\"", 99 | "echo ------", 100 | "mkcert -install", 101 | "mkdir -p .ssl", 102 | "mkcert -key-file .ssl/localhost-key.pem -cert-file .ssl/localhost.pem localhost 127.0.0.1 ::1", 103 | "nx serve --ssl" 104 | ] 105 | } 106 | }, 107 | "test": { 108 | "executor": "@nrwl/jest:jest", 109 | "outputs": ["coverage/projects/demo"], 110 | "options": { 111 | "jestConfig": "projects/demo/jest.config.js", 112 | "passWithNoTests": true 113 | } 114 | }, 115 | "server": { 116 | "executor": "@angular-devkit/build-angular:server", 117 | "options": { 118 | "outputPath": "dist/demo/server", 119 | "main": "projects/demo/server.ts", 120 | "tsConfig": "projects/demo/tsconfig.server.json", 121 | "sourceMap": true, 122 | "optimization": false 123 | }, 124 | "configurations": { 125 | "production": { 126 | "outputHashing": "media", 127 | "sourceMap": false, 128 | "optimization": true 129 | }, 130 | "development": {} 131 | }, 132 | "defaultConfiguration": "production" 133 | }, 134 | "serve-ssr": { 135 | "executor": "@nguniversal/builders:ssr-dev-server", 136 | "options": { 137 | "browserTarget": "demo:build", 138 | "serverTarget": "demo:server" 139 | }, 140 | "configurations": { 141 | "production": { 142 | "browserTarget": "demo:build:production", 143 | "serverTarget": "demo:server:production" 144 | }, 145 | "development": { 146 | "serverTarget": "demo:server:development", 147 | "browserTarget": "demo:build:development" 148 | } 149 | }, 150 | "defaultConfiguration": "development" 151 | }, 152 | "prerender": { 153 | "executor": "@nguniversal/builders:prerender", 154 | "options": { 155 | "browserTarget": "demo:build:production", 156 | "serverTarget": "demo:server:production", 157 | "routes": ["/"] 158 | }, 159 | "configurations": { 160 | "production": {} 161 | } 162 | } 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | Copyright 2019 Tinkoff Bank 179 | 180 | Licensed under the Apache License, Version 2.0 (the "License"); 181 | you may not use this file except in compliance with the License. 182 | You may obtain a copy of the License at 183 | 184 | http://www.apache.org/licenses/LICENSE-2.0 185 | 186 | Unless required by applicable law or agreed to in writing, software 187 | distributed under the License is distributed on an "AS IS" BASIS, 188 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 189 | See the License for the specific language governing permissions and 190 | limitations under the License. --------------------------------------------------------------------------------