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..139dbb119b 100644
--- a/server/api/changelog/md/[provider]/[owner]/[repo]/[...path].get.ts
+++ b/server/api/changelog/md/[provider]/[owner]/[repo]/[...path].get.ts
@@ -2,10 +2,14 @@ import { resolveURL } from 'ufo'
import * as v from 'valibot'
import { getBaseFileUrl } from '~~/server/utils/changelog/baseFileUrl'
import {
+ createBitbucketRepoInfo,
createForgejoRepoInfo,
+ createGiteaRepoInfo,
+ createGiteeRepoInfo,
createGithubRepoInfo,
createGitLabRepoInfo,
- createTangledInfo,
+ createSourcehutRepoInfo,
+ createTangledRepoInfo,
} from '~~/server/utils/changelog/mdRepoInfo'
import { validateHostWithValibot } from '~~/server/utils/changelog/validateHost'
import {
@@ -56,6 +60,7 @@ export default defineCachedEventHandler(
headers: {
'User-Agent': 'npmx.dev',
},
+ timeout: 15_000,
})
const markdown = v.parse(v.string(), data)
if (raw != undefined) {
@@ -113,6 +118,14 @@ 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':
+ 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 20e618072a..9737803d41 100644
--- a/server/api/changelog/releases/[provider]/[owner]/[repo].get.ts
+++ b/server/api/changelog/releases/[provider]/[owner]/[repo].get.ts
@@ -9,11 +9,19 @@ import {
GithubReleaseCollectionSchama,
ForgejoReleaseCollectionSchema,
GitlabReleaseCollectionSchema,
+ GiteaReleaseCollectionSchema,
+ GiteeReleaseCollectionSchema,
} 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'
+const TIMEOUT_TIME = 15_000
+
export default defineCachedEventHandler(
async event => {
const provider = getRouterParam(event, 'provider') as ProviderId
@@ -39,6 +47,10 @@ 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)
+ case 'gitee':
+ return await getReleasesFromGitee(owner, repo)
default:
throw createError({
@@ -79,6 +91,7 @@ async function getReleasesFromGithub(owner: string, repo: string) {
'Accept': '*/*',
'User-Agent': 'npmx.dev',
},
+ timeout: TIMEOUT_TIME,
})
const { releases } = v.parse(GithubReleaseCollectionSchama, data)
@@ -107,6 +120,7 @@ async function getReleasesFromForgejo(owner: string, repo: string, host: string)
headers: {
'User-Agent': 'npmx.dev',
},
+ timeout: TIMEOUT_TIME,
})
const releases = v.parse(ForgejoReleaseCollectionSchema, data)
@@ -137,6 +151,7 @@ async function getReleasesFromGitlab(owner: string, repo: string, host: string)
headers: {
'User-Agent': 'npmx.dev',
},
+ timeout: TIMEOUT_TIME,
})
const releases = v.parse(GitlabReleaseCollectionSchema, data)
@@ -158,3 +173,60 @@ 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',
+ },
+ timeout: TIMEOUT_TIME,
+ })
+ 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
+ })
+}
+
+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/api/changelog/releases/[provider]/[owner]/[repo]/raw/[tag].get.ts b/server/api/changelog/releases/[provider]/[owner]/[repo]/raw/[tag].get.ts
index a09079a1a8..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,11 +3,15 @@ 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'
import { validateHostWithValibot } from '~~/server/utils/changelog/validateHost'
+const TIMEOUT_TIME = 15_000
+
export default defineCachedEventHandler(
async event => {
const provider = getRouterParam(event, 'provider') as ProviderId
@@ -40,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,
@@ -84,8 +92,9 @@ async function getMarkdownFromGithub(owner: string, repo: string, tag: string, e
'User-Agent': 'npmx.dev',
},
ignoreResponseError: true,
+ timeout: TIMEOUT_TIME,
},
- )
+ ).catch(() => null)
const parsed = v.safeParse(v.object({ body: v.string() }), responseGithub)
@@ -101,6 +110,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)
@@ -129,6 +139,7 @@ async function getMarkdownFromForgejo(
headers: {
'User-Agent': 'npmx.dev',
},
+ timeout: TIMEOUT_TIME,
})
const release = v.parse(ForgejoReleaseSchama, data)
@@ -150,9 +161,50 @@ async function getMarkdownFromGitlab(
headers: {
'User-Agent': 'npmx.dev',
},
+ timeout: TIMEOUT_TIME,
})
const release = v.parse(GitlabReleaseSchame, data)
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
+}
diff --git a/server/utils/changelog/baseFileUrl.ts b/server/utils/changelog/baseFileUrl.ts
index 7cda63169b..0d90efa086 100644
--- a/server/utils/changelog/baseFileUrl.ts
+++ b/server/utils/changelog/baseFileUrl.ts
@@ -16,7 +16,14 @@ 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://${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 separate because they're separately maintained projects
+ const host = ref.host ?? 'gitea.com'
return {
blob: `https://${host}/${ref.owner}/${ref.repo}/src/branch/HEAD`,
raw: `https://${host}/${ref.owner}/${ref.repo}/raw/branch/HEAD`,
@@ -35,6 +42,24 @@ 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`,
+ }
+ }
+ 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`,
+ }
+ }
+ case 'gitee': {
+ return {
+ blob: `https://gitee.com/${ref.owner}/${ref.repo}/blob/HEAD`,
+ raw: `https://gitee.com/${ref.owner}/${ref.repo}/raw/HEAD`,
+ }
+ }
}
return null
}
diff --git a/server/utils/changelog/detectChangelog.ts b/server/utils/changelog/detectChangelog.ts
index 6289a7aebd..148142ec63 100644
--- a/server/utils/changelog/detectChangelog.ts
+++ b/server/utils/changelog/detectChangelog.ts
@@ -8,6 +8,8 @@ import {
GithubReleaseSchama,
ForgejoReleaseSchama,
GitlabReleaseSchame,
+ GiteaReleaseSchema,
+ GiteeReleaseSchema,
} from '~~/shared/schemas/changelog/release'
import { resolveURL } from 'ufo'
import * as v from 'valibot'
@@ -66,6 +68,12 @@ async function checkReleases(
case 'gitlab': {
return checkLatestGitlabRelease(ref, directory)
}
+ case 'gitea': {
+ return checkLatestGiteaRelease(ref, directory)
+ }
+ case 'gitee': {
+ return checkLatestGiteeRelease(ref, directory)
+ }
}
return [false, null]
}
@@ -229,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/')) {
@@ -340,3 +348,130 @@ 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]
+}
+
+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 e57d3f42e3..319c6d32ba 100644
--- a/server/utils/changelog/markdown.ts
+++ b/server/utils/changelog/markdown.ts
@@ -118,19 +118,33 @@ 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
+ /** 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
+ 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
+ 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) {
@@ -218,15 +232,17 @@ 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 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 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
@@ -238,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)}`
})
- .replace(issuePrRegexes[mdInfo.issueChar], match => {
- const id = match.replace(mdInfo.issueChar, '')
- return `${match}`
- })
+ 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 => {
+ const id = match.replace(mdInfo.issueChar!, '').toUpperCase()
+ return `${match}`
+ })
+ }
// 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 fca0f1dd74..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 {
@@ -78,3 +82,81 @@ 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`,
+ }
+}
+
+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`,
+ accountChar: false,
+ // 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}/tree/HEAD/item`,
+ rawBaseUrl: `${hostBaseUrl}/${owner}/${repo}/blob/HEAD`,
+ path,
+ commitBaseUrl: `${hostBaseUrl}/${owner}/${repo}/commit`,
+ issueChar: '#',
+ issueBaseUrl: `https://todo.sr.ht/${owner}/${repo}`,
+ accountChar: '~',
+ keepAccountChar: true,
+ // source hut doesn't have/support pr & compare
+ }
+}
+
+export function createGiteeRepoInfo(owner: string, repo: string, path?: string): MarkdownRepoInfo {
+ const hostBaseUrl = `https://gitee.com`
+ return {
+ hostBaseUrl,
+ 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/gi,
+ 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..f6915855ca 100644
--- a/shared/schemas/changelog/release.ts
+++ b/shared/schemas/changelog/release.ts
@@ -46,3 +46,27 @@ export const GitlabReleaseSchame = v.object({
})
export const GitlabReleaseCollectionSchema = v.array(GitlabReleaseSchame)
+
+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)
+
+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..44bccb2185 100644
--- a/test/unit/server/utils/changelog/markdown.spec.ts
+++ b/test/unit/server/utils/changelog/markdown.spec.ts
@@ -1,9 +1,21 @@
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 { describe, expect, it, vi, beforeAll, test } from 'vitest'
+import {
+ createGithubRepoInfo,
+ createGitLabRepoInfo,
+ createGiteeRepoInfo,
+ createBitbucketRepoInfo,
+ createForgejoRepoInfo,
+ createGiteaRepoInfo,
+ createSourcehutRepoInfo,
+ 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',
@@ -23,11 +35,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', () => {
@@ -540,20 +552,135 @@ 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', () => {
+ test('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.
`)
+ })
+
+ test('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.
+
+`)
+ })
+
+ 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
+ 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.
+
+`)
+ })
+
+ test('tangled', async () => {
+ 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.
+- 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.
+
+`)
+ })
+
+ test('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.
+
+`)
+ })
+
+ 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)
+ // 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.
+
+`)
+ })
+
+ test('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.
+
+`)
+ })
+
+ test('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 () => {
@@ -641,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.
@@ -662,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 = 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
+- 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 () => {
@@ -677,20 +959,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,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/ImRodry https://gitee.com/vueuse/vueuse/commit/b1688bd2
+- createInjectionState: Non-undefined return when default specified - by Laupetin in https://gitee.com/vueuse/vueuse/issues/I9T5LW 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 #ImRodry b1688bd
+- createInjectionState: Non-undefined return when default specified - by Laupetin in #I9T5LW 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 () => {