fix: hijs code block parser, fixed #928

Signed-off-by: Innei <tukon479@gmail.com>
This commit is contained in:
Innei
2024-10-14 13:09:06 +08:00
parent 7ef68282ce
commit 3f06c717a2
9 changed files with 455 additions and 2129 deletions
+3 -1
View File
@@ -3,6 +3,8 @@ import { name, version } from "@pkg"
import { parseHTML } from "linkedom"
import { fetch } from "ofetch"
import { isDev } from "~/env"
const userAgents = `Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 ${name}/${version}`
export async function readability(url: string) {
@@ -41,7 +43,7 @@ export async function readability(url: string) {
})
const reader = new Readability(document, {
// debug: isDev,
debug: isDev,
})
return reader.parse()
}
@@ -0,0 +1,376 @@
import { describe, expect, it } from "vitest"
import { extractCodeFromHtml } from "../parse-html"
describe("extractCodeFromHtml", () => {
it("should extract code from div elements", () => {
const htmlString = "<div>line 1</div><div>line 2</div>"
const result = extractCodeFromHtml(htmlString)
expect(result).toBe("line 1\nline 2\n")
})
it("should extract code from span elements with line breaks", () => {
const htmlString = "<span><span>line 1</span></span><span><span>line 2</span></span>"
const result = extractCodeFromHtml(htmlString)
expect(result).toMatchInlineSnapshot(`
"line 1line 2
"
`)
})
it("should extract code from span elements without line breaks", () => {
const htmlString = "<span><span>line 1</span></span><span><span>line 2</span></span>"
const result = extractCodeFromHtml(htmlString)
expect(result).toMatchInlineSnapshot(`
"line 1line 2
"
`)
})
it("should return the entire text content if no specific structure is found", () => {
const htmlString = "plain text content"
const result = extractCodeFromHtml(htmlString)
expect(result).toBe("plain text content")
})
it("pre > code", () => {
const htmlString = `<pre class="language-ts lang-ts"><code class="language-ts lang-ts">import i18next from &#x27;i18next&#x27; import { initReactI18next } from &#x27;react-i18next&#x27; import en from &#x27;@/locales/en.json&#x27; import zhCN from &#x27;@/locales/zh_CN.json&#x27; i18next.use(initReactI18next).init({ lng: &#x27;zh&#x27;, fallbackLng: &#x27;en&#x27;, resources: { en: { translation: en, }, zh: { translation: zhCN, }, }, })</code></pre>`
const result = extractCodeFromHtml(htmlString)
expect(result).toMatchInlineSnapshot(
`"import i18next from 'i18next' import { initReactI18next } from 'react-i18next' import en from '@/locales/en.json' import zhCN from '@/locales/zh_CN.json' i18next.use(initReactI18next).init({ lng: 'zh', fallbackLng: 'en', resources: { en: { translation: en, }, zh: { translation: zhCN, }, }, })"`,
)
})
it("pre > code 2", () => {
const htmlString = `<pre><code class="language-ts">import type { Namespace } from '@/types'; export const namespace: Namespace = { // ... }; </code></pre>`
const result = extractCodeFromHtml(htmlString)
expect(result).toMatchInlineSnapshot(
`"import type { Namespace } from '@/types'; export const namespace: Namespace = { // ... }; "`,
)
})
it("pre", () => {
const htmlString = `<pre>@keyframes seed {
0%{--seed:0}1%{--seed:1}2%{--seed:2}3%{--seed:3}4%{--seed:4}5%{--seed:5}6%{--seed:6}7%{--seed:7}8%{--seed:8}9%{--seed:9}10%{--seed:10}11%{--seed:11}...95%{--seed:95}96%{--seed:96}97%{--seed:97}98%{--seed:98}99%{--seed:99}100%{--seed:100}
}</pre>`
const result = extractCodeFromHtml(htmlString)
expect(result).toMatchInlineSnapshot(`"@keyframes seed {
0%{--seed:0}1%{--seed:1}2%{--seed:2}3%{--seed:3}4%{--seed:4}5%{--seed:5}6%{--seed:6}7%{--seed:7}8%{--seed:8}9%{--seed:9}10%{--seed:10}11%{--seed:11}...95%{--seed:95}96%{--seed:96}97%{--seed:97}98%{--seed:98}99%{--seed:99}100%{--seed:100}
}"`)
})
it("pre 2", () => {
const htmlString = `<pre>@property --seed {
syntax: "&lt;integer&gt;";
inherits: true;
initial-value: 0;
}
@keyframes seed {
from { --seed: 0; }
to { --seed: 100; }
}</pre>`
const result = extractCodeFromHtml(htmlString)
expect(result).toMatchInlineSnapshot(`
"@property --seed {
syntax: "<integer>";
inherits: true;
initial-value: 0;
}
@keyframes seed {
from { --seed: 0; }
to { --seed: 100; }
}"
`)
})
it("pre > lang-*", () => {
const htmlString = `<pre><code class="lang-javascript">module.exports = {
output: &quot;standalone&quot;,
}</code></pre>`
const result = extractCodeFromHtml(htmlString)
expect(result).toMatchInlineSnapshot(`
"module.exports = {
output: "standalone",
}"
`)
})
it("shiki render", () => {
const htmlString = `<pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line" style="width: 920px;"><span style="color:#D73A49;--shiki-dark:#F97583">import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> i18next </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> 'i18next'</span></span>
<span class="line" style="width: 920px;"><span style="color:#D73A49;--shiki-dark:#F97583">import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { initReactI18next } </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> 'react-i18next'</span></span>
<span class="line" style="width: 920px;"></span>
<span class="line" style="width: 920px;"><span style="color:#D73A49;--shiki-dark:#F97583">import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> en </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> '@/locales/en.json'</span></span>
<span class="line" style="width: 920px;"><span style="color:#D73A49;--shiki-dark:#F97583">import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> zhCN </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> '@/locales/zh_CN.json'</span></span>
<span class="line" style="width: 920px;"></span>
<span class="line" style="width: 920px;"><span style="color:#24292E;--shiki-dark:#E1E4E8">i18next.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">use</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(initReactI18next).</span><span style="color:#6F42C1;--shiki-dark:#B392F0">init</span><span style="color:#24292E;--shiki-dark:#E1E4E8">({</span></span>
<span class="line" style="width: 920px;"><span style="color:#24292E;--shiki-dark:#E1E4E8"> lng: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">'zh'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line" style="width: 920px;"><span style="color:#24292E;--shiki-dark:#E1E4E8"> fallbackLng: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">'en'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line" style="width: 920px;"><span style="color:#24292E;--shiki-dark:#E1E4E8"> resources: {</span></span>
<span class="line" style="width: 920px;"><span style="color:#24292E;--shiki-dark:#E1E4E8"> en: {</span></span>
<span class="line" style="width: 920px;"><span style="color:#24292E;--shiki-dark:#E1E4E8"> translation: en,</span></span>
<span class="line" style="width: 920px;"><span style="color:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line" style="width: 920px;"><span style="color:#24292E;--shiki-dark:#E1E4E8"> zh: {</span></span>
<span class="line" style="width: 920px;"><span style="color:#24292E;--shiki-dark:#E1E4E8"> translation: zhCN,</span></span>
<span class="line" style="width: 920px;"><span style="color:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line" style="width: 920px;"><span style="color:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line" style="width: 920px;"><span style="color:#24292E;--shiki-dark:#E1E4E8">})</span></span></code></pre>`
const result = extractCodeFromHtml(htmlString)
expect(result).toMatchInlineSnapshot(`
"import i18next from 'i18next'
import { initReactI18next } from 'react-i18next'
import en from '@/locales/en.json'
import zhCN from '@/locales/zh_CN.json'
i18next.use(initReactI18next).init({
lng: 'zh',
fallbackLng: 'en',
resources: {
en: {
translation: en,
},
zh: {
translation: zhCN,
},
},
})
"
`)
})
it("pre > code, without line wrapper", () => {
const htmlString = `<pre class=" language-dockerfile"><code class=" language-dockerfile"><span class="token keyword">FROM</span> node<span class="token punctuation">:</span>20.15<span class="token punctuation">-</span>alpine AS runner
<span class="token keyword">ENV</span> NODE_ENV production
<span class="token comment"># Create app directory</span>
<span class="token keyword">WORKDIR</span> /app
<span class="token keyword">RUN</span> addgroup <span class="token punctuation">-</span><span class="token punctuation">-</span>system <span class="token punctuation">-</span><span class="token punctuation">-</span>gid 1001 nodejs
<span class="token keyword">RUN</span> adduser <span class="token punctuation">-</span><span class="token punctuation">-</span>system <span class="token punctuation">-</span><span class="token punctuation">-</span>uid 1001 nextjs
<span class="token keyword">COPY</span> public /app/public
<span class="token comment"># Set the correct permission for prerender cache</span>
<span class="token keyword">RUN</span> mkdir .next
<span class="token keyword">RUN</span> chown nextjs<span class="token punctuation">:</span>nodejs .next
<span class="token comment"># Automatically leverage output traces to reduce image size</span>
<span class="token comment"># https://nextjs.org/docs/advanced-features/output-file-tracing</span>
<span class="token keyword">COPY</span> <span class="token punctuation">-</span><span class="token punctuation">-</span>chown=nextjs<span class="token punctuation">:</span>nodejs .next/standalone ./
<span class="token keyword">COPY</span> <span class="token punctuation">-</span><span class="token punctuation">-</span>chown=nextjs<span class="token punctuation">:</span>nodejs .next/static .next/static
<span class="token keyword">USER</span> nextjs
<span class="token keyword">EXPOSE</span> 3000
<span class="token keyword">ENV</span> PORT 3000
<span class="token keyword">CMD</span> <span class="token punctuation">[</span><span class="token string">"ls"</span><span class="token punctuation">,</span> <span class="token string">"-l"</span><span class="token punctuation">]</span>
<span class="token comment"># server.js is created by next build from the standalone output</span>
<span class="token comment"># https://nextjs.org/docs/pages/api-reference/next-config-js/output</span>
<span class="token keyword">CMD</span> HOSTNAME=<span class="token string">"0.0.0.0"</span> node server.js</code></pre>`
const result = extractCodeFromHtml(htmlString)
expect(result).toMatchInlineSnapshot(`
"FROM node:20.15-alpine AS runner
ENV NODE_ENV production
# Create app directory
WORKDIR /app
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY public /app/public
# Set the correct permission for prerender cache
RUN mkdir .next
RUN chown nextjs:nodejs .next
# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY --chown=nextjs:nodejs .next/standalone ./
COPY --chown=nextjs:nodejs .next/static .next/static
USER nextjs
EXPOSE 3000
ENV PORT 3000
CMD ["ls", "-l"]
# server.js is created by next build from the standalone output
# https://nextjs.org/docs/pages/api-reference/next-config-js/output
CMD HOSTNAME="0.0.0.0" node server.js"
`)
})
it("hijs", () => {
const htmlString = `<pre tabindex="0" class="hljs language-css"><a href="javascript:" class="copy" tabindex="0" title="复制"><svg class="icon-copy"><use xlink:href="#icon-copy"></use></svg></a><a href="javascript:" class="beatuy revert" tabindex="0" title="还原"><svg class="icon-palette"><use xlink:href="#icon-palette"></use></svg></a><span class="hljs-keyword">@keyframes</span> seed {
<span class="hljs-number">0%</span>{<span class="hljs-attr">--seed</span>:<span class="hljs-number">0</span>}<span class="hljs-number">1%</span>{<span class="hljs-attr">--seed</span>:<span class="hljs-number">1</span>}<span class="hljs-number">2%</span>{<span class="hljs-attr">--seed</span>:<span class="hljs-number">2</span>}<span class="hljs-number">3%</span>{<span class="hljs-attr">--seed</span>:<span class="hljs-number">3</span>}<span class="hljs-number">4%</span>{<span class="hljs-attr">--seed</span>:<span class="hljs-number">4</span>}<span class="hljs-number">5%</span>{<span class="hljs-attr">--seed</span>:<span class="hljs-number">5</span>}<span class="hljs-number">6%</span>{<span class="hljs-attr">--seed</span>:<span class="hljs-number">6</span>}<span class="hljs-number">7%</span>{<span class="hljs-attr">--seed</span>:<span class="hljs-number">7</span>}<span class="hljs-number">8%</span>{<span class="hljs-attr">--seed</span>:<span class="hljs-number">8</span>}<span class="hljs-number">9%</span>{<span class="hljs-attr">--seed</span>:<span class="hljs-number">9</span>}<span class="hljs-number">10%</span>{<span class="hljs-attr">--seed</span>:<span class="hljs-number">10</span>}<span class="hljs-number">11%</span>{<span class="hljs-attr">--seed</span>:<span class="hljs-number">11</span>}...<span class="hljs-number">95%</span>{<span class="hljs-attr">--seed</span>:<span class="hljs-number">95</span>}<span class="hljs-number">96%</span>{<span class="hljs-attr">--seed</span>:<span class="hljs-number">96</span>}<span class="hljs-number">97%</span>{<span class="hljs-attr">--seed</span>:<span class="hljs-number">97</span>}<span class="hljs-number">98%</span>{<span class="hljs-attr">--seed</span>:<span class="hljs-number">98</span>}<span class="hljs-number">99%</span>{<span class="hljs-attr">--seed</span>:<span class="hljs-number">99</span>}<span class="hljs-number">100%</span>{<span class="hljs-attr">--seed</span>:<span class="hljs-number">100</span>}
}</pre>`
const result = extractCodeFromHtml(htmlString)
expect(result).toMatchInlineSnapshot(`
"@keyframes seed {
0%{--seed:0}1%{--seed:1}2%{--seed:2}3%{--seed:3}4%{--seed:4}5%{--seed:5}6%{--seed:6}7%{--seed:7}8%{--seed:8}9%{--seed:9}10%{--seed:10}11%{--seed:11}...95%{--seed:95}96%{--seed:96}97%{--seed:97}98%{--seed:98}99%{--seed:99}100%{--seed:100}
}"
`)
})
it("hijs 2", () => {
const htmlString = `<pre translate="no" class="flat-scrollbar-normal not-prose rounded-md bg-[#F6F8FA] p-2 text-sm selection:bg-gray-300 selection:text-inherit dark:bg-[#0d1117] dark:selection:bg-gray-700"><code class="hljs language-typescript"><span class="hljs-keyword">function</span> <span class="hljs-title function_">randInterval</span>(<span class="hljs-params"></span>): <span class="hljs-built_in">number</span> {
<span class="hljs-keyword">return</span> <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">random</span>();
}
<span class="hljs-keyword">function</span> <span class="hljs-title function_">boxMuller</span>(<span class="hljs-params">mu: <span class="hljs-built_in">number</span>, sigma: <span class="hljs-built_in">number</span></span>): [<span class="hljs-built_in">number</span>, <span class="hljs-built_in">number</span>] {
<span class="hljs-keyword">const</span> u = <span class="hljs-title function_">randInterval</span>();
<span class="hljs-keyword">const</span> v = <span class="hljs-title function_">randInterval</span>();
<span class="hljs-keyword">const</span> x = <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">cos</span>(<span class="hljs-number">2</span> * <span class="hljs-title class_">Math</span>.<span class="hljs-property">PI</span> * u) * <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">sqrt</span>(-<span class="hljs-number">2</span> * <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">log</span>(v));
<span class="hljs-keyword">const</span> y = <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">sin</span>(<span class="hljs-number">2</span> * <span class="hljs-title class_">Math</span>.<span class="hljs-property">PI</span> * u) * <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">sqrt</span>(-<span class="hljs-number">2</span> * <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">log</span>(v));
<span class="hljs-keyword">return</span> [x * sigma + mu, y * sigma + mu];
}
<span class="hljs-comment">// Usage example</span>
<span class="hljs-keyword">const</span> [value1, value2] = <span class="hljs-title function_">boxMuller</span>(<span class="hljs-number">0</span>, <span class="hljs-number">1</span>);
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(value1, value2);
<span class="hljs-comment">// Outputs two normally distributed random numbers</span>
</code></pre>`
const result = extractCodeFromHtml(htmlString)
expect(result).toMatchInlineSnapshot(`
"function randInterval(): number {
return Math.random();
}
function boxMuller(mu: number, sigma: number): [number, number] {
const u = randInterval();
const v = randInterval();
const x = Math.cos(2 * Math.PI * u) * Math.sqrt(-2 * Math.log(v));
const y = Math.sin(2 * Math.PI * u) * Math.sqrt(-2 * Math.log(v));
return [x * sigma + mu, y * sigma + mu];
}
// Usage example
const [value1, value2] = boxMuller(0, 1);
console.log(value1, value2);
// Outputs two normally distributed random numbers
"
`)
})
it("hijs without wrapper", () => {
const htmlString = `<pre><span>import</span> * <span>as</span> fs <span>from</span> <span>&quot;fs&quot;</span>;
<span>import</span> * <span>as</span> plotly <span>from</span> <span>&quot;plotly.js-dist&quot;</span>;
<span>// Generate random numbers following a normal distribution using Box-Muller</span>
<span>function</span> <span>randInterval</span>(<span></span>): <span>number</span> {
<span>return</span> <span>Math</span>.<span>random</span>();
}
<span>function</span> <span>boxMuller</span>(<span>mu: <span>number</span>, sigma: <span>number</span></span>): [<span>number</span>, <span>number</span>] {
<span>const</span> u = <span>randInterval</span>();
<span>const</span> v = <span>randInterval</span>();
<span>const</span> x = <span>Math</span>.<span>cos</span>(<span>2</span> * <span>Math</span>.<span>PI</span> * u) * <span>Math</span>.<span>sqrt</span>(-<span>2</span> * <span>Math</span>.<span>log</span>(v));
<span>const</span> y = <span>Math</span>.<span>sin</span>(<span>2</span> * <span>Math</span>.<span>PI</span> * u) * <span>Math</span>.<span>sqrt</span>(-<span>2</span> * <span>Math</span>.<span>log</span>(v));
<span>return</span> [x * sigma + mu, y * sigma + mu];
}
<span>// Generate n normally distributed random numbers</span>
<span>function</span> <span>generateNormalDistribution</span>(<span>
mu: <span>number</span>,
sigma: <span>number</span>,
n: <span>number</span>
</span>): <span>number</span>[] {
<span>const</span> <span>values</span>: <span>number</span>[] = [];
<span>for</span> (<span>let</span> i = <span>0</span>; i &lt; n / <span>2</span>; i++) {
<span>const</span> [value1, value2] = <span>boxMuller</span>(mu, sigma);
values.<span>push</span>(value1, value2);
}
<span>return</span> values;
}
<span>// Generate the data</span>
<span>const</span> mu = <span>0</span>;
<span>const</span> sigma = <span>1</span>;
<span>const</span> sampleSize = <span>10000</span>; <span>// Sample size</span>
<span>const</span> values = <span>generateNormalDistribution</span>(mu, sigma, sampleSize);
<span>// Calculate the mean and variance of the generated random numbers</span>
<span>const</span> mean = values.<span>reduce</span>(<span>(<span>acc, val</span>) =&gt;</span> acc + val, <span>0</span>) / values.<span>length</span>;
<span>const</span> variance =
values.<span>reduce</span>(<span>(<span>acc, val</span>) =&gt;</span> acc + (val - mean) ** <span>2</span>, <span>0</span>) / values.<span>length</span>;
</pre>
`
const result = extractCodeFromHtml(htmlString)
expect(result).toMatchInlineSnapshot(`
"import * as fs from "fs";
import * as plotly from "plotly.js-dist";
// Generate random numbers following a normal distribution using Box-Muller
function randInterval(): number {
return Math.random();
}
function boxMuller(mu: number, sigma: number): [number, number] {
const u = randInterval();
const v = randInterval();
const x = Math.cos(2 * Math.PI * u) * Math.sqrt(-2 * Math.log(v));
const y = Math.sin(2 * Math.PI * u) * Math.sqrt(-2 * Math.log(v));
return [x * sigma + mu, y * sigma + mu];
}
// Generate n normally distributed random numbers
function generateNormalDistribution(
mu: number,
sigma: number,
n: number
): number[] {
const values: number[] = [];
for (let i = 0; i < n / 2; i++) {
const [value1, value2] = boxMuller(mu, sigma);
values.push(value1, value2);
}
return values;
}
// Generate the data
const mu = 0;
const sigma = 1;
const sampleSize = 10000; // Sample size
const values = generateNormalDistribution(mu, sigma, sampleSize);
// Calculate the mean and variance of the generated random numbers
const mean = values.reduce((acc, val) => acc + val, 0) / values.length;
const variance =
values.reduce((acc, val) => acc + (val - mean) ** 2, 0) / values.length;
"
`)
})
it("no <code />", () => {
const htmlString = `<span class="line"><span class="keyword">if</span> theme.<span class="property">twikoo</span>.<span class="property">enable</span> == <span class="literal">true</span></span><br><span class="line"> #tcomment</span><br><span class="line"> <span class="title function_">script</span>(src=<span class="string">'https://registry.npmmirror.com/twikoo/1.6.39/files/dist/twikoo.all.min.js'</span>)</span><br><span class="line"> script.</span><br><span class="line"> twikoo.<span class="title function_">init</span>({</span><br><span class="line"> <span class="attr">envId</span>: <span class="string">'#{theme.twikoo.envId}'</span>,</span><br><span class="line"> <span class="attr">el</span>: <span class="string">'#tcomment'</span>,</span><br><span class="line"> <span class="attr">region</span>: <span class="string">'#{theme.twikoo.region}'</span>,</span><br><span class="line"> <span class="attr">path</span>: <span class="string">'#{theme.twikoo.path}'</span>,</span><br><span class="line"> <span class="attr">onCommentLoaded</span>: <span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">const</span> commentCountElement = <span class="variable language_">document</span>.<span class="title function_">querySelector</span>(<span class="string">'.tk-comments-count'</span>);</span><br><span class="line"> <span class="keyword">const</span> targetElement = <span class="variable language_">document</span>.<span class="title function_">querySelector</span>(<span class="string">'.waline-comment-count'</span>);</span><br><span class="line"> <span class="keyword">if</span> (commentCountElement) {</span><br><span class="line"> <span class="keyword">const</span> countSpan = commentCountElement.<span class="title function_">querySelector</span>(<span class="string">'span:first-child'</span>);</span><br><span class="line"> <span class="keyword">const</span> commentCount = <span class="built_in">parseInt</span>(countSpan.<span class="property">textContent</span>);</span><br><span class="line"> targetElement.<span class="property">textContent</span> = commentCount;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'未找到评论数量元素'</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> })</span><br><span class="line"></span><br>`
const result = extractCodeFromHtml(htmlString)
expect(result).toMatchInlineSnapshot(
`"if theme.twikoo.enable == true #tcomment script(src='https://registry.npmmirror.com/twikoo/1.6.39/files/dist/twikoo.all.min.js') script. twikoo.init({ envId: '#{theme.twikoo.envId}', el: '#tcomment', region: '#{theme.twikoo.region}', path: '#{theme.twikoo.path}', onCommentLoaded: function () { const commentCountElement = document.querySelector('.tk-comments-count'); const targetElement = document.querySelector('.waline-comment-count'); if (commentCountElement) { const countSpan = commentCountElement.querySelector('span:first-child'); const commentCount = parseInt(countSpan.textContent); targetElement.textContent = commentCount; } else { console.log('未找到评论数量元素'); } } })"`,
)
})
})
@@ -8,7 +8,7 @@ import {
parseRegexpPathParams,
regexpPathToPath,
transformUriPath,
} from "./path-parser"
} from "../path-parser"
describe("test `transformUriPath()`", () => {
test("normal path", () => {
+18 -5
View File
@@ -177,11 +177,11 @@ export const parseHtml = (
codeString = props.children.toString()
} else {
const propsChildren = props.children
const children = Array.isArray(propsChildren)
let children = Array.isArray(propsChildren)
? propsChildren.find((i) => i.type === "code")
: propsChildren
if (!children) return createElement("pre", props, props.children)
if (!children) children = createElement("pre", props, props.children)
if (
"type" in children &&
@@ -190,8 +190,8 @@ export const parseHtml = (
) {
language = children.props.className.replace("language-", "")
}
const code = "props" in children && children.props.children
if (!code) return createElement("pre", props, props.children)
const code = ("props" in children && children.props.children) || children
if (!code) return null
try {
codeString = extractCodeFromHtml(renderToString(code))
@@ -241,10 +241,15 @@ const Img: Components["img"] = ({ node, ...props }) => {
)
}
function extractCodeFromHtml(htmlString: string) {
export function extractCodeFromHtml(htmlString: string) {
const tempDiv = document.createElement("div")
tempDiv.innerHTML = htmlString
const hasPre = tempDiv.querySelector("pre")
if (!hasPre) {
tempDiv.innerHTML = `<pre><code>${htmlString}</code></pre>`
}
// 1. line break via <div />
const divElements = tempDiv.querySelectorAll("div")
@@ -272,6 +277,14 @@ function extractCodeFromHtml(htmlString: string) {
if (spanAsLineBreak) break
}
}
if (!spanAsLineBreak) {
const usingBr = tempDiv.querySelector("br")
if (usingBr) {
spanAsLineBreak = true
}
}
if (spanElements.length > 0) {
for (const node of tempDiv.children) {
if (spanAsLineBreak) {
+5 -1
View File
@@ -5,6 +5,8 @@ import { fileURLToPath } from "node:url"
import tsconfigPath from "vite-tsconfig-paths"
import { defineProject } from "vitest/config"
import { twMacro } from "../../plugins/vite/tw-macro"
const pkg = JSON.parse(readFileSync("package.json", "utf8"))
const __dirname = fileURLToPath(new URL(".", import.meta.url))
@@ -13,7 +15,7 @@ export default defineProject({
test: {
globals: true,
setupFiles: [resolve(__dirname, "./setup-file.ts")],
environment: "node",
environment: "happy-dom",
includeSource: [resolve(__dirname, ".")],
},
@@ -31,5 +33,7 @@ export default defineProject({
tsconfigPath({
projects: ["./tsconfig.json"],
}),
twMacro(),
],
})
+3 -2
View File
@@ -79,6 +79,7 @@
"eslint": "^9.12.0",
"eslint-config-hyoban": "^3.1.10",
"fake-indexeddb": "6.0.0",
"happy-dom": "15.7.4",
"hono": "4.6.3",
"lint-staged": "15.2.10",
"nbump": "2.0.4",
@@ -104,10 +105,10 @@
"patchedDependencies": {
"sonner@1.5.0": "patches/sonner@1.5.0.patch",
"immer@10.1.1": "patches/immer@10.1.1.patch",
"@mozilla/readability@0.5.0": "patches/@mozilla__readability@0.5.0.patch",
"re-resizable": "patches/re-resizable@6.9.17.patch",
"hono": "patches/hono.patch",
"electron-context-menu": "patches/electron-context-menu.patch"
"electron-context-menu": "patches/electron-context-menu.patch",
"@mozilla/readability": "patches/@mozilla__readability.patch"
},
"overrides": {
"is-core-module": "npm:@nolyfill/is-core-module@^1",
+13
View File
@@ -0,0 +1,13 @@
diff --git a/Readability-readerable.js b/Readability-readerable.js
index 892169fb94999987e895846b7605f9c4cf32802c..6c75e61f3c5e0b6143860fa1aa8c652548a78760 100644
--- a/Readability-readerable.js
+++ b/Readability-readerable.js
@@ -29,7 +29,7 @@ var REGEXPS = {
function isNodeVisible(node) {
// Have to null-check node.style and node.className.indexOf to deal with SVG and MathML nodes.
return (!node.style || node.style.display != "none")
- && !node.hasAttribute("hidden")
+ && !(node.hasAttribute("hidden") && !node.id.startsWith("S:"))
//check for "fallback-image" so that wikimedia math images are displayed
&& (!node.hasAttribute("aria-hidden") || node.getAttribute("aria-hidden") != "true" || (node.className && node.className.indexOf && node.className.indexOf("fallback-image") !== -1));
}
File diff suppressed because it is too large Load Diff
+36 -10
View File
@@ -9,9 +9,9 @@ overrides:
isarray: npm:@nolyfill/isarray@^1
patchedDependencies:
'@mozilla/readability@0.5.0':
hash: fgkvsbckled47trggkhdkimzbm
path: patches/@mozilla__readability@0.5.0.patch
'@mozilla/readability':
hash: 43niildbdafdxi7qfcwhpkkxwa
path: patches/@mozilla__readability.patch
electron-context-menu:
hash: c53at4t5fflixuwjz35hmcqdu4
path: patches/electron-context-menu.patch
@@ -88,7 +88,7 @@ importers:
version: 4.3.1
'@mozilla/readability':
specifier: ^0.5.0
version: 0.5.0(patch_hash=fgkvsbckled47trggkhdkimzbm)
version: 0.5.0(patch_hash=43niildbdafdxi7qfcwhpkkxwa)
'@pengx17/electron-forge-maker-appimage':
specifier: 1.2.1
version: 1.2.1(dmg-builder@25.0.5(electron-builder-squirrel-windows@25.0.5))(electron-builder-squirrel-windows@25.0.5(dmg-builder@25.0.5))
@@ -155,6 +155,9 @@ importers:
fake-indexeddb:
specifier: 6.0.0
version: 6.0.0
happy-dom:
specifier: 15.7.4
version: 15.7.4
hono:
specifier: 4.6.3
version: 4.6.3(patch_hash=iej2g43ojhll5opwbnvyorjq4e)
@@ -214,7 +217,7 @@ importers:
version: 5.0.1(typescript@5.6.2)(vite@5.4.8(@types/node@22.7.4)(terser@5.34.1))
vitest:
specifier: 2.0.5
version: 2.0.5(@types/node@22.7.4)(terser@5.34.1)
version: 2.0.5(@types/node@22.7.4)(happy-dom@15.7.4)(terser@5.34.1)
apps/main:
dependencies:
@@ -235,7 +238,7 @@ importers:
version: link:../../packages/shared
'@mozilla/readability':
specifier: ^0.5.0
version: 0.5.0(patch_hash=fgkvsbckled47trggkhdkimzbm)
version: 0.5.0(patch_hash=43niildbdafdxi7qfcwhpkkxwa)
'@sentry/electron':
specifier: 5.4.0
version: 5.4.0
@@ -350,7 +353,7 @@ importers:
version: 4.3.1
'@mozilla/readability':
specifier: ^0.5.0
version: 0.5.0(patch_hash=fgkvsbckled47trggkhdkimzbm)
version: 0.5.0(patch_hash=43niildbdafdxi7qfcwhpkkxwa)
'@radix-ui/react-avatar':
specifier: 1.1.0
version: 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -642,7 +645,7 @@ importers:
version: 18.3.1(react@18.3.1)
vitest:
specifier: '2.0'
version: 2.0.5(@types/node@22.7.4)(terser@5.34.1)
version: 2.0.5(@types/node@22.7.4)(happy-dom@15.7.4)(terser@5.34.1)
packages/shared:
dependencies:
@@ -5672,6 +5675,10 @@ packages:
engines: {node: '>=0.4.7'}
hasBin: true
happy-dom@15.7.4:
resolution: {integrity: sha512-r1vadDYGMtsHAAsqhDuk4IpPvr6N8MGKy5ntBo7tSdim+pWDxus2PNqOcOt8LuDZ4t3KJHE+gCuzupcx/GKnyQ==}
engines: {node: '>=18.0.0'}
has-flag@3.0.0:
resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
engines: {node: '>=4'}
@@ -8760,6 +8767,10 @@ packages:
webidl-conversions@4.0.2:
resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==}
webidl-conversions@7.0.0:
resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==}
engines: {node: '>=12'}
webpack-sources@3.2.3:
resolution: {integrity: sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==}
engines: {node: '>=10.13.0'}
@@ -8771,6 +8782,10 @@ packages:
resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==}
engines: {node: '>=18'}
whatwg-mimetype@3.0.0:
resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==}
engines: {node: '>=12'}
whatwg-mimetype@4.0.0:
resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==}
engines: {node: '>=18'}
@@ -10768,7 +10783,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@mozilla/readability@0.5.0(patch_hash=fgkvsbckled47trggkhdkimzbm)': {}
'@mozilla/readability@0.5.0(patch_hash=43niildbdafdxi7qfcwhpkkxwa)': {}
'@nodelib/fs.scandir@2.1.5':
dependencies:
@@ -15116,6 +15131,12 @@ snapshots:
optionalDependencies:
uglify-js: 3.19.2
happy-dom@15.7.4:
dependencies:
entities: 4.5.0
webidl-conversions: 7.0.0
whatwg-mimetype: 3.0.0
has-flag@3.0.0: {}
has-flag@4.0.0: {}
@@ -18570,7 +18591,7 @@ snapshots:
fsevents: 2.3.3
terser: 5.34.1
vitest@2.0.5(@types/node@22.7.4)(terser@5.34.1):
vitest@2.0.5(@types/node@22.7.4)(happy-dom@15.7.4)(terser@5.34.1):
dependencies:
'@ampproject/remapping': 2.3.0
'@vitest/expect': 2.0.5
@@ -18593,6 +18614,7 @@ snapshots:
why-is-node-running: 2.3.0
optionalDependencies:
'@types/node': 22.7.4
happy-dom: 15.7.4
transitivePeerDependencies:
- less
- lightningcss
@@ -18617,6 +18639,8 @@ snapshots:
webidl-conversions@4.0.2: {}
webidl-conversions@7.0.0: {}
webpack-sources@3.2.3: {}
webpack-virtual-modules@0.5.0: {}
@@ -18625,6 +18649,8 @@ snapshots:
dependencies:
iconv-lite: 0.6.3
whatwg-mimetype@3.0.0: {}
whatwg-mimetype@4.0.0: {}
whatwg-url@5.0.0: