@masumdev/markforge 0.3.0 → 0.4.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.
@@ -0,0 +1,328 @@
1
+ #!/usr/bin/env node
2
+ try {
3
+ if (typeof globalThis !== "undefined" && (!globalThis.localStorage || typeof globalThis.localStorage.getItem !== "function")) {
4
+ Object.defineProperty(globalThis, "localStorage", {
5
+ value: { getItem: () => null, setItem: () => {}, removeItem: () => {}, clear: () => {}, key: () => null, length: 0 },
6
+ configurable: true, writable: true,
7
+ });
8
+ }
9
+ } catch {}
10
+ import {
11
+ MARKFORGE_VERSION
12
+ } from "./chunk-TNWZ4MFS.mjs";
13
+ import {
14
+ buildDocxDocument,
15
+ buildHtmlDocument,
16
+ buildPdfDocument,
17
+ parseMarkdownDocument
18
+ } from "./chunk-CRV7R2BG.mjs";
19
+ import {
20
+ DEFAULT_CONFIG
21
+ } from "./chunk-BPHDY6VB.mjs";
22
+ import "./chunk-YZHGBG4N.mjs";
23
+
24
+ // src/ui/App.tsx
25
+ import { useState, useEffect } from "react";
26
+ import { Box as Box4 } from "ink";
27
+
28
+ // src/ui/Header.tsx
29
+ import * as path from "path";
30
+ import { Box, Text } from "ink";
31
+ import { jsx, jsxs } from "react/jsx-runtime";
32
+ function formatDisplayPath(p) {
33
+ if (!p) return "";
34
+ try {
35
+ const rel = path.relative(process.cwd(), p);
36
+ return rel && !rel.startsWith("..") && rel.length < p.length ? `./${rel}` : p;
37
+ } catch {
38
+ return p;
39
+ }
40
+ }
41
+ var Header = ({
42
+ inputFile,
43
+ theme = "corporate",
44
+ targetFormats = ["docx", "pdf"]
45
+ }) => {
46
+ const themeLabel = typeof theme === "object" ? "Custom (ThemeProps)" : String(theme || "corporate");
47
+ return /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginY: 1, children: /* @__PURE__ */ jsxs(
48
+ Box,
49
+ {
50
+ borderStyle: "round",
51
+ borderColor: "cyan",
52
+ paddingX: 2,
53
+ paddingY: 1,
54
+ flexDirection: "column",
55
+ children: [
56
+ /* @__PURE__ */ jsxs(Box, { justifyContent: "space-between", alignItems: "center", children: [
57
+ /* @__PURE__ */ jsxs(Box, { children: [
58
+ /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "MARKFORGE" }),
59
+ /* @__PURE__ */ jsx(Text, { color: "gray", children: " | Document Publishing Engine" })
60
+ ] }),
61
+ /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, { color: "black", backgroundColor: "cyan", bold: true, children: [
62
+ " ",
63
+ "v",
64
+ MARKFORGE_VERSION,
65
+ " "
66
+ ] }) })
67
+ ] }),
68
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
69
+ inputFile && /* @__PURE__ */ jsxs(Box, { children: [
70
+ /* @__PURE__ */ jsx(Text, { color: "gray", children: "Source: " }),
71
+ /* @__PURE__ */ jsx(Text, { color: "white", bold: true, children: formatDisplayPath(inputFile) })
72
+ ] }),
73
+ /* @__PURE__ */ jsxs(Box, { justifyContent: "space-between", marginTop: 0, children: [
74
+ /* @__PURE__ */ jsxs(Box, { children: [
75
+ /* @__PURE__ */ jsx(Text, { color: "gray", children: "Theme: " }),
76
+ /* @__PURE__ */ jsx(Text, { color: "magenta", bold: true, children: themeLabel })
77
+ ] }),
78
+ targetFormats && targetFormats.length > 0 && /* @__PURE__ */ jsxs(Box, { children: [
79
+ /* @__PURE__ */ jsx(Text, { color: "gray", children: "Targets: " }),
80
+ /* @__PURE__ */ jsx(Text, { color: "yellow", bold: true, children: targetFormats.map((f) => f.toUpperCase()).join(", ") })
81
+ ] })
82
+ ] })
83
+ ] }),
84
+ /* @__PURE__ */ jsxs(Box, { marginTop: 1, justifyContent: "space-between", children: [
85
+ /* @__PURE__ */ jsxs(Box, { children: [
86
+ /* @__PURE__ */ jsx(Text, { color: "gray", children: "Author: " }),
87
+ /* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "Ma'sum" }),
88
+ /* @__PURE__ */ jsx(Text, { color: "gray", children: " (@masumrpg)" })
89
+ ] }),
90
+ /* @__PURE__ */ jsxs(Box, { children: [
91
+ /* @__PURE__ */ jsx(Text, { color: "gray", children: "GitHub: " }),
92
+ /* @__PURE__ */ jsx(Text, { color: "blue", underline: true, children: "https://github.com/masumrpg" })
93
+ ] })
94
+ ] })
95
+ ]
96
+ }
97
+ ) });
98
+ };
99
+
100
+ // src/ui/LiveProgress.tsx
101
+ import { Box as Box2, Text as Text2 } from "ink";
102
+ import Spinner from "ink-spinner";
103
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
104
+ var LiveProgress = ({ status, isCompiling }) => {
105
+ if (!isCompiling) return null;
106
+ return /* @__PURE__ */ jsxs2(Box2, { marginY: 1, alignItems: "center", children: [
107
+ /* @__PURE__ */ jsx2(Text2, { color: "cyan", children: /* @__PURE__ */ jsx2(Spinner, { type: "dots" }) }),
108
+ /* @__PURE__ */ jsxs2(Box2, { marginLeft: 1, children: [
109
+ /* @__PURE__ */ jsx2(Text2, { color: "yellow", children: "[COMPILING] " }),
110
+ /* @__PURE__ */ jsx2(Text2, { color: "white", children: status })
111
+ ] })
112
+ ] });
113
+ };
114
+
115
+ // src/ui/SummaryTable.tsx
116
+ import * as path2 from "path";
117
+ import { Box as Box3, Text as Text3 } from "ink";
118
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
119
+ function formatBytes(bytes) {
120
+ if (bytes === 0) return "0 B";
121
+ const k = 1024;
122
+ const sizes = ["B", "KB", "MB", "GB"];
123
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
124
+ return `${parseFloat((bytes / Math.pow(k, i)).toFixed(1))} ${sizes[i]}`;
125
+ }
126
+ function formatDisplayPath2(p) {
127
+ if (!p) return "";
128
+ try {
129
+ const rel = path2.relative(process.cwd(), p);
130
+ return rel && !rel.startsWith("..") && rel.length < p.length ? `./${rel}` : p;
131
+ } catch {
132
+ return p;
133
+ }
134
+ }
135
+ var SummaryTable = ({ result }) => {
136
+ if (!result) return null;
137
+ const hasErrors = result.errors.length > 0;
138
+ return /* @__PURE__ */ jsx3(Box3, { flexDirection: "column", marginY: 1, children: /* @__PURE__ */ jsxs3(
139
+ Box3,
140
+ {
141
+ borderStyle: "round",
142
+ borderColor: hasErrors ? "red" : "green",
143
+ paddingX: 2,
144
+ paddingY: 1,
145
+ flexDirection: "column",
146
+ children: [
147
+ /* @__PURE__ */ jsxs3(Box3, { justifyContent: "space-between", alignItems: "center", children: [
148
+ /* @__PURE__ */ jsx3(Box3, { children: /* @__PURE__ */ jsx3(Text3, { bold: true, color: hasErrors ? "red" : "green", children: hasErrors ? "[COMPILATION FAILED]" : "[COMPILATION SUCCESSFUL]" }) }),
149
+ /* @__PURE__ */ jsxs3(Box3, { children: [
150
+ /* @__PURE__ */ jsx3(Text3, { color: "gray", children: "Duration: " }),
151
+ /* @__PURE__ */ jsxs3(Text3, { color: "yellow", bold: true, children: [
152
+ result.durationMs,
153
+ "ms"
154
+ ] })
155
+ ] })
156
+ ] }),
157
+ result.files.length > 0 && /* @__PURE__ */ jsxs3(Box3, { marginTop: 1, flexDirection: "column", children: [
158
+ /* @__PURE__ */ jsxs3(Text3, { bold: true, color: "white", children: [
159
+ "Generated Documents (",
160
+ result.files.length,
161
+ "):"
162
+ ] }),
163
+ result.files.map((f, i) => {
164
+ const formatColor = f.format === "docx" ? "blue" : f.format === "pdf" ? "red" : "yellow";
165
+ const paddedBadge = `[${f.format.toUpperCase()}]`.padEnd(7, " ");
166
+ return /* @__PURE__ */ jsxs3(Box3, { marginTop: 0, flexDirection: "column", marginLeft: 1, children: [
167
+ /* @__PURE__ */ jsxs3(Box3, { justifyContent: "space-between", alignItems: "center", children: [
168
+ /* @__PURE__ */ jsxs3(Box3, { children: [
169
+ /* @__PURE__ */ jsx3(Text3, { color: formatColor, bold: true, children: paddedBadge }),
170
+ /* @__PURE__ */ jsx3(Text3, { color: "white", bold: true, children: f.fileName })
171
+ ] }),
172
+ /* @__PURE__ */ jsx3(Text3, { color: "cyan", bold: true, children: formatBytes(f.sizeBytes) })
173
+ ] }),
174
+ /* @__PURE__ */ jsx3(Box3, { marginLeft: 2, children: /* @__PURE__ */ jsxs3(Text3, { color: "gray", children: [
175
+ "\u2514\u2500 ",
176
+ formatDisplayPath2(f.filePath)
177
+ ] }) })
178
+ ] }, i);
179
+ })
180
+ ] }),
181
+ hasErrors && /* @__PURE__ */ jsxs3(Box3, { marginTop: 1, flexDirection: "column", children: [
182
+ /* @__PURE__ */ jsx3(Text3, { bold: true, color: "red", children: "Errors:" }),
183
+ result.errors.map((err, i) => /* @__PURE__ */ jsx3(Box3, { marginLeft: 2, children: /* @__PURE__ */ jsxs3(Text3, { color: "red", children: [
184
+ "- ",
185
+ err
186
+ ] }) }, i))
187
+ ] }),
188
+ /* @__PURE__ */ jsxs3(Box3, { marginTop: 1, justifyContent: "space-between", alignItems: "center", children: [
189
+ /* @__PURE__ */ jsxs3(Box3, { children: [
190
+ /* @__PURE__ */ jsx3(Text3, { color: "gray", children: "Author: " }),
191
+ /* @__PURE__ */ jsx3(Text3, { color: "cyan", bold: true, children: "Ma'sum" }),
192
+ /* @__PURE__ */ jsx3(Text3, { color: "gray", children: " (@masumrpg)" })
193
+ ] }),
194
+ /* @__PURE__ */ jsxs3(Box3, { children: [
195
+ /* @__PURE__ */ jsx3(Text3, { color: "gray", children: "GitHub: " }),
196
+ /* @__PURE__ */ jsx3(Text3, { color: "blue", underline: true, children: "https://github.com/masumrpg" })
197
+ ] })
198
+ ] })
199
+ ]
200
+ }
201
+ ) });
202
+ };
203
+
204
+ // src/core/engine.ts
205
+ import * as fs from "fs";
206
+ import * as path3 from "path";
207
+ async function compileMarkdown(inputFilePathOrContent, userConfig = {}, onProgress) {
208
+ const startTime = Date.now();
209
+ const config = { ...DEFAULT_CONFIG, ...userConfig };
210
+ let rawMarkdown = "";
211
+ let baseDir = process.cwd();
212
+ let inputFileName = "document.md";
213
+ let isFilePath = false;
214
+ if (fs.existsSync(inputFilePathOrContent)) {
215
+ isFilePath = true;
216
+ rawMarkdown = fs.readFileSync(inputFilePathOrContent, "utf-8");
217
+ baseDir = path3.dirname(path3.resolve(inputFilePathOrContent));
218
+ inputFileName = path3.basename(inputFilePathOrContent);
219
+ } else {
220
+ rawMarkdown = inputFilePathOrContent;
221
+ }
222
+ onProgress == null ? void 0 : onProgress(`Parsing markdown AST: ${inputFileName}...`);
223
+ const parsedDoc = parseMarkdownDocument(rawMarkdown);
224
+ const baseName = inputFileName.replace(/\.(md|mdx|markdown)$/i, "");
225
+ const outputDir = config.outputDir ? path3.isAbsolute(config.outputDir) ? config.outputDir : path3.resolve(process.cwd(), config.outputDir) : baseDir;
226
+ if (!fs.existsSync(outputDir)) {
227
+ fs.mkdirSync(outputDir, { recursive: true });
228
+ }
229
+ const formats = Array.isArray(config.to) ? config.to : [config.to || "docx", "pdf"];
230
+ const generatedFiles = [];
231
+ const errors = [];
232
+ for (const fmt of formats) {
233
+ try {
234
+ if (fmt === "docx") {
235
+ onProgress == null ? void 0 : onProgress(`Generating DOCX document: ${baseName}.docx...`);
236
+ const docxBuffer = await buildDocxDocument(parsedDoc, config, baseDir);
237
+ const docxPath = path3.join(outputDir, `${baseName}.docx`);
238
+ fs.writeFileSync(docxPath, docxBuffer);
239
+ generatedFiles.push({
240
+ format: "docx",
241
+ filePath: docxPath,
242
+ fileName: `${baseName}.docx`,
243
+ sizeBytes: docxBuffer.length
244
+ });
245
+ } else if (fmt === "html") {
246
+ onProgress == null ? void 0 : onProgress(`Generating HTML document: ${baseName}.html...`);
247
+ const htmlString = await buildHtmlDocument(parsedDoc, config, baseDir);
248
+ const htmlPath = path3.join(outputDir, `${baseName}.html`);
249
+ fs.writeFileSync(htmlPath, htmlString, "utf-8");
250
+ generatedFiles.push({
251
+ format: "html",
252
+ filePath: htmlPath,
253
+ fileName: `${baseName}.html`,
254
+ sizeBytes: Buffer.byteLength(htmlString, "utf-8")
255
+ });
256
+ } else if (fmt === "pdf") {
257
+ onProgress == null ? void 0 : onProgress(`Generating PDF document: ${baseName}.pdf...`);
258
+ const pdfBuffer = await buildPdfDocument(parsedDoc, config, baseDir);
259
+ const pdfPath = path3.join(outputDir, `${baseName}.pdf`);
260
+ fs.writeFileSync(pdfPath, pdfBuffer);
261
+ generatedFiles.push({
262
+ format: "pdf",
263
+ filePath: pdfPath,
264
+ fileName: `${baseName}.pdf`,
265
+ sizeBytes: pdfBuffer.length
266
+ });
267
+ }
268
+ } catch (err) {
269
+ const errMsg = err instanceof Error ? err.message : String(err);
270
+ errors.push(`Failed to generate ${fmt}: ${errMsg}`);
271
+ }
272
+ }
273
+ const durationMs = Date.now() - startTime;
274
+ return {
275
+ inputFile: isFilePath ? inputFilePathOrContent : "inline-string",
276
+ durationMs,
277
+ metadata: parsedDoc.metadata,
278
+ files: generatedFiles,
279
+ errors
280
+ };
281
+ }
282
+
283
+ // src/ui/App.tsx
284
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
285
+ var App = ({ inputFile, config, onComplete }) => {
286
+ const [status, setStatus] = useState("Initializing...");
287
+ const [isCompiling, setIsCompiling] = useState(true);
288
+ const [result, setResult] = useState(null);
289
+ useEffect(() => {
290
+ async function run() {
291
+ try {
292
+ const res = await compileMarkdown(inputFile, config, (msg) => setStatus(msg));
293
+ setResult(res);
294
+ setIsCompiling(false);
295
+ onComplete == null ? void 0 : onComplete(res);
296
+ } catch (err) {
297
+ const errMsg = err instanceof Error ? err.message : String(err);
298
+ const errResult = {
299
+ inputFile,
300
+ durationMs: 0,
301
+ metadata: {},
302
+ files: [],
303
+ errors: [errMsg]
304
+ };
305
+ setResult(errResult);
306
+ setIsCompiling(false);
307
+ onComplete == null ? void 0 : onComplete(errResult);
308
+ }
309
+ }
310
+ run();
311
+ }, [inputFile, config, onComplete]);
312
+ const targetFormats = Array.isArray(config.to) ? config.to : config.to ? [String(config.to)] : ["docx", "pdf"];
313
+ return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
314
+ /* @__PURE__ */ jsx4(
315
+ Header,
316
+ {
317
+ inputFile,
318
+ theme: config.theme,
319
+ targetFormats
320
+ }
321
+ ),
322
+ /* @__PURE__ */ jsx4(LiveProgress, { status, isCompiling }),
323
+ /* @__PURE__ */ jsx4(SummaryTable, { result })
324
+ ] });
325
+ };
326
+ export {
327
+ App
328
+ };
@@ -48,6 +48,12 @@ var DEFAULT_CONFIG = {
48
48
  },
49
49
  toc: false,
50
50
  watermark: void 0,
51
+ signatures: void 0,
52
+ coverPage: void 0,
53
+ backCover: void 0,
54
+ numberHeadings: void 0,
55
+ security: void 0,
56
+ math: true,
51
57
  embedImages: true,
52
58
  metadata: void 0,
53
59
  watch: false,