👏
NestJS + SWC + Vitest + TypeScript(Bundler) 構成
はじめに
- GitHub 見ても、同構成をしている Repo が見つからなかったので投稿。
- Claude 3.5 も全く解決できなかった。(というかだいぶ嘘つかれた)
発端
vitest に怒られちゃった...
The CJS build of Vite's Node API is deprecated. See https://vitejs.dev/guide/troubleshooting.html#vite-cjs-node-api-deprecated for more details.
CJS はやめとけよ、と。
前提
- ESM 形式にするが、tsファイルの import の中に 拡張子を絶対に含めない。(美しさを著しく損ねるため)
- ほんとダメ
import { Module } from '@nestjs/common';
import { AppController } from './app.controller.js';
import { AppService } from './app.service.js';
構成
tsconfig.json
"compilerOptions": {
"module": "ESNext",
"moduleResolution": "Bundler",
"target": "ESNext",
- 神の Bundler 利用。
.swcrc
{
"$schema": "https://json.schemastore.org/swcrc",
"module": {
"type": "es6",
"resolveFully": true
},
"sourceMaps": true,
"jsc": {
"parser": {
"syntax": "typescript",
"decorators": true,
"dynamicImport": true
},
"baseUrl": "./"
},
"minify": false
}
ポイント
- resolveFully: true を指定しないと絶対に動かない。ERR_MODULE_NOT_FOUND で 永久に苦しめられる。
> nest start --builder swc
> SWC Running...
Successfully compiled: 5 files with swc (56.88ms)
node:internal/modules/esm/resolve:265
throw new ERR_MODULE_NOT_FOUND(
^
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/root/.ghq_src/github.com/TakashiAihara/redd/dist/app.module' imported from /root/.ghq_src/github.com/TakashiAihara/redd/dist/main.js
at finalizeResolution (node:internal/modules/esm/resolve:265:11)
at moduleResolve (node:internal/modules/esm/resolve:933:10)
at defaultResolve (node:internal/modules/esm/resolve:1157:11)
at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:383:12)
at ModuleLoader.resolve (node:internal/modules/esm/loader:352:25)
at ModuleLoader.getModuleJob (node:internal/modules/esm/loader:227:38)
at ModuleWrap.<anonymous> (node:internal/modules/esm/module_job:87:39)
at link (node:internal/modules/esm/module_job:86:36) {
code: 'ERR_MODULE_NOT_FOUND',
url: 'file:///root/.ghq_src/github.com/TakashiAihara/redd/dist/app.module'
}
- resolveFully により、ビルド後に自動的に拡張子を含めてくれる。
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
↓↓↓
import { Module } from "@nestjs/common";
import { AppController } from "./app.controller.js";
import { AppService } from "./app.service.js";
nest-cli.json
{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"deleteOutDir": true,
"tsConfigPath": "tsconfig.json",
"builder": {
"typeCheck": true,
"type": "swc",
"options": {
"swcrcPath": ".swcrc"
}
}
}
}
- ありがちな感じで。
- .swcrc を明示。
vitest.config.ts
import swc from 'unplugin-swc';
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
globals: true,
root: './',
watch: false,
},
plugins: [
swc.vite({
jsc: {
parser: {
syntax: 'typescript',
tsx: true,
dynamicImport: true,
decorators: true,
},
transform: {
legacyDecorator: true,
decoratorMetadata: true,
react: {
runtime: 'automatic',
},
},
target: 'esnext',
loose: false,
},
module: { type: 'es6' },
}),
],
});
おわりに
- VSCode は tsconfig.json で解決
- SWC は .swcrc で解決
みたいに、それぞれ見るものが違う感じになるので、成果物ベースで正しさを担保する必要があるのは辛いですね。
Discussion