11import type { ChangelogMarkdownInfo , ChangelogInfo } from '~~/shared/types/changelog'
2- import { FetchError } from 'ofetch'
32import type { ExtendedPackageJson } from '~~/shared/utils/package-analysis'
43import { type RepoRef , parseRepoUrl } from '~~/shared/utils/git-providers'
4+ import { type RepoFileUrl , getBaseFileUrl } from './baseFileUrl'
5+ import { FetchError } from 'ofetch'
56import { ERROR_CHANGELOG_NOT_FOUND , ERROR_UNGH_API_KEY_EXHAUSTED } from '~~/shared/utils/constants'
67import {
78 GithubReleaseSchama ,
@@ -24,6 +25,7 @@ export async function detectChangelog(pkg: ExtendedPackageJson) {
2425 }
2526
2627 const repoRef = parseRepoUrl ( pkg . repository . url )
28+ console . log ( { repoRef } )
2729 if ( ! repoRef ) {
2830 return false
2931 }
@@ -71,6 +73,63 @@ async function checkReleases(
7173 return [ false , null ]
7274}
7375
76+ /// changelog markdown
77+
78+ const EXTENSIONS = [ '.md' , '' ] as const
79+
80+ const CHANGELOG_FILENAMES = [ 'changelog' , 'releases' , 'changes' , 'history' , 'news' ]
81+ . map ( fileName => {
82+ const fileNameUpperCase = fileName . toUpperCase ( )
83+ return EXTENSIONS . map ( ext => [ `${ fileNameUpperCase } ${ ext } ` , `${ fileName } ${ ext } ` ] )
84+ } )
85+ . flat ( 3 )
86+
87+ async function checkChangelogFile (
88+ ref : RepoRef ,
89+ directory ?: string ,
90+ ) : Promise < ChangelogMarkdownInfo | false > {
91+ const baseUrl = getBaseFileUrl ( ref )
92+ if ( ! baseUrl ) {
93+ return false
94+ }
95+
96+ if ( directory ) {
97+ const inDir = await checkFiles ( ref , baseUrl , directory )
98+ if ( inDir ) {
99+ return inDir
100+ }
101+ }
102+ return checkFiles ( ref , baseUrl )
103+ }
104+
105+ async function checkFiles ( ref : RepoRef , baseUrl : RepoFileUrl , dir ?: string ) {
106+ for ( const fileName of CHANGELOG_FILENAMES ) {
107+ const exists = await fetch ( resolveURL ( baseUrl . raw , dir ?? '' , fileName ) , {
108+ headers : {
109+ // GitHub API requires User-Agent
110+ 'User-Agent' : 'npmx.dev' ,
111+ } ,
112+ method : ref . provider != 'tangled' ? 'HEAD' : 'GET' , // we just need to know if it exists or not, tangled doesn't support HEAD
113+ } )
114+ . then ( r => r . ok )
115+ . catch ( ( ) => false )
116+ const owner = ref . provider == 'gitlab' ? encodeURIComponent ( ref . owner ) : ref . owner
117+ if ( exists ) {
118+ return {
119+ type : 'md' ,
120+ provider : ref . provider ,
121+ path : resolveURL ( dir ?? '' , fileName ) ,
122+ repo : `${ owner } /${ ref . repo } ` ,
123+ link : resolveURL ( baseUrl . blob , dir ?? '' , fileName ) ,
124+ host : ref . host ,
125+ } satisfies ChangelogMarkdownInfo
126+ }
127+ }
128+ return false
129+ }
130+
131+ // releases
132+
74133const MD_REGEX = / (?< = \[ .* ?( c h a n g e l o g | r e l e a s e s | c h a n g e s | h i s t o r y | n e w s ) \. m d .* ?\] \( ) ( .* ?) (? = \) ) / i
75134const ROOT_ONLY_REGEX = / ^ \/ [ ^ / ] + $ /
76135
@@ -144,91 +203,6 @@ async function checkLatestGithubRelease(
144203 }
145204}
146205
147- /// changelog markdown
148-
149- const EXTENSIONS = [ '.md' , '' ] as const
150-
151- const CHANGELOG_FILENAMES = [ 'changelog' , 'releases' , 'changes' , 'history' , 'news' ]
152- . map ( fileName => {
153- const fileNameUpperCase = fileName . toUpperCase ( )
154- return EXTENSIONS . map ( ext => [ `${ fileNameUpperCase } ${ ext } ` , `${ fileName } ${ ext } ` ] )
155- } )
156- . flat ( 3 )
157-
158- async function checkChangelogFile (
159- ref : RepoRef ,
160- directory ?: string ,
161- ) : Promise < ChangelogMarkdownInfo | false > {
162- const baseUrl = getBaseFileUrl ( ref )
163- if ( ! baseUrl ) {
164- return false
165- }
166-
167- if ( directory ) {
168- const inDir = await checkFiles ( ref , baseUrl , directory )
169- if ( inDir ) {
170- return inDir
171- }
172- }
173- return checkFiles ( ref , baseUrl )
174- }
175-
176- async function checkFiles ( ref : RepoRef , baseUrl : RepoFileUrl , dir ?: string ) {
177- for ( const fileName of CHANGELOG_FILENAMES ) {
178- const exists = await fetch ( resolveURL ( baseUrl . raw , dir ?? '' , fileName ) , {
179- headers : {
180- // GitHub API requires User-Agent
181- 'User-Agent' : 'npmx.dev' ,
182- } ,
183- method : 'HEAD' , // we just need to know if it exists or not
184- } )
185- . then ( r => r . ok )
186- . catch ( ( ) => false )
187- if ( exists ) {
188- return {
189- type : 'md' ,
190- provider : ref . provider ,
191- path : resolveURL ( dir ?? '' , fileName ) ,
192- repo : `${ encodeURIComponent ( ref . owner ) } /${ ref . repo } ` ,
193- link : resolveURL ( baseUrl . blob , dir ?? '' , fileName ) ,
194- host : ref . host ,
195- } satisfies ChangelogMarkdownInfo
196- }
197- }
198- return false
199- }
200-
201- interface RepoFileUrl {
202- raw : string
203- blob : string
204- }
205-
206- function getBaseFileUrl ( ref : RepoRef ) : RepoFileUrl | null {
207- switch ( ref . provider ) {
208- case 'github' :
209- return {
210- raw : `https://raw.githubusercontent.com/${ ref . owner } /${ ref . repo } /HEAD` ,
211- blob : `https://github.com/${ ref . owner } /${ ref . repo } /blob/HEAD` ,
212- }
213- case 'codeberg' :
214- case 'forgejo' : {
215- const host = ref . host ?? 'codeberg.org'
216- return {
217- blob : `https://${ host } /${ ref . owner } /${ ref . repo } /src/branch/HEAD` ,
218- raw : `https://${ host } /${ ref . owner } /${ ref . repo } /raw/branch/HEAD` ,
219- }
220- }
221- case 'gitlab' : {
222- const host = ref . host ?? 'gitlab.com'
223- return {
224- blob : `https://${ host } /${ ref . owner } /${ ref . repo } /-/blob/HEAD` ,
225- raw : `https://${ host } /${ ref . owner } /${ ref . repo } /-/raw/HEAD` ,
226- }
227- }
228- }
229- return null
230- }
231-
232206// codeberg / forgejo
233207
234208async function checkLatestForgejoRelease (
0 commit comments