docs: added edit date (#8623)

This commit is contained in:
Shahed Nasser authored and GitHub committed 2024-08-19 09:47:57 +03:00
1 parent 8c784a8b30
commit c479fe8220
16 files changed
+575 -17

No files matched your search

@@ -0,0 +1,156 @@
import { exec } from "child_process"
import { existsSync } from "fs"
import { readdir, readFile, stat, writeFile } from "fs/promises"
import path from "path"
import util from "util"
const promiseExec = util.promisify(exec)
const projectBasePath = path.resolve()
const rootPath = path.resolve("..", "..", "..")
const monorepoRelativePath = projectBasePath
.replace(rootPath, "")
.replace(/^\//, "")
const generatedFilePath = path.join(
projectBasePath,
"generated",
"edit-dates.mjs"
)
const getGitEditDatesOfPaths = async (
paths: string[]
): Promise<Record<string, string>> => {
const editDates: Record<string, string> = {}
await Promise.all(
paths.map(async (filePath) => {
const editDateProcess = await promiseExec(
`git log -1 --pretty="format:%cI" ${filePath}`
)
editDates[filePath] = editDateProcess.stdout || new Date().toISOString()
})
)
return editDates
}
// Note: this gets the date a file was edited in the OS. One drawback here
// is if a file is edited on a day, but the PR is merged a few days later, so the
// date doesn't accurately represent the publish date of the file.
const getOsLastEditDates = async (
paths: string[]
): Promise<Record<string, string>> => {
const editDates: Record<string, string> = {}
await Promise.all(
paths.map(async (filePath) => {
const fileStat = await stat(filePath)
editDates[filePath] = fileStat.mtime.toISOString()
})
)
return editDates
}
const getChangedFiles = async (): Promise<string[]> => {
const changedFiles: string[] = []
// get untracked files
const untrackedFiles = await promiseExec(
`git ls-files --other --exclude-standard`
)
untrackedFiles.stdout.split("\n").forEach((file) => {
if (path.basename(file) !== "page.mdx") {
return
}
changedFiles.push(path.relative(projectBasePath, file))
})
// get tracked files
const trackedFiles = await promiseExec(
`{ git diff --name-only ; git diff --name-only --staged ; } | sort | uniq`
)
trackedFiles.stdout.split("\n").forEach((file) => {
// The above command retrieve the full file path relative to the monorepo
// so all parts before the project's path should be removed.
if (
!file.startsWith(monorepoRelativePath) ||
path.basename(file) !== "page.mdx"
) {
return
}
changedFiles.push(file.replace(`${monorepoRelativePath}/`, ""))
})
return changedFiles
}
const getAllProjectFiles = async (): Promise<string[]> => {
const appDir = path.join(projectBasePath, "app")
const allFiles: string[] = []
const filesInDir = await readdir(appDir, {
withFileTypes: true,
recursive: true,
})
filesInDir.forEach((file) => {
if (file.isDirectory() || path.basename(file.name) !== "page.mdx") {
return
}
const fullFilePath = path.join(file.path, file.name)
// TODO check if this produces correct file paths
allFiles.push(path.relative(projectBasePath, fullFilePath))
})
return allFiles
}
export const generateEditedDates = async () => {
const generatedFileExists = existsSync(generatedFilePath)
const type = generatedFileExists ? "git" : "all"
let files: string[] = []
let editDates: Record<string, string> = {}
if (type === "all") {
// get all files in a project
files = await getAllProjectFiles()
editDates = await getGitEditDatesOfPaths(files)
} else {
// set the last edit dates for git changes only
files = await getChangedFiles()
editDates = await getOsLastEditDates(files)
}
if (!files.length) {
return
}
const fileContentPrefix = `export const generatedEditDates = `
if (generatedFileExists) {
const existingEditDates = JSON.parse(
(await readFile(generatedFilePath, "utf-8")).replace(
fileContentPrefix,
""
)
)
editDates = Object.assign(existingEditDates, editDates)
}
await writeFile(
generatedFilePath,
`${fileContentPrefix}${JSON.stringify(editDates, undefined, 2)}`,
{
flag: "w",
}
)
}
+1
View File
@@ -1,3 +1,4 @@
export * from "./generate-edited-dates.js"
export * from "./generate-sidebar.js"
export * from "./utils/find-metadata-title.js"
@@ -4,10 +4,7 @@ import React from "react"
export const MainNavDivider = () => {
return (
<span
className={clsx(
"h-docs_0.75 w-px block bg-medusa-border-base",
"mx-docs_0.5"
)}
className={clsx("h-docs_0.75 w-px block bg-medusa-border-base")}
></span>
)
}
@@ -0,0 +1,30 @@
import React from "react"
const DATE_REGEX = /^[a-zA-Z]+ (?<month>[a-zA-Z]+)/
type MainNavEditDateProps = {
date: string
}
export const MainNavEditDate = ({ date }: MainNavEditDateProps) => {
const today = new Date(date)
const dateObj = new Date(date)
const formattedDate = dateObj.toString()
const dateMatch = DATE_REGEX.exec(formattedDate)
if (!dateMatch?.groups?.month) {
return <></>
}
return (
<>
<span>
Edited {dateMatch.groups.month} {dateObj.getDate()}
{dateObj.getFullYear() !== today.getFullYear()
? `, ${dateObj.getFullYear()}`
: ""}
</span>
<span className="text-compact-small-plus">&#183;</span>
</>
)
}
@@ -11,9 +11,10 @@ import { MainNavDivider } from "./Divider"
import { MainNavSidebarOpener } from "./SidebarOpener"
import { MainNavHelpButton } from "./HelpButton"
import { SidebarLeftIcon } from "../Icons/SidebarLeft"
import { MainNavEditDate } from "./EditDate"
export const MainNav = () => {
const { reportIssueLink } = useMainNav()
const { reportIssueLink, editDate } = useMainNav()
const { setMobileSidebarOpen } = useSidebar()
return (
<div
@@ -35,14 +36,17 @@ export const MainNav = () => {
<MainNavigationDropdown />
<MainNavBreadcrumbs />
</div>
<div className="flex items-center gap-docs_0.25">
<Link href={reportIssueLink} className="text-medusa-fg-muted">
Report Issue
</Link>
<div className="flex items-center gap-docs_0.75">
<div className="flex items-center gap-[6px] text-medusa-fg-muted">
{editDate && <MainNavEditDate date={editDate} />}
<Link href={reportIssueLink}>Report Issue</Link>
</div>
<MainNavDivider />
<MainNavHelpButton />
<MainNavColorMode />
<SearchModalOpener />
<div className="flex items-center gap-docs_0.25">
<MainNavHelpButton />
<MainNavColorMode />
<SearchModalOpener />
</div>
</div>
</div>
)
@@ -1,12 +1,13 @@
"use client"
import React, { createContext, useContext, useMemo, useState } from "react"
import React, { createContext, useContext, useMemo } from "react"
import { NavigationDropdownItem } from "types"
export type MainNavContext = {
navItems: NavigationDropdownItem[]
activeItem?: NavigationDropdownItem
reportIssueLink: string
editDate?: string
}
const MainNavContext = createContext<MainNavContext | null>(null)
@@ -14,6 +15,7 @@ const MainNavContext = createContext<MainNavContext | null>(null)
export type MainNavProviderProps = {
navItems: NavigationDropdownItem[]
reportIssueLink: string
editDate?: string
children?: React.ReactNode
}
@@ -21,6 +23,7 @@ export const MainNavProvider = ({
navItems,
reportIssueLink,
children,
editDate,
}: MainNavProviderProps) => {
const activeItem = useMemo(
() => navItems.find((item) => item.type === "link" && item.isActive),
@@ -33,6 +36,7 @@ export const MainNavProvider = ({
navItems,
activeItem,
reportIssueLink,
editDate,
}}
>
{children}
+2 -2
View File
@@ -9,9 +9,9 @@
"tsBuildInfoFile": "./dist/.tsbuildinfo-client",
"noEmit": false,
"jsx": "react",
"lib": ["dom", "ES2015", "es2022"],
"lib": ["dom", "es2022"],
"module": "ESNext",
"target": "es6",
"target": "ESNext",
"declaration": true,
"declarationMap": true
},