-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add cliffy + node support (#17)
- Loading branch information
1 parent
56fde66
commit d3dca06
Showing
8 changed files
with
200 additions
and
146 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import { unimplemented } from "../deps/std/assert.ts" | ||
import { Buffer } from "../deps/std/io.ts" | ||
|
||
export interface BrowserRunFlags { | ||
browser?: string | ||
project: string | ||
reload: string | ||
} | ||
|
||
export default function run(_: BrowserRunFlags) { | ||
return async (pathname: string, logs: Buffer): Promise<number> => { | ||
unimplemented() | ||
|
||
// TODO | ||
// const project = rest.project ?? await (async () => { | ||
// for (const pathname of ["deno.json", "deno.jsonc"]) { | ||
// try { | ||
// return JSON.parse(await Deno.readTextFile(pathname)) | ||
// } catch (_e) {} | ||
// } | ||
// return | ||
// })() | ||
// const _importMapURL = project.importMap | ||
// ? path.toFileUrl(path.join(Deno.cwd(), project.importMap)) | ||
// : undefined | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import { Buffer, readLines } from "../deps/std/io.ts" | ||
import { readerFromStreamReader, writeAll } from "../deps/std/streams.ts" | ||
|
||
export interface DenoRunFlags { | ||
reload?: string | ||
} | ||
|
||
export function runDeno({ reload }: DenoRunFlags) { | ||
return async (pathname: string, logs: Buffer): Promise<number> => { | ||
const flags = reload ? [`-r${reload === "" ? "" : `=${reload}`}`] : [] | ||
const process = new Deno.Command(Deno.execPath(), { | ||
args: ["run", "-A", ...flags, pathname], | ||
stdout: "piped", | ||
stderr: "piped", | ||
}).spawn() | ||
return collectResults(process, logs) | ||
} | ||
} | ||
|
||
export function runNode({}: Record<string, unknown>) { | ||
return async (pathname: string, logs: Buffer): Promise<number> => { | ||
const process = new Deno.Command("node", { | ||
args: [pathname], | ||
stdout: "piped", | ||
stderr: "piped", | ||
}).spawn() | ||
return collectResults(process, logs) | ||
} | ||
} | ||
|
||
async function collectResults(process: Deno.ChildProcess, logs: Buffer) { | ||
const [{ code }] = await Promise.all([ | ||
process.status, | ||
...[process.stdout, process.stderr].map(async (stream) => { | ||
const lineIter = readLines(readerFromStreamReader(stream.getReader())) | ||
const encoder = new TextEncoder() | ||
for await (const line of lineIter) await writeAll(logs, encoder.encode(`${line}\n`)) | ||
}), | ||
]) | ||
return code | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from "https://deno.land/x/cliffy@v0.25.7/command/mod.ts" |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
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>) => { | ||
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) { | ||
return value !== undefined | ||
}, | ||
}) | ||
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)) | ||
.command("node") | ||
.arguments("<includePatterns..>") | ||
.action(globalRunner(runNode)) | ||
.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)), | ||
) | ||
.parse(Deno.args) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { deferred } from "./deps/std/async.ts" | ||
|
||
export function runWithConcurrency<T>(fns: ReadonlyArray<() => Promise<T>>, concurrency: number) { | ||
const queue = [...fns] | ||
let running = 0 | ||
const results: Promise<T>[] = [] | ||
const final = deferred<T[]>() | ||
flushQueue() | ||
return final | ||
|
||
function flushQueue() { | ||
for (; running < concurrency; running++) { | ||
if (!queue.length) { | ||
final.resolve(Promise.all(results)) | ||
return | ||
} | ||
const promise = queue.shift()!() | ||
results.push(promise) | ||
promise.finally(() => { | ||
running-- | ||
flushQueue() | ||
}) | ||
} | ||
} | ||
} |