sequence

Why build your own project?

This started with writing a scaffold tool for the team two days ago. During this period, I just finished building a system, which was the team’s first full-stack project in a formal sense. The team has its own front-end scaffolding, but no back-end scaffolding, so think about writing a back-end scaffolding tool for the team, so that the team can use the scaffolding to build the project when carrying out subsequent full-stack projects, avoiding the repetitive work of building the initial architecture of the project, and the members can write the business code directly.

Two days ago, I finished the first version of scaffolding with JavaScript. During the development process, I found that there was no code hint, which was very uncomfortable, and such code was not conducive to the subsequent iteration and maintenance.

So I decided to refactor in typescript, but the authorities didn’t seem to provide a proper scaffolding tool, so I started building typescript projects myself. The biggest advantage of self-construction is that it can be controlled independently. The real-time compilation development environment is integrated in the project, ESLint + Prettier ensures the uniform code quality and style, project construction tools, and the standardization of Git submission information, all of which are the most basic and necessary configuration of a project.

This is the end of the story, but a problem with refactoring scaffolding is that it repeats the scaffolding process every time a new typescript project is written, for example: One NPM package needs to be developed today, and another scaffold needs to be developed tomorrow, which seems to be too much trouble, so the whole construction process is written as a scaffold, so that in the subsequent development work can achieve one-click project creation, simple, convenient and comfortable

What can you learn from this passage?

  • How to build a typescript-based project framework
  • How to integrate ESLint and Prettier into a project to ensure code quality
  • How do I normalize Git commit information
  • How to develop your own scaffolding tools for your team

Set up the project

Initialize the project directory

mkdir ts-project && cd ts-project && npm init -y && npm i typescript -D && npx tsc --init
Copy the code

This command creates a package.json file in the ts-project directory and executes NPM init -y in the ts-project directory. Then run NPM I typescript -d to install the typescript package in the development environment and run NPX TSC –init to generate the tsconfig.json file

All subsequent operations are rooted in ts-project

mkdir src && touch src/index.ts
Copy the code

Create a new SRC directory as the source directory for the project (development directory), and create an index.ts file in SRC as the entry file for the project

Set the tsconfig. Json

Json indicates that the tsconfig.json file is the root of the typescirpt project. The tsconfig.json file specifies the root file and compilation options used to compile the project. The tsconfig.json file generated with TSC –init contains a large number of options, most of which are commented out, and generally we just need to configure the following:

{
  "compileOnSave": true."compilerOptions": {
    "target": "ES2018"."module": "commonjs"."moduleResolution": "node"."experimentalDecorators": true."emitDecoratorMetadata": true."inlineSourceMap":true."noImplicitThis": true."noUnusedLocals": true."stripInternal": true."pretty": true."declaration": true."outDir": "lib"."baseUrl": ". /"."paths": {
      "*": ["src/*"]}},"exclude": [
    "lib"."node_modules"]}Copy the code

@types/node

npm i @types/node -D
Copy the code

This is the node.js type definition package

The development environment compiles in real-time

npm i ts-node-dev -D
Copy the code

Add the following to the scripts of package.json

{
  "scripts": {
    "dev:comment": "Start the development environment"."dev": "ts-node-dev --respawn --transpile-only src/index.ts"}}Copy the code

NPM run dev starts the development environment and compiles in real time as files are modified

The code quality

Code quality for a system of maintainability, iterative is critical, especially in a large project with the people, if not the code quality control tools, each of a set of coding style, such a system in the later maintenance difficulty cans be imagined, basically will be a difficult ancestral to iterative upgrade system, in addition to no choice but to rewrite.

As a result, tools were created to control code quality, and ESLint was the best of them, killing off competitors. Typescript used TSLint until now, but in January 2019 it was officially decided to adopt ESLint as a code checking tool.

ESLint

The community’s open source configuration solution, ESlint-config-Standard, is straightforward enough to handle most projects

npx eslint --init
Copy the code

Eslintrc.js file will be created in the root directory of the project. Add the following configuration to the scripts of package.json

