Skip to content

Commit

Permalink
Adding new way to manage versions. Can now use preview, private, or p…
Browse files Browse the repository at this point in the history
…ublic status.
  • Loading branch information
wking-io committed Jun 25, 2024
1 parent d3f8942 commit 2338e1b
Show file tree
Hide file tree
Showing 10 changed files with 133 additions and 49 deletions.
2 changes: 1 addition & 1 deletion .tool-versions
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
nodejs 20.12.2
nodejs 20.12.0
java openjdk-17
36 changes: 33 additions & 3 deletions app/components/layout/Container.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { Dialog, Menu, Transition } from '@headlessui/react'
import { ChevronDownIcon, ChevronRightIcon } from '@heroicons/react/20/solid'
import {
ChevronDownIcon,
ChevronRightIcon,
ExclamationTriangleIcon,
} from '@heroicons/react/20/solid'
import { MagnifyingGlassIcon } from '@heroicons/react/24/solid'
import { Link, NavLink, useLocation } from '@remix-run/react'
import { clsx } from 'clsx'
Expand All @@ -13,6 +17,7 @@ type ContainerProps = {
menu: NavItem[]
product: TNavLink
productRef: string
isPreview: boolean
versions: Zipper.NonEmptyZipperObj<TNavLink> | null
links: TNavLink[]
basePath: string
Expand All @@ -24,6 +29,7 @@ export default function Container({
menu,
product,
productRef,
isPreview,
versions,
links,
basePath,
Expand Down Expand Up @@ -57,7 +63,7 @@ export default function Container({

return (
<div>
<header className="sticky top-0 z-50 flex flex-wrap items-center justify-between bg-crunchy px-4 py-2 sm:px-6 lg:px-8">
<header className="sticky top-0 z-50 flex h-16 flex-wrap items-center justify-between bg-crunchy px-4 py-2 sm:px-6 lg:px-8">
<div className="mr-6 flex lg:hidden">
<MobileNavigation menu={menu} basePath={basePath} />
</div>
Expand Down Expand Up @@ -161,7 +167,31 @@ export default function Container({
</div>
</div>
</div>
<div className="min-w-0 flex-1 py-8">{children}</div>
<div className="min-w-0 flex-1">
{isPreview ? (
<div className=" bg-warning-500/20 text-warning-900">
<div className="not-prose px-6 pt-4">
<p className="flex items-center font-display font-bold">
<ExclamationTriangleIcon className="h-4 w-4" />
<span className="pl-1">Warning</span>
</p>
</div>
<div className="warning-body px-6 pb-4">
This is a preview version of the documentation. Content is
subject to change and if you are looking for the latest stable
version you can find that{' '}
<Link
className="font-semibold underline hover:no-underline"
to={`${basePath}${product.to.replace(productRef, 'latest')}`}
>
here
</Link>
.
</div>
</div>
) : null}
<div className="py-8">{children}</div>
</div>
</div>
</div>
)
Expand Down
10 changes: 5 additions & 5 deletions app/components/layout/Content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import TableOfContents from '~/components/layout/TableOfContents.tsx'
import { getDoc } from '~/lib/docs/doc.server.ts'
import { type NavItem } from '~/lib/docs/menu.server.ts'
import { getBreadcrumbs, getChildren, getPagination } from '~/lib/docs/menu.ts'
import { getProductVersions } from '~/lib/docs/versions.server.ts'
import { getProductVersions, getVersion } from '~/lib/docs/versions.server.ts'
import { type NavLink } from '~/types.ts'
import { CACHE_CONTROL } from '~/utils/http.server.ts'
import { removeEndSlashes } from '~/utils/removeEndSlashes.ts'
Expand All @@ -50,7 +50,7 @@ export async function publicLoader({ params }: LoaderFunctionArgs) {

const versions = await getProductVersions({ product })
const isLatest = ref === 'latest'
const version = isLatest ? versions[0] : ref
const { version } = getVersion(versions, ref)

const doc = await getDoc({
product,
Expand Down Expand Up @@ -91,7 +91,7 @@ export async function privateLoader({ params }: LoaderFunctionArgs) {

const versions = await getProductVersions({ product, isPrivate: true })
const isLatest = ref === 'latest'
const version = isLatest ? versions[0] : ref
const { version } = getVersion(versions, ref)

const doc = await getDoc({
product,
Expand Down Expand Up @@ -208,11 +208,11 @@ export function Content({
) : null}
</div>
{showTitle ? (
<h1 className="mt-8 font-display text-3xl font-bold text-primary md:text-4xl">
<h1 className="mb-6 mt-8 font-display text-3xl font-bold text-primary md:text-4xl">
{attributes.title}
</h1>
) : null}
<div className="prose mt-6 w-full max-w-none pb-8 lg:prose-sm xl:prose-base prose-headings:font-display prose-headings:text-primary">
<div className="prose w-full max-w-none pb-8 lg:prose-sm xl:prose-base prose-headings:font-display prose-headings:text-primary">
{html ? (
<Component
components={{
Expand Down
11 changes: 9 additions & 2 deletions app/lib/docs/params.server.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
import { type Version } from './versions.server.ts'

export function validateParams(
tags: string[],
tags: Version[],
branches: string[],
params: { product: string; ref?: string; ['*']?: string },
): string | null {
let { product, ref, '*': slug } = params

if (!ref || (ref && !tags.includes(ref) && !branches.includes(ref))) {
if (
!ref ||
(ref &&
!tags.some(({ version }) => ref === version) &&
!branches.includes(ref))
) {
return `${product}/latest${slug ? `/${slug}` : ''}`
}

Expand Down
97 changes: 69 additions & 28 deletions app/lib/docs/versions.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,41 @@ export function getLatestVersion(versions: string[]) {
}

const versionValidator = z.string().regex(/\d(\.\d){0,2}/)
const oldVersionJsonValidator = z.array(versionValidator).nonempty()
const newVersionJsonValidator = z
.array(z.object({ version: versionValidator, isPublic: z.boolean() }))
const versionOldSchema = z.object({
version: versionValidator,
isPublic: z.boolean(),
})

const versionNewSchema = z.object({
version: versionValidator,
status: z.union([
z.literal('preview'),
z.literal('private'),
z.literal('public'),
]),
})
const versionJsonValidator = z
.array(
z.union([versionOldSchema, versionNewSchema]).transform(version => {
if ('isPublic' in version) {
return {
version: version.version,
status: version.isPublic ? 'public' : 'private',
}
}
return version
}),
)
.nonempty()
const versionJsonValidator = z.union([
oldVersionJsonValidator,
newVersionJsonValidator,
])

export type Versions = z.infer<typeof versionJsonValidator>

export type Version = { version: string; isPreview: boolean }
declare global {
var versionsCache: LRUCache<string, string[]>
var versionsCache: LRUCache<string, Version[]>
}

// global for SS "HMR", we need a better story here
global.versionsCache ??= createCache<string[]>(async key => {
global.versionsCache ??= createCache<Version[]>(async key => {
console.log('Fetching fresh versions')
let [access, product] = key.split(':')
return getAllVersions({ product, isPrivate: access === 'private' })
Expand All @@ -62,39 +80,62 @@ async function getAllVersions({
}: {
product: string
isPrivate?: boolean
}): Promise<string[]> {
}): Promise<{ version: string; isPreview: boolean }[]> {
const base = isPrivate ? privateRootPath(product) : rootPath(product)
const versions = await getJsonFile(
path.join(base, 'versions.json'),
versionJsonValidator.parse,
)

const isNewSchema = typeof versions[0] !== 'string'

if (!isNewSchema) return versions as string[]

return isPrivate
? (versions as z.infer<typeof newVersionJsonValidator>).map(
({ version }) => version,
)
: (versions as z.infer<typeof newVersionJsonValidator>).flatMap(
({ isPublic, version }) => (isPublic ? [version] : []),
? versions.map(({ status, version }) => ({
version,
isPreview: status === 'preview',
}))
: versions.flatMap(({ status, version }) =>
status !== 'private'
? [
{
version,
isPreview: status === 'preview',
},
]
: [],
)
}

export function versionsToMenu(
product: string,
ref: string,
versions: string[],
versions: Version[],
): NonEmptyZipperObj<NavLink> | null {
const sorted = versions.map((v, i) => ({
label: i === 0 ? `${v} (latest)` : v,
to: `/${product}/${i === 0 ? 'latest' : v}`,
}))
const zipped = fromArray(sorted)
let latestFound = false
const sorted = versions.flatMap(({ version, isPreview }, i) => {
if (isPreview) return []
const item = {
label: latestFound ? version : `${version} (latest)`,
to: `/${product}/${latestFound ? version : 'latest'}`,
}
latestFound = true
return item
})

const version = versions.find(({ version }) => version === ref)
if (version?.isPreview) {
return fromArray([
{ label: `${ref} (preview)`, to: `/${product}/${ref}` },
...sorted,
])
}

const zipped = fromArray(sorted)
if (isEmpty(zipped)) return null
if (ref === 'latest') return zipped
return find<NavLink>(zipped, ({ label }) => label === ref)
}

export function getVersion(versions: Version[], ref: string): Version {
return ref === 'latest'
? zipped
: find<NavLink>(zipped, ({ label }) => label === ref)
? versions.find(({ isPreview }) => !isPreview) ?? versions[0]
: versions.find(v => v.version === ref) ?? versions[0]
}
4 changes: 2 additions & 2 deletions app/routes/documentation.$product.$ref.actions.search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { json, type LoaderFunctionArgs } from '@remix-run/node'
import lunr from 'lunr'
import invariant from 'tiny-invariant'
import { getSearch, type SearchDoc } from '~/lib/docs/search.server.ts'
import { getProductVersions } from '~/lib/docs/versions.server.ts'
import { getProductVersions, getVersion } from '~/lib/docs/versions.server.ts'

function getBodyContext(body: string, term: string) {
const numContextWords = 2
Expand Down Expand Up @@ -33,7 +33,7 @@ export async function loader({ request, params }: LoaderFunctionArgs) {
if (!term) return json({ results: [] })

const versions = await getProductVersions({ product })
const version = ref === 'latest' ? versions[0] : ref
const { version } = getVersion(versions, ref)

const search = await getSearch({ product, version, ref })
if (!search) return json({ results: [] })
Expand Down
7 changes: 5 additions & 2 deletions app/routes/documentation.$product.$ref.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { validateParams } from '~/lib/docs/params.server.ts'
import { getProductData } from '~/lib/docs/product.server.ts'
import {
getProductVersions,
getVersion,
versionsToMenu,
} from '~/lib/docs/versions.server.ts'

Expand All @@ -24,7 +25,7 @@ export async function loader({ params }: LoaderFunctionArgs) {
invariant(ref, 'expected `params.ref`')

const versions = await getProductVersions({ product })
const version = ref === 'latest' ? versions[0] : ref
const { version, isPreview } = getVersion(versions, ref)

let betterUrl = validateParams(versions, ['latest'], {
product,
Expand All @@ -45,6 +46,7 @@ export async function loader({ params }: LoaderFunctionArgs) {
links,
ref,
version,
isPreview,
basePath: '/documentation',
})
}
Expand All @@ -58,13 +60,14 @@ export function useDocLayoutLoaderData(): SerializeFrom<typeof loader> {
}

export default function DocLayout() {
const { menu, product, versions, links, basePath, ref } =
const { menu, product, versions, links, basePath, ref, isPreview } =
useLoaderData<typeof loader>()
return (
<Container
menu={menu}
product={product}
productRef={ref}
isPreview={isPreview}
versions={versions}
links={links}
basePath={basePath}
Expand Down
4 changes: 2 additions & 2 deletions app/routes/documentation.$product.$ref[.]pdf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import fs from 'fs/promises'
import path from 'path'
import invariant from 'tiny-invariant'
import { contentPath } from '~/lib/docs/fs.server.ts'
import { getProductVersions } from '~/lib/docs/versions.server.ts'
import { getProductVersions, getVersion } from '~/lib/docs/versions.server.ts'
import { pdf } from '~/utils/responses.server.ts'

export { headers } from '~/components/layout/Content.tsx'
Expand All @@ -14,7 +14,7 @@ export async function loader({ params }: LoaderFunctionArgs) {
invariant(ref, 'expected `params.ref`')

const versions = await getProductVersions({ product })
const version = ref === 'latest' ? versions[0] : ref
const { version } = getVersion(versions, ref)

const pdfResponse = await fs.readFile(
path.join(contentPath(product, version), 'documentation.pdf'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import lunr from 'lunr'
import invariant from 'tiny-invariant'
import { getSearch, type SearchDoc } from '~/lib/docs/search.server.ts'
import { getProductAccess } from '~/lib/docs/utils.ts'
import { getProductVersions } from '~/lib/docs/versions.server.ts'
import { getProductVersions, getVersion } from '~/lib/docs/versions.server.ts'

function getBodyContext(body: string, term: string) {
const numContextWords = 2
Expand Down Expand Up @@ -36,7 +36,7 @@ export async function loader({ request, params }: LoaderFunctionArgs) {
if (!term) return json({ results: [] })

const versions = await getProductVersions({ product, isPrivate: true })
const version = ref === 'latest' ? versions[0] : ref
const { version } = getVersion(versions, ref)

const search = await getSearch({
product,
Expand Down
Loading

0 comments on commit 2338e1b

Please sign in to comment.