├── apps
├── .gitkeep
├── runtime-config-loader-demo
│ ├── src
│ │ ├── assets
│ │ │ ├── .gitkeep
│ │ │ └── config
│ │ │ │ └── config.json
│ │ ├── environments
│ │ │ ├── environment.prod.ts
│ │ │ └── environment.ts
│ │ ├── styles.scss
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ ├── main.ts
│ │ ├── test-setup.ts
│ │ ├── app
│ │ │ ├── app.component.spec.ts
│ │ │ ├── app.component.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.component.scss
│ │ │ └── app.component.html
│ │ └── polyfills.ts
│ ├── tsconfig.editor.json
│ ├── tsconfig.spec.json
│ ├── tsconfig.app.json
│ ├── tsconfig.json
│ ├── jest.config.ts
│ ├── .eslintrc.json
│ └── project.json
└── runtime-config-loader-demo-e2e
│ ├── src
│ ├── support
│ │ ├── app.po.ts
│ │ ├── index.ts
│ │ └── commands.ts
│ ├── fixtures
│ │ └── example.json
│ └── integration
│ │ └── app.spec.ts
│ ├── .eslintrc.json
│ ├── cypress.json
│ ├── tsconfig.json
│ └── project.json
├── libs
├── .gitkeep
└── runtime-config-loader
│ ├── src
│ ├── index.ts
│ ├── lib
│ │ ├── runtime-config.ts
│ │ ├── runtime-config-loader.module.ts
│ │ └── runtime-config-loader
│ │ │ ├── runtime-config-loader.service.ts
│ │ │ └── runtime-config-loader.service.spec.ts
│ └── test-setup.ts
│ ├── ng-package.json
│ ├── tsconfig.spec.json
│ ├── tsconfig.lib.prod.json
│ ├── tsconfig.lib.json
│ ├── tsconfig.json
│ ├── jest.config.ts
│ ├── .eslintrc.json
│ ├── package.json
│ ├── project.json
│ └── README.md
├── .eslintignore
├── .prettierrc
├── .prettierignore
├── jest.config.ts
├── .vscode
└── extensions.json
├── .editorconfig
├── tools
└── tsconfig.tools.json
├── tsconfig.base.json
├── jest.preset.js
├── .gitignore
├── .eslintrc.json
├── nx.json
├── package.json
├── decorate-angular-cli.js
├── migrations.json
└── README.md
/apps/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/libs/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/apps/runtime-config-loader-demo/src/assets/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "useTabs": true,
4 | "tabWidth": 4
5 | }
6 |
--------------------------------------------------------------------------------
/apps/runtime-config-loader-demo-e2e/src/support/app.po.ts:
--------------------------------------------------------------------------------
1 | export const getGreeting = () => cy.get('h1');
2 |
--------------------------------------------------------------------------------
/apps/runtime-config-loader-demo/src/assets/config/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "testValue": "This is a test"
3 | }
4 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | # Add files here to ignore them from prettier formatting
2 |
3 | /dist
4 | /coverage
5 |
6 | /.nx/cache
--------------------------------------------------------------------------------
/apps/runtime-config-loader-demo/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true,
3 | };
4 |
--------------------------------------------------------------------------------
/apps/runtime-config-loader-demo/src/styles.scss:
--------------------------------------------------------------------------------
1 | /* You can add global styles to this file, and also import other style files */
2 |
--------------------------------------------------------------------------------
/jest.config.ts:
--------------------------------------------------------------------------------
1 | const { getJestProjects } = require('@nx/jest');
2 |
3 | export default {
4 | projects: getJestProjects(),
5 | };
6 |
--------------------------------------------------------------------------------
/apps/runtime-config-loader-demo-e2e/src/fixtures/example.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Using fixtures to represent data",
3 | "email": "hello@cypress.io"
4 | }
5 |
--------------------------------------------------------------------------------
/apps/runtime-config-loader-demo/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pjlamb12/runtime-config-loader/HEAD/apps/runtime-config-loader-demo/src/favicon.ico
--------------------------------------------------------------------------------
/libs/runtime-config-loader/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './lib/runtime-config-loader.module';
2 | export * from './lib/runtime-config';
3 | export * from './lib/runtime-config-loader/runtime-config-loader.service';
4 |
--------------------------------------------------------------------------------
/apps/runtime-config-loader-demo/tsconfig.editor.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "include": ["**/*.ts"],
4 | "compilerOptions": {
5 | "types": ["jest", "node"]
6 | },
7 | "exclude": ["jest.config.ts"]
8 | }
9 |
--------------------------------------------------------------------------------
/libs/runtime-config-loader/src/lib/runtime-config.ts:
--------------------------------------------------------------------------------
1 | export class RuntimeConfig {
2 | configUrl: string | string[];
3 |
4 | constructor(obj: any = {}) {
5 | this.configUrl = obj.configUrl || './assets/config.json';
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/libs/runtime-config-loader/ng-package.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
3 | "dest": "../../dist/libs/runtime-config-loader",
4 | "lib": {
5 | "entryFile": "src/index.ts"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "angular.ng-template",
4 | "nrwl.angular-console",
5 | "esbenp.prettier-vscode",
6 | "firsttris.vscode-jest-runner",
7 | "dbaeumer.vscode-eslint"
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = tab
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 |
--------------------------------------------------------------------------------
/tools/tsconfig.tools.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.base.json",
3 | "compilerOptions": {
4 | "outDir": "../dist/out-tsc/tools",
5 | "rootDir": ".",
6 | "module": "commonjs",
7 | "target": "es5",
8 | "types": ["node"],
9 | "importHelpers": false
10 | },
11 | "include": ["**/*.ts"]
12 | }
13 |
--------------------------------------------------------------------------------
/libs/runtime-config-loader/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": ["jest", "node"]
7 | },
8 | "files": ["src/test-setup.ts"],
9 | "include": ["**/*.spec.ts", "**/*.test.ts", "**/*.d.ts", "jest.config.ts"]
10 | }
11 |
--------------------------------------------------------------------------------
/apps/runtime-config-loader-demo/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": ["jest", "node"]
7 | },
8 | "files": ["src/test-setup.ts"],
9 | "include": ["**/*.spec.ts", "**/*.test.ts", "**/*.d.ts", "jest.config.ts"]
10 | }
11 |
--------------------------------------------------------------------------------
/libs/runtime-config-loader/tsconfig.lib.prod.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.lib.json",
3 | "compilerOptions": {
4 | "declarationMap": false,
5 | "target": "ES2022",
6 | "useDefineForClassFields": false
7 | },
8 | "angularCompilerOptions": {
9 | "compilationMode": "partial"
10 | },
11 | "exclude": ["jest.config.ts"]
12 | }
13 |
--------------------------------------------------------------------------------
/apps/runtime-config-loader-demo/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "types": [],
6 | "target": "ES2022",
7 | "useDefineForClassFields": false
8 | },
9 | "files": ["src/main.ts", "src/polyfills.ts"],
10 | "include": ["src/**/*.d.ts"],
11 | "exclude": ["jest.config.ts"]
12 | }
13 |
--------------------------------------------------------------------------------
/apps/runtime-config-loader-demo-e2e/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["plugin:cypress/recommended", "../../.eslintrc.json"],
3 | "ignorePatterns": ["!**/*"],
4 | "overrides": [
5 | {
6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
7 | "rules": {}
8 | },
9 | {
10 | "files": ["src/plugins/index.js"],
11 | "rules": {
12 | "@typescript-eslint/no-var-requires": "off",
13 | "no-undef": "off"
14 | }
15 | }
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/apps/runtime-config-loader-demo/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | RuntimeConfigLoaderDemo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/apps/runtime-config-loader-demo/src/main.ts:
--------------------------------------------------------------------------------
1 | import { enableProdMode } from '@angular/core';
2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
3 |
4 | import { AppModule } from './app/app.module';
5 | import { environment } from './environments/environment';
6 |
7 | if (environment.production) {
8 | enableProdMode();
9 | }
10 |
11 | platformBrowserDynamic()
12 | .bootstrapModule(AppModule)
13 | .catch((err) => console.error(err));
14 |
--------------------------------------------------------------------------------
/libs/runtime-config-loader/src/test-setup.ts:
--------------------------------------------------------------------------------
1 | import 'jest-preset-angular/setup-jest';
2 |
3 | import { getTestBed } from '@angular/core/testing';
4 | import {
5 | BrowserDynamicTestingModule,
6 | platformBrowserDynamicTesting,
7 | } from '@angular/platform-browser-dynamic/testing';
8 |
9 | getTestBed().resetTestEnvironment();
10 | getTestBed().initTestEnvironment(
11 | BrowserDynamicTestingModule,
12 | platformBrowserDynamicTesting(),
13 | { teardown: { destroyAfterEach: false } }
14 | );
15 |
--------------------------------------------------------------------------------
/apps/runtime-config-loader-demo/src/test-setup.ts:
--------------------------------------------------------------------------------
1 | import 'jest-preset-angular/setup-jest';
2 |
3 | import { getTestBed } from '@angular/core/testing';
4 | import {
5 | BrowserDynamicTestingModule,
6 | platformBrowserDynamicTesting,
7 | } from '@angular/platform-browser-dynamic/testing';
8 |
9 | getTestBed().resetTestEnvironment();
10 | getTestBed().initTestEnvironment(
11 | BrowserDynamicTestingModule,
12 | platformBrowserDynamicTesting(),
13 | { teardown: { destroyAfterEach: false } }
14 | );
15 |
--------------------------------------------------------------------------------
/libs/runtime-config-loader/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "target": "ES2022",
6 | "declaration": true,
7 | "declarationMap": true,
8 | "inlineSources": true,
9 | "types": [],
10 | "lib": ["dom", "es2018"],
11 | "useDefineForClassFields": false
12 | },
13 | "exclude": [
14 | "src/test-setup.ts",
15 | "**/*.spec.ts",
16 | "**/*.test.ts",
17 | "jest.config.ts"
18 | ],
19 | "include": ["**/*.ts"]
20 | }
21 |
--------------------------------------------------------------------------------
/apps/runtime-config-loader-demo-e2e/cypress.json:
--------------------------------------------------------------------------------
1 | {
2 | "fileServerFolder": ".",
3 | "fixturesFolder": "./src/fixtures",
4 | "integrationFolder": "./src/integration",
5 | "modifyObstructiveCode": false,
6 | "supportFile": "./src/support/index.ts",
7 | "pluginsFile": false,
8 | "video": true,
9 | "videosFolder": "../../dist/cypress/apps/runtime-config-loader-demo-e2e/videos",
10 | "screenshotsFolder": "../../dist/cypress/apps/runtime-config-loader-demo-e2e/screenshots",
11 | "chromeWebSecurity": false
12 | }
13 |
--------------------------------------------------------------------------------
/apps/runtime-config-loader-demo-e2e/src/integration/app.spec.ts:
--------------------------------------------------------------------------------
1 | import { getGreeting } from '../support/app.po';
2 |
3 | describe('runtime-config-loader-demo', () => {
4 | beforeEach(() => cy.visit('/'));
5 |
6 | it('should display welcome message', () => {
7 | // Custom command example, see `../support/commands.ts` file
8 | cy.login('my-email@something.com', 'myPassword');
9 |
10 | // Function helper example, see `../support/app.po.ts` file
11 | getGreeting().contains('Welcome to runtime-config-loader-demo!');
12 | });
13 | });
14 |
--------------------------------------------------------------------------------
/apps/runtime-config-loader-demo/src/app/app.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { RuntimeConfigLoaderService } from 'runtime-config-loader';
2 | import { AppComponent } from './app.component';
3 |
4 | describe('AppComponent', () => {
5 | let app: AppComponent;
6 | let mockRuntimeConfigService = {} as RuntimeConfigLoaderService;
7 |
8 | beforeEach(() => {
9 | app = new AppComponent(
10 | mockRuntimeConfigService as RuntimeConfigLoaderService
11 | );
12 | });
13 |
14 | it('should create the app', () => {
15 | expect(app).toBeTruthy();
16 | });
17 | });
18 |
--------------------------------------------------------------------------------
/apps/runtime-config-loader-demo/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { RuntimeConfigLoaderService } from 'runtime-config-loader';
3 |
4 | @Component({
5 | selector: 'runtime-config-loader-root',
6 | templateUrl: './app.component.html',
7 | styleUrls: ['./app.component.scss'],
8 | })
9 | export class AppComponent implements OnInit {
10 | title = 'runtime-config-loader-demo';
11 |
12 | constructor(private _config: RuntimeConfigLoaderService) {}
13 |
14 | ngOnInit() {
15 | console.log(this._config.getConfig());
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/apps/runtime-config-loader-demo/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { BrowserModule } from '@angular/platform-browser';
3 | import { RuntimeConfigLoaderModule } from 'runtime-config-loader';
4 |
5 | import { AppComponent } from './app.component';
6 |
7 | @NgModule({
8 | declarations: [AppComponent],
9 | imports: [
10 | BrowserModule,
11 | RuntimeConfigLoaderModule.forRoot({
12 | configUrl: './assets/config/config.json',
13 | }),
14 | ],
15 | providers: [],
16 | bootstrap: [AppComponent],
17 | })
18 | export class AppModule {}
19 |
--------------------------------------------------------------------------------
/tsconfig.base.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "rootDir": ".",
5 | "sourceMap": true,
6 | "declaration": false,
7 | "moduleResolution": "node",
8 | "emitDecoratorMetadata": true,
9 | "experimentalDecorators": true,
10 | "importHelpers": true,
11 | "target": "es2015",
12 | "module": "esnext",
13 | "lib": ["es2017", "dom"],
14 | "skipLibCheck": true,
15 | "skipDefaultLibCheck": true,
16 | "baseUrl": ".",
17 | "paths": {
18 | "runtime-config-loader": ["libs/runtime-config-loader/src/index.ts"]
19 | }
20 | },
21 | "exclude": ["node_modules", "tmp"]
22 | }
23 |
--------------------------------------------------------------------------------
/apps/runtime-config-loader-demo-e2e/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "sourceMap": false,
5 | "outDir": "../../dist/out-tsc",
6 | "allowJs": true,
7 | "types": ["cypress", "node"],
8 | "forceConsistentCasingInFileNames": true,
9 | "strict": true,
10 | "noImplicitReturns": true,
11 | "noFallthroughCasesInSwitch": true
12 | },
13 | "include": ["src/**/*.ts", "src/**/*.js"],
14 | "angularCompilerOptions": {
15 | "strictInjectionParameters": true,
16 | "strictInputAccessModifiers": true,
17 | "strictTemplates": true
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/libs/runtime-config-loader/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "files": [],
4 | "include": [],
5 | "references": [
6 | {
7 | "path": "./tsconfig.lib.json"
8 | },
9 | {
10 | "path": "./tsconfig.lib.prod.json"
11 | },
12 | {
13 | "path": "./tsconfig.spec.json"
14 | }
15 | ],
16 | "compilerOptions": {
17 | "forceConsistentCasingInFileNames": true,
18 | "strict": true,
19 | "noImplicitReturns": true,
20 | "noFallthroughCasesInSwitch": true,
21 | "target": "es2020"
22 | },
23 | "angularCompilerOptions": {
24 | "strictInjectionParameters": true,
25 | "strictInputAccessModifiers": true,
26 | "strictTemplates": true
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/apps/runtime-config-loader-demo/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "files": [],
4 | "include": [],
5 | "references": [
6 | {
7 | "path": "./tsconfig.app.json"
8 | },
9 | {
10 | "path": "./tsconfig.spec.json"
11 | },
12 | {
13 | "path": "./tsconfig.editor.json"
14 | }
15 | ],
16 | "compilerOptions": {
17 | "forceConsistentCasingInFileNames": true,
18 | "strict": true,
19 | "noImplicitReturns": true,
20 | "noFallthroughCasesInSwitch": true,
21 | "target": "es2020"
22 | },
23 | "angularCompilerOptions": {
24 | "strictInjectionParameters": true,
25 | "strictInputAccessModifiers": true,
26 | "strictTemplates": true
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/jest.preset.js:
--------------------------------------------------------------------------------
1 | const nxPreset = require('@nx/jest/preset').default;
2 |
3 | module.exports = {
4 | ...nxPreset,
5 | /* TODO: Update to latest Jest snapshotFormat
6 | * By default Nx has kept the older style of Jest Snapshot formats
7 | * to prevent breaking of any existing tests with snapshots.
8 | * It's recommend you update to the latest format.
9 | * You can do this by removing snapshotFormat property
10 | * and running tests with --update-snapshot flag.
11 | * Example: "nx affected --targets=test --update-snapshot"
12 | * More info: https://jestjs.io/docs/upgrading-to-jest29#snapshot-format
13 | */
14 | snapshotFormat: { escapeString: true, printBasicPrototype: true },
15 | };
16 |
--------------------------------------------------------------------------------
/apps/runtime-config-loader-demo-e2e/src/support/index.ts:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example support/index.js is processed and
3 | // loaded automatically before your test files.
4 | //
5 | // This is a great place to put global configuration and
6 | // behavior that modifies Cypress.
7 | //
8 | // You can change the location of this file or turn off
9 | // automatically serving support files with the
10 | // 'supportFile' configuration option.
11 | //
12 | // You can read more here:
13 | // https://on.cypress.io/configuration
14 | // ***********************************************************
15 |
16 | // Import commands.js using ES2015 syntax:
17 | import './commands';
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | /dist
5 | /tmp
6 | /out-tsc
7 |
8 | # dependencies
9 | /node_modules
10 |
11 | # IDEs and editors
12 | /.idea
13 | .project
14 | .classpath
15 | .c9/
16 | *.launch
17 | .settings/
18 | *.sublime-workspace
19 |
20 | # IDE - VSCode
21 | .vscode/*
22 | !.vscode/settings.json
23 | !.vscode/tasks.json
24 | !.vscode/launch.json
25 | !.vscode/extensions.json
26 |
27 | # misc
28 | /.angular/cache
29 | /.sass-cache
30 | /connect.lock
31 | /coverage
32 | /libpeerconnection.log
33 | npm-debug.log
34 | yarn-error.log
35 | testem.log
36 | /typings
37 |
38 | # System Files
39 | .DS_Store
40 | Thumbs.db
41 |
42 | .nx/cache
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "ignorePatterns": ["**/*"],
4 | "plugins": ["@nx"],
5 | "overrides": [
6 | {
7 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
8 | "rules": {
9 | "@nx/enforce-module-boundaries": [
10 | "error",
11 | {
12 | "enforceBuildableLibDependency": true,
13 | "allow": [],
14 | "depConstraints": [
15 | {
16 | "sourceTag": "*",
17 | "onlyDependOnLibsWithTags": ["*"]
18 | }
19 | ]
20 | }
21 | ]
22 | }
23 | },
24 | {
25 | "files": ["*.ts", "*.tsx"],
26 | "extends": ["plugin:@nx/typescript"],
27 | "rules": {}
28 | },
29 | {
30 | "files": ["*.js", "*.jsx"],
31 | "extends": ["plugin:@nx/javascript"],
32 | "rules": {}
33 | }
34 | ]
35 | }
36 |
--------------------------------------------------------------------------------
/apps/runtime-config-loader-demo/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // This file can be replaced during build by using the `fileReplacements` array.
2 | // `ng build` replaces `environment.ts` with `environment.prod.ts`.
3 | // The list of file replacements can be found in `angular.json`.
4 |
5 | export const environment = {
6 | production: false,
7 | };
8 |
9 | /*
10 | * For easier debugging in development mode, you can import the following file
11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
12 | *
13 | * This import should be commented out in production mode because it will have a negative impact
14 | * on performance if an error is thrown.
15 | */
16 | // import 'zone.js/plugins/zone-error'; // Included with Angular CLI.
17 |
--------------------------------------------------------------------------------
/libs/runtime-config-loader/jest.config.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | export default {
3 | displayName: 'runtime-config-loader',
4 |
5 | setupFilesAfterEnv: ['/src/test-setup.ts'],
6 | globals: {},
7 | coverageDirectory: '../../coverage/libs/runtime-config-loader',
8 | transform: {
9 | '^.+.(ts|mjs|js|html)$': [
10 | 'jest-preset-angular',
11 | {
12 | tsconfig: '/tsconfig.spec.json',
13 | stringifyContentPathRegex: '\\.(html|svg)$',
14 | },
15 | ],
16 | },
17 | transformIgnorePatterns: ['node_modules/(?!.*.mjs$)'],
18 | snapshotSerializers: [
19 | 'jest-preset-angular/build/serializers/no-ng-attributes',
20 | 'jest-preset-angular/build/serializers/ng-snapshot',
21 | 'jest-preset-angular/build/serializers/html-comment',
22 | ],
23 | preset: '../../jest.preset.js',
24 | };
25 |
--------------------------------------------------------------------------------
/apps/runtime-config-loader-demo/jest.config.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | export default {
3 | displayName: 'runtime-config-loader-demo',
4 |
5 | setupFilesAfterEnv: ['/src/test-setup.ts'],
6 | globals: {},
7 | coverageDirectory: '../../coverage/apps/runtime-config-loader-demo',
8 | transform: {
9 | '^.+.(ts|mjs|js|html)$': [
10 | 'jest-preset-angular',
11 | {
12 | tsconfig: '/tsconfig.spec.json',
13 | stringifyContentPathRegex: '\\.(html|svg)$',
14 | },
15 | ],
16 | },
17 | transformIgnorePatterns: ['node_modules/(?!.*.mjs$)'],
18 | snapshotSerializers: [
19 | 'jest-preset-angular/build/serializers/no-ng-attributes',
20 | 'jest-preset-angular/build/serializers/ng-snapshot',
21 | 'jest-preset-angular/build/serializers/html-comment',
22 | ],
23 | preset: '../../jest.preset.js',
24 | };
25 |
--------------------------------------------------------------------------------
/libs/runtime-config-loader/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["../../.eslintrc.json"],
3 | "ignorePatterns": ["!**/*"],
4 | "overrides": [
5 | {
6 | "files": ["*.ts"],
7 | "extends": [
8 | "plugin:@nx/angular",
9 | "plugin:@angular-eslint/template/process-inline-templates"
10 | ],
11 | "rules": {
12 | "@angular-eslint/directive-selector": [
13 | "error",
14 | {
15 | "type": "attribute",
16 | "prefix": "runtimeConfigLoader",
17 | "style": "camelCase"
18 | }
19 | ],
20 | "@angular-eslint/component-selector": [
21 | "error",
22 | {
23 | "type": "element",
24 | "prefix": "runtime-config-loader",
25 | "style": "kebab-case"
26 | }
27 | ]
28 | }
29 | },
30 | {
31 | "files": ["*.html"],
32 | "extends": ["plugin:@nx/angular-template"],
33 | "rules": {}
34 | }
35 | ]
36 | }
37 |
--------------------------------------------------------------------------------
/apps/runtime-config-loader-demo/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["../../.eslintrc.json"],
3 | "ignorePatterns": ["!**/*"],
4 | "overrides": [
5 | {
6 | "files": ["*.ts"],
7 | "extends": [
8 | "plugin:@nx/angular",
9 | "plugin:@angular-eslint/template/process-inline-templates"
10 | ],
11 | "rules": {
12 | "@angular-eslint/directive-selector": [
13 | "error",
14 | {
15 | "type": "attribute",
16 | "prefix": "runtimeConfigLoader",
17 | "style": "camelCase"
18 | }
19 | ],
20 | "@angular-eslint/component-selector": [
21 | "error",
22 | {
23 | "type": "element",
24 | "prefix": "runtime-config-loader",
25 | "style": "kebab-case"
26 | }
27 | ]
28 | }
29 | },
30 | {
31 | "files": ["*.html"],
32 | "extends": ["plugin:@nx/angular-template"],
33 | "rules": {}
34 | }
35 | ]
36 | }
37 |
--------------------------------------------------------------------------------
/libs/runtime-config-loader/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "runtime-config-loader",
3 | "version": "5.0.2",
4 | "author": {
5 | "email": "preston.j.lamb@gmail.com",
6 | "name": "Preston Lamb",
7 | "url": "http://www.prestonlamb.com"
8 | },
9 | "homepage": "https://stackblitz.com/edit/runtime-config-loader-demo",
10 | "maintainers": [
11 | "Preston Lamb"
12 | ],
13 | "repository": {
14 | "type": "git",
15 | "url": "https://github.com/pjlamb12/runtime-config-loader"
16 | },
17 | "license": "MIT",
18 | "keywords": [
19 | "angular",
20 | "configuration",
21 | "runtime configuration",
22 | "runtime",
23 | "config"
24 | ],
25 | "peerDependencies": {
26 | "@angular/common": ">=13.0.0",
27 | "@angular/core": ">=13.0.0",
28 | "@angular/platform-browser-dynamic": ">=13.0.0",
29 | "rxjs": ">=6.6.0"
30 | },
31 | "dependencies": {
32 | "tslib": "^2.3.0"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/apps/runtime-config-loader-demo-e2e/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "runtime-config-loader-demo-e2e",
3 | "$schema": "../../node_modules/nx/schemas/project-schema.json",
4 | "sourceRoot": "apps/runtime-config-loader-demo-e2e/src",
5 | "projectType": "application",
6 | "targets": {
7 | "e2e": {
8 | "executor": "@nx/cypress:cypress",
9 | "options": {
10 | "cypressConfig": "apps/runtime-config-loader-demo-e2e/cypress.json",
11 | "devServerTarget": "runtime-config-loader-demo:serve:development",
12 | "tsConfig": "apps/runtime-config-loader-demo-e2e/tsconfig.json"
13 | },
14 | "configurations": {
15 | "production": {
16 | "devServerTarget": "runtime-config-loader-demo:serve:production"
17 | }
18 | }
19 | },
20 | "lint": {
21 | "executor": "@nx/eslint:lint",
22 | "outputs": ["{options.outputFile}"],
23 | "options": {
24 | "lintFilePatterns": [
25 | "apps/runtime-config-loader-demo-e2e/**/*.{js,ts}"
26 | ]
27 | }
28 | }
29 | },
30 | "tags": [],
31 | "implicitDependencies": ["runtime-config-loader-demo"]
32 | }
33 |
--------------------------------------------------------------------------------
/libs/runtime-config-loader/src/lib/runtime-config-loader.module.ts:
--------------------------------------------------------------------------------
1 | import { HttpClientModule } from '@angular/common/http';
2 | import { APP_INITIALIZER, ModuleWithProviders, NgModule } from '@angular/core';
3 | import { RuntimeConfig } from './runtime-config';
4 | import { RuntimeConfigLoaderService } from './runtime-config-loader/runtime-config-loader.service';
5 |
6 | export function initConfig(configSvc: RuntimeConfigLoaderService) {
7 | return () => configSvc.loadConfig();
8 | }
9 |
10 | @NgModule({
11 | imports: [HttpClientModule],
12 | providers: [
13 | RuntimeConfigLoaderService,
14 | {
15 | provide: APP_INITIALIZER,
16 | useFactory: initConfig,
17 | deps: [RuntimeConfigLoaderService],
18 | multi: true,
19 | },
20 | ],
21 | })
22 | export class RuntimeConfigLoaderModule {
23 | static forRoot(
24 | config: RuntimeConfig
25 | ): ModuleWithProviders {
26 | return {
27 | ngModule: RuntimeConfigLoaderModule,
28 | providers: [
29 | {
30 | provide: RuntimeConfig,
31 | useValue: config,
32 | },
33 | RuntimeConfigLoaderService,
34 | ],
35 | };
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/apps/runtime-config-loader-demo-e2e/src/support/commands.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 | // eslint-disable-next-line @typescript-eslint/no-namespace
12 | declare namespace Cypress {
13 | // eslint-disable-next-line @typescript-eslint/no-unused-vars
14 | interface Chainable {
15 | login(email: string, password: string): void;
16 | }
17 | }
18 | //
19 | // -- This is a parent command --
20 | Cypress.Commands.add('login', (email, password) => {
21 | console.log('Custom command example: Login', email, password);
22 | });
23 | //
24 | // -- This is a child command --
25 | // Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
26 | //
27 | //
28 | // -- This is a dual command --
29 | // Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
30 | //
31 | //
32 | // -- This will overwrite an existing command --
33 | // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
34 |
--------------------------------------------------------------------------------
/libs/runtime-config-loader/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "runtime-config-loader",
3 | "$schema": "../../node_modules/nx/schemas/project-schema.json",
4 | "projectType": "library",
5 | "sourceRoot": "libs/runtime-config-loader/src",
6 | "prefix": "runtime-config-loader",
7 | "targets": {
8 | "build": {
9 | "executor": "@nx/angular:package",
10 | "outputs": ["{workspaceRoot}/dist/libs/runtime-config-loader"],
11 | "options": {
12 | "project": "libs/runtime-config-loader/ng-package.json"
13 | },
14 | "configurations": {
15 | "production": {
16 | "tsConfig": "libs/runtime-config-loader/tsconfig.lib.prod.json"
17 | },
18 | "development": {
19 | "tsConfig": "libs/runtime-config-loader/tsconfig.lib.json"
20 | }
21 | },
22 | "defaultConfiguration": "production"
23 | },
24 | "test": {
25 | "executor": "@nx/jest:jest",
26 | "outputs": ["{workspaceRoot}/coverage/libs/runtime-config-loader"],
27 | "options": {
28 | "jestConfig": "libs/runtime-config-loader/jest.config.ts"
29 | }
30 | },
31 | "lint": {
32 | "executor": "@nx/eslint:lint",
33 | "options": {
34 | "lintFilePatterns": [
35 | "libs/runtime-config-loader/src/**/*.ts",
36 | "libs/runtime-config-loader/src/**/*.html"
37 | ]
38 | }
39 | }
40 | },
41 | "tags": ["scope:shared", "type:util"]
42 | }
43 |
--------------------------------------------------------------------------------
/nx.json:
--------------------------------------------------------------------------------
1 | {
2 | "affected": {
3 | "defaultBase": "main"
4 | },
5 | "implicitDependencies": {
6 | "package.json": {
7 | "dependencies": "*",
8 | "devDependencies": "*"
9 | },
10 | ".eslintrc.json": "*"
11 | },
12 | "cli": {
13 | "packageManager": "npm"
14 | },
15 | "defaultProject": "runtime-config-loader-demo",
16 | "generators": {
17 | "@nx/angular:application": {
18 | "style": "scss",
19 | "linter": "eslint",
20 | "unitTestRunner": "jest",
21 | "e2eTestRunner": "cypress"
22 | },
23 | "@nx/angular:library": {
24 | "linter": "eslint",
25 | "unitTestRunner": "jest"
26 | },
27 | "@nx/angular:component": {
28 | "style": "scss"
29 | },
30 | "@nx/angular": {
31 | "application": {
32 | "linter": "eslint"
33 | },
34 | "library": {
35 | "linter": "eslint"
36 | },
37 | "storybook-configuration": {
38 | "linter": "eslint"
39 | }
40 | }
41 | },
42 | "$schema": "./node_modules/nx/schemas/nx-schema.json",
43 | "targetDefaults": {
44 | "build": {
45 | "dependsOn": ["^build"],
46 | "cache": true
47 | },
48 | "e2e": {
49 | "inputs": ["default", "^default"],
50 | "cache": true
51 | },
52 | "lint": {
53 | "inputs": ["default", "{workspaceRoot}/.eslintrc.json"],
54 | "cache": true
55 | },
56 | "@nx/jest:jest": {
57 | "inputs": ["default", "^default", "{workspaceRoot}/jest.preset.js"],
58 | "cache": true,
59 | "options": {
60 | "passWithNoTests": true
61 | },
62 | "configurations": {
63 | "ci": {
64 | "ci": true,
65 | "codeCoverage": true
66 | }
67 | }
68 | }
69 | },
70 | "nxCloudAccessToken": "M2EyMjNlOWUtNjk1MC00Nzc1LWEyZWYtZWFiNDQ0ZjliNzY0fHJlYWQtd3JpdGU=",
71 | "parallel": 1
72 | }
73 |
--------------------------------------------------------------------------------
/libs/runtime-config-loader/src/lib/runtime-config-loader/runtime-config-loader.service.ts:
--------------------------------------------------------------------------------
1 | import { HttpClient } from '@angular/common/http';
2 | import { Injectable, Optional } from '@angular/core';
3 | import { RuntimeConfig } from '../runtime-config';
4 | import { forkJoin, Observable, of, Subject, zip } from 'rxjs';
5 | import { catchError, take, tap } from 'rxjs/operators';
6 |
7 | @Injectable()
8 | export class RuntimeConfigLoaderService {
9 | private configUrl: string | string[] = './assets/config.json';
10 | private configObject: any = null;
11 | public configSubject: Subject = new Subject();
12 |
13 | constructor(private _http: HttpClient, @Optional() config: RuntimeConfig) {
14 | if (config) {
15 | this.configUrl = config.configUrl;
16 | }
17 | }
18 |
19 | loadConfig(): Observable {
20 | const urls: string[] = Array.isArray(this.configUrl)
21 | ? this.configUrl
22 | : [this.configUrl];
23 |
24 | const observables: Observable[] = urls.map((url) =>
25 | this.makeHttpCall(url)
26 | );
27 |
28 | return forkJoin(observables).pipe(
29 | tap((configDataArray: any[]) => {
30 | this.configObject = configDataArray.reduce(
31 | (acc, configData) => {
32 | return { ...acc, ...configData };
33 | },
34 | {}
35 | );
36 |
37 | this.configSubject.next(this.configObject);
38 | }),
39 | catchError((err: any) => {
40 | console.error('Error loading config: ', err);
41 | this.configObject = null;
42 | this.configSubject.next(this.configObject);
43 | return of(null);
44 | })
45 | );
46 | }
47 |
48 | private makeHttpCall(url: string): Observable {
49 | return this._http.get(url).pipe(take(1));
50 | }
51 |
52 | getConfig() {
53 | return this.configObject;
54 | }
55 |
56 | getConfigObjectKey(key: string) {
57 | return this.configObject ? this.configObject[key] : null;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "runtime-config-loader",
3 | "version": "0.0.0",
4 | "license": "MIT",
5 | "scripts": {
6 | "ng": "nx",
7 | "postinstall": "node ./decorate-angular-cli.js",
8 | "start": "nx serve",
9 | "build": "nx build",
10 | "test": "nx test",
11 | "affected:test": "nx affected --target test --parallel --maxParallel 3",
12 | "build:lib": "ng build runtime-config-loader --configuration production && npm run build:copy-readme",
13 | "build:copy-readme": "cp ./README.md ./dist/libs/runtime-config-loader"
14 | },
15 | "private": true,
16 | "dependencies": {
17 | "@angular/animations": "17.0.2",
18 | "@angular/common": "17.0.2",
19 | "@angular/compiler": "17.0.2",
20 | "@angular/core": "17.0.2",
21 | "@angular/forms": "17.0.2",
22 | "@angular/platform-browser": "17.0.2",
23 | "@angular/platform-browser-dynamic": "17.0.2",
24 | "@angular/router": "17.0.2",
25 | "rxjs": "~6.6.0",
26 | "tslib": "^2.0.0",
27 | "zone.js": "0.14.2",
28 | "@nx/angular": "17.1.2"
29 | },
30 | "devDependencies": {
31 | "@angular-devkit/build-angular": "17.0.0",
32 | "@angular-devkit/core": "17.0.0",
33 | "@angular-devkit/schematics": "17.0.0",
34 | "@angular-eslint/eslint-plugin": "17.0.1",
35 | "@angular-eslint/eslint-plugin-template": "17.0.1",
36 | "@angular-eslint/template-parser": "17.0.1",
37 | "@angular/cli": "~17.0.0",
38 | "@angular/compiler-cli": "17.0.2",
39 | "@angular/language-service": "17.0.2",
40 | "@nx/cypress": "17.1.2",
41 | "@nx/eslint-plugin": "17.1.2",
42 | "@nx/jest": "17.1.2",
43 | "@nx/workspace": "17.1.2",
44 | "@schematics/angular": "17.0.0",
45 | "@types/jest": "29.4.4",
46 | "@types/node": "14.14.33",
47 | "@typescript-eslint/eslint-plugin": "6.11.0",
48 | "@typescript-eslint/parser": "6.11.0",
49 | "cypress": "^8.3.0",
50 | "eslint": "8.46.0",
51 | "eslint-config-prettier": "9.0.0",
52 | "eslint-plugin-cypress": "2.15.1",
53 | "jest": "29.4.3",
54 | "jest-environment-jsdom": "29.4.3",
55 | "jest-preset-angular": "13.1.4",
56 | "ng-packagr": "17.0.0",
57 | "nx": "17.1.2",
58 | "postcss": "^8.3.9",
59 | "postcss-import": "14.1.0",
60 | "postcss-preset-env": "7.5.0",
61 | "postcss-url": "10.1.3",
62 | "prettier": "2.7.1",
63 | "ts-jest": "29.1.0",
64 | "ts-node": "10.9.1",
65 | "typescript": "5.2.2",
66 | "@nx/eslint": "17.1.2"
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/apps/runtime-config-loader-demo/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This file includes polyfills needed by Angular and is loaded before the app.
3 | * You can add your own extra polyfills to this file.
4 | *
5 | * This file is divided into 2 sections:
6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
8 | * file.
9 | *
10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
13 | *
14 | * Learn more in https://angular.io/guide/browser-support
15 | */
16 |
17 | /***************************************************************************************************
18 | * BROWSER POLYFILLS
19 | */
20 |
21 | /**
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'; // Included with Angular CLI.
49 |
50 | /***************************************************************************************************
51 | * APPLICATION IMPORTS
52 | */
53 |
--------------------------------------------------------------------------------
/apps/runtime-config-loader-demo/src/app/app.component.scss:
--------------------------------------------------------------------------------
1 | /*
2 | * Remove template code below
3 | */
4 | :host {
5 | display: block;
6 | font-family: sans-serif;
7 | min-width: 300px;
8 | max-width: 600px;
9 | margin: 50px auto;
10 | }
11 |
12 | .gutter-left {
13 | margin-left: 9px;
14 | }
15 |
16 | .col-span-2 {
17 | grid-column: span 2;
18 | }
19 |
20 | .flex {
21 | display: flex;
22 | align-items: center;
23 | justify-content: center;
24 | }
25 |
26 | header {
27 | background-color: #143055;
28 | color: white;
29 | padding: 5px;
30 | border-radius: 3px;
31 | }
32 |
33 | main {
34 | padding: 0 36px;
35 | }
36 |
37 | p {
38 | text-align: center;
39 | }
40 |
41 | h1 {
42 | text-align: center;
43 | margin-left: 18px;
44 | font-size: 24px;
45 | }
46 |
47 | h2 {
48 | text-align: center;
49 | font-size: 20px;
50 | margin: 40px 0 10px 0;
51 | }
52 |
53 | .resources {
54 | text-align: center;
55 | list-style: none;
56 | padding: 0;
57 | display: grid;
58 | grid-gap: 9px;
59 | grid-template-columns: 1fr 1fr;
60 | }
61 |
62 | .resource {
63 | color: #0094ba;
64 | height: 36px;
65 | background-color: rgba(0, 0, 0, 0);
66 | border: 1px solid rgba(0, 0, 0, 0.12);
67 | border-radius: 4px;
68 | padding: 3px 9px;
69 | text-decoration: none;
70 | }
71 |
72 | .resource:hover {
73 | background-color: rgba(68, 138, 255, 0.04);
74 | }
75 |
76 | pre {
77 | padding: 9px;
78 | border-radius: 4px;
79 | background-color: black;
80 | color: #eee;
81 | }
82 |
83 | details {
84 | border-radius: 4px;
85 | color: #333;
86 | background-color: rgba(0, 0, 0, 0);
87 | border: 1px solid rgba(0, 0, 0, 0.12);
88 | padding: 3px 9px;
89 | margin-bottom: 9px;
90 | }
91 |
92 | summary {
93 | cursor: pointer;
94 | outline: none;
95 | height: 36px;
96 | line-height: 36px;
97 | }
98 |
99 | .github-star-container {
100 | margin-top: 12px;
101 | line-height: 20px;
102 | }
103 |
104 | .github-star-container a {
105 | display: flex;
106 | align-items: center;
107 | text-decoration: none;
108 | color: #333;
109 | }
110 |
111 | .github-star-badge {
112 | color: #24292e;
113 | display: flex;
114 | align-items: center;
115 | font-size: 12px;
116 | padding: 3px 10px;
117 | border: 1px solid rgba(27, 31, 35, 0.2);
118 | border-radius: 3px;
119 | background-image: linear-gradient(-180deg, #fafbfc, #eff3f6 90%);
120 | margin-left: 4px;
121 | font-weight: 600;
122 | }
123 |
124 | .github-star-badge:hover {
125 | background-image: linear-gradient(-180deg, #f0f3f6, #e6ebf1 90%);
126 | border-color: rgba(27, 31, 35, 0.35);
127 | background-position: -0.5em;
128 | }
129 | .github-star-badge .material-icons {
130 | height: 16px;
131 | width: 16px;
132 | margin-right: 4px;
133 | }
134 |
--------------------------------------------------------------------------------
/decorate-angular-cli.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This file decorates the Angular CLI with the Nx CLI to enable features such as computation caching
3 | * and faster execution of tasks.
4 | *
5 | * It does this by:
6 | *
7 | * - Patching the Angular CLI to warn you in case you accidentally use the undecorated ng command.
8 | * - Symlinking the ng to nx command, so all commands run through the Nx CLI
9 | * - Updating the package.json postinstall script to give you control over this script
10 | *
11 | * The Nx CLI decorates the Angular CLI, so the Nx CLI is fully compatible with it.
12 | * Every command you run should work the same when using the Nx CLI, except faster.
13 | *
14 | * Because of symlinking you can still type `ng build/test/lint` in the terminal. The ng command, in this case,
15 | * will point to nx, which will perform optimizations before invoking ng. So the Angular CLI is always invoked.
16 | * The Nx CLI simply does some optimizations before invoking the Angular CLI.
17 | *
18 | * To opt out of this patch:
19 | * - Replace occurrences of nx with ng in your package.json
20 | * - Remove the script from your postinstall script in your package.json
21 | * - Delete and reinstall your node_modules
22 | */
23 |
24 | const fs = require('fs');
25 | const os = require('os');
26 | const cp = require('child_process');
27 | const isWindows = os.platform() === 'win32';
28 | let output;
29 | try {
30 | output = require('@nx/workspace').output;
31 | } catch (e) {
32 | console.warn(
33 | 'Angular CLI could not be decorated to enable computation caching. Please ensure @nx/workspace is installed.'
34 | );
35 | process.exit(0);
36 | }
37 |
38 | /**
39 | * Symlink of ng to nx, so you can keep using `ng build/test/lint` and still
40 | * invoke the Nx CLI and get the benefits of computation caching.
41 | */
42 | function symlinkNgCLItoNxCLI() {
43 | try {
44 | const ngPath = './node_modules/.bin/ng';
45 | const nxPath = './node_modules/.bin/nx';
46 | if (isWindows) {
47 | /**
48 | * This is the most reliable way to create symlink-like behavior on Windows.
49 | * Such that it works in all shells and works with npx.
50 | */
51 | ['', '.cmd', '.ps1'].forEach((ext) => {
52 | if (fs.existsSync(nxPath + ext))
53 | fs.writeFileSync(
54 | ngPath + ext,
55 | fs.readFileSync(nxPath + ext)
56 | );
57 | });
58 | } else {
59 | // If unix-based, symlink
60 | cp.execSync(`ln -sf ./nx ${ngPath}`);
61 | }
62 | } catch (e) {
63 | output.error({
64 | title:
65 | 'Unable to create a symlink from the Angular CLI to the Nx CLI:' +
66 | e.message,
67 | });
68 | throw e;
69 | }
70 | }
71 |
72 | try {
73 | symlinkNgCLItoNxCLI();
74 | require('@nrwl/cli/lib/decorate-cli').decorateCli();
75 | output.log({
76 | title: 'Angular CLI has been decorated to enable computation caching.',
77 | });
78 | } catch (e) {
79 | output.error({
80 | title: 'Decoration of the Angular CLI did not complete successfully',
81 | });
82 | }
83 |
--------------------------------------------------------------------------------
/apps/runtime-config-loader-demo/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "runtime-config-loader-demo",
3 | "$schema": "../../node_modules/nx/schemas/project-schema.json",
4 | "projectType": "application",
5 | "sourceRoot": "apps/runtime-config-loader-demo/src",
6 | "prefix": "runtime-config-loader",
7 | "targets": {
8 | "build": {
9 | "executor": "@angular-devkit/build-angular:browser",
10 | "outputs": ["{options.outputPath}"],
11 | "options": {
12 | "outputPath": "dist/apps/runtime-config-loader-demo",
13 | "index": "apps/runtime-config-loader-demo/src/index.html",
14 | "main": "apps/runtime-config-loader-demo/src/main.ts",
15 | "polyfills": "apps/runtime-config-loader-demo/src/polyfills.ts",
16 | "tsConfig": "apps/runtime-config-loader-demo/tsconfig.app.json",
17 | "inlineStyleLanguage": "scss",
18 | "assets": [
19 | "apps/runtime-config-loader-demo/src/favicon.ico",
20 | "apps/runtime-config-loader-demo/src/assets"
21 | ],
22 | "styles": ["apps/runtime-config-loader-demo/src/styles.scss"],
23 | "scripts": []
24 | },
25 | "configurations": {
26 | "production": {
27 | "budgets": [
28 | {
29 | "type": "initial",
30 | "maximumWarning": "500kb",
31 | "maximumError": "1mb"
32 | },
33 | {
34 | "type": "anyComponentStyle",
35 | "maximumWarning": "2kb",
36 | "maximumError": "4kb"
37 | }
38 | ],
39 | "fileReplacements": [
40 | {
41 | "replace": "apps/runtime-config-loader-demo/src/environments/environment.ts",
42 | "with": "apps/runtime-config-loader-demo/src/environments/environment.prod.ts"
43 | }
44 | ],
45 | "outputHashing": "all"
46 | },
47 | "development": {
48 | "buildOptimizer": false,
49 | "optimization": false,
50 | "vendorChunk": true,
51 | "extractLicenses": false,
52 | "sourceMap": true,
53 | "namedChunks": true
54 | }
55 | },
56 | "defaultConfiguration": "production"
57 | },
58 | "serve": {
59 | "executor": "@angular-devkit/build-angular:dev-server",
60 | "configurations": {
61 | "production": {
62 | "buildTarget": "runtime-config-loader-demo:build:production"
63 | },
64 | "development": {
65 | "buildTarget": "runtime-config-loader-demo:build:development"
66 | }
67 | },
68 | "defaultConfiguration": "development"
69 | },
70 | "extract-i18n": {
71 | "executor": "@angular-devkit/build-angular:extract-i18n",
72 | "options": {
73 | "buildTarget": "runtime-config-loader-demo:build"
74 | }
75 | },
76 | "lint": {
77 | "executor": "@nx/eslint:lint",
78 | "options": {
79 | "lintFilePatterns": [
80 | "apps/runtime-config-loader-demo/src/**/*.ts",
81 | "apps/runtime-config-loader-demo/src/**/*.html"
82 | ]
83 | }
84 | },
85 | "test": {
86 | "executor": "@nx/jest:jest",
87 | "outputs": [
88 | "{workspaceRoot}/coverage/apps/runtime-config-loader-demo"
89 | ],
90 | "options": {
91 | "jestConfig": "apps/runtime-config-loader-demo/jest.config.ts"
92 | }
93 | }
94 | },
95 | "tags": []
96 | }
97 |
--------------------------------------------------------------------------------
/migrations.json:
--------------------------------------------------------------------------------
1 | {
2 | "migrations": [
3 | {
4 | "cli": "nx",
5 | "version": "17.1.0-beta.5",
6 | "requires": {
7 | "@angular/core": ">=17.0.0"
8 | },
9 | "description": "Update the @angular/cli package version to ~17.0.0.",
10 | "factory": "./src/migrations/update-17-1-0/update-angular-cli",
11 | "package": "@nx/angular",
12 | "name": "update-angular-cli-version-17-0-0"
13 | },
14 | {
15 | "cli": "nx",
16 | "version": "17.1.0-beta.5",
17 | "requires": {
18 | "@angular/core": ">=17.0.0"
19 | },
20 | "description": "Rename 'browserTarget' to 'buildTarget'.",
21 | "factory": "./src/migrations/update-17-1-0/browser-target-to-build-target",
22 | "package": "@nx/angular",
23 | "name": "rename-browser-target-to-build-target"
24 | },
25 | {
26 | "cli": "nx",
27 | "version": "17.1.0-beta.5",
28 | "requires": {
29 | "@angular/core": ">=17.0.0"
30 | },
31 | "description": "Replace usages of '@nguniversal/builders' with '@angular-devkit/build-angular'.",
32 | "factory": "./src/migrations/update-17-1-0/replace-nguniversal-builders",
33 | "package": "@nx/angular",
34 | "name": "replace-nguniversal-builders"
35 | },
36 | {
37 | "cli": "nx",
38 | "version": "17.1.0-beta.5",
39 | "requires": {
40 | "@angular/core": ">=17.0.0"
41 | },
42 | "description": "Replace usages of '@nguniversal/' packages with '@angular/ssr'.",
43 | "factory": "./src/migrations/update-17-1-0/replace-nguniversal-engines",
44 | "package": "@nx/angular",
45 | "name": "replace-nguniversal-engines"
46 | },
47 | {
48 | "cli": "nx",
49 | "version": "17.1.0-beta.5",
50 | "requires": {
51 | "@angular/core": ">=17.0.0"
52 | },
53 | "description": "Replace the deep imports from 'zone.js/dist/zone' and 'zone.js/dist/zone-testing' with 'zone.js' and 'zone.js/testing'.",
54 | "factory": "./src/migrations/update-17-1-0/update-zone-js-deep-import",
55 | "package": "@nx/angular",
56 | "name": "update-zone-js-deep-import"
57 | },
58 | {
59 | "version": "17.1.0-beta.2",
60 | "description": "Move jest executor options to nx.json targetDefaults",
61 | "implementation": "./src/migrations/update-17-1-0/move-options-to-target-defaults",
62 | "package": "@nx/jest",
63 | "name": "move-options-to-target-defaults"
64 | },
65 | {
66 | "version": "17.1.0-beta.1",
67 | "description": "Updates for @typescript-utils/utils v6.9.1+",
68 | "implementation": "./src/migrations/update-17-1-0/update-typescript-eslint",
69 | "package": "@nx/eslint",
70 | "name": "update-typescript-eslint"
71 | },
72 | {
73 | "version": "17.0.0",
74 | "description": "Angular v17 introduces a new control flow syntax that uses the @ and } characters. This migration replaces the existing usages with their corresponding HTML entities.",
75 | "factory": "./migrations/block-template-entities/bundle",
76 | "package": "@angular/core",
77 | "name": "block-template-entities"
78 | },
79 | {
80 | "version": "17.0.0",
81 | "description": "CompilerOption.useJit and CompilerOption.missingTranslation are unused under Ivy. This migration removes their usage",
82 | "factory": "./migrations/compiler-options/bundle",
83 | "package": "@angular/core",
84 | "name": "migration-v17-compiler-options"
85 | },
86 | {
87 | "version": "17.0.0",
88 | "description": "Updates `TransferState`, `makeStateKey`, `StateKey` imports from `@angular/platform-browser` to `@angular/core`.",
89 | "factory": "./migrations/transfer-state/bundle",
90 | "package": "@angular/core",
91 | "name": "migration-transfer-state"
92 | }
93 | ]
94 | }
95 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Angular Runtime Configuration Loader
2 |
3 | Most applications require certain configuration values that can be changed at runtime of the app. The `environment.ts` files in an Angular application technically work for setting configuration values in an app, but those are buildtime configuration values. This means that they are set when the application is built, and can't be changed unless the app is built again.
4 |
5 | ## Overview
6 |
7 | This library provides an easy way to load one or more JSON files with configuration values or make one or more HTTP GET calls to an API endpoint that returns those values. The config objects that are returned from the call(s) will be combined into a single configuration object. You can then use that configuration throughout the application. The default location of the JSON file is in the `assets` folder, at `./assets/config.json`. When the service loads the file, it stores that configuration object in a local variable which can be accessed via the `getConfig()` and `getConfigObjectKey(key: string)` methods. `getConfig` returns the entire configuration object; `getConfigObjectKey(key: string)` returns part of the configuration object, namely the part defined by the key passed in. In some cases, the `config.json` is not finished loading before other modules/services are, so the above methods will return null. If that is the case, subscribe to the `configSubject` and access the configuration object that way.
8 |
9 | ## How to Implement
10 |
11 | In your `app.module.ts` file, add the following to the `@NgModule` decorator:
12 |
13 | ```ts
14 | imports: [..., RuntimeConfigLoaderModule, ...],
15 | ```
16 |
17 | That's it; it's that simple. In the `RuntimeConfigLoaderModule`, the `APP_INITIALIZER` token is used to run a function which loads the configuration from a file or an API endpoint that can be used throughout the application.
18 |
19 | If you implement the library exactly as it is above, the configuration file needs to be in the `./assets/config.json` location as mentioned above. If you'd like to load the file from a different location, provide that location in the `.forRoot()` method when importing the `RuntimeConfigLoaderModule`:
20 |
21 | ```ts
22 | imports: [
23 | ...,
24 | RuntimeConfigLoaderModule.forRoot(
25 | { configUrl: './path/to/config/config.json' }
26 | ),
27 | ...]
28 | ```
29 |
30 | If you want to load multiple files, the value of `configUrl` should be an array of strings:
31 |
32 | ```ts
33 | imports: [
34 | ...,
35 | RuntimeConfigLoaderModule.forRoot(
36 | { configUrl: ['./path/to/config/config-1.json', './path/to/config/config-2.json'] }
37 | ),
38 | ...]
39 | ```
40 |
41 | > Make sure that the path(s) you provide here is accessible by the Angular application, meaning that the file is somewhere the app can load it. In my opinion, the `assets` folder is the easiest place to work from.
42 |
43 | ## Multiple Config Paths
44 |
45 | One reason you may want to load multiple configuration objects is so that you can set the configuration on your machine without affecting anyone else. For example, you could have a `local.config.json` file that is not included in source control. Some of the values in that file would overwrite the values in a config file that everyone can use. Another use case is that some config values don't change between environments, and some do. The ones that don't change could go in one file, the ones that do change could go in a second file. Each developer/environment can provide the second file with values they want or need.
46 |
47 | It's important to know that if an attribute is repeated in two configuration files, the latest value is kept. So, let's say you have `apiUrl` in both files, `config-1.json` and `config-2.json`. Let's assume the files are passed in to the `forRoot` method like this:
48 |
49 | ```ts
50 | imports: [
51 | ...,
52 | RuntimeConfigLoaderModule.forRoot(
53 | { configUrl: ['./path/to/config/config-1.json', './path/to/config/config-2.json'] }
54 | ),
55 | ...]
56 | ```
57 |
58 | In this case, the `apiUrl` value from `config-2.json` will override the value from `config-1.json`.
59 |
--------------------------------------------------------------------------------
/libs/runtime-config-loader/README.md:
--------------------------------------------------------------------------------
1 | # Angular Runtime Configuration Loader
2 |
3 | Most applications require certain configuration values that can be changed at runtime of the app. The `environment.ts` files in an Angular application technically work for setting configuration values in an app, but those are buildtime configuration values. This means that they are set when the application is built, and can't be changed unless the app is built again.
4 |
5 | ## Overview
6 |
7 | This library provides an easy way to load one or more JSON files with configuration values or make one or more HTTP GET calls to an API endpoint that returns those values. The config objects that are returned from the call(s) will be combined into a single configuration object. You can then use that configuration throughout the application. The default location of the JSON file is in the `assets` folder, at `./assets/config.json`. When the service loads the file, it stores that configuration object in a local variable which can be accessed via the `getConfig()` and `getConfigObjectKey(key: string)` methods. `getConfig` returns the entire configuration object; `getConfigObjectKey(key: string)` returns part of the configuration object, namely the part defined by the key passed in. In some cases, the `config.json` is not finished loading before other modules/services are, so the above methods will return null. If that is the case, subscribe to the `configSubject` and access the configuration object that way.
8 |
9 | ## How to Implement
10 |
11 | In your `app.module.ts` file, add the following to the `@NgModule` decorator:
12 |
13 | ```ts
14 | imports: [..., RuntimeConfigLoaderModule, ...],
15 | ```
16 |
17 | That's it; it's that simple. In the `RuntimeConfigLoaderModule`, the `APP_INITIALIZER` token is used to run a function which loads the configuration from a file or an API endpoint that can be used throughout the application.
18 |
19 | If you implement the library exactly as it is above, the configuration file needs to be in the `./assets/config.json` location as mentioned above. If you'd like to load the file from a different location, provide that location in the `.forRoot()` method when importing the `RuntimeConfigLoaderModule`:
20 |
21 | ```ts
22 | imports: [
23 | ...,
24 | RuntimeConfigLoaderModule.forRoot(
25 | { configUrl: './path/to/config/config.json' }
26 | ),
27 | ...]
28 | ```
29 |
30 | If you want to load multiple files, the value of `configUrl` should be an array of strings:
31 |
32 | ```ts
33 | imports: [
34 | ...,
35 | RuntimeConfigLoaderModule.forRoot(
36 | { configUrl: ['./path/to/config/config-1.json', './path/to/config/config-2.json'] }
37 | ),
38 | ...]
39 | ```
40 |
41 | > Make sure that the path(s) you provide here is accessible by the Angular application, meaning that the file is somewhere the app can load it. In my opinion, the `assets` folder is the easiest place to work from.
42 |
43 | ## Multiple Config Paths
44 |
45 | One reason you may want to load multiple configuration objects is so that you can set the configuration on your machine without affecting anyone else. For example, you could have a `local.config.json` file that is not included in source control. Some of the values in that file would overwrite the values in a config file that everyone can use. Another use case is that some config values don't change between environments, and some do. The ones that don't change could go in one file, the ones that do change could go in a second file. Each developer/environment can provide the second file with values they want or need.
46 |
47 | It's important to know that if an attribute is repeated in two configuration files, the latest value is kept. So, let's say you have `apiUrl` in both files, `config-1.json` and `config-2.json`. Let's assume the files are passed in to the `forRoot` method like this:
48 |
49 | ```ts
50 | imports: [
51 | ...,
52 | RuntimeConfigLoaderModule.forRoot(
53 | { configUrl: ['./path/to/config/config-1.json', './path/to/config/config-2.json'] }
54 | ),
55 | ...]
56 | ```
57 |
58 | In this case, the `apiUrl` value from `config-2.json` will override the value from `config-1.json`.
59 |
--------------------------------------------------------------------------------
/libs/runtime-config-loader/src/lib/runtime-config-loader/runtime-config-loader.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { HttpClient } from '@angular/common/http';
2 | import { waitForAsync } from '@angular/core/testing';
3 | import { of, throwError } from 'rxjs';
4 | import { RuntimeConfig } from '../runtime-config';
5 | import { RuntimeConfigLoaderService } from './runtime-config-loader.service';
6 |
7 | describe('RuntimeConfigLoaderService', () => {
8 | let service: RuntimeConfigLoaderService;
9 | let mockSingleConfig: RuntimeConfig = {
10 | configUrl: './test-config.json',
11 | };
12 | let mockDoubleConfig: RuntimeConfig = {
13 | configUrl: ['./test-config.json', './test-config2.json'],
14 | };
15 | let mockConfigData1 = {
16 | apiUrl: 'https://test-api.com',
17 | nestedObject: {
18 | nested: 'value',
19 | },
20 | };
21 | let mockConfigData2 = {
22 | apiUrl: 'https://test-2-api.com',
23 | appName: 'App Name 2',
24 | nestedObject: {
25 | nested: 'value 2',
26 | },
27 | };
28 | let mockHttpService: any;
29 |
30 | describe('Single Config URL', () => {
31 | beforeEach(() => {
32 | mockHttpService = { get: null } as any as HttpClient;
33 | service = new RuntimeConfigLoaderService(
34 | mockHttpService,
35 | mockSingleConfig
36 | );
37 | });
38 |
39 | it('should be created', () => {
40 | expect(service).toBeTruthy();
41 | });
42 |
43 | it(
44 | 'should load the config object',
45 | waitForAsync(() => {
46 | mockHttpService.get = jest
47 | .fn()
48 | .mockReturnValue(of(mockConfigData1));
49 | service.loadConfig().subscribe();
50 |
51 | const config = service.getConfig();
52 | expect(config).toStrictEqual(mockConfigData1);
53 | })
54 | );
55 |
56 | it(
57 | 'should return the key from the config object',
58 | waitForAsync(() => {
59 | mockHttpService.get = jest
60 | .fn()
61 | .mockReturnValue(of(mockConfigData1));
62 | service.loadConfig().subscribe();
63 |
64 | const configKey = service.getConfigObjectKey('apiUrl');
65 | expect(configKey).toStrictEqual(mockConfigData1.apiUrl);
66 | })
67 | );
68 |
69 | it(
70 | 'should handle a load error',
71 | waitForAsync(() => {
72 | mockHttpService.get = jest
73 | .fn()
74 | .mockReturnValue(throwError('Test Error'));
75 | service.loadConfig().subscribe();
76 |
77 | const config = service.getConfig();
78 | expect(config).toStrictEqual(null);
79 | })
80 | );
81 | });
82 |
83 | describe('Double Config URL', () => {
84 | beforeEach(() => {
85 | mockHttpService = { get: null } as any as HttpClient;
86 | service = new RuntimeConfigLoaderService(
87 | mockHttpService,
88 | mockDoubleConfig
89 | );
90 | });
91 |
92 | it('should be created', () => {
93 | expect(service).toBeTruthy();
94 | });
95 |
96 | it(
97 | 'should load the config object from both sources',
98 | waitForAsync(() => {
99 | mockHttpService.get = jest
100 | .fn()
101 | .mockReturnValueOnce(of(mockConfigData1))
102 | .mockReturnValueOnce(of(mockConfigData2));
103 | service.loadConfig().subscribe();
104 |
105 | const config = service.getConfig();
106 | expect(config).toStrictEqual(mockConfigData2);
107 | })
108 | );
109 |
110 | it(
111 | 'should return the key from the config object after combining both sources',
112 | waitForAsync(() => {
113 | mockHttpService.get = jest
114 | .fn()
115 | .mockReturnValueOnce(of(mockConfigData1))
116 | .mockReturnValueOnce(of(mockConfigData2));
117 | service.loadConfig().subscribe();
118 |
119 | const configKey = service.getConfigObjectKey('apiUrl');
120 | expect(configKey).toStrictEqual(mockConfigData2.apiUrl);
121 | })
122 | );
123 |
124 | it(
125 | 'should handle a load error when the first file fails',
126 | waitForAsync(() => {
127 | mockHttpService.get = jest
128 | .fn()
129 | .mockReturnValueOnce(throwError('Test Error'))
130 | .mockReturnValueOnce(of(mockConfigData2));
131 | service.loadConfig().subscribe();
132 |
133 | const config = service.getConfig();
134 | expect(config).toStrictEqual(null);
135 | })
136 | );
137 |
138 | it(
139 | 'should handle a load error when the second file fails',
140 | waitForAsync(() => {
141 | mockHttpService.get = jest
142 | .fn()
143 | .mockReturnValueOnce(of(mockConfigData1))
144 | .mockReturnValueOnce(throwError('Test Error'));
145 | service.loadConfig().subscribe();
146 |
147 | const config = service.getConfig();
148 | expect(config).toStrictEqual(null);
149 | })
150 | );
151 | });
152 | });
153 |
--------------------------------------------------------------------------------
/apps/runtime-config-loader-demo/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
13 |
18 |
23 |
28 |
33 |
34 | Welcome to {{ title }}!
35 |
36 |
37 | Resources & Tools
38 | Thank you for using and showing some ♥ for Nx.
39 |
63 | Here are some links to help you get started.
64 |
111 | Next Steps
112 | Here are some things you can do with Nx.
113 |
114 | Add UI library
115 |
116 | # Generate UI lib
117 | nx g @nx/angular:lib ui
118 |
119 | # Add a component
120 | nx g @nx/angular:component xyz --project ui
122 |
123 |
124 | View dependency graph
125 | nx dep-graph
126 |
127 |
128 | Run affected commands
129 |
130 | # see what's been affected by changes
131 | nx affected:dep-graph
132 |
133 | # run tests for current changes
134 | nx affected:test
135 |
136 | # run e2e tests for current changes
137 | nx affected:e2e
138 |
140 |
141 |
142 |
--------------------------------------------------------------------------------