@masumdev/markforge 0.2.3 → 0.2.4
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 +103 -13
- package/dist/{App-DKCNXX3Z.mjs → App-IJVCGBJM.mjs} +1 -1
- package/dist/chunk-IYGPJIK5.mjs +165 -0
- package/dist/cli.mjs +9 -6
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +74 -29
- package/dist/index.mjs +74 -29
- package/dist/{loadConfig-T4ZV6YY2.mjs → loadConfig-PD6ENMAM.mjs} +1 -1
- package/package.json +1 -1
- package/schema.json +44 -13
- package/dist/chunk-ASXSHC6H.mjs +0 -120
package/README.md
CHANGED
|
@@ -165,43 +165,133 @@ footer:
|
|
|
165
165
|
|
|
166
166
|
---
|
|
167
167
|
|
|
168
|
-
## ⚙️ Configuration
|
|
168
|
+
## ⚙️ Configuration Reference
|
|
169
|
+
|
|
170
|
+
MarkForge automatically discovers configuration files starting from the input file's directory up to the workspace root, or via the `-c, --config <path>` CLI flag.
|
|
171
|
+
|
|
172
|
+
### 1. TypeScript (`markforge.config.ts`)
|
|
169
173
|
|
|
170
174
|
```typescript
|
|
171
175
|
import { defineConfig } from "@masumdev/markforge";
|
|
172
176
|
|
|
173
177
|
export default defineConfig({
|
|
178
|
+
// Output formats: "docx" | "pdf" | "html"
|
|
174
179
|
to: ["docx", "pdf", "html"],
|
|
175
180
|
outputDir: "./dist/documents",
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
181
|
+
|
|
182
|
+
// Theme: "default" | "academic" | "github" | "corporate" | "minimal" | "dracula"
|
|
183
|
+
theme: "academic",
|
|
184
|
+
|
|
185
|
+
// Custom CSS stylesheet(s) to inject
|
|
186
|
+
css: ["./styles/custom.css"],
|
|
187
|
+
|
|
188
|
+
// Page layout & dimensions
|
|
189
|
+
orientation: "portrait", // "portrait" | "landscape"
|
|
190
|
+
paperSize: "A4", // "A4" | "Letter" | "Legal" | "A3" | "A5"
|
|
179
191
|
margins: {
|
|
180
192
|
top: "2.5cm",
|
|
181
193
|
bottom: "2.5cm",
|
|
182
194
|
left: "3cm",
|
|
183
195
|
right: "3cm",
|
|
184
196
|
},
|
|
197
|
+
|
|
198
|
+
// Multi-zone header & footer (supports tokens: {title}, {author}, {version}, {date}, {page}, {pages})
|
|
185
199
|
header: {
|
|
186
|
-
left: "
|
|
200
|
+
left: "Enterprise Architecture",
|
|
187
201
|
center: "{title}",
|
|
188
202
|
right: "v{version}",
|
|
189
203
|
},
|
|
190
204
|
footer: {
|
|
191
|
-
left: "Confidential",
|
|
205
|
+
left: "Confidential — Internal Use Only",
|
|
192
206
|
right: "Page {page} of {pages}",
|
|
193
207
|
},
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
208
|
+
|
|
209
|
+
// Document features
|
|
210
|
+
toc: true, // Auto-generate Table of Contents
|
|
211
|
+
embedImages: true, // Embed/inline all images as Base64 data URIs
|
|
212
|
+
bundleHtml: true, // Self-contained HTML with embedded styles and scripts
|
|
213
|
+
syntaxTheme: "github-dark", // "github-dark" | "github-light" | "dracula" | "monokai" | "nord"
|
|
214
|
+
|
|
215
|
+
// Diagonal page watermark
|
|
216
|
+
watermark: "CONFIDENTIAL",
|
|
217
|
+
|
|
218
|
+
// Default document metadata
|
|
219
|
+
metadata: {
|
|
220
|
+
title: "System Architecture Specification",
|
|
221
|
+
author: "Ma'sum",
|
|
222
|
+
version: "1.0.0",
|
|
223
|
+
company: "My Organization",
|
|
201
224
|
},
|
|
225
|
+
|
|
226
|
+
// Server & watch options
|
|
227
|
+
watch: false,
|
|
228
|
+
serve: false,
|
|
229
|
+
port: 4000,
|
|
230
|
+
open: false,
|
|
202
231
|
});
|
|
203
232
|
```
|
|
204
233
|
|
|
234
|
+
### 2. JSON with `$schema` (`markforge.config.json`)
|
|
235
|
+
|
|
236
|
+
Adding `$schema` enables **instant autocompletion and validation** in VS Code, WebStorm, and other IDEs:
|
|
237
|
+
|
|
238
|
+
```json
|
|
239
|
+
{
|
|
240
|
+
"$schema": "https://raw.githubusercontent.com/masumdev/react-native-library/main/packages/markforge/schema.json",
|
|
241
|
+
"to": ["docx", "pdf", "html"],
|
|
242
|
+
"outputDir": "./dist/documents",
|
|
243
|
+
"theme": "academic",
|
|
244
|
+
"orientation": "portrait",
|
|
245
|
+
"paperSize": "A4",
|
|
246
|
+
"margins": {
|
|
247
|
+
"top": "2.5cm",
|
|
248
|
+
"bottom": "2.5cm",
|
|
249
|
+
"left": "3cm",
|
|
250
|
+
"right": "3cm"
|
|
251
|
+
},
|
|
252
|
+
"header": {
|
|
253
|
+
"left": "Enterprise Architecture",
|
|
254
|
+
"right": "{title}"
|
|
255
|
+
},
|
|
256
|
+
"footer": {
|
|
257
|
+
"left": "Confidential",
|
|
258
|
+
"right": "Page {page} of {pages}"
|
|
259
|
+
},
|
|
260
|
+
"toc": true,
|
|
261
|
+
"embedImages": true,
|
|
262
|
+
"syntaxTheme": "github-dark",
|
|
263
|
+
"watermark": "CONFIDENTIAL"
|
|
264
|
+
}
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### 3. YAML (`markforge.config.yaml` / `.markforgerc.yaml`)
|
|
268
|
+
|
|
269
|
+
```yaml
|
|
270
|
+
to:
|
|
271
|
+
- docx
|
|
272
|
+
- pdf
|
|
273
|
+
- html
|
|
274
|
+
outputDir: ./dist/documents
|
|
275
|
+
theme: academic
|
|
276
|
+
orientation: portrait
|
|
277
|
+
paperSize: A4
|
|
278
|
+
margins:
|
|
279
|
+
top: 2.5cm
|
|
280
|
+
bottom: 2.5cm
|
|
281
|
+
left: 3cm
|
|
282
|
+
right: 3cm
|
|
283
|
+
header:
|
|
284
|
+
left: Enterprise Architecture
|
|
285
|
+
right: "{title}"
|
|
286
|
+
footer:
|
|
287
|
+
left: Confidential
|
|
288
|
+
right: "Page {page} of {pages}"
|
|
289
|
+
toc: true
|
|
290
|
+
embedImages: true
|
|
291
|
+
watermark: CONFIDENTIAL
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
|
|
205
295
|
---
|
|
206
296
|
|
|
207
297
|
## 💻 Programmatic API
|
|
@@ -0,0 +1,165 @@
|
|
|
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
|
+
__require
|
|
12
|
+
} from "./chunk-YZHGBG4N.mjs";
|
|
13
|
+
|
|
14
|
+
// src/config/loadConfig.ts
|
|
15
|
+
import * as fs from "fs";
|
|
16
|
+
import * as path from "path";
|
|
17
|
+
import { pathToFileURL } from "url";
|
|
18
|
+
import * as YAML from "yaml";
|
|
19
|
+
var DEFAULT_CONFIG_FILENAMES = [
|
|
20
|
+
"markforge.config.json",
|
|
21
|
+
".markforgerc.json",
|
|
22
|
+
"markforge.config.yaml",
|
|
23
|
+
"markforge.config.yml",
|
|
24
|
+
".markforgerc.yaml",
|
|
25
|
+
".markforgerc.yml",
|
|
26
|
+
".markforgerc",
|
|
27
|
+
"markforge.config.ts",
|
|
28
|
+
"markforge.config.js",
|
|
29
|
+
"markforge.config.mjs",
|
|
30
|
+
"markforge.config.cjs"
|
|
31
|
+
];
|
|
32
|
+
var DEFAULT_CONFIG = {
|
|
33
|
+
to: ["docx", "pdf"],
|
|
34
|
+
outputDir: void 0,
|
|
35
|
+
theme: "default",
|
|
36
|
+
css: void 0,
|
|
37
|
+
orientation: "portrait",
|
|
38
|
+
paperSize: "A4",
|
|
39
|
+
margins: {
|
|
40
|
+
top: "2.5cm",
|
|
41
|
+
bottom: "2.5cm",
|
|
42
|
+
left: "2.5cm",
|
|
43
|
+
right: "2.5cm"
|
|
44
|
+
},
|
|
45
|
+
header: void 0,
|
|
46
|
+
footer: {
|
|
47
|
+
right: "Page {page} of {pages}"
|
|
48
|
+
},
|
|
49
|
+
toc: false,
|
|
50
|
+
watermark: void 0,
|
|
51
|
+
embedImages: true,
|
|
52
|
+
metadata: void 0,
|
|
53
|
+
watch: false,
|
|
54
|
+
serve: false,
|
|
55
|
+
port: 4e3,
|
|
56
|
+
open: false,
|
|
57
|
+
bundleHtml: true,
|
|
58
|
+
syntaxTheme: "github-dark"
|
|
59
|
+
};
|
|
60
|
+
function discoverConfigFile(startDir) {
|
|
61
|
+
const dirsToCheck = [];
|
|
62
|
+
let curr = path.resolve(startDir);
|
|
63
|
+
while (curr) {
|
|
64
|
+
dirsToCheck.push(curr);
|
|
65
|
+
const parent = path.dirname(curr);
|
|
66
|
+
if (parent === curr) break;
|
|
67
|
+
curr = parent;
|
|
68
|
+
}
|
|
69
|
+
const cwd = path.resolve(process.cwd());
|
|
70
|
+
if (!dirsToCheck.includes(cwd)) {
|
|
71
|
+
dirsToCheck.push(cwd);
|
|
72
|
+
}
|
|
73
|
+
for (const dir of dirsToCheck) {
|
|
74
|
+
for (const filename of DEFAULT_CONFIG_FILENAMES) {
|
|
75
|
+
const candidate = path.join(dir, filename);
|
|
76
|
+
if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {
|
|
77
|
+
return candidate;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
async function loadConfig(customPath, startDir = process.cwd()) {
|
|
84
|
+
let resolvedPath = null;
|
|
85
|
+
if (customPath) {
|
|
86
|
+
resolvedPath = path.isAbsolute(customPath) ? customPath : path.resolve(process.cwd(), customPath);
|
|
87
|
+
if (!fs.existsSync(resolvedPath)) {
|
|
88
|
+
const altPath = path.resolve(startDir, customPath);
|
|
89
|
+
if (fs.existsSync(altPath)) {
|
|
90
|
+
resolvedPath = altPath;
|
|
91
|
+
} else {
|
|
92
|
+
throw new Error(`Configuration file not found: ${resolvedPath}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
} else {
|
|
96
|
+
resolvedPath = discoverConfigFile(startDir);
|
|
97
|
+
}
|
|
98
|
+
if (!resolvedPath) {
|
|
99
|
+
return {
|
|
100
|
+
config: { ...DEFAULT_CONFIG },
|
|
101
|
+
configPath: null
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
const ext = path.extname(resolvedPath).toLowerCase();
|
|
105
|
+
let userConfig = {};
|
|
106
|
+
try {
|
|
107
|
+
if (ext === ".json" || ext === "" || resolvedPath.endsWith(".markforgerc")) {
|
|
108
|
+
const raw = fs.readFileSync(resolvedPath, "utf-8").trim();
|
|
109
|
+
try {
|
|
110
|
+
userConfig = JSON.parse(raw);
|
|
111
|
+
} catch {
|
|
112
|
+
userConfig = YAML.parse(raw) || {};
|
|
113
|
+
}
|
|
114
|
+
} else if (ext === ".yaml" || ext === ".yml") {
|
|
115
|
+
const raw = fs.readFileSync(resolvedPath, "utf-8");
|
|
116
|
+
userConfig = YAML.parse(raw) || {};
|
|
117
|
+
} else if (ext === ".ts" || ext === ".js" || ext === ".mjs" || ext === ".cjs") {
|
|
118
|
+
try {
|
|
119
|
+
const fileUrl = `${pathToFileURL(resolvedPath).href}?t=${Date.now()}`;
|
|
120
|
+
const mod = await import(fileUrl);
|
|
121
|
+
const rawExport = mod.default ?? mod.config ?? mod;
|
|
122
|
+
userConfig = (typeof rawExport === "function" ? await rawExport() : rawExport) || {};
|
|
123
|
+
} catch (importErr) {
|
|
124
|
+
try {
|
|
125
|
+
const mod = __require(resolvedPath);
|
|
126
|
+
const rawExport = mod.default ?? mod.config ?? mod;
|
|
127
|
+
userConfig = (typeof rawExport === "function" ? await rawExport() : rawExport) || {};
|
|
128
|
+
} catch {
|
|
129
|
+
throw importErr;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
} catch (err) {
|
|
134
|
+
throw new Error(
|
|
135
|
+
`Failed to parse configuration file at ${resolvedPath}: ${err instanceof Error ? err.message : String(err)}`
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
if (userConfig && typeof userConfig === "object" && "$schema" in userConfig) {
|
|
139
|
+
delete userConfig.$schema;
|
|
140
|
+
}
|
|
141
|
+
const parsedConfig = userConfig;
|
|
142
|
+
const mergedConfig = {
|
|
143
|
+
...DEFAULT_CONFIG,
|
|
144
|
+
...parsedConfig,
|
|
145
|
+
margins: {
|
|
146
|
+
...DEFAULT_CONFIG.margins,
|
|
147
|
+
...parsedConfig.margins || {}
|
|
148
|
+
},
|
|
149
|
+
header: parsedConfig.header !== void 0 ? parsedConfig.header : DEFAULT_CONFIG.header,
|
|
150
|
+
footer: parsedConfig.footer !== void 0 ? parsedConfig.footer : DEFAULT_CONFIG.footer,
|
|
151
|
+
metadata: {
|
|
152
|
+
...DEFAULT_CONFIG.metadata || {},
|
|
153
|
+
...parsedConfig.metadata || {}
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
return {
|
|
157
|
+
config: mergedConfig,
|
|
158
|
+
configPath: resolvedPath
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export {
|
|
163
|
+
DEFAULT_CONFIG,
|
|
164
|
+
loadConfig
|
|
165
|
+
};
|
package/dist/cli.mjs
CHANGED
|
@@ -45,7 +45,7 @@ process.on("warning", (w) => {
|
|
|
45
45
|
});
|
|
46
46
|
async function main() {
|
|
47
47
|
const program = new Command();
|
|
48
|
-
program.name("markforge").description("Modern Markdown & MDX multi-format publishing engine & CLI (DOCX, PDF, HTML, Images)").version(MARKFORGE_VERSION, "-V, --version", "Output current markforge version").argument("[input-file]", "Markdown or MDX source file to compile", "README.md").option("-t, --to <formats>", "Target formats to compile to (comma-separated: docx,pdf,html)"
|
|
48
|
+
program.name("markforge").description("Modern Markdown & MDX multi-format publishing engine & CLI (DOCX, PDF, HTML, Images)").version(MARKFORGE_VERSION, "-V, --version", "Output current markforge version").argument("[input-file]", "Markdown or MDX source file to compile", "README.md").option("-t, --to <formats>", "Target formats to compile to (comma-separated: docx,pdf,html)").option("-o, --output <dir>", "Output directory for generated documents").option("--theme <theme>", "Built-in theme name (default, academic, github, corporate, minimal)").option("--css <path...>", "Custom CSS stylesheet(s) to inject").option("-c, --config <path>", "Path to custom markforge configuration file").option("--toc", "Force Table of Contents generation").option("-w, --watch", "Watch input file for changes and recompile", false).option("-s, --serve [port]", "Start local HTTP preview server", false).option("-O, --open", "Open compiled document in default browser", false);
|
|
49
49
|
program.parse(process.argv);
|
|
50
50
|
const options = program.opts();
|
|
51
51
|
const inputArg = program.args[0] || "README.md";
|
|
@@ -54,16 +54,19 @@ async function main() {
|
|
|
54
54
|
console.error(`[ERROR] File not found: ${resolvedInputPath}`);
|
|
55
55
|
process.exit(1);
|
|
56
56
|
}
|
|
57
|
-
const
|
|
57
|
+
const cliFormats = options.to ? options.to.split(",").map((f) => f.trim().toLowerCase()).filter(Boolean) : void 0;
|
|
58
58
|
const [{ render }, { App }, { loadConfig }] = await Promise.all([
|
|
59
59
|
import("ink"),
|
|
60
|
-
import("./App-
|
|
61
|
-
import("./loadConfig-
|
|
60
|
+
import("./App-IJVCGBJM.mjs"),
|
|
61
|
+
import("./loadConfig-PD6ENMAM.mjs")
|
|
62
62
|
]);
|
|
63
|
-
const fileConfig = await loadConfig(
|
|
63
|
+
const { config: fileConfig } = await loadConfig(
|
|
64
|
+
options.config,
|
|
65
|
+
path.dirname(resolvedInputPath)
|
|
66
|
+
);
|
|
64
67
|
const mergedConfig = {
|
|
65
68
|
...fileConfig,
|
|
66
|
-
to:
|
|
69
|
+
to: cliFormats && cliFormats.length > 0 ? cliFormats : fileConfig.to ?? ["docx", "pdf"],
|
|
67
70
|
...options.output ? { outputDir: options.output } : {},
|
|
68
71
|
...options.theme ? { theme: options.theme } : {},
|
|
69
72
|
...options.css ? { css: options.css } : {},
|
package/dist/index.d.mts
CHANGED
|
@@ -333,7 +333,7 @@ declare const DEFAULT_CONFIG: Required<Omit<MarkforgeConfig, "outputDir" | "css"
|
|
|
333
333
|
/**
|
|
334
334
|
* Resolves and loads markforge configuration from disk.
|
|
335
335
|
*/
|
|
336
|
-
declare function loadConfig(customPath?: string,
|
|
336
|
+
declare function loadConfig(customPath?: string, startDir?: string): Promise<{
|
|
337
337
|
config: MarkforgeConfig;
|
|
338
338
|
configPath: string | null;
|
|
339
339
|
}>;
|
package/dist/index.d.ts
CHANGED
|
@@ -333,7 +333,7 @@ declare const DEFAULT_CONFIG: Required<Omit<MarkforgeConfig, "outputDir" | "css"
|
|
|
333
333
|
/**
|
|
334
334
|
* Resolves and loads markforge configuration from disk.
|
|
335
335
|
*/
|
|
336
|
-
declare function loadConfig(customPath?: string,
|
|
336
|
+
declare function loadConfig(customPath?: string, startDir?: string): Promise<{
|
|
337
337
|
config: MarkforgeConfig;
|
|
338
338
|
configPath: string | null;
|
|
339
339
|
}>;
|
package/dist/index.js
CHANGED
|
@@ -2302,21 +2302,43 @@ var DEFAULT_CONFIG = {
|
|
|
2302
2302
|
bundleHtml: true,
|
|
2303
2303
|
syntaxTheme: "github-dark"
|
|
2304
2304
|
};
|
|
2305
|
-
|
|
2305
|
+
function discoverConfigFile(startDir) {
|
|
2306
|
+
const dirsToCheck = [];
|
|
2307
|
+
let curr = path5.resolve(startDir);
|
|
2308
|
+
while (curr) {
|
|
2309
|
+
dirsToCheck.push(curr);
|
|
2310
|
+
const parent = path5.dirname(curr);
|
|
2311
|
+
if (parent === curr) break;
|
|
2312
|
+
curr = parent;
|
|
2313
|
+
}
|
|
2314
|
+
const cwd = path5.resolve(process.cwd());
|
|
2315
|
+
if (!dirsToCheck.includes(cwd)) {
|
|
2316
|
+
dirsToCheck.push(cwd);
|
|
2317
|
+
}
|
|
2318
|
+
for (const dir of dirsToCheck) {
|
|
2319
|
+
for (const filename of DEFAULT_CONFIG_FILENAMES) {
|
|
2320
|
+
const candidate = path5.join(dir, filename);
|
|
2321
|
+
if (fs5.existsSync(candidate) && fs5.statSync(candidate).isFile()) {
|
|
2322
|
+
return candidate;
|
|
2323
|
+
}
|
|
2324
|
+
}
|
|
2325
|
+
}
|
|
2326
|
+
return null;
|
|
2327
|
+
}
|
|
2328
|
+
async function loadConfig(customPath, startDir = process.cwd()) {
|
|
2306
2329
|
let resolvedPath = null;
|
|
2307
2330
|
if (customPath) {
|
|
2308
|
-
resolvedPath = path5.isAbsolute(customPath) ? customPath : path5.resolve(cwd, customPath);
|
|
2331
|
+
resolvedPath = path5.isAbsolute(customPath) ? customPath : path5.resolve(process.cwd(), customPath);
|
|
2309
2332
|
if (!fs5.existsSync(resolvedPath)) {
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
if (fs5.existsSync(candidate)) {
|
|
2316
|
-
resolvedPath = candidate;
|
|
2317
|
-
break;
|
|
2333
|
+
const altPath = path5.resolve(startDir, customPath);
|
|
2334
|
+
if (fs5.existsSync(altPath)) {
|
|
2335
|
+
resolvedPath = altPath;
|
|
2336
|
+
} else {
|
|
2337
|
+
throw new Error(`Configuration file not found: ${resolvedPath}`);
|
|
2318
2338
|
}
|
|
2319
2339
|
}
|
|
2340
|
+
} else {
|
|
2341
|
+
resolvedPath = discoverConfigFile(startDir);
|
|
2320
2342
|
}
|
|
2321
2343
|
if (!resolvedPath) {
|
|
2322
2344
|
return {
|
|
@@ -2326,32 +2348,55 @@ async function loadConfig(customPath, cwd = process.cwd()) {
|
|
|
2326
2348
|
}
|
|
2327
2349
|
const ext = path5.extname(resolvedPath).toLowerCase();
|
|
2328
2350
|
let userConfig = {};
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
const
|
|
2339
|
-
userConfig =
|
|
2340
|
-
}
|
|
2341
|
-
|
|
2342
|
-
|
|
2351
|
+
try {
|
|
2352
|
+
if (ext === ".json" || ext === "" || resolvedPath.endsWith(".markforgerc")) {
|
|
2353
|
+
const raw = fs5.readFileSync(resolvedPath, "utf-8").trim();
|
|
2354
|
+
try {
|
|
2355
|
+
userConfig = JSON.parse(raw);
|
|
2356
|
+
} catch {
|
|
2357
|
+
userConfig = YAML.parse(raw) || {};
|
|
2358
|
+
}
|
|
2359
|
+
} else if (ext === ".yaml" || ext === ".yml") {
|
|
2360
|
+
const raw = fs5.readFileSync(resolvedPath, "utf-8");
|
|
2361
|
+
userConfig = YAML.parse(raw) || {};
|
|
2362
|
+
} else if (ext === ".ts" || ext === ".js" || ext === ".mjs" || ext === ".cjs") {
|
|
2363
|
+
try {
|
|
2364
|
+
const fileUrl = `${(0, import_node_url3.pathToFileURL)(resolvedPath).href}?t=${Date.now()}`;
|
|
2365
|
+
const mod = await import(fileUrl);
|
|
2366
|
+
const rawExport = mod.default ?? mod.config ?? mod;
|
|
2367
|
+
userConfig = (typeof rawExport === "function" ? await rawExport() : rawExport) || {};
|
|
2368
|
+
} catch (importErr) {
|
|
2369
|
+
try {
|
|
2370
|
+
const mod = require(resolvedPath);
|
|
2371
|
+
const rawExport = mod.default ?? mod.config ?? mod;
|
|
2372
|
+
userConfig = (typeof rawExport === "function" ? await rawExport() : rawExport) || {};
|
|
2373
|
+
} catch {
|
|
2374
|
+
throw importErr;
|
|
2375
|
+
}
|
|
2376
|
+
}
|
|
2343
2377
|
}
|
|
2378
|
+
} catch (err) {
|
|
2379
|
+
throw new Error(
|
|
2380
|
+
`Failed to parse configuration file at ${resolvedPath}: ${err instanceof Error ? err.message : String(err)}`
|
|
2381
|
+
);
|
|
2382
|
+
}
|
|
2383
|
+
if (userConfig && typeof userConfig === "object" && "$schema" in userConfig) {
|
|
2384
|
+
delete userConfig.$schema;
|
|
2344
2385
|
}
|
|
2386
|
+
const parsedConfig = userConfig;
|
|
2345
2387
|
const mergedConfig = {
|
|
2346
2388
|
...DEFAULT_CONFIG,
|
|
2347
|
-
...
|
|
2389
|
+
...parsedConfig,
|
|
2348
2390
|
margins: {
|
|
2349
2391
|
...DEFAULT_CONFIG.margins,
|
|
2350
|
-
...
|
|
2392
|
+
...parsedConfig.margins || {}
|
|
2351
2393
|
},
|
|
2352
|
-
header:
|
|
2353
|
-
footer:
|
|
2354
|
-
metadata:
|
|
2394
|
+
header: parsedConfig.header !== void 0 ? parsedConfig.header : DEFAULT_CONFIG.header,
|
|
2395
|
+
footer: parsedConfig.footer !== void 0 ? parsedConfig.footer : DEFAULT_CONFIG.footer,
|
|
2396
|
+
metadata: {
|
|
2397
|
+
...DEFAULT_CONFIG.metadata || {},
|
|
2398
|
+
...parsedConfig.metadata || {}
|
|
2399
|
+
}
|
|
2355
2400
|
};
|
|
2356
2401
|
return {
|
|
2357
2402
|
config: mergedConfig,
|
package/dist/index.mjs
CHANGED
|
@@ -2267,21 +2267,43 @@ var DEFAULT_CONFIG = {
|
|
|
2267
2267
|
bundleHtml: true,
|
|
2268
2268
|
syntaxTheme: "github-dark"
|
|
2269
2269
|
};
|
|
2270
|
-
|
|
2270
|
+
function discoverConfigFile(startDir) {
|
|
2271
|
+
const dirsToCheck = [];
|
|
2272
|
+
let curr = path5.resolve(startDir);
|
|
2273
|
+
while (curr) {
|
|
2274
|
+
dirsToCheck.push(curr);
|
|
2275
|
+
const parent = path5.dirname(curr);
|
|
2276
|
+
if (parent === curr) break;
|
|
2277
|
+
curr = parent;
|
|
2278
|
+
}
|
|
2279
|
+
const cwd = path5.resolve(process.cwd());
|
|
2280
|
+
if (!dirsToCheck.includes(cwd)) {
|
|
2281
|
+
dirsToCheck.push(cwd);
|
|
2282
|
+
}
|
|
2283
|
+
for (const dir of dirsToCheck) {
|
|
2284
|
+
for (const filename of DEFAULT_CONFIG_FILENAMES) {
|
|
2285
|
+
const candidate = path5.join(dir, filename);
|
|
2286
|
+
if (fs5.existsSync(candidate) && fs5.statSync(candidate).isFile()) {
|
|
2287
|
+
return candidate;
|
|
2288
|
+
}
|
|
2289
|
+
}
|
|
2290
|
+
}
|
|
2291
|
+
return null;
|
|
2292
|
+
}
|
|
2293
|
+
async function loadConfig(customPath, startDir = process.cwd()) {
|
|
2271
2294
|
let resolvedPath = null;
|
|
2272
2295
|
if (customPath) {
|
|
2273
|
-
resolvedPath = path5.isAbsolute(customPath) ? customPath : path5.resolve(cwd, customPath);
|
|
2296
|
+
resolvedPath = path5.isAbsolute(customPath) ? customPath : path5.resolve(process.cwd(), customPath);
|
|
2274
2297
|
if (!fs5.existsSync(resolvedPath)) {
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
if (fs5.existsSync(candidate)) {
|
|
2281
|
-
resolvedPath = candidate;
|
|
2282
|
-
break;
|
|
2298
|
+
const altPath = path5.resolve(startDir, customPath);
|
|
2299
|
+
if (fs5.existsSync(altPath)) {
|
|
2300
|
+
resolvedPath = altPath;
|
|
2301
|
+
} else {
|
|
2302
|
+
throw new Error(`Configuration file not found: ${resolvedPath}`);
|
|
2283
2303
|
}
|
|
2284
2304
|
}
|
|
2305
|
+
} else {
|
|
2306
|
+
resolvedPath = discoverConfigFile(startDir);
|
|
2285
2307
|
}
|
|
2286
2308
|
if (!resolvedPath) {
|
|
2287
2309
|
return {
|
|
@@ -2291,32 +2313,55 @@ async function loadConfig(customPath, cwd = process.cwd()) {
|
|
|
2291
2313
|
}
|
|
2292
2314
|
const ext = path5.extname(resolvedPath).toLowerCase();
|
|
2293
2315
|
let userConfig = {};
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
const
|
|
2304
|
-
userConfig =
|
|
2305
|
-
}
|
|
2306
|
-
|
|
2307
|
-
|
|
2316
|
+
try {
|
|
2317
|
+
if (ext === ".json" || ext === "" || resolvedPath.endsWith(".markforgerc")) {
|
|
2318
|
+
const raw = fs5.readFileSync(resolvedPath, "utf-8").trim();
|
|
2319
|
+
try {
|
|
2320
|
+
userConfig = JSON.parse(raw);
|
|
2321
|
+
} catch {
|
|
2322
|
+
userConfig = YAML.parse(raw) || {};
|
|
2323
|
+
}
|
|
2324
|
+
} else if (ext === ".yaml" || ext === ".yml") {
|
|
2325
|
+
const raw = fs5.readFileSync(resolvedPath, "utf-8");
|
|
2326
|
+
userConfig = YAML.parse(raw) || {};
|
|
2327
|
+
} else if (ext === ".ts" || ext === ".js" || ext === ".mjs" || ext === ".cjs") {
|
|
2328
|
+
try {
|
|
2329
|
+
const fileUrl = `${pathToFileURL3(resolvedPath).href}?t=${Date.now()}`;
|
|
2330
|
+
const mod = await import(fileUrl);
|
|
2331
|
+
const rawExport = mod.default ?? mod.config ?? mod;
|
|
2332
|
+
userConfig = (typeof rawExport === "function" ? await rawExport() : rawExport) || {};
|
|
2333
|
+
} catch (importErr) {
|
|
2334
|
+
try {
|
|
2335
|
+
const mod = __require(resolvedPath);
|
|
2336
|
+
const rawExport = mod.default ?? mod.config ?? mod;
|
|
2337
|
+
userConfig = (typeof rawExport === "function" ? await rawExport() : rawExport) || {};
|
|
2338
|
+
} catch {
|
|
2339
|
+
throw importErr;
|
|
2340
|
+
}
|
|
2341
|
+
}
|
|
2308
2342
|
}
|
|
2343
|
+
} catch (err) {
|
|
2344
|
+
throw new Error(
|
|
2345
|
+
`Failed to parse configuration file at ${resolvedPath}: ${err instanceof Error ? err.message : String(err)}`
|
|
2346
|
+
);
|
|
2347
|
+
}
|
|
2348
|
+
if (userConfig && typeof userConfig === "object" && "$schema" in userConfig) {
|
|
2349
|
+
delete userConfig.$schema;
|
|
2309
2350
|
}
|
|
2351
|
+
const parsedConfig = userConfig;
|
|
2310
2352
|
const mergedConfig = {
|
|
2311
2353
|
...DEFAULT_CONFIG,
|
|
2312
|
-
...
|
|
2354
|
+
...parsedConfig,
|
|
2313
2355
|
margins: {
|
|
2314
2356
|
...DEFAULT_CONFIG.margins,
|
|
2315
|
-
...
|
|
2357
|
+
...parsedConfig.margins || {}
|
|
2316
2358
|
},
|
|
2317
|
-
header:
|
|
2318
|
-
footer:
|
|
2319
|
-
metadata:
|
|
2359
|
+
header: parsedConfig.header !== void 0 ? parsedConfig.header : DEFAULT_CONFIG.header,
|
|
2360
|
+
footer: parsedConfig.footer !== void 0 ? parsedConfig.footer : DEFAULT_CONFIG.footer,
|
|
2361
|
+
metadata: {
|
|
2362
|
+
...DEFAULT_CONFIG.metadata || {},
|
|
2363
|
+
...parsedConfig.metadata || {}
|
|
2364
|
+
}
|
|
2320
2365
|
};
|
|
2321
2366
|
return {
|
|
2322
2367
|
config: mergedConfig,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@masumdev/markforge",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"description": "Modern, high-performance Markdown & MDX multi-format document publishing engine and CLI (DOCX, PDF, HTML, and Images) with embedded HTML/CSS, image inliner, and Ink terminal UI.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
package/schema.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
3
|
"title": "MarkforgeConfig",
|
|
4
|
-
"description": "
|
|
4
|
+
"description": "Type-safe configuration schema for @masumdev/markforge Markdown & MDX document publishing engine",
|
|
5
5
|
"type": "object",
|
|
6
6
|
"properties": {
|
|
7
7
|
"$schema": {
|
|
8
8
|
"type": "string",
|
|
9
|
-
"description": "JSON
|
|
9
|
+
"description": "JSON Schema reference URI for IDE autocompletion and validation"
|
|
10
10
|
},
|
|
11
11
|
"to": {
|
|
12
|
-
"description": "Target formats to compile markdown to",
|
|
12
|
+
"description": "Target formats to compile markdown to (docx, pdf, html, png)",
|
|
13
13
|
"oneOf": [
|
|
14
14
|
{
|
|
15
15
|
"type": "string",
|
|
@@ -31,12 +31,12 @@
|
|
|
31
31
|
},
|
|
32
32
|
"theme": {
|
|
33
33
|
"type": "string",
|
|
34
|
-
"description": "Visual document theme",
|
|
34
|
+
"description": "Visual document theme name",
|
|
35
35
|
"enum": ["default", "academic", "github", "corporate", "minimal", "dracula"],
|
|
36
36
|
"default": "default"
|
|
37
37
|
},
|
|
38
38
|
"css": {
|
|
39
|
-
"description": "Custom CSS stylesheet(s) to inject",
|
|
39
|
+
"description": "Custom CSS stylesheet path(s) to inject",
|
|
40
40
|
"oneOf": [
|
|
41
41
|
{ "type": "string" },
|
|
42
42
|
{ "type": "array", "items": { "type": "string" } }
|
|
@@ -44,56 +44,87 @@
|
|
|
44
44
|
},
|
|
45
45
|
"orientation": {
|
|
46
46
|
"type": "string",
|
|
47
|
+
"description": "Page orientation for PDF and DOCX",
|
|
47
48
|
"enum": ["portrait", "landscape"],
|
|
48
49
|
"default": "portrait"
|
|
49
50
|
},
|
|
50
51
|
"paperSize": {
|
|
51
52
|
"type": "string",
|
|
53
|
+
"description": "Physical page size",
|
|
52
54
|
"enum": ["A4", "Letter", "Legal", "A3", "A5"],
|
|
53
55
|
"default": "A4"
|
|
54
56
|
},
|
|
55
57
|
"margins": {
|
|
56
58
|
"type": "object",
|
|
59
|
+
"description": "Document margins (supports units like '2.5cm', '1in', '20mm', or pt numbers)",
|
|
57
60
|
"properties": {
|
|
58
61
|
"top": { "type": ["string", "number"] },
|
|
59
62
|
"bottom": { "type": ["string", "number"] },
|
|
60
63
|
"left": { "type": ["string", "number"] },
|
|
61
64
|
"right": { "type": ["string", "number"] }
|
|
62
|
-
}
|
|
65
|
+
},
|
|
66
|
+
"additionalProperties": false
|
|
63
67
|
},
|
|
64
68
|
"header": {
|
|
65
69
|
"type": "object",
|
|
70
|
+
"description": "Header text across pages",
|
|
66
71
|
"properties": {
|
|
67
72
|
"left": { "type": "string" },
|
|
68
73
|
"center": { "type": "string" },
|
|
69
74
|
"right": { "type": "string" }
|
|
70
|
-
}
|
|
75
|
+
},
|
|
76
|
+
"additionalProperties": false
|
|
71
77
|
},
|
|
72
78
|
"footer": {
|
|
73
79
|
"type": "object",
|
|
80
|
+
"description": "Footer text across pages (supports {page} and {pages} variables)",
|
|
74
81
|
"properties": {
|
|
75
82
|
"left": { "type": "string" },
|
|
76
83
|
"center": { "type": "string" },
|
|
77
84
|
"right": { "type": "string" }
|
|
78
|
-
}
|
|
85
|
+
},
|
|
86
|
+
"additionalProperties": false
|
|
79
87
|
},
|
|
80
88
|
"toc": {
|
|
81
89
|
"type": "boolean",
|
|
82
|
-
"description": "Auto-generate Table of Contents",
|
|
90
|
+
"description": "Auto-generate Table of Contents with page references",
|
|
83
91
|
"default": false
|
|
84
92
|
},
|
|
85
93
|
"watermark": {
|
|
86
94
|
"type": "string",
|
|
87
|
-
"description": "
|
|
95
|
+
"description": "Diagonal watermark text across document pages"
|
|
88
96
|
},
|
|
89
97
|
"embedImages": {
|
|
90
98
|
"type": "boolean",
|
|
91
|
-
"description": "Whether to inline/embed all images",
|
|
99
|
+
"description": "Whether to inline/embed all images as Base64 in outputs",
|
|
92
100
|
"default": true
|
|
93
101
|
},
|
|
102
|
+
"bundleHtml": {
|
|
103
|
+
"type": "boolean",
|
|
104
|
+
"description": "Produce self-contained HTML with embedded styles and scripts",
|
|
105
|
+
"default": true
|
|
106
|
+
},
|
|
107
|
+
"syntaxTheme": {
|
|
108
|
+
"type": "string",
|
|
109
|
+
"description": "Code block syntax highlighting color scheme",
|
|
110
|
+
"enum": ["github-dark", "github-light", "dracula", "monokai", "nord"],
|
|
111
|
+
"default": "github-dark"
|
|
112
|
+
},
|
|
113
|
+
"metadata": {
|
|
114
|
+
"type": "object",
|
|
115
|
+
"description": "Document frontmatter metadata defaults",
|
|
116
|
+
"properties": {
|
|
117
|
+
"title": { "type": "string" },
|
|
118
|
+
"author": { "type": "string" },
|
|
119
|
+
"date": { "type": "string" },
|
|
120
|
+
"version": { "type": "string" },
|
|
121
|
+
"company": { "type": "string" }
|
|
122
|
+
},
|
|
123
|
+
"additionalProperties": true
|
|
124
|
+
},
|
|
94
125
|
"watch": {
|
|
95
126
|
"type": "boolean",
|
|
96
|
-
"description": "Watch mode for
|
|
127
|
+
"description": "Watch mode for re-compilation on source file changes",
|
|
97
128
|
"default": false
|
|
98
129
|
},
|
|
99
130
|
"serve": {
|
|
@@ -108,7 +139,7 @@
|
|
|
108
139
|
},
|
|
109
140
|
"open": {
|
|
110
141
|
"type": "boolean",
|
|
111
|
-
"description": "
|
|
142
|
+
"description": "Automatically open document in browser on compile",
|
|
112
143
|
"default": false
|
|
113
144
|
}
|
|
114
145
|
},
|
package/dist/chunk-ASXSHC6H.mjs
DELETED
|
@@ -1,120 +0,0 @@
|
|
|
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
|
-
__require
|
|
12
|
-
} from "./chunk-YZHGBG4N.mjs";
|
|
13
|
-
|
|
14
|
-
// src/config/loadConfig.ts
|
|
15
|
-
import * as fs from "fs";
|
|
16
|
-
import * as path from "path";
|
|
17
|
-
import { pathToFileURL } from "url";
|
|
18
|
-
import * as YAML from "yaml";
|
|
19
|
-
var DEFAULT_CONFIG_FILENAMES = [
|
|
20
|
-
"markforge.config.json",
|
|
21
|
-
".markforgerc.json",
|
|
22
|
-
"markforge.config.yaml",
|
|
23
|
-
"markforge.config.yml",
|
|
24
|
-
".markforgerc.yaml",
|
|
25
|
-
".markforgerc.yml",
|
|
26
|
-
".markforgerc",
|
|
27
|
-
"markforge.config.ts",
|
|
28
|
-
"markforge.config.js",
|
|
29
|
-
"markforge.config.mjs",
|
|
30
|
-
"markforge.config.cjs"
|
|
31
|
-
];
|
|
32
|
-
var DEFAULT_CONFIG = {
|
|
33
|
-
to: ["docx", "pdf"],
|
|
34
|
-
outputDir: void 0,
|
|
35
|
-
theme: "default",
|
|
36
|
-
css: void 0,
|
|
37
|
-
orientation: "portrait",
|
|
38
|
-
paperSize: "A4",
|
|
39
|
-
margins: {
|
|
40
|
-
top: "2.5cm",
|
|
41
|
-
bottom: "2.5cm",
|
|
42
|
-
left: "2.5cm",
|
|
43
|
-
right: "2.5cm"
|
|
44
|
-
},
|
|
45
|
-
header: void 0,
|
|
46
|
-
footer: {
|
|
47
|
-
right: "Page {page} of {pages}"
|
|
48
|
-
},
|
|
49
|
-
toc: false,
|
|
50
|
-
watermark: void 0,
|
|
51
|
-
embedImages: true,
|
|
52
|
-
metadata: void 0,
|
|
53
|
-
watch: false,
|
|
54
|
-
serve: false,
|
|
55
|
-
port: 4e3,
|
|
56
|
-
open: false,
|
|
57
|
-
bundleHtml: true,
|
|
58
|
-
syntaxTheme: "github-dark"
|
|
59
|
-
};
|
|
60
|
-
async function loadConfig(customPath, cwd = process.cwd()) {
|
|
61
|
-
let resolvedPath = null;
|
|
62
|
-
if (customPath) {
|
|
63
|
-
resolvedPath = path.isAbsolute(customPath) ? customPath : path.resolve(cwd, customPath);
|
|
64
|
-
if (!fs.existsSync(resolvedPath)) {
|
|
65
|
-
throw new Error(`Configuration file not found: ${resolvedPath}`);
|
|
66
|
-
}
|
|
67
|
-
} else {
|
|
68
|
-
for (const filename of DEFAULT_CONFIG_FILENAMES) {
|
|
69
|
-
const candidate = path.resolve(cwd, filename);
|
|
70
|
-
if (fs.existsSync(candidate)) {
|
|
71
|
-
resolvedPath = candidate;
|
|
72
|
-
break;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
if (!resolvedPath) {
|
|
77
|
-
return {
|
|
78
|
-
config: { ...DEFAULT_CONFIG },
|
|
79
|
-
configPath: null
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
const ext = path.extname(resolvedPath).toLowerCase();
|
|
83
|
-
let userConfig = {};
|
|
84
|
-
if (ext === ".json" || resolvedPath.endsWith(".markforgerc")) {
|
|
85
|
-
const raw = fs.readFileSync(resolvedPath, "utf-8");
|
|
86
|
-
userConfig = JSON.parse(raw);
|
|
87
|
-
} else if (ext === ".yaml" || ext === ".yml") {
|
|
88
|
-
const raw = fs.readFileSync(resolvedPath, "utf-8");
|
|
89
|
-
userConfig = YAML.parse(raw);
|
|
90
|
-
} else if (ext === ".ts" || ext === ".js" || ext === ".mjs" || ext === ".cjs") {
|
|
91
|
-
try {
|
|
92
|
-
const fileUrl = pathToFileURL(resolvedPath).href;
|
|
93
|
-
const mod = await import(fileUrl);
|
|
94
|
-
userConfig = mod.default || mod;
|
|
95
|
-
} catch {
|
|
96
|
-
const required = __require(resolvedPath);
|
|
97
|
-
userConfig = required.default || required;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
const mergedConfig = {
|
|
101
|
-
...DEFAULT_CONFIG,
|
|
102
|
-
...userConfig,
|
|
103
|
-
margins: {
|
|
104
|
-
...DEFAULT_CONFIG.margins,
|
|
105
|
-
...userConfig.margins
|
|
106
|
-
},
|
|
107
|
-
header: userConfig.header || DEFAULT_CONFIG.header,
|
|
108
|
-
footer: userConfig.footer || DEFAULT_CONFIG.footer,
|
|
109
|
-
metadata: userConfig.metadata
|
|
110
|
-
};
|
|
111
|
-
return {
|
|
112
|
-
config: mergedConfig,
|
|
113
|
-
configPath: resolvedPath
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
export {
|
|
118
|
-
DEFAULT_CONFIG,
|
|
119
|
-
loadConfig
|
|
120
|
-
};
|