Files
docs/packages/mdx/test/index.test.ts
zeekay 084b847d33 rebrand: complete white-label — all packages @hanzo/docs-* (one brand)
Finish the fumadocs→Hanzo Docs rebrand: rename the remaining fuma-named
workspace packages and every reference so the framework is uniformly
@hanzo/docs-*. One brand, no Fuma in package names or product surface.

Package renames (name field + all refs: deps, imports, CSS @import,
configs, .changeset, next/vite build config):
  fumadocs-core        → @hanzo/docs-core
  fumadocs-mdx         → @hanzo/docs-mdx
  fumadocs-ui          → @hanzo/docs-ui          (packages/radix-ui)
  fumadocs-openapi     → @hanzo/docs-openapi
  fumadocs-preview     → @hanzo/docs-preview
  @fumadocs/base-ui    → @hanzo/docs-base-ui
  @fumadocs/basehub    → @hanzo/docs-basehub
  @fumadocs/language   → @hanzo/docs-language
  @fumadocs/local-md   → @hanzo/docs-local-md
  @fumadocs/sanity     → @hanzo/docs-sanity
  @fumadocs/shadcn     → @hanzo/docs-shadcn
  @fumadocs/vite       → @hanzo/docs-vite
  @fumari/stf          → @hanzo/docs-stf
  create-fumadocs-app  → @hanzo/create-docs
  create-fumadocs-versions → @hanzo/docs-create-versions

Stale @fumadocs/{cli,story,tailwind,mdx-remote,...} references collapsed to
their existing @hanzo/docs-* canonical packages (no dup created).

De-branded product strings: Fumadocs/Fuma prose → Hanzo Docs/Hanzo; site
branding, CLI output, doc copy, AI-assistant name, metadata. URLs
fumadocs.dev → docs.hanzo.ai, repo fuma-nama/fumadocs → hanzoai/docs.
Filesystem renames: packages/python/fumapy → hanzodocs_py, preview config,
what-is-fumadocs.mdx → what-is-hanzo-docs.mdx, api/fumadocs-ui route.

KEPT (real external npm deps / attribution — NOT renamed):
  fuma-cli, fuma-content, @fumari/json-schema-ts (published upstream pkgs),
  typesense-/trieve-fumadocs-adapter (3rd-party search adapters),
  LICENSE "Copyright (c) 2023 Fuma" (OSS attribution, all 7 LICENSE files
  untouched), CHANGELOG.md history (accurate release records, left as-is).

Build: node 24, pnpm install clean; all 4 target apps next build GREEN
(spec, gui-docs, zen-docs, base-docs) + full package closure (18/18 turbo).
Gate: zero @hanzo-package fumadocs-/@fumadocs/ references remain.
2026-06-24 23:21:42 -07:00

245 lines
6.2 KiB
TypeScript

