@ng-native/platform 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ashley Hunter
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,57 @@
1
1
  # @ng-native/platform
2
2
 
3
- A placeholder: this version has no code. See https://ng-native.com.
3
+ The only Angular-aware file in the rendering stack: a `Renderer2`/`RendererFactory2` pair that
4
+ turns Angular's renderer calls into mutations on `@ng-native/fabric`'s retained tree, and `mount()`,
5
+ which stands in for `bootstrapApplication` plus `@angular/platform-browser`.
6
+
7
+ Alpha: APIs may change before 1.0.
8
+
9
+ ## Install
10
+
11
+ Most apps start from `npx create-expo-app@latest my-app --template @ng-native/template`, which
12
+ already calls `mount()` in `index.ts`. Otherwise:
13
+
14
+ ```sh
15
+ npm install @ng-native/platform
16
+ npm install @angular/core react-native
17
+ ```
18
+
19
+ `@angular/common` is an optional peer, needed only for `provideNativeHttpClient`.
20
+
21
+ ## Example
22
+
23
+ ```ts
24
+ // index.ts
25
+ import { AppRegistry, Image, Platform, processColor } from 'react-native';
26
+ import { mount } from '@ng-native/platform';
27
+ import { getFabricUIManager, registerPlatformComponents } from '@ng-native/fabric';
28
+ import { App } from './app.ts';
29
+
30
+ registerPlatformComponents(Platform.OS);
31
+
32
+ AppRegistry.registerRunnable('main', ({ rootTag }) => {
33
+ mount(Number(rootTag), App, getFabricUIManager(), {
34
+ processColor,
35
+ resolveAssetSource: (value) => Image.resolveAssetSource(value as never),
36
+ });
37
+ });
38
+ ```
39
+
40
+ ## What's in the package
41
+
42
+ - `.` - `mount()`, `NativeRendererFactory`, `NativeRenderer`.
43
+ - `./http` - `provideNativeHttpClient()`, an `HttpClient` backend over React Native's
44
+ `XMLHttpRequest` (Angular's default `fetch` backend cannot read a React Native `fetch` response
45
+ body). Use it in place of `provideHttpClient()`.
46
+
47
+ ## Docs
48
+
49
+ - [Platform](https://ng-native.com/packages/platform)
50
+ - [Bootstrapping](https://ng-native.com/packages/platform/bootstrapping) and
51
+ [renderer](https://ng-native.com/packages/platform/renderer)
52
+ - [Root README](https://github.com/ng-native/ng-native/blob/main/README.md) and
53
+ [ARCHITECTURE.md](https://github.com/ng-native/ng-native/blob/main/ARCHITECTURE.md)
54
+
55
+ ## License
56
+
57
+ MIT
@@ -0,0 +1,100 @@
1
+ /**
2
+ * The only Angular-aware file: the engine below it never imports Angular. Renderer2 over the
3
+ * engine, plus bootstrap.
4
+ *
5
+ * Bootstrap leans on private `ɵ` exports of `@angular/core`, which can change in any major.
6
+ * `bootstrap.test.ts` asserts each one still exists, so an upgrade fails there rather than on a
7
+ * device.
8
+ */
9
+ import { ApplicationRef, Renderer2, RendererFactory2, type ComponentRef, type EnvironmentProviders, type Provider, type Type } from '@angular/core';
10
+ import { Engine, type EngineOptions, type FabricUIManager } from '@ng-native/fabric';
11
+ /**
12
+ * The slice of Angular's component definition `createRenderer` receives that says which element
13
+ * names a template can legitimately contain. Private shape, read only to keep the engine's typo
14
+ * check quiet, so a missing piece means "nothing to declare" rather than an error.
15
+ */
16
+ interface ComponentDefLike {
17
+ id?: string;
18
+ type?: unknown;
19
+ tView?: {
20
+ directiveRegistry?: readonly {
21
+ selectors?: readonly (readonly unknown[])[];
22
+ inputs?: object;
23
+ }[] | null;
24
+ } | null;
25
+ ngContentSelectors?: readonly string[];
26
+ }
27
+ export declare class NativeRendererFactory implements RendererFactory2 {
28
+ private readonly renderer;
29
+ /** One renderer per component type, because each carries its own stylesheet. */
30
+ private readonly byComponent;
31
+ readonly engine: Engine;
32
+ constructor(engine: Engine);
33
+ /**
34
+ * Angular hands us the component definition here, which is how emulated encapsulation gets
35
+ * scoped: the renderer a component uses is bound to that component's compiled stylesheet, so
36
+ * its elements match its rules and nobody else's.
37
+ */
38
+ createRenderer(host: unknown, type: ComponentDefLike | null): Renderer2;
39
+ /**
40
+ * Angular's HMR calls this before it recreates a hot-swapped component's views. The renderer
41
+ * cached for that component is bound to the sheet it had, so an edited stylesheet would never
42
+ * reach the recreated views without letting it go.
43
+ */
44
+ componentReplaced(id: string): void;
45
+ /** Component definitions whose templates have been looked through already. */
46
+ private readonly declared;
47
+ /**
48
+ * Tell the engine every element name this component's template may select by tag.
49
+ *
50
+ * Angular builds a component's template view, with its directive registry resolved from the
51
+ * component's `imports`, before it asks for the renderer, so the registry is in hand here and
52
+ * before the template that uses those names runs. This is what keeps a directive that owns an
53
+ * element without hosting a component, such as Angular's `router-outlet`, from reading as a
54
+ * typo; a component host is marked directly instead, above.
55
+ */
56
+ private declareElements;
57
+ /**
58
+ * Tell the engine the attribute names that are Angular's rather than a native view's: what a
59
+ * directive selects on or takes as an input, which Angular also writes to the node as a static
60
+ * attribute, and what the component's `<ng-content select>` projects by.
61
+ */
62
+ private declareAttributes;
63
+ private renderStarted;
64
+ private frame;
65
+ /**
66
+ * Drive CSS transitions.
67
+ *
68
+ * A transition is not a binding: nothing in Angular changes while one runs, so change detection
69
+ * would never fire again and the animation would stop on its first frame. This pumps the engine
70
+ * directly for as long as one has frames left, and commits each one.
71
+ */
72
+ private readonly pump;
73
+ private schedule;
74
+ /**
75
+ * A change that lands outside a render pass has no commit coming. The engine reports the moment
76
+ * the tree goes dirty, and this arranges a frame for it, which is how the classes Angular's
77
+ * animation queue adds after a pass reach native at all.
78
+ */
79
+ readonly commitSoon: () => void;
80
+ private rendering;
81
+ begin(): void;
82
+ /** The single flush point: at most one commit per change-detection pass. */
83
+ end(): void;
84
+ }
85
+ export interface MountResult {
86
+ componentRef: ComponentRef<unknown>;
87
+ applicationRef: ApplicationRef;
88
+ factory: NativeRendererFactory;
89
+ engine: Engine;
90
+ }
91
+ export declare function mount(rootTag: number, component: Type<unknown>, fabric: FabricUIManager, options?: EngineOptions & {
92
+ providers?: (Provider | EnvironmentProviders)[];
93
+ /**
94
+ * Root component inputs, set before its first change detection. That is the only moment a
95
+ * `required` input can be given, and `mount` runs that first pass itself, so a caller cannot
96
+ * set one afterwards. What `render()` in `@ng-native/testing` passes a component's inputs as.
97
+ */
98
+ inputs?: Record<string, unknown>;
99
+ }): MountResult;
100
+ export {};
@@ -0,0 +1,539 @@
1
+ /**
2
+ * The only Angular-aware file: the engine below it never imports Angular. Renderer2 over the
3
+ * engine, plus bootstrap.
4
+ *
5
+ * Bootstrap leans on private `ɵ` exports of `@angular/core`, which can change in any major.
6
+ * `bootstrap.test.ts` asserts each one still exists, so an upgrade fails there rather than on a
7
+ * device.
8
+ */
9
+ import { APP_BOOTSTRAP_LISTENER, ApplicationInitStatus, ApplicationRef, DOCUMENT, ErrorHandler, Renderer2, RendererFactory2, createComponent, createEnvironmentInjector, PLATFORM_ID, ɵcreateOrReusePlatformInjector as createOrReusePlatformInjector, ɵINJECTOR_SCOPE as INJECTOR_SCOPE, ɵprovideZonelessChangeDetectionInternal as provideZonelessChangeDetectionInternal, } from '@angular/core';
10
+ import { installDateParse } from "./date-parse.js";
11
+ import { PLATFORM_NATIVE_ID } from "./platform-id.js";
12
+ import { Engine, HostEngine, installDeferTriggers, markComponentHost, nativePlatform, reportUnboundFormsInput, styleSheetOf, } from '@ng-native/fabric';
13
+ import { calmLoadingBanner } from "./dev-loading-view.js";
14
+ /**
15
+ * Angular `(touchEnd)` -> Fabric `topTouchEnd`.
16
+ *
17
+ * There is no alias for `press`: `onPress` is not a native event, RN synthesises it in JS from
18
+ * the touch responder system. `Pressable` in `components.ts` does the same, and because Angular
19
+ * resolves a directive output before an element event, `(press)` on a `<pressable>` binds to
20
+ * that output rather than reaching the renderer at all.
21
+ */
22
+ function topLevelType(eventName) {
23
+ return 'top' + eventName.charAt(0).toUpperCase() + eventName.slice(1);
24
+ }
25
+ /**
26
+ * What Angular's compiler emits for a style binding is aimed at the DOM: `[style.font-size]`
27
+ * arrives dash-cased, and `[style.width.px]` arrives as the string `'10px'`. Fabric reads
28
+ * camelCase keys and plain numbers, and drops both forms without a word.
29
+ */
30
+ const PX = /^-?\d*\.?\d+px$/;
31
+ const NUMBER = /^-?\d*\.?\d+$/;
32
+ const styleKey = (name) => name.includes('-') ? name.replace(/-([a-z])/g, (_, c) => c.toUpperCase()) : name;
33
+ /**
34
+ * A bare number arrives as a string too, and Yoga wants a number.
35
+ *
36
+ * Angular parses `[style]="'flex: 1'"` itself and calls `setStyle('flex', '1')`, so the same
37
+ * declaration written as an object gave `1` and written as a string gave `'1'` - the two spellings
38
+ * of one design disagreeing at the only layer that can tell.
39
+ */
40
+ const styleValue = (value) => typeof value === 'string' && (PX.test(value) || NUMBER.test(value)) ? parseFloat(value) : value;
41
+ /**
42
+ * `flex: 1; margin-top: 4px` -> `{ flex: 1, marginTop: 4 }`.
43
+ *
44
+ * Only the static-attribute path needs this. Angular compiles `[style]` and `[style.x]` into
45
+ * styling instructions that parse the string themselves and arrive one declaration at a time; a
46
+ * *static* `style="..."` is not a binding at all, so it comes through `setAttribute` whole.
47
+ *
48
+ * Splitting on `;` and `:` rather than parsing properly, because the values that reach here are
49
+ * lengths, numbers, keywords and colours. A `url()` or a quoted font stack containing either
50
+ * character would be cut in the wrong place - neither is expressible on this platform, and the
51
+ * one thing worse than not supporting them would be appearing to.
52
+ */
53
+ /**
54
+ * A bound custom property's name, as it was written. Angular 22 compiles `[style.--tint]` to
55
+ * `--%NS%tint`, for a DOM renderer to fill with a namespace; there is none here.
56
+ */
57
+ const customPropertyName = (style) => style.replace(/%NS%/g, '');
58
+ function parseStyleAttribute(css) {
59
+ const out = {};
60
+ for (const declaration of css.split(';')) {
61
+ const at = declaration.indexOf(':');
62
+ if (at === -1)
63
+ continue;
64
+ const name = declaration.slice(0, at).trim();
65
+ const value = declaration.slice(at + 1).trim();
66
+ // A custom property keeps its name, which is case-sensitive, and its text for the engine.
67
+ if (name.startsWith('--'))
68
+ out[name] = value;
69
+ else if (name)
70
+ out[styleKey(name)] = styleValue(value);
71
+ }
72
+ return out;
73
+ }
74
+ class NativeRenderer {
75
+ data = Object.create(null);
76
+ destroyNode;
77
+ engine;
78
+ /** The creating component's compiled CSS. Elements this renderer makes are matched against it. */
79
+ sheet;
80
+ constructor(engine, sheet = null) {
81
+ this.engine = engine;
82
+ this.sheet = sheet;
83
+ this.destroyNode = (node) => this.engine.destroyNode(node);
84
+ }
85
+ destroy() { }
86
+ createElement(name) {
87
+ return this.engine.createElement(name, this.sheet);
88
+ }
89
+ createComment() {
90
+ return this.engine.createAnchor();
91
+ }
92
+ createText(value) {
93
+ return this.engine.createText(value);
94
+ }
95
+ appendChild(parent, newChild) {
96
+ this.engine.appendChild(parent, newChild);
97
+ }
98
+ insertBefore(parent, newChild, refChild) {
99
+ if (!parent)
100
+ return;
101
+ this.engine.insertBefore(parent, newChild, refChild);
102
+ }
103
+ removeChild(parent, oldChild) {
104
+ this.engine.removeChild(parent, oldChild);
105
+ }
106
+ selectRootElement(selectorOrNode) {
107
+ if (typeof selectorOrNode !== 'string')
108
+ return selectorOrNode;
109
+ return this.engine.root;
110
+ }
111
+ parentNode(node) {
112
+ return this.engine.parentNode(node);
113
+ }
114
+ nextSibling(node) {
115
+ return this.engine.nextSibling(node);
116
+ }
117
+ setAttribute(el, name, value) {
118
+ // `class` drives selector matching, not a native prop: nothing on the native side reads it.
119
+ if (name === 'class') {
120
+ this.engine.setClasses(el, value);
121
+ return;
122
+ }
123
+ /*
124
+ * A *static* `style="flex: 1"` is not a binding, so it never becomes a styling instruction -
125
+ * it arrives here as one attribute holding the whole declaration list. Handing that string on
126
+ * as the `style` prop meant Fabric received a string where it merges objects, which it drops
127
+ * without a word: every static style in the codebase, and in this project's own doc comments,
128
+ * was doing nothing at all.
129
+ */
130
+ if (name === 'style') {
131
+ for (const [key, next] of Object.entries(parseStyleAttribute(value))) {
132
+ this.setStyle(el, key, next);
133
+ }
134
+ return;
135
+ }
136
+ this.engine.setProp(el, name, value);
137
+ }
138
+ removeAttribute(el, name) {
139
+ this.engine.setProp(el, name, null);
140
+ }
141
+ addClass(el, name) {
142
+ this.engine.addClass(el, name);
143
+ }
144
+ removeClass(el, name) {
145
+ this.engine.removeClass(el, name);
146
+ }
147
+ setStyle(el, style, value, _flags) {
148
+ if (style.startsWith('--')) {
149
+ this.engine.setCustomProperty(el, customPropertyName(style), value);
150
+ return;
151
+ }
152
+ const key = styleKey(style);
153
+ const next = styleValue(value);
154
+ const current = el.props['style'];
155
+ if (current && current === el.ownStyle) {
156
+ if (current[key] === next)
157
+ return;
158
+ current[key] = next;
159
+ this.engine.styleChanged(el);
160
+ return;
161
+ }
162
+ const owned = { ...(current ?? {}), [key]: next };
163
+ el.ownStyle = owned;
164
+ this.engine.setProp(el, 'style', owned);
165
+ }
166
+ removeStyle(el, style, _flags) {
167
+ if (style.startsWith('--')) {
168
+ this.engine.setCustomProperty(el, customPropertyName(style), null);
169
+ return;
170
+ }
171
+ const current = el.props['style'];
172
+ if (!current || !(styleKey(style) in current))
173
+ return;
174
+ if (current === el.ownStyle) {
175
+ delete current[styleKey(style)];
176
+ this.engine.styleChanged(el);
177
+ return;
178
+ }
179
+ const { [styleKey(style)]: _removed, ...rest } = current;
180
+ el.ownStyle = rest;
181
+ this.engine.setProp(el, 'style', rest);
182
+ }
183
+ setProperty(el, name, value) {
184
+ if (this.engine.dev)
185
+ reportUnboundFormsInput(this.engine, el, name);
186
+ this.engine.setProp(el, name, value);
187
+ }
188
+ setValue(node, value) {
189
+ this.engine.setText(node, value);
190
+ }
191
+ listen(target, eventName, callback) {
192
+ return this.engine.setEventListener(target, topLevelType(eventName), callback);
193
+ }
194
+ }
195
+ /** The attribute names in a projection selector such as `[listHeader]` or `text[slot=a]`. */
196
+ const PROJECTED_ATTRIBUTE = /\[([^\]=~|^$*\s]+)/g;
197
+ export class NativeRendererFactory {
198
+ renderer;
199
+ /** One renderer per component type, because each carries its own stylesheet. */
200
+ byComponent = new Map();
201
+ engine;
202
+ constructor(engine) {
203
+ this.engine = engine;
204
+ this.renderer = new NativeRenderer(engine);
205
+ }
206
+ /**
207
+ * Angular hands us the component definition here, which is how emulated encapsulation gets
208
+ * scoped: the renderer a component uses is bound to that component's compiled stylesheet, so
209
+ * its elements match its rules and nobody else's.
210
+ */
211
+ createRenderer(host, type) {
212
+ const sheet = styleSheetOf(type?.type);
213
+ // `host` is the element this component is mounted on, and Angular created it with the
214
+ // *parent's* renderer, so it carries the parent's sheet. Tagging it here is the only moment
215
+ // the two are both in hand, and it is what makes `:host` reachable at all.
216
+ //
217
+ // It is also the one sure sign that an element hosts a component, whatever the selector
218
+ // that put it there, which is what tells an `<x-card>` from a typo.
219
+ if (host && typeof host === 'object') {
220
+ host.hostSheet = sheet;
221
+ markComponentHost(host);
222
+ }
223
+ if (type)
224
+ this.declareElements(type);
225
+ if (!sheet)
226
+ return this.renderer;
227
+ const key = type?.id ?? '';
228
+ let renderer = this.byComponent.get(key);
229
+ if (!renderer) {
230
+ renderer = new NativeRenderer(this.engine, sheet);
231
+ this.byComponent.set(key, renderer);
232
+ }
233
+ return renderer;
234
+ }
235
+ /**
236
+ * Angular's HMR calls this before it recreates a hot-swapped component's views. The renderer
237
+ * cached for that component is bound to the sheet it had, so an edited stylesheet would never
238
+ * reach the recreated views without letting it go.
239
+ */
240
+ componentReplaced(id) {
241
+ this.byComponent.delete(id);
242
+ }
243
+ /** Component definitions whose templates have been looked through already. */
244
+ declared = new WeakSet();
245
+ /**
246
+ * Tell the engine every element name this component's template may select by tag.
247
+ *
248
+ * Angular builds a component's template view, with its directive registry resolved from the
249
+ * component's `imports`, before it asks for the renderer, so the registry is in hand here and
250
+ * before the template that uses those names runs. This is what keeps a directive that owns an
251
+ * element without hosting a component, such as Angular's `router-outlet`, from reading as a
252
+ * typo; a component host is marked directly instead, above.
253
+ */
254
+ declareElements(def) {
255
+ // Only once the template view exists. The root component asks for a renderer twice, first
256
+ // without a host and before its template view is built, and marking it seen then would
257
+ // skip the call that has the registry.
258
+ const registry = def.tView?.directiveRegistry;
259
+ if (!registry || this.declared.has(def))
260
+ return;
261
+ this.declared.add(def);
262
+ for (const directive of registry) {
263
+ for (const selector of directive.selectors ?? []) {
264
+ const tag = selector[0];
265
+ if (typeof tag === 'string' && tag)
266
+ this.engine.declareElement(tag);
267
+ }
268
+ }
269
+ this.declareAttributes(def, registry);
270
+ }
271
+ /**
272
+ * Tell the engine the attribute names that are Angular's rather than a native view's: what a
273
+ * directive selects on or takes as an input, which Angular also writes to the node as a static
274
+ * attribute, and what the component's `<ng-content select>` projects by.
275
+ */
276
+ declareAttributes(def, registry) {
277
+ for (const directive of registry) {
278
+ for (const selector of directive.selectors ?? []) {
279
+ for (const name of selector.filter((part, at) => at % 2 === 1)) {
280
+ if (typeof name === 'string')
281
+ this.engine.declareAttribute(name);
282
+ }
283
+ }
284
+ for (const input of Object.keys(directive.inputs ?? {}))
285
+ this.engine.declareAttribute(input);
286
+ }
287
+ for (const selector of def.ngContentSelectors ?? []) {
288
+ for (const [, name] of selector.matchAll(PROJECTED_ATTRIBUTE)) {
289
+ this.engine.declareAttribute(name);
290
+ }
291
+ }
292
+ }
293
+ renderStarted = 0;
294
+ frame = 0;
295
+ /**
296
+ * Drive CSS transitions.
297
+ *
298
+ * A transition is not a binding: nothing in Angular changes while one runs, so change detection
299
+ * would never fire again and the animation would stop on its first frame. This pumps the engine
300
+ * directly for as long as one has frames left, and commits each one.
301
+ */
302
+ pump = () => {
303
+ this.frame = 0;
304
+ this.engine.advanceAnimations();
305
+ // Also the commit for anything changed outside change detection. Angular's `animate.enter`
306
+ // and `animate.leave` add their classes from an animation queue that runs after the render
307
+ // pass, so without a frame behind it the class would sit on the node and never reach native.
308
+ this.engine.commit();
309
+ // `pending` as well as `transitioning`: a transition's end listener runs after the commit
310
+ // that finished it, and removing a leaving element is exactly what it tends to do.
311
+ if (this.engine.animating || this.engine.pending)
312
+ this.schedule();
313
+ };
314
+ schedule() {
315
+ if (this.frame)
316
+ return;
317
+ // `requestAnimationFrame` is a global in React Native. The fallback is for Node, where the
318
+ // suite drives `advanceTransitions` itself and this never runs.
319
+ const request = globalThis.requestAnimationFrame ??
320
+ ((callback) => setTimeout(() => callback(Date.now()), 16));
321
+ this.frame = request(this.pump);
322
+ }
323
+ /**
324
+ * A change that lands outside a render pass has no commit coming. The engine reports the moment
325
+ * the tree goes dirty, and this arranges a frame for it, which is how the classes Angular's
326
+ * animation queue adds after a pass reach native at all.
327
+ */
328
+ commitSoon = () => {
329
+ if (!this.rendering)
330
+ this.schedule();
331
+ };
332
+ rendering = false;
333
+ begin() {
334
+ this.rendering = true;
335
+ this.renderStarted = globalThis.performance?.now?.() ?? Date.now();
336
+ }
337
+ /** The single flush point: at most one commit per change-detection pass. */
338
+ end() {
339
+ this.rendering = false;
340
+ this.engine.commit();
341
+ // A commit is what starts a transition, because it is where the cascade is recomputed.
342
+ if (this.engine.animating)
343
+ this.schedule();
344
+ // Excludes the mount, which is not representative of a steady-state frame.
345
+ const elapsed = (globalThis.performance?.now?.() ?? Date.now()) - this.renderStarted;
346
+ const stats = this.engine.stats;
347
+ if (stats.commits > 1 && elapsed > stats.worstRenderMs)
348
+ stats.worstRenderMs = elapsed;
349
+ }
350
+ }
351
+ /**
352
+ * `ɵINJECTOR_SCOPE` and `DOCUMENT` are both load-bearing: without the first Angular
353
+ * cannot resolve `ChangeDetectionSchedulerImpl` (NG0201), without the second
354
+ * `getStyleHost` throws NG0210.
355
+ */
356
+ /**
357
+ * Angular decides whether `animate.enter` and `animate.leave` do anything when `@angular/core` is
358
+ * first evaluated, by reading a global that a Metro polyfill has to have set by then. Nothing
359
+ * later can change the answer, so the most that can be done is to notice and say so. Checked here
360
+ * because booting is the one moment every app passes through.
361
+ */
362
+ function warnIfAnimationsAreOff() {
363
+ const document = globalThis
364
+ .document;
365
+ if (typeof document?.documentElement?.getAnimations === 'function')
366
+ return;
367
+ console.error('[angular-native] `animate.enter` and `animate.leave` will do nothing: Angular reads ' +
368
+ 'whether to support them when it is first imported, before anything here runs. Build ' +
369
+ 'metro.config.js with `withAngularNative` from `@ng-native/metro/config.cjs`, which ' +
370
+ 'adds the polyfill that sets it. Animated styles and CSS transitions work either way.');
371
+ }
372
+ function installReloadHook() {
373
+ const scope = globalThis;
374
+ if (scope.__angularNativeReload)
375
+ return;
376
+ scope.__angularNativeReload = () => {
377
+ /*
378
+ * Expo's reload first, React Native's only as a fallback.
379
+ *
380
+ * `DevSettings.reload()` under Expo Go re-runs the bundle the app downloaded when it launched
381
+ * rather than fetching the current one, so the app comes back holding the code from before
382
+ * the edit - forever one generation behind, however long you wait. That is worse than not
383
+ * reloading, because it looks like it worked.
384
+ *
385
+ * Required rather than imported, both of them: React Native ships Flow, which Node cannot
386
+ * parse, and this file has to stay importable by the test suite. Expo is genuinely optional.
387
+ */
388
+ try {
389
+ const expo = require('expo');
390
+ if (expo.reloadAppAsync) {
391
+ void expo.reloadAppAsync('angular-native: more than a template changed');
392
+ return;
393
+ }
394
+ }
395
+ catch {
396
+ // Not an Expo app. React Native's own reload does re-fetch outside Expo Go.
397
+ }
398
+ const devSettings = require('react-native').DevSettings;
399
+ devSettings?.reload('angular-native: more than a template changed');
400
+ };
401
+ }
402
+ /** Dev only, and required rather than imported: React Native ships Flow, which Node cannot parse. */
403
+ function calmDevBanner() {
404
+ try {
405
+ const { default: banner } = require('react-native/Libraries/Utilities/DevLoadingView');
406
+ if (banner)
407
+ calmLoadingBanner(banner);
408
+ }
409
+ catch {
410
+ // No React Native under us: a test, or the web.
411
+ }
412
+ }
413
+ /**
414
+ * React Native's `NativeAnimatedHelper`, for the engine to move sticky headers by a scroll on the
415
+ * native side. Required rather than imported for the same reason as the banner, and null where
416
+ * there is no native animated module to talk to: a test, or the web.
417
+ */
418
+ function nativeAnimated() {
419
+ try {
420
+ const { default: helper } = require('react-native/src/private/animated/NativeAnimatedHelper');
421
+ if (!helper)
422
+ return null;
423
+ helper.assertNativeAnimatedModule();
424
+ return helper;
425
+ }
426
+ catch {
427
+ return null;
428
+ }
429
+ }
430
+ /**
431
+ * Run the app's initializers, `provideAppInitializer` and `APP_INITIALIZER`, before the root
432
+ * component exists, as `bootstrapApplication` does. `createEnvironmentInjector` runs only
433
+ * environment initializers, so without this an app initializer silently never ran.
434
+ *
435
+ * Through Angular's own `ApplicationInitStatus`, so its `done` and `donePromise` stay true to what
436
+ * happened for anything that waits on them. `runInitializers` is what Angular's bootstrap calls;
437
+ * it is missing from the public types, which is why it is reached through a cast.
438
+ *
439
+ * An initializer that returns a promise is started, not awaited: `mount` is synchronous and the
440
+ * first frame does not wait. One that fails reaches the `ErrorHandler` rather than vanishing.
441
+ */
442
+ function runInitializers(injector) {
443
+ const status = injector.get(ApplicationInitStatus);
444
+ status.runInitializers();
445
+ status.donePromise.catch((error) => injector.get(ErrorHandler).handleError(error));
446
+ }
447
+ export function mount(rootTag, component, fabric, options = {}) {
448
+ // The bare identifier, behind `typeof` so Node does not throw: Metro inlines `__DEV__` and folds
449
+ // this branch away in a release build, and a property read through `globalThis` it cannot, which
450
+ // left the reload hook and its `require('expo')` in every release bundle.
451
+ if (typeof __DEV__ !== 'undefined' && __DEV__) {
452
+ warnIfAnimationsAreOff();
453
+ installReloadHook();
454
+ calmDevBanner();
455
+ }
456
+ installDeferTriggers();
457
+ const { providers: extraProviders = [], inputs = {}, ...engineOptions } = options;
458
+ // The factory needs the engine and the engine needs the factory's dirty hook, so the hook is
459
+ // handed over once both exist rather than through the constructor.
460
+ const engine = new Engine(fabric, rootTag, {
461
+ ...engineOptions,
462
+ nativeAnimated: engineOptions.nativeAnimated === undefined ? nativeAnimated() : engineOptions.nativeAnimated,
463
+ });
464
+ installDateParse(engine.dev);
465
+ const factory = new NativeRendererFactory(engine);
466
+ engine.setOnDirty(factory.commitSoon);
467
+ // `getElementById` is what `TransferState` calls, and `TransferState` is what `resource()`
468
+ // reaches for the moment one is created. Without it every resource in an app throws
469
+ // `doc.getElementById is not a function` before its loader has run - which is a long way from
470
+ // anything a stylesheet does, and is why the stub is not as thin as it looks.
471
+ const documentStub = { head: undefined, body: undefined, getElementById: () => null };
472
+ // Parent the app injector to the platform injector, exactly as `internalCreateApplication`
473
+ // does. Some Angular services are `providedIn: 'platform'` - `Console` among them - and with a
474
+ // null parent they simply do not exist: the router fails with NG0201 the moment it logs.
475
+ const platformInjector = createOrReusePlatformInjector();
476
+ const injector = createEnvironmentInjector([
477
+ { provide: INJECTOR_SCOPE, useValue: 'root' },
478
+ provideZonelessChangeDetectionInternal(),
479
+ { provide: RendererFactory2, useValue: factory },
480
+ // Exposed so a component can read commit stats, or reach a node for a native command.
481
+ { provide: Engine, useValue: engine },
482
+ // The contract the shared packages inject. `Engine` is still provided under its own token
483
+ // for the three worklet components that need Fabric's own handles. See `fabric/host.ts`.
484
+ { provide: HostEngine, useExisting: Engine },
485
+ { provide: DOCUMENT, useValue: documentStub },
486
+ // 'native', not Angular's default 'unknown': see `platform-id.ts`.
487
+ { provide: PLATFORM_ID, useValue: PLATFORM_NATIVE_ID },
488
+ { provide: ErrorHandler, useClass: ErrorHandler },
489
+ ...extraProviders,
490
+ ],
491
+ // The platform injector is an EnvironmentInjector in practice; the factory's return type is
492
+ // just declared more loosely.
493
+ platformInjector);
494
+ // Errors the engine catches in its native event dispatch, which is outside every Angular
495
+ // listener wrapper: responder handlers, engine-level listeners, a commit a focus change starts.
496
+ // Looked up when one happens rather than now, as Angular's own handler is, so an app's
497
+ // `ErrorHandler` that injects something not yet constructible is not forced at boot. A caller
498
+ // that passed its own `onError` keeps it.
499
+ if (!engineOptions.onError) {
500
+ engine.setOnError((error) => injector.get(ErrorHandler).handleError(error));
501
+ }
502
+ runInitializers(injector);
503
+ const componentRef = createComponent(component, {
504
+ environmentInjector: injector,
505
+ // The engine root doubles as the host node.
506
+ hostElement: engine.root,
507
+ });
508
+ // What `@ng-native/tailwind`'s `ios:` and `android:` variants match beneath. On the root so
509
+ // they work with nothing to set up; an app that had to add it itself, and did not, got platform
510
+ // variants that silently matched nothing. After the component rather than before, because a
511
+ // root with a static `class` sets the whole list as it is created.
512
+ engine.addClass(engine.root, `platform-${nativePlatform()}`);
513
+ for (const [name, value] of Object.entries(inputs))
514
+ componentRef.setInput(name, value);
515
+ const applicationRef = injector.get(ApplicationRef);
516
+ // `ApplicationRef.bootstrap()` does all of this; `createComponent` does not, and we
517
+ // deliberately do not use `bootstrap()` because it wants a DOM selector. Order and
518
+ // bookkeeping both matter, so this mirrors `_loadComponent` step for step.
519
+ applicationRef.componentTypes.push(component);
520
+ applicationRef.attachView(componentRef.hostView);
521
+ // Before the listeners, so the root component's template has run and any outlet in it has
522
+ // registered itself by the time the router's initial navigation activates a route.
523
+ applicationRef.tick();
524
+ // Load-bearing, not bookkeeping: the router's bootstrap listener returns early unless the
525
+ // ref it is handed is `ApplicationRef.components[0]`, so with an empty array the initial
526
+ // navigation never runs and a routed app renders nothing but its outlet's host.
527
+ applicationRef.components.push(componentRef);
528
+ componentRef.onDestroy(() => {
529
+ applicationRef.detachView(componentRef.hostView);
530
+ const at = applicationRef.components.indexOf(componentRef);
531
+ if (at !== -1)
532
+ applicationRef.components.splice(at, 1);
533
+ });
534
+ for (const listener of injector.get(APP_BOOTSTRAP_LISTENER, [])) {
535
+ listener(componentRef);
536
+ }
537
+ return { componentRef, applicationRef, factory, engine };
538
+ }
539
+ //# sourceMappingURL=adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.js","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The only Angular-aware file: the engine below it never imports Angular. Renderer2 over the\n * engine, plus bootstrap.\n *\n * Bootstrap leans on private `ɵ` exports of `@angular/core`, which can change in any major.\n * `bootstrap.test.ts` asserts each one still exists, so an upgrade fails there rather than on a\n * device.\n */\nimport {\n APP_BOOTSTRAP_LISTENER,\n ApplicationInitStatus,\n ApplicationRef,\n DOCUMENT,\n ErrorHandler,\n Renderer2,\n RendererFactory2,\n createComponent,\n createEnvironmentInjector,\n PLATFORM_ID,\n type ComponentRef,\n type EnvironmentInjector,\n type EnvironmentProviders,\n type Provider,\n type RendererStyleFlags2,\n type Type,\n ɵcreateOrReusePlatformInjector as createOrReusePlatformInjector,\n ɵINJECTOR_SCOPE as INJECTOR_SCOPE,\n ɵprovideZonelessChangeDetectionInternal as provideZonelessChangeDetectionInternal,\n} from '@angular/core';\nimport { installDateParse } from './date-parse.ts';\nimport { PLATFORM_NATIVE_ID } from './platform-id.ts';\nimport {\n Engine,\n HostEngine,\n installDeferTriggers,\n markComponentHost,\n nativePlatform,\n reportUnboundFormsInput,\n styleSheetOf,\n type EngineNode,\n type EngineOptions,\n type FabricUIManager,\n type NativeAnimated,\n type StyleSheet,\n} from '@ng-native/fabric';\nimport { calmLoadingBanner, type LoadingBanner } from './dev-loading-view.ts';\n\n/**\n * Angular `(touchEnd)` -> Fabric `topTouchEnd`.\n *\n * There is no alias for `press`: `onPress` is not a native event, RN synthesises it in JS from\n * the touch responder system. `Pressable` in `components.ts` does the same, and because Angular\n * resolves a directive output before an element event, `(press)` on a `<pressable>` binds to\n * that output rather than reaching the renderer at all.\n */\nfunction topLevelType(eventName: string): string {\n return 'top' + eventName.charAt(0).toUpperCase() + eventName.slice(1);\n}\n\n/**\n * What Angular's compiler emits for a style binding is aimed at the DOM: `[style.font-size]`\n * arrives dash-cased, and `[style.width.px]` arrives as the string `'10px'`. Fabric reads\n * camelCase keys and plain numbers, and drops both forms without a word.\n */\nconst PX = /^-?\\d*\\.?\\d+px$/;\nconst NUMBER = /^-?\\d*\\.?\\d+$/;\nconst styleKey = (name: string): string =>\n name.includes('-') ? name.replace(/-([a-z])/g, (_, c: string) => c.toUpperCase()) : name;\n/**\n * A bare number arrives as a string too, and Yoga wants a number.\n *\n * Angular parses `[style]=\"'flex: 1'\"` itself and calls `setStyle('flex', '1')`, so the same\n * declaration written as an object gave `1` and written as a string gave `'1'` - the two spellings\n * of one design disagreeing at the only layer that can tell.\n */\nconst styleValue = (value: unknown): unknown =>\n typeof value === 'string' && (PX.test(value) || NUMBER.test(value)) ? parseFloat(value) : value;\n\n/**\n * `flex: 1; margin-top: 4px` -> `{ flex: 1, marginTop: 4 }`.\n *\n * Only the static-attribute path needs this. Angular compiles `[style]` and `[style.x]` into\n * styling instructions that parse the string themselves and arrive one declaration at a time; a\n * *static* `style=\"...\"` is not a binding at all, so it comes through `setAttribute` whole.\n *\n * Splitting on `;` and `:` rather than parsing properly, because the values that reach here are\n * lengths, numbers, keywords and colours. A `url()` or a quoted font stack containing either\n * character would be cut in the wrong place - neither is expressible on this platform, and the\n * one thing worse than not supporting them would be appearing to.\n */\n/**\n * A bound custom property's name, as it was written. Angular 22 compiles `[style.--tint]` to\n * `--%NS%tint`, for a DOM renderer to fill with a namespace; there is none here.\n */\nconst customPropertyName = (style: string): string => style.replace(/%NS%/g, '');\n\nfunction parseStyleAttribute(css: string): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const declaration of css.split(';')) {\n const at = declaration.indexOf(':');\n if (at === -1) continue;\n const name = declaration.slice(0, at).trim();\n const value = declaration.slice(at + 1).trim();\n // A custom property keeps its name, which is case-sensitive, and its text for the engine.\n if (name.startsWith('--')) out[name] = value;\n else if (name) out[styleKey(name)] = styleValue(value);\n }\n return out;\n}\n\nclass NativeRenderer implements Renderer2 {\n readonly data: { [key: string]: unknown } = Object.create(null);\n destroyNode: (node: EngineNode) => void;\n\n private readonly engine: Engine;\n /** The creating component's compiled CSS. Elements this renderer makes are matched against it. */\n private readonly sheet: StyleSheet | null;\n\n constructor(engine: Engine, sheet: StyleSheet | null = null) {\n this.engine = engine;\n this.sheet = sheet;\n this.destroyNode = (node) => this.engine.destroyNode(node);\n }\n\n destroy(): void {}\n\n createElement(name: string): EngineNode {\n return this.engine.createElement(name, this.sheet);\n }\n\n createComment(): EngineNode {\n return this.engine.createAnchor();\n }\n\n createText(value: string): EngineNode {\n return this.engine.createText(value);\n }\n\n appendChild(parent: EngineNode, newChild: EngineNode): void {\n this.engine.appendChild(parent, newChild);\n }\n\n insertBefore(parent: EngineNode, newChild: EngineNode, refChild: EngineNode | null): void {\n if (!parent) return;\n this.engine.insertBefore(parent, newChild, refChild);\n }\n\n removeChild(parent: EngineNode | null, oldChild: EngineNode): void {\n this.engine.removeChild(parent, oldChild);\n }\n\n selectRootElement(selectorOrNode: string | EngineNode): EngineNode {\n if (typeof selectorOrNode !== 'string') return selectorOrNode;\n return this.engine.root;\n }\n\n parentNode(node: EngineNode): EngineNode | null {\n return this.engine.parentNode(node);\n }\n\n nextSibling(node: EngineNode): EngineNode | null {\n return this.engine.nextSibling(node);\n }\n\n setAttribute(el: EngineNode, name: string, value: string): void {\n // `class` drives selector matching, not a native prop: nothing on the native side reads it.\n if (name === 'class') {\n this.engine.setClasses(el, value);\n return;\n }\n /*\n * A *static* `style=\"flex: 1\"` is not a binding, so it never becomes a styling instruction -\n * it arrives here as one attribute holding the whole declaration list. Handing that string on\n * as the `style` prop meant Fabric received a string where it merges objects, which it drops\n * without a word: every static style in the codebase, and in this project's own doc comments,\n * was doing nothing at all.\n */\n if (name === 'style') {\n for (const [key, next] of Object.entries(parseStyleAttribute(value))) {\n this.setStyle(el, key, next);\n }\n return;\n }\n this.engine.setProp(el, name, value);\n }\n\n removeAttribute(el: EngineNode, name: string): void {\n this.engine.setProp(el, name, null);\n }\n\n addClass(el: EngineNode, name: string): void {\n this.engine.addClass(el, name);\n }\n\n removeClass(el: EngineNode, name: string): void {\n this.engine.removeClass(el, name);\n }\n\n setStyle(el: EngineNode, style: string, value: unknown, _flags?: RendererStyleFlags2): void {\n if (style.startsWith('--')) {\n this.engine.setCustomProperty(el, customPropertyName(style), value);\n return;\n }\n const key = styleKey(style);\n const next = styleValue(value);\n const current = el.props['style'] as Record<string, unknown> | undefined;\n if (current && current === el.ownStyle) {\n if (current[key] === next) return;\n current[key] = next;\n this.engine.styleChanged(el);\n return;\n }\n const owned = { ...(current ?? {}), [key]: next };\n el.ownStyle = owned;\n this.engine.setProp(el, 'style', owned);\n }\n\n removeStyle(el: EngineNode, style: string, _flags?: RendererStyleFlags2): void {\n if (style.startsWith('--')) {\n this.engine.setCustomProperty(el, customPropertyName(style), null);\n return;\n }\n const current = el.props['style'] as Record<string, unknown> | undefined;\n if (!current || !(styleKey(style) in current)) return;\n if (current === el.ownStyle) {\n delete current[styleKey(style)];\n this.engine.styleChanged(el);\n return;\n }\n const { [styleKey(style)]: _removed, ...rest } = current;\n el.ownStyle = rest;\n this.engine.setProp(el, 'style', rest);\n }\n\n setProperty(el: EngineNode, name: string, value: unknown): void {\n if (this.engine.dev) reportUnboundFormsInput(this.engine, el, name);\n this.engine.setProp(el, name, value);\n }\n\n setValue(node: EngineNode, value: string): void {\n this.engine.setText(node, value);\n }\n\n listen(\n target: EngineNode,\n eventName: string,\n callback: (event: unknown) => boolean | void,\n ): () => void {\n return this.engine.setEventListener(target, topLevelType(eventName), callback);\n }\n}\n\n/**\n * The slice of Angular's component definition `createRenderer` receives that says which element\n * names a template can legitimately contain. Private shape, read only to keep the engine's typo\n * check quiet, so a missing piece means \"nothing to declare\" rather than an error.\n */\ninterface ComponentDefLike {\n id?: string;\n type?: unknown;\n tView?: {\n directiveRegistry?:\n readonly { selectors?: readonly (readonly unknown[])[]; inputs?: object }[] | null;\n } | null;\n ngContentSelectors?: readonly string[];\n}\n\n/** The attribute names in a projection selector such as `[listHeader]` or `text[slot=a]`. */\nconst PROJECTED_ATTRIBUTE = /\\[([^\\]=~|^$*\\s]+)/g;\n\nexport class NativeRendererFactory implements RendererFactory2 {\n private readonly renderer: NativeRenderer;\n /** One renderer per component type, because each carries its own stylesheet. */\n private readonly byComponent = new Map<string, NativeRenderer>();\n readonly engine: Engine;\n\n constructor(engine: Engine) {\n this.engine = engine;\n this.renderer = new NativeRenderer(engine);\n }\n\n /**\n * Angular hands us the component definition here, which is how emulated encapsulation gets\n * scoped: the renderer a component uses is bound to that component's compiled stylesheet, so\n * its elements match its rules and nobody else's.\n */\n createRenderer(host: unknown, type: ComponentDefLike | null): Renderer2 {\n const sheet = styleSheetOf(type?.type);\n\n // `host` is the element this component is mounted on, and Angular created it with the\n // *parent's* renderer, so it carries the parent's sheet. Tagging it here is the only moment\n // the two are both in hand, and it is what makes `:host` reachable at all.\n //\n // It is also the one sure sign that an element hosts a component, whatever the selector\n // that put it there, which is what tells an `<x-card>` from a typo.\n if (host && typeof host === 'object') {\n (host as EngineNode).hostSheet = sheet;\n markComponentHost(host as EngineNode);\n }\n if (type) this.declareElements(type);\n\n if (!sheet) return this.renderer;\n\n const key = type?.id ?? '';\n let renderer = this.byComponent.get(key);\n if (!renderer) {\n renderer = new NativeRenderer(this.engine, sheet);\n this.byComponent.set(key, renderer);\n }\n return renderer;\n }\n\n /**\n * Angular's HMR calls this before it recreates a hot-swapped component's views. The renderer\n * cached for that component is bound to the sheet it had, so an edited stylesheet would never\n * reach the recreated views without letting it go.\n */\n componentReplaced(id: string): void {\n this.byComponent.delete(id);\n }\n\n /** Component definitions whose templates have been looked through already. */\n private readonly declared = new WeakSet<object>();\n\n /**\n * Tell the engine every element name this component's template may select by tag.\n *\n * Angular builds a component's template view, with its directive registry resolved from the\n * component's `imports`, before it asks for the renderer, so the registry is in hand here and\n * before the template that uses those names runs. This is what keeps a directive that owns an\n * element without hosting a component, such as Angular's `router-outlet`, from reading as a\n * typo; a component host is marked directly instead, above.\n */\n private declareElements(def: ComponentDefLike): void {\n // Only once the template view exists. The root component asks for a renderer twice, first\n // without a host and before its template view is built, and marking it seen then would\n // skip the call that has the registry.\n const registry = def.tView?.directiveRegistry;\n if (!registry || this.declared.has(def)) return;\n this.declared.add(def);\n for (const directive of registry) {\n for (const selector of directive.selectors ?? []) {\n const tag = selector[0];\n if (typeof tag === 'string' && tag) this.engine.declareElement(tag);\n }\n }\n this.declareAttributes(def, registry);\n }\n\n /**\n * Tell the engine the attribute names that are Angular's rather than a native view's: what a\n * directive selects on or takes as an input, which Angular also writes to the node as a static\n * attribute, and what the component's `<ng-content select>` projects by.\n */\n private declareAttributes(\n def: ComponentDefLike,\n registry: NonNullable<NonNullable<ComponentDefLike['tView']>['directiveRegistry']>,\n ): void {\n for (const directive of registry) {\n for (const selector of directive.selectors ?? []) {\n for (const name of selector.filter((part, at) => at % 2 === 1)) {\n if (typeof name === 'string') this.engine.declareAttribute(name);\n }\n }\n for (const input of Object.keys(directive.inputs ?? {})) this.engine.declareAttribute(input);\n }\n for (const selector of def.ngContentSelectors ?? []) {\n for (const [, name] of selector.matchAll(PROJECTED_ATTRIBUTE)) {\n this.engine.declareAttribute(name!);\n }\n }\n }\n\n private renderStarted = 0;\n private frame = 0;\n\n /**\n * Drive CSS transitions.\n *\n * A transition is not a binding: nothing in Angular changes while one runs, so change detection\n * would never fire again and the animation would stop on its first frame. This pumps the engine\n * directly for as long as one has frames left, and commits each one.\n */\n private readonly pump = (): void => {\n this.frame = 0;\n this.engine.advanceAnimations();\n // Also the commit for anything changed outside change detection. Angular's `animate.enter`\n // and `animate.leave` add their classes from an animation queue that runs after the render\n // pass, so without a frame behind it the class would sit on the node and never reach native.\n this.engine.commit();\n // `pending` as well as `transitioning`: a transition's end listener runs after the commit\n // that finished it, and removing a leaving element is exactly what it tends to do.\n if (this.engine.animating || this.engine.pending) this.schedule();\n };\n\n private schedule(): void {\n if (this.frame) return;\n // `requestAnimationFrame` is a global in React Native. The fallback is for Node, where the\n // suite drives `advanceTransitions` itself and this never runs.\n const request =\n globalThis.requestAnimationFrame ??\n ((callback: FrameRequestCallback) => setTimeout(() => callback(Date.now()), 16));\n this.frame = request(this.pump) as unknown as number;\n }\n\n /**\n * A change that lands outside a render pass has no commit coming. The engine reports the moment\n * the tree goes dirty, and this arranges a frame for it, which is how the classes Angular's\n * animation queue adds after a pass reach native at all.\n */\n readonly commitSoon = (): void => {\n if (!this.rendering) this.schedule();\n };\n\n private rendering = false;\n\n begin(): void {\n this.rendering = true;\n this.renderStarted = globalThis.performance?.now?.() ?? Date.now();\n }\n\n /** The single flush point: at most one commit per change-detection pass. */\n end(): void {\n this.rendering = false;\n this.engine.commit();\n\n // A commit is what starts a transition, because it is where the cascade is recomputed.\n if (this.engine.animating) this.schedule();\n\n // Excludes the mount, which is not representative of a steady-state frame.\n const elapsed = (globalThis.performance?.now?.() ?? Date.now()) - this.renderStarted;\n const stats = this.engine.stats;\n if (stats.commits > 1 && elapsed > stats.worstRenderMs) stats.worstRenderMs = elapsed;\n }\n}\n\nexport interface MountResult {\n componentRef: ComponentRef<unknown>;\n applicationRef: ApplicationRef;\n factory: NativeRendererFactory;\n engine: Engine;\n}\n\n/**\n * `ɵINJECTOR_SCOPE` and `DOCUMENT` are both load-bearing: without the first Angular\n * cannot resolve `ChangeDetectionSchedulerImpl` (NG0201), without the second\n * `getStyleHost` throws NG0210.\n */\n/**\n * Angular decides whether `animate.enter` and `animate.leave` do anything when `@angular/core` is\n * first evaluated, by reading a global that a Metro polyfill has to have set by then. Nothing\n * later can change the answer, so the most that can be done is to notice and say so. Checked here\n * because booting is the one moment every app passes through.\n */\nfunction warnIfAnimationsAreOff(): void {\n const document = (globalThis as { document?: { documentElement?: { getAnimations?: unknown } } })\n .document;\n if (typeof document?.documentElement?.getAnimations === 'function') return;\n console.error(\n '[angular-native] `animate.enter` and `animate.leave` will do nothing: Angular reads ' +\n 'whether to support them when it is first imported, before anything here runs. Build ' +\n 'metro.config.js with `withAngularNative` from `@ng-native/metro/config.cjs`, which ' +\n 'adds the polyfill that sets it. Animated styles and CSS transitions work either way.',\n );\n}\n\n/**\n * The reload the HMR block falls back to when a change is more than a template.\n *\n * Each compiled component module can patch its own live class when only the template moved. For\n * anything else - a new method, a changed selector, different imports - there is no metadata to\n * replace, so it calls `globalThis.__angularNativeReload` instead. React Native's own Fast Refresh\n * cannot cover for it: that is React-specific, and it accepts the update and reports success, so\n * Metro never falls back on its own.\n *\n * This is installed here because `mount` is the one call every app makes. It used to be a line in\n * an example app's entry file, which meant every other app logged \"hmr reload\", called an empty\n * function, and carried on running the code from before the edit - with a second copy of every\n * class in the changed file now loaded, which Angular reports as `NG0912`. An edit that ships and\n * does nothing is a bad enough failure; one that also makes the next few screenshots lie about\n * what the app is running is worse.\n *\n * An app that sets its own is left alone: it will have a better reason string than this one.\n */\ndeclare const __DEV__: boolean | undefined;\n\nfunction installReloadHook(): void {\n const scope = globalThis as { __angularNativeReload?: () => void };\n if (scope.__angularNativeReload) return;\n scope.__angularNativeReload = () => {\n /*\n * Expo's reload first, React Native's only as a fallback.\n *\n * `DevSettings.reload()` under Expo Go re-runs the bundle the app downloaded when it launched\n * rather than fetching the current one, so the app comes back holding the code from before\n * the edit - forever one generation behind, however long you wait. That is worse than not\n * reloading, because it looks like it worked.\n *\n * Required rather than imported, both of them: React Native ships Flow, which Node cannot\n * parse, and this file has to stay importable by the test suite. Expo is genuinely optional.\n */\n try {\n const expo = require('expo') as { reloadAppAsync?: (reason?: string) => Promise<void> };\n if (expo.reloadAppAsync) {\n void expo.reloadAppAsync('angular-native: more than a template changed');\n return;\n }\n } catch {\n // Not an Expo app. React Native's own reload does re-fetch outside Expo Go.\n }\n const devSettings = (\n require('react-native') as { DevSettings?: { reload(reason?: string): void } }\n ).DevSettings;\n devSettings?.reload('angular-native: more than a template changed');\n };\n}\n\n/** Dev only, and required rather than imported: React Native ships Flow, which Node cannot parse. */\nfunction calmDevBanner(): void {\n try {\n const { default: banner } = require('react-native/Libraries/Utilities/DevLoadingView') as {\n default?: LoadingBanner;\n };\n if (banner) calmLoadingBanner(banner);\n } catch {\n // No React Native under us: a test, or the web.\n }\n}\n\n/**\n * React Native's `NativeAnimatedHelper`, for the engine to move sticky headers by a scroll on the\n * native side. Required rather than imported for the same reason as the banner, and null where\n * there is no native animated module to talk to: a test, or the web.\n */\nfunction nativeAnimated(): NativeAnimated | null {\n try {\n const { default: helper } =\n require('react-native/src/private/animated/NativeAnimatedHelper') as {\n default?: NativeAnimated & { assertNativeAnimatedModule(): void };\n };\n if (!helper) return null;\n helper.assertNativeAnimatedModule();\n return helper;\n } catch {\n return null;\n }\n}\n\n/**\n * Run the app's initializers, `provideAppInitializer` and `APP_INITIALIZER`, before the root\n * component exists, as `bootstrapApplication` does. `createEnvironmentInjector` runs only\n * environment initializers, so without this an app initializer silently never ran.\n *\n * Through Angular's own `ApplicationInitStatus`, so its `done` and `donePromise` stay true to what\n * happened for anything that waits on them. `runInitializers` is what Angular's bootstrap calls;\n * it is missing from the public types, which is why it is reached through a cast.\n *\n * An initializer that returns a promise is started, not awaited: `mount` is synchronous and the\n * first frame does not wait. One that fails reaches the `ErrorHandler` rather than vanishing.\n */\nfunction runInitializers(injector: EnvironmentInjector): void {\n const status = injector.get(ApplicationInitStatus);\n (status as unknown as { runInitializers(): void }).runInitializers();\n status.donePromise.catch((error: unknown) => injector.get(ErrorHandler).handleError(error));\n}\n\nexport function mount(\n rootTag: number,\n component: Type<unknown>,\n fabric: FabricUIManager,\n options: EngineOptions & {\n providers?: (Provider | EnvironmentProviders)[];\n /**\n * Root component inputs, set before its first change detection. That is the only moment a\n * `required` input can be given, and `mount` runs that first pass itself, so a caller cannot\n * set one afterwards. What `render()` in `@ng-native/testing` passes a component's inputs as.\n */\n inputs?: Record<string, unknown>;\n } = {},\n): MountResult {\n // The bare identifier, behind `typeof` so Node does not throw: Metro inlines `__DEV__` and folds\n // this branch away in a release build, and a property read through `globalThis` it cannot, which\n // left the reload hook and its `require('expo')` in every release bundle.\n if (typeof __DEV__ !== 'undefined' && __DEV__) {\n warnIfAnimationsAreOff();\n installReloadHook();\n calmDevBanner();\n }\n installDeferTriggers();\n const { providers: extraProviders = [], inputs = {}, ...engineOptions } = options;\n // The factory needs the engine and the engine needs the factory's dirty hook, so the hook is\n // handed over once both exist rather than through the constructor.\n const engine = new Engine(fabric, rootTag, {\n ...engineOptions,\n nativeAnimated:\n engineOptions.nativeAnimated === undefined ? nativeAnimated() : engineOptions.nativeAnimated,\n });\n installDateParse(engine.dev);\n const factory = new NativeRendererFactory(engine);\n engine.setOnDirty(factory.commitSoon);\n // `getElementById` is what `TransferState` calls, and `TransferState` is what `resource()`\n // reaches for the moment one is created. Without it every resource in an app throws\n // `doc.getElementById is not a function` before its loader has run - which is a long way from\n // anything a stylesheet does, and is why the stub is not as thin as it looks.\n const documentStub = { head: undefined, body: undefined, getElementById: () => null };\n\n // Parent the app injector to the platform injector, exactly as `internalCreateApplication`\n // does. Some Angular services are `providedIn: 'platform'` - `Console` among them - and with a\n // null parent they simply do not exist: the router fails with NG0201 the moment it logs.\n const platformInjector = createOrReusePlatformInjector();\n\n const injector = createEnvironmentInjector(\n [\n { provide: INJECTOR_SCOPE, useValue: 'root' },\n provideZonelessChangeDetectionInternal(),\n { provide: RendererFactory2, useValue: factory },\n // Exposed so a component can read commit stats, or reach a node for a native command.\n { provide: Engine, useValue: engine },\n // The contract the shared packages inject. `Engine` is still provided under its own token\n // for the three worklet components that need Fabric's own handles. See `fabric/host.ts`.\n { provide: HostEngine, useExisting: Engine },\n { provide: DOCUMENT, useValue: documentStub },\n // 'native', not Angular's default 'unknown': see `platform-id.ts`.\n { provide: PLATFORM_ID, useValue: PLATFORM_NATIVE_ID },\n { provide: ErrorHandler, useClass: ErrorHandler },\n ...extraProviders,\n ],\n // The platform injector is an EnvironmentInjector in practice; the factory's return type is\n // just declared more loosely.\n platformInjector as EnvironmentInjector,\n );\n\n // Errors the engine catches in its native event dispatch, which is outside every Angular\n // listener wrapper: responder handlers, engine-level listeners, a commit a focus change starts.\n // Looked up when one happens rather than now, as Angular's own handler is, so an app's\n // `ErrorHandler` that injects something not yet constructible is not forced at boot. A caller\n // that passed its own `onError` keeps it.\n if (!engineOptions.onError) {\n engine.setOnError((error) => injector.get(ErrorHandler).handleError(error));\n }\n\n runInitializers(injector);\n\n const componentRef = createComponent(component, {\n environmentInjector: injector,\n // The engine root doubles as the host node.\n hostElement: engine.root as unknown as Element,\n });\n\n // What `@ng-native/tailwind`'s `ios:` and `android:` variants match beneath. On the root so\n // they work with nothing to set up; an app that had to add it itself, and did not, got platform\n // variants that silently matched nothing. After the component rather than before, because a\n // root with a static `class` sets the whole list as it is created.\n engine.addClass(engine.root, `platform-${nativePlatform()}`);\n\n for (const [name, value] of Object.entries(inputs)) componentRef.setInput(name, value);\n\n const applicationRef = injector.get(ApplicationRef);\n\n // `ApplicationRef.bootstrap()` does all of this; `createComponent` does not, and we\n // deliberately do not use `bootstrap()` because it wants a DOM selector. Order and\n // bookkeeping both matter, so this mirrors `_loadComponent` step for step.\n applicationRef.componentTypes.push(component);\n applicationRef.attachView(componentRef.hostView);\n // Before the listeners, so the root component's template has run and any outlet in it has\n // registered itself by the time the router's initial navigation activates a route.\n applicationRef.tick();\n // Load-bearing, not bookkeeping: the router's bootstrap listener returns early unless the\n // ref it is handed is `ApplicationRef.components[0]`, so with an empty array the initial\n // navigation never runs and a routed app renders nothing but its outlet's host.\n applicationRef.components.push(componentRef);\n componentRef.onDestroy(() => {\n applicationRef.detachView(componentRef.hostView);\n const at = applicationRef.components.indexOf(componentRef);\n if (at !== -1) applicationRef.components.splice(at, 1);\n });\n\n for (const listener of injector.get(APP_BOOTSTRAP_LISTENER, [])) {\n listener(componentRef);\n }\n\n return { componentRef, applicationRef, factory, engine };\n}\n"]}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * `Date.parse` for the date strings Angular's `DatePipe` builds to read a timezone argument.
3
+ *
4
+ * `formatDate` turns `'UTC'`, `'+0500'` or `'EST'` into an offset by parsing
5
+ * `'Jan 01, 1970 00:00:00 ' + timezone` and falls back to the device's own zone when that is
6
+ * `NaN`. A browser's parser accepts that legacy form; Hermes' accepts ISO 8601 and not this, so
7
+ * every timezone argument was ignored and the pipe printed local time without a word.
8
+ *
9
+ * `installDateParse` wraps `Date.parse` once, from `mount()`. The platform's own answer is used
10
+ * whenever it has one; only a string it cannot read and this can is answered here, so nothing a
11
+ * parser already understood changes. A timezone Angular itself cannot honour, in a browser or
12
+ * here, is still ignored, and in development said once per zone.
13
+ */
14
+ /** Wrap `Date.parse` so the legacy form reads everywhere. Idempotent. */
15
+ export declare function installDateParse(dev: boolean): void;
@@ -0,0 +1,98 @@
1
+ /**
2
+ * `Date.parse` for the date strings Angular's `DatePipe` builds to read a timezone argument.
3
+ *
4
+ * `formatDate` turns `'UTC'`, `'+0500'` or `'EST'` into an offset by parsing
5
+ * `'Jan 01, 1970 00:00:00 ' + timezone` and falls back to the device's own zone when that is
6
+ * `NaN`. A browser's parser accepts that legacy form; Hermes' accepts ISO 8601 and not this, so
7
+ * every timezone argument was ignored and the pipe printed local time without a word.
8
+ *
9
+ * `installDateParse` wraps `Date.parse` once, from `mount()`. The platform's own answer is used
10
+ * whenever it has one; only a string it cannot read and this can is answered here, so nothing a
11
+ * parser already understood changes. A timezone Angular itself cannot honour, in a browser or
12
+ * here, is still ignored, and in development said once per zone.
13
+ */
14
+ const MONTHS = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'];
15
+ /** The North American abbreviations a browser's legacy parser knows, as minutes east of UTC. */
16
+ const ZONES = {
17
+ UT: 0,
18
+ UTC: 0,
19
+ GMT: 0,
20
+ Z: 0,
21
+ EST: -300,
22
+ EDT: -240,
23
+ CST: -360,
24
+ CDT: -300,
25
+ MST: -420,
26
+ MDT: -360,
27
+ PST: -480,
28
+ PDT: -420,
29
+ };
30
+ /** `Mon DD, YYYY HH:MM:SS` with an optional zone: the form `DatePipe` and older code write. */
31
+ const LEGACY = /^([a-z]{3})[a-z]*\.? (\d{1,2}),? (\d{4}) (\d{1,2}):(\d{2})(?::(\d{2}))?(?: (.*))?$/i;
32
+ /** `+0500`, `-05:00` or `+05`, with or without a `GMT` or `UTC` in front. */
33
+ const OFFSET = /^(?:GMT|UTC|UT)?([+-])(\d{2}):?(\d{2})?$/i;
34
+ /** What `DatePipe` asks to learn a timezone's offset. */
35
+ const PROBE = 'Jan 01, 1970 00:00:00 ';
36
+ /** Minutes east of UTC for a zone, `null` for none (local time), `NaN` for one not understood. */
37
+ function zoneOffset(zone) {
38
+ if (!zone)
39
+ return null;
40
+ const named = ZONES[zone.toUpperCase()];
41
+ if (named !== undefined)
42
+ return named;
43
+ const offset = OFFSET.exec(zone);
44
+ if (!offset)
45
+ return NaN;
46
+ const minutes = Number(offset[2]) * 60 + Number(offset[3] ?? 0);
47
+ return offset[1] === '-' ? -minutes : minutes;
48
+ }
49
+ /** The legacy form above, as a time; `NaN` for anything else. */
50
+ function parseLegacyDate(value) {
51
+ const match = LEGACY.exec(value.trim());
52
+ if (!match)
53
+ return NaN;
54
+ const [, month, day, year, hours, minutes, seconds, zone] = match;
55
+ const index = MONTHS.indexOf(month.toLowerCase());
56
+ const east = zoneOffset(zone);
57
+ if (index < 0 || Number.isNaN(east))
58
+ return NaN;
59
+ const fields = [
60
+ Number(year),
61
+ index,
62
+ Number(day),
63
+ Number(hours),
64
+ Number(minutes),
65
+ Number(seconds ?? 0),
66
+ ];
67
+ if (east === null)
68
+ return new Date(...fields).getTime();
69
+ return Date.UTC(...fields) - east * 60_000;
70
+ }
71
+ let installed = false;
72
+ const reported = new Set();
73
+ /** Wrap `Date.parse` so the legacy form reads everywhere. Idempotent. */
74
+ export function installDateParse(dev) {
75
+ if (installed)
76
+ return;
77
+ installed = true;
78
+ const native = Date.parse;
79
+ Date.parse = function parse(value) {
80
+ const time = native.call(Date, value);
81
+ if (!Number.isNaN(time) || typeof value !== 'string')
82
+ return time;
83
+ const legacy = parseLegacyDate(value);
84
+ if (Number.isNaN(legacy) && dev)
85
+ reportZone(value);
86
+ return legacy;
87
+ };
88
+ }
89
+ function reportZone(value) {
90
+ if (!value.startsWith(PROBE))
91
+ return;
92
+ const zone = value.slice(PROBE.length);
93
+ if (reported.has(zone))
94
+ return;
95
+ reported.add(zone);
96
+ console.warn(`[angular-native] DatePipe cannot use the timezone '${zone}', so it formats in the device's own zone. Angular reads an offset such as '+0100' or '-05:00', 'UTC' or 'GMT', or a North American abbreviation such as 'EST'; a named zone such as 'Europe/London' needs Intl.DateTimeFormat with a timeZone option.`);
97
+ }
98
+ //# sourceMappingURL=date-parse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"date-parse.js","sourceRoot":"","sources":["../src/date-parse.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `Date.parse` for the date strings Angular's `DatePipe` builds to read a timezone argument.\n *\n * `formatDate` turns `'UTC'`, `'+0500'` or `'EST'` into an offset by parsing\n * `'Jan 01, 1970 00:00:00 ' + timezone` and falls back to the device's own zone when that is\n * `NaN`. A browser's parser accepts that legacy form; Hermes' accepts ISO 8601 and not this, so\n * every timezone argument was ignored and the pipe printed local time without a word.\n *\n * `installDateParse` wraps `Date.parse` once, from `mount()`. The platform's own answer is used\n * whenever it has one; only a string it cannot read and this can is answered here, so nothing a\n * parser already understood changes. A timezone Angular itself cannot honour, in a browser or\n * here, is still ignored, and in development said once per zone.\n */\n\nconst MONTHS = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'];\n\n/** The North American abbreviations a browser's legacy parser knows, as minutes east of UTC. */\nconst ZONES: Readonly<Record<string, number>> = {\n UT: 0,\n UTC: 0,\n GMT: 0,\n Z: 0,\n EST: -300,\n EDT: -240,\n CST: -360,\n CDT: -300,\n MST: -420,\n MDT: -360,\n PST: -480,\n PDT: -420,\n};\n\n/** `Mon DD, YYYY HH:MM:SS` with an optional zone: the form `DatePipe` and older code write. */\nconst LEGACY =\n /^([a-z]{3})[a-z]*\\.? (\\d{1,2}),? (\\d{4}) (\\d{1,2}):(\\d{2})(?::(\\d{2}))?(?: (.*))?$/i;\n/** `+0500`, `-05:00` or `+05`, with or without a `GMT` or `UTC` in front. */\nconst OFFSET = /^(?:GMT|UTC|UT)?([+-])(\\d{2}):?(\\d{2})?$/i;\n\n/** What `DatePipe` asks to learn a timezone's offset. */\nconst PROBE = 'Jan 01, 1970 00:00:00 ';\n\n/** Minutes east of UTC for a zone, `null` for none (local time), `NaN` for one not understood. */\nfunction zoneOffset(zone: string | undefined): number | null {\n if (!zone) return null;\n const named = ZONES[zone.toUpperCase()];\n if (named !== undefined) return named;\n const offset = OFFSET.exec(zone);\n if (!offset) return NaN;\n const minutes = Number(offset[2]) * 60 + Number(offset[3] ?? 0);\n return offset[1] === '-' ? -minutes : minutes;\n}\n\n/** The legacy form above, as a time; `NaN` for anything else. */\nfunction parseLegacyDate(value: string): number {\n const match = LEGACY.exec(value.trim());\n if (!match) return NaN;\n const [, month, day, year, hours, minutes, seconds, zone] = match;\n const index = MONTHS.indexOf(month!.toLowerCase());\n const east = zoneOffset(zone);\n if (index < 0 || Number.isNaN(east)) return NaN;\n const fields = [\n Number(year),\n index,\n Number(day),\n Number(hours),\n Number(minutes),\n Number(seconds ?? 0),\n ] as const;\n if (east === null) return new Date(...fields).getTime();\n return Date.UTC(...fields) - east * 60_000;\n}\n\nlet installed = false;\nconst reported = new Set<string>();\n\n/** Wrap `Date.parse` so the legacy form reads everywhere. Idempotent. */\nexport function installDateParse(dev: boolean): void {\n if (installed) return;\n installed = true;\n const native = Date.parse;\n Date.parse = function parse(value: string): number {\n const time = native.call(Date, value);\n if (!Number.isNaN(time) || typeof value !== 'string') return time;\n const legacy = parseLegacyDate(value);\n if (Number.isNaN(legacy) && dev) reportZone(value);\n return legacy;\n };\n}\n\nfunction reportZone(value: string): void {\n if (!value.startsWith(PROBE)) return;\n const zone = value.slice(PROBE.length);\n if (reported.has(zone)) return;\n reported.add(zone);\n console.warn(\n `[angular-native] DatePipe cannot use the timezone '${zone}', so it formats in the device's own zone. Angular reads an offset such as '+0100' or '-05:00', 'UTC' or 'GMT', or a North American abbreviation such as 'EST'; a named zone such as 'Europe/London' needs Intl.DateTimeFormat with a timeZone option.`,\n );\n}\n"]}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * React Native's dev banner - "Refreshing...", "Downloading..." - as its callers drive it.
3
+ */
4
+ export interface LoadingBanner {
5
+ showMessage(message: string, type: string, options?: unknown): void;
6
+ hide(): void;
7
+ }
8
+ /**
9
+ * One show and one hide per burst, however many the callers send.
10
+ *
11
+ * A single edit reaches the banner from three places - Expo's HMR client for each update it
12
+ * receives, its lazy bundle loader, and React Native's own HMR client - so one save sends two or
13
+ * three show/hide pairs inside half a second. On iOS every `hide()` starts a fresh slide upwards
14
+ * from wherever the window is, and a `show()` in between snaps it back while the old slide is still
15
+ * running. UIKit adds the animations together, and the banner is drawn a whole banner-height
16
+ * down: under the navigation bar instead of under the status bar.
17
+ *
18
+ * So a hide goes through only if a show did since the last one, and nothing but an error goes
19
+ * through while a hide is still playing out, which native would half-honour anyway.
20
+ */
21
+ export declare function calmLoadingBanner(banner: LoadingBanner, now?: () => number): void;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * How long a native hide takes to finish: iOS keeps the banner up for at least 0.6s, then slides
3
+ * it away over 0.25s. Anything sent before then lands on a banner still leaving.
4
+ */
5
+ const SETTLE_MS = 900;
6
+ /**
7
+ * One show and one hide per burst, however many the callers send.
8
+ *
9
+ * A single edit reaches the banner from three places - Expo's HMR client for each update it
10
+ * receives, its lazy bundle loader, and React Native's own HMR client - so one save sends two or
11
+ * three show/hide pairs inside half a second. On iOS every `hide()` starts a fresh slide upwards
12
+ * from wherever the window is, and a `show()` in between snaps it back while the old slide is still
13
+ * running. UIKit adds the animations together, and the banner is drawn a whole banner-height
14
+ * down: under the navigation bar instead of under the status bar.
15
+ *
16
+ * So a hide goes through only if a show did since the last one, and nothing but an error goes
17
+ * through while a hide is still playing out, which native would half-honour anyway.
18
+ */
19
+ export function calmLoadingBanner(banner, now = Date.now) {
20
+ const show = banner.showMessage.bind(banner);
21
+ const hide = banner.hide.bind(banner);
22
+ let showing = false;
23
+ let settlesAt = 0;
24
+ banner.showMessage = (message, type, options) => {
25
+ // A compile error always shows: it is the one banner that matters.
26
+ if (type !== 'error' && now() < settlesAt)
27
+ return;
28
+ showing = true;
29
+ show(message, type, options);
30
+ };
31
+ banner.hide = () => {
32
+ if (!showing)
33
+ return;
34
+ showing = false;
35
+ settlesAt = now() + SETTLE_MS;
36
+ hide();
37
+ };
38
+ }
39
+ //# sourceMappingURL=dev-loading-view.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev-loading-view.js","sourceRoot":"","sources":["../src/dev-loading-view.ts"],"names":[],"mappings":"AAQA;;;GAGG;AACH,MAAM,SAAS,GAAG,GAAG,CAAC;AAEtB;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAqB,EAAE,MAAoB,IAAI,CAAC,GAAG;IACnF,MAAM,IAAI,GAAG,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC7C,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACtC,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,SAAS,GAAG,CAAC,CAAC;IAElB,MAAM,CAAC,WAAW,GAAG,CAAC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE;QAC9C,mEAAmE;QACnE,IAAI,IAAI,KAAK,OAAO,IAAI,GAAG,EAAE,GAAG,SAAS;YAAE,OAAO;QAClD,OAAO,GAAG,IAAI,CAAC;QACf,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IAC/B,CAAC,CAAC;IACF,MAAM,CAAC,IAAI,GAAG,GAAG,EAAE;QACjB,IAAI,CAAC,OAAO;YAAE,OAAO;QACrB,OAAO,GAAG,KAAK,CAAC;QAChB,SAAS,GAAG,GAAG,EAAE,GAAG,SAAS,CAAC;QAC9B,IAAI,EAAE,CAAC;IACT,CAAC,CAAC;AACJ,CAAC","sourcesContent":["/**\n * React Native's dev banner - \"Refreshing...\", \"Downloading...\" - as its callers drive it.\n */\nexport interface LoadingBanner {\n showMessage(message: string, type: string, options?: unknown): void;\n hide(): void;\n}\n\n/**\n * How long a native hide takes to finish: iOS keeps the banner up for at least 0.6s, then slides\n * it away over 0.25s. Anything sent before then lands on a banner still leaving.\n */\nconst SETTLE_MS = 900;\n\n/**\n * One show and one hide per burst, however many the callers send.\n *\n * A single edit reaches the banner from three places - Expo's HMR client for each update it\n * receives, its lazy bundle loader, and React Native's own HMR client - so one save sends two or\n * three show/hide pairs inside half a second. On iOS every `hide()` starts a fresh slide upwards\n * from wherever the window is, and a `show()` in between snaps it back while the old slide is still\n * running. UIKit adds the animations together, and the banner is drawn a whole banner-height\n * down: under the navigation bar instead of under the status bar.\n *\n * So a hide goes through only if a show did since the last one, and nothing but an error goes\n * through while a hide is still playing out, which native would half-honour anyway.\n */\nexport function calmLoadingBanner(banner: LoadingBanner, now: () => number = Date.now): void {\n const show = banner.showMessage.bind(banner);\n const hide = banner.hide.bind(banner);\n let showing = false;\n let settlesAt = 0;\n\n banner.showMessage = (message, type, options) => {\n // A compile error always shows: it is the one banner that matters.\n if (type !== 'error' && now() < settlesAt) return;\n showing = true;\n show(message, type, options);\n };\n banner.hide = () => {\n if (!showing) return;\n showing = false;\n settlesAt = now() + SETTLE_MS;\n hide();\n };\n}\n"]}
package/dist/http.d.ts ADDED
@@ -0,0 +1,20 @@
1
+ /**
2
+ * `HttpClient`, configured for React Native.
3
+ *
4
+ * ```ts
5
+ * mount(rootTag, App, getFabricUIManager(), {
6
+ * providers: [provideNativeHttpClient(withInterceptors([auth]))],
7
+ * });
8
+ * ```
9
+ *
10
+ * Use this instead of `provideHttpClient()`, which on a device fails silently. Angular's default
11
+ * backend is `fetch`, and it reads a response body only through `response.body`'s stream. React
12
+ * Native's `fetch` is `whatwg-fetch` over XHR, whose `Response` has no `body`, so every request
13
+ * resolves with `null` and nothing reports why. RN's `XMLHttpRequest` is native and complete,
14
+ * upload progress included, so the XHR backend is the one to use.
15
+ *
16
+ * A separate entry point so an app that never makes a request does not need `@angular/common`.
17
+ */
18
+ import type { EnvironmentProviders } from '@angular/core';
19
+ import { type HttpFeature, type HttpFeatureKind } from '@angular/common/http';
20
+ export declare function provideNativeHttpClient(...features: HttpFeature<HttpFeatureKind>[]): EnvironmentProviders;
package/dist/http.js ADDED
@@ -0,0 +1,5 @@
1
+ import { provideHttpClient, withXhr, } from '@angular/common/http';
2
+ export function provideNativeHttpClient(...features) {
3
+ return provideHttpClient(withXhr(), ...features);
4
+ }
5
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAkBA,OAAO,EACL,iBAAiB,EACjB,OAAO,GAGR,MAAM,sBAAsB,CAAC;AAE9B,MAAM,UAAU,uBAAuB,CACrC,GAAG,QAAwC;IAE3C,OAAO,iBAAiB,CAAC,OAAO,EAAE,EAAE,GAAG,QAAQ,CAAC,CAAC;AACnD,CAAC","sourcesContent":["/**\n * `HttpClient`, configured for React Native.\n *\n * ```ts\n * mount(rootTag, App, getFabricUIManager(), {\n * providers: [provideNativeHttpClient(withInterceptors([auth]))],\n * });\n * ```\n *\n * Use this instead of `provideHttpClient()`, which on a device fails silently. Angular's default\n * backend is `fetch`, and it reads a response body only through `response.body`'s stream. React\n * Native's `fetch` is `whatwg-fetch` over XHR, whose `Response` has no `body`, so every request\n * resolves with `null` and nothing reports why. RN's `XMLHttpRequest` is native and complete,\n * upload progress included, so the XHR backend is the one to use.\n *\n * A separate entry point so an app that never makes a request does not need `@angular/common`.\n */\nimport type { EnvironmentProviders } from '@angular/core';\nimport {\n provideHttpClient,\n withXhr,\n type HttpFeature,\n type HttpFeatureKind,\n} from '@angular/common/http';\n\nexport function provideNativeHttpClient(\n ...features: HttpFeature<HttpFeatureKind>[]\n): EnvironmentProviders {\n return provideHttpClient(withXhr(), ...features);\n}\n"]}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * The Angular half of the seam: `Renderer2` / `RendererFactory2` over the engine's mutation
3
+ * API, plus the bootstrap that stands in for `platform-browser`, which is never booted here.
4
+ */
5
+ export { NativeRendererFactory, mount, type MountResult } from './adapter.ts';
6
+ export { PLATFORM_NATIVE_ID, isPlatformNative } from './platform-id.ts';
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ /**
2
+ * The Angular half of the seam: `Renderer2` / `RendererFactory2` over the engine's mutation
3
+ * API, plus the bootstrap that stands in for `platform-browser`, which is never booted here.
4
+ */
5
+ export { NativeRendererFactory, mount } from "./adapter.js";
6
+ export { PLATFORM_NATIVE_ID, isPlatformNative } from "./platform-id.js";
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,qBAAqB,EAAE,KAAK,EAAoB,MAAM,cAAc,CAAC;AAC9E,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC","sourcesContent":["/**\n * The Angular half of the seam: `Renderer2` / `RendererFactory2` over the engine's mutation\n * API, plus the bootstrap that stands in for `platform-browser`, which is never booted here.\n */\nexport { NativeRendererFactory, mount, type MountResult } from './adapter.ts';\nexport { PLATFORM_NATIVE_ID, isPlatformNative } from './platform-id.ts';\n"]}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * What `PLATFORM_ID` says on a device, and the check that asks it.
3
+ *
4
+ * Angular's own values are `'browser'` and `'server'`, and its default when nothing provides one
5
+ * is `'unknown'`, which `isPlatformBrowser()` and `isPlatformServer()` both answer false for. A
6
+ * native app provides `'native'`, so a library can tell it apart - and one that guards its DOM
7
+ * work with `isPlatformBrowser(id)` keeps its hands off, as it should, since there is no DOM here.
8
+ *
9
+ * ```ts
10
+ * private readonly platformId = inject(PLATFORM_ID);
11
+ * if (isPlatformNative(this.platformId)) { ... }
12
+ * ```
13
+ */
14
+ export declare const PLATFORM_NATIVE_ID = "native";
15
+ /** Whether a `PLATFORM_ID` is a native app's, as `isPlatformBrowser()` is for a browser's. */
16
+ export declare function isPlatformNative(platformId: object | string): boolean;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * What `PLATFORM_ID` says on a device, and the check that asks it.
3
+ *
4
+ * Angular's own values are `'browser'` and `'server'`, and its default when nothing provides one
5
+ * is `'unknown'`, which `isPlatformBrowser()` and `isPlatformServer()` both answer false for. A
6
+ * native app provides `'native'`, so a library can tell it apart - and one that guards its DOM
7
+ * work with `isPlatformBrowser(id)` keeps its hands off, as it should, since there is no DOM here.
8
+ *
9
+ * ```ts
10
+ * private readonly platformId = inject(PLATFORM_ID);
11
+ * if (isPlatformNative(this.platformId)) { ... }
12
+ * ```
13
+ */
14
+ export const PLATFORM_NATIVE_ID = 'native';
15
+ /** Whether a `PLATFORM_ID` is a native app's, as `isPlatformBrowser()` is for a browser's. */
16
+ export function isPlatformNative(platformId) {
17
+ return platformId === PLATFORM_NATIVE_ID;
18
+ }
19
+ //# sourceMappingURL=platform-id.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"platform-id.js","sourceRoot":"","sources":["../src/platform-id.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,QAAQ,CAAC;AAE3C,8FAA8F;AAC9F,MAAM,UAAU,gBAAgB,CAAC,UAA2B;IAC1D,OAAO,UAAU,KAAK,kBAAkB,CAAC;AAC3C,CAAC","sourcesContent":["/**\n * What `PLATFORM_ID` says on a device, and the check that asks it.\n *\n * Angular's own values are `'browser'` and `'server'`, and its default when nothing provides one\n * is `'unknown'`, which `isPlatformBrowser()` and `isPlatformServer()` both answer false for. A\n * native app provides `'native'`, so a library can tell it apart - and one that guards its DOM\n * work with `isPlatformBrowser(id)` keeps its hands off, as it should, since there is no DOM here.\n *\n * ```ts\n * private readonly platformId = inject(PLATFORM_ID);\n * if (isPlatformNative(this.platformId)) { ... }\n * ```\n */\nexport const PLATFORM_NATIVE_ID = 'native';\n\n/** Whether a `PLATFORM_ID` is a native app's, as `isPlatformBrowser()` is for a browser's. */\nexport function isPlatformNative(platformId: object | string): boolean {\n return platformId === PLATFORM_NATIVE_ID;\n}\n"]}
package/package.json CHANGED
@@ -1,12 +1,72 @@
1
1
  {
2
2
  "name": "@ng-native/platform",
3
- "version": "0.0.1",
4
- "description": "Reserved for ng-native. The first release follows.",
3
+ "version": "0.1.0",
4
+ "description": "Angular Native's Renderer2 and bootstrap: mounts an Angular application onto React Native's Fabric.",
5
+ "keywords": [
6
+ "angular",
7
+ "react-native",
8
+ "angular-native",
9
+ "ios",
10
+ "android",
11
+ "fabric",
12
+ "native",
13
+ "renderer",
14
+ "bootstrap"
15
+ ],
16
+ "type": "module",
17
+ "main": "dist/index.js",
18
+ "exports": {
19
+ ".": {
20
+ "types": "./dist/index.d.ts",
21
+ "default": "./dist/index.js"
22
+ },
23
+ "./http": {
24
+ "types": "./dist/http.d.ts",
25
+ "default": "./dist/http.js"
26
+ },
27
+ "./package.json": "./package.json"
28
+ },
29
+ "publishConfig": {
30
+ "access": "public"
31
+ },
32
+ "dependencies": {
33
+ "@ng-native/fabric": "0.1.0"
34
+ },
35
+ "peerDependencies": {
36
+ "@angular/core": "^22.0.0",
37
+ "@angular/common": "^22.0.0"
38
+ },
39
+ "peerDependenciesMeta": {
40
+ "@angular/common": {
41
+ "optional": true
42
+ }
43
+ },
44
+ "nx": {
45
+ "tags": [
46
+ "scope:platform",
47
+ "layer:angular"
48
+ ]
49
+ },
5
50
  "license": "MIT",
51
+ "homepage": "https://ng-native.com/packages/platform",
52
+ "bugs": {
53
+ "url": "https://github.com/ng-native/ng-native/issues"
54
+ },
6
55
  "repository": {
7
56
  "type": "git",
8
57
  "url": "git+https://github.com/ng-native/ng-native.git",
9
58
  "directory": "packages/platform"
10
59
  },
11
- "homepage": "https://ng-native.com/packages/platform"
60
+ "files": [
61
+ "dist"
62
+ ],
63
+ "devDependencies": {
64
+ "@angular/common": "22.2.0",
65
+ "@angular/core": "22.2.0"
66
+ },
67
+ "scripts": {
68
+ "build": "ngc -p tsconfig.build.json",
69
+ "typecheck": "tsc -p tsconfig.json --noEmit"
70
+ },
71
+ "types": "dist/index.d.ts"
12
72
  }