Skip to content

Commit 0e08234

Browse files
committed
Add types
1 parent c78b3c5 commit 0e08234

File tree

156 files changed

+3102
-914
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

156 files changed

+3102
-914
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ data/
77
node_modules/
88
.env
99
yarn.lock
10+
!/generate/types.d.ts

asset/houdini.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1+
/// <reference lib="dom" />
2+
13
/* eslint-env browser */
24
if ('paintWorklet' in CSS) {
5+
// @ts-expect-error: CSS is not yet fully typed.
36
CSS.paintWorklet.addModule(
7+
// To do: update.
48
'https://www.unpkg.com/[email protected]/squircle.min.js'
59
)
610
}

asset/search.js

Lines changed: 156 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,28 @@
1+
/// <reference lib="dom" />
2+
13
/* eslint-env browser */
24

5+
/**
6+
* @import {ElementContent} from 'hast'
7+
* @import {Data} from '../generate/data.js'
8+
* @import {Index as FlexSearch} from 'flexsearch'
9+
*/
10+
11+
/**
12+
* @typedef Search
13+
* @property {HTMLElement} $scope
14+
* @property {(search: Search) => Promise<undefined>} create
15+
* @property {(data: Data, query: string) => ElementContent} empty
16+
* @property {((data: Data, result: ReadonlyArray<string>) => Array<string>) | undefined} [filter]
17+
* @property {FlexSearch} index
18+
* @property {(data: Data, result: ReadonlyArray<string>) => ElementContent} results
19+
* @property {(data: Data) => ElementContent} preview
20+
* @property {string} selector
21+
* @property {(item: string) => number} weight
22+
*/
23+
24+
import {ok as assert} from 'devlop'
325
import flexsearch from 'flexsearch'
4-
import mean from 'compute-mean'
526
import {toDom} from 'hast-util-to-dom'
627
import {data} from '../generate/data.js'
728
import {search as searchForm} from '../generate/molecule/search.js'
@@ -41,50 +62,83 @@ function init() {
4162
const keywords = Object.keys(data.packagesByKeyword)
4263
const topics = Object.keys(data.projectsByTopic)
4364
const $root = document.querySelector('#' + id)
65+
assert($root)
4466
const $form = toDom(searchForm(data, parameter))
67+
assert($form instanceof HTMLElement)
4568
const $input = $form.querySelector('[name=' + parameter + ']')
69+
assert($input)
4670

4771
$root.prepend($form)
4872

4973
const promises = [
5074
{
5175
selector: '#root-keyword',
52-
create: (search) =>
53-
new Promise((resolve) => {
76+
/**
77+
* @param {Search} search
78+
* @returns {Promise<undefined>}
79+
*/
80+
create(search) {
81+
return new Promise((resolve) => {
5482
window.requestAnimationFrame(() => {
5583
keywords.forEach((d) => search.index.add(d, d))
56-
resolve()
84+
resolve(undefined)
5785
})
58-
}),
59-
weight: (d) => data.packagesByKeyword[d].length,
86+
})
87+
},
88+
/**
89+
* @param {string} d
90+
* @returns {number}
91+
*/
92+
weight(d) {
93+
return data.packagesByKeyword[d].length
94+
},
6095
filter: keywordFilter,
6196
preview: keywordPreview,
6297
empty: keywordEmpty,
6398
results: keywordResults
6499
},
65100
{
66101
selector: '#root-topic',
67-
create: (search) =>
68-
new Promise((resolve) => {
102+
/**
103+
* @param {Search} search
104+
* @returns {Promise<undefined>}
105+
*/
106+
create(search) {
107+
return new Promise((resolve) => {
69108
window.requestAnimationFrame(() => {
70109
topics.forEach((d) => search.index.add(d, d))
71-
resolve()
110+
resolve(undefined)
72111
})
73-
}),
74-
weight: (d) => data.projectsByTopic[d].length,
112+
})
113+
},
114+
/**
115+
* @param {string} d
116+
* @returns {number}
117+
*/
118+
weight(d) {
119+
return data.projectsByTopic[d].length
120+
},
75121
filter: topicFilter,
76122
preview: topicPreview,
77123
empty: topicEmpty,
78124
results: topicResults
79125
},
80126
{
81127
selector: '#root-package',
82-
create: (search) =>
83-
new Promise((resolve) => {
128+
/**
129+
* @param {Search} search
130+
* @returns {Promise<undefined>}
131+
*/
132+
create(search) {
133+
return new Promise((resolve) => {
84134
const size = 100
85135

86136
window.requestAnimationFrame(() => next(0))
87137

138+
/**
139+
* @param {number} start
140+
* @returns {undefined}
141+
*/
88142
function next(start) {
89143
const end = start + size
90144
const slice = names.slice(start, end)
@@ -97,25 +151,40 @@ function init() {
97151
)
98152

99153
if (slice.length === 0) {
100-
resolve()
154+
resolve(undefined)
101155
} else {
102156
window.requestAnimationFrame(() => next(end))
103157
}
104158
}
105-
}),
106-
weight: (d) => data.packageByName[d].score,
159+
})
160+
},
161+
/**
162+
* @param {string} d
163+
* @returns {number}
164+
*/
165+
weight(d) {
166+
return data.packageByName[d].score
167+
},
107168
preview: packagePreview,
108169
empty: packageEmpty,
109170
results: packageResults
110171
},
111172
{
112173
selector: '#root-project',
113-
create: (search) =>
114-
new Promise((resolve) => {
174+
/**
175+
* @param {Search} search
176+
* @returns {Promise<undefined>}
177+
*/
178+
create(search) {
179+
return new Promise((resolve) => {
115180
const size = 100
116181

117182
window.requestAnimationFrame(() => next(0))
118183

184+
/**
185+
* @param {number} start
186+
* @returns {undefined}
187+
*/
119188
function next(start) {
120189
const end = start + size
121190
const slice = repos.slice(start, end)
@@ -128,46 +197,73 @@ function init() {
128197
})
129198

130199
if (slice.length === 0) {
131-
resolve()
200+
resolve(undefined)
132201
} else {
133202
window.requestAnimationFrame(() => next(end))
134203
}
135204
}
136-
}),
137-
weight: (d) => helperReduceScore(data, d),
205+
})
206+
},
207+
/**
208+
* @param {string} d
209+
* @returns {number}
210+
*/
211+
weight(d) {
212+
return helperReduceScore(data, d)
213+
},
138214
preview: projectPreview,
139215
empty: projectEmpty,
140216
results: projectResults
141217
}
142-
].map((d) => {
143-
const $scope = document.querySelector(d.selector)
144-
const index = new Index({preset: 'score', tokenize: 'full'})
145-
const view = {...d, index, $scope}
146-
147-
return view.create(view).then(() => view)
148-
})
218+
].map(
219+
/**
220+
*
221+
* @param {Omit<Search, '$scope' | 'index'>} d
222+
* @returns
223+
*/
224+
function (d) {
225+
const $scope = document.querySelector(d.selector)
226+
assert($scope instanceof HTMLElement)
227+
const index = new Index({preset: 'score', tokenize: 'full'})
228+
/** @type {Search} */
229+
const view = {...d, index, $scope}
230+
231+
return view.create(view).then(() => view)
232+
}
233+
)
149234

