formatferry 1.0.29 → 1.0.31
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 +39 -5
- package/dist/index.js +97 -26
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -44,11 +44,44 @@ already have real text. It is deliberately absent here — the CLI ships no WASM
|
|
|
44
44
|
OCR engine, which keeps the install small and the conversion fully offline.
|
|
45
45
|
|
|
46
46
|
For a PDF that mixes real text pages with scanned inserts, the conversion
|
|
47
|
-
**succeeds**
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
47
|
+
still **succeeds** with exit code 0 — the document has enough text elsewhere
|
|
48
|
+
to avoid the `IMAGE_ONLY` error above — and the scanned pages come through
|
|
49
|
+
empty. That loss is no longer silent: stderr carries one warning naming the
|
|
50
|
+
pages that could not be fully read:
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
Warning: Page(s) 3 could not be fully read: they have little or no extractable text and OCR is not available in this environment, so their content may be missing or incomplete.
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Pipelines that would rather fail than accept a warned conversion can pass
|
|
57
|
+
`--strict`. The complete output is still written (to stdout or the `-o`
|
|
58
|
+
file), and the process then exits 1 after a summary line:
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
$ formatferry -i mixed.pdf --strict; echo "exit=$?"
|
|
62
|
+
Warning: Page(s) 3 could not be fully read: they have little or no extractable text and OCR is not available in this environment, so their content may be missing or incomplete.
|
|
63
|
+
Error: --strict: conversion produced 1 warning(s) [pdf.warnings.ocrUnavailablePages].
|
|
64
|
+
exit=1
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
The summary names the machine-readable warning code(s) in brackets, so
|
|
68
|
+
pipelines can triage without parsing prose.
|
|
69
|
+
|
|
70
|
+
Be aware that **any** warning trips `--strict`, not only page-loss warnings:
|
|
71
|
+
advisory notices count too. For example, the xlsx warning "This spreadsheet
|
|
72
|
+
has many columns" fires on wide-but-ordinary spreadsheets and would exit 1
|
|
73
|
+
under `--strict`. This any-warning meaning of bare `--strict` is the permanent
|
|
74
|
+
contract: if a curated data-loss-only mode is ever added, it will be a
|
|
75
|
+
separate additive spelling (for example `--strict=loss`) — bare `--strict`
|
|
76
|
+
will not change meaning underneath existing pipelines.
|
|
77
|
+
|
|
78
|
+
Under `--batch`, each file's warnings are printed to stderr as
|
|
79
|
+
`Warning (<file>): <message>`, and `--strict` exits 1 after the batch
|
|
80
|
+
completes (all outputs still written) if any file warned.
|
|
81
|
+
|
|
82
|
+
One caveat for MCP users: whether the external `formatferry-mcp` wrapper
|
|
83
|
+
forwards `--strict` to the CLI has not been verified; if it filters unknown
|
|
84
|
+
flags, the flag is inert there.
|
|
52
85
|
|
|
53
86
|
Conversion warnings that *are* produced (for example unusual table structures)
|
|
54
87
|
are written to stderr, never stdout, so `formatferry -i doc.pdf > out.md` keeps
|
|
@@ -72,6 +105,7 @@ formatferry [options]
|
|
|
72
105
|
| `--url <url>` | Fetch and convert a web article (requires API key) |
|
|
73
106
|
| `--batch <glob>` | Batch convert files matching a glob pattern (Premium) |
|
|
74
107
|
| `--output-dir <dir>` | Output directory for batch mode |
|
|
108
|
+
| `--strict` | Exit with code 1 if the conversion produced any warnings (including advisory ones) |
|
|
75
109
|
|
|
76
110
|
#### Input Modes
|
|
77
111
|
|
package/dist/index.js
CHANGED
|
@@ -58500,6 +58500,25 @@ function normalizeAtlassianHTML(html, domParser) {
|
|
|
58500
58500
|
|
|
58501
58501
|
// ../../src/shared/confluence-slack-services.ts
|
|
58502
58502
|
var import_turndown = __toESM(require_turndown_cjs(), 1);
|
|
58503
|
+
function escapeSlackEntities(text) {
|
|
58504
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
58505
|
+
}
|
|
58506
|
+
var ZERO_WIDTH_SPACE = "\u200B";
|
|
58507
|
+
function escapeSlackText(text) {
|
|
58508
|
+
return escapeSlackEntities(text).replace(
|
|
58509
|
+
/(^|[^\p{L}\p{N}])([*_~`])/gu,
|
|
58510
|
+
`$1${ZERO_WIDTH_SPACE}$2`
|
|
58511
|
+
);
|
|
58512
|
+
}
|
|
58513
|
+
function slackLabel(text) {
|
|
58514
|
+
return text.replace(/\|/g, "\uFF5C");
|
|
58515
|
+
}
|
|
58516
|
+
function escapeConfluenceText(text) {
|
|
58517
|
+
return text.replace(/([{}[\]\\])/g, "\\$1").replace(/(^|\n)([*#-])(?=\s)/g, "$1\\$2").replace(/(^|[^\p{L}\p{N}\\])([*_+\-^~])(?=\S)/gu, "$1\\$2").replace(
|
|
58518
|
+
/(?<=\S)([*_+\-^~])(?=$|[^\p{L}\p{N}])/gu,
|
|
58519
|
+
(m, c, offset, str) => str[offset - 1] === "\\" ? m : `\\${c}`
|
|
58520
|
+
);
|
|
58521
|
+
}
|
|
58503
58522
|
function createSlackService(deps) {
|
|
58504
58523
|
const service = new import_turndown.default({
|
|
58505
58524
|
headingStyle: "atx",
|
|
@@ -58509,6 +58528,7 @@ function createSlackService(deps) {
|
|
|
58509
58528
|
fence: "```",
|
|
58510
58529
|
emDelimiter: "_"
|
|
58511
58530
|
});
|
|
58531
|
+
service.escape = escapeSlackText;
|
|
58512
58532
|
deps.addListRules(service);
|
|
58513
58533
|
deps.addPdfPageMarkerRules(service);
|
|
58514
58534
|
service.addRule("slackBold", {
|
|
@@ -58557,12 +58577,33 @@ function createSlackService(deps) {
|
|
|
58557
58577
|
const fontFamily = style.toLowerCase();
|
|
58558
58578
|
return fontFamily.includes("monospace") || fontFamily.includes("courier") || fontFamily.includes("consolas") || fontFamily.includes("monaco") || fontFamily.includes("menlo") || fontFamily.includes("source code") || fontFamily.includes("fira code") || fontFamily.includes("roboto mono") || fontFamily.includes("ubuntu mono");
|
|
58559
58579
|
},
|
|
58560
|
-
replacement: (content) => {
|
|
58561
|
-
const trimmed =
|
|
58580
|
+
replacement: (content, node) => {
|
|
58581
|
+
const trimmed = escapeSlackEntities((node.textContent || "").trim());
|
|
58562
58582
|
if (!trimmed) return content;
|
|
58563
58583
|
return trimmed.includes("`") ? `\`\` ${trimmed} \`\`` : `\`${trimmed}\``;
|
|
58564
58584
|
}
|
|
58565
58585
|
});
|
|
58586
|
+
service.addRule("slackCodeBlock", {
|
|
58587
|
+
filter: (node) => node.nodeName === "PRE",
|
|
58588
|
+
replacement: (_content, node) => {
|
|
58589
|
+
const code = (node.textContent || "").replace(/\n$/, "");
|
|
58590
|
+
return `
|
|
58591
|
+
|
|
58592
|
+
\`\`\`
|
|
58593
|
+
${escapeSlackEntities(code)}
|
|
58594
|
+
\`\`\`
|
|
58595
|
+
|
|
58596
|
+
`;
|
|
58597
|
+
}
|
|
58598
|
+
});
|
|
58599
|
+
service.addRule("slackInlineCode", {
|
|
58600
|
+
filter: (node) => node.nodeName === "CODE" && node.parentNode?.nodeName !== "PRE",
|
|
58601
|
+
replacement: (content, node) => {
|
|
58602
|
+
const code = escapeSlackEntities(node.textContent || "");
|
|
58603
|
+
if (!code) return content;
|
|
58604
|
+
return code.includes("`") ? `\`\` ${code} \`\`` : `\`${code}\``;
|
|
58605
|
+
}
|
|
58606
|
+
});
|
|
58566
58607
|
service.addRule("slackHeading", {
|
|
58567
58608
|
filter: ["h1", "h2", "h3", "h4", "h5", "h6"],
|
|
58568
58609
|
replacement: (content) => content.trim() ? `*${content.trim()}*
|
|
@@ -58599,8 +58640,8 @@ function createSlackService(deps) {
|
|
|
58599
58640
|
service.addRule("slackLink", {
|
|
58600
58641
|
filter: (node) => node.nodeName === "A" && !!node.getAttribute("href"),
|
|
58601
58642
|
replacement: (content, node) => {
|
|
58602
|
-
const href = node.getAttribute("href") || "";
|
|
58603
|
-
const text = content.trim()
|
|
58643
|
+
const href = escapeSlackEntities(node.getAttribute("href") || "");
|
|
58644
|
+
const text = slackLabel(content.trim());
|
|
58604
58645
|
if (!text || text === href) return `<${href}>`;
|
|
58605
58646
|
return `<${href}|${text}>`;
|
|
58606
58647
|
}
|
|
@@ -58608,15 +58649,15 @@ function createSlackService(deps) {
|
|
|
58608
58649
|
service.addRule("slackImage", {
|
|
58609
58650
|
filter: "img",
|
|
58610
58651
|
replacement: (_content, node) => {
|
|
58611
|
-
const src = node.getAttribute("src") || "";
|
|
58612
|
-
const alt = (node.getAttribute("alt") || "image")
|
|
58652
|
+
const src = escapeSlackEntities(node.getAttribute("src") || "");
|
|
58653
|
+
const alt = slackLabel(escapeSlackEntities(node.getAttribute("alt") || "image"));
|
|
58613
58654
|
return `<${src}|${alt}>`;
|
|
58614
58655
|
}
|
|
58615
58656
|
});
|
|
58616
58657
|
service.addRule("slackTable", {
|
|
58617
58658
|
filter: "table",
|
|
58618
58659
|
replacement: (_content, node) => {
|
|
58619
|
-
const plainText = deps.tableToPlainText(node);
|
|
58660
|
+
const plainText = escapeSlackEntities(deps.tableToPlainText(node));
|
|
58620
58661
|
return `\`\`\`
|
|
58621
58662
|
${plainText}
|
|
58622
58663
|
\`\`\`
|
|
@@ -58664,6 +58705,7 @@ function createConfluenceService(deps) {
|
|
|
58664
58705
|
codeBlockStyle: "fenced",
|
|
58665
58706
|
emDelimiter: "_"
|
|
58666
58707
|
});
|
|
58708
|
+
service.escape = escapeConfluenceText;
|
|
58667
58709
|
deps.addPdfPageMarkerRules(service);
|
|
58668
58710
|
service.addRule("confluenceHeading", {
|
|
58669
58711
|
filter: ["h1", "h2", "h3", "h4", "h5", "h6"],
|
|
@@ -58754,7 +58796,10 @@ ${codeContent}
|
|
|
58754
58796
|
const fontFamily = style.toLowerCase();
|
|
58755
58797
|
return fontFamily.includes("monospace") || fontFamily.includes("courier") || fontFamily.includes("consolas") || fontFamily.includes("monaco") || fontFamily.includes("menlo") || fontFamily.includes("source code") || fontFamily.includes("fira code") || fontFamily.includes("roboto mono") || fontFamily.includes("ubuntu mono");
|
|
58756
58798
|
},
|
|
58757
|
-
replacement: (content
|
|
58799
|
+
replacement: (content, node) => {
|
|
58800
|
+
const code = (node.textContent || "").trim();
|
|
58801
|
+
return code ? `{{${code.replace(/}+/g, (m) => m.split("").join(" "))}}}` : content;
|
|
58802
|
+
}
|
|
58758
58803
|
});
|
|
58759
58804
|
service.addRule("confluenceLink", {
|
|
58760
58805
|
filter: (node) => node.nodeName === "A" && !!node.getAttribute("href"),
|
|
@@ -61636,6 +61681,9 @@ function localize2(translate, key, vars) {
|
|
|
61636
61681
|
if (key === "pdf.warnings.ocrPageSkippedTooLarge") {
|
|
61637
61682
|
return `Page(s) ${vars.pages} are too large to OCR and were skipped.`;
|
|
61638
61683
|
}
|
|
61684
|
+
if (key === "pdf.warnings.ocrUnavailablePages") {
|
|
61685
|
+
return `Page(s) ${vars.pages} could not be fully read: they have little or no extractable text and OCR is not available in this environment, so their content may be missing or incomplete.`;
|
|
61686
|
+
}
|
|
61639
61687
|
return key;
|
|
61640
61688
|
}
|
|
61641
61689
|
var PdfAdapter = class {
|
|
@@ -62039,6 +62087,9 @@ var PdfAdapter = class {
|
|
|
62039
62087
|
}
|
|
62040
62088
|
}
|
|
62041
62089
|
}
|
|
62090
|
+
if (routedPages.length > 0 && !fullyScanned && !ocrRan) {
|
|
62091
|
+
pushCodedWarning("pdf.warnings.ocrUnavailablePages", routedPages);
|
|
62092
|
+
}
|
|
62042
62093
|
let documentHasUsableText = false;
|
|
62043
62094
|
if (ocrRan) {
|
|
62044
62095
|
const { isUsableText: isUsableText2 } = await Promise.resolve().then(() => (init_pdf_ocr_adapter(), pdf_ocr_adapter_exports));
|
|
@@ -63941,7 +63992,7 @@ async function binaryToMarkdown(buffer, type, format) {
|
|
|
63941
63992
|
preserveLists: true,
|
|
63942
63993
|
customRules: []
|
|
63943
63994
|
});
|
|
63944
|
-
return { markdown: converted.markdown, warnings: result.warnings };
|
|
63995
|
+
return { markdown: converted.markdown, warnings: result.warnings, warningCodes: result.warningCodes };
|
|
63945
63996
|
}
|
|
63946
63997
|
case "pdf": {
|
|
63947
63998
|
const result = await pdfAdapter.parseToText(toArrayBuffer());
|
|
@@ -63952,7 +64003,7 @@ async function binaryToMarkdown(buffer, type, format) {
|
|
|
63952
64003
|
preserveLists: true,
|
|
63953
64004
|
customRules: []
|
|
63954
64005
|
});
|
|
63955
|
-
return { markdown: converted.markdown, warnings: result.warnings };
|
|
64006
|
+
return { markdown: converted.markdown, warnings: result.warnings, warningCodes: result.warningCodes };
|
|
63956
64007
|
}
|
|
63957
64008
|
case "xlsx":
|
|
63958
64009
|
case "csv": {
|
|
@@ -63964,7 +64015,7 @@ async function binaryToMarkdown(buffer, type, format) {
|
|
|
63964
64015
|
preserveLists: true,
|
|
63965
64016
|
customRules: []
|
|
63966
64017
|
});
|
|
63967
|
-
return { markdown: converted.markdown, warnings: result.warnings };
|
|
64018
|
+
return { markdown: converted.markdown, warnings: result.warnings, warningCodes: result.warningCodes };
|
|
63968
64019
|
}
|
|
63969
64020
|
case "json": {
|
|
63970
64021
|
const result = await dataAdapter.parseJsonToHtml(toArrayBuffer());
|
|
@@ -63975,7 +64026,7 @@ async function binaryToMarkdown(buffer, type, format) {
|
|
|
63975
64026
|
preserveLists: true,
|
|
63976
64027
|
customRules: []
|
|
63977
64028
|
});
|
|
63978
|
-
return { markdown: converted.markdown, warnings: result.warnings };
|
|
64029
|
+
return { markdown: converted.markdown, warnings: result.warnings, warningCodes: result.warningCodes };
|
|
63979
64030
|
}
|
|
63980
64031
|
case "xml": {
|
|
63981
64032
|
const result = await dataAdapter.parseXmlToHtml(toArrayBuffer());
|
|
@@ -63986,7 +64037,7 @@ async function binaryToMarkdown(buffer, type, format) {
|
|
|
63986
64037
|
preserveLists: true,
|
|
63987
64038
|
customRules: []
|
|
63988
64039
|
});
|
|
63989
|
-
return { markdown: converted.markdown, warnings: result.warnings };
|
|
64040
|
+
return { markdown: converted.markdown, warnings: result.warnings, warningCodes: result.warningCodes };
|
|
63990
64041
|
}
|
|
63991
64042
|
case "pptx": {
|
|
63992
64043
|
const result = await pptxAdapter.parseToHtml(toArrayBuffer());
|
|
@@ -63997,7 +64048,7 @@ async function binaryToMarkdown(buffer, type, format) {
|
|
|
63997
64048
|
preserveLists: true,
|
|
63998
64049
|
customRules: []
|
|
63999
64050
|
});
|
|
64000
|
-
return { markdown: converted.markdown, warnings: result.warnings };
|
|
64051
|
+
return { markdown: converted.markdown, warnings: result.warnings, warningCodes: result.warningCodes };
|
|
64001
64052
|
}
|
|
64002
64053
|
}
|
|
64003
64054
|
}
|
|
@@ -64111,6 +64162,7 @@ async function convertCommand(options) {
|
|
|
64111
64162
|
}
|
|
64112
64163
|
let markdown;
|
|
64113
64164
|
let conversionWarnings = [];
|
|
64165
|
+
let conversionWarningCodes = [];
|
|
64114
64166
|
if (options.input) {
|
|
64115
64167
|
let fileBuffer;
|
|
64116
64168
|
try {
|
|
@@ -64139,6 +64191,7 @@ async function convertCommand(options) {
|
|
|
64139
64191
|
const binary = await binaryToMarkdown(fileBuffer, inputType, format);
|
|
64140
64192
|
markdown = binary.markdown;
|
|
64141
64193
|
conversionWarnings = binary.warnings;
|
|
64194
|
+
conversionWarningCodes = binary.warningCodes ?? [];
|
|
64142
64195
|
}
|
|
64143
64196
|
} else {
|
|
64144
64197
|
const html = await readStdin();
|
|
@@ -64170,6 +64223,13 @@ async function convertCommand(options) {
|
|
|
64170
64223
|
} else {
|
|
64171
64224
|
process.stdout.write(markdown);
|
|
64172
64225
|
}
|
|
64226
|
+
if (options.strict && conversionWarnings.length > 0) {
|
|
64227
|
+
const codes = [...new Set(conversionWarningCodes.map((w) => w.code))];
|
|
64228
|
+
const codeList = codes.length > 0 ? ` [${codes.join(", ")}]` : "";
|
|
64229
|
+
console.error(`Error: --strict: conversion produced ${conversionWarnings.length} warning(s)${codeList}.`);
|
|
64230
|
+
process.exitCode = 1;
|
|
64231
|
+
return;
|
|
64232
|
+
}
|
|
64173
64233
|
}
|
|
64174
64234
|
|
|
64175
64235
|
// src/commands/batch.ts
|
|
@@ -64195,38 +64255,38 @@ async function convertFile(filePath, format) {
|
|
|
64195
64255
|
preserveLists: true,
|
|
64196
64256
|
customRules: []
|
|
64197
64257
|
});
|
|
64198
|
-
return result.markdown;
|
|
64258
|
+
return { markdown: result.markdown, warnings: [] };
|
|
64199
64259
|
}
|
|
64200
64260
|
const arrayBuffer = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
|
|
64201
64261
|
if (ext === ".docx") {
|
|
64202
64262
|
const r = await docxAdapter.parseToHtml(arrayBuffer);
|
|
64203
64263
|
const c = await engine.convert(sanitizeHTML(r.html), { format, preserveTables: true, preserveLists: true, customRules: [] });
|
|
64204
|
-
return c.markdown;
|
|
64264
|
+
return { markdown: c.markdown, warnings: r.warnings ?? [] };
|
|
64205
64265
|
}
|
|
64206
64266
|
if (ext === ".pdf") {
|
|
64207
64267
|
const r = await pdfAdapter.parseToText(arrayBuffer);
|
|
64208
64268
|
const c = await engine.convert(sanitizeHTML(r.html), { format, preserveTables: true, preserveLists: true, customRules: [] });
|
|
64209
|
-
return c.markdown;
|
|
64269
|
+
return { markdown: c.markdown, warnings: r.warnings ?? [] };
|
|
64210
64270
|
}
|
|
64211
64271
|
if (ext === ".xlsx" || ext === ".csv") {
|
|
64212
64272
|
const r = await spreadsheetAdapter.parseToHtml(arrayBuffer, ext.slice(1), 0);
|
|
64213
64273
|
const c = await engine.convert(sanitizeHTML(r.html), { format, preserveTables: true, preserveLists: true, customRules: [] });
|
|
64214
|
-
return c.markdown;
|
|
64274
|
+
return { markdown: c.markdown, warnings: r.warnings ?? [] };
|
|
64215
64275
|
}
|
|
64216
64276
|
if (ext === ".json") {
|
|
64217
64277
|
const r = await dataAdapter.parseJsonToHtml(arrayBuffer);
|
|
64218
64278
|
const c = await engine.convert(sanitizeHTML(r.html), { format, preserveTables: true, preserveLists: true, customRules: [] });
|
|
64219
|
-
return c.markdown;
|
|
64279
|
+
return { markdown: c.markdown, warnings: r.warnings ?? [] };
|
|
64220
64280
|
}
|
|
64221
64281
|
if (ext === ".xml") {
|
|
64222
64282
|
const r = await dataAdapter.parseXmlToHtml(arrayBuffer);
|
|
64223
64283
|
const c = await engine.convert(sanitizeHTML(r.html), { format, preserveTables: true, preserveLists: true, customRules: [] });
|
|
64224
|
-
return c.markdown;
|
|
64284
|
+
return { markdown: c.markdown, warnings: r.warnings ?? [] };
|
|
64225
64285
|
}
|
|
64226
64286
|
if (ext === ".pptx") {
|
|
64227
64287
|
const r = await pptxAdapter.parseToHtml(arrayBuffer);
|
|
64228
64288
|
const c = await engine.convert(sanitizeHTML(r.html), { format, preserveTables: true, preserveLists: true, customRules: [] });
|
|
64229
|
-
return c.markdown;
|
|
64289
|
+
return { markdown: c.markdown, warnings: r.warnings ?? [] };
|
|
64230
64290
|
}
|
|
64231
64291
|
throw new Error(`Unsupported file type: ${ext}`);
|
|
64232
64292
|
}
|
|
@@ -64268,11 +64328,17 @@ async function batchCommand(options) {
|
|
|
64268
64328
|
}
|
|
64269
64329
|
const format = options.format ?? "github";
|
|
64270
64330
|
if (options.outputDir) mkdirSync2(options.outputDir, { recursive: true });
|
|
64271
|
-
let successful = 0, failed = 0;
|
|
64331
|
+
let successful = 0, failed = 0, batchWarnings = 0;
|
|
64272
64332
|
let totalOriginal = 0, totalCompressed = 0, toonifiedCount = 0;
|
|
64273
64333
|
for (const filePath of files) {
|
|
64274
64334
|
try {
|
|
64275
|
-
|
|
64335
|
+
const converted = await convertFile(filePath, format);
|
|
64336
|
+
for (const w of converted.warnings) {
|
|
64337
|
+
process.stderr.write(`Warning (${basename(filePath)}): ${w}
|
|
64338
|
+
`);
|
|
64339
|
+
}
|
|
64340
|
+
batchWarnings += converted.warnings.length;
|
|
64341
|
+
let markdown = converted.markdown;
|
|
64276
64342
|
if (options.toonify) {
|
|
64277
64343
|
if (markdown.trim()) {
|
|
64278
64344
|
if (Buffer.byteLength(markdown, "utf8") > TOONIFY_MAX_INPUT_BYTES) {
|
|
@@ -64311,6 +64377,11 @@ async function batchCommand(options) {
|
|
|
64311
64377
|
process.stderr.write(`
|
|
64312
64378
|
Batch complete: ${successful} succeeded, ${failed} failed, ${files.length} total
|
|
64313
64379
|
`);
|
|
64380
|
+
if (options.strict && batchWarnings > 0) {
|
|
64381
|
+
process.stderr.write(`Error: --strict: conversion produced ${batchWarnings} warning(s) across the batch.
|
|
64382
|
+
`);
|
|
64383
|
+
process.exitCode = 1;
|
|
64384
|
+
}
|
|
64314
64385
|
}
|
|
64315
64386
|
|
|
64316
64387
|
// src/commands/auth.ts
|
|
@@ -64518,8 +64589,8 @@ import { readFileSync as readFileSync5 } from "fs";
|
|
|
64518
64589
|
import { resolve, dirname as dirname3 } from "path";
|
|
64519
64590
|
import { fileURLToPath } from "url";
|
|
64520
64591
|
var getVersion = () => {
|
|
64521
|
-
if ("1.0.
|
|
64522
|
-
return "1.0.
|
|
64592
|
+
if ("1.0.31") {
|
|
64593
|
+
return "1.0.31";
|
|
64523
64594
|
}
|
|
64524
64595
|
try {
|
|
64525
64596
|
const __dirname2 = dirname3(fileURLToPath(import.meta.url));
|
|
@@ -64532,7 +64603,7 @@ var getVersion = () => {
|
|
|
64532
64603
|
}
|
|
64533
64604
|
};
|
|
64534
64605
|
var VERSION = getVersion();
|
|
64535
|
-
program.name("formatferry").version(VERSION).description("Convert HTML, DOCX, PDF, XLSX, CSV, JSON, XML, PPTX to Markdown").option("-i, --input <path>", "Input file path").option("-o, --output <path>", "Output file path").option("-f, --format <format>", "Markdown format (github, commonmark, slack, discord, reddit, confluence, gdocs, rmarkdown)", "github").option("--url <url>", "Fetch article from URL").option("--batch <glob>", "Batch convert files (Premium)").option("--output-dir <dir>", "Output directory for batch").option("--toonify", "Compress output to TOON format (Premium)").option("--offline", "Skip entitlement check (use cached or free tier)").option("--timeout <ms>", "License server timeout in milliseconds (default: 2000)", "2000").action(async (options) => {
|
|
64606
|
+
program.name("formatferry").version(VERSION).description("Convert HTML, DOCX, PDF, XLSX, CSV, JSON, XML, PPTX to Markdown").option("-i, --input <path>", "Input file path").option("-o, --output <path>", "Output file path").option("-f, --format <format>", "Markdown format (github, commonmark, slack, discord, reddit, confluence, gdocs, rmarkdown)", "github").option("--url <url>", "Fetch article from URL").option("--batch <glob>", "Batch convert files (Premium)").option("--output-dir <dir>", "Output directory for batch").option("--toonify", "Compress output to TOON format (Premium)").option("--offline", "Skip entitlement check (use cached or free tier)").option("--timeout <ms>", "License server timeout in milliseconds (default: 2000)", "2000").option("--strict", "Exit with code 1 if the conversion produced any warnings (including advisory ones)").action(async (options) => {
|
|
64536
64607
|
if (options.batch) {
|
|
64537
64608
|
await batchCommand(options);
|
|
64538
64609
|
} else {
|