mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-11-07 06:19:43 -10:00
Hide internal docs from docs site (#4357)
This commit is contained in:
1 parent
707f1bdb5f
commit
59c283670e
9 files changed
+195
-8
No files matched your search
+4
-2
@@ -17,13 +17,15 @@ npm install
|
|||||||
|
|
||||||
### Setup Content
|
### Setup Content
|
||||||
|
|
||||||
Link the documentation content from the parent `docs` directory:
|
Prepare the public documentation content from the parent `docs` directory:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm run link
|
npm run link
|
||||||
```
|
```
|
||||||
|
|
||||||
This creates a symbolic link from `../docs` to `content` in the project.
|
This creates a `content` directory with copies of the public docs sections.
|
||||||
|
Internal planning, design, and E2E notes remain outside the docs site content
|
||||||
|
tree.
|
||||||
|
|
||||||
### Development
|
### Development
|
||||||
|
|
||||||
|
|||||||
@@ -7,10 +7,10 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"link": "ln -s ../docs content",
|
"link": "node scripts/link-public-docs.mjs",
|
||||||
"clean": "rm -rf .next",
|
"clean": "rm -rf .next",
|
||||||
"dev": "npm run clean && next --turbopack",
|
"dev": "npm run clean && next --turbopack",
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"test": "vitest run --config vitest.config.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"next": "^16.0.8",
|
"next": "^16.0.8",
|
||||||
@@ -18,5 +18,8 @@
|
|||||||
"nextra-theme-docs": "^4.6.1",
|
"nextra-theme-docs": "^4.6.1",
|
||||||
"react": "^19.2.1",
|
"react": "^19.2.1",
|
||||||
"react-dom": "^19.2.1"
|
"react-dom": "^19.2.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"vitest": "^3.2.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { cp, mkdir, rm, symlink } from 'node:fs/promises';
|
||||||
|
import { join } from 'node:path';
|
||||||
|
|
||||||
|
import { PUBLIC_DOC_ROOTS } from '../src/app/public-docs.js';
|
||||||
|
|
||||||
|
const contentDir = 'content';
|
||||||
|
|
||||||
|
async function linkPublicDocs() {
|
||||||
|
try {
|
||||||
|
await rm(contentDir, { force: true, recursive: true });
|
||||||
|
await mkdir(contentDir);
|
||||||
|
await cp('../docs/index.md', join(contentDir, 'index.md'));
|
||||||
|
await cp('../docs/_meta.ts', join(contentDir, '_meta.ts'));
|
||||||
|
|
||||||
|
for (const root of PUBLIC_DOC_ROOTS) {
|
||||||
|
await symlink(join('..', '..', 'docs', root), join(contentDir, root));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
const message = error instanceof Error ? error.message : String(error);
|
||||||
|
throw new Error(
|
||||||
|
`Failed to link public docs into ${contentDir}: ${message}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await linkPublicDocs();
|
||||||
@@ -1,10 +1,23 @@
|
|||||||
import { generateStaticParamsFor, importPage } from 'nextra/pages';
|
import { generateStaticParamsFor, importPage } from 'nextra/pages';
|
||||||
|
import { notFound } from 'next/navigation';
|
||||||
import { useMDXComponents as getMDXComponents } from '../../../mdx-components';
|
import { useMDXComponents as getMDXComponents } from '../../../mdx-components';
|
||||||
|
import { filterPublicStaticParams, isPublicDocsPath } from '../public-docs';
|
||||||
|
|
||||||
export const generateStaticParams = generateStaticParamsFor('mdxPath');
|
const generateAllStaticParams = generateStaticParamsFor('mdxPath');
|
||||||
|
|
||||||
|
export const dynamicParams = false;
|
||||||
|
|
||||||
|
export async function generateStaticParams(...args) {
|
||||||
|
const staticParams = await generateAllStaticParams(...args);
|
||||||
|
return filterPublicStaticParams(staticParams);
|
||||||
|
}
|
||||||
|
|
||||||
export async function generateMetadata(props) {
|
export async function generateMetadata(props) {
|
||||||
const params = await props.params;
|
const params = await props.params;
|
||||||
|
if (!isPublicDocsPath(params.mdxPath)) {
|
||||||
|
notFound();
|
||||||
|
}
|
||||||
|
|
||||||
const { metadata } = await importPage(params.mdxPath);
|
const { metadata } = await importPage(params.mdxPath);
|
||||||
return metadata;
|
return metadata;
|
||||||
}
|
}
|
||||||
@@ -13,6 +26,10 @@ const Wrapper = getMDXComponents().wrapper;
|
|||||||
|
|
||||||
export default async function Page(props) {
|
export default async function Page(props) {
|
||||||
const params = await props.params;
|
const params = await props.params;
|
||||||
|
if (!isPublicDocsPath(params.mdxPath)) {
|
||||||
|
notFound();
|
||||||
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
default: MDXContent,
|
default: MDXContent,
|
||||||
toc,
|
toc,
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||||
|
|
||||||
|
const mocks = vi.hoisted(() => {
|
||||||
|
const generateAllStaticParams = vi.fn();
|
||||||
|
|
||||||
|
return {
|
||||||
|
generateAllStaticParams,
|
||||||
|
generateStaticParamsFor: vi.fn(() => generateAllStaticParams),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
vi.mock('nextra/pages', () => ({
|
||||||
|
generateStaticParamsFor: mocks.generateStaticParamsFor,
|
||||||
|
importPage: vi.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock('next/navigation', () => ({
|
||||||
|
notFound: vi.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock('../../../mdx-components', () => ({
|
||||||
|
useMDXComponents: () => ({
|
||||||
|
wrapper: ({ children }) => children,
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('generateStaticParams', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('filters internal docs from Nextra static params', async () => {
|
||||||
|
mocks.generateAllStaticParams.mockResolvedValue([
|
||||||
|
{ mdxPath: [] },
|
||||||
|
{ mdxPath: ['users', 'foo'] },
|
||||||
|
{ mdxPath: ['en', 'users'] },
|
||||||
|
{ mdxPath: ['design', 'bar'] },
|
||||||
|
{ mdxPath: ['plans'] },
|
||||||
|
]);
|
||||||
|
|
||||||
|
const { generateStaticParams } = await import('./page.jsx');
|
||||||
|
|
||||||
|
await expect(generateStaticParams()).resolves.toEqual([
|
||||||
|
{ mdxPath: [] },
|
||||||
|
{ mdxPath: ['users', 'foo'] },
|
||||||
|
{ mdxPath: ['en', 'users'] },
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails closed if Nextra changes the static params shape', async () => {
|
||||||
|
mocks.generateAllStaticParams.mockResolvedValue([{ slug: ['users'] }]);
|
||||||
|
|
||||||
|
const { generateStaticParams } = await import('./page.jsx');
|
||||||
|
|
||||||
|
await expect(generateStaticParams()).rejects.toThrow(
|
||||||
|
'Expected generateStaticParamsFor("mdxPath") to return objects with an mdxPath array.',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
const LOCALE_SEGMENTS = new Set(['en', 'zh', 'de', 'fr', 'ja', 'ru', 'pt-BR']);
|
||||||
|
|
||||||
|
// Keep this in sync with the public top-level page entries in docs/_meta.ts.
|
||||||
|
// docs-site/scripts/link-public-docs.mjs consumes the same allowlist.
|
||||||
|
export const PUBLIC_DOC_ROOTS = ['users', 'developers'];
|
||||||
|
|
||||||
|
const PUBLIC_DOC_ROOT_SET = new Set(PUBLIC_DOC_ROOTS);
|
||||||
|
|
||||||
|
function publicRootFromSegments(segments = []) {
|
||||||
|
if (segments.length === 0 || (segments.length === 1 && segments[0] === '')) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rootIndex = LOCALE_SEGMENTS.has(segments[0]) ? 1 : 0;
|
||||||
|
return segments[rootIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isPublicDocsPath(mdxPath = []) {
|
||||||
|
const root = publicRootFromSegments(mdxPath);
|
||||||
|
return root === undefined || PUBLIC_DOC_ROOT_SET.has(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function filterPublicStaticParams(staticParams = []) {
|
||||||
|
return staticParams.filter((staticParam) => {
|
||||||
|
if (!Array.isArray(staticParam?.mdxPath)) {
|
||||||
|
throw new TypeError(
|
||||||
|
'Expected generateStaticParamsFor("mdxPath") to return objects with an mdxPath array.',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return isPublicDocsPath(staticParam.mdxPath);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
|
import { filterPublicStaticParams, isPublicDocsPath } from './public-docs.js';
|
||||||
|
|
||||||
|
describe('isPublicDocsPath', () => {
|
||||||
|
it.each([
|
||||||
|
[[], true],
|
||||||
|
[[''], true],
|
||||||
|
[['users', 'foo'], true],
|
||||||
|
[['design', 'bar'], false],
|
||||||
|
[['en', 'users'], true],
|
||||||
|
[['plans'], false],
|
||||||
|
[['en'], true],
|
||||||
|
])('returns %s for %j', (mdxPath, expected) => {
|
||||||
|
expect(isPublicDocsPath(mdxPath)).toBe(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('filterPublicStaticParams', () => {
|
||||||
|
it('keeps public paths and rejects internal docs paths', () => {
|
||||||
|
expect(
|
||||||
|
filterPublicStaticParams([
|
||||||
|
{ mdxPath: [] },
|
||||||
|
{ mdxPath: [''] },
|
||||||
|
{ mdxPath: ['users', 'foo'] },
|
||||||
|
{ mdxPath: ['en', 'developers'] },
|
||||||
|
{ mdxPath: ['design', 'bar'] },
|
||||||
|
{ mdxPath: ['plans'] },
|
||||||
|
]),
|
||||||
|
).toEqual([
|
||||||
|
{ mdxPath: [] },
|
||||||
|
{ mdxPath: [''] },
|
||||||
|
{ mdxPath: ['users', 'foo'] },
|
||||||
|
{ mdxPath: ['en', 'developers'] },
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails closed if Nextra changes the static params shape', () => {
|
||||||
|
expect(() => filterPublicStaticParams([{ slug: ['users'] }])).toThrow(
|
||||||
|
'Expected generateStaticParamsFor("mdxPath") to return objects with an mdxPath array.',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { defineConfig } from 'vitest/config';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
test: {
|
||||||
|
include: ['src/**/*.test.{js,jsx}'],
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -1,9 +1,6 @@
|
|||||||
export default {
|
export default {
|
||||||
settings: 'Settings',
|
settings: 'Settings',
|
||||||
auth: 'Authentication',
|
auth: 'Authentication',
|
||||||
memory: {
|
|
||||||
display: 'hidden',
|
|
||||||
},
|
|
||||||
'qwen-ignore': 'Ignoring Files',
|
'qwen-ignore': 'Ignoring Files',
|
||||||
'trusted-folders': 'Trusted Folders',
|
'trusted-folders': 'Trusted Folders',
|
||||||
themes: 'Themes',
|
themes: 'Themes',
|
||||||
|
|||||||
Reference in new issue
Block a user