feat: add flags support
This commit is contained in:
16
deno.jsonc
16
deno.jsonc
@@ -64,6 +64,22 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"demo:flags": {
|
||||||
|
"description": "🎬 Demo: flags",
|
||||||
|
"task": "echo '$<foo> $<bar>'",
|
||||||
|
"flags": {
|
||||||
|
"foo": {
|
||||||
|
"alias": "f",
|
||||||
|
"required": true,
|
||||||
|
"description": "Example of a required flag"
|
||||||
|
},
|
||||||
|
"bar": {
|
||||||
|
"alias": "b",
|
||||||
|
"default": "bar",
|
||||||
|
"description": "Example of a standard flag"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"ci": {
|
"ci": {
|
||||||
"description": "🤖 CI checks",
|
"description": "🤖 CI checks",
|
||||||
"task": [
|
"task": [
|
||||||
|
|||||||
1
deno.lock
generated
1
deno.lock
generated
@@ -177,6 +177,7 @@
|
|||||||
"https://deno.land/std@0.204.0/path/windows/to_namespaced_path.ts": "e0f4d4a5e77f28a5708c1a33ff24360f35637ba6d8f103d19661255ef7bfd50d",
|
"https://deno.land/std@0.204.0/path/windows/to_namespaced_path.ts": "e0f4d4a5e77f28a5708c1a33ff24360f35637ba6d8f103d19661255ef7bfd50d",
|
||||||
"https://deno.land/std@0.205.0/assert/assert.ts": "9a97dad6d98c238938e7540736b826440ad8c1c1e54430ca4c4e623e585607ee",
|
"https://deno.land/std@0.205.0/assert/assert.ts": "9a97dad6d98c238938e7540736b826440ad8c1c1e54430ca4c4e623e585607ee",
|
||||||
"https://deno.land/std@0.205.0/assert/assertion_error.ts": "4d0bde9b374dfbcbe8ac23f54f567b77024fb67dbb1906a852d67fe050d42f56",
|
"https://deno.land/std@0.205.0/assert/assertion_error.ts": "4d0bde9b374dfbcbe8ac23f54f567b77024fb67dbb1906a852d67fe050d42f56",
|
||||||
|
"https://deno.land/std@0.205.0/flags/mod.ts": "0948466fc437f017f00c0b972a422b3dc3317a790bcf326429d23182977eaf9f",
|
||||||
"https://deno.land/std@0.205.0/fmt/colors.ts": "c51c4642678eb690dcf5ffee5918b675bf01a33fba82acf303701ae1a4f8c8d9",
|
"https://deno.land/std@0.205.0/fmt/colors.ts": "c51c4642678eb690dcf5ffee5918b675bf01a33fba82acf303701ae1a4f8c8d9",
|
||||||
"https://deno.land/std@0.205.0/fs/_util.ts": "fbf57dcdc9f7bc8128d60301eece608246971a7836a3bb1e78da75314f08b978",
|
"https://deno.land/std@0.205.0/fs/_util.ts": "fbf57dcdc9f7bc8128d60301eece608246971a7836a3bb1e78da75314f08b978",
|
||||||
"https://deno.land/std@0.205.0/fs/copy.ts": "ca19e4837965914471df38fbd61e16f9e8adfe89f9cffb0c83615c83ea3fc2bf",
|
"https://deno.land/std@0.205.0/fs/copy.ts": "ca19e4837965914471df38fbd61e16f9e8adfe89f9cffb0c83615c83ea3fc2bf",
|
||||||
|
|||||||
81
mod.ts
81
mod.ts
@@ -2,7 +2,16 @@
|
|||||||
import * as JSONC from "https://deno.land/std@0.205.0/jsonc/mod.ts"
|
import * as JSONC from "https://deno.land/std@0.205.0/jsonc/mod.ts"
|
||||||
import { z as is } from "https://deno.land/x/zod@v3.21.4/mod.ts"
|
import { z as is } from "https://deno.land/x/zod@v3.21.4/mod.ts"
|
||||||
import { fromZodError } from "https://esm.sh/zod-validation-error@1.5.0?pin=v133"
|
import { fromZodError } from "https://esm.sh/zod-validation-error@1.5.0?pin=v133"
|
||||||
import { bgBrightBlue, bold, gray, italic, underline, yellow } from "https://deno.land/std@0.205.0/fmt/colors.ts"
|
import {
|
||||||
|
bgBrightBlue,
|
||||||
|
bold,
|
||||||
|
gray,
|
||||||
|
italic,
|
||||||
|
magenta,
|
||||||
|
underline,
|
||||||
|
yellow,
|
||||||
|
} from "https://deno.land/std@0.205.0/fmt/colors.ts"
|
||||||
|
import { parse } from "https://deno.land/std@0.205.0/flags/mod.ts"
|
||||||
|
|
||||||
// Structure flags =========================================================================================================
|
// Structure flags =========================================================================================================
|
||||||
|
|
||||||
@@ -378,6 +387,15 @@ const _make = is.object({
|
|||||||
)
|
)
|
||||||
).default(() => ({})),
|
).default(() => ({})),
|
||||||
cwd: is.string().optional(),
|
cwd: is.string().optional(),
|
||||||
|
flags: is.record(
|
||||||
|
is.string(),
|
||||||
|
is.object({
|
||||||
|
alias: is.string().optional(),
|
||||||
|
default: is.unknown().optional(),
|
||||||
|
required: is.boolean().default(false),
|
||||||
|
description: is.string().default(""),
|
||||||
|
}),
|
||||||
|
).default(() => ({})),
|
||||||
deno: is.object({
|
deno: is.object({
|
||||||
bench,
|
bench,
|
||||||
bundle,
|
bundle,
|
||||||
@@ -403,15 +421,42 @@ const _make = is.object({
|
|||||||
/** Compute command to execute after applying deno flags */
|
/** Compute command to execute after applying deno flags */
|
||||||
export function command(
|
export function command(
|
||||||
raw: string,
|
raw: string,
|
||||||
{ deno }: Pick<is.infer<typeof _make>, "deno">,
|
{ flags, deno, argv = [] }: Pick<is.infer<typeof _make>, "deno" | "flags"> & { argv?: string[] },
|
||||||
{ colors = false } = {},
|
{ colors = false, parseArgv = true } = {},
|
||||||
) {
|
) {
|
||||||
for (const [subcommand, flags] of Object.entries(deno)) {
|
for (const [subcommand, options] of Object.entries(deno)) {
|
||||||
raw = raw.replaceAll(
|
raw = raw.replaceAll(
|
||||||
`deno ${subcommand}`,
|
`deno ${subcommand}`,
|
||||||
`deno ${subcommand} ${colors ? italic(underline(flags)) : flags}`,
|
`deno ${subcommand} ${colors ? italic(underline(options)) : options}`,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
const { _: args, ...options } = parse(argv, {
|
||||||
|
alias: Object.fromEntries(
|
||||||
|
Object.entries(flags).filter(([_, { alias }]) => alias).map(([key, { alias }]) => [alias, key]),
|
||||||
|
),
|
||||||
|
default: Object.fromEntries(
|
||||||
|
Object.entries(flags).filter(([_, options]) => "default" in options).map((
|
||||||
|
[key, options],
|
||||||
|
) => [key, options.default]),
|
||||||
|
),
|
||||||
|
})
|
||||||
|
for (const [key, { required }] of Object.entries(flags)) {
|
||||||
|
if (parseArgv && required && (!(key in options))) {
|
||||||
|
throw new ReferenceError(`Missing flag: ${key}`)
|
||||||
|
}
|
||||||
|
if (parseArgv) {
|
||||||
|
raw = raw.replaceAll(`$<${key}>`, `${options[key]}`)
|
||||||
|
} else if (colors) {
|
||||||
|
raw = raw.replaceAll(`$<${key}>`, italic(underline(`$<${key}>`)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let key = 0; key < args.length; key++) {
|
||||||
|
if (parseArgv) {
|
||||||
|
raw = raw.replaceAll(`$<${key}>`, `${args[key]}`)
|
||||||
|
} else if (colors) {
|
||||||
|
raw = raw.replaceAll(`$<${key}>`, italic(underline(`$<${key}>`)))
|
||||||
|
}
|
||||||
|
}
|
||||||
return raw
|
return raw
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -419,6 +464,7 @@ export function command(
|
|||||||
export async function make(
|
export async function make(
|
||||||
{
|
{
|
||||||
task = "",
|
task = "",
|
||||||
|
argv = [] as string[],
|
||||||
config = "deno.jsonc",
|
config = "deno.jsonc",
|
||||||
log = console.log,
|
log = console.log,
|
||||||
exit = true,
|
exit = true,
|
||||||
@@ -440,11 +486,11 @@ export async function make(
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
if (task) {
|
if (task) {
|
||||||
const { task: raw, env, deno, cwd } = tasks[task]
|
const { task: raw, env, deno, flags, cwd } = tasks[task]
|
||||||
const temp = ".deno-make.json"
|
const temp = ".deno-make.json"
|
||||||
const decoder = new TextDecoder()
|
const decoder = new TextDecoder()
|
||||||
try {
|
try {
|
||||||
const make = command(raw, { deno })
|
const make = command(raw, { deno, flags, argv })
|
||||||
await Deno.writeTextFile(temp, JSON.stringify({ tasks: { make } }))
|
await Deno.writeTextFile(temp, JSON.stringify({ tasks: { make } }))
|
||||||
const process = new Deno.Command("deno", {
|
const process = new Deno.Command("deno", {
|
||||||
args: ["task", ...(cwd ? ["--cwd", cwd] : []), "--config", temp, "make"],
|
args: ["task", ...(cwd ? ["--cwd", cwd] : []), "--config", temp, "make"],
|
||||||
@@ -470,7 +516,7 @@ export async function make(
|
|||||||
}
|
}
|
||||||
} else if (Object.keys(tasks).length) {
|
} else if (Object.keys(tasks).length) {
|
||||||
for (
|
for (
|
||||||
const [name, { task, description, env, cwd, deno }] of Object.entries(
|
const [name, { task, description, env, cwd, deno, flags }] of Object.entries(
|
||||||
tasks,
|
tasks,
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
@@ -490,12 +536,26 @@ export async function make(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (Object.keys(flags).length) {
|
||||||
|
log(magenta(`Flags:`))
|
||||||
|
for (const [key, { alias, required, description, ...options }] of Object.entries(flags)) {
|
||||||
|
log(
|
||||||
|
`${
|
||||||
|
magenta(
|
||||||
|
` --${key}${alias ? `, -${alias}` : ""}${
|
||||||
|
"default" in options ? italic(` [=${options.default}]`) : required ? italic(bold(` (required)`)) : ""
|
||||||
|
}`,
|
||||||
|
)
|
||||||
|
}\t${description}`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
if (cwd) {
|
if (cwd) {
|
||||||
log(gray(`Working directory:`))
|
log(gray(`Working directory:`))
|
||||||
log(gray(` ${cwd}`))
|
log(gray(` ${cwd}`))
|
||||||
}
|
}
|
||||||
log(gray(`Task:`))
|
log(gray(`Task:`))
|
||||||
log(gray(` ${command(task, { deno }, { colors: true })}`))
|
log(gray(` ${command(task, { deno, flags }, { colors: true, parseArgv: false })}`))
|
||||||
log("")
|
log("")
|
||||||
}
|
}
|
||||||
return { code: 0 }
|
return { code: 0 }
|
||||||
@@ -505,5 +565,6 @@ export async function make(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (import.meta.main) {
|
if (import.meta.main) {
|
||||||
await make({ task: Deno.args[0] })
|
const [task, ...argv] = Deno.args
|
||||||
|
await make({ task, argv })
|
||||||
}
|
}
|
||||||
|
|||||||
48
mod_test.ts
48
mod_test.ts
@@ -36,6 +36,54 @@ Deno.test("deno task make: print tasks", async () => {
|
|||||||
expect(code).to.equal(0)
|
expect(code).to.equal(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Deno.test("deno task make: flags required", async () => {
|
||||||
|
const stdio = [] as string[]
|
||||||
|
const { code } = await make({
|
||||||
|
task: "make:flags_required",
|
||||||
|
argv: ["--foo", "🦕"],
|
||||||
|
config: "tests/deno_make.jsonc",
|
||||||
|
log: (message) => stdio.push(message),
|
||||||
|
stdio: "piped",
|
||||||
|
exit: false,
|
||||||
|
})
|
||||||
|
expect(stdio.join("\n")).to.include("🦕")
|
||||||
|
expect(code).to.equal(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
Deno.test.ignore("deno task make: flags required throw", async () => {
|
||||||
|
await expect(() =>
|
||||||
|
make({ task: "make:flags_required", argv: [], config: "tests/deno_make.jsonc", stdio: "null", exit: false })
|
||||||
|
).to
|
||||||
|
})
|
||||||
|
|
||||||
|
Deno.test("deno task make: flags defaults", async () => {
|
||||||
|
const stdio = [] as string[]
|
||||||
|
const { code } = await make({
|
||||||
|
task: "make:flags_defaults",
|
||||||
|
argv: [],
|
||||||
|
config: "tests/deno_make.jsonc",
|
||||||
|
log: (message) => stdio.push(message),
|
||||||
|
stdio: "piped",
|
||||||
|
exit: false,
|
||||||
|
})
|
||||||
|
expect(stdio.join("\n")).to.include("🦕")
|
||||||
|
expect(code).to.equal(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
Deno.test("deno task make: flags positional", async () => {
|
||||||
|
const stdio = [] as string[]
|
||||||
|
const { code } = await make({
|
||||||
|
task: "make:flags_positional",
|
||||||
|
argv: ["🦕"],
|
||||||
|
config: "tests/deno_make.jsonc",
|
||||||
|
log: (message) => stdio.push(message),
|
||||||
|
stdio: "piped",
|
||||||
|
exit: false,
|
||||||
|
})
|
||||||
|
expect(stdio.join("\n")).to.include("🦕")
|
||||||
|
expect(code).to.equal(0)
|
||||||
|
})
|
||||||
|
|
||||||
Deno.test("deno task make: exit code", async () => {
|
Deno.test("deno task make: exit code", async () => {
|
||||||
const { exit } = Deno
|
const { exit } = Deno
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -20,6 +20,34 @@
|
|||||||
"TEST_INHERIT": true
|
"TEST_INHERIT": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"make:flags_required": {
|
||||||
|
"task": "echo '$<foo>'",
|
||||||
|
"cwd": "tests",
|
||||||
|
"flags": {
|
||||||
|
"foo": {
|
||||||
|
"alias": "f",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"make:flags_defaults": {
|
||||||
|
"task": "echo '$<foo>'",
|
||||||
|
"cwd": "tests",
|
||||||
|
"flags": {
|
||||||
|
"foo": {
|
||||||
|
"default": "🦕"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"make:flags_positional": {
|
||||||
|
"task": "echo '$<0>'",
|
||||||
|
"cwd": "tests",
|
||||||
|
"flags": {
|
||||||
|
"unused": {
|
||||||
|
"description": "This flag is unused"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"make:multiline": {
|
"make:multiline": {
|
||||||
"task": ["deno help"],
|
"task": ["deno help"],
|
||||||
"cwd": "tests",
|
"cwd": "tests",
|
||||||
|
|||||||
Reference in New Issue
Block a user