Skip to content

Commit 0dc35ee

Browse files
committed
feat: replace npmjs urls in readme with local
1 parent 81e13c0 commit 0dc35ee

2 files changed

Lines changed: 56 additions & 0 deletions

File tree

server/utils/readme.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,33 @@ function slugify(text: string): string {
183183
.replace(/^-|-$/g, '') // Trim leading/trailing hyphens
184184
}
185185

186+
/** These path on npmjs.com don't belong to packages or search, so we shouldn't try to replace them with npmx.dev urls */
187+
const reservedPathsNpmJs = [
188+
'products',
189+
'login',
190+
'signup',
191+
// 'advisories',
192+
'blog',
193+
'about',
194+
'press',
195+
'policies',
196+
]
197+
198+
const isNpmJsUrlThatCanBeRedirected = (url: URL) => {
199+
if (url.host !== 'www.npmjs.com' && url.host !== 'npmjs.com') {
200+
return false
201+
}
202+
203+
if (
204+
url.pathname === '/' ||
205+
reservedPathsNpmJs.some(path => url.pathname.startsWith(`/${path}`))
206+
) {
207+
return false
208+
}
209+
210+
return true
211+
}
212+
186213
/**
187214
* Resolve a relative URL to an absolute URL.
188215
* If repository info is available, resolve to provider's raw file URLs.
@@ -199,6 +226,11 @@ function resolveUrl(url: string, packageName: string, repoInfo?: RepositoryInfo)
199226
try {
200227
const parsed = new URL(url, 'https://example.com')
201228
if (parsed.protocol === 'http:' || parsed.protocol === 'https:') {
229+
// Redirect npmjs urls to ourself
230+
console.log({ isnpm: isNpmJsUrlThatCanBeRedirected(parsed) })
231+
if (isNpmJsUrlThatCanBeRedirected(parsed)) {
232+
return parsed.pathname + parsed.search + parsed.hash
233+
}
202234
return url
203235
}
204236
} catch {
@@ -362,6 +394,7 @@ ${html}
362394
// Resolve link URLs, add security attributes, and collect playground links
363395
renderer.link = function ({ href, title, tokens }: Tokens.Link) {
364396
const resolvedHref = resolveUrl(href, packageName, repoInfo)
397+
console.log({ resolvedHref })
365398
const text = this.parser.parseInline(tokens)
366399
const titleAttr = title ? ` title="${title}"` : ''
367400

test/unit/server/utils/readme.spec.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,29 @@ describe('Markdown File URL Resolution', () => {
306306
)
307307
})
308308
})
309+
310+
describe('npm.js urls', () => {
311+
it('redirects npmjs.com urls to local', async () => {
312+
const markdown = `[Some npmjs.com link](https://www.npmjs.com/package/test-pkg)`
313+
const result = await renderReadmeHtml(markdown, 'test-pkg')
314+
315+
expect(result.html).toContain('href="/package/test-pkg"')
316+
})
317+
318+
it('redirects npmjs.com urls to local (no www and http)', async () => {
319+
const markdown = `[Some npmjs.com link](http://npmjs.com/package/test-pkg)`
320+
const result = await renderReadmeHtml(markdown, 'test-pkg')
321+
322+
expect(result.html).toContain('href="/package/test-pkg"')
323+
})
324+
325+
it('does not redirect npmjs.com to local if they are in the list of exceptions', async () => {
326+
const markdown = `[Root Contributing](https://www.npmjs.com/products)`
327+
const result = await renderReadmeHtml(markdown, 'test-pkg')
328+
329+
expect(result.html).toContain('href="https://www.npmjs.com/products"')
330+
})
331+
})
309332
})
310333

311334
describe('Markdown Content Extraction', () => {

0 commit comments

Comments
 (0)