1 Entry Point

1.1 First encounter with yargs

There is a command line entry point file quartz/bootstrap-cli.mjs, and it uses yargs node package to parse command arguments.

Also there is a shebang at the top of file:

#!/usr/bin/env -S node --no-deprecation

Argument -S means --split-string, it will split the string into independent parts, avoid passing arguments as a complete string. Using /usr/bin/env can find node executable program automatically, so it enables the project to be cross-platform. --no-deprecation shuts down all API deprecation warning of node.

yargs(hideBin(process.argv))  // hideBin(): slice process.argv from index 2
  .scriptName("quartz")       // scriptName(): set the name of our script $0
  .version(version)           // version(): show the version when argument is `--version` or `-v`
  .usage("$0 <cmd> [args]")   // usage(): $0 is replaced by script name and <cmd> will be list of command's names
  .showHelpOnFail(false)      // showHelpOnFail(): the argument `enable` is set to false, so the message won't show up
  .help()                     // help(): show the help message when argument is `--help` or `-h`

Variable version is parsed from package.json using readFileSync() and JSON.parse() to read version property in it. version is imported from quartz/cli/constants.js (Notice when importing in ESM module system, node needs to use explicit postfix .js).

/** @type {{ version: string }} */
export const { version } = JSON.parse(readFileSync("./package.json").toString());

1.2 The setup of commands in yargs

In quartz, command function imported from yargs is used as below:

// Instance of yargs ...
.command(
  // <command-name>, 
  // <command-description>,
  // <arguments-vector>,
  // <asynchronous-handler-function>
)
// Other commands of yargs

<arguments-vector> is an object defined in quartz/cli/args.js, and its structure is like below (every usage and purpose of commands will show up in following devlogs):

export const CommonArgv = {
  directory: {
    // The command argument's name, commonly full name like `--directory`
    string: true,
    // The type of current command argument
    alias: ["d"],
    // Alias list of current command argument, like `-d`
    default: "content",
    // Default value when the value is not given
    describe: "directory to look for content files",
    // Description shown in help
  },
  verbose: {
    boolean: true,
    alias: ["v"],
    default: false,
    describe: "print out extra logging information",
  },
};

CommonArgv is common parts of arguments vector of all commands. So other arguments vectors can be seen as derived from CommonArgv like:

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",
  }
};

Now using commands like ./quartz/bootstrap-cli.mjs create --help will show

❯ ./quartz/bootstrap-cli.mjs create -s --help
quartz create
 
Initialize Quartz
 
Options:
      --version    Show version number                                 [boolean]
      --help       Show help                                           [boolean]
  -d, --directory  directory to look for content files
                                                   [string] [default: "content"]
  -v, --verbose    print out extra logging information[boolean] [default: false]
  -s, --source     source directory to copy/create symlink from         [string]
  -X, --strategy   strategy to resolve links
                                    [string] [choices: "new", "copy", "symlink"]

<asynchronous-handler-function> is an asynchronous handler function defined in quartz/cli/handlers.js.