{
  "scripts": {
    "eslint:comment": "Use ESLint to check and automatically repair all files with the.ts extension in the SRC directory"."eslint": "eslint --fix src --ext .ts --max-warnings=0"}}Copy the code

Prettier

Perttier is a tool that focuses on uniform code formatting styles. Some might wonder, why Prettier when ESLint can standardize our code? In a nutshell, ESLint actually has two types of rules:

  • Format rules: key-spacing, comma-spacing, etc
  • Quality rules: such as the most common no-VAR, etc

The format rule is mainly to control the code style, which simply means that the code looks good and easy to read, while the quality rule is mainly to find potential bugs or places that may create bugs in the code. In simple terms, it is more concerned from the grammar level. For example, it is forbidden to use var to declare variables. Prettier focuses on formatting rules, so prettier is the more specialized prettier for formatting.

If you use ESLint and Prettier together and configure Sava Auto Fix in your editor, it makes one-click formatting extremely painful because of conflicting formatting rules, so I recommend (or don’t like) configuring ESLint and Prettier in your editor, Three reasons:

  • Don’t like to be interrupted by the editor because of format checking problems
  • It’s nice to be able to handle formatting issues through Git hooks
  • Good habits, generally written code does not exist format problems, meaning from their own coding habits triggered from the root to solve the problem

The next step is to install and configure Prettier

npm i prettier -D
Copy the code

Install the dependency needed for Prettier and add.prettierrc.js to the project directory where Prettier is installed. The recommended configuration is as follows:

module.exports = {
    // A line of up to 80 characters
    printWidth: 80.// Indent with 2 Spaces
    tabWidth: 2.// Instead of using TAB indentation, use Spaces
    useTabs: false.// A semicolon is required at the end of the line
    semi: true.// Use single quotes instead of double quotes
    singleQuote: true.// The key of the object is quoted only when necessary
    quoteProps: 'as-needed'.JSX uses double quotes instead of single quotes
    jsxSingleQuote: false.// Use a comma at the end
    trailingComma: 'all'.{foo: bar}}
    bracketSpacing: true.// JSX tag Angle brackets require line breaks
    jsxBracketSameLine: false.// Arrow functions with only one argument also need parentheses
    arrowParens: 'always'.// The range in which each file is formatted is the entire content of the file
    rangeStart: 0.rangeEnd: Infinity.// There is no need to write @prettier at the beginning of the file
    requirePragma: false.// There is no need to automatically insert @prettier at the beginning of a file
    insertPragma: false.// Use the default line folding standard
    proseWrap: 'preserve'.// Depending on the display style, HTML should be folded or not
    htmlWhitespaceSensitivity: 'css'.// Use lf for line breaks
    endOfLine: 'lf'
}
Copy the code

Add the following to the scripts of package.json

{
  "scripts": {
    "prettier:comment": "Automatically format all.ts files in SRC directory"."prettier": "prettier --write \"src/**/*.ts\""}}Copy the code

Prettier if you wanted to configure ESLint and Prettier in an editor, where eslint-config-prettier shuts off the format rule in ESLint and uses Prettier only

commitizen

In system development, if git submission instructions are accurate, it will be documented in the later collaboration and bug handling, which in a way improves the maintainability of the system. Moreover, development logs can be generated quickly according to the standard submission instructions, so as to facilitate developers or users to track the development information and function features of the project. Commitizen is a tool that implements specification submission specifications.

Use Commitizen to generate submission instructions that conform to AngularJS specifications in your project and initialize the CZ-Xconventional – Changelog adapter

npx commitizen init cz-conventional-changelog --save --save-exact
Copy the code

Initialization does three main things:

  • Install the CZ-Xcom – Changelog adapter dependency in the project
  • Save adapter dependencies in the devDependencies object of package.json
  • The config.commitizen field is added in package.json, which is used to configure the adapter path of the CZ tool

As follows:

{
  "devDependencies": {
    "cz-conventional-changelog": "^ 3.3.0"
  },
  "config": {
    "commitizen": {
      "path": "./node_modules/cz-conventional-changelog"}}}Copy the code

Next, a validation tool is installed to verify that the submitted information complies with the specification

npm i @commitlint/cli @commitlint/config-conventional -D
Copy the code

