marhup 0.1.7 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/README.md +481 -4
  2. package/dist/cli.js +659 -78
  3. package/dist/cli.js.map +1 -1
  4. package/dist/errors.d.ts +29 -0
  5. package/dist/errors.d.ts.map +1 -0
  6. package/dist/errors.js +62 -0
  7. package/dist/errors.js.map +1 -0
  8. package/dist/generator/code.d.ts +4 -3
  9. package/dist/generator/code.d.ts.map +1 -1
  10. package/dist/generator/code.js +7 -7
  11. package/dist/generator/code.js.map +1 -1
  12. package/dist/generator/gen-animations.d.ts +13 -0
  13. package/dist/generator/gen-animations.d.ts.map +1 -0
  14. package/dist/generator/gen-animations.js +77 -0
  15. package/dist/generator/gen-animations.js.map +1 -0
  16. package/dist/generator/image.d.ts +4 -3
  17. package/dist/generator/image.d.ts.map +1 -1
  18. package/dist/generator/image.js +67 -80
  19. package/dist/generator/image.js.map +1 -1
  20. package/dist/generator/index.d.ts +2 -0
  21. package/dist/generator/index.d.ts.map +1 -1
  22. package/dist/generator/index.js +9 -18
  23. package/dist/generator/index.js.map +1 -1
  24. package/dist/generator/list.d.ts +4 -3
  25. package/dist/generator/list.d.ts.map +1 -1
  26. package/dist/generator/list.js +15 -17
  27. package/dist/generator/list.js.map +1 -1
  28. package/dist/generator/mermaid.d.ts +9 -4
  29. package/dist/generator/mermaid.d.ts.map +1 -1
  30. package/dist/generator/mermaid.js +120 -85
  31. package/dist/generator/mermaid.js.map +1 -1
  32. package/dist/generator/pptx.d.ts +1 -1
  33. package/dist/generator/pptx.d.ts.map +1 -1
  34. package/dist/generator/pptx.js +232 -60
  35. package/dist/generator/pptx.js.map +1 -1
  36. package/dist/generator/presentation.d.ts +149 -0
  37. package/dist/generator/presentation.d.ts.map +1 -0
  38. package/dist/generator/presentation.js +163 -0
  39. package/dist/generator/presentation.js.map +1 -0
  40. package/dist/generator/table.d.ts +4 -3
  41. package/dist/generator/table.d.ts.map +1 -1
  42. package/dist/generator/table.js +12 -33
  43. package/dist/generator/table.js.map +1 -1
  44. package/dist/generator/text.d.ts +5 -4
  45. package/dist/generator/text.d.ts.map +1 -1
  46. package/dist/generator/text.js +34 -20
  47. package/dist/generator/text.js.map +1 -1
  48. package/dist/generator/video.d.ts +18 -0
  49. package/dist/generator/video.d.ts.map +1 -0
  50. package/dist/generator/video.js +83 -0
  51. package/dist/generator/video.js.map +1 -0
  52. package/dist/index.d.ts +8 -1
  53. package/dist/index.d.ts.map +1 -1
  54. package/dist/index.js +144 -55
  55. package/dist/index.js.map +1 -1
  56. package/dist/layout/auto.d.ts +14 -2
  57. package/dist/layout/auto.d.ts.map +1 -1
  58. package/dist/layout/auto.js +129 -37
  59. package/dist/layout/auto.js.map +1 -1
  60. package/dist/layout/engine.d.ts +1 -1
  61. package/dist/layout/engine.d.ts.map +1 -1
  62. package/dist/layout/engine.js +10 -12
  63. package/dist/layout/engine.js.map +1 -1
  64. package/dist/layout/index.d.ts +1 -1
  65. package/dist/layout/index.d.ts.map +1 -1
  66. package/dist/layout/index.js +2 -10
  67. package/dist/layout/index.js.map +1 -1
  68. package/dist/layout/types.d.ts +1 -0
  69. package/dist/layout/types.d.ts.map +1 -1
  70. package/dist/layout/types.js +1 -2
  71. package/dist/layout/types.js.map +1 -1
  72. package/dist/mcp-handlers.d.ts.map +1 -1
  73. package/dist/mcp-handlers.js +45 -54
  74. package/dist/mcp-handlers.js.map +1 -1
  75. package/dist/mcp.js +10 -12
  76. package/dist/mcp.js.map +1 -1
  77. package/dist/parser/frontmatter.d.ts.map +1 -1
  78. package/dist/parser/frontmatter.js +95 -16
  79. package/dist/parser/frontmatter.js.map +1 -1
  80. package/dist/parser/grid.d.ts +5 -7
  81. package/dist/parser/grid.d.ts.map +1 -1
  82. package/dist/parser/grid.js +217 -30
  83. package/dist/parser/grid.js.map +1 -1
  84. package/dist/parser/index.js +3 -13
  85. package/dist/parser/index.js.map +1 -1
  86. package/dist/parser/markdown.d.ts +1 -1
  87. package/dist/parser/markdown.d.ts.map +1 -1
  88. package/dist/parser/markdown.js +249 -137
  89. package/dist/parser/markdown.js.map +1 -1
  90. package/dist/theme/default.d.ts +1 -1
  91. package/dist/theme/default.d.ts.map +1 -1
  92. package/dist/theme/default.js +16 -8
  93. package/dist/theme/default.js.map +1 -1
  94. package/dist/theme/index.d.ts +11 -0
  95. package/dist/theme/index.d.ts.map +1 -1
  96. package/dist/theme/index.js +70 -7
  97. package/dist/theme/index.js.map +1 -1
  98. package/dist/types/index.d.ts +56 -2
  99. package/dist/types/index.d.ts.map +1 -1
  100. package/dist/types/index.js +4 -6
  101. package/dist/types/index.js.map +1 -1
  102. package/dist/types/plugin.d.ts +50 -0
  103. package/dist/types/plugin.d.ts.map +1 -0
  104. package/dist/types/plugin.js +5 -0
  105. package/dist/types/plugin.js.map +1 -0
  106. package/dist/utils/file-lock.d.ts +27 -0
  107. package/dist/utils/file-lock.d.ts.map +1 -0
  108. package/dist/utils/file-lock.js +118 -0
  109. package/dist/utils/file-lock.js.map +1 -0
  110. package/dist/utils/i18n.d.ts +5 -0
  111. package/dist/utils/i18n.d.ts.map +1 -0
  112. package/dist/utils/i18n.js +41 -0
  113. package/dist/utils/i18n.js.map +1 -0
  114. package/dist/utils/logger.d.ts +7 -0
  115. package/dist/utils/logger.d.ts.map +1 -0
  116. package/dist/utils/logger.js +43 -0
  117. package/dist/utils/logger.js.map +1 -0
  118. package/dist/utils/path-validation.d.ts +20 -0
  119. package/dist/utils/path-validation.d.ts.map +1 -0
  120. package/dist/utils/path-validation.js +39 -0
  121. package/dist/utils/path-validation.js.map +1 -0
  122. package/dist/utils/plugin-manager.d.ts +18 -0
  123. package/dist/utils/plugin-manager.d.ts.map +1 -0
  124. package/dist/utils/plugin-manager.js +108 -0
  125. package/dist/utils/plugin-manager.js.map +1 -0
  126. package/dist/utils/sanitizer.d.ts +14 -0
  127. package/dist/utils/sanitizer.d.ts.map +1 -0
  128. package/dist/utils/sanitizer.js +73 -0
  129. package/dist/utils/sanitizer.js.map +1 -0
  130. package/package.json +23 -8
