astro-accelerator 0.0.102 → 0.0.104
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
CHANGED
package/public/css/main.css
CHANGED
|
@@ -14,11 +14,11 @@ html {
|
|
|
14
14
|
outline: 0;
|
|
15
15
|
padding: 0;
|
|
16
16
|
vertical-align: baseline;
|
|
17
|
-
|
|
17
|
+
}
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
html {
|
|
20
20
|
scroll-padding-top: 5em;
|
|
21
|
-
|
|
21
|
+
}
|
|
22
22
|
|
|
23
23
|
body {
|
|
24
24
|
background-color: var(--aft);
|
|
@@ -642,6 +642,28 @@ form.site-search button {
|
|
|
642
642
|
font-family: var(--code-font);
|
|
643
643
|
}
|
|
644
644
|
|
|
645
|
+
.result-headings li {
|
|
646
|
+
display: none;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
.result-headings li:nth-child(-n+3) {
|
|
650
|
+
display: block;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
.result-headings:has(li:nth-child(n+4))::after {
|
|
654
|
+
content: 'See more >';
|
|
655
|
+
color: var(--fore-link);
|
|
656
|
+
text-decoration: underline;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
.result-headings:focus-within li {
|
|
660
|
+
display: block;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
.result-headings:focus-within::after {
|
|
664
|
+
display: none;
|
|
665
|
+
}
|
|
666
|
+
|
|
645
667
|
.show-more {
|
|
646
668
|
padding: 0.5em 1em;
|
|
647
669
|
background-color: var(--fore-link);
|
|
@@ -43,7 +43,7 @@ function highlight(string, terms) {
|
|
|
43
43
|
* Simplifies a string to plain lower case, removing diacritic characters and hyphens
|
|
44
44
|
* This means a search for "co-op" will be found in "COOP" and "Café" will be found in "cafe"
|
|
45
45
|
* @param {string} string
|
|
46
|
-
* @returns
|
|
46
|
+
* @returns {string}
|
|
47
47
|
*/
|
|
48
48
|
function sanitise(string) {
|
|
49
49
|
// @ts-ignore
|
package/public/js/search.js
CHANGED
|
@@ -31,6 +31,194 @@ import { contains, containsWord, sanitise, explode, highlight } from './modules/
|
|
|
31
31
|
} SearchEntry
|
|
32
32
|
*/
|
|
33
33
|
|
|
34
|
+
/**
|
|
35
|
+
* Removes "morphological and inflexional endings" from words
|
|
36
|
+
* See: http://www.tartarus.org/~martin/PorterStemmer
|
|
37
|
+
*/
|
|
38
|
+
const stemmer = (function () {
|
|
39
|
+
const step2list = {
|
|
40
|
+
"ational": "ate",
|
|
41
|
+
"tional": "tion",
|
|
42
|
+
"enci": "ence",
|
|
43
|
+
"anci": "ance",
|
|
44
|
+
"izer": "ize",
|
|
45
|
+
"bli": "ble",
|
|
46
|
+
"alli": "al",
|
|
47
|
+
"entli": "ent",
|
|
48
|
+
"eli": "e",
|
|
49
|
+
"ousli": "ous",
|
|
50
|
+
"ization": "ize",
|
|
51
|
+
"ation": "ate",
|
|
52
|
+
"ator": "ate",
|
|
53
|
+
"alism": "al",
|
|
54
|
+
"iveness": "ive",
|
|
55
|
+
"fulness": "ful",
|
|
56
|
+
"ousness": "ous",
|
|
57
|
+
"aliti": "al",
|
|
58
|
+
"iviti": "ive",
|
|
59
|
+
"biliti": "ble",
|
|
60
|
+
"logi": "log"
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const step3list = {
|
|
64
|
+
"icate": "ic",
|
|
65
|
+
"ative": "",
|
|
66
|
+
"alize": "al",
|
|
67
|
+
"iciti": "ic",
|
|
68
|
+
"ical": "ic",
|
|
69
|
+
"ful": "",
|
|
70
|
+
"ness": ""
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const c = "[^aeiou]", // consonant
|
|
74
|
+
v = "[aeiouy]", // vowel
|
|
75
|
+
C = c + "[^aeiouy]*", // consonant sequence
|
|
76
|
+
V = v + "[aeiou]*", // vowel sequence
|
|
77
|
+
|
|
78
|
+
mgr0 = "^(" + C + ")?" + V + C, // [C]VC... is m>0
|
|
79
|
+
meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$", // [C]VC[V] is m=1
|
|
80
|
+
mgr1 = "^(" + C + ")?" + V + C + V + C, // [C]VCVC... is m>1
|
|
81
|
+
s_v = "^(" + C + ")?" + v; // vowel in stem
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* @param {string} w
|
|
85
|
+
* @returns {string}
|
|
86
|
+
*/
|
|
87
|
+
return function (w) {
|
|
88
|
+
var stem,
|
|
89
|
+
suffix,
|
|
90
|
+
firstch,
|
|
91
|
+
re,
|
|
92
|
+
re2,
|
|
93
|
+
re3,
|
|
94
|
+
re4,
|
|
95
|
+
origword = w;
|
|
96
|
+
|
|
97
|
+
if (w.length < 3) { return w; }
|
|
98
|
+
|
|
99
|
+
firstch = w.substring(0, 1);
|
|
100
|
+
|
|
101
|
+
if (firstch == "y") {
|
|
102
|
+
w = firstch.toUpperCase() + w.substring(1, w.length);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Step 1a
|
|
106
|
+
re = /^(.+?)(ss|i)es$/;
|
|
107
|
+
re2 = /^(.+?)([^s])s$/;
|
|
108
|
+
|
|
109
|
+
if (re.test(w)) {
|
|
110
|
+
w = w.replace(re, "$1$2");
|
|
111
|
+
} else if (re2.test(w)) {
|
|
112
|
+
w = w.replace(re2, "$1$2");
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Step 1b
|
|
116
|
+
re = /^(.+?)eed$/;
|
|
117
|
+
re2 = /^(.+?)(ed|ing)$/;
|
|
118
|
+
if (re.test(w)) {
|
|
119
|
+
var fp = re.exec(w);
|
|
120
|
+
re = new RegExp(mgr0);
|
|
121
|
+
if (re.test(fp[1])) {
|
|
122
|
+
re = /.$/;
|
|
123
|
+
w = w.replace(re, "");
|
|
124
|
+
}
|
|
125
|
+
} else if (re2.test(w)) {
|
|
126
|
+
var fp = re2.exec(w);
|
|
127
|
+
stem = fp[1];
|
|
128
|
+
re2 = new RegExp(s_v);
|
|
129
|
+
if (re2.test(stem)) {
|
|
130
|
+
w = stem;
|
|
131
|
+
re2 = /(at|bl|iz)$/;
|
|
132
|
+
re3 = new RegExp("([^aeiouylsz])\\1$");
|
|
133
|
+
re4 = new RegExp("^" + C + v + "[^aeiouwxy]$");
|
|
134
|
+
if (re2.test(w)) { w = w + "e"; }
|
|
135
|
+
else if (re3.test(w)) { re = /.$/; w = w.replace(re, ""); }
|
|
136
|
+
else if (re4.test(w)) { w = w + "e"; }
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Step 1c
|
|
141
|
+
re = /^(.+?)y$/;
|
|
142
|
+
if (re.test(w)) {
|
|
143
|
+
var fp = re.exec(w);
|
|
144
|
+
stem = fp[1];
|
|
145
|
+
re = new RegExp(s_v);
|
|
146
|
+
if (re.test(stem)) { w = stem + "i"; }
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Step 2
|
|
150
|
+
re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/;
|
|
151
|
+
if (re.test(w)) {
|
|
152
|
+
var fp = re.exec(w);
|
|
153
|
+
stem = fp[1];
|
|
154
|
+
suffix = fp[2];
|
|
155
|
+
re = new RegExp(mgr0);
|
|
156
|
+
if (re.test(stem)) {
|
|
157
|
+
w = stem + step2list[suffix];
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Step 3
|
|
162
|
+
re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/;
|
|
163
|
+
if (re.test(w)) {
|
|
164
|
+
var fp = re.exec(w);
|
|
165
|
+
stem = fp[1];
|
|
166
|
+
suffix = fp[2];
|
|
167
|
+
re = new RegExp(mgr0);
|
|
168
|
+
if (re.test(stem)) {
|
|
169
|
+
w = stem + step3list[suffix];
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Step 4
|
|
174
|
+
re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/;
|
|
175
|
+
re2 = /^(.+?)(s|t)(ion)$/;
|
|
176
|
+
if (re.test(w)) {
|
|
177
|
+
var fp = re.exec(w);
|
|
178
|
+
stem = fp[1];
|
|
179
|
+
re = new RegExp(mgr1);
|
|
180
|
+
if (re.test(stem)) {
|
|
181
|
+
w = stem;
|
|
182
|
+
}
|
|
183
|
+
} else if (re2.test(w)) {
|
|
184
|
+
var fp = re2.exec(w);
|
|
185
|
+
stem = fp[1] + fp[2];
|
|
186
|
+
re2 = new RegExp(mgr1);
|
|
187
|
+
if (re2.test(stem)) {
|
|
188
|
+
w = stem;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Step 5
|
|
193
|
+
re = /^(.+?)e$/;
|
|
194
|
+
if (re.test(w)) {
|
|
195
|
+
var fp = re.exec(w);
|
|
196
|
+
stem = fp[1];
|
|
197
|
+
re = new RegExp(mgr1);
|
|
198
|
+
re2 = new RegExp(meq1);
|
|
199
|
+
re3 = new RegExp("^" + C + v + "[^aeiouwxy]$");
|
|
200
|
+
if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) {
|
|
201
|
+
w = stem;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
re = /ll$/;
|
|
206
|
+
re2 = new RegExp(mgr1);
|
|
207
|
+
if (re.test(w) && re2.test(w)) {
|
|
208
|
+
re = /.$/;
|
|
209
|
+
w = w.replace(re, "");
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// and turn initial Y back to y
|
|
213
|
+
|
|
214
|
+
if (firstch == "y") {
|
|
215
|
+
w = firstch.toLowerCase() + w.substr(1);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return w;
|
|
219
|
+
}
|
|
220
|
+
})();
|
|
221
|
+
|
|
34
222
|
/** @type {SearchEntry[]} */
|
|
35
223
|
var haystack = [];
|
|
36
224
|
var currentQuery = '';
|
|
@@ -62,8 +250,21 @@ function search(s, r) {
|
|
|
62
250
|
raiseEvent('searched', { search: s });
|
|
63
251
|
|
|
64
252
|
currentQuery = cleanQuery;
|
|
253
|
+
/** @type {string[]} */
|
|
254
|
+
const stemmedTerms = [];
|
|
65
255
|
const queryTerms = explode(currentQuery);
|
|
66
256
|
|
|
257
|
+
for (const term of queryTerms) {
|
|
258
|
+
const stemmed = stemmer(term);
|
|
259
|
+
if (stemmed !== term) {
|
|
260
|
+
stemmedTerms.push(stemmed);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const allTerms = queryTerms.concat(stemmedTerms);
|
|
265
|
+
|
|
266
|
+
console.log(allTerms);
|
|
267
|
+
|
|
67
268
|
cleanQuery.length > 0 && haystack.forEach( (item) => {
|
|
68
269
|
|
|
69
270
|
let foundWords = 0;
|
|
@@ -94,8 +295,9 @@ function search(s, r) {
|
|
|
94
295
|
|
|
95
296
|
// Part 2 - Term Matches, i.e. "Kitchen" or "Sink"
|
|
96
297
|
|
|
97
|
-
|
|
298
|
+
allTerms.forEach(term => {
|
|
98
299
|
let isTermFound = false;
|
|
300
|
+
const isUserTerm = queryTerms.includes(term);
|
|
99
301
|
|
|
100
302
|
// Title
|
|
101
303
|
if (contains(item.safeTitle, term)) {
|
|
@@ -140,7 +342,7 @@ function search(s, r) {
|
|
|
140
342
|
}
|
|
141
343
|
});
|
|
142
344
|
|
|
143
|
-
item.foundWords = foundWords /
|
|
345
|
+
item.foundWords = foundWords / allTerms.length;
|
|
144
346
|
|
|
145
347
|
if (item.score > 0) {
|
|
146
348
|
needles.push(item);
|
|
@@ -177,6 +379,7 @@ function search(s, r) {
|
|
|
177
379
|
const url = isSameHost ? address.pathname : needle.url;
|
|
178
380
|
|
|
179
381
|
const a = document.createElement('a');
|
|
382
|
+
// Only highlight user query terms, not stemmed terms
|
|
180
383
|
a.innerHTML = highlight(needle.title, queryTerms);
|
|
181
384
|
a.href = url;
|
|
182
385
|
|
|
@@ -186,27 +389,34 @@ function search(s, r) {
|
|
|
186
389
|
|
|
187
390
|
const markers = document.createElement('div');
|
|
188
391
|
markers.className = 'result-text';
|
|
392
|
+
// Only highlight user query terms, not stemmed terms
|
|
189
393
|
markers.innerHTML = highlight(needle.description, queryTerms);
|
|
190
394
|
|
|
191
|
-
const headings = document.createElement('ul');
|
|
192
|
-
markers.className = 'result-headings';
|
|
193
|
-
|
|
194
|
-
needle.matchedHeadings
|
|
195
|
-
.forEach(h => {
|
|
196
|
-
const item = document.createElement('li');
|
|
197
|
-
const link = document.createElement('a');
|
|
198
|
-
link.href = url + '#' + h.slug;
|
|
199
|
-
link.innerHTML = highlight(h.text, queryTerms);
|
|
200
|
-
item.appendChild(link);
|
|
201
|
-
headings.append(item);
|
|
202
|
-
});
|
|
203
|
-
|
|
204
395
|
const li = document.createElement('li');
|
|
396
|
+
li.dataset.score = (Math.round((needle.score/ total) * 100)).toString();
|
|
205
397
|
li.appendChild(a);
|
|
206
398
|
li.appendChild(path);
|
|
207
399
|
li.appendChild(markers);
|
|
208
|
-
|
|
209
|
-
|
|
400
|
+
|
|
401
|
+
if (needle.matchedHeadings.length > 0) {
|
|
402
|
+
const headings = document.createElement('ul');
|
|
403
|
+
headings.className = 'result-headings';
|
|
404
|
+
|
|
405
|
+
headings.tabIndex = 0;
|
|
406
|
+
|
|
407
|
+
needle.matchedHeadings
|
|
408
|
+
.forEach(h => {
|
|
409
|
+
const item = document.createElement('li');
|
|
410
|
+
const link = document.createElement('a');
|
|
411
|
+
link.href = url + '#' + h.slug;
|
|
412
|
+
// Only highlight user query terms, not stemmed terms
|
|
413
|
+
link.innerHTML = highlight(h.text, queryTerms);
|
|
414
|
+
item.appendChild(link);
|
|
415
|
+
headings.append(item);
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
li.appendChild(headings);
|
|
419
|
+
}
|
|
210
420
|
|
|
211
421
|
ol.appendChild(li);
|
|
212
422
|
}
|
|
@@ -128,12 +128,12 @@ for (const file of filesToProcess) {
|
|
|
128
128
|
// Only resize if the image is larger than the target size
|
|
129
129
|
sharp(source)
|
|
130
130
|
.resize(size[key], null)
|
|
131
|
-
.webp({ quality:
|
|
131
|
+
.webp({ quality: 90 })
|
|
132
132
|
.toFile(resizeDestination + '.webp');
|
|
133
133
|
} else {
|
|
134
134
|
// Don't resize as it's smaller than target size
|
|
135
135
|
sharp(source)
|
|
136
|
-
.webp({ quality:
|
|
136
|
+
.webp({ quality: 90 })
|
|
137
137
|
.toFile(resizeDestination + '.webp');
|
|
138
138
|
}
|
|
139
139
|
}
|