@analogjs/astro-angular 3.0.0-alpha.91 → 3.0.0-alpha.93

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@analogjs/astro-angular",
3
- "version": "3.0.0-alpha.91",
3
+ "version": "3.0.0-alpha.93",
4
4
  "description": "Use Angular components within Astro",
5
5
  "type": "module",
6
6
  "author": "Brandon Roberts <robertsbt@gmail.com>",
@@ -56,7 +56,7 @@
56
56
  "url": "https://github.com/sponsors/brandonroberts"
57
57
  },
58
58
  "dependencies": {
59
- "@analogjs/vite-plugin-angular": "3.0.0-alpha.91",
59
+ "@analogjs/vite-plugin-angular": "3.0.0-alpha.93",
60
60
  "rehype": "^13.0.2"
61
61
  },
62
62
  "peerDependencies": {
package/src/client-ngh.js CHANGED
@@ -1,11 +1,12 @@
1
+ import { buildProjectableNodes, collectProjectedNodes } from "./projection.js";
1
2
  import { ID_PROP_NAME } from "./id.js";
2
- import { createComponentBindings, getComponentElementTag } from "./create-component.js";
3
+ import { createComponentBindings, getComponentElementTag, registerRootComponent } from "./create-component.js";
3
4
  import { ensureSsrIntegrityMarker } from "./ssr-integrity.js";
4
- import { APP_BOOTSTRAP_LISTENER, APP_ID, createComponent, provideZonelessChangeDetection, reflectComponentType } from "@angular/core";
5
+ import { APP_ID, createComponent, provideZonelessChangeDetection, reflectComponentType } from "@angular/core";
5
6
  import { createApplication, provideClientHydration } from "@angular/platform-browser";
6
7
  //#region packages/astro-angular/src/client-ngh.ts
7
8
  var client_ngh_default = (element) => {
8
- return (Component, props) => {
9
+ return (Component, props, slots) => {
9
10
  const mirror = reflectComponentType(Component);
10
11
  if (!mirror) return;
11
12
  ensureSsrIntegrityMarker();
@@ -17,7 +18,8 @@ var client_ngh_default = (element) => {
17
18
  reuseDom = false;
18
19
  }
19
20
  const ngAppId = hostElement?.getAttribute(ID_PROP_NAME);
20
- createApplication({ providers: [
21
+ const projectableNodes = (reuseDom && ngAppId ? collectProjectedNodes(hostElement, mirror, ngAppId) : void 0) ?? buildProjectableNodes(mirror, slots, document);
22
+ return createApplication({ providers: [
21
23
  provideZonelessChangeDetection(),
22
24
  reuseDom ? provideClientHydration(...Component.hydrationFeatures?.() || []) : [],
23
25
  {
@@ -26,14 +28,13 @@ var client_ngh_default = (element) => {
26
28
  },
27
29
  ...Component.clientProviders || []
28
30
  ] }).then((appRef) => {
29
- const componentRef = createComponent(Component, {
31
+ registerRootComponent(appRef, createComponent(Component, {
30
32
  environmentInjector: appRef.injector,
31
33
  hostElement,
34
+ projectableNodes,
32
35
  bindings: createComponentBindings(mirror, props, hostElement)
33
- });
34
- appRef.attachView(componentRef.hostView);
35
- appRef.components.push(componentRef);
36
- appRef.injector.get(APP_BOOTSTRAP_LISTENER, []).forEach((cb) => cb(componentRef));
36
+ }));
37
+ return appRef;
37
38
  }).catch((error) => {
38
39
  console.error("Failed to hydrate Angular component:", error);
39
40
  });
@@ -1 +1 @@
1
- {"version":3,"file":"client-ngh.js","names":[],"sources":["../../src/client-ngh.ts"],"sourcesContent":["import {\n type EnvironmentProviders,\n type Provider,\n reflectComponentType,\n provideZonelessChangeDetection,\n Type,\n APP_ID,\n createComponent,\n APP_BOOTSTRAP_LISTENER,\n} from '@angular/core';\nimport {\n createApplication,\n type HydrationFeature,\n type HydrationFeatureKind,\n provideClientHydration,\n} from '@angular/platform-browser';\nimport {\n createComponentBindings,\n getComponentElementTag,\n} from './create-component.ts';\nimport { ID_PROP_NAME } from './id.ts';\nimport { ensureSsrIntegrityMarker } from './ssr-integrity.ts';\n\nexport default (element: HTMLElement) => {\n return (\n Component: Type<unknown> & {\n clientProviders?: (Provider | EnvironmentProviders)[];\n hydrationFeatures?: () => HydrationFeature<HydrationFeatureKind>[];\n },\n props?: Record<string, unknown>,\n ) => {\n const mirror = reflectComponentType(Component);\n\n if (!mirror) {\n // Not an Angular component\n return;\n }\n\n ensureSsrIntegrityMarker();\n\n let hostElement = element.querySelector(mirror.selector);\n let reuseDom = true;\n\n if (!hostElement) {\n // This is a client-only component\n hostElement = document.createElement(getComponentElementTag(mirror));\n element.appendChild(hostElement);\n reuseDom = false;\n }\n\n const ngAppId = hostElement?.getAttribute(ID_PROP_NAME);\n\n createApplication({\n providers: [\n provideZonelessChangeDetection(),\n reuseDom\n ? provideClientHydration(...(Component.hydrationFeatures?.() || []))\n : [],\n {\n provide: APP_ID,\n useValue: ngAppId || 'ng',\n },\n ...(Component.clientProviders || []),\n ],\n })\n .then((appRef) => {\n const componentRef = createComponent(Component, {\n environmentInjector: appRef.injector,\n hostElement,\n bindings: createComponentBindings(mirror, props, hostElement),\n });\n\n appRef.attachView(componentRef.hostView);\n\n appRef.components.push(componentRef);\n\n appRef.injector\n .get(APP_BOOTSTRAP_LISTENER, [])\n .forEach((cb) => cb(componentRef));\n })\n .catch((error) => {\n console.error('Failed to hydrate Angular component:', error);\n });\n };\n};\n"],"mappings":";;;;;;AAuBA,IAAA,sBAAgB,YAAyB;AACvC,SACE,WAIA,UACG;EACH,MAAM,SAAS,qBAAqB,UAAU;AAE9C,MAAI,CAAC,OAEH;AAGF,4BAA0B;EAE1B,IAAI,cAAc,QAAQ,cAAc,OAAO,SAAS;EACxD,IAAI,WAAW;AAEf,MAAI,CAAC,aAAa;AAEhB,iBAAc,SAAS,cAAc,uBAAuB,OAAO,CAAC;AACpE,WAAQ,YAAY,YAAY;AAChC,cAAW;;EAGb,MAAM,UAAU,aAAa,aAAa,aAAa;AAEvD,oBAAkB,EAChB,WAAW;GACT,gCAAgC;GAChC,WACI,uBAAuB,GAAI,UAAU,qBAAqB,IAAI,EAAE,CAAE,GAClE,EAAE;GACN;IACE,SAAS;IACT,UAAU,WAAW;IACtB;GACD,GAAI,UAAU,mBAAmB,EAAE;GACpC,EACF,CAAC,CACC,MAAM,WAAW;GAChB,MAAM,eAAe,gBAAgB,WAAW;IAC9C,qBAAqB,OAAO;IAC5B;IACA,UAAU,wBAAwB,QAAQ,OAAO,YAAY;IAC9D,CAAC;AAEF,UAAO,WAAW,aAAa,SAAS;AAExC,UAAO,WAAW,KAAK,aAAa;AAEpC,UAAO,SACJ,IAAI,wBAAwB,EAAE,CAAC,CAC/B,SAAS,OAAO,GAAG,aAAa,CAAC;IACpC,CACD,OAAO,UAAU;AAChB,WAAQ,MAAM,wCAAwC,MAAM;IAC5D"}
1
+ {"version":3,"file":"client-ngh.js","names":[],"sources":["../../src/client-ngh.ts"],"sourcesContent":["import {\n type EnvironmentProviders,\n type Provider,\n reflectComponentType,\n provideZonelessChangeDetection,\n Type,\n APP_ID,\n createComponent,\n} from '@angular/core';\nimport {\n createApplication,\n type HydrationFeature,\n type HydrationFeatureKind,\n provideClientHydration,\n} from '@angular/platform-browser';\nimport {\n createComponentBindings,\n getComponentElementTag,\n registerRootComponent,\n} from './create-component.ts';\nimport { ID_PROP_NAME } from './id.ts';\nimport { ensureSsrIntegrityMarker } from './ssr-integrity.ts';\nimport { buildProjectableNodes, collectProjectedNodes } from './projection.ts';\n\nexport default (element: HTMLElement) => {\n return (\n Component: Type<unknown> & {\n clientProviders?: (Provider | EnvironmentProviders)[];\n hydrationFeatures?: () => HydrationFeature<HydrationFeatureKind>[];\n },\n props?: Record<string, unknown>,\n slots?: unknown,\n ) => {\n const mirror = reflectComponentType(Component);\n\n if (!mirror) {\n // Not an Angular component\n return;\n }\n\n ensureSsrIntegrityMarker();\n\n let hostElement = element.querySelector(mirror.selector);\n let reuseDom = true;\n\n if (!hostElement) {\n // This is a client-only component\n hostElement = document.createElement(getComponentElementTag(mirror));\n element.appendChild(hostElement);\n reuseDom = false;\n }\n\n const ngAppId = hostElement?.getAttribute(ID_PROP_NAME);\n\n // Hydration reuses the server-rendered DOM, so hand Angular the projected\n // nodes it already rendered instead of freshly parsed copies.\n const projectableNodes =\n (reuseDom && ngAppId\n ? collectProjectedNodes(hostElement, mirror, ngAppId)\n : undefined) ?? buildProjectableNodes(mirror, slots, document);\n\n return createApplication({\n providers: [\n provideZonelessChangeDetection(),\n reuseDom\n ? provideClientHydration(...(Component.hydrationFeatures?.() || []))\n : [],\n {\n provide: APP_ID,\n useValue: ngAppId || 'ng',\n },\n ...(Component.clientProviders || []),\n ],\n })\n .then((appRef) => {\n const componentRef = createComponent(Component, {\n environmentInjector: appRef.injector,\n hostElement,\n projectableNodes,\n bindings: createComponentBindings(mirror, props, hostElement),\n });\n\n registerRootComponent(appRef, componentRef);\n\n return appRef;\n })\n .catch((error) => {\n console.error('Failed to hydrate Angular component:', error);\n });\n };\n};\n"],"mappings":";;;;;;;AAwBA,IAAA,sBAAgB,YAAyB;AACvC,SACE,WAIA,OACA,UACG;EACH,MAAM,SAAS,qBAAqB,UAAU;AAE9C,MAAI,CAAC,OAEH;AAGF,4BAA0B;EAE1B,IAAI,cAAc,QAAQ,cAAc,OAAO,SAAS;EACxD,IAAI,WAAW;AAEf,MAAI,CAAC,aAAa;AAEhB,iBAAc,SAAS,cAAc,uBAAuB,OAAO,CAAC;AACpE,WAAQ,YAAY,YAAY;AAChC,cAAW;;EAGb,MAAM,UAAU,aAAa,aAAa,aAAa;EAIvD,MAAM,oBACH,YAAY,UACT,sBAAsB,aAAa,QAAQ,QAAQ,GACnD,KAAA,MAAc,sBAAsB,QAAQ,OAAO,SAAS;AAElE,SAAO,kBAAkB,EACvB,WAAW;GACT,gCAAgC;GAChC,WACI,uBAAuB,GAAI,UAAU,qBAAqB,IAAI,EAAE,CAAE,GAClE,EAAE;GACN;IACE,SAAS;IACT,UAAU,WAAW;IACtB;GACD,GAAI,UAAU,mBAAmB,EAAE;GACpC,EACF,CAAC,CACC,MAAM,WAAW;AAQhB,yBAAsB,QAPD,gBAAgB,WAAW;IAC9C,qBAAqB,OAAO;IAC5B;IACA;IACA,UAAU,wBAAwB,QAAQ,OAAO,YAAY;IAC9D,CAAC,CAEyC;AAE3C,UAAO;IACP,CACD,OAAO,UAAU;AAChB,WAAQ,MAAM,wCAAwC,MAAM;IAC5D"}
package/src/client.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { EnvironmentProviders, Provider, ɵComponentType as ComponentType } from "@angular/core";
2
2
  declare const _default: (element: HTMLElement) => ((Component: ComponentType<unknown> & {
3
3
  clientProviders?: (Provider | EnvironmentProviders)[];
4
- }, props?: Record<string, unknown>, _childHTML?: unknown) => void);
4
+ }, props?: Record<string, unknown>, slots?: unknown) => void);
5
5
  export default _default;
package/src/client.js CHANGED
@@ -1,19 +1,22 @@
1
+ import { buildProjectableNodes } from "./projection.js";
1
2
  import { createComponent, provideZonelessChangeDetection, reflectComponentType } from "@angular/core";
2
3
  import { createApplication } from "@angular/platform-browser";
3
4
  import { Subject, takeUntil } from "rxjs";
4
5
  //#region packages/astro-angular/src/client.ts
5
6
  var client_default = (element) => {
6
- return (Component, props, _childHTML) => {
7
+ return (Component, props, slots) => {
8
+ const mirror = reflectComponentType(Component);
9
+ if (!mirror) return;
7
10
  createApplication({ providers: [provideZonelessChangeDetection(), ...Component.clientProviders || []] }).then((appRef) => {
8
11
  const componentRef = createComponent(Component, {
9
12
  environmentInjector: appRef.injector,
10
- hostElement: element
13
+ hostElement: element,
14
+ projectableNodes: buildProjectableNodes(mirror, slots, document)
11
15
  });
12
- const mirror = reflectComponentType(Component);
13
- if (props && mirror) {
16
+ if (props) {
14
17
  for (const [key, value] of Object.entries(props)) if (mirror.inputs.some(({ templateName, propName }) => templateName === key || propName === key)) componentRef.setInput(key, value);
15
18
  }
16
- if (mirror?.outputs.length && props?.["data-analog-id"]) {
19
+ if (mirror.outputs.length && props?.["data-analog-id"]) {
17
20
  const destroySubject = new Subject();
18
21
  element.setAttribute("data-analog-id", props["data-analog-id"]);
19
22
  mirror.outputs.forEach(({ templateName, propName }) => {
package/src/client.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","names":[],"sources":["../../src/client.ts"],"sourcesContent":["import {\n EnvironmentProviders,\n Provider,\n reflectComponentType,\n ɵComponentType as ComponentType,\n provideZonelessChangeDetection,\n} from '@angular/core';\nimport { ApplicationRef, createComponent } from '@angular/core';\nimport { createApplication } from '@angular/platform-browser';\nimport { Observable, Subject, takeUntil } from 'rxjs';\n\nexport default (\n element: HTMLElement,\n): ((\n Component: ComponentType<unknown> & {\n clientProviders?: (Provider | EnvironmentProviders)[];\n },\n props?: Record<string, unknown>,\n _childHTML?: unknown,\n) => void) => {\n return (\n Component: ComponentType<unknown> & {\n clientProviders?: (Provider | EnvironmentProviders)[];\n },\n props?: Record<string, unknown>,\n _childHTML?: unknown,\n ): void => {\n createApplication({\n providers: [\n provideZonelessChangeDetection(),\n ...(Component.clientProviders || []),\n ],\n }).then((appRef: ApplicationRef) => {\n const componentRef = createComponent(Component, {\n environmentInjector: appRef.injector,\n hostElement: element,\n });\n\n const mirror = reflectComponentType(Component);\n if (props && mirror) {\n for (const [key, value] of Object.entries(props)) {\n if (\n mirror.inputs.some(\n ({ templateName, propName }) =>\n templateName === key || propName === key,\n )\n ) {\n componentRef.setInput(key, value);\n }\n }\n }\n\n if (mirror?.outputs.length && props?.['data-analog-id']) {\n const destroySubject = new Subject<void>();\n element.setAttribute(\n 'data-analog-id',\n props['data-analog-id'] as string,\n );\n\n mirror.outputs.forEach(({ templateName, propName }) => {\n const outputName = templateName || propName;\n const component = componentRef.instance as Record<\n string,\n Observable<unknown>\n >;\n component[outputName]\n .pipe(takeUntil(destroySubject))\n .subscribe((detail) => {\n const event = new CustomEvent(outputName, {\n bubbles: true,\n cancelable: true,\n composed: true,\n detail,\n });\n element.dispatchEvent(event);\n });\n });\n\n appRef.onDestroy(() => {\n destroySubject.next();\n destroySubject.complete();\n });\n }\n\n appRef.attachView(componentRef.hostView);\n });\n };\n};\n"],"mappings":";;;;AAWA,IAAA,kBACE,YAOY;AACZ,SACE,WAGA,OACA,eACS;AACT,oBAAkB,EAChB,WAAW,CACT,gCAAgC,EAChC,GAAI,UAAU,mBAAmB,EAAE,CACpC,EACF,CAAC,CAAC,MAAM,WAA2B;GAClC,MAAM,eAAe,gBAAgB,WAAW;IAC9C,qBAAqB,OAAO;IAC5B,aAAa;IACd,CAAC;GAEF,MAAM,SAAS,qBAAqB,UAAU;AAC9C,OAAI,SAAS;SACN,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,CAC9C,KACE,OAAO,OAAO,MACX,EAAE,cAAc,eACf,iBAAiB,OAAO,aAAa,IACxC,CAED,cAAa,SAAS,KAAK,MAAM;;AAKvC,OAAI,QAAQ,QAAQ,UAAU,QAAQ,mBAAmB;IACvD,MAAM,iBAAiB,IAAI,SAAe;AAC1C,YAAQ,aACN,kBACA,MAAM,kBACP;AAED,WAAO,QAAQ,SAAS,EAAE,cAAc,eAAe;KACrD,MAAM,aAAa,gBAAgB;AACjB,kBAAa,SAIrB,YACP,KAAK,UAAU,eAAe,CAAC,CAC/B,WAAW,WAAW;MACrB,MAAM,QAAQ,IAAI,YAAY,YAAY;OACxC,SAAS;OACT,YAAY;OACZ,UAAU;OACV;OACD,CAAC;AACF,cAAQ,cAAc,MAAM;OAC5B;MACJ;AAEF,WAAO,gBAAgB;AACrB,oBAAe,MAAM;AACrB,oBAAe,UAAU;MACzB;;AAGJ,UAAO,WAAW,aAAa,SAAS;IACxC"}
1
+ {"version":3,"file":"client.js","names":[],"sources":["../../src/client.ts"],"sourcesContent":["import {\n EnvironmentProviders,\n Provider,\n reflectComponentType,\n ɵComponentType as ComponentType,\n provideZonelessChangeDetection,\n} from '@angular/core';\nimport { ApplicationRef, createComponent } from '@angular/core';\nimport { createApplication } from '@angular/platform-browser';\nimport { Observable, Subject, takeUntil } from 'rxjs';\nimport { buildProjectableNodes } from './projection.ts';\n\nexport default (\n element: HTMLElement,\n): ((\n Component: ComponentType<unknown> & {\n clientProviders?: (Provider | EnvironmentProviders)[];\n },\n props?: Record<string, unknown>,\n slots?: unknown,\n) => void) => {\n return (\n Component: ComponentType<unknown> & {\n clientProviders?: (Provider | EnvironmentProviders)[];\n },\n props?: Record<string, unknown>,\n slots?: unknown,\n ): void => {\n const mirror = reflectComponentType(Component);\n\n if (!mirror) {\n // Not an Angular component\n return;\n }\n\n createApplication({\n providers: [\n provideZonelessChangeDetection(),\n ...(Component.clientProviders || []),\n ],\n }).then((appRef: ApplicationRef) => {\n const componentRef = createComponent(Component, {\n environmentInjector: appRef.injector,\n hostElement: element,\n projectableNodes: buildProjectableNodes(mirror, slots, document),\n });\n\n if (props) {\n for (const [key, value] of Object.entries(props)) {\n if (\n mirror.inputs.some(\n ({ templateName, propName }) =>\n templateName === key || propName === key,\n )\n ) {\n componentRef.setInput(key, value);\n }\n }\n }\n\n if (mirror.outputs.length && props?.['data-analog-id']) {\n const destroySubject = new Subject<void>();\n element.setAttribute(\n 'data-analog-id',\n props['data-analog-id'] as string,\n );\n\n mirror.outputs.forEach(({ templateName, propName }) => {\n const outputName = templateName || propName;\n const component = componentRef.instance as Record<\n string,\n Observable<unknown>\n >;\n component[outputName]\n .pipe(takeUntil(destroySubject))\n .subscribe((detail) => {\n const event = new CustomEvent(outputName, {\n bubbles: true,\n cancelable: true,\n composed: true,\n detail,\n });\n element.dispatchEvent(event);\n });\n });\n\n appRef.onDestroy(() => {\n destroySubject.next();\n destroySubject.complete();\n });\n }\n\n appRef.attachView(componentRef.hostView);\n });\n };\n};\n"],"mappings":";;;;;AAYA,IAAA,kBACE,YAOY;AACZ,SACE,WAGA,OACA,UACS;EACT,MAAM,SAAS,qBAAqB,UAAU;AAE9C,MAAI,CAAC,OAEH;AAGF,oBAAkB,EAChB,WAAW,CACT,gCAAgC,EAChC,GAAI,UAAU,mBAAmB,EAAE,CACpC,EACF,CAAC,CAAC,MAAM,WAA2B;GAClC,MAAM,eAAe,gBAAgB,WAAW;IAC9C,qBAAqB,OAAO;IAC5B,aAAa;IACb,kBAAkB,sBAAsB,QAAQ,OAAO,SAAS;IACjE,CAAC;AAEF,OAAI;SACG,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,CAC9C,KACE,OAAO,OAAO,MACX,EAAE,cAAc,eACf,iBAAiB,OAAO,aAAa,IACxC,CAED,cAAa,SAAS,KAAK,MAAM;;AAKvC,OAAI,OAAO,QAAQ,UAAU,QAAQ,mBAAmB;IACtD,MAAM,iBAAiB,IAAI,SAAe;AAC1C,YAAQ,aACN,kBACA,MAAM,kBACP;AAED,WAAO,QAAQ,SAAS,EAAE,cAAc,eAAe;KACrD,MAAM,aAAa,gBAAgB;AACjB,kBAAa,SAIrB,YACP,KAAK,UAAU,eAAe,CAAC,CAC/B,WAAW,WAAW;MACrB,MAAM,QAAQ,IAAI,YAAY,YAAY;OACxC,SAAS;OACT,YAAY;OACZ,UAAU;OACV;OACD,CAAC;AACF,cAAQ,cAAc,MAAM;OAC5B;MACJ;AAEF,WAAO,gBAAgB;AACrB,oBAAe,MAAM;AACrB,oBAAe,UAAU;MACzB;;AAGJ,UAAO,WAAW,aAAa,SAAS;IACxC"}
@@ -1,5 +1,12 @@
1
- import { type Binding, type ComponentMirror } from "@angular/core";
1
+ import { type ApplicationRef, type Binding, type ComponentMirror, type ComponentRef } from "@angular/core";
2
2
  export declare function getComponentElementTag(mirror: ComponentMirror<unknown>): string;
3
3
  export declare function createInputBindings(mirror: ComponentMirror<unknown>, props?: Record<string, unknown>): Binding[];
4
4
  export declare function createOutputBindings(hostElement: Element, mirror: ComponentMirror<unknown>): Binding[];
5
5
  export declare function createComponentBindings(mirror: ComponentMirror<unknown>, props?: Record<string, unknown>, hostElement?: Element): Binding[];
6
+ /**
7
+ * Registers a component created with `createComponent` as a root component,
8
+ * the way `bootstrapApplication` does: attach the view, track it on the
9
+ * application, run the initial change detection and notify
10
+ * `APP_BOOTSTRAP_LISTENER`s such as the router's.
11
+ */
12
+ export declare function registerRootComponent(appRef: ApplicationRef, componentRef: ComponentRef<unknown>): void;
@@ -1,5 +1,5 @@
1
1
  import "./id.js";
2
- import { inputBinding, outputBinding } from "@angular/core";
2
+ import { APP_BOOTSTRAP_LISTENER, inputBinding, outputBinding } from "@angular/core";
3
3
  //#region packages/astro-angular/src/create-component.ts
4
4
  function getComponentElementTag(mirror) {
5
5
  return mirror.selector.split(",")[0] || "ng-component";
@@ -25,7 +25,19 @@ function createComponentBindings(mirror, props, hostElement) {
25
25
  const outputBindings = createOutputBindings(hostElement, mirror);
26
26
  return [...inputBindings, ...outputBindings];
27
27
  }
28
+ /**
29
+ * Registers a component created with `createComponent` as a root component,
30
+ * the way `bootstrapApplication` does: attach the view, track it on the
31
+ * application, run the initial change detection and notify
32
+ * `APP_BOOTSTRAP_LISTENER`s such as the router's.
33
+ */
34
+ function registerRootComponent(appRef, componentRef) {
35
+ appRef.attachView(componentRef.hostView);
36
+ appRef.tick();
37
+ appRef.components.push(componentRef);
38
+ appRef.injector.get(APP_BOOTSTRAP_LISTENER, []).forEach((listener) => listener(componentRef));
39
+ }
28
40
  //#endregion
29
- export { createComponentBindings, getComponentElementTag };
41
+ export { createComponentBindings, createInputBindings, getComponentElementTag, registerRootComponent };
30
42
 
31
43
  //# sourceMappingURL=create-component.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"create-component.js","names":[],"sources":["../../src/create-component.ts"],"sourcesContent":["import {\n type Binding,\n type ComponentMirror,\n inputBinding,\n outputBinding,\n} from '@angular/core';\nimport { ID_PROP_NAME } from './id.ts';\n\nexport function getComponentElementTag(\n mirror: ComponentMirror<unknown>,\n): string {\n return mirror.selector.split(',')[0] || 'ng-component';\n}\n\nexport function createInputBindings(\n mirror: ComponentMirror<unknown>,\n props?: Record<string, unknown>,\n): Binding[] {\n if (!props) {\n return [];\n }\n\n const inputBindings = Object.entries(props)\n .filter(([key]) =>\n mirror.inputs.some(({ templateName }) => templateName === key),\n )\n .map(([key, value]) => inputBinding(key, () => value));\n\n return inputBindings;\n}\n\nexport function createOutputBindings(\n hostElement: Element,\n mirror: ComponentMirror<unknown>,\n): Binding[] {\n const outputBindings = mirror.outputs.map(({ templateName }) =>\n outputBinding(templateName, (detail) => {\n const event = new CustomEvent(templateName, {\n bubbles: true,\n cancelable: true,\n composed: true,\n detail,\n });\n hostElement.dispatchEvent(event);\n }),\n );\n\n return outputBindings;\n}\n\nexport function createComponentBindings(\n mirror: ComponentMirror<unknown>,\n props?: Record<string, unknown>,\n hostElement?: Element,\n): Binding[] {\n const inputBindings = createInputBindings(mirror, props);\n\n if (!mirror.outputs.length || !props?.[ID_PROP_NAME] || !hostElement) {\n return inputBindings;\n }\n\n const outputBindings = createOutputBindings(hostElement, mirror);\n\n return [...inputBindings, ...outputBindings];\n}\n"],"mappings":";;;AAQA,SAAgB,uBACd,QACQ;AACR,QAAO,OAAO,SAAS,MAAM,IAAI,CAAC,MAAM;;AAG1C,SAAgB,oBACd,QACA,OACW;AACX,KAAI,CAAC,MACH,QAAO,EAAE;AASX,QANsB,OAAO,QAAQ,MAAM,CACxC,QAAQ,CAAC,SACR,OAAO,OAAO,MAAM,EAAE,mBAAmB,iBAAiB,IAAI,CAC/D,CACA,KAAK,CAAC,KAAK,WAAW,aAAa,WAAW,MAAM,CAAC;;AAK1D,SAAgB,qBACd,aACA,QACW;AAaX,QAZuB,OAAO,QAAQ,KAAK,EAAE,mBAC3C,cAAc,eAAe,WAAW;EACtC,MAAM,QAAQ,IAAI,YAAY,cAAc;GAC1C,SAAS;GACT,YAAY;GACZ,UAAU;GACV;GACD,CAAC;AACF,cAAY,cAAc,MAAM;GAChC,CACH;;AAKH,SAAgB,wBACd,QACA,OACA,aACW;CACX,MAAM,gBAAgB,oBAAoB,QAAQ,MAAM;AAExD,KAAI,CAAC,OAAO,QAAQ,UAAU,CAAC,QAAA,qBAAyB,CAAC,YACvD,QAAO;CAGT,MAAM,iBAAiB,qBAAqB,aAAa,OAAO;AAEhE,QAAO,CAAC,GAAG,eAAe,GAAG,eAAe"}
1
+ {"version":3,"file":"create-component.js","names":[],"sources":["../../src/create-component.ts"],"sourcesContent":["import {\n APP_BOOTSTRAP_LISTENER,\n type ApplicationRef,\n type Binding,\n type ComponentMirror,\n type ComponentRef,\n inputBinding,\n outputBinding,\n} from '@angular/core';\nimport { ID_PROP_NAME } from './id.ts';\n\nexport function getComponentElementTag(\n mirror: ComponentMirror<unknown>,\n): string {\n return mirror.selector.split(',')[0] || 'ng-component';\n}\n\nexport function createInputBindings(\n mirror: ComponentMirror<unknown>,\n props?: Record<string, unknown>,\n): Binding[] {\n if (!props) {\n return [];\n }\n\n const inputBindings = Object.entries(props)\n .filter(([key]) =>\n mirror.inputs.some(({ templateName }) => templateName === key),\n )\n .map(([key, value]) => inputBinding(key, () => value));\n\n return inputBindings;\n}\n\nexport function createOutputBindings(\n hostElement: Element,\n mirror: ComponentMirror<unknown>,\n): Binding[] {\n const outputBindings = mirror.outputs.map(({ templateName }) =>\n outputBinding(templateName, (detail) => {\n const event = new CustomEvent(templateName, {\n bubbles: true,\n cancelable: true,\n composed: true,\n detail,\n });\n hostElement.dispatchEvent(event);\n }),\n );\n\n return outputBindings;\n}\n\nexport function createComponentBindings(\n mirror: ComponentMirror<unknown>,\n props?: Record<string, unknown>,\n hostElement?: Element,\n): Binding[] {\n const inputBindings = createInputBindings(mirror, props);\n\n if (!mirror.outputs.length || !props?.[ID_PROP_NAME] || !hostElement) {\n return inputBindings;\n }\n\n const outputBindings = createOutputBindings(hostElement, mirror);\n\n return [...inputBindings, ...outputBindings];\n}\n\n/**\n * Registers a component created with `createComponent` as a root component,\n * the way `bootstrapApplication` does: attach the view, track it on the\n * application, run the initial change detection and notify\n * `APP_BOOTSTRAP_LISTENER`s such as the router's.\n */\nexport function registerRootComponent(\n appRef: ApplicationRef,\n componentRef: ComponentRef<unknown>,\n): void {\n appRef.attachView(componentRef.hostView);\n appRef.tick();\n appRef.components.push(componentRef);\n appRef.injector\n .get(APP_BOOTSTRAP_LISTENER, [])\n .forEach((listener) => listener(componentRef));\n}\n"],"mappings":";;;AAWA,SAAgB,uBACd,QACQ;AACR,QAAO,OAAO,SAAS,MAAM,IAAI,CAAC,MAAM;;AAG1C,SAAgB,oBACd,QACA,OACW;AACX,KAAI,CAAC,MACH,QAAO,EAAE;AASX,QANsB,OAAO,QAAQ,MAAM,CACxC,QAAQ,CAAC,SACR,OAAO,OAAO,MAAM,EAAE,mBAAmB,iBAAiB,IAAI,CAC/D,CACA,KAAK,CAAC,KAAK,WAAW,aAAa,WAAW,MAAM,CAAC;;AAK1D,SAAgB,qBACd,aACA,QACW;AAaX,QAZuB,OAAO,QAAQ,KAAK,EAAE,mBAC3C,cAAc,eAAe,WAAW;EACtC,MAAM,QAAQ,IAAI,YAAY,cAAc;GAC1C,SAAS;GACT,YAAY;GACZ,UAAU;GACV;GACD,CAAC;AACF,cAAY,cAAc,MAAM;GAChC,CACH;;AAKH,SAAgB,wBACd,QACA,OACA,aACW;CACX,MAAM,gBAAgB,oBAAoB,QAAQ,MAAM;AAExD,KAAI,CAAC,OAAO,QAAQ,UAAU,CAAC,QAAA,qBAAyB,CAAC,YACvD,QAAO;CAGT,MAAM,iBAAiB,qBAAqB,aAAa,OAAO;AAEhE,QAAO,CAAC,GAAG,eAAe,GAAG,eAAe;;;;;;;;AAS9C,SAAgB,sBACd,QACA,cACM;AACN,QAAO,WAAW,aAAa,SAAS;AACxC,QAAO,MAAM;AACb,QAAO,WAAW,KAAK,aAAa;AACpC,QAAO,SACJ,IAAI,wBAAwB,EAAE,CAAC,CAC/B,SAAS,aAAa,SAAS,aAAa,CAAC"}
@@ -0,0 +1,28 @@
1
+ import type { ComponentMirror } from "@angular/core";
2
+ /**
3
+ * Distributes the HTML Astro rendered for the component's slots across the
4
+ * component's `<ng-content>` slots, using Angular's own selector semantics.
5
+ *
6
+ * The result is indexed to match `mirror.ngContentSelectors`. Angular does not
7
+ * apply `select` matching to `projectableNodes`, so the matching happens here.
8
+ * @param mirror The component mirror, to read the `ng-content` selectors
9
+ * @param slots The slot HTML passed by Astro, keyed by slot name
10
+ * @param document The document the component is rendered into
11
+ * @returns The nodes to project, or `undefined` when the component has no `ng-content`
12
+ */
13
+ export declare function buildProjectableNodes(mirror: ComponentMirror<unknown>, slots: unknown, document: Document): Node[][] | undefined;
14
+ /**
15
+ * Wraps each non-empty slot in comment markers, so the client can find the
16
+ * server-rendered nodes again when hydrating. Angular reuses the DOM during
17
+ * hydration, so it must be handed those nodes rather than freshly parsed ones.
18
+ * @param projectableNodes The distributed nodes, see `buildProjectableNodes`
19
+ * @param appId The island's APP_ID, to keep markers apart from nested islands
20
+ * @param document The document the component is rendered into
21
+ */
22
+ export declare function markProjectableNodes(projectableNodes: Node[][], appId: string, document: Document): Node[][];
23
+ /**
24
+ * Collects the server-rendered projected nodes from the host element using the
25
+ * markers added by `markProjectableNodes`.
26
+ * @returns The nodes per slot, or `undefined` when the host carries no markers
27
+ */
28
+ export declare function collectProjectedNodes(hostElement: Element, mirror: ComponentMirror<unknown>, appId: string): Node[][] | undefined;
@@ -0,0 +1,98 @@
1
+ import { isSameSelector, parseSelectorList } from "./selector.js";
2
+ //#region packages/astro-angular/src/projection.ts
3
+ var ELEMENT_NODE = 1;
4
+ var TEXT_NODE = 3;
5
+ var COMMENT_NODE = 8;
6
+ var SHOW_COMMENT = 128;
7
+ var MARKER_PREFIX = "analog-slot:";
8
+ /**
9
+ * Distributes the HTML Astro rendered for the component's slots across the
10
+ * component's `<ng-content>` slots, using Angular's own selector semantics.
11
+ *
12
+ * The result is indexed to match `mirror.ngContentSelectors`. Angular does not
13
+ * apply `select` matching to `projectableNodes`, so the matching happens here.
14
+ * @param mirror The component mirror, to read the `ng-content` selectors
15
+ * @param slots The slot HTML passed by Astro, keyed by slot name
16
+ * @param document The document the component is rendered into
17
+ * @returns The nodes to project, or `undefined` when the component has no `ng-content`
18
+ */
19
+ function buildProjectableNodes(mirror, slots, document) {
20
+ const selectors = mirror.ngContentSelectors;
21
+ if (!selectors.length) return;
22
+ const projectableNodes = selectors.map(() => []);
23
+ const wildcardIndex = selectors.lastIndexOf("*");
24
+ for (const node of parseSlots(slots, document)) {
25
+ if (node.nodeType === TEXT_NODE && !node.textContent?.trim()) continue;
26
+ const matched = selectors.findIndex((selector) => selector !== "*" && matches(node, selector));
27
+ const target = matched > -1 ? matched : wildcardIndex;
28
+ if (target > -1) projectableNodes[target].push(node);
29
+ }
30
+ return projectableNodes;
31
+ }
32
+ /**
33
+ * Wraps each non-empty slot in comment markers, so the client can find the
34
+ * server-rendered nodes again when hydrating. Angular reuses the DOM during
35
+ * hydration, so it must be handed those nodes rather than freshly parsed ones.
36
+ * @param projectableNodes The distributed nodes, see `buildProjectableNodes`
37
+ * @param appId The island's APP_ID, to keep markers apart from nested islands
38
+ * @param document The document the component is rendered into
39
+ */
40
+ function markProjectableNodes(projectableNodes, appId, document) {
41
+ return projectableNodes.map((nodes, index) => nodes.length ? [
42
+ document.createComment(`${MARKER_PREFIX}${appId}:${index}`),
43
+ ...nodes,
44
+ document.createComment(`/${MARKER_PREFIX}${appId}:${index}`)
45
+ ] : nodes);
46
+ }
47
+ /**
48
+ * Collects the server-rendered projected nodes from the host element using the
49
+ * markers added by `markProjectableNodes`.
50
+ * @returns The nodes per slot, or `undefined` when the host carries no markers
51
+ */
52
+ function collectProjectedNodes(hostElement, mirror, appId) {
53
+ const selectors = mirror.ngContentSelectors;
54
+ if (!selectors.length) return;
55
+ const projectableNodes = selectors.map(() => []);
56
+ const walker = hostElement.ownerDocument.createTreeWalker(hostElement, SHOW_COMMENT);
57
+ let found = false;
58
+ for (let start = walker.nextNode(); start; start = walker.nextNode()) {
59
+ const match = start.textContent?.startsWith(`${MARKER_PREFIX}${appId}:`) ? Number(start.textContent.slice(`${MARKER_PREFIX}${appId}:`.length)) : NaN;
60
+ if (Number.isNaN(match) || match >= selectors.length) continue;
61
+ const end = `/${MARKER_PREFIX}${appId}:${match}`;
62
+ const nodes = [start];
63
+ let node = start.nextSibling;
64
+ while (node && !(node.nodeType === COMMENT_NODE && node.textContent === end)) {
65
+ nodes.push(node);
66
+ node = node.nextSibling;
67
+ }
68
+ if (node) nodes.push(node);
69
+ projectableNodes[match] = nodes;
70
+ found = true;
71
+ }
72
+ return found ? projectableNodes : void 0;
73
+ }
74
+ function parseSlots(slots, document) {
75
+ const html = slots && typeof slots === "object" ? Object.values(slots).join("") : String(slots ?? "");
76
+ if (!html) return [];
77
+ const template = document.createElement("template");
78
+ template.innerHTML = html;
79
+ return Array.from(template.content.childNodes);
80
+ }
81
+ function matches(node, selector) {
82
+ if (node.nodeType !== ELEMENT_NODE) return false;
83
+ const element = node;
84
+ const projectAs = element.getAttribute("ngProjectAs");
85
+ if (projectAs !== null) {
86
+ const [alias] = parseSelectorList(projectAs);
87
+ return parseSelectorList(selector).some((candidate) => isSameSelector(alias, candidate));
88
+ }
89
+ try {
90
+ return element.matches(selector);
91
+ } catch {
92
+ return false;
93
+ }
94
+ }
95
+ //#endregion
96
+ export { buildProjectableNodes, collectProjectedNodes, markProjectableNodes };
97
+
98
+ //# sourceMappingURL=projection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projection.js","names":[],"sources":["../../src/projection.ts"],"sourcesContent":["import type { ComponentMirror } from '@angular/core';\nimport { isSameSelector, parseSelectorList } from './selector.ts';\n\nconst ELEMENT_NODE = 1;\nconst TEXT_NODE = 3;\nconst COMMENT_NODE = 8;\nconst SHOW_COMMENT = 128;\n\nconst MARKER_PREFIX = 'analog-slot:';\n\n/**\n * Distributes the HTML Astro rendered for the component's slots across the\n * component's `<ng-content>` slots, using Angular's own selector semantics.\n *\n * The result is indexed to match `mirror.ngContentSelectors`. Angular does not\n * apply `select` matching to `projectableNodes`, so the matching happens here.\n * @param mirror The component mirror, to read the `ng-content` selectors\n * @param slots The slot HTML passed by Astro, keyed by slot name\n * @param document The document the component is rendered into\n * @returns The nodes to project, or `undefined` when the component has no `ng-content`\n */\nexport function buildProjectableNodes(\n mirror: ComponentMirror<unknown>,\n slots: unknown,\n document: Document,\n): Node[][] | undefined {\n const selectors = mirror.ngContentSelectors;\n\n if (!selectors.length) {\n return undefined;\n }\n\n const projectableNodes: Node[][] = selectors.map(() => []);\n // As in Angular, unmatched content goes to the last default slot.\n const wildcardIndex = selectors.lastIndexOf('*');\n\n for (const node of parseSlots(slots, document)) {\n // Angular drops whitespace-only text nodes between elements at compile\n // time, so projecting them would only suppress `ng-content` fallbacks.\n if (node.nodeType === TEXT_NODE && !node.textContent?.trim()) {\n continue;\n }\n\n const matched = selectors.findIndex(\n (selector) => selector !== '*' && matches(node, selector),\n );\n const target = matched > -1 ? matched : wildcardIndex;\n\n if (target > -1) {\n projectableNodes[target].push(node);\n }\n }\n\n return projectableNodes;\n}\n\n/**\n * Wraps each non-empty slot in comment markers, so the client can find the\n * server-rendered nodes again when hydrating. Angular reuses the DOM during\n * hydration, so it must be handed those nodes rather than freshly parsed ones.\n * @param projectableNodes The distributed nodes, see `buildProjectableNodes`\n * @param appId The island's APP_ID, to keep markers apart from nested islands\n * @param document The document the component is rendered into\n */\nexport function markProjectableNodes(\n projectableNodes: Node[][],\n appId: string,\n document: Document,\n): Node[][] {\n return projectableNodes.map((nodes, index) =>\n nodes.length\n ? [\n document.createComment(`${MARKER_PREFIX}${appId}:${index}`),\n ...nodes,\n document.createComment(`/${MARKER_PREFIX}${appId}:${index}`),\n ]\n : nodes,\n );\n}\n\n/**\n * Collects the server-rendered projected nodes from the host element using the\n * markers added by `markProjectableNodes`.\n * @returns The nodes per slot, or `undefined` when the host carries no markers\n */\nexport function collectProjectedNodes(\n hostElement: Element,\n mirror: ComponentMirror<unknown>,\n appId: string,\n): Node[][] | undefined {\n const selectors = mirror.ngContentSelectors;\n\n if (!selectors.length) {\n return undefined;\n }\n\n const projectableNodes: Node[][] = selectors.map(() => []);\n const walker = hostElement.ownerDocument.createTreeWalker(\n hostElement,\n SHOW_COMMENT,\n );\n let found = false;\n\n for (let start = walker.nextNode(); start; start = walker.nextNode()) {\n const match = start.textContent?.startsWith(`${MARKER_PREFIX}${appId}:`)\n ? Number(start.textContent.slice(`${MARKER_PREFIX}${appId}:`.length))\n : NaN;\n\n if (Number.isNaN(match) || match >= selectors.length) {\n continue;\n }\n\n const end = `/${MARKER_PREFIX}${appId}:${match}`;\n const nodes: Node[] = [start];\n let node = start.nextSibling;\n\n while (\n node &&\n !(node.nodeType === COMMENT_NODE && node.textContent === end)\n ) {\n nodes.push(node);\n node = node.nextSibling;\n }\n\n if (node) {\n nodes.push(node);\n }\n\n projectableNodes[match] = nodes;\n found = true;\n }\n\n return found ? projectableNodes : undefined;\n}\n\nfunction parseSlots(slots: unknown, document: Document): Node[] {\n const html =\n slots && typeof slots === 'object'\n ? Object.values(slots).join('')\n : String(slots ?? '');\n\n if (!html) {\n return [];\n }\n\n const template = document.createElement('template');\n template.innerHTML = html;\n\n return Array.from(template.content.childNodes);\n}\n\nfunction matches(node: Node, selector: string): boolean {\n if (node.nodeType !== ELEMENT_NODE) {\n return false;\n }\n\n const element = node as Element;\n const projectAs = element.getAttribute('ngProjectAs');\n\n // As in Angular, `ngProjectAs` replaces the element's own selector and is\n // compared structurally against the `select` list.\n if (projectAs !== null) {\n const [alias] = parseSelectorList(projectAs);\n return parseSelectorList(selector).some((candidate) =>\n isSameSelector(alias, candidate),\n );\n }\n\n try {\n return element.matches(selector);\n } catch {\n // `ngContentSelectors` may contain selectors that `matches()` rejects.\n return false;\n }\n}\n"],"mappings":";;AAGA,IAAM,eAAe;AACrB,IAAM,YAAY;AAClB,IAAM,eAAe;AACrB,IAAM,eAAe;AAErB,IAAM,gBAAgB;;;;;;;;;;;;AAatB,SAAgB,sBACd,QACA,OACA,UACsB;CACtB,MAAM,YAAY,OAAO;AAEzB,KAAI,CAAC,UAAU,OACb;CAGF,MAAM,mBAA6B,UAAU,UAAU,EAAE,CAAC;CAE1D,MAAM,gBAAgB,UAAU,YAAY,IAAI;AAEhD,MAAK,MAAM,QAAQ,WAAW,OAAO,SAAS,EAAE;AAG9C,MAAI,KAAK,aAAa,aAAa,CAAC,KAAK,aAAa,MAAM,CAC1D;EAGF,MAAM,UAAU,UAAU,WACvB,aAAa,aAAa,OAAO,QAAQ,MAAM,SAAS,CAC1D;EACD,MAAM,SAAS,UAAU,KAAK,UAAU;AAExC,MAAI,SAAS,GACX,kBAAiB,QAAQ,KAAK,KAAK;;AAIvC,QAAO;;;;;;;;;;AAWT,SAAgB,qBACd,kBACA,OACA,UACU;AACV,QAAO,iBAAiB,KAAK,OAAO,UAClC,MAAM,SACF;EACE,SAAS,cAAc,GAAG,gBAAgB,MAAM,GAAG,QAAQ;EAC3D,GAAG;EACH,SAAS,cAAc,IAAI,gBAAgB,MAAM,GAAG,QAAQ;EAC7D,GACD,MACL;;;;;;;AAQH,SAAgB,sBACd,aACA,QACA,OACsB;CACtB,MAAM,YAAY,OAAO;AAEzB,KAAI,CAAC,UAAU,OACb;CAGF,MAAM,mBAA6B,UAAU,UAAU,EAAE,CAAC;CAC1D,MAAM,SAAS,YAAY,cAAc,iBACvC,aACA,aACD;CACD,IAAI,QAAQ;AAEZ,MAAK,IAAI,QAAQ,OAAO,UAAU,EAAE,OAAO,QAAQ,OAAO,UAAU,EAAE;EACpE,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG,gBAAgB,MAAM,GAAG,GACpE,OAAO,MAAM,YAAY,MAAM,GAAG,gBAAgB,MAAM,GAAG,OAAO,CAAC,GACnE;AAEJ,MAAI,OAAO,MAAM,MAAM,IAAI,SAAS,UAAU,OAC5C;EAGF,MAAM,MAAM,IAAI,gBAAgB,MAAM,GAAG;EACzC,MAAM,QAAgB,CAAC,MAAM;EAC7B,IAAI,OAAO,MAAM;AAEjB,SACE,QACA,EAAE,KAAK,aAAa,gBAAgB,KAAK,gBAAgB,MACzD;AACA,SAAM,KAAK,KAAK;AAChB,UAAO,KAAK;;AAGd,MAAI,KACF,OAAM,KAAK,KAAK;AAGlB,mBAAiB,SAAS;AAC1B,UAAQ;;AAGV,QAAO,QAAQ,mBAAmB,KAAA;;AAGpC,SAAS,WAAW,OAAgB,UAA4B;CAC9D,MAAM,OACJ,SAAS,OAAO,UAAU,WACtB,OAAO,OAAO,MAAM,CAAC,KAAK,GAAG,GAC7B,OAAO,SAAS,GAAG;AAEzB,KAAI,CAAC,KACH,QAAO,EAAE;CAGX,MAAM,WAAW,SAAS,cAAc,WAAW;AACnD,UAAS,YAAY;AAErB,QAAO,MAAM,KAAK,SAAS,QAAQ,WAAW;;AAGhD,SAAS,QAAQ,MAAY,UAA2B;AACtD,KAAI,KAAK,aAAa,aACpB,QAAO;CAGT,MAAM,UAAU;CAChB,MAAM,YAAY,QAAQ,aAAa,cAAc;AAIrD,KAAI,cAAc,MAAM;EACtB,MAAM,CAAC,SAAS,kBAAkB,UAAU;AAC5C,SAAO,kBAAkB,SAAS,CAAC,MAAM,cACvC,eAAe,OAAO,UAAU,CACjC;;AAGH,KAAI;AACF,SAAO,QAAQ,QAAQ,SAAS;SAC1B;AAEN,SAAO"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * A CSS selector in the shape Angular's compiler uses for `ng-content` and
3
+ * `ngProjectAs` matching. Ported from `CssSelector` in `@angular/compiler`.
4
+ */
5
+ export interface ParsedSelector {
6
+ element: string;
7
+ attrs: [string, string][];
8
+ classNames: string[];
9
+ notSelectors: ParsedSelector[];
10
+ }
11
+ export declare function parseSelectorList(selector: string): ParsedSelector[];
12
+ /**
13
+ * Structural equality, matching how Angular compares an `ngProjectAs` selector
14
+ * against the selectors of an `ng-content`.
15
+ */
16
+ export declare function isSameSelector(a: ParsedSelector, b: ParsedSelector): boolean;
@@ -0,0 +1,63 @@
1
+ //#region packages/astro-angular/src/selector.ts
2
+ var SELECTOR_REGEXP = /* @__PURE__ */ new RegExp("(\\:not\\()|(([\\.\\#]?)[-\\w]+)|(?:\\[([-.\\w*\\\\$]+)(?:=([\"']?)([^\\]\"']*)\\5)?\\])|(\\))|(\\s*,\\s*)", "g");
3
+ var NOT = 1;
4
+ var TAG = 2;
5
+ var PREFIX = 3;
6
+ var ATTRIBUTE = 4;
7
+ var ATTRIBUTE_VALUE = 6;
8
+ var NOT_END = 7;
9
+ var SEPARATOR = 8;
10
+ function createSelector() {
11
+ return {
12
+ element: "",
13
+ attrs: [],
14
+ classNames: [],
15
+ notSelectors: []
16
+ };
17
+ }
18
+ function parseSelectorList(selector) {
19
+ const results = [];
20
+ let cssSelector = createSelector();
21
+ let current = cssSelector;
22
+ let match;
23
+ const regexp = new RegExp(SELECTOR_REGEXP.source, "g");
24
+ while (match = regexp.exec(selector)) {
25
+ if (match[NOT]) {
26
+ current = createSelector();
27
+ cssSelector.notSelectors.push(current);
28
+ }
29
+ const tag = match[TAG];
30
+ if (tag) {
31
+ const prefix = match[PREFIX];
32
+ if (prefix === "#") current.attrs.push(["id", tag.slice(1)]);
33
+ else if (prefix === ".") current.classNames.push(tag.slice(1).toLowerCase());
34
+ else current.element = tag;
35
+ }
36
+ const attribute = match[ATTRIBUTE];
37
+ if (attribute) current.attrs.push([attribute, match[ATTRIBUTE_VALUE] ?? ""]);
38
+ if (match[NOT_END]) current = cssSelector;
39
+ if (match[SEPARATOR]) {
40
+ results.push(cssSelector);
41
+ cssSelector = current = createSelector();
42
+ }
43
+ }
44
+ results.push(cssSelector);
45
+ return results;
46
+ }
47
+ /**
48
+ * Structural equality, matching how Angular compares an `ngProjectAs` selector
49
+ * against the selectors of an `ng-content`.
50
+ */
51
+ function isSameSelector(a, b) {
52
+ return normalizeElement(a.element) === normalizeElement(b.element) && a.attrs.length === b.attrs.length && a.attrs.every(([name, value], i) => {
53
+ const [otherName, otherValue] = b.attrs[i];
54
+ return name === otherName && value === otherValue;
55
+ }) && a.classNames.length === b.classNames.length && a.classNames.every((name, i) => name === b.classNames[i]) && a.notSelectors.length === b.notSelectors.length && a.notSelectors.every((not, i) => isSameSelector(not, b.notSelectors[i]));
56
+ }
57
+ function normalizeElement(element) {
58
+ return element === "*" ? "" : element;
59
+ }
60
+ //#endregion
61
+ export { isSameSelector, parseSelectorList };
62
+
63
+ //# sourceMappingURL=selector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selector.js","names":[],"sources":["../../src/selector.ts"],"sourcesContent":["/**\n * A CSS selector in the shape Angular's compiler uses for `ng-content` and\n * `ngProjectAs` matching. Ported from `CssSelector` in `@angular/compiler`.\n */\nexport interface ParsedSelector {\n element: string;\n attrs: [string, string][];\n classNames: string[];\n notSelectors: ParsedSelector[];\n}\n\nconst SELECTOR_REGEXP = new RegExp(\n '(\\\\:not\\\\()|' + // 1: \":not(\"\n '(([\\\\.\\\\#]?)[-\\\\w]+)|' + // 2: \"tag\"; 3: \".\"/\"#\"\n '(?:\\\\[([-.\\\\w*\\\\\\\\$]+)(?:=([\"\\']?)([^\\\\]\"\\']*)\\\\5)?\\\\])|' + // 4: attribute; 5: quote; 6: value\n '(\\\\))|' + // 7: \")\"\n '(\\\\s*,\\\\s*)', // 8: \",\"\n 'g',\n);\n\nconst NOT = 1;\nconst TAG = 2;\nconst PREFIX = 3;\nconst ATTRIBUTE = 4;\nconst ATTRIBUTE_VALUE = 6;\nconst NOT_END = 7;\nconst SEPARATOR = 8;\n\nfunction createSelector(): ParsedSelector {\n return { element: '', attrs: [], classNames: [], notSelectors: [] };\n}\n\nexport function parseSelectorList(selector: string): ParsedSelector[] {\n const results: ParsedSelector[] = [];\n let cssSelector = createSelector();\n let current = cssSelector;\n let inNot = false;\n let match: RegExpExecArray | null;\n\n const regexp = new RegExp(SELECTOR_REGEXP.source, 'g');\n\n while ((match = regexp.exec(selector))) {\n if (match[NOT]) {\n inNot = true;\n current = createSelector();\n cssSelector.notSelectors.push(current);\n }\n\n const tag = match[TAG];\n if (tag) {\n const prefix = match[PREFIX];\n if (prefix === '#') {\n current.attrs.push(['id', tag.slice(1)]);\n } else if (prefix === '.') {\n current.classNames.push(tag.slice(1).toLowerCase());\n } else {\n current.element = tag;\n }\n }\n\n const attribute = match[ATTRIBUTE];\n if (attribute) {\n current.attrs.push([attribute, match[ATTRIBUTE_VALUE] ?? '']);\n }\n\n if (match[NOT_END]) {\n inNot = false;\n current = cssSelector;\n }\n\n if (match[SEPARATOR]) {\n results.push(cssSelector);\n cssSelector = current = createSelector();\n }\n }\n\n results.push(cssSelector);\n\n return results;\n}\n\n/**\n * Structural equality, matching how Angular compares an `ngProjectAs` selector\n * against the selectors of an `ng-content`.\n */\nexport function isSameSelector(a: ParsedSelector, b: ParsedSelector): boolean {\n return (\n normalizeElement(a.element) === normalizeElement(b.element) &&\n a.attrs.length === b.attrs.length &&\n a.attrs.every(([name, value], i) => {\n const [otherName, otherValue] = b.attrs[i];\n return name === otherName && value === otherValue;\n }) &&\n a.classNames.length === b.classNames.length &&\n a.classNames.every((name, i) => name === b.classNames[i]) &&\n a.notSelectors.length === b.notSelectors.length &&\n a.notSelectors.every((not, i) => isSameSelector(not, b.notSelectors[i]))\n );\n}\n\nfunction normalizeElement(element: string): string {\n return element === '*' ? '' : element;\n}\n"],"mappings":";AAWA,IAAM,kCAAkB,IAAI,OAC1B,8GAKA,IACD;AAED,IAAM,MAAM;AACZ,IAAM,MAAM;AACZ,IAAM,SAAS;AACf,IAAM,YAAY;AAClB,IAAM,kBAAkB;AACxB,IAAM,UAAU;AAChB,IAAM,YAAY;AAElB,SAAS,iBAAiC;AACxC,QAAO;EAAE,SAAS;EAAI,OAAO,EAAE;EAAE,YAAY,EAAE;EAAE,cAAc,EAAE;EAAE;;AAGrE,SAAgB,kBAAkB,UAAoC;CACpE,MAAM,UAA4B,EAAE;CACpC,IAAI,cAAc,gBAAgB;CAClC,IAAI,UAAU;CAEd,IAAI;CAEJ,MAAM,SAAS,IAAI,OAAO,gBAAgB,QAAQ,IAAI;AAEtD,QAAQ,QAAQ,OAAO,KAAK,SAAS,EAAG;AACtC,MAAI,MAAM,MAAM;AAEd,aAAU,gBAAgB;AAC1B,eAAY,aAAa,KAAK,QAAQ;;EAGxC,MAAM,MAAM,MAAM;AAClB,MAAI,KAAK;GACP,MAAM,SAAS,MAAM;AACrB,OAAI,WAAW,IACb,SAAQ,MAAM,KAAK,CAAC,MAAM,IAAI,MAAM,EAAE,CAAC,CAAC;YAC/B,WAAW,IACpB,SAAQ,WAAW,KAAK,IAAI,MAAM,EAAE,CAAC,aAAa,CAAC;OAEnD,SAAQ,UAAU;;EAItB,MAAM,YAAY,MAAM;AACxB,MAAI,UACF,SAAQ,MAAM,KAAK,CAAC,WAAW,MAAM,oBAAoB,GAAG,CAAC;AAG/D,MAAI,MAAM,SAER,WAAU;AAGZ,MAAI,MAAM,YAAY;AACpB,WAAQ,KAAK,YAAY;AACzB,iBAAc,UAAU,gBAAgB;;;AAI5C,SAAQ,KAAK,YAAY;AAEzB,QAAO;;;;;;AAOT,SAAgB,eAAe,GAAmB,GAA4B;AAC5E,QACE,iBAAiB,EAAE,QAAQ,KAAK,iBAAiB,EAAE,QAAQ,IAC3D,EAAE,MAAM,WAAW,EAAE,MAAM,UAC3B,EAAE,MAAM,OAAO,CAAC,MAAM,QAAQ,MAAM;EAClC,MAAM,CAAC,WAAW,cAAc,EAAE,MAAM;AACxC,SAAO,SAAS,aAAa,UAAU;GACvC,IACF,EAAE,WAAW,WAAW,EAAE,WAAW,UACrC,EAAE,WAAW,OAAO,MAAM,MAAM,SAAS,EAAE,WAAW,GAAG,IACzD,EAAE,aAAa,WAAW,EAAE,aAAa,UACzC,EAAE,aAAa,OAAO,KAAK,MAAM,eAAe,KAAK,EAAE,aAAa,GAAG,CAAC;;AAI5E,SAAS,iBAAiB,SAAyB;AACjD,QAAO,YAAY,MAAM,KAAK"}
package/src/server-ngh.js CHANGED
@@ -1,9 +1,9 @@
1
+ import { buildProjectableNodes, markProjectableNodes } from "./projection.js";
1
2
  import { ID_PROP_NAME } from "./id.js";
2
- import { getComponentElementTag } from "./create-component.js";
3
+ import { createInputBindings, getComponentElementTag, registerRootComponent } from "./create-component.js";
3
4
  import { getContext, incrementId } from "./context.js";
4
- import { provideBootstrapListener } from "./server-providers.js";
5
- import { APP_ID, DOCUMENT, provideZonelessChangeDetection, reflectComponentType } from "@angular/core";
6
- import { bootstrapApplication, provideClientHydration } from "@angular/platform-browser";
5
+ import { APP_ID, DOCUMENT, createComponent, provideZonelessChangeDetection, reflectComponentType } from "@angular/core";
6
+ import { createApplication, provideClientHydration } from "@angular/platform-browser";
7
7
  import { platformServer, provideServerRendering, renderApplication, ɵSERVER_CONTEXT } from "@angular/platform-server";
8
8
  import { readFileSync } from "node:fs";
9
9
  import { createRequire } from "node:module";
@@ -18,7 +18,7 @@ function getHydrationScript() {
18
18
  async function check(Component) {
19
19
  return !!reflectComponentType(Component);
20
20
  }
21
- async function renderToStaticMarkup(Component, props, _children, metadata) {
21
+ async function renderToStaticMarkup(Component, props, children, metadata) {
22
22
  const mirror = reflectComponentType(Component);
23
23
  if (!mirror) throw new Error((metadata?.displayName || "<unknown component>") + " is not an Angular component");
24
24
  const elementTag = getComponentElementTag(mirror);
@@ -27,21 +27,32 @@ async function renderToStaticMarkup(Component, props, _children, metadata) {
27
27
  const platformRef = platformServer();
28
28
  const document = platformRef.injector.get(DOCUMENT);
29
29
  document.body.innerHTML = `${getHydrationScript()}<${elementTag} ${ID_PROP_NAME}="${ngAppId}"></${elementTag}>`;
30
- const bootstrap = (context) => bootstrapApplication(Component, { providers: [
31
- provideBootstrapListener(mirror, props),
32
- provideServerRendering(),
33
- {
34
- provide: ɵSERVER_CONTEXT,
35
- useValue: "analog"
36
- },
37
- provideZonelessChangeDetection(),
38
- metadata?.hydrate ? provideClientHydration(...Component.hydrationFeatures?.() || []) : [],
39
- {
40
- provide: APP_ID,
41
- useValue: ngAppId
42
- },
43
- ...Component.renderProviders || []
44
- ] }, context);
30
+ const hostElement = document.querySelector(elementTag);
31
+ let projectableNodes = buildProjectableNodes(mirror, children, document);
32
+ if (metadata?.hydrate && projectableNodes) projectableNodes = markProjectableNodes(projectableNodes, String(ngAppId), document);
33
+ const bootstrap = async (context) => {
34
+ const appRef = await createApplication({ providers: [
35
+ provideServerRendering(),
36
+ {
37
+ provide: ɵSERVER_CONTEXT,
38
+ useValue: "analog"
39
+ },
40
+ provideZonelessChangeDetection(),
41
+ metadata?.hydrate ? provideClientHydration(...Component.hydrationFeatures?.() || []) : [],
42
+ {
43
+ provide: APP_ID,
44
+ useValue: ngAppId
45
+ },
46
+ ...Component.renderProviders || []
47
+ ] }, context);
48
+ registerRootComponent(appRef, createComponent(Component, {
49
+ environmentInjector: appRef.injector,
50
+ hostElement,
51
+ projectableNodes,
52
+ bindings: createInputBindings(mirror, props)
53
+ }));
54
+ return appRef;
55
+ };
45
56
  const html = await renderApplication(bootstrap, { document });
46
57
  document.documentElement.innerHTML = html;
47
58
  let styleTags = "";
@@ -1 +1 @@
1
- {"version":3,"file":"server-ngh.js","names":[],"sources":["../../src/server-ngh.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { createRequire } from 'node:module';\n\nimport type { EnvironmentProviders, Provider, Type } from '@angular/core';\nimport {\n reflectComponentType,\n provideZonelessChangeDetection,\n DOCUMENT,\n APP_ID,\n} from '@angular/core';\nimport {\n provideServerRendering,\n renderApplication,\n ɵSERVER_CONTEXT,\n platformServer,\n} from '@angular/platform-server';\nimport {\n bootstrapApplication,\n type HydrationFeature,\n type HydrationFeatureKind,\n provideClientHydration,\n type BootstrapContext,\n} from '@angular/platform-browser';\nimport type { AstroComponentMetadata, SSRLoadedRendererValue } from 'astro';\nimport { getContext, incrementId, type RendererContext } from './context.ts';\nimport { provideBootstrapListener } from './server-providers.ts';\nimport { ID_PROP_NAME } from './id.ts';\nimport { getComponentElementTag } from './create-component.ts';\n\nconst require = createRequire(import.meta.url);\nlet jsActionContractScript: string | undefined = undefined;\n\nconst HYDRATION_SCRIPT_ID = 'ng-event-dispatch-contract';\n\nfunction getHydrationScript(): string {\n jsActionContractScript ??=\n `<script type=\"text/javascript\" id=\"${HYDRATION_SCRIPT_ID}\">` +\n readFileSync(\n require.resolve('@angular/core/event-dispatch-contract.min.js'),\n 'utf-8',\n ) +\n '</script>';\n return jsActionContractScript;\n}\n\nasync function check(Component: Type<unknown>) {\n return !!reflectComponentType(Component);\n}\n\nasync function renderToStaticMarkup(\n this: RendererContext,\n Component: Type<unknown> & {\n renderProviders?: (Provider | EnvironmentProviders)[];\n hydrationFeatures?: () => HydrationFeature<HydrationFeatureKind>[];\n },\n props: Record<string, unknown>,\n _children: unknown,\n metadata?: AstroComponentMetadata,\n) {\n const mirror = reflectComponentType(Component);\n\n if (!mirror) {\n // This should be unreachable: the `check` function verifies that Component is an Angular component.\n throw new Error(\n (metadata?.displayName || '<unknown component>') +\n ' is not an Angular component',\n );\n }\n\n const elementTag = getComponentElementTag(mirror);\n const ngAppId = props?.[ID_PROP_NAME] || incrementId(getContext(this.result));\n\n // When the platform ref is destroyed, it will reset ngServerMode back to `undefined` if it was not defined when it is\n // created. See https://github.com/angular/angular/blob/2ce0e98f79a02ddc550d00580e8e232cfed3bfb2/packages/platform-server/src/server.ts#L138\n globalThis.ngServerMode = true;\n\n const platformRef = platformServer();\n const document = platformRef.injector.get(DOCUMENT);\n\n // Incremental hydration requires the event dispatch script to be present.\n document.body.innerHTML = `${getHydrationScript()}<${elementTag} ${ID_PROP_NAME}=\"${ngAppId}\"></${elementTag}>`;\n\n const bootstrap = (context?: BootstrapContext) =>\n bootstrapApplication(\n Component,\n {\n providers: [\n provideBootstrapListener(mirror, props),\n provideServerRendering(),\n { provide: ɵSERVER_CONTEXT, useValue: 'analog' },\n provideZonelessChangeDetection(),\n metadata?.hydrate\n ? provideClientHydration(...(Component.hydrationFeatures?.() || []))\n : [],\n {\n provide: APP_ID,\n useValue: ngAppId,\n },\n ...(Component.renderProviders || []),\n ],\n },\n context,\n );\n\n const html = await renderApplication(bootstrap, {\n document,\n });\n\n document.documentElement.innerHTML = html;\n let styleTags = '';\n\n document.head.childNodes.forEach((node) => {\n if (node.nodeName === 'STYLE') {\n styleTags += (node as HTMLElement).outerHTML;\n }\n });\n\n // Remove the hydration script, so only one is present on the page.\n document.getElementById(HYDRATION_SCRIPT_ID)?.remove();\n\n const correctedHtml = styleTags + document.body.innerHTML;\n\n platformRef.destroy();\n\n return { html: correctedHtml };\n}\n\nexport default {\n check,\n renderToStaticMarkup,\n renderHydrationScript: () => getHydrationScript(),\n} satisfies SSRLoadedRendererValue;\n"],"mappings":";;;;;;;;;;AA6BA,IAAM,UAAU,cAAc,OAAO,KAAK,IAAI;AAC9C,IAAI,yBAA6C,KAAA;AAEjD,IAAM,sBAAsB;AAE5B,SAAS,qBAA6B;AACpC,4BACE,sCAAsC,oBAAoB,MAC1D,aACE,QAAQ,QAAQ,+CAA+C,EAC/D,QACD,GACD;AACF,QAAO;;AAGT,eAAe,MAAM,WAA0B;AAC7C,QAAO,CAAC,CAAC,qBAAqB,UAAU;;AAG1C,eAAe,qBAEb,WAIA,OACA,WACA,UACA;CACA,MAAM,SAAS,qBAAqB,UAAU;AAE9C,KAAI,CAAC,OAEH,OAAM,IAAI,OACP,UAAU,eAAe,yBACxB,+BACH;CAGH,MAAM,aAAa,uBAAuB,OAAO;CACjD,MAAM,UAAU,QAAA,qBAAyB,YAAY,WAAW,KAAK,OAAO,CAAC;AAI7E,YAAW,eAAe;CAE1B,MAAM,cAAc,gBAAgB;CACpC,MAAM,WAAW,YAAY,SAAS,IAAI,SAAS;AAGnD,UAAS,KAAK,YAAY,GAAG,oBAAoB,CAAC,GAAG,WAAW,GAAG,aAAa,IAAI,QAAQ,MAAM,WAAW;CAE7G,MAAM,aAAa,YACjB,qBACE,WACA,EACE,WAAW;EACT,yBAAyB,QAAQ,MAAM;EACvC,wBAAwB;EACxB;GAAE,SAAS;GAAiB,UAAU;GAAU;EAChD,gCAAgC;EAChC,UAAU,UACN,uBAAuB,GAAI,UAAU,qBAAqB,IAAI,EAAE,CAAE,GAClE,EAAE;EACN;GACE,SAAS;GACT,UAAU;GACX;EACD,GAAI,UAAU,mBAAmB,EAAE;EACpC,EACF,EACD,QACD;CAEH,MAAM,OAAO,MAAM,kBAAkB,WAAW,EAC9C,UACD,CAAC;AAEF,UAAS,gBAAgB,YAAY;CACrC,IAAI,YAAY;AAEhB,UAAS,KAAK,WAAW,SAAS,SAAS;AACzC,MAAI,KAAK,aAAa,QACpB,cAAc,KAAqB;GAErC;AAGF,UAAS,eAAe,oBAAoB,EAAE,QAAQ;CAEtD,MAAM,gBAAgB,YAAY,SAAS,KAAK;AAEhD,aAAY,SAAS;AAErB,QAAO,EAAE,MAAM,eAAe;;AAGhC,IAAA,qBAAe;CACb;CACA;CACA,6BAA6B,oBAAoB;CAClD"}
1
+ {"version":3,"file":"server-ngh.js","names":[],"sources":["../../src/server-ngh.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { createRequire } from 'node:module';\n\nimport type { EnvironmentProviders, Provider, Type } from '@angular/core';\nimport {\n createComponent,\n reflectComponentType,\n provideZonelessChangeDetection,\n DOCUMENT,\n APP_ID,\n} from '@angular/core';\nimport {\n provideServerRendering,\n renderApplication,\n ɵSERVER_CONTEXT,\n platformServer,\n} from '@angular/platform-server';\nimport {\n createApplication,\n type HydrationFeature,\n type HydrationFeatureKind,\n provideClientHydration,\n type BootstrapContext,\n} from '@angular/platform-browser';\nimport type { AstroComponentMetadata, SSRLoadedRendererValue } from 'astro';\nimport { getContext, incrementId, type RendererContext } from './context.ts';\nimport { ID_PROP_NAME } from './id.ts';\nimport {\n createInputBindings,\n getComponentElementTag,\n registerRootComponent,\n} from './create-component.ts';\nimport { buildProjectableNodes, markProjectableNodes } from './projection.ts';\n\nconst require = createRequire(import.meta.url);\nlet jsActionContractScript: string | undefined = undefined;\n\nconst HYDRATION_SCRIPT_ID = 'ng-event-dispatch-contract';\n\nfunction getHydrationScript(): string {\n jsActionContractScript ??=\n `<script type=\"text/javascript\" id=\"${HYDRATION_SCRIPT_ID}\">` +\n readFileSync(\n require.resolve('@angular/core/event-dispatch-contract.min.js'),\n 'utf-8',\n ) +\n '</script>';\n return jsActionContractScript;\n}\n\nasync function check(Component: Type<unknown>) {\n return !!reflectComponentType(Component);\n}\n\nasync function renderToStaticMarkup(\n this: RendererContext,\n Component: Type<unknown> & {\n renderProviders?: (Provider | EnvironmentProviders)[];\n hydrationFeatures?: () => HydrationFeature<HydrationFeatureKind>[];\n },\n props: Record<string, unknown>,\n children: unknown,\n metadata?: AstroComponentMetadata,\n) {\n const mirror = reflectComponentType(Component);\n\n if (!mirror) {\n // This should be unreachable: the `check` function verifies that Component is an Angular component.\n throw new Error(\n (metadata?.displayName || '<unknown component>') +\n ' is not an Angular component',\n );\n }\n\n const elementTag = getComponentElementTag(mirror);\n const ngAppId = props?.[ID_PROP_NAME] || incrementId(getContext(this.result));\n\n // When the platform ref is destroyed, it will reset ngServerMode back to `undefined` if it was not defined when it is\n // created. See https://github.com/angular/angular/blob/2ce0e98f79a02ddc550d00580e8e232cfed3bfb2/packages/platform-server/src/server.ts#L138\n globalThis.ngServerMode = true;\n\n const platformRef = platformServer();\n const document = platformRef.injector.get(DOCUMENT);\n\n // Incremental hydration requires the event dispatch script to be present.\n document.body.innerHTML = `${getHydrationScript()}<${elementTag} ${ID_PROP_NAME}=\"${ngAppId}\"></${elementTag}>`;\n\n const hostElement = document.querySelector(elementTag) as Element;\n let projectableNodes = buildProjectableNodes(mirror, children, document);\n\n if (metadata?.hydrate && projectableNodes) {\n projectableNodes = markProjectableNodes(\n projectableNodes,\n String(ngAppId),\n document,\n );\n }\n\n const bootstrap = async (context?: BootstrapContext) => {\n const appRef = await createApplication(\n {\n providers: [\n provideServerRendering(),\n { provide: ɵSERVER_CONTEXT, useValue: 'analog' },\n provideZonelessChangeDetection(),\n metadata?.hydrate\n ? provideClientHydration(...(Component.hydrationFeatures?.() || []))\n : [],\n {\n provide: APP_ID,\n useValue: ngAppId,\n },\n ...(Component.renderProviders || []),\n ],\n },\n context,\n );\n\n const componentRef = createComponent(Component, {\n environmentInjector: appRef.injector,\n hostElement,\n projectableNodes,\n bindings: createInputBindings(mirror, props),\n });\n\n registerRootComponent(appRef, componentRef);\n\n return appRef;\n };\n\n const html = await renderApplication(bootstrap, {\n document,\n });\n\n document.documentElement.innerHTML = html;\n let styleTags = '';\n\n document.head.childNodes.forEach((node) => {\n if (node.nodeName === 'STYLE') {\n styleTags += (node as HTMLElement).outerHTML;\n }\n });\n\n // Remove the hydration script, so only one is present on the page.\n document.getElementById(HYDRATION_SCRIPT_ID)?.remove();\n\n const correctedHtml = styleTags + document.body.innerHTML;\n\n platformRef.destroy();\n\n return { html: correctedHtml };\n}\n\nexport default {\n check,\n renderToStaticMarkup,\n renderHydrationScript: () => getHydrationScript(),\n} satisfies SSRLoadedRendererValue;\n"],"mappings":";;;;;;;;;;AAkCA,IAAM,UAAU,cAAc,OAAO,KAAK,IAAI;AAC9C,IAAI,yBAA6C,KAAA;AAEjD,IAAM,sBAAsB;AAE5B,SAAS,qBAA6B;AACpC,4BACE,sCAAsC,oBAAoB,MAC1D,aACE,QAAQ,QAAQ,+CAA+C,EAC/D,QACD,GACD;AACF,QAAO;;AAGT,eAAe,MAAM,WAA0B;AAC7C,QAAO,CAAC,CAAC,qBAAqB,UAAU;;AAG1C,eAAe,qBAEb,WAIA,OACA,UACA,UACA;CACA,MAAM,SAAS,qBAAqB,UAAU;AAE9C,KAAI,CAAC,OAEH,OAAM,IAAI,OACP,UAAU,eAAe,yBACxB,+BACH;CAGH,MAAM,aAAa,uBAAuB,OAAO;CACjD,MAAM,UAAU,QAAA,qBAAyB,YAAY,WAAW,KAAK,OAAO,CAAC;AAI7E,YAAW,eAAe;CAE1B,MAAM,cAAc,gBAAgB;CACpC,MAAM,WAAW,YAAY,SAAS,IAAI,SAAS;AAGnD,UAAS,KAAK,YAAY,GAAG,oBAAoB,CAAC,GAAG,WAAW,GAAG,aAAa,IAAI,QAAQ,MAAM,WAAW;CAE7G,MAAM,cAAc,SAAS,cAAc,WAAW;CACtD,IAAI,mBAAmB,sBAAsB,QAAQ,UAAU,SAAS;AAExE,KAAI,UAAU,WAAW,iBACvB,oBAAmB,qBACjB,kBACA,OAAO,QAAQ,EACf,SACD;CAGH,MAAM,YAAY,OAAO,YAA+B;EACtD,MAAM,SAAS,MAAM,kBACnB,EACE,WAAW;GACT,wBAAwB;GACxB;IAAE,SAAS;IAAiB,UAAU;IAAU;GAChD,gCAAgC;GAChC,UAAU,UACN,uBAAuB,GAAI,UAAU,qBAAqB,IAAI,EAAE,CAAE,GAClE,EAAE;GACN;IACE,SAAS;IACT,UAAU;IACX;GACD,GAAI,UAAU,mBAAmB,EAAE;GACpC,EACF,EACD,QACD;AASD,wBAAsB,QAPD,gBAAgB,WAAW;GAC9C,qBAAqB,OAAO;GAC5B;GACA;GACA,UAAU,oBAAoB,QAAQ,MAAM;GAC7C,CAAC,CAEyC;AAE3C,SAAO;;CAGT,MAAM,OAAO,MAAM,kBAAkB,WAAW,EAC9C,UACD,CAAC;AAEF,UAAS,gBAAgB,YAAY;CACrC,IAAI,YAAY;AAEhB,UAAS,KAAK,WAAW,SAAS,SAAS;AACzC,MAAI,KAAK,aAAa,QACpB,cAAc,KAAqB;GAErC;AAGF,UAAS,eAAe,oBAAoB,EAAE,QAAQ;CAEtD,MAAM,gBAAgB,YAAY,SAAS,KAAK;AAEhD,aAAY,SAAS;AAErB,QAAO,EAAE,MAAM,eAAe;;AAGhC,IAAA,qBAAe;CACb;CACA;CACA,6BAA6B,oBAAoB;CAClD"}
package/src/server.d.ts CHANGED
@@ -2,7 +2,7 @@ import type { EnvironmentProviders, Provider, ɵComponentType as ComponentType }
2
2
  declare function check(Component: ComponentType<unknown>, _props: Record<string, unknown>, _children: unknown): boolean;
3
3
  declare function renderToStaticMarkup(Component: ComponentType<unknown> & {
4
4
  renderProviders: (Provider | EnvironmentProviders)[];
5
- }, props: Record<string, unknown>, _children: unknown): Promise<{
5
+ }, props: Record<string, unknown>, children: unknown): Promise<{
6
6
  html: string;
7
7
  }>;
8
8
  declare const renderer: {
package/src/server.js CHANGED
@@ -1,53 +1,39 @@
1
- import { ApplicationRef, DOCUMENT, InjectionToken, provideZonelessChangeDetection, reflectComponentType } from "@angular/core";
2
- import { bootstrapApplication } from "@angular/platform-browser";
3
- import { BEFORE_APP_SERIALIZED, platformServer, provideServerRendering, renderApplication, ɵSERVER_CONTEXT } from "@angular/platform-server";
1
+ import { buildProjectableNodes } from "./projection.js";
2
+ import { createInputBindings, getComponentElementTag, registerRootComponent } from "./create-component.js";
3
+ import { DOCUMENT, createComponent, provideZonelessChangeDetection, reflectComponentType } from "@angular/core";
4
+ import { createApplication } from "@angular/platform-browser";
5
+ import { platformServer, provideServerRendering, renderApplication, ɵSERVER_CONTEXT } from "@angular/platform-server";
4
6
  //#region packages/astro-angular/src/server.ts
5
- var ANALOG_ASTRO_STATIC_PROPS = new InjectionToken("@analogjs/astro-angular: Static Props w/ Mirror Provider", { factory() {
6
- return {
7
- props: {},
8
- mirror: {}
9
- };
10
- } });
11
7
  function check(Component, _props, _children) {
12
8
  return !!reflectComponentType(Component);
13
9
  }
14
- var STATIC_PROPS_HOOK_PROVIDER = {
15
- provide: BEFORE_APP_SERIALIZED,
16
- useFactory: (appRef, { props, mirror }) => {
17
- return () => {
18
- const compRef = appRef.components[0];
19
- if (compRef && props && mirror) {
20
- for (const [key, value] of Object.entries(props)) if (mirror.inputs.some(({ templateName, propName }) => templateName === key || propName === key)) compRef.setInput(key, value);
21
- compRef.changeDetectorRef.detectChanges();
22
- }
23
- };
24
- },
25
- deps: [ApplicationRef, ANALOG_ASTRO_STATIC_PROPS],
26
- multi: true
27
- };
28
- async function renderToStaticMarkup(Component, props, _children) {
10
+ async function renderToStaticMarkup(Component, props, children) {
29
11
  const mirror = reflectComponentType(Component);
30
- const appId = mirror?.selector.split(",")[0] || Component.name.toString().toLowerCase();
12
+ if (!mirror) throw new Error(Component.name + " is not an Angular component");
13
+ const elementTag = getComponentElementTag(mirror);
31
14
  const platformRef = platformServer();
32
15
  const document = platformRef.injector.get(DOCUMENT);
33
- document.body.innerHTML = `<${appId}></${appId}>`;
34
- const bootstrap = (context) => bootstrapApplication(Component, { providers: [
35
- {
36
- provide: ANALOG_ASTRO_STATIC_PROPS,
37
- useValue: {
38
- props,
39
- mirror
40
- }
41
- },
42
- STATIC_PROPS_HOOK_PROVIDER,
43
- provideServerRendering(),
44
- {
45
- provide: ɵSERVER_CONTEXT,
46
- useValue: "analog"
47
- },
48
- provideZonelessChangeDetection(),
49
- ...Component.renderProviders || []
50
- ] }, context);
16
+ document.body.innerHTML = `<${elementTag}></${elementTag}>`;
17
+ const hostElement = document.querySelector(elementTag);
18
+ const projectableNodes = buildProjectableNodes(mirror, children, document);
19
+ const bootstrap = async (context) => {
20
+ const appRef = await createApplication({ providers: [
21
+ provideServerRendering(),
22
+ {
23
+ provide: ɵSERVER_CONTEXT,
24
+ useValue: "analog"
25
+ },
26
+ provideZonelessChangeDetection(),
27
+ ...Component.renderProviders || []
28
+ ] }, context);
29
+ registerRootComponent(appRef, createComponent(Component, {
30
+ environmentInjector: appRef.injector,
31
+ hostElement,
32
+ projectableNodes,
33
+ bindings: createInputBindings(mirror, props)
34
+ }));
35
+ return appRef;
36
+ };
51
37
  const html = await renderApplication(bootstrap, { document });
52
38
  document.documentElement.innerHTML = html;
53
39
  let styleTags = "";
package/src/server.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","names":[],"sources":["../../src/server.ts"],"sourcesContent":["import type {\n ComponentMirror,\n EnvironmentProviders,\n Provider,\n ɵComponentType as ComponentType,\n} from '@angular/core';\nimport {\n ApplicationRef,\n InjectionToken,\n reflectComponentType,\n provideZonelessChangeDetection,\n DOCUMENT,\n} from '@angular/core';\nimport {\n BEFORE_APP_SERIALIZED,\n provideServerRendering,\n renderApplication,\n ɵSERVER_CONTEXT,\n platformServer,\n} from '@angular/platform-server';\nimport {\n bootstrapApplication,\n type BootstrapContext,\n} from '@angular/platform-browser';\n\nconst ANALOG_ASTRO_STATIC_PROPS = new InjectionToken<{\n props: Record<string, unknown>;\n mirror: ComponentMirror<unknown>;\n}>('@analogjs/astro-angular: Static Props w/ Mirror Provider', {\n factory() {\n return { props: {}, mirror: {} as ComponentMirror<unknown> };\n },\n});\n\nfunction check(\n Component: ComponentType<unknown>,\n _props: Record<string, unknown>,\n _children: unknown,\n): boolean {\n return !!reflectComponentType(Component);\n}\n\n// Run beforeAppInitialized hook to set Input on the ComponentRef\n// before the platform renders to string\nconst STATIC_PROPS_HOOK_PROVIDER: Provider = {\n provide: BEFORE_APP_SERIALIZED,\n useFactory: (\n appRef: ApplicationRef,\n {\n props,\n mirror,\n }: {\n props: Record<string, unknown>;\n mirror: ComponentMirror<unknown>;\n },\n ) => {\n return () => {\n const compRef = appRef.components[0];\n if (compRef && props && mirror) {\n for (const [key, value] of Object.entries(props)) {\n if (\n // we double-check inputs on ComponentMirror\n // because Astro might add additional props\n // that aren't actually Input defined on the Component\n mirror.inputs.some(\n ({ templateName, propName }) =>\n templateName === key || propName === key,\n )\n ) {\n compRef.setInput(key, value);\n }\n }\n compRef.changeDetectorRef.detectChanges();\n }\n };\n },\n deps: [ApplicationRef, ANALOG_ASTRO_STATIC_PROPS],\n multi: true,\n};\n\nasync function renderToStaticMarkup(\n Component: ComponentType<unknown> & {\n renderProviders: (Provider | EnvironmentProviders)[];\n },\n props: Record<string, unknown>,\n _children: unknown,\n): Promise<{ html: string }> {\n const mirror = reflectComponentType(Component);\n const appId =\n mirror?.selector.split(',')[0] || Component.name.toString().toLowerCase();\n\n const platformRef = platformServer();\n const document = platformRef.injector.get(DOCUMENT);\n document.body.innerHTML = `<${appId}></${appId}>`;\n\n const bootstrap = (context?: BootstrapContext) =>\n bootstrapApplication(\n Component,\n {\n providers: [\n {\n provide: ANALOG_ASTRO_STATIC_PROPS,\n useValue: { props, mirror },\n },\n STATIC_PROPS_HOOK_PROVIDER,\n provideServerRendering(),\n { provide: ɵSERVER_CONTEXT, useValue: 'analog' },\n provideZonelessChangeDetection(),\n ...(Component.renderProviders || []),\n ],\n },\n context,\n );\n\n const html = await renderApplication(bootstrap, {\n document,\n });\n\n document.documentElement.innerHTML = html;\n let styleTags = '';\n\n document.head.childNodes.forEach((node) => {\n if (node.nodeName === 'STYLE') {\n styleTags += (node as HTMLElement).outerHTML;\n }\n });\n\n const correctedHtml = styleTags + document.body.innerHTML;\n\n platformRef.destroy();\n\n return { html: correctedHtml };\n}\n\nconst renderer: {\n check: typeof check;\n renderToStaticMarkup: typeof renderToStaticMarkup;\n} = {\n check: check,\n renderToStaticMarkup: renderToStaticMarkup,\n};\nexport default renderer;\n"],"mappings":";;;;AAyBA,IAAM,4BAA4B,IAAI,eAGnC,4DAA4D,EAC7D,UAAU;AACR,QAAO;EAAE,OAAO,EAAE;EAAE,QAAQ,EAAE;EAA8B;GAE/D,CAAC;AAEF,SAAS,MACP,WACA,QACA,WACS;AACT,QAAO,CAAC,CAAC,qBAAqB,UAAU;;AAK1C,IAAM,6BAAuC;CAC3C,SAAS;CACT,aACE,QACA,EACE,OACA,aAKC;AACH,eAAa;GACX,MAAM,UAAU,OAAO,WAAW;AAClC,OAAI,WAAW,SAAS,QAAQ;AAC9B,SAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,CAC9C,KAIE,OAAO,OAAO,MACX,EAAE,cAAc,eACf,iBAAiB,OAAO,aAAa,IACxC,CAED,SAAQ,SAAS,KAAK,MAAM;AAGhC,YAAQ,kBAAkB,eAAe;;;;CAI/C,MAAM,CAAC,gBAAgB,0BAA0B;CACjD,OAAO;CACR;AAED,eAAe,qBACb,WAGA,OACA,WAC2B;CAC3B,MAAM,SAAS,qBAAqB,UAAU;CAC9C,MAAM,QACJ,QAAQ,SAAS,MAAM,IAAI,CAAC,MAAM,UAAU,KAAK,UAAU,CAAC,aAAa;CAE3E,MAAM,cAAc,gBAAgB;CACpC,MAAM,WAAW,YAAY,SAAS,IAAI,SAAS;AACnD,UAAS,KAAK,YAAY,IAAI,MAAM,KAAK,MAAM;CAE/C,MAAM,aAAa,YACjB,qBACE,WACA,EACE,WAAW;EACT;GACE,SAAS;GACT,UAAU;IAAE;IAAO;IAAQ;GAC5B;EACD;EACA,wBAAwB;EACxB;GAAE,SAAS;GAAiB,UAAU;GAAU;EAChD,gCAAgC;EAChC,GAAI,UAAU,mBAAmB,EAAE;EACpC,EACF,EACD,QACD;CAEH,MAAM,OAAO,MAAM,kBAAkB,WAAW,EAC9C,UACD,CAAC;AAEF,UAAS,gBAAgB,YAAY;CACrC,IAAI,YAAY;AAEhB,UAAS,KAAK,WAAW,SAAS,SAAS;AACzC,MAAI,KAAK,aAAa,QACpB,cAAc,KAAqB;GAErC;CAEF,MAAM,gBAAgB,YAAY,SAAS,KAAK;AAEhD,aAAY,SAAS;AAErB,QAAO,EAAE,MAAM,eAAe;;AAGhC,IAAM,WAGF;CACK;CACe;CACvB"}
1
+ {"version":3,"file":"server.js","names":[],"sources":["../../src/server.ts"],"sourcesContent":["import type {\n EnvironmentProviders,\n Provider,\n ɵComponentType as ComponentType,\n} from '@angular/core';\nimport {\n createComponent,\n reflectComponentType,\n provideZonelessChangeDetection,\n DOCUMENT,\n} from '@angular/core';\nimport {\n provideServerRendering,\n renderApplication,\n ɵSERVER_CONTEXT,\n platformServer,\n} from '@angular/platform-server';\nimport {\n createApplication,\n type BootstrapContext,\n} from '@angular/platform-browser';\nimport {\n createInputBindings,\n getComponentElementTag,\n registerRootComponent,\n} from './create-component.ts';\nimport { buildProjectableNodes } from './projection.ts';\n\nfunction check(\n Component: ComponentType<unknown>,\n _props: Record<string, unknown>,\n _children: unknown,\n): boolean {\n return !!reflectComponentType(Component);\n}\n\nasync function renderToStaticMarkup(\n Component: ComponentType<unknown> & {\n renderProviders: (Provider | EnvironmentProviders)[];\n },\n props: Record<string, unknown>,\n children: unknown,\n): Promise<{ html: string }> {\n const mirror = reflectComponentType(Component);\n\n if (!mirror) {\n // This should be unreachable: the `check` function verifies that Component is an Angular component.\n throw new Error(Component.name + ' is not an Angular component');\n }\n\n const elementTag = getComponentElementTag(mirror);\n\n const platformRef = platformServer();\n const document = platformRef.injector.get(DOCUMENT);\n document.body.innerHTML = `<${elementTag}></${elementTag}>`;\n\n const hostElement = document.querySelector(elementTag) as Element;\n const projectableNodes = buildProjectableNodes(mirror, children, document);\n\n const bootstrap = async (context?: BootstrapContext) => {\n const appRef = await createApplication(\n {\n providers: [\n provideServerRendering(),\n { provide: ɵSERVER_CONTEXT, useValue: 'analog' },\n provideZonelessChangeDetection(),\n ...(Component.renderProviders || []),\n ],\n },\n context,\n );\n\n const componentRef = createComponent(Component, {\n environmentInjector: appRef.injector,\n hostElement,\n projectableNodes,\n bindings: createInputBindings(mirror, props),\n });\n\n registerRootComponent(appRef, componentRef);\n\n return appRef;\n };\n\n const html = await renderApplication(bootstrap, {\n document,\n });\n\n document.documentElement.innerHTML = html;\n let styleTags = '';\n\n document.head.childNodes.forEach((node) => {\n if (node.nodeName === 'STYLE') {\n styleTags += (node as HTMLElement).outerHTML;\n }\n });\n\n const correctedHtml = styleTags + document.body.innerHTML;\n\n platformRef.destroy();\n\n return { html: correctedHtml };\n}\n\nconst renderer: {\n check: typeof check;\n renderToStaticMarkup: typeof renderToStaticMarkup;\n} = {\n check: check,\n renderToStaticMarkup: renderToStaticMarkup,\n};\nexport default renderer;\n"],"mappings":";;;;;;AA4BA,SAAS,MACP,WACA,QACA,WACS;AACT,QAAO,CAAC,CAAC,qBAAqB,UAAU;;AAG1C,eAAe,qBACb,WAGA,OACA,UAC2B;CAC3B,MAAM,SAAS,qBAAqB,UAAU;AAE9C,KAAI,CAAC,OAEH,OAAM,IAAI,MAAM,UAAU,OAAO,+BAA+B;CAGlE,MAAM,aAAa,uBAAuB,OAAO;CAEjD,MAAM,cAAc,gBAAgB;CACpC,MAAM,WAAW,YAAY,SAAS,IAAI,SAAS;AACnD,UAAS,KAAK,YAAY,IAAI,WAAW,KAAK,WAAW;CAEzD,MAAM,cAAc,SAAS,cAAc,WAAW;CACtD,MAAM,mBAAmB,sBAAsB,QAAQ,UAAU,SAAS;CAE1E,MAAM,YAAY,OAAO,YAA+B;EACtD,MAAM,SAAS,MAAM,kBACnB,EACE,WAAW;GACT,wBAAwB;GACxB;IAAE,SAAS;IAAiB,UAAU;IAAU;GAChD,gCAAgC;GAChC,GAAI,UAAU,mBAAmB,EAAE;GACpC,EACF,EACD,QACD;AASD,wBAAsB,QAPD,gBAAgB,WAAW;GAC9C,qBAAqB,OAAO;GAC5B;GACA;GACA,UAAU,oBAAoB,QAAQ,MAAM;GAC7C,CAAC,CAEyC;AAE3C,SAAO;;CAGT,MAAM,OAAO,MAAM,kBAAkB,WAAW,EAC9C,UACD,CAAC;AAEF,UAAS,gBAAgB,YAAY;CACrC,IAAI,YAAY;AAEhB,UAAS,KAAK,WAAW,SAAS,SAAS;AACzC,MAAI,KAAK,aAAa,QACpB,cAAc,KAAqB;GAErC;CAEF,MAAM,gBAAgB,YAAY,SAAS,KAAK;AAEhD,aAAY,SAAS;AAErB,QAAO,EAAE,MAAM,eAAe;;AAGhC,IAAM,WAGF;CACK;CACe;CACvB"}
@@ -1,8 +0,0 @@
1
- import { type ComponentMirror, type Provider } from "@angular/core";
2
- /**
3
- * Provide bootstrap listeners to ensure the rendered state matches the settled component state after applying component inputs.
4
- * @param mirror The component mirror, to detect component inputs
5
- * @param props Properties applied to the component in the Astro file
6
- * @returns A providers array for the server renderer
7
- */
8
- export declare function provideBootstrapListener(mirror: ComponentMirror<unknown>, props: Record<string, unknown>): Provider[];
@@ -1,23 +0,0 @@
1
- import { APP_BOOTSTRAP_LISTENER } from "@angular/core";
2
- //#region packages/astro-angular/src/server-providers.ts
3
- /**
4
- * Provide bootstrap listeners to ensure the rendered state matches the settled component state after applying component inputs.
5
- * @param mirror The component mirror, to detect component inputs
6
- * @param props Properties applied to the component in the Astro file
7
- * @returns A providers array for the server renderer
8
- */
9
- function provideBootstrapListener(mirror, props) {
10
- return [{
11
- provide: APP_BOOTSTRAP_LISTENER,
12
- useValue: (compRef) => {
13
- if (props) {
14
- for (const [key, value] of Object.entries(props)) if (mirror.inputs.some(({ templateName }) => templateName === key)) compRef.setInput(key, value);
15
- }
16
- },
17
- multi: true
18
- }];
19
- }
20
- //#endregion
21
- export { provideBootstrapListener };
22
-
23
- //# sourceMappingURL=server-providers.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"server-providers.js","names":[],"sources":["../../src/server-providers.ts"],"sourcesContent":["import {\n APP_BOOTSTRAP_LISTENER,\n type ComponentMirror,\n type ComponentRef,\n type Provider,\n} from '@angular/core';\n\n/**\n * Provide bootstrap listeners to ensure the rendered state matches the settled component state after applying component inputs.\n * @param mirror The component mirror, to detect component inputs\n * @param props Properties applied to the component in the Astro file\n * @returns A providers array for the server renderer\n */\nexport function provideBootstrapListener(\n mirror: ComponentMirror<unknown>,\n props: Record<string, unknown>,\n): Provider[] {\n return [\n {\n provide: APP_BOOTSTRAP_LISTENER,\n useValue: (compRef: ComponentRef<unknown>) => {\n if (props) {\n for (const [key, value] of Object.entries(props)) {\n if (\n mirror.inputs.some(({ templateName }) => templateName === key)\n ) {\n compRef.setInput(key, value);\n }\n }\n }\n },\n multi: true,\n },\n ];\n}\n"],"mappings":";;;;;;;;AAaA,SAAgB,yBACd,QACA,OACY;AACZ,QAAO,CACL;EACE,SAAS;EACT,WAAW,YAAmC;AAC5C,OAAI;SACG,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,CAC9C,KACE,OAAO,OAAO,MAAM,EAAE,mBAAmB,iBAAiB,IAAI,CAE9D,SAAQ,SAAS,KAAK,MAAM;;;EAKpC,OAAO;EACR,CACF"}