UNPKG

directory-lister-cli

Version:

A simple Node.js CLI tool to generate a directory structure map.

264 lines (263 loc) 12.6 kB
#!/usr/bin/env node "use strict"; 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();