Create a new commitlint.config.js file in the root directory of the project and set the validation rules

module.exports = {
  extends: ['@commitlint/config-conventional']};Copy the code

Then add the following to the scripts of package.json

{
  "scripts": {
    "commit:comment": "Bootstrap set normalized submission information"."commit": "cz"}}Copy the code

Next, you can only use the normalized commit information. If you don’t know what the specification is, you can add. Instead of git commit, use NPM run commit to create a list of commits that meet your specifications

husky@4And lint – staged

Note how husky version 4 is used. If your project has not yet performed git init, that is, the project is not managed by Git, make sure to do git init first and then later, otherwise you will need to reinstall Husky later

npm i husky@4 lint-staged -D
Copy the code

Add the following to package.json

{
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"."commit-msg": "commitlint -E HUSKY_GIT_PARAMS"}},"lint-staged": {
    "*.ts": ["npm run eslint"."npm run prettier"]}}Copy the code

ESLint, Prettier, and Commitizen have so far only worked if the developer manually executed NPM Run xx. That doesn’t work because it’s not smart and what if the developer forgets to do it?

This is where husky and Lint-staged commands need to be introduced. This configuration simply listens for Git hook scripts and executes certain commands (Lint-staged) before pre-commit.

husky v5

Note that husky@5 is not used in the same way as version 4. If husky@5 is still used in the same way as version 4, the online solution will not work either

  • Install the husky

    npm i husky -D
    Copy the code
  • Use git hooks

    npx husky install
    Copy the code

    This step generates a.husky folder in the project root directory

  • Add hooks

    npx husky add .husky/pre-commit "npm test"
    Copy the code

    A pre-commit script file is generated in the.husky directory

Then use git commit -m “message” to see the hook take effect.

build

Since the project focuses on developing simple typescript projects that need to encapsulate their own NPM packages, there are no third-party build tools that integrate with typescript’s own build capabilities, which are easy to use and have no learning costs

Add the following to package.json

{
  "scripts": {
    "build:comment": "Build"."build": "npm run eslint && npm run prettier && rm -rf lib && tsc --build",}}Copy the code

summary

Project here ok, just as well build, although some optimization and extension, but used to develop a simple NPM package or scaffold project is enough, if necessary can be based on the above content further complement and extend again, hope everybody can get you need content from the inside.

The scaffold

Next, the whole construction process above is encapsulated into a scaffold, and the development of the scaffold is carried out in the above construction project

The preparatory work

Common tool kit

Developing a scaffold usually requires the support of several toolkits. The following toolkits were used in the project:

  • commander

    Complete Node.js command line solution, inspired by Ruby commander

  • chalk

    Decorate your terminal, add styles to your terminal text

  • shelljs

    Lets you use Unix shell commands in Node.js

  • inquirer

    A universal interactive command line user interface that collects user choices

  • clear-console

    Clear the current interface of the command line, similar to clear() on the browser console and clear on the command line

  • ora

    Further enrich your command line by adding ICONS and actions

  • download-git-repo

    Allows you to download code from git repositories using Node.js

The tools listed above are basically the tools you will need to develop a scaffold and then install the project:

npm i commander chalk shelljs inquirer clear-console -S
Copy the code

Local debugging

Add the following content to pacakge.json in the root directory of the project:

{
  "bin": {
    "ts-cli": "./bin/ts-cli.js"}}Copy the code

Bin indicates the location of the executable file of the command ts-cli. Then run NPM link in the root directory of the project and add a global link to the value path of the property bin in package.json

When a user installs a package with the bin field, NPM will use symbolic links to link these files to /usr/local/node_modules/. Bin (in the global node_modules/. Bin). /node_modules/.bin/ will be linked to if installed locally.

If you change your directory name after executing the NPM link command, it will not work in unlink, you can only manually delete the corresponding soft link in the global node_modules

Add the bin directory to the root directory of the project, and create ts-cli.js in the bin directory. The file content is as follows:

#! /usr/bin/env node

// Use index.js in the build directory (lib) as the scaffold entry
require('.. /lib/index')
Copy the code

Then the development phase was officially entered

The development of

