1 Handlers of command create
1.1 Usage of library @clack/prompts
quartz uses @clack/prompts to show the interaction in command line. intro
is the intro part of command line interface like:
┌ Quartz v1.0.0
│In entrypoint we say asynchronous handler function
defined in quartz/cli/handlers.js receives an arguments vector, so we need to
declare a type of those arguments vectors for handlers, otherwise TypeScript
server won’t take effects:
/**
* @typedef {object} CreateArgvInterface
* @property {string} directory
* @property {boolean} verbose
* @property {string} source
* @property {"new" | "copy" | "symlink"} [strategy]
* @property {string} [links]
*/
export const CreateArgv = {
...CommonArgv,
source: {
string: true,
alias: ["s"],
describe: "source directory to copy/create symlink from",
},
strategy: {
string: true,
alias: ["X"],
choices: ["new", "copy", "symlink"],
// Choices of value of current command argument
describe: "strategy to resolve links",
},
links: {
string: true,
alias: ["l"],
choices: ["absolute", "shortest", "relative"],
describe: "strategy to resolve links",
}
};and we can declare jsdoc like this for handler function handleCreate:
/**
* Handles command `npx quartz create`
* @param {import("./args.js").CreateArgvInterface} argv
*/
export async function handleCreate(argv) { /* ... */ }At the beginning of function handleCreate, it first checks if argument
--strategy and --link provided. When --strategy is not "new", we will
check the property of sourceDirectory (which is the value of argument --source),
if it’s not provided, then we exit the command line interface using outro function
of @clack/prompts:
outro(
styleText(
"red",
`Setup strategies (arg '${styleText(
"yellow",
`-${CreateArgv.strategy.alias[0]}`,
)}') other than '${styleText(
"yellow",
"new"
)}' require content folder argument ('${styleText(
"yellow",
`-${CreateArgv.source.alias[0]}`,
)}') to be set`,
),
);and the effect is like this:
└ Setup strategies (arg '-X') other than 'new' require content folder argument ('-s') to be setother errors like --source is not a directory, --source directory does not
exist, we all use outro to handle them.
If --strategy is not provided, then our command line interface will ask user to
select a strategy from "new", "copy" and "strategy". We can use the
select function of @clack/prompts:
if (!setupStrategy) {
setupStrategy = exitIfCancel(
await select({
message: `Choose how to initialize the content in \`${contentFolder}\``,
options: [
{
value: "new",
label: "Empty Quartz",
},
{
value: "copy",
label: "Copy an existing folder",
},
{
value: "symlink",
label: "Symlink an existing folder",
hint: "don't select this unless you know what you are doing!",
}
]
})
);
}and its effect is like this:
┌ Quartz v1.0.0
│
◆ Choose how to initialize the content in `/home/ashgrey/Github/Open.Quartz/content`
│ ○ Empty Quartz
│ ○ Copy an existing folder
│ ● Symlink an existing folder (don't select this unless you know what you are doing!)
└When we cancel the selection, the command line interface should also exit, so
we can introduce an util function in quartz/cli/helpers.js:
import { isCancel, outro } from "@clack/prompts";
export function exitIfCancel(val) {
if (isCancel(val)) {
outro(styleText("red", "Exiting"));
process.exit(0);
} else {
return val;
}
}1.2 First encountered bug
There is a bug of regex replacement in function escapePath, and I have reported
the issue and a
fix PR.
Original regex will convert path like "../Test Directory" to literal string
"$1" but not matched content. So I fix the function escapePath like this:
export function escapePath(fp) {
return fp
.replace(/\\ /g, " ")
.replace(/^"(.*)"$/, "$1")
.replace(/^'(.*)'$/, "$1")
.trim();
}1.3 Some new tricks for me
- After ES6, function property defined in object can omit
functionkeyword:
// Before ES6
const testObject = {
validate: function(filePath) {
// ...
}
}
// After ES6
const testObject = {
validate(filePath) {
// ...
}
}- In shell, command like
<command-a> || <command-b>will first execute commandaand if it returns non-zero return code, shell will execute commandb.