@webjsdev/core 0.7.50 → 0.7.51
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/webjs-core-browser.js +5 -5
- package/dist/webjs-core-browser.js.map +3 -3
- package/dist/webjs-core-testing.js +20 -20
- package/dist/webjs-core-testing.js.map +3 -3
- package/dist/webjs-core.js +7 -7
- package/dist/webjs-core.js.map +3 -3
- package/index-browser.js +1 -1
- package/index.d.ts +1 -1
- package/index.js +1 -1
- package/package.json +1 -1
- package/src/component/lifecycle.js +1481 -0
- package/src/component/reactive.js +171 -0
- package/src/component/server-element.js +248 -0
- package/src/component.js +2 -1896
- package/src/render-client/parts.js +2007 -0
- package/src/render-client/reconciler.js +388 -0
- package/src/render-client/template-compiler.js +525 -0
- package/src/render-client.js +1 -2938
- package/src/render-server/attrs.js +160 -0
- package/src/render-server/dsd.js +445 -0
- package/src/render-server/html-scan.js +402 -0
- package/src/render-server/slots.js +213 -0
- package/src/render-server/stream.js +163 -0
- package/src/render-server/template-renderer.js +923 -0
- package/src/render-server/text.js +163 -0
- package/src/render-server.js +6 -2405
- package/src/router-client/anchors.js +44 -0
- package/src/router-client/boundaries.js +221 -0
- package/src/router-client/constants.js +187 -0
- package/src/router-client/diagnostics.js +377 -0
- package/src/router-client/dom-differ.js +709 -0
- package/src/router-client/dom-parse.js +150 -0
- package/src/router-client/events.js +139 -0
- package/src/router-client/fetch-apply.js +387 -0
- package/src/router-client/form-encoder.js +135 -0
- package/src/router-client/frames.js +329 -0
- package/src/router-client/head-merge.js +344 -0
- package/src/router-client/nav-error.js +105 -0
- package/src/router-client/navigator.js +781 -0
- package/src/router-client/prefetch.js +824 -0
- package/src/router-client/scroll.js +259 -0
- package/src/router-client/snapshot-cache.js +97 -0
- package/src/router-client/state.js +92 -0
- package/src/router-client/stream.js +206 -0
- package/src/router-client/swap.js +451 -0
- package/src/router-client/upgrade.js +261 -0
- package/src/router-client/view-transition.js +223 -0
- package/src/router-client.d.ts +10 -1
- package/src/router-client.js +75 -5345
- package/src/slot/assignment.js +304 -0
- package/src/slot/interception.js +558 -0
- package/src/slot/polyfills.js +365 -0
- package/src/slot/project.js +432 -0
- package/src/slot/sensors.js +303 -0
- package/src/slot/state.js +245 -0
- package/src/slot/symbols.js +85 -0
- package/src/slot.js +7 -2199
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import NAMED_ENTITIES, { LEGACY_NAMES } from '../html-entities.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Name-case conversion and HTML character-reference decoding.
|
|
5
|
+
*
|
|
6
|
+
* A leaf like `html-scan.js`, and for the same reason: `template-renderer.js`
|
|
7
|
+
* needs `kebabCase` to serialize property-binding names, and taking it from
|
|
8
|
+
* here rather than from `dsd.js` is half of what keeps the two acyclic.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/** @param {string} s */
|
|
12
|
+
export function camelCase(s) {
|
|
13
|
+
return s.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Inverse of camelCase. `userName` -> `user-name`, `userID` -> `user-i-d`.
|
|
18
|
+
* Used to serialize property-binding names into HTML attribute names,
|
|
19
|
+
* which are case-insensitive in the parser. The original JS property
|
|
20
|
+
* name is recovered via camelCase() on the consumer side.
|
|
21
|
+
*
|
|
22
|
+
* @param {string} s
|
|
23
|
+
*/
|
|
24
|
+
export function kebabCase(s) {
|
|
25
|
+
return s.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** windows-1252 mappings the HTML tokenizer applies to the C1 range. */
|
|
29
|
+
const C1_REPLACEMENTS = {
|
|
30
|
+
0x80: 0x20ac, 0x82: 0x201a, 0x83: 0x0192, 0x84: 0x201e, 0x85: 0x2026,
|
|
31
|
+
0x86: 0x2020, 0x87: 0x2021, 0x88: 0x02c6, 0x89: 0x2030, 0x8a: 0x0160,
|
|
32
|
+
0x8b: 0x2039, 0x8c: 0x0152, 0x8e: 0x017d, 0x91: 0x2018, 0x92: 0x2019,
|
|
33
|
+
0x93: 0x201c, 0x94: 0x201d, 0x95: 0x2022, 0x96: 0x2013, 0x97: 0x2014,
|
|
34
|
+
0x98: 0x02dc, 0x99: 0x2122, 0x9a: 0x0161, 0x9b: 0x203a, 0x9c: 0x0153,
|
|
35
|
+
0x9e: 0x017e, 0x9f: 0x0178,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// The trailing `;` is OPTIONAL on the named arm, because a browser decodes a
|
|
39
|
+
// legacy semicolon-less reference too. The callback decides which of the two
|
|
40
|
+
// forms it has; see `decodeNamed`.
|
|
41
|
+
const CHAR_REF = /&(?:#(\d+);?|#[xX]([0-9a-fA-F]+);?|([a-zA-Z][a-zA-Z0-9]*)(;?))/g;
|
|
42
|
+
// A Map, not the imported object, so a name that collides with something on
|
|
43
|
+
// `Object.prototype` misses instead of returning a function. See `decodeNamed`.
|
|
44
|
+
const NAMED = new Map(Object.entries(NAMED_ENTITIES));
|
|
45
|
+
const LEGACY = new Set(LEGACY_NAMES);
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Decode HTML character references in an attribute value.
|
|
49
|
+
*
|
|
50
|
+
* `parseAttrs` hands back the literal characters between the quote marks, so
|
|
51
|
+
* nothing has decoded them yet, while a browser decodes EVERY reference before
|
|
52
|
+
* any reader sees the value. This closes that gap (#1341): the full WHATWG
|
|
53
|
+
* named table plus decimal and hexadecimal numeric references, with the
|
|
54
|
+
* tokenizer's own numeric fix-ups (null, a surrogate, and anything past
|
|
55
|
+
* U+10FFFF become U+FFFD; the C1 range maps through windows-1252). It replaced
|
|
56
|
+
* a three-entity `unescapeAttr`, which left a `String`-typed prop undecoded
|
|
57
|
+
* entirely and turned `<script>` into the half-decoded `<script>`.
|
|
58
|
+
*
|
|
59
|
+
* SINGLE PASS on purpose. A replacement is never rescanned, so `&lt;`
|
|
60
|
+
* decodes to the literal `<` and never to `<`. The old function got that
|
|
61
|
+
* from replacing `&` last, which does not generalise past three entities.
|
|
62
|
+
*
|
|
63
|
+
* The 106 legacy semicolon-LESS names are covered too, because a browser really
|
|
64
|
+
* does decode them and measurably: Chromium, Firefox, and WebKit all hand a
|
|
65
|
+
* reader U+00A0 for `s=" "`. Leaving them literal would have been a value
|
|
66
|
+
* divergence of exactly the kind this function exists to remove, not a
|
|
67
|
+
* harmless non-goal. The rule that governs them, in an ATTRIBUTE value, is a
|
|
68
|
+
* one-character LOOKAHEAD rather than deep tokenizer state, which is why it is
|
|
69
|
+
* implementable here. `decodeNamed` carries the rule and the reason it takes
|
|
70
|
+
* the shape it does; the short version is that ` ` at the end of a value
|
|
71
|
+
* decodes while ` x`, ` =x`, and `¬in` stay literal, all three
|
|
72
|
+
* verified against the three engines.
|
|
73
|
+
*
|
|
74
|
+
* The same function also decodes `data-webjs-fallback`, which is MARKUP, where
|
|
75
|
+
* the tokenizer applies no such carve-out. Using the attribute rule there is
|
|
76
|
+
* deliberate and strictly conservative: that payload is written by
|
|
77
|
+
* `escapeAttr`, which emits only `&` / `"` / `<`, so every
|
|
78
|
+
* reference in it is semicolon-terminated and never reaches this path.
|
|
79
|
+
*
|
|
80
|
+
* @param {string} s
|
|
81
|
+
* @returns {string}
|
|
82
|
+
*/
|
|
83
|
+
export function decodeAttrEntities(s) {
|
|
84
|
+
// Load bearing, not a micro-optimisation: skipping the scan for the common
|
|
85
|
+
// no-`&` value is what makes this cheaper per attribute than the three
|
|
86
|
+
// chained `replace` calls it replaced, which paid for three passes always.
|
|
87
|
+
if (s.indexOf('&') === -1) return s;
|
|
88
|
+
return s.replace(CHAR_REF, (match, dec, hex, name, semi, offset) => {
|
|
89
|
+
if (dec !== undefined) return fromCodePoint(parseInt(dec, 10));
|
|
90
|
+
if (hex !== undefined) return fromCodePoint(parseInt(hex, 16));
|
|
91
|
+
return decodeNamed(match, name, semi === ';', s[offset + match.length]);
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Resolve one named reference, semicolon-terminated or legacy.
|
|
97
|
+
*
|
|
98
|
+
* The table is read through a `Map` rather than by indexing the imported
|
|
99
|
+
* object. An object-literal lookup resolves through `Object.prototype`, so
|
|
100
|
+
* `&constructor;` / `&toString;` / `&hasOwnProperty;` and four more returned a
|
|
101
|
+
* FUNCTION instead of `undefined` and threw on the spread in
|
|
102
|
+
* `codePointsToString`, a path `seedServerAttrs` reaches for every attribute of
|
|
103
|
+
* every custom element, which rendered the whole component as an SSR error box.
|
|
104
|
+
* A browser leaves those literal, since they are not named references, so the
|
|
105
|
+
* throw was a divergence of exactly the kind this file exists to remove. A
|
|
106
|
+
* `Map` has no prototype chain to fall through, which closes the shape rather
|
|
107
|
+
* than guarding one call site.
|
|
108
|
+
*
|
|
109
|
+
* WHY THERE IS NO LONGEST-PREFIX LOOP. The tokenizer consumes the longest name
|
|
110
|
+
* in the table, so `¬in` is `¬` followed by `in`. Here that always
|
|
111
|
+
* collapses to the whole name: `CHAR_REF` captures `[a-zA-Z][a-zA-Z0-9]*`, so a
|
|
112
|
+
* prefix SHORTER than the name is by construction followed by an ASCII
|
|
113
|
+
* alphanumeric, which is precisely when the attribute carve-out declines to
|
|
114
|
+
* decode. A loop over shorter prefixes could therefore only ever return
|
|
115
|
+
* `match`, which is what falling through to the end already does. `¬in`
|
|
116
|
+
* stays literal either way, verified against the three engines, though note it
|
|
117
|
+
* gets there by not being a legacy name rather than by the carve-out.
|
|
118
|
+
*
|
|
119
|
+
* That same greediness means an ASCII alphanumeric character can never follow
|
|
120
|
+
* the match: whatever follows is by construction not `[a-zA-Z0-9]`, or the
|
|
121
|
+
* capture would have eaten it. So only the `=` check is needed for the
|
|
122
|
+
* attribute carve-out. ` x` is literal because `nbspx` is not a legacy
|
|
123
|
+
* name, NOT because of the lookahead; ` =x` is the shape the lookahead
|
|
124
|
+
* actually decides.
|
|
125
|
+
*
|
|
126
|
+
* @param {string} match the whole matched reference, returned unchanged when nothing decodes
|
|
127
|
+
* @param {string} name the name, with no `&` and no `;`
|
|
128
|
+
* @param {boolean} hadSemi
|
|
129
|
+
* @param {string|undefined} nextChar the character after the match, if any
|
|
130
|
+
* @returns {string}
|
|
131
|
+
*/
|
|
132
|
+
export function decodeNamed(match, name, hadSemi, nextChar) {
|
|
133
|
+
if (hadSemi) {
|
|
134
|
+
const cp = NAMED.get(name);
|
|
135
|
+
return cp === undefined ? match : codePointsToString(cp);
|
|
136
|
+
}
|
|
137
|
+
if (!LEGACY.has(name)) return match;
|
|
138
|
+
// The attribute carve-out: a legacy name decodes only when what follows is
|
|
139
|
+
// neither `=` nor an ASCII alphanumeric. Nothing following at all (the end of
|
|
140
|
+
// the value) decodes, which is the common `s=" "` shape. Because CHAR_REF
|
|
141
|
+
// captures greedily, what follows is never an ASCII alphanumeric, so only `=`
|
|
142
|
+
// needs to be checked here.
|
|
143
|
+
if (nextChar === '=') return match;
|
|
144
|
+
// Every legacy name is in the table under the same name, asserted by a test
|
|
145
|
+
// rather than left to trust, so this cannot miss.
|
|
146
|
+
return codePointsToString(NAMED.get(name));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** @param {number|number[]} cp */
|
|
150
|
+
export function codePointsToString(cp) {
|
|
151
|
+
return typeof cp === 'number' ? String.fromCodePoint(cp) : String.fromCodePoint(...cp);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* The tokenizer's numeric character reference fix-ups.
|
|
156
|
+
* @param {number} n
|
|
157
|
+
* @returns {string}
|
|
158
|
+
*/
|
|
159
|
+
export function fromCodePoint(n) {
|
|
160
|
+
if (n === 0 || n > 0x10ffff || (n >= 0xd800 && n <= 0xdfff)) return '\uFFFD';
|
|
161
|
+
if (C1_REPLACEMENTS[n] !== undefined) return String.fromCodePoint(C1_REPLACEMENTS[n]);
|
|
162
|
+
return String.fromCodePoint(n);
|
|
163
|
+
}
|