@adaas/are-html 0.0.22 → 0.0.24
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/dist/browser/index.d.mts +194 -10
- package/dist/browser/index.mjs +696 -245
- package/dist/browser/index.mjs.map +1 -1
- package/dist/node/{AreBinding.attribute-doUvtOjc.d.mts → AreBinding.attribute-BWzEIw6H.d.mts} +45 -0
- package/dist/node/{AreBinding.attribute-Bm5LlOyE.d.ts → AreBinding.attribute-GpT-5Qmf.d.ts} +45 -0
- package/dist/node/attributes/AreBinding.attribute.d.mts +1 -1
- package/dist/node/attributes/AreBinding.attribute.d.ts +1 -1
- package/dist/node/attributes/AreDirective.attribute.d.mts +1 -1
- package/dist/node/attributes/AreDirective.attribute.d.ts +1 -1
- package/dist/node/attributes/AreEvent.attribute.d.mts +1 -1
- package/dist/node/attributes/AreEvent.attribute.d.ts +1 -1
- package/dist/node/attributes/AreStatic.attribute.d.mts +1 -1
- package/dist/node/attributes/AreStatic.attribute.d.ts +1 -1
- package/dist/node/directives/AreDirectiveFor.directive.d.mts +18 -1
- package/dist/node/directives/AreDirectiveFor.directive.d.ts +18 -1
- package/dist/node/directives/AreDirectiveFor.directive.js +57 -9
- package/dist/node/directives/AreDirectiveFor.directive.js.map +1 -1
- package/dist/node/directives/AreDirectiveFor.directive.mjs +57 -9
- package/dist/node/directives/AreDirectiveFor.directive.mjs.map +1 -1
- package/dist/node/directives/AreDirectiveIf.directive.d.mts +18 -2
- package/dist/node/directives/AreDirectiveIf.directive.d.ts +18 -2
- package/dist/node/directives/AreDirectiveIf.directive.js +29 -6
- package/dist/node/directives/AreDirectiveIf.directive.js.map +1 -1
- package/dist/node/directives/AreDirectiveIf.directive.mjs +29 -6
- package/dist/node/directives/AreDirectiveIf.directive.mjs.map +1 -1
- package/dist/node/directives/AreDirectiveShow.directive.d.mts +1 -1
- package/dist/node/directives/AreDirectiveShow.directive.d.ts +1 -1
- package/dist/node/engine/AreHTML.compiler.d.mts +4 -2
- package/dist/node/engine/AreHTML.compiler.d.ts +4 -2
- package/dist/node/engine/AreHTML.compiler.js +11 -4
- package/dist/node/engine/AreHTML.compiler.js.map +1 -1
- package/dist/node/engine/AreHTML.compiler.mjs +11 -4
- package/dist/node/engine/AreHTML.compiler.mjs.map +1 -1
- package/dist/node/engine/AreHTML.constants.d.mts +33 -1
- package/dist/node/engine/AreHTML.constants.d.ts +33 -1
- package/dist/node/engine/AreHTML.constants.js +166 -0
- package/dist/node/engine/AreHTML.constants.js.map +1 -1
- package/dist/node/engine/AreHTML.constants.mjs +165 -1
- package/dist/node/engine/AreHTML.constants.mjs.map +1 -1
- package/dist/node/engine/AreHTML.context.d.mts +66 -0
- package/dist/node/engine/AreHTML.context.d.ts +66 -0
- package/dist/node/engine/AreHTML.context.js +98 -0
- package/dist/node/engine/AreHTML.context.js.map +1 -1
- package/dist/node/engine/AreHTML.context.mjs +98 -0
- package/dist/node/engine/AreHTML.context.mjs.map +1 -1
- package/dist/node/engine/AreHTML.interpreter.d.mts +3 -0
- package/dist/node/engine/AreHTML.interpreter.d.ts +3 -0
- package/dist/node/engine/AreHTML.interpreter.js +66 -10
- package/dist/node/engine/AreHTML.interpreter.js.map +1 -1
- package/dist/node/engine/AreHTML.interpreter.mjs +66 -10
- package/dist/node/engine/AreHTML.interpreter.mjs.map +1 -1
- package/dist/node/engine/AreHTML.lifecycle.d.mts +1 -8
- package/dist/node/engine/AreHTML.lifecycle.d.ts +1 -8
- package/dist/node/engine/AreHTML.lifecycle.js +29 -44
- package/dist/node/engine/AreHTML.lifecycle.js.map +1 -1
- package/dist/node/engine/AreHTML.lifecycle.mjs +29 -44
- package/dist/node/engine/AreHTML.lifecycle.mjs.map +1 -1
- package/dist/node/engine/AreHTML.tokenizer.d.mts +1 -1
- package/dist/node/engine/AreHTML.tokenizer.d.ts +1 -1
- package/dist/node/engine/AreHTML.tokenizer.js +7 -1
- package/dist/node/engine/AreHTML.tokenizer.js.map +1 -1
- package/dist/node/engine/AreHTML.tokenizer.mjs +7 -1
- package/dist/node/engine/AreHTML.tokenizer.mjs.map +1 -1
- package/dist/node/engine/AreHTML.transformer.d.mts +1 -1
- package/dist/node/engine/AreHTML.transformer.d.ts +1 -1
- package/dist/node/index.d.mts +4 -3
- package/dist/node/index.d.ts +4 -3
- package/dist/node/index.js +7 -0
- package/dist/node/index.mjs +1 -0
- package/dist/node/instructions/AddStaticHTML.instruction.d.mts +8 -0
- package/dist/node/instructions/AddStaticHTML.instruction.d.ts +8 -0
- package/dist/node/instructions/AddStaticHTML.instruction.js +31 -0
- package/dist/node/instructions/AddStaticHTML.instruction.js.map +1 -0
- package/dist/node/instructions/AddStaticHTML.instruction.mjs +24 -0
- package/dist/node/instructions/AddStaticHTML.instruction.mjs.map +1 -0
- package/dist/node/instructions/AreHTML.instructions.constants.d.mts +1 -0
- package/dist/node/instructions/AreHTML.instructions.constants.d.ts +1 -0
- package/dist/node/instructions/AreHTML.instructions.constants.js +1 -0
- package/dist/node/instructions/AreHTML.instructions.constants.js.map +1 -1
- package/dist/node/instructions/AreHTML.instructions.constants.mjs +1 -0
- package/dist/node/instructions/AreHTML.instructions.constants.mjs.map +1 -1
- package/dist/node/instructions/AreHTML.instructions.types.d.mts +9 -1
- package/dist/node/instructions/AreHTML.instructions.types.d.ts +9 -1
- package/dist/node/lib/AreDirective/AreDirective.component.d.mts +1 -1
- package/dist/node/lib/AreDirective/AreDirective.component.d.ts +1 -1
- package/dist/node/lib/AreDirective/AreDirective.types.d.mts +1 -1
- package/dist/node/lib/AreDirective/AreDirective.types.d.ts +1 -1
- package/dist/node/lib/AreHTML/AreHTML.tokenizer.d.mts +1 -1
- package/dist/node/lib/AreHTML/AreHTML.tokenizer.d.ts +1 -1
- package/dist/node/lib/AreHTMLAttribute/AreHTML.attribute.d.mts +1 -1
- package/dist/node/lib/AreHTMLAttribute/AreHTML.attribute.d.ts +1 -1
- package/dist/node/lib/AreHTMLNode/AreHTMLNode.d.mts +1 -1
- package/dist/node/lib/AreHTMLNode/AreHTMLNode.d.ts +1 -1
- package/dist/node/lib/AreHTMLNode/AreHTMLNode.js +51 -0
- package/dist/node/lib/AreHTMLNode/AreHTMLNode.js.map +1 -1
- package/dist/node/lib/AreHTMLNode/AreHTMLNode.mjs +51 -0
- package/dist/node/lib/AreHTMLNode/AreHTMLNode.mjs.map +1 -1
- package/dist/node/lib/AreRoot/AreRoot.component.js.map +1 -1
- package/dist/node/lib/AreRoot/AreRoot.component.mjs.map +1 -1
- package/dist/node/nodes/AreComment.d.mts +1 -1
- package/dist/node/nodes/AreComment.d.ts +1 -1
- package/dist/node/nodes/AreComponent.d.mts +1 -1
- package/dist/node/nodes/AreComponent.d.ts +1 -1
- package/dist/node/nodes/AreInterpolation.d.mts +1 -1
- package/dist/node/nodes/AreInterpolation.d.ts +1 -1
- package/dist/node/nodes/AreRoot.d.mts +1 -1
- package/dist/node/nodes/AreRoot.d.ts +1 -1
- package/dist/node/nodes/AreText.d.mts +1 -1
- package/dist/node/nodes/AreText.d.ts +1 -1
- package/examples/dashboard/concept.ts +1 -1
- package/examples/dashboard/dist/index.html +1 -1
- package/examples/dashboard/dist/{mqh9ryml-xat335.js → mqiw5sqa-ypckmj.js} +403 -57
- package/examples/for-perf/dist/index.html +1 -1
- package/examples/for-perf/dist/{mqh9ryfo-6a8d0o.js → mqp8i2py-vltsx0.js} +3030 -2474
- package/examples/lazy-loading/README.md +76 -0
- package/examples/lazy-loading/concept.ts +55 -0
- package/examples/lazy-loading/containers/UI.container.ts +215 -0
- package/examples/lazy-loading/dist/app.js +3803 -0
- package/examples/{for-perf/dist/mqh9ryfq-4pf5cv.js → lazy-loading/dist/chunks/chunk-6K72IBO4.js} +2708 -5476
- package/examples/lazy-loading/dist/index.html +36 -0
- package/examples/lazy-loading/dist/lazy/about-page.js +59 -0
- package/examples/lazy-loading/dist/lazy/reports-page.js +65 -0
- package/examples/lazy-loading/dist/lazy/settings-page.js +54 -0
- package/examples/lazy-loading/public/index.html +36 -0
- package/examples/lazy-loading/src/components/AppShell.component.ts +44 -0
- package/examples/lazy-loading/src/components/HomePage.component.ts +59 -0
- package/examples/lazy-loading/src/components/LazyOutlet.component.ts +108 -0
- package/examples/lazy-loading/src/components/NavBar.component.ts +98 -0
- package/examples/lazy-loading/src/concept.ts +116 -0
- package/examples/lazy-loading/src/lazy/AboutPage.component.ts +54 -0
- package/examples/lazy-loading/src/lazy/ReportsPage.component.ts +56 -0
- package/examples/lazy-loading/src/lazy/SettingsPage.component.ts +45 -0
- package/examples/lazy-loading/src/runtime/ComponentManifest.fragment.ts +61 -0
- package/examples/lazy-loading/src/runtime/LazyComponentResolver.fragment.ts +77 -0
- package/examples/os-desktop/README.md +91 -0
- package/examples/os-desktop/concept.ts +54 -0
- package/examples/os-desktop/containers/OS.container.ts +198 -0
- package/examples/os-desktop/containers/apps/AppBackend.ts +29 -0
- package/examples/os-desktop/containers/apps/GanttApp.backend.ts +56 -0
- package/examples/os-desktop/containers/apps/MarketingApp.backend.ts +68 -0
- package/examples/os-desktop/dist/app.js +4410 -0
- package/examples/os-desktop/dist/apps/gantt/app.js +271 -0
- package/examples/os-desktop/dist/apps/marketing/app.js +346 -0
- package/examples/{for-perf/dist/mqh9ryde-m243t8.js → os-desktop/dist/chunks/chunk-6K72IBO4.js} +2708 -5476
- package/examples/os-desktop/dist/chunks/chunk-EIIGUL6N.js +30 -0
- package/examples/os-desktop/dist/chunks/chunk-WOH7L5UR.js +30 -0
- package/examples/os-desktop/dist/index.html +33 -0
- package/examples/os-desktop/public/index.html +33 -0
- package/examples/os-desktop/src/apps/gantt/GanttApp.component.ts +41 -0
- package/examples/os-desktop/src/apps/gantt/GanttChart.component.ts +126 -0
- package/examples/os-desktop/src/apps/gantt/GanttStore.ts +47 -0
- package/examples/os-desktop/src/apps/gantt/GanttToolbar.component.ts +73 -0
- package/examples/os-desktop/src/apps/gantt/index.ts +13 -0
- package/examples/os-desktop/src/apps/marketing/MarketingApp.component.ts +53 -0
- package/examples/os-desktop/src/apps/marketing/MarketingStore.ts +34 -0
- package/examples/os-desktop/src/apps/marketing/PostEditor.component.ts +153 -0
- package/examples/os-desktop/src/apps/marketing/PostPreview.component.ts +110 -0
- package/examples/os-desktop/src/apps/marketing/index.ts +16 -0
- package/examples/os-desktop/src/concept.ts +126 -0
- package/examples/os-desktop/src/os/AppStage.component.ts +112 -0
- package/examples/os-desktop/src/os/AppWindow.component.ts +102 -0
- package/examples/os-desktop/src/os/Desktop.component.ts +106 -0
- package/examples/os-desktop/src/os/Dock.component.ts +174 -0
- package/examples/os-desktop/src/os/Hud.component.ts +83 -0
- package/examples/os-desktop/src/os/Launchpad.component.ts +191 -0
- package/examples/os-desktop/src/os/MenuBar.component.ts +156 -0
- package/examples/os-desktop/src/runtime/AppComponentResolver.fragment.ts +121 -0
- package/examples/os-desktop/src/runtime/AppRegistry.fragment.ts +104 -0
- package/examples/os-desktop/src/signals/MouseState.signal.ts +34 -0
- package/examples/os-desktop/src/signals/OSRoute.signal.ts +37 -0
- package/examples/os-desktop/src/signals/SelectionState.signal.ts +34 -0
- package/examples/signal-routing/dist/index.html +1 -1
- package/examples/signal-routing/dist/{mqh9ryc9-dkcbkx.js → mqp8hgce-4d6rh0.js} +3196 -2640
- package/package.json +13 -9
- package/src/directives/AreDirectiveFor.directive.ts +99 -16
- package/src/directives/AreDirectiveIf.directive.ts +33 -4
- package/src/engine/AreHTML.compiler.ts +25 -2
- package/src/engine/AreHTML.constants.ts +142 -0
- package/src/engine/AreHTML.context.ts +112 -0
- package/src/engine/AreHTML.interpreter.ts +114 -13
- package/src/engine/AreHTML.lifecycle.ts +81 -74
- package/src/engine/AreHTML.tokenizer.ts +30 -1
- package/src/index.ts +1 -0
- package/src/instructions/AddStaticHTML.instruction.ts +23 -0
- package/src/instructions/AreHTML.instructions.constants.ts +1 -0
- package/src/instructions/AreHTML.instructions.types.ts +9 -0
- package/src/lib/AreHTMLNode/AreHTMLNode.ts +74 -0
- package/src/lib/AreRoot/AreRoot.component.ts +3 -3
- package/tests/PropPropagation.test.ts +181 -0
- package/tests/StaticIsland.test.ts +115 -0
- package/tests/jest.setup.ts +11 -0
|
@@ -16,6 +16,19 @@ import { AreDirectiveMeta } from "@adaas/are-html/directive/AreDirective.meta";
|
|
|
16
16
|
description: 'AreHTMLNode represents a node in the HTML structure. It extends the base AreNode and includes properties and methods specific to HTML nodes, such as handling attributes, directives, events, and styles.'
|
|
17
17
|
})
|
|
18
18
|
export class AreHTMLNode extends AreNode {
|
|
19
|
+
/**
|
|
20
|
+
* When set, this node is a *static island* root: its entire inner subtree
|
|
21
|
+
* was detected (at tokenize time) to contain no ARE-reactive constructs —
|
|
22
|
+
* no interpolations, no dynamic attributes and only standard HTML tags.
|
|
23
|
+
*
|
|
24
|
+
* Instead of being exploded into one child AreNode per element/text node,
|
|
25
|
+
* the inner markup is preserved verbatim here and materialised in a single
|
|
26
|
+
* pass by the interpreter (browser-parsed `innerHTML` / cached `<template>`
|
|
27
|
+
* clone). The node's OWN attributes (including any dynamic `:`/`@`/`$` on
|
|
28
|
+
* the island root) still compile and stay reactive as usual.
|
|
29
|
+
*/
|
|
30
|
+
protected _staticInnerHTML?: string;
|
|
31
|
+
|
|
19
32
|
/**
|
|
20
33
|
* Actual node type.
|
|
21
34
|
* By default it's a tag name
|
|
@@ -23,6 +36,67 @@ export class AreHTMLNode extends AreNode {
|
|
|
23
36
|
get tag(): string {
|
|
24
37
|
return this.aseid.entity;
|
|
25
38
|
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The verbatim inner markup captured when this node was identified as a
|
|
42
|
+
* static island, or `undefined` for ordinary (per-node) nodes.
|
|
43
|
+
*/
|
|
44
|
+
get staticInnerHTML(): string | undefined {
|
|
45
|
+
return this._staticInnerHTML;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Whether this node is a static-island root (see `_staticInnerHTML`).
|
|
50
|
+
*/
|
|
51
|
+
get isStaticIsland(): boolean {
|
|
52
|
+
return this._staticInnerHTML !== undefined;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Marks this node as a static-island root, capturing the verbatim inner
|
|
57
|
+
* markup to be materialised in one shot by the interpreter. Called by the
|
|
58
|
+
* tokenizer when the node's inner content is detected to be fully static.
|
|
59
|
+
*/
|
|
60
|
+
markStatic(innerHTML: string): void {
|
|
61
|
+
this._staticInnerHTML = innerHTML;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Deep-clone the node. Overridden to carry over the static-island marker
|
|
66
|
+
* (`_staticInnerHTML`), which lives on AreHTMLNode and is therefore NOT
|
|
67
|
+
* copied by the base AreNode.clone(). Without this, cloning a directive
|
|
68
|
+
* template ($if/$for) that wraps a static island (e.g. `<span $if>★</span>`)
|
|
69
|
+
* would drop the captured inner markup and render an empty element. The
|
|
70
|
+
* base clone() recurses via each child's polymorphic clone(), so nested
|
|
71
|
+
* island children are preserved automatically through this override.
|
|
72
|
+
*/
|
|
73
|
+
clone<T extends AreNode = AreNode>(this: T): T {
|
|
74
|
+
const cloned = super.clone() as unknown as AreHTMLNode;
|
|
75
|
+
const self = this as unknown as AreHTMLNode;
|
|
76
|
+
|
|
77
|
+
if (self._staticInnerHTML !== undefined)
|
|
78
|
+
cloned.markStatic(self._staticInnerHTML);
|
|
79
|
+
|
|
80
|
+
return cloned as unknown as T;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Clone the node while transferring its existing scope to the clone (used by
|
|
85
|
+
* the $if/$for directives to turn the original node into a lightweight group
|
|
86
|
+
* container). Overridden for the same reason as `clone()`: the static-island
|
|
87
|
+
* marker must survive so a directive applied to an island root keeps its
|
|
88
|
+
* inner markup.
|
|
89
|
+
*/
|
|
90
|
+
cloneWithScope<T extends AreNode = AreNode>(this: T): T {
|
|
91
|
+
const cloned = super.cloneWithScope() as unknown as AreHTMLNode;
|
|
92
|
+
const self = this as unknown as AreHTMLNode;
|
|
93
|
+
|
|
94
|
+
if (self._staticInnerHTML !== undefined)
|
|
95
|
+
cloned.markStatic(self._staticInnerHTML);
|
|
96
|
+
|
|
97
|
+
return cloned as unknown as T;
|
|
98
|
+
}
|
|
99
|
+
|
|
26
100
|
/**
|
|
27
101
|
* The static attributes defined for the node, which are typically used to represent static properties or characteristics of the node that do not change based on the context or state. These attributes are usually defined in the template and are not reactive.
|
|
28
102
|
*
|
|
@@ -160,9 +160,9 @@ export class AreRoot extends Are {
|
|
|
160
160
|
child.transform();
|
|
161
161
|
|
|
162
162
|
child.compile();
|
|
163
|
-
// The
|
|
164
|
-
//
|
|
165
|
-
//
|
|
163
|
+
// The initial mount is atomic (synchronous): the routed subtree is
|
|
164
|
+
// rendered in one uninterrupted pass so no update can observe a
|
|
165
|
+
// partially mounted tree. The await is a harmless no-op here.
|
|
166
166
|
await child.mount();
|
|
167
167
|
}
|
|
168
168
|
}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment jsdom
|
|
3
|
+
* @jest-environment-options {"customExportConditions": ["node", "require", "default"]}
|
|
4
|
+
*
|
|
5
|
+
* Integration coverage for parent → child PROP propagation via `:prop="expr"`
|
|
6
|
+
* bindings, including the two structural directives `$if` and `$for`.
|
|
7
|
+
*
|
|
8
|
+
* These render a real component tree into a jsdom document through the full
|
|
9
|
+
* ARE pipeline (tokenize → transform → compile → interpret → mount) exactly
|
|
10
|
+
* the way the browser examples bootstrap, then assert on the produced DOM.
|
|
11
|
+
*
|
|
12
|
+
* Scenarios:
|
|
13
|
+
* 1. Plain parent → child prop (literal + store-derived expression).
|
|
14
|
+
* 2. A child prop INSIDE an `$if` block.
|
|
15
|
+
* 3. A child prop INSIDE a `$for` loop that references the loop variable
|
|
16
|
+
* (`:label="item.name"`) — the case that requires the prop-binding
|
|
17
|
+
* compile path to merge the directive (loop) scope.
|
|
18
|
+
* 4. Reactivity: updating the parent store flows into the child prop.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { A_Concept, A_Context } from '@adaas/a-concept';
|
|
22
|
+
import { A_Inject, A_Caller } from '@adaas/a-concept';
|
|
23
|
+
import { A_Config, ConfigReader } from '@adaas/a-utils/a-config';
|
|
24
|
+
import { A_Logger, A_LOGGER_ENV_KEYS } from '@adaas/a-utils/a-logger';
|
|
25
|
+
import { A_SignalBus, A_SignalState } from '@adaas/a-utils/a-signal';
|
|
26
|
+
import { A_Polyfill } from '@adaas/a-utils/a-polyfill';
|
|
27
|
+
|
|
28
|
+
import { Are, AreNode, AreStore, AreContainer, AreInit, AreRoute } from '@adaas/are';
|
|
29
|
+
import { AreRoot } from '@adaas/are-html/root/AreRoot.component';
|
|
30
|
+
import { AreHTMLEngine } from '@adaas/are-html/engine';
|
|
31
|
+
import { AreHTMLEngineContext } from '@adaas/are-html/context';
|
|
32
|
+
import { AreDirectiveIf } from '@adaas/are-html/directives/AreDirectiveIf.directive';
|
|
33
|
+
import { AreDirectiveFor } from '@adaas/are-html/directives/AreDirectiveFor.directive';
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
37
|
+
// ── Test components ──────────────────────────────────────────────────────────
|
|
38
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
39
|
+
|
|
40
|
+
/** Leaf component: renders whatever `label` prop it receives. */
|
|
41
|
+
class TestChild extends Are {
|
|
42
|
+
props: Record<string, any> = {
|
|
43
|
+
label: { type: 'string', default: '' },
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
@Are.Template
|
|
47
|
+
template(@A_Inject(A_Caller) node: AreNode): void {
|
|
48
|
+
node.setContent(`<span class="child">{{label}}</span>`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@Are.Data
|
|
52
|
+
data(@A_Inject(AreStore) store: AreStore): void {
|
|
53
|
+
store.set({ label: store.get('label') ?? '' });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Parent: passes props down plainly, inside `$if`, and inside `$for`. */
|
|
58
|
+
class TestParent extends Are {
|
|
59
|
+
@Are.Template
|
|
60
|
+
template(@A_Inject(A_Caller) node: AreNode): void {
|
|
61
|
+
node.setContent(`
|
|
62
|
+
<div class="parent">
|
|
63
|
+
<test-child class="basic" :label="topLabel"></test-child>
|
|
64
|
+
|
|
65
|
+
<div class="if-wrap" $if="show">
|
|
66
|
+
<test-child class="in-if" :label="topLabel"></test-child>
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
<test-child class="in-for"
|
|
70
|
+
$for="item in items track item.id"
|
|
71
|
+
:label="item.name"></test-child>
|
|
72
|
+
</div>
|
|
73
|
+
`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
@Are.Data
|
|
77
|
+
data(@A_Inject(AreStore) store: AreStore): void {
|
|
78
|
+
store.set({
|
|
79
|
+
topLabel: 'Hello',
|
|
80
|
+
show: true,
|
|
81
|
+
items: [
|
|
82
|
+
{ id: 1, name: 'Alpha' },
|
|
83
|
+
{ id: 2, name: 'Beta' },
|
|
84
|
+
{ id: 3, name: 'Gamma' },
|
|
85
|
+
],
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
92
|
+
// ── Harness ──────────────────────────────────────────────────────────────────
|
|
93
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
94
|
+
|
|
95
|
+
const flush = () => new Promise<void>((r) => setTimeout(r, 0));
|
|
96
|
+
|
|
97
|
+
async function bootstrap(): Promise<A_Concept> {
|
|
98
|
+
// The engine context reads `container.body.innerHTML` as its source at
|
|
99
|
+
// construction time, so the markup must be in the DOM BEFORE we build it.
|
|
100
|
+
document.body.innerHTML = `<are-root id="app"><test-parent></test-parent></are-root>`;
|
|
101
|
+
|
|
102
|
+
const container = new AreContainer({
|
|
103
|
+
name: 'ARE Prop-Propagation Test',
|
|
104
|
+
components: [
|
|
105
|
+
TestParent,
|
|
106
|
+
TestChild,
|
|
107
|
+
AreDirectiveIf,
|
|
108
|
+
AreDirectiveFor,
|
|
109
|
+
A_SignalBus,
|
|
110
|
+
AreRoot,
|
|
111
|
+
ConfigReader,
|
|
112
|
+
AreHTMLEngine,
|
|
113
|
+
A_Logger,
|
|
114
|
+
],
|
|
115
|
+
entities: [AreInit, AreRoute],
|
|
116
|
+
fragments: [
|
|
117
|
+
new A_SignalState([AreRoute]),
|
|
118
|
+
new AreHTMLEngineContext({ container: document }),
|
|
119
|
+
new A_Config({
|
|
120
|
+
defaults: { [A_LOGGER_ENV_KEYS.LOG_LEVEL]: 'error' },
|
|
121
|
+
}),
|
|
122
|
+
],
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
const concept = new A_Concept({
|
|
126
|
+
name: 'adaas-are-html-prop-propagation-test',
|
|
127
|
+
fragments: [
|
|
128
|
+
new A_Config({
|
|
129
|
+
variables: ['CONFIG_VERBOSE', 'DEV_MODE'] as const,
|
|
130
|
+
defaults: { CONFIG_VERBOSE: false, DEV_MODE: false },
|
|
131
|
+
}),
|
|
132
|
+
],
|
|
133
|
+
components: [A_Logger, ConfigReader, A_Polyfill],
|
|
134
|
+
containers: [container],
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
await concept.load();
|
|
138
|
+
await concept.start();
|
|
139
|
+
await flush();
|
|
140
|
+
|
|
141
|
+
return concept;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
afterEach(() => {
|
|
145
|
+
A_Context.reset();
|
|
146
|
+
document.body.innerHTML = '';
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
151
|
+
// ── Tests ────────────────────────────────────────────────────────────────────
|
|
152
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
153
|
+
|
|
154
|
+
describe('Prop propagation — parent → child via :prop', () => {
|
|
155
|
+
|
|
156
|
+
it('passes a store-derived prop to a plain child', async () => {
|
|
157
|
+
await bootstrap();
|
|
158
|
+
|
|
159
|
+
const basic = document.querySelector('.basic .child');
|
|
160
|
+
expect(basic).not.toBeNull();
|
|
161
|
+
expect(basic!.textContent).toBe('Hello');
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('passes a prop to a child rendered inside an $if block', async () => {
|
|
165
|
+
await bootstrap();
|
|
166
|
+
|
|
167
|
+
const inIf = document.querySelector('.in-if .child');
|
|
168
|
+
expect(inIf).not.toBeNull();
|
|
169
|
+
expect(inIf!.textContent).toBe('Hello');
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it('passes the loop variable as a prop to children inside a $for', async () => {
|
|
173
|
+
await bootstrap();
|
|
174
|
+
|
|
175
|
+
const forChildren = Array.from(
|
|
176
|
+
document.querySelectorAll('.in-for .child'),
|
|
177
|
+
).map((el) => el.textContent);
|
|
178
|
+
|
|
179
|
+
expect(forChildren).toEqual(['Alpha', 'Beta', 'Gamma']);
|
|
180
|
+
});
|
|
181
|
+
});
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { isStaticMarkup, STANDARD_HTML_TAGS } from '../src/engine/AreHTML.constants';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Unit coverage for the static-island detector that drives the
|
|
5
|
+
* tokenizer fast-path (skip per-node explosion) and the AddStaticHTML
|
|
6
|
+
* one-shot materialisation. Correctness here is what guarantees we never
|
|
7
|
+
* collapse a subtree that actually needs reactive per-node handling.
|
|
8
|
+
*/
|
|
9
|
+
describe('isStaticMarkup — static-island detection', () => {
|
|
10
|
+
|
|
11
|
+
describe('returns TRUE for fully static content', () => {
|
|
12
|
+
it('plain text', () => {
|
|
13
|
+
expect(isStaticMarkup('Hello World')).toBe(true);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('text with HTML entities (the case)', () => {
|
|
17
|
+
expect(isStaticMarkup('Hello World')).toBe(true);
|
|
18
|
+
expect(isStaticMarkup('a & b   c A')).toBe(true);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('a single standard element with static attributes', () => {
|
|
22
|
+
expect(isStaticMarkup('<span class="x">hi</span>')).toBe(true);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('nested standard elements', () => {
|
|
26
|
+
expect(isStaticMarkup('<div class="card"><span>Hello World</span></div>')).toBe(true);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('static attribute value containing a colon (url / style)', () => {
|
|
30
|
+
expect(isStaticMarkup('<a href="https://example.com">x</a>')).toBe(true);
|
|
31
|
+
expect(isStaticMarkup('<span style="color:red;margin:0">x</span>')).toBe(true);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('static attribute value containing @ (email)', () => {
|
|
35
|
+
expect(isStaticMarkup('<span data-mail="a@b.com">x</span>')).toBe(true);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('void elements and self-closing tags', () => {
|
|
39
|
+
expect(isStaticMarkup('line<br/>break<hr>')).toBe(true);
|
|
40
|
+
expect(isStaticMarkup('<img src="x.png" alt="pic">')).toBe(true);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('table fragments', () => {
|
|
44
|
+
expect(isStaticMarkup('<tr><td>a</td><td>b</td></tr>')).toBe(true);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('html comments are inert', () => {
|
|
48
|
+
expect(isStaticMarkup('<span>x</span><!-- a note --><b>y</b>')).toBe(true);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('single-quoted static attributes', () => {
|
|
52
|
+
expect(isStaticMarkup("<div class='a b'>x</div>")).toBe(true);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe('returns FALSE for dynamic content', () => {
|
|
57
|
+
it('interpolations', () => {
|
|
58
|
+
expect(isStaticMarkup('<span>{{name}}</span>')).toBe(false);
|
|
59
|
+
expect(isStaticMarkup('Hello {{name}}')).toBe(false);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('binding attribute (:)', () => {
|
|
63
|
+
expect(isStaticMarkup('<div :class="x">y</div>')).toBe(false);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('event attribute (@)', () => {
|
|
67
|
+
expect(isStaticMarkup('<button @click="$do()">y</button>')).toBe(false);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('directive attribute ($)', () => {
|
|
71
|
+
expect(isStaticMarkup('<div $if="cond">y</div>')).toBe(false);
|
|
72
|
+
expect(isStaticMarkup('<li $for="x in items">y</li>')).toBe(false);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('dynamic attribute nested deep in an otherwise static tree', () => {
|
|
76
|
+
expect(isStaticMarkup('<div class="a"><span><b :title="t">x</b></span></div>')).toBe(false);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
describe('returns FALSE for non-standard tags (components / custom / svg)', () => {
|
|
81
|
+
it('custom component (kebab-case)', () => {
|
|
82
|
+
expect(isStaticMarkup('<dashboard-header></dashboard-header>')).toBe(false);
|
|
83
|
+
expect(isStaticMarkup('<div><the-card>x</the-card></div>')).toBe(false);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('are-root outlet', () => {
|
|
87
|
+
expect(isStaticMarkup('<are-root id="x"></are-root>')).toBe(false);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('svg elements (need namespace handling)', () => {
|
|
91
|
+
expect(isStaticMarkup('<svg><rect/></svg>')).toBe(false);
|
|
92
|
+
expect(isStaticMarkup('<div><path d="M0 0"/></div>')).toBe(false);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
describe('edge cases', () => {
|
|
97
|
+
it('empty / falsy content is not an island', () => {
|
|
98
|
+
expect(isStaticMarkup('')).toBe(false);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('unterminated tag bails to the safe path', () => {
|
|
102
|
+
expect(isStaticMarkup('<div class="x"')).toBe(false);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('whitespace-only content is treated as static text', () => {
|
|
106
|
+
expect(isStaticMarkup(' ')).toBe(true);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('STANDARD_HTML_TAGS excludes svg/component tags', () => {
|
|
110
|
+
expect(STANDARD_HTML_TAGS.has('div')).toBe(true);
|
|
111
|
+
expect(STANDARD_HTML_TAGS.has('svg')).toBe(false);
|
|
112
|
+
expect(STANDARD_HTML_TAGS.has('rect')).toBe(false);
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
});
|
package/tests/jest.setup.ts
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
// jsdom test environments do not expose Node's TextEncoder/TextDecoder globals,
|
|
2
|
+
// which the @adaas codec layer requires. Polyfill them when missing (no-op under
|
|
3
|
+
// the default `node` test environment where they already exist).
|
|
4
|
+
import { TextEncoder, TextDecoder } from 'util';
|
|
5
|
+
if (typeof (globalThis as any).TextEncoder === 'undefined') {
|
|
6
|
+
(globalThis as any).TextEncoder = TextEncoder;
|
|
7
|
+
}
|
|
8
|
+
if (typeof (globalThis as any).TextDecoder === 'undefined') {
|
|
9
|
+
(globalThis as any).TextDecoder = TextDecoder;
|
|
10
|
+
}
|
|
11
|
+
|
|
1
12
|
// import { A_Context } from '@adaas/a-concept/a-context';
|
|
2
13
|
// import fs from 'fs';
|
|
3
14
|
|