@wenyan-md/core 3.0.9 → 3.0.11
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/dist/core.js +47 -1
- package/dist/publish.js +16 -1
- package/dist/types/core/parser/frontMatterParser.d.ts +2 -0
- package/dist/types/publish.d.ts +9 -1
- package/dist/types/wechat.d.ts +24 -0
- package/dist/wechat.js +42 -2
- package/dist/wrapper.js +52 -5
- package/package.json +1 -1
package/dist/core.js
CHANGED
|
@@ -202,7 +202,19 @@ async function handleFrontMatter(markdown) {
|
|
|
202
202
|
const { attributes, body } = fm(markdown);
|
|
203
203
|
const result = { content: body || "" };
|
|
204
204
|
let head = "";
|
|
205
|
-
const {
|
|
205
|
+
const {
|
|
206
|
+
title,
|
|
207
|
+
description,
|
|
208
|
+
cover,
|
|
209
|
+
author,
|
|
210
|
+
source_url,
|
|
211
|
+
pic_crop_235_1,
|
|
212
|
+
pic_crop_1_1,
|
|
213
|
+
need_open_comment,
|
|
214
|
+
only_fans_can_comment,
|
|
215
|
+
image_list,
|
|
216
|
+
type
|
|
217
|
+
} = attributes;
|
|
206
218
|
if (title) {
|
|
207
219
|
result.title = title;
|
|
208
220
|
}
|
|
@@ -219,6 +231,12 @@ async function handleFrontMatter(markdown) {
|
|
|
219
231
|
if (source_url) {
|
|
220
232
|
result.source_url = source_url;
|
|
221
233
|
}
|
|
234
|
+
if (pic_crop_235_1) {
|
|
235
|
+
result.pic_crop_235_1 = String(pic_crop_235_1);
|
|
236
|
+
}
|
|
237
|
+
if (pic_crop_1_1) {
|
|
238
|
+
result.pic_crop_1_1 = String(pic_crop_1_1);
|
|
239
|
+
}
|
|
222
240
|
if (need_open_comment !== void 0) {
|
|
223
241
|
result.need_open_comment = need_open_comment;
|
|
224
242
|
}
|
|
@@ -996,6 +1014,34 @@ function wechatPostRender(element) {
|
|
|
996
1014
|
}
|
|
997
1015
|
li.appendChild(section);
|
|
998
1016
|
});
|
|
1017
|
+
const flattenNestedLists = () => {
|
|
1018
|
+
let nestedLists = element.querySelectorAll(
|
|
1019
|
+
"li > section > ul, li > section > ol"
|
|
1020
|
+
);
|
|
1021
|
+
while (nestedLists.length > 0) {
|
|
1022
|
+
Array.from(nestedLists).reverse().forEach((nestedList) => {
|
|
1023
|
+
const isOrdered = nestedList.tagName === "OL";
|
|
1024
|
+
const doc = element.ownerDocument;
|
|
1025
|
+
const items = Array.from(nestedList.children).filter(
|
|
1026
|
+
(child) => child.tagName === "LI"
|
|
1027
|
+
);
|
|
1028
|
+
const wrapper = doc.createElement("section");
|
|
1029
|
+
wrapper.style.marginLeft = "1em";
|
|
1030
|
+
items.forEach((item, index) => {
|
|
1031
|
+
const content = item.querySelector("section");
|
|
1032
|
+
const sectionEl = doc.createElement("section");
|
|
1033
|
+
const marker = isOrdered ? `${index + 1}. ` : "• ";
|
|
1034
|
+
sectionEl.innerHTML = marker + (content?.innerHTML ?? item.innerHTML);
|
|
1035
|
+
wrapper.appendChild(sectionEl);
|
|
1036
|
+
});
|
|
1037
|
+
nestedList.replaceWith(wrapper);
|
|
1038
|
+
});
|
|
1039
|
+
nestedLists = element.querySelectorAll(
|
|
1040
|
+
"li > section > ul, li > section > ol"
|
|
1041
|
+
);
|
|
1042
|
+
}
|
|
1043
|
+
};
|
|
1044
|
+
flattenNestedLists();
|
|
999
1045
|
element.style.color = "rgb(0, 0, 0)";
|
|
1000
1046
|
element.style.caretColor = "rgb(0, 0, 0)";
|
|
1001
1047
|
}
|
package/dist/publish.js
CHANGED
|
@@ -196,12 +196,18 @@ class WechatPublisher {
|
|
|
196
196
|
uploadCacheStore;
|
|
197
197
|
uploadMaterial;
|
|
198
198
|
publishArticle;
|
|
199
|
+
_listDraftsFn;
|
|
200
|
+
_getDraftFn;
|
|
201
|
+
_updateDraftFn;
|
|
199
202
|
fetchAccessToken;
|
|
200
203
|
constructor(httpAdapter, tokenStoreAdapter, uploadCacheStoreAdapter) {
|
|
201
|
-
const { uploadMaterial, publishArticle, fetchAccessToken } = createWechatClient(httpAdapter);
|
|
204
|
+
const { uploadMaterial, publishArticle, fetchAccessToken, listDrafts, getDraft, updateDraft } = createWechatClient(httpAdapter);
|
|
202
205
|
this.uploadMaterial = uploadMaterial;
|
|
203
206
|
this.publishArticle = publishArticle;
|
|
204
207
|
this.fetchAccessToken = fetchAccessToken;
|
|
208
|
+
this._listDraftsFn = listDrafts;
|
|
209
|
+
this._getDraftFn = getDraft;
|
|
210
|
+
this._updateDraftFn = updateDraft;
|
|
205
211
|
this.tokenStore = tokenStoreAdapter ? new TokenStore(tokenStoreAdapter) : void 0;
|
|
206
212
|
this.uploadCacheStore = uploadCacheStoreAdapter ? new UploadCacheStore(uploadCacheStoreAdapter) : void 0;
|
|
207
213
|
}
|
|
@@ -242,6 +248,15 @@ class WechatPublisher {
|
|
|
242
248
|
async publishToDraft(accessToken, options) {
|
|
243
249
|
return await this.publishArticle(accessToken, options);
|
|
244
250
|
}
|
|
251
|
+
async listDrafts(accessToken, offset = 0, count = 20, noContent = 0) {
|
|
252
|
+
return await this._listDraftsFn(accessToken, offset, count, noContent);
|
|
253
|
+
}
|
|
254
|
+
async getDraft(accessToken, mediaId) {
|
|
255
|
+
return await this._getDraftFn(accessToken, mediaId);
|
|
256
|
+
}
|
|
257
|
+
async updateDraft(accessToken, mediaId, articleIndex, options) {
|
|
258
|
+
await this._updateDraftFn(accessToken, mediaId, articleIndex, options);
|
|
259
|
+
}
|
|
245
260
|
async clearCache() {
|
|
246
261
|
if (this.tokenStore) {
|
|
247
262
|
await this.tokenStore.clear();
|
package/dist/types/publish.d.ts
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { HttpAdapter } from "./http.js";
|
|
2
2
|
import { TokenStorageAdapter } from "./tokenStore.js";
|
|
3
3
|
import { UploadCacheStorageAdapter } from "./uploadCacheStore.js";
|
|
4
|
-
import { WechatPublishOptions, WechatPublishResponse, WechatUploadResponse } from "./wechat.js";
|
|
4
|
+
import { WechatPublishOptions, WechatPublishResponse, WechatUploadResponse, WechatDraftListResponse, WechatDraftGetResponse } from "./wechat.js";
|
|
5
5
|
export interface ArticleOptions {
|
|
6
6
|
title: string;
|
|
7
7
|
content: string;
|
|
8
8
|
cover?: string;
|
|
9
9
|
author?: string;
|
|
10
10
|
source_url?: string;
|
|
11
|
+
pic_crop_235_1?: string;
|
|
12
|
+
pic_crop_1_1?: string;
|
|
11
13
|
need_open_comment?: boolean;
|
|
12
14
|
only_fans_can_comment?: boolean;
|
|
13
15
|
}
|
|
@@ -19,11 +21,17 @@ export declare class WechatPublisher {
|
|
|
19
21
|
private uploadCacheStore;
|
|
20
22
|
private uploadMaterial;
|
|
21
23
|
private publishArticle;
|
|
24
|
+
private _listDraftsFn;
|
|
25
|
+
private _getDraftFn;
|
|
26
|
+
private _updateDraftFn;
|
|
22
27
|
private fetchAccessToken;
|
|
23
28
|
constructor(httpAdapter: HttpAdapter, tokenStoreAdapter?: TokenStorageAdapter, uploadCacheStoreAdapter?: UploadCacheStorageAdapter);
|
|
24
29
|
getAccessTokenWithCache(appId: string, appSecret: string): Promise<string>;
|
|
25
30
|
uploadImage(file: Blob, filename: string, accessToken: string, appId?: string): Promise<WechatUploadResponse>;
|
|
26
31
|
publishToDraft(accessToken: string, options: WechatPublishOptions): Promise<WechatPublishResponse>;
|
|
32
|
+
listDrafts(accessToken: string, offset?: number, count?: number, noContent?: number): Promise<WechatDraftListResponse>;
|
|
33
|
+
getDraft(accessToken: string, mediaId: string): Promise<WechatDraftGetResponse>;
|
|
34
|
+
updateDraft(accessToken: string, mediaId: string, articleIndex: number, options: Partial<WechatPublishOptions>): Promise<void>;
|
|
27
35
|
clearCache(): Promise<void>;
|
|
28
36
|
setExternalToken(appid: string, accessToken: string): Promise<void>;
|
|
29
37
|
}
|
package/dist/types/wechat.d.ts
CHANGED
|
@@ -19,6 +19,8 @@ export interface WechatPublishOptions {
|
|
|
19
19
|
content: string;
|
|
20
20
|
thumb_media_id: string;
|
|
21
21
|
content_source_url?: string;
|
|
22
|
+
pic_crop_235_1?: string;
|
|
23
|
+
pic_crop_1_1?: string;
|
|
22
24
|
article_type?: "news" | "newspic";
|
|
23
25
|
image_info?: ImageInfo;
|
|
24
26
|
need_open_comment?: 0 | 1;
|
|
@@ -39,9 +41,31 @@ export interface WechatTokenResponse {
|
|
|
39
41
|
export interface WechatPublishResponse {
|
|
40
42
|
media_id: string;
|
|
41
43
|
}
|
|
44
|
+
export interface WechatDraftListItem {
|
|
45
|
+
media_id: string;
|
|
46
|
+
content: {
|
|
47
|
+
news_item: WechatPublishOptions[];
|
|
48
|
+
};
|
|
49
|
+
update_time: number;
|
|
50
|
+
}
|
|
51
|
+
export interface WechatDraftListResponse {
|
|
52
|
+
total_count: number;
|
|
53
|
+
item_count: number;
|
|
54
|
+
item: WechatDraftListItem[];
|
|
55
|
+
}
|
|
56
|
+
export interface WechatDraftGetResponse {
|
|
57
|
+
news_item: WechatPublishOptions[];
|
|
58
|
+
}
|
|
59
|
+
export interface WechatDraftUpdateResponse {
|
|
60
|
+
errcode: number;
|
|
61
|
+
errmsg: string;
|
|
62
|
+
}
|
|
42
63
|
export declare function createWechatClient(httpAdapter: HttpAdapter): {
|
|
43
64
|
fetchAccessToken(appId: string, appSecret: string): Promise<WechatTokenResponse>;
|
|
44
65
|
uploadMaterial(type: string, file: Blob, filename: string, accessToken: string): Promise<WechatUploadResponse>;
|
|
45
66
|
publishArticle(accessToken: string, options: WechatPublishOptions): Promise<WechatPublishResponse>;
|
|
67
|
+
listDrafts(accessToken: string, offset?: number, count?: number, noContent?: number): Promise<WechatDraftListResponse>;
|
|
68
|
+
getDraft(accessToken: string, mediaId: string): Promise<WechatDraftGetResponse>;
|
|
69
|
+
updateDraft(accessToken: string, mediaId: string, articleIndex: number, options: Partial<WechatPublishOptions>): Promise<void>;
|
|
46
70
|
};
|
|
47
71
|
export type WechatClient = ReturnType<typeof createWechatClient>;
|
package/dist/wechat.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
const tokenUrl = "https://api.weixin.qq.com/cgi-bin/token";
|
|
2
2
|
const publishUrl = "https://api.weixin.qq.com/cgi-bin/draft/add";
|
|
3
3
|
const uploadUrl = "https://api.weixin.qq.com/cgi-bin/material/add_material";
|
|
4
|
+
const draftListUrl = "https://api.weixin.qq.com/cgi-bin/draft/batchget";
|
|
5
|
+
const draftGetUrl = "https://api.weixin.qq.com/cgi-bin/draft/get";
|
|
6
|
+
const draftUpdateUrl = "https://api.weixin.qq.com/cgi-bin/draft/update";
|
|
4
7
|
function createWechatClient(httpAdapter) {
|
|
5
8
|
return {
|
|
6
9
|
async fetchAccessToken(appId, appSecret) {
|
|
@@ -37,12 +40,49 @@ function createWechatClient(httpAdapter) {
|
|
|
37
40
|
const data = await res.json();
|
|
38
41
|
assertWechatSuccess(data);
|
|
39
42
|
return data;
|
|
43
|
+
},
|
|
44
|
+
async listDrafts(accessToken, offset = 0, count = 20, noContent = 0) {
|
|
45
|
+
const res = await httpAdapter.fetch(`${draftListUrl}?access_token=${accessToken}`, {
|
|
46
|
+
method: "POST",
|
|
47
|
+
body: JSON.stringify({ offset, count, no_content: noContent })
|
|
48
|
+
});
|
|
49
|
+
if (!res.ok) throw new Error(await res.text());
|
|
50
|
+
const data = await res.json();
|
|
51
|
+
assertWechatSuccess(data);
|
|
52
|
+
return data;
|
|
53
|
+
},
|
|
54
|
+
async getDraft(accessToken, mediaId) {
|
|
55
|
+
const res = await httpAdapter.fetch(`${draftGetUrl}?access_token=${accessToken}`, {
|
|
56
|
+
method: "POST",
|
|
57
|
+
body: JSON.stringify({ media_id: mediaId })
|
|
58
|
+
});
|
|
59
|
+
if (!res.ok) throw new Error(await res.text());
|
|
60
|
+
const data = await res.json();
|
|
61
|
+
assertWechatSuccess(data);
|
|
62
|
+
return data;
|
|
63
|
+
},
|
|
64
|
+
async updateDraft(accessToken, mediaId, articleIndex, options) {
|
|
65
|
+
const res = await httpAdapter.fetch(`${draftUpdateUrl}?access_token=${accessToken}`, {
|
|
66
|
+
method: "POST",
|
|
67
|
+
body: JSON.stringify({
|
|
68
|
+
media_id: mediaId,
|
|
69
|
+
index: articleIndex,
|
|
70
|
+
articles: options
|
|
71
|
+
})
|
|
72
|
+
});
|
|
73
|
+
if (!res.ok) throw new Error(await res.text());
|
|
74
|
+
const data = await res.json();
|
|
75
|
+
assertWechatSuccess(data);
|
|
40
76
|
}
|
|
41
77
|
};
|
|
42
78
|
}
|
|
79
|
+
const WECHAT_ERROR_HINTS = {
|
|
80
|
+
45166: "内容超长。小绿书模式有内容长度限制,请精简正文后重试。"
|
|
81
|
+
};
|
|
43
82
|
function assertWechatSuccess(data) {
|
|
44
|
-
if ("errcode" in data) {
|
|
45
|
-
|
|
83
|
+
if ("errcode" in data && data.errcode !== 0) {
|
|
84
|
+
const hint = WECHAT_ERROR_HINTS[data.errcode];
|
|
85
|
+
throw new Error(hint ? `${data.errcode}: ${hint} (${data.errmsg})` : `${data.errcode}: ${data.errmsg}`);
|
|
46
86
|
}
|
|
47
87
|
}
|
|
48
88
|
export {
|
package/dist/wrapper.js
CHANGED
|
@@ -269,7 +269,8 @@ function needUpload(url) {
|
|
|
269
269
|
return !/^(https?:\/\/|data:|asset:\/\/)/i.test(url);
|
|
270
270
|
}
|
|
271
271
|
async function uploadLocalImage(originalUrl, serverUrl, headers, relativePath) {
|
|
272
|
-
const
|
|
272
|
+
const decodedUrl = decodeURIComponent(originalUrl);
|
|
273
|
+
const imagePath = RuntimeEnv.resolveLocalPath(decodedUrl, relativePath);
|
|
273
274
|
let fileBuffer;
|
|
274
275
|
try {
|
|
275
276
|
fileBuffer = await readBinaryFile(imagePath);
|
|
@@ -435,7 +436,8 @@ async function uploadImage(imageUrl, accessToken, fileName, relativePath, appId)
|
|
|
435
436
|
const contentType = response.headers.get("content-type") || "image/jpeg";
|
|
436
437
|
fileData = new Blob([arrayBuffer], { type: contentType });
|
|
437
438
|
} else {
|
|
438
|
-
const
|
|
439
|
+
const decodedUrl = decodeURIComponent(imageUrl);
|
|
440
|
+
const resolvedPath = RuntimeEnv.resolveLocalPath(decodedUrl, relativePath);
|
|
439
441
|
const stats = await stat(resolvedPath);
|
|
440
442
|
if (stats.size === 0) {
|
|
441
443
|
throw new Error(`本地图片大小为0,无法上传: ${resolvedPath}`);
|
|
@@ -476,7 +478,17 @@ async function uploadImages(content, accessToken, relativePath, appId) {
|
|
|
476
478
|
return { html: updatedHtml, firstImageId };
|
|
477
479
|
}
|
|
478
480
|
async function publishToWechatDraft(articleOptions, publishOptions = {}) {
|
|
479
|
-
const {
|
|
481
|
+
const {
|
|
482
|
+
title,
|
|
483
|
+
content,
|
|
484
|
+
cover,
|
|
485
|
+
author,
|
|
486
|
+
source_url,
|
|
487
|
+
pic_crop_235_1,
|
|
488
|
+
pic_crop_1_1,
|
|
489
|
+
need_open_comment,
|
|
490
|
+
only_fans_can_comment
|
|
491
|
+
} = articleOptions;
|
|
480
492
|
const { appId, appSecret, relativePath } = publishOptions;
|
|
481
493
|
const { appId: appIdFinal, appSecret: appSecretFinal } = await getAppIdAndSecret(appId, appSecret);
|
|
482
494
|
const accessToken = await wechatPublisher.getAccessTokenWithCache(appIdFinal, appSecretFinal);
|
|
@@ -512,6 +524,8 @@ async function publishToWechatDraft(articleOptions, publishOptions = {}) {
|
|
|
512
524
|
thumb_media_id: thumbMediaId,
|
|
513
525
|
author,
|
|
514
526
|
content_source_url: source_url,
|
|
527
|
+
pic_crop_235_1,
|
|
528
|
+
pic_crop_1_1,
|
|
515
529
|
need_open_comment: need_open_comment ? 1 : 0,
|
|
516
530
|
only_fans_can_comment: only_fans_can_comment ? 1 : 0
|
|
517
531
|
});
|
|
@@ -569,11 +583,30 @@ async function publishImageTextToWechatDraft(articleOptions, publishOptions = {}
|
|
|
569
583
|
if (!thumbMediaId) {
|
|
570
584
|
throw new Error("未能获取封面图的 media_id");
|
|
571
585
|
}
|
|
586
|
+
let plainContent = content || "";
|
|
587
|
+
if (plainContent) {
|
|
588
|
+
if (plainContent.includes("<br") || plainContent.includes("<p")) {
|
|
589
|
+
plainContent = plainContent.replace(/<br\s*\/?>\n/g, "\n");
|
|
590
|
+
const dom = new JSDOM(`<body>${plainContent}</body>`);
|
|
591
|
+
const document = dom.window.document;
|
|
592
|
+
const brs = document.querySelectorAll("br");
|
|
593
|
+
for (const br of brs) {
|
|
594
|
+
br.replaceWith(document.createTextNode("\n"));
|
|
595
|
+
}
|
|
596
|
+
const paragraphs = document.querySelectorAll("p");
|
|
597
|
+
for (const p of paragraphs) {
|
|
598
|
+
const text = document.createTextNode("\n" + p.textContent + "\n");
|
|
599
|
+
p.replaceWith(text);
|
|
600
|
+
}
|
|
601
|
+
plainContent = document.body.textContent?.trim() || "";
|
|
602
|
+
}
|
|
603
|
+
plainContent = plainContent.replace(/^#{1,6}\s+/gm, "").replace(/\*\*(.*?)\*\*/g, "$1").replace(/\*(.*?)\*/g, "$1").replace(/__(.*?)__/g, "$1").replace(/_(.*?)_/g, "$1").replace(/`{1,3}[^`]*`{1,3}/g, "").replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/!\[([^\]]*)\]\([^)]+\)/g, "").replace(/^\s*[-*+]\s+/gm, "").replace(/^\s*\d+\.\s+/gm, "").replace(/^\s*>\s+/gm, "").replace(/\n{3,}/g, "\n\n").trim();
|
|
604
|
+
}
|
|
572
605
|
const data = await wechatPublisher.publishToDraft(
|
|
573
606
|
accessToken,
|
|
574
607
|
{
|
|
575
608
|
title,
|
|
576
|
-
content,
|
|
609
|
+
content: plainContent,
|
|
577
610
|
thumb_media_id: thumbMediaId,
|
|
578
611
|
author,
|
|
579
612
|
article_type: "newspic",
|
|
@@ -862,7 +895,21 @@ async function extractAndCleanImages(body) {
|
|
|
862
895
|
parent.remove();
|
|
863
896
|
}
|
|
864
897
|
}
|
|
865
|
-
|
|
898
|
+
for (const child of Array.from(wenyan.children)) {
|
|
899
|
+
if (!child.textContent?.trim()) {
|
|
900
|
+
child.remove();
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
const paragraphs = Array.from(wenyan.querySelectorAll("p"));
|
|
904
|
+
for (const p of paragraphs) {
|
|
905
|
+
const frag = document.createDocumentFragment();
|
|
906
|
+
while (p.firstChild) {
|
|
907
|
+
frag.appendChild(p.firstChild);
|
|
908
|
+
}
|
|
909
|
+
frag.appendChild(document.createElement("br"));
|
|
910
|
+
p.parentNode?.replaceChild(frag, p);
|
|
911
|
+
}
|
|
912
|
+
return { imagePaths, cleanedHtml: wenyan.innerHTML.trim() };
|
|
866
913
|
}
|
|
867
914
|
const nodeMermaidRenderer = createNodeMermaidRenderer();
|
|
868
915
|
const wenyanCoreInstance = await createWenyanCore({
|
package/package.json
CHANGED