From ddb37bece8d8cd211ee4cd075c8b130c9268555f Mon Sep 17 00:00:00 2001 From: Vordgi Date: Wed, 11 Feb 2026 16:23:46 +0000 Subject: [PATCH 1/4] perf: load markdown source code on copy instead of on main request --- app/pages/package/[[org]]/[name].vue | 41 ++++++- server/api/registry/readme/[...pkg].get.ts | 110 +---------------- .../registry/readme/markdown/[...pkg].get.ts | 14 +++ server/utils/readme-loaders.ts | 115 ++++++++++++++++++ server/utils/readme.ts | 4 +- shared/types/readme.ts | 9 +- 6 files changed, 182 insertions(+), 111 deletions(-) create mode 100644 server/api/registry/readme/markdown/[...pkg].get.ts create mode 100644 server/utils/readme-loaders.ts diff --git a/app/pages/package/[[org]]/[name].vue b/app/pages/package/[[org]]/[name].vue index 092071131..0d7448002 100644 --- a/app/pages/package/[[org]]/[name].vue +++ b/app/pages/package/[[org]]/[name].vue @@ -5,6 +5,7 @@ import type { PackumentVersion, ProvenanceDetails, ReadmeResponse, + ReadmeMarkdownResponse, SkillsListResponse, } from '#shared/types' import type { JsrPackageInfo } from '#shared/types/jsr' @@ -109,12 +110,44 @@ const { data: readmeData } = useLazyFetch( { default: () => ({ html: '', md: '', playgroundLinks: [], toc: [] }) }, ) +const { + data: readmeMarkdownData, + status: readmeMarkdownStatus, + execute: fetchReadmeMarkdown, +} = useLazyFetch( + () => { + const base = `/api/registry/readme/markdown/${packageName.value}` + const version = requestedVersion.value + return version ? `${base}/v/${version}` : base + }, + { + server: false, + immediate: false, + default: () => ({}), + }, +) + //copy README file as Markdown const { copied: copiedReadme, copy: copyReadme } = useClipboard({ - source: () => readmeData.value?.md ?? '', + source: () => '', copiedDuring: 2000, }) +function prefetchReadmeMarkdown() { + if (readmeMarkdownStatus.value === 'idle') { + fetchReadmeMarkdown() + } +} + +async function copyReadmeHandler() { + await fetchReadmeMarkdown() + + const markdown = readmeMarkdownData.value?.markdown + if (!markdown) return + + await copyReadme(markdown) +} + // Track active TOC item based on scroll position const tocItems = computed(() => readmeData.value?.toc ?? []) const { activeId: activeTocId } = useActiveTocItem(tocItems) @@ -1238,12 +1271,14 @@ const showSkeleton = shallowRef(false)
Title + it('extracts toc from headings', async () => { + const markdown = `# Install\n\n## CLI\n\n## API` + const result = await renderReadmeHtml(markdown, 'test-pkg') + + expect(result.toc).toHaveLength(3) + expect(result.toc[0]).toMatchObject({ text: 'Install', depth: 1 }) + expect(result.toc[1]).toMatchObject({ text: 'CLI', depth: 2 }) + expect(result.toc[2]).toMatchObject({ text: 'API', depth: 2 }) + expect(result.toc.every(t => t.id.startsWith('user-content-'))).toBe(true) + }) +}) + +describe('HTML output', () => { + it('returns sanitized html', async () => { + const markdown = `# Title\n\nSome **bold** text and a [link](https://example.com).` + const result = await renderReadmeHtml(markdown, 'test-pkg') + + expect(result.html).toBe(`

Title

Some bold text and a link.

`) - }) }) }) From 262ed5efb073aa95ec971bb3f35b738d3fbe84a4 Mon Sep 17 00:00:00 2001 From: Vordgi Date: Wed, 11 Feb 2026 17:12:47 +0000 Subject: [PATCH 4/4] perf: improve readme-loaders coverage --- app/pages/package/[[org]]/[name].vue | 2 +- test/unit/server/utils/readme-loaders.spec.ts | 189 ++++++++++++++++-- 2 files changed, 174 insertions(+), 17 deletions(-) diff --git a/app/pages/package/[[org]]/[name].vue b/app/pages/package/[[org]]/[name].vue index 0d7448002..6990e3046 100644 --- a/app/pages/package/[[org]]/[name].vue +++ b/app/pages/package/[[org]]/[name].vue @@ -107,7 +107,7 @@ const { data: readmeData } = useLazyFetch( const version = requestedVersion.value return version ? `${base}/v/${version}` : base }, - { default: () => ({ html: '', md: '', playgroundLinks: [], toc: [] }) }, + { default: () => ({ html: '', mdExists: false, playgroundLinks: [], toc: [] }) }, ) const { diff --git a/test/unit/server/utils/readme-loaders.spec.ts b/test/unit/server/utils/readme-loaders.spec.ts index a954f3af6..8fb2ce818 100644 --- a/test/unit/server/utils/readme-loaders.spec.ts +++ b/test/unit/server/utils/readme-loaders.spec.ts @@ -1,11 +1,20 @@ -import { describe, expect, it, vi } from 'vitest' +import { describe, expect, it, vi, beforeEach } from 'vitest' +import { parsePackageParams } from '../../../../server/utils/parse-package-params' +import { NPM_MISSING_README_SENTINEL } from '#shared/utils/constants' // Mock Nitro globals before importing the module vi.stubGlobal('defineCachedFunction', (fn: Function) => fn) const $fetchMock = vi.fn() vi.stubGlobal('$fetch', $fetchMock) +vi.stubGlobal('parsePackageParams', parsePackageParams) -const { fetchReadmeFromJsdelivr, isStandardReadme } = +const fetchNpmPackageMock = vi.fn() +vi.stubGlobal('fetchNpmPackage', fetchNpmPackageMock) + +const parseRepositoryInfoMock = vi.fn() +vi.stubGlobal('parseRepositoryInfo', parseRepositoryInfoMock) + +const { fetchReadmeFromJsdelivr, isStandardReadme, resolvePackageReadmeSource } = await import('../../../../server/utils/readme-loaders') describe('isStandardReadme', () => { @@ -43,20 +52,6 @@ describe('fetchReadmeFromJsdelivr', () => { expect(fetchMock).toHaveBeenCalledWith('https://cdn.jsdelivr.net/npm/some-pkg/README.md') }) - it('tries next filename when response is not ok', async () => { - const content = '# Fallback' - const fetchMock = vi - .fn() - .mockResolvedValueOnce({ ok: false }) - .mockResolvedValueOnce({ ok: true, text: async () => content }) - vi.stubGlobal('fetch', fetchMock) - - const result = await fetchReadmeFromJsdelivr('pkg', ['README.md', 'readme.md']) - - expect(result).toBe(content) - expect(fetchMock).toHaveBeenCalledTimes(2) - }) - it('includes version in URL when version is passed', async () => { const fetchMock = vi.fn().mockResolvedValue({ ok: true, @@ -79,3 +74,165 @@ describe('fetchReadmeFromJsdelivr', () => { expect(fetchMock).toHaveBeenCalledTimes(2) }) }) + +describe('resolvePackageReadmeSource', () => { + beforeEach(() => { + fetchNpmPackageMock.mockReset() + parseRepositoryInfoMock.mockReset() + }) + + it('returns markdown and repoInfo when package has valid npm readme (latest)', async () => { + const markdown = '# Hello' + fetchNpmPackageMock.mockResolvedValue({ + readme: markdown, + readmeFilename: 'README.md', + repository: { url: 'https://github.com/u/r' }, + versions: {}, + }) + parseRepositoryInfoMock.mockReturnValue({ + provider: 'github', + owner: 'u', + repo: 'r', + rawBaseUrl: 'https://raw.githubusercontent.com/u/r/HEAD', + blobBaseUrl: 'https://github.com/u/r/blob/HEAD', + }) + + const result = await resolvePackageReadmeSource('some-pkg') + + expect(result).toMatchObject({ + packageName: 'some-pkg', + version: undefined, + markdown, + repoInfo: { provider: 'github', owner: 'u', repo: 'r' }, + }) + expect(fetchNpmPackageMock).toHaveBeenCalledWith('some-pkg') + }) + + it('returns markdown from version when packagePath includes version', async () => { + const markdown = '# Version readme' + fetchNpmPackageMock.mockResolvedValue({ + readme: 'latest readme', + readmeFilename: 'README.md', + repository: undefined, + versions: { + '1.0.0': { readme: markdown, readmeFilename: 'README.md' }, + }, + }) + parseRepositoryInfoMock.mockReturnValue(undefined) + + const result = await resolvePackageReadmeSource('some-pkg/v/1.0.0') + + expect(result).toMatchObject({ + packageName: 'some-pkg', + version: '1.0.0', + markdown, + }) + }) + + it('falls back to jsdelivr when npm readme is missing sentinel', async () => { + const jsdelivrContent = '# From CDN' + fetchNpmPackageMock.mockResolvedValue({ + readme: NPM_MISSING_README_SENTINEL, + readmeFilename: 'README.md', + repository: undefined, + versions: {}, + }) + parseRepositoryInfoMock.mockReturnValue(undefined) + const fetchMock = vi.fn().mockResolvedValue({ + ok: true, + text: async () => jsdelivrContent, + }) + vi.stubGlobal('fetch', fetchMock) + + const result = await resolvePackageReadmeSource('pkg') + + expect(result).toMatchObject({ + packageName: 'pkg', + markdown: jsdelivrContent, + repoInfo: undefined, + }) + expect(fetchMock).toHaveBeenCalled() + }) + + it('falls back to jsdelivr when readmeFilename is not standard', async () => { + const jsdelivrContent = '# From CDN' + fetchNpmPackageMock.mockResolvedValue({ + readme: 'content', + readmeFilename: 'DOCS.md', + repository: undefined, + versions: {}, + }) + parseRepositoryInfoMock.mockReturnValue(undefined) + const fetchMock = vi.fn().mockResolvedValue({ + ok: true, + text: async () => jsdelivrContent, + }) + vi.stubGlobal('fetch', fetchMock) + + const result = await resolvePackageReadmeSource('pkg') + + expect(result).toMatchObject({ markdown: jsdelivrContent }) + }) + + it('returns undefined markdown when no content and jsdelivr fails', async () => { + fetchNpmPackageMock.mockResolvedValue({ + readme: undefined, + readmeFilename: undefined, + repository: undefined, + versions: {}, + }) + parseRepositoryInfoMock.mockReturnValue(undefined) + const fetchMock = vi.fn().mockResolvedValue({ ok: false }) + vi.stubGlobal('fetch', fetchMock) + + const result = await resolvePackageReadmeSource('pkg') + + expect(result).toMatchObject({ + packageName: 'pkg', + version: undefined, + markdown: undefined, + repoInfo: undefined, + }) + }) + + it('returns undefined markdown when content is NPM_MISSING_README_SENTINEL and jsdelivr fails', async () => { + fetchNpmPackageMock.mockResolvedValue({ + readme: NPM_MISSING_README_SENTINEL, + readmeFilename: 'README.md', + repository: undefined, + versions: {}, + }) + const fetchMock = vi.fn().mockResolvedValue({ ok: false }) + vi.stubGlobal('fetch', fetchMock) + + const result = await resolvePackageReadmeSource('pkg') + + expect(result).toMatchObject({ + packageName: 'pkg', + markdown: undefined, + repoInfo: undefined, + }) + }) + + it('uses package repository for repoInfo when markdown is present', async () => { + fetchNpmPackageMock.mockResolvedValue({ + readme: '# Hi', + readmeFilename: 'README.md', + repository: { url: 'https://github.com/a/b' }, + versions: {}, + }) + const repoInfo = { + provider: 'github' as const, + owner: 'a', + repo: 'b', + rawBaseUrl: 'https://raw.githubusercontent.com/a/b/HEAD', + blobBaseUrl: 'https://github.com/a/b/blob/HEAD', + } + parseRepositoryInfoMock.mockReturnValue(repoInfo) + + const result = await resolvePackageReadmeSource('pkg') + + expect(result?.repoInfo).toEqual(repoInfo) + expect(parseRepositoryInfoMock).toHaveBeenCalledWith({ url: 'https://github.com/a/b' }) + }) +})