From 68b119616836c3b30e20a393e4d51cc0e02f88be Mon Sep 17 00:00:00 2001 From: Nathan Walker Date: Mon, 17 Aug 2026 10:11:52 -0700 Subject: [PATCH 1/8] fix: bootstrap NG0210 when the runtime exposes a global PerformanceObserver (#178) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Angular's dev-mode ImagePerformanceWarning only bails out early when PerformanceObserver is missing or both image warnings are disabled. NativeScript runtimes that ship a Web Performance API now define that global, so the service proceeds to getDocument() and throws NG0210, failing every debug boot. Default IMAGE_CONFIG to disabled for both warnings — NativeScript has no elements for them to scan. Apps can still override it, since their own providers are applied after the NativeScript ones. (cherry picked from commit 6daed0021a0560e59c1bd1bfd1efeb550cca6f1e) --- .../src/tests/image-config.spec.ts | 20 +++++++++++++++++++ packages/angular/src/lib/nativescript.ts | 10 ++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 apps/nativescript-demo-ng/src/tests/image-config.spec.ts diff --git a/apps/nativescript-demo-ng/src/tests/image-config.spec.ts b/apps/nativescript-demo-ng/src/tests/image-config.spec.ts new file mode 100644 index 00000000..f4ff3131 --- /dev/null +++ b/apps/nativescript-demo-ng/src/tests/image-config.spec.ts @@ -0,0 +1,20 @@ +import { IMAGE_CONFIG } from '@angular/common'; +import { TestBed } from '@angular/core/testing'; +import { NativeScriptModule } from '@nativescript/angular'; + +describe('IMAGE_CONFIG', () => { + beforeEach(() => { + return TestBed.configureTestingModule({ + imports: [NativeScriptModule], + }).compileComponents(); + }); + + // Both flags are required: with either one unset, Angular reaches for `document` during + // bootstrap on any runtime that exposes a global PerformanceObserver. + it('disables both dev-mode image warnings', () => { + const config = TestBed.inject(IMAGE_CONFIG); + + expect(config.disableImageSizeWarning).toBe(true); + expect(config.disableImageLazyLoadWarning).toBe(true); + }); +}); diff --git a/packages/angular/src/lib/nativescript.ts b/packages/angular/src/lib/nativescript.ts index 7fb656d8..bb7aaccd 100644 --- a/packages/angular/src/lib/nativescript.ts +++ b/packages/angular/src/lib/nativescript.ts @@ -1,4 +1,4 @@ -import { ViewportScroller, XhrFactory, ɵNullViewportScroller as NullViewportScroller } from '@angular/common'; +import { IMAGE_CONFIG, ViewportScroller, XhrFactory, ɵNullViewportScroller as NullViewportScroller } from '@angular/common'; import { ApplicationModule, ErrorHandler, Inject, NgModule, NO_ERRORS_SCHEMA, Optional, Provider, RendererFactory2, SkipSelf, StaticProvider, ɵINJECTOR_SCOPE as INJECTOR_SCOPE } from '@angular/core'; import { Color, Device, View } from '@nativescript/core'; import { AppHostView } from './app-host-view'; @@ -40,7 +40,13 @@ export const NATIVESCRIPT_MODULE_STATIC_PROVIDERS: StaticProvider[] = [ { provide: DEVICE, useValue: Device }, { provide: XhrFactory, useClass: NativescriptXhrFactory, deps: [] }, ]; -export const NATIVESCRIPT_MODULE_PROVIDERS: Provider[] = [{ provide: ViewportScroller, useClass: NullViewportScroller }]; +export const NATIVESCRIPT_MODULE_PROVIDERS: Provider[] = [ + { provide: ViewportScroller, useClass: NullViewportScroller }, + // Angular's dev-mode image warnings scan the DOM for elements, which + // NativeScript has none of. Angular only skips them when both flags are set, + // and reaches for `document` as soon as a global PerformanceObserver exists. + { provide: IMAGE_CONFIG, useValue: { disableImageSizeWarning: true, disableImageLazyLoadWarning: true } }, +]; @NgModule({ imports: [ApplicationModule, DetachedLoader, NativeScriptCommonModule], From 834e10ef1e0198027153b10485124d5aad1acb02 Mon Sep 17 00:00:00 2001 From: Eduardo Speroni Date: Mon, 17 Aug 2026 16:05:27 -0300 Subject: [PATCH 2/8] fix: answer DOM probes on NativeScriptDocument instead of throwing (#176) * Angular discovers some optional configuration by reading the DOM. The CSP_NONCE default factory, for example, evaluates `DOCUMENT.body.querySelector('[ngCspNonce]')`. `NativeScriptDocument.body` was a hollow object, so the optional chaining in those probes did not help and they failed with `body?.querySelector is not a function`. Signal forms reach this on the first `[formField]` binding, which crashed every signal-form dialog at construction. * `querySelector`, `querySelectorAll`, `getElementById`, `getElementsByTagName` and `getElementsByClassName` now answer "nothing found" on the document and on `body`/`head`. `createElement` still throws, since constructing real nodes is a caller bug rather than a probe. * CSP_NONCE is also provided as `null`, as there is no CSP in a NativeScript runtime. It has to be a root provider: the root injector resolves `providedIn: 'root'` tokens itself before consulting the platform injector, so providing it in COMMON_PROVIDERS would have had no effect. (cherry picked from commit 5a5315d19355461c59d5d7ee90f55957e3cab2b0) --- packages/angular/src/lib/nativescript.ts | 6 +++- .../angular/src/lib/platform-nativescript.ts | 29 +++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/packages/angular/src/lib/nativescript.ts b/packages/angular/src/lib/nativescript.ts index bb7aaccd..1b5cb764 100644 --- a/packages/angular/src/lib/nativescript.ts +++ b/packages/angular/src/lib/nativescript.ts @@ -1,5 +1,5 @@ import { IMAGE_CONFIG, ViewportScroller, XhrFactory, ɵNullViewportScroller as NullViewportScroller } from '@angular/common'; -import { ApplicationModule, ErrorHandler, Inject, NgModule, NO_ERRORS_SCHEMA, Optional, Provider, RendererFactory2, SkipSelf, StaticProvider, ɵINJECTOR_SCOPE as INJECTOR_SCOPE } from '@angular/core'; +import { ApplicationModule, CSP_NONCE, ErrorHandler, Inject, NgModule, NO_ERRORS_SCHEMA, Optional, Provider, RendererFactory2, SkipSelf, StaticProvider, ɵINJECTOR_SCOPE as INJECTOR_SCOPE } from '@angular/core'; import { Color, Device, View } from '@nativescript/core'; import { AppHostView } from './app-host-view'; import { NativescriptXhrFactory } from './nativescript-xhr-factory'; @@ -39,6 +39,10 @@ export const NATIVESCRIPT_MODULE_STATIC_PROVIDERS: StaticProvider[] = [ { provide: NAMESPACE_FILTERS, useClass: PlatformNamespaceFilter, deps: [DEVICE], multi: true }, { provide: DEVICE, useValue: Device }, { provide: XhrFactory, useClass: NativescriptXhrFactory, deps: [] }, + // No CSP in a NativeScript runtime. Providing it also stops the token's default + // factory reading `DOCUMENT.body.querySelector()`. Must stay root-scoped: the root + // injector resolves `providedIn: 'root'` tokens before consulting the platform. + { provide: CSP_NONCE, useValue: null }, ]; export const NATIVESCRIPT_MODULE_PROVIDERS: Provider[] = [ { provide: ViewportScroller, useClass: NullViewportScroller }, diff --git a/packages/angular/src/lib/platform-nativescript.ts b/packages/angular/src/lib/platform-nativescript.ts index 321174f3..3989a1ff 100644 --- a/packages/angular/src/lib/platform-nativescript.ts +++ b/packages/angular/src/lib/platform-nativescript.ts @@ -37,12 +37,41 @@ export class NativeScriptSanitizer extends Sanitizer { // }; // } +/** + * Angular probes the DOM to discover optional configuration it may have been handed + * out of band - a `ngCspNonce` attribute on ``, a transfer state `