mount-observer 0.1.20 → 0.1.22

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/Events.js CHANGED
@@ -76,3 +76,12 @@ export class ResolvedEvent extends Event {
76
76
  this.export = exportValue;
77
77
  }
78
78
  }
79
+ export const addedScriptElementEventName = 'addedscriptelement';
80
+ export class AddedScriptElementEvent extends Event {
81
+ scriptElement;
82
+ static eventName = addedScriptElementEventName;
83
+ constructor(scriptElement) {
84
+ super(AddedScriptElementEvent.eventName, { bubbles: false, composed: false });
85
+ this.scriptElement = scriptElement;
86
+ }
87
+ }
package/Events.ts CHANGED
@@ -73,3 +73,13 @@ export class ResolvedEvent extends Event {
73
73
  this.export = exportValue;
74
74
  }
75
75
  }
76
+
77
+ export const addedScriptElementEventName = 'addedscriptelement';
78
+
79
+ export class AddedScriptElementEvent extends Event {
80
+ static eventName: typeof addedScriptElementEventName = addedScriptElementEventName;
81
+
82
+ constructor(public scriptElement: HTMLScriptElement) {
83
+ super(AddedScriptElementEvent.eventName, { bubbles: false, composed: false });
84
+ }
85
+ }
package/README.md CHANGED
@@ -666,6 +666,203 @@ export default class MyEnhancement {
666
666
 
667
667
  [Implemented as EMCScript requirement](requirements/Done/EMCScript.md)
668
668
 
669
+ ## Syndicating Mount Observers with Synthesizer
670
+
671
+ The `Synthesizer` abstract base class enables automatic propagation of mount observer configurations across shadow DOM boundaries. It acts as a "syndicator-subscriber" pattern where a syndicator in the document root broadcasts script elements to subscribers in shadow roots.
672
+
673
+ **Why use Synthesizer?**
674
+
675
+ - Automatically share mount observer configurations across shadow roots
676
+ - Eliminates manual observer setup in each shadow root
677
+ - Ensures consistent behavior across component boundaries
678
+ - Works with both MOSE and EMC script elements
679
+ - Provides a declarative, inheritance-based approach
680
+
681
+ **How it works:**
682
+
683
+ 1. **Syndicator** (in document root): Watches for `script[type="mountobserver"]` and `script[type="emc"]` elements and broadcasts them to subscribers
684
+ 2. **Subscriber** (in shadow roots): Receives and clones script elements from the syndicator
685
+ 3. **Automatic activation**: Both syndicator and subscriber activate 5 built-in handlers in their respective root nodes
686
+
687
+ **Basic usage:**
688
+
689
+ ```html
690
+ <!-- Define your Synthesizer custom element -->
691
+ <script type="module">
692
+ import { Synthesizer } from 'mount-observer/Synthesizer.js';
693
+
694
+ class AppSynthesizer extends Synthesizer {}
695
+ customElements.define('app-synthesizer', AppSynthesizer);
696
+ </script>
697
+
698
+ <!-- Syndicator in document root with mount observer scripts -->
699
+ <app-synthesizer>
700
+ <script type="mountobserver">
701
+ {
702
+ "matching": "button.primary",
703
+ "import": "./primary-button.js",
704
+ "do": "builtIns.defineCustomElement"
705
+ }
706
+ </script>
707
+
708
+ <script type="emc">
709
+ {
710
+ "matching": ".interactive",
711
+ "enhConfig": {
712
+ "spawn": "./interactive.js",
713
+ "enhKey": "interactive"
714
+ }
715
+ }
716
+ </script>
717
+ </app-synthesizer>
718
+
719
+ <!-- Component with shadow root -->
720
+ <my-component>
721
+ #shadow
722
+ <!-- Subscriber automatically receives scripts from syndicator -->
723
+ <app-synthesizer></app-synthesizer>
724
+
725
+ <!-- These elements will be enhanced by the syndicated observers -->
726
+ <button class="primary">Click me</button>
727
+ <div class="interactive">Interactive content</div>
728
+ </my-component>
729
+ ```
730
+
731
+ **What happens:**
732
+
733
+ 1. The syndicator (`<app-synthesizer>` in document root) activates 5 built-in handlers:
734
+ - `builtIns.mountObserverScript`
735
+ - `builtIns.scriptExport`
736
+ - `builtIns.HTMLInclude`
737
+ - `builtIns.hoistTemplate`
738
+ - `builtIns.emcScript`
739
+
740
+ 2. The syndicator watches for script elements being added to its light children
741
+
742
+ 3. When a script is added, it waits for the `resolved` event (ensuring the script is parsed)
743
+
744
+ 4. The syndicator dispatches an `AddedScriptElementEvent` with the script element
745
+
746
+ 5. Subscribers in shadow roots:
747
+ - Find the syndicator in the document root (matching localName)
748
+ - Process existing scripts from the syndicator
749
+ - Subscribe to `addedscriptelement` events for new scripts
750
+ - Clone each script element and copy its `export` property
751
+ - Append cloned scripts to their own light children
752
+ - Activate the same 5 built-in handlers in their shadow root
753
+
754
+ **Syndicator vs Subscriber:**
755
+
756
+ The Synthesizer automatically determines its role based on its root node:
757
+ - **Document root** → Acts as syndicator (broadcasts scripts)
758
+ - **Shadow root** → Acts as subscriber (receives scripts)
759
+
760
+ **Activation of built-in handlers:**
761
+
762
+ Both syndicator and subscriber call `element.mount()` to activate handlers in their respective scopes:
763
+
764
+ ```javascript
765
+ // Activated in both syndicator and subscriber root nodes
766
+ await this.getRootNode().mount({
767
+ do: 'builtIns.mountObserverScript'
768
+ });
769
+ await this.getRootNode().mount({
770
+ do: 'builtIns.scriptExport'
771
+ });
772
+ await this.getRootNode().mount({
773
+ do: 'builtIns.HTMLInclude'
774
+ });
775
+ await this.getRootNode().mount({
776
+ do: 'builtIns.hoistTemplate'
777
+ });
778
+ await this.getRootNode().mount({
779
+ do: 'builtIns.emcScript'
780
+ });
781
+ ```
782
+
783
+ This ensures that:
784
+ - MOSE scripts are processed in each scope
785
+ - Script exports are available
786
+ - HTML includes work within each shadow root
787
+ - Templates are hoisted for performance
788
+ - EMC scripts enhance elements in each scope
789
+
790
+ **Script processing:**
791
+
792
+ When a subscriber receives a script element:
793
+
794
+ 1. Checks if the script has an `export` property (parsed configuration)
795
+ 2. If not, waits for the `resolved` event (with 5-second timeout)
796
+ 3. Clones the script element
797
+ 4. Copies the `export` property from source to clone (by reference)
798
+ 5. Appends the cloned script to the subscriber's light children
799
+ 6. The activated handlers process the cloned script in the shadow root's scope
800
+
801
+ **Benefits:**
802
+
803
+ - **Declarative**: Define observers once in the document root
804
+ - **Automatic**: Scripts propagate to all shadow roots automatically
805
+ - **Scoped**: Each shadow root gets its own observer instances
806
+ - **Efficient**: Parsed configurations are shared (not re-parsed)
807
+ - **Maintainable**: Update observers in one place, changes propagate everywhere
808
+
809
+ **Example - Multiple components:**
810
+
811
+ ```html
812
+ <!-- Syndicator with shared observers -->
813
+ <app-synthesizer>
814
+ <script type="mountobserver">
815
+ {
816
+ "matching": "button",
817
+ "import": "./button-enhancement.js",
818
+ "do": "builtIns.enhanceMountedElement"
819
+ }
820
+ </script>
821
+ </app-synthesizer>
822
+
823
+ <!-- Component 1 -->
824
+ <my-header>
825
+ #shadow
826
+ <app-synthesizer></app-synthesizer>
827
+ <button>Header Button</button> <!-- Enhanced -->
828
+ </my-header>
829
+
830
+ <!-- Component 2 -->
831
+ <my-footer>
832
+ #shadow
833
+ <app-synthesizer></app-synthesizer>
834
+ <button>Footer Button</button> <!-- Enhanced -->
835
+ </my-footer>
836
+ ```
837
+
838
+ Both components receive the button enhancement observer automatically.
839
+
840
+ **Error handling:**
841
+
842
+ - Logs errors if handler activation fails
843
+ - Logs errors if script processing fails
844
+ - Continues processing other scripts even if one fails
845
+ - Provides 5-second timeout for waiting on `resolved` events
846
+
847
+ **Requirements:**
848
+
849
+ - Must extend the `Synthesizer` abstract class
850
+ - Must be defined as a custom element
851
+ - Syndicator must be in the document root
852
+ - Subscribers must be in shadow roots
853
+ - All handlers must be imported and registered before use
854
+
855
+ **Comparison with `mountGlobally()`:**
856
+
857
+ Unlike `mountGlobally()`, which discovers shadow roots by observing custom elements, Synthesizer:
858
+ - Uses explicit syndicator-subscriber pattern
859
+ - Provides more control over which scripts are syndicated
860
+ - Works with any shadow root structure
861
+ - Doesn't rely on custom element discovery
862
+ - Allows for selective script propagation
863
+
864
+ [Implemented as Syndicating Mount Observers With Synthesizer requirement](requirements/Done/Syndicating Mount Observers With Synthesizer.md)
865
+
669
866
  ## Intra-Document HTML Includes with HTMLInclude
670
867
 
671
868
  The `builtIns.HTMLInclude` handler enables declarative HTML fragment reuse within a document using `<template src="#id">` syntax. Think of it as "constants for HTML" - define content once with an ID, then reference it multiple times throughout your document.
package/Synthesizer.js ADDED
@@ -0,0 +1,180 @@
1
+ import './ElementMountExtension.js';
2
+ import { waitForEvent } from 'assign-gingerly/waitForEvent.js';
3
+ import { AddedScriptElementEvent } from './Events.js';
4
+ /**
5
+ * Track which root nodes have already had handlers activated.
6
+ * Uses WeakSet to avoid memory leaks when nodes are garbage collected.
7
+ */
8
+ const activatedRootNodes = new WeakSet();
9
+ /**
10
+ * Abstract base class for syndicating mount observer and EMC script elements across shadow roots.
11
+ *
12
+ * Synthesizer instances act as either:
13
+ * - Syndicator (in document root): Broadcasts script elements to subscribers
14
+ * - Subscriber (in shadow roots): Receives and clones script elements from syndicator
15
+ *
16
+ * Ensures that handlers are only activated once per root node, even if multiple
17
+ * Synthesizer instances exist in the same root.
18
+ *
19
+ * Usage:
20
+ * ```javascript
21
+ * class MySynthesizer extends Synthesizer {}
22
+ * customElements.define('my-synthesizer', MySynthesizer);
23
+ * ```
24
+ *
25
+ * ```html
26
+ * <!-- Syndicator in document -->
27
+ * <my-synthesizer>
28
+ * <script type="mountobserver">...</script>
29
+ * <script type="emc">...</script>
30
+ * </my-synthesizer>
31
+ *
32
+ * <!-- Subscriber in shadow root -->
33
+ * <my-component>
34
+ * #shadow-root
35
+ * <my-synthesizer></my-synthesizer>
36
+ * </my-component>
37
+ * ```
38
+ */
39
+ export class Synthesizer extends HTMLElement {
40
+ #mutationObserver;
41
+ #isSyndicator = false;
42
+ /**
43
+ * List of built-in handlers to activate.
44
+ */
45
+ static builtInHandlers = [
46
+ 'builtIns.mountObserverScript',
47
+ 'builtIns.scriptExport',
48
+ 'builtIns.HTMLInclude',
49
+ 'builtIns.hoistTemplate',
50
+ 'builtIns.emcScript'
51
+ ];
52
+ connectedCallback() {
53
+ // Synthesizer elements are infrastructure, not UI
54
+ this.hidden = true;
55
+ // Identify the root node
56
+ const rootNode = this.getRootNode();
57
+ // Determine if this is a syndicator or subscriber
58
+ this.#isSyndicator = rootNode === document;
59
+ // Activate handlers on the root node
60
+ this.#activateHandlers(rootNode);
61
+ if (this.#isSyndicator) {
62
+ // Act as syndicator
63
+ this.#initializeSyndicator();
64
+ }
65
+ else {
66
+ // Act as subscriber
67
+ this.#initializeSubscriber();
68
+ }
69
+ }
70
+ disconnectedCallback() {
71
+ if (this.#mutationObserver) {
72
+ this.#mutationObserver.disconnect();
73
+ }
74
+ }
75
+ /**
76
+ * Activate mount observer handlers in the specified root node.
77
+ * Only activates once per root node, even if multiple Synthesizer instances exist.
78
+ */
79
+ async #activateHandlers(rootNode) {
80
+ // Check if handlers have already been activated for this root node
81
+ if (activatedRootNodes.has(rootNode)) {
82
+ return;
83
+ }
84
+ // Mark this root node as activated
85
+ activatedRootNodes.add(rootNode);
86
+ const constructor = this.constructor;
87
+ for (const handlerName of constructor.builtInHandlers) {
88
+ try {
89
+ await rootNode.mount({
90
+ do: handlerName
91
+ });
92
+ }
93
+ catch (error) {
94
+ console.error(`Synthesizer: Failed to activate handler ${handlerName}:`, error);
95
+ }
96
+ }
97
+ }
98
+ /**
99
+ * Initialize as syndicator (in document root).
100
+ * Watches for script elements and broadcasts them to subscribers.
101
+ */
102
+ #initializeSyndicator() {
103
+ // Process existing script elements
104
+ const scripts = this.querySelectorAll('script[type="mountobserver"], script[type="emc"]');
105
+ scripts.forEach(script => {
106
+ this.#broadcastScript(script);
107
+ });
108
+ // Watch for new script elements
109
+ this.#mutationObserver = new MutationObserver((mutations) => {
110
+ for (const mutation of mutations) {
111
+ for (const node of mutation.addedNodes) {
112
+ if (node instanceof HTMLScriptElement) {
113
+ const type = node.getAttribute('type');
114
+ if (type === 'mountobserver' || type === 'emc') {
115
+ this.#broadcastScript(node);
116
+ }
117
+ }
118
+ }
119
+ }
120
+ });
121
+ this.#mutationObserver.observe(this, {
122
+ childList: true,
123
+ subtree: false
124
+ });
125
+ }
126
+ /**
127
+ * Broadcast a script element to subscribers.
128
+ */
129
+ #broadcastScript(scriptElement) {
130
+ this.dispatchEvent(new AddedScriptElementEvent(scriptElement));
131
+ }
132
+ /**
133
+ * Initialize as subscriber (in shadow root).
134
+ * Subscribes to syndicator and processes script elements.
135
+ */
136
+ #initializeSubscriber() {
137
+ // Find the syndicator in document root
138
+ const syndicator = document.querySelector(this.localName);
139
+ if (!syndicator) {
140
+ console.warn(`Synthesizer: No syndicator found in document for ${this.localName}`);
141
+ return;
142
+ }
143
+ // Process existing scripts from syndicator
144
+ const scripts = syndicator.querySelectorAll('script[type="mountobserver"], script[type="emc"]');
145
+ scripts.forEach(script => {
146
+ this.#processScript(script);
147
+ });
148
+ // Subscribe to new scripts
149
+ syndicator.addEventListener(AddedScriptElementEvent.eventName, (e) => {
150
+ const event = e;
151
+ this.#processScript(event.scriptElement);
152
+ });
153
+ }
154
+ /**
155
+ * Process a script element from the syndicator.
156
+ * Waits for export property, then clones and appends.
157
+ */
158
+ async #processScript(scriptElement) {
159
+ try {
160
+ // Check if export property exists
161
+ let exportValue = scriptElement.export;
162
+ if (!exportValue) {
163
+ // Wait for resolved event with timeout
164
+ const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout waiting for resolved event')), 5000));
165
+ const eventPromise = waitForEvent(scriptElement, 'resolved');
166
+ const event = await Promise.race([eventPromise, timeoutPromise]);
167
+ exportValue = event.export;
168
+ }
169
+ // Clone the script element
170
+ const clonedScript = scriptElement.cloneNode(true);
171
+ // Copy the export property
172
+ clonedScript.export = exportValue;
173
+ // Append to this element's children
174
+ this.appendChild(clonedScript);
175
+ }
176
+ catch (error) {
177
+ console.error('Synthesizer: Failed to process script element:', error);
178
+ }
179
+ }
180
+ }
package/Synthesizer.ts ADDED
@@ -0,0 +1,207 @@
1
+ import './ElementMountExtension.js';
2
+ import { waitForEvent } from 'assign-gingerly/waitForEvent.js';
3
+ import { AddedScriptElementEvent } from './Events.js';
4
+
5
+ /**
6
+ * Track which root nodes have already had handlers activated.
7
+ * Uses WeakSet to avoid memory leaks when nodes are garbage collected.
8
+ */
9
+ const activatedRootNodes = new WeakSet<Node>();
10
+
11
+ /**
12
+ * Abstract base class for syndicating mount observer and EMC script elements across shadow roots.
13
+ *
14
+ * Synthesizer instances act as either:
15
+ * - Syndicator (in document root): Broadcasts script elements to subscribers
16
+ * - Subscriber (in shadow roots): Receives and clones script elements from syndicator
17
+ *
18
+ * Ensures that handlers are only activated once per root node, even if multiple
19
+ * Synthesizer instances exist in the same root.
20
+ *
21
+ * Usage:
22
+ * ```javascript
23
+ * class MySynthesizer extends Synthesizer {}
24
+ * customElements.define('my-synthesizer', MySynthesizer);
25
+ * ```
26
+ *
27
+ * ```html
28
+ * <!-- Syndicator in document -->
29
+ * <my-synthesizer>
30
+ * <script type="mountobserver">...</script>
31
+ * <script type="emc">...</script>
32
+ * </my-synthesizer>
33
+ *
34
+ * <!-- Subscriber in shadow root -->
35
+ * <my-component>
36
+ * #shadow-root
37
+ * <my-synthesizer></my-synthesizer>
38
+ * </my-component>
39
+ * ```
40
+ */
41
+ export abstract class Synthesizer extends HTMLElement {
42
+ #mutationObserver: MutationObserver | undefined;
43
+ #isSyndicator: boolean = false;
44
+
45
+ /**
46
+ * List of built-in handlers to activate.
47
+ */
48
+ protected static builtInHandlers = [
49
+ 'builtIns.mountObserverScript',
50
+ 'builtIns.scriptExport',
51
+ 'builtIns.HTMLInclude',
52
+ 'builtIns.hoistTemplate',
53
+ 'builtIns.emcScript'
54
+ ];
55
+
56
+ connectedCallback(): void {
57
+ // Synthesizer elements are infrastructure, not UI
58
+ this.hidden = true;
59
+
60
+ // Identify the root node
61
+ const rootNode = this.getRootNode();
62
+
63
+ // Determine if this is a syndicator or subscriber
64
+ this.#isSyndicator = rootNode === document;
65
+
66
+ // Activate handlers on the root node
67
+ this.#activateHandlers(rootNode);
68
+
69
+ if (this.#isSyndicator) {
70
+ // Act as syndicator
71
+ this.#initializeSyndicator();
72
+ } else {
73
+ // Act as subscriber
74
+ this.#initializeSubscriber();
75
+ }
76
+ }
77
+
78
+ disconnectedCallback(): void {
79
+ if (this.#mutationObserver) {
80
+ this.#mutationObserver.disconnect();
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Activate mount observer handlers in the specified root node.
86
+ * Only activates once per root node, even if multiple Synthesizer instances exist.
87
+ */
88
+ async #activateHandlers(rootNode: Node): Promise<void> {
89
+ // Check if handlers have already been activated for this root node
90
+ if (activatedRootNodes.has(rootNode)) {
91
+ return;
92
+ }
93
+
94
+ // Mark this root node as activated
95
+ activatedRootNodes.add(rootNode);
96
+
97
+ const constructor = this.constructor as typeof Synthesizer;
98
+
99
+ for (const handlerName of constructor.builtInHandlers) {
100
+ try {
101
+ await (rootNode as any).mount({
102
+ do: handlerName
103
+ });
104
+ } catch (error) {
105
+ console.error(`Synthesizer: Failed to activate handler ${handlerName}:`, error);
106
+ }
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Initialize as syndicator (in document root).
112
+ * Watches for script elements and broadcasts them to subscribers.
113
+ */
114
+ #initializeSyndicator(): void {
115
+ // Process existing script elements
116
+ const scripts = this.querySelectorAll('script[type="mountobserver"], script[type="emc"]');
117
+ scripts.forEach(script => {
118
+ this.#broadcastScript(script as HTMLScriptElement);
119
+ });
120
+
121
+ // Watch for new script elements
122
+ this.#mutationObserver = new MutationObserver((mutations) => {
123
+ for (const mutation of mutations) {
124
+ for (const node of mutation.addedNodes) {
125
+ if (node instanceof HTMLScriptElement) {
126
+ const type = node.getAttribute('type');
127
+ if (type === 'mountobserver' || type === 'emc') {
128
+ this.#broadcastScript(node);
129
+ }
130
+ }
131
+ }
132
+ }
133
+ });
134
+
135
+ this.#mutationObserver.observe(this, {
136
+ childList: true,
137
+ subtree: false
138
+ });
139
+ }
140
+
141
+ /**
142
+ * Broadcast a script element to subscribers.
143
+ */
144
+ #broadcastScript(scriptElement: HTMLScriptElement): void {
145
+ this.dispatchEvent(new AddedScriptElementEvent(scriptElement));
146
+ }
147
+
148
+ /**
149
+ * Initialize as subscriber (in shadow root).
150
+ * Subscribes to syndicator and processes script elements.
151
+ */
152
+ #initializeSubscriber(): void {
153
+ // Find the syndicator in document root
154
+ const syndicator = document.querySelector(this.localName) as Synthesizer | null;
155
+
156
+ if (!syndicator) {
157
+ console.warn(`Synthesizer: No syndicator found in document for ${this.localName}`);
158
+ return;
159
+ }
160
+
161
+ // Process existing scripts from syndicator
162
+ const scripts = syndicator.querySelectorAll('script[type="mountobserver"], script[type="emc"]');
163
+ scripts.forEach(script => {
164
+ this.#processScript(script as HTMLScriptElement);
165
+ });
166
+
167
+ // Subscribe to new scripts
168
+ syndicator.addEventListener(AddedScriptElementEvent.eventName, (e) => {
169
+ const event = e as AddedScriptElementEvent;
170
+ this.#processScript(event.scriptElement);
171
+ });
172
+ }
173
+
174
+ /**
175
+ * Process a script element from the syndicator.
176
+ * Waits for export property, then clones and appends.
177
+ */
178
+ async #processScript(scriptElement: HTMLScriptElement): Promise<void> {
179
+ try {
180
+ // Check if export property exists
181
+ let exportValue = (scriptElement as any).export;
182
+
183
+ if (!exportValue) {
184
+ // Wait for resolved event with timeout
185
+ const timeoutPromise = new Promise((_, reject) =>
186
+ setTimeout(() => reject(new Error('Timeout waiting for resolved event')), 5000)
187
+ );
188
+
189
+ const eventPromise = waitForEvent(scriptElement, 'resolved');
190
+
191
+ const event = await Promise.race([eventPromise, timeoutPromise]);
192
+ exportValue = (event as any).export;
193
+ }
194
+
195
+ // Clone the script element
196
+ const clonedScript = scriptElement.cloneNode(true) as HTMLScriptElement;
197
+
198
+ // Copy the export property
199
+ (clonedScript as any).export = exportValue;
200
+
201
+ // Append to this element's children
202
+ this.appendChild(clonedScript);
203
+ } catch (error) {
204
+ console.error('Synthesizer: Failed to process script element:', error);
205
+ }
206
+ }
207
+ }
@@ -1,5 +1,4 @@
1
1
  import { EvtRt } from '../EvtRt.js';
