tutuca 0.9.27 → 0.9.28
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/README.md +4 -4
- package/dist/tutuca-cli.js +44 -45
- package/dist/tutuca-dev.js +44 -38
- package/dist/tutuca-dev.min.js +2 -2
- package/dist/tutuca-extra.js +44 -38
- package/dist/tutuca-extra.min.js +2 -2
- package/dist/tutuca.js +44 -38
- package/dist/tutuca.min.js +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ Zero-dependency batteries included SPA framework.
|
|
|
7
7
|
- **Fits in your head** (and the context window)
|
|
8
8
|
- **View source friendly** — step through the whole stack
|
|
9
9
|
- **As much HTML as possible, as little JS as needed**
|
|
10
|
-
- ~
|
|
10
|
+
- ~169KB minified, ~37KB brotli compressed
|
|
11
11
|
|
|
12
12
|
## Quick Start
|
|
13
13
|
|
|
@@ -64,17 +64,17 @@ Zero-dependency batteries included SPA framework.
|
|
|
64
64
|
Tutuca ships a single-file CLI (`dist/tutuca-cli.js`) for inspecting, linting,
|
|
65
65
|
documenting, and rendering components defined in an ES module. The module just
|
|
66
66
|
needs to export `getComponents()` and, for render-time commands, `getExamples()`
|
|
67
|
-
in the storybook shape `{ title, description?,
|
|
67
|
+
in the storybook shape `{ title, description?, items: [{ title, description?, value, view? }] }` (a single section, or an array of sections).
|
|
68
68
|
|
|
69
69
|
### Setup
|
|
70
70
|
|
|
71
71
|
```sh
|
|
72
|
-
npm install --save-dev tutuca
|
|
72
|
+
npm install --save-dev tutuca
|
|
73
73
|
# prettier is optional, only needed for --pretty
|
|
74
74
|
npm install --save-dev prettier
|
|
75
75
|
```
|
|
76
76
|
|
|
77
|
-
The package exposes `tutuca` via `bin`, so `npx tutuca` (or a global `npm i -g tutuca
|
|
77
|
+
The package exposes `tutuca` via `bin`, so `npx tutuca` (or a global `npm i -g tutuca`) just works. `jsdom` ships as a regular dependency (it's needed by `render`, `lint`, and `doctor`) and is installed automatically.
|
|
78
78
|
|
|
79
79
|
### Commands
|
|
80
80
|
|
package/dist/tutuca-cli.js
CHANGED
|
@@ -221,15 +221,11 @@ function normalizeModule(mod, { path = null } = {}) {
|
|
|
221
221
|
"getMacros",
|
|
222
222
|
"getRequestHandlers",
|
|
223
223
|
"getExamples",
|
|
224
|
-
"getStoryBookSection",
|
|
225
224
|
"getRoot"
|
|
226
225
|
]) {
|
|
227
226
|
if (typeof mod[key] === "function")
|
|
228
227
|
present.add(key);
|
|
229
228
|
}
|
|
230
|
-
if (present.has("getStoryBookSection") && !present.has("getExamples")) {
|
|
231
|
-
throw shapeError("module exports getStoryBookSection; rename it to getExamples.", "module");
|
|
232
|
-
}
|
|
233
229
|
const components = present.has("getComponents") ? mod.getComponents() : [];
|
|
234
230
|
const macros = present.has("getMacros") ? mod.getMacros() : null;
|
|
235
231
|
const requestHandlers = present.has("getRequestHandlers") ? mod.getRequestHandlers() : null;
|
|
@@ -1442,26 +1438,28 @@ class ParseContext {
|
|
|
1442
1438
|
onAttributes(_attrs, _wrapperAttrs, _textChild, _isMacroCall) {}
|
|
1443
1439
|
}
|
|
1444
1440
|
function condenseChildsWhites(childs) {
|
|
1445
|
-
|
|
1446
|
-
if (end === 0)
|
|
1441
|
+
if (childs.length === 0)
|
|
1447
1442
|
return childs;
|
|
1448
|
-
let start = 0;
|
|
1449
1443
|
let changed = false;
|
|
1450
|
-
if (
|
|
1451
|
-
|
|
1444
|
+
if (childs[0].isWhiteSpace?.()) {
|
|
1445
|
+
childs[0].condenseWhiteSpace();
|
|
1452
1446
|
changed = true;
|
|
1453
1447
|
}
|
|
1454
|
-
|
|
1455
|
-
|
|
1448
|
+
const last = childs.length - 1;
|
|
1449
|
+
if (last > 0 && childs[last].isWhiteSpace?.()) {
|
|
1450
|
+
childs[last].condenseWhiteSpace();
|
|
1456
1451
|
changed = true;
|
|
1457
1452
|
}
|
|
1458
|
-
for (let i = 1;i <
|
|
1453
|
+
for (let i = 1;i < last; i++) {
|
|
1459
1454
|
const cur = childs[i];
|
|
1460
|
-
if (
|
|
1461
|
-
|
|
1455
|
+
if (cur.isWhiteSpace?.() && cur.hasNewLine()) {
|
|
1456
|
+
const bothBlock = isBlockDomNode(childs[i - 1]) && isBlockDomNode(childs[i + 1]);
|
|
1457
|
+
cur.condenseWhiteSpace(bothBlock ? "" : " ");
|
|
1458
|
+
if (bothBlock)
|
|
1459
|
+
changed = true;
|
|
1462
1460
|
}
|
|
1463
1461
|
}
|
|
1464
|
-
return changed ? childs.
|
|
1462
|
+
return changed ? childs.filter((c) => !(c instanceof TextNode && c.val === "")) : childs;
|
|
1465
1463
|
}
|
|
1466
1464
|
|
|
1467
1465
|
class NodeEvents {
|
|
@@ -1517,7 +1515,10 @@ function compileModifiers(eventName, names) {
|
|
|
1517
1515
|
return w(this, f, args, ctx);
|
|
1518
1516
|
};
|
|
1519
1517
|
}
|
|
1520
|
-
var TextNode, CommentNode, ChildsNode, DomNode, FragmentNode, maybeFragment = (xs) => xs.length === 1 ? xs[0] : new FragmentNode(xs), VALID_NODE_RE, _parser = null, ANode, MacroNode, RenderViewId, RenderNode, RenderItNode, RenderEachNode, RenderTextNode, RenderOnceNode, WrapperNode, ShowNode, HideNode, PushViewNameNode, SlotNode, ScopeNode, EachNode, filterAlwaysTrue = (_v, _k, _seq) => true, nullLoopWith = (seq) => ({ seq }), WRAPPER_NODES,
|
|
1518
|
+
var TextNode, CommentNode, ChildsNode, DomNode, FragmentNode, maybeFragment = (xs) => xs.length === 1 ? xs[0] : new FragmentNode(xs), VALID_NODE_RE, _parser = null, ANode, MacroNode, RenderViewId, RenderNode, RenderItNode, RenderEachNode, RenderTextNode, RenderOnceNode, WrapperNode, ShowNode, HideNode, PushViewNameNode, SlotNode, ScopeNode, EachNode, filterAlwaysTrue = (_v, _k, _seq) => true, nullLoopWith = (seq) => ({ seq }), WRAPPER_NODES, _htmlBlockTags = "ADDRESS,ARTICLE,ASIDE,BLOCKQUOTE,CAPTION,COL,COLGROUP,DETAILS,DIALOG,DIV,DD,DL,DT,FIELDSET,FIGCAPTION,FIGURE,FOOTER,FORM,H1,H2,H3,H4,H5,H6,HEADER,HGROUP,HR,LEGEND,LI,MAIN,MENU,NAV,OL,P,PRE,SECTION,SUMMARY,TABLE,TBODY,TD,TFOOT,TH,THEAD,TR,UL", HTML_BLOCK_TAGS, isBlockDomNode = (n) => {
|
|
1519
|
+
const node = n instanceof FragmentNode ? n.childs[0] : n;
|
|
1520
|
+
return node instanceof DomNode && HTML_BLOCK_TAGS.has(node.tagName);
|
|
1521
|
+
}, isMac, fwdIfCtxPred = (pred) => (w) => (that, f, args, ctx) => pred(ctx) ? w(that, f, args, ctx) : that, fwdIfKey = (keyName) => fwdIfCtxPred((ctx) => ctx.e.key === keyName), fwdCtrl, fwdMeta, fwdAlt, metaWraps, MOD_WRAPPERS_BY_EVENT, identityModifierWrapper = (f, _ctx) => f;
|
|
1521
1522
|
var init_anode = __esm(() => {
|
|
1522
1523
|
init_attribute();
|
|
1523
1524
|
init_path();
|
|
@@ -1546,8 +1547,8 @@ var init_anode = __esm(() => {
|
|
|
1546
1547
|
}
|
|
1547
1548
|
return false;
|
|
1548
1549
|
}
|
|
1549
|
-
condenseWhiteSpace() {
|
|
1550
|
-
this.val =
|
|
1550
|
+
condenseWhiteSpace(replacement = "") {
|
|
1551
|
+
this.val = replacement;
|
|
1551
1552
|
}
|
|
1552
1553
|
isConstant() {
|
|
1553
1554
|
return true;
|
|
@@ -1664,7 +1665,7 @@ var init_anode = __esm(() => {
|
|
|
1664
1665
|
if (textChild)
|
|
1665
1666
|
childs.unshift(new RenderTextNode(null, textChild));
|
|
1666
1667
|
const domChilds = tag !== "PRE" ? condenseChildsWhites(childs) : childs;
|
|
1667
|
-
return wrap(new DomNode(tag
|
|
1668
|
+
return wrap(new DomNode(tag, nAttrs, domChilds), px, wrappers);
|
|
1668
1669
|
}
|
|
1669
1670
|
return new CommentNode(`Error: InvalidTagName ${tag}`);
|
|
1670
1671
|
}
|
|
@@ -1840,6 +1841,7 @@ var init_anode = __esm(() => {
|
|
|
1840
1841
|
scope: ScopeNode,
|
|
1841
1842
|
"push-view": PushViewNameNode
|
|
1842
1843
|
};
|
|
1844
|
+
HTML_BLOCK_TAGS = new Set(_htmlBlockTags.split(","));
|
|
1843
1845
|
isMac = (globalThis.navigator?.userAgent ?? "").toLowerCase().includes("mac");
|
|
1844
1846
|
fwdCtrl = fwdIfCtxPred(({ e }) => isMac && e.metaKey || e.ctrlKey);
|
|
1845
1847
|
fwdMeta = fwdIfCtxPred(({ e }) => e.metaKey);
|
|
@@ -2987,23 +2989,20 @@ function morphNode(domNode, source, target, opts) {
|
|
|
2987
2989
|
domNode.data = target.text;
|
|
2988
2990
|
return domNode;
|
|
2989
2991
|
}
|
|
2990
|
-
if (type === 1 && source.
|
|
2992
|
+
if (type === 1 && source.isSameKind(target)) {
|
|
2991
2993
|
const propsDiff = diffProps(source.attrs, target.attrs);
|
|
2992
2994
|
const isSelect = source.tag === "SELECT";
|
|
2993
2995
|
if (propsDiff) {
|
|
2994
2996
|
if (isSelect && "value" in propsDiff) {
|
|
2995
2997
|
const { value: _v, ...rest } = propsDiff;
|
|
2996
2998
|
applyProperties(domNode, rest, source.attrs);
|
|
2997
|
-
} else
|
|
2999
|
+
} else
|
|
2998
3000
|
applyProperties(domNode, propsDiff, source.attrs);
|
|
2999
|
-
}
|
|
3000
3001
|
}
|
|
3001
|
-
if (!target.attrs.dangerouslySetInnerHTML)
|
|
3002
|
+
if (!target.attrs.dangerouslySetInnerHTML)
|
|
3002
3003
|
morphChildren(domNode, source.childs, target.childs, opts);
|
|
3003
|
-
|
|
3004
|
-
if (isSelect && target.attrs.value !== undefined) {
|
|
3004
|
+
if (isSelect && target.attrs.value !== undefined)
|
|
3005
3005
|
applyProperties(domNode, { value: target.attrs.value }, source.attrs);
|
|
3006
|
-
}
|
|
3007
3006
|
return domNode;
|
|
3008
3007
|
}
|
|
3009
3008
|
if (type === 11) {
|
|
@@ -3082,23 +3081,20 @@ function morphChildren(parentDom, oldChilds, newChilds, opts) {
|
|
|
3082
3081
|
if (!used[i] && domNodes[i].parentNode === parentDom)
|
|
3083
3082
|
parentDom.removeChild(domNodes[i]);
|
|
3084
3083
|
}
|
|
3085
|
-
function render(vnode, container, options) {
|
|
3086
|
-
const cached = renderCache.get(container);
|
|
3084
|
+
function render(vnode, container, options, prev) {
|
|
3087
3085
|
const isFragment = vnode instanceof VFragment;
|
|
3088
|
-
if (
|
|
3089
|
-
const oldDom = isFragment ? container :
|
|
3090
|
-
const newDom = morphNode(oldDom,
|
|
3091
|
-
|
|
3092
|
-
return newDom;
|
|
3086
|
+
if (prev && prev.vnode instanceof VFragment === isFragment) {
|
|
3087
|
+
const oldDom = isFragment ? container : prev.dom;
|
|
3088
|
+
const newDom = morphNode(oldDom, prev.vnode, vnode, options);
|
|
3089
|
+
return { vnode, dom: isFragment ? container : newDom };
|
|
3093
3090
|
}
|
|
3094
|
-
renderCache.delete(container);
|
|
3095
3091
|
const domNode = vnode.toDom(options);
|
|
3096
3092
|
container.replaceChildren(domNode);
|
|
3097
|
-
|
|
3098
|
-
return domNode;
|
|
3093
|
+
return { vnode, dom: isFragment ? container : domNode };
|
|
3099
3094
|
}
|
|
3100
3095
|
function h(tagName, properties, children) {
|
|
3101
|
-
const
|
|
3096
|
+
const c = tagName.charCodeAt(0);
|
|
3097
|
+
const tag = c >= 97 && c <= 122 ? tagName.toUpperCase() : tagName;
|
|
3102
3098
|
const props = {};
|
|
3103
3099
|
let key, namespace;
|
|
3104
3100
|
if (properties) {
|
|
@@ -3126,7 +3122,7 @@ function h(tagName, properties, children) {
|
|
|
3126
3122
|
addChild(normalizedChildren, children);
|
|
3127
3123
|
return new VNode(tag, props, normalizedChildren, key, namespace);
|
|
3128
3124
|
}
|
|
3129
|
-
var isHtmlAttribute = (propName) => propName[4] === "-" && (propName[0] === "d" || propName[0] === "a"), isObject = (v) => v !== null && typeof v === "object", prototypesDiffer = (a, b) => Object.getPrototypeOf(a) !== Object.getPrototypeOf(b), getKey = (child) => child instanceof VNode ? child.key : undefined, isIterable = (obj) => obj != null && typeof obj !== "string" && typeof obj[Symbol.iterator] === "function", VText, VComment, VFragment, VNode
|
|
3125
|
+
var isHtmlAttribute = (propName) => propName[4] === "-" && (propName[0] === "d" || propName[0] === "a"), isObject = (v) => v !== null && typeof v === "object", prototypesDiffer = (a, b) => Object.getPrototypeOf(a) !== Object.getPrototypeOf(b), getKey = (child) => child instanceof VNode ? child.key : undefined, isIterable = (obj) => obj != null && typeof obj !== "string" && typeof obj[Symbol.iterator] === "function", VText, VComment, VFragment, VNode;
|
|
3130
3126
|
var init_vdom = __esm(() => {
|
|
3131
3127
|
VText = class VText extends VBase {
|
|
3132
3128
|
constructor(text) {
|
|
@@ -3190,10 +3186,13 @@ var init_vdom = __esm(() => {
|
|
|
3190
3186
|
get nodeType() {
|
|
3191
3187
|
return 1;
|
|
3192
3188
|
}
|
|
3189
|
+
isSameKind(other) {
|
|
3190
|
+
return this.tag === other.tag && this.namespace === other.namespace && this.key === other.key;
|
|
3191
|
+
}
|
|
3193
3192
|
isEqualTo(other) {
|
|
3194
3193
|
if (this === other)
|
|
3195
3194
|
return true;
|
|
3196
|
-
if (!(other instanceof VNode) || this.
|
|
3195
|
+
if (!(other instanceof VNode) || !this.isSameKind(other) || this.childs.length !== other.childs.length) {
|
|
3197
3196
|
return false;
|
|
3198
3197
|
}
|
|
3199
3198
|
if (this.attrs !== other.attrs) {
|
|
@@ -3221,7 +3220,6 @@ var init_vdom = __esm(() => {
|
|
|
3221
3220
|
return node;
|
|
3222
3221
|
}
|
|
3223
3222
|
};
|
|
3224
|
-
renderCache = new WeakMap;
|
|
3225
3223
|
});
|
|
3226
3224
|
|
|
3227
3225
|
// src/app.js
|
|
@@ -3244,6 +3242,7 @@ class App {
|
|
|
3244
3242
|
};
|
|
3245
3243
|
this._compiled = false;
|
|
3246
3244
|
this._renderOpts = { document: rootNode.ownerDocument };
|
|
3245
|
+
this._renderState = null;
|
|
3247
3246
|
}
|
|
3248
3247
|
get state() {
|
|
3249
3248
|
return this.transactor.state;
|
|
@@ -3350,7 +3349,10 @@ class App {
|
|
|
3350
3349
|
render() {
|
|
3351
3350
|
const root = this.state.val;
|
|
3352
3351
|
const stack = this.makeStack(root);
|
|
3353
|
-
|
|
3352
|
+
const { renderer, rootNode, _renderOpts, _renderState } = this;
|
|
3353
|
+
const newState = render(renderer.renderRoot(stack, root), rootNode, _renderOpts, _renderState);
|
|
3354
|
+
this._renderState = newState;
|
|
3355
|
+
return newState.dom;
|
|
3354
3356
|
}
|
|
3355
3357
|
onChange(callback) {
|
|
3356
3358
|
this.transactor.state.onChange(callback);
|
|
@@ -7721,7 +7723,7 @@ class Renderer {
|
|
|
7721
7723
|
renderToDOM(stack, val) {
|
|
7722
7724
|
const rootNode = document.createElement("div");
|
|
7723
7725
|
const rOpts = { document };
|
|
7724
|
-
render(h("
|
|
7726
|
+
render(h("DIV", null, [this.renderRoot(stack, val)]), rootNode, rOpts);
|
|
7725
7727
|
return rootNode.childNodes[0];
|
|
7726
7728
|
}
|
|
7727
7729
|
renderToString(stack, val, cleanAttrs = true) {
|
|
@@ -8069,9 +8071,6 @@ MODULE CONVENTION
|
|
|
8069
8071
|
|
|
8070
8072
|
export function getRoot() // optional; returned by info
|
|
8071
8073
|
|
|
8072
|
-
The legacy \`getStoryBookSection()\` name fails fast with
|
|
8073
|
-
EXAMPLES_SHAPE_MISMATCH — rename it to \`getExamples\`.
|
|
8074
|
-
|
|
8075
8074
|
COMMANDS (require <module-path>)
|
|
8076
8075
|
info
|
|
8077
8076
|
Summarize which getX() exports are present and count components,
|
package/dist/tutuca-dev.js
CHANGED
|
@@ -918,8 +918,8 @@ class TextNode extends BaseNode {
|
|
|
918
918
|
}
|
|
919
919
|
return false;
|
|
920
920
|
}
|
|
921
|
-
condenseWhiteSpace() {
|
|
922
|
-
this.val =
|
|
921
|
+
condenseWhiteSpace(replacement = "") {
|
|
922
|
+
this.val = replacement;
|
|
923
923
|
}
|
|
924
924
|
isConstant() {
|
|
925
925
|
return true;
|
|
@@ -1058,7 +1058,7 @@ class ANode extends BaseNode {
|
|
|
1058
1058
|
if (textChild)
|
|
1059
1059
|
childs.unshift(new RenderTextNode(null, textChild));
|
|
1060
1060
|
const domChilds = tag !== "PRE" ? condenseChildsWhites(childs) : childs;
|
|
1061
|
-
return wrap(new DomNode(tag
|
|
1061
|
+
return wrap(new DomNode(tag, nAttrs, domChilds), px, wrappers);
|
|
1062
1062
|
}
|
|
1063
1063
|
return new CommentNode(`Error: InvalidTagName ${tag}`);
|
|
1064
1064
|
}
|
|
@@ -1365,29 +1365,35 @@ class ParseContext {
|
|
|
1365
1365
|
}
|
|
1366
1366
|
onAttributes(_attrs, _wrapperAttrs, _textChild, _isMacroCall) {}
|
|
1367
1367
|
}
|
|
1368
|
-
var
|
|
1369
|
-
var
|
|
1368
|
+
var _htmlBlockTags = "ADDRESS,ARTICLE,ASIDE,BLOCKQUOTE,CAPTION,COL,COLGROUP,DETAILS,DIALOG,DIV,DD,DL,DT,FIELDSET,FIGCAPTION,FIGURE,FOOTER,FORM,H1,H2,H3,H4,H5,H6,HEADER,HGROUP,HR,LEGEND,LI,MAIN,MENU,NAV,OL,P,PRE,SECTION,SUMMARY,TABLE,TBODY,TD,TFOOT,TH,THEAD,TR,UL";
|
|
1369
|
+
var HTML_BLOCK_TAGS = new Set(_htmlBlockTags.split(","));
|
|
1370
|
+
var isBlockDomNode = (n) => {
|
|
1371
|
+
const node = n instanceof FragmentNode ? n.childs[0] : n;
|
|
1372
|
+
return node instanceof DomNode && HTML_BLOCK_TAGS.has(node.tagName);
|
|
1373
|
+
};
|
|
1370
1374
|
function condenseChildsWhites(childs) {
|
|
1371
|
-
|
|
1372
|
-
if (end === 0)
|
|
1375
|
+
if (childs.length === 0)
|
|
1373
1376
|
return childs;
|
|
1374
|
-
let start = 0;
|
|
1375
1377
|
let changed = false;
|
|
1376
|
-
if (
|
|
1377
|
-
|
|
1378
|
+
if (childs[0].isWhiteSpace?.()) {
|
|
1379
|
+
childs[0].condenseWhiteSpace();
|
|
1378
1380
|
changed = true;
|
|
1379
1381
|
}
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
+
const last = childs.length - 1;
|
|
1383
|
+
if (last > 0 && childs[last].isWhiteSpace?.()) {
|
|
1384
|
+
childs[last].condenseWhiteSpace();
|
|
1382
1385
|
changed = true;
|
|
1383
1386
|
}
|
|
1384
|
-
for (let i = 1;i <
|
|
1387
|
+
for (let i = 1;i < last; i++) {
|
|
1385
1388
|
const cur = childs[i];
|
|
1386
|
-
if (
|
|
1387
|
-
|
|
1389
|
+
if (cur.isWhiteSpace?.() && cur.hasNewLine()) {
|
|
1390
|
+
const bothBlock = isBlockDomNode(childs[i - 1]) && isBlockDomNode(childs[i + 1]);
|
|
1391
|
+
cur.condenseWhiteSpace(bothBlock ? "" : " ");
|
|
1392
|
+
if (bothBlock)
|
|
1393
|
+
changed = true;
|
|
1388
1394
|
}
|
|
1389
1395
|
}
|
|
1390
|
-
return changed ? childs.
|
|
1396
|
+
return changed ? childs.filter((c) => !(c instanceof TextNode && c.val === "")) : childs;
|
|
1391
1397
|
}
|
|
1392
1398
|
|
|
1393
1399
|
class View {
|
|
@@ -2824,10 +2830,13 @@ class VNode extends VBase {
|
|
|
2824
2830
|
get nodeType() {
|
|
2825
2831
|
return 1;
|
|
2826
2832
|
}
|
|
2833
|
+
isSameKind(other) {
|
|
2834
|
+
return this.tag === other.tag && this.namespace === other.namespace && this.key === other.key;
|
|
2835
|
+
}
|
|
2827
2836
|
isEqualTo(other) {
|
|
2828
2837
|
if (this === other)
|
|
2829
2838
|
return true;
|
|
2830
|
-
if (!(other instanceof VNode) || this.
|
|
2839
|
+
if (!(other instanceof VNode) || !this.isSameKind(other) || this.childs.length !== other.childs.length) {
|
|
2831
2840
|
return false;
|
|
2832
2841
|
}
|
|
2833
2842
|
if (this.attrs !== other.attrs) {
|
|
@@ -2902,23 +2911,20 @@ function morphNode(domNode, source, target, opts) {
|
|
|
2902
2911
|
domNode.data = target.text;
|
|
2903
2912
|
return domNode;
|
|
2904
2913
|
}
|
|
2905
|
-
if (type === 1 && source.
|
|
2914
|
+
if (type === 1 && source.isSameKind(target)) {
|
|
2906
2915
|
const propsDiff = diffProps(source.attrs, target.attrs);
|
|
2907
2916
|
const isSelect = source.tag === "SELECT";
|
|
2908
2917
|
if (propsDiff) {
|
|
2909
2918
|
if (isSelect && "value" in propsDiff) {
|
|
2910
2919
|
const { value: _v, ...rest } = propsDiff;
|
|
2911
2920
|
applyProperties(domNode, rest, source.attrs);
|
|
2912
|
-
} else
|
|
2921
|
+
} else
|
|
2913
2922
|
applyProperties(domNode, propsDiff, source.attrs);
|
|
2914
|
-
}
|
|
2915
2923
|
}
|
|
2916
|
-
if (!target.attrs.dangerouslySetInnerHTML)
|
|
2924
|
+
if (!target.attrs.dangerouslySetInnerHTML)
|
|
2917
2925
|
morphChildren(domNode, source.childs, target.childs, opts);
|
|
2918
|
-
|
|
2919
|
-
if (isSelect && target.attrs.value !== undefined) {
|
|
2926
|
+
if (isSelect && target.attrs.value !== undefined)
|
|
2920
2927
|
applyProperties(domNode, { value: target.attrs.value }, source.attrs);
|
|
2921
|
-
}
|
|
2922
2928
|
return domNode;
|
|
2923
2929
|
}
|
|
2924
2930
|
if (type === 11) {
|
|
@@ -2997,24 +3003,20 @@ function morphChildren(parentDom, oldChilds, newChilds, opts) {
|
|
|
2997
3003
|
if (!used[i] && domNodes[i].parentNode === parentDom)
|
|
2998
3004
|
parentDom.removeChild(domNodes[i]);
|
|
2999
3005
|
}
|
|
3000
|
-
|
|
3001
|
-
function render(vnode, container, options) {
|
|
3002
|
-
const cached = renderCache.get(container);
|
|
3006
|
+
function render(vnode, container, options, prev) {
|
|
3003
3007
|
const isFragment = vnode instanceof VFragment;
|
|
3004
|
-
if (
|
|
3005
|
-
const oldDom = isFragment ? container :
|
|
3006
|
-
const newDom = morphNode(oldDom,
|
|
3007
|
-
|
|
3008
|
-
return newDom;
|
|
3008
|
+
if (prev && prev.vnode instanceof VFragment === isFragment) {
|
|
3009
|
+
const oldDom = isFragment ? container : prev.dom;
|
|
3010
|
+
const newDom = morphNode(oldDom, prev.vnode, vnode, options);
|
|
3011
|
+
return { vnode, dom: isFragment ? container : newDom };
|
|
3009
3012
|
}
|
|
3010
|
-
renderCache.delete(container);
|
|
3011
3013
|
const domNode = vnode.toDom(options);
|
|
3012
3014
|
container.replaceChildren(domNode);
|
|
3013
|
-
|
|
3014
|
-
return domNode;
|
|
3015
|
+
return { vnode, dom: isFragment ? container : domNode };
|
|
3015
3016
|
}
|
|
3016
3017
|
function h(tagName, properties, children) {
|
|
3017
|
-
const
|
|
3018
|
+
const c = tagName.charCodeAt(0);
|
|
3019
|
+
const tag = c >= 97 && c <= 122 ? tagName.toUpperCase() : tagName;
|
|
3018
3020
|
const props = {};
|
|
3019
3021
|
let key, namespace;
|
|
3020
3022
|
if (properties) {
|
|
@@ -3065,6 +3067,7 @@ class App {
|
|
|
3065
3067
|
};
|
|
3066
3068
|
this._compiled = false;
|
|
3067
3069
|
this._renderOpts = { document: rootNode.ownerDocument };
|
|
3070
|
+
this._renderState = null;
|
|
3068
3071
|
}
|
|
3069
3072
|
get state() {
|
|
3070
3073
|
return this.transactor.state;
|
|
@@ -3171,7 +3174,10 @@ class App {
|
|
|
3171
3174
|
render() {
|
|
3172
3175
|
const root = this.state.val;
|
|
3173
3176
|
const stack = this.makeStack(root);
|
|
3174
|
-
|
|
3177
|
+
const { renderer, rootNode, _renderOpts, _renderState } = this;
|
|
3178
|
+
const newState = render(renderer.renderRoot(stack, root), rootNode, _renderOpts, _renderState);
|
|
3179
|
+
this._renderState = newState;
|
|
3180
|
+
return newState.dom;
|
|
3175
3181
|
}
|
|
3176
3182
|
onChange(callback) {
|
|
3177
3183
|
this.transactor.state.onChange(callback);
|
|
@@ -8093,7 +8099,7 @@ class Renderer {
|
|
|
8093
8099
|
renderToDOM(stack, val) {
|
|
8094
8100
|
const rootNode = document.createElement("div");
|
|
8095
8101
|
const rOpts = { document };
|
|
8096
|
-
render(h("
|
|
8102
|
+
render(h("DIV", null, [this.renderRoot(stack, val)]), rootNode, rOpts);
|
|
8097
8103
|
return rootNode.childNodes[0];
|
|
8098
8104
|
}
|
|
8099
8105
|
renderToString(stack, val, cleanAttrs = true) {
|