├── .editorconfig ├── .eslintrc.json ├── .github └── workflows │ ├── ci.yml │ └── release.yml ├── .gitignore ├── .prettierignore ├── .prettierrc ├── .vscode └── extensions.json ├── README.md ├── apps └── demo-app │ ├── .eslintrc.json │ ├── jest.config.ts │ ├── project.json │ ├── src │ ├── app │ │ ├── app.component.html │ │ ├── app.component.scss │ │ └── app.component.ts │ ├── assets │ │ └── .gitkeep │ ├── environments │ │ ├── environment.prod.ts │ │ └── environment.ts │ ├── favicon.ico │ ├── index.html │ ├── main.ts │ ├── styles.css │ ├── styles.scss │ └── test-setup.ts │ ├── tsconfig.app.json │ ├── tsconfig.editor.json │ ├── tsconfig.json │ └── tsconfig.spec.json ├── decorate-angular-cli.js ├── jest.config.ts ├── jest.preset.js ├── libs └── ngx-rxjs-zone-scheduler │ ├── .eslintrc.json │ ├── README.md │ ├── jest.config.ts │ ├── ng-package.json │ ├── package.json │ ├── project.json │ ├── src │ ├── index.ts │ ├── lib │ │ ├── providers.ts │ │ ├── rx-ng-zone-scheduler.module.ts │ │ ├── rx-ng-zone-scheduler.spec.ts │ │ └── rx-ng-zone-scheduler.ts │ └── test-setup.ts │ ├── tsconfig.json │ ├── tsconfig.lib.json │ ├── tsconfig.lib.prod.json │ └── tsconfig.spec.json ├── nx.json ├── package.json ├── tools └── tsconfig.tools.json ├── tsconfig.base.json └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.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 | "@typescript-eslint/no-extra-semi": "error", 29 | "no-extra-semi": "off" 30 | } 31 | }, 32 | { 33 | "files": ["*.js", "*.jsx"], 34 | "extends": ["plugin:@nx/javascript"], 35 | "rules": { 36 | "@typescript-eslint/no-extra-semi": "error", 37 | "no-extra-semi": "off" 38 | } 39 | } 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | 9 | jobs: 10 | main: 11 | name: Nx Cloud - Main Job 12 | uses: nrwl/ci/.github/workflows/nx-cloud-main.yml@v0.15.0 13 | secrets: 14 | NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} 15 | NX_CLOUD_AUTH_TOKEN: ${{ secrets.NX_CLOUD_AUTH_TOKEN }} 16 | with: 17 | main-branch-name: main 18 | number-of-agents: 3 19 | init-commands: | 20 | yarn nx-cloud start-ci-run --stop-agents-after="build" --agent-count=3 21 | parallel-commands: | 22 | yarn nx-cloud record -- yarn nx format:check 23 | parallel-commands-on-agents: | 24 | yarn nx affected --target=lint --parallel=3 25 | yarn nx affected --target=test --parallel=3 --ci --code-coverage 26 | yarn nx affected --target=build --parallel=3 27 | 28 | agents: 29 | name: Nx Cloud - Agents 30 | uses: nrwl/ci/.github/workflows/nx-cloud-agents.yml@v0.13.0 31 | secrets: 32 | NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} 33 | NX_CLOUD_AUTH_TOKEN: ${{ secrets.NX_CLOUD_AUTH_TOKEN }} 34 | with: 35 | number-of-agents: 3 36 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*.*.*' 7 | 8 | jobs: 9 | build-and-publish: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout repository 13 | uses: actions/checkout@v3 14 | - name: Setup Node.js 15 | uses: actions/setup-node@v3 16 | with: 17 | node-version: '20' 18 | cache: 'yarn' 19 | - name: Install dependencies 20 | run: yarn install 21 | - name: Build the project 22 | run: yarn build 23 | - name: Create .npmrc file 24 | run: echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > ~/.npmrc 25 | - name: Publish to npm 26 | run: npm publish dist/libs/ngx-rxjs-zone-scheduler 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | 8 | # dependencies 9 | /node_modules 10 | 11 | # IDEs and editors 12 | /.idea 13 | .project 14 | .classpath 15 | .c9/ 16 | *.launch 17 | .settings/ 18 | *.sublime-workspace 19 | 20 | # IDE - VSCode 21 | .vscode/* 22 | !.vscode/settings.json 23 | !.vscode/tasks.json 24 | !.vscode/launch.json 25 | !.vscode/extensions.json 26 | 27 | # misc 28 | /.sass-cache 29 | /connect.lock 30 | /coverage 31 | /libpeerconnection.log 32 | npm-debug.log 33 | yarn-error.log 34 | testem.log 35 | /typings 36 | 37 | # System Files 38 | .DS_Store 39 | Thumbs.db 40 | 41 | .angular/cache 42 | 43 | .nx/cache 44 | .nx/workspace-data 45 | 46 | *.env 47 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Add files here to ignore them from prettier formatting 2 | 3 | /dist 4 | /coverage 5 | 6 | /.nx/cache 7 | 8 | /.nx/workspace-data 9 | /.angular/cache 10 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true 3 | } 4 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NgxRxjsZoneScheduler 2 | 3 | [![CI](https://github.com/ftischler/ngx-rxjs-zone-scheduler/actions/workflows/ci.yml/badge.svg)](https://github.com/ftischler/ngx-rxjs-zone-scheduler/actions/workflows/ci.yml) 4 | 5 | ## Library 6 | 7 | [Documentation and code here](https://github.com/ftischler/ngx-rxjs-zone-scheduler/blob/main/libs/ngx-rxjs-zone-scheduler) 8 | 9 | ## Demo app 10 | 11 | [Code here](https://github.com/ftischler/ngx-rxjs-zone-scheduler/blob/main/apps/demo-app) 12 | 13 | ## Installation 14 | 15 | `npm install ngx-rxjs-zone-scheduler --save` or 16 | `yarn add ngx-rxjs-zone-scheduler` 17 | 18 | ## Publishing a new version 19 | 20 | - Increment the version in root [package.json](package.json) and [libs/ngx-rxjs-zone-scheduler/package.json](libs/ngx-rxjs-zone-scheduler/package.json) 21 | - `yarn build` 22 | - `cd dist/libs/ngx-rxjs-zone-scheduler && npm pack` 23 | - `npm publish ngx-rxjs-zone-scheduler-.tgz` 24 | -------------------------------------------------------------------------------- /apps/demo-app/.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": "ngxRxjsZoneScheduler", 17 | "style": "camelCase" 18 | } 19 | ], 20 | "@angular-eslint/component-selector": [ 21 | "error", 22 | { 23 | "type": "element", 24 | "prefix": "ngx-rxjs-zone-scheduler", 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/demo-app/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'demo-app', 4 | preset: '../../jest.preset.js', 5 | setupFilesAfterEnv: ['/src/test-setup.ts'], 6 | globals: {}, 7 | snapshotSerializers: [ 8 | 'jest-preset-angular/build/serializers/no-ng-attributes', 9 | 'jest-preset-angular/build/serializers/ng-snapshot', 10 | 'jest-preset-angular/build/serializers/html-comment', 11 | ], 12 | transform: { 13 | '^.+\\.(ts|js|mjs|html|svg)$': [ 14 | 'jest-preset-angular', 15 | { 16 | tsconfig: '/tsconfig.spec.json', 17 | stringifyContentPathRegex: '\\.(html|svg)$', 18 | }, 19 | ], 20 | }, 21 | moduleFileExtensions: ['ts', 'html', 'js', 'json', 'mjs'], 22 | resolver: 'jest-preset-angular/build/resolvers/ng-jest-resolver.js', 23 | transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], 24 | }; 25 | -------------------------------------------------------------------------------- /apps/demo-app/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo-app", 3 | "$schema": "../../node_modules/nx/schemas/project-schema.json", 4 | "projectType": "application", 5 | "sourceRoot": "apps/demo-app/src", 6 | "prefix": "ngx-rxjs-zone-scheduler", 7 | "tags": [], 8 | "targets": { 9 | "build": { 10 | "executor": "@angular-devkit/build-angular:browser", 11 | "outputs": ["{options.outputPath}"], 12 | "options": { 13 | "outputPath": "dist/apps/demo-app", 14 | "index": "apps/demo-app/src/index.html", 15 | "main": "apps/demo-app/src/main.ts", 16 | "polyfills": ["zone.js"], 17 | "tsConfig": "apps/demo-app/tsconfig.app.json", 18 | "inlineStyleLanguage": "scss", 19 | "assets": ["apps/demo-app/src/favicon.ico", "apps/demo-app/src/assets"], 20 | "styles": ["apps/demo-app/src/styles.scss"], 21 | "scripts": [] 22 | }, 23 | "configurations": { 24 | "production": { 25 | "budgets": [ 26 | { 27 | "type": "initial", 28 | "maximumWarning": "500kb", 29 | "maximumError": "1mb" 30 | }, 31 | { 32 | "type": "anyComponentStyle", 33 | "maximumWarning": "2kb", 34 | "maximumError": "4kb" 35 | } 36 | ], 37 | "fileReplacements": [ 38 | { 39 | "replace": "apps/demo-app/src/environments/environment.ts", 40 | "with": "apps/demo-app/src/environments/environment.prod.ts" 41 | } 42 | ], 43 | "outputHashing": "all" 44 | }, 45 | "development": { 46 | "buildOptimizer": false, 47 | "optimization": false, 48 | "vendorChunk": true, 49 | "extractLicenses": false, 50 | "sourceMap": true, 51 | "namedChunks": true 52 | } 53 | }, 54 | "defaultConfiguration": "production" 55 | }, 56 | "serve": { 57 | "executor": "@angular-devkit/build-angular:dev-server", 58 | "configurations": { 59 | "production": { 60 | "buildTarget": "demo-app:build:production" 61 | }, 62 | "development": { 63 | "buildTarget": "demo-app:build:development" 64 | } 65 | }, 66 | "defaultConfiguration": "development" 67 | }, 68 | "extract-i18n": { 69 | "executor": "@angular-devkit/build-angular:extract-i18n", 70 | "options": { 71 | "buildTarget": "demo-app:build" 72 | } 73 | }, 74 | "lint": { 75 | "executor": "@nx/eslint:lint" 76 | }, 77 | "test": { 78 | "executor": "@nx/jest:jest", 79 | "outputs": ["{workspaceRoot}/coverage/apps/demo-app"], 80 | "options": { 81 | "jestConfig": "apps/demo-app/jest.config.ts" 82 | } 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /apps/demo-app/src/app/app.component.html: -------------------------------------------------------------------------------- 1 |

