@waline/vercel 1.32.3 → 1.32.4

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.
@@ -0,0 +1,20 @@
1
+ FROM node:lts-alpine AS base
2
+ ENV PNPM_HOME="/pnpm"
3
+ ENV PATH="$PNPM_HOME:$PATH"
4
+ WORKDIR /app
5
+
6
+ RUN apk add --no-cache bash tzdata && \
7
+ cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
8
+ echo "Asia/Shanghai" > /etc/timezone && \
9
+ corepack enable
10
+
11
+ COPY . .
12
+
13
+ FROM base AS prod-deps
14
+ RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --prod
15
+
16
+ FROM base AS runtime
17
+ ENV NODE_ENV=production
18
+ COPY --from=prod-deps /app /app
19
+ EXPOSE 8360
20
+ CMD ["node", "vanilla.js"]
@@ -60,14 +60,14 @@ Waline is a good framework. :money:
60
60
 
61
61
  it('Should not autoplay or preload media', () => {
62
62
  expect(parser('<audio autoplay preload="auto" src="x">')).toEqual(
63
- '<audio src="x" preload="none"></audio>',
63
+ '<audio preload="none" src="x"></audio>',
64
64
  );
65
65
  expect(parser('<audio autoplay src="x"></audio>')).toEqual(
66
66
  '<p><audio src="x" preload="none"></audio></p>\n',
67
67
  );
68
68
 
69
69
  expect(parser('<video autoplay preload="auto" src="x">')).toEqual(
70
- '<video src="x" preload="none"></video>',
70
+ '<video preload="none" src="x"></video>',
71
71
  );
72
72
  expect(parser('<video autoplay src="x"></video>')).toEqual(
73
73
  '<p><video src="x" preload="none"></video></p>\n',
@@ -83,7 +83,7 @@ Waline is a good framework. :money:
83
83
  '<p><a href="https://example.com" rel="opener prefetch">link</a></p>',
84
84
  ),
85
85
  ).toEqual(
86
- '<p><a rel="nofollow noreferrer noopener" href="https://example.com" target="_blank">link</a></p>',
86
+ '<p><a href="https://example.com" rel="nofollow noreferrer noopener" target="_blank">link</a></p>',
87
87
  );
88
88
  });
89
89
 
