KDP(電子出版)のメモ 急急如律令

Amazon Kindleダイレクト・パブリッシングでの電子出版や電子書籍の作成販売について、文章やイラストの作成や編集方法について書いています。

Commander.jsをつかってコマンドラインオプションの取得

コマンドラインオプションの取得が思ったとおりにできていなかったが、Javaとの仕様の違いが原因だった。 -dのオプションをdではなくdstとして取得するなど省略していたので動かなかった。

You:

JSに書き換えて、Commander.jsをつかって相対パスをdstPathにフルパスにして

        //出力パス確認
        if (commandLine.hasOption("d")) {
            dstPath = new File(commandLine.getOptionValue("d"));
            if (!dstPath.isDirectory()) {
                LogAppender.error("-d : dst path not exist. "+dstPath.getAbsolutePath());
                return;
            }
        }

ChatGPT:

Javaコードの処理は

  1. -d オプションがあるか確認

  2. 指定されたパスを取得

  3. 相対パスならフルパスに変換

  4. ディレクトリか確認

  5. そうでなければエラー

なので、Node.js + Commander.js で書くと次のようになります。

JavaScript

import { Command } from "commander";  
import fs from "fs";  
import path from "path";  
  
const program \= new Command();  
  
program  
  .option("-d <dir>", "destination directory");  
  
program.parse(process.argv);  
  
const options \= program.opts();  
  
let dstPath \= null;  
  
// 出力パス確認  
if (options.d) {  
  dstPath \= path.resolve(options.d); // 相対パス → フルパス  
  
  if (!fs.existsSync(dstPath) || !fs.statSync(dstPath).isDirectory()) {  
    LogAppender.error("-d : dst path not exist. " + dstPath);  
    return;  
  }  
}

Java版との対応

Java JS
commandLine.hasOption("d") if (options.d)
commandLine.getOptionValue("d") options.d
new File(path) path.resolve(path)
getAbsolutePath() dstPath (resolve後)
isDirectory() fs.statSync(...).isDirectory()

ポイント

  • path.resolve() を使うと 相対パス → フルパス変換されます。

  • existsSyncstatSync().isDirectory() を両方チェックすると Javaの isDirectory() に近い挙動になります。


もし AozoraEpub3 の Java CLI と完全互換の挙動にするなら、

  • -d が相対パスの場合 入力ファイル基準でresolve

  • ~ (home) 展開

  • Windowsパス対応

なども実装できます。必要ならその 完全互換版CLIパス処理も書きます。

You:

coverファイルが取得できない const options = new Command(); options .option('-c, --cover ', '表紙画像\n[0:先頭の挿絵]\n[1:ファイル名と同じ画像]\n[ファイル名 or URL]') options.parse(process.argv); if (options.args.length === 0) { options.help(); } const commandLine = options.opts(); if (commandLine.cover) {coverFileName = commandLine.c;} node AozoraEpub3 -c "C:\Users\Owner\Downloads\kokoro.jpg" "C:\Users\Owner\Downloads\773_ruby_5968.zip"

ChatGPT:

原因はここです。

JavaScript

if (commandLine.cover) {coverFileName \= commandLine.c;}

Commander.js では
opts() のプロパティ名は 長いオプション名になります

つまり

\-c, --cover <cover>

の場合、取得できるのは

commandLine.cover

であり、commandLine.c は存在しません。


修正コード

JavaScript

import { Command } from "commander";  
  
const program \= new Command();  
  
program  
  .option('-c, --cover <cover>', '表紙画像\\n\[0:先頭の挿絵\]\\n\[1:ファイル名と同じ画像\]\\n\[ファイル名 or URL\]');  
  
program.parse(process.argv);  
  
if (program.args.length \=== 0) {  
  program.help();  
}  
  
const commandLine \= program.opts();  
  
let coverFileName \= null;  
  
if (commandLine.cover) {  
  coverFileName \= commandLine.cover;  
}

実行例

node AozoraEpub3.js -c "C:\\Users\\Owner\\Downloads\\kokoro.jpg" "C:\\Users\\Owner\\Downloads\\773\_ruby\_5968.zip"

取得結果

commandLine.cover  
\= C:\\Users\\Owner\\Downloads\\kokoro.jpg

さらに安全な書き方(おすすめ)

Commanderは直接取得もできます。

JavaScript

const coverFileName \= program.opts().cover;

参考(Javaとの対応)

Java (Apache CLI) Commander.js
hasOption("c") if (opts.cover)
getOptionValue("c") opts.cover

