-
Notifications
You must be signed in to change notification settings - Fork 8
/
build.ts
147 lines (123 loc) · 4.11 KB
/
build.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#!/usr/bin/env deno --quiet --allow-all
import * as path from "https://deno.land/std@0.155.0/path/mod.ts";
import { debounce } from "https://deno.land/std@0.155.0/async/debounce.ts";
function timer() {
const start = performance.now();
return () => performance.now() - start;
}
type GenerateInit = {
inputDirectory: string;
outputJsonDevices: string;
outputJsonIds: string;
outputTypescript: string;
};
async function generate({
inputDirectory,
outputJsonDevices,
outputJsonIds,
outputTypescript,
}: GenerateInit) {
const time = timer();
const readPromises = [...Deno.readDirSync(inputDirectory)]
.map((entry) => entry.name)
.filter((name) => name.endsWith(".json"))
.sort((a, b) => a.localeCompare(b))
.map((name) => path.join(inputDirectory, name))
.map((path) => Deno.readTextFile(path));
const files = await Promise.all(readPromises);
const objects = files.map((text) => JSON.parse(text));
const data = Object.assign({}, ...objects) as Record<string, string>;
const idsToNames = {} as Partial<Record<string, string[]>>;
for (const [name, id] of Object.entries(data)) {
const arr = (idsToNames[id] ??= [] as string[]);
arr.push(name);
}
const identifiers = JSON.stringify(idsToNames);
const devices = JSON.stringify(data);
// 'YYYY-MM-DD'.length === 10
const yyyymmdd = new Date().toISOString().slice(0, 10);
const mod = `
/**
* This file was generated automatically on ${yyyymmdd}.
*
* @module
*/
/**
* A map of device name to device identifier.
*/
export const devices = ${devices} as const;
/**
* A map of device identifier to device name.
*/
export const identifiers = ${identifiers} as const;
/**
* A type that maps device name to device identifier.
*/
export type Devices = typeof devices;
/**
* An exclusive union of device names. Only currently known device names
* can be assigned to this type.
*
* Most of the time, \`AnyDeviceName\` is a better fit as it also allows
* any string to be assigned to it.
*/
export type DeviceName = keyof typeof devices;
/**
* An exclusive union of device identifiers. Only currently known identifiers
* can be assigned to this type.
*
* Most of the time, \`AnyIdentifier\` is a better fit as it also allows
* any string to be assigned to it.
*/
export type Identifier = keyof typeof identifiers;
// All strings can be assigned to \`string & {}\`, but because it's a distinct
// type from \`string\`, the compiler can't simplify the type.
// deno-lint-ignore ban-types
type StringSuggestions<T extends string> = T | (string & {});
/**
* A union of device identifiers. Any string can be assigned to this
* type, which allows for future identifiers that were unknown at the time this
* list was created.
*/
export type AnyIdentifier = StringSuggestions<Identifier>;
/**
* A union of device names. Any string can be assigned to this
* type, which allows for future devices that were unknown at the time this
* list was created.
*/
export type AnyDeviceName = StringSuggestions<DeviceName>;
`.trimStart();
await Promise.all([
Deno.writeTextFile(outputJsonDevices, devices + "\n"),
Deno.writeTextFile(outputJsonIds, identifiers + "\n"),
Deno.writeTextFile(outputTypescript, mod),
]);
await Deno.run({ cmd: ["deno", "fmt", "--quiet"] }).status();
console.log("%c√", "color: green", `Generated in ${time()} ms`);
}
async function main() {
const thisFile = path.fromFileUrl(import.meta.url);
const thisDir = path.dirname(thisFile);
const inputDirectory = path.join(thisDir, "devices");
const outputJsonDevices = path.join(thisDir, "devices.json");
const outputJsonIds = path.join(thisDir, "ids.json");
const outputTypescript = path.join(thisDir, "mod.ts");
const run = () =>
generate({
inputDirectory,
outputJsonDevices,
outputJsonIds,
outputTypescript,
});
if (Deno.args.includes("--watch")) {
console.log("Building and watching for changes, ^c to exit");
const generateDebounced = debounce(run, 500);
run();
for await (const _ of Deno.watchFs(inputDirectory)) {
generateDebounced();
}
} else {
run();
}
}
if (import.meta.main) main();