@lyhue1991/wxgzh 0.1.3 → 0.2.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
@@ -1,6 +1,6 @@
1
- # wxgzh
1
+ # wxgzh,这个SKILL打通了AI写公众号文章的最后一公里
2
2
 
3
- 一个面向微信公众号的 SKILL,基于 NodeJS CLI.
3
+ 这是一个面向微信公众号的 SKILL,基于 NodeJS CLI.
4
4
 
5
5
  把 Markdown 文章处理成适合公众号发布的 HTML,上传图片,生成封面,并提交到公众号草稿箱。
6
6
 
@@ -10,6 +10,12 @@
10
10
  npx skills add lyhue1991/wxgzh
11
11
  ```
12
12
 
13
+ 效果演示
14
+
15
+ ![](wxgzh_shot1.png)
16
+
17
+ ![](wxgzh_shot2.png)
18
+
13
19
 
14
20
  ## 一、功能概述
15
21
 
@@ -409,6 +415,7 @@ enableComment: true
409
415
 
410
416
  ```bash
411
417
  wxgzh --help
418
+ wxgzh --version
412
419
  wxgzh article.md
413
420
  wxgzh config --list
414
421
  wxgzh config --list-themes
@@ -418,4 +425,3 @@ wxgzh cover --title "我的文章" --to .wxgzh/cover.jpg
418
425
  wxgzh publish --article .wxgzh/article.html --cover .wxgzh/cover.jpg
419
426
  ```
420
427
 
421
-
package/dist/cli/index.js CHANGED
@@ -36,6 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
36
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
+ const node_fs_1 = require("node:fs");
39
40
  const promises_1 = require("node:fs/promises");
40
41
  const node_path_1 = __importDefault(require("node:path"));
41
42
  const commander_1 = require("commander");
@@ -65,6 +66,14 @@ const EXAMPLES_TEXT = [
65
66
  ' $ wxgzh cover --title "我的文章" --to .wxgzh/cover.jpg',
66
67
  ' $ wxgzh publish --article .wxgzh/article.html --cover .wxgzh/cover.jpg'
67
68
  ].join('\n');
69
+ function getCliVersion() {
70
+ const packageJsonPath = node_path_1.default.resolve(__dirname, '../../package.json');
71
+ const packageJson = JSON.parse((0, node_fs_1.readFileSync)(packageJsonPath, 'utf8'));
72
+ if (typeof packageJson.version !== 'string' || packageJson.version.trim() === '') {
73
+ throw new Error('读取版本号失败: package.json 中缺少有效的 version 字段');
74
+ }
75
+ return packageJson.version;
76
+ }
68
77
  function resolveCoverValue(value) {
69
78
  return typeof value === 'string' ? value : undefined;
70
79
  }
@@ -123,6 +132,7 @@ async function main() {
123
132
  .description('把 Markdown 文章处理成微信公众号草稿,支持主题、图片修复、封面生成与一键发布')
124
133
  .usage('[article.md] [options]')
125
134
  .helpOption('-h, --help', '查看帮助')
135
+ .version(getCliVersion(), '-V, --version', '查看版本')
126
136
  .showHelpAfterError('(使用 --help 查看完整帮助)')
127
137
  .argument('[article]', 'Markdown 文件路径;传入后会自动执行 md2html -> fix -> cover -> publish')
128
138
  .option('--theme <theme>', `指定主题名,可用值:${(0, themes_1.listAvailableThemes)().join(', ')}`)
@@ -260,6 +260,32 @@ section.tbl-wrapper a {
260
260
  text-decoration: none !important;
261
261
  }
262
262
  `;
