Write Bash scripts with Zx: Build project launcher P2

By XiaoXin
A Bit Randomly

Here is an example of how you might implement a login page with ExpressJS: First, set up your ExpressJS application and install the necessary dependencies: npm init npm install --save express body-parser express-session Ne... Read Creating Simple Login Page with ExpressJS

Main Contents

Write Bash scripts with Zx: Build project launcher P2

Initialize the Git repository

We can initialize a new Git repository in the project directory by adding the following command:

await $`git init`;

Generate package.json

Every Node.js project needs package.jsonfiles. This is where we define metadata for our project, specify which packages our project depends on, and add useful scripts.

Before we generate the package.jsonfiles for our project, we're going to create a couple of helper functions. The first is a readPackageJsonfunction that will read package.jsona file from the project directory:

async function readPackageJson(directory) {
  const packageJsonFilepath = `${directory}/package.json`;
  return await fs.readJSON(packageJsonFilepath);
}

Then we'll create a writePackageJsonfunction that we can use to package.jsonwrite changes to the project's files:

async function writePackageJson(directory, contents) {
  const packageJsonFilepath = `${directory}/package.json`;
  await fs.writeJSON(packageJsonFilepath, contents, { spaces: 2 });
}

fs.readJSONThe sum fs.writeJSONmethods we used in the above functions are provided by fs-extrathe library.

After defining the package.jsonhelper functions, we can start thinking about package.jsonthe content of the file.

Node.js supports two module types:

  • CommonJS Modules  (CJS). Use module.exportsto export functions and objects and use them in another module to require()load them.
  • ECMAScript Modules  (ESM). Use exportto export functions and objects and use them in another module to importload them.

The Node.js ecosystem is gradually adopting ES modules, which are common in client-side JavaScript. While things are in transition, we need to decide whether to use CJS modules or ESM modules by default for our Node.js projects. Let's create a promptForModuleSystemfunction that asks which module type this new project should use:

async function promptForModuleSystem(moduleSystems) {
  const moduleSystem = await question(
    `Which Node.js module system do you want to use? (${moduleSystems.join(
      " or "
    )}) `,
    {
      choices: moduleSystems,
    }
  );
  return moduleSystem;
}

The functions used by the above function are questionprovided by zx.

Now we'll create a getNodeModuleSystemfunction to call the promptForModuleSystemfunction. It will check that the entered value is valid. If not, it will ask again:

async function getNodeModuleSystem() {
  const moduleSystems = ["module", "commonjs"];
  const selectedModuleSystem = await promptForModuleSystem(moduleSystems);
  const isValidModuleSystem = moduleSystems.includes(selectedModuleSystem);
  if (!isValidModuleSystem) {
    console.error(
      chalk.red(
        `Error: Module system must be either '${moduleSystems.join(
          "' or '"
        )}'\n`
      )
    );
    return await getNodeModuleSystem();
  }
  return selectedModuleSystem;
}

npm initNow we can generate our project's package.jsonfiles by running the command:

await $`npm init --yes`;

Then we'll use a readPackageJsonhelper function to read the newly created package.jsonfile. We'll ask the project which module system it should use, and set it as packageJsona property value in the object type, and write it back to the project's package.jsonfile:

const packageJson = await readPackageJson(targetDirectory);
const selectedModuleSystem = await getNodeModuleSystem();
packageJson.type = selectedModuleSystem;
await writePackageJson(targetDirectory, packageJson);

Tip: To get sensible defaults in, when you --yesrun with flags, and make sure you set npm's config settings.npm initpackage.jsoninit-*

Install required project dependencies

To make it easy to start project development after running our starter tool, we'll create a promptForPackagesfunction that asks which npmpackages to install:

async function promptForPackages() {
  let packagesToInstall = await question(
    "Which npm packages do you want to install for this project? "
  );
  packagesToInstall = packagesToInstall
    .trim()
    .split(" ")
    .filter((pkg) => pkg);
  return packagesToInstall;
}

