TypeScript开发环境搭建(VSCode+NodeJs)自动创建

TypeScript开发环境搭建(VSCode+NodeJs)自动创建

如果只是使用VS Code + Node.js来编写TypeScript代码,可能需要以下的命令:

npm init -y
npm install typescript --save-dev
npm install @types/node --save-dev
npx tsc --init --rootDir src --outDir lib --esModuleInterop --resolveJsonModule --lib es6,dom --module commonjs
npm install --save-dev ts-node
npm install --save-dev nodemon

并像下面这样配置package.json文件,才能完成开发环境的搭建。

"scripts": {
    "start": "npm run build:live",
    "build": "tsc -p .",
    "build:live": "nodemon --watch src/**/*.ts --exec ts-node src/index.ts",
    "all": "start & build"
},

每次都做这些配置就是是不是很麻烦?那么怎么解决?编写Node.js CLI程序,这样每次创建同样的目录结构和文件时,只需要在命令行输入命令即可。

创建Node.js CLI

  1. 新建文件夹mycli。

  2. 在新建的文件夹下打开命令行,执行命令npm init -y以创建package.json文件。按以下方式配置bin字段。配置后才能在控制台使用自己创建的命令。

"bin": {
    "mycli": "./index.js"
}
  1. 在当前目录(即mycli)下创建index.js文件。这是命令要执行的文件。
  • 在文件首行写“#!Node”,表示使用node来执行该文件。(Windows系统)
  • 在文件首行写“#!/usr/bin/env node”。(Linux/Unix系统)
  • 在index.js中写上console.log("Hello World!");以展示运行效果。用实际代码替换掉即可。(之后要将这里的代码替换为创建目录及文件的代码。)
  1. 此时,已完成命令行的编写。只需将当前项目到全局环境,然后就可使用“mycli”命令了。全局安装。在当前目录(即mycli)下执行以下命令以安装。
npm install -g
  1. 使用:新建一个目录,并运行命令行,在命令行中执行mycli命令。即可看到效果。

  2. 例子:在index.js中写上创建目录及文件的代码以快速生成项目目录结构及文件。

编写NodeJs的CLI工具

参考资料:使用Node.js编写CLI工具

流程

  1. 输入项目类型及项目根目录。
  2. 根据目录(及文件)结构的模板创建文件夹和文件。
  3. 输出反馈信息。

代码

创建package.json

  1. 创建文件夹MakeProject。
  2. 运行VS Code,通过快捷键Ctrl + ~ 打开终端。在终端中运行命令npm init -y创建package.json。
  3. 配置package.json如下:
{
  "name": "mkproject",
  "version": "1.0.0",
  "description": "Create node or typescript project automatically",
  "main": "index.js",
  "scripts": {
    "start": "node index.js"
  },
  "bin": {
    "mkproject": "./index.js"
  },
  "author": "",
  "license": "ISC"
}

"name"是项目名称。
"version"是项目版本。
"description"是项目的描述。
"scripts"是npm命令脚本,如果改为上面那样,则在终端中可以通过npm run start来运行这个名为start的脚本。
name、version、description和scripts可改可不改。
bin这里一定要修改。mkproject就是之后要使用的node.js cli命令。喜欢的华可以改成自己的。

键盘输入

  1. 在MakeProject文件夹下创建lib文件夹。然后创建console.js文件。
  2. 在Node.js中从命令行接收输入。从命令行读取输入是异步地,为了让异步操作能够像同步操作一样按顺序执行这里使用了Promise
    代码如下:参考文档
const readline = require('readline');

/**
 * 提示用户在客户端进行输入。
 * @param {string} prompt 提示用户进行输入时,需要显示的信息。
 * @author kokiafan
 * @example
 * const readline = require('./lib/console');
 * 
 * async function main() {
 *     let msg = await readline("请输入一行文字:");
 *     console.log("刚刚输入的信息:%s", msg);
 * }
 * 
 * main();
 */
function readlineAsync(prompt = "Please enter: ") {

    const rl = readline.createInterface({
        input: process.stdin,
        output: process.stdout
    });

    return new Promise(resolve => {
        rl.question(prompt, (data) => {
            rl.close();
            resolve(data);
        })
    });
};

