@praxisjs/runtime 0.7.1 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +33 -0
- package/dist/children.d.ts.map +1 -1
- package/dist/children.js +2 -1
- package/dist/children.js.map +1 -1
- package/dist/component.d.ts +2 -3
- package/dist/component.d.ts.map +1 -1
- package/dist/component.js +133 -60
- package/dist/component.js.map +1 -1
- package/dist/context.d.ts +13 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +24 -2
- package/dist/context.js.map +1 -1
- package/dist/dom/constants.d.ts +1 -1
- package/dist/dom/constants.d.ts.map +1 -1
- package/dist/dom/constants.js +24 -83
- package/dist/dom/constants.js.map +1 -1
- package/dist/dom/create.d.ts +1 -1
- package/dist/dom/create.d.ts.map +1 -1
- package/dist/dom/create.js +3 -3
- package/dist/dom/create.js.map +1 -1
- package/dist/dom/events.d.ts +28 -0
- package/dist/dom/events.d.ts.map +1 -1
- package/dist/dom/events.js +163 -17
- package/dist/dom/events.js.map +1 -1
- package/dist/dom/props.d.ts +4 -0
- package/dist/dom/props.d.ts.map +1 -1
- package/dist/dom/props.js +55 -7
- package/dist/dom/props.js.map +1 -1
- package/dist/dom/range.d.ts +4 -0
- package/dist/dom/range.d.ts.map +1 -0
- package/dist/dom/range.js +43 -0
- package/dist/dom/range.js.map +1 -0
- package/dist/dom/remove.d.ts.map +1 -1
- package/dist/dom/remove.js +2 -8
- package/dist/dom/remove.js.map +1 -1
- package/dist/element.d.ts.map +1 -1
- package/dist/element.js +7 -5
- package/dist/element.js.map +1 -1
- package/dist/hmr.d.ts +35 -0
- package/dist/hmr.d.ts.map +1 -0
- package/dist/hmr.js +85 -0
- package/dist/hmr.js.map +1 -0
- package/dist/hydration-context.d.ts +9 -1
- package/dist/hydration-context.d.ts.map +1 -1
- package/dist/hydration-context.js +34 -13
- package/dist/hydration-context.js.map +1 -1
- package/dist/hydration.d.ts.map +1 -1
- package/dist/hydration.js +4 -11
- package/dist/hydration.js.map +1 -1
- package/dist/index.d.ts +16 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +31 -7
- package/dist/index.js.map +1 -1
- package/dist/list.d.ts +72 -0
- package/dist/list.d.ts.map +1 -0
- package/dist/list.js +786 -0
- package/dist/list.js.map +1 -0
- package/dist/portal.d.ts.map +1 -1
- package/dist/portal.js +3 -0
- package/dist/portal.js.map +1 -1
- package/dist/reactive.d.ts +2 -2
- package/dist/reactive.d.ts.map +1 -1
- package/dist/reactive.js +109 -34
- package/dist/reactive.js.map +1 -1
- package/dist/scope.d.ts +14 -4
- package/dist/scope.d.ts.map +1 -1
- package/dist/scope.js +55 -15
- package/dist/scope.js.map +1 -1
- package/dist/template.d.ts +48 -0
- package/dist/template.d.ts.map +1 -0
- package/dist/template.js +270 -0
- package/dist/template.js.map +1 -0
- package/package.json +15 -6
- package/src/children.ts +2 -1
- package/src/component.ts +135 -63
- package/src/context.ts +35 -4
- package/src/dom/constants.ts +25 -82
- package/src/dom/create.ts +3 -3
- package/src/dom/events.ts +184 -16
- package/src/dom/props.ts +65 -8
- package/src/dom/range.ts +45 -0
- package/src/dom/remove.ts +3 -7
- package/src/element.ts +8 -6
- package/src/hmr.ts +110 -0
- package/src/hydration-context.ts +34 -13
- package/src/hydration.ts +4 -9
- package/src/index.ts +54 -7
- package/src/list.ts +886 -0
- package/src/portal.ts +3 -0
- package/src/reactive.ts +147 -40
- package/src/scope.ts +63 -16
- package/src/template.ts +271 -0
- package/dist/__tests__/children.test.d.ts +0 -2
- package/dist/__tests__/children.test.d.ts.map +0 -1
- package/dist/__tests__/children.test.js +0 -241
- package/dist/__tests__/children.test.js.map +0 -1
- package/dist/__tests__/component.test.d.ts +0 -2
- package/dist/__tests__/component.test.d.ts.map +0 -1
- package/dist/__tests__/component.test.js +0 -347
- package/dist/__tests__/component.test.js.map +0 -1
- package/dist/__tests__/context.test.d.ts +0 -2
- package/dist/__tests__/context.test.d.ts.map +0 -1
- package/dist/__tests__/context.test.js +0 -111
- package/dist/__tests__/context.test.js.map +0 -1
- package/dist/__tests__/dom.test.d.ts +0 -2
- package/dist/__tests__/dom.test.d.ts.map +0 -1
- package/dist/__tests__/dom.test.js +0 -429
- package/dist/__tests__/dom.test.js.map +0 -1
- package/dist/__tests__/hydration.test.d.ts +0 -2
- package/dist/__tests__/hydration.test.d.ts.map +0 -1
- package/dist/__tests__/hydration.test.js +0 -299
- package/dist/__tests__/hydration.test.js.map +0 -1
- package/dist/__tests__/portal.test.d.ts +0 -2
- package/dist/__tests__/portal.test.d.ts.map +0 -1
- package/dist/__tests__/portal.test.js +0 -157
- package/dist/__tests__/portal.test.js.map +0 -1
- package/dist/__tests__/remove.test.d.ts +0 -2
- package/dist/__tests__/remove.test.d.ts.map +0 -1
- package/dist/__tests__/remove.test.js +0 -45
- package/dist/__tests__/remove.test.js.map +0 -1
- package/dist/__tests__/render.test.d.ts +0 -2
- package/dist/__tests__/render.test.d.ts.map +0 -1
- package/dist/__tests__/render.test.js +0 -97
- package/dist/__tests__/render.test.js.map +0 -1
- package/dist/__tests__/scope.test.d.ts +0 -2
- package/dist/__tests__/scope.test.d.ts.map +0 -1
- package/dist/__tests__/scope.test.js +0 -110
- package/dist/__tests__/scope.test.js.map +0 -1
- package/src/__tests__/children.test.ts +0 -276
- package/src/__tests__/component.test.ts +0 -381
- package/src/__tests__/context.test.ts +0 -128
- package/src/__tests__/dom.test.ts +0 -483
- package/src/__tests__/hydration.test.ts +0 -360
- package/src/__tests__/portal.test.ts +0 -177
- package/src/__tests__/remove.test.ts +0 -51
- package/src/__tests__/render.test.ts +0 -119
- package/src/__tests__/scope.test.ts +0 -120
- package/tsconfig.json +0 -8
package/src/hydration-context.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import type { Scope } from "./scope";
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* True during a hydration build pass (see runInRecording). Exported as a live binding so the hot
|
|
5
|
+
* render paths read a variable instead of calling isRecording() for every binding.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export let recording = false;
|
|
4
9
|
|
|
5
10
|
/**
|
|
6
11
|
* Marks the synchronous "build" pass of a hydration attempt: `factory()` runs
|
|
@@ -11,17 +16,17 @@ let _recording = false;
|
|
|
11
16
|
* element instead of the fresh one. No-op / zero cost outside a build pass.
|
|
12
17
|
*/
|
|
13
18
|
export function runInRecording<T>(fn: () => T): T {
|
|
14
|
-
const prev =
|
|
15
|
-
|
|
19
|
+
const prev = recording;
|
|
20
|
+
recording = true;
|
|
16
21
|
try {
|
|
17
22
|
return fn();
|
|
18
23
|
} finally {
|
|
19
|
-
|
|
24
|
+
recording = prev;
|
|
20
25
|
}
|
|
21
26
|
}
|
|
22
27
|
|
|
23
28
|
export function isRecording(): boolean {
|
|
24
|
-
return
|
|
29
|
+
return recording;
|
|
25
30
|
}
|
|
26
31
|
|
|
27
32
|
interface PropRecord {
|
|
@@ -35,19 +40,35 @@ export function recordProps(el: Element, props: Record<string, unknown>, scope:
|
|
|
35
40
|
_propRecords.set(el, { props, scope });
|
|
36
41
|
}
|
|
37
42
|
|
|
43
|
+
/** Adds one prop to an element's record, for elements whose props are applied one at a time. */
|
|
44
|
+
export function recordProp(el: Element, key: string, value: unknown, scope: Scope): void {
|
|
45
|
+
const record = _propRecords.get(el);
|
|
46
|
+
if (record === undefined) _propRecords.set(el, { props: { [key]: value }, scope });
|
|
47
|
+
else record.props[key] = value;
|
|
48
|
+
}
|
|
49
|
+
|
|
38
50
|
export function getRecordedProps(el: Element): PropRecord | undefined {
|
|
39
51
|
return _propRecords.get(el);
|
|
40
52
|
}
|
|
41
53
|
|
|
42
|
-
//
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
|
|
54
|
+
// Code that keeps references to fresh nodes for its *next* update (mountReactive's
|
|
55
|
+
// `currentNodes`, a list row's boundaries) registers how to repoint them, so reconcile()
|
|
56
|
+
// can tell it when it keeps a real element in place of a fresh one. One node can have several
|
|
57
|
+
// owners (a list row's element is also part of the nodes an enclosing reactive child returned).
|
|
58
|
+
const _replacers = new WeakMap<Node, (real: Node) => void>();
|
|
46
59
|
|
|
47
|
-
export function
|
|
48
|
-
|
|
60
|
+
export function onHydrationReplace(node: Node, replace: (real: Node) => void): void {
|
|
61
|
+
const prev = _replacers.get(node);
|
|
62
|
+
_replacers.set(node, prev === undefined ? replace : (real) => { prev(real); replace(real); });
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function getHydrationReplacer(node: Node): ((real: Node) => void) | undefined {
|
|
66
|
+
return _replacers.get(node);
|
|
49
67
|
}
|
|
50
68
|
|
|
51
|
-
export function
|
|
52
|
-
|
|
69
|
+
export function registerReactiveNode(node: Node, owner: Node[]): void {
|
|
70
|
+
onHydrationReplace(node, (real) => {
|
|
71
|
+
const idx = owner.indexOf(node);
|
|
72
|
+
if (idx !== -1) owner[idx] = real;
|
|
73
|
+
});
|
|
53
74
|
}
|
package/src/hydration.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { applyProp } from "./dom/props";
|
|
2
|
-
import {
|
|
2
|
+
import { getHydrationReplacer, getRecordedProps } from "./hydration-context";
|
|
3
3
|
|
|
4
4
|
function replayProps(real: Element, fresh: Element): void {
|
|
5
5
|
const record = getRecordedProps(fresh);
|
|
@@ -10,15 +10,10 @@ function replayProps(real: Element, fresh: Element): void {
|
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
// mountReactive()'s `currentNodes`
|
|
14
|
-
// *next*
|
|
15
|
-
// needs to see the swap too — it's registered by reference, so this mutation
|
|
16
|
-
// is visible to whichever effect closure owns it.
|
|
13
|
+
// mountReactive()'s `currentNodes` and list rows keep fresh node references for their
|
|
14
|
+
// *next* update. If we're keeping `real` instead of `fresh` here, they need to see the swap.
|
|
17
15
|
function notifyReplacement(fresh: Node, real: Node): void {
|
|
18
|
-
|
|
19
|
-
if (!owner) return;
|
|
20
|
-
const idx = owner.indexOf(fresh);
|
|
21
|
-
if (idx !== -1) owner[idx] = real;
|
|
16
|
+
getHydrationReplacer(fresh)?.(real);
|
|
22
17
|
}
|
|
23
18
|
|
|
24
19
|
/**
|
package/src/index.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { mountChildren } from "./children";
|
|
2
2
|
import { runInScope } from "./context";
|
|
3
|
+
import { addEventRoot } from "./dom/events";
|
|
3
4
|
import { reconcile } from "./hydration";
|
|
4
5
|
import { runInRecording } from "./hydration-context";
|
|
5
6
|
import { Scope } from "./scope";
|
|
7
|
+
import { syncTemplateDocument } from "./template";
|
|
6
8
|
|
|
7
9
|
/** Written by `@praxisjs/ssg` onto the mount container in its prerendered HTML. */
|
|
8
10
|
const SSG_MARKER = "data-praxis-ssg";
|
|
@@ -11,8 +13,10 @@ function mountCreate(
|
|
|
11
13
|
factory: () => Node | Node[] | null,
|
|
12
14
|
container: HTMLElement,
|
|
13
15
|
): () => void {
|
|
16
|
+
syncTemplateDocument();
|
|
14
17
|
const rootScope = new Scope();
|
|
15
18
|
container.innerHTML = "";
|
|
19
|
+
const removeEventRoot = addEventRoot(container);
|
|
16
20
|
|
|
17
21
|
runInScope(rootScope, () => {
|
|
18
22
|
const result = factory();
|
|
@@ -21,6 +25,7 @@ function mountCreate(
|
|
|
21
25
|
|
|
22
26
|
return () => {
|
|
23
27
|
rootScope.dispose();
|
|
28
|
+
removeEventRoot();
|
|
24
29
|
container.innerHTML = "";
|
|
25
30
|
};
|
|
26
31
|
}
|
|
@@ -29,6 +34,7 @@ function hydrate(
|
|
|
29
34
|
factory: () => Node | Node[] | null,
|
|
30
35
|
container: HTMLElement,
|
|
31
36
|
): () => void {
|
|
37
|
+
syncTemplateDocument();
|
|
32
38
|
const rootScope = new Scope();
|
|
33
39
|
const scratch = document.createDocumentFragment();
|
|
34
40
|
|
|
@@ -44,24 +50,40 @@ function hydrate(
|
|
|
44
50
|
});
|
|
45
51
|
|
|
46
52
|
reconcile(container, Array.from(scratch.childNodes));
|
|
53
|
+
const removeEventRoot = addEventRoot(container);
|
|
47
54
|
|
|
48
55
|
return () => {
|
|
49
56
|
rootScope.dispose();
|
|
57
|
+
removeEventRoot();
|
|
50
58
|
container.innerHTML = "";
|
|
51
59
|
};
|
|
52
60
|
}
|
|
53
61
|
|
|
62
|
+
// Reached only through enableHydration(), so an app that never calls it leaves the reconciliation
|
|
63
|
+
// and every recording branch of the render paths out of its bundle.
|
|
64
|
+
let hydrateRoot: typeof hydrate | null = null;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Lets render() hydrate the HTML `@praxisjs/ssg` prerendered. `ssgPlugin()` calls it at the start of
|
|
68
|
+
* the client bundle, so apps it builds need nothing more; a page prerendered another way calls it
|
|
69
|
+
* once before render(). Without it, a container carrying the hydration marker is cleared and
|
|
70
|
+
* mounted fresh.
|
|
71
|
+
*/
|
|
72
|
+
export function enableHydration(): void {
|
|
73
|
+
hydrateRoot = hydrate;
|
|
74
|
+
}
|
|
75
|
+
|
|
54
76
|
/**
|
|
55
77
|
* Renders a component tree into a container element.
|
|
56
78
|
*
|
|
57
79
|
* The factory function is called once inside the root scope,
|
|
58
80
|
* so `jsx()` can access the current scope via `getCurrentScope()`.
|
|
59
81
|
*
|
|
60
|
-
*
|
|
61
|
-
* HTML
|
|
62
|
-
* reconciled against the existing DOM (real elements are kept and have their
|
|
63
|
-
* props/listeners replayed onto them; text/comments and any mismatched nodes
|
|
64
|
-
*
|
|
82
|
+
* Once hydration is enabled (see `enableHydration()`), a `container` carrying the marker
|
|
83
|
+
* `@praxisjs/ssg` stamps onto prerendered HTML has its tree built normally into a detached
|
|
84
|
+
* scratch node and then reconciled against the existing DOM (real elements are kept and have their
|
|
85
|
+
* props/listeners replayed onto them; text/comments and any mismatched nodes are recreated)
|
|
86
|
+
* instead of being cleared and mounted fresh.
|
|
65
87
|
*
|
|
66
88
|
* @example
|
|
67
89
|
* render(() => <App />, document.getElementById('app'));
|
|
@@ -70,8 +92,8 @@ export function render(
|
|
|
70
92
|
factory: () => Node | Node[] | null,
|
|
71
93
|
container: HTMLElement,
|
|
72
94
|
): () => void {
|
|
73
|
-
if (container.hasAttribute(SSG_MARKER)) {
|
|
74
|
-
return
|
|
95
|
+
if (hydrateRoot !== null && container.hasAttribute(SSG_MARKER)) {
|
|
96
|
+
return hydrateRoot(factory, container);
|
|
75
97
|
}
|
|
76
98
|
return mountCreate(factory, container);
|
|
77
99
|
}
|
|
@@ -79,5 +101,30 @@ export function render(
|
|
|
79
101
|
export { Scope } from "./scope";
|
|
80
102
|
export { runInScope, getCurrentScope } from "./context";
|
|
81
103
|
export { mountElement } from "./element";
|
|
104
|
+
export { mountChildren } from "./children";
|
|
105
|
+
export {
|
|
106
|
+
template as _$template,
|
|
107
|
+
html as _$html,
|
|
108
|
+
element as _$element,
|
|
109
|
+
prop as _$prop,
|
|
110
|
+
reactive as _$reactive,
|
|
111
|
+
insert as _$insert,
|
|
112
|
+
reactiveOnly as _$reactiveOnly,
|
|
113
|
+
insertOnly as _$insertOnly,
|
|
114
|
+
on as _$on,
|
|
115
|
+
event as _$event,
|
|
116
|
+
onEvent as _$onEvent,
|
|
117
|
+
className as _$className,
|
|
118
|
+
scope as _$scope,
|
|
119
|
+
} from "./template";
|
|
82
120
|
export { mountComponent } from "./component";
|
|
121
|
+
export { registerHot as _$hmrRegister, acceptsHotUpdate as _$hmrAccepts } from "./hmr";
|
|
83
122
|
export { Portal, type PortalProps } from "./portal";
|
|
123
|
+
export {
|
|
124
|
+
For,
|
|
125
|
+
Index,
|
|
126
|
+
type ForProps,
|
|
127
|
+
type IndexProps,
|
|
128
|
+
type ListSource,
|
|
129
|
+
type ListTransition,
|
|
130
|
+
} from "./list";
|