@lyhue1991/wxgzh 0.1.2 → 0.1.3
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 +11 -2
- package/dist/core/converter.js +99 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
# wxgzh
|
|
2
2
|
|
|
3
|
-
一个面向微信公众号的
|
|
3
|
+
一个面向微信公众号的 SKILL,基于 NodeJS CLI.
|
|
4
|
+
|
|
5
|
+
把 Markdown 文章处理成适合公众号发布的 HTML,上传图片,生成封面,并提交到公众号草稿箱。
|
|
6
|
+
|
|
7
|
+
<mark>安装此SKILL</mark>
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
npx skills add lyhue1991/wxgzh
|
|
11
|
+
```
|
|
12
|
+
|
|
4
13
|
|
|
5
14
|
## 一、功能概述
|
|
6
15
|
|
|
@@ -12,7 +21,7 @@
|
|
|
12
21
|
- 支持主题样式、自定义 CSS、自动摘要、封面图生成。
|
|
13
22
|
- 也支持分步执行,便于你单独检查 HTML、封面或发布结果。
|
|
14
23
|
|
|
15
|
-
|
|
24
|
+
**手动操作方法**
|
|
16
25
|
|
|
17
26
|
* step1: 一键安装(要求:Node.js >= 18)
|
|
18
27
|
|
package/dist/core/converter.js
CHANGED
|
@@ -371,6 +371,9 @@ function escapeHtmlAttribute(value) {
|
|
|
371
371
|
function isTextNodeEmpty(node) {
|
|
372
372
|
return node.type === 'text' && !node.data.trim();
|
|
373
373
|
}
|
|
374
|
+
function isTagNode(node, tagName) {
|
|
375
|
+
return node.type === 'tag' && node.tagName.toLowerCase() === tagName;
|
|
376
|
+
}
|
|
374
377
|
function isStandaloneImageParagraph($paragraph) {
|
|
375
378
|
const nodes = $paragraph.contents().toArray().filter((node) => !isTextNodeEmpty(node));
|
|
376
379
|
return nodes.every((node) => node.type === 'tag' && ['img', 'br'].includes(node.tagName.toLowerCase()));
|
|
@@ -410,10 +413,33 @@ async function loadCustomCss() {
|
|
|
410
413
|
function normalizeListItems($) {
|
|
411
414
|
$('li').each((_, element) => {
|
|
412
415
|
const $item = $(element);
|
|
413
|
-
const
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
416
|
+
const initialChildren = $item.contents().toArray();
|
|
417
|
+
const hasParagraphChild = initialChildren.some((node) => isTagNode(node, 'p'));
|
|
418
|
+
if (hasParagraphChild) {
|
|
419
|
+
const fragments = [];
|
|
420
|
+
let previousWasParagraph = false;
|
|
421
|
+
for (const node of initialChildren) {
|
|
422
|
+
if (isTextNodeEmpty(node)) {
|
|
423
|
+
continue;
|
|
424
|
+
}
|
|
425
|
+
if (isTagNode(node, 'p')) {
|
|
426
|
+
const paragraphHtml = renderSoftBreakFriendlyHtml(node.children ?? []);
|
|
427
|
+
if (!paragraphHtml.trim()) {
|
|
428
|
+
continue;
|
|
429
|
+
}
|
|
430
|
+
if (previousWasParagraph) {
|
|
431
|
+
fragments.push('<br>');
|
|
432
|
+
}
|
|
433
|
+
fragments.push(paragraphHtml);
|
|
434
|
+
previousWasParagraph = true;
|
|
435
|
+
continue;
|
|
436
|
+
}
|
|
437
|
+
fragments.push(renderSoftBreakFriendlyNode(node, true));
|
|
438
|
+
previousWasParagraph = false;
|
|
439
|
+
}
|
|
440
|
+
$item.html(fragments.join(''));
|
|
441
|
+
}
|
|
442
|
+
const childElements = $item.contents().toArray().filter((node) => !isTextNodeEmpty(node));
|
|
417
443
|
if (childElements.length === 0) {
|
|
418
444
|
return;
|
|
419
445
|
}
|
|
@@ -425,7 +451,7 @@ function normalizeListItems($) {
|
|
|
425
451
|
return ['p', 'ul', 'ol', 'section', 'blockquote', 'pre', 'table'].includes(tagName);
|
|
426
452
|
});
|
|
427
453
|
const firstChild = childElements[0];
|
|
428
|
-
if (!hasBlockChild && firstChild
|
|
454
|
+
if (!hasBlockChild && isTagNode(firstChild, 'span') && childElements.length === 1) {
|
|
429
455
|
return;
|
|
430
456
|
}
|
|
431
457
|
if (hasBlockChild) {
|
|
@@ -585,12 +611,80 @@ function setInlineStyleProperty(existingStyle, property, value) {
|
|
|
585
611
|
.map(([name, declarationValue]) => `${name}: ${declarationValue}`)
|
|
586
612
|
.join('; ');
|
|
587
613
|
}
|
|
614
|
+
function removeInlineStyleProperties(existingStyle, properties) {
|
|
615
|
+
if (!existingStyle) {
|
|
616
|
+
return undefined;
|
|
617
|
+
}
|
|
618
|
+
const propertiesToRemove = new Set(properties.map((property) => property.trim().toLowerCase()).filter(Boolean));
|
|
619
|
+
const declarations = existingStyle
|
|
620
|
+
.split(';')
|
|
621
|
+
.map((declaration) => declaration.trim())
|
|
622
|
+
.filter(Boolean)
|
|
623
|
+
.filter((declaration) => {
|
|
624
|
+
const separatorIndex = declaration.indexOf(':');
|
|
625
|
+
if (separatorIndex === -1) {
|
|
626
|
+
return true;
|
|
627
|
+
}
|
|
628
|
+
const name = declaration.slice(0, separatorIndex).trim().toLowerCase();
|
|
629
|
+
return !propertiesToRemove.has(name);
|
|
630
|
+
});
|
|
631
|
+
if (declarations.length === 0) {
|
|
632
|
+
return undefined;
|
|
633
|
+
}
|
|
634
|
+
return declarations.join('; ');
|
|
635
|
+
}
|
|
636
|
+
function normalizeInlinedListStyles($) {
|
|
637
|
+
$('ol').each((_, element) => {
|
|
638
|
+
const $list = $(element);
|
|
639
|
+
const depth = $list.parents('ol').length;
|
|
640
|
+
const cleanedStyle = removeInlineStyleProperties($list.attr('style'), ['list-style', 'list-style-type', 'list-style-position']);
|
|
641
|
+
const listType = depth === 0 ? '1' : depth === 1 ? 'a' : 'i';
|
|
642
|
+
if (cleanedStyle) {
|
|
643
|
+
$list.attr('style', cleanedStyle);
|
|
644
|
+
}
|
|
645
|
+
else {
|
|
646
|
+
$list.removeAttr('style');
|
|
647
|
+
}
|
|
648
|
+
$list.attr('type', listType);
|
|
649
|
+
});
|
|
650
|
+
$('ul').each((_, element) => {
|
|
651
|
+
const $list = $(element);
|
|
652
|
+
const cleanedStyle = removeInlineStyleProperties($list.attr('style'), ['list-style', 'list-style-type', 'list-style-position']);
|
|
653
|
+
if (cleanedStyle) {
|
|
654
|
+
$list.attr('style', cleanedStyle);
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
$list.removeAttr('style');
|
|
658
|
+
});
|
|
659
|
+
$('ol > li, ul > li').each((_, element) => {
|
|
660
|
+
const $item = $(element);
|
|
661
|
+
const cleanedStyle = removeInlineStyleProperties($item.attr('style'), ['list-style', 'list-style-type', 'list-style-position']);
|
|
662
|
+
if (cleanedStyle) {
|
|
663
|
+
$item.attr('style', cleanedStyle);
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
$item.removeAttr('style');
|
|
667
|
+
});
|
|
668
|
+
}
|
|
669
|
+
function removeWechatUnsafeListWhitespace($) {
|
|
670
|
+
$('ol, ul').each((_, element) => {
|
|
671
|
+
$(element)
|
|
672
|
+
.contents()
|
|
673
|
+
.toArray()
|
|
674
|
+
.filter((node) => isTextNodeEmpty(node))
|
|
675
|
+
.forEach((node) => {
|
|
676
|
+
$(node).remove();
|
|
677
|
+
});
|
|
678
|
+
});
|
|
679
|
+
}
|
|
588
680
|
function cleanInlinedHtml(html) {
|
|
589
681
|
const $ = cheerio.load(html);
|
|
590
682
|
$('pre > code').each((_, element) => {
|
|
591
683
|
const $element = $(element);
|
|
592
684
|
$element.html(renderWechatFriendlyCodeHtml($element.contents().toArray()));
|
|
593
685
|
});
|
|
686
|
+
normalizeInlinedListStyles($);
|
|
687
|
+
removeWechatUnsafeListWhitespace($);
|
|
594
688
|
const $article = $('body > #write > article').first();
|
|
595
689
|
const $firstChild = $article.children().first();
|
|
596
690
|
if ($firstChild.length > 0) {
|