-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.ts
107 lines (100 loc) · 3.69 KB
/
main.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
import runBrowser from "./cli/browser.ts"
import { runDeno, runNode } from "./cli/process.ts"
import { Command } from "./deps/cliffy.ts"
import { blue, dim, gray, green, red, yellow } from "./deps/std/fmt/colors.ts"
import { walk } from "./deps/std/fs.ts"
import { Buffer } from "./deps/std/io.ts"
import * as path from "./deps/std/path.ts"
import { parseFrontmatter } from "./frontmatter.ts"
import { runWithConcurrency } from "./util.ts"
interface GlobalRunnerParams {
concurrency: number
skip: boolean
}
type Run<T extends GlobalRunnerParams> = (
t: Omit<T, keyof GlobalRunnerParams>,
) => (pathname: string, logs: Buffer) => Promise<number>
const globalRunner = <T extends GlobalRunnerParams>(f: Run<T>, skipArg: string) => {
return async ({ concurrency, skip, ...rest }: T, includePatterns: string) => {
const include: string[] = []
for await (
const { path: pathname } of walk(".", {
exts: [".ts", ".tsx", ".js"],
followSymlinks: true,
includeDirs: false,
match: includePatterns.split(" ").map((value) => {
if (typeof value !== "string") {
throw new Error(
`Specified an invalid include \`${value}\` (expected a glob or path to example file)`,
)
}
return path.isGlob(value) ? path.globToRegExp(value) : new RegExp(value)
}),
})
) include.push(pathname)
const failed: string[] = []
let passed = 0
let skipped = 0
await runWithConcurrency(
include.map((pathname) => async () => {
const { frontmatter } = parseFrontmatter(pathname, await Deno.readTextFile(pathname), {
test_skip(value) {
const skipArgs = new Set(value?.split(" ") ?? [])
return value === "" || skipArgs.has(skipArg)
},
})
const quotedPathname = `"${pathname}"`
if (skip && frontmatter.test_skip) {
console.log(yellow("Skipped"), quotedPathname)
skipped++
return
}
console.log(gray("Testing"), quotedPathname)
const logs = new Buffer()
const code = await f(rest)(pathname, logs)
passed++
const progress = dim(`(${passed + skipped}/${include.length})`)
if (code) {
failed.push(pathname)
console.log(red("Failed"), progress, quotedPathname)
console.log(new TextDecoder().decode(logs.bytes()))
} else {
console.log(green("Passed"), progress, quotedPathname)
}
}),
concurrency,
)
if (failed.length) {
console.log(`${red("Erroring examples")}: \n - "${failed.join(`"\n - "`)}"`)
Deno.exit(1)
} else {
if (passed) console.log(blue(`${passed} examples passed`))
if (skipped) console.log(gray(`${skipped} examples skipped`))
Deno.exit(0)
}
}
}
await new Command()
.name("egts")
.description("Example-related utilities used in Capi")
.command(
"test",
new Command()
.name("test")
.globalOption("-c, --concurrency <concurrency:integer>", "concurrency", { default: Infinity })
.globalOption("--no-skip", "ignore skip frontmatter")
.command("deno")
.arguments("<includePatterns..>")
.option("-r, --reload <reload>", "reload")
.action(globalRunner(runDeno, "deno"))
.command("node")
.arguments("<includePatterns..>")
.action(globalRunner(runNode, "node"))
.command("browser")
.arguments("<includePatterns..>")
.option("-b, --browser <binary>", "browser binary")
.option("-p, --project <project>", "project", { required: true })
.option("-r, --reload <reload>", "reload", { required: true })
.action(globalRunner(runBrowser, "browser")),
)
.parse(Deno.args)