This is a directory structure for the source code

/src/index.ts

import { program } from 'commander';
import create from './order/create';

Ts-cli -v, ts-cli --version
// Temporarily disable the rule to ensure that the version number in package.json can be obtained using the require method
/* eslint-disable @typescript-eslint/no-var-requires */
program
  .version(`The ${require('.. /package.json').version}`.'-v --version')
  .usage('<command> [options]');

// ts-cli create newPro
program
  .command('create <app-name>')
  .description('Create new project from => ts-cli create yourProjectName')
  .action(async (name: string) = > {// The creation command does exactly what it does here. Name is the newPro you specified
    await create(name);
  });

program.parse(process.argv);

Copy the code

/src/order/create.ts

/** * The task of the create command */

import {
  changePackageInfo,
  end,
  initProjectDir,
  installDevEnviroment,
  installFeature,
  installTSAndInit,
  installTypesNode,
  isFileExist,
  selectFeature,
} from '.. /utils/create';

/ / create command
export default async function create(projecrName: string) :Promise<void> {
  // Check whether the file already exists
  isFileExist(projecrName);
  // Select the desired functionality
  const feature = await selectFeature();
  // Initialize the project directory
  initProjectDir(projecrName);
  // Rewrite the basic information of the project package.json, such as name and description
  changePackageInfo(projecrName);
  Install typescript and initialize it
  installTSAndInit();
  / / install @ types/node
  installTypesNode();
  // Install the development environment to support real-time compilation
  installDevEnviroment();
  / / install feature
  installFeature(feature);
  / / end
  end(projecrName);
}

Copy the code

/src/utils/create.ts

/** * all the methods used by the create command */
import {
  getProjectPath,
  PackageJSON,
  JSON,
  printMsg,
  readJsonFile,
  writeJsonFile,
  clearConsole,
} from '.. /utils/common';
import { existsSync } from 'fs';
import { prompt } from 'inquirer';
import { blue, cyan, gray, red, yellow } from 'chalk';
import * as shell from 'shelljs';
import * as installFeatureMethod from './installFeature';