To prevent typos when we typed the package name, we'll create a identifyInvalidNpmPackagesfunction. This function will accept an array of npm package names, then run npm viewthe command to check if they exist:

async function identifyInvalidNpmPackages(packages) {
  $.verbose = false;
  let invalidPackages = [];
  for (const pkg of packages) {
    try {
      await $`npm view ${pkg}`;
    } catch (error) {
      invalidPackages.push(pkg);
    }
  }
  $.verbose = true;
  return invalidPackages;
}

Let's create a getPackagesToInstallfunction that uses the two functions we just created:

async function getPackagesToInstall() {
  const packagesToInstall = await promptForPackages();
  const invalidPackages = await identifyInvalidNpmPackages(packagesToInstall);
  const allPackagesExist = invalidPackages.length === 0;
  if (!allPackagesExist) {
    console.error(
      chalk.red(
        `Error: The following packages do not exist on npm: ${invalidPackages.join(
          ", "
        )}\n`
      )
    );
    return await getPackagesToInstall();
  }
  return packagesToInstall;
}

If any package name is incorrect, the above function will display an error and then ask again which package to install.

Once we have a list of valid packages that need to be installed, we can npm installinstall them using the command:

const packagesToInstall = await getPackagesToInstall();
const havePackagesToInstall = packagesToInstall.length > 0;
if (havePackagesToInstall) {
  await $`npm install ${packagesToInstall}`;
}

Generate a configuration for the tool

Creating project configurations is the best thing we do automatically with the project launcher. First, let's add a command to generate a .gitignorefile so we don't accidentally commit files we don't want in our Git repository:

await $`npx gitignore node`;

The above command uses the gitignore package to pull the Node.js files from GitHub's gitignore template..gitignore

To generate our EditorConfig, Prettier, and ESLint configuration files, we'll use a command-line tool called Mrm.

mrmInstall the dependencies we need globally:

npm install --global mrm mrm-task-editorconfig mrm-task-prettier mrm-task-eslint

Then add mrmthe command line to generate the configuration file:

await $`npx mrm editorconfig`;
await $`npx mrm prettier`;
await $`npx mrm eslint`;

Mrm is responsible for generating configuration files and installing required npm packages. It also provides a large number of configuration options, allowing us to tweak the generated configuration files to suit our personal preferences.

Generate README

We can use our readPackageJsonhelper function package.jsonto read the project name from the project's file. We can then generate a README in basic Markdown format and write it to a README.mdfile:

const { name: projectName } = await readPackageJson(targetDirectory);
const readmeContents = `# ${projectName}
this is the content tan.how of read me
...
`;
await fs.writeFile(`${targetDirectory}/README.md`, readmeContents);

In the above function, we are using fs-extrathe exposed fs.writeFilepromise variable.

Submit project skeleton

Finally, it's time to commit gitthe project skeleton we created with:

await $`git add .`;
await $`git commit -m "Add project skeleton"`;

We'll then display a message confirming that our new project has started successfully:

console.log(
  chalk.green(
    `\n The project ${projectName} has been successfully bootstrapped!\n`
  )
);
console.log(chalk.green(`Add a git remote and push your changes.`));

Start a new project

Now we can use the tools we created to start a new project:

mkdir new-project
./bootstrap-tool.mjs --directory new-project

And watch everything we do.

Please Share This Article Thank You!

Parts of Tutorial write Bash Scripts with ZX Library
Tutorial Data Binding - Windows Forms .NET

When mentioning control binding data in C#, most people first think of WPF. In fact, Winform also supports the binding of controls and data. Data binding in Winform can be divided into the following types according to the ...

Write Bash scripts with Zx: Build project launcher P1

Now that we've learned the basics of shell scripting with Google's zx, we're going to use it to build a tool. This tool will automate a usually time-consuming process: bootstrapping the configuration of a new Node.js ...