Skip to content

Commit 4ec200e

Browse files
committed
chore: add deno task gen to generate mod.ts files
1 parent 06da9f8 commit 4ec200e

File tree

2 files changed

+125
-0
lines changed

2 files changed

+125
-0
lines changed

.scripts/gen-mod.ts

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import { fromFileUrl, globToRegExp, join, relative } from "@std/path";
2+
import { map } from "@core/iterutil/map";
3+
import { flatMap } from "@core/iterutil/async/flat-map";
4+
5+
const decoder = new TextDecoder();
6+
7+
const excludes = [
8+
"mod.ts",
9+
"*_test.ts",
10+
"*_bench.ts",
11+
"_*.ts",
12+
];
13+
14+
type DenoDocEntry = {
15+
name: string;
16+
location: {
17+
filename: string;
18+
};
19+
declarationKind: string;
20+
jsDoc: {
21+
doc: string;
22+
};
23+
kind: string;
24+
};
25+
26+
function isDenoDocEntry(x: unknown): x is DenoDocEntry {
27+
if (x == null || typeof x !== "object") return false;
28+
if (typeof (x as DenoDocEntry).name !== "string") return false;
29+
if (typeof (x as DenoDocEntry).location !== "object") return false;
30+
if (typeof (x as DenoDocEntry).location.filename !== "string") return false;
31+
if (typeof (x as DenoDocEntry).declarationKind !== "string") return false;
32+
if (typeof (x as DenoDocEntry).jsDoc !== "object") return false;
33+
if (typeof (x as DenoDocEntry).jsDoc.doc !== "string") return false;
34+
if (typeof (x as DenoDocEntry).kind !== "string") return false;
35+
return true;
36+
}
37+
38+
async function listDenoDocEntries(path: string): Promise<DenoDocEntry[]> {
39+
const cmd = new Deno.Command(Deno.execPath(), {
40+
args: ["doc", "--json", path],
41+
stdout: "piped",
42+
stderr: "piped",
43+
});
44+
const { success, stdout, stderr } = await cmd.output();
45+
if (!success) {
46+
throw new Error(decoder.decode(stderr));
47+
}
48+
const json = JSON.parse(decoder.decode(stdout));
49+
if (!Array.isArray(json)) {
50+
throw new Error(`Expected array but got ${JSON.stringify(json)}`);
51+
}
52+
return json.filter(isDenoDocEntry);
53+
}
54+
55+
async function* iterModules(path: string): AsyncIterable<string> {
56+
const patterns = excludes.map((p) => globToRegExp(p));
57+
for await (const entry of Deno.readDir(path)) {
58+
if (!entry.isFile || !entry.name.endsWith(".ts")) continue;
59+
if (patterns.some((p) => p.test(entry.name))) continue;
60+
yield join(path, entry.name);
61+
}
62+
}
63+
64+
async function generateModTs(
65+
namespace: string,
66+
): Promise<void> {
67+
const path = fromFileUrl(import.meta.resolve(`../${namespace}/`));
68+
const exports = (await Array.fromAsync(
69+
flatMap(iterModules(path), (x) => listDenoDocEntries(x)),
70+
))
71+
.filter((x) => x.kind === "function")
72+
.filter((x) => x.declarationKind === "export")
73+
.filter((x) => x.name.startsWith(namespace))
74+
.map((x) => ({
75+
path: relative(path, fromFileUrl(x.location.filename)),
76+
name: x.name,
77+
doc: x.jsDoc.doc,
78+
}))
79+
.toSorted((a, b) => a.name.localeCompare(b.name));
80+
const lines = [
81+
"// NOTE: This file is generated by gen-mod.ts",
82+
...exports.map((x) => {
83+
return `import { ${x.name} } from "./${x.path}";`;
84+
}),
85+
"",
86+
...map((new Set(exports.map((x) => x.path))).values(), (x) => {
87+
return `export * from "./${x}";`;
88+
}),
89+
"",
90+
"/**",
91+
` * An object containing all the functions in ${namespace} module.`,
92+
" */",
93+
`export const ${namespace}: {`,
94+
...exports.flatMap((x) => {
95+
return [
96+
" /**",
97+
...x.doc.split("\n").map((line) => ` * ${line}`.trimEnd()),
98+
" */",
99+
` ${x.name.replace(namespace, "")}: typeof ${x.name};`.trimEnd(),
100+
];
101+
}),
102+
"} = {",
103+
...exports.flatMap((x) => {
104+
return [
105+
` ${x.name.replace(namespace, "")}: ${x.name},`.trimEnd(),
106+
];
107+
}),
108+
"};",
109+
];
110+
await Deno.writeTextFile(join(path, "mod.ts"), lines.join("\n") + "\n");
111+
}
112+
113+
async function main(): Promise<void> {
114+
await generateModTs("is");
115+
await generateModTs("as");
116+
}
117+
118+
if (import.meta.main) {
119+
main().catch((err) => {
120+
console.error(err);
121+
Deno.exit(1);
122+
});
123+
}

deno.jsonc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
]
6767
},
6868
"imports": {
69+
"@core/iterutil": "jsr:@core/iterutil@^0.3.0",
6970
"@core/unknownutil": "./mod.ts",
7071
"@deno/dnt": "jsr:@deno/dnt@^0.41.1",
7172
"@std/assert": "jsr:@std/assert@^0.221.0",
@@ -78,6 +79,7 @@
7879
"test": "deno test -A --doc --parallel --shuffle",
7980
"test:coverage": "deno task test --coverage=.coverage",
8081
"coverage": "deno coverage .coverage",
82+
"gen": "deno run --allow-run=deno --allow-read --allow-write=. .scripts/gen-mod.ts",
8183
"update": "deno run --allow-env --allow-read --allow-write=. --allow-run=git,deno --allow-net=jsr.io,registry.npmjs.org jsr:@molt/cli ./*.ts",
8284
"update:commit": "deno task -q update --commit --prefix deps: --pre-commit=fmt,lint"
8385
}

0 commit comments

Comments
 (0)