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
29c4087dea
commit
0688a2415f
@ -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 = {
|
const SyncArgv = {
|
||||||
...CommonArgv,
|
...CommonArgv,
|
||||||
commit: {
|
commit: {
|
||||||
@ -147,24 +168,73 @@ yargs(hideBin(process.argv))
|
|||||||
.scriptName("quartz")
|
.scriptName("quartz")
|
||||||
.version(version)
|
.version(version)
|
||||||
.usage("$0 <cmd> [args]")
|
.usage("$0 <cmd> [args]")
|
||||||
.command("create", "Initialize Quartz", CommonArgv, async (argv) => {
|
.command("create", "Initialize Quartz", CreateArgv, async (argv) => {
|
||||||
console.log()
|
console.log()
|
||||||
intro(chalk.bgGreen.black(` Quartz v${version} `))
|
intro(chalk.bgGreen.black(` Quartz v${version} `))
|
||||||
const contentFolder = path.join(cwd, argv.directory)
|
const contentFolder = path.join(cwd, argv.directory)
|
||||||
const setupStrategy = exitIfCancel(
|
let setupStrategy = argv.strategy?.toLowerCase()
|
||||||
await select({
|
let linkResolutionStrategy = argv.links?.toLowerCase()
|
||||||
message: `Choose how to initialize the content in \`${contentFolder}\``,
|
const sourceDirectory = argv.source
|
||||||
options: [
|
|
||||||
{ value: "new", label: "Empty Quartz" },
|
// If all cmd arguments were provided, check if theyre valid
|
||||||
{ value: "copy", label: "Copy an existing folder", hint: "overwrites `content`" },
|
if (setupStrategy && linkResolutionStrategy) {
|
||||||
{
|
// If setup isn't, "new", source argument is required
|
||||||
value: "symlink",
|
if (setupStrategy !== "new") {
|
||||||
label: "Symlink an existing folder",
|
// Error handling
|
||||||
hint: "don't select this unless you know what you are doing!",
|
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() {
|
async function rmContentFolder() {
|
||||||
const contentStat = await fs.promises.lstat(contentFolder)
|
const contentStat = await fs.promises.lstat(contentFolder)
|
||||||
@ -177,23 +247,28 @@ yargs(hideBin(process.argv))
|
|||||||
|
|
||||||
await fs.promises.unlink(path.join(contentFolder, ".gitkeep"))
|
await fs.promises.unlink(path.join(contentFolder, ".gitkeep"))
|
||||||
if (setupStrategy === "copy" || setupStrategy === "symlink") {
|
if (setupStrategy === "copy" || setupStrategy === "symlink") {
|
||||||
const originalFolder = escapePath(
|
let originalFolder = sourceDirectory
|
||||||
exitIfCancel(
|
|
||||||
await text({
|
// If input directory was not passed, use cli
|
||||||
message: "Enter the full path to existing content folder",
|
if (!sourceDirectory) {
|
||||||
placeholder:
|
originalFolder = escapePath(
|
||||||
"On most terminal emulators, you can drag and drop a folder into the window and it will paste the full path",
|
exitIfCancel(
|
||||||
validate(fp) {
|
await text({
|
||||||
const fullPath = escapePath(fp)
|
message: "Enter the full path to existing content folder",
|
||||||
if (!fs.existsSync(fullPath)) {
|
placeholder:
|
||||||
return "The given path doesn't exist"
|
"On most terminal emulators, you can drag and drop a folder into the window and it will paste the full path",
|
||||||
} else if (!fs.lstatSync(fullPath).isDirectory()) {
|
validate(fp) {
|
||||||
return "The given path is not a folder"
|
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()
|
await rmContentFolder()
|
||||||
if (setupStrategy === "copy") {
|
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
|
// Use cli process if cmd args werent provided
|
||||||
const linkResolutionStrategy = exitIfCancel(
|
if (!linkResolutionStrategy) {
|
||||||
await select({
|
// get a preferred link resolution strategy
|
||||||
message: `Choose how Quartz should resolve links in your content. You can change this later in \`quartz.config.ts\`.`,
|
linkResolutionStrategy = exitIfCancel(
|
||||||
options: [
|
await select({
|
||||||
{
|
message: `Choose how Quartz should resolve links in your content. You can change this later in \`quartz.config.ts\`.`,
|
||||||
value: "absolute",
|
options: [
|
||||||
label: "Treat links as absolute path",
|
{
|
||||||
hint: "for content made for Quartz 3 and Hugo",
|
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: "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",
|
value: "relative",
|
||||||
},
|
label: "Treat links as relative paths",
|
||||||
],
|
hint: "for just normal Markdown files",
|
||||||
}),
|
},
|
||||||
)
|
],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// now, do config changes
|
// now, do config changes
|
||||||
const configFilePath = path.join(cwd, "quartz.config.ts")
|
const configFilePath = path.join(cwd, "quartz.config.ts")
|
||||||
|
Loading…
Reference in New Issue
Block a user