263
+ const WECHAT_CODE_BLOCK_INLINE_CSS = `
264
+ pre {
265
+ overflow-x: auto !important;
266
+ overflow-y: hidden !important;
267
+ -webkit-overflow-scrolling: touch;
268
+ }
269
+
270
+ pre code.hljs,
271
+ pre code[class*="language-"] {
272
+ display: block !important;
273
+ width: max-content !important;
274
+ min-width: 100% !important;
275
+ box-sizing: border-box !important;
276
+ white-space: nowrap !important;
277
+ word-break: normal !important;
278
+ overflow-wrap: normal !important;
279
+ overflow: visible !important;
280
+ }
281
+
282
+ pre code.hljs *,
283
+ pre code[class*="language-"] * {
284
+ white-space: inherit !important;
285
+ word-break: inherit !important;
286
+ overflow-wrap: inherit !important;
287
+ }
288
+ `;
263
289
  const WECHAT_MATH_INLINE_CSS = `
264
290
  span.math-inline {
265
291
  display: inline-block !important;
@@ -587,51 +613,59 @@ function createDocumentHtml(content, metadata) {
587
613
  ].join('\n');
588
614
  }
589
615
  function setInlineStyleProperty(existingStyle, property, value) {
590
- const entries = new Map();
591
- if (existingStyle) {
592
- for (const declaration of existingStyle.split(';')) {
593
- const trimmed = declaration.trim();
594
- if (!trimmed) {
595
- continue;
596
- }
597
- const separatorIndex = trimmed.indexOf(':');
598
- if (separatorIndex === -1) {
599
- continue;
600
- }
601
- const name = trimmed.slice(0, separatorIndex).trim();
602
- const declarationValue = trimmed.slice(separatorIndex + 1).trim();
603
- if (!name || !declarationValue) {
604
- continue;
605
- }
606
- entries.set(name, declarationValue);
607
- }
608
- }
616
+ const entries = parseInlineStyle(existingStyle);
609
617
  entries.set(property, value);
610
- return Array.from(entries.entries())
611
- .map(([name, declarationValue]) => `${name}: ${declarationValue}`)
612
- .join('; ');
618
+ return stringifyInlineStyle(entries) ?? '';
613
619
  }
614
620
  function removeInlineStyleProperties(existingStyle, properties) {
621
+ const entries = parseInlineStyle(existingStyle);
622
+ const propertiesToRemove = new Set(properties.map((property) => property.trim().toLowerCase()).filter(Boolean));
623
+ for (const name of Array.from(entries.keys())) {
624
+ if (propertiesToRemove.has(name.toLowerCase())) {
625
+ entries.delete(name);
626
+ }
627
+ }
628
+ return stringifyInlineStyle(entries);
629
+ }
630
+ function parseInlineStyle(existingStyle) {
631
+ const entries = new Map();
615
632
  if (!existingStyle) {
616
- return undefined;
633
+ return entries;
617
634
  }
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(':');
635
+ for (const declaration of existingStyle.split(';')) {
636
+ const trimmed = declaration.trim();
637
+ if (!trimmed) {
638
+ continue;
639
+ }
640
+ const separatorIndex = trimmed.indexOf(':');
625
641
  if (separatorIndex === -1) {
626
- return true;
642
+ continue;
627
643
  }
628
- const name = declaration.slice(0, separatorIndex).trim().toLowerCase();
629
- return !propertiesToRemove.has(name);
630
- });
631
- if (declarations.length === 0) {
644
+ const name = trimmed.slice(0, separatorIndex).trim();
645
+ const declarationValue = trimmed.slice(separatorIndex + 1).trim();
646
+ if (!name || !declarationValue) {
647
+ continue;
648
+ }
649
+ entries.set(name, declarationValue);
650
+ }
651
+ return entries;
652
+ }
653
+ function stringifyInlineStyle(entries) {
654
+ if (entries.size === 0) {
632
655
  return undefined;
633
656
  }
634
- return declarations.join('; ');
657
+ return Array.from(entries.entries())
658
+ .map(([name, declarationValue]) => `${name}: ${declarationValue}`)
659
+ .join('; ');
660
+ }
661
+ function getInlineStyleProperty(existingStyle, property) {
662
+ const entries = parseInlineStyle(existingStyle);
663
+ for (const [name, declarationValue] of entries.entries()) {
664
+ if (name.trim().toLowerCase() === property.trim().toLowerCase()) {
665
+ return declarationValue;
666
+ }
667
+ }
668
+ return undefined;
635
669
  }
636
670
  function normalizeInlinedListStyles($) {
637
671
  $('ol').each((_, element) => {
@@ -677,6 +711,95 @@ function removeWechatUnsafeListWhitespace($) {
677
711
  });
678
712
  });
679
713
  }
714
+ function normalizeWechatCodeBlocks($) {
715
+ const backgroundStyleProperties = [
716
+ 'background',
717
+ 'background-color',
718
+ 'background-image',
719
+ 'background-repeat',
720
+ 'background-position',
721
+ 'background-size'
722
+ ];
723
+ const codeStyleProperties = [
724
+ ...backgroundStyleProperties,
725
+ 'border',
726
+ 'border-top',
727
+ 'border-right',
728
+ 'border-bottom',
729
+ 'border-left',
730
+ 'border-color',
731
+ 'border-style',
732
+ 'border-width',
733
+ 'border-radius',
734
+ 'box-shadow',
735
+ 'padding',
736
+ 'padding-top',
737
+ 'padding-right',
738
+ 'padding-bottom',
739
+ 'padding-left'
740
+ ];
741
+ const wrapperStyleProperties = ['margin', 'margin-top', 'margin-right', 'margin-bottom', 'margin-left'];
742
+ $('pre').each((_, element) => {
743
+ const $pre = $(element);
744
+ const $code = $pre.children('code').first();
745
+ if ($code.length === 0) {
746
+ return;
747
+ }
748
+ if (!$pre.parent().is('section.code-block-wrapper')) {
749
+ $pre.wrap('<section class="code-block-wrapper"></section>');
750
+ }
751
+ const $wrapper = $pre.parent();
752
+ const codeStyle = $code.attr('style');
753
+ const preStyle = $pre.attr('style');
754
+ for (const property of wrapperStyleProperties) {
755
+ const value = getInlineStyleProperty(codeStyle, property) ?? getInlineStyleProperty(preStyle, property);
756
+ if (!value) {
757
+ continue;
758
+ }
759
+ $wrapper.attr('style', setInlineStyleProperty($wrapper.attr('style'), property, value));
760
+ }
761
+ $wrapper.attr('style', setInlineStyleProperty($wrapper.attr('style'), 'display', 'block !important'));
762
+ $wrapper.attr('style', setInlineStyleProperty($wrapper.attr('style'), 'overflow-x', 'auto !important'));
763
+ $wrapper.attr('style', setInlineStyleProperty($wrapper.attr('style'), 'overflow-y', 'hidden !important'));
764
+ $wrapper.attr('style', setInlineStyleProperty($wrapper.attr('style'), '-webkit-overflow-scrolling', 'touch'));
765
+ for (const property of codeStyleProperties) {
766
+ const value = getInlineStyleProperty(codeStyle, property);
767
+ if (!value) {
768
+ continue;
769
+ }
770
+ $pre.attr('style', setInlineStyleProperty($pre.attr('style'), property, value));
771
+ }
772
+ $pre.attr('style', removeInlineStyleProperties($pre.attr('style'), wrapperStyleProperties));
773
+ $pre.attr('style', setInlineStyleProperty($pre.attr('style'), 'display', 'inline-block !important'));
774
+ $pre.attr('style', setInlineStyleProperty($pre.attr('style'), 'width', 'max-content !important'));
775
+ $pre.attr('style', setInlineStyleProperty($pre.attr('style'), 'min-width', '100% !important'));
776
+ $pre.attr('style', setInlineStyleProperty($pre.attr('style'), 'max-width', 'none !important'));
777
+ $pre.attr('style', setInlineStyleProperty($pre.attr('style'), 'box-sizing', 'border-box !important'));
778
+ $pre.attr('style', setInlineStyleProperty($pre.attr('style'), 'white-space', 'normal !important'));
779
+ $pre.attr('style', setInlineStyleProperty($pre.attr('style'), 'word-break', 'normal !important'));
780
+ $pre.attr('style', setInlineStyleProperty($pre.attr('style'), 'overflow', 'visible !important'));
781
+ $pre.attr('style', setInlineStyleProperty($pre.attr('style'), 'vertical-align', 'top !important'));
782
+ $code.attr('style', removeInlineStyleProperties($code.attr('style'), [...codeStyleProperties, ...wrapperStyleProperties]));
783
+ $code.attr('style', setInlineStyleProperty($code.attr('style'), 'display', 'inline-block !important'));
784
+ $code.attr('style', setInlineStyleProperty($code.attr('style'), 'width', 'max-content !important'));
785
+ $code.attr('style', setInlineStyleProperty($code.attr('style'), 'min-width', '100% !important'));
786
+ $code.attr('style', setInlineStyleProperty($code.attr('style'), 'max-width', 'none !important'));
787
+ $code.attr('style', setInlineStyleProperty($code.attr('style'), 'white-space', 'nowrap !important'));
788
+ $code.attr('style', setInlineStyleProperty($code.attr('style'), 'word-break', 'normal !important'));
789
+ $code.attr('style', setInlineStyleProperty($code.attr('style'), 'overflow-wrap', 'normal !important'));
790
+ $code.attr('style', setInlineStyleProperty($code.attr('style'), 'border', '0 !important'));
791
+ $code.attr('style', setInlineStyleProperty($code.attr('style'), 'border-radius', '0 !important'));
792
+ $code.attr('style', setInlineStyleProperty($code.attr('style'), 'padding', '0 !important'));
793
+ $code.attr('style', setInlineStyleProperty($code.attr('style'), 'margin', '0 !important'));
794
+ for (const property of backgroundStyleProperties) {
795
+ const value = getInlineStyleProperty(codeStyle, property) ?? getInlineStyleProperty(preStyle, property);
796
+ if (!value) {
797
+ continue;
798
+ }
799
+ $code.attr('style', setInlineStyleProperty($code.attr('style'), property, value));
800
+ }
801
+ });
802
+ }
680
803
  function cleanInlinedHtml(html) {
681
804
  const $ = cheerio.load(html);
682
805
  $('pre > code').each((_, element) => {
@@ -685,6 +808,7 @@ function cleanInlinedHtml(html) {
685
808
  });
686
809
  normalizeInlinedListStyles($);
687
810
  removeWechatUnsafeListWhitespace($);
811
+ normalizeWechatCodeBlocks($);
688
812
  const $article = $('body > #write > article').first();
689
813
  const $firstChild = $article.children().first();
690
814
  if ($firstChild.length > 0) {
@@ -739,7 +863,7 @@ async function renderMarkdownToHtml(body, metadata) {
739
863
  const articleHtml = $('article').html() ?? '';
740
864
  const baseHtml = createDocumentHtml(articleHtml, metadata);
741
865
  const inlinedHtml = (0, css_inline_1.inline)(baseHtml, {
742
- extraCss: `${themeCss}\n${HIGHLIGHT_INLINE_CSS}\n${WECHAT_BLOCKQUOTE_INLINE_CSS}\n${WECHAT_TABLE_INLINE_CSS}\n${WECHAT_MATH_INLINE_CSS}\n${WECHAT_LAYOUT_INLINE_CSS}\n${customCss}`,
866
+ extraCss: `${themeCss}\n${HIGHLIGHT_INLINE_CSS}\n${WECHAT_BLOCKQUOTE_INLINE_CSS}\n${WECHAT_TABLE_INLINE_CSS}\n${WECHAT_CODE_BLOCK_INLINE_CSS}\n${WECHAT_MATH_INLINE_CSS}\n${WECHAT_LAYOUT_INLINE_CSS}\n${customCss}`,
743
867
  keepAtRules: true,
744
868
  applyWidthAttributes: false,
745
869
  applyHeightAttributes: false
@@ -7,12 +7,17 @@ exports.listCoverPresets = listCoverPresets;
7
7
  exports.createCover = createCover;
8
8
  const node_fs_1 = require("node:fs");
9
9
  const node_path_1 = __importDefault(require("node:path"));
10
+ const opentype_js_1 = require("opentype.js");
10
11
  const sharp_1 = __importDefault(require("sharp"));
11
12
  const BUILTIN_BACKGROUND_DIR = node_path_1.default.resolve(__dirname, '../../assets/backgrounds');
13
+ const COVER_TITLE_FONT_PATH = node_path_1.default.resolve(__dirname, '../../assets/fonts/仓耳小丸子.ttf');
12
14
  const FALLBACK_GRADIENT = {
13
15
  start: '#e8f3ef',
14
16
  end: '#c9ded6'
15
17
  };
18
+ const DEFAULT_TITLE_LINE_LENGTH = 15;
19
+ const COVER_TEXT_COLOR = '#000000b8';
20
+ let cachedCoverFont;
16
21
  function escapeXml(value) {
17
22
  return value
18
23
  .replace(/&/g, '&amp;')
@@ -21,8 +26,16 @@ function escapeXml(value) {
21
26
  .replace(/"/g, '&quot;')
22
27
  .replace(/'/g, '&apos;');
23
28
  }
24
- function escapeAttribute(value) {
25
- return escapeXml(value);
29
+ function getCoverFont() {
30
+ if (!(0, node_fs_1.existsSync)(COVER_TITLE_FONT_PATH)) {
31
+ return undefined;
32
+ }
33
+ if (!cachedCoverFont) {
34
+ const fontBuffer = (0, node_fs_1.readFileSync)(COVER_TITLE_FONT_PATH);
35
+ const arrayBuffer = fontBuffer.buffer.slice(fontBuffer.byteOffset, fontBuffer.byteOffset + fontBuffer.byteLength);
36
+ cachedCoverFont = (0, opentype_js_1.parse)(arrayBuffer);
37
+ }
38
+ return cachedCoverFont;
26
39
  }
27
40
  function readFiles(dirPath, pattern) {
28
41
  try {
@@ -79,29 +92,66 @@ function buildGradientBackground(width, height) {
79
92
  </svg>`;
