├── .browserslistrc ├── .editorconfig ├── .env.EXAMPLE ├── .eslintrc.json ├── .gitignore ├── .prettierignore ├── .prettierrc ├── README.md ├── angular.json ├── karma.conf.js ├── package-lock.json ├── package.json ├── server.ts ├── setup-env.ts ├── src ├── app │ ├── app-routing.module.ts │ ├── app.browser.module.ts │ ├── app.component.html │ ├── app.component.scss │ ├── app.component.ts │ ├── app.module.ts │ ├── app.server.module.ts │ ├── guards │ │ └── auth.guard.ts │ ├── interceptors │ │ ├── browser-transfer-state.interceptor.ts │ │ └── server-transfer-state.interceptor.ts │ ├── loaders │ │ ├── translate-browser.loader.ts │ │ └── translate-server.loader.ts │ ├── services │ │ ├── cookies.service.ts │ │ └── language.service.ts │ └── utils │ │ └── domino.utils.ts ├── assets │ ├── .gitkeep │ └── i18n │ │ ├── en-us.json │ │ └── pt-br.json ├── environments │ └── .gitkeep ├── favicon.ico ├── index.html ├── main.browser.ts ├── main.server.ts ├── polyfills.ts ├── styles.scss ├── styles │ └── variables.scss └── test.ts ├── tsconfig.app.json ├── tsconfig.json ├── tsconfig.server.json └── tsconfig.spec.json /.browserslistrc: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | # For the full list of supported browsers by the Angular framework, please see: 6 | # https://angular.io/guide/browser-support 7 | 8 | # You can see what browsers were selected by your queries by running: 9 | # npx browserslist 10 | 11 | last 1 Chrome version 12 | last 1 Firefox version 13 | last 2 Edge major versions 14 | last 2 Safari major versions 15 | last 2 iOS major versions 16 | Firefox ESR 17 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://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 | [*.ts] 12 | quote_type = single 13 | 14 | [*.md] 15 | max_line_length = off 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /.env.EXAMPLE: -------------------------------------------------------------------------------- 1 | PRODUCTION=false 2 | APP_ID=my-app 3 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "ignorePatterns": [ 4 | "projects/**/*" 5 | ], 6 | "overrides": [ 7 | { 8 | "files": [ 9 | "*.ts" 10 | ], 11 | "parserOptions": { 12 | "project": [ 13 | "tsconfig.json" 14 | ], 15 | "createDefaultProgram": true 16 | }, 17 | "extends": [ 18 | "plugin:@angular-eslint/recommended", 19 | "plugin:@angular-eslint/template/process-inline-templates" 20 | ], 21 | "rules": { 22 | "@angular-eslint/directive-selector": [ 23 | "error", 24 | { 25 | "type": "attribute", 26 | "prefix": "app", 27 | "style": "camelCase" 28 | } 29 | ], 30 | "@angular-eslint/component-selector": [ 31 | "error", 32 | { 33 | "type": "element", 34 | "prefix": "app", 35 | "style": "kebab-case" 36 | } 37 | ], 38 | "@typescript-eslint/semi": [ 39 | "error" 40 | ], 41 | "indent": [ 42 | "error", 43 | 2 44 | ], 45 | "keyword-spacing": [ 46 | "error", 47 | { 48 | "before": true, 49 | "after": true 50 | } 51 | ], 52 | "space-before-function-paren": [ 53 | "error", 54 | { 55 | "anonymous": "never", 56 | "named": "never", 57 | "asyncArrow": "always" 58 | } 59 | ], 60 | "object-curly-spacing": [ 61 | "error", 62 | "never" 63 | ], 64 | "array-bracket-spacing": [ 65 | "error", 66 | "never" 67 | ], 68 | "space-in-parens": [ 69 | "error", 70 | "never" 71 | ], 72 | "space-before-blocks": [ 73 | "error" 74 | ], 75 | "comma-spacing": [ 76 | 2, 77 | { 78 | "before": false, 79 | "after": true 80 | } 81 | ], 82 | "no-multi-spaces": [ 83 | "error" 84 | ], 85 | "quotes": [ 86 | "error", 87 | "single" 88 | ], 89 | "key-spacing": [ 90 | "error", 91 | { 92 | "beforeColon": false 93 | } 94 | ], 95 | "padded-blocks": [ 96 | "error", 97 | "never" 98 | ], 99 | "array-element-newline": [ 100 | "error", 101 | "consistent" 102 | ] 103 | } 104 | }, 105 | { 106 | "files": [ 107 | "*.html" 108 | ], 109 | "extends": [ 110 | "plugin:@angular-eslint/template/recommended" 111 | ], 112 | "rules": {} 113 | } 114 | ] 115 | } 116 | -------------------------------------------------------------------------------- /.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 | # Only exists if Bazel was run 8 | /bazel-out 9 | 10 | # dependencies 11 | /node_modules 12 | 13 | # profiling files 14 | chrome-profiler-events*.json 15 | 16 | # IDEs and editors 17 | /.idea 18 | .project 19 | .classpath 20 | .c9/ 21 | *.launch 22 | .settings/ 23 | *.sublime-workspace 24 | 25 | # IDE - VSCode 26 | .vscode/* 27 | !.vscode/settings.json 28 | !.vscode/tasks.json 29 | !.vscode/launch.json 30 | !.vscode/extensions.json 31 | .history/* 32 | 33 | # misc 34 | /.angular/cache 35 | /.sass-cache 36 | /connect.lock 37 | /coverage 38 | /libpeerconnection.log 39 | npm-debug.log 40 | yarn-error.log 41 | testem.log 42 | /typings 43 | 44 | #environment 45 | /src/environments/environment.ts 46 | .env 47 | 48 | # System Files 49 | .DS_Store 50 | Thumbs.db 51 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Ignore artifacts 2 | 3 | node_modules 4 | build 5 | coverage 6 | e2e 7 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "useTabs": false, 4 | "semi": true, 5 | "bracketSpacing": false, 6 | "singleQuote": true, 7 | "bracketSameLine": true 8 | } 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Angular Starter Application 2 | 3 | Angular version: `13.1.3` 4 | 5 | ### Preconfigured 6 | ``` 7 | - Angular Universal (server side rendering) 8 | - i18n (Multi languages) 9 | - Cache http requests state (server state to client state) 10 | - Cookies 11 | - Dotenv environments setup 12 | - registerLocaleData (pt) 13 | - ESLint 14 | - Prettier 15 | ``` 16 | 17 | ### Environments 18 | 19 | - Create a `.env` file in project root; 20 | - Use `.env.EXAMPLE` as example; 21 | - Exec `npm run config` command to generate TS environment file; 22 | 23 | ### i18n - Multi languages 24 | 25 | - There are two preconfigured languages, `pt-br` and `en-us`; 26 | - You can add more creating `.json` files with language short name in `assets/i18n/`; 27 | - To change language in runtime, use `LanguageService` to do this; 28 | - [Read docs](https://github.com/ngx-translate/core) 29 | 30 | ### Cookies 31 | 32 | - Persist data with cookie (Good to use in SSR, for auth, for example); 33 | - Use `CookiesService` to manage cookies; 34 | - [Read docs](https://github.com/salemdar/ngx-cookie) 35 | 36 | ### Cache - Transfer State 37 | 38 | - All success http requests are cached and transferred to client app (to avoid double requests); 39 | - You can make changes in `BrowserStateInterceptor` to improve cache method; 40 | 41 | ### Prettier 42 | 43 | - Exec `npm run prettier` to format files; 44 | - [Read docs](https://prettier.io/docs/en/index.html) 45 | 46 | ### Linter (ESLint) 47 | 48 | - Exec `npm run lint` to analyze files; 49 | - [Read docs](https://github.com/angular-eslint/angular-eslint) 50 | 51 | ### Run dev server 52 | 53 | - Exec `npm run start` to run dev server; 54 | - Exec `npm run start:ssr` to run dev SSR server; 55 | 56 | ### Build production 57 | 58 | #### Client only 59 | 60 | - Exec `npm run build` to build client app only; 61 | - Browser dist files are in `dist/my-app/browser`; 62 | 63 | #### Client and server 64 | 65 | - Exec `npm run build:ssr` to build client and server; 66 | - Browser dist files are in `dist/my-app/browser`; 67 | - Server dist files are in `dist/my-app/server`; 68 | 69 |
70 | 71 | [@gesielrosa](https://gesiel.com) 72 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "my-app": { 7 | "projectType": "application", 8 | "schematics": { 9 | "@schematics/angular:component": { 10 | "style": "scss" 11 | }, 12 | "@schematics/angular:application": { 13 | "strict": true 14 | } 15 | }, 16 | "root": "", 17 | "sourceRoot": "src", 18 | "prefix": "app", 19 | "architect": { 20 | "build": { 21 | "builder": "@angular-devkit/build-angular:browser", 22 | "options": { 23 | "outputPath": "dist/my-app/browser", 24 | "index": "src/index.html", 25 | "main": "src/main.browser.ts", 26 | "polyfills": "src/polyfills.ts", 27 | "tsConfig": "tsconfig.app.json", 28 | "inlineStyleLanguage": "scss", 29 | "assets": [ 30 | "src/favicon.ico", 31 | "src/assets" 32 | ], 33 | "styles": [ 34 | "src/styles.scss" 35 | ], 36 | "scripts": [] 37 | }, 38 | "configurations": { 39 | "production": { 40 | "budgets": [ 41 | { 42 | "type": "initial", 43 | "maximumWarning": "500kb", 44 | "maximumError": "1mb" 45 | }, 46 | { 47 | "type": "anyComponentStyle", 48 | "maximumWarning": "2kb", 49 | "maximumError": "4kb" 50 | } 51 | ], 52 | "outputHashing": "all" 53 | }, 54 | "development": { 55 | "buildOptimizer": false, 56 | "optimization": false, 57 | "vendorChunk": true, 58 | "extractLicenses": false, 59 | "sourceMap": true, 60 | "namedChunks": true 61 | } 62 | }, 63 | "defaultConfiguration": "production" 64 | }, 65 | "serve": { 66 | "builder": "@angular-devkit/build-angular:dev-server", 67 | "configurations": { 68 | "production": { 69 | "browserTarget": "my-app:build:production" 70 | }, 71 | "development": { 72 | "browserTarget": "my-app:build:development" 73 | } 74 | }, 75 | "defaultConfiguration": "development" 76 | }, 77 | "extract-i18n": { 78 | "builder": "@angular-devkit/build-angular:extract-i18n", 79 | "options": { 80 | "browserTarget": "my-app:build" 81 | } 82 | }, 83 | "test": { 84 | "builder": "@angular-devkit/build-angular:karma", 85 | "options": { 86 | "main": "src/test.ts", 87 | "polyfills": "src/polyfills.ts", 88 | "tsConfig": "tsconfig.spec.json", 89 | "karmaConfig": "karma.conf.js", 90 | "inlineStyleLanguage": "scss", 91 | "assets": [ 92 | "src/favicon.ico", 93 | "src/assets" 94 | ], 95 | "styles": [ 96 | "src/styles.scss" 97 | ], 98 | "scripts": [] 99 | } 100 | }, 101 | "server": { 102 | "builder": "@angular-devkit/build-angular:server", 103 | "options": { 104 | "outputPath": "dist/my-app/server", 105 | "main": "server.ts", 106 | "tsConfig": "tsconfig.server.json", 107 | "inlineStyleLanguage": "scss" 108 | }, 109 | "configurations": { 110 | "production": { 111 | "outputHashing": "media", 112 | "fileReplacements": [] 113 | }, 114 | "development": { 115 | "optimization": false, 116 | "sourceMap": true, 117 | "extractLicenses": false 118 | } 119 | }, 120 | "defaultConfiguration": "production" 121 | }, 122 | "serve-ssr": { 123 | "builder": "@nguniversal/builders:ssr-dev-server", 124 | "configurations": { 125 | "development": { 126 | "browserTarget": "my-app:build:development", 127 | "serverTarget": "my-app:server:development" 128 | }, 129 | "production": { 130 | "browserTarget": "my-app:build:production", 131 | "serverTarget": "my-app:server:production" 132 | } 133 | }, 134 | "defaultConfiguration": "development" 135 | }, 136 | "prerender": { 137 | "builder": "@nguniversal/builders:prerender", 138 | "options": { 139 | "routes": [ 140 | "/" 141 | ] 142 | }, 143 | "configurations": { 144 | "production": { 145 | "browserTarget": "my-app:build:production", 146 | "serverTarget": "my-app:server:production" 147 | }, 148 | "development": { 149 | "browserTarget": "my-app:build:development", 150 | "serverTarget": "my-app:server:development" 151 | } 152 | }, 153 | "defaultConfiguration": "production" 154 | }, 155 | "lint": { 156 | "builder": "@angular-eslint/builder:lint", 157 | "options": { 158 | "lintFilePatterns": [ 159 | "src/**/*.ts", 160 | "src/**/*.html" 161 | ] 162 | } 163 | } 164 | } 165 | } 166 | }, 167 | "defaultProject": "my-app", 168 | "cli": { 169 | "defaultCollection": "@angular-eslint/schematics" 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /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 | const {environment} = require("./src/environments/environment"); 5 | 6 | module.exports = function (config) { 7 | config.set({ 8 | basePath: '', 9 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 10 | plugins: [ 11 | require('karma-jasmine'), 12 | require('karma-chrome-launcher'), 13 | require('karma-jasmine-html-reporter'), 14 | require('karma-coverage'), 15 | require('@angular-devkit/build-angular/plugins/karma') 16 | ], 17 | client: { 18 | jasmine: { 19 | // you can add configuration options for Jasmine here 20 | // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html 21 | // for example, you can disable the random execution with `random: false` 22 | // or set a specific seed with `seed: 4321` 23 | }, 24 | clearContext: false // leave Jasmine Spec Runner output visible in browser 25 | }, 26 | jasmineHtmlReporter: { 27 | suppressAll: true // removes the duplicated traces 28 | }, 29 | coverageReporter: { 30 | dir: require('path').join(__dirname, './coverage/', environment.appId), 31 | subdir: '.', 32 | reporters: [ 33 | {type: 'html'}, 34 | {type: 'text-summary'} 35 | ] 36 | }, 37 | reporters: ['progress', 'kjhtml'], 38 | port: 9876, 39 | colors: true, 40 | logLevel: config.LOG_INFO, 41 | autoWatch: true, 42 | browsers: ['Chrome'], 43 | singleRun: false, 44 | restartOnFileChange: true 45 | }); 46 | }; 47 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-app", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "config": "ts-node setup-env.ts", 6 | "ng": "ng", 7 | "start": "npm run config && NG_PERSISTENT_BUILD_CACHE=1 ng serve", 8 | "start:ssr": "npm run config && ng run my-app:serve-ssr", 9 | "build": "npm run config && NG_PERSISTENT_BUILD_CACHE=1 ng build", 10 | "build:ssr": "npm run build && ng run my-app:server", 11 | "serve:ssr": "node dist/my-app/server/main.js", 12 | "test": "npm run config && ng test", 13 | "lint": "ng lint", 14 | "prettier": "prettier --config ./.prettierrc --write \"src/**/*{.ts,.html,.scss}\"", 15 | "postinstall": "ngcc" 16 | }, 17 | "private": true, 18 | "dependencies": { 19 | "@angular/animations": "~13.1.3", 20 | "@angular/common": "~13.1.3", 21 | "@angular/compiler": "~13.1.3", 22 | "@angular/core": "~13.1.3", 23 | "@angular/forms": "~13.1.3", 24 | "@angular/platform-browser": "~13.1.3", 25 | "@angular/platform-browser-dynamic": "~13.1.3", 26 | "@angular/platform-server": "~13.1.3", 27 | "@angular/router": "~13.1.3", 28 | "@nguniversal/express-engine": "^13.0.2", 29 | "@ngx-translate/core": "^14.0.0", 30 | "@ngx-translate/http-loader": "^7.0.0", 31 | "cookie-parser": "^1.4.6", 32 | "express": "^4.17.2", 33 | "globalthis": "^1.0.2", 34 | "ngx-cookie": "^5.0.2", 35 | "ngx-cookie-backend": "^5.0.2", 36 | "rxjs": "~7.4.0", 37 | "tslib": "^2.3.0", 38 | "zone.js": "~0.11.4" 39 | }, 40 | "devDependencies": { 41 | "@angular-devkit/build-angular": "^13.1.4", 42 | "@angular-eslint/builder": "^13.0.1", 43 | "@angular-eslint/eslint-plugin": "^13.0.1", 44 | "@angular-eslint/eslint-plugin-template": "^13.0.1", 45 | "@angular-eslint/schematics": "^13.0.1", 46 | "@angular-eslint/template-parser": "^13.0.1", 47 | "@angular/cli": "~13.1.4", 48 | "@angular/compiler-cli": "~13.1.3", 49 | "@nguniversal/builders": "^13.0.2", 50 | "@types/cookie-parser": "^1.4.2", 51 | "@types/express": "^4.17.13", 52 | "@types/jasmine": "~3.10.3", 53 | "@types/node": "^12.20.42", 54 | "@typescript-eslint/eslint-plugin": "5.3.0", 55 | "@typescript-eslint/parser": "5.3.0", 56 | "dotenv": "^14.3.0", 57 | "eslint": "^8.2.0", 58 | "jasmine-core": "~3.10.0", 59 | "karma": "~6.3.0", 60 | "karma-chrome-launcher": "~3.1.0", 61 | "karma-coverage": "~2.0.3", 62 | "karma-jasmine": "~4.0.0", 63 | "karma-jasmine-html-reporter": "~1.7.0", 64 | "prettier": "^2.5.1", 65 | "ts-node": "^10.4.0", 66 | "typescript": "~4.4.3" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /server.ts: -------------------------------------------------------------------------------- 1 | import 'zone.js/dist/zone-node'; 2 | import 'globalthis/auto'; 3 | 4 | import {ngExpressEngine} from '@nguniversal/express-engine'; 5 | import * as express from 'express'; 6 | import {join} from 'path'; 7 | 8 | import {applyDomino} from '@utils/domino.utils'; 9 | import {environment} from '@env/environment'; 10 | 11 | import {AppServerModule} from './src/main.server'; 12 | import {APP_BASE_HREF} from '@angular/common'; 13 | import {existsSync} from 'fs'; 14 | 15 | import {REQUEST, RESPONSE} from '@nguniversal/express-engine/tokens'; 16 | import * as cookieparser from 'cookie-parser'; 17 | 18 | // The Express app is exported so that it can be used by serverless Functions. 19 | export function app(): express.Express { 20 | const server = express(); 21 | const distFolder = join(process.cwd(), `dist/${environment?.appId}/browser`); 22 | applyDomino(global, join(distFolder, 'index.html')); 23 | const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index'; 24 | 25 | // Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine) 26 | server.engine('html', ngExpressEngine({ 27 | bootstrap: AppServerModule, 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('*.*', express.static(distFolder, { 37 | maxAge: '1y' 38 | })); 39 | 40 | // All regular routes use the Universal engine 41 | server.get('*', (req, res) => { 42 | res.render(indexHtml, { 43 | req, 44 | providers: [ 45 | { 46 | provide: APP_BASE_HREF, 47 | useValue: req.baseUrl 48 | }, 49 | { 50 | provide: REQUEST, 51 | useValue: req, 52 | }, 53 | { 54 | provide: RESPONSE, 55 | useValue: res, 56 | } 57 | ] 58 | }); 59 | }); 60 | 61 | return server; 62 | } 63 | 64 | function run(): void { 65 | const port = process.env['PORT'] || 4000; 66 | 67 | // Start up the Node server 68 | const server = app(); 69 | server.use(cookieparser()); 70 | server.listen(port, () => { 71 | console.log(`Node Express server listening on http://localhost:${port}`); 72 | }); 73 | } 74 | 75 | // Webpack will replace 'require' with '__webpack_require__' 76 | // '__non_webpack_require__' is a proxy to Node 'require' 77 | // The below code is to ensure that the server is run only when not requiring the bundle. 78 | declare const __non_webpack_require__: NodeRequire; 79 | const mainModule = __non_webpack_require__.main; 80 | const moduleFilename = mainModule && mainModule.filename || ''; 81 | if (moduleFilename === __filename || moduleFilename.includes('iisnode')) { 82 | run(); 83 | } 84 | 85 | export * from './src/main.server'; 86 | -------------------------------------------------------------------------------- /setup-env.ts: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | // Configure Angular `environment.ts` file path 3 | const targetPath = './src/environments/environment.ts'; 4 | // Load node modules 5 | const colors = require('colors'); 6 | require('dotenv').config(); 7 | // `environment.ts` file structure 8 | const envConfigFile = `export const environment = { 9 | production: ${process.env['PRODUCTION'] || false}, 10 | appId: '${process.env['APP_ID'] || 'my-app'}' 11 | };`; 12 | console.log(colors.magenta('The file `environment.ts` will be written with the following content: \n')); 13 | console.log(colors.brightGreen(envConfigFile + ' \n')); 14 | fs.writeFile(targetPath, envConfigFile, (err: any) => { 15 | if (err) { 16 | throw console?.error(err); 17 | } else { 18 | console.log(colors.magenta(`Angular environment.ts file generated correctly at ${targetPath} \n`)); 19 | } 20 | }); 21 | -------------------------------------------------------------------------------- /src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | import {RouterModule, Routes} from '@angular/router'; 3 | 4 | const routes: Routes = []; 5 | 6 | @NgModule({ 7 | imports: [ 8 | RouterModule.forRoot(routes, { 9 | initialNavigation: 'enabledBlocking', 10 | scrollPositionRestoration: 'top' 11 | }), 12 | ], 13 | exports: [RouterModule], 14 | }) 15 | export class AppRoutingModule {} 16 | -------------------------------------------------------------------------------- /src/app/app.browser.module.ts: -------------------------------------------------------------------------------- 1 | import {LOCALE_ID, NgModule} from '@angular/core'; 2 | import {BrowserTransferStateModule, TransferState} from '@angular/platform-browser'; 3 | import {HTTP_INTERCEPTORS, HttpClient} from '@angular/common/http'; 4 | import {StateTransferInitializerModule} from '@nguniversal/common'; 5 | 6 | import {TranslateLoader, TranslateModule} from '@ngx-translate/core'; 7 | 8 | import {AppModule} from './app.module'; 9 | import {AppComponent} from './app.component'; 10 | 11 | import {translateBrowserLoaderFactory} from '@loaders/translate-browser.loader'; 12 | import {BrowserStateInterceptor} from '@interceptors/browser-transfer-state.interceptor'; 13 | 14 | @NgModule({ 15 | imports: [ 16 | AppModule, 17 | StateTransferInitializerModule, 18 | BrowserTransferStateModule, 19 | TranslateModule.forRoot({ 20 | loader: { 21 | provide: TranslateLoader, 22 | useFactory: translateBrowserLoaderFactory, 23 | deps: [HttpClient, TransferState], 24 | }, 25 | }), 26 | ], 27 | bootstrap: [AppComponent], 28 | providers: [ 29 | { 30 | provide: HTTP_INTERCEPTORS, 31 | useClass: BrowserStateInterceptor, 32 | multi: true, 33 | }, 34 | { 35 | provide: 'ORIGIN_URL', 36 | useValue: location.origin, 37 | }, 38 | { 39 | provide: LOCALE_ID, 40 | useValue: 'pt', 41 | }, 42 | ], 43 | }) 44 | export class AppBrowserModule { 45 | } 46 | -------------------------------------------------------------------------------- /src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/app/app.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gesielrosa/angular-starter-app/d09f9b00291935b09780e2ddf42a60b390e6ebe7/src/app/app.component.scss -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, OnInit} from '@angular/core'; 2 | 3 | import {LanguageService} from '@services/language.service'; 4 | 5 | @Component({ 6 | selector: 'app-root', 7 | templateUrl: './app.component.html', 8 | styleUrls: ['./app.component.scss'], 9 | }) 10 | export class AppComponent implements OnInit { 11 | constructor(private _languageService: LanguageService) {} 12 | 13 | public ngOnInit(): void { 14 | this._languageService.init(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import {LOCALE_ID, NgModule} from '@angular/core'; 2 | import {BrowserModule} from '@angular/platform-browser'; 3 | import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; 4 | import {HttpClientModule} from '@angular/common/http'; 5 | import {registerLocaleData} from '@angular/common'; 6 | import localePt from '@angular/common/locales/pt'; 7 | 8 | import {CookieModule} from 'ngx-cookie'; 9 | 10 | import {AppRoutingModule} from './app-routing.module'; 11 | import {AppComponent} from './app.component'; 12 | 13 | import {environment} from '@env/environment'; 14 | 15 | registerLocaleData(localePt, 'pt'); 16 | 17 | @NgModule({ 18 | declarations: [AppComponent], 19 | imports: [ 20 | BrowserModule.withServerTransition({appId: environment.appId}), 21 | AppRoutingModule, 22 | BrowserAnimationsModule, 23 | HttpClientModule, 24 | CookieModule.forRoot(), 25 | ], 26 | providers: [{provide: LOCALE_ID, useValue: 'pt'}], 27 | bootstrap: [AppComponent], 28 | }) 29 | export class AppModule {} 30 | -------------------------------------------------------------------------------- /src/app/app.server.module.ts: -------------------------------------------------------------------------------- 1 | import {LOCALE_ID, NgModule} from '@angular/core'; 2 | import {ServerModule, ServerTransferStateModule} from '@angular/platform-server'; 3 | import {NoopAnimationsModule} from '@angular/platform-browser/animations'; 4 | import {TransferState} from '@angular/platform-browser'; 5 | import {HTTP_INTERCEPTORS} from '@angular/common/http'; 6 | 7 | import {CookieBackendModule} from 'ngx-cookie-backend'; 8 | import {TranslateLoader, TranslateModule} from '@ngx-translate/core'; 9 | 10 | import {AppModule} from './app.module'; 11 | import {AppComponent} from './app.component'; 12 | 13 | import {translateServerLoaderFactory} from '@loaders/translate-server.loader'; 14 | import {ServerTransferStateInterceptor} from '@interceptors/server-transfer-state.interceptor'; 15 | 16 | @NgModule({ 17 | imports: [ 18 | AppModule, 19 | ServerModule, 20 | NoopAnimationsModule, 21 | ServerTransferStateModule, 22 | TranslateModule.forRoot({ 23 | loader: { 24 | provide: TranslateLoader, 25 | useFactory: translateServerLoaderFactory, 26 | deps: [TransferState], 27 | }, 28 | }), 29 | CookieBackendModule.forRoot(), 30 | ], 31 | bootstrap: [AppComponent], 32 | providers: [ 33 | { 34 | provide: HTTP_INTERCEPTORS, 35 | useClass: ServerTransferStateInterceptor, 36 | multi: true, 37 | }, 38 | { 39 | provide: LOCALE_ID, 40 | useValue: 'pt', 41 | }, 42 | ], 43 | }) 44 | export class AppServerModule {} 45 | -------------------------------------------------------------------------------- /src/app/guards/auth.guard.ts: -------------------------------------------------------------------------------- 1 | import {Injectable} from '@angular/core'; 2 | import {ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot, UrlTree} from '@angular/router'; 3 | 4 | import {Observable} from 'rxjs'; 5 | 6 | @Injectable({ 7 | providedIn: 'root', 8 | }) 9 | export class AuthGuard implements CanActivate { 10 | public canActivate( 11 | route: ActivatedRouteSnapshot, 12 | state: RouterStateSnapshot 13 | ): Observable | Promise | boolean | UrlTree { 14 | // add guard validation here 15 | return true; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/app/interceptors/browser-transfer-state.interceptor.ts: -------------------------------------------------------------------------------- 1 | import {HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse} from '@angular/common/http'; 2 | import {Injectable} from '@angular/core'; 3 | import {makeStateKey, TransferState} from '@angular/platform-browser'; 4 | import {Observable, of} from 'rxjs'; 5 | 6 | @Injectable({ 7 | providedIn: 'root', 8 | }) 9 | export class BrowserStateInterceptor implements HttpInterceptor { 10 | constructor(private transferState: TransferState) { 11 | } 12 | 13 | public intercept(req: HttpRequest, next: HttpHandler): Observable> { 14 | if (req.method === 'GET') { 15 | const key = makeStateKey(req.url); 16 | const storedResponse: string = this.transferState.get(key, null); 17 | if (storedResponse) { 18 | const response = new HttpResponse({body: storedResponse, status: 200}); 19 | return of(response); 20 | } 21 | } 22 | 23 | return next.handle(req); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/app/interceptors/server-transfer-state.interceptor.ts: -------------------------------------------------------------------------------- 1 | import {HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse} from '@angular/common/http'; 2 | import {Injectable} from '@angular/core'; 3 | import {makeStateKey, TransferState} from '@angular/platform-browser'; 4 | import {tap} from 'rxjs/operators'; 5 | import {Observable} from 'rxjs'; 6 | 7 | @Injectable() 8 | export class ServerTransferStateInterceptor implements HttpInterceptor { 9 | constructor(private _transferState: TransferState) { 10 | } 11 | 12 | public intercept(req: HttpRequest, next: HttpHandler): Observable> { 13 | return next.handle(req).pipe( 14 | tap((event) => { 15 | if (event instanceof HttpResponse && (event.status === 200 || event.status === 202)) { 16 | this._transferState.set(makeStateKey(req.url), event.body); 17 | } 18 | }) 19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/app/loaders/translate-browser.loader.ts: -------------------------------------------------------------------------------- 1 | import {Observable} from 'rxjs'; 2 | import {makeStateKey, StateKey, TransferState} from '@angular/platform-browser'; 3 | import {HttpClient} from '@angular/common/http'; 4 | 5 | import {TranslateLoader} from '@ngx-translate/core'; 6 | import {TranslateHttpLoader} from '@ngx-translate/http-loader'; 7 | 8 | export class TranslateBrowserLoader implements TranslateLoader { 9 | constructor(private _http: HttpClient, private _transferState: TransferState) {} 10 | 11 | public getTranslation(lang: string): Observable { 12 | const key: StateKey = makeStateKey('transfer-translate-' + lang); 13 | const data = this._transferState.get(key, null); 14 | 15 | if (data) { 16 | return new Observable((observer) => { 17 | observer.next(data); 18 | observer.complete(); 19 | }); 20 | } else { 21 | return new TranslateHttpLoader(this._http, './assets/i18n/', '.json').getTranslation(lang); 22 | } 23 | } 24 | } 25 | 26 | export function translateBrowserLoaderFactory(httpClient: HttpClient, transferState: TransferState) { 27 | return new TranslateBrowserLoader(httpClient, transferState); 28 | } 29 | -------------------------------------------------------------------------------- /src/app/loaders/translate-server.loader.ts: -------------------------------------------------------------------------------- 1 | import {makeStateKey, StateKey, TransferState} from '@angular/platform-browser'; 2 | 3 | import {TranslateLoader} from '@ngx-translate/core'; 4 | import {join} from 'path'; 5 | import {Observable} from 'rxjs'; 6 | import * as fs from 'fs'; 7 | 8 | import {environment} from '@env/environment'; 9 | 10 | export class TranslateServerLoader implements TranslateLoader { 11 | constructor( 12 | private _transferState: TransferState, 13 | private _prefix: string = 'i18n', 14 | private _suffix: string = '.json' 15 | ) {} 16 | 17 | public getTranslation(lang: string): Observable { 18 | return new Observable((observer) => { 19 | const assets_folder = join(process.cwd(), 'dist', environment.appId, 'browser', 'assets', this._prefix); 20 | 21 | const jsonData = JSON.parse(fs.readFileSync(`${assets_folder}/${lang}${this._suffix}`, 'utf8')); 22 | 23 | // Here we save the translations in the transfer-state 24 | const key: StateKey = makeStateKey('transfer-translate-' + lang); 25 | this._transferState.set(key, jsonData); 26 | 27 | observer.next(jsonData); 28 | observer.complete(); 29 | }); 30 | } 31 | } 32 | 33 | export function translateServerLoaderFactory(transferState: TransferState) { 34 | return new TranslateServerLoader(transferState); 35 | } 36 | -------------------------------------------------------------------------------- /src/app/services/cookies.service.ts: -------------------------------------------------------------------------------- 1 | import {Injectable} from '@angular/core'; 2 | 3 | import {CookieService as NgxCookieService} from 'ngx-cookie'; 4 | 5 | @Injectable({ 6 | providedIn: 'root', 7 | }) 8 | export class CookiesService { 9 | constructor(private _cookieService: NgxCookieService) { 10 | } 11 | 12 | public setItem(key: string, value: string): void { 13 | this._cookieService.put(key, value); 14 | } 15 | 16 | public setObjectItem(key: string, value: any): void { 17 | this._cookieService.putObject(key, value); 18 | } 19 | 20 | public getItem(key: string): string { 21 | return this._cookieService.get(key); 22 | } 23 | 24 | public getObjectItem(key: string): any { 25 | return this._cookieService.getObject(key); 26 | } 27 | 28 | public removeItem(key: string): void { 29 | this._cookieService.remove(key); 30 | } 31 | 32 | public removeAll(): void { 33 | this._cookieService.removeAll(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/app/services/language.service.ts: -------------------------------------------------------------------------------- 1 | import {Inject, Injectable, InjectionToken, PLATFORM_ID} from '@angular/core'; 2 | import {DOCUMENT, isPlatformBrowser} from '@angular/common'; 3 | 4 | import {REQUEST} from '@nguniversal/express-engine/tokens'; 5 | import {TranslateService} from '@ngx-translate/core'; 6 | import {Request} from 'express'; 7 | 8 | import {CookiesService} from '@services/cookies.service'; 9 | 10 | @Injectable({ 11 | providedIn: 'root', 12 | }) 13 | export class LanguageService { 14 | private _language: string; 15 | 16 | private readonly _isBrowser: boolean; 17 | 18 | constructor( 19 | private _translate: TranslateService, 20 | private _storage: CookiesService, 21 | @Inject(REQUEST) private _request: Request, 22 | @Inject(DOCUMENT) private _document: Document, 23 | @Inject(PLATFORM_ID) private _platform_ID: InjectionToken 24 | ) { 25 | this._isBrowser = isPlatformBrowser(this._platform_ID); 26 | } 27 | 28 | public init(): void { 29 | const defaultLang = this._getDefaultLanguage(); 30 | this._translate.setDefaultLang(defaultLang); 31 | 32 | const userLanguage = this.getLanguage(); 33 | this.setLanguage(userLanguage ? userLanguage : defaultLang, false); 34 | } 35 | 36 | public setLanguage(language: string, save: boolean): void { 37 | this._language = language; 38 | this._translate.use(language); 39 | this._document.documentElement.lang = language; 40 | 41 | if (globalThis) { 42 | globalThis.locale = language; 43 | } 44 | 45 | if (save) { 46 | this._storage.setItem('language', language); 47 | } 48 | } 49 | 50 | public getLanguage(): string { 51 | if (!this._language) { 52 | this._language = this._storage.getItem('language'); 53 | } 54 | return this._language; 55 | } 56 | 57 | private _getDefaultLanguage(): string { 58 | if (!this._isBrowser) { 59 | const serverLanguage = this._request?.acceptsLanguages()[0]?.toLowerCase() || ''; 60 | return serverLanguage?.includes('pt') ? 'pt-br' : 'en-us'; 61 | } else { 62 | const browserLanguage = navigator?.language?.toLowerCase() || ''; 63 | return browserLanguage?.includes('pt') ? 'pt-br' : 'en-us'; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/app/utils/domino.utils.ts: -------------------------------------------------------------------------------- 1 | import * as domino from 'domino'; 2 | import {PathLike, readFileSync} from 'fs'; 3 | 4 | export function applyDomino(global: any, templatePath: PathLike) { 5 | const template = readFileSync(templatePath).toString(); 6 | const win = domino.createWindow(template); 7 | 8 | // mock window // 9 | global['window'] = win; 10 | // mock document 11 | global['document'] = win.document; 12 | // mock navigator 13 | global['navigator'] = win.navigator; 14 | // not implemented property and functions 15 | Object.defineProperty(win.document.body.style, 'transform', createTransformOptions()); 16 | global['CSS'] = null; 17 | // global['XMLHttpRequest'] = require('xmlhttprequest').XMLHttpRequest; 18 | global['Prism'] = null; 19 | global['MutationObserver'] = getMockMutationObserver(); 20 | } 21 | 22 | function createTransformOptions() { 23 | const value = () => ({ 24 | enumerable: true, 25 | configurable: true, 26 | }); 27 | return {value}; 28 | } 29 | 30 | function getMockMutationObserver() { 31 | return {} as MutationObserver; 32 | } 33 | -------------------------------------------------------------------------------- /src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gesielrosa/angular-starter-app/d09f9b00291935b09780e2ddf42a60b390e6ebe7/src/assets/.gitkeep -------------------------------------------------------------------------------- /src/assets/i18n/en-us.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /src/assets/i18n/pt-br.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /src/environments/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gesielrosa/angular-starter-app/d09f9b00291935b09780e2ddf42a60b390e6ebe7/src/environments/.gitkeep -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gesielrosa/angular-starter-app/d09f9b00291935b09780e2ddf42a60b390e6ebe7/src/favicon.ico -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MyApp 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/main.browser.ts: -------------------------------------------------------------------------------- 1 | import {enableProdMode} from '@angular/core'; 2 | import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; 3 | 4 | import {AppBrowserModule} from '@app/app.browser.module'; 5 | import {environment} from '@env/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | function bootstrap() { 12 | platformBrowserDynamic() 13 | .bootstrapModule(AppBrowserModule) 14 | .catch((err) => console.error(err)); 15 | } 16 | 17 | if (document.readyState === 'complete') { 18 | bootstrap(); 19 | } else { 20 | document.addEventListener('DOMContentLoaded', bootstrap); 21 | } 22 | -------------------------------------------------------------------------------- /src/main.server.ts: -------------------------------------------------------------------------------- 1 | /*************************************************************************************************** 2 | * Initialize the server environment - for example, adding DOM built-in types to the global scope. 3 | * 4 | * NOTE: 5 | * This import must come before any imports (direct or transitive) that rely on DOM built-ins being 6 | * available, such as `@angular/elements`. 7 | */ 8 | import '@angular/platform-server/init'; 9 | 10 | import {enableProdMode} from '@angular/core'; 11 | 12 | import {environment} from '@env/environment'; 13 | 14 | if (environment.production) { 15 | enableProdMode(); 16 | } 17 | 18 | export {AppServerModule} from './app/app.server.module'; 19 | export {renderModule} from '@angular/platform-server'; 20 | -------------------------------------------------------------------------------- /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 recent versions of Safari, Chrome (including 12 | * Opera), Edge on the desktop, and iOS and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/guide/browser-support 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** 22 | * By default, zone.js will patch all possible macroTask and DomEvents 23 | * user can disable parts of macroTask/DomEvents patch by setting following flags 24 | * because those flags need to be set before `zone.js` being loaded, and webpack 25 | * will put import in the top of bundle, so user need to create a separate file 26 | * in this directory (for example: zone-flags.ts), and put the following flags 27 | * into that file, and then add the following code before importing zone.js. 28 | * import './zone-flags'; 29 | * 30 | * The flags allowed in zone-flags.ts are listed here. 31 | * 32 | * The following flags will work for all browsers. 33 | * 34 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 35 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 36 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 37 | * 38 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 39 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 40 | * 41 | * (window as any).__Zone_enable_cross_context_check = true; 42 | * 43 | */ 44 | 45 | /*************************************************************************************************** 46 | * Zone JS is required by default for Angular itself. 47 | */ 48 | import 'zone.js'; 49 | 50 | /*************************************************************************************************** 51 | * APPLICATION IMPORTS 52 | */ 53 | import 'globalthis/auto'; 54 | 55 | declare global { 56 | interface Window { 57 | locale: 'en-us' | 'pt-br'; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/styles.scss: -------------------------------------------------------------------------------- 1 | @import 'styles/variables'; 2 | -------------------------------------------------------------------------------- /src/styles/variables.scss: -------------------------------------------------------------------------------- 1 | :root { 2 | --primary: #b20000; 3 | } 4 | -------------------------------------------------------------------------------- /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/testing'; 4 | import {getTestBed} from '@angular/core/testing'; 5 | import {BrowserDynamicTestingModule, platformBrowserDynamicTesting} from '@angular/platform-browser-dynamic/testing'; 6 | 7 | declare const require: { 8 | context( 9 | path: string, 10 | deep?: boolean, 11 | filter?: RegExp 12 | ): { 13 | keys(): string[]; 14 | (id: string): T; 15 | }; 16 | }; 17 | 18 | // First, initialize the Angular testing environment. 19 | getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting()); 20 | 21 | // Then we find all the tests. 22 | const context = require.context('./', true, /\.spec\.ts$/); 23 | // And load the modules. 24 | context.keys().map(context); 25 | -------------------------------------------------------------------------------- /tsconfig.app.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/app", 6 | "types": [] 7 | }, 8 | "files": [ 9 | "src/main.browser.ts", 10 | "src/polyfills.ts" 11 | ], 12 | "include": [ 13 | "src/**/*.d.ts" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "compileOnSave": false, 4 | "compilerOptions": { 5 | "baseUrl": "./", 6 | "outDir": "./dist/out-tsc", 7 | "forceConsistentCasingInFileNames": true, 8 | "strict": true, 9 | "noImplicitOverride": true, 10 | "noPropertyAccessFromIndexSignature": true, 11 | "noImplicitReturns": true, 12 | "noFallthroughCasesInSwitch": true, 13 | "strictPropertyInitialization": false, 14 | "sourceMap": true, 15 | "declaration": false, 16 | "downlevelIteration": true, 17 | "experimentalDecorators": true, 18 | "moduleResolution": "node", 19 | "importHelpers": true, 20 | "noImplicitAny": false, 21 | "strictNullChecks": false, 22 | "lib": [ 23 | "es2020", 24 | "dom" 25 | ], 26 | "paths": { 27 | "@app/*": [ 28 | "src/app/*" 29 | ], 30 | "@components/*": [ 31 | "src/app/components/*" 32 | ], 33 | "@constants/*": [ 34 | "src/app/constants/*" 35 | ], 36 | "@models/*": [ 37 | "src/app/models/*" 38 | ], 39 | "@pages/*": [ 40 | "src/app/pages/*" 41 | ], 42 | "@pipes/*": [ 43 | "src/app/pipes/*" 44 | ], 45 | "@animations/*": [ 46 | "src/app/animations/*" 47 | ], 48 | "@services/*": [ 49 | "src/app/services/*" 50 | ], 51 | "@utils/*": [ 52 | "src/app/utils/*" 53 | ], 54 | "@modals/*": [ 55 | "src/app/modals/*" 56 | ], 57 | "@common/*": [ 58 | "src/app/common/*" 59 | ], 60 | "@controllers/*": [ 61 | "src/app/controllers/*" 62 | ], 63 | "@builders/*": [ 64 | "src/app/builders/*" 65 | ], 66 | "@guards/*": [ 67 | "src/app/guards/*" 68 | ], 69 | "@validators/*": [ 70 | "src/app/validators/*" 71 | ], 72 | "@env/*": [ 73 | "src/environments/*" 74 | ], 75 | "@assets/*": [ 76 | "src/assets/*" 77 | ], 78 | "@e2e/*": [ 79 | "e2e/*" 80 | ], 81 | "@interceptors/*": [ 82 | "src/app/interceptors/*" 83 | ], 84 | "@loaders/*": [ 85 | "src/app/loaders/*" 86 | ] 87 | } 88 | }, 89 | "angularCompilerOptions": { 90 | "enableI18nLegacyMessageIdFormat": false, 91 | "strictInjectionParameters": true, 92 | "strictInputAccessModifiers": true, 93 | "strictTemplates": true 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /tsconfig.server.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.app.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/server", 6 | "target": "es2019", 7 | "types": [ 8 | "node" 9 | ] 10 | }, 11 | "files": [ 12 | "src/main.server.ts", 13 | "server.ts" 14 | ], 15 | "angularCompilerOptions": { 16 | "entryModule": "./src/app/app.server.module#AppServerModule" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/spec", 6 | "types": [ 7 | "jasmine" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts", 12 | "src/polyfills.ts" 13 | ], 14 | "include": [ 15 | "src/**/*.spec.ts", 16 | "src/**/*.d.ts" 17 | ] 18 | } 19 | --------------------------------------------------------------------------------