@jacobbubu/md-to-lark 1.2.0 → 1.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
CHANGED
|
@@ -23,7 +23,7 @@ Notes:
|
|
|
23
23
|
- Recursively publishing multiple `.md` files from a directory
|
|
24
24
|
- Preparing local assets, remote images, and standalone URLs before publish
|
|
25
25
|
- Running a full dry-run without writing to Feishu
|
|
26
|
-
- Rewriting Markdown before publish with presets
|
|
26
|
+
- Rewriting Markdown before publish with one or more ordered presets
|
|
27
27
|
|
|
28
28
|
## Quick Start
|
|
29
29
|
|
|
@@ -109,6 +109,7 @@ Presets, Mermaid, and stage artifacts:
|
|
|
109
109
|
```bash
|
|
110
110
|
npm run publish:md -- --input ./test-md/comp/comp.md --preset medium --dry-run
|
|
111
111
|
npm run publish:md -- --input ./test-md/comp/comp.md --preset zh-format --dry-run
|
|
112
|
+
npm run publish:md -- --input ./test-md/comp/comp.md --preset zh-format --preset ./my-preset.mjs --dry-run
|
|
112
113
|
npm run publish:md -- --input ./test-md/mermaid.md --mermaid-target board --dry-run
|
|
113
114
|
npm run publish:md -- --input ./test-md/comp/comp.md --pipeline-cache-dir ./out/debug-cache --dry-run
|
|
114
115
|
```
|
|
@@ -173,6 +174,7 @@ Guardrails:
|
|
|
173
174
|
- Mermaid `text-drawing` and `board` output paths
|
|
174
175
|
- Table width heuristics and numeric-column right alignment
|
|
175
176
|
- Chinese Markdown formatting preset (`zh-format`)
|
|
177
|
+
- Ordered preset composition from CLI and programmatic usage
|
|
176
178
|
- Stage cache output from `00-source` to `05-publish`
|
|
177
179
|
- Programmatic access through `publishMdToLark`
|
|
178
180
|
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
function usage() {
|
|
2
2
|
return [
|
|
3
|
-
'Usage: npm run publish:md -- --input <file.md|dir> [--title <doc_title_or_prefix>] [--date-prefix|--no-date-prefix] [--preset <preset_name_or_module_path>] [--document-base-url <base_url>] [--folder <folder_token>] [--doc <document_id>] [--download-remote-images|--no-download-remote-images] [--yt-dlp-path <path>] [--yt-dlp-cookies-path <path>] [--pipeline-cache-dir <dir>] [--mermaid-target <text-drawing|board>] [--mermaid-board-syntax-type <int>] [--mermaid-board-style-type <int>] [--mermaid-board-diagram-type <int>] [--dry-run] [--help|-h]',
|
|
3
|
+
'Usage: npm run publish:md -- --input <file.md|dir> [--title <doc_title_or_prefix>] [--date-prefix|--no-date-prefix] [--preset <preset_name_or_module_path>]... [--document-base-url <base_url>] [--folder <folder_token>] [--doc <document_id>] [--download-remote-images|--no-download-remote-images] [--yt-dlp-path <path>] [--yt-dlp-cookies-path <path>] [--pipeline-cache-dir <dir>] [--mermaid-target <text-drawing|board>] [--mermaid-board-syntax-type <int>] [--mermaid-board-style-type <int>] [--mermaid-board-diagram-type <int>] [--dry-run] [--help|-h]',
|
|
4
4
|
'',
|
|
5
5
|
'Options:',
|
|
6
6
|
' --input Markdown file path, or directory path (publish all *.md recursively).',
|
|
7
7
|
' --title Single-file title. In directory mode this is used as title prefix.',
|
|
8
8
|
' --date-prefix Enable date prefix in final title: YYYYMMDD-<title>. Default: enabled.',
|
|
9
9
|
' --no-date-prefix Disable date prefix in final title.',
|
|
10
|
-
' --preset Optional preset module path (js/mjs/cjs/ts) or built-in name (e.g. medium).
|
|
10
|
+
' --preset Optional preset module path (js/mjs/cjs/ts) or built-in name (e.g. medium). Repeatable; presets run in the given order before publish pipeline.',
|
|
11
11
|
' --document-base-url Base URL used to build documentUrl results (for example https://li.feishu.cn).',
|
|
12
12
|
' --folder Feishu folder token. Default: LARK_FOLDER_TOKEN from .env',
|
|
13
13
|
' --doc Existing Feishu document id (single-file only). If set, publish directly into this doc (and clear content first).',
|
|
@@ -51,7 +51,7 @@ export function parsePublishMdArgs(argv, env = process.env) {
|
|
|
51
51
|
let inputPath = '';
|
|
52
52
|
let title = '';
|
|
53
53
|
let titleDatePrefix;
|
|
54
|
-
|
|
54
|
+
const presetPaths = [];
|
|
55
55
|
let documentBaseUrl = '';
|
|
56
56
|
let folderToken = (env.LARK_FOLDER_TOKEN ?? '').trim();
|
|
57
57
|
let documentId;
|
|
@@ -111,7 +111,7 @@ export function parsePublishMdArgs(argv, env = process.env) {
|
|
|
111
111
|
const value = argv[i + 1];
|
|
112
112
|
if (!value)
|
|
113
113
|
throw new Error('Missing value for --preset.');
|
|
114
|
-
|
|
114
|
+
presetPaths.push(value);
|
|
115
115
|
i += 1;
|
|
116
116
|
continue;
|
|
117
117
|
}
|
|
@@ -214,11 +214,17 @@ export function parsePublishMdArgs(argv, env = process.env) {
|
|
|
214
214
|
if (!documentId && !folderToken) {
|
|
215
215
|
throw new Error('Folder token is required when --doc is not provided. Use --folder or set LARK_FOLDER_TOKEN.');
|
|
216
216
|
}
|
|
217
|
+
const normalizedPresetPaths = presetPaths.map((presetPath) => presetPath.trim()).filter(Boolean);
|
|
217
218
|
return {
|
|
218
219
|
inputPath: inputPath.trim(),
|
|
219
220
|
...(title.trim() ? { title: title.trim() } : {}),
|
|
220
221
|
...(titleDatePrefix === undefined ? {} : { titleDatePrefix }),
|
|
221
|
-
...(
|
|
222
|
+
...(normalizedPresetPaths.length > 0
|
|
223
|
+
? {
|
|
224
|
+
...(normalizedPresetPaths.length === 1 ? { presetPath: normalizedPresetPaths[0] } : {}),
|
|
225
|
+
presetPaths: normalizedPresetPaths,
|
|
226
|
+
}
|
|
227
|
+
: {}),
|
|
222
228
|
...(documentBaseUrl.trim() ? { documentBaseUrl: documentBaseUrl.trim() } : {}),
|
|
223
229
|
folderToken,
|
|
224
230
|
...(documentId ? { documentId: documentId.trim() } : {}),
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import { getPublishMdUsage, hasPublishMdHelpFlag, parsePublishMdArgs } from './args.js';
|
|
2
2
|
import { resolvePublishInputSet } from './input-resolver.js';
|
|
3
|
-
import {
|
|
3
|
+
import { loadMarkdownPresets } from './preset-loader.js';
|
|
4
4
|
import { createDocument, listFolderChildren, normalizeDocumentId } from '../../lark/docx/ops.js';
|
|
5
5
|
import { processSingleMarkdownFile } from '../../publish/process-file.js';
|
|
6
6
|
import { buildPublishRuntime, logPublishRuntimeSummary } from '../../publish/runtime.js';
|
|
7
7
|
import { sleep } from '../../shared/rate-limiter.js';
|
|
8
8
|
export { getPublishMdUsage, parsePublishMdArgs };
|
|
9
|
+
function resolveMarkdownPresetRefs(options) {
|
|
10
|
+
if (options.presetPaths && options.presetPaths.length > 0) {
|
|
11
|
+
return options.presetPaths.map((presetPath) => presetPath.trim()).filter(Boolean);
|
|
12
|
+
}
|
|
13
|
+
return options.presetPath?.trim() ? [options.presetPath.trim()] : [];
|
|
14
|
+
}
|
|
9
15
|
function buildFolderDocIndex(entries) {
|
|
10
16
|
const byTitle = new Map();
|
|
11
17
|
for (const entry of entries) {
|
|
@@ -60,11 +66,11 @@ function createFolderDocumentResolver(runtime, options) {
|
|
|
60
66
|
}
|
|
61
67
|
export async function publishMdToLark(options, env = process.env) {
|
|
62
68
|
const inputSet = await resolvePublishInputSet(options.inputPath);
|
|
63
|
-
const
|
|
69
|
+
const markdownPresets = await loadMarkdownPresets(resolveMarkdownPresetRefs(options));
|
|
64
70
|
if (options.documentId && inputSet.markdownFiles.length !== 1) {
|
|
65
71
|
throw new Error('--doc only supports single markdown input file.');
|
|
66
72
|
}
|
|
67
|
-
const runtime = buildPublishRuntime(options, env,
|
|
73
|
+
const runtime = buildPublishRuntime(options, env, markdownPresets);
|
|
68
74
|
logPublishRuntimeSummary(runtime, inputSet.markdownFiles.length, inputSet.mode);
|
|
69
75
|
const normalizedDocumentId = options.documentId ? normalizeDocumentId(options.documentId) : undefined;
|
|
70
76
|
const resolveTargetDocumentId = options.dryRun || normalizedDocumentId
|
|
@@ -66,7 +66,7 @@ function resolveBuiltInPreset(rawPreset) {
|
|
|
66
66
|
export function listBuiltinMarkdownPresetNames() {
|
|
67
67
|
return Object.keys(BUILTIN_MARKDOWN_PRESETS);
|
|
68
68
|
}
|
|
69
|
-
|
|
69
|
+
async function loadSingleMarkdownPreset(rawPath) {
|
|
70
70
|
const trimmed = (rawPath ?? '').trim();
|
|
71
71
|
if (!trimmed)
|
|
72
72
|
return null;
|
|
@@ -111,3 +111,20 @@ export async function loadMarkdownPreset(rawPath) {
|
|
|
111
111
|
transform: wrappedTransform,
|
|
112
112
|
};
|
|
113
113
|
}
|
|
114
|
+
export async function loadMarkdownPresets(rawPaths) {
|
|
115
|
+
const normalized = (rawPaths ?? []).map((rawPath) => rawPath.trim()).filter(Boolean);
|
|
116
|
+
if (normalized.length === 0)
|
|
117
|
+
return [];
|
|
118
|
+
const presets = [];
|
|
119
|
+
for (const rawPath of normalized) {
|
|
120
|
+
const preset = await loadSingleMarkdownPreset(rawPath);
|
|
121
|
+
if (preset) {
|
|
122
|
+
presets.push(preset);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return presets;
|
|
126
|
+
}
|
|
127
|
+
export async function loadMarkdownPreset(rawPath) {
|
|
128
|
+
const presets = await loadMarkdownPresets(rawPath ? [rawPath] : []);
|
|
129
|
+
return presets[0] ?? null;
|
|
130
|
+
}
|
|
@@ -70,18 +70,20 @@ export async function processSingleMarkdownFile(params) {
|
|
|
70
70
|
const startedAt = new Date().toISOString();
|
|
71
71
|
const sourceMarkdown = await readFile(markdownPath, 'utf8');
|
|
72
72
|
let markdown = sourceMarkdown;
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
for (let presetIndex = 0; presetIndex < runtime.markdownPresets.length; presetIndex += 1) {
|
|
74
|
+
const preset = runtime.markdownPresets[presetIndex];
|
|
75
|
+
markdown = await preset.transform(markdown, {
|
|
75
76
|
inputPath: markdownPath,
|
|
76
77
|
index,
|
|
77
78
|
total: inputSet.markdownFiles.length,
|
|
78
79
|
env: runtime.env,
|
|
79
|
-
log: (...args) => console.error(`[preset ${index + 1}/${inputSet.markdownFiles.length}]`, ...args.map((arg) => String(arg))),
|
|
80
|
+
log: (...args) => console.error(`[preset ${presetIndex + 1}/${runtime.markdownPresets.length} file ${index + 1}/${inputSet.markdownFiles.length}] [${preset.displayPath}]`, ...args.map((arg) => String(arg))),
|
|
80
81
|
});
|
|
81
82
|
}
|
|
82
83
|
await writeSourceStage(stagePaths, sourceMarkdown, markdown, {
|
|
83
84
|
sourcePath: path.resolve(markdownPath),
|
|
84
|
-
preset: runtime.
|
|
85
|
+
preset: runtime.markdownPresets.length === 1 ? runtime.markdownPresets[0].displayPath : null,
|
|
86
|
+
presets: runtime.markdownPresets.map((preset) => preset.displayPath),
|
|
85
87
|
startedAt,
|
|
86
88
|
});
|
|
87
89
|
const prepareResult = await prepareMarkdownBeforePublish(markdownPath, markdown, {
|
package/dist/publish/runtime.js
CHANGED
|
@@ -53,7 +53,7 @@ function normalizeOptionalPath(value) {
|
|
|
53
53
|
}
|
|
54
54
|
return trimmed;
|
|
55
55
|
}
|
|
56
|
-
export function buildPublishRuntime(options, env,
|
|
56
|
+
export function buildPublishRuntime(options, env, markdownPresets) {
|
|
57
57
|
const config = createLarkClientConfigFromEnv(env);
|
|
58
58
|
const sdkClient = new lark.Client({
|
|
59
59
|
appId: config.appId,
|
|
@@ -100,7 +100,8 @@ export function buildPublishRuntime(options, env, markdownPreset) {
|
|
|
100
100
|
};
|
|
101
101
|
return {
|
|
102
102
|
env,
|
|
103
|
-
|
|
103
|
+
markdownPresets,
|
|
104
|
+
markdownPreset: markdownPresets[0] ?? null,
|
|
104
105
|
documentBaseUrl,
|
|
105
106
|
documentUrlFor: (documentId) => buildLarkDocumentUrl(documentBaseUrl, documentId),
|
|
106
107
|
authOptions,
|
|
@@ -136,6 +137,10 @@ export function logPublishRuntimeSummary(runtime, inputCount, inputMode) {
|
|
|
136
137
|
? `Mermaid: target=board syntax_type=${String(runtime.mermaidRenderConfig.board.syntaxType)} style_type=${String(runtime.mermaidRenderConfig.board.styleType ?? '(default)')} diagram_type=${String(runtime.mermaidRenderConfig.board.diagramType ?? '(default)')}`
|
|
137
138
|
: 'Mermaid: target=text-drawing');
|
|
138
139
|
console.error(`Document URL base: ${runtime.documentBaseUrl}`);
|
|
140
|
+
if (runtime.markdownPresets.length > 1) {
|
|
141
|
+
console.error(`Presets: ${runtime.markdownPresets.map((preset) => preset.displayPath).join(' -> ')}`);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
139
144
|
if (runtime.markdownPreset) {
|
|
140
145
|
console.error(`Preset: ${runtime.markdownPreset.displayPath}`);
|
|
141
146
|
}
|