hyper-html-api 0.13.0 → 0.14.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/README.md +4 -1
- package/conformance/MANIFEST.json +2 -2
- package/conformance/cases/all-matches-read-quoted.expected.json +3 -0
- package/conformance/cases/all-matches-read-quoted.html +5 -0
- package/conformance/cases/all-matches-read-quoted.meta +7 -0
- package/conformance/cases/all-matches-read-quoted.parsed.json +3 -0
- package/conformance/cases/all-matches-read.expected.json +3 -0
- package/conformance/cases/all-matches-read.html +6 -0
- package/conformance/cases/all-matches-read.meta +7 -0
- package/conformance/cases/all-matches-read.parsed.json +3 -0
- package/conformance/cases/write-all-matches-attr.after.html +1 -0
- package/conformance/cases/write-all-matches-attr.after.json +3 -0
- package/conformance/cases/write-all-matches-attr.data.json +1 -0
- package/conformance/cases/write-all-matches-attr.html +1 -0
- package/conformance/cases/write-all-matches-attr.meta +6 -0
- package/conformance/cases/write-all-matches-attr.write.json +4 -0
- package/conformance/cases/write-all-matches-group.after.html +1 -0
- package/conformance/cases/write-all-matches-group.after.json +3 -0
- package/conformance/cases/write-all-matches-group.data.json +1 -0
- package/conformance/cases/write-all-matches-group.html +1 -0
- package/conformance/cases/write-all-matches-group.meta +6 -0
- package/conformance/cases/write-all-matches-group.write.json +4 -0
- package/conformance/cases/write-all-matches-text.after.html +1 -0
- package/conformance/cases/write-all-matches-text.after.json +3 -0
- package/conformance/cases/write-all-matches-text.data.json +1 -0
- package/conformance/cases/write-all-matches-text.html +1 -0
- package/conformance/cases/write-all-matches-text.meta +6 -0
- package/conformance/cases/write-all-matches-text.write.json +4 -0
- package/conformance/cases/write-all-matches-unmatched.data.json +1 -0
- package/conformance/cases/write-all-matches-unmatched.error.json +14 -0
- package/conformance/cases/write-all-matches-unmatched.html +1 -0
- package/conformance/cases/write-all-matches-unmatched.meta +6 -0
- package/dist/hyper-html-api.engine.esm.js +1 -1
- package/dist/hyper-html-api.engine.min.js +1 -1
- package/dist/hyper-html-api.min.js +8 -8
- package/dist/hyper-html-api.upgrade.min.js +2 -2
- package/package.json +3 -2
- package/src/engine/apply.js +6 -7
- package/src/engine/extract.js +6 -3
- package/src/engine/round-trip.js +9 -1
- package/src/engine/rule-syntax.js +13 -3
- package/src/engine/write-plan.js +2 -3
- package/src/page.js +15 -5
- package/src/site.js +164 -0
- package/src/upgrade/migrate.js +2 -1
- package/src/upgrade/shape-match.js +32 -13
- package/src/write.js +1 -1
|
@@ -1,14 +1,29 @@
|
|
|
1
|
+
import { DOM_PROPERTIES_READ_ONLY_SET } from '../engine/dom-properties.js'
|
|
2
|
+
import { ruleAttrIndex } from '../engine/rule-syntax.js'
|
|
3
|
+
|
|
1
4
|
/**
|
|
2
5
|
* Walk v2Rules pulling matching values from v1Data.
|
|
3
6
|
*
|
|
7
|
+
* Outside a list, what v1 has no value for keeps v2's template default:
|
|
8
|
+
*
|
|
4
9
|
* - Scalar leaves: carry the v1 value if defined, else leave undefined so
|
|
5
10
|
* the engine keeps v2's template default on apply.
|
|
6
|
-
* -
|
|
7
|
-
*
|
|
8
|
-
* in v1 are left undefined → template default.
|
|
9
|
-
* - Scalar arrays (`sel[]`): carry as-is.
|
|
11
|
+
* - Lists: carry v1's items, trimmed to v1's count; a list v1 never had
|
|
12
|
+
* keeps v2's rows.
|
|
10
13
|
* - Objects: recurse per key.
|
|
11
14
|
*
|
|
15
|
+
* Inside a list item nothing keeps a default. A row is the owner's own. It is
|
|
16
|
+
* written over a v2 sample row, or over a clone of one, so a field left
|
|
17
|
+
* undefined would take that sample's value (its label, its cover, its done
|
|
18
|
+
* mark, its id). So in a row:
|
|
19
|
+
*
|
|
20
|
+
* - A field v1 read as null, a field v1 never had (v2 added it), and a
|
|
21
|
+
* scalar field where v1 holds an object are all carried as null, and the
|
|
22
|
+
* write clears them.
|
|
23
|
+
* - A list v1 never had is carried as an empty list.
|
|
24
|
+
* - A property nothing can write (tagName, childElementCount) stays
|
|
25
|
+
* undefined, because writing null to it refuses the whole write.
|
|
26
|
+
*
|
|
12
27
|
* Returns { data, summary } where summary counts scalar leaves carried,
|
|
13
28
|
* dropped, and total list items preserved.
|
|
14
29
|
*/
|
|
@@ -19,31 +34,30 @@ export function shapeMatch(v1Data, v2Rules) {
|
|
|
19
34
|
return { data, summary }
|
|
20
35
|
}
|
|
21
36
|
|
|
22
|
-
function walk(rule, v1, summary) {
|
|
37
|
+
function walk(rule, v1, summary, inRow = false) {
|
|
23
38
|
if (typeof rule === 'string') {
|
|
24
39
|
if (rule.endsWith('[]')) {
|
|
25
|
-
if (!Array.isArray(v1)) return undefined
|
|
40
|
+
if (!Array.isArray(v1)) return inRow ? [] : undefined
|
|
26
41
|
summary.listItems += v1.length
|
|
27
42
|
summary.carriedOver += v1.length
|
|
28
43
|
return v1
|
|
29
44
|
}
|
|
30
|
-
|
|
31
|
-
//
|
|
32
|
-
|
|
33
|
-
if (typeof v1 === 'object') return undefined
|
|
45
|
+
// A scalar slot can't hold an object/array from v1 (collectDiscards counts
|
|
46
|
+
// the dropped leaves), so it is treated as a slot v1 has no value for.
|
|
47
|
+
if (v1 == null || typeof v1 === 'object') return inRow && !readOnly(rule) ? null : undefined
|
|
34
48
|
summary.carriedOver++
|
|
35
49
|
return v1
|
|
36
50
|
}
|
|
37
51
|
if (Array.isArray(rule)) {
|
|
38
52
|
const [, shape] = rule
|
|
39
|
-
if (!Array.isArray(v1)) return undefined
|
|
53
|
+
if (!Array.isArray(v1)) return inRow ? [] : undefined
|
|
40
54
|
summary.listItems += v1.length
|
|
41
|
-
return v1.map((item) => walk(shape, item, summary))
|
|
55
|
+
return v1.map((item) => walk(shape, item, summary, true))
|
|
42
56
|
}
|
|
43
57
|
if (typeof rule === 'object' && rule !== null) {
|
|
44
58
|
const out = {}
|
|
45
59
|
for (const [k, sub] of Object.entries(rule)) {
|
|
46
|
-
const v = walk(sub, v1 == null ? undefined : v1[k], summary)
|
|
60
|
+
const v = walk(sub, v1 == null ? undefined : v1[k], summary, inRow)
|
|
47
61
|
if (v !== undefined) out[k] = v
|
|
48
62
|
}
|
|
49
63
|
return out
|
|
@@ -51,6 +65,11 @@ function walk(rule, v1, summary) {
|
|
|
51
65
|
return undefined
|
|
52
66
|
}
|
|
53
67
|
|
|
68
|
+
function readOnly(rule) {
|
|
69
|
+
const at = ruleAttrIndex(rule)
|
|
70
|
+
return at !== -1 && DOM_PROPERTIES_READ_ONLY_SET.has(rule.slice(at + 1))
|
|
71
|
+
}
|
|
72
|
+
|
|
54
73
|
function collectDiscards(rule, v1, summary) {
|
|
55
74
|
if (v1 == null) return
|
|
56
75
|
if (typeof rule === 'object' && rule !== null && !Array.isArray(rule)) {
|
package/src/write.js
CHANGED
|
@@ -46,7 +46,7 @@ export function writeDocument(load, src, data, options = {}) {
|
|
|
46
46
|
// pays for the field it wrote, not for every rule in the document.
|
|
47
47
|
const written = writtenRules(rules, data)
|
|
48
48
|
const writtenRoot = load(html).root()
|
|
49
|
-
assertRoundTrip(written, data, extract(cheerioAdapter, writtenRoot, written, opts))
|
|
49
|
+
assertRoundTrip(written, data, extract(cheerioAdapter, writtenRoot, written, { ...opts, allMatches: true }))
|
|
50
50
|
return { html, changed: html !== src, spliced }
|
|
51
51
|
}
|
|
52
52
|
|