0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

[ TypeScript ] fs などで相対パスを使うとビルド時にパスがずれちゃう問題の処方箋

Posted at

症状

ある日、いつものように TypeScript を使って開発をしていました。
フォルダ構造はこんな感じ。

project
├ src
│ └ test.ts
└ resource
  └ data.json

srcの中にソースコードを置き、resourceの中にデータを設置する、よくあるプロジェクトです。

ビルド設定として、buildフォルダ内にビルド結果を出力するようにします。
結果としてはこんな感じ。

project
	src
		test.ts
	resource
		data.json
+ 	build
+		src
+			test.ts

たとえばsrc/test.tsに書いたコードは、ビルドを通してbuild/src/test.jsに置かれる、といった具合ですね。

そしたらコーディングをしていきます。
test.tsの中身はこんな感じ。

test.ts
import fs from 'fs';
import path from 'path';

// 相対パス
const filePath = path.join(__dirname, '../resource/deck.json');

// data.json ファイルを読み込み
let fileContents = fs.readFileSync(filePath, 'utf8');
let data = JSON.parse(fileContents);

fs モジュールで json ファイルの中身を読み込む処理です。
パスは path モジュールを使った、test.ts からの相対パスで書いています。

そしたら実行してみます。
npx tsc して、npm run start すると...

Error: ENOENT: no such file or directory, open 'C:\hogehoge\project\build\src\resource\deck.json'

あれれ...エラーが出てしまいました。

原因

このエラーが発生する原因は、相対パスにあります。
ビルド前のtest.tsとビルド後のtest.jsの、data.jsonまでの相対パスを比較してみます。

  • test.ts ( ビルド前 ) : ../resource/data.json
  • test.js ( ビルド後 ) : ../../resource/data.json

そう、原因はここにあります。
ビルドファイルのほうが階層が一つ深いために、元のコードとずれが生じてしまうわけですね。

対処法

もっともシンプルなアイデアとして、 resourceフォルダをビルド時にbuildフォルダにコピーする というのがあります。

「相対パスの先にフォルダがないなら作っちゃえばいいじゃん!」って話ですね。
実際、ほかのビルドツール(Gradleなど)ではこの方針を取っていた記憶があります。


というわけで実践です。
実際には、以下のような手順で実現していきます。

  1. fs-extra というライブラリをインストールする
  2. resourceフォルダをコピーするスクリプト、copy-files.jsを書く
  3. ビルド時にcopy-files.jsを一緒に呼ぶようにする

fs-extra をインストールする

ファイル・フォルダのコピーには、fs-extraを使います。
早い話が「fsの機能を拡張するライブラリ」ですね。

プロジェクトフォルダにcdし、以下のコマンドを実行してください。

npm i fs-extra

これでOK。

copy-files.jsを書く

そしたら、ビルド時に実行するスクリプトを書いていきます。
置き場所はどこでもよいです。僕はプロジェクトフォルダ直下に置きます。

  project
  ├ src
  │ └ test.ts
  ├ resource
  │ └ data.json
  ├ build
  │ └ src
  │   └ test.ts
+ └ copy-files.js

そしたらスクリプトを書きます。
以下のような感じ。

const fs = require('fs-extra');
const path = require('path');

// ビルドディレクトリへのパス
const buildPath = path.join(__dirname, 'build');

// フォルダをコピーする
fs.copySync(path.join(__dirname, 'resource'), path.join(buildPath, 'resource'));

8行目でresourceフォルダをまるごとbuildの中にコピーしてるわけですね。

ビルド時にcopy-files.jsを呼び出し

最後に、ビルド時にこのスクリプトを呼び出すようにします。
方法は簡単、package.jsonの中のnpm run buildの内容にちょっと付け足すだけ。

  "scripts": {
+   "build": "tsc && node copy-files.js",
    "start": "node ./build/app.js"
  },

buildコマンドにnode copy-files.jsを追加して、ビルド時に呼ぶようにしています。
この状態でビルドすると...

image.png

buildフォルダの中に、resourceがコピーされていますね。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?