@wenyan-md/core 1.0.13 → 1.0.15
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 +95 -133
- package/dist/browser/wenyan-core.js +51 -51
- package/dist/core.js +430 -278
- package/dist/hltheme.js +39 -22
- package/dist/math/wenyan-math.js +9 -40
- package/dist/publish.js +166 -346
- package/dist/styles/wenyan-styles.js +19 -19
- package/dist/theme.js +388 -34
- package/dist/types/core.d.ts +17 -0
- package/dist/types/runtimeEnv.d.ts +6 -0
- package/dist/types/utils.d.ts +1 -0
- package/dist/utils-YieK94fG.js +9 -0
- package/dist/wrapper.js +15 -10
- package/package.json +8 -5
- package/dist/atom-one-dark.min-hABhDLRj.js +0 -4
- package/dist/atom-one-light.min-CwiVhPEv.js +0 -4
- package/dist/default-D-dyLptq.js +0 -184
- package/dist/dracula.min-OeyC4Nkp.js +0 -11
- package/dist/github-dark.min-DOlD5Ewr.js +0 -13
- package/dist/github.min-BZ2GvPsn.js +0 -14
- package/dist/juejin_default-13x4lhT9.js +0 -134
- package/dist/lapis-BUlsdDG6.js +0 -194
- package/dist/maize-DFW0x6O3.js +0 -194
- package/dist/medium_default-Zyy9RBMn.js +0 -135
- package/dist/monokai.min-CH2iHqHz.js +0 -4
- package/dist/orangeheart-Da7uQj8U.js +0 -184
- package/dist/phycat-CATVZm-R.js +0 -338
- package/dist/pie-Q9UMu3CB.js +0 -240
- package/dist/purple-Da1-Vfos.js +0 -183
- package/dist/rainbow-Bv0kNhYA.js +0 -168
- package/dist/solarized-dark.min-BxbYljx4.js +0 -11
- package/dist/solarized-light.min-Bb25xgOC.js +0 -11
- package/dist/toutiao_default-CLGOaI2x.js +0 -145
- package/dist/xcode.min-CG-lWQgl.js +0 -4
- package/dist/zhihu_default-Cz8bIkGO.js +0 -133
package/dist/publish.js
CHANGED
|
@@ -1,372 +1,192 @@
|
|
|
1
|
-
import { JSDOM
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
async function
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
async function* D(t) {
|
|
26
|
-
const e = t.getReader();
|
|
27
|
-
for (; ; ) {
|
|
28
|
-
const { done: r, value: a } = await e.read();
|
|
29
|
-
if (r)
|
|
30
|
-
break;
|
|
31
|
-
yield a;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
async function* x(t) {
|
|
35
|
-
for await (const e of t)
|
|
36
|
-
yield* z(e);
|
|
37
|
-
}
|
|
38
|
-
var H = (t) => {
|
|
39
|
-
if (C(t))
|
|
40
|
-
return x(t);
|
|
41
|
-
if (h(t.getReader))
|
|
42
|
-
return x(D(t));
|
|
43
|
-
throw new TypeError(
|
|
44
|
-
"Unsupported data source: Expected either ReadableStream or async iterable."
|
|
45
|
-
);
|
|
46
|
-
};
|
|
47
|
-
async function* O(t) {
|
|
48
|
-
let e = 0;
|
|
49
|
-
for (; e !== t.size; ) {
|
|
50
|
-
const a = await t.slice(
|
|
51
|
-
e,
|
|
52
|
-
Math.min(t.size, e + A)
|
|
53
|
-
).arrayBuffer();
|
|
54
|
-
e += a.byteLength, yield new Uint8Array(a);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
async function* T(t, e = !1) {
|
|
58
|
-
for (const r of t)
|
|
59
|
-
ArrayBuffer.isView(r) ? e ? yield* z(r) : yield r : h(r.stream) ? yield* H(r.stream()) : yield* O(r);
|
|
60
|
-
}
|
|
61
|
-
function* U(t, e, r = 0, a) {
|
|
62
|
-
a ??= e;
|
|
63
|
-
let s = r < 0 ? Math.max(e + r, 0) : Math.min(r, e), i = a < 0 ? Math.max(e + a, 0) : Math.min(a, e);
|
|
64
|
-
const n = Math.max(i - s, 0);
|
|
65
|
-
let o = 0;
|
|
66
|
-
for (const d of t) {
|
|
67
|
-
if (o >= n)
|
|
68
|
-
break;
|
|
69
|
-
const c = ArrayBuffer.isView(d) ? d.byteLength : d.size;
|
|
70
|
-
if (s && c <= s)
|
|
71
|
-
s -= c, i -= c;
|
|
72
|
-
else {
|
|
73
|
-
let l;
|
|
74
|
-
ArrayBuffer.isView(d) ? (l = d.subarray(s, Math.min(c, i)), o += l.byteLength) : (l = d.slice(s, Math.min(c, i)), o += l.size), i -= c, s = 0, yield l;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
var u, k, g, V = class E {
|
|
79
|
-
/**
|
|
80
|
-
* Returns a new [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) object.
|
|
81
|
-
* The content of the blob consists of the concatenation of the values given in the parameter array.
|
|
82
|
-
*
|
|
83
|
-
* @param blobParts An `Array` strings, or [`ArrayBuffer`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer), [`ArrayBufferView`](https://developer.mozilla.org/en-US/docs/Web/API/ArrayBufferView), [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) objects, or a mix of any of such objects, that will be put inside the [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob).
|
|
84
|
-
* @param options An optional object of type `BlobPropertyBag`.
|
|
85
|
-
*/
|
|
86
|
-
constructor(e = [], r = {}) {
|
|
87
|
-
if (w(this, u, []), w(this, k, ""), w(this, g, 0), r ??= {}, typeof e != "object" || e === null)
|
|
88
|
-
throw new TypeError(
|
|
89
|
-
"Failed to construct 'Blob': The provided value cannot be converted to a sequence."
|
|
90
|
-
);
|
|
91
|
-
if (!h(e[Symbol.iterator]))
|
|
92
|
-
throw new TypeError(
|
|
93
|
-
"Failed to construct 'Blob': The object must have a callable @@iterator property."
|
|
94
|
-
);
|
|
95
|
-
if (typeof r != "object" && !h(r))
|
|
96
|
-
throw new TypeError(
|
|
97
|
-
"Failed to construct 'Blob': parameter 2 cannot convert to dictionary."
|
|
98
|
-
);
|
|
99
|
-
const a = new TextEncoder();
|
|
100
|
-
for (const i of e) {
|
|
101
|
-
let n;
|
|
102
|
-
ArrayBuffer.isView(i) ? n = new Uint8Array(i.buffer.slice(
|
|
103
|
-
i.byteOffset,
|
|
104
|
-
i.byteOffset + i.byteLength
|
|
105
|
-
)) : i instanceof ArrayBuffer ? n = new Uint8Array(i.slice(0)) : i instanceof E ? n = i : n = a.encode(String(i)), p(this, g, f(this, g) + (ArrayBuffer.isView(n) ? n.byteLength : n.size)), f(this, u).push(n);
|
|
106
|
-
}
|
|
107
|
-
const s = r.type === void 0 ? "" : String(r.type);
|
|
108
|
-
p(this, k, /^[\x20-\x7E]*$/.test(s) ? s : "");
|
|
109
|
-
}
|
|
110
|
-
static [Symbol.hasInstance](e) {
|
|
111
|
-
return !!(e && typeof e == "object" && h(e.constructor) && (h(e.stream) || h(e.arrayBuffer)) && /^(Blob|File)$/.test(e[Symbol.toStringTag]));
|
|
112
|
-
}
|
|
113
|
-
/**
|
|
114
|
-
* Returns the [`MIME type`](https://developer.mozilla.org/en-US/docs/Glossary/MIME_type) of the [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) or [`File`](https://developer.mozilla.org/en-US/docs/Web/API/File).
|
|
115
|
-
*/
|
|
116
|
-
get type() {
|
|
117
|
-
return f(this, k);
|
|
118
|
-
}
|
|
119
|
-
/**
|
|
120
|
-
* Returns the size of the [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) or [`File`](https://developer.mozilla.org/en-US/docs/Web/API/File) in bytes.
|
|
121
|
-
*/
|
|
122
|
-
get size() {
|
|
123
|
-
return f(this, g);
|
|
124
|
-
}
|
|
125
|
-
/**
|
|
126
|
-
* Creates and returns a new [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) object which contains data from a subset of the blob on which it's called.
|
|
127
|
-
*
|
|
128
|
-
* @param start An index into the Blob indicating the first byte to include in the new Blob. If you specify a negative value, it's treated as an offset from the end of the Blob toward the beginning. For example, -10 would be the 10th from last byte in the Blob. The default value is 0. If you specify a value for start that is larger than the size of the source Blob, the returned Blob has size 0 and contains no data.
|
|
129
|
-
* @param end An index into the Blob indicating the first byte that will *not* be included in the new Blob (i.e. the byte exactly at this index is not included). If you specify a negative value, it's treated as an offset from the end of the Blob toward the beginning. For example, -10 would be the 10th from last byte in the Blob. The default value is size.
|
|
130
|
-
* @param contentType The content type to assign to the new Blob; this will be the value of its type property. The default value is an empty string.
|
|
131
|
-
*/
|
|
132
|
-
slice(e, r, a) {
|
|
133
|
-
return new E(U(f(this, u), this.size, e, r), {
|
|
134
|
-
type: a
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
/**
|
|
138
|
-
* Returns a [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) that resolves with a string containing the contents of the blob, interpreted as UTF-8.
|
|
139
|
-
*/
|
|
140
|
-
async text() {
|
|
141
|
-
const e = new TextDecoder();
|
|
142
|
-
let r = "";
|
|
143
|
-
for await (const a of T(f(this, u)))
|
|
144
|
-
r += e.decode(a, { stream: !0 });
|
|
145
|
-
return r += e.decode(), r;
|
|
146
|
-
}
|
|
147
|
-
/**
|
|
148
|
-
* Returns a [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) that resolves with the contents of the blob as binary data contained in an [`ArrayBuffer`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer).
|
|
149
|
-
*/
|
|
150
|
-
async arrayBuffer() {
|
|
151
|
-
const e = new Uint8Array(this.size);
|
|
152
|
-
let r = 0;
|
|
153
|
-
for await (const a of T(f(this, u)))
|
|
154
|
-
e.set(a, r), r += a.length;
|
|
155
|
-
return e.buffer;
|
|
156
|
-
}
|
|
157
|
-
/**
|
|
158
|
-
* Returns a [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) which upon reading returns the data contained within the [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob).
|
|
159
|
-
*/
|
|
160
|
-
stream() {
|
|
161
|
-
const e = T(f(this, u), !0);
|
|
162
|
-
return new ReadableStream({
|
|
163
|
-
async pull(r) {
|
|
164
|
-
const { value: a, done: s } = await e.next();
|
|
165
|
-
if (s)
|
|
166
|
-
return queueMicrotask(() => r.close());
|
|
167
|
-
r.enqueue(a);
|
|
168
|
-
},
|
|
169
|
-
async cancel() {
|
|
170
|
-
await e.return();
|
|
171
|
-
}
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
get [Symbol.toStringTag]() {
|
|
175
|
-
return "Blob";
|
|
176
|
-
}
|
|
177
|
-
};
|
|
178
|
-
u = /* @__PURE__ */ new WeakMap();
|
|
179
|
-
k = /* @__PURE__ */ new WeakMap();
|
|
180
|
-
g = /* @__PURE__ */ new WeakMap();
|
|
181
|
-
var S = V;
|
|
182
|
-
Object.defineProperties(S.prototype, {
|
|
183
|
-
type: { enumerable: !0 },
|
|
184
|
-
size: { enumerable: !0 },
|
|
185
|
-
slice: { enumerable: !0 },
|
|
186
|
-
stream: { enumerable: !0 },
|
|
187
|
-
text: { enumerable: !0 },
|
|
188
|
-
arrayBuffer: { enumerable: !0 }
|
|
189
|
-
});
|
|
190
|
-
var M, v, G = class extends S {
|
|
191
|
-
/**
|
|
192
|
-
* Creates a new File instance.
|
|
193
|
-
*
|
|
194
|
-
* @param fileBits An `Array` strings, or [`ArrayBuffer`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer), [`ArrayBufferView`](https://developer.mozilla.org/en-US/docs/Web/API/ArrayBufferView), [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) objects, or a mix of any of such objects, that will be put inside the [`File`](https://developer.mozilla.org/en-US/docs/Web/API/File).
|
|
195
|
-
* @param name The name of the file.
|
|
196
|
-
* @param options An options object containing optional attributes for the file.
|
|
197
|
-
*/
|
|
198
|
-
constructor(t, e, r = {}) {
|
|
199
|
-
if (super(t, r), w(this, M, void 0), w(this, v, 0), arguments.length < 2)
|
|
200
|
-
throw new TypeError(
|
|
201
|
-
`Failed to construct 'File': 2 arguments required, but only ${arguments.length} present.`
|
|
202
|
-
);
|
|
203
|
-
p(this, M, String(e));
|
|
204
|
-
const a = r.lastModified === void 0 ? Date.now() : Number(r.lastModified);
|
|
205
|
-
Number.isNaN(a) || p(this, v, a);
|
|
206
|
-
}
|
|
207
|
-
static [Symbol.hasInstance](t) {
|
|
208
|
-
return t instanceof S && t[Symbol.toStringTag] === "File" && typeof t.name == "string";
|
|
209
|
-
}
|
|
210
|
-
/**
|
|
211
|
-
* Name of the file referenced by the File object.
|
|
212
|
-
*/
|
|
213
|
-
get name() {
|
|
214
|
-
return f(this, M);
|
|
215
|
-
}
|
|
216
|
-
/* c8 ignore next 3 */
|
|
217
|
-
get webkitRelativePath() {
|
|
218
|
-
return "";
|
|
219
|
-
}
|
|
220
|
-
/**
|
|
221
|
-
* The last modified date of the file as the number of milliseconds since the Unix epoch (January 1, 1970 at midnight). Files without a known last modified date return the current date.
|
|
222
|
-
*/
|
|
223
|
-
get lastModified() {
|
|
224
|
-
return f(this, v);
|
|
225
|
-
}
|
|
226
|
-
get [Symbol.toStringTag]() {
|
|
227
|
-
return "File";
|
|
228
|
-
}
|
|
229
|
-
};
|
|
230
|
-
M = /* @__PURE__ */ new WeakMap();
|
|
231
|
-
v = /* @__PURE__ */ new WeakMap();
|
|
232
|
-
var m, y, J = class q {
|
|
233
|
-
constructor(e) {
|
|
234
|
-
w(this, m, void 0), w(this, y, void 0), p(this, m, e.path), p(this, y, e.start || 0), this.name = N(f(this, m)), this.size = e.size, this.lastModified = e.lastModified;
|
|
235
|
-
}
|
|
236
|
-
slice(e, r) {
|
|
237
|
-
return new q({
|
|
238
|
-
path: f(this, m),
|
|
239
|
-
lastModified: this.lastModified,
|
|
240
|
-
start: f(this, y) + e,
|
|
241
|
-
size: r - e
|
|
242
|
-
});
|
|
243
|
-
}
|
|
244
|
-
async *stream() {
|
|
245
|
-
const { mtimeMs: e } = await F(f(this, m));
|
|
246
|
-
if (e > this.lastModified)
|
|
247
|
-
throw new DOMException(
|
|
248
|
-
"The requested file could not be read, typically due to permission problems that have occurred after a reference to a file was acquired.",
|
|
249
|
-
"NotReadableError"
|
|
250
|
-
);
|
|
251
|
-
this.size && (yield* L(f(this, m), {
|
|
252
|
-
start: f(this, y),
|
|
253
|
-
end: f(this, y) + this.size - 1
|
|
254
|
-
}));
|
|
255
|
-
}
|
|
256
|
-
get [Symbol.toStringTag]() {
|
|
257
|
-
return "File";
|
|
258
|
-
}
|
|
259
|
-
};
|
|
260
|
-
m = /* @__PURE__ */ new WeakMap();
|
|
261
|
-
y = /* @__PURE__ */ new WeakMap();
|
|
262
|
-
var K = J;
|
|
263
|
-
function X(t, { mtimeMs: e, size: r }, a, s = {}) {
|
|
264
|
-
let i;
|
|
265
|
-
P(a) ? [s, i] = [a, void 0] : i = a;
|
|
266
|
-
const n = new K({ path: t, size: r, lastModified: e });
|
|
267
|
-
return i || (i = n.name), new G([n], i, {
|
|
268
|
-
...s,
|
|
269
|
-
lastModified: n.lastModified
|
|
270
|
-
});
|
|
271
|
-
}
|
|
272
|
-
async function Z(t, e, r) {
|
|
273
|
-
const a = await F(t);
|
|
274
|
-
return X(t, a, e, r);
|
|
275
|
-
}
|
|
276
|
-
/*! Based on fetch-blob. MIT License. Jimmy Wärting <https://jimmy.warting.se/opensource> & David Frank */
|
|
277
|
-
const Q = "https://api.weixin.qq.com/cgi-bin/token", Y = "https://api.weixin.qq.com/cgi-bin/draft/add", ee = "https://api.weixin.qq.com/cgi-bin/material/add_material", te = process.env.WECHAT_APP_ID || "", re = process.env.WECHAT_APP_SECRET || "";
|
|
278
|
-
async function ae(t, e) {
|
|
279
|
-
t = t ?? te, e = e ?? re;
|
|
280
|
-
const r = await fetch(`${Q}?grant_type=client_credential&appid=${t}&secret=${e}`);
|
|
281
|
-
if (!r.ok) {
|
|
282
|
-
const a = await r.text();
|
|
283
|
-
throw new Error(`获取AccessToken失败: ${r.status} ${a}`);
|
|
284
|
-
}
|
|
285
|
-
return await r.json();
|
|
1
|
+
import { JSDOM } from "jsdom";
|
|
2
|
+
import { fileFromPath } from "formdata-node/file-from-path";
|
|
3
|
+
import { FormData, Blob } from "formdata-node";
|
|
4
|
+
import path$1 from "node:path";
|
|
5
|
+
import { stat } from "node:fs/promises";
|
|
6
|
+
import { FormDataEncoder } from "form-data-encoder";
|
|
7
|
+
import { Readable } from "node:stream";
|
|
8
|
+
import path from "path";
|
|
9
|
+
const tokenUrl = "https://api.weixin.qq.com/cgi-bin/token";
|
|
10
|
+
const publishUrl = "https://api.weixin.qq.com/cgi-bin/draft/add";
|
|
11
|
+
const uploadUrl = `https://api.weixin.qq.com/cgi-bin/material/add_material`;
|
|
12
|
+
const appIdEnv = process.env.WECHAT_APP_ID || "";
|
|
13
|
+
const appSecretEnv = process.env.WECHAT_APP_SECRET || "";
|
|
14
|
+
async function fetchAccessToken(appId, appSecret) {
|
|
15
|
+
appId = appId ?? appIdEnv;
|
|
16
|
+
appSecret = appSecret ?? appSecretEnv;
|
|
17
|
+
const response = await fetch(`${tokenUrl}?grant_type=client_credential&appid=${appId}&secret=${appSecret}`);
|
|
18
|
+
if (!response.ok) {
|
|
19
|
+
const errorText = await response.text();
|
|
20
|
+
throw new Error(`获取AccessToken失败: ${response.status} ${errorText}`);
|
|
21
|
+
}
|
|
22
|
+
return await response.json();
|
|
286
23
|
}
|
|
287
|
-
async function
|
|
288
|
-
const
|
|
289
|
-
|
|
290
|
-
const
|
|
24
|
+
async function uploadMaterial(type, fileData, fileName, accessToken) {
|
|
25
|
+
const form = new FormData();
|
|
26
|
+
form.append("media", fileData, fileName);
|
|
27
|
+
const encoder = new FormDataEncoder(form);
|
|
28
|
+
const response = await fetch(`${uploadUrl}?access_token=${accessToken}&type=${type}`, {
|
|
291
29
|
method: "POST",
|
|
292
|
-
|
|
30
|
+
headers: encoder.headers,
|
|
31
|
+
body: Readable.from(encoder),
|
|
32
|
+
duplex: "half"
|
|
293
33
|
});
|
|
294
|
-
if (!
|
|
295
|
-
const
|
|
296
|
-
throw new Error(`上传素材失败: ${
|
|
34
|
+
if (!response.ok) {
|
|
35
|
+
const errorText = await response.text();
|
|
36
|
+
throw new Error(`上传素材失败: ${response.status} ${errorText}`);
|
|
37
|
+
}
|
|
38
|
+
const data = await response.json();
|
|
39
|
+
if (data.errcode && data.errcode !== 0) {
|
|
40
|
+
throw new Error(`上传素材失败: ${data.errcode} ${data.errmsg}`);
|
|
297
41
|
}
|
|
298
|
-
|
|
299
|
-
|
|
42
|
+
if (data.url && data.url.startsWith("http://")) {
|
|
43
|
+
data.url = data.url.replace(/^http:\/\//i, "https://");
|
|
44
|
+
}
|
|
45
|
+
return data;
|
|
300
46
|
}
|
|
301
|
-
async function
|
|
302
|
-
const
|
|
47
|
+
async function publishArticle(title, content, thumbMediaId, accessToken) {
|
|
48
|
+
const response = await fetch(`${publishUrl}?access_token=${accessToken}`, {
|
|
303
49
|
method: "POST",
|
|
304
50
|
body: JSON.stringify({
|
|
305
51
|
articles: [
|
|
306
52
|
{
|
|
307
|
-
title
|
|
308
|
-
content
|
|
309
|
-
thumb_media_id:
|
|
53
|
+
title,
|
|
54
|
+
content,
|
|
55
|
+
thumb_media_id: thumbMediaId
|
|
310
56
|
}
|
|
311
57
|
]
|
|
312
58
|
})
|
|
313
59
|
});
|
|
314
|
-
if (!
|
|
315
|
-
const
|
|
316
|
-
throw new Error(`发布失败: ${
|
|
60
|
+
if (!response.ok) {
|
|
61
|
+
const errorText = await response.text();
|
|
62
|
+
throw new Error(`发布失败: ${response.status} ${errorText}`);
|
|
63
|
+
}
|
|
64
|
+
return await response.json();
|
|
65
|
+
}
|
|
66
|
+
function normalizePath(p) {
|
|
67
|
+
if (!p) return "";
|
|
68
|
+
let res = p.replace(/\\/g, "/");
|
|
69
|
+
if (res.endsWith("/")) {
|
|
70
|
+
res = res.slice(0, -1);
|
|
317
71
|
}
|
|
318
|
-
return
|
|
72
|
+
return res;
|
|
319
73
|
}
|
|
320
|
-
const
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
if (!
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
74
|
+
const RuntimeEnv = {
|
|
75
|
+
isContainer: !!process.env.CONTAINERIZED,
|
|
76
|
+
hostFilePath: normalizePath(process.env.HOST_FILE_PATH || ""),
|
|
77
|
+
containerFilePath: normalizePath(process.env.CONTAINER_FILE_PATH || "/mnt/host-downloads"),
|
|
78
|
+
resolveLocalPath(inputPath) {
|
|
79
|
+
if (!this.isContainer || !this.hostFilePath) {
|
|
80
|
+
return path.resolve(inputPath);
|
|
81
|
+
}
|
|
82
|
+
const normalizedInput = normalizePath(inputPath);
|
|
83
|
+
if (normalizedInput.startsWith(this.hostFilePath)) {
|
|
84
|
+
let relativePart = normalizedInput.slice(this.hostFilePath.length);
|
|
85
|
+
if (relativePart && !relativePart.startsWith("/")) {
|
|
86
|
+
return normalizedInput;
|
|
87
|
+
}
|
|
88
|
+
if (!relativePart.startsWith("/")) {
|
|
89
|
+
relativePart = "/" + relativePart;
|
|
90
|
+
}
|
|
91
|
+
return this.containerFilePath + relativePart;
|
|
92
|
+
}
|
|
93
|
+
return normalizedInput;
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
async function uploadImage(imageUrl, accessToken, fileName) {
|
|
97
|
+
let fileData;
|
|
98
|
+
let finalName;
|
|
99
|
+
if (imageUrl.startsWith("http")) {
|
|
100
|
+
const response = await fetch(imageUrl);
|
|
101
|
+
if (!response.ok || !response.body) {
|
|
102
|
+
throw new Error(`Failed to download image from URL: ${imageUrl}`);
|
|
103
|
+
}
|
|
104
|
+
const fileNameFromUrl = path$1.basename(imageUrl.split("?")[0]);
|
|
105
|
+
const ext = path$1.extname(fileNameFromUrl);
|
|
106
|
+
finalName = fileName ?? (ext === "" ? `${fileNameFromUrl}.jpg` : fileNameFromUrl);
|
|
107
|
+
const buffer = await response.arrayBuffer();
|
|
108
|
+
if (buffer.byteLength === 0) {
|
|
109
|
+
throw new Error(`远程图片大小为0,无法上传: ${imageUrl}`);
|
|
110
|
+
}
|
|
111
|
+
const contentType = response.headers.get("content-type") || "image/jpeg";
|
|
112
|
+
fileData = new Blob([buffer], { type: contentType });
|
|
331
113
|
} else {
|
|
332
|
-
const
|
|
333
|
-
|
|
114
|
+
const resolvedPath = RuntimeEnv.resolveLocalPath(imageUrl);
|
|
115
|
+
const safePath = path$1.resolve(resolvedPath);
|
|
116
|
+
const stats = await stat(safePath);
|
|
117
|
+
if (stats.size === 0) {
|
|
118
|
+
throw new Error(`本地图片大小为0,无法上传: ${safePath}`);
|
|
119
|
+
}
|
|
120
|
+
const fileNameFromLocal = path$1.basename(resolvedPath);
|
|
121
|
+
const ext = path$1.extname(fileNameFromLocal);
|
|
122
|
+
finalName = fileName ?? (ext === "" ? `${fileNameFromLocal}.jpg` : fileNameFromLocal);
|
|
123
|
+
fileData = await fileFromPath(safePath);
|
|
334
124
|
}
|
|
335
|
-
const
|
|
336
|
-
if (
|
|
337
|
-
throw new Error(`上传失败,错误码:${
|
|
338
|
-
|
|
125
|
+
const data = await uploadMaterial("image", fileData, finalName, accessToken);
|
|
126
|
+
if (data.errcode) {
|
|
127
|
+
throw new Error(`上传失败,错误码:${data.errcode},错误信息:${data.errmsg}`);
|
|
128
|
+
}
|
|
129
|
+
return data;
|
|
339
130
|
}
|
|
340
|
-
async function
|
|
341
|
-
if (!
|
|
342
|
-
return { html:
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
131
|
+
async function uploadImages(content, accessToken) {
|
|
132
|
+
if (!content.includes("<img")) {
|
|
133
|
+
return { html: content, firstImageId: "" };
|
|
134
|
+
}
|
|
135
|
+
const dom = new JSDOM(content);
|
|
136
|
+
const document = dom.window.document;
|
|
137
|
+
const images = Array.from(document.querySelectorAll("img"));
|
|
138
|
+
const uploadPromises = images.map(async (element) => {
|
|
139
|
+
const dataSrc = element.getAttribute("src");
|
|
140
|
+
if (dataSrc) {
|
|
141
|
+
if (!dataSrc.startsWith("https://mmbiz.qpic.cn")) {
|
|
142
|
+
const resp = await uploadImage(dataSrc, accessToken);
|
|
143
|
+
element.setAttribute("src", resp.url);
|
|
144
|
+
return resp.media_id;
|
|
145
|
+
} else {
|
|
146
|
+
return dataSrc;
|
|
351
147
|
}
|
|
352
148
|
}
|
|
353
149
|
return null;
|
|
354
|
-
})
|
|
355
|
-
|
|
150
|
+
});
|
|
151
|
+
const mediaIds = (await Promise.all(uploadPromises)).filter(Boolean);
|
|
152
|
+
const firstImageId = mediaIds[0] || "";
|
|
153
|
+
const updatedHtml = dom.serialize();
|
|
154
|
+
return { html: updatedHtml, firstImageId };
|
|
356
155
|
}
|
|
357
|
-
async function
|
|
358
|
-
const
|
|
359
|
-
if (!
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
156
|
+
async function publishToDraft(title, content, cover, appId, appSecret) {
|
|
157
|
+
const accessToken = await fetchAccessToken(appId, appSecret);
|
|
158
|
+
if (!accessToken.access_token) {
|
|
159
|
+
if (accessToken.errcode) {
|
|
160
|
+
throw new Error(`获取 Access Token 失败,错误码:${accessToken.errcode},${accessToken.errmsg}`);
|
|
161
|
+
} else {
|
|
162
|
+
throw new Error(`获取 Access Token 失败: ${accessToken}`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
const { html, firstImageId } = await uploadImages(content, accessToken.access_token);
|
|
166
|
+
let thumbMediaId = "";
|
|
167
|
+
if (cover) {
|
|
168
|
+
const resp = await uploadImage(cover, accessToken.access_token, "cover.jpg");
|
|
169
|
+
thumbMediaId = resp.media_id;
|
|
170
|
+
} else {
|
|
171
|
+
if (firstImageId.startsWith("https://mmbiz.qpic.cn")) {
|
|
172
|
+
const resp = await uploadImage(firstImageId, accessToken.access_token, "cover.jpg");
|
|
173
|
+
thumbMediaId = resp.media_id;
|
|
174
|
+
} else {
|
|
175
|
+
thumbMediaId = firstImageId;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (!thumbMediaId) {
|
|
364
179
|
throw new Error("你必须指定一张封面图或者在正文中至少出现一张图片。");
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
180
|
+
}
|
|
181
|
+
const data = await publishArticle(title, html, thumbMediaId, accessToken.access_token);
|
|
182
|
+
if (data.media_id) {
|
|
183
|
+
return data;
|
|
184
|
+
} else if (data.errcode) {
|
|
185
|
+
throw new Error(`上传到公众号草稿失败,错误码:${data.errcode},${data.errmsg}`);
|
|
186
|
+
} else {
|
|
187
|
+
throw new Error(`上传到公众号草稿失败: ${data}`);
|
|
188
|
+
}
|
|
369
189
|
}
|
|
370
190
|
export {
|
|
371
|
-
|
|
191
|
+
publishToDraft
|
|
372
192
|
};
|