module.exports = { readlineAsync };

上面的函数头有JSDoc注释。这个可以给VS Code的Intellisence提供提示信息,有助于调用该函数的程序员使用。参考资料如下:
参考资料
JSDoc的使用:https://www.jianshu.com/p/46519b0499c3
JSDoc的文档:https://www.html.cn/doc/jsdoc/tags-example.html

JSDoc提示

文件创建

  1. 在lib文件夹下创建console.js文件。
  2. 代码如下:
const fs = require('fs');

/**
 * 传入一个目录名称字符串数组及根目录名称,异步地创建文件夹。
 * @param {string[]} directories 
 * @param {string} root 
 * @author kokiafan
 * @example
 * const io = require('./lib/io');
 * 
 * async function main() {
 *     io.createDirectories(['dist/css', 'src/css', 'src/html'], './myapps')
 *         .then(() => console.log('目录创建完成!'));
 * }
 * 
 * main();
 */
function createDirectories(directories, root) {
    return new Promise(resolve => {
        for (let dir of directories) {
            let path = root + '/' + dir;

            fs.mkdirSync(path, { recursive: true }, error => {
                if (error) {
                    console.log(error);
                }
            });
            console.log(path);
        }
        resolve(root);
    });
}

/**
 * 传入一个目录名称字符串数组及根目录名称,异步地创建文件夹。
 * @param {{filename:string, content: string}[]} files 
 * @param {string} root 
 * @author kokiafan
 * @example
 * const io = require('./lib/io');
 * 
 * const file1 = { filename: 'text1.txt', content: "hello" };
 * const file2 = { filename: 'text2.txt', content: "hello" };
 * 
 * async function main() {
 *     io.createFiles([file1, file2], '.')
 *         .then(() => console.log('文件创建完成!'));
 * }
 * 
 * main();
 */
function createFiles(files, root) {
    return new Promise(resolve => {
        for (let file of files) {
            let path = root + '/' + file.filename;

            fs.writeFile(path, file.content, 'utf8', error => {
                if (error) {
                    console.log(error);
                }
            });
            console.log(path);
        }
        resolve(root);
    });
}

module.exports = {
    createDirectories,
    createFiles
}

数据准备

  1. 简单的TypeScript项目:
  • 需要创建的文件夹及文件:MakeProject\data\ts\dirs\dirs.js,MakeProject\data\ts\files\files.js,package_json.js,src_index_ts.js,tsconfig_json.js。
  • 代码:

dirs.js

// 需要被创建的文件夹
module.exports = ['src'];

files.js

const package_json = require('./package_json');
const tsconfig_json = require('./tsconfig_json');
const index_ts = require('./src_index_ts');

// 需要被创建的文件
const files = [
    { filename: package_json.filename, content: package_json.content },
    { filename: tsconfig_json.filename, content: tsconfig_json.content },
    { filename: index_ts.filename, content: index_ts.content }
];

module.exports = files;

package_json.js

// 声明需要被创建的文件及其内容
const package_json = {
    filename: "package.json",
    content:
        `
{
    "name": "ts_app",
    "version": "1.0.0",
    "description": "",
    "main": "index.js",
    "scripts": {
        "start": "npm run build:live",
        "build": "tsc",
        "build:live": "nodemon --watch src/**/*.ts --exec ts-node src/index.ts"
    },
    "keywords": [],
    "author": "",
    "license": "ISC",
    "devDependencies": {
        "@types/node": "^14.14.16",
        "nodemon": "^2.0.6",
        "ts-node": "^9.1.1",
        "typescript": "^4.1.3"
    }
}
`
};

module.exports = package_json;

src_index_ts.js

// 声明需要被创建的文件及其内容
const src_index_ts = {
    filename: "src/index.ts",
    content:
        `
import * as fs from "fs";

const path = "./message.txt";
const data = "Hello World!";
const encoding = "utf8";

console.log(data);

fs.writeFile(path, data, encoding, error => {
    if (error) {
        console.log(error);
    }
});
`
};

module.exports = src_index_ts;

tsconfig_json.js

