feat: support CLI arguments for npx quartz create
(#421)
* feat(cli): add new args for content + link resolve * feat(cli): validate cmd args * feat(cli): add chalk + error code to errors * feat(cli): support for setup/link via args * refactor(cli): use yargs choices instead of manual Scrap manual check if arguments are valid, use yargs "choices" field instead. * feat(cli): add in-dir argument+ handle errors add new "in-directory" argument, used if "setup" is "copy" or "symlink" to determine source. add error handling for invalid permutations of arguments or non existent path * feat(cli): dynamically use cli or provided args use "in-directory" arg as `originalFolder` if available, otherwise get it from manual cli process * run format * fix: use process.exit instead of return * refactor: split CommonArgv and CreateArgv * refactor(cli): rename create args, use ${} syntax * fix(cli): fix link resolution strategy arg * format * feat(consistency): allow partial cmd args
This commit is contained in:
parent
74c3ebb7bd
commit
ad4145fb10
@ -43,6 +43,27 @@ const CommonArgv = {
|
||||
},
|
||||
}
|
||||
|
||||
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"],
|
||||
describe: "strategy for content folder setup",
|
||||
},
|
||||
links: {
|
||||
string: true,
|
||||
alias: ["l"],
|
||||
choices: ["absolute", "shortest", "relative"],
|
||||
describe: "strategy to resolve links",
|
||||
},
|
||||
}
|
||||
|
||||
const SyncArgv = {
|
||||
...CommonArgv,
|
||||
commit: {
|
||||
@ -147,24 +168,73 @@ yargs(hideBin(process.argv))
|
||||
.scriptName("quartz")
|
||||
.version(version)
|
||||
.usage("$0 <cmd> [args]")
|
||||
.command("create", "Initialize Quartz", CommonArgv, async (argv) => {
|
||||
.command("create", "Initialize Quartz", CreateArgv, async (argv) => {
|
||||
console.log()
|
||||
intro(chalk.bgGreen.black(` Quartz v${version} `))
|
||||
const contentFolder = path.join(cwd, argv.directory)
|
||||
const 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", hint: "overwrites `content`" },
|
||||
{
|
||||
value: "symlink",
|
||||
label: "Symlink an existing folder",
|
||||
hint: "don't select this unless you know what you are doing!",
|
||||
},
|
||||
],
|
||||
}),
|
||||
)
|
||||
let setupStrategy = argv.strategy?.toLowerCase()
|
||||
let linkResolutionStrategy = argv.links?.toLowerCase()
|
||||
const sourceDirectory = argv.source
|
||||
|
||||
// If all cmd arguments were provided, check if theyre valid
|
||||
if (setupStrategy && linkResolutionStrategy) {
|
||||
// If setup isn't, "new", source argument is required
|
||||
if (setupStrategy !== "new") {
|
||||
// Error handling
|
||||
if (!sourceDirectory) {
|
||||
outro(
|
||||
chalk.red(
|
||||
`Setup strategies (arg '${chalk.yellow(
|
||||
`-${CreateArgv.strategy.alias[0]}`,
|
||||
)}') other than '${chalk.yellow(
|
||||
"new",
|
||||
)}' require content folder argument ('${chalk.yellow(
|
||||
`-${CreateArgv.source.alias[0]}`,
|
||||
)}') to be set`,
|
||||
),
|
||||
)
|
||||
process.exit(1)
|
||||
} else {
|
||||
if (!fs.existsSync(sourceDirectory)) {
|
||||
outro(
|
||||
chalk.red(
|
||||
`Input directory to copy/symlink 'content' from not found ('${chalk.yellow(
|
||||
sourceDirectory,
|
||||
)}', invalid argument "${chalk.yellow(`-${CreateArgv.source.alias[0]}`)})`,
|
||||
),
|
||||
)
|
||||
process.exit(1)
|
||||
} else if (!fs.lstatSync(sourceDirectory).isDirectory()) {
|
||||
outro(
|
||||
chalk.red(
|
||||
`Source directory to copy/symlink 'content' from is not a directory (found file at '${chalk.yellow(
|
||||
sourceDirectory,
|
||||
)}', invalid argument ${chalk.yellow(`-${CreateArgv.source.alias[0]}`)}")`,
|
||||
),
|
||||
)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use cli process if cmd args werent provided
|
||||
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", hint: "overwrites `content`" },
|
||||
{
|
||||
value: "symlink",
|
||||
label: "Symlink an existing folder",
|
||||
hint: "don't select this unless you know what you are doing!",
|
||||
},
|
||||
],
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
async function rmContentFolder() {
|
||||
const contentStat = await fs.promises.lstat(contentFolder)
|
||||
@ -177,23 +247,28 @@ yargs(hideBin(process.argv))
|
||||
|
||||
await fs.promises.unlink(path.join(contentFolder, ".gitkeep"))
|
||||
if (setupStrategy === "copy" || setupStrategy === "symlink") {
|
||||
const originalFolder = escapePath(
|
||||
exitIfCancel(
|
||||
await text({
|
||||
message: "Enter the full path to existing content folder",
|
||||
placeholder:
|
||||
"On most terminal emulators, you can drag and drop a folder into the window and it will paste the full path",
|
||||
validate(fp) {
|
||||
const fullPath = escapePath(fp)
|
||||
if (!fs.existsSync(fullPath)) {
|
||||
return "The given path doesn't exist"
|
||||
} else if (!fs.lstatSync(fullPath).isDirectory()) {
|
||||
return "The given path is not a folder"
|
||||
}
|
||||
},
|
||||
}),
|
||||
),
|
||||
)
|
||||
let originalFolder = sourceDirectory
|
||||
|
||||
// If input directory was not passed, use cli
|
||||
if (!sourceDirectory) {
|
||||
originalFolder = escapePath(
|
||||
exitIfCancel(
|
||||
await text({
|
||||
message: "Enter the full path to existing content folder",
|
||||
placeholder:
|
||||
"On most terminal emulators, you can drag and drop a folder into the window and it will paste the full path",
|
||||
validate(fp) {
|
||||
const fullPath = escapePath(fp)
|
||||
if (!fs.existsSync(fullPath)) {
|
||||
return "The given path doesn't exist"
|
||||
} else if (!fs.lstatSync(fullPath).isDirectory()) {
|
||||
return "The given path is not a folder"
|
||||
}
|
||||
},
|
||||
}),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
await rmContentFolder()
|
||||
if (setupStrategy === "copy") {
|
||||
@ -217,29 +292,32 @@ See the [documentation](https://quartz.jzhao.xyz) for how to get started.
|
||||
)
|
||||
}
|
||||
|
||||
// get a preferred link resolution strategy
|
||||
const linkResolutionStrategy = exitIfCancel(
|
||||
await select({
|
||||
message: `Choose how Quartz should resolve links in your content. You can change this later in \`quartz.config.ts\`.`,
|
||||
options: [
|
||||
{
|
||||
value: "absolute",
|
||||
label: "Treat links as absolute path",
|
||||
hint: "for content made for Quartz 3 and Hugo",
|
||||
},
|
||||
{
|
||||
value: "shortest",
|
||||
label: "Treat links as shortest path",
|
||||
hint: "for most Obsidian vaults",
|
||||
},
|
||||
{
|
||||
value: "relative",
|
||||
label: "Treat links as relative paths",
|
||||
hint: "for just normal Markdown files",
|
||||
},
|
||||
],
|
||||
}),
|
||||
)
|
||||
// Use cli process if cmd args werent provided
|
||||
if (!linkResolutionStrategy) {
|
||||
// get a preferred link resolution strategy
|
||||
linkResolutionStrategy = exitIfCancel(
|
||||
await select({
|
||||
message: `Choose how Quartz should resolve links in your content. You can change this later in \`quartz.config.ts\`.`,
|
||||
options: [
|
||||
{
|
||||
value: "absolute",
|
||||
label: "Treat links as absolute path",
|
||||
hint: "for content made for Quartz 3 and Hugo",
|
||||
},
|
||||
{
|
||||
value: "shortest",
|
||||
label: "Treat links as shortest path",
|
||||
hint: "for most Obsidian vaults",
|
||||
},
|
||||
{
|
||||
value: "relative",
|
||||
label: "Treat links as relative paths",
|
||||
hint: "for just normal Markdown files",
|
||||
},
|
||||
],
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
// now, do config changes
|
||||
const configFilePath = path.join(cwd, "quartz.config.ts")
|
||||
|
Loading…
Reference in New Issue
Block a user