Skip to content

Commit b370dfd

Browse files
committed
Start TypeScripting some stuff
1 parent aea3f05 commit b370dfd

File tree

11 files changed

+98
-51
lines changed

11 files changed

+98
-51
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
coverage/
22
dist/javascript/
3+
dist/js/
4+
dist/json/
5+
dist/ts/
36
node_modules/
47

58
.DS_Store

lib/matcher.ts

Lines changed: 69 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,42 @@ const matchGroups = matchGroupsJSON.matchGroups;
1313
const trees = treesJSON.trees;
1414

1515

16+
type Vec2 = [number, number];
17+
type Vec3 = [number, number, number];
18+
type Location = Vec2 | Vec3 | string | number;
19+
20+
interface LocationSet {
21+
include?: Array<Location>,
22+
exclude?: Array<Location>
23+
};
24+
25+
interface LocationConflation {
26+
validateLocation: (a: Location) => unknown;
27+
resolveLocation: (a: Location) => unknown;
28+
validateLocationSet: (a: LocationSet) => unknown;
29+
resolveLocationSet: (a: LocationSet) => unknown;
30+
};
31+
32+
type HitType = 'primary' | 'alternate' | 'excludeGeneric' | 'excludeNamed';
33+
interface Hit {
34+
match: HitType;
35+
itemID?: string;
36+
area?: number;
37+
kv?: string;
38+
nsimple?: string;
39+
pattern?: string;
40+
};
41+
42+
1643
export class Matcher {
17-
//
44+
private matchIndex;
45+
private genericWords = new Map();
46+
private itemLocation;
47+
private locationSets;
48+
private locationIndex;
49+
private warnings: Array<string> = [];
50+
51+
1852
// `constructor`
1953
// initialize the genericWords regexes
2054
constructor() {
@@ -105,7 +139,7 @@ export class Matcher {
105139
// …
106140
// }
107141
//
108-
buildMatchIndex(data) {
142+
buildMatchIndex(data: Record<string, any>): void {
109143
const that = this;
110144
if (that.matchIndex) return; // it was built already
111145
that.matchIndex = new Map();
@@ -248,7 +282,7 @@ export class Matcher {
248282

249283

250284
// Insert this item into the matchIndex
251-
function insertName(which, t, kv, nsimple, itemID) {
285+
function insertName(which: string, t: string, kv: string, nsimple: string, itemID: string) {
252286
if (!nsimple) {
253287
that.warnings.push(`Warning: skipping empty ${which} name for item ${t}/${kv}: ${itemID}`);
254288
return;
@@ -287,7 +321,7 @@ export class Matcher {
287321
}
288322

289323
// For certain categories we do not want to match generic KV pairs like `building/yes` or `amenity/yes`
290-
function skipGenericKVMatches(t, k, v) {
324+
function skipGenericKVMatches(t: string, k: string, v: string): boolean {
291325
return (
292326
t === 'flags' ||
293327
t === 'transit' ||
@@ -324,7 +358,7 @@ export class Matcher {
324358
// …
325359
// }
326360
//
327-
buildLocationIndex(data, loco) {
361+
buildLocationIndex(data: Record<string, any>, loco: LocationConflation): void {
328362
const that = this;
329363
if (that.locationIndex) return; // it was built already
330364

@@ -341,8 +375,9 @@ export class Matcher {
341375
let resolved;
342376
try {
343377
resolved = loco.resolveLocationSet(item.locationSet); // resolve a feature for this locationSet
344-
} catch (err) {
345-
console.warn(`buildLocationIndex: ${err.message}`); // couldn't resolve
378+
} catch (err: unknown) {
379+
const message = (err instanceof Error) ? err.message : err;
380+
console.warn(`buildLocationIndex: ${message}`); // couldn't resolve
346381
}
347382
if (!resolved || !resolved.id) return;
348383

@@ -420,7 +455,7 @@ export class Matcher {
420455
// 3. If the [k,v,n] tuple matches nothing of any kind, return `null`
421456
//
422457
//
423-
match(k, v, n, loc) {
458+
match(k: string, v: string, n: string, loc?: Vec2): Array<Hit> | null {
424459
const that = this;
425460
if (!that.matchIndex) {
426461
throw new Error('match: matchIndex not built.');
@@ -436,8 +471,8 @@ export class Matcher {
436471

437472
const nsimple = simplify(n);
438473

439-
let seen = new Set();
440-
let results = [];
474+
const seen = new Set();
475+
const results: Array<Hit> = [];
441476
gatherResults('primary');
442477
gatherResults('alternate');
443478
if (results.length) return results;
@@ -446,14 +481,14 @@ export class Matcher {
446481
return results.length ? results : null;
447482

448483

449-
function gatherResults(which) {
484+
function gatherResults(which: string): void {
450485
// First try an exact match on k/v
451486
const kv = `${k}/${v}`;
452487
let didMatch = tryMatch(which, kv);
453488
if (didMatch) return;
454489

455490
// If that didn't work, look in match groups for other pairs considered equivalent to k/v..
456-
for (let mg in matchGroups) {
491+
for (const mg in matchGroups) {
457492
const matchGroup = matchGroups[mg];
458493
const inGroup = matchGroup.some(otherkv => otherkv === kv);
459494
if (!inGroup) continue;
@@ -476,38 +511,39 @@ export class Matcher {
476511
}
477512
}
478513

479-
480-
function tryMatch(which, kv) {
514+
function tryMatch(which: string, kv: string): boolean {
481515
const branch = that.matchIndex.get(kv);
482-
if (!branch) return;
516+
if (!branch) return false;
483517

484518
if (which === 'exclude') { // Test name `n` against named and generic exclude patterns
485519
let regex = [...branch.excludeNamed.values()].find(regex => regex.test(n));
486520
if (regex) {
487521
results.push({ match: 'excludeNamed', pattern: String(regex), kv: kv });
488-
return;
522+
return false;
489523
}
490524
regex = [...branch.excludeGeneric.values()].find(regex => regex.test(n));
491525
if (regex) {
492526
results.push({ match: 'excludeGeneric', pattern: String(regex), kv: kv });
493-
return;
527+
return false;
494528
}
495-
return;
529+
return false;
496530
}
497531

498532
const leaf = branch[which].get(nsimple);
499-
if (!leaf || !leaf.size) return;
533+
if (!leaf || !leaf.size) return false;
534+
if (!(which === 'primary' || which === 'alternate')) return false;
500535

501536
// If we get here, we matched something..
502537
// Prepare the results, calculate areas (if location index was set up)
503-
let hits = Array.from(leaf).map(itemID => {
538+
let hits: Array<Hit> = [];
539+
for (const itemID of [...leaf]) {
504540
let area = Infinity;
505541
if (that.itemLocation && that.locationSets) {
506542
const location = that.locationSets.get(that.itemLocation.get(itemID));
507543
area = (location && location.properties.area) || Infinity;
508544
}
509-
return { match: which, itemID: itemID, area: area, kv: kv, nsimple: nsimple };
510-
});
545+
hits.push({ match: which, itemID: itemID, area: area, kv: kv, nsimple: nsimple });
546+
}
511547

512548
let sortFn = byAreaDescending;
513549

@@ -517,7 +553,7 @@ export class Matcher {
517553
sortFn = byAreaAscending;
518554
}
519555

520-
if (!hits.length) return;
556+
if (!hits.length) return false;
521557

522558
// push results
523559
hits.sort(sortFn).forEach(hit => {
@@ -529,17 +565,21 @@ export class Matcher {
529565
return true;
530566

531567

532-
function isValidLocation(hit) {
568+
function isValidLocation(hit: Hit): boolean {
533569
if (!that.itemLocation) return true;
534570
return matchLocations.find(props => props.id === that.itemLocation.get(hit.itemID));
535571
}
536572
// Sort smaller (more local) locations first.
537-
function byAreaAscending(hitA, hitB) {
538-
return hitA.area - hitB.area;
573+
function byAreaAscending(hitA: Hit, hitB: Hit): number {
574+
const areaA = hitA.area || 0;
575+
const areaB = hitB.area || 0;
576+
return areaA - areaB;
539577
}
540578
// Sort larger (more worldwide) locations first.
541-
function byAreaDescending(hitA, hitB) {
542-
return hitB.area - hitA.area;
579+
function byAreaDescending(hitA: Hit, hitB: Hit): number {
580+
const areaA = hitA.area || 0;
581+
const areaB = hitB.area || 0;
582+
return areaB - areaA;
543583
}
544584
}
545585
}
@@ -550,7 +590,7 @@ export class Matcher {
550590
// Return any warnings discovered when buiding the index.
551591
// (currently this does nothing)
552592
//
553-
getWarnings() {
593+
getWarnings(): Array<string> {
554594
return this.warnings;
555595
}
556596
}

package.json

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,16 +80,19 @@
8080
],
8181
"type": "module",
8282
"exports": {
83+
"bun": "./src/nsi.ts",
8384
"dist/*": "./dist/*",
84-
"import": "./dist/javascript/nsi.mjs",
85-
"require": "./dist/javascript/nsi.cjs",
86-
"browser": "./dist/javascript/nsi.iife.js"
85+
"types": "./dist/ts/src/nsi.d.ts",
86+
"import": "./dist/js/nsi.mjs",
87+
"require": "./dist/js/nsi.cjs",
88+
"browser": "./dist/js/nsi.iife.js"
8789
},
8890
"scripts": {
8991
"all": "run-s clean lint build test",
90-
"build": "run-p build:js build:json",
92+
"build": "run-p build:js build:json build:ts",
9193
"build:js": "bun ./scripts/build_js.ts",
9294
"build:json": "bun ./scripts/build_json.ts",
95+
"build:ts": "tsc",
9396
"clean": "bun ./scripts/clean.ts",
9497
"dist": "run-s dist:version dist:files",
9598
"dist:files": "bun ./scripts/dist_files.js",

scripts/build_js.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ await Promise.all([
1717
}),
1818

1919
Bun.build({
20-
entrypoints: ['./src/nsi.mjs'],
20+
entrypoints: ['./src/nsi.ts'],
2121
outdir: './dist/javascript',
2222
target: 'node',
2323
format: 'cjs',
@@ -27,7 +27,7 @@ await Promise.all([
2727
}),
2828

2929
Bun.build({
30-
entrypoints: ['./src/nsi.mjs'],
30+
entrypoints: ['./src/nsi.ts'],
3131
outdir: './dist/javascript',
3232
target: 'node',
3333
format: 'esm',

scripts/clean.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ for (const f of files) {
1919
// Remove these specific folders
2020
const folders = [
2121
'./coverage',
22-
'./dist/javascript'
22+
'./dist/javascript',
23+
'./dist/js',
24+
'./dist/json',
25+
'./dist/ts'
2326
];
2427
for (const f of folders) {
2528
await $`rm -rf ${f}`;

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
import * as nsi from './nsi.mjs';
1+
import * as nsi from './nsi.ts';
22
globalThis.nsi = nsi;
File renamed without changes.

test/matcher.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { afterAll, afterEach, beforeAll, beforeEach, describe, it } from 'bun:test';
22
import { strict as assert } from 'bun:assert';
33
import LocationConflation from '@rapideditor/location-conflation';
4-
import { Matcher } from '../src/nsi.mjs';
4+
import { Matcher } from '../src/nsi.ts';
55

66
const data = await Bun.file('./test/matcher.data.json').json();
77

test/simplify.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { describe, it } from 'bun:test';
22
import { strict as assert } from 'bun:assert';
3-
import { simplify } from '../src/nsi.mjs';
3+
import { simplify } from '../src/nsi.ts';
44

55

66
describe('simplify', () => {

test/stemmer.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { describe, it } from 'bun:test';
22
import { strict as assert } from 'bun:assert';
3-
import { stemmer } from '../src/nsi.mjs';
3+
import { stemmer } from '../src/nsi.ts';
44

55

66
describe('stemmer', () => {

0 commit comments

Comments
 (0)