import path from "path"
import yargs from "yargs/yargs"
import { Argv } from "yargs"
import { Compiler, DartCompiler, ExecutableCompiler } from "./compiler"
export interface CliArgs {
root: string
verbose: boolean
impl: string
compiler: Compiler
interactive: boolean
testDirs: string[]
todoMode?: string
}
const implArgs: Record<string, string[]> = {
"dart-sass": ["--no-unicode", "--no-color"],
libsass: ["--style", "expanded"],
}
const usageText = `
Usage: ts-node ./sass-spec.ts [options] [spec_directory...]
This script will search for all files under the spec (or specified) directory
that are named input.scss. It will then run a specified binary and check that
the output matches the expected output.
Make sure the command you provide prints to stdout.
`.trim()
/**
* Parse command line args into options used by the sass-spec runner.
*
* @param cliArgs the arguments to parse as an array
* @param wrap an optional function to wrap the initial argv instance (useful for testing)
*/
export async function parseArgs(
cliArgs: string[],
wrap = (t: Argv<{}>) => t
): Promise<CliArgs> {
const argv = wrap(yargs(cliArgs))
.usage(usageText)
.example(
"npm run ./sass-spec.js -- spec/basic",
"Run tests only in the spec/basic folder"
)
.option("verbose", {
alias: "v",
description: "Run verbosely",
type: "boolean",
})
.option("dart", {
description: "Run Dart Sass, whose repo should be at the given path",
type: "string",
})
.option("command", {
alias: "c",
description: "Sets a specific binary to run",
type: "string",
})
.conflicts("dart", "command")
.check(({ dart, command }) => {
if (!dart && !command) {
throw new Error("Must specify --dart or --command")
} else {
return true
}
})
.option("cmd-args", {
description: "Pass args to command or Dart Sass",
type: "string",
})
.option("impl", {
description: "Sets the name of the implementation being tested.",
type: "string",
})
.options("run-todo", {
description: "Run any tests marked as todo",
type: "boolean",
})
.options("probe-todo", {
description: "Run and report tests marked as todo that unexpectedly pass",
type: "boolean",
})
.conflicts("run-todo", "probe-todo")
.option("root-path", {
description:
"The root path to start searching for tests and test configuration, and the path to pass into --load-path",
type: "string",
default: "spec",
})
.options("interactive", {
description:
"When a test fails, enter into a dialog for how to handle it",
type: "boolean",
default: false,
}).argv
const root = path.resolve(process.cwd(), argv["root-path"])
const args: Partial<CliArgs> = {
root,
verbose: argv.verbose,
interactive: argv.interactive,
testDirs: argv._,
todoMode: argv["run-todo"]
? "run"
: argv["probe-todo"]
? "probe"
: undefined,
}
args.impl = argv.dart ? "dart-sass" : argv.impl!
let cmdArgs = implArgs[args.impl] ?? []
cmdArgs.push(`--load-path=${root}`)
if (argv["cmd-args"]) {
cmdArgs = cmdArgs.concat(argv["cmd-args"].split(" "))
}
if (argv.command) {
args.compiler = new ExecutableCompiler(
path.resolve(process.cwd(), argv.command),
cmdArgs
)
}
if (argv.dart) {
const repoPath = path.resolve(process.cwd(), argv.dart)
args.compiler = await DartCompiler.fromRepo(repoPath, cmdArgs)
}
return args as CliArgs
}