directory-lister-cli
Version:
A simple Node.js CLI tool to generate a directory structure map.
264 lines (263 loc) • 12.6 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
// src/index.ts
const fs_1 = require("fs");
const path_1 = __importDefault(require("path"));
const inquirer_1 = __importDefault(require("inquirer"));
const fileLister_1 = require("./core/fileLister");
const outputFormatter_1 = require("./utils/outputFormatter");
const markdownFormatter_1 = require("./utils/markdownFormatter");
const jsonFormatter_1 = require("./utils/jsonFormatter");
const yamlFormatter_1 = require("./utils/yamlFormatter");
const dirConfigFormatter_1 = require("./utils/dirConfigFormatter");
// ディレクトリ再構築処理の関数 (変更なし)
async function reconstructDirectory(configFilePath, targetPath) {
console.log(`\n📂 ディレクトリ構成を再構築中: ${configFilePath} から ${targetPath} へ...`);
try {
const configContent = await fs_1.promises.readFile(configFilePath, 'utf8');
const configTree = JSON.parse(configContent);
if (configTree.type !== 'directory' || !configTree.children) {
throw new Error("無効なディレクトリ構成データファイルです。ルートはディレクトリである必要があります。");
}
if (!(await (0, fileLister_1.isDirectory)(targetPath))) {
await fs_1.promises.mkdir(targetPath, { recursive: true });
console.log(`✅ ターゲットディレクトリ '${targetPath}' を作成しました。`);
}
await createNodes(configTree.children, targetPath);
console.log(`🎉 ディレクトリ構成の再構築が完了しました!`);
}
catch (error) {
if (error instanceof Error) {
console.error(`🔴 ディレクトリ再構築エラー: ${error.message}`);
}
else {
console.error(`🔴 予期せぬディレクトリ再構築エラー:`, error);
}
process.exit(1);
}
}
// 再帰的にノードを作成するヘルパー関数 (変更なし)
async function createNodes(nodes, currentPath) {
for (const node of nodes) {
const nodeFullPath = path_1.default.join(currentPath, node.name);
if (node.type === 'directory') {
await fs_1.promises.mkdir(nodeFullPath, { recursive: true });
console.log(`📁 作成: ${nodeFullPath}/`);
if (node.children) {
await createNodes(node.children, nodeFullPath);
}
}
else if (node.type === 'file') {
try {
if (node.content) {
const buffer = Buffer.from(node.content, 'base64');
await fs_1.promises.writeFile(nodeFullPath, buffer);
console.log(`📝 作成 (内容復元): ${nodeFullPath}`);
}
else {
await fs_1.promises.writeFile(nodeFullPath, '');
console.log(`📝 作成 (空ファイル): ${nodeFullPath}`);
}
}
catch (writeErr) {
console.warn(`⚠️ ファイル '${nodeFullPath}' の書き込み中にエラーが発生しました。スキップします:`, writeErr);
}
}
}
}
// main 関数 (変更箇所)
async function main() {
const args = process.argv.slice(2);
const command = args[0];
if (command === 'reconstruct') {
const configFilePath = args[1];
const targetPath = args[2] || process.cwd();
if (!configFilePath) {
console.error('🔴 エラー: `reconstruct` コマンドには、再構築するデータファイルのパスが必要です。\n 例: dir-list reconstruct .dirconfig.json [出力先ディレクトリ]');
process.exit(1);
}
await reconstructDirectory(path_1.default.resolve(process.cwd(), configFilePath), path_1.default.resolve(process.cwd(), targetPath));
return;
}
else if (command === 'duplicate') {
const duplicateAnswers = await inquirer_1.default.prompt([
{
type: 'input',
name: 'sourceDir',
message: '複製元ディレクトリのパスを入力してください:',
default: process.cwd(),
validate: async (input) => {
const fullPath = path_1.default.resolve(process.cwd(), input);
try {
const stats = await fs_1.promises.stat(fullPath);
return stats.isDirectory() ? true : '指定されたパスはディレクトリではありません。';
}
catch (error) {
return '有効なディレクトリパスを入力してください。';
}
}
},
{
type: 'input',
name: 'destinationDir',
message: '複製先ディレクトリのパスを入力してください:',
validate: (input) => input.trim() !== '' ? true : '複製先ディレクトリは必須です。'
},
{
type: 'list',
name: 'osType',
message: '複製コマンドを生成するOSを選択してください:',
choices: ['Windows (CMD/PowerShell)', 'Linux / macOS']
}
]);
const { sourceDir, destinationDir, osType } = duplicateAnswers;
let copyCommand = '';
const sourcePath = path_1.default.resolve(process.cwd(), sourceDir);
const destPath = path_1.default.resolve(process.cwd(), destinationDir);
console.log(`\n📂 複製コマンド生成中: '${sourcePath}' から '${destPath}' へ...`);
if (osType === 'Windows (CMD/PowerShell)') {
copyCommand = `robocopy "${sourcePath}" "${destPath}" /E /COPYALL /DCOPY:T`;
console.log('\n--- Windows (CMD/PowerShell) コマンド ---');
console.log('大容量のファイルをコピーする際は、時間がかかる場合があります。');
console.log('```cmd');
console.log(copyCommand);
console.log('```');
console.log('-----------------------------------------');
}
else {
copyCommand = `rsync -avh "${sourcePath}/" "${destPath}"`;
console.log('\n--- Linux / macOS コマンド ---');
console.log('大容量のファイルをコピーする際は、時間がかかる場合があります。');
console.log('```bash');
console.log(copyCommand);
console.log('```');
console.log('------------------------------');
}
console.log('\n👆 上記コマンドをコピーしてターミナルで実行してください。');
return;
}
const TARGET_DIR = args[0] || process.cwd();
const answers = await inquirer_1.default.prompt([
{
type: 'list',
name: 'outputFormat',
message: '出力形式を選択してください:',
choices: [
{ name: 'プレーンテキスト (.txt)', value: 'text' },
{ name: 'Markdown (.md)', value: 'markdown' },
{ name: 'JSON (.json)', value: 'json' },
{ name: 'YAML (.yaml)', value: 'yaml' },
{ name: 'ディレクトリ構成データファイル (.dirconfig.json)', value: 'dirconfig' },
],
default: 'text'
},
{
type: 'confirm',
name: 'includeFileContents',
message: 'ディレクトリ構成データファイルにファイルの内容を含めますか?(大容量ファイルでクラッシュの可能性あり)',
default: false,
when: (currentAnswers) => currentAnswers.outputFormat === 'dirconfig'
},
{
type: 'input',
name: 'outputFileName',
message: '出力ファイル名を入力してください:',
default: (currentAnswers) => {
switch (currentAnswers.outputFormat) {
case 'markdown': return 'directory_structure.md';
case 'json': return 'directory_structure.json';
case 'yaml': return 'directory_structure.yaml';
case 'dirconfig': return '.dirconfig.json';
default: return 'directory_structure.txt';
}
}
},
{
type: 'confirm',
name: 'includeIgnoreFiles',
message: '.gitignore および .dirignore に含まれるファイルを無視しますか?',
default: true
}
]);
const { outputFormat, outputFileName, includeIgnoreFiles, includeFileContents } = answers;
// --- ここから修正ロジックを追加 ---
let finalOutputFileName = outputFileName;
const currentExt = path_1.default.extname(outputFileName); // 現在の拡張子を取得
// 選択されたフォーマットに基づいて期待される拡張子を決定
let expectedExt = '';
switch (outputFormat) {
case 'text':
expectedExt = '.txt';
break;
case 'markdown':
expectedExt = '.md';
break;
case 'json':
expectedExt = '.json';
break;
case 'yaml':
expectedExt = '.yaml';
break;
case 'dirconfig':
expectedExt = '.dirconfig.json'; // .dirconfig.json は特殊な拡張子
break;
}
// 期待される拡張子が付いていない場合のみ追加
// .dirconfig.json のような複数ドットの拡張子にも対応
if (expectedExt && !finalOutputFileName.endsWith(expectedExt)) {
if (outputFormat === 'dirconfig' && currentExt === '.json') {
// 例外: .dirconfig.json を選んでいて、ユーザーが既に .json を付けている場合、何もしない
// これは .dirconfig.json が実質 .json 形式であるため
}
else {
finalOutputFileName += expectedExt;
}
}
// --- 修正ロジックここまで ---
const outputPath = path_1.default.resolve(process.cwd(), finalOutputFileName); // 最終的なファイル名を使用
console.log(`\n選択されたオプション:`);
console.log(` 出力形式: ${outputFormat}`);
console.log(` 出力ファイル名: ${finalOutputFileName}`); // 表示も修正
console.log(` 無視ファイルを考慮: ${includeIgnoreFiles ? 'はい' : 'いいえ'}`);
if (outputFormat === 'dirconfig') {
console.log(` ファイル内容を含める: ${includeFileContents ? 'はい' : 'いいえ'}\n`);
}
else {
console.log('\n');
}
const fileNodes = await (0, fileLister_1.getFilesAndDirectories)(TARGET_DIR, includeIgnoreFiles, 0);
const filteredNodes = fileNodes.filter(node => path_1.default.relative(TARGET_DIR, node.path) !== '');
let outputContent;
if (outputFormat === 'json' || outputFormat === 'yaml' || outputFormat === 'dirconfig') {
const nodesForJsonTree = fileNodes.map(node => ({ path: node.path, isDirectory: node.isDirectory }));
const tree = (0, jsonFormatter_1.buildJsonTree)(nodesForJsonTree, TARGET_DIR, !!includeFileContents);
if (outputFormat === 'json') {
outputContent = (0, jsonFormatter_1.formatAsJson)(tree);
}
else if (outputFormat === 'yaml') {
outputContent = (0, yamlFormatter_1.formatAsYaml)(tree);
}
else { // dirconfig
outputContent = (0, dirConfigFormatter_1.formatAsDirConfig)(tree);
}
}
else {
outputContent = (0, outputFormatter_1.generateHeader)(TARGET_DIR);
for (const node of filteredNodes) {
if (outputFormat === 'markdown') {
outputContent += (0, markdownFormatter_1.formatAsMarkdown)(node, TARGET_DIR) + '\n';
}
else { // text
outputContent += (0, outputFormatter_1.formatPathForOutput)(node, TARGET_DIR) + '\n';
}
}
}
await fs_1.promises.writeFile(outputPath, outputContent);
console.log(`✅ ディレクトリ構成図を '${finalOutputFileName}' に保存しました。`); // 表示も修正
console.log(`出力先: ${outputPath}`);
}
main();