@uniweb/projections 0.1.3 → 0.1.4
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/package.json +3 -3
- package/src/search/extract.js +37 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uniweb/projections",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Projections of a Uniweb site's content — agent index, per-page markdown, search index. Pure JS, runs anywhere.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -31,8 +31,8 @@
|
|
|
31
31
|
"node": ">=20.19"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@uniweb/
|
|
35
|
-
"@uniweb/
|
|
34
|
+
"@uniweb/content-writer": "0.2.9",
|
|
35
|
+
"@uniweb/core": "0.7.32"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"vitest": "^4.1.7",
|
package/src/search/extract.js
CHANGED
|
@@ -108,12 +108,17 @@ function extractFromPage(page) {
|
|
|
108
108
|
|
|
109
109
|
/**
|
|
110
110
|
* Extract search entries from a section (and subsections)
|
|
111
|
+
*
|
|
111
112
|
* @param {Object} section - Section data
|
|
112
113
|
* @param {Object} page - Parent page
|
|
113
114
|
* @param {Object} options - Extraction options
|
|
115
|
+
* @param {string} [ancestorAnchor] - The anchor of the nearest ancestor that
|
|
116
|
+
* is actually rendered with a wrapper. Absent at the top level, where the
|
|
117
|
+
* section renders itself; inherited by every nested level. See the note on
|
|
118
|
+
* anchors below.
|
|
114
119
|
* @returns {Array<Object>} Array of search entries
|
|
115
120
|
*/
|
|
116
|
-
function extractFromSection(section, page, options) {
|
|
121
|
+
function extractFromSection(section, page, options, ancestorAnchor) {
|
|
117
122
|
const entries = []
|
|
118
123
|
const {
|
|
119
124
|
includeHeadings,
|
|
@@ -126,6 +131,27 @@ function extractFromSection(section, page, options) {
|
|
|
126
131
|
const sectionId = section.id || 'unknown'
|
|
127
132
|
const component = section.component || section.type || 'unknown'
|
|
128
133
|
|
|
134
|
+
// Where a reader gets taken for a hit in this section.
|
|
135
|
+
//
|
|
136
|
+
// A top-level section is wrapped by the renderer and carries its own id, so
|
|
137
|
+
// it is its own destination. A NESTED section usually is not: `<ChildBlocks>`
|
|
138
|
+
// renders children bare by default — no wrapper, and therefore no id — and
|
|
139
|
+
// whether a foundation opted into `wrapAs` is a runtime decision this
|
|
140
|
+
// build-time pass cannot see. Emitting the child's own id produced a
|
|
141
|
+
// fragment that existed nowhere, so a hit landed on the page without
|
|
142
|
+
// scrolling and did nothing at all when the reader was already on it.
|
|
143
|
+
//
|
|
144
|
+
// The nearest rendered ancestor is the honest answer: it always exists, and
|
|
145
|
+
// the child's content is physically inside it, so the reader lands where the
|
|
146
|
+
// matching text actually is. Precision is lost only in the rarer `wrapAs`
|
|
147
|
+
// case, where the alternative was being wrong in the common one.
|
|
148
|
+
//
|
|
149
|
+
// Consequence worth knowing: a parent and its children now share an anchor,
|
|
150
|
+
// so several results can point at one destination. They are still distinct
|
|
151
|
+
// content with their own titles and excerpts; which to surface is the
|
|
152
|
+
// consumer's ranking decision, not ours to collapse here.
|
|
153
|
+
const anchor = ancestorAnchor || sectionDomId(section)
|
|
154
|
+
|
|
129
155
|
// Skip excluded components
|
|
130
156
|
if (excludeComponents.includes(component)) {
|
|
131
157
|
return entries
|
|
@@ -162,12 +188,9 @@ function extractFromSection(section, page, options) {
|
|
|
162
188
|
type: 'section',
|
|
163
189
|
route: page.route,
|
|
164
190
|
sectionId,
|
|
165
|
-
//
|
|
166
|
-
// to
|
|
167
|
-
|
|
168
|
-
// page. Derived by the shared rule rather than spelled out here,
|
|
169
|
-
// because that is exactly how the two drifted apart before.
|
|
170
|
-
anchor: sectionDomId(section),
|
|
191
|
+
// Resolved above. Never spelled out here: the id format is the shared
|
|
192
|
+
// rule's to own, which is exactly how these two drifted apart before.
|
|
193
|
+
anchor,
|
|
171
194
|
component,
|
|
172
195
|
title: sectionTitle,
|
|
173
196
|
pageTitle: page.title || '',
|
|
@@ -179,9 +202,14 @@ function extractFromSection(section, page, options) {
|
|
|
179
202
|
})
|
|
180
203
|
}
|
|
181
204
|
|
|
182
|
-
// Recursively process subsections
|
|
205
|
+
// Recursively process subsections.
|
|
206
|
+
//
|
|
207
|
+
// `anchor` is passed down rather than the subsection resolving its own, so
|
|
208
|
+
// every level below the rendered one inherits the same destination — a
|
|
209
|
+
// grandchild is no more rendered than its parent, and inheriting only one
|
|
210
|
+
// level would put it back to naming an id that does not exist.
|
|
183
211
|
for (const subsection of section.subsections || []) {
|
|
184
|
-
const subEntries = extractFromSection(subsection, page, options)
|
|
212
|
+
const subEntries = extractFromSection(subsection, page, options, anchor)
|
|
185
213
|
entries.push(...subEntries)
|
|
186
214
|
}
|
|
187
215
|
|