Demo app for ngx-rxjs-zone-scheduler

2 |
3 |

4 | {{ demotext }} 5 |

6 | 7 |

Refreshing...

8 |
9 |
10 |
11 |
12 | 15 |
16 |
17 |
18 | 21 |
22 |
23 |
24 | 27 |
28 |
29 |
30 | 33 |
34 |
35 |
36 |
37 |
38 | 41 |
42 |
43 | -------------------------------------------------------------------------------- /apps/demo-app/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 | -------------------------------------------------------------------------------- /apps/demo-app/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { Observable, of } from 'rxjs'; 3 | import { delay, tap } from 'rxjs/operators'; 4 | import { RxNgZoneScheduler } from 'ngx-rxjs-zone-scheduler'; 5 | import { AsyncPipe, NgIf } from '@angular/common'; 6 | 7 | @Component({ 8 | selector: 'ngx-rxjs-zone-scheduler-root', 9 | templateUrl: './app.component.html', 10 | styleUrls: ['./app.component.scss'], 11 | imports: [AsyncPipe, NgIf], 12 | }) 13 | export class AppComponent implements OnInit { 14 | public demotext$?: Observable; 15 | 16 | constructor(private zoneScheduler: RxNgZoneScheduler) {} 17 | 18 | ngOnInit() { 19 | this.demotext$ = of('This is the initial text'); 20 | } 21 | 22 | public runChangeDetection(): boolean { 23 | // Button click event will trigger change detection. 24 | return true; 25 | } 26 | 27 | public updateProducerOnNgZone(): void { 28 | const text = 'This text is updated on NgZone by the producer'; 29 | this.demotext$ = of(text).pipe( 30 | delay(500, this.zoneScheduler.enterNgZone()), 31 | tap(() => console.log(text)) 32 | ); 33 | } 34 | 35 | public updateProducerOutOfNgZone(): void { 36 | const text = 'This text is updated out of NgZone by the producer'; 37 | this.demotext$ = of(text).pipe( 38 | delay(500, this.zoneScheduler.leaveNgZone()), 39 | tap(() => console.log(text)) 40 | ); 41 | } 42 | 43 | public updateObserverOnNgZone(): void { 44 | const text = 45 | 'This text is updated out of NgZone by the producer and observed on NgZone'; 46 | this.demotext$ = of(text).pipe( 47 | delay(500, this.zoneScheduler.leaveNgZone()), 48 | this.zoneScheduler.observeOnNgZone(), 49 | tap(() => console.log(text)) 50 | ); 51 | } 52 | 53 | public updateObserverOutOfNgZone(): void { 54 | const text = 55 | 'This text is updated out of NgZone by the producer and observed on NgZone'; 56 | this.demotext$ = of(text).pipe( 57 | delay(500, this.zoneScheduler.enterNgZone()), 58 | this.zoneScheduler.observeOutOfNgZone(), 59 | tap(() => console.log(text)) 60 | ); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /apps/demo-app/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ftischler/ngx-rxjs-zone-scheduler/de56649b698f5bc0157e600b113280dad0bcf043/apps/demo-app/src/assets/.gitkeep -------------------------------------------------------------------------------- /apps/demo-app/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | }; 4 | -------------------------------------------------------------------------------- /apps/demo-app/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false, 7 | }; 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 | -------------------------------------------------------------------------------- /apps/demo-app/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ftischler/ngx-rxjs-zone-scheduler/de56649b698f5bc0157e600b113280dad0bcf043/apps/demo-app/src/favicon.ico -------------------------------------------------------------------------------- /apps/demo-app/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | DemoApp 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /apps/demo-app/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { environment } from './environments/environment'; 3 | import { bootstrapApplication } from '@angular/platform-browser'; 4 | import { AppComponent } from './app/app.component'; 5 | import { provideRxNgZoneScheduler } from 'ngx-rxjs-zone-scheduler'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | bootstrapApplication(AppComponent, { 12 | providers: [provideRxNgZoneScheduler()], 13 | }).catch((err) => console.error(err)); 14 | -------------------------------------------------------------------------------- /apps/demo-app/src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /apps/demo-app/src/styles.scss: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /apps/demo-app/src/test-setup.ts: -------------------------------------------------------------------------------- 1 | import 'jest-preset-angular/setup-jest'; 2 | -------------------------------------------------------------------------------- /apps/demo-app/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"], 10 | "include": ["src/**/*.d.ts"], 11 | "exclude": ["jest.config.ts"] 12 | } 13 | -------------------------------------------------------------------------------- /apps/demo-app/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 | -------------------------------------------------------------------------------- /apps/demo-app/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 | -------------------------------------------------------------------------------- /apps/demo-app/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", "**/*.d.ts", "jest.config.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /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(ngPath + ext, fs.readFileSync(nxPath + ext)); 54 | }); 55 | } else { 56 | // If unix-based, symlink 57 | cp.execSync(`ln -sf ./nx ${ngPath}`); 58 | } 59 | } catch (e) { 60 | output.error({ 61 | title: 62 | 'Unable to create a symlink from the Angular CLI to the Nx CLI:' + 63 | e.message, 64 | }); 65 | throw e; 66 | } 67 | } 68 | 69 | try { 70 | symlinkNgCLItoNxCLI(); 71 | require('@nrwl/cli/lib/decorate-cli').decorateCli(); 72 | output.log({ 73 | title: 'Angular CLI has been decorated to enable computation caching.', 74 | }); 75 | } catch (e) { 76 | output.error({ 77 | title: 'Decoration of the Angular CLI did not complete successfully', 78 | }); 79 | } 80 | -------------------------------------------------------------------------------- /jest.config.ts: -------------------------------------------------------------------------------- 1 | const { getJestProjectsAsync } = require('@nx/jest'); 2 | 3 | export default async () => ({ 4 | projects: await getJestProjectsAsync(), 5 | }); 6 | -------------------------------------------------------------------------------- /jest.preset.js: -------------------------------------------------------------------------------- 1 | const nxPreset = require('@nx/jest/preset').default; 2 | 3 | module.exports = { 4 | ...nxPreset, 5 | }; 6 | -------------------------------------------------------------------------------- /libs/ngx-rxjs-zone-scheduler/.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": "ngxRxjsZoneScheduler", 17 | "style": "camelCase" 18 | } 19 | ], 20 | "@angular-eslint/component-selector": [ 21 | "error", 22 | { 23 | "type": "element", 24 | "prefix": "ngx-rxjs-zone-scheduler", 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/ngx-rxjs-zone-scheduler/README.md: -------------------------------------------------------------------------------- 1 | # ngx-rxjs-zone-scheduler 2 | 3 | A library for Angular providing rxjs schedulers to run some kind of work inside or outside of `NgZone`. 4 | 5 | ## Purpose 6 | 7 | Sometimes in Angular you need to decide if a task should run inside or outside of `NgZone`. 8 | Usually this is possible by injecting `NgZone`: 9 | 10 | ```typescript 11 | import { Component, inject, NgZone, OnInit } from '@angular/core'; 12 | 13 | @Component({ 14 | // ... 15 | }) 16 | export class AppComponent implements OnInit { 17 | private ngZone = inject(NgZone); 18 | 19 | ngOnInit() { 20 | this.ngZone.run(() => { 21 | /* run inside NgZone */ 22 | }); 23 | this.ngZone.runOutsideAngular(() => { 24 | /* run outside NgZone */ 25 | }); 26 | // ... 27 | } 28 | } 29 | ``` 30 | 31 | This is a simple library to wrap this functionality into a rxjs scheduler in order to use it directly with rxjs `Observable`. 32 | 33 | ## Installation 34 | 35 | `npm install ngx-rxjs-zone-scheduler --save` or 36 | `yarn add ngx-rxjs-zone-scheduler` 37 | 38 | ## Usage 39 | 40 | Provide `RxNgZoneScheduler` via `provideRxNgZoneScheduler()` in your `applicationConfig` or `main.ts`: 41 | 42 | ```typescript 43 | import { ApplicationConfig } from '@angular/core'; 44 | import { provideRxNgZoneScheduler } from 'ngx-rxjs-zone-scheduler'; 45 | // ... 46 | providers: [ 47 | // ... 48 | provideRxNgZoneScheduler(), 49 | ]; 50 | // ... 51 | ``` 52 | 53 | Now you can inject `RxNgZoneScheduler` in your services or components: 54 | 55 | ```typescript 56 | import { Component, inject, OnInit } from '@angular/core'; 57 | import { Observable, of } from 'rxjs'; 58 | import { delay } from 'rxjs/operators'; 59 | 60 | import { RxNgZoneScheduler } from 'ngx-rxjs-zone-scheduler'; 61 | 62 | @Component({ 63 | // ... 64 | }) 65 | export class AppComponent implements OnInit { 66 | public demotext1$: Observable; 67 | public demotext2$: Observable; 68 | public demotext3$: Observable; 69 | public demotext4$: Observable; 70 | 71 | private zoneScheduler = inject(RxNgZoneScheduler); 72 | 73 | ngOnInit() { 74 | // Usage of pipeable operators as wrappers for observeOn and subscribeOn: 75 | 76 | this.demotext1$ = of('This is the initial text').pipe( 77 | this.zoneScheduler.observeOutOfNgZone() // To observe outside of NgZone - like runOutsideAngular() 78 | ); 79 | 80 | this.demotext2$ = of('This is the initial text').pipe( 81 | this.zoneScheduler.observeOnNgZone() // To observe inside of NgZone - like run() 82 | ); 83 | 84 | // Direct usage of the scheduler: 85 | this.demotext3$ = of('This is the initial text').pipe( 86 | delay(100, this.zoneScheduler.leaveNgZone()) // To produce outside of NgZone - like runOutsideAngular() 87 | ); 88 | 89 | this.demotext4$ = of('This is the initial text').pipe( 90 | delay(100, this.zoneScheduler.enterNgZone()) // To produce inside of NgZone - like run() 91 | ); 92 | } 93 | 94 | // ... 95 | } 96 | ``` 97 | 98 | It is also possible to use the schedulers without importing RxNgZoneSchedulerModule: 99 | 100 | ```typescript 101 | import { Component, OnInit, NgZone, inject } from '@angular/core'; 102 | import { Observable, of } from 'rxjs'; 103 | import { delay, observeOn } from 'rxjs/operators'; 104 | 105 | import { enterNgZone, leaveNgZone } from 'ngx-rxjs-zone-scheduler'; 106 | 107 | @Component({ 108 | // ... 109 | }) 110 | export class AppComponent implements OnInit { 111 | public demotext1$: Observable; 112 | public demotext2$: Observable; 113 | 114 | private ngZone = inject(NgZone); 115 | 116 | ngOnInit() { 117 | this.demotext1$ = of('This is the initial text').pipe( 118 | observeOn(enterNgZone(this.ngZone)) 119 | ); 120 | 121 | this.demotext2$ = of('This is the initial text').pipe( 122 | observeOn(leaveNgZone(this.ngZone)) 123 | ); 124 | } 125 | 126 | // ... 127 | } 128 | ``` 129 | -------------------------------------------------------------------------------- /libs/ngx-rxjs-zone-scheduler/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'ngx-rxjs-zone-scheduler', 4 | preset: '../../jest.preset.js', 5 | setupFilesAfterEnv: ['/src/test-setup.ts'], 6 | globals: {}, 7 | transform: { 8 | '^.+\\.(ts|mjs|js|html)$': [ 9 | 'jest-preset-angular', 10 | { 11 | tsconfig: '/tsconfig.spec.json', 12 | stringifyContentPathRegex: '\\.(html|svg)$', 13 | }, 14 | ], 15 | }, 16 | transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], 17 | snapshotSerializers: [ 18 | 'jest-preset-angular/build/serializers/no-ng-attributes', 19 | 'jest-preset-angular/build/serializers/ng-snapshot', 20 | 'jest-preset-angular/build/serializers/html-comment', 21 | ], 22 | }; 23 | -------------------------------------------------------------------------------- /libs/ngx-rxjs-zone-scheduler/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../../dist/libs/ngx-rxjs-zone-scheduler", 4 | "lib": { 5 | "entryFile": "src/index.ts" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /libs/ngx-rxjs-zone-scheduler/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ngx-rxjs-zone-scheduler", 3 | "version": "19.0.0", 4 | "author": "Florian Tischler", 5 | "repository": "https://github.com/ftischler/ngx-rxjs-zone-scheduler", 6 | "license": "MIT", 7 | "keywords": [ 8 | "Angular", 9 | "rxjs", 10 | "NgZone", 11 | "Zone", 12 | "Scheduler" 13 | ], 14 | "homepage": "https://github.com/ftischler/ngx-rxjs-zone-scheduler/blob/main/libs/ngx-rxjs-zone-scheduler", 15 | "readme": "https://github.com/ftischler/ngx-rxjs-zone-scheduler/blob/main/libs/ngx-rxjs-zone-scheduler/README.md", 16 | "peerDependencies": { 17 | "@angular/common": "^19.0.0", 18 | "@angular/core": "^19.0.0", 19 | "rxjs": "^7.8.1" 20 | }, 21 | "dependencies": { 22 | "tslib": "^2.8.1" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /libs/ngx-rxjs-zone-scheduler/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ngx-rxjs-zone-scheduler", 3 | "$schema": "../../node_modules/nx/schemas/project-schema.json", 4 | "projectType": "library", 5 | "sourceRoot": "libs/ngx-rxjs-zone-scheduler/src", 6 | "prefix": "ngx-rxjs-zone-scheduler", 7 | "tags": [], 8 | "targets": { 9 | "build": { 10 | "executor": "@nx/angular:package", 11 | "outputs": ["{workspaceRoot}/dist/libs/ngx-rxjs-zone-scheduler"], 12 | "options": { 13 | "project": "libs/ngx-rxjs-zone-scheduler/ng-package.json" 14 | }, 15 | "configurations": { 16 | "production": { 17 | "tsConfig": "libs/ngx-rxjs-zone-scheduler/tsconfig.lib.prod.json" 18 | }, 19 | "development": { 20 | "tsConfig": "libs/ngx-rxjs-zone-scheduler/tsconfig.lib.json" 21 | } 22 | }, 23 | "defaultConfiguration": "production" 24 | }, 25 | "test": { 26 | "executor": "@nx/jest:jest", 27 | "outputs": ["{workspaceRoot}/coverage/libs/ngx-rxjs-zone-scheduler"], 28 | "options": { 29 | "jestConfig": "libs/ngx-rxjs-zone-scheduler/jest.config.ts" 30 | } 31 | }, 32 | "lint": { 33 | "executor": "@nx/eslint:lint" 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /libs/ngx-rxjs-zone-scheduler/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/rx-ng-zone-scheduler.module'; 2 | export * from './lib/rx-ng-zone-scheduler'; 3 | export * from './lib/providers'; 4 | -------------------------------------------------------------------------------- /libs/ngx-rxjs-zone-scheduler/src/lib/providers.ts: -------------------------------------------------------------------------------- 1 | import { makeEnvironmentProviders } from '@angular/core'; 2 | import { RxNgZoneScheduler } from './rx-ng-zone-scheduler'; 3 | 4 | export const provideRxNgZoneScheduler = () => 5 | makeEnvironmentProviders([RxNgZoneScheduler]); 6 | -------------------------------------------------------------------------------- /libs/ngx-rxjs-zone-scheduler/src/lib/rx-ng-zone-scheduler.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RxNgZoneScheduler } from './rx-ng-zone-scheduler'; 3 | 4 | @NgModule({ 5 | providers: [RxNgZoneScheduler], 6 | }) 7 | export class RxNgZoneSchedulerModule {} 8 | -------------------------------------------------------------------------------- /libs/ngx-rxjs-zone-scheduler/src/lib/rx-ng-zone-scheduler.spec.ts: -------------------------------------------------------------------------------- 1 | import { NgZone } from '@angular/core'; 2 | import { fakeAsync, TestBed, tick, waitForAsync } from '@angular/core/testing'; 3 | import { of, Subject } from 'rxjs'; 4 | import { delay, skip } from 'rxjs/operators'; 5 | 6 | import { RxNgZoneScheduler } from './rx-ng-zone-scheduler'; 7 | 8 | class MockNgZone { 9 | public run = jest.fn((fn) => this.ngZone.run(fn)); 10 | public runOutsideAngular = jest.fn((fn) => this.ngZone.runOutsideAngular(fn)); 11 | 12 | constructor(private ngZone: NgZone) {} 13 | } 14 | 15 | describe('RxNgZoneScheduler', () => { 16 | let mockNgZone: MockNgZone; 17 | let service: RxNgZoneScheduler; 18 | 19 | beforeEach(() => { 20 | TestBed.configureTestingModule({ 21 | providers: [ 22 | { provide: MockNgZone, useClass: MockNgZone, deps: [NgZone] }, 23 | { 24 | provide: RxNgZoneScheduler, 25 | useClass: RxNgZoneScheduler, 26 | deps: [MockNgZone], 27 | }, 28 | ], 29 | }); 30 | 31 | mockNgZone = TestBed.inject(MockNgZone); 32 | service = TestBed.inject(RxNgZoneScheduler); 33 | }); 34 | 35 | describe('producers', () => { 36 | it('should call run on ngZone after 300ms delay', fakeAsync(() => { 37 | expect.hasAssertions(); 38 | 39 | const test$ = of('this is a test').pipe( 40 | delay(300, service.enterNgZone()) 41 | ); 42 | 43 | test$.subscribe(() => { 44 | expect(mockNgZone.run).toHaveBeenCalledTimes(1); 45 | expect(mockNgZone.runOutsideAngular).not.toHaveBeenCalled(); 46 | }); 47 | 48 | tick(300); 49 | })); 50 | 51 | it('should call runOutsideAngular on ngZone after 300ms delay', fakeAsync(() => { 52 | expect.hasAssertions(); 53 | 54 | const test$ = of('this is a test').pipe( 55 | delay(300, service.leaveNgZone()) 56 | ); 57 | 58 | test$.subscribe(() => { 59 | expect(mockNgZone.runOutsideAngular).toHaveBeenCalledTimes(1); 60 | expect(mockNgZone.run).not.toHaveBeenCalled(); 61 | }); 62 | 63 | tick(300); 64 | })); 65 | }); 66 | 67 | describe('observeOn operators', () => { 68 | it('should call run on ngZone', waitForAsync(() => { 69 | expect.hasAssertions(); 70 | 71 | const test$ = of('this is a test').pipe(service.observeOnNgZone()); 72 | 73 | test$.subscribe(() => { 74 | expect(mockNgZone.run).toHaveBeenCalledTimes(2); 75 | expect(mockNgZone.runOutsideAngular).not.toHaveBeenCalled(); 76 | }); 77 | })); 78 | 79 | it('should not re-enter the Angular zone if the value has been emitted within the Angular zone', waitForAsync(() => { 80 | expect.hasAssertions(); 81 | 82 | const test$ = new Subject(); 83 | 84 | test$.pipe(service.observeOnNgZone(), skip(1)).subscribe(() => { 85 | // Previously, it would've been called 3 times, since we call it manually through `mockNgZone.run` 86 | // and the `EnterZoneScheduler` would've called it 2 times. 87 | expect(mockNgZone.run).toHaveBeenCalledTimes(2); 88 | }); 89 | 90 | mockNgZone.run(() => test$.next('within the Angular zone')); 91 | mockNgZone.runOutsideAngular(() => 92 | test$.next('outside of the Angular zone') 93 | ); 94 | })); 95 | 96 | it('should call runOutsideAngular on ngZone', waitForAsync(() => { 97 | expect.hasAssertions(); 98 | 99 | const test$ = of('this is a test').pipe(service.observeOutOfNgZone()); 100 | 101 | test$.subscribe(() => { 102 | expect(mockNgZone.runOutsideAngular).toHaveBeenCalledTimes(2); 103 | expect(mockNgZone.run).not.toHaveBeenCalled(); 104 | }); 105 | })); 106 | }); 107 | 108 | describe('subscribeOn operators', () => { 109 | it('should call run on ngZone', waitForAsync(() => { 110 | expect.hasAssertions(); 111 | 112 | const test$ = of('this is a test').pipe(service.subscribeOnNgZone()); 113 | 114 | test$.subscribe(() => { 115 | expect(mockNgZone.run).toHaveBeenCalledTimes(1); 116 | expect(mockNgZone.runOutsideAngular).not.toHaveBeenCalled(); 117 | }); 118 | })); 119 | 120 | it('should call runOutsideAngular on ngZone', waitForAsync(() => { 121 | expect.hasAssertions(); 122 | 123 | const test$ = of('this is a test').pipe(service.subscribeOutOfNgZone()); 124 | 125 | test$.subscribe(() => { 126 | expect(mockNgZone.runOutsideAngular).toHaveBeenCalledTimes(1); 127 | expect(mockNgZone.run).not.toHaveBeenCalled(); 128 | }); 129 | })); 130 | }); 131 | }); 132 | -------------------------------------------------------------------------------- /libs/ngx-rxjs-zone-scheduler/src/lib/rx-ng-zone-scheduler.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, NgZone } from '@angular/core'; 2 | import { 3 | asyncScheduler, 4 | MonoTypeOperatorFunction, 5 | SchedulerLike, 6 | Subscription, 7 | } from 'rxjs'; 8 | import { observeOn, subscribeOn } from 'rxjs/operators'; 9 | 10 | type Work = (state?: T) => void; 11 | type Delay = number | undefined; 12 | 13 | abstract class ZoneScheduler implements SchedulerLike { 14 | constructor(protected ngZone: NgZone, protected scheduler: SchedulerLike) {} 15 | 16 | abstract schedule(...args: [Work, Delay, T]): Subscription; 17 | 18 | now(): number { 19 | return this.scheduler.now(); 20 | } 21 | } 22 | 23 | class LeaveZoneScheduler extends ZoneScheduler { 24 | override schedule(...args: [Work, Delay, T]): Subscription { 25 | return this.ngZone.runOutsideAngular(() => { 26 | return this.scheduler.schedule(...args); 27 | }); 28 | } 29 | } 30 | 31 | class EnterZoneScheduler extends ZoneScheduler { 32 | override schedule(...args: [Work, Delay, T]): Subscription { 33 | return NgZone.isInAngularZone() 34 | ? this.scheduler.schedule(...args) 35 | : this.ngZone.run(() => this.scheduler.schedule(...args)); 36 | } 37 | } 38 | 39 | export function enterNgZone( 40 | ngZone: NgZone, 41 | scheduler: SchedulerLike = asyncScheduler 42 | ): SchedulerLike { 43 | return new EnterZoneScheduler(ngZone, scheduler); 44 | } 45 | 46 | export function leaveNgZone( 47 | ngZone: NgZone, 48 | scheduler: SchedulerLike = asyncScheduler 49 | ): SchedulerLike { 50 | return new LeaveZoneScheduler(ngZone, scheduler); 51 | } 52 | 53 | @Injectable() 54 | export class RxNgZoneScheduler { 55 | constructor(private ngZone: NgZone) {} 56 | 57 | public observeOnNgZone( 58 | scheduler?: SchedulerLike 59 | ): MonoTypeOperatorFunction { 60 | return observeOn(this.enterNgZone(scheduler)); 61 | } 62 | 63 | public observeOutOfNgZone( 64 | scheduler?: SchedulerLike 65 | ): MonoTypeOperatorFunction { 66 | return observeOn(this.leaveNgZone(scheduler)); 67 | } 68 | 69 | public subscribeOnNgZone( 70 | scheduler?: SchedulerLike 71 | ): MonoTypeOperatorFunction { 72 | return subscribeOn(this.enterNgZone(scheduler)); 73 | } 74 | 75 | public subscribeOutOfNgZone( 76 | scheduler?: SchedulerLike 77 | ): MonoTypeOperatorFunction { 78 | return subscribeOn(this.leaveNgZone(scheduler)); 79 | } 80 | 81 | public enterNgZone(scheduler: SchedulerLike = asyncScheduler): SchedulerLike { 82 | return enterNgZone(this.ngZone, scheduler); 83 | } 84 | 85 | public leaveNgZone(scheduler: SchedulerLike = asyncScheduler): SchedulerLike { 86 | return leaveNgZone(this.ngZone, scheduler); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /libs/ngx-rxjs-zone-scheduler/src/test-setup.ts: -------------------------------------------------------------------------------- 1 | import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone'; 2 | 3 | setupZoneTestEnv(); 4 | -------------------------------------------------------------------------------- /libs/ngx-rxjs-zone-scheduler/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2022", 4 | "useDefineForClassFields": false, 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "noImplicitOverride": true, 8 | "noPropertyAccessFromIndexSignature": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true 11 | }, 12 | "files": [], 13 | "include": [], 14 | "references": [ 15 | { 16 | "path": "./tsconfig.lib.json" 17 | }, 18 | { 19 | "path": "./tsconfig.spec.json" 20 | } 21 | ], 22 | "extends": "../../tsconfig.base.json", 23 | "angularCompilerOptions": { 24 | "enableI18nLegacyMessageIdFormat": false, 25 | "strictInjectionParameters": true, 26 | "strictInputAccessModifiers": true, 27 | "strictTemplates": true 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /libs/ngx-rxjs-zone-scheduler/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "declaration": true, 6 | "declarationMap": true, 7 | "inlineSources": true, 8 | "types": [] 9 | }, 10 | "exclude": [ 11 | "src/**/*.spec.ts", 12 | "src/test-setup.ts", 13 | "jest.config.ts", 14 | "src/**/*.test.ts" 15 | ], 16 | "include": ["src/**/*.ts"] 17 | } 18 | -------------------------------------------------------------------------------- /libs/ngx-rxjs-zone-scheduler/tsconfig.lib.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.lib.json", 3 | "compilerOptions": { 4 | "declarationMap": false 5 | }, 6 | "angularCompilerOptions": { 7 | "compilationMode": "partial" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /libs/ngx-rxjs-zone-scheduler/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "target": "es2016", 7 | "types": ["jest", "node"] 8 | }, 9 | "files": ["src/test-setup.ts"], 10 | "include": [ 11 | "jest.config.ts", 12 | "src/**/*.test.ts", 13 | "src/**/*.spec.ts", 14 | "src/**/*.d.ts" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /nx.json: -------------------------------------------------------------------------------- 1 | { 2 | "cli": { 3 | "packageManager": "yarn" 4 | }, 5 | "generators": { 6 | "@nx/angular:application": { 7 | "style": "scss", 8 | "linter": "eslint", 9 | "unitTestRunner": "jest", 10 | "e2eTestRunner": "cypress" 11 | }, 12 | "@nx/angular:library": { 13 | "linter": "eslint", 14 | "unitTestRunner": "jest" 15 | }, 16 | "@nx/angular:component": { 17 | "style": "scss", 18 | "standalone": true 19 | } 20 | }, 21 | "defaultProject": "demo-app", 22 | "$schema": "./node_modules/nx/schemas/nx-schema.json", 23 | "targetDefaults": { 24 | "build": { 25 | "dependsOn": ["^build"], 26 | "inputs": ["production", "^production"], 27 | "cache": true 28 | }, 29 | "@nx/jest:jest": { 30 | "inputs": ["default", "^production", "{workspaceRoot}/jest.preset.js"], 31 | "cache": true, 32 | "options": { 33 | "passWithNoTests": true 34 | }, 35 | "configurations": { 36 | "ci": { 37 | "ci": true, 38 | "codeCoverage": true 39 | } 40 | } 41 | }, 42 | "@nx/eslint:lint": { 43 | "inputs": ["default", "{workspaceRoot}/.eslintrc.json"], 44 | "cache": true 45 | } 46 | }, 47 | "namedInputs": { 48 | "default": ["{projectRoot}/**/*", "sharedGlobals"], 49 | "sharedGlobals": [], 50 | "production": [ 51 | "default", 52 | "!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)", 53 | "!{projectRoot}/tsconfig.spec.json", 54 | "!{projectRoot}/jest.config.[jt]s", 55 | "!{projectRoot}/.eslintrc.json", 56 | "!{projectRoot}/src/test-setup.[jt]s" 57 | ] 58 | }, 59 | "useInferencePlugins": false, 60 | "defaultBase": "main", 61 | "nxCloudId": "6787b94036998c25c5850b4e" 62 | } 63 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ngx-rxjs-zone-scheduler", 3 | "version": "19.0.0", 4 | "license": "MIT", 5 | "repository": "https://github.com/ftischler/ngx-rxjs-zone-scheduler", 6 | "scripts": { 7 | "ng": "nx", 8 | "start": "nx serve demo-app", 9 | "build": "nx build --prod --project ngx-rxjs-zone-scheduler", 10 | "test": "nx test" 11 | }, 12 | "dependencies": { 13 | "@angular/animations": "19.0.6", 14 | "@angular/common": "19.0.6", 15 | "@angular/compiler": "19.0.6", 16 | "@angular/core": "19.0.6", 17 | "@angular/forms": "19.0.6", 18 | "@angular/platform-browser": "19.0.6", 19 | "@angular/platform-browser-dynamic": "19.0.6", 20 | "@angular/router": "19.0.6", 21 | "@nx/angular": "20.3.1", 22 | "reflect-metadata": "0.1.13", 23 | "rxjs": "7.8.1", 24 | "tslib": "2.8.1", 25 | "zone.js": "0.15.0" 26 | }, 27 | "devDependencies": { 28 | "@angular-devkit/build-angular": "19.0.7", 29 | "@angular-devkit/core": "19.0.7", 30 | "@angular-devkit/schematics": "19.0.7", 31 | "@angular-eslint/eslint-plugin": "19.0.2", 32 | "@angular-eslint/eslint-plugin-template": "19.0.2", 33 | "@angular-eslint/template-parser": "19.0.2", 34 | "@angular/cli": "~19.0.0", 35 | "@angular/compiler-cli": "19.0.6", 36 | "@angular/language-service": "19.0.6", 37 | "@nx/cypress": "20.3.1", 38 | "@nx/eslint-plugin": "20.3.1", 39 | "@nx/jest": "20.3.1", 40 | "@nx/eslint": "20.3.1", 41 | "@nx/node": "20.3.1", 42 | "@nx/workspace": "20.3.1", 43 | "@schematics/angular": "19.0.7", 44 | "@types/jest": "29.5.14", 45 | "@types/node": "18.16.9", 46 | "@typescript-eslint/eslint-plugin": "7.18.0", 47 | "@typescript-eslint/parser": "7.18.0", 48 | "@typescript-eslint/utils": "7.18.0", 49 | "autoprefixer": "^10.4.0", 50 | "cypress": "13.17.0", 51 | "eslint": "8.57.0", 52 | "eslint-config-prettier": "9.1.0", 53 | "eslint-plugin-cypress": "2.15.1", 54 | "jest": "29.7.0", 55 | "jest-environment-jsdom": "29.7.0", 56 | "jest-preset-angular": "14.4.2", 57 | "ng-packagr": "19.0.1", 58 | "nx": "20.3.1", 59 | "postcss": "8.4.49", 60 | "postcss-import": "15.1.0", 61 | "postcss-preset-env": "7.8.3", 62 | "postcss-url": "10.1.3", 63 | "prettier": "2.7.1", 64 | "ts-jest": "29.1.0", 65 | "typescript": "5.6.3" 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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": "es2022", 12 | "module": "es2020", 13 | "lib": ["es2017", "dom"], 14 | "skipLibCheck": true, 15 | "skipDefaultLibCheck": true, 16 | "baseUrl": ".", 17 | "paths": { 18 | "ngx-rxjs-zone-scheduler": ["libs/ngx-rxjs-zone-scheduler/src/index.ts"] 19 | } 20 | }, 21 | "exclude": ["node_modules", "tmp"] 22 | } 23 | --------------------------------------------------------------------------------