@jxsuite/search 0.3.0 → 0.4.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/LICENSE +1 -1
- package/dist/search-index.js +14 -1
- package/package.json +2 -2
- package/src/search-index.ts +15 -5
- package/src/shared.ts +24 -0
package/LICENSE
CHANGED
package/dist/search-index.js
CHANGED
|
@@ -183,6 +183,16 @@ function entryUrl(basePath, id, trailingSlash) {
|
|
|
183
183
|
function jxTreeToText(children) {
|
|
184
184
|
return collectText(children).join(" ").replaceAll(/\s+/g, " ").trim();
|
|
185
185
|
}
|
|
186
|
+
var ENTITIES = {
|
|
187
|
+
"$": "$",
|
|
188
|
+
"&": "&",
|
|
189
|
+
">": ">",
|
|
190
|
+
"<": "<",
|
|
191
|
+
""": '"'
|
|
192
|
+
};
|
|
193
|
+
function innerHtmlText(html) {
|
|
194
|
+
return html.replaceAll(/<[^>]*>/g, " ").replaceAll(/$|&|>|<|"/g, (entity) => ENTITIES[entity]);
|
|
195
|
+
}
|
|
186
196
|
function collectText(children) {
|
|
187
197
|
const parts = [];
|
|
188
198
|
for (const node of children ?? []) {
|
|
@@ -195,6 +205,8 @@ function collectText(children) {
|
|
|
195
205
|
}
|
|
196
206
|
if (typeof node.textContent === "string") {
|
|
197
207
|
parts.push(node.textContent);
|
|
208
|
+
} else if (typeof node.innerHTML === "string") {
|
|
209
|
+
parts.push(innerHtmlText(node.innerHTML));
|
|
198
210
|
}
|
|
199
211
|
if (Array.isArray(node.children)) {
|
|
200
212
|
parts.push(...collectText(node.children));
|
|
@@ -286,7 +298,8 @@ var SearchIndex = {
|
|
|
286
298
|
continue;
|
|
287
299
|
}
|
|
288
300
|
const locale = i18n === null ? undefined : canonicalizeLocale(entry._meta?.locale) ?? undefined;
|
|
289
|
-
const
|
|
301
|
+
const stamped = entry._meta?.url;
|
|
302
|
+
const url = typeof stamped === "string" && stamped.startsWith("/") ? stamped : entryUrl(`${localeUrlPrefix(locale, i18n)}${collection.basePath}`, entry.id, trailingSlash);
|
|
290
303
|
const title = typeof data.title === "string" ? data.title : entry.id;
|
|
291
304
|
const description = typeof data.description === "string" ? data.description : "";
|
|
292
305
|
const children = entry.$children;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jxsuite/search",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Static site search for Jx: build-time index emission from content collections and a headless MiniSearch browser client",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"upgrade": "bun update --latest && bun install"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@jxsuite/schema": "^
|
|
39
|
+
"@jxsuite/schema": "^3.0.0",
|
|
40
40
|
"minisearch": "^7.2.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
package/src/search-index.ts
CHANGED
|
@@ -137,11 +137,21 @@ export const SearchIndex = {
|
|
|
137
137
|
*/
|
|
138
138
|
const locale =
|
|
139
139
|
i18n === null ? undefined : (canonicalizeLocale(entry._meta?.locale) ?? undefined);
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
140
|
+
/*
|
|
141
|
+
* An entry of a content type with a `route` carries its URL (`_meta.url`), stamped by the
|
|
142
|
+
* loader from the template its page and its links were generated from, so the index cannot
|
|
143
|
+
* point at a URL the site does not have. Only an entry with none is placed by the
|
|
144
|
+
* collection's `basePath` + id, the older, flat rule.
|
|
145
|
+
*/
|
|
146
|
+
const stamped = entry._meta?.url;
|
|
147
|
+
const url =
|
|
148
|
+
typeof stamped === "string" && stamped.startsWith("/")
|
|
149
|
+
? stamped
|
|
150
|
+
: entryUrl(
|
|
151
|
+
`${localeUrlPrefix(locale, i18n)}${collection.basePath}`,
|
|
152
|
+
entry.id,
|
|
153
|
+
trailingSlash,
|
|
154
|
+
);
|
|
145
155
|
const title = typeof data.title === "string" ? data.title : entry.id;
|
|
146
156
|
const description = typeof data.description === "string" ? data.description : "";
|
|
147
157
|
const children = entry.$children as (JxElement | string)[] | undefined;
|
package/src/shared.ts
CHANGED
|
@@ -84,6 +84,28 @@ export function jxTreeToText(children?: (JxElement | string)[]): string {
|
|
|
84
84
|
return collectText(children).join(" ").replaceAll(/\s+/g, " ").trim();
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
/** The five entities the parser's escaping and an author's markup commonly leave in `innerHTML`. */
|
|
88
|
+
const ENTITIES: Record<string, string> = {
|
|
89
|
+
"$": "$",
|
|
90
|
+
"&": "&",
|
|
91
|
+
">": ">",
|
|
92
|
+
"<": "<",
|
|
93
|
+
""": '"',
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* The text of an `innerHTML` string: tags dropped, the common entities decoded.
|
|
98
|
+
*
|
|
99
|
+
* The content loader writes text that contains `${` as escaped `innerHTML` so the build does not
|
|
100
|
+
* read it as a template (parser.md §3.5), which means the words of a code sample live there and not
|
|
101
|
+
* in `textContent`. An index that skipped `innerHTML` would silently stop finding them.
|
|
102
|
+
*/
|
|
103
|
+
function innerHtmlText(html: string): string {
|
|
104
|
+
return html
|
|
105
|
+
.replaceAll(/<[^>]*>/g, " ")
|
|
106
|
+
.replaceAll(/$|&|>|<|"/g, (entity) => ENTITIES[entity]!);
|
|
107
|
+
}
|
|
108
|
+
|
|
87
109
|
function collectText(children: (JxElement | string)[] | undefined): string[] {
|
|
88
110
|
const parts: string[] = [];
|
|
89
111
|
for (const node of children ?? []) {
|
|
@@ -96,6 +118,8 @@ function collectText(children: (JxElement | string)[] | undefined): string[] {
|
|
|
96
118
|
}
|
|
97
119
|
if (typeof node.textContent === "string") {
|
|
98
120
|
parts.push(node.textContent);
|
|
121
|
+
} else if (typeof node.innerHTML === "string") {
|
|
122
|
+
parts.push(innerHtmlText(node.innerHTML));
|
|
99
123
|
}
|
|
100
124
|
if (Array.isArray(node.children)) {
|
|
101
125
|
parts.push(...collectText(node.children as (JxElement | string)[]));
|