From 49736330792b344f172943b858237b2ffe0fb7a0 Mon Sep 17 00:00:00 2001 From: WilcoSp Date: Wed, 2 Sep 2026 16:37:04 +0200 Subject: [PATCH 01/17] added support for gitea for changelogs --- .../[owner]/[repo]/[...path].get.ts | 3 + .../releases/[provider]/[owner]/[repo].get.ts | 35 +++++++++- server/utils/changelog/baseFileUrl.ts | 12 +++- server/utils/changelog/detectChangelog.ts | 69 +++++++++++++++++++ server/utils/changelog/mdRepoInfo.ts | 22 ++++++ shared/schemas/changelog/release.ts | 5 ++ 6 files changed, 142 insertions(+), 4 deletions(-) diff --git a/server/api/changelog/md/[provider]/[owner]/[repo]/[...path].get.ts b/server/api/changelog/md/[provider]/[owner]/[repo]/[...path].get.ts index a1582b1414..66bd991de7 100644 --- a/server/api/changelog/md/[provider]/[owner]/[repo]/[...path].get.ts +++ b/server/api/changelog/md/[provider]/[owner]/[repo]/[...path].get.ts @@ -3,6 +3,7 @@ import * as v from 'valibot' import { getBaseFileUrl } from '~~/server/utils/changelog/baseFileUrl' import { createForgejoRepoInfo, + createGiteaRepoInfo, createGithubRepoInfo, createGitLabRepoInfo, createTangledInfo, @@ -114,5 +115,7 @@ function getRepoInfo( return createGitLabRepoInfo(host ?? 'gitlab.com', owner, repo, path) case 'tangled': return createTangledInfo(owner, repo, path) + case 'gitea': + return createGiteaRepoInfo(host ?? 'gitea.com', owner, repo, path) } } diff --git a/server/api/changelog/releases/[provider]/[owner]/[repo].get.ts b/server/api/changelog/releases/[provider]/[owner]/[repo].get.ts index 20e618072a..cac921157f 100644 --- a/server/api/changelog/releases/[provider]/[owner]/[repo].get.ts +++ b/server/api/changelog/releases/[provider]/[owner]/[repo].get.ts @@ -9,9 +9,14 @@ import { GithubReleaseCollectionSchama, ForgejoReleaseCollectionSchema, GitlabReleaseCollectionSchema, + GiteaReleaseCollectionSchema, } from '~~/shared/schemas/changelog/release' import { changelogRenderer } from '~~/server/utils/changelog/markdown' -import { createForgejoRepoInfo, createGithubRepoInfo } from '~~/server/utils/changelog/mdRepoInfo' +import { + createForgejoRepoInfo, + createGiteaRepoInfo, + createGithubRepoInfo, +} from '~~/server/utils/changelog/mdRepoInfo' import { validateHostWithValibot } from '~~/server/utils/changelog/validateHost' export default defineCachedEventHandler( @@ -39,6 +44,8 @@ export default defineCachedEventHandler( return await getReleasesFromForgejo(owner, repo, host) case 'gitlab': return await getReleasesFromGitlab(owner, repo, host) + case 'gitea': + return await getReleasesFromGitea(owner, repo, host) default: throw createError({ @@ -158,3 +165,29 @@ async function getReleasesFromGitlab(owner: string, repo: string, host: string) } satisfies ReleaseData }) } + +async function getReleasesFromGitea(owner: string, repo: string, host: string) { + const data = await $fetch(`https://${host}/api/v1/repos/${owner}/${repo}/releases?draft=false`, { + headers: { + 'User-Agent': 'npmx.dev', + }, + }) + const releases = v.parse(GiteaReleaseCollectionSchema, data) + + const render = await changelogRenderer(createGiteaRepoInfo(host, owner, repo)) + + return releases.map(r => { + const { html, toc } = render(r.body, r.id) + return { + id: r.id, + html: html?.replace(/(?)\n/g, '
') ?? null, + title: r.name || r.tag_name, + prerelease: r.prerelease, + toc, + link: r.html_url, + publishedAt: r.published_at, + draft: r.draft, + tag: r.tag_name, + } satisfies ReleaseData + }) +} diff --git a/server/utils/changelog/baseFileUrl.ts b/server/utils/changelog/baseFileUrl.ts index 7cda63169b..9f79a7e226 100644 --- a/server/utils/changelog/baseFileUrl.ts +++ b/server/utils/changelog/baseFileUrl.ts @@ -16,10 +16,16 @@ export function getBaseFileUrl(ref: RepoRef): RepoFileUrl | null { raw: `https://codeberg.org/${ref.owner}/${ref.repo}/raw/branch/HEAD`, } case 'forgejo': { - const host = ref.host return { - blob: `https://${host}/${ref.owner}/${ref.repo}/src/branch/HEAD`, - raw: `https://${host}/${ref.owner}/${ref.repo}/raw/branch/HEAD`, + blob: `https://${ref.host}/${ref.owner}/${ref.repo}/src/branch/HEAD`, + raw: `https://${ref.host}/${ref.owner}/${ref.repo}/raw/branch/HEAD`, + } + } + case 'gitea': { + // although similar to forgejo, we keep these seperate because they're seperate maintained projects + return { + blob: `https://${ref.host}/${ref.owner}/${ref.repo}/src/branch/HEAD`, + raw: `https://${ref.host}/${ref.owner}/${ref.repo}/raw/branch/HEAD`, } } case 'gitlab': { diff --git a/server/utils/changelog/detectChangelog.ts b/server/utils/changelog/detectChangelog.ts index 6289a7aebd..1d04fd86df 100644 --- a/server/utils/changelog/detectChangelog.ts +++ b/server/utils/changelog/detectChangelog.ts @@ -8,6 +8,7 @@ import { GithubReleaseSchama, ForgejoReleaseSchama, GitlabReleaseSchame, + GiteaReleaseSchema, } from '~~/shared/schemas/changelog/release' import { resolveURL } from 'ufo' import * as v from 'valibot' @@ -66,6 +67,9 @@ async function checkReleases( case 'gitlab': { return checkLatestGitlabRelease(ref, directory) } + case 'gitea': { + return checkLatestGiteaRelease(ref, directory) + } } return [false, null] } @@ -340,3 +344,68 @@ async function checkLatestGitlabRelease( } return [false, null] } + +// gitea +async function checkLatestGiteaRelease( + ref: RepoRef, + directory?: string, +): Promise> { + try { + const host = ref.host ?? 'gitea.com' + + const response = await $fetch( + `https://${host}/api/v1/repos/${ref.owner}/${ref.repo}/releases/latest`, + { + headers: { + 'User-Agent': 'npmx.dev', + 'accept': 'application/json', + }, + }, + ) + + const release = v.parse(GiteaReleaseSchema, response) + + const matchedChangelog = release.body?.match(MD_REGEX)?.at(0) + + // /src/branch/ can be similar to /blob/ + if (!matchedChangelog || !matchedChangelog.includes('/src/branch/')) { + return [ + { + type: 'release', + link: `https://${host}/${ref.owner}/${ref.repo}/releases`, + provider: ref.provider, + repo: `${ref.owner}/${ref.repo}`, + host: ref.host, + }, + null, + ] + } + + const path = matchedChangelog.replace(/^.*\/src\/branch\/[^/]+\//i, '') + if ( + directory && + !( + path.startsWith(directory.endsWith('/') ? directory : `${directory}/`) || + ROOT_ONLY_REGEX.test(path) + ) + ) { + return [false, null] as const + } + return [ + { + provider: ref.provider, + type: 'md', + path, + repo: `${ref.owner}/${ref.repo}`, + link: matchedChangelog, + host: ref.host, + }, + null, + ] + } catch (e) { + if (e instanceof Error) { + return [null, e] + } + } + return [false, null] +} diff --git a/server/utils/changelog/mdRepoInfo.ts b/server/utils/changelog/mdRepoInfo.ts index fca0f1dd74..ece9875ccf 100644 --- a/server/utils/changelog/mdRepoInfo.ts +++ b/server/utils/changelog/mdRepoInfo.ts @@ -78,3 +78,25 @@ export function createTangledInfo(owner: string, repo: string, path?: string): M compareBaseUrl: `${hostBaseUrl}/${owner}/${repo}/compare`, } } + +// similar to forgejo, but they're seperate projects so keeping this also seperate +export function createGiteaRepoInfo( + host: string, + owner: string, + repo: string, + path?: string, +): MarkdownRepoInfo { + const hostBaseUrl = `https://${host}` + return { + hostBaseUrl, + blobBaseUrl: `${hostBaseUrl}/${owner}/${repo}/src/branch/HEAD`, + rawBaseUrl: `${hostBaseUrl}/${owner}/${repo}/raw/branch/HEAD`, + path, + commitBaseUrl: `${hostBaseUrl}/${owner}/${repo}/commit`, + issueChar: '#', + issueBaseUrl: `${hostBaseUrl}/${owner}/${repo}/issues`, + prChar: '#', + prBaseUrl: `${hostBaseUrl}/${owner}/${repo}/pulls`, + compareBaseUrl: `${hostBaseUrl}/${owner}/${repo}/compare`, + } +} diff --git a/shared/schemas/changelog/release.ts b/shared/schemas/changelog/release.ts index 0ac03ddd5c..d02edd9742 100644 --- a/shared/schemas/changelog/release.ts +++ b/shared/schemas/changelog/release.ts @@ -46,3 +46,8 @@ export const GitlabReleaseSchame = v.object({ }) export const GitlabReleaseCollectionSchema = v.array(GitlabReleaseSchame) + +// these are currently the same with forgejo, but exporting this one seperetely in case either change their schema +export const GiteaReleaseSchema = ForgejoReleaseSchama + +export const GiteaReleaseCollectionSchema = v.array(GiteaReleaseSchema) From 36d2ac9e2ef51be2c3f4fe14bfcc159c38248230 Mon Sep 17 00:00:00 2001 From: WilcoSp Date: Wed, 2 Sep 2026 16:45:51 +0200 Subject: [PATCH 02/17] ... --- shared/schemas/changelog/release.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/shared/schemas/changelog/release.ts b/shared/schemas/changelog/release.ts index d02edd9742..8734395b3c 100644 --- a/shared/schemas/changelog/release.ts +++ b/shared/schemas/changelog/release.ts @@ -47,7 +47,15 @@ export const GitlabReleaseSchame = v.object({ export const GitlabReleaseCollectionSchema = v.array(GitlabReleaseSchame) -// these are currently the same with forgejo, but exporting this one seperetely in case either change their schema -export const GiteaReleaseSchema = ForgejoReleaseSchama +export const GiteaReleaseSchema = v.object({ + id: v.number(), + tag_name: v.string(), + name: v.string(), + body: v.string(), + html_url: v.pipe(v.string(), v.url()), + draft: v.boolean(), + prerelease: v.boolean(), + published_at: v.pipe(v.string(), v.isoTimestamp()), +}) export const GiteaReleaseCollectionSchema = v.array(GiteaReleaseSchema) From 0756d225130180ceb9657d574b6a5f2a05d3232d Mon Sep 17 00:00:00 2001 From: WilcoSp Date: Wed, 2 Sep 2026 18:27:08 +0200 Subject: [PATCH 03/17] adding support for bitbucket to changelog --- .../[owner]/[repo]/[...path].get.ts | 3 +++ server/utils/changelog/baseFileUrl.ts | 8 ++++++- server/utils/changelog/markdown.ts | 21 +++++++++++-------- server/utils/changelog/mdRepoInfo.ts | 18 ++++++++++++++++ 4 files changed, 40 insertions(+), 10 deletions(-) diff --git a/server/api/changelog/md/[provider]/[owner]/[repo]/[...path].get.ts b/server/api/changelog/md/[provider]/[owner]/[repo]/[...path].get.ts index 66bd991de7..56bb1f112e 100644 --- a/server/api/changelog/md/[provider]/[owner]/[repo]/[...path].get.ts +++ b/server/api/changelog/md/[provider]/[owner]/[repo]/[...path].get.ts @@ -2,6 +2,7 @@ import { resolveURL } from 'ufo' import * as v from 'valibot' import { getBaseFileUrl } from '~~/server/utils/changelog/baseFileUrl' import { + createBitbucketRepoInfo, createForgejoRepoInfo, createGiteaRepoInfo, createGithubRepoInfo, @@ -117,5 +118,7 @@ function getRepoInfo( return createTangledInfo(owner, repo, path) case 'gitea': return createGiteaRepoInfo(host ?? 'gitea.com', owner, repo, path) + case 'bitbucket': + return createBitbucketRepoInfo(owner, repo, path) } } diff --git a/server/utils/changelog/baseFileUrl.ts b/server/utils/changelog/baseFileUrl.ts index 9f79a7e226..aa0b771ca9 100644 --- a/server/utils/changelog/baseFileUrl.ts +++ b/server/utils/changelog/baseFileUrl.ts @@ -22,7 +22,7 @@ export function getBaseFileUrl(ref: RepoRef): RepoFileUrl | null { } } case 'gitea': { - // although similar to forgejo, we keep these seperate because they're seperate maintained projects + // although similar to forgejo, we keep these seperate because they're seperately maintained projects return { blob: `https://${ref.host}/${ref.owner}/${ref.repo}/src/branch/HEAD`, raw: `https://${ref.host}/${ref.owner}/${ref.repo}/raw/branch/HEAD`, @@ -41,6 +41,12 @@ export function getBaseFileUrl(ref: RepoRef): RepoFileUrl | null { raw: `https://tangled.org/${ref.owner}/${ref.repo}/raw/HEAD`, } } + case 'bitbucket': { + return { + blob: `https://bitbucket.org/${ref.owner}/${ref.repo}/src/HEAD`, + raw: `https://bitbucket.org/${ref.owner}/${ref.repo}/raw/HEAD`, + } + } } return null } diff --git a/server/utils/changelog/markdown.ts b/server/utils/changelog/markdown.ts index e57d3f42e3..4edd42b82c 100644 --- a/server/utils/changelog/markdown.ts +++ b/server/utils/changelog/markdown.ts @@ -118,9 +118,9 @@ export interface MarkdownRepoInfo { /** the base url of repository commit */ commitBaseUrl: string /** base url for a repository issue */ - issueBaseUrl: string + issueBaseUrl?: string /** the text char that indicates an issue */ - issueChar: keyof typeof issuePrRegexes + issueChar?: keyof typeof issuePrRegexes /** base url for a repository pull/merge request */ prBaseUrl: string /** @@ -130,7 +130,7 @@ export interface MarkdownRepoInfo { x*/ prChar: keyof typeof issuePrRegexes /** base url for a repository compare */ - compareBaseUrl: string + compareBaseUrl?: string } function resolveUrl(url: string, repoInfo: MarkdownRepoInfo, toUserContentId: ToUserContentIdFn) { @@ -220,13 +220,13 @@ function resolveGitLinkText(href: string, label: string, repoInfo: MarkdownRepoI case href.startsWith(repoInfo.commitBaseUrl): { return lastSegment.slice(0, 7) // only show the first 6 letters/numbers of a commit } - case href.startsWith(repoInfo.issueBaseUrl): { + case !!repoInfo.issueBaseUrl && href.startsWith(repoInfo.issueBaseUrl): { return `${repoInfo.issueChar}${lastSegment}` } case href.startsWith(repoInfo.prBaseUrl): { return `${repoInfo.prChar}${lastSegment}` } - case href.startsWith(repoInfo.compareBaseUrl): { + case !!repoInfo.compareBaseUrl && href.startsWith(repoInfo.compareBaseUrl): { return lastSegment } // for account we don't resolve, this is something the git providers also don't do @@ -256,16 +256,19 @@ function createResolveGitTextToLinks(mdInfo: MarkdownRepoInfo): IOptions['textFi return `${match.slice(0, 7)}` }) - .replace(issuePrRegexes[mdInfo.issueChar], match => { - const id = match.replace(mdInfo.issueChar, '') - return `${match}` - }) // account .replace(accountRegex, match => { const acc = match.replace('@', '') return `${match}` }) + if (mdInfo.issueChar && mdInfo.issueBaseUrl) { + text = text.replace(issuePrRegexes[mdInfo.issueChar], match => { + const id = match.replace(mdInfo.issueChar!, '') + return `${match}` + }) + } + // pr/mr if (mdInfo.issueChar != mdInfo.prChar) { text = text.replace(issuePrRegexes[mdInfo.prChar], match => { diff --git a/server/utils/changelog/mdRepoInfo.ts b/server/utils/changelog/mdRepoInfo.ts index ece9875ccf..2681d9bdaf 100644 --- a/server/utils/changelog/mdRepoInfo.ts +++ b/server/utils/changelog/mdRepoInfo.ts @@ -100,3 +100,21 @@ export function createGiteaRepoInfo( compareBaseUrl: `${hostBaseUrl}/${owner}/${repo}/compare`, } } + +export function createBitbucketRepoInfo( + owner: string, + repo: string, + path?: string, +): MarkdownRepoInfo { + const hostBaseUrl = 'https://bitbucket.org' + return { + hostBaseUrl, + blobBaseUrl: `${hostBaseUrl}/${owner}/${repo}/src/HEAD`, + rawBaseUrl: `${hostBaseUrl}/${owner}/${repo}/raw/HEAD`, + path, + commitBaseUrl: `${hostBaseUrl}/${owner}/${repo}/commits`, + prChar: '#', + prBaseUrl: `${hostBaseUrl}/${owner}/${repo}/pull-requests`, + // bitbucket uses jira instead of normal issues which can't be resolved, further the compare page also seems to be different + } +} From cc30bd03e710472dc5fffc31de18de264bb959ea Mon Sep 17 00:00:00 2001 From: WilcoSp Date: Wed, 2 Sep 2026 20:39:04 +0200 Subject: [PATCH 04/17] added an timeout & added fallback for if gitea host isn't defined --- .../changelog/md/[provider]/[owner]/[repo]/[...path].get.ts | 1 + .../api/changelog/releases/[provider]/[owner]/[repo].get.ts | 6 ++++++ .../releases/[provider]/[owner]/[repo]/raw/[tag].get.ts | 1 + server/utils/changelog/baseFileUrl.ts | 5 +++-- 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/server/api/changelog/md/[provider]/[owner]/[repo]/[...path].get.ts b/server/api/changelog/md/[provider]/[owner]/[repo]/[...path].get.ts index 56bb1f112e..1f7edb0e3c 100644 --- a/server/api/changelog/md/[provider]/[owner]/[repo]/[...path].get.ts +++ b/server/api/changelog/md/[provider]/[owner]/[repo]/[...path].get.ts @@ -58,6 +58,7 @@ export default defineCachedEventHandler( headers: { 'User-Agent': 'npmx.dev', }, + timeout: 15_000, }) const markdown = v.parse(v.string(), data) if (raw != undefined) { diff --git a/server/api/changelog/releases/[provider]/[owner]/[repo].get.ts b/server/api/changelog/releases/[provider]/[owner]/[repo].get.ts index cac921157f..87b19ce128 100644 --- a/server/api/changelog/releases/[provider]/[owner]/[repo].get.ts +++ b/server/api/changelog/releases/[provider]/[owner]/[repo].get.ts @@ -19,6 +19,8 @@ import { } from '~~/server/utils/changelog/mdRepoInfo' import { validateHostWithValibot } from '~~/server/utils/changelog/validateHost' +const TIMEOUT_TIME = 15_000 + export default defineCachedEventHandler( async event => { const provider = getRouterParam(event, 'provider') as ProviderId @@ -86,6 +88,7 @@ async function getReleasesFromGithub(owner: string, repo: string) { 'Accept': '*/*', 'User-Agent': 'npmx.dev', }, + timeout: TIMEOUT_TIME, }) const { releases } = v.parse(GithubReleaseCollectionSchama, data) @@ -114,6 +117,7 @@ async function getReleasesFromForgejo(owner: string, repo: string, host: string) headers: { 'User-Agent': 'npmx.dev', }, + timeout: TIMEOUT_TIME, }) const releases = v.parse(ForgejoReleaseCollectionSchema, data) @@ -144,6 +148,7 @@ async function getReleasesFromGitlab(owner: string, repo: string, host: string) headers: { 'User-Agent': 'npmx.dev', }, + timeout: TIMEOUT_TIME, }) const releases = v.parse(GitlabReleaseCollectionSchema, data) @@ -171,6 +176,7 @@ async function getReleasesFromGitea(owner: string, repo: string, host: string) { headers: { 'User-Agent': 'npmx.dev', }, + timeout: TIMEOUT_TIME, }) const releases = v.parse(GiteaReleaseCollectionSchema, data) diff --git a/server/api/changelog/releases/[provider]/[owner]/[repo]/raw/[tag].get.ts b/server/api/changelog/releases/[provider]/[owner]/[repo]/raw/[tag].get.ts index a09079a1a8..6f3cf9f3b7 100644 --- a/server/api/changelog/releases/[provider]/[owner]/[repo]/raw/[tag].get.ts +++ b/server/api/changelog/releases/[provider]/[owner]/[repo]/raw/[tag].get.ts @@ -84,6 +84,7 @@ async function getMarkdownFromGithub(owner: string, repo: string, tag: string, e 'User-Agent': 'npmx.dev', }, ignoreResponseError: true, + timeout: 15_000, }, ) diff --git a/server/utils/changelog/baseFileUrl.ts b/server/utils/changelog/baseFileUrl.ts index aa0b771ca9..bd321ab81f 100644 --- a/server/utils/changelog/baseFileUrl.ts +++ b/server/utils/changelog/baseFileUrl.ts @@ -23,9 +23,10 @@ export function getBaseFileUrl(ref: RepoRef): RepoFileUrl | null { } case 'gitea': { // although similar to forgejo, we keep these seperate because they're seperately maintained projects + const host = ref.host ?? 'gitea.com' return { - blob: `https://${ref.host}/${ref.owner}/${ref.repo}/src/branch/HEAD`, - raw: `https://${ref.host}/${ref.owner}/${ref.repo}/raw/branch/HEAD`, + blob: `https://${host}/${ref.owner}/${ref.repo}/src/branch/HEAD`, + raw: `https://${host}/${ref.owner}/${ref.repo}/raw/branch/HEAD`, } } case 'gitlab': { From d04de2aa4f288e144f4c350cda0c85cf20bf57a1 Mon Sep 17 00:00:00 2001 From: WilcoSp Date: Wed, 2 Sep 2026 20:54:33 +0200 Subject: [PATCH 05/17] adding more timeout when trying to retrieve markdown from releases --- .../releases/[provider]/[owner]/[repo]/raw/[tag].get.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/server/api/changelog/releases/[provider]/[owner]/[repo]/raw/[tag].get.ts b/server/api/changelog/releases/[provider]/[owner]/[repo]/raw/[tag].get.ts index 6f3cf9f3b7..94df96b8c7 100644 --- a/server/api/changelog/releases/[provider]/[owner]/[repo]/raw/[tag].get.ts +++ b/server/api/changelog/releases/[provider]/[owner]/[repo]/raw/[tag].get.ts @@ -8,6 +8,8 @@ import { } from '~~/shared/schemas/changelog/release' import { validateHostWithValibot } from '~~/server/utils/changelog/validateHost' +const TIMEOUT_TIME = 15_000 + export default defineCachedEventHandler( async event => { const provider = getRouterParam(event, 'provider') as ProviderId @@ -84,7 +86,7 @@ async function getMarkdownFromGithub(owner: string, repo: string, tag: string, e 'User-Agent': 'npmx.dev', }, ignoreResponseError: true, - timeout: 15_000, + timeout: TIMEOUT_TIME, }, ) @@ -102,6 +104,7 @@ async function getMarkdownFromGithub(owner: string, repo: string, tag: string, e 'Accept': '*/*', 'User-Agent': 'npmx.dev', }, + timeout: TIMEOUT_TIME, }) const { releases } = v.parse(GithubReleaseCollectionSchama, responseUngh) @@ -130,6 +133,7 @@ async function getMarkdownFromForgejo( headers: { 'User-Agent': 'npmx.dev', }, + timeout: TIMEOUT_TIME, }) const release = v.parse(ForgejoReleaseSchama, data) @@ -151,6 +155,7 @@ async function getMarkdownFromGitlab( headers: { 'User-Agent': 'npmx.dev', }, + timeout: TIMEOUT_TIME, }) const release = v.parse(GitlabReleaseSchame, data) From 36c408ffd3e721841fef09ee2be26d141bd3971a Mon Sep 17 00:00:00 2001 From: WilcoSp Date: Wed, 2 Sep 2026 21:38:58 +0200 Subject: [PATCH 06/17] handling timeout --- .../releases/[provider]/[owner]/[repo]/raw/[tag].get.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/api/changelog/releases/[provider]/[owner]/[repo]/raw/[tag].get.ts b/server/api/changelog/releases/[provider]/[owner]/[repo]/raw/[tag].get.ts index 94df96b8c7..98bb2a25a9 100644 --- a/server/api/changelog/releases/[provider]/[owner]/[repo]/raw/[tag].get.ts +++ b/server/api/changelog/releases/[provider]/[owner]/[repo]/raw/[tag].get.ts @@ -88,7 +88,7 @@ async function getMarkdownFromGithub(owner: string, repo: string, tag: string, e ignoreResponseError: true, timeout: TIMEOUT_TIME, }, - ) + ).catch(() => null) const parsed = v.safeParse(v.object({ body: v.string() }), responseGithub) From be0c0bacc162758866026286ecdca8e080045340 Mon Sep 17 00:00:00 2001 From: WilcoSp Date: Wed, 9 Sep 2026 14:49:08 +0200 Subject: [PATCH 07/17] added support for sourcehut --- .../[provider]/[owner]/[repo]/[...path].get.ts | 3 +++ server/utils/changelog/baseFileUrl.ts | 6 ++++++ server/utils/changelog/markdown.ts | 16 +++++++++------- server/utils/changelog/mdRepoInfo.ts | 16 ++++++++++++++++ 4 files changed, 34 insertions(+), 7 deletions(-) diff --git a/server/api/changelog/md/[provider]/[owner]/[repo]/[...path].get.ts b/server/api/changelog/md/[provider]/[owner]/[repo]/[...path].get.ts index 1f7edb0e3c..caac1ec4c8 100644 --- a/server/api/changelog/md/[provider]/[owner]/[repo]/[...path].get.ts +++ b/server/api/changelog/md/[provider]/[owner]/[repo]/[...path].get.ts @@ -7,6 +7,7 @@ import { createGiteaRepoInfo, createGithubRepoInfo, createGitLabRepoInfo, + createSourcehutRepoInfo, createTangledInfo, } from '~~/server/utils/changelog/mdRepoInfo' import { validateHostWithValibot } from '~~/server/utils/changelog/validateHost' @@ -121,5 +122,7 @@ function getRepoInfo( return createGiteaRepoInfo(host ?? 'gitea.com', owner, repo, path) case 'bitbucket': return createBitbucketRepoInfo(owner, repo, path) + case 'sourcehut': + return createSourcehutRepoInfo(owner, repo, path) } } diff --git a/server/utils/changelog/baseFileUrl.ts b/server/utils/changelog/baseFileUrl.ts index bd321ab81f..540f44c9b5 100644 --- a/server/utils/changelog/baseFileUrl.ts +++ b/server/utils/changelog/baseFileUrl.ts @@ -48,6 +48,12 @@ export function getBaseFileUrl(ref: RepoRef): RepoFileUrl | null { raw: `https://bitbucket.org/${ref.owner}/${ref.repo}/raw/HEAD`, } } + case 'sourcehut': { + return { + blob: `https://git.sr.ht/${ref.owner}/${ref.repo}/tree/HEAD/item`, + raw: `https://git.sr.ht/${ref.owner}/${ref.repo}/blob/HEAD`, + } + } } return null } diff --git a/server/utils/changelog/markdown.ts b/server/utils/changelog/markdown.ts index 4edd42b82c..f3e715384a 100644 --- a/server/utils/changelog/markdown.ts +++ b/server/utils/changelog/markdown.ts @@ -122,13 +122,13 @@ export interface MarkdownRepoInfo { /** the text char that indicates an issue */ issueChar?: keyof typeof issuePrRegexes /** base url for a repository pull/merge request */ - prBaseUrl: string + prBaseUrl?: string /** * the text char that indicates a pull/merge request * * if it's the same as issueChar, than links will be parsed as issues and repo host is reponsible to redirect to pull/merge request x*/ - prChar: keyof typeof issuePrRegexes + prChar?: keyof typeof issuePrRegexes /** base url for a repository compare */ compareBaseUrl?: string } @@ -220,10 +220,12 @@ function resolveGitLinkText(href: string, label: string, repoInfo: MarkdownRepoI case href.startsWith(repoInfo.commitBaseUrl): { return lastSegment.slice(0, 7) // only show the first 6 letters/numbers of a commit } - case !!repoInfo.issueBaseUrl && href.startsWith(repoInfo.issueBaseUrl): { + case !!repoInfo.issueChar && + !!repoInfo.issueBaseUrl && + href.startsWith(repoInfo.issueBaseUrl): { return `${repoInfo.issueChar}${lastSegment}` } - case href.startsWith(repoInfo.prBaseUrl): { + case !!repoInfo.prChar && !!repoInfo.prBaseUrl && href.startsWith(repoInfo.prBaseUrl): { return `${repoInfo.prChar}${lastSegment}` } case !!repoInfo.compareBaseUrl && href.startsWith(repoInfo.compareBaseUrl): { @@ -270,10 +272,10 @@ function createResolveGitTextToLinks(mdInfo: MarkdownRepoInfo): IOptions['textFi } // pr/mr - if (mdInfo.issueChar != mdInfo.prChar) { + if (mdInfo.issueChar != mdInfo.prChar && mdInfo.prChar && mdInfo.prBaseUrl) { text = text.replace(issuePrRegexes[mdInfo.prChar], match => { - const id = match.replace(mdInfo.prChar, '') - return `${match}` + const id = match.replace(mdInfo.prChar!, '') + return `${match}` }) } diff --git a/server/utils/changelog/mdRepoInfo.ts b/server/utils/changelog/mdRepoInfo.ts index 2681d9bdaf..ab3b71a9f1 100644 --- a/server/utils/changelog/mdRepoInfo.ts +++ b/server/utils/changelog/mdRepoInfo.ts @@ -118,3 +118,19 @@ export function createBitbucketRepoInfo( // bitbucket uses jira instead of normal issues which can't be resolved, further the compare page also seems to be different } } + +export function createSourcehutRepoInfo( + owner: string, + repo: string, + path?: string, +): MarkdownRepoInfo { + const hostBaseUrl = 'https://git.sr.ht' + return { + hostBaseUrl, + blobBaseUrl: `${hostBaseUrl}/${owner}/${repo}/src/HEAD`, + rawBaseUrl: `${hostBaseUrl}/${owner}/${repo}/raw/HEAD`, + path, + commitBaseUrl: `${hostBaseUrl}/${owner}/${repo}/commit`, + // bitbucket uses jira instead of normal issues which can't be resolved, further the compare page also seems to be different + } +} From dcb22eb6cc113d6d1cf10e0ac71fb16e3a96779a Mon Sep 17 00:00:00 2001 From: WilcoSp Date: Wed, 9 Sep 2026 15:00:00 +0200 Subject: [PATCH 08/17] fixed forgotten base urls for sourcehut --- server/utils/changelog/mdRepoInfo.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/utils/changelog/mdRepoInfo.ts b/server/utils/changelog/mdRepoInfo.ts index ab3b71a9f1..0ea01977aa 100644 --- a/server/utils/changelog/mdRepoInfo.ts +++ b/server/utils/changelog/mdRepoInfo.ts @@ -127,8 +127,8 @@ export function createSourcehutRepoInfo( const hostBaseUrl = 'https://git.sr.ht' return { hostBaseUrl, - blobBaseUrl: `${hostBaseUrl}/${owner}/${repo}/src/HEAD`, - rawBaseUrl: `${hostBaseUrl}/${owner}/${repo}/raw/HEAD`, + blobBaseUrl: `https://git.sr.ht/${owner}/${repo}/tree/HEAD/item`, + rawBaseUrl: `https://git.sr.ht/${owner}/${repo}/blob/HEAD`, path, commitBaseUrl: `${hostBaseUrl}/${owner}/${repo}/commit`, // bitbucket uses jira instead of normal issues which can't be resolved, further the compare page also seems to be different From f80b04110ffb149df287a2383a53a91d09bba9ba Mon Sep 17 00:00:00 2001 From: WilcoSp Date: Wed, 9 Sep 2026 15:14:15 +0200 Subject: [PATCH 09/17] .. --- server/utils/changelog/mdRepoInfo.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/utils/changelog/mdRepoInfo.ts b/server/utils/changelog/mdRepoInfo.ts index 0ea01977aa..539b2580c9 100644 --- a/server/utils/changelog/mdRepoInfo.ts +++ b/server/utils/changelog/mdRepoInfo.ts @@ -127,8 +127,8 @@ export function createSourcehutRepoInfo( const hostBaseUrl = 'https://git.sr.ht' return { hostBaseUrl, - blobBaseUrl: `https://git.sr.ht/${owner}/${repo}/tree/HEAD/item`, - rawBaseUrl: `https://git.sr.ht/${owner}/${repo}/blob/HEAD`, + blobBaseUrl: `${hostBaseUrl}/${owner}/${repo}/tree/HEAD/item`, + rawBaseUrl: `${hostBaseUrl}/${owner}/${repo}/blob/HEAD`, path, commitBaseUrl: `${hostBaseUrl}/${owner}/${repo}/commit`, // bitbucket uses jira instead of normal issues which can't be resolved, further the compare page also seems to be different From 3b3e2ce0bbd0a6f4967114fa73e345d0531cdceb Mon Sep 17 00:00:00 2001 From: WilcoSp Date: Wed, 9 Sep 2026 15:18:34 +0200 Subject: [PATCH 10/17] .. --- server/utils/changelog/mdRepoInfo.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/utils/changelog/mdRepoInfo.ts b/server/utils/changelog/mdRepoInfo.ts index 539b2580c9..a1cd540a4f 100644 --- a/server/utils/changelog/mdRepoInfo.ts +++ b/server/utils/changelog/mdRepoInfo.ts @@ -131,6 +131,6 @@ export function createSourcehutRepoInfo( rawBaseUrl: `${hostBaseUrl}/${owner}/${repo}/blob/HEAD`, path, commitBaseUrl: `${hostBaseUrl}/${owner}/${repo}/commit`, - // bitbucket uses jira instead of normal issues which can't be resolved, further the compare page also seems to be different + // source hut doesn't have/support issues,pr & compare } } From 444487b370dc3fe12007df7a5887e0eadd4a418e Mon Sep 17 00:00:00 2001 From: WilcoSp Date: Sat, 12 Sep 2026 19:46:41 +0200 Subject: [PATCH 11/17] added support for gitee --- .../[owner]/[repo]/[...path].get.ts | 3 + .../releases/[provider]/[owner]/[repo].get.ts | 33 ++++++++++ server/utils/changelog/baseFileUrl.ts | 6 ++ server/utils/changelog/detectChangelog.ts | 66 +++++++++++++++++++ server/utils/changelog/markdown.ts | 6 +- server/utils/changelog/mdRepoInfo.ts | 17 +++++ shared/schemas/changelog/release.ts | 11 ++++ .../server/utils/changelog/markdown.spec.ts | 43 +++++++++++- 8 files changed, 182 insertions(+), 3 deletions(-) diff --git a/server/api/changelog/md/[provider]/[owner]/[repo]/[...path].get.ts b/server/api/changelog/md/[provider]/[owner]/[repo]/[...path].get.ts index caac1ec4c8..07174f2518 100644 --- a/server/api/changelog/md/[provider]/[owner]/[repo]/[...path].get.ts +++ b/server/api/changelog/md/[provider]/[owner]/[repo]/[...path].get.ts @@ -5,6 +5,7 @@ import { createBitbucketRepoInfo, createForgejoRepoInfo, createGiteaRepoInfo, + createGiteeRepoInfo, createGithubRepoInfo, createGitLabRepoInfo, createSourcehutRepoInfo, @@ -124,5 +125,7 @@ function getRepoInfo( return createBitbucketRepoInfo(owner, repo, path) case 'sourcehut': return createSourcehutRepoInfo(owner, repo, path) + case 'gitee': + return createGiteeRepoInfo(owner, repo, path) } } diff --git a/server/api/changelog/releases/[provider]/[owner]/[repo].get.ts b/server/api/changelog/releases/[provider]/[owner]/[repo].get.ts index 87b19ce128..9737803d41 100644 --- a/server/api/changelog/releases/[provider]/[owner]/[repo].get.ts +++ b/server/api/changelog/releases/[provider]/[owner]/[repo].get.ts @@ -10,6 +10,7 @@ import { ForgejoReleaseCollectionSchema, GitlabReleaseCollectionSchema, GiteaReleaseCollectionSchema, + GiteeReleaseCollectionSchema, } from '~~/shared/schemas/changelog/release' import { changelogRenderer } from '~~/server/utils/changelog/markdown' import { @@ -48,6 +49,8 @@ export default defineCachedEventHandler( return await getReleasesFromGitlab(owner, repo, host) case 'gitea': return await getReleasesFromGitea(owner, repo, host) + case 'gitee': + return await getReleasesFromGitee(owner, repo) default: throw createError({ @@ -197,3 +200,33 @@ async function getReleasesFromGitea(owner: string, repo: string, host: string) { } satisfies ReleaseData }) } + +async function getReleasesFromGitee(owner: string, repo: string) { + const data = await $fetch( + `https://gitee.com/api/v5/repos/${owner}/${repo}/releases?per_page=30`, + { + headers: { + 'User-Agent': 'npmx.dev', + }, + timeout: TIMEOUT_TIME, + }, + ) + + const releases = v.parse(GiteeReleaseCollectionSchema, data) + + const render = await changelogRenderer(createGiteeRepoInfo(owner, repo)) + + return releases.map(r => { + const { html, toc } = render(r.body, r.id) + return { + id: r.id, + html: html?.replace(/(?)\n/g, '
') ?? null, + title: r.name || r.tag_name, + prerelease: r.prerelease, + toc, + link: `https://gitee.com/${owner}/${repo}/releases/tag/${r.tag_name}`, + publishedAt: r.created_at, + tag: r.tag_name, + } satisfies ReleaseData + }) +} diff --git a/server/utils/changelog/baseFileUrl.ts b/server/utils/changelog/baseFileUrl.ts index 540f44c9b5..8643dc3e3e 100644 --- a/server/utils/changelog/baseFileUrl.ts +++ b/server/utils/changelog/baseFileUrl.ts @@ -54,6 +54,12 @@ export function getBaseFileUrl(ref: RepoRef): RepoFileUrl | null { raw: `https://git.sr.ht/${ref.owner}/${ref.repo}/blob/HEAD`, } } + case 'gitee': { + return { + blob: `https://gitee.com/${ref.owner}/${ref.repo}/blob/main`, + raw: `https://gitee.com/${ref.owner}/${ref.repo}/raw/main`, + } + } } return null } diff --git a/server/utils/changelog/detectChangelog.ts b/server/utils/changelog/detectChangelog.ts index 1d04fd86df..f7e3be71b1 100644 --- a/server/utils/changelog/detectChangelog.ts +++ b/server/utils/changelog/detectChangelog.ts @@ -9,6 +9,7 @@ import { ForgejoReleaseSchama, GitlabReleaseSchame, GiteaReleaseSchema, + GiteeReleaseSchema, } from '~~/shared/schemas/changelog/release' import { resolveURL } from 'ufo' import * as v from 'valibot' @@ -70,6 +71,9 @@ async function checkReleases( case 'gitea': { return checkLatestGiteaRelease(ref, directory) } + case 'gitee': { + return checkLatestGiteeRelease(ref, directory) + } } return [false, null] } @@ -409,3 +413,65 @@ async function checkLatestGiteaRelease( } return [false, null] } + +async function checkLatestGiteeRelease( + ref: RepoRef, + directory?: string, +): Promise> { + try { + const response = await $fetch( + `https://gitee.com/api/v5/repos/${ref.owner}/${ref.repo}/releases/latest`, + { + headers: { + 'User-Agent': 'npmx.dev', + 'accept': 'application/json', + }, + }, + ) + + const release = v.parse(GiteeReleaseSchema, response) + + const matchedChangelog = release.body?.match(MD_REGEX)?.at(0) + + // if no changelog.md or the url doesn't contain /blob/ + if (!matchedChangelog || !matchedChangelog.includes('/blob/')) { + return [ + { + provider: ref.provider, + type: 'release', + repo: `${ref.owner}/${ref.repo}`, + link: `https://gitee.com/${ref.owner}/${ref.repo}/releases`, + }, + null, + ] + } + + const path = matchedChangelog.replace(/^.*\/blob\/[^/]+\//i, '') + + // makes sure that the correct directory is matched + if ( + directory && + !( + path.startsWith(directory.endsWith('/') ? directory : `${directory}/`) || + ROOT_ONLY_REGEX.test(path) + ) + ) { + return [false, null] + } + return [ + { + provider: ref.provider, + type: 'md', + path, + repo: `${ref.owner}/${ref.repo}`, + link: matchedChangelog, + }, + null, + ] + } catch (e) { + if (e instanceof Error) { + return [null, e] + } + } + return [false, null] +} diff --git a/server/utils/changelog/markdown.ts b/server/utils/changelog/markdown.ts index f3e715384a..43ca44b870 100644 --- a/server/utils/changelog/markdown.ts +++ b/server/utils/changelog/markdown.ts @@ -121,6 +121,8 @@ export interface MarkdownRepoInfo { issueBaseUrl?: string /** the text char that indicates an issue */ issueChar?: keyof typeof issuePrRegexes + /** custom regex in case the git provider's issues uses a different format */ + issueRegex?: RegExp /** base url for a repository pull/merge request */ prBaseUrl?: string /** @@ -218,7 +220,7 @@ function resolveGitLinkText(href: string, label: string, repoInfo: MarkdownRepoI switch (true) { case href.startsWith(repoInfo.commitBaseUrl): { - return lastSegment.slice(0, 7) // only show the first 6 letters/numbers of a commit + return lastSegment.slice(0, 7) // only show the first 7 letters/numbers of a commit } case !!repoInfo.issueChar && !!repoInfo.issueBaseUrl && @@ -265,7 +267,7 @@ function createResolveGitTextToLinks(mdInfo: MarkdownRepoInfo): IOptions['textFi }) if (mdInfo.issueChar && mdInfo.issueBaseUrl) { - text = text.replace(issuePrRegexes[mdInfo.issueChar], match => { + text = text.replace(mdInfo.issueRegex ?? issuePrRegexes[mdInfo.issueChar], match => { const id = match.replace(mdInfo.issueChar!, '') return `${match}` }) diff --git a/server/utils/changelog/mdRepoInfo.ts b/server/utils/changelog/mdRepoInfo.ts index a1cd540a4f..fec175da4f 100644 --- a/server/utils/changelog/mdRepoInfo.ts +++ b/server/utils/changelog/mdRepoInfo.ts @@ -134,3 +134,20 @@ export function createSourcehutRepoInfo( // source hut doesn't have/support issues,pr & compare } } + +export function createGiteeRepoInfo(owner: string, repo: string, path?: string): MarkdownRepoInfo { + const hostBaseUrl = `https://gitee.com` + return { + hostBaseUrl, + blobBaseUrl: `${hostBaseUrl}/${owner}/${repo}/blob/HREAD`, + rawBaseUrl: `${hostBaseUrl}/${owner}/${repo}/raw/HEAD`, + path, + commitBaseUrl: `${hostBaseUrl}/${owner}/${repo}/commit`, + issueChar: '#', + issueBaseUrl: `${hostBaseUrl}/${owner}/${repo}/issues`, + issueRegex: /\B#[\dA-Z]+\b/g, + prChar: '!', + prBaseUrl: `${hostBaseUrl}/${owner}/${repo}/pulls`, + compareBaseUrl: `${hostBaseUrl}/${owner}/${repo}/compare`, + } +} diff --git a/shared/schemas/changelog/release.ts b/shared/schemas/changelog/release.ts index 8734395b3c..f6915855ca 100644 --- a/shared/schemas/changelog/release.ts +++ b/shared/schemas/changelog/release.ts @@ -59,3 +59,14 @@ export const GiteaReleaseSchema = v.object({ }) export const GiteaReleaseCollectionSchema = v.array(GiteaReleaseSchema) + +export const GiteeReleaseSchema = v.object({ + id: v.number(), + tag_name: v.string(), + name: v.string(), + body: v.string(), + prerelease: v.boolean(), + created_at: v.pipe(v.string(), v.isoTimestamp()), +}) + +export const GiteeReleaseCollectionSchema = v.array(GiteeReleaseSchema) diff --git a/test/unit/server/utils/changelog/markdown.spec.ts b/test/unit/server/utils/changelog/markdown.spec.ts index 55f841b1b7..2b4aed5f07 100644 --- a/test/unit/server/utils/changelog/markdown.spec.ts +++ b/test/unit/server/utils/changelog/markdown.spec.ts @@ -1,6 +1,10 @@ import type { MarkdownRepoInfo } from '~~/server/utils/changelog/markdown' import { describe, expect, it, vi, beforeAll } from 'vitest' -import { createGithubRepoInfo, createGitLabRepoInfo } from '~~/server/utils/changelog/mdRepoInfo' +import { + createGithubRepoInfo, + createGitLabRepoInfo, + createGiteeRepoInfo, +} from '~~/server/utils/changelog/mdRepoInfo' // testing changelog specific needs, others things are tested at ../readme.spec.ts @@ -729,6 +733,43 @@ describe('Turn plaintext #isssue/#pr, !pr, @account & commmit into links', () => `, ) }) + + // gitee + describe('gitee issue formatting', () => { + it('should turn issue into formatted links', async () => { + const info = createGiteeRepoInfo('test-owner', 'test-repo') + const renderer = await changelogRenderer(info) + // text from date-fns v4.3.0 + const markdown = `- Fixed pt locale first day of week to be Sunday. See #IKF9K6 +- Fixed zh-CN, zh-HK, and zh-TW locale month parsing for October, November, and December. See #I9T5LW +` + const result = renderer(markdown) + + expect(result.html).toBe(`
    +
  • Fixed pt locale first day of week to be Sunday. See #IKF9K6
  • +
  • Fixed zh-CN, zh-HK, and zh-TW locale month parsing for October, November, and December. See #I9T5LW
  • +
+`) + }) + + it('should turn issue/pr into links between ()', async () => { + const info = createGiteeRepoInfo('test-owner', 'test-repo') + const renderer = await changelogRenderer(info) + // text comes from npmx release 0.15.0 + const markdown = `- Minor ui improvements (#IKF9K6) +- deps: Update module-replacements (#I9T5LW) +- Release v0.15.0 (#IKEIK1)` + + const result = renderer(markdown) + + expect(result.html).toBe(`
    +
  • Minor ui improvements (#IKF9K6)
  • +
  • deps: Update module-replacements (#I9T5LW)
  • +
  • Release v0.15.0 (#IKEIK1)
  • +
+`) + }) + }) }) describe('format unformatted/auto links to git', () => { From 29e36c69cda30d49cd027c3d373c498b86d81bdf Mon Sep 17 00:00:00 2001 From: WilcoSp Date: Sat, 12 Sep 2026 19:58:28 +0200 Subject: [PATCH 12/17] added support for getting markdown from gitea & gitee --- .../[owner]/[repo]/raw/[tag].get.ts | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/server/api/changelog/releases/[provider]/[owner]/[repo]/raw/[tag].get.ts b/server/api/changelog/releases/[provider]/[owner]/[repo]/raw/[tag].get.ts index 98bb2a25a9..4b623d2124 100644 --- a/server/api/changelog/releases/[provider]/[owner]/[repo]/raw/[tag].get.ts +++ b/server/api/changelog/releases/[provider]/[owner]/[repo]/raw/[tag].get.ts @@ -3,6 +3,8 @@ import * as v from 'valibot' import { ERROR_THROW_INCOMPLETE_PARAM } from '~~/shared/utils/constants' import { ForgejoReleaseSchama, + GiteaReleaseSchema, + GiteeReleaseSchema, GithubReleaseCollectionSchama, GitlabReleaseSchame, } from '~~/shared/schemas/changelog/release' @@ -42,6 +44,10 @@ export default defineCachedEventHandler( return await getMarkdownFromForgejo(owner, repo, encodedTag, host) case 'gitlab': return await getMarkdownFromGitlab(owner, repo, encodedTag, host) + case 'gitea': + return await getMarkdownFromGitea(owner, repo, encodedTag, host) + case 'gitee': + return await getMarkdownFromGitee(owner, repo, encodedTag) default: throw createError({ status: 404, @@ -162,3 +168,43 @@ async function getMarkdownFromGitlab( return release.description } + +async function getMarkdownFromGitea( + owner: string, + repo: string, + /** tag should be encoded */ + tag: string, + host: string = 'gitea.com', +) { + const data = await $fetch(`https://${host}/api/v1/repos/${owner}/${repo}/releases/tags/${tag}`, { + headers: { + 'User-Agent': 'npmx.dev', + }, + timeout: TIMEOUT_TIME, + }) + + const release = v.parse(GiteaReleaseSchema, data) + + return release.body +} + +async function getMarkdownFromGitee( + owner: string, + repo: string, + /** tag should be encoded */ + tag: string, +) { + const data = await $fetch( + `https://gitee.com/api/v5/repos/${owner}/${repo}/releases/tags/${tag}`, + { + headers: { + 'User-Agent': 'npmx.dev', + }, + timeout: TIMEOUT_TIME, + }, + ) + + const release = v.parse(GiteeReleaseSchema, data) + + return release.body +} From ca52fec74f975ce7475a0ea6ca9f5e9e01882123 Mon Sep 17 00:00:00 2001 From: WilcoSp Date: Sat, 12 Sep 2026 20:12:31 +0200 Subject: [PATCH 13/17] .. --- test/unit/server/utils/changelog/markdown.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/server/utils/changelog/markdown.spec.ts b/test/unit/server/utils/changelog/markdown.spec.ts index 2b4aed5f07..cc9e6ca1c4 100644 --- a/test/unit/server/utils/changelog/markdown.spec.ts +++ b/test/unit/server/utils/changelog/markdown.spec.ts @@ -752,7 +752,7 @@ describe('Turn plaintext #isssue/#pr, !pr, @account & commmit into links', () => `) }) - it('should turn issue/pr into links between ()', async () => { + it('should turn issue into links between ()', async () => { const info = createGiteeRepoInfo('test-owner', 'test-repo') const renderer = await changelogRenderer(info) // text comes from npmx release 0.15.0 From acb5674990f9f9cc992cd5f955ea816eaa99234b Mon Sep 17 00:00:00 2001 From: WilcoSp Date: Sun, 13 Sep 2026 14:42:48 +0200 Subject: [PATCH 14/17] fixed some issues & made gitee issues case insensitive --- server/utils/changelog/baseFileUrl.ts | 4 ++-- server/utils/changelog/detectChangelog.ts | 6 +++--- server/utils/changelog/markdown.ts | 2 +- server/utils/changelog/mdRepoInfo.ts | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/server/utils/changelog/baseFileUrl.ts b/server/utils/changelog/baseFileUrl.ts index 8643dc3e3e..28d2e1c8ba 100644 --- a/server/utils/changelog/baseFileUrl.ts +++ b/server/utils/changelog/baseFileUrl.ts @@ -56,8 +56,8 @@ export function getBaseFileUrl(ref: RepoRef): RepoFileUrl | null { } case 'gitee': { return { - blob: `https://gitee.com/${ref.owner}/${ref.repo}/blob/main`, - raw: `https://gitee.com/${ref.owner}/${ref.repo}/raw/main`, + blob: `https://gitee.com/${ref.owner}/${ref.repo}/blob/HEAD`, + raw: `https://gitee.com/${ref.owner}/${ref.repo}/raw/HEAD`, } } } diff --git a/server/utils/changelog/detectChangelog.ts b/server/utils/changelog/detectChangelog.ts index f7e3be71b1..148142ec63 100644 --- a/server/utils/changelog/detectChangelog.ts +++ b/server/utils/changelog/detectChangelog.ts @@ -237,7 +237,7 @@ async function checkLatestForgejoRelease( const release = v.parse(ForgejoReleaseSchama, response) - const matchedChangelog = release.body?.match(MD_REGEX)?.at(0) + const matchedChangelog = release.body.match(MD_REGEX)?.at(0) // /src/branch/ can be similar to /blob/ if (!matchedChangelog || !matchedChangelog.includes('/src/branch/')) { @@ -369,7 +369,7 @@ async function checkLatestGiteaRelease( const release = v.parse(GiteaReleaseSchema, response) - const matchedChangelog = release.body?.match(MD_REGEX)?.at(0) + const matchedChangelog = release.body.match(MD_REGEX)?.at(0) // /src/branch/ can be similar to /blob/ if (!matchedChangelog || !matchedChangelog.includes('/src/branch/')) { @@ -431,7 +431,7 @@ async function checkLatestGiteeRelease( const release = v.parse(GiteeReleaseSchema, response) - const matchedChangelog = release.body?.match(MD_REGEX)?.at(0) + const matchedChangelog = release.body.match(MD_REGEX)?.at(0) // if no changelog.md or the url doesn't contain /blob/ if (!matchedChangelog || !matchedChangelog.includes('/blob/')) { diff --git a/server/utils/changelog/markdown.ts b/server/utils/changelog/markdown.ts index 43ca44b870..a4972007f3 100644 --- a/server/utils/changelog/markdown.ts +++ b/server/utils/changelog/markdown.ts @@ -268,7 +268,7 @@ function createResolveGitTextToLinks(mdInfo: MarkdownRepoInfo): IOptions['textFi if (mdInfo.issueChar && mdInfo.issueBaseUrl) { text = text.replace(mdInfo.issueRegex ?? issuePrRegexes[mdInfo.issueChar], match => { - const id = match.replace(mdInfo.issueChar!, '') + const id = match.replace(mdInfo.issueChar!, '').toUpperCase() return `${match}` }) } diff --git a/server/utils/changelog/mdRepoInfo.ts b/server/utils/changelog/mdRepoInfo.ts index fec175da4f..db1d3ae314 100644 --- a/server/utils/changelog/mdRepoInfo.ts +++ b/server/utils/changelog/mdRepoInfo.ts @@ -139,13 +139,13 @@ export function createGiteeRepoInfo(owner: string, repo: string, path?: string): const hostBaseUrl = `https://gitee.com` return { hostBaseUrl, - blobBaseUrl: `${hostBaseUrl}/${owner}/${repo}/blob/HREAD`, + blobBaseUrl: `${hostBaseUrl}/${owner}/${repo}/blob/HEAD`, rawBaseUrl: `${hostBaseUrl}/${owner}/${repo}/raw/HEAD`, path, commitBaseUrl: `${hostBaseUrl}/${owner}/${repo}/commit`, issueChar: '#', issueBaseUrl: `${hostBaseUrl}/${owner}/${repo}/issues`, - issueRegex: /\B#[\dA-Z]+\b/g, + issueRegex: /\B#[\dA-Z]+\b/gi, prChar: '!', prBaseUrl: `${hostBaseUrl}/${owner}/${repo}/pulls`, compareBaseUrl: `${hostBaseUrl}/${owner}/${repo}/compare`, From d99597fd00ddbc75174c78adb077c94a5b746012 Mon Sep 17 00:00:00 2001 From: WilcoSp Date: Wed, 16 Sep 2026 17:10:04 +0200 Subject: [PATCH 15/17] source hut now also has support for issues added tests for all git providers for plaintext issue, pr & account --- server/utils/changelog/markdown.ts | 26 ++- server/utils/changelog/mdRepoInfo.ts | 7 +- .../server/utils/changelog/markdown.spec.ts | 192 ++++++++++++------ 3 files changed, 161 insertions(+), 64 deletions(-) diff --git a/server/utils/changelog/markdown.ts b/server/utils/changelog/markdown.ts index a4972007f3..319c6d32ba 100644 --- a/server/utils/changelog/markdown.ts +++ b/server/utils/changelog/markdown.ts @@ -133,6 +133,18 @@ export interface MarkdownRepoInfo { prChar?: keyof typeof issuePrRegexes /** base url for a repository compare */ compareBaseUrl?: string + /** + * the character that should be used to indicate an account. + * + * if it's not supported than set this to false + * + * @default '@' if left empty + */ + accountChar?: false | keyof typeof accountRegexes + /** + * keep account character in the url + */ + keepAccountChar?: true } function resolveUrl(url: string, repoInfo: MarkdownRepoInfo, toUserContentId: ToUserContentIdFn) { @@ -242,7 +254,11 @@ const issuePrRegexes = { '!': /\B!\d+\b/g, } as const -const accountRegex = /\B@(?![\d.]+\b)(?![\w.-]*\/)[\w\-.]+\b/g +const accountRegexes = { + '@': /\B@(?![\d.]+\b)(?![\w.-]*\/)[\w\-.]+\b/g, + '~': /\B~(?![\d.]+\b)(?![\w.-]*\/)[\w\-.]+\b/g, +} as const + const commitRegex = /(?${match.slice(0, 7)}` }) + if (mdInfo.accountChar !== false) { // account - .replace(accountRegex, match => { - const acc = match.replace('@', '') + text = text.replace(accountRegexes[mdInfo.accountChar ?? '@'], match => { + const acc = mdInfo.keepAccountChar + ? match + : match.replace((mdInfo.accountChar as string) ?? '@', '') return `${match}` }) + } if (mdInfo.issueChar && mdInfo.issueBaseUrl) { text = text.replace(mdInfo.issueRegex ?? issuePrRegexes[mdInfo.issueChar], match => { diff --git a/server/utils/changelog/mdRepoInfo.ts b/server/utils/changelog/mdRepoInfo.ts index db1d3ae314..75fddf12f6 100644 --- a/server/utils/changelog/mdRepoInfo.ts +++ b/server/utils/changelog/mdRepoInfo.ts @@ -115,6 +115,7 @@ export function createBitbucketRepoInfo( commitBaseUrl: `${hostBaseUrl}/${owner}/${repo}/commits`, prChar: '#', prBaseUrl: `${hostBaseUrl}/${owner}/${repo}/pull-requests`, + accountChar: false, // bitbucket uses jira instead of normal issues which can't be resolved, further the compare page also seems to be different } } @@ -131,7 +132,11 @@ export function createSourcehutRepoInfo( rawBaseUrl: `${hostBaseUrl}/${owner}/${repo}/blob/HEAD`, path, commitBaseUrl: `${hostBaseUrl}/${owner}/${repo}/commit`, - // source hut doesn't have/support issues,pr & compare + issueChar: '#', + issueBaseUrl: `https://todo.sr.ht/${owner}/${repo}`, + accountChar: '~', + keepAccountChar: true, + // source hut doesn't have/support pr & compare } } diff --git a/test/unit/server/utils/changelog/markdown.spec.ts b/test/unit/server/utils/changelog/markdown.spec.ts index cc9e6ca1c4..9e18901b0b 100644 --- a/test/unit/server/utils/changelog/markdown.spec.ts +++ b/test/unit/server/utils/changelog/markdown.spec.ts @@ -4,8 +4,15 @@ import { createGithubRepoInfo, createGitLabRepoInfo, createGiteeRepoInfo, + createBitbucketRepoInfo, + createForgejoRepoInfo, + createGiteaRepoInfo, + createSourcehutRepoInfo, + createTangledInfo, } from '~~/server/utils/changelog/mdRepoInfo' +const TEST_OWNER = 'test-owner' +const TEST_REPO = 'test-repo' // testing changelog specific needs, others things are tested at ../readme.spec.ts beforeAll(() => { @@ -27,11 +34,11 @@ beforeAll(() => { const { changelogRenderer } = await import('#server/utils/changelog/markdown') function changelogMdinfo(): MarkdownRepoInfo { - return createGithubRepoInfo('test-owner', 'test-repo') + return createGithubRepoInfo(TEST_OWNER, TEST_REPO) } function changelogMdInfoWithPath() { - return createGithubRepoInfo('test-owner', 'test-repo', 'packages/test/changelog.md') + return createGithubRepoInfo(TEST_OWNER, TEST_REPO, 'packages/test/changelog.md') } describe('URL Resolution', () => { @@ -544,20 +551,136 @@ describe('Turn plaintext #isssue/#pr, !pr, @account & commmit into links', () => }) }) - it('should turn issue/pr & account into links', async () => { - const info = changelogMdinfo() - const renderer = await changelogRenderer(info) - // text from date-fns v4.3.0 - const markdown = `- Fixed pt locale first day of week to be Sunday. See #4195 by @ImRodry. + describe('should turn issue/pr & account into links', () => { + // in this test we check all providers to make sure they give the correct link, other tests won't as it's almost the same test n amount of time + it('github', async () => { + const info = changelogMdinfo() + const renderer = await changelogRenderer(info) + // text from date-fns v4.3.0 + const markdown = `- Fixed pt locale first day of week to be Sunday. See #4195 by @ImRodry. - Fixed zh-CN, zh-HK, and zh-TW locale month parsing for October, November, and December. See #4194 by @puneetdixit200. ` - const result = renderer(markdown) + const result = renderer(markdown) - expect(result.html).toBe(`
    + expect(result.html).toBe(`
    • Fixed pt locale first day of week to be Sunday. See #4195 by @ImRodry.
    • Fixed zh-CN, zh-HK, and zh-TW locale month parsing for October, November, and December. See #4194 by @puneetdixit200.
    `) + }) + + it('gitlab', async () => { + const info = createGitLabRepoInfo('gitlab.com', TEST_OWNER, TEST_REPO) + const renderer = await changelogRenderer(info) + // text from date-fns v4.3.0 + const markdown = `- Fixed pt locale first day of week to be Sunday. See #4195 by @ImRodry. +- Fixed zh-CN, zh-HK, and zh-TW locale month parsing for October, November, and December. See !4194 by @puneetdixit200. +` + const result = renderer(markdown) + + expect(result.html).toBe(`
      +
    • Fixed pt locale first day of week to be Sunday. See #4195 by @ImRodry.
    • +
    • Fixed zh-CN, zh-HK, and zh-TW locale month parsing for October, November, and December. See !4194 by @puneetdixit200.
    • +
    +`) + }) + + it('codeberg/forgejo', async () => { + const info = createForgejoRepoInfo('codeberg.org', TEST_OWNER, TEST_REPO) + const renderer = await changelogRenderer(info) + // text from date-fns v4.3.0 + const markdown = `- Fixed pt locale first day of week to be Sunday. See #4195 by @ImRodry. +- Fixed zh-CN, zh-HK, and zh-TW locale month parsing for October, November, and December. See #4194 by @puneetdixit200. +` + const result = renderer(markdown) + + expect(result.html).toBe(`
      +
    • Fixed pt locale first day of week to be Sunday. See #4195 by @ImRodry.
    • +
    • Fixed zh-CN, zh-HK, and zh-TW locale month parsing for October, November, and December. See #4194 by @puneetdixit200.
    • +
    +`) + }) + + it('tangled', async () => { + const info = createTangledInfo(TEST_OWNER, TEST_REPO) + const renderer = await changelogRenderer(info) + // text from date-fns v4.3.0 + const markdown = `- Fixed pt locale first day of week to be Sunday. See #4195 by @ImRodry. +- Fixed zh-CN, zh-HK, and zh-TW locale month parsing for October, November, and December. See #4194 by @puneetdixit200. +` + const result = renderer(markdown) + + expect(result.html).toBe(`
      +
    • Fixed pt locale first day of week to be Sunday. See #4195 by @ImRodry.
    • +
    • Fixed zh-CN, zh-HK, and zh-TW locale month parsing for October, November, and December. See #4194 by @puneetdixit200.
    • +
    +`) + }) + + it('gitea', async () => { + const info = createGiteaRepoInfo('gitea.com', TEST_OWNER, TEST_REPO) + const renderer = await changelogRenderer(info) + // text from date-fns v4.3.0 + const markdown = `- Fixed pt locale first day of week to be Sunday. See #4195 by @ImRodry. +- Fixed zh-CN, zh-HK, and zh-TW locale month parsing for October, November, and December. See #4194 by @puneetdixit200. +` + const result = renderer(markdown) + + expect(result.html).toBe(`
      +
    • Fixed pt locale first day of week to be Sunday. See #4195 by @ImRodry.
    • +
    • Fixed zh-CN, zh-HK, and zh-TW locale month parsing for October, November, and December. See #4194 by @puneetdixit200.
    • +
    +`) + }) + + it('bitbucket', async () => { + // for bitbucket the support is only pull requests. Jira & accounts are external and not offline resolveable + const info = createBitbucketRepoInfo(TEST_OWNER, TEST_REPO) + const renderer = await changelogRenderer(info) + // text from date-fns v4.3.0 + const markdown = `- Fixed pt locale first day of week to be Sunday. See #4195 by @ImRodry. +- Fixed zh-CN, zh-HK, and zh-TW locale month parsing for October, November, and December. See #4194 by @puneetdixit200. +` + const result = renderer(markdown) + + expect(result.html).toBe(`
      +
    • Fixed pt locale first day of week to be Sunday. See #4195 by @ImRodry.
    • +
    • Fixed zh-CN, zh-HK, and zh-TW locale month parsing for October, November, and December. See #4194 by @puneetdixit200.
    • +
    +`) + }) + + it('sourcehut', async () => { + const info = createSourcehutRepoInfo(`~${TEST_OWNER}`, TEST_REPO) + const renderer = await changelogRenderer(info) + // text from date-fns v4.3.0 + const markdown = `- Fixed pt locale first day of week to be Sunday. See #4195 by ~ImRodry. +- Fixed zh-CN, zh-HK, and zh-TW locale month parsing for October, November, and December. See #4194 by ~puneetdixit200. +` + const result = renderer(markdown) + + expect(result.html).toBe(`
      +
    • Fixed pt locale first day of week to be Sunday. See #4195 by ~ImRodry.
    • +
    • Fixed zh-CN, zh-HK, and zh-TW locale month parsing for October, November, and December. See #4194 by ~puneetdixit200.
    • +
    +`) + }) + + it('gitee', async () => { + const info = createGiteeRepoInfo('test-owner', 'test-repo') + const renderer = await changelogRenderer(info) + // text from date-fns v4.3.0 + const markdown = `- Fixed pt locale first day of week to be Sunday. See #IKF9K6 by @ImRodry +- Fixed zh-CN, zh-HK, and zh-TW locale month parsing for October, November, and December. See #I9T5LW by @ImRodry +` + const result = renderer(markdown) + + expect(result.html).toBe(`
      +
    • Fixed pt locale first day of week to be Sunday. See #IKF9K6 by @ImRodry
    • +
    • Fixed zh-CN, zh-HK, and zh-TW locale month parsing for October, November, and December. See #I9T5LW by @ImRodry
    • +
    +`) + }) }) it('should turn issue/pr into links between ()', async () => { @@ -681,20 +804,6 @@ describe('Turn plaintext #isssue/#pr, !pr, @account & commmit into links', () => ) }) - it('should format gitlab merge requests', async () => { - const info = createGitLabRepoInfo('gitlab.com', 'test', 'test') - const renderer = await changelogRenderer(info) - - const markdown = `!123 hallo\n\nhttps://gitlab.com/test/test/-/merge_requests/321 world` - const result = renderer(markdown) - - expect(result.html).toBe( - `

    !123 hallo

    -

    !321 world

    -`, - ) - }) - it('should format at proto @account handle but not @version', async () => { const info = changelogMdinfo() const renderer = await changelogRenderer(info) @@ -733,43 +842,6 @@ describe('Turn plaintext #isssue/#pr, !pr, @account & commmit into links', () => `, ) }) - - // gitee - describe('gitee issue formatting', () => { - it('should turn issue into formatted links', async () => { - const info = createGiteeRepoInfo('test-owner', 'test-repo') - const renderer = await changelogRenderer(info) - // text from date-fns v4.3.0 - const markdown = `- Fixed pt locale first day of week to be Sunday. See #IKF9K6 -- Fixed zh-CN, zh-HK, and zh-TW locale month parsing for October, November, and December. See #I9T5LW -` - const result = renderer(markdown) - - expect(result.html).toBe(`
      -
    • Fixed pt locale first day of week to be Sunday. See #IKF9K6
    • -
    • Fixed zh-CN, zh-HK, and zh-TW locale month parsing for October, November, and December. See #I9T5LW
    • -
    -`) - }) - - it('should turn issue into links between ()', async () => { - const info = createGiteeRepoInfo('test-owner', 'test-repo') - const renderer = await changelogRenderer(info) - // text comes from npmx release 0.15.0 - const markdown = `- Minor ui improvements (#IKF9K6) -- deps: Update module-replacements (#I9T5LW) -- Release v0.15.0 (#IKEIK1)` - - const result = renderer(markdown) - - expect(result.html).toBe(`
      -
    • Minor ui improvements (#IKF9K6)
    • -
    • deps: Update module-replacements (#I9T5LW)
    • -
    • Release v0.15.0 (#IKEIK1)
    • -
    -`) - }) - }) }) describe('format unformatted/auto links to git', () => { From 5e70f3c0d028c695121e6e7f1883999d8d0ba4fb Mon Sep 17 00:00:00 2001 From: WilcoSp Date: Wed, 16 Sep 2026 17:47:43 +0200 Subject: [PATCH 16/17] added tests for commits --- .../server/utils/changelog/markdown.spec.ts | 189 ++++++++++++++++-- 1 file changed, 172 insertions(+), 17 deletions(-) diff --git a/test/unit/server/utils/changelog/markdown.spec.ts b/test/unit/server/utils/changelog/markdown.spec.ts index 9e18901b0b..bd01d53ebf 100644 --- a/test/unit/server/utils/changelog/markdown.spec.ts +++ b/test/unit/server/utils/changelog/markdown.spec.ts @@ -1,5 +1,5 @@ import type { MarkdownRepoInfo } from '~~/server/utils/changelog/markdown' -import { describe, expect, it, vi, beforeAll } from 'vitest' +import { describe, expect, it, vi, beforeAll, test } from 'vitest' import { createGithubRepoInfo, createGitLabRepoInfo, @@ -553,7 +553,7 @@ describe('Turn plaintext #isssue/#pr, !pr, @account & commmit into links', () => describe('should turn issue/pr & account into links', () => { // in this test we check all providers to make sure they give the correct link, other tests won't as it's almost the same test n amount of time - it('github', async () => { + test('github', async () => { const info = changelogMdinfo() const renderer = await changelogRenderer(info) // text from date-fns v4.3.0 @@ -569,7 +569,7 @@ describe('Turn plaintext #isssue/#pr, !pr, @account & commmit into links', () => `) }) - it('gitlab', async () => { + test('gitlab', async () => { const info = createGitLabRepoInfo('gitlab.com', TEST_OWNER, TEST_REPO) const renderer = await changelogRenderer(info) // text from date-fns v4.3.0 @@ -585,7 +585,7 @@ describe('Turn plaintext #isssue/#pr, !pr, @account & commmit into links', () => `) }) - it('codeberg/forgejo', async () => { + test('codeberg/forgejo', async () => { const info = createForgejoRepoInfo('codeberg.org', TEST_OWNER, TEST_REPO) const renderer = await changelogRenderer(info) // text from date-fns v4.3.0 @@ -601,7 +601,7 @@ describe('Turn plaintext #isssue/#pr, !pr, @account & commmit into links', () => `) }) - it('tangled', async () => { + test('tangled', async () => { const info = createTangledInfo(TEST_OWNER, TEST_REPO) const renderer = await changelogRenderer(info) // text from date-fns v4.3.0 @@ -617,7 +617,7 @@ describe('Turn plaintext #isssue/#pr, !pr, @account & commmit into links', () => `) }) - it('gitea', async () => { + test('gitea', async () => { const info = createGiteaRepoInfo('gitea.com', TEST_OWNER, TEST_REPO) const renderer = await changelogRenderer(info) // text from date-fns v4.3.0 @@ -633,7 +633,7 @@ describe('Turn plaintext #isssue/#pr, !pr, @account & commmit into links', () => `) }) - it('bitbucket', async () => { + test('bitbucket', async () => { // for bitbucket the support is only pull requests. Jira & accounts are external and not offline resolveable const info = createBitbucketRepoInfo(TEST_OWNER, TEST_REPO) const renderer = await changelogRenderer(info) @@ -650,7 +650,7 @@ describe('Turn plaintext #isssue/#pr, !pr, @account & commmit into links', () => `) }) - it('sourcehut', async () => { + test('sourcehut', async () => { const info = createSourcehutRepoInfo(`~${TEST_OWNER}`, TEST_REPO) const renderer = await changelogRenderer(info) // text from date-fns v4.3.0 @@ -666,7 +666,7 @@ describe('Turn plaintext #isssue/#pr, !pr, @account & commmit into links', () => `) }) - it('gitee', async () => { + test('gitee', async () => { const info = createGiteeRepoInfo('test-owner', 'test-repo') const renderer = await changelogRenderer(info) // text from date-fns v4.3.0 @@ -768,20 +768,20 @@ describe('Turn plaintext #isssue/#pr, !pr, @account & commmit into links', () =>
`) }) - - it('should turn commits into links', async () => { - const info = changelogMdinfo() - const renderer = await changelogRenderer(info) - // from tiptap 3.27.1, 3.27.0, 3.26.0 & npmx 0.14.0 & 0.14.1 - const markdown = `- a16901d: Fix ordered list parsing so under-indented continuation lines preserve their first character + describe('should turn commits into links', () => { + test('github', async () => { + const info = changelogMdinfo() + const renderer = await changelogRenderer(info) + // from tiptap 3.27.1, 3.27.0, 3.26.0 & npmx 0.14.0 & 0.14.1 + const markdown = `- a16901d: Fix ordered list parsing so under-indented continuation lines preserve their first character - Updated dependencies [6270b99] - 7fb19eb: Only add hash attributes to nodes, not to marks. - Release v0.14.0 36128a54 - Empty (4cab893c)` - const result = renderer(markdown) + const result = renderer(markdown) - expect(result.html).toBe(`
    + expect(result.html).toBe(`
    • a16901d: Fix ordered list parsing so under-indented continuation lines preserve their first character
    • Updated dependencies [6270b99]
    • 7fb19eb: Only add hash attributes to nodes, not to marks.
    • @@ -789,6 +789,161 @@ describe('Turn plaintext #isssue/#pr, !pr, @account & commmit into links', () =>
    • Empty (4cab893)
    `) + }) + + test('gitlab', async () => { + const info = createGitLabRepoInfo('gitlab.com', TEST_OWNER, TEST_REPO) + const renderer = await changelogRenderer(info) + // from tiptap 3.27.1, 3.27.0, 3.26.0 & npmx 0.14.0 & 0.14.1 + const markdown = `- a16901d: Fix ordered list parsing so under-indented continuation lines preserve their first character +- Updated dependencies [6270b99] +- 7fb19eb: Only add hash attributes to nodes, not to marks. +- Release v0.14.0 36128a54 +- Empty (4cab893c)` + + const result = renderer(markdown) + + expect(result.html).toBe(`
      +
    • a16901d: Fix ordered list parsing so under-indented continuation lines preserve their first character
    • +
    • Updated dependencies [6270b99]
    • +
    • 7fb19eb: Only add hash attributes to nodes, not to marks.
    • +
    • Release v0.14.0 36128a5
    • +
    • Empty (4cab893)
    • +
    +`) + }) + + test('codeberg/forgejo', async () => { + const info = createForgejoRepoInfo('codeberg.org', TEST_OWNER, TEST_REPO) + const renderer = await changelogRenderer(info) + // from tiptap 3.27.1, 3.27.0, 3.26.0 & npmx 0.14.0 & 0.14.1 + const markdown = `- a16901d: Fix ordered list parsing so under-indented continuation lines preserve their first character +- Updated dependencies [6270b99] +- 7fb19eb: Only add hash attributes to nodes, not to marks. +- Release v0.14.0 36128a54 +- Empty (4cab893c)` + + const result = renderer(markdown) + + expect(result.html).toBe(`
      +
    • a16901d: Fix ordered list parsing so under-indented continuation lines preserve their first character
    • +
    • Updated dependencies [6270b99]
    • +
    • 7fb19eb: Only add hash attributes to nodes, not to marks.
    • +
    • Release v0.14.0 36128a5
    • +
    • Empty (4cab893)
    • +
    +`) + }) + + test('tangled', async () => { + const info = createTangledInfo(TEST_OWNER, TEST_REPO) + const renderer = await changelogRenderer(info) + // from tiptap 3.27.1, 3.27.0, 3.26.0 & npmx 0.14.0 & 0.14.1 + const markdown = `- a16901d: Fix ordered list parsing so under-indented continuation lines preserve their first character +- Updated dependencies [6270b99] +- 7fb19eb: Only add hash attributes to nodes, not to marks. +- Release v0.14.0 36128a54 +- Empty (4cab893c)` + + const result = renderer(markdown) + + expect(result.html).toBe(`
      +
    • a16901d: Fix ordered list parsing so under-indented continuation lines preserve their first character
    • +
    • Updated dependencies [6270b99]
    • +
    • 7fb19eb: Only add hash attributes to nodes, not to marks.
    • +
    • Release v0.14.0 36128a5
    • +
    • Empty (4cab893)
    • +
    +`) + }) + + test('gitea', async () => { + const info = createGiteaRepoInfo('gitea.com', TEST_OWNER, TEST_REPO) + const renderer = await changelogRenderer(info) + // from tiptap 3.27.1, 3.27.0, 3.26.0 & npmx 0.14.0 & 0.14.1 + const markdown = `- a16901d: Fix ordered list parsing so under-indented continuation lines preserve their first character +- Updated dependencies [6270b99] +- 7fb19eb: Only add hash attributes to nodes, not to marks. +- Release v0.14.0 36128a54 +- Empty (4cab893c)` + + const result = renderer(markdown) + + expect(result.html).toBe(`
      +
    • a16901d: Fix ordered list parsing so under-indented continuation lines preserve their first character
    • +
    • Updated dependencies [6270b99]
    • +
    • 7fb19eb: Only add hash attributes to nodes, not to marks.
    • +
    • Release v0.14.0 36128a5
    • +
    • Empty (4cab893)
    • +
    +`) + }) + + test('bitbucket', async () => { + const info = createBitbucketRepoInfo(TEST_OWNER, TEST_REPO) + const renderer = await changelogRenderer(info) + // from tiptap 3.27.1, 3.27.0, 3.26.0 & npmx 0.14.0 & 0.14.1 + const markdown = `- a16901d: Fix ordered list parsing so under-indented continuation lines preserve their first character +- Updated dependencies [6270b99] +- 7fb19eb: Only add hash attributes to nodes, not to marks. +- Release v0.14.0 36128a54 +- Empty (4cab893c)` + + const result = renderer(markdown) + + expect(result.html).toBe(`
      +
    • a16901d: Fix ordered list parsing so under-indented continuation lines preserve their first character
    • +
    • Updated dependencies [6270b99]
    • +
    • 7fb19eb: Only add hash attributes to nodes, not to marks.
    • +
    • Release v0.14.0 36128a5
    • +
    • Empty (4cab893)
    • +
    +`) + }) + + test('sourcehut', async () => { + const info = createSourcehutRepoInfo(`~${TEST_OWNER}`, TEST_REPO) + const renderer = await changelogRenderer(info) + // from tiptap 3.27.1, 3.27.0, 3.26.0 & npmx 0.14.0 & 0.14.1 + const markdown = `- a16901d: Fix ordered list parsing so under-indented continuation lines preserve their first character +- Updated dependencies [6270b99] +- 7fb19eb: Only add hash attributes to nodes, not to marks. +- Release v0.14.0 36128a54 +- Empty (4cab893c)` + + const result = renderer(markdown) + + expect(result.html).toBe(`
      +
    • a16901d: Fix ordered list parsing so under-indented continuation lines preserve their first character
    • +
    • Updated dependencies [6270b99]
    • +
    • 7fb19eb: Only add hash attributes to nodes, not to marks.
    • +
    • Release v0.14.0 36128a5
    • +
    • Empty (4cab893)
    • +
    +`) + }) + + test('gitee', async () => { + const info = createGiteeRepoInfo(TEST_OWNER, TEST_REPO) + const renderer = await changelogRenderer(info) + // from tiptap 3.27.1, 3.27.0, 3.26.0 & npmx 0.14.0 & 0.14.1 + const markdown = `- a16901d: Fix ordered list parsing so under-indented continuation lines preserve their first character +- Updated dependencies [6270b99] +- 7fb19eb: Only add hash attributes to nodes, not to marks. +- Release v0.14.0 36128a54 +- Empty (4cab893c)` + + const result = renderer(markdown) + + expect(result.html).toBe(`
      +
    • a16901d: Fix ordered list parsing so under-indented continuation lines preserve their first character
    • +
    • Updated dependencies [6270b99]
    • +
    • 7fb19eb: Only add hash attributes to nodes, not to marks.
    • +
    • Release v0.14.0 36128a5
    • +
    • Empty (4cab893)
    • +
    +`) + }) }) it('should not format an issue/pr into a commit', async () => { From 440bcfd995f24a38177f3ed79794e19524ee3519 Mon Sep 17 00:00:00 2001 From: WilcoSp Date: Wed, 16 Sep 2026 21:43:15 +0200 Subject: [PATCH 17/17] added tests to parse plain links --- .../[owner]/[repo]/[...path].get.ts | 4 +- server/utils/changelog/mdRepoInfo.ts | 6 +- .../server/utils/changelog/markdown.spec.ts | 178 ++++++++++++++++-- 3 files changed, 171 insertions(+), 17 deletions(-) diff --git a/server/api/changelog/md/[provider]/[owner]/[repo]/[...path].get.ts b/server/api/changelog/md/[provider]/[owner]/[repo]/[...path].get.ts index 07174f2518..139dbb119b 100644 --- a/server/api/changelog/md/[provider]/[owner]/[repo]/[...path].get.ts +++ b/server/api/changelog/md/[provider]/[owner]/[repo]/[...path].get.ts @@ -9,7 +9,7 @@ import { createGithubRepoInfo, createGitLabRepoInfo, createSourcehutRepoInfo, - createTangledInfo, + createTangledRepoInfo, } from '~~/server/utils/changelog/mdRepoInfo' import { validateHostWithValibot } from '~~/server/utils/changelog/validateHost' import { @@ -118,7 +118,7 @@ function getRepoInfo( case 'gitlab': return createGitLabRepoInfo(host ?? 'gitlab.com', owner, repo, path) case 'tangled': - return createTangledInfo(owner, repo, path) + return createTangledRepoInfo(owner, repo, path) case 'gitea': return createGiteaRepoInfo(host ?? 'gitea.com', owner, repo, path) case 'bitbucket': diff --git a/server/utils/changelog/mdRepoInfo.ts b/server/utils/changelog/mdRepoInfo.ts index 75fddf12f6..d806e384d4 100644 --- a/server/utils/changelog/mdRepoInfo.ts +++ b/server/utils/changelog/mdRepoInfo.ts @@ -62,7 +62,11 @@ export function createGitLabRepoInfo( } } -export function createTangledInfo(owner: string, repo: string, path?: string): MarkdownRepoInfo { +export function createTangledRepoInfo( + owner: string, + repo: string, + path?: string, +): MarkdownRepoInfo { const hostBaseUrl = 'https://tangled.org' return { diff --git a/test/unit/server/utils/changelog/markdown.spec.ts b/test/unit/server/utils/changelog/markdown.spec.ts index bd01d53ebf..f11f8377e5 100644 --- a/test/unit/server/utils/changelog/markdown.spec.ts +++ b/test/unit/server/utils/changelog/markdown.spec.ts @@ -8,13 +8,14 @@ import { createForgejoRepoInfo, createGiteaRepoInfo, createSourcehutRepoInfo, - createTangledInfo, + createTangledRepoInfo, } from '~~/server/utils/changelog/mdRepoInfo' const TEST_OWNER = 'test-owner' const TEST_REPO = 'test-repo' // testing changelog specific needs, others things are tested at ../readme.spec.ts +// some tests test with all git providers, other only the logic of the changelog markdown parser beforeAll(() => { vi.stubGlobal( 'getShikiHighlighter', @@ -552,7 +553,6 @@ describe('Turn plaintext #isssue/#pr, !pr, @account & commmit into links', () => }) describe('should turn issue/pr & account into links', () => { - // in this test we check all providers to make sure they give the correct link, other tests won't as it's almost the same test n amount of time test('github', async () => { const info = changelogMdinfo() const renderer = await changelogRenderer(info) @@ -602,7 +602,7 @@ describe('Turn plaintext #isssue/#pr, !pr, @account & commmit into links', () => }) test('tangled', async () => { - const info = createTangledInfo(TEST_OWNER, TEST_REPO) + const info = createTangledRepoInfo(TEST_OWNER, TEST_REPO) const renderer = await changelogRenderer(info) // text from date-fns v4.3.0 const markdown = `- Fixed pt locale first day of week to be Sunday. See #4195 by @ImRodry. @@ -836,7 +836,7 @@ describe('Turn plaintext #isssue/#pr, !pr, @account & commmit into links', () => }) test('tangled', async () => { - const info = createTangledInfo(TEST_OWNER, TEST_REPO) + const info = createTangledRepoInfo(TEST_OWNER, TEST_REPO) const renderer = await changelogRenderer(info) // from tiptap 3.27.1, 3.27.0, 3.26.0 & npmx 0.14.0 & 0.14.1 const markdown = `- a16901d: Fix ordered list parsing so under-indented continuation lines preserve their first character @@ -1001,26 +1001,176 @@ describe('Turn plaintext #isssue/#pr, !pr, @account & commmit into links', () => describe('format unformatted/auto links to git', () => { // links to account won't be formatted, this is something also git providers don't do - it('should turn issue, pr, commit & compare links to formatted links', async () => { - const info = createGithubRepoInfo('vueuse', 'vueuse') - const renderer = await changelogRenderer(info) - // from vueuse 14.3.0 (last 2 links changed from `issues` -> `pull`) - const markdown = `- Expose pointer event onLongPress - by mrcwbr in https://github.com/vueuse/vueuse/issues/5295 https://github.com/vueuse/vueuse/commit/b1688bd2 + describe('should turn issue, pr, commit & compare links to formatted links', () => { + test('github', async () => { + const info = createGithubRepoInfo('vueuse', 'vueuse') + const renderer = await changelogRenderer(info) + // from vueuse 14.3.0 (last 2 links changed from `issues` -> `pull`) + const markdown = `- Expose pointer event onLongPress - by mrcwbr in https://github.com/vueuse/vueuse/issues/5295 https://github.com/vueuse/vueuse/commit/b1688bd2 - createInjectionState: Non-undefined return when default specified - by Laupetin in https://github.com/vueuse/vueuse/issues/5306 https://github.com/vueuse/vueuse/commit/b0c51c27 - createReusableTemplate: Add support for specifying component names - by wbolster in https://github.com/vueuse/vueuse/pull/5300 https://github.com/vueuse/vueuse/commit/ea29d5cb -- nuxt: Add composable variants to auto imports - by OrbisK in https://github.com/vueuse/vueuse/issues/5285 https://github.com/vueuse/vueuse/commit/ac2ef95d - +- nuxt: Add composable variants to auto imports - by OrbisK in https://github.com/vueuse/vueuse/pull/5285 https://github.com/vueuse/vueuse/commit/ac2ef95d + https://github.com/vueuse/vueuse/compare/v14.2.1...v14.3.0` - const result = renderer(markdown) - expect(result.html).toBe(`
      + const result = renderer(markdown) + expect(result.html).toBe(`
      • Expose pointer event onLongPress - by mrcwbr in #5295 b1688bd
      • createInjectionState: Non-undefined return when default specified - by Laupetin in #5306 b0c51c2
      • createReusableTemplate: Add support for specifying component names - by wbolster in #5300 ea29d5c
      • -
      • nuxt: Add composable variants to auto imports - by OrbisK in #5285 ac2ef95
      • +
      • nuxt: Add composable variants to auto imports - by OrbisK in #5285 ac2ef95

      v14.2.1...v14.3.0

      `) + }) + + test('gitlab', async () => { + const info = createGitLabRepoInfo('gitlab.com', 'vueuse', 'vueuse') + const renderer = await changelogRenderer(info) + // from vueuse 14.3.0 (last 2 links changed from `issues` -> `pull`) + const markdown = `- Expose pointer event onLongPress - by mrcwbr in https://gitlab.com/vueuse/vueuse/-/work_items/5295 https://gitlab.com/vueuse/vueuse/-/commit/b1688bd2 +- createInjectionState: Non-undefined return when default specified - by Laupetin in https://gitlab.com/vueuse/vueuse/-/work_items/5306 https://gitlab.com/vueuse/vueuse/-/commit/b0c51c27 +- createReusableTemplate: Add support for specifying component names - by wbolster in https://gitlab.com/vueuse/vueuse/-/merge_requests/5300 https://gitlab.com/vueuse/vueuse/-/commit/ea29d5cb +- nuxt: Add composable variants to auto imports - by OrbisK in https://gitlab.com/vueuse/vueuse/-/merge_requests/5285 https://gitlab.com/vueuse/vueuse/-/commit/ac2ef95d + +https://gitlab.com/vueuse/vueuse/-/compare/v14.2.1...v14.3.0` + + const result = renderer(markdown) + expect(result.html).toBe(`
        +
      • Expose pointer event onLongPress - by mrcwbr in #5295 b1688bd
      • +
      • createInjectionState: Non-undefined return when default specified - by Laupetin in #5306 b0c51c2
      • +
      • createReusableTemplate: Add support for specifying component names - by wbolster in !5300 ea29d5c
      • +
      • nuxt: Add composable variants to auto imports - by OrbisK in !5285 ac2ef95
      • +
      +

      v14.2.1...v14.3.0

      +`) + }) + + test('codeberg/forgejo', async () => { + const info = createForgejoRepoInfo('codeberg.org', 'vueuse', 'vueuse') + const renderer = await changelogRenderer(info) + // from vueuse 14.3.0 (last 2 links changed from `issues` -> `pull`) + const markdown = `- Expose pointer event onLongPress - by mrcwbr in https://codeberg.org/vueuse/vueuse/issues/5295 https://codeberg.org/vueuse/vueuse/commit/b1688bd2 +- createInjectionState: Non-undefined return when default specified - by Laupetin in https://codeberg.org/vueuse/vueuse/issues/5306 https://codeberg.org/vueuse/vueuse/commit/b0c51c27 +- createReusableTemplate: Add support for specifying component names - by wbolster in https://codeberg.org/vueuse/vueuse/pulls/5300 https://codeberg.org/vueuse/vueuse/commit/ea29d5cb +- nuxt: Add composable variants to auto imports - by OrbisK in https://codeberg.org/vueuse/vueuse/pulls/5285 https://codeberg.org/vueuse/vueuse/commit/ac2ef95d + +https://codeberg.org/vueuse/vueuse/compare/v14.2.1...v14.3.0` + + const result = renderer(markdown) + expect(result.html).toBe(`
        +
      • Expose pointer event onLongPress - by mrcwbr in #5295 b1688bd
      • +
      • createInjectionState: Non-undefined return when default specified - by Laupetin in #5306 b0c51c2
      • +
      • createReusableTemplate: Add support for specifying component names - by wbolster in #5300 ea29d5c
      • +
      • nuxt: Add composable variants to auto imports - by OrbisK in #5285 ac2ef95
      • +
      +

      v14.2.1...v14.3.0

      +`) + }) + + test('tangled', async () => { + const info = createTangledRepoInfo('vueuse', 'vueuse') + const renderer = await changelogRenderer(info) + // from vueuse 14.3.0 (last 2 links changed from `issues` -> `pull`) + const markdown = `- Expose pointer event onLongPress - by mrcwbr in https://tangled.org/vueuse/vueuse/issues/5295 https://tangled.org/vueuse/vueuse/commit/b1688bd2 +- createInjectionState: Non-undefined return when default specified - by Laupetin in https://tangled.org/vueuse/vueuse/issues/5306 https://tangled.org/vueuse/vueuse/commit/b0c51c27 +- createReusableTemplate: Add support for specifying component names - by wbolster in https://tangled.org/vueuse/vueuse/pulls/5300 https://tangled.org/vueuse/vueuse/commit/ea29d5cb +- nuxt: Add composable variants to auto imports - by OrbisK in https://tangled.org/vueuse/vueuse/pulls/5285 https://tangled.org/vueuse/vueuse/commit/ac2ef95d + +https://tangled.org/vueuse/vueuse/compare/v14.2.1...v14.3.0` + + const result = renderer(markdown) + expect(result.html).toBe(`
        +
      • Expose pointer event onLongPress - by mrcwbr in #5295 b1688bd
      • +
      • createInjectionState: Non-undefined return when default specified - by Laupetin in #5306 b0c51c2
      • +
      • createReusableTemplate: Add support for specifying component names - by wbolster in #5300 ea29d5c
      • +
      • nuxt: Add composable variants to auto imports - by OrbisK in #5285 ac2ef95
      • +
      +

      v14.2.1...v14.3.0

      +`) + }) + + test('gitea', async () => { + const info = createGiteaRepoInfo('gitea.com', 'vueuse', 'vueuse') + const renderer = await changelogRenderer(info) + // from vueuse 14.3.0 (last 2 links changed from `issues` -> `pull`) + const markdown = `- Expose pointer event onLongPress - by mrcwbr in https://gitea.com/vueuse/vueuse/issues/5295 https://gitea.com/vueuse/vueuse/commit/b1688bd2 +- createInjectionState: Non-undefined return when default specified - by Laupetin in https://gitea.com/vueuse/vueuse/issues/5306 https://gitea.com/vueuse/vueuse/commit/b0c51c27 +- createReusableTemplate: Add support for specifying component names - by wbolster in https://gitea.com/vueuse/vueuse/pulls/5300 https://gitea.com/vueuse/vueuse/commit/ea29d5cb +- nuxt: Add composable variants to auto imports - by OrbisK in https://gitea.com/vueuse/vueuse/pulls/5285 https://gitea.com/vueuse/vueuse/commit/ac2ef95d + +https://gitea.com/vueuse/vueuse/compare/v14.2.1...v14.3.0` + + const result = renderer(markdown) + expect(result.html).toBe(`
        +
      • Expose pointer event onLongPress - by mrcwbr in #5295 b1688bd
      • +
      • createInjectionState: Non-undefined return when default specified - by Laupetin in #5306 b0c51c2
      • +
      • createReusableTemplate: Add support for specifying component names - by wbolster in #5300 ea29d5c
      • +
      • nuxt: Add composable variants to auto imports - by OrbisK in #5285 ac2ef95
      • +
      +

      v14.2.1...v14.3.0

      +`) + }) + + test('bitbucket', async () => { + const info = createBitbucketRepoInfo('vueuse', 'vueuse') + const renderer = await changelogRenderer(info) + // from vueuse 14.3.0 (last 2 links changed from `issues` -> `pull`) + const markdown = `- Expose pointer event onLongPress - by mrcwbr in https://bitbucket.org/vueuse/vueuse/pull-requests/5295 https://bitbucket.org/vueuse/vueuse/commits/b1688bd2 +- createInjectionState: Non-undefined return when default specified - by Laupetin in https://bitbucket.org/vueuse/vueuse/pull-requests/5306 https://bitbucket.org/vueuse/vueuse/commits/b0c51c27 +- createReusableTemplate: Add support for specifying component names - by wbolster in https://bitbucket.org/vueuse/vueuse/pull-requests/5300 https://bitbucket.org/vueuse/vueuse/commits/ea29d5cb +- nuxt: Add composable variants to auto imports - by OrbisK in https://bitbucket.org/vueuse/vueuse/pull-requests/5285 https://bitbucket.org/vueuse/vueuse/commits/ac2ef95d` + // compare is not supported + const result = renderer(markdown) + expect(result.html).toBe(`
        +
      • Expose pointer event onLongPress - by mrcwbr in #5295 b1688bd
      • +
      • createInjectionState: Non-undefined return when default specified - by Laupetin in #5306 b0c51c2
      • +
      • createReusableTemplate: Add support for specifying component names - by wbolster in #5300 ea29d5c
      • +
      • nuxt: Add composable variants to auto imports - by OrbisK in #5285 ac2ef95
      • +
      +`) + }) + + test('sourcehut', async () => { + const info = createSourcehutRepoInfo('~vueuse', 'vueuse') + const renderer = await changelogRenderer(info) + // from vueuse 14.3.0 (last 2 links changed from `issues` -> `pull`) + const markdown = `- Expose pointer event onLongPress - by mrcwbr in https://todo.sr.ht/~vueuse/vueuse/5295 https://git.sr.ht/~vueuse/vueuse/commit/b1688bd2 +- createInjectionState: Non-undefined return when default specified - by Laupetin in https://todo.sr.ht/~vueuse/vueuse/5306 https://git.sr.ht/~vueuse/vueuse/commit/b0c51c27 +- createReusableTemplate: Add support for specifying component names - by wbolster in https://todo.sr.ht/~vueuse/vueuse/5300 https://git.sr.ht/~vueuse/vueuse/commit/ea29d5cb +- nuxt: Add composable variants to auto imports - by OrbisK in https://todo.sr.ht/~vueuse/vueuse/5285 https://git.sr.ht/~vueuse/vueuse/commit/ac2ef95d` + + const result = renderer(markdown) + expect(result.html).toBe(`
        +
      • Expose pointer event onLongPress - by mrcwbr in #5295 b1688bd
      • +
      • createInjectionState: Non-undefined return when default specified - by Laupetin in #5306 b0c51c2
      • +
      • createReusableTemplate: Add support for specifying component names - by wbolster in #5300 ea29d5c
      • +
      • nuxt: Add composable variants to auto imports - by OrbisK in #5285 ac2ef95
      • +
      +`) + }) + + test('gitee', async () => { + const info = createGiteeRepoInfo('vueuse', 'vueuse') + const renderer = await changelogRenderer(info) + // from vueuse 14.3.0 (last 2 links changed from `issues` -> `pull`) + const markdown = `- Expose pointer event onLongPress - by mrcwbr in https://gitee.com/vueuse/vueuse/issues/5295 https://gitee.com/vueuse/vueuse/commit/b1688bd2 +- createInjectionState: Non-undefined return when default specified - by Laupetin in https://gitee.com/vueuse/vueuse/issues/5306 https://gitee.com/vueuse/vueuse/commit/b0c51c27 +- createReusableTemplate: Add support for specifying component names - by wbolster in https://gitee.com/vueuse/vueuse/pulls/5300 https://gitee.com/vueuse/vueuse/commit/ea29d5cb +- nuxt: Add composable variants to auto imports - by OrbisK in https://gitee.com/vueuse/vueuse/pulls/5285 https://gitee.com/vueuse/vueuse/commit/ac2ef95d + +https://gitee.com/vueuse/vueuse/compare/v14.2.1...v14.3.0` + + const result = renderer(markdown) + expect(result.html).toBe(`
        +
      • Expose pointer event onLongPress - by mrcwbr in #5295 b1688bd
      • +
      • createInjectionState: Non-undefined return when default specified - by Laupetin in #5306 b0c51c2
      • +
      • createReusableTemplate: Add support for specifying component names - by wbolster in !5300 ea29d5c
      • +
      • nuxt: Add composable variants to auto imports - by OrbisK in !5285 ac2ef95
      • +
      +

      v14.2.1...v14.3.0

      +`) + }) }) it('should ignore formatted links', async () => {