📝

エイリアス付きの TypeScript + NodeJS 環境を1発でセットアップする

2023/01/06に公開

はじめに

フレームワークを使わないちょっとしたプログラムを NodeJSTypeScript で書きたい時に毎回手作業でセットアップするのは手間なので、セットアップ用の js ファイルを作って毎回それを使っています。
自分用にメモしていたのですが、せっかくなのでzennにアップして公開したいと思います。

initialize.js

プロジェクトをセットアップしたいフォルダに以下の内容を initialize.js のような名前で保存し、 node コマンドで直接実行するだけです。

node initialize.js

実行が終わったら initialize.js は削除して下さい。
この時点で諸々のファイルが出来上がっているので、src/app.ts を開いてすぐに開発を始める事が出来ます!

yarnの場合

const fs = require("fs");
const { execSync } = require("child_process");

console.log("init npm");
execSync("yarn init -y");

console.log("edit package.json");
const p = JSON.parse(fs.readFileSync("package.json").toString());
p.main = "dist/app.js";
if (!p.scripts) p.scripts = {};
p.scripts.build = "cross-env NODE_ENV=production tsc && tsc-alias";
p.scripts.start =
  "cross-env NODE_ENV=production node dist/app.js";
p.scripts.dev =
  "cross-env NODE_ENV=development ts-node-dev -r tsconfig-paths/register --respawn src/app.ts";
fs.writeFileSync("package.json", JSON.stringify(p, null, 2));

console.log("install npm modules");
execSync("yarn add cross-env tslib");
execSync("yarn add --dev typescript @types/node ts-node ts-node-dev tsc-alias tsconfig-paths");
execSync(
  "yarn add --dev eslint @typescript-eslint/eslint-plugin @typescript-eslint/parser prettier eslint-config-prettier eslint-plugin-prettier"
);

console.log("create tsconfig.json");
fs.writeFileSync(
  "tsconfig.json",
  `{
  "compilerOptions": {
    "target": "ES2019",
    "module": "commonjs",
    "sourceMap": true,
    "outDir": "./dist",
    "importHelpers": true,
    "strict": true,
    "allowJs": true,
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    },
    "esModuleInterop": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "forceConsistentCasingInFileNames": true
  }
}`
);

console.log("create .gitignore");
fs.writeFileSync(
  ".gitignore",
  `node_modules
dist`
);

console.log("create .eslintrc.js");
fs.writeFileSync(
  ".eslintrc.js",
  `
module.exports = {
  root: true,
  env: {
    node: true
  },
  extends: [
    "eslint:recommended",
    "plugin:@typescript-eslint/eslint-recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:prettier/recommended",
    "prettier/@typescript-eslint"
  ],
  parserOptions: {
    ecmaVersion: 2020
  },
  rules: {
    "no-console": process.env.NODE_ENV === "production" ? "error" : "off",
    "no-debugger": process.env.NODE_ENV === "production" ? "error" : "off"
  }
};`
);

console.log("create entry point");
fs.mkdirSync("src");
fs.writeFileSync("src/app.ts", "");

console.log("done!");

npmの場合

const fs = require("fs");
const { execSync } = require("child_process");

console.log("init npm");
execSync("npm init -y");

console.log("edit package.json");
const p = JSON.parse(fs.readFileSync("package.json").toString());
p.main = "dist/app.js";
if (!p.scripts) p.scripts = {};
p.scripts.build = "cross-env NODE_ENV=production tsc && tsc-alias";
p.scripts.start =
  "cross-env NODE_ENV=production node dist/app.js";
p.scripts.dev =
  "cross-env NODE_ENV=development ts-node-dev -r tsconfig-paths/register --respawn src/app.ts";
fs.writeFileSync("package.json", JSON.stringify(p, null, 2));

console.log("install npm modules");
execSync("npm i -S cross-env tslib");
execSync("npm i -D typescript @types/node ts-node ts-node-dev tsc-alias tsconfig-paths");
execSync(
  "npm i -D eslint @typescript-eslint/eslint-plugin @typescript-eslint/parser prettier eslint-config-prettier eslint-plugin-prettier"
);

console.log("create tsconfig.json");
fs.writeFileSync(
  "tsconfig.json",
  `{
  "compilerOptions": {
    "target": "ES2019",
    "module": "commonjs",
    "sourceMap": true,
    "outDir": "./dist",
    "importHelpers": true,
    "strict": true,
    "allowJs": true,
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    },
    "esModuleInterop": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "forceConsistentCasingInFileNames": true
  }
}`
);

console.log("create .gitignore");
fs.writeFileSync(
  ".gitignore",
  `node_modules
dist`
);

console.log("create .eslintrc.js");
fs.writeFileSync(
  ".eslintrc.js",
  `
module.exports = {
  root: true,
  env: {
    node: true
  },
  extends: [
    "eslint:recommended",
    "plugin:@typescript-eslint/eslint-recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:prettier/recommended",
    "prettier/@typescript-eslint"
  ],
  parserOptions: {
    ecmaVersion: 2020
  },
  rules: {
    "no-console": process.env.NODE_ENV === "production" ? "error" : "off",
    "no-debugger": process.env.NODE_ENV === "production" ? "error" : "off"
  }
};`
);

console.log("create entry point");
fs.mkdirSync("src");
fs.writeFileSync("src/app.ts", "");

console.log("done!");

説明

TypeScript + NodeJS 開発のスタンダードと言える ts-node/ts-node-dev + tsconfig-paths という構成になっています。
ただそのままだと、 tsc した時に tsconfig で設定したエイリアスがそのままになってしまって実行時エラーが起こります。

そこで、この構成では tsc-alias を使用してトランスパイル後のファイルからエイリアス部分の置き替えを行っています。

https://www.npmjs.com/package/tsc-alias

おわりに

この程度であれば毎回手打ちしても良いのですが、結局毎回同じ事を手打ちするならスクリプトにした方が良いなと思って作ってみました。

tsconfig.json.eslint.js は好みで設定が変わると思いますし、他に毎回必ず入れておきたいモジュールがある人もいると思うので、色々とカスタマイズして使ってみてください。

Discussion