mirror of
https://github.com/hanzo-docs/docs.git
synced 2026-10-26 23:49:51 +00:00
Core: Add unit tests for MDX plugins
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"fumadocs-core": patch
|
||||
---
|
||||
|
||||
Mark `getTableOfContentsFromPortableText` deprecated
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"fumadocs-core": patch
|
||||
---
|
||||
|
||||
Support relative resolve path for remark-dynamic-content
|
||||
@@ -133,6 +133,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@algolia/client-search": "^4.22.1",
|
||||
"@mdx-js/mdx": "^3.0.1",
|
||||
"@types/flexsearch": "0.7.6",
|
||||
"@types/hast": "^3.0.4",
|
||||
"@types/mdast": "^4.0.3",
|
||||
|
||||
@@ -7,14 +7,25 @@ import { visit } from 'unist-util-visit';
|
||||
const regex = /^\|reference:(?<path>.+)\|/;
|
||||
|
||||
export interface RemarkDynamicContentOptions {
|
||||
/** @defaultValue `process.cwd()` */
|
||||
// todo: Remove in next major
|
||||
/**
|
||||
* @defaultValue `vfile.cwd`
|
||||
* @deprecated use the `cwd` option from remark instead
|
||||
* */
|
||||
cwd?: string;
|
||||
|
||||
/** @defaultValue true */
|
||||
trim?: boolean;
|
||||
|
||||
/**
|
||||
* Resolve reference files relative to `vfile.path`
|
||||
* @defaultValue false
|
||||
*/
|
||||
relative?: boolean;
|
||||
|
||||
/**
|
||||
* Filter specific element types
|
||||
* @defaultValue `['text','code']`
|
||||
* @defaultValue ['text','code']
|
||||
* */
|
||||
visit?: string[];
|
||||
}
|
||||
@@ -29,12 +40,14 @@ export function remarkDynamicContent(
|
||||
options: RemarkDynamicContentOptions = {},
|
||||
): Transformer<Root, Root> {
|
||||
const {
|
||||
cwd = process.cwd(),
|
||||
trim = true,
|
||||
relative = false,
|
||||
visit: filter = ['text', 'code'],
|
||||
} = options;
|
||||
|
||||
return (tree) => {
|
||||
return (tree, file) => {
|
||||
const cwd = options.cwd ?? file.cwd;
|
||||
|
||||
visit(tree, filter, (node) => {
|
||||
const canReplace = 'value' in node && typeof node.value === 'string';
|
||||
if (!canReplace) return;
|
||||
@@ -42,7 +55,9 @@ export function remarkDynamicContent(
|
||||
const result = regex.exec(node.value);
|
||||
|
||||
if (result) {
|
||||
const dest = path.resolve(cwd, result[1]);
|
||||
const dest = relative
|
||||
? path.resolve(cwd, path.dirname(file.path), result[1])
|
||||
: path.resolve(cwd, result[1]);
|
||||
let value = fs.readFileSync(dest).toString();
|
||||
if (trim) value = value.trim();
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
## Hello World
|
||||
|
||||
```md
|
||||
|reference:./fixtures/remark-dynamic-content.md|
|
||||
```
|
||||
@@ -0,0 +1,9 @@
|
||||
## Hello World
|
||||
|
||||
````md
|
||||
## Hello World
|
||||
|
||||
```md
|
||||
|reference:./fixtures/remark-dynamic-content.md|
|
||||
```
|
||||
````
|
||||
@@ -0,0 +1,5 @@
|
||||
## Hello World
|
||||
|
||||
```md
|
||||
|reference:./remark-dynamic-content.md|
|
||||
```
|
||||
@@ -0,0 +1,9 @@
|
||||
## Hello World
|
||||
|
||||
````md
|
||||
## Hello World
|
||||
|
||||
```md
|
||||
|reference:./fixtures/remark-dynamic-content.md|
|
||||
```
|
||||
````
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
# Heading 1
|
||||
|
||||
Some text here
|
||||
|
||||
## Heading 2
|
||||
|
||||
Some text here
|
||||
|
||||
### Heading 3
|
||||
|
||||
Some text here
|
||||
@@ -0,0 +1,17 @@
|
||||
[
|
||||
{
|
||||
"depth": 1,
|
||||
"title": "Heading 1",
|
||||
"url": "#heading-1",
|
||||
},
|
||||
{
|
||||
"depth": 2,
|
||||
"title": "Heading 2",
|
||||
"url": "#heading-2",
|
||||
},
|
||||
{
|
||||
"depth": 3,
|
||||
"title": "Heading 3",
|
||||
"url": "#heading-3",
|
||||
},
|
||||
]
|
||||
@@ -0,0 +1,3 @@
|
||||
```package-install
|
||||
next
|
||||
```
|
||||
@@ -0,0 +1,50 @@
|
||||
import {jsx as _jsx, jsxs as _jsxs} from "react/jsx-runtime";
|
||||
function _createMdxContent(props) {
|
||||
const _components = {
|
||||
code: "code",
|
||||
pre: "pre",
|
||||
...props.components
|
||||
}, {Tab, Tabs} = _components;
|
||||
if (!Tab) _missingMdxReference("Tab", true);
|
||||
if (!Tabs) _missingMdxReference("Tabs", true);
|
||||
return _jsxs(Tabs, {
|
||||
items: ["npm", "pnpm", "yarn"],
|
||||
children: [_jsx(Tab, {
|
||||
value: "npm",
|
||||
children: _jsx(_components.pre, {
|
||||
children: _jsx(_components.code, {
|
||||
className: "language-bash",
|
||||
children: "npm install next\n"
|
||||
})
|
||||
})
|
||||
}), _jsx(Tab, {
|
||||
value: "pnpm",
|
||||
children: _jsx(_components.pre, {
|
||||
children: _jsx(_components.code, {
|
||||
className: "language-bash",
|
||||
children: "pnpm add next\n"
|
||||
})
|
||||
})
|
||||
}), _jsx(Tab, {
|
||||
value: "yarn",
|
||||
children: _jsx(_components.pre, {
|
||||
children: _jsx(_components.code, {
|
||||
className: "language-bash",
|
||||
children: "yarn add next\n"
|
||||
})
|
||||
})
|
||||
})]
|
||||
});
|
||||
}
|
||||
export default function MDXContent(props = {}) {
|
||||
const {wrapper: MDXLayout} = props.components || ({});
|
||||
return MDXLayout ? _jsx(MDXLayout, {
|
||||
...props,
|
||||
children: _jsx(_createMdxContent, {
|
||||
...props
|
||||
})
|
||||
}) : _createMdxContent(props);
|
||||
}
|
||||
function _missingMdxReference(id, component) {
|
||||
throw new Error("Expected " + (component ? "component" : "object") + " `" + id + "` to be defined: you likely forgot to import, pass, or provide it.");
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
# Heading 1
|
||||
|
||||
Some text here
|
||||
|
||||
## Heading 2
|
||||
|
||||
Some text here
|
||||
|
||||
### Heading 3
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"contents": [
|
||||
{
|
||||
"content": "Some text here",
|
||||
"heading": "heading-1",
|
||||
},
|
||||
{
|
||||
"content": "Some text here",
|
||||
"heading": "heading-2",
|
||||
},
|
||||
],
|
||||
"headings": [
|
||||
{
|
||||
"content": "Heading 1",
|
||||
"id": "heading-1",
|
||||
},
|
||||
{
|
||||
"content": "Heading 2",
|
||||
"id": "heading-2",
|
||||
},
|
||||
{
|
||||
"content": "Heading 3",
|
||||
"id": "heading-3",
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -1,9 +1,12 @@
|
||||
import { structure } from '@/mdx-plugins';
|
||||
import { createI18nSearchAPI, createSearchAPI } from '@/search/server';
|
||||
import { getTableOfContents, findNeighbour } from '@/server';
|
||||
|
||||
import {
|
||||
joinPaths,
|
||||
parseFilePath,
|
||||
parseFolderPath,
|
||||
splitPath,
|
||||
} from '@/source/path';
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import type { Root } from '@/server/page-tree';
|
||||
import { expect, test } from 'vitest';
|
||||
import { findNeighbour } from '@/server/page-tree-utils';
|
||||
|
||||
test('Find Neighbours', () => {
|
||||
const tree: Root = {
|
||||
@@ -35,162 +38,72 @@ test('Find Neighbours', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('Get TOC', async () => {
|
||||
const content = `# Heading 1
|
||||
describe('Path utilities', () => {
|
||||
test('parse file path', () => {
|
||||
expect(parseFilePath('test.mdx')).toEqual({
|
||||
dirname: '',
|
||||
name: 'test',
|
||||
flattenedPath: 'test',
|
||||
locale: undefined,
|
||||
path: 'test.mdx',
|
||||
});
|
||||
|
||||
Some text here
|
||||
expect(parseFilePath('nested/test.mdx')).toEqual({
|
||||
dirname: 'nested',
|
||||
name: 'test',
|
||||
flattenedPath: 'nested/test',
|
||||
locale: undefined,
|
||||
path: 'nested/test.mdx',
|
||||
});
|
||||
|
||||
## Heading 2
|
||||
expect(parseFilePath('nested/test.cn.mdx')).toEqual({
|
||||
dirname: 'nested',
|
||||
name: 'test',
|
||||
flattenedPath: 'nested/test.cn',
|
||||
locale: 'cn',
|
||||
path: 'nested/test.cn.mdx',
|
||||
});
|
||||
|
||||
Some text here
|
||||
|
||||
### Heading 3`;
|
||||
|
||||
expect(await getTableOfContents(content)).toStrictEqual([
|
||||
expect.objectContaining({ title: 'Heading 1', url: '#heading-1' }),
|
||||
expect.objectContaining({ title: 'Heading 2', url: '#heading-2' }),
|
||||
expect.objectContaining({ title: 'Heading 3', url: '#heading-3' }),
|
||||
]);
|
||||
});
|
||||
|
||||
test('Structure', async () => {
|
||||
const content = `# Heading 1
|
||||
|
||||
Some text here
|
||||
|
||||
## Heading 2
|
||||
|
||||
Some text here
|
||||
|
||||
### Heading 3`;
|
||||
|
||||
expect(structure(content)).toMatchInlineSnapshot(`
|
||||
{
|
||||
"contents": [
|
||||
{
|
||||
"content": "Some text here",
|
||||
"heading": "heading-1",
|
||||
},
|
||||
{
|
||||
"content": "Some text here",
|
||||
"heading": "heading-2",
|
||||
},
|
||||
],
|
||||
"headings": [
|
||||
{
|
||||
"content": "Heading 1",
|
||||
"id": "heading-1",
|
||||
},
|
||||
{
|
||||
"content": "Heading 2",
|
||||
"id": "heading-2",
|
||||
},
|
||||
{
|
||||
"content": "Heading 3",
|
||||
"id": "heading-3",
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('Search API', () => {
|
||||
const api = createSearchAPI('simple', {
|
||||
indexes: [
|
||||
{
|
||||
title: 'Hello World',
|
||||
content: 'Hello World',
|
||||
url: '/hello-world',
|
||||
},
|
||||
{
|
||||
title: 'Nothing',
|
||||
content: 'Nothing',
|
||||
url: '/nothing',
|
||||
},
|
||||
],
|
||||
expect(parseFilePath('nested\\test.cn.mdx')).toEqual(
|
||||
parseFilePath('nested/test.cn.mdx'),
|
||||
);
|
||||
});
|
||||
|
||||
expect(api.search('Hello')).toHaveLength(1);
|
||||
expect(api.search('pterodactyl')).toHaveLength(0);
|
||||
});
|
||||
test('parse folder path', () => {
|
||||
expect(parseFolderPath('nested')).toEqual({
|
||||
dirname: '',
|
||||
name: 'nested',
|
||||
flattenedPath: 'nested',
|
||||
locale: undefined,
|
||||
path: 'nested',
|
||||
});
|
||||
|
||||
test('Search API Advanced', () => {
|
||||
const api = createSearchAPI('advanced', {
|
||||
tag: true,
|
||||
indexes: [
|
||||
{
|
||||
id: '1',
|
||||
title: 'Index',
|
||||
structuredData: structure(
|
||||
`## Hello World
|
||||
expect(parseFolderPath('nested/nested')).toEqual({
|
||||
dirname: 'nested',
|
||||
name: 'nested',
|
||||
flattenedPath: 'nested/nested',
|
||||
locale: undefined,
|
||||
path: 'nested/nested',
|
||||
});
|
||||
|
||||
something`,
|
||||
),
|
||||
url: '/',
|
||||
tag: 'my-tag',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
title: 'Page',
|
||||
structuredData: structure(
|
||||
`## My Page
|
||||
|
||||
something`,
|
||||
),
|
||||
url: '/page',
|
||||
tag: '',
|
||||
},
|
||||
],
|
||||
expect(parseFolderPath('nested\\nested')).toEqual(
|
||||
parseFolderPath('nested/nested'),
|
||||
);
|
||||
});
|
||||
|
||||
expect(api.search('Page')).toHaveLength(2);
|
||||
expect(api.search('something')).toHaveLength(4);
|
||||
expect(api.search('', { tag: 'my-tag' })).toHaveLength(3);
|
||||
test('join paths', () => {
|
||||
expect(joinPaths(['a', 'b', 'c'])).toBe('a/b/c');
|
||||
expect(joinPaths(['/a'])).toBe('a');
|
||||
expect(joinPaths(['a/', '/b'])).toBe('a/b');
|
||||
expect(joinPaths(['a/', 'b/c'])).toBe('a/b/c');
|
||||
|
||||
expect(api.search('Hello')).toMatchInlineSnapshot(`
|
||||
[
|
||||
{
|
||||
"content": "Index",
|
||||
"id": "1",
|
||||
"type": "page",
|
||||
"url": "/",
|
||||
},
|
||||
{
|
||||
"content": "Hello World",
|
||||
"id": "10",
|
||||
"type": "heading",
|
||||
"url": "/#hello-world",
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test('Seach API I18n', () => {
|
||||
const api = createI18nSearchAPI('simple', {
|
||||
indexes: [
|
||||
[
|
||||
'cn',
|
||||
[
|
||||
{
|
||||
title: 'Hello World Chinese',
|
||||
content: 'Hello World',
|
||||
url: '/hello-world',
|
||||
},
|
||||
],
|
||||
],
|
||||
[
|
||||
'en',
|
||||
[
|
||||
{
|
||||
title: 'Hello World English',
|
||||
content: 'Hello World',
|
||||
url: '/hello-world',
|
||||
},
|
||||
],
|
||||
],
|
||||
],
|
||||
expect(joinPaths(['a', 'b'], 'leading')).toBe('/a/b');
|
||||
expect(joinPaths(['a', 'b'], 'trailing')).toBe('a/b/');
|
||||
});
|
||||
|
||||
expect(api.search('English', { locale: 'en' })).toHaveLength(1);
|
||||
expect(api.search('Hello World Chinese', { locale: 'cn' })).toHaveLength(1);
|
||||
test('split paths', () => {
|
||||
expect(splitPath('a/b/c')).toEqual(['a', 'b', 'c']);
|
||||
expect(splitPath('a//c')).toEqual(['a', 'c']);
|
||||
expect(splitPath('/a/c')).toEqual(['a', 'c']);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
import { expect, test } from 'vitest';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { remark } from 'remark';
|
||||
import {
|
||||
remarkDynamicContent,
|
||||
remarkHeading,
|
||||
remarkInstall,
|
||||
remarkStructure,
|
||||
} from '@/mdx-plugins';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { createProcessor } from '@mdx-js/mdx';
|
||||
|
||||
const cwd = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
test('Remark Dynamic Content', async () => {
|
||||
const content = readFileSync(
|
||||
path.resolve(cwd, './fixtures/remark-dynamic-content.md'),
|
||||
);
|
||||
|
||||
const result = await remark()
|
||||
.use(remarkDynamicContent)
|
||||
.process({ value: content, cwd });
|
||||
|
||||
expect(String(result.value)).toMatchFileSnapshot(
|
||||
path.resolve(cwd, './fixtures/remark-dynamic-content.output.md'),
|
||||
);
|
||||
});
|
||||
|
||||
test('Remark Dynamic Content - Relative', async () => {
|
||||
const file = path.resolve(
|
||||
cwd,
|
||||
'./fixtures/remark-dynamic-content.relative.md',
|
||||
);
|
||||
const content = readFileSync(file);
|
||||
|
||||
const result = await remark()
|
||||
.use(remarkDynamicContent, { relative: true })
|
||||
.process({ path: file, value: content });
|
||||
|
||||
expect(String(result.value)).toMatchFileSnapshot(
|
||||
path.resolve(cwd, './fixtures/remark-dynamic-content.relative.output.md'),
|
||||
);
|
||||
});
|
||||
|
||||
test('Remark Heading', async () => {
|
||||
const file = path.resolve(cwd, './fixtures/remark-heading.md');
|
||||
const content = readFileSync(file);
|
||||
|
||||
const result = await remark().use(remarkHeading).process(content);
|
||||
|
||||
expect(result.data.toc).toMatchFileSnapshot(
|
||||
path.resolve(cwd, './fixtures/remark-heading.output.json'),
|
||||
);
|
||||
});
|
||||
|
||||
test('Remark Install', async () => {
|
||||
const file = path.resolve(cwd, './fixtures/remark-install.md');
|
||||
const content = readFileSync(file);
|
||||
|
||||
const result = await createProcessor({
|
||||
remarkPlugins: [remarkInstall],
|
||||
}).process(content);
|
||||
|
||||
expect(result.toString()).toMatchFileSnapshot(
|
||||
path.resolve(cwd, './fixtures/remark-install.output.js'),
|
||||
);
|
||||
});
|
||||
|
||||
test('Remark Structure', async () => {
|
||||
const content = readFileSync(
|
||||
path.resolve(cwd, './fixtures/remark-structure.md'),
|
||||
);
|
||||
const result = await remark().use(remarkStructure).process(content);
|
||||
|
||||
expect(result.data.structuredData).toMatchFileSnapshot(
|
||||
path.resolve(cwd, './fixtures/remark-structure.output.json'),
|
||||
);
|
||||
});
|
||||
@@ -1,77 +0,0 @@
|
||||
import {
|
||||
joinPaths,
|
||||
parseFilePath,
|
||||
parseFolderPath,
|
||||
splitPath,
|
||||
} from '../src/source/path';
|
||||
import { describe, expect, test } from 'vitest';
|
||||
|
||||
describe('Path utilities', () => {
|
||||
test('parse file path', () => {
|
||||
expect(parseFilePath('test.mdx')).toEqual({
|
||||
dirname: '',
|
||||
name: 'test',
|
||||
flattenedPath: 'test',
|
||||
locale: undefined,
|
||||
path: 'test.mdx',
|
||||
});
|
||||
|
||||
expect(parseFilePath('nested/test.mdx')).toEqual({
|
||||
dirname: 'nested',
|
||||
name: 'test',
|
||||
flattenedPath: 'nested/test',
|
||||
locale: undefined,
|
||||
path: 'nested/test.mdx',
|
||||
});
|
||||
|
||||
expect(parseFilePath('nested/test.cn.mdx')).toEqual({
|
||||
dirname: 'nested',
|
||||
name: 'test',
|
||||
flattenedPath: 'nested/test.cn',
|
||||
locale: 'cn',
|
||||
path: 'nested/test.cn.mdx',
|
||||
});
|
||||
|
||||
expect(parseFilePath('nested\\test.cn.mdx')).toEqual(
|
||||
parseFilePath('nested/test.cn.mdx'),
|
||||
);
|
||||
});
|
||||
|
||||
test('parse folder path', () => {
|
||||
expect(parseFolderPath('nested')).toEqual({
|
||||
dirname: '',
|
||||
name: 'nested',
|
||||
flattenedPath: 'nested',
|
||||
locale: undefined,
|
||||
path: 'nested',
|
||||
});
|
||||
|
||||
expect(parseFolderPath('nested/nested')).toEqual({
|
||||
dirname: 'nested',
|
||||
name: 'nested',
|
||||
flattenedPath: 'nested/nested',
|
||||
locale: undefined,
|
||||
path: 'nested/nested',
|
||||
});
|
||||
|
||||
expect(parseFolderPath('nested\\nested')).toEqual(
|
||||
parseFolderPath('nested/nested'),
|
||||
);
|
||||
});
|
||||
|
||||
test('join paths', () => {
|
||||
expect(joinPaths(['a', 'b', 'c'])).toBe('a/b/c');
|
||||
expect(joinPaths(['/a'])).toBe('a');
|
||||
expect(joinPaths(['a/', '/b'])).toBe('a/b');
|
||||
expect(joinPaths(['a/', 'b/c'])).toBe('a/b/c');
|
||||
|
||||
expect(joinPaths(['a', 'b'], 'leading')).toBe('/a/b');
|
||||
expect(joinPaths(['a', 'b'], 'trailing')).toBe('a/b/');
|
||||
});
|
||||
|
||||
test('split paths', () => {
|
||||
expect(splitPath('a/b/c')).toEqual(['a', 'b', 'c']);
|
||||
expect(splitPath('a//c')).toEqual(['a', 'c']);
|
||||
expect(splitPath('/a/c')).toEqual(['a', 'c']);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,104 @@
|
||||
import { createI18nSearchAPI, createSearchAPI } from '@/search/server';
|
||||
import { expect, test } from 'vitest';
|
||||
import { structure } from '@/mdx-plugins';
|
||||
|
||||
test('Search API', () => {
|
||||
const api = createSearchAPI('simple', {
|
||||
indexes: [
|
||||
{
|
||||
title: 'Hello World',
|
||||
content: 'Hello World',
|
||||
url: '/hello-world',
|
||||
},
|
||||
{
|
||||
title: 'Nothing',
|
||||
content: 'Nothing',
|
||||
url: '/nothing',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(api.search('Hello')).toHaveLength(1);
|
||||
expect(api.search('pterodactyl')).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('Search API Advanced', () => {
|
||||
const api = createSearchAPI('advanced', {
|
||||
tag: true,
|
||||
indexes: [
|
||||
{
|
||||
id: '1',
|
||||
title: 'Index',
|
||||
structuredData: structure(
|
||||
`## Hello World
|
||||
|
||||
something`,
|
||||
),
|
||||
url: '/',
|
||||
tag: 'my-tag',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
title: 'Page',
|
||||
structuredData: structure(
|
||||
`## My Page
|
||||
|
||||
something`,
|
||||
),
|
||||
url: '/page',
|
||||
tag: '',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(api.search('Page')).toHaveLength(2);
|
||||
expect(api.search('something')).toHaveLength(4);
|
||||
expect(api.search('', { tag: 'my-tag' })).toHaveLength(3);
|
||||
|
||||
expect(api.search('Hello')).toMatchInlineSnapshot(`
|
||||
[
|
||||
{
|
||||
"content": "Index",
|
||||
"id": "1",
|
||||
"type": "page",
|
||||
"url": "/",
|
||||
},
|
||||
{
|
||||
"content": "Hello World",
|
||||
"id": "10",
|
||||
"type": "heading",
|
||||
"url": "/#hello-world",
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test('Search API I18n', () => {
|
||||
const api = createI18nSearchAPI('simple', {
|
||||
indexes: [
|
||||
[
|
||||
'cn',
|
||||
[
|
||||
{
|
||||
title: 'Hello World Chinese',
|
||||
content: 'Hello World',
|
||||
url: '/hello-world',
|
||||
},
|
||||
],
|
||||
],
|
||||
[
|
||||
'en',
|
||||
[
|
||||
{
|
||||
title: 'Hello World English',
|
||||
content: 'Hello World',
|
||||
url: '/hello-world',
|
||||
},
|
||||
],
|
||||
],
|
||||
],
|
||||
});
|
||||
|
||||
expect(api.search('English', { locale: 'en' })).toHaveLength(1);
|
||||
expect(api.search('Hello World Chinese', { locale: 'cn' })).toHaveLength(1);
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
import { expect, test } from 'vitest';
|
||||
import { loader } from '../src/source';
|
||||
import { loader } from '@/source/loader';
|
||||
|
||||
test('Simple', () => {
|
||||
const result = loader({
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { loader } from '../src/source';
|
||||
import { Storage } from '../src/source/file-system';
|
||||
import { parseFilePath, parseFolderPath } from '../src/source/path';
|
||||
import { Storage } from '@/source/file-system';
|
||||
import { parseFilePath, parseFolderPath } from '@/source/path';
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import { load } from '@/source/load';
|
||||
|
||||
describe('Virtual Storage', () => {
|
||||
const storage = new Storage();
|
||||
@@ -35,101 +35,60 @@ describe('Virtual Storage', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Building File Graph', () => {
|
||||
test('Simple', () => {
|
||||
loader({
|
||||
source: {
|
||||
files: [
|
||||
{
|
||||
type: 'page',
|
||||
path: 'test.mdx',
|
||||
data: {
|
||||
title: 'Hello',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'meta',
|
||||
path: 'meta.json',
|
||||
data: {
|
||||
pages: ['test'],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
transformers: [
|
||||
({ storage }) => {
|
||||
expect(storage.root().children).toEqual([
|
||||
expect.objectContaining({
|
||||
type: 'page',
|
||||
file: parseFilePath('test.mdx'),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
type: 'meta',
|
||||
file: parseFilePath('meta.json'),
|
||||
}),
|
||||
]);
|
||||
test('Building File Graph', () => {
|
||||
const result = load({
|
||||
rootDir: '',
|
||||
getSlugs: () => [''],
|
||||
files: [
|
||||
{
|
||||
type: 'page',
|
||||
path: 'test.mdx',
|
||||
data: {
|
||||
title: 'Hello',
|
||||
},
|
||||
],
|
||||
});
|
||||
},
|
||||
{
|
||||
type: 'page',
|
||||
path: '/nested/test.mdx',
|
||||
data: {
|
||||
title: 'Nested Page',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'page',
|
||||
path: '/nested/nested/test.mdx',
|
||||
data: {
|
||||
title: 'Nested Nested Page',
|
||||
},
|
||||
},
|
||||
],
|
||||
getUrl: () => '',
|
||||
});
|
||||
|
||||
test('Nested Directories', async () => {
|
||||
loader({
|
||||
source: {
|
||||
files: [
|
||||
{
|
||||
type: 'page',
|
||||
path: 'test.mdx',
|
||||
data: {
|
||||
title: 'Hello',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'page',
|
||||
path: '/nested/test.mdx',
|
||||
data: {
|
||||
title: 'Nested Page',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'page',
|
||||
path: '/nested/nested/test.mdx',
|
||||
data: {
|
||||
title: 'Nested Nested Page',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
transformers: [
|
||||
({ storage }) => {
|
||||
expect(storage.root().children).toEqual([
|
||||
expect(result.storage.root().children).toEqual([
|
||||
expect.objectContaining({
|
||||
type: 'page',
|
||||
file: parseFilePath('test.mdx'),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
type: 'folder',
|
||||
file: parseFolderPath('nested'),
|
||||
children: [
|
||||
expect.objectContaining({
|
||||
type: 'page',
|
||||
file: parseFilePath('nested/test.mdx'),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
type: 'folder',
|
||||
file: parseFolderPath('nested/nested'),
|
||||
children: [
|
||||
expect.objectContaining({
|
||||
type: 'page',
|
||||
file: parseFilePath('test.mdx'),
|
||||
file: parseFilePath('nested/nested/test.mdx'),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
type: 'folder',
|
||||
file: parseFolderPath('nested'),
|
||||
children: [
|
||||
expect.objectContaining({
|
||||
type: 'page',
|
||||
file: parseFilePath('nested/test.mdx'),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
type: 'folder',
|
||||
file: parseFolderPath('nested/nested'),
|
||||
children: [
|
||||
expect.objectContaining({
|
||||
type: 'page',
|
||||
file: parseFilePath('nested/nested/test.mdx'),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
]);
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
});
|
||||
});
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
Generated
+31
-24
@@ -1,9 +1,5 @@
|
||||
lockfileVersion: '6.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
overrides:
|
||||
'@opentelemetry/api': ^1.4.1
|
||||
remark-rehype: ^11.0.0
|
||||
@@ -330,6 +326,9 @@ importers:
|
||||
'@algolia/client-search':
|
||||
specifier: ^4.22.1
|
||||
version: 4.22.1
|
||||
'@mdx-js/mdx':
|
||||
specifier: ^3.0.1
|
||||
version: 3.0.1
|
||||
'@types/flexsearch':
|
||||
specifier: 0.7.6
|
||||
version: 0.7.6
|
||||
@@ -1346,6 +1345,10 @@ packages:
|
||||
peerDependencies:
|
||||
'@effect-ts/otel-node': '*'
|
||||
peerDependenciesMeta:
|
||||
'@effect-ts/core':
|
||||
optional: true
|
||||
'@effect-ts/otel':
|
||||
optional: true
|
||||
'@effect-ts/otel-node':
|
||||
optional: true
|
||||
dependencies:
|
||||
@@ -1383,7 +1386,7 @@ packages:
|
||||
resolution: {integrity: sha512-47gAg0O2pW5Jlo86jfzjdkwL5a7Bzb+Kj5WTmdu4CxYRfWn9ytKjuuYIfsNDW8neuhdKzn+P5wCddgEh0glYyQ==}
|
||||
peerDependencies:
|
||||
'@effect-ts/core': ^0.60.2
|
||||
'@opentelemetry/api': ^1.4.1
|
||||
'@opentelemetry/api': ^1.4.0
|
||||
'@opentelemetry/core': ^1.13.0
|
||||
'@opentelemetry/exporter-trace-otlp-grpc': ^0.39.0
|
||||
'@opentelemetry/sdk-trace-base': ^1.13.0
|
||||
@@ -1399,7 +1402,7 @@ packages:
|
||||
resolution: {integrity: sha512-a2sF0ylmn8xOJs8fNeT/spJ1gUcsksAJCALxo9WOfuTCMtTwMVtVhCKEPEeQoL7wFqU+JgPkVdP91+FJ/Rkeow==}
|
||||
peerDependencies:
|
||||
'@effect-ts/core': ^0.60.2
|
||||
'@opentelemetry/api': ^1.4.1
|
||||
'@opentelemetry/api': ^1.4.0
|
||||
'@opentelemetry/core': ^1.13.0
|
||||
'@opentelemetry/sdk-trace-base': ^1.13.0
|
||||
'@opentelemetry/sdk-trace-node': ^1.13.0
|
||||
@@ -1415,7 +1418,7 @@ packages:
|
||||
resolution: {integrity: sha512-AmZJHl7t0+Peh7Yb2+hqn6r9+rd9/UfeA4AMV9h0YGTdOyouyFfD3wzWlxnAUzAQ4Lrod4kC7Noruret4EpqpA==}
|
||||
peerDependencies:
|
||||
'@effect-ts/core': ^0.60.2
|
||||
'@opentelemetry/api': ^1.4.1
|
||||
'@opentelemetry/api': ^1.4.0
|
||||
'@opentelemetry/core': ^1.13.0
|
||||
'@opentelemetry/sdk-trace-base': ^1.13.0
|
||||
dependencies:
|
||||
@@ -2234,7 +2237,7 @@ packages:
|
||||
resolution: {integrity: sha512-bDIRCgpKniSyhORU0fTL9ISW6ucU9nruKyXKwYrEBep/2f3uLz8LFyF51ZUK9QxIwBHw6WJudK/2UqttWzER4w==}
|
||||
engines: {node: '>=14'}
|
||||
peerDependencies:
|
||||
'@opentelemetry/api': ^1.4.1
|
||||
'@opentelemetry/api': '>=1.0.0 <1.7.0'
|
||||
dependencies:
|
||||
'@opentelemetry/api': 1.6.0
|
||||
|
||||
@@ -2242,7 +2245,7 @@ packages:
|
||||
resolution: {integrity: sha512-2dBX3Sj99H96uwJKvc2w9NOiNgbvAO6mOFJFramNkKfS9O4Um+VWgpnlAazoYjT6kUJ1MP70KQ5ngD4ed+4NUw==}
|
||||
engines: {node: '>=14'}
|
||||
peerDependencies:
|
||||
'@opentelemetry/api': ^1.4.1
|
||||
'@opentelemetry/api': '>=1.0.0 <1.5.0'
|
||||
dependencies:
|
||||
'@opentelemetry/api': 1.6.0
|
||||
'@opentelemetry/semantic-conventions': 1.13.0
|
||||
@@ -2251,7 +2254,7 @@ packages:
|
||||
resolution: {integrity: sha512-tfnl3h+UefCgx1aeN2xtrmr6BmdWGKXypk0pflQR0urFS40aE88trnkOMc2HTJZbMrqEEl4HsaBeFhwLVXsrJg==}
|
||||
engines: {node: '>=14'}
|
||||
peerDependencies:
|
||||
'@opentelemetry/api': ^1.4.1
|
||||
'@opentelemetry/api': '>=1.0.0 <1.7.0'
|
||||
dependencies:
|
||||
'@opentelemetry/api': 1.6.0
|
||||
'@opentelemetry/semantic-conventions': 1.17.0
|
||||
@@ -2260,7 +2263,7 @@ packages:
|
||||
resolution: {integrity: sha512-l5RhLKx6U+yuLhMrtgavTDthX50E1mZM3/SSySC7OPZiArFHV/b/9x9jxAzrOgIQUDxyj4N0V9aLKSA2t7Qzxg==}
|
||||
engines: {node: '>=14'}
|
||||
peerDependencies:
|
||||
'@opentelemetry/api': ^1.4.1
|
||||
'@opentelemetry/api': ^1.0.0
|
||||
dependencies:
|
||||
'@grpc/grpc-js': 1.9.3
|
||||
'@opentelemetry/api': 1.6.0
|
||||
@@ -2274,7 +2277,7 @@ packages:
|
||||
resolution: {integrity: sha512-Pv5X8fbi6jD/RJBePyn7MnCSuE6MbPB6dl+7YYBWJ5RcMGYMwvLXjd4h2jWsPV2TSUg38H/RoSP0aXvQ06Y7iw==}
|
||||
engines: {node: '>=14'}
|
||||
peerDependencies:
|
||||
'@opentelemetry/api': ^1.4.1
|
||||
'@opentelemetry/api': ^1.0.0
|
||||
dependencies:
|
||||
'@opentelemetry/api': 1.6.0
|
||||
'@opentelemetry/core': 1.13.0(@opentelemetry/[email protected])
|
||||
@@ -2283,7 +2286,7 @@ packages:
|
||||
resolution: {integrity: sha512-u3ErFRQqQFKjjIMuwLWxz/tLPYInfmiAmSy//fGSCzCh2ZdJgqQjMOAxBgqFtCF2xFL+OmMhyuC2ThMzceGRWA==}
|
||||
engines: {node: '>=14'}
|
||||
peerDependencies:
|
||||
'@opentelemetry/api': ^1.4.1
|
||||
'@opentelemetry/api': ^1.0.0
|
||||
dependencies:
|
||||
'@grpc/grpc-js': 1.9.3
|
||||
'@opentelemetry/api': 1.6.0
|
||||
@@ -2295,7 +2298,7 @@ packages:
|
||||
resolution: {integrity: sha512-0hgVnXXz5efI382B/24NxD4b6Zxlh7nxCdJkxkdmQMbn0yRiwoq/ZT+QG8eUL6JNzsBAV1WJlF5aJNsL8skHvw==}
|
||||
engines: {node: '>=14'}
|
||||
peerDependencies:
|
||||
'@opentelemetry/api': ^1.4.1
|
||||
'@opentelemetry/api': '>=1.3.0 <1.5.0'
|
||||
dependencies:
|
||||
'@opentelemetry/api': 1.6.0
|
||||
'@opentelemetry/api-logs': 0.39.1
|
||||
@@ -2309,7 +2312,7 @@ packages:
|
||||
resolution: {integrity: sha512-oklstXImtaly4vDaL+rGtX41YXZR50jp5a7CSEPMcStp1B7ozdZ5G2I5wftrDvOlOcLt/TIkGWDCr/OkVN7kWg==}
|
||||
engines: {node: '>=14'}
|
||||
peerDependencies:
|
||||
'@opentelemetry/api': ^1.4.1
|
||||
'@opentelemetry/api': '>=1.0.0 <1.7.0'
|
||||
dependencies:
|
||||
'@opentelemetry/api': 1.6.0
|
||||
'@opentelemetry/core': 1.17.0(@opentelemetry/[email protected])
|
||||
@@ -2318,7 +2321,7 @@ packages:
|
||||
resolution: {integrity: sha512-iZzu8K0QkZZ16JH9yox6hZk7/Rxc4SPeGU37pvlB9DtzfNxAEX1FMK9zvowv3ve7r2uzZNpa7JGVUwpy5ewdHQ==}
|
||||
engines: {node: '>=14'}
|
||||
peerDependencies:
|
||||
'@opentelemetry/api': ^1.4.1
|
||||
'@opentelemetry/api': '>=1.0.0 <1.7.0'
|
||||
dependencies:
|
||||
'@opentelemetry/api': 1.6.0
|
||||
'@opentelemetry/core': 1.17.0(@opentelemetry/[email protected])
|
||||
@@ -2327,7 +2330,7 @@ packages:
|
||||
resolution: {integrity: sha512-euqjOkiN6xhjE//0vQYGvbStxoD/WWQRhDiO0OTLlnLBO9Yw2Gd/VoSx2H+svsebjzYk5OxLuREBmcdw6rbUNg==}
|
||||
engines: {node: '>=14'}
|
||||
peerDependencies:
|
||||
'@opentelemetry/api': ^1.4.1
|
||||
'@opentelemetry/api': '>=1.0.0 <1.5.0'
|
||||
dependencies:
|
||||
'@opentelemetry/api': 1.6.0
|
||||
'@opentelemetry/core': 1.13.0(@opentelemetry/[email protected])
|
||||
@@ -2337,7 +2340,7 @@ packages:
|
||||
resolution: {integrity: sha512-+u0ciVnj8lhuL/qGRBPeVYvk7fL+H/vOddfvmOeJaA1KC+5/3UED1c9KoZQlRsNT5Kw1FaK8LkY2NVLYfOVZQw==}
|
||||
engines: {node: '>=14'}
|
||||
peerDependencies:
|
||||
'@opentelemetry/api': ^1.4.1
|
||||
'@opentelemetry/api': '>=1.0.0 <1.7.0'
|
||||
dependencies:
|
||||
'@opentelemetry/api': 1.6.0
|
||||
'@opentelemetry/core': 1.17.0(@opentelemetry/[email protected])
|
||||
@@ -2347,7 +2350,7 @@ packages:
|
||||
resolution: {integrity: sha512-/gmgKfZ1ZVFporKuwsewqIyvaUIGpv76JZ7lBpHQQPb37IMpaXO6pdqFI4ebHAWfNIm3akMyhmdtzivcgF3lgw==}
|
||||
engines: {node: '>=14'}
|
||||
peerDependencies:
|
||||
'@opentelemetry/api': ^1.4.1
|
||||
'@opentelemetry/api': '>=1.4.0 <1.5.0'
|
||||
'@opentelemetry/api-logs': '>=0.38.0'
|
||||
dependencies:
|
||||
'@opentelemetry/api': 1.6.0
|
||||
@@ -2359,7 +2362,7 @@ packages:
|
||||
resolution: {integrity: sha512-MOjZX6AnSOqLliCcZUrb+DQKjAWXBiGeICGbHAGe5w0BB18PJIeIo995lO5JSaFfHpmUMgJButTPfJJD27W3Vg==}
|
||||
engines: {node: '>=14'}
|
||||
peerDependencies:
|
||||
'@opentelemetry/api': ^1.4.1
|
||||
'@opentelemetry/api': '>=1.3.0 <1.5.0'
|
||||
dependencies:
|
||||
'@opentelemetry/api': 1.6.0
|
||||
'@opentelemetry/core': 1.13.0(@opentelemetry/[email protected])
|
||||
@@ -2370,7 +2373,7 @@ packages:
|
||||
resolution: {integrity: sha512-moTiQtc0uPR1hQLt6gLDJH9IIkeBhgRb71OKjNHZPE1VF45fHtD6nBDi5J/DkTHTwYP5X3kBJLa3xN7ub6J4eg==}
|
||||
engines: {node: '>=14'}
|
||||
peerDependencies:
|
||||
'@opentelemetry/api': ^1.4.1
|
||||
'@opentelemetry/api': '>=1.0.0 <1.5.0'
|
||||
dependencies:
|
||||
'@opentelemetry/api': 1.6.0
|
||||
'@opentelemetry/core': 1.13.0(@opentelemetry/[email protected])
|
||||
@@ -2381,7 +2384,7 @@ packages:
|
||||
resolution: {integrity: sha512-2T5HA1/1iE36Q9eg6D4zYlC4Y4GcycI1J6NsHPKZY9oWfAxWsoYnRlkPfUqyY5XVtocCo/xHpnJvGNHwzT70oQ==}
|
||||
engines: {node: '>=14'}
|
||||
peerDependencies:
|
||||
'@opentelemetry/api': ^1.4.1
|
||||
'@opentelemetry/api': '>=1.0.0 <1.7.0'
|
||||
dependencies:
|
||||
'@opentelemetry/api': 1.6.0
|
||||
'@opentelemetry/core': 1.17.0(@opentelemetry/[email protected])
|
||||
@@ -2392,7 +2395,7 @@ packages:
|
||||
resolution: {integrity: sha512-Twlaje+t16b5j62CfcaKU869rP9oyBG/sVQWBI5+kDaWuP/YIFnF4LbovaEahK9GwAnW8vPIn6iYLAl/jZBidA==}
|
||||
engines: {node: '>=14'}
|
||||
peerDependencies:
|
||||
'@opentelemetry/api': ^1.4.1
|
||||
'@opentelemetry/api': '>=1.0.0 <1.7.0'
|
||||
dependencies:
|
||||
'@opentelemetry/api': 1.6.0
|
||||
'@opentelemetry/context-async-hooks': 1.17.0(@opentelemetry/[email protected])
|
||||
@@ -8295,7 +8298,7 @@ packages:
|
||||
engines: {node: '>=18.17.0'}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
'@opentelemetry/api': ^1.4.1
|
||||
'@opentelemetry/api': ^1.1.0
|
||||
react: ^18.2.0
|
||||
react-dom: ^18.2.0
|
||||
sass: ^1.3.0
|
||||
@@ -11102,3 +11105,7 @@ packages:
|
||||
|
||||
/[email protected]:
|
||||
resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
Reference in New Issue
Block a user