diff --git a/app/components/EntrypointSelector.vue b/app/components/EntrypointSelector.vue new file mode 100644 index 000000000..edbd1b385 --- /dev/null +++ b/app/components/EntrypointSelector.vue @@ -0,0 +1,28 @@ + + + diff --git a/app/pages/package-docs/[...path].vue b/app/pages/package-docs/[...path].vue index 07e6dbe92..76c28c46b 100644 --- a/app/pages/package-docs/[...path].vue +++ b/app/pages/package-docs/[...path].vue @@ -21,17 +21,26 @@ const parsedRoute = computed(() => { return { packageName: segments.join('/'), version: null as string | null, + entrypoint: null as string | null, } } + // Version is the segment right after "v" + const version = segments[vIndex + 1]! + // Everything after the version is the entrypoint path (e.g., "router.js") + const entrypointSegments = segments.slice(vIndex + 2) + const entrypoint = entrypointSegments.length > 0 ? entrypointSegments.join('/') : null + return { packageName: segments.slice(0, vIndex).join('/'), - version: segments.slice(vIndex + 1).join('/'), + version, + entrypoint, } }) const packageName = computed(() => parsedRoute.value.packageName) const requestedVersion = computed(() => parsedRoute.value.version) +const entrypoint = computed(() => parsedRoute.value.entrypoint) // Validate package name on server-side for early error detection if (import.meta.server && packageName.value) { @@ -72,7 +81,8 @@ const resolvedVersion = computed(() => requestedVersion.value ?? latestVersion.v const docsUrl = computed(() => { if (!packageName.value || !resolvedVersion.value) return null - return `/api/registry/docs/${packageName.value}/v/${resolvedVersion.value}` + const base = `/api/registry/docs/${packageName.value}/v/${resolvedVersion.value}` + return entrypoint.value ? `${base}/${entrypoint.value}` : base }) const shouldFetch = computed(() => !!docsUrl.value) @@ -119,6 +129,33 @@ const showLoading = computed( () => docsStatus.value === 'pending' || (docsStatus.value === 'idle' && docsUrl.value !== null), ) const showEmptyState = computed(() => docsData.value?.status !== 'ok') + +// Multi-entrypoint support +const entrypoints = computed(() => docsData.value?.entrypoints ?? null) +const currentEntrypoint = computed(() => docsData.value?.entrypoint ?? entrypoint.value ?? '') + +// Preserve entrypoint when switching versions +const versionUrlPattern = computed(() => { + const base = `/package-docs/${packageName.value}/v/{version}` + return entrypoint.value ? `${base}/${entrypoint.value}` : base +}) + +// Redirect to first entrypoint for multi-entrypoint packages +watch(docsData, data => { + if (data?.entrypoints?.length && !entrypoint.value && resolvedVersion.value) { + const firstEntrypoint = data.entrypoints[0]! + const pathSegments = [ + ...packageName.value.split('/'), + 'v', + resolvedVersion.value, + ...firstEntrypoint.split('/'), + ] + router.replace({ + name: 'docs', + params: { path: pathSegments as [string, ...string[]] }, + }) + } +})