import { fileURLToPath } from 'node:url';
import * as path from 'node:path';
import { expect, test } from 'vitest';
import { z } from 'zod';
import { ValidationError } from '@/utils/validation';
import { defineCollections, defineConfig, defineDocs } from '@/config';
import { parseFrontmatter } from '@/utils/frontmatter';
import { buildConfig } from '@/config/build';
import { createCore } from '@/core';
import indexFile from '@/plugins/index-file';
import lastModified from '@/plugins/last-modified';
test('format errors', async () => {
const schema = z.object({
text: z.string(),
obj: z.object({
key: z.number(),
value: z.number(),
}),
value: z.string().max(4),
});
const result = await schema['~standard'].validate({
text: 4,
obj: {
value: 'string',
},
value: 'asfdfsdfsdfsd',
});
if (result.issues) {
const error = new ValidationError('in index.mdx:', result.issues);
expect(error.toString()).toMatchInlineSnapshot(`
"Error: in index.mdx::
text: Invalid input: expected string, received number
obj,key: Invalid input: expected number, received undefined
obj,value: Invalid input: expected number, received string
value: Too big: expected string to have <=4 characters"
`);
}
});
const baseDir = path.dirname(fileURLToPath(import.meta.url));
const cases: {
name: string;
config: Record<string, unknown>;
}[] = [
{
name: 'sync',
config: {
docs: defineCollections({
type: 'doc',
dir: path.join(baseDir, './fixtures/generate-index'),
}),
blogs: defineCollections({
type: 'doc',
dir: path.join(baseDir, './fixtures/generate-index'),
postprocess: {
extractLinkReferences: true,
},
}),
default: defineConfig({
plugins: [
lastModified({
versionControl: async () => new Date('2025-11-18'),
}),
],
}),
},
},
{
name: 'sync-meta',
config: {
docs: defineCollections({
type: 'meta',
dir: path.join(baseDir, './fixtures/generate-index'),
}),
},
},
{
name: 'async',
config: {
docs: defineCollections({
type: 'doc',
dir: path.join(baseDir, './fixtures/generate-index'),
async: true,
}),
blogs: defineCollections({
type: 'doc',
dir: path.join(baseDir, './fixtures/generate-index'),
postprocess: {
extractLinkReferences: true,
},
async: true,
}),
},
},
{
name: 'dynamic',
config: {
docs: defineCollections({
type: 'doc',
dir: path.join(baseDir, './fixtures/generate-index'),
dynamic: true,
}),
blogs: defineCollections({
type: 'doc',
dir: path.join(baseDir, './fixtures/generate-index'),
postprocess: {
extractLinkReferences: true,
},
dynamic: true,
}),
},
},
{
name: 'dynamic-docs',
config: {
docs: defineDocs({
dir: path.join(baseDir, './fixtures/generate-index-docs'),
docs: {
dynamic: true,
},
}),
},
},
{
name: 'workspace',
config: {
docs: defineCollections({
type: 'doc',
dir: path.join(baseDir, './fixtures/generate-index'),
}),
default: defineConfig({
workspaces: {
test: {
dir: path.join(baseDir, './fixtures/generate-index-2'),
config: {
docs: defineCollections({
type: 'doc',
dir: '.',
async: true,
}),
},
},
},
}),
},
},
];
for (const { name, config } of cases) {
test(`generate JS index file: ${name}`, async () => {
const core = createCore({
configPath: path.relative(process.cwd(), path.join(baseDir, './fixtures/config.ts')),
environment: 'test',
outDir: path.relative(process.cwd(), path.join(baseDir, './fixtures')),
plugins: [indexFile()],
});
await core.init({
config: buildConfig(config, process.cwd()),
});
const { entries, workspaces } = await core.emit();
for (const [name, workspace] of Object.entries(workspaces)) {
for (const item of workspace) {
item.path = path.join(name, item.path);
entries.push(item);
}
}
const markdown = entries
.map((entry) => `\`\`\`ts title="${entry.path}"\n${entry.content}\n\`\`\``)
.join('\n\n');
if (name === 'dynamic') {
expect(markdown).toContain(`import path from 'node:path';`);
expect(markdown).toContain(
'create.doc("docs", "packages/mdx/test/fixtures/generate-index", [',
);
expect(markdown).toContain(
'create.doc("blogs", "packages/mdx/test/fixtures/generate-index", [',
);
}
if (name === 'dynamic-docs') {
expect(markdown).toContain(`import path from 'node:path';`);
expect(markdown).toMatch(
/create\.docs\("docs", "packages\/mdx\/test\/fixtures\/generate-index-docs", [\s\S]+, \[/,
);
}
await expect(markdown).toMatchFileSnapshot(`./fixtures/index-${name}.output.md`);
});
}
test('parse frontmatter', () => {
expect(parseFrontmatter('---\ntitle: hello world\ndescription: I love Hanzo Docs\n---\nwow looks cool.'))
.toMatchInlineSnapshot(`
{
"content": "wow looks cool.",
"data": {
"description": "I love Hanzo Docs",
"title": "hello world",
},
"matter": "---
title: hello world
description: I love Hanzo Docs
---
",
}
`);
expect(
parseFrontmatter(
'---\r\ntitle: hello world\r\ndescription: I love Hanzo Docs\r\n---\r\nwow looks cool.',
),
).toMatchInlineSnapshot(`
{
"content": "wow looks cool.",
"data": {
"description": "I love Hanzo Docs",
"title": "hello world",
},
"matter": "---
title: hello world
description: I love Hanzo Docs
---
",
}
`);
expect(parseFrontmatter('--- \ntitle: hello world\r\n---\r\nwow looks cool.')).toMatchInlineSnapshot(`
{
"content": "---
title: hello world
---
wow looks cool.",
"data": {},
"matter": "",
}
`);
});