marhup 0.1.8 → 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.
- package/README.md +419 -2
- package/dist/cli.js +659 -78
- package/dist/cli.js.map +1 -1
- package/dist/errors.d.ts +29 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +62 -0
- package/dist/errors.js.map +1 -0
- package/dist/generator/code.d.ts +4 -3
- package/dist/generator/code.d.ts.map +1 -1
- package/dist/generator/code.js +7 -7
- package/dist/generator/code.js.map +1 -1
- package/dist/generator/gen-animations.d.ts +13 -0
- package/dist/generator/gen-animations.d.ts.map +1 -0
- package/dist/generator/gen-animations.js +77 -0
- package/dist/generator/gen-animations.js.map +1 -0
- package/dist/generator/image.d.ts +4 -3
- package/dist/generator/image.d.ts.map +1 -1
- package/dist/generator/image.js +67 -80
- package/dist/generator/image.js.map +1 -1
- package/dist/generator/index.d.ts +2 -0
- package/dist/generator/index.d.ts.map +1 -1
- package/dist/generator/index.js +9 -18
- package/dist/generator/index.js.map +1 -1
- package/dist/generator/list.d.ts +4 -3
- package/dist/generator/list.d.ts.map +1 -1
- package/dist/generator/list.js +15 -17
- package/dist/generator/list.js.map +1 -1
- package/dist/generator/mermaid.d.ts +9 -4
- package/dist/generator/mermaid.d.ts.map +1 -1
- package/dist/generator/mermaid.js +120 -85
- package/dist/generator/mermaid.js.map +1 -1
- package/dist/generator/pptx.d.ts +1 -1
- package/dist/generator/pptx.d.ts.map +1 -1
- package/dist/generator/pptx.js +232 -60
- package/dist/generator/pptx.js.map +1 -1
- package/dist/generator/presentation.d.ts +149 -0
- package/dist/generator/presentation.d.ts.map +1 -0
- package/dist/generator/presentation.js +163 -0
- package/dist/generator/presentation.js.map +1 -0
- package/dist/generator/table.d.ts +4 -3
- package/dist/generator/table.d.ts.map +1 -1
- package/dist/generator/table.js +12 -33
- package/dist/generator/table.js.map +1 -1
- package/dist/generator/text.d.ts +5 -4
- package/dist/generator/text.d.ts.map +1 -1
- package/dist/generator/text.js +34 -20
- package/dist/generator/text.js.map +1 -1
- package/dist/generator/video.d.ts +18 -0
- package/dist/generator/video.d.ts.map +1 -0
- package/dist/generator/video.js +83 -0
- package/dist/generator/video.js.map +1 -0
- package/dist/index.d.ts +8 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +144 -55
- package/dist/index.js.map +1 -1
- package/dist/layout/auto.d.ts +14 -2
- package/dist/layout/auto.d.ts.map +1 -1
- package/dist/layout/auto.js +129 -37
- package/dist/layout/auto.js.map +1 -1
- package/dist/layout/engine.d.ts +1 -1
- package/dist/layout/engine.d.ts.map +1 -1
- package/dist/layout/engine.js +10 -12
- package/dist/layout/engine.js.map +1 -1
- package/dist/layout/index.d.ts +1 -1
- package/dist/layout/index.d.ts.map +1 -1
- package/dist/layout/index.js +2 -10
- package/dist/layout/index.js.map +1 -1
- package/dist/layout/types.d.ts +1 -0
- package/dist/layout/types.d.ts.map +1 -1
- package/dist/layout/types.js +1 -2
- package/dist/layout/types.js.map +1 -1
- package/dist/mcp-handlers.d.ts.map +1 -1
- package/dist/mcp-handlers.js +15 -51
- package/dist/mcp-handlers.js.map +1 -1
- package/dist/mcp.js +10 -12
- package/dist/mcp.js.map +1 -1
- package/dist/parser/frontmatter.d.ts.map +1 -1
- package/dist/parser/frontmatter.js +95 -16
- package/dist/parser/frontmatter.js.map +1 -1
- package/dist/parser/grid.d.ts +5 -7
- package/dist/parser/grid.d.ts.map +1 -1
- package/dist/parser/grid.js +190 -19
- package/dist/parser/grid.js.map +1 -1
- package/dist/parser/index.js +3 -13
- package/dist/parser/index.js.map +1 -1
- package/dist/parser/markdown.d.ts +1 -1
- package/dist/parser/markdown.d.ts.map +1 -1
- package/dist/parser/markdown.js +247 -137
- package/dist/parser/markdown.js.map +1 -1
- package/dist/theme/default.d.ts.map +1 -1
- package/dist/theme/default.js +13 -8
- package/dist/theme/default.js.map +1 -1
- package/dist/theme/index.d.ts +11 -0
- package/dist/theme/index.d.ts.map +1 -1
- package/dist/theme/index.js +70 -7
- package/dist/theme/index.js.map +1 -1
- package/dist/types/index.d.ts +54 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +4 -6
- package/dist/types/index.js.map +1 -1
- package/dist/types/plugin.d.ts +50 -0
- package/dist/types/plugin.d.ts.map +1 -0
- package/dist/types/plugin.js +5 -0
- package/dist/types/plugin.js.map +1 -0
- package/dist/utils/file-lock.d.ts +27 -0
- package/dist/utils/file-lock.d.ts.map +1 -0
- package/dist/utils/file-lock.js +118 -0
- package/dist/utils/file-lock.js.map +1 -0
- package/dist/utils/i18n.d.ts +5 -0
- package/dist/utils/i18n.d.ts.map +1 -0
- package/dist/utils/i18n.js +41 -0
- package/dist/utils/i18n.js.map +1 -0
- package/dist/utils/logger.d.ts +7 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +43 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/path-validation.d.ts +20 -0
- package/dist/utils/path-validation.d.ts.map +1 -0
- package/dist/utils/path-validation.js +39 -0
- package/dist/utils/path-validation.js.map +1 -0
- package/dist/utils/plugin-manager.d.ts +18 -0
- package/dist/utils/plugin-manager.d.ts.map +1 -0
- package/dist/utils/plugin-manager.js +108 -0
- package/dist/utils/plugin-manager.js.map +1 -0
- package/dist/utils/sanitizer.d.ts +14 -0
- package/dist/utils/sanitizer.d.ts.map +1 -0
- package/dist/utils/sanitizer.js +73 -0
- package/dist/utils/sanitizer.js.map +1 -0
- 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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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('
|
|
47
|
-
.description(
|
|
48
|
-
.version('
|
|
49
|
-
.
|
|
50
|
-
.option('-
|
|
51
|
-
.option('-t, --theme <name>', '
|
|
52
|
-
.option('
|
|
53
|
-
.option('
|
|
54
|
-
.
|
|
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(
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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:
|
|
69
|
-
grid:
|
|
469
|
+
theme: mergedOptions.theme,
|
|
470
|
+
grid: mergedOptions.grid,
|
|
70
471
|
});
|
|
71
|
-
console.log(
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
|
|
95
|
-
|
|
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
|