🥜
Simpacker 用の manifest.json を出力する Node.js スクリプト を作った
最後に気づいたのですが、なぜか今回の記事は「だ、である調」で書いてました。
理由は不明です。
TL;DR
Simpacker 用の manifest.json を出力したかった
Simpacker を Parcel で使うとかなり構成がシンプルになるが、
そもそも yarn だけでできないか考えていた。
PostCSS や TypeScript のトランスパイルは yarn だけでできる。
あとは Simpacker 用の manifest.json を自動生成できれば特に不便はなさそう。
つまり Rails+Simpacker+Parcel 環境時の
parcel-plugin-bundle-manifest
に相当する物を
Node.js スクリプトで作ってやればできるはずだ。
- parcel-plugin-bundle-manifest | GitHub
処理の流れ
-
public/
配下を対象として -
public/
直下のファイルを除外しつつ再帰でファイルを列挙して - パスから
public/
は抜いてやりつつファイル名をKey, ファイルパスをValueにしたjsonを出力 - ファイルパスにキャッシュバスター(
?v=HOGE
とか)がついてるとなお良さそう
うん、いけそう。
キャッシュバスターの話はこことかに書いてあるが
要するにクエリ文字列つきのURL参照にしてやってブラウザキャッシュを強制的に効かなくする感じ
- 【ASP】キャッシュを有効にしつつ、cssやjsファイルの変更を確実に反映させる(Cache Busting) | Qiita
- CSSファイルやJavaScriptファイルを読み込むときの末尾にあるクエリー文字列は何のためにあるか | 小粋空間
できた
できた。
使い方
- 基本的には Rails+Simpacker プロジェクトのルートで
-
node output_manifest_js.js public
でOK
-
- コードの定数部分を変えればいいんだが、manifest.json のパスを変えたいときは
-
node output_manifest_js.js public public/packs/hoge-manifest.json
とかでできるようにしてある
-
// ==================================================
// Simpacker 用 manifest.json 出力 Node.js スクリプト
// 基本の使い方:node output_manifest_js.js public
// ==================================================
const fs = require('fs');
const path = require('path');
const crypto = require('crypto')
// default ignore Path
const DEFAULT_IGNORE_PATH = 'public';
// default manifest.json path
const DEFAULT_OUTPUT_MANIFEST_PATH = 'public/packs/manifest.json';
// Cache Bustring String Source
const S = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
// Number of Cache Bustring String
const N = 6;
// Check args
function showUsageAndExit() {
const basename = path.basename(process.argv[1]);
console.error(`Usage: node ${basename} <target directory path> <optionla:ignore path> <optional:manifest.json path>`);
process.exit(1);
}
const getFiles = (dirpath, callback) => {
fs.readdir(dirpath, {withFileTypes: true}, (err, dirents) => {
if (err) {
console.error(err);
return;
}
for (const dirent of dirents) {
const fp = path.join(dirpath, dirent.name);
if (dirent.isDirectory()) {
getFiles(fp, callback);
} else if (ignorePath === dirpath) {
// public ディレクトリ直下は処理対象外
continue;
} else if (fp === outputManifestPath) {
// マニフェストファイルそのものは処理対象外
continue;
} else {
cacheBustringString = Array.from(crypto.randomFillSync(new Uint8Array(N))).map((n)=>S[n%S.length]).join('');
outputObject[dirent.name] = `${fp.replace(ignorePath, '')}?v=${cacheBustringString}`;
callback(fp);
}
}
});
}
// process.argv[0] : Node.jsの実行プロセスのフルパス
// process.argv[1] : スクリプトファイルのフルパス
// なので3番目以降をargsに取り出す
const args = process.argv.slice(2);
if (args.length < 1) {
showUsageAndExit();
}
if (args.length < 2) {
args.push(DEFAULT_OUTPUT_MANIFEST_PATH);
args.push(DEFAULT_IGNORE_PATH);
}
if (args.length < 3) {
args.push(DEFAULT_IGNORE_PATH);
}
const checkPath = args[0];
const outputManifestPath = args[1];
const ignorePath = args[2];
const outputObject = {};
getFiles(checkPath, console.log);
// getFiles の処理が終わり切ってから実行するために setTimeout
// Promise化すれば getFiles を同期実行できそう……
setTimeout(console.log, 500, `wait: output ${outputManifestPath} ...`);
setTimeout(()=>{ fs.writeFileSync(outputManifestPath, JSON.stringify(outputObject, null, 2)); }, 510);
実行。
node output_manifest_js.js public
出力例。
public/packs/manifest.json
{
"baz.js": "/packs/js/baz.js?v=EDQ0N0",
"sample.js": "/packs/js/sample.js?v=N7TG5H"
}
- Simpacker 用 manifest.json 出力 Node.js スクリプト | JUNKI555 | GitHub gist
作るのに色々検索した履歴
Node.js でコマンドライン引数を取得する
// process.argv[0] は Node.jsの実行プロセスのフルパス
// process.argv[1] は スクリプトファイルのフルパス
// process.argv[2] 以降がコマンドライン引数
process.argv
- コマンドライン引数(パラメータ)を取得する (process.argv) | まくまくNode.jsノート
- コマンドライン引数を処理する | JavaScript Primer
ファイルパスを再帰で出力する
const fs = require('fs');
// fs.readdir(path[, options], callback)
- Node.jsで高速にファイル一覧を取得するfs.readdirのwithFileTypesオプション | Qiita
- File system | Node.js v15.3.0 Documentation
ランダム文字列の生成
const S = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
const N = 6;
Array.from(crypto.randomFillSync(new Uint8Array(N))).map((n)=>S[n%S.length]).join('');
- JavaScriptでお手軽にランダム文字列の生成 | Qiita
json を出力する
fs.writeFileSync(outputManifestPath, JSON.stringify(outputObject, null, 2));
- [Node.js] JSONをキレイに整形して出力する | ねこの足跡R
- "JSON.stringify()の第2引数にnull、第3引数に数値を指定することで人間がパッと見て分かるよう整形した文字列が返されます"
- これをしないと全て1行で出力される
- https://blog.katsubemakito.net/nodejs/json-pretty
- fs.writeFileの使い方。Node.jsでファイルを書き込み | おちゃカメラ。
- [node.js]ディレクトリを再帰的にたどってファイル一覧をJSONとして出力する | GUNMA GIS GEEK
npm パッケージにしてみる?
モジュールバンドラに依存しない、似たようなものは存在しないみたいでした……。
YAML を読み込むパッケージなんかも取り入れて config/simpacker.yml を読み込めば
出力するべき manifest.json のファイルパスもわかりますし、
npm に公開しようかなとも考えたんですが、需要ありますかね……?
(需要なくても2021年初ぐらいに公開してみたいかも)
- Yaml ファイルを読み書きする (js-yaml) | まくまくNode.jsノート
Discussion