package/development.js ADDED
@@ -0,0 +1,30 @@
1
+ const path = require('node:path');
2
+
3
+ require('dotenv').config({
4
+ path: path.join(__dirname, '../../.env'),
5
+ quiet: true,
6
+ });
7
+
8
+ const watcher = require('think-watcher');
9
+ const Application = require('thinkjs');
10
+
11
+ const instance = new Application({
12
+ ROOT_PATH: __dirname,
13
+ APP_PATH: path.join(__dirname, 'src'),
14
+ proxy: false,
15
+ watcher: watcher,
16
+ env: 'development',
17
+ });
18
+
19
+ instance.run();
20
+
21
+ let config = {};
22
+
23
+ try {
24
+ config = require('./config.js');
25
+ } catch {
26
+ // do nothing
27
+ }
28
+ for (const k in config) {
29
+ think.config(k, config[k]);
30
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@waline/vercel",
3
- "version": "1.32.3",
3
+ "version": "1.32.4",
4
4
  "description": "vercel server for waline comment system",
5
5
  "keywords": [
6
6
  "waline",
@@ -14,32 +14,33 @@
14
14
  },
15
15
  "license": "MIT",
16
16
  "author": "lizheming <i@imnerd.org>",
17
+ "scripts": {
18
+ "dev": "node development.js 9090"
19
+ },
17
20
  "dependencies": {
18
- "@cloudbase/node-sdk": "^3.3.7",
21
+ "@cloudbase/node-sdk": "^3.10.1",
19
22
  "@koa/cors": "^5.0.0",
20
- "@mdit/plugin-katex": "0.8.0",
21
- "@mdit/plugin-mathjax": "0.4.8",
22
- "@mdit/plugin-sub": "0.8.0",
23
- "@mdit/plugin-sup": "0.8.0",
23
+ "@mdit/plugin-katex": "0.23.2-cjs.0",
24
+ "@mdit/plugin-mathjax": "0.23.2-cjs.0",
25
+ "@mdit/plugin-sub": "0.22.2-cjs.0",
26
+ "@mdit/plugin-sup": "0.22.2-cjs.0",
24
27
  "akismet": "^2.0.7",
25
28
  "deta": "^2.0.0",
26
- "dompurify": "^3.2.2",
29
+ "dompurify": "^3.2.7",
27
30
  "dy-node-ip2region": "^1.0.1",
28
- "fast-csv": "^5.0.2",
29
- "form-data": "^4.0.1",
30
- "jsdom": "^25.0.1",
31
+ "fast-csv": "^5.0.5",
32
+ "form-data": "^4.0.4",
33
+ "jsdom": "^27.0.0",
31
34
  "jsonwebtoken": "^9.0.2",
32
- "katex": "^0.16.11",
33
35
  "koa-compose": "^4.1.0",
34
36
  "leancloud-storage": "^4.15.2",
35
37
  "markdown-it": "^14.1.0",
36
38
  "markdown-it-emoji": "^3.0.0",
37
39
  "mathjax-full": "^3.2.2",
38
- "node-fetch": "^2.7.0",
39
- "nodemailer": "^6.9.16",
40
+ "nodemailer": "^7.0.6",
40
41
  "nunjucks": "^3.2.4",
41
42
  "phpass": "^0.1.1",
42
- "prismjs": "^1.29.0",
43
+ "prismjs": "^1.30.0",
43
44
  "speakeasy": "^2.0.0",
44
45
  "think-helper": "^1.1.4",
45
46
  "think-logger3": "^1.4.0",
@@ -50,11 +51,15 @@
50
51
  "think-model-sqlite": "^1.3.2",
51
52
  "think-mongo": "^2.2.1",
52
53
  "think-router-rest": "^1.0.5",
53
- "thinkjs": "^3.2.15",
54
- "ua-parser-js": "^2.0.0"
54
+ "thinkjs": "4.0.0-alpha.0",
55
+ "ua-parser-js": "^2.0.5"
56
+ },
57
+ "devDependencies": {
58
+ "dotenv": "17.2.2",
59
+ "think-watcher": "3.0.4"
55
60
  },
56
61
  "engines": {
57
- "node": ">=16"
62
+ "node": ">=20"
58
63
  },
59
64
  "publishConfig": {
60
65
  "provenance": true
@@ -3,11 +3,13 @@ const Mysql = require('think-model-mysql');
3
3
  const Mysql2 = require('think-model-mysql2');
4
4
  const Postgresql = require('think-model-postgresql');
5
5
 
6
- let Sqlite = class {};
6
+ let Sqlite;
7
7
 
8
8
  try {
9
9
  Sqlite = require('think-model-sqlite');
10
10
  } catch (err) {
11
+ // eslint-disable-next-line @typescript-eslint/no-extraneous-class
12
+ Sqlite = class {};
11
13
  console.log(err);
12
14
  }
13
15
 
@@ -1,4 +1,3 @@
1
- const fetch = require('node-fetch');
2
1
  const Model = require('think-model');
3
2
  const Mongo = require('think-mongo');
4
3
 
@@ -175,8 +175,8 @@ module.exports = class extends BaseRest {
175
175
  think.logger.debug(`Comment initial status is ${data.status}`);
176
176
 
177
177
  if (data.status === 'approved') {
178
- const spam = await akismet(data, this.ctx.serverURL).catch((e) =>
179
- console.log(e),
178
+ const spam = await akismet(data, this.ctx.serverURL).catch((err) =>
179
+ console.log(err),
180
180
  ); // ignore akismet error
181
181
 
182
182
  if (spam === true) {
@@ -1,5 +1,4 @@
1
1
  const jwt = require('jsonwebtoken');
2
- const fetch = require('node-fetch');
3
2
 
4
3
  module.exports = class extends think.Controller {
5
4
  constructor(ctx) {
@@ -16,17 +15,12 @@ module.exports = class extends think.Controller {
16
15
 
17
16
  if (!hasCode) {
18
17
  const { serverURL } = this.ctx;
19
- const redirectUrl = `${serverURL}/api/oauth?${new URLSearchParams({
20
- redirect,
21
- type,
22
- }).toString()}`;
18
+ const redirectUrl = think.buildUrl(`${serverURL}/api/oauth`, {redirect, type});
23
19
 
24
- return this.redirect(
25
- `${oauthUrl}/${type}?${new URLSearchParams({
26
- redirect: redirectUrl,
27
- state: this.ctx.state.token || '',
28
- }).toString()}`,
29
- );
20
+ return this.redirect(think.buildUrl(`${oauthUrl}/${type}`, {
21
+ redirect: redirectUrl,
22
+ state: this.ctx.state.token || ''
23
+ }));
30
24
  }
31
25
 
32
26
  /**
@@ -36,19 +30,16 @@ module.exports = class extends think.Controller {
36
30
 
37
31
  if (type === 'facebook') {
38
32
  const { serverURL } = this.ctx;
39
- const redirectUrl = `${serverURL}/api/oauth?${new URLSearchParams({
40
- redirect,
41
- type,
42
- }).toString()}`;
33
+ const redirectUrl = think.buildUrl(`${serverURL}/api/oauth`, {redirect, type});
43
34
 
44
- params.state = new URLSearchParams({
35
+ params.state = think.buildUrl(undefined, {
45
36
  redirect: redirectUrl,
46
37
  state: this.ctx.state.token || '',
47
38
  });
48
39
  }
49
40
 
50
41
  const user = await fetch(
51
- `${oauthUrl}/${type}?${new URLSearchParams(params).toString()}`,
42
+ think.buildUrl(`${oauthUrl}/${type}`, params),
52
43
  {
53
44
  method: 'GET',
54
45
  headers: {
@@ -67,9 +58,7 @@ module.exports = class extends think.Controller {
67
58
  const token = jwt.sign(userBySocial[0].email, this.config('jwtKey'));
68
59
 
69
60
  if (redirect) {
70
- return this.redirect(
71
- redirect + (redirect.includes('?') ? '&' : '?') + 'token=' + token,
72
- );
61
+ return this.redirect(think.buildUrl(redirect, { token }));
73
62
  }
74
63
 
75
64
  return this.success();
@@ -91,10 +91,7 @@ module.exports = class extends BaseRest {
91
91
 
92
92
  try {
93
93
  const notify = this.service('notify', this);
94
- const apiUrl =
95
- this.ctx.serverURL +
96
- '/verification?' +
97
- new URLSearchParams({ token, email: data.email }).toString();
94
+ const apiUrl = think.buildUrl(this.ctx.serverURL + '/verification', { token, email: data.email });
98
95
 
99
96
  await notify.transporter.sendMail({
100
97
  from:
@@ -110,8 +107,8 @@ module.exports = class extends BaseRest {
110
107
  { url: apiUrl },
111
108
  ),
112
109
  });
113
- } catch (e) {
114
- console.log(e);
110
+ } catch (err) {
111
+ console.log(err);
115
112
 
116
113
  return this.fail(
117
114
  this.locale(
@@ -83,8 +83,8 @@ module.exports = {
83
83
  );
84
84
 
85
85
  return address.slice(0, depth).join(' ');
86
- } catch (e) {
87
- console.log(e);
86
+ } catch (err) {
87
+ console.log(err);
88
88
 
89
89
  return '';
90
90
  }
@@ -154,4 +154,27 @@ module.exports = {
154
154
  )
155
155
  .filter((v) => v);
156
156
  },
157
+ buildUrl(path, query = {}) {
158
+ const notEmptyQuery = {};
159
+
160
+ for(const key in query) {
161
+ if (!query[key]) {
162
+ continue;
163
+ }
164
+ notEmptyQuery[key] = query[key];
165
+ }
166
+
167
+ const notEmptyQueryStr = new URLSearchParams(notEmptyQuery).toString();
168
+
169
+ let destUrl = path;
170
+
171
+ if (destUrl && notEmptyQueryStr) {
172
+ destUrl += destUrl.indexOf('?') !== -1 ? '&' : '?';
173
+ }
174
+ if (notEmptyQueryStr) {
175
+ destUrl += notEmptyQueryStr;
176
+ }
177
+
178
+ return destUrl;
179
+ }
157
180
  };
@@ -13,7 +13,7 @@
13
13
  "Comment too fast": "評論太快啦,請慢點!",
14
14
  "Registration confirm mail send failed, please {%- if isAdmin -%}check your mail configuration{%- else -%}check your email address and contact administrator{%- endif -%}.": "註冊確認郵件發送失敗,{%- if isAdmin -%}檢查一下網站的郵件相關配置{% else %}確認你的郵箱輸入無誤後聯繫管理員{%- endif -%}。",
15
15
  "MAIL_SUBJECT": "{{parent.nick | safe}},『{{site.name | safe}}』上的評論收到回復",
16
- "MAIL_TEMPLATE": "<div style='border-top:2px solid #12ADDB;box-shadow:0 1px 3px #AAAAAA;line-height:180%;padding:0 15px 12px;margin:50px auto;font-size:12px;'> <h2 style='border-bottom:1px solid #DDD;font-size:14px;font-weight:normal;padding:13px 0 10px 8px;'> 您在<a style='text-decoration:none;color: #12ADDB;' href='{{site.url}}' target='_blank'>{{site.name}}</a>上的品論有新的回復 </h2>{{parent.nick}}同學,您層發表評論: <div style='padding:0 12px 0 12px;margin-top:18px'> <div style='background-color: #f5f5f5;padding: 10px 15px;margin:18px 0;word-wrap:break-word;'>{{parent.comment | safe}}</div><p><strong>{{self.nick}}</strong>回復說:</p><div style='background-color: #f5f5f5;padding: 10px 15px;margin:18px 0;word-wrap:break-word;'>{{self.comment | safe}}</div><p>您可以點擊<a style='text-decoration:none; color:#12addb' href='{{site.postUrl}}' target='_blank'>查看回復的完整內容</a>,歡迎再次光臨<a style='text-decoration:none; color:#12addb' href='{{site.url}}' target='_blank'>{{site.name}}</a>。</p><br/> </div></div>",
16
+ "MAIL_TEMPLATE": "<div style='border-top:2px solid #12ADDB;box-shadow:0 1px 3px #AAAAAA;line-height:180%;padding:0 15px 12px;margin:50px auto;font-size:12px;'> <h2 style='border-bottom:1px solid #DDD;font-size:14px;font-weight:normal;padding:13px 0 10px 8px;'> 您在<a style='text-decoration:none;color: #12ADDB;' href='{{site.url}}' target='_blank'>{{site.name}}</a>上的評論有新的回復 </h2>{{parent.nick}}同學,您層發表評論: <div style='padding:0 12px 0 12px;margin-top:18px'> <div style='background-color: #f5f5f5;padding: 10px 15px;margin:18px 0;word-wrap:break-word;'>{{parent.comment | safe}}</div><p><strong>{{self.nick}}</strong>回復說:</p><div style='background-color: #f5f5f5;padding: 10px 15px;margin:18px 0;word-wrap:break-word;'>{{self.comment | safe}}</div><p>您可以點擊<a style='text-decoration:none; color:#12addb' href='{{site.postUrl}}' target='_blank'>查看回復的完整內容</a>,歡迎再次光臨<a style='text-decoration:none; color:#12addb' href='{{site.url}}' target='_blank'>{{site.name}}</a>。</p><br/> </div></div>",
17
17
  "MAIL_SUBJECT_ADMIN": "{{site.name | safe}} 上有新評論了",
18
18
  "MAIL_TEMPLATE_ADMIN": "<div style='border-top:2px solid #12ADDB;box-shadow:0 1px 3px #AAAAAA;line-height:180%;padding:0 15px 12px;margin:50px auto;font-size:12px;'> <h2 style='border-bottom:1px solid #DDD;font-size:14px;font-weight:normal;padding:13px 0 10px 8px;'> 您在<a style='text-decoration:none;color: #12ADDB;' href='{{site.url}}' target='_blank'>{{site.name}}</a>上的文章有新評論了 </h2> <p><strong>{{self.nick}}</strong>回復說:</p><div style='background-color: #f5f5f5;padding: 10px 15px;margin:18px 0;word-wrap:break-word;'>{{self.comment | safe}}</div><p>您可以點擊<a style='text-decoration:none; color:#12addb' href='{{site.postUrl}}' target='_blank'>查看回復的完整內容</a></p><br/> </div>"
19
19
  }
package/src/logic/base.js CHANGED
@@ -2,7 +2,6 @@ const path = require('node:path');
2
2
  const qs = require('node:querystring');
3
3
 
4
4
  const jwt = require('jsonwebtoken');
5
- const fetch = require('node-fetch');
6
5
  const helper = require('think-helper');
7
6
 
8
7
  module.exports = class extends think.Logic {
@@ -15,9 +14,21 @@ module.exports = class extends think.Logic {
15
14
 
16
15
  async __before() {
17
16
  const referrer = this.ctx.referrer(true);
17
+ let origin = this.ctx.origin;
18
+
19
+ if (origin) {
20
+ try {
21
+ const parsedOrigin = new URL(origin);
22
+
23
+ origin = parsedOrigin.hostname;
24
+ } catch (e) {
25
+ console.error('Invalid origin format:', origin, e);
26
+ }
27
+ }
28
+
18
29
  let { secureDomains } = this.config();
19
30
 
20
- if (secureDomains && referrer && this.ctx.host.indexOf(referrer) !== 0) {
31
+ if (secureDomains) {
21
32
  secureDomains = think.isArray(secureDomains)
22
33
  ? secureDomains
23
34
  : [secureDomains];
@@ -31,13 +42,41 @@ module.exports = class extends think.Logic {
31
42
  'graph.qq.com',
32
43
  );
33
44
 
34
- const match = secureDomains.some((domain) =>
45
+ // 转换可能的正则表达式字符串为正则表达式对象
46
+ secureDomains = secureDomains
47
+ .map((domain) => {
48
+ // 如果是正则表达式字符串,创建一个 RegExp 对象
49
+ if (
50
+ typeof domain === 'string' &&
51
+ domain.startsWith('/') &&
52
+ domain.endsWith('/')
53
+ ) {
54
+ try {
55
+ return new RegExp(domain.slice(1, -1)); // 去掉斜杠并创建 RegExp 对象
56
+ } catch (e) {
57
+ console.error(
58
+ 'Invalid regex pattern in secureDomains:',
59
+ domain,
60
+ e,
61
+ );
62
+
63
+ return null;
64
+ }
65
+ }
66
+
67
+ return domain;
68
+ })
69
+ .filter(Boolean); // 过滤掉无效的正则表达式
70
+
71
+ // 有 referrer 检查 referrer,没有则检查 origin
72
+ const checking = referrer ? referrer : origin;
73
+ const isSafe = secureDomains.some((domain) =>
35
74
  think.isFunction(domain.test)
36
- ? domain.test(referrer)
37
- : domain === referrer,
75
+ ? domain.test(checking)
76
+ : domain === checking,
38
77
  );
39
78
 
40
- if (!match) {
79
+ if (!isSafe) {
41
80
  return this.ctx.throw(403);
42
81
  }
43
82
  }
@@ -8,56 +8,48 @@ const { SVG } = require('mathjax-full/js/output/svg.js');
8
8
  const { inlineTeX, blockTeX } = require('./mathCommon');
9
9
  const { escapeHtml } = require('./utils');
10
10
 
11
- // set MathJax as the renderer
12
- class MathToSvg {
13
- constructor() {
14
- const adaptor = liteAdaptor();
15
-
16
- RegisterHTMLHandler(adaptor);
11
+ const mathjaxPlugin = (md) => {
12
+ const adaptor = liteAdaptor();
17
13
 
18
- const packages = AllPackages.sort();
19
- const tex = new TeX({ packages });
20
- const svg = new SVG({ fontCache: 'none' });
14
+ RegisterHTMLHandler(adaptor);
21
15
 
22
- this.adaptor = adaptor;
23
- this.texToNode = mathjax.document('', { InputJax: tex, OutputJax: svg });
16
+ const packages = AllPackages.sort();
17
+ const tex = new TeX({ packages });
18
+ const svg = new SVG({ fontCache: 'none' });
24
19
 
25
- this.inline = function (tex) {
26
- const node = this.texToNode.convert(tex, { display: false });
27
- let svg = this.adaptor.innerHTML(node);
20
+ const texToNode = mathjax.document('', { InputJax: tex, OutputJax: svg });
28
21
 
29
- if (svg.includes('data-mml-node="merror"')) {
30
- const errorTitle = svg.match(/<title>(.*?)<\/title>/)[1];
22
+ const inline = (tex) => {
23
+ const node = texToNode.convert(tex, { display: false });
24
+ let svg = adaptor.innerHTML(node);
31
25
 
32
- svg = `<span class='mathjax-error' title='${escapeHtml(
33
- errorTitle,
34
- )}'>${escapeHtml(tex)}</span>`;
35
- }
26
+ if (svg.includes('data-mml-node="merror"')) {
27
+ const errorTitle = svg.match(/<title>(.*?)<\/title>/)[1];
36
28
 
37
- return svg;
38
- };
29
+ svg = `<span class='mathjax-error' title='${escapeHtml(
30
+ errorTitle,
31
+ )}'>${escapeHtml(tex)}</span>`;
32
+ }
39
33
 
40
- this.block = function (tex) {
41
- const node = this.texToNode.convert(tex, { display: true });
42
- let svg = this.adaptor.innerHTML(node);
34
+ return svg;
35
+ };
43
36
 
44
- if (svg.includes('data-mml-node="merror"')) {
45
- const errorTitle = svg.match(/<title>(.*?)<\/title>/)[1];
37
+ const block = (tex) => {
38
+ const node = texToNode.convert(tex, { display: true });
39
+ let svg = adaptor.innerHTML(node);
46
40
 
47
- svg = `<p class='mathjax-block mathjax-error' title='${escapeHtml(
48
- errorTitle,
49
- )}'>${escapeHtml(tex)}</p>`;
50
- } else {
51
- svg = svg.replace(/(width=".*?")/, 'width="100%"');
52
- }
41
+ if (svg.includes('data-mml-node="merror"')) {
42
+ const errorTitle = svg.match(/<title>(.*?)<\/title>/)[1];
53
43
 
54
- return svg;
55
- };
56
- }
57
- }
44
+ svg = `<p class='mathjax-block mathjax-error' title='${escapeHtml(
45
+ errorTitle,
46
+ )}'>${escapeHtml(tex)}</p>`;
47
+ } else {
48
+ svg = svg.replace(/(width=".*?")/, 'width="100%"');
49
+ }
58
50
 
59
- const mathjaxPlugin = (md) => {
60
- const mathToSvg = new MathToSvg();
51
+ return svg;
52
+ };
61
53
 
62
54
  md.inline.ruler.after('escape', 'inlineTeX', inlineTeX);
63
55
 
@@ -66,11 +58,10 @@ const mathjaxPlugin = (md) => {
66
58
  alt: ['paragraph', 'reference', 'blockquote', 'list'],
67
59
  });
68
60
 
69
- md.renderer.rules.inlineTeX = (tokens, idx) =>
70
- mathToSvg.inline(tokens[idx].content);
61
+ md.renderer.rules.inlineTeX = (tokens, idx) => inline(tokens[idx].content);
71
62
 
72
63
  md.renderer.rules.blockTeX = (tokens, idx) =>
73
- `${mathToSvg.block(tokens[idx].content)}\n`;
64
+ `${block(tokens[idx].content)}\n`;
74
65
  };
75
66
 
76
67
  module.exports = {
@@ -1,7 +1,6 @@
1
1
  const crypto = require('node:crypto');
2
2
 
3
3
  const FormData = require('form-data');
4
- const fetch = require('node-fetch');
5
4
  const nodemailer = require('nodemailer');
6
5
  const nunjucks = require('nunjucks');
7
6
 
@@ -213,9 +213,10 @@ module.exports = class extends Base {
213
213
 
214
214
  fieldMap.add('objectId');
215
215
  data.forEach((item) => {
216
- for (const k in item) {
217
- if (!fieldMap.has(k)) {
218
- delete item[k];
216
+ for (const key in item) {
217
+ if (!fieldMap.has(key)) {
218
+ // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
219
+ delete item[key];
219
220
  }
220
221
  }
221
222
  });
@@ -1,7 +1,6 @@
1
1
  const path = require('node:path');
2
2
 
3
3
  const { parseString, writeToString } = require('fast-csv');
4
- const fetch = require('node-fetch');
5
4
 
6
5
  const Base = require('./base.js');
7
6
 
@@ -51,6 +51,7 @@ module.exports = class extends MySQL {
51
51
  val instanceof Date
52
52
  ? think.datetime(val, 'YYYY-MM-DD HH:mm:ss')
53
53
  : val;
54
+ // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
54
55
  delete data[key];
55
56
  });
56
57
 
@@ -1,50 +0,0 @@
1
- const katex = require('katex');
2
-
3
- const { inlineTeX, blockTeX } = require('./mathCommon.js');
4
- const { escapeHtml } = require('./utils.js');
5
-
6
- // set KaTeX as the renderer for markdown-it-simplemath
7
- const katexInline = (tex, options) => {
8
- options.displayMode = false;
9
- try {
10
- return katex.renderToString(tex, options);
11
- } catch (error) {
12
- if (options.throwOnError) console.warn(error);
13
-
14
- return `<span class='katex-error' title='${escapeHtml(
15
- error.toString(),
16
- )}'>${escapeHtml(tex)}</span>`;
17
- }
18
- };
19
-
20
- const katexBlock = (tex, options) => {
21
- options.displayMode = true;
22
- try {
23
- return `<p class='katex-block'>${katex.renderToString(tex, options)}</p>`;
24
- } catch (error) {
25
- if (options.throwOnError) console.warn(error);
26
-
27
- return `<p class='katex-block katex-error' title='${escapeHtml(
28
- error.toString(),
29
- )}'>${escapeHtml(tex)}</p>`;
30
- }
31
- };
32
-
33
- const katexPlugin = (md, options = { throwOnError: false }) => {
34
- md.inline.ruler.after('escape', 'inlineTeX', inlineTeX);
35
-
36
- // It’s a workaround here because types issue
37
- md.block.ruler.after('blockquote', 'blockTeX', blockTeX, {
38
- alt: ['paragraph', 'reference', 'blockquote', 'list'],
39
- });
40
-
41
- md.renderer.rules.inlineTeX = (tokens, idx) =>
42
- katexInline(tokens[idx].content, options);
43
-
44
- md.renderer.rules.blockTeX = (tokens, idx) =>
45
- `${katexBlock(tokens[idx].content, options)}\n`;
46
- };
47
-
48
- module.exports = {
49
- katexPlugin,
50
- };