80
93
  return Buffer.from(svg);
81
94
  }
95
+ function getFontLineMetrics(font, fontSize) {
96
+ const unitsPerEm = font.unitsPerEm || 1000;
97
+ const ascender = Math.round(font.ascender / unitsPerEm * fontSize);
98
+ const descender = Math.abs(Math.round(font.descender / unitsPerEm * fontSize));
99
+ return {
100
+ ascender,
101
+ lineHeight: Math.max(Math.round((ascender + descender) * 1.08), fontSize)
102
+ };
103
+ }
104
+ function buildFontPath(font, text, fontSize) {
105
+ const pathObject = font.getPath(text, 0, 0, fontSize);
106
+ const box = pathObject.getBoundingBox();
107
+ return {
108
+ pathData: pathObject.toPathData(3),
109
+ width: Math.max(box.x2 - box.x1, 0),
110
+ x1: box.x1,
111
+ x2: box.x2
112
+ };
113
+ }
114
+ function buildPathElement(pathData, x, y) {
115
+ return `<path d="${pathData}" transform="translate(${x.toFixed(3)} ${y.toFixed(3)})" fill="${COVER_TEXT_COLOR}" />`;
116
+ }
117
+ function buildTitlePath(font, line, index, canvasWidth, titleTop, lineHeight, ascender, fontSize) {
118
+ const glyphPath = buildFontPath(font, line, fontSize);
119
+ const baselineY = titleTop + index * lineHeight + ascender;
120
+ const startX = (canvasWidth - glyphPath.width) / 2 - glyphPath.x1;
121
+ return {
122
+ right: startX + glyphPath.x2,
123
+ path: buildPathElement(glyphPath.pathData, startX, baselineY)
124
+ };
125
+ }
126
+ function buildAuthorPath(font, text, fontSize, right, baselineY) {
127
+ const glyphPath = buildFontPath(font, text, fontSize);
128
+ const startX = right - glyphPath.x2;
129
+ return buildPathElement(glyphPath.pathData, startX, baselineY);
130
+ }
82
131
  function buildTextSvg(params) {
83
- const lines = buildTitleLines(params.title, 10);
132
+ const font = getCoverFont();
133
+ if (!font) {
134
+ return undefined;
135
+ }
136
+ const lines = buildTitleLines(params.title, DEFAULT_TITLE_LINE_LENGTH);
84
137
  const sizes = estimateFontSize(params.width, lines);
85
- const lineHeight = Math.round(sizes.title * 1.15);
138
+ const titleMetrics = getFontLineMetrics(font, sizes.title);
139
+ const authorMetrics = getFontLineMetrics(font, sizes.author);
140
+ const lineHeight = titleMetrics.lineHeight;
86
141
  const titleBlockHeight = lineHeight * lines.length;
87
142
  const titleTop = Math.round((params.height - titleBlockHeight) / 2);
88
- const fontFamily = '"PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif';
89
- const titleWidth = sizes.longestChars * sizes.title;
90
- const titleRight = Math.round(params.width / 2 + titleWidth / 2);
91
- const authorY = titleTop + titleBlockHeight + sizes.author + Math.round(sizes.author * 0.4);
92
- const titleText = lines
93
- .map((line, index) => {
94
- const y = titleTop + sizes.title + index * lineHeight;
95
- return `<text x="50%" y="${y}" text-anchor="middle" font-size="${sizes.title}" font-family="${escapeAttribute(fontFamily)}" fill="rgba(0,0,0,0.72)">${escapeXml(line)}</text>`;
96
- })
97
- .join('');
98
- const authorText = params.author.trim()
99
- ? `<text x="${titleRight}" y="${authorY}" text-anchor="end" font-size="${sizes.author}" font-family="${escapeAttribute(fontFamily)}" fill="rgba(0,0,0,0.72)">${escapeXml(params.author.trim())}</text>`
143
+ const titlePaths = lines.map((line, index) => buildTitlePath(font, line, index, params.width, titleTop, lineHeight, titleMetrics.ascender, sizes.title));
144
+ const titleRight = titlePaths.length > 0
145
+ ? Math.max(...titlePaths.map((item) => item.right))
146
+ : params.width / 2;
147
+ const authorText = params.author.trim();
148
+ const authorPath = authorText
149
+ ? buildAuthorPath(font, authorText, sizes.author, titleRight, titleTop + titleBlockHeight + Math.round(sizes.author * 0.4) + authorMetrics.ascender)
100
150
  : '';
101
151
  const svg = `
102
152
  <svg width="${params.width}" height="${params.height}" viewBox="0 0 ${params.width} ${params.height}" xmlns="http://www.w3.org/2000/svg">
103
- ${titleText}
104
- ${authorText}
153
+ ${titlePaths.map((item) => item.path).join('')}
154
+ ${authorPath}
105
155
  </svg>`;
106
156
  return Buffer.from(svg);
107
157
  }