// 声明需要被创建的文件及其内容
const tsconfig_json = {
    filename: "tsconfig.json",
    content:
`
{
    "compilerOptions": {
      /* Visit https://aka.ms/tsconfig.json to read more about this file */
      /* Basic Options */
      // "incremental": true,                   /* Enable incremental compilation */
      "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
      "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
      "lib": [
        "es6",
        "dom"
      ], /* Specify library files to be included in the compilation. */
      // "allowJs": true,                       /* Allow javascript files to be compiled. */
      // "checkJs": true,                       /* Report errors in .js files. */
      // "jsx": "preserve",                     /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
      // "declaration": true,                   /* Generates corresponding '.d.ts' file. */
      // "declarationMap": true,                /* Generates a sourcemap for each corresponding '.d.ts' file. */
      // "sourceMap": true,                     /* Generates corresponding '.map' file. */
      // "outFile": "./",                       /* Concatenate and emit output to single file. */
      "outDir": "lib", /* Redirect output structure to the directory. */
      "rootDir": "src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
      // "composite": true,                     /* Enable project compilation */
      // "tsBuildInfoFile": "./",               /* Specify file to store incremental compilation information */
      // "removeComments": true,                /* Do not emit comments to output. */
      // "noEmit": true,                        /* Do not emit outputs. */
      // "importHelpers": true,                 /* Import emit helpers from 'tslib'. */
      // "downlevelIteration": true,            /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
      // "isolatedModules": true,               /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
      /* Strict Type-Checking Options */
      "strict": true, /* Enable all strict type-checking options. */
      // "noImplicitAny": true,                 /* Raise error on expressions and declarations with an implied 'any' type. */
      // "strictNullChecks": true,              /* Enable strict null checks. */
      // "strictFunctionTypes": true,           /* Enable strict checking of function types. */
      // "strictBindCallApply": true,           /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
      // "strictPropertyInitialization": true,  /* Enable strict checking of property initialization in classes. */
      // "noImplicitThis": true,                /* Raise error on 'this' expressions with an implied 'any' type. */
      // "alwaysStrict": true,                  /* Parse in strict mode and emit "use strict" for each source file. */
      /* Additional Checks */
      // "noUnusedLocals": true,                /* Report errors on unused locals. */
      // "noUnusedParameters": true,            /* Report errors on unused parameters. */
      // "noImplicitReturns": true,             /* Report error when not all code paths in function return a value. */
      // "noFallthroughCasesInSwitch": true,    /* Report errors for fallthrough cases in switch statement. */
      // "noUncheckedIndexedAccess": true,      /* Include 'undefined' in index signature results */
      /* Module Resolution Options */
      // "moduleResolution": "node",            /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
      // "baseUrl": "./",                       /* Base directory to resolve non-absolute module names. */
      // "paths": {},                           /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
      // "rootDirs": [],                        /* List of root folders whose combined content represents the structure of the project at runtime. */
      // "typeRoots": [],                       /* List of folders to include type definitions from. */
      // "types": [],                           /* Type declaration files to be included in compilation. */
      // "allowSyntheticDefaultImports": true,  /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
      "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
      // "preserveSymlinks": true,              /* Do not resolve the real path of symlinks. */
      // "allowUmdGlobalAccess": true,          /* Allow accessing UMD globals from modules. */
      /* Source Map Options */
      // "sourceRoot": "",                      /* Specify the location where debugger should locate TypeScript files instead of source locations. */
      // "mapRoot": "",                         /* Specify the location where debugger should locate map files instead of generated locations. */
      // "inlineSourceMap": true,               /* Emit a single file with source maps instead of having a separate file. */
      // "inlineSources": true,                 /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
      /* Experimental Options */
      // "experimentalDecorators": true,        /* Enables experimental support for ES7 decorators. */
      // "emitDecoratorMetadata": true,         /* Enables experimental support for emitting type metadata for decorators. */
      /* Advanced Options */
      "resolveJsonModule": true, /* Include modules imported with '.json' extension */
      "skipLibCheck": true, /* Skip type checking of declaration files. */
      "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
    },
    "include": [
      "src/**/*"
    ],
    "exclude": [
      "node_modules"
    ]
}
`
};