もしよければ、AozoraEpub3 Java版と完全互換のCLI(30個以上のオプション)をCommander.jsで再現したテンプレートも作れます。
実は かなり短いコードで全部書けます。

You:

let coverFile = new File(coverFileName); node:internal/file:40 throw new ERR_MISSING_ARGS('fileBits', 'fileName'); ^ TypeError [ERR_MISSING_ARGS]: The "fileBits" and "fileName" arguments must be specified at new File (node:internal/file:40:13) at main (file:///C:/Users/Owner/Documents/GitHub/AozoraEpub3JS/src/AozoraEpub3.js:448:25) { code: 'ERR_MISSING_ARGS' }

ChatGPT:

原因は Java の File と Node.js の File が別物だからです。

Java

Java

new File(path)

Node.js

  • Fileブラウザ互換API

  • new File(fileBits, fileName) が必要

つまり

JavaScript

new File(coverFileName)

引数不足エラーになります。

ERR\_MISSING\_ARGS: The "fileBits" and "fileName" arguments must be specified

Java の File 相当の書き方(Node.js)

Nodeでは パスは単なる文字列です。
必要なら pathfs を使います。

JavaScript

import fs from "fs";  
import path from "path";  
  
let coverFile \= null;  
  
if (coverFileName) {  
  coverFile \= path.resolve(coverFileName);  
  
  if (!fs.existsSync(coverFile)) {  
    LogAppender.error("cover file not exist. " + coverFile);  
    return;  
  }  
}

Javaコードとの対応

Java Node.js
File file = new File(path) const file = path.resolve(path)
file.exists() fs.existsSync(file)
file.isDirectory() fs.statSync(file).isDirectory()
file.getAbsolutePath() path.resolve(file)

AozoraEpub3移植の場合(おすすめ)

Java版のコード

Java

File coverFile \= new File(coverFileName);

そのまま

JavaScript

let coverFile \= coverFileName;

にしてしまう方が Java構造に近くて移植しやすいです。

必要なときだけ

JavaScript

fs.existsSync(coverFile)

を使います。


💡 AozoraEpub3のJava→Node移植で一番安全な方法

Javaの

File  
BufferedReader  
InputStream  
ZipOutputStream

Nodeで対応するクラスにマッピングすると
コード構造をほぼ変えずに移植できます。

もし希望があれば **AozoraEpub3用の Node互換 File クラス(Java互換API)**も作れます。
これを作ると Javaコードを9割そのままJS化できます。

You:

JSに変換して if (coverFileName != null && !coverFileName.startsWith("http")) { File coverFile = new File(coverFileName); if (!coverFile.exists()) { coverFileName = srcFile.getParent()+"/"+coverFileName; if (!new File(coverFileName).exists()) { coverFileName = null; LogAppender.println("[WARN] 表紙画像ファイルが見つかりません : "+coverFile.getAbsolutePath()); } } }

ChatGPT:

Javaの File をそのまま使わず、Node.js の fspath で書き換えると次の形になります。

JavaScript

import fs from "fs";  
import path from "path";  
  
if (coverFileName != null && !coverFileName.startsWith("http")) {  
  let coverFile \= coverFileName;  
  
  if (!fs.existsSync(coverFile)) {  
    coverFileName \= path.join(path.dirname(srcFile), coverFileName);  
  
    if (!fs.existsSync(coverFileName)) {  
      coverFileName \= null;  
      LogAppender.println("\[WARN\] 表紙画像ファイルが見つかりません : " + path.resolve(coverFile));  
    }  
  }  
}

Javaとの対応

Java JS
coverFileName.startsWith("http") 同じ
new File(path) 文字列パス
file.exists() fs.existsSync()
srcFile.getParent() path.dirname(srcFile)
getAbsolutePath() path.resolve()

重要ポイント

Javaでは

Java

srcFile.getParent()

ですが、Nodeでは

JavaScript

path.dirname(srcFile)

になります。


AozoraEpub3移植でよくある落とし穴

Java版

Java

srcFile.getParent()+"/"+coverFileName

をJSでそのままやると Windowsで壊れます
そのため

JavaScript

path.join(...)

を使うのが安全です。


もしよければ、
**AozoraEpub3 Java → Node 移植で使う File互換ヘルパー(約40行)**も出します。
これを入れると

new File()  
.exists()  
.getParent()  
.getAbsolutePath()

ほぼJavaのまま書けるようになります。