@hirokisakabe/pom-cli 0.2.4 → 0.3.0
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 +35 -0
- package/dist/build.d.ts +7 -1
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +79 -4
- package/dist/cli.js +57 -28
- package/dist/preview.d.ts +3 -1
- package/dist/preview.d.ts.map +1 -1
- package/dist/preview.js +29 -8
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -21,8 +21,20 @@ pom preview slides.pom.md
|
|
|
21
21
|
|
|
22
22
|
Open http://localhost:3000 in your browser. The page updates automatically when the file is saved.
|
|
23
23
|
|
|
24
|
+
To use a different port (e.g. when 3000 is already in use):
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pom preview slides.pom.xml --port 3001
|
|
28
|
+
```
|
|
29
|
+
|
|
24
30
|
Use the zoom buttons in the toolbar or press `+` / `-` to zoom in and out. The current zoom level is saved across sessions.
|
|
25
31
|
|
|
32
|
+
To print per-step timing on stderr when each rebuild completes:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pom preview slides.pom.xml --verbose
|
|
36
|
+
```
|
|
37
|
+
|
|
26
38
|
### Build
|
|
27
39
|
|
|
28
40
|
Converts a pom file to a PPTX file.
|
|
@@ -32,6 +44,29 @@ pom build slides.pom.xml -o output.pptx
|
|
|
32
44
|
pom build slides.pom.md -o output.pptx
|
|
33
45
|
```
|
|
34
46
|
|
|
47
|
+
To watch for file changes and rebuild automatically:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pom build slides.pom.xml -o output.pptx --watch
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The process stays running and rebuilds every time the input file is saved. Build progress is printed to stderr:
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
[pom] Watching: slides.pom.xml
|
|
57
|
+
[pom] Built: output.pptx (367ms)
|
|
58
|
+
[pom] File changed, rebuilding...
|
|
59
|
+
[pom] Built: output.pptx (342ms)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
If a build fails, the error is printed but the process continues watching for the next change. Press `Ctrl+C` to stop.
|
|
63
|
+
|
|
64
|
+
To print per-step timing on stderr:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
pom build slides.pom.xml -o output.pptx --verbose
|
|
68
|
+
```
|
|
69
|
+
|
|
35
70
|
## Fonts
|
|
36
71
|
|
|
37
72
|
This package bundles Carlito and Noto Sans CJK JP fonts for SVG rendering. These fonts are used when converting slides to SVG in the preview server. System fonts are not scanned.
|
package/dist/build.d.ts
CHANGED
|
@@ -1,2 +1,8 @@
|
|
|
1
|
-
export declare function runBuild(inputFile: string, outputFile: string
|
|
1
|
+
export declare function runBuild(inputFile: string, outputFile: string, options?: {
|
|
2
|
+
verbose?: boolean;
|
|
3
|
+
silent?: boolean;
|
|
4
|
+
}): Promise<void>;
|
|
5
|
+
export declare function runBuildWatch(inputFile: string, outputFile: string, options?: {
|
|
6
|
+
verbose?: boolean;
|
|
7
|
+
}): Promise<void>;
|
|
2
8
|
//# sourceMappingURL=build.d.ts.map
|
package/dist/build.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"AAUA,wBAAsB,QAAQ,CAC5B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GACpD,OAAO,CAAC,IAAI,CAAC,CAwEf;AAED,wBAAsB,aAAa,CACjC,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAO,GAClC,OAAO,CAAC,IAAI,CAAC,CA2Df"}
|
package/dist/build.js
CHANGED
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
import fs from "fs";
|
|
2
2
|
import path from "path";
|
|
3
|
-
import { buildPptx } from "@hirokisakabe/pom";
|
|
3
|
+
import { buildPptx, DiagnosticsError } from "@hirokisakabe/pom";
|
|
4
4
|
import { parseMd } from "@hirokisakabe/pom-md";
|
|
5
|
-
|
|
5
|
+
function makeLog(verbose) {
|
|
6
|
+
if (!verbose)
|
|
7
|
+
return (_msg) => { };
|
|
8
|
+
return (msg) => process.stderr.write(`[pom] ${msg}\n`);
|
|
9
|
+
}
|
|
10
|
+
export async function runBuild(inputFile, outputFile, options = {}) {
|
|
11
|
+
const verbose = options.verbose ?? false;
|
|
12
|
+
const log = makeLog(verbose);
|
|
13
|
+
const totalStart = Date.now();
|
|
6
14
|
const absInput = path.resolve(inputFile);
|
|
7
15
|
const absOutput = path.resolve(outputFile);
|
|
8
16
|
if (!fs.existsSync(absInput)) {
|
|
9
17
|
throw new Error(`Input file not found: ${absInput}`);
|
|
10
18
|
}
|
|
19
|
+
log(`Reading file: ${absInput}`);
|
|
11
20
|
const content = fs.readFileSync(absInput, "utf-8");
|
|
12
21
|
const ext = path.extname(absInput);
|
|
13
22
|
let xml;
|
|
@@ -15,10 +24,12 @@ export async function runBuild(inputFile, outputFile) {
|
|
|
15
24
|
let slideHeight = 720;
|
|
16
25
|
let masterPptxData;
|
|
17
26
|
if (ext === ".md") {
|
|
27
|
+
const t = Date.now();
|
|
18
28
|
const result = parseMd(content);
|
|
19
29
|
xml = result.xml;
|
|
20
30
|
slideWidth = result.meta.size.w;
|
|
21
31
|
slideHeight = result.meta.size.h;
|
|
32
|
+
log(`Parsing Markdown... done (${Date.now() - t}ms)`);
|
|
22
33
|
if (result.meta.masterPptx) {
|
|
23
34
|
const masterPath = path.resolve(path.dirname(absInput), result.meta.masterPptx);
|
|
24
35
|
try {
|
|
@@ -37,11 +48,75 @@ export async function runBuild(inputFile, outputFile) {
|
|
|
37
48
|
else {
|
|
38
49
|
xml = content;
|
|
39
50
|
}
|
|
40
|
-
const
|
|
51
|
+
const t1 = Date.now();
|
|
52
|
+
const { pptx } = await buildPptx(xml, { w: slideWidth, h: slideHeight }, {
|
|
53
|
+
...(masterPptxData ? { masterPptx: masterPptxData } : {}),
|
|
54
|
+
strict: true,
|
|
55
|
+
});
|
|
56
|
+
log(`Building PPTX... done (${Date.now() - t1}ms)`);
|
|
57
|
+
const t2 = Date.now();
|
|
41
58
|
const buffer = await pptx.write({ outputType: "uint8array" });
|
|
42
59
|
if (!(buffer instanceof Uint8Array)) {
|
|
43
60
|
throw new Error("Unexpected output type from pptx.write");
|
|
44
61
|
}
|
|
45
62
|
fs.writeFileSync(absOutput, buffer);
|
|
46
|
-
|
|
63
|
+
log(`Writing output... done (${Date.now() - t2}ms)`);
|
|
64
|
+
const total = Date.now() - totalStart;
|
|
65
|
+
log(`Total: ${total}ms`);
|
|
66
|
+
if (!options.silent) {
|
|
67
|
+
console.log(`PPTX saved: ${absOutput}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
export async function runBuildWatch(inputFile, outputFile, options = {}) {
|
|
71
|
+
const absInput = path.resolve(inputFile);
|
|
72
|
+
const absOutput = path.resolve(outputFile);
|
|
73
|
+
if (!fs.existsSync(absInput)) {
|
|
74
|
+
throw new Error(`Input file not found: ${absInput}`);
|
|
75
|
+
}
|
|
76
|
+
const watchLog = (msg) => process.stderr.write(`[pom] ${msg}\n`);
|
|
77
|
+
watchLog(`Watching: ${path.basename(absInput)}`);
|
|
78
|
+
let isBuilding = false;
|
|
79
|
+
let pendingRebuild = false;
|
|
80
|
+
async function doBuild() {
|
|
81
|
+
if (isBuilding) {
|
|
82
|
+
pendingRebuild = true;
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
isBuilding = true;
|
|
86
|
+
pendingRebuild = false;
|
|
87
|
+
const start = Date.now();
|
|
88
|
+
try {
|
|
89
|
+
await runBuild(inputFile, outputFile, { ...options, silent: true });
|
|
90
|
+
watchLog(`Built: ${path.basename(absOutput)} (${Date.now() - start}ms)`);
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
if (err instanceof DiagnosticsError) {
|
|
94
|
+
const count = err.diagnostics.length;
|
|
95
|
+
process.stderr.write(`[pom] Build failed (${count} ${count === 1 ? "error" : "errors"})\n`);
|
|
96
|
+
for (const d of err.diagnostics) {
|
|
97
|
+
process.stderr.write(`[pom] [${d.code}] ${d.message}\n`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
process.stderr.write(`[pom] Error: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
finally {
|
|
105
|
+
isBuilding = false;
|
|
106
|
+
if (pendingRebuild) {
|
|
107
|
+
watchLog("File changed, rebuilding...");
|
|
108
|
+
void doBuild();
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
await doBuild();
|
|
113
|
+
let debounceTimer = null;
|
|
114
|
+
fs.watch(absInput, () => {
|
|
115
|
+
if (debounceTimer)
|
|
116
|
+
clearTimeout(debounceTimer);
|
|
117
|
+
debounceTimer = setTimeout(() => {
|
|
118
|
+
watchLog("File changed, rebuilding...");
|
|
119
|
+
void doBuild();
|
|
120
|
+
}, 100);
|
|
121
|
+
});
|
|
47
122
|
}
|
package/dist/cli.js
CHANGED
|
@@ -1,38 +1,67 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import { Command } from "commander";
|
|
4
|
+
import { DiagnosticsError } from "@hirokisakabe/pom";
|
|
5
|
+
import { runBuild, runBuildWatch } from "./build.js";
|
|
3
6
|
import { runPreview } from "./preview.js";
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
const require = createRequire(import.meta.url);
|
|
8
|
+
const { version } = require("../package.json");
|
|
9
|
+
const program = new Command();
|
|
10
|
+
program
|
|
11
|
+
.name("pom")
|
|
12
|
+
.description("CLI tool for pom — preview and build presentations")
|
|
13
|
+
.version(version);
|
|
14
|
+
program
|
|
15
|
+
.command("preview")
|
|
16
|
+
.description("Start a live preview server for a presentation")
|
|
17
|
+
.argument("<input>", "Input file (.pom.xml or .pom.md)")
|
|
18
|
+
.option("--port <number>", "Port to listen on")
|
|
19
|
+
.option("--verbose", "Show build step timing on stderr")
|
|
20
|
+
.action((input, options) => {
|
|
21
|
+
let port;
|
|
22
|
+
if (options.port !== undefined) {
|
|
23
|
+
port = Number(options.port);
|
|
24
|
+
if (!Number.isInteger(port) || port <= 0 || port > 65535) {
|
|
25
|
+
console.error(`Invalid port: ${options.port}`);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
11
28
|
}
|
|
12
29
|
try {
|
|
13
|
-
runPreview(
|
|
30
|
+
runPreview(input, port, { verbose: options.verbose });
|
|
14
31
|
}
|
|
15
32
|
catch (err) {
|
|
16
33
|
console.error(err instanceof Error ? err.message : String(err));
|
|
17
34
|
process.exit(1);
|
|
18
35
|
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
36
|
+
});
|
|
37
|
+
program
|
|
38
|
+
.command("build")
|
|
39
|
+
.description("Build a presentation to PPTX")
|
|
40
|
+
.argument("<input>", "Input file (.pom.xml or .pom.md)")
|
|
41
|
+
.requiredOption("-o <output>", "Output PPTX file")
|
|
42
|
+
.option("--verbose", "Show build step timing on stderr")
|
|
43
|
+
.option("--watch", "Watch for file changes and rebuild automatically")
|
|
44
|
+
.action((input, options) => {
|
|
45
|
+
if (options.watch) {
|
|
46
|
+
runBuildWatch(input, options.o, { verbose: options.verbose }).catch((err) => {
|
|
47
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
48
|
+
process.exit(1);
|
|
49
|
+
});
|
|
27
50
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
51
|
+
else {
|
|
52
|
+
runBuild(input, options.o, { verbose: options.verbose }).catch((err) => {
|
|
53
|
+
if (err instanceof DiagnosticsError) {
|
|
54
|
+
const count = err.diagnostics.length;
|
|
55
|
+
console.error(`✗ Build failed (${count} ${count === 1 ? "error" : "errors"})\n`);
|
|
56
|
+
for (const d of err.diagnostics) {
|
|
57
|
+
console.error(` [${d.code}] ${d.message}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
62
|
+
}
|
|
63
|
+
process.exit(1);
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
program.parse();
|
package/dist/preview.d.ts
CHANGED
package/dist/preview.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preview.d.ts","sourceRoot":"","sources":["../src/preview.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"preview.d.ts","sourceRoot":"","sources":["../src/preview.ts"],"names":[],"mappings":"AA8XA,wBAAgB,UAAU,CACxB,SAAS,EAAE,MAAM,EACjB,IAAI,GAAE,MAAqB,EAC3B,OAAO,GAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAO,GAClC,IAAI,CAkHN"}
|
package/dist/preview.js
CHANGED
|
@@ -7,11 +7,17 @@ import { parseMd } from "@hirokisakabe/pom-md";
|
|
|
7
7
|
import { convertPptxToSvg } from "pptx-glimpse";
|
|
8
8
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
9
9
|
const DEFAULT_PORT = 3000;
|
|
10
|
+
function makeLog(verbose) {
|
|
11
|
+
if (!verbose)
|
|
12
|
+
return (_msg) => { };
|
|
13
|
+
return (msg) => process.stderr.write(`[pom] ${msg}\n`);
|
|
14
|
+
}
|
|
10
15
|
const EXTRA_FONT_MAPPING = {
|
|
11
16
|
"游ゴシック Light": "Noto Sans CJK JP",
|
|
12
17
|
"Yu Gothic Light": "Noto Sans CJK JP",
|
|
13
18
|
};
|
|
14
|
-
async function generateSvgs(inputFile) {
|
|
19
|
+
async function generateSvgs(inputFile, verbose = false) {
|
|
20
|
+
const log = makeLog(verbose);
|
|
15
21
|
const content = fs.readFileSync(inputFile, "utf-8");
|
|
16
22
|
const ext = path.extname(inputFile);
|
|
17
23
|
let xml;
|
|
@@ -19,10 +25,12 @@ async function generateSvgs(inputFile) {
|
|
|
19
25
|
let slideHeight = 720;
|
|
20
26
|
let masterPptxData;
|
|
21
27
|
if (ext === ".md") {
|
|
28
|
+
const t = Date.now();
|
|
22
29
|
const result = parseMd(content);
|
|
23
30
|
xml = result.xml;
|
|
24
31
|
slideWidth = result.meta.size.w;
|
|
25
32
|
slideHeight = result.meta.size.h;
|
|
33
|
+
log(`Parsing Markdown... done (${Date.now() - t}ms)`);
|
|
26
34
|
if (result.meta.masterPptx) {
|
|
27
35
|
const masterPath = path.resolve(path.dirname(inputFile), result.meta.masterPptx);
|
|
28
36
|
try {
|
|
@@ -44,10 +52,12 @@ async function generateSvgs(inputFile) {
|
|
|
44
52
|
if (!xml.trim()) {
|
|
45
53
|
return { type: "empty" };
|
|
46
54
|
}
|
|
55
|
+
const t1 = Date.now();
|
|
47
56
|
const { pptx } = await buildPptx(xml, { w: slideWidth, h: slideHeight }, {
|
|
48
57
|
textMeasurement: "fallback",
|
|
49
58
|
...(masterPptxData ? { masterPptx: masterPptxData } : {}),
|
|
50
59
|
});
|
|
60
|
+
log(`Building PPTX... done (${Date.now() - t1}ms)`);
|
|
51
61
|
const buffer = await pptx.write({ outputType: "uint8array" });
|
|
52
62
|
if (!(buffer instanceof Uint8Array)) {
|
|
53
63
|
throw new Error("Unexpected output type from pptx.write");
|
|
@@ -57,12 +67,14 @@ async function generateSvgs(inputFile) {
|
|
|
57
67
|
throw new Error(`Bundled fonts directory not found: ${fontsDir}. The package may be corrupted.`);
|
|
58
68
|
}
|
|
59
69
|
const fontDirs = [fontsDir];
|
|
70
|
+
const t2 = Date.now();
|
|
60
71
|
const slides = await convertPptxToSvg(buffer, {
|
|
61
72
|
width: slideWidth,
|
|
62
73
|
fontDirs,
|
|
63
74
|
fontMapping: EXTRA_FONT_MAPPING,
|
|
64
75
|
skipSystemFonts: true,
|
|
65
76
|
});
|
|
77
|
+
log(`Converting to SVG... done (${Date.now() - t2}ms)`);
|
|
66
78
|
const svgs = slides.map((s) => s.svg);
|
|
67
79
|
return { type: "success", svgs, slideWidth };
|
|
68
80
|
}
|
|
@@ -336,7 +348,9 @@ function buildPreviewHtml(filename) {
|
|
|
336
348
|
</body>
|
|
337
349
|
</html>`;
|
|
338
350
|
}
|
|
339
|
-
export function runPreview(inputFile) {
|
|
351
|
+
export function runPreview(inputFile, port = DEFAULT_PORT, options = {}) {
|
|
352
|
+
const verbose = options.verbose ?? false;
|
|
353
|
+
const log = makeLog(verbose);
|
|
340
354
|
const absInput = path.resolve(inputFile);
|
|
341
355
|
if (!fs.existsSync(absInput)) {
|
|
342
356
|
throw new Error(`Input file not found: ${absInput}`);
|
|
@@ -358,9 +372,14 @@ export function runPreview(inputFile) {
|
|
|
358
372
|
}
|
|
359
373
|
}
|
|
360
374
|
function refresh() {
|
|
375
|
+
const totalStart = Date.now();
|
|
376
|
+
log("File changed, rebuilding...");
|
|
361
377
|
broadcastBuilding();
|
|
362
|
-
generateSvgs(absInput)
|
|
363
|
-
.then(
|
|
378
|
+
generateSvgs(absInput, verbose)
|
|
379
|
+
.then((result) => {
|
|
380
|
+
log(`Done (total: ${Date.now() - totalStart}ms)`);
|
|
381
|
+
broadcast(result);
|
|
382
|
+
})
|
|
364
383
|
.catch((err) => {
|
|
365
384
|
broadcast({
|
|
366
385
|
type: "error",
|
|
@@ -368,10 +387,12 @@ export function runPreview(inputFile) {
|
|
|
368
387
|
});
|
|
369
388
|
});
|
|
370
389
|
}
|
|
371
|
-
|
|
390
|
+
const initialStart = Date.now();
|
|
391
|
+
generateSvgs(absInput, verbose)
|
|
372
392
|
.then((result) => {
|
|
373
393
|
currentResult = result;
|
|
374
394
|
initialBuildDone = true;
|
|
395
|
+
log(`Initial build done (total: ${Date.now() - initialStart}ms)`);
|
|
375
396
|
broadcast(result);
|
|
376
397
|
})
|
|
377
398
|
.catch((err) => {
|
|
@@ -417,15 +438,15 @@ export function runPreview(inputFile) {
|
|
|
417
438
|
});
|
|
418
439
|
server.on("error", (err) => {
|
|
419
440
|
if (err.code === "EADDRINUSE") {
|
|
420
|
-
console.error(`Port ${
|
|
441
|
+
console.error(`Port ${port} is already in use. Is another pom preview running?`);
|
|
421
442
|
}
|
|
422
443
|
else {
|
|
423
444
|
console.error(`Server error: ${err.message}`);
|
|
424
445
|
}
|
|
425
446
|
process.exit(1);
|
|
426
447
|
});
|
|
427
|
-
server.listen(
|
|
428
|
-
console.log(`Preview server: http://localhost:${
|
|
448
|
+
server.listen(port, () => {
|
|
449
|
+
console.log(`Preview server: http://localhost:${port}`);
|
|
429
450
|
console.log(`Watching: ${absInput}`);
|
|
430
451
|
console.log("Press Ctrl+C to stop");
|
|
431
452
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hirokisakabe/pom-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "CLI tool for pom — preview and build presentations",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -30,9 +30,10 @@
|
|
|
30
30
|
"node": ">=18"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"
|
|
34
|
-
"@hirokisakabe/pom": "
|
|
35
|
-
"
|
|
33
|
+
"@hirokisakabe/pom": "^8.2.0",
|
|
34
|
+
"@hirokisakabe/pom-md": "^3.0.0",
|
|
35
|
+
"commander": "^15.0.0",
|
|
36
|
+
"pptx-glimpse": "^0.11.0"
|
|
36
37
|
},
|
|
37
38
|
"devDependencies": {
|
|
38
39
|
"@types/node": "^25.9.1",
|