module.exports = tsconfig_json;
  1. JavaScript Mvc项目:
  • 需要创建的文件夹及文件:MakeProject\data\jsmvc\dirs\dirs.js,MakeProject\data\jsmvc\files\files.js,package_json.js,index_js.js。
  • 代码:

dirs.js

// 需要被创建的文件夹
module.exports = ['public', 'models', 'views', 'controllers', 'middleware'];

files.js

const package_json = require('./package_json');
const index_js = require('./index_js');

// 需要被创建的文件
const files = [
    { filename: package_json.filename, content: package_json.content },
    { filename: index_js.filename, content: index_js.content }
];

module.exports = files;

package_json.js

// 声明需要被创建的文件及其内容
const package_json = {
    filename: "package.json",
    content:
`{
  "name": "nodejs",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "node index.js",
    "test": "echo \\"Error: no test specified\\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
    "dependencies": {
    "express": "^4.17.1"
  }
}`
};

module.exports = package_json;

index_js.js

const index_js = {
    filename: "index.js",
    content:
`
const express = require('express');
const app = express();

const port = process.env.port || 3000;

app.get('/', (request, response) => {
    response.send('Hello World');
});

app.listen(port, () => {
    console.log(\`Express web app available at localhost: \${port}\`);
});`
}

module.exports = index_js;
  1. TypeScript项目(VSCode配置 + TypeScript + WebPack + WebPack-Cli + html-webpack + ts-loader + lite-server):
  • 需要创建的文件夹及文件:MakeProject\data\tswebpack\dirs\dirs.js,MakeProject\data\tswebpack\files\files.babelrs.js,
    , bs_config_json.js,
    ,dist_css_banner_css.js
    ,package_json.js
    ,src_css_banner_css.js
    ,src_html_index_html.js
    ,src_ts_app_ts.js
    ,src_ts_helloworld_ts.js
    ,src_ts_index_ts.js
    ,src_ts_slider_ts.js
    ,src_ts_sum_ts.js
    ,tsconfig_json.js
    ,vscode_launch_json.js
    ,vscode_taskss_json.js
    ,webpack_config_js.js

  • 代码:

dirs.js

// 需要被创建的文件夹
module.exports = ['.vscode', 'dist/css', 'src/css', 'src/html', 'src/ts'];

其余的略:

组装程序

将上面的console.js、io.js和data文件夹里的数据组合到程序中,完成整个程序。在MakeProject文件夹下创建index.js文件。代码如下:

#!Node

// #!/usr/bin/env node /linux下这样写

const consoleio = require('./lib/console');
const fsio = require('./lib/io');

/**
 * 输入项目类型的字符串,返回项目所需要的文件夹及文件。
 * @param {'ts'|'jsmvc'|'tswebpack'} projectType 用户想要的项目类型。
 * @return { dirs: string[], files: { filename: string, content: string }[] }
 * @author kokiafan
 * @example
 * let projectType = select('ts');
 */
function select(projectType) {
    if (projectType === 'ts') {
        return {
            dirs: require('./data/ts/dirs/dirs'),
            files: require('./data/ts/files/files')
        };
    }

    if (projectType === 'jsmvc') {
        return {
            dirs: require('./data/jsmvc/dirs/dirs'),
            files: require('./data/jsmvc/files/files')
        };
    }

    if (projectType === 'tswebpack') {
        return {
            dirs: require('./data/tswebpack/dirs/dirs'),
            files: require('./data/tswebpack/files/files')
        };
    }

    return null;
}

async function main() {
    let root = await consoleio.readlineAsync("请输入项目根目录:");
    let type = await consoleio.readlineAsync("请输入项目的类型:(选项ts|jsmvc|tswebpack)");
    let projectType = select(type);

    if (projectType === null) {
        console.log("项目类型错误。")
        return;
    }

    fsio.createDirectories(projectType.dirs, root)
        .then(() => fsio.createFiles(projectType.files, root));
}

main();

至此,程序写完。在终端中输入命令npm install -g安装该项目后,即可使用mkproject来创建项目了。

代码库:https://github.com/kokiafan/makeProject

posted @ 2021-01-07 18:11  kokiafan  阅读(552)  评论(0)    收藏  举报