Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 1 addition & 9 deletions app/components/Package/Header.vue
Original file line number Diff line number Diff line change
Expand Up @@ -164,15 +164,7 @@ const diffLink = computed((): RouteLocationRaw | null => {

const timelineLink = computed((): RouteLocationRaw | null => {
if (props.pkg == null || props.resolvedVersion == null) return null
const split = props.pkg.name.split('/')
return {
name: 'timeline',
params: {
org: split.length === 2 ? split[0] : undefined,
packageName: split.length === 2 ? split[1]! : split[0]!,
version: props.resolvedVersion,
},
}
return packageTimelineRoute(props.pkg.name, props.resolvedVersion)
})

useShortcuts({
Expand Down
14 changes: 14 additions & 0 deletions app/composables/useCommandPalettePackageCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,20 @@ export function useCommandPalettePackageCommands(
activeLabel: activeLabel(route.name === 'code', t('command_palette.here')),
to: codeLink,
},
{
id: 'package-timeline',
group: 'package',
label: t('package.links.timeline'),
keywords: [
resolvedContext.packageName,
t('shortcuts.open_timeline'),
t('package.links.timeline'),
],
iconClass: 'i-lucide:history',
active: route.name === 'timeline',
activeLabel: activeLabel(route.name === 'timeline', t('command_palette.here')),
to: packageTimelineRoute(resolvedContext.packageName, resolvedContext.resolvedVersion),
},
{
id: 'package-compare',
group: 'package',
Expand Down
15 changes: 9 additions & 6 deletions app/composables/useCommandPaletteVersionCommands.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
// @unocss-include
import type { MaybeRefOrGetter } from 'vue'
import type { MaybeRef, MaybeRefOrGetter } from 'vue'
import type { RouteLocationRaw } from 'vue-router'
import type {
CommandPaletteCommand,
CommandPaletteContextCommandInput,
CommandPalettePackageContext,
} from '~/types/command-palette'
import { compare, satisfies, validRange } from 'semver'

type CommandPaletteVersionRoute = (version: string) => RouteLocationRaw

function getSortedVersions(context: CommandPalettePackageContext) {
return [...context.versions].sort((a, b) => {
if (a === context.resolvedVersion) return -1
Expand All @@ -19,7 +22,7 @@ function createVersionCommands(
context: CommandPalettePackageContext,
t: ReturnType<typeof useI18n>['t'],
versions = getSortedVersions(context),
urlPattern?: string | null,
routeForVersion?: CommandPaletteVersionRoute | null,
): CommandPaletteCommand[] {
return versions.map(version => ({
id: `version:${version}`,
Expand All @@ -28,13 +31,13 @@ function createVersionCommands(
keywords: [context.packageName, version, t('command_palette.groups.versions')],
iconClass: 'i-lucide:tag',
active: version === context.resolvedVersion,
to: urlPattern?.replace('{version}', version) ?? packageRoute(context.packageName, version),
to: routeForVersion?.(version) ?? packageRoute(context.packageName, version),
}))
}

export function useCommandPaletteVersionCommands(
context: MaybeRefOrGetter<CommandPalettePackageContext | null>,
urlPattern?: MaybeRefOrGetter<string | null | undefined>,
routeForVersion?: MaybeRef<CommandPaletteVersionRoute | null | undefined>,
) {
const { t } = useI18n()

Expand All @@ -43,7 +46,7 @@ export function useCommandPaletteVersionCommands(
const resolvedContext = toValue(context)
if (!resolvedContext?.resolvedVersion) return []

return createVersionCommands(resolvedContext, t, undefined, toValue(urlPattern))
return createVersionCommands(resolvedContext, t, undefined, unref(routeForVersion))
}),
)

Expand All @@ -58,6 +61,6 @@ export function useCommandPaletteVersionCommands(
satisfies(version, semverRange, { includePrerelease: true }),
)

return createVersionCommands(resolvedContext, t, matchingVersions, toValue(urlPattern))
return createVersionCommands(resolvedContext, t, matchingVersions, unref(routeForVersion))
})
}
20 changes: 10 additions & 10 deletions app/pages/diff/[[org]]/[packageName]/v/[versionRange].vue
Original file line number Diff line number Diff line change
Expand Up @@ -116,19 +116,19 @@ const latestVersionDetailed = computed(() => {
return pkg.value.versions[latestTag] ?? null
})

const normalizeRoutePath = (routeLocation: RouteLocationRaw) => {
const resolvedHref = router.resolve(routeLocation).href
return resolvedHref.replace(/%7B/g, '{').replace(/%7D/g, '}')
function diffVersionUrlPattern(from: string, to: string) {
const { org, packageName: name } = route.params
return `/diff/${org ? `${org}/` : ''}${name}/v/${from}...${to}`
}

const fromVersionUrlPattern = computed(() => {
return normalizeRoutePath(diffRoute(packageName.value, '{version}', toVersion.value))
})
const toVersionUrlPattern = computed(() => {
return normalizeRoutePath(diffRoute(packageName.value, fromVersion.value, '{version}'))
})
const fromVersionUrlPattern = computed(() => diffVersionUrlPattern('{version}', toVersion.value))
const toVersionUrlPattern = computed(() => diffVersionUrlPattern(fromVersion.value, '{version}'))

function fromVersionRoute(version: string): RouteLocationRaw {
return diffRoute(packageName.value, version, toVersion.value)
}

useCommandPaletteVersionCommands(commandPalettePackageContext, fromVersionUrlPattern)
useCommandPaletteVersionCommands(commandPalettePackageContext, fromVersionRoute)

useSeoMeta({
title: () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<script setup lang="ts">
import type { RouteLocationRaw } from 'vue-router'
import type { CommandPaletteContextCommandInput } from '~/types/command-palette'

// Maximum file size we'll try to load (500KB) - must match server
Expand Down Expand Up @@ -86,7 +87,16 @@ const versionUrlPattern = computed(() =>
}),
)

useCommandPaletteVersionCommands(commandPalettePackageContext, versionUrlPattern)
function codeVersionRoute(nextVersion: string): RouteLocationRaw {
return getCodeUrl({
org: route.params.org,
packageName: route.params.packageName,
version: nextVersion,
filePath: filePath.value,
})
}

useCommandPaletteVersionCommands(commandPalettePackageContext, codeVersionRoute)

// Fetch file tree
const { data: fileTree, status: treeStatus } = useFetch<PackageFileTreeResponse>(
Expand Down
15 changes: 14 additions & 1 deletion app/pages/package-docs/[...path].vue
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<script setup lang="ts">
import type { RouteLocationRaw } from 'vue-router'
import { setResponseHeader } from 'h3'

definePageMeta({
Expand Down Expand Up @@ -123,7 +124,19 @@ const versionUrlPattern = computed(
() => `/package-docs/${pkg.value?.name || packageName.value}/v/{version}`,
)

useCommandPaletteVersionCommands(commandPalettePackageContext, versionUrlPattern)
function docsVersionRoute(version: string): RouteLocationRaw {
const name = pkg.value?.name || packageName.value
const [firstSegment = name, ...remainingSegments] = name.split('/')

return {
name: 'docs',
params: {
path: [firstSegment, ...remainingSegments, 'v', version],
},
}
}

useCommandPaletteVersionCommands(commandPalettePackageContext, docsVersionRoute)

const pageTitle = computed(() => {
if (!packageName.value) return t('package.docs.page_title')
Expand Down
23 changes: 23 additions & 0 deletions app/pages/package-timeline/[[org]]/[packageName].vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ const packageName = computed(() =>
const version = computed(() => route.params.version)

const { data: pkg } = usePackage(packageName, version)
const { versions: commandPaletteVersions, ensureLoaded: ensureCommandPaletteVersionsLoaded } =
useCommandPalettePackageVersions(packageName)

const latestVersion = computed(() => {
if (!pkg.value) return null
Expand All @@ -30,11 +32,32 @@ const latestVersion = computed(() => {
return pkg.value.versions[latestTag] ?? null
})

const commandPalettePackageContext = computed(() => {
const packageData = pkg.value
if (!packageData) return null

return {
packageName: packageData.name,
resolvedVersion: version.value ?? packageData['dist-tags']?.latest ?? null,
latestVersion: packageData['dist-tags']?.latest ?? null,
versions: commandPaletteVersions.value ?? Object.keys(packageData.versions ?? {}),
}
})

useCommandPalettePackageContext(commandPalettePackageContext, {
onOpen: ensureCommandPaletteVersionsLoaded,
})
useCommandPalettePackageCommands(commandPalettePackageContext)

const versionUrlPattern = computed(() => {
const { org, packageName: name } = route.params
return `/package-timeline/${org ? `${org}/` : ''}${name}/v/{version}`
})

useCommandPaletteVersionCommands(commandPalettePackageContext, nextVersion =>
packageTimelineRoute(packageName.value, nextVersion),
)

function packageRoute(ver: string): RouteLocationRaw {
return {
name: 'package-version',
Expand Down
4 changes: 3 additions & 1 deletion app/pages/package/[[org]]/[name].vue
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,9 @@ const versionUrlPattern = computed(
() => `/package/${pkg.value?.name || packageName.value}/v/{version}`,
)

useCommandPaletteVersionCommands(commandPalettePackageContext, versionUrlPattern)
useCommandPaletteVersionCommands(commandPalettePackageContext, version =>
packageRoute(packageName.value, version),
)

const dependencyCount = computed(() => getDependencyCount(displayVersion.value))

Expand Down
13 changes: 13 additions & 0 deletions app/utils/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,16 @@ export function diffRoute(
},
}
}

export function packageTimelineRoute(packageName: string, version: string): RouteLocationRaw {
const { org, name } = splitPackageName(packageName)

return {
name: 'timeline',
params: {
org: org || undefined,
packageName: name,
version: version.replace(/\s+/g, ''),
},
}
}
28 changes: 21 additions & 7 deletions test/nuxt/composables/use-command-palette-commands.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { afterEach, describe, expect, it, vi } from 'vitest'
import { computed, defineComponent, h, ref, watchEffect, type Ref } from 'vue'
import type { RouteLocationRaw } from 'vue-router'
import { mockNuxtImport, mountSuspended } from '@nuxt/test-utils/runtime'
import { downloadPackageTarball } from '~/utils/package-download'
import type {
Expand Down Expand Up @@ -49,7 +50,7 @@ async function captureCommandPalette(options?: {
npmUser?: string | null
atprotoHandle?: string | null
packageContext?: CommandPalettePackageContext | null
versionUrlPattern?: string
versionRoute?: (version: string) => RouteLocationRaw
contextCommands?: CommandPaletteContextCommandInput[]
}) {
const groupedCommands = ref<CommandPaletteCommandGroup[]>([]) as Ref<CommandPaletteCommandGroup[]>
Expand All @@ -76,10 +77,7 @@ async function captureCommandPalette(options?: {
if (options?.packageContext) {
setPackageContext(options.packageContext)
useCommandPalettePackageCommands(() => options.packageContext ?? null)
useCommandPaletteVersionCommands(
() => options.packageContext ?? null,
() => options.versionUrlPattern,
)
useCommandPaletteVersionCommands(() => options.packageContext ?? null, options.versionRoute)
} else {
clearPackageContext()
}
Expand Down Expand Up @@ -232,6 +230,14 @@ describe('useCommandPaletteCommands', () => {
expect(flatCommands.value.find(command => command.id === 'package-diff')).toBeTruthy()
expect(flatCommands.value.find(command => command.id === 'package-download')).toBeTruthy()
expect(flatCommands.value.find(command => command.id === 'package-main')?.to).toBeTruthy()
expect(flatCommands.value.find(command => command.id === 'package-timeline')?.to).toEqual({
name: 'timeline',
params: {
org: undefined,
packageName: 'vue',
version: '3.4.0',
},
})
expect(groupedCommands.value.at(-1)?.id).toBe('versions')
expect(groupedCommands.value.at(-1)?.items[0]?.id).toBe('version:3.4.0')
expect(groupedCommands.value.at(-1)?.items[0]?.active).toBe(true)
Expand Down Expand Up @@ -345,7 +351,7 @@ describe('useCommandPaletteCommands', () => {
wrapper.unmount()
})

it('keeps version navigation on the current surface when a version URL pattern is provided', async () => {
it('keeps version navigation on the current surface when a version route builder is provided', async () => {
const { wrapper, flatCommands, routePath } = await captureCommandPalette({
route: '/package-code/vue/v/3.4.2/src/index.ts',
packageContext: {
Expand All @@ -354,7 +360,7 @@ describe('useCommandPaletteCommands', () => {
latestVersion: '4.0.0',
versions: ['4.0.0', '3.5.0', '3.4.2'],
},
versionUrlPattern: '/package-code/vue/v/{version}/src/index.ts',
versionRoute: version => `/package-code/vue/v/${version}/src/index.ts`,
})

const versionCommand = flatCommands.value.find(command => command.id === 'version:3.5.0')
Expand Down Expand Up @@ -513,6 +519,14 @@ describe('useCommandPaletteCommands', () => {
path: ['@scope', 'pkg', 'v', '1.0.0'],
},
})
expect(flatCommands.value.find(command => command.id === 'package-timeline')?.to).toEqual({
name: 'timeline',
params: {
org: '@scope',
packageName: 'pkg',
version: '1.0.0',
},
})

wrapper.unmount()
})
Expand Down
Loading