/** * Verify that the specified file already exists in the current directory. If so, exit@param Filename indicates the filename */
export function isFileExist(filename: string) :void {
  // File path
  const file = getProjectPath(filename);
  // Verify that the file already exists and push the process if it does
  if (existsSync(file)) {
    printMsg(red(`${file}Existing '));
    process.exit(1); }}/** * Interactive command line, let the user select the desired function * return ['ESLint', 'Prettier', 'CZ'] */
export async function selectFeature() :Promise<Array<string>> {
  // Clear the command line
  clearConsole();
  // Output information
  /* eslint-disable @typescript-eslint/no-var-requires */
  printMsg(blue(`TS CLI vThe ${require('.. /.. /package.json').version}`));
  printMsg('Start initializing the project:');
  printMsg(' ');
  // Select functionality, which provides a good extension mechanism for scaffolding with the following installFeature methods and./ installfeature.ts files
  // To extend other functionality in the future, simply add configuration items in the Choices array and install methods in the./ installfeature.ts file
  const { feature } = await prompt([
    {
      name: 'feature'.type: 'checkbox'.message: 'Check the features needed for your project'.choices: [{name: 'ESLint'.value: 'ESLint' },
        { name: 'Prettier'.value: 'Prettier' },
        { name: 'CZ'.value: 'CZ'},],},]);return feature as Array<string>;
}

/** * Initialize the project directory */
export function initProjectDir(projectName: string) :void {
  shell.exec(`mkdir ${projectName}`);
  shell.cd(projectName);
  shell.exec('npm init -y');
}

/** * Overwrite the name, description */ of package.json in the project
export function changePackageInfo(projectName: string) :void {
  const packageJSON: PackageJSON = readJsonFile<PackageJSON>('./package.json');
  packageJSON.name = packageJSON.description = projectName;
  writeJsonFile<PackageJSON>('./package.json', packageJSON);
}

/** * Install typescript and initialize */
export function installTSAndInit() :void {
  // Install typescript and run the TSC --init command to generate tsconfig.json
  shell.exec('npm i typescript -D && npx tsc --init');
  / / overwrite tsconfig. Json
  const tsconfigJson: JSON = {
    compileOnSave: true.compilerOptions: {
      target: 'ES2018'.module: 'commonjs'.moduleResolution: 'node'.experimentalDecorators: true.emitDecoratorMetadata: true.inlineSourceMap: true.noImplicitThis: true.noUnusedLocals: true.stripInternal: true.pretty: true.declaration: true.outDir: 'lib'.baseUrl: '/'.paths: {
        The '*': ['src/*'],}},exclude: ['lib'.'node_modules']}; writeJsonFile<JSON> ('./tsconfig.json', tsconfigJson);
  // Create the SRC directory and/SRC /index.ts
  shell.exec('mkdir src && touch src/index.ts');
}

/ * * * installation@types/node * This is the node.js type definition package */
export function installTypesNode() :void {
  shell.exec('npm i @types/node -D');
}

/** * Install the development environment to support real-time compilation */
export function installDevEnviroment() :void {
  shell.exec('npm i ts-node-dev -D');
  /** * Add the following contents to package.json scripts * "dev:comment": "start dev environment ", * "dev": "ts-node-dev --respawn --transpile-only src/index.ts" */
  const packageJson = readJsonFile<PackageJSON>('./package.json');
  packageJson.scripts['dev:comment'] = 'Start the development environment';
  packageJson.scripts['dev'] =
    'ts-node-dev --respawn --transpile-only src/index.ts';
  writeJsonFile<PackageJSON>('./package.json', packageJson);
}

/** * Install user-selected features *@param Feature list */
export function installFeature(feature: Array<string>) :void {
  feature.forEach((item) = > {
    const func = (installFeatureMethod[
      `install${item}`
    ] as unknown) as() = >void;
    func();
  });
  // Install Husky and Lint-staged
  installHusky(feature);
  // Install build tools
  installFeatureMethod.installBuild(feature);
}

/** * Install Husky and Lint-staged commands and set them up according to functionality *@param Feature List of features selected by the user */
function installHusky(feature: Array<string>) :void {
  A copy of the / / feature
  const featureBak = JSON.parse(JSON.stringify(feature));

  / / set the hook
  const hooks = {};
  // Check whether the user selected CZ, if so, set hooks
  if (featureBak.includes('CZ')) {
    hooks['commit-msg'] = 'commitlint -E HUSKY_GIT_PARAMS';
  }

  / / set lintStaged
  const lintStaged: Array<string> = [];
  if (featureBak.includes('ESLint')) {
    lintStaged.push('eslint');
  }
  if (featureBak.includes('Prettier')) {
    lintStaged.push('prettier');
  }

  installFeatureMethod.installHusky(hooks, lintStaged);
}

/** * The entire project is installed, give the user a prompt message */
export function end(projectName: string) :void {
  printMsg(`Successfully created project ${yellow(projectName)}`);
  printMsg('Get started with the following commands:');
  printMsg(' ');
  printMsg(`${gray('$')} ${cyan('cd ' + projectName)}`);
  printMsg(`${gray('$')} ${cyan('npm run dev')}`);
  printMsg(' ');
}

Copy the code

/src/utils/installFeature.ts

/** ** implement each function of the installation method */
import * as shell from 'shelljs';
import { writeFileSync } from 'fs';
import { PackageJSON, printMsg, readJsonFile, writeJsonFile } from './common';
import { red } from 'chalk';

/** * Install ESLint */
export function installESLint() :void {
  shell.exec(
    'npm i eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin -D',);/ / add. Eslintrc. Js
  const eslintrc = `module.exports = { "env": { "es2021": true, "node": true }, "extends": [ "eslint:recommended", "plugin:@typescript-eslint/recommended" ], "parser": "@typescript-eslint/parser", "parserOptions": { "ecmaVersion": 12, "sourceType": "module" }, "plugins": [ "@typescript-eslint" ], "rules": { } }; `;
  try {
    writeFileSync('./.eslintrc.js', eslintrc, { encoding: 'utf-8' });
  } catch (err) {
    printMsg(`${red('Failed to write .eslintrc.js file content')}`);
    printMsg(`${red('Please add the following content in .eslintrc.js')}`);
    printMsg(`${red(eslintrc)}`);
  }

  / / rewrite package. Json
  const packageJson = readJsonFile<PackageJSON>('./package.json');
  packageJson.scripts['eslint:comment'] =
    'Use ESLint to check and automatically repair all files with the.ts extension in SRC';
  packageJson.scripts['eslint'] = 'eslint --fix src --ext .ts --max-warnings=0';
  writeJsonFile<PackageJSON>('./package.json', packageJson);
}

/** * Install Prettier */
export function installPrettier() :void {
  shell.exec('npm i prettier -D');
  / / add. Prettierrc. Js
  const prettierrc = 'module.exports = {// a line of up to 80 characters printWidth: 80, // uses 2 Spaces to indent tabWidth: 2, // does not use TAB indentation, uses the space useTabs: False, // A semicolon at the end of a line should be used, // singleQuote: true, // The key of the object should be quoted only when necessary: 'as-needed', // JSX uses double quotation marks instead of single quotation marks jsxSingleQuote: false, // trailingComma trailingComma: 'all', // trailing Spaces inside curly braces {foo: JsxBracketSameLine: false, // Arrow function, when only one argument, also need the parenthesis arrowParens: RangeStart: 0, rangeEnd: Infinity, @prettier requirePragma: False, // Do not automatically insert @prettier insertPragma: false, // use the default line folding standard proseWrap: 'preserve, / / according to display style decided to don't fold line htmlWhitespaceSensitivity HTML:' CSS ', / / a newline using lf endOfLine: 'lf'}; `;
  try {
    writeFileSync('./.prettierrc.js', prettierrc, { encoding: 'utf-8' });
  } catch (err) {
    printMsg(`${red('Failed to write .prettierrc.js file content')}`);
    printMsg(`${red('Please add the following content in .prettierrc.js')}`);
    printMsg(`${red(prettierrc)}`);
  }
  / / rewrite package. Json
  const packageJson = readJsonFile<PackageJSON>('./package.json');
  packageJson.scripts['prettier:comment'] =
    'Automatically format all.ts files in SRC directory';
  packageJson.scripts['prettier'] = 'prettier --write "src/**/*.ts"';
  writeJsonFile<PackageJSON>('./package.json', packageJson);
}

/** ** Install CZ, standardize git commit information */
export function installCZ() :void {
  shell.exec(
    'npx commitizen init cz-conventional-changelog --save --save-exact',); shell.exec('npm i @commitlint/cli @commitlint/config-conventional -D');
  / / add commitlint. Config. Js
  const commitlint = `module.exports = { extends: ['@commitlint/config-conventional'] }; `;
  try {
    writeFileSync('./commitlint.config.js', commitlint, { encoding: 'utf-8' });
  } catch (err) {
    printMsg(`${red('Failed to write commitlint.config.js file content')}`);
    printMsg(
      `${red('Please add the following content in commitlint.config.js')}`,); printMsg(`${red(commitlint)}`);
  }
  / / rewrite package. Json
  const packageJson = readJsonFile<PackageJSON>('./package.json');
  packageJson.scripts['commit:comment'] = 'Bootstrap set normalized submission information';
  packageJson.scripts['commit'] = 'cz';
  writeJsonFile<PackageJSON>('./package.json', packageJson);
}

/** * Install Husky and Lint-staged validations automatically when Git commit *@param Hooks, which need to be automatically executed@param LintStaged commands that require the hook to run */
export function installHusky(
  hooks: { [key: string] :string },
  lintStaged: Array<string>,
) :void {
  // Initialize git repository
  shell.exec('git init');
  // While installing Husky and Lint-staged
  shell.exec('npm i husky lint-staged -D');
  / / set package. Json
  const packageJson = readJsonFile<PackageJSON>('./package.json');
  packageJson['husky'] = {
    hooks: {
      'pre-commit': 'lint-staged'. hooks, }, }; packageJson['lint-staged'] = {
    '*.ts': lintStaged.map((item) = > `npm run ${item}`),}; writeJsonFile<PackageJSON>('./package.json', packageJson);
}

/** * Install build tools, currently used for small projects, so use typescript's native build capabilities */
export function installBuild(feature: Array<string>) :void {
  / / set package. Json
  const packageJson = readJsonFile<PackageJSON>('./package.json');
  packageJson.scripts['build:comment'] = 'build';
  let order = ' ';
  if (feature.includes('ESLint')) {
    order += 'npm run eslint';
  }
  if (feature.includes('Prettier')) {
    order += ' && npm run prettier';
  }
  order += ' && rm -rf lib && tsc --build';
  packageJson.scripts['build'] = order;
  writeJsonFile<PackageJSON>('./package.json', packageJson);
}

Copy the code

/src/utils/common.ts

/** * put some generic tool methods */
import { readFileSync, writeFileSync } from 'fs';
import { resolve } from 'path';
import * as clear from 'clear-console';

export interface PackageJSON {
  name: string;
  version: string;
  description: string;
  scripts: {
    [key: string] :string;
  };
}

export interface JSON {
  [key: string]: unknown;
}

/** * Read the json file * in the specified path@param Filename Specifies the path of the JSON file */
export function readJsonFile<T> (filename: string) :T {
  return JSON.parse(readFileSync(filename, { encoding: 'utf-8'.flag: 'r' }));
}

/** * overwrites the json file * in the specified path@param Filename Path of the JSON file *@param Content Json content */
export function writeJsonFile<T> (filename: string, content: T) :void {
  writeFileSync(filename, JSON.stringify(content, null.2));
}

/** * Get the absolute path of the project *@param ProjectName projectName */
export function getProjectPath(projectName: string) :string {
  return resolve(process.cwd(), projectName);
}

/** * Prints information *@param MSG information * /
export function printMsg(msg: string) :void {
  console.log(msg);
}

/** * Empty the command line */
export function clearConsole() :void {
  clear();
}

Copy the code

build

Execute NPM run build to build, check code quality and style during build, some problems can be fixed automatically, some problems can not be fixed manually as prompted, and then rebuild

After the build is complete, find a test directory and run ts-cli -v or ts-cli –version to check the scaffolding version 👌

Execute TS-CLI create test to create a typescript project named test

Published to the NPM

To prepare

Modify the following content in package.json

{
  "name": "@liyongning/ts-cli"
  "main": "./lib/index.js"."keywords": ["typescript"."cli"."Typescript scaffolding"."Ts Scaffolding"."ts-cli".scaffolding]."author": "Li Yongning"."files": ["package.json"."README.md"."lib"]."repository": {
    "type": "git"."url": "https://github.com/liyongning/ts-cli.git"}},Copy the code
  • Name: indicates the package name. You can add your OWN NPM account name before the package name. NPM Scope is used to organize the package directory in a different way from ordinary packages and effectively avoid package name conflicts with others
  • Main: indicates the entry position of the package
  • Keywords: other people search your bag
  • Files: tells NPM which packages to publish to the NPM repository
  • Repository: project repository

NPM account name, password needless to say, essential

release

Add a publish script publish.sh to the project root directory

#! /bin/bash

echo 'Start building scaffolding'

npm run build

echo 'Scaffolding built, now ready for release'

npm publish --access public
Copy the code

To start publishing, run the following command from the project root directory one time:

npm login
Copy the code

Enter related information as prompted, and then run the following command to publish information

sh publish.sh
Copy the code

Check it out at NPM

The end of the

Well, this is the end of the project and scaffolding package, although there are some areas that can be optimized and extended, but the scaffolding is more than adequate for developing a simple library or scaffolding project

You can extend it if you want, because scaffolding has built-in extensibility, so it’s easy to add new functionality to an existing create command, or to add a new command, so you can redevelop it for your own needs

The source code has been uploaded to Github and scaffolding has been posted to NPM

reference

Build a TypeScript project quickly

Configure TypeScript projects from scratch

commitizen/cz-cli

Git commit specification

leoforfree/cz-customizable

Frikki/validate-commit-message

Getting started with TypeScript – Code Checking

ESLint application practices in medium and large teams

TypeScript

standard/eslint-config-standard