150235
Promise.all(promises).then((searches) => {
151236
start()
152237

153238
$form.addEventListener('submit', onsubmit)
154239
window.addEventListener('popstate', onpopstate)
155240

241+
/**
242+
* @returns {undefined}
243+
*/
156244
function start() {
157-
const query = clean(new URL(loc).searchParams.get(parameter))
245+
const query = clean(new URL(loc.href).searchParams.get(parameter))
158246

159247
if (query) {
160248
onpopstate()
161249
}
162250
}
163251

252+
/**
253+
* @returns {undefined}
254+
*/
164255
function onpopstate() {
165-
search(clean(new URL(loc).searchParams.get(parameter)))
256+
search(clean(new URL(loc.href).searchParams.get(parameter)))
166257
}
167258

259+
/**
260+
* @param {HTMLElementEventMap['submit']} ev
261+
* @returns {undefined}
262+
*/
168263
function onsubmit(ev) {
169-
const url = new URL(loc)
264+
const url = new URL(loc.href)
170265
const current = clean(url.searchParams.get(parameter))
266+
assert($input instanceof HTMLInputElement)
171267
const value = clean($input.value)
172268

173269
ev.preventDefault()
@@ -182,38 +278,49 @@ function init() {
182278
url.searchParams.delete(parameter)
183279
}
184280

185-
history.pushState(
186-
{},
187-
null,
188-
url.pathname + url.search.replace(/%20/g, '+')
189-
)
281+
history.pushState({}, '', url.pathname + url.search.replace(/%20/g, '+'))
190282

191283
search(value)
192284
}
193285

286+
/**
287+
* @param {string} query
288+
* @returns {undefined}
289+
*/
194290
function search(query) {
291+
const $release = document.querySelector('#root-release')
292+
assert($release instanceof HTMLElement)
293+
assert($input instanceof HTMLInputElement)
195294
$input.value = query
196295

197296
if (!query) {
198-
document.querySelector('#root-release').style = ''
297+
$release.style.removeProperty('display')
199298
searches.forEach((search) => replace(search, [], query))
200299
return
201300
}
202301

203-
document.querySelector('#root-release').style = 'display:none'
302+
$release.style.display = 'block'
204303

205304
searches.forEach((search) => {
206305
search.index.searchAsync(query, {suggest: true}, (result) => {
207-
const clean = result.filter(unique)
306+
const clean = /** @type {Array<string>} */ (result.filter(unique))
208307
const weighted = desc(clean, weight)
209308

210309
replace(search, asc(clean, combined), query)
211310

311+
/**
312+
* @param {string} d
313+
* @returns {number}
314+
*/
212315
function combined(d) {
213-
return mean([clean.indexOf(d), weighted.indexOf(d)])
316+
return (clean.indexOf(d) + weighted.indexOf(d)) / 2
214317
}
215318
})
216319

320+
/**
321+
* @param {string} d
322+
* @returns {number}
323+
*/
217324
function weight(d) {
218325
return search.weight(d)
219326
}
@@ -222,6 +329,11 @@ function init() {
222329
})
223330
}
224331

332+
/**
333+
* @param {Search} search
334+
* @param {ReadonlyArray<string>} result
335+
* @param {string} query
336+
*/
225337
function replace(search, result, query) {
226338
const {$scope, filter, preview, empty, results} = search
227339

@@ -244,6 +356,10 @@ function replace(search, result, query) {
244356
$scope.append($next)
245357
}
246358

359+
/**
360+
* @param {string | null} value
361+
* @returns {string}
362+
*/
247363
function clean(value) {
248364
return (value || '').trim().toLowerCase()
249365
}

0 commit comments

Comments
 (0)