@@ -137,8 +187,13 @@ async function createCover(options) {
137
187
  const background = backgroundPath
138
188
  ? (0, sharp_1.default)(backgroundPath).resize(width, height, { fit: 'cover' })
139
189
  : (0, sharp_1.default)(buildGradientBackground(width, height));
140
- await background
141
- .composite([
190
+ const textSvg = buildTextSvg({
191
+ title: options.title,
192
+ author,
193
+ width,
194
+ height
195
+ });
196
+ const overlays = [
142
197
  {
143
198
  input: {
144
199
  create: {
@@ -148,16 +203,13 @@ async function createCover(options) {
148
203
  background: { r: 255, g: 255, b: 255, alpha: 0.58 }
149
204
  }
150
205
  }
151
- },
152
- {
153
- input: buildTextSvg({
154
- title: options.title,
155
- author,
156
- width,
157
- height
158
- })
159
206
  }
160
- ])
207
+ ];
208
+ if (textSvg) {
209
+ overlays.push({ input: textSvg });
210
+ }
211
+ await background
212
+ .composite(overlays)
161
213
  .jpeg({ quality: 92 })
162
214
  .toFile(outputPath);
163
215
  return outputPath;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lyhue1991/wxgzh",
3
- "version": "0.1.3",
3
+ "version": "0.2.0",
4
4
  "description": "Markdown to WeChat Official Account draft CLI",
5
5
  "license": "MIT",
6
6
  "main": "dist/cli/index.js",
@@ -37,6 +37,7 @@
37
37
  "markdown-it": "^14.1.0",
38
38
  "markdown-it-mathjax3": "^5.2.0",
39
39
  "mathjax": "^4.1.1",
40
+ "opentype.js": "^1.3.4",
40
41
  "sharp": "^0.33.5"
41
42
  },
42
43
  "devDependencies": {
package/spec.md CHANGED
@@ -482,6 +482,7 @@ POST https://api.weixin.qq.com/cgi-bin/draft/add
482
482
  npm run typecheck
483
483
  npm run build
484
484
  node dist/cli/index.js --help
485
+ node dist/cli/index.js --version
485
486
  node dist/cli/index.js config --help
486
487
  node dist/cli/index.js md2html --help
487
488
  node dist/cli/index.js fix --help