2
- import { MountObserver } from '../MountObserver.js';
3
2
  import '../ElementMountExtension.js';
4
3
  import 'assign-gingerly/object-extension.js';
5
4
  /**
@@ -68,15 +67,9 @@ export class EMCScriptHandler extends EvtRt {
68
67
  if (!scriptElement.id && scriptElement.parentElement) {
69
68
  scriptElement.id = `${scriptElement.parentElement.localName}.${enhKey}`;
70
69
  }
71
- // Construct MountConfig from EMC config
70
+ // Construct MountConfig from EMC config and mount it
72
71
  const mountConfig = await this.buildMountConfig(emcConfig);
73
- // Create a MountObserver to watch for elements matching the config
74
- const observer = new MountObserver(mountConfig);
75
- // Store observer reference for cleanup
76
- scriptElement.emcObserver = observer;
77
- // Observe from the script element's parent or root node
78
- const observeTarget = scriptElement.parentElement || scriptElement.getRootNode();
79
- await observer.observe(observeTarget);
72
+ await scriptElement.mount(mountConfig);
80
73
  }
81
74
  /**
82
75
  * Build a MountConfig from an EMC config.
@@ -174,5 +167,6 @@ export class EMCScriptHandler extends EvtRt {
174
167
  }
175
168
  }
176
169
  // Register built-in handler
170
+ import { MountObserver } from '../MountObserver.js';
177
171
  export const emc = 'builtIns.emcScript';
178
172
  MountObserver.define(emc, EMCScriptHandler);
@@ -1,6 +1,5 @@
1
1
  import { EvtRt } from '../EvtRt.js';
2
2
  import { EMC, MountConfig, MountContext } from '../types/mount-observer/types.js';
3
- import { MountObserver } from '../MountObserver.js';
4
3
  import '../ElementMountExtension.js';
5
4
  import 'assign-gingerly/object-extension.js';
6
5
 
@@ -80,18 +79,9 @@ export class EMCScriptHandler extends EvtRt {
80
79
  scriptElement.id = `${scriptElement.parentElement.localName}.${enhKey}`;
81
80
  }
82
81
 
83
- // Construct MountConfig from EMC config
82
+ // Construct MountConfig from EMC config and mount it
84
83
  const mountConfig = await this.buildMountConfig(emcConfig);
85
-
86
- // Create a MountObserver to watch for elements matching the config
87
- const observer = new MountObserver(mountConfig);
88
-
89
- // Store observer reference for cleanup
90
- (scriptElement as any).emcObserver = observer;
91
-
92
- // Observe from the script element's parent or root node
93
- const observeTarget = scriptElement.parentElement || scriptElement.getRootNode() as Node;
94
- await observer.observe(observeTarget);
84
+ await scriptElement.mount(mountConfig);
95
85
  }
96
86
 
97
87
  /**
@@ -211,6 +201,8 @@ export class EMCScriptHandler extends EvtRt {
211
201
  }
212
202
 
213
203
  // Register built-in handler
204
+ import { MountObserver } from '../MountObserver.js';
205
+
214
206
  export const emc = 'builtIns.emcScript';
215
207
 
216
208
  MountObserver.define(emc, EMCScriptHandler);
package/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  // Main entry point for MountObserver v2
2
2
  export { MountObserver } from './MountObserver.js';
3
+ export { Synthesizer } from './Synthesizer.js';
3
4
  export { withScopePerimeter } from './withScopePerimeter.js';
4
5
  export { emitMountedElementEvents } from './emitEvents.js';
5
6
  export { arr } from './arr.js';
@@ -12,7 +13,7 @@ export { EMCScriptHandler } from './handlers/EMCScript.js';
12
13
  export { HoistTemplateHandler } from './handlers/HoistTemplate.js';
13
14
  export { HTMLIncludeHandler } from './handlers/HTMLInclude.js';
14
15
  export { upShadowSearch } from './upShadowSearch.js';
15
- export { mountEventName, dismountEventName, disconnectEventName, loadEventName, mediamatchEventName, mediaunmatchEventName } from './Events.js';
16
+ export { mountEventName, dismountEventName, disconnectEventName, loadEventName, mediamatchEventName, mediaunmatchEventName, addedScriptElementEventName } from './Events.js';
16
17
  // Register built-in handlers
17
18
  import './EvtRt.js';
18
19
  import './handlers/DefineCustomElement.js';
package/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  // Main entry point for MountObserver v2
2
2
  export { MountObserver } from './MountObserver.js';
3
+ export { Synthesizer } from './Synthesizer.js';
3
4
  export { withScopePerimeter } from './withScopePerimeter.js';
4
5
  export { emitMountedElementEvents } from './emitEvents.js';
5
6
  export { arr } from './arr.js';
@@ -29,7 +30,8 @@ export {
29
30
  disconnectEventName,
30
31
  loadEventName,
31
32
  mediamatchEventName,
32
- mediaunmatchEventName
33
+ mediaunmatchEventName,
34
+ addedScriptElementEventName
33
35
  } from './Events.js';
34
36
 
35
37
  // Register built-in handlers
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mount-observer",
3
- "version": "0.1.20",
3
+ "version": "0.1.22",
4
4
  "description": "Observe and act on css matches.",
5
5
  "main": "MountObserver.js",
6
6
  "module": "MountObserver.js",