diff --git a/www/.vercelignore b/www/.vercelignore
new file mode 100644
index 0000000000..6625243220
--- /dev/null
+++ b/www/.vercelignore
@@ -0,0 +1 @@
+utils
\ No newline at end of file
diff --git a/www/apps/book/package.json b/www/apps/book/package.json
index 7676164bf5..fa1c188194 100644
--- a/www/apps/book/package.json
+++ b/www/apps/book/package.json
@@ -38,6 +38,7 @@
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-react-hooks": "^5.0.0",
"postcss": "^8",
+ "tags": "*",
"tailwind": "*",
"tailwindcss": "^3.3.0",
"tsconfig": "*",
diff --git a/www/apps/book/scripts/prepare.mjs b/www/apps/book/scripts/prepare.mjs
index 2d46138e23..990c87e418 100644
--- a/www/apps/book/scripts/prepare.mjs
+++ b/www/apps/book/scripts/prepare.mjs
@@ -1,7 +1,10 @@
import { generateEditedDates } from "build-scripts"
+import path from "path"
+import { generateTags } from "tags"
async function main() {
await generateEditedDates()
+ await generateTags(path.resolve("..", "..", "packages", "tags"))
}
void main()
diff --git a/www/apps/resources/.vercelignore b/www/apps/resources/.vercelignore
new file mode 100644
index 0000000000..6b1839103b
--- /dev/null
+++ b/www/apps/resources/.vercelignore
@@ -0,0 +1 @@
+../../utils
\ No newline at end of file
diff --git a/www/apps/resources/next.config.mjs b/www/apps/resources/next.config.mjs
index 5065b8f3ff..038d88fd2a 100644
--- a/www/apps/resources/next.config.mjs
+++ b/www/apps/resources/next.config.mjs
@@ -6,6 +6,7 @@ import {
typeListLinkFixerPlugin,
workflowDiagramLinkFixerPlugin,
} from "remark-rehype-plugins"
+import bundleAnalyzer from "@next/bundle-analyzer"
import mdxPluginOptions from "./mdx-options.mjs"
import path from "node:path"
@@ -137,4 +138,8 @@ const nextConfig = {
// },
}
-export default withMDX(nextConfig)
+const withBundleAnalyzer = bundleAnalyzer({
+ enabled: process.env.ANALYZE === "true",
+})
+
+export default withMDX(withBundleAnalyzer(nextConfig))
diff --git a/www/apps/resources/package.json b/www/apps/resources/package.json
index f0e425ff4d..de66ef5b9b 100644
--- a/www/apps/resources/package.json
+++ b/www/apps/resources/package.json
@@ -27,17 +27,20 @@
"remark-frontmatter": "^5.0.0"
},
"devDependencies": {
+ "@next/bundle-analyzer": "^15.1.1",
"@types/mdx": "^2.0.13",
"@types/node": "^20",
"@types/react": "npm:types-react@rc",
"@types/react-dom": "npm:types-react@rc",
"autoprefixer": "^10.0.1",
"build-scripts": "*",
+ "docs-utils": "*",
"eslint": "^9.13.0",
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-react-hooks": "^5.0.0",
"postcss": "^8",
"remark-rehype-plugins": "*",
+ "tags": "*",
"tailwind": "*",
"tailwindcss": "^3.3.0",
"ts-node": "^10.9.2",
diff --git a/www/apps/resources/scripts/prepare.mjs b/www/apps/resources/scripts/prepare.mjs
index a9ec814a87..13054a2b59 100644
--- a/www/apps/resources/scripts/prepare.mjs
+++ b/www/apps/resources/scripts/prepare.mjs
@@ -1,13 +1,16 @@
import { generateEditedDates, generateSidebar } from "build-scripts"
+import { generateTags } from "tags"
import { main as generateSlugChanges } from "./generate-slug-changes.mjs"
import { main as generateFilesMap } from "./generate-files-map.mjs"
import { sidebar } from "../sidebar.mjs"
+import path from "path"
async function main() {
await generateSidebar(sidebar)
await generateSlugChanges()
await generateFilesMap()
await generateEditedDates()
+ await generateTags(path.resolve("..", "..", "packages", "tags"))
}
void main()
diff --git a/www/apps/resources/utils/get-slugs.mjs b/www/apps/resources/utils/get-slugs.mjs
index b3e247b5bb..6c0249021b 100644
--- a/www/apps/resources/utils/get-slugs.mjs
+++ b/www/apps/resources/utils/get-slugs.mjs
@@ -1,6 +1,6 @@
import { statSync, readdirSync } from "fs"
import path from "path"
-import { getFileSlugUtil } from "remark-rehype-plugins"
+import { getFileSlug } from "../../../packages/docs-utils/dist"
const monoRepoPath = path.resolve("..", "..", "..")
@@ -42,7 +42,7 @@ export default async function getSlugs(options = {}) {
continue
}
- const newSlug = await getFileSlugUtil(filePath)
+ const newSlug = await getFileSlug(filePath)
if (newSlug) {
slugs.push({
diff --git a/www/eslint.config.mjs b/www/eslint.config.mjs
index cd8931d7e6..ee47933a0a 100644
--- a/www/eslint.config.mjs
+++ b/www/eslint.config.mjs
@@ -29,6 +29,7 @@ export default [
"**/public",
"**/.eslintrc.js",
"**/generated",
+ "packages/tags/src/tags"
],
}, ...compat.extends(
"eslint:recommended",
diff --git a/www/package.json b/www/package.json
index fc0b769768..6f6f277eb7 100644
--- a/www/package.json
+++ b/www/package.json
@@ -16,7 +16,8 @@
"start": "turbo run start:monorepo",
"dev": "turbo run dev:monorepo",
"lint": "turbo run lint",
- "lint:content": "turbo run lint:content"
+ "lint:content": "turbo run lint:content",
+ "watch": "turbo run watch"
},
"dependencies": {
"autoprefixer": "10.4.14",
diff --git a/www/packages/build-scripts/package.json b/www/packages/build-scripts/package.json
index 487155176b..d8fdcffbe3 100644
--- a/www/packages/build-scripts/package.json
+++ b/www/packages/build-scripts/package.json
@@ -24,13 +24,15 @@
],
"scripts": {
"build": "yarn clean && tsc",
- "clean": "rimraf dist"
+ "clean": "rimraf dist",
+ "watch": "tsc --watch"
},
"dependencies": {
"remark-rehype-plugins": "*"
},
"devDependencies": {
"@types/node": "^20.11.20",
+ "docs-utils": "*",
"rimraf": "^5.0.5",
"tsconfig": "*",
"types": "*",
diff --git a/www/packages/build-scripts/src/index.ts b/www/packages/build-scripts/src/index.ts
index 5de4765f63..7f2025ac86 100644
--- a/www/packages/build-scripts/src/index.ts
+++ b/www/packages/build-scripts/src/index.ts
@@ -2,8 +2,6 @@ export * from "./generate-edited-dates.js"
export * from "./generate-sidebar.js"
export * from "./retrieve-mdx-pages.js"
-export * from "./utils/find-metadata-title.js"
-export * from "./utils/find-page-heading.js"
export * from "./utils/get-core-flows-ref-sidebar-children.js"
export * from "./utils/get-sidebar-item-link.js"
export * from "./utils/sidebar-attach-href-common-options.js"
diff --git a/www/packages/build-scripts/src/retrieve-mdx-pages.ts b/www/packages/build-scripts/src/retrieve-mdx-pages.ts
index fb26ce2097..0f9cc8073d 100644
--- a/www/packages/build-scripts/src/retrieve-mdx-pages.ts
+++ b/www/packages/build-scripts/src/retrieve-mdx-pages.ts
@@ -1,6 +1,6 @@
import { readdirSync } from "fs"
import path from "path"
-import { getFileSlugSyncUtil } from "remark-rehype-plugins"
+import { getFileSlugSync } from "../../docs-utils/dist/index.js"
type Options = {
basePath: string
@@ -24,7 +24,7 @@ export function retrieveMdxPages({ basePath }: Options): string[] {
continue
}
- const slug = getFileSlugSyncUtil(filePath)
+ const slug = getFileSlugSync(filePath)
urls.push(
slug ||
diff --git a/www/packages/build-scripts/src/utils/find-metadata-title.ts b/www/packages/build-scripts/src/utils/find-metadata-title.ts
deleted file mode 100644
index f668022a37..0000000000
--- a/www/packages/build-scripts/src/utils/find-metadata-title.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-const REGEX = /export const metadata = {[\s\S]*title: `(?
.*)`/
-
-export default function findMetadataTitle(content: string): string | undefined {
- const headingMatch = REGEX.exec(content)
-
- return headingMatch?.groups?.title
-}
diff --git a/www/packages/build-scripts/src/utils/find-page-heading.ts b/www/packages/build-scripts/src/utils/find-page-heading.ts
deleted file mode 100644
index 46aad5f368..0000000000
--- a/www/packages/build-scripts/src/utils/find-page-heading.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-const HEADING_REGEX = /# (?.*)/
-
-export default function findPageHeading(content: string): string | undefined {
- const headingMatch = HEADING_REGEX.exec(content)
-
- return headingMatch?.groups?.title
-}
diff --git a/www/packages/build-scripts/src/utils/get-sidebar-item-link.ts b/www/packages/build-scripts/src/utils/get-sidebar-item-link.ts
index 62f6f790b3..794aa0abfa 100644
--- a/www/packages/build-scripts/src/utils/get-sidebar-item-link.ts
+++ b/www/packages/build-scripts/src/utils/get-sidebar-item-link.ts
@@ -1,8 +1,5 @@
-import { getFrontMatterUtil } from "remark-rehype-plugins"
+import { getFrontMatter, findPageTitle } from "../../../docs-utils/dist/index.js"
import { ItemsToAdd, sidebarAttachHrefCommonOptions } from "../index.js"
-import { readFileSync } from "fs"
-import findMetadataTitle from "./find-metadata-title.js"
-import findPageHeading from "./find-page-heading.js"
import { InteractiveSidebarItem } from "types"
export async function getSidebarItemLink({
@@ -14,26 +11,18 @@ export async function getSidebarItemLink({
basePath: string
fileBasename: string
}): Promise {
- const frontmatter = await getFrontMatterUtil(filePath)
+ const frontmatter = await getFrontMatter(filePath)
if (frontmatter.sidebar_autogenerate_exclude) {
return
}
- const fileContent = frontmatter.sidebar_label
- ? ""
- : readFileSync(filePath, "utf-8")
-
const newItem = sidebarAttachHrefCommonOptions([
{
type: "link",
path:
frontmatter.slug ||
filePath.replace(basePath, "").replace(`/${fileBasename}`, ""),
- title:
- frontmatter.sidebar_label ||
- findMetadataTitle(fileContent) ||
- findPageHeading(fileContent) ||
- "",
+ title: frontmatter.sidebar_label || findPageTitle(filePath) || "",
},
])[0] as InteractiveSidebarItem
diff --git a/www/packages/build-scripts/tsconfig.json b/www/packages/build-scripts/tsconfig.json
index a4cb9a6d0d..cab37f3aec 100644
--- a/www/packages/build-scripts/tsconfig.json
+++ b/www/packages/build-scripts/tsconfig.json
@@ -6,6 +6,7 @@
"tsBuildInfoFile": "./dist/.tsbuildinfo-client",
"noEmit": false,
"lib": ["es2022"],
+ "target": "es2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"declaration": true,
diff --git a/www/packages/docs-utils/package.json b/www/packages/docs-utils/package.json
new file mode 100644
index 0000000000..c5c5c4a773
--- /dev/null
+++ b/www/packages/docs-utils/package.json
@@ -0,0 +1,48 @@
+{
+ "name": "docs-utils",
+ "version": "0.0.0",
+ "private": true,
+ "license": "MIT",
+ "publishConfig": {
+ "access": "public"
+ },
+ "type": "module",
+ "main": "./dist/index.js",
+ "types": "./dist/index.d.ts",
+ "module": "./dist/index.js",
+ "exports": {
+ ".": {
+ "import": {
+ "types": "./dist/index.d.ts",
+ "default": "./dist/index.js"
+ }
+ }
+ },
+ "sideEffects": false,
+ "files": [
+ "dist/**"
+ ],
+ "scripts": {
+ "build": "yarn clean && tsc",
+ "clean": "rimraf dist",
+ "watch": "tsc --watch"
+ },
+ "dependencies": {
+ "remark-frontmatter": "^5.0.0",
+ "remark-parse": "^11.0.0",
+ "remark-stringify": "^11.0.0",
+ "to-vfile": "^8.0.0",
+ "unified": "^11.0.4",
+ "vfile-matter": "^5.0.0"
+ },
+ "devDependencies": {
+ "@types/node": "^20.11.20",
+ "rimraf": "^5.0.5",
+ "tsconfig": "*",
+ "types": "*",
+ "typescript": "^5.3.3"
+ },
+ "engines": {
+ "node": ">=18.17.0"
+ }
+}
diff --git a/www/packages/docs-utils/src/find-title.ts b/www/packages/docs-utils/src/find-title.ts
new file mode 100644
index 0000000000..7dc849e327
--- /dev/null
+++ b/www/packages/docs-utils/src/find-title.ts
@@ -0,0 +1,23 @@
+import { readFileSync } from "fs"
+
+const REGEX = /export const metadata = {[\s\S]*title: `(?.*)`/
+
+export function findMetadataTitle(content: string): string | undefined {
+ const headingMatch = REGEX.exec(content)
+
+ return headingMatch?.groups?.title
+}
+
+const HEADING_REGEX = /# (?.*)/
+
+export function findPageHeading(content: string): string | undefined {
+ const headingMatch = HEADING_REGEX.exec(content)
+
+ return headingMatch?.groups?.title
+}
+
+export function findPageTitle(filePath: string): string | undefined {
+ const content = readFileSync(filePath, "utf-8")
+
+ return findMetadataTitle(content) || findPageHeading(content)
+}
diff --git a/www/packages/remark-rehype-plugins/src/utils/get-file-slug.ts b/www/packages/docs-utils/src/get-file-slug.ts
similarity index 55%
rename from www/packages/remark-rehype-plugins/src/utils/get-file-slug.ts
rename to www/packages/docs-utils/src/get-file-slug.ts
index 4b4b816bea..026a6bdd51 100644
--- a/www/packages/remark-rehype-plugins/src/utils/get-file-slug.ts
+++ b/www/packages/docs-utils/src/get-file-slug.ts
@@ -1,12 +1,12 @@
-import { getFrontMatterUtil } from "./get-front-matter.js"
import { matter } from "vfile-matter"
import { readSync } from "to-vfile"
-import { FrontMatter } from "../types/index.js"
+import { FrontMatter } from "types"
+import { getFrontMatter } from "./get-front-matter.js"
-export async function getFileSlugUtil(
+export async function getFileSlug(
filePath: string
): Promise {
- const fileFrontmatter = await getFrontMatterUtil(filePath)
+ const fileFrontmatter = await getFrontMatter(filePath)
if (fileFrontmatter.slug) {
// add to slugs array
@@ -14,7 +14,7 @@ export async function getFileSlugUtil(
}
}
-export function getFileSlugSyncUtil(filePath: string): string | undefined {
+export function getFileSlugSync(filePath: string): string | undefined {
const content = readSync(filePath)
matter(content)
diff --git a/www/packages/remark-rehype-plugins/src/utils/get-front-matter.ts b/www/packages/docs-utils/src/get-front-matter.ts
similarity index 60%
rename from www/packages/remark-rehype-plugins/src/utils/get-front-matter.ts
rename to www/packages/docs-utils/src/get-front-matter.ts
index 6324920850..71e8b90186 100644
--- a/www/packages/remark-rehype-plugins/src/utils/get-front-matter.ts
+++ b/www/packages/docs-utils/src/get-front-matter.ts
@@ -2,13 +2,11 @@ import remarkFrontmatter from "remark-frontmatter"
import remarkParse from "remark-parse"
import remarkStringify from "remark-stringify"
import { unified } from "unified"
-import { read } from "to-vfile"
+import { read, readSync } from "to-vfile"
import { matter } from "vfile-matter"
-import { FrontMatter } from "../types/index.js"
+import { FrontMatter } from "types"
-export async function getFrontMatterUtil(
- filePath: string
-): Promise {
+export async function getFrontMatter(filePath: string): Promise {
return (
await unified()
.use(remarkParse)
@@ -22,3 +20,11 @@ export async function getFrontMatterUtil(
.process(await read(filePath))
).data.matter as FrontMatter
}
+
+export function getFrontMatterSync(filePath: string): FrontMatter {
+ const content = readSync(filePath)
+
+ matter(content)
+
+ return content.data.matter as FrontMatter
+}
diff --git a/www/packages/docs-utils/src/index.ts b/www/packages/docs-utils/src/index.ts
new file mode 100644
index 0000000000..3f3937b08f
--- /dev/null
+++ b/www/packages/docs-utils/src/index.ts
@@ -0,0 +1,3 @@
+export * from "./find-title.js"
+export * from "./get-file-slug.js"
+export * from "./get-front-matter.js"
diff --git a/www/packages/docs-utils/tsconfig.json b/www/packages/docs-utils/tsconfig.json
new file mode 100644
index 0000000000..ce3e8d1889
--- /dev/null
+++ b/www/packages/docs-utils/tsconfig.json
@@ -0,0 +1,20 @@
+{
+ "extends": ["tsconfig/base.json"],
+ "compilerOptions": {
+ "rootDir": "src",
+ "outDir": "dist",
+ "tsBuildInfoFile": "./dist/.tsbuildinfo-client",
+ "noEmit": false,
+ "lib": ["es2022"],
+ "module": "NodeNext",
+ "moduleResolution": "NodeNext",
+ "target": "ESNext",
+ "declaration": true,
+ "declarationMap": true,
+ "esModuleInterop": true,
+ "strict": true,
+ "skipLibCheck": true,
+ "resolveJsonModule": true
+ },
+ "include": ["src"]
+}
diff --git a/www/packages/remark-rehype-plugins/package.json b/www/packages/remark-rehype-plugins/package.json
index 2456c59154..796bc23ac1 100644
--- a/www/packages/remark-rehype-plugins/package.json
+++ b/www/packages/remark-rehype-plugins/package.json
@@ -24,10 +24,12 @@
],
"scripts": {
"build": "yarn clean && tsc",
- "clean": "rimraf dist"
+ "clean": "rimraf dist",
+ "watch": "tsc --watch"
},
"dependencies": {
"@cloudinary/url-gen": "^1.17.0",
+ "docs-utils": "*",
"remark-frontmatter": "^5.0.0",
"remark-parse": "^11.0.0",
"remark-stringify": "^11.0.0",
diff --git a/www/packages/remark-rehype-plugins/src/index.ts b/www/packages/remark-rehype-plugins/src/index.ts
index 049992a9f5..9e1ab7c242 100644
--- a/www/packages/remark-rehype-plugins/src/index.ts
+++ b/www/packages/remark-rehype-plugins/src/index.ts
@@ -9,5 +9,3 @@ export * from "./type-list-link-fixer.js"
export * from "./workflow-diagram-link-fixer.js"
export * from "./utils/fix-link.js"
-export * from "./utils/get-file-slug.js"
-export * from "./utils/get-front-matter.js"
diff --git a/www/packages/remark-rehype-plugins/src/types/index.ts b/www/packages/remark-rehype-plugins/src/types/index.ts
index faaad3a18c..7a0543b2c9 100644
--- a/www/packages/remark-rehype-plugins/src/types/index.ts
+++ b/www/packages/remark-rehype-plugins/src/types/index.ts
@@ -98,15 +98,6 @@ export declare type CloudinaryConfig = {
roundCorners?: number
}
-export declare type FrontMatter = {
- slug?: string
- sidebar_label?: string
- sidebar_group?: string
- sidebar_group_main?: boolean
- sidebar_position?: number
- sidebar_autogenerate_exclude?: boolean
-}
-
export declare type CrossProjectLinksOptions = {
baseUrl: string
projectUrls?: {
diff --git a/www/packages/remark-rehype-plugins/src/utils/fix-link.ts b/www/packages/remark-rehype-plugins/src/utils/fix-link.ts
index 8ebab0a0b3..9122d5cb41 100644
--- a/www/packages/remark-rehype-plugins/src/utils/fix-link.ts
+++ b/www/packages/remark-rehype-plugins/src/utils/fix-link.ts
@@ -1,5 +1,5 @@
import path from "path"
-import { getFileSlugSyncUtil } from "./get-file-slug.js"
+import { getFileSlugSync } from "../../../docs-utils/dist/index.js"
export type FixLinkOptions = {
currentPageFilePath: string
@@ -20,7 +20,7 @@ export function fixLinkUtil({
fullLinkedFilePath = fullLinkedFilePath.replace(hash, "")
// get absolute path of the URL
const linkedFilePath = fullLinkedFilePath.replace(basePath, "")
- const linkedFileSlug = getFileSlugSyncUtil(fullLinkedFilePath)
+ const linkedFileSlug = getFileSlugSync(fullLinkedFilePath)
const newLink =
linkedFileSlug ||
diff --git a/www/packages/tags/package.json b/www/packages/tags/package.json
new file mode 100644
index 0000000000..a26f29c964
--- /dev/null
+++ b/www/packages/tags/package.json
@@ -0,0 +1,45 @@
+{
+ "name": "tags",
+ "version": "0.0.0",
+ "private": true,
+ "license": "MIT",
+ "publishConfig": {
+ "access": "public"
+ },
+ "type": "module",
+ "main": "./dist/index.js",
+ "types": "./dist/index.d.ts",
+ "module": "./dist/index.js",
+ "exports": {
+ ".": {
+ "import": {
+ "types": "./dist/index.d.ts",
+ "default": "./dist/index.js"
+ }
+ }
+ },
+ "sideEffects": false,
+ "files": [
+ "dist/**"
+ ],
+ "scripts": {
+ "build": "yarn clean && yarn generate:tags && tsc",
+ "generate:tags": "node --loader ts-node/esm src/scripts/generate-tags.ts",
+ "clean": "rimraf dist",
+ "watch": "tsc --watch"
+ },
+ "dependencies": {
+ "docs-utils": "*"
+ },
+ "devDependencies": {
+ "@types/node": "^20.11.20",
+ "rimraf": "^5.0.5",
+ "ts-node": "^10.9.1",
+ "tsconfig": "*",
+ "types": "*",
+ "typescript": "^5.3.3"
+ },
+ "engines": {
+ "node": ">=18.17.0"
+ }
+}
diff --git a/www/packages/tags/src/index.ts b/www/packages/tags/src/index.ts
new file mode 100644
index 0000000000..42c04ccdf0
--- /dev/null
+++ b/www/packages/tags/src/index.ts
@@ -0,0 +1 @@
+export * from "./utils/index.js"
diff --git a/www/packages/tags/src/scripts/generate-tags.ts b/www/packages/tags/src/scripts/generate-tags.ts
new file mode 100644
index 0000000000..0db1797d9d
--- /dev/null
+++ b/www/packages/tags/src/scripts/generate-tags.ts
@@ -0,0 +1,3 @@
+import { generateTags } from "../utils/generate-tags.js"
+
+void generateTags()
diff --git a/www/packages/tags/src/tags/index.ts b/www/packages/tags/src/tags/index.ts
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/www/packages/tags/src/utils/generate-tags.ts b/www/packages/tags/src/utils/generate-tags.ts
new file mode 100644
index 0000000000..1b7d6b04ab
--- /dev/null
+++ b/www/packages/tags/src/utils/generate-tags.ts
@@ -0,0 +1,119 @@
+import { statSync } from "fs"
+import { mkdir, readdir, rm, writeFile } from "fs/promises"
+import path from "path"
+import type { Tags } from "types"
+import { findPageTitle, getFrontMatterSync } from "docs-utils"
+
+type ConfigItem = {
+ path: string
+ contentPaths: string[]
+}
+
+const config: ConfigItem[] = [
+ {
+ path: path.resolve("..", "..", "apps", "book"),
+ contentPaths: ["app"],
+ },
+ {
+ path: path.resolve("..", "..", "apps", "resources"),
+ contentPaths: ["app", "references"],
+ },
+ {
+ path: path.resolve("..", "..", "apps", "ui"),
+ contentPaths: [path.join("src", "content", "docs")],
+ },
+ {
+ path: path.resolve("..", "..", "apps", "user-guide"),
+ contentPaths: ["app"],
+ },
+]
+
+function normalizePageTitle(title: string): string {
+ // remove variables from title
+ return title.replaceAll(/\$\{.+\}/g, "").trim()
+}
+
+function tagNameToFileName(tagName: string): string {
+ return `${tagName.toLowerCase().replaceAll(" ", "-")}.ts`
+}
+
+function tagNameToVarName(tagName: string): string {
+ return tagName
+ .toLowerCase()
+ .replaceAll(/\s([a-zA-Z\d])/g, (captured) => captured.toUpperCase().trim())
+}
+
+export async function generateTags(basePath?: string) {
+ basePath = basePath || path.resolve()
+ const tags: Tags = {}
+ async function getTags(item: ConfigItem) {
+ async function scanDirectory(dirPath: string) {
+ const files = await readdir(dirPath)
+
+ for (const file of files) {
+ const fullPath = path.join(dirPath, file)
+ if (!file.endsWith(".mdx") || file.startsWith("_")) {
+ if (statSync(fullPath).isDirectory()) {
+ await scanDirectory(fullPath)
+ }
+ continue
+ }
+
+ const frontmatter = getFrontMatterSync(fullPath)
+ const fileBasename = path.basename(file)
+
+ frontmatter.tags?.forEach((tag) => {
+ if (!Object.hasOwn(tags, tag)) {
+ tags[tag] = []
+ }
+
+ tags[tag].push({
+ title: normalizePageTitle(
+ frontmatter.sidebar_label || findPageTitle(fullPath) || ""
+ ),
+ path:
+ frontmatter.slug ||
+ fullPath.replace(item.path, "").replace(`/${fileBasename}`, ""),
+ })
+ })
+ }
+ }
+
+ for (const contentPath of item.contentPaths) {
+ const basePath = path.join(item.path, contentPath)
+
+ await scanDirectory(basePath)
+ }
+ }
+
+ await Promise.all(
+ config.map(async (item) => {
+ await getTags(item)
+ })
+ )
+
+ const tagsDir = path.join(basePath, "src", "tags")
+ // clear existing tags
+ await rm(tagsDir, {
+ recursive: true,
+ force: true,
+ })
+ await mkdir(tagsDir)
+ // write tags
+ const files: string[] = []
+ await Promise.all(
+ Object.keys(tags).map(async (tagName) => {
+ const fileName = tagNameToFileName(tagName)
+ const varName = tagNameToVarName(tagName)
+
+ const content = `export const ${varName} = ${JSON.stringify(tags[tagName], null, 2)}`
+
+ await writeFile(path.join(tagsDir, fileName), content)
+ files.push(fileName.replace(/\.ts$/, ".js"))
+ })
+ )
+
+ // write index.ts
+ const indexContent = files.map((file) => `export * from "./${file}"\n`)
+ await writeFile(path.join(tagsDir, "index.ts"), indexContent)
+}
diff --git a/www/packages/tags/src/utils/index.ts b/www/packages/tags/src/utils/index.ts
new file mode 100644
index 0000000000..d4fe9b7b32
--- /dev/null
+++ b/www/packages/tags/src/utils/index.ts
@@ -0,0 +1,2 @@
+export * from "./generate-tags.js"
+export * from "./tags.js"
diff --git a/www/packages/tags/src/utils/tags.ts b/www/packages/tags/src/utils/tags.ts
new file mode 100644
index 0000000000..99ebd6c386
--- /dev/null
+++ b/www/packages/tags/src/utils/tags.ts
@@ -0,0 +1,13 @@
+import { Tag, Tags } from "types"
+import * as tags from "../tags/index.js"
+
+export const getTagItems = (tagName: string): Tag | undefined => {
+ if (!Object.hasOwn(tags, tagName)) {
+ return
+ }
+ return tags[tagName as keyof typeof tags]
+}
+
+export const getAllTags = (): Tags => {
+ return tags
+}
diff --git a/www/packages/tags/tsconfig.json b/www/packages/tags/tsconfig.json
new file mode 100644
index 0000000000..a16fba15fc
--- /dev/null
+++ b/www/packages/tags/tsconfig.json
@@ -0,0 +1,25 @@
+{
+ "extends": "tsconfig/base.json",
+ "compilerOptions": {
+ "rootDir": "src",
+ "outDir": "dist",
+ "tsBuildInfoFile": "./dist/.tsbuildinfo-client",
+ "noEmit": false,
+ "lib": ["es2022"],
+ "module": "NodeNext",
+ "moduleResolution": "NodeNext",
+ "target": "ESNext",
+ "declaration": true,
+ "declarationMap": true,
+ "esModuleInterop": true,
+ "strict": true,
+ "skipLibCheck": true,
+ "resolveJsonModule": true
+ },
+ "include": ["src"],
+ "ts-node": {
+ "esm": true,
+ "experimentalSpecifierResolution": "node",
+ "transpileOnly": true
+ }
+}
diff --git a/www/packages/types/package.json b/www/packages/types/package.json
index 526002825e..612ad62690 100644
--- a/www/packages/types/package.json
+++ b/www/packages/types/package.json
@@ -28,7 +28,8 @@
],
"scripts": {
"build": "yarn clean && tsc",
- "clean": "rimraf dist"
+ "clean": "rimraf dist",
+ "watch": "tsc --watch"
},
"devDependencies": {
"@types/node": "^20.11.20",
diff --git a/www/packages/types/src/frontmatter.ts b/www/packages/types/src/frontmatter.ts
new file mode 100644
index 0000000000..f9af43a216
--- /dev/null
+++ b/www/packages/types/src/frontmatter.ts
@@ -0,0 +1,9 @@
+export declare type FrontMatter = {
+ slug?: string
+ sidebar_label?: string
+ sidebar_group?: string
+ sidebar_group_main?: boolean
+ sidebar_position?: number
+ sidebar_autogenerate_exclude?: boolean
+ tags?: string[]
+}
diff --git a/www/packages/types/src/index.ts b/www/packages/types/src/index.ts
index 67a85c06d0..069be48cdc 100644
--- a/www/packages/types/src/index.ts
+++ b/www/packages/types/src/index.ts
@@ -1,10 +1,12 @@
export * from "./api-testing.js"
export * from "./build-scripts.js"
export * from "./config.js"
+export * from "./frontmatter.js"
export * from "./general.js"
export * from "./menu.js"
export * from "./navigation.js"
export * from "./navigation-dropdown.js"
export * from "./sidebar.js"
+export * from "./tags.js"
export * from "./toc.js"
export * from "./workflow.js"
diff --git a/www/packages/types/src/tags.ts b/www/packages/types/src/tags.ts
new file mode 100644
index 0000000000..8605aa40d8
--- /dev/null
+++ b/www/packages/types/src/tags.ts
@@ -0,0 +1,8 @@
+export type Tag = {
+ title: string
+ path: string
+}[]
+
+export type Tags = {
+ [k: string]: Tag
+}
diff --git a/www/turbo.json b/www/turbo.json
index c2392e88e1..bb72e09e18 100644
--- a/www/turbo.json
+++ b/www/turbo.json
@@ -22,6 +22,7 @@
},
"lint": { },
"lint:content": { },
+ "watch": { },
"dev:monorepo": {
"dependsOn": [
"^build-scripts#build",
diff --git a/www/yarn.lock b/www/yarn.lock
index fc3cd7c749..0bf88e1fd4 100644
--- a/www/yarn.lock
+++ b/www/yarn.lock
@@ -1804,6 +1804,15 @@ __metadata:
languageName: node
linkType: hard
+"@next/bundle-analyzer@npm:^15.1.1":
+ version: 15.1.1
+ resolution: "@next/bundle-analyzer@npm:15.1.1"
+ dependencies:
+ webpack-bundle-analyzer: 4.10.1
+ checksum: 2a85db96b1b37a6273d7ee81978ffd11181335b2017c636bcccfc6bca3fdd9a7f1b3c45fba35b4b51f5ca844da76f604d5e8568c894fbdae5a14ff27548cc0d6
+ languageName: node
+ linkType: hard
+
"@next/env@npm:15.0.4":
version: 15.0.4
resolution: "@next/env@npm:15.0.4"
@@ -7142,6 +7151,7 @@ __metadata:
rehype-mdx-code-props: ^2.0.0
rehype-slug: ^6.0.0
remark-rehype-plugins: "*"
+ tags: "*"
tailwind: "*"
tailwindcss: ^3.3.0
tsconfig: "*"
@@ -7204,6 +7214,7 @@ __metadata:
resolution: "build-scripts@workspace:packages/build-scripts"
dependencies:
"@types/node": ^20.11.20
+ docs-utils: "*"
remark-rehype-plugins: "*"
rimraf: ^5.0.5
tsconfig: "*"
@@ -8460,6 +8471,24 @@ __metadata:
languageName: unknown
linkType: soft
+"docs-utils@*, docs-utils@workspace:packages/docs-utils":
+ version: 0.0.0-use.local
+ resolution: "docs-utils@workspace:packages/docs-utils"
+ dependencies:
+ "@types/node": ^20.11.20
+ remark-frontmatter: ^5.0.0
+ remark-parse: ^11.0.0
+ remark-stringify: ^11.0.0
+ rimraf: ^5.0.5
+ to-vfile: ^8.0.0
+ tsconfig: "*"
+ types: "*"
+ typescript: ^5.3.3
+ unified: ^11.0.4
+ vfile-matter: ^5.0.0
+ languageName: unknown
+ linkType: soft
+
"doctrine@npm:^2.1.0":
version: 2.1.0
resolution: "doctrine@npm:2.1.0"
@@ -14615,6 +14644,7 @@ __metadata:
"@cloudinary/url-gen": ^1.17.0
"@types/node": ^20.11.20
docs-ui: "*"
+ docs-utils: "*"
remark-frontmatter: ^5.0.0
remark-parse: ^11.0.0
remark-stringify: ^11.0.0
@@ -14797,6 +14827,7 @@ __metadata:
"@mdx-js/loader": ^3.1.0
"@mdx-js/react": ^3.1.0
"@medusajs/icons": ^2.0.0
+ "@next/bundle-analyzer": ^15.1.1
"@next/mdx": 15.0.4
"@types/mdx": ^2.0.13
"@types/node": ^20
@@ -14806,6 +14837,7 @@ __metadata:
build-scripts: "*"
clsx: ^2.1.0
docs-ui: "*"
+ docs-utils: "*"
eslint: ^9.13.0
eslint-plugin-prettier: ^5.2.1
eslint-plugin-react-hooks: ^5.0.0
@@ -14817,6 +14849,7 @@ __metadata:
remark-directive: ^3.0.0
remark-frontmatter: ^5.0.0
remark-rehype-plugins: "*"
+ tags: "*"
tailwind: "*"
tailwindcss: ^3.3.0
ts-node: ^10.9.2
@@ -15649,6 +15682,20 @@ __metadata:
languageName: node
linkType: hard
+"tags@*, tags@workspace:packages/tags":
+ version: 0.0.0-use.local
+ resolution: "tags@workspace:packages/tags"
+ dependencies:
+ "@types/node": ^20.11.20
+ docs-utils: "*"
+ rimraf: ^5.0.5
+ ts-node: ^10.9.1
+ tsconfig: "*"
+ types: "*"
+ typescript: ^5.3.3
+ languageName: unknown
+ linkType: soft
+
"tailwind-merge@npm:^2.2.1":
version: 2.2.1
resolution: "tailwind-merge@npm:2.2.1"