package/dist/cli.js CHANGED
@@ -1,99 +1,680 @@
1
1
  #!/usr/bin/env node
2
- "use strict";
3
2
  /**
4
3
  * marhup CLI
5
4
  */
6
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
- if (k2 === undefined) k2 = k;
8
- var desc = Object.getOwnPropertyDescriptor(m, k);
9
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
- desc = { enumerable: true, get: function() { return m[k]; } };
11
- }
12
- Object.defineProperty(o, k2, desc);
13
- }) : (function(o, m, k, k2) {
14
- if (k2 === undefined) k2 = k;
15
- o[k2] = m[k];
16
- }));
17
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
- Object.defineProperty(o, "default", { enumerable: true, value: v });
19
- }) : function(o, v) {
20
- o["default"] = v;
21
- });
22
- var __importStar = (this && this.__importStar) || (function () {
23
- var ownKeys = function(o) {
24
- ownKeys = Object.getOwnPropertyNames || function (o) {
25
- var ar = [];
26
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
- return ar;
28
- };
29
- return ownKeys(o);
30
- };
31
- return function (mod) {
32
- if (mod && mod.__esModule) return mod;
33
- var result = {};
34
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
- __setModuleDefault(result, mod);
36
- return result;
5
+ import { Command } from 'commander';
6
+ import * as fs from 'fs';
7
+ import * as path from 'path';
8
+ import chokidar from 'chokidar';
9
+ import { marhupFile, parse, marhupFromJson, previewLayout } from './index.js';
10
+ import { MarhupError } from './errors.js';
11
+ import { validateAndResolvePath } from './utils/path-validation.js';
12
+ import logger from './utils/logger.js';
13
+ import { initI18n, t, changeLanguage, getCurrentLanguage } from './utils/i18n.js';
14
+ // Detect language from command line arguments
15
+ const args = process.argv.slice(2);
16
+ let detectedLang;
17
+ for (let i = 0; i < args.length; i++) {
18
+ if ((args[i] === '-l' || args[i] === '--lang') && i + 1 < args.length) {
19
+ detectedLang = args[i + 1];
20
+ break;
21
+ }
22
+ }
23
+ // Initialize i18n with detected language or auto-detect
24
+ await initI18n(detectedLang);
25
+ const program = new Command();
26
+ /**
27
+ * Load configuration from file
28
+ */
29
+ async function loadConfig(configPath) {
30
+ const cwd = process.cwd();
31
+ if (configPath) {
32
+ try {
33
+ configPath = validateAndResolvePath(configPath, cwd);
34
+ }
35
+ catch (pathError) {
36
+ const errorMessage = pathError instanceof Error ? pathError.message : String(pathError);
37
+ console.warn(t('cli.messages.configPathInvalid', { configPath, error: errorMessage }));
38
+ return {};
39
+ }
40
+ }
41
+ else {
42
+ // Try default locations
43
+ const defaultPaths = ['.marhuprc', '.marhuprc.json', '.marhuprc.js', 'marhup.config.js'];
44
+ for (const path of defaultPaths) {
45
+ try {
46
+ const resolved = validateAndResolvePath(path, cwd);
47
+ if (fs.existsSync(resolved)) {
48
+ configPath = resolved;
49
+ break;
50
+ }
51
+ }
52
+ catch {
53
+ // Skip invalid default paths
54
+ }
55
+ }
56
+ }
57
+ if (!configPath || !fs.existsSync(configPath)) {
58
+ return {};
59
+ }
60
+ try {
61
+ if (configPath.endsWith('.js')) {
62
+ // For JS files, use dynamic import
63
+ const configModule = await import(path.resolve(configPath));
64
+ const config = configModule.default || configModule;
65
+ return config;
66
+ }
67
+ else {
68
+ // For JSON files
69
+ const content = fs.readFileSync(configPath, 'utf-8');
70
+ return JSON.parse(content);
71
+ }
72
+ }
73
+ catch (error) {
74
+ console.warn(t('cli.messages.configLoadFailed', { configPath }));
75
+ console.warn(t('cli.messages.configLoadError', { error: error instanceof Error ? error.message : error }));
76
+ return {};
77
+ }
78
+ }
79
+ /**
80
+ * Merge config options with CLI options, CLI takes precedence, then apply defaults
81
+ */
82
+ function mergeOptions(cliOptions, configOptions) {
83
+ return {
84
+ output: cliOptions.output || configOptions.output || 'output.pptx',
85
+ outputDir: cliOptions.outputDir || configOptions.outputDir,
86
+ theme: cliOptions.theme || configOptions.theme || 'default',
87
+ grid: cliOptions.grid || configOptions.grid || '12x9',
88
+ watch: cliOptions.watch || configOptions.watch || false,
89
+ config: cliOptions.config,
90
+ lang: cliOptions.lang || configOptions.lang || 'en',
91
+ pluginDir: cliOptions.pluginDir || configOptions.pluginDir,
37
92
  };
38
- })();
39
- Object.defineProperty(exports, "__esModule", { value: true });
40
- const commander_1 = require("commander");
41
- const fs = __importStar(require("fs"));
42
- const path = __importStar(require("path"));
43
- const index_js_1 = require("./index.js");
44
- const program = new commander_1.Command();
93
+ }
94
+ /**
95
+ * Format and display errors in a user-friendly way
96
+ */
97
+ function handleError(error, context) {
98
+ logger.error('CLI error occurred', { error: error instanceof Error ? error.message : String(error), context });
99
+ if (error instanceof MarhupError) {
100
+ console.error(`❌ ${error.name}: ${error.message}`);
101
+ if (error.filePath) {
102
+ console.error(' File: ' + error.filePath + (error.lineNumber ? ':' + error.lineNumber : ''));
103
+ }
104
+ if (error.suggestion) {
105
+ console.error(' 💡 ' + error.suggestion);
106
+ }
107
+ if (error.code) {
108
+ console.error(' Code: ' + error.code);
109
+ }
110
+ }
111
+ else if (error instanceof Error) {
112
+ const contextMsg = context ? ` ${t(`errors.${context.replace(/ /g, '')}`, { defaultValue: context })}` : '';
113
+ console.error(`❌ ${t('errors.processingFailed')}${contextMsg}: ${error.message}`);
114
+ console.error(` 💡 ${t('errors.checkInputAndTryAgain')}`);
115
+ }
116
+ else {
117
+ const contextMsg = context ? ` ${t(`errors.${context.replace(/ /g, '')}`, { defaultValue: context })}` : '';
118
+ console.error(`❌ ${t('errors.unknownError')}${contextMsg}:`, error);
119
+ }
120
+ process.exit(1);
121
+ }
45
122
  program
46
- .name('marhup')
47
- .description('Markdownからグリッドベースのレイアウトで PowerPoint (PPTX) を生成')
48
- .version('0.1.0')
49
- .argument('<input>', '入力Markdownファイル')
50
- .option('-o, --output <file>', '出力ファイル名', 'output.pptx')
51
- .option('-t, --theme <name>', 'テーマ名', 'default')
52
- .option('-w, --watch', '監視モード', false)
53
- .option('--grid <size>', 'デフォルトグリッドサイズ', '12x9')
54
- .action(async (input, options) => {
123
+ .name(t('cli.name'))
124
+ .description(t('cli.description'))
125
+ .version(t('cli.version'))
126
+ .option('-o, --output <file>', t('cli.options.output'))
127
+ .option('-d, --output-dir <dir>', t('cli.options.outputDir'))
128
+ .option('-t, --theme <name>', t('cli.options.theme'))
129
+ .option('--grid <size>', t('cli.options.grid'))
130
+ .option('-w, --watch', t('cli.options.watch'))
131
+ .option('-c, --config <file>', t('cli.options.config'))
132
+ .option('-l, --lang <language>', 'Language (en/ja)', 'en')
133
+ .option('--plugin-dir <dir>', 'Plugin directory')
134
+ .addHelpText('after', `
135
+ ${t('cli.examples.basic')}
136
+ ${t('cli.examples.theme')}
137
+ ${t('cli.examples.watch')}
138
+ ${t('cli.examples.batch')}
139
+
140
+ ${t('cli.help.documentation')}`);
141
+ program
142
+ .argument('<inputs...>', t('cli.options.inputFiles'))
143
+ .action(async (inputs, options) => {
144
+ // Load and merge config
145
+ const config = await loadConfig(options.config);
146
+ const mergedOptions = mergeOptions(options, config);
147
+ const cwd = process.cwd();
148
+ // Validate pluginDir if specified
149
+ if (mergedOptions.pluginDir) {
150
+ try {
151
+ mergedOptions.pluginDir = validateAndResolvePath(mergedOptions.pluginDir, cwd);
152
+ }
153
+ catch (error) {
154
+ console.error(t('cli.messages.invalidPath', { path: mergedOptions.pluginDir, error: error instanceof Error ? error.message : String(error) }));
155
+ process.exit(1);
156
+ }
157
+ }
158
+ // Change language if specified
159
+ if (mergedOptions.lang && mergedOptions.lang !== getCurrentLanguage()) {
160
+ await changeLanguage(mergedOptions.lang);
161
+ }
162
+ logger.info('CLI started', { inputs, output: mergedOptions.output, outputDir: mergedOptions.outputDir, theme: mergedOptions.theme, grid: mergedOptions.grid });
163
+ try {
164
+ // Validate input file paths
165
+ const inputPaths = [];
166
+ for (const input of inputs) {
167
+ try {
168
+ const resolved = validateAndResolvePath(input, cwd);
169
+ inputPaths.push(resolved);
170
+ }
171
+ catch (error) {
172
+ console.error(t('cli.messages.invalidPath', { path: input, error: error instanceof Error ? error.message : String(error) }));
173
+ process.exit(1);
174
+ }
175
+ }
176
+ // Check input files exist
177
+ const missingFiles = inputPaths.filter(inputPath => !fs.existsSync(inputPath));
178
+ if (missingFiles.length > 0) {
179
+ logger.error('Input files not found', { missingFiles });
180
+ console.error(t('cli.messages.fileNotFound'));
181
+ missingFiles.forEach(file => console.error(` - ${file}`));
182
+ process.exit(1);
183
+ }
184
+ // Check output directory if specified
185
+ let outputDir;
186
+ if (mergedOptions.outputDir) {
187
+ try {
188
+ outputDir = validateAndResolvePath(mergedOptions.outputDir, cwd);
189
+ if (!fs.existsSync(outputDir)) {
190
+ fs.mkdirSync(outputDir, { recursive: true });
191
+ }
192
+ }
193
+ catch (error) {
194
+ console.error(t('cli.messages.invalidPath', { path: mergedOptions.outputDir, error: error instanceof Error ? error.message : String(error) }));
195
+ process.exit(1);
196
+ }
197
+ }
198
+ if (inputs.length === 1) {
199
+ // Single file case
200
+ const inputPath = inputPaths[0];
201
+ let outputPath;
202
+ try {
203
+ outputPath = validateAndResolvePath(mergedOptions.output, cwd);
204
+ }
205
+ catch (error) {
206
+ console.error(t('cli.messages.invalidPath', { path: mergedOptions.output, error: error instanceof Error ? error.message : String(error) }));
207
+ process.exit(1);
208
+ }
209
+ console.log(t('cli.messages.converting', { input: inputPath }));
210
+ await marhupFile(inputPath, {
211
+ output: outputPath,
212
+ theme: mergedOptions.theme,
213
+ grid: mergedOptions.grid,
214
+ pluginDir: mergedOptions.pluginDir,
215
+ });
216
+ console.log(t('cli.messages.generationComplete', { output: outputPath }));
217
+ }
218
+ else {
219
+ // Multiple files case
220
+ if (mergedOptions.output !== 'output.pptx') {
221
+ console.warn(t('cli.messages.outputDirIgnoreWarning'));
222
+ }
223
+ console.log(t('cli.messages.batchProcessing', { count: inputs.length }));
224
+ let successCount = 0;
225
+ let errorCount = 0;
226
+ for (let i = 0; i < inputs.length; i++) {
227
+ const input = inputs[i];
228
+ const inputPath = inputPaths[i];
229
+ const outputPath = outputDir
230
+ ? path.join(outputDir, path.basename(input, path.extname(input)) + '.pptx')
231
+ : path.resolve(path.basename(input, path.extname(input)) + '.pptx');
232
+ try {
233
+ console.log(t('cli.messages.batchItem', { index: i + 1, total: inputs.length, input, output: outputPath }));
234
+ await marhupFile(inputPath, {
235
+ output: outputPath,
236
+ theme: mergedOptions.theme,
237
+ grid: mergedOptions.grid,
238
+ });
239
+ successCount++;
240
+ }
241
+ catch (error) {
242
+ console.error(t('cli.messages.batchItemError', { input }));
243
+ handleError(error, `processing ${input}`);
244
+ errorCount++;
245
+ }
246
+ }
247
+ console.log(t('cli.messages.batchComplete', { success: successCount, error: errorCount }));
248
+ }
249
+ }
250
+ catch (error) {
251
+ handleError(error);
252
+ }
253
+ });
254
+ program
255
+ .command('generate')
256
+ .description(t('cli.commands.generate.description'))
257
+ .argument('<inputs...>', t('cli.options.inputFiles'))
258
+ .option('-o, --output <file>', t('cli.options.output'))
259
+ .option('-d, --output-dir <dir>', t('cli.options.outputDir'))
260
+ .option('-t, --theme <name>', t('cli.options.theme'))
261
+ .option('--grid <size>', t('cli.options.grid'))
262
+ .option('-c, --config <file>', t('cli.options.config'))
263
+ .option('--plugin-dir <dir>', 'Plugin directory')
264
+ .addHelpText('after', `
265
+ ${t('cli.commands.generate.examples.basic')}
266
+ ${t('cli.commands.generate.examples.theme')}
267
+ ${t('cli.commands.generate.examples.batch')}`)
268
+ .action(async (inputs, options) => {
269
+ try {
270
+ // Load and merge config
271
+ const config = await loadConfig(options.config);
272
+ const mergedOptions = mergeOptions(options, config);
273
+ const cwd = process.cwd();
274
+ // Validate pluginDir if specified
275
+ if (mergedOptions.pluginDir) {
276
+ try {
277
+ mergedOptions.pluginDir = validateAndResolvePath(mergedOptions.pluginDir, cwd);
278
+ }
279
+ catch (error) {
280
+ console.error(t('cli.messages.invalidPath', { path: mergedOptions.pluginDir, error: error instanceof Error ? error.message : String(error) }));
281
+ process.exit(1);
282
+ }
283
+ }
284
+ // Validate input file paths
285
+ const inputPaths = [];
286
+ for (const input of inputs) {
287
+ try {
288
+ const resolved = validateAndResolvePath(input, cwd);
289
+ inputPaths.push(resolved);
290
+ }
291
+ catch (error) {
292
+ console.error(t('cli.messages.invalidPath', { path: input, error: error instanceof Error ? error.message : String(error) }));
293
+ process.exit(1);
294
+ }
295
+ }
296
+ // Check input files exist
297
+ const missingFiles = inputPaths.filter(inputPath => !fs.existsSync(inputPath));
298
+ if (missingFiles.length > 0) {
299
+ console.error(t('cli.messages.fileNotFound'));
300
+ missingFiles.forEach(file => console.error(` - ${file}`));
301
+ process.exit(1);
302
+ }
303
+ // Check output directory if specified
304
+ let outputDir;
305
+ if (mergedOptions.outputDir) {
306
+ try {
307
+ outputDir = validateAndResolvePath(mergedOptions.outputDir, cwd);
308
+ if (!fs.existsSync(outputDir)) {
309
+ fs.mkdirSync(outputDir, { recursive: true });
310
+ }
311
+ }
312
+ catch (error) {
313
+ console.error(t('cli.messages.invalidPath', { path: mergedOptions.outputDir, error: error instanceof Error ? error.message : String(error) }));
314
+ process.exit(1);
315
+ }
316
+ }
317
+ if (inputs.length === 1) {
318
+ // Single file case
319
+ const inputPath = inputPaths[0];
320
+ let outputPath;
321
+ try {
322
+ outputPath = validateAndResolvePath(mergedOptions.output, cwd);
323
+ }
324
+ catch (error) {
325
+ console.error(t('cli.messages.invalidPath', { path: mergedOptions.output, error: error instanceof Error ? error.message : String(error) }));
326
+ process.exit(1);
327
+ }
328
+ console.log(t('cli.messages.converting', { input: inputPath }));
329
+ await marhupFile(inputPath, {
330
+ output: outputPath,
331
+ theme: mergedOptions.theme,
332
+ grid: mergedOptions.grid,
333
+ pluginDir: mergedOptions.pluginDir,
334
+ });
335
+ console.log(t('cli.messages.generationComplete', { output: outputPath }));
336
+ }
337
+ else {
338
+ // Multiple files case
339
+ if (mergedOptions.output !== 'output.pptx') {
340
+ console.warn(t('cli.messages.outputDirIgnoreWarning'));
341
+ }
342
+ console.log(t('cli.messages.batchProcessing', { count: inputs.length }));
343
+ let successCount = 0;
344
+ let errorCount = 0;
345
+ for (let i = 0; i < inputs.length; i++) {
346
+ const input = inputs[i];
347
+ const inputPath = inputPaths[i];
348
+ try {
349
+ // Determine output file name
350
+ const inputBaseName = path.basename(input, path.extname(input));
351
+ let outputPath;
352
+ if (outputDir) {
353
+ outputPath = path.join(outputDir, `${inputBaseName}.pptx`);
354
+ }
355
+ else {
356
+ outputPath = path.join(path.dirname(inputPath), `${inputBaseName}.pptx`);
357
+ }
358
+ console.log(t('cli.messages.batchItemSuccess', { index: i + 1, total: inputs.length, input, output: path.relative(process.cwd(), outputPath) }));
359
+ await marhupFile(inputPath, {
360
+ output: outputPath,
361
+ theme: mergedOptions.theme,
362
+ grid: mergedOptions.grid,
363
+ pluginDir: mergedOptions.pluginDir,
364
+ });
365
+ successCount++;
366
+ }
367
+ catch (error) {
368
+ console.error(t('cli.messages.batchItemError', { input }));
369
+ handleError(error, `processing ${input}`);
370
+ errorCount++;
371
+ }
372
+ }
373
+ console.log(`\n${t('cli.messages.batchComplete', { success: successCount, error: errorCount })}`);
374
+ }
375
+ }
376
+ catch (error) {
377
+ handleError(error, 'during PPTX generation');
378
+ }
379
+ });
380
+ program
381
+ .command('undo')
382
+ .description(t('cli.commands.undo.description'))
383
+ .argument('<output>', t('cli.options.output'))
384
+ .addHelpText('after', `
385
+ ${t('cli.commands.undo.examples.basic')}
386
+
387
+ ${t('cli.commands.undo.note')}`)
388
+ .action((output) => {
389
+ const outputPath = path.resolve(output);
390
+ const outputDir = path.dirname(outputPath);
391
+ const outputBase = path.basename(outputPath);
392
+ // バックアップファイルを検索(新しい形式と古い形式の両方)
393
+ const backupPattern = new RegExp(`^${outputBase}\\.bak(\\.\\d+\\.\\w+)?$`);
394
+ let latestBackup = null;
395
+ try {
396
+ const files = fs.readdirSync(outputDir);
397
+ for (const file of files) {
398
+ if (backupPattern.test(file)) {
399
+ const filePath = path.join(outputDir, file);
400
+ const stats = fs.statSync(filePath);
401
+ if (!latestBackup || stats.mtime.getTime() > latestBackup.mtime) {
402
+ latestBackup = { path: filePath, mtime: stats.mtime.getTime() };
403
+ }
404
+ }
405
+ }
406
+ }
407
+ catch (error) {
408
+ console.error(t('cli.messages.backupSearchFailed', { error }));
409
+ process.exit(1);
410
+ }
411
+ if (latestBackup) {
412
+ fs.renameSync(latestBackup.path, outputPath);
413
+ console.log(t('cli.messages.undoComplete', { output: outputPath }));
414
+ }
415
+ else {
416
+ console.error(t('cli.messages.noBackupFound'));
417
+ process.exit(1);
418
+ }
419
+ });
420
+ program
421
+ .command('export')
422
+ .description(t('cli.commands.export.description'))
423
+ .argument('<input>', t('cli.options.inputFiles'))
424
+ .option('-o, --output <file>', t('cli.options.output'), 'document.json')
425
+ .addHelpText('after', `
426
+ ${t('cli.commands.export.examples.basic')}`)
427
+ .action((input, options) => {
55
428
  try {
56
- // 入力ファイルの存在確認
57
429
  const inputPath = path.resolve(input);
58
430
  if (!fs.existsSync(inputPath)) {
59
- console.error(`エラー: ファイルが見つかりません: ${inputPath}`);
431
+ console.error(t('cli.messages.fileNotFound') + `: ${inputPath}`);
60
432
  process.exit(1);
61
433
  }
62
- // 出力パス
434
+ const markdown = fs.readFileSync(inputPath, 'utf-8');
435
+ const document = parse(markdown);
63
436
  const outputPath = path.resolve(options.output);
64
- console.log(`📝 ${input} を変換中...`);
65
- // 変換実行
66
- await (0, index_js_1.marhupFile)(inputPath, {
437
+ fs.writeFileSync(outputPath, JSON.stringify(document, null, 2));
438
+ console.log(t('cli.messages.exportComplete', { output: outputPath }));
439
+ // markdownをnull化
440
+ }
441
+ catch (error) {
442
+ handleError(error, 'duringJsonExport');
443
+ }
444
+ });
445
+ program
446
+ .command('import')
447
+ .description(t('cli.commands.import.description'))
448
+ .argument('<input>', t('cli.options.inputFiles'))
449
+ .option('-o, --output <file>', t('cli.options.output'))
450
+ .option('-t, --theme <name>', t('cli.options.theme'))
451
+ .option('--grid <size>', t('cli.options.grid'))
452
+ .option('-c, --config <file>', t('cli.options.config'))
453
+ .addHelpText('after', `
454
+ ${t('cli.commands.import.examples.basic')}`)
455
+ .action(async (input, options) => {
456
+ try {
457
+ // Load and merge config
458
+ const config = await loadConfig(options.config);
459
+ const mergedOptions = mergeOptions(options, config);
460
+ const inputPath = path.resolve(input);
461
+ if (!fs.existsSync(inputPath)) {
462
+ console.error(t('cli.messages.fileNotFound') + `: ${inputPath}`);
463
+ process.exit(1);
464
+ }
465
+ const outputPath = path.resolve(mergedOptions.output);
466
+ console.log(t('cli.messages.processingFromJson', { input }));
467
+ await marhupFromJson(inputPath, {
67
468
  output: outputPath,
68
- theme: options.theme,
69
- grid: options.grid,
469
+ theme: mergedOptions.theme,
470
+ grid: mergedOptions.grid,
70
471
  });
71
- console.log(`✅ 生成完了: ${outputPath}`);
72
- // 監視モード
73
- if (options.watch) {
74
- console.log('\n👀 監視モード開始... (Ctrl+C で終了)');
75
- fs.watch(inputPath, async (eventType) => {
76
- if (eventType === 'change') {
77
- console.log(`\n🔄 変更を検出: ${input}`);
78
- try {
79
- await (0, index_js_1.marhupFile)(inputPath, {
80
- output: outputPath,
81
- theme: options.theme,
82
- grid: options.grid,
83
- });
84
- console.log(`✅ 再生成完了: ${outputPath}`);
85
- }
86
- catch (error) {
87
- console.error('❌ 変換エラー:', error);
88
- }
89
- }
90
- });
472
+ console.log(t('cli.messages.generationComplete', { output: outputPath }));
473
+ }
474
+ catch (error) {
475
+ handleError(error, 'duringPptxGenerationFromJson');
476
+ }
477
+ });
478
+ program
479
+ .command('preview')
480
+ .description(t('cli.commands.preview.description'))
481
+ .argument('<input>', t('cli.options.inputFiles'))
482
+ .option('--grid <size>', t('cli.options.grid'), '12x9')
483
+ .addHelpText('after', `
484
+ ${t('cli.commands.preview.examples.basic')}
485
+ ${t('cli.commands.preview.examples.grid')}`)
486
+ .action((input, options) => {
487
+ try {
488
+ const inputPath = path.resolve(input);
489
+ if (!fs.existsSync(inputPath)) {
490
+ console.error(t('cli.messages.fileNotFound') + `: ${inputPath}`);
491
+ process.exit(1);
91
492
  }
493
+ const markdown = fs.readFileSync(inputPath, 'utf-8');
494
+ console.log(t('cli.messages.layoutPreview', { input }));
495
+ previewLayout(markdown, { grid: options.grid });
496
+ // markdownをnull化
92
497
  }
93
498
  catch (error) {
94
- console.error('❌ エラー:', error);
95
- process.exit(1);
499
+ handleError(error, 'duringLayoutPreview');
500
+ }
501
+ });
502
+ program
503
+ .command('watch')
504
+ .description(t('cli.commands.watch.description'))
505
+ .argument('<input>', t('cli.options.inputFiles'))
506
+ .option('-o, --output <file>', t('cli.options.output'))
507
+ .option('-t, --theme <name>', t('cli.options.theme'))
508
+ .option('--grid <size>', t('cli.options.grid'))
509
+ .option('-c, --config <file>', t('cli.options.config'))
510
+ .addHelpText('after', `
511
+ ${t('cli.commands.watch.examples.basic')}
512
+ ${t('cli.commands.watch.examples.theme')}
513
+
514
+ ${t('cli.commands.watch.note')}`)
515
+ .action(async (input, options) => {
516
+ try {
517
+ // Load and merge config
518
+ const config = await loadConfig(options.config);
519
+ const mergedOptions = mergeOptions(options, config);
520
+ // Check input file exists
521
+ const inputPath = path.resolve(input);
522
+ if (!fs.existsSync(inputPath)) {
523
+ console.error(t('cli.messages.fileNotFound') + `: ${inputPath}`);
524
+ process.exit(1);
525
+ }
526
+ // Output path
527
+ const outputPath = path.resolve(mergedOptions.output);
528
+ console.log(t('cli.messages.initialConversion', { input }));
529
+ await marhupFile(inputPath, {
530
+ output: outputPath,
531
+ theme: mergedOptions.theme,
532
+ grid: mergedOptions.grid,
533
+ });
534
+ console.log(t('cli.messages.generationComplete', { output: outputPath }));
535
+ console.log(t('cli.messages.watchModeStarted'));
536
+ const watcher = chokidar.watch(inputPath, {
537
+ persistent: true,
538
+ ignoreInitial: true,
539
+ });
540
+ watcher.on('change', async () => {
541
+ console.log(t('cli.messages.changeDetected', { input }));
542
+ try {
543
+ await marhupFile(inputPath, {
544
+ output: outputPath,
545
+ theme: mergedOptions.theme,
546
+ grid: mergedOptions.grid,
547
+ });
548
+ console.log(t('cli.messages.regenerationComplete', { output: outputPath }));
549
+ }
550
+ catch (error) {
551
+ handleError(error, 'duringWatchModeRegeneration');
552
+ }
553
+ });
554
+ // Graceful shutdown
555
+ process.on('SIGINT', () => {
556
+ watcher.close();
557
+ process.exit(0);
558
+ });
96
559
  }
560
+ catch (error) {
561
+ handleError(error, 'duringInitialConversion');
562
+ }
563
+ });
564
+ // デフォルトコマンド(後方互換性)- 引数チェック
565
+ const rawArgs = process.argv.slice(2);
566
+ const firstArg = rawArgs[0];
567
+ if (firstArg && !program.commands.some(cmd => cmd.name() === firstArg) && !firstArg.startsWith('-')) {
568
+ // サブコマンドが指定されていない場合(ファイルパスが最初の引数の場合)
569
+ // 手動でオプションをパース
570
+ const { inputs, options } = parseArgs(rawArgs);
571
+ if (inputs.length === 0) {
572
+ program.help();
573
+ process.exit(0);
574
+ }
575
+ (async () => {
576
+ try {
577
+ // Load and merge config for backward compatibility
578
+ const config = await loadConfig(options.config);
579
+ const mergedOptions = mergeOptions(options, config);
580
+ if (options.watch) {
581
+ // Watch mode supports single file only
582
+ if (inputs.length > 1) {
583
+ console.error(t('cli.messages.watchSingleFileOnly'));
584
+ process.exit(1);
585
+ }
586
+ // Delegate to watch command
587
+ const watchCmd = program.commands.find(cmd => cmd.name() === 'watch');
588
+ if (watchCmd) {
589
+ await watchCmd.action(inputs[0], mergedOptions);
590
+ }
591
+ }
592
+ else {
593
+ // generateコマンドに委譲
594
+ const generateCmd = program.commands.find(cmd => cmd.name() === 'generate');
595
+ if (generateCmd) {
596
+ await generateCmd.action(inputs, mergedOptions);
597
+ }
598
+ }
599
+ }
600
+ catch (error) {
601
+ handleError(error, 'during processing');
602
+ }
603
+ })();
604
+ }
605
+ else {
606
+ // 通常のサブコマンド処理
607
+ program.parse();
608
+ }
609
+ /**
610
+ * 引数を手動でパースする関数
611
+ */
612
+ function parseArgs(args) {
613
+ const inputs = [];
614
+ const options = {};
615
+ for (let i = 0; i < args.length; i++) {
616
+ const arg = args[i];
617
+ if (arg.startsWith('-')) {
618
+ switch (arg) {
619
+ case '-o':
620
+ case '--output':
621
+ options.output = args[++i];
622
+ break;
623
+ case '-d':
624
+ case '--output-dir':
625
+ options.outputDir = args[++i];
626
+ break;
627
+ case '-t':
628
+ case '--theme':
629
+ options.theme = args[++i];
630
+ break;
631
+ case '--grid':
632
+ options.grid = args[++i];
633
+ break;
634
+ case '-w':
635
+ case '--watch':
636
+ options.watch = true;
637
+ break;
638
+ case '-c':
639
+ case '--config':
640
+ options.config = args[++i];
641
+ break;
642
+ case '-l':
643
+ case '--lang':
644
+ options.lang = args[++i];
645
+ break;
646
+ default:
647
+ // 未知のオプションは無視
648
+ break;
649
+ }
650
+ }
651
+ else {
652
+ inputs.push(arg);
653
+ }
654
+ }
655
+ return { inputs, options };
656
+ }
657
+ program
658
+ .command('help')
659
+ .description(t('cli.commands.help.description'))
660
+ .action(() => {
661
+ console.log(t('cli.help.title'));
662
+ console.log(t('cli.help.basicUsage'));
663
+ console.log(t('cli.help.codeExamples.basic'));
664
+ console.log(t('cli.help.codeExamples.theme'));
665
+ console.log(t('cli.help.codeExamples.watch'));
666
+ console.log(t('cli.help.codeExamples.batch'));
667
+ console.log(t('cli.help.codeExamples.config'));
668
+ console.log(t('cli.help.configFiles'));
669
+ console.log(t('cli.help.supportedFiles'));
670
+ console.log(t('cli.help.configExamples'));
671
+ console.log(t('cli.help.syntaxGuide'));
672
+ console.log(t('cli.help.slideSeparation'));
673
+ console.log(t('cli.help.frontMatter'));
674
+ console.log(t('cli.help.availableCommands'));
675
+ console.log(t('cli.help.availableStyles'));
676
+ console.log(t('cli.help.animations'));
677
+ console.log(t('cli.help.documentation'));
97
678
  });
98
679
  program.parse();
99
680
  //# sourceMappingURL=cli.js.map