Skip to content

Commit

Permalink
fix: sidebar label & toc hmr not work (#640)
Browse files Browse the repository at this point in the history
  • Loading branch information
sanyuan0704 authored Feb 22, 2024
1 parent 52e4b99 commit 24c03fc
Show file tree
Hide file tree
Showing 18 changed files with 181 additions and 75 deletions.
4 changes: 2 additions & 2 deletions .changeset/khaki-olives-confess.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
"@rspress/theme-default": patch
'@rspress/theme-default': patch
---

Updated codeblock - fix copyBtn Icons && updated wordWrapBtn animations for equality between both buttons
fix: copyBtn Icons && updated wordWrapBtn animations for equality between both buttons
8 changes: 8 additions & 0 deletions .changeset/shiny-forks-clean.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@rspress/plugin-auto-nav-sidebar": patch
"@rspress/theme-default": patch
"@rspress/shared": patch
"@rspress/core": patch
---

fix: update sidebar after h1 change
8 changes: 8 additions & 0 deletions .changeset/slimy-dogs-happen.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'@rspress/plugin-auto-nav-sidebar': patch
'@rspress/theme-default': patch
'@rspress/shared': patch
'@rspress/core': minor
---

feat: trigger auto page reload after page meta changed
8 changes: 8 additions & 0 deletions .changeset/sour-meals-enjoy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'@rspress/plugin-auto-nav-sidebar': patch
'@rspress/theme-default': patch
'@rspress/shared': patch
'@rspress/core': patch
---

fix: doc fragments' headers disappeared in development
6 changes: 3 additions & 3 deletions .changeset/spotty-needles-clap.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
"@rspress/theme-default": patch
"@rspress/docs": patch
'@rspress/theme-default': patch
'@rspress/docs': patch
---

fix: ui switch query
fix(theme-default): ui switch query
4 changes: 2 additions & 2 deletions .changeset/wise-rivers-deny.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
"@rspress/theme-default": patch
'@rspress/theme-default': patch
---

feat: set search index initing min time
feat(theme-default): set search index initing min time
2 changes: 1 addition & 1 deletion e2e/tests/modern-js.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ test.describe('plugin test', async () => {
});

test('Basic', async ({ page }) => {
await page.goto(`http://localhost:${appPort}/en/general/button`, {
await page.goto(`http://localhost:${appPort}/general/button`, {
waitUntil: 'networkidle',
});
const h1 = await page.getByRole('heading', {
Expand Down
78 changes: 63 additions & 15 deletions packages/core/src/node/mdx/loader.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
import path from 'path';
import { pathToFileURL } from 'url';
import type { Rspack } from '@rsbuild/core';
import { createProcessor } from '@mdx-js/mdx';
import type { Header, UserConfig } from '@rspress/shared';
import { isProduction, type Header, type UserConfig } from '@rspress/shared';
import { logger } from '@rspress/shared/logger';
import { loadFrontMatter } from '@rspress/shared/node-utils';
import fs from 'fs-extra';
import type { RouteService } from '../route/RouteService';
import { normalizePath, escapeMarkdownHeadingIds } from '../utils';
import {
normalizePath,
escapeMarkdownHeadingIds,
flattenMdxContent,
} from '../utils';
import { PluginDriver } from '../PluginDriver';
import { TEMP_DIR } from '../constants';
import { RuntimeModuleID } from '../runtimeModule';
import { createMDXOptions } from './options';
import { TocItem } from './remarkPlugins/toc';
import { checkLinks } from './remarkPlugins/checkDeadLink';
Expand All @@ -25,15 +33,57 @@ export interface PageMeta {
frontmatter?: Record<string, any>;
}

export async function triggerReload() {
const siteDataModulePath = path.join(
TEMP_DIR,
'runtime',
`${RuntimeModuleID.SiteData}.mjs`,
);
const { default: siteData } = await import(
pathToFileURL(siteDataModulePath).href
);
await fs.writeFile(
siteDataModulePath,
`export default ${JSON.stringify({
...siteData,
timestamp: Date.now().toString(),
})}`,
);
}

export function createCheckPageMetaUpdateFn() {
const pageMetaMap = new Map<string, string>();
return (modulePath: string, pageMeta: PageMeta) => {
const prevMeta = pageMetaMap.get(modulePath);
const deserializedMeta = JSON.stringify(pageMeta);
pageMetaMap.set(modulePath, deserializedMeta);

if (!prevMeta) {
return;
}

if (prevMeta !== deserializedMeta) {
setTimeout(async () => {
logger.info(
`⭐️ Page metadata changed, rspress will trigger page reload...`,
);
await triggerReload();
});
}
};
}

const checkPageMetaUpdate = createCheckPageMetaUpdateFn();

export default async function mdxLoader(
context: Rspack.LoaderContext<LoaderOptions>,
source: string,
callback: Rspack.LoaderContext['callback'],
) {
const options = context.getOptions();
const filepath = context.resourcePath;
const { alias } = context._compiler.options.resolve;
context.cacheable(true);

let pageMeta = {
title: '',
toc: [],
Expand All @@ -50,8 +100,9 @@ export default async function mdxLoader(
);

// preprocessor
const preprocessedContent = escapeMarkdownHeadingIds(content);
const isHomePage = frontmatter.pageType === 'home';
const preprocessedContent = escapeMarkdownHeadingIds(
await flattenMdxContent(content, filepath, alias as Record<string, string>),
);

let enableMdxRs;
const mdxRs = config?.markdown?.mdxRs ?? true;
Expand Down Expand Up @@ -114,16 +165,13 @@ export default async function mdxLoader(
checkLinks(links, filepath, docDirectory, routeService);
}
}
// Note: encode filename to be compatible with Windows

const result = `${
// Why do we add the frontmatter to the compiled result?
// 1. Inject the frontmatter to the compiled result, so that we can get the frontmatter in the mdx content
// 2. Fix the home page won't update when the frontmatter is changed. When the frontmatter in the mdx of home page is changed, the home page will not be updated. The reason is that the home page component isn't the mdx module compiled currently. But the frontmatter of the mdx module have sincerely changed. So we reload the page and the home page will be updated.
// In react-refresh, if a module can be hot updated, all the its exports need to be component(function/class & upper case first letter), otherwise it will trigger a full reload.
// In this case, we export a component named `export` to make the home page can be hot updated after the frontmatter is changed.
isHomePage ? 'export' : ''
} const frontmatter = ${JSON.stringify(frontmatter)};

// If page meta changed, we trigger page reload to ensure the page is up to date.
if (!isProduction()) {
checkPageMetaUpdate(filepath, pageMeta);
}

const result = `const frontmatter = ${JSON.stringify(frontmatter)};
${compileResult}
MDXContent.__RSPRESS_PAGE_META = {};
Expand Down
20 changes: 9 additions & 11 deletions packages/core/src/node/runtimeModule/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { UserConfig } from '@rspress/shared';
import type { RsbuildPlugin } from '@rsbuild/core';
import { RspackVirtualModulePlugin } from 'rspack-plugin-virtual-module';
import { RsbuildPlugin } from '@rsbuild/core';
import { RouteService } from '../route/RouteService';
import { PluginDriver } from '../PluginDriver';
import { routeVMPlugin } from './routeData';
Expand Down Expand Up @@ -57,7 +57,6 @@ export const runtimeModuleFactory: RuntimeModuleFactory[] = [
];

// We will use this plugin to generate runtime module in browser, which is important to ensure the client have access to some compile-time data
// TODO: We can separate the compile-time data generation and runtime module generation logic instead of putting them together(such as `siteDataVMPlugin` plugin, it does too much thing) to make it more clear
export function rsbuildPluginDocVM(
factoryContext: Omit<FactoryContext, 'alias'>,
): RsbuildPlugin {
Expand All @@ -73,7 +72,7 @@ export function rsbuildPluginDocVM(
for (const factory of runtimeModuleFactory) {
const moduleResult = await factory({
...factoryContext,
alias,
alias: alias as Record<string, string>,
});
Object.assign(runtimeModule, moduleResult);
}
Expand All @@ -87,14 +86,13 @@ export function rsbuildPluginDocVM(
}
runtimeModule[key] = modulesByPlugin[key];
});
bundlerChain
.plugin(`rspress-runtime-module`)
.use(
new RspackVirtualModulePlugin(
runtimeModule,
factoryContext.runtimeTempDir,
),
);
bundlerChain.plugin(`rspress-runtime-module`).use(
// @ts-expect-error
new RspackVirtualModulePlugin(
runtimeModule,
factoryContext.runtimeTempDir,
),
);
});
},
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ export async function extractPageData(
}
});

// TODO: we will find a more efficient way to do this
const flattenContent = await flattenMdxContent(
applyReplaceRules(strippedFrontMatter, replaceRules),
route.absolutePath,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
NavItem,
Sidebar,
SidebarDivider,
slash,
} from '@rspress/shared';
import { applyReplaceRules } from '../../utils/applyReplaceRules';
import { getI18nData } from '../i18n';
Expand All @@ -37,21 +38,22 @@ export function normalizeThemeConfig(
const i18nTextData = getI18nData(docConfig);
// In following code, we will normalize the theme config reference to the pages data extracted from mdx files
const normalizeLinkPrefix = (link = '', currentLang = '') => {
const normalizedLink = slash(link);
if (
!currentLang ||
!link ||
withoutBase(link, base).startsWith(`/${currentLang}`) ||
isExternalUrl(link) ||
withoutBase(normalizedLink, base).startsWith(`/${currentLang}`) ||
isExternalUrl(normalizedLink) ||
// In multi version case, we have got the complete link prefix in `plugin-auto-nav-sidebar` and does not need to add the lang prefix
hasMultiVersion
) {
return link;
return normalizedLink;
}
// if lang exists, we should add the lang prefix to the link
// such /guide -> /en/guide
return lang === currentLang
? link
: `/${currentLang}${addLeadingSlash(link)}`;
? normalizedLink
: `/${currentLang}${addLeadingSlash(normalizedLink)}`;
};

const getI18nText = (key = '', currentLang = '') => {
Expand All @@ -72,6 +74,7 @@ export function normalizeThemeConfig(
): NormalizedSidebarGroup | SidebarItem | SidebarDivider => {
if (typeof item === 'object' && 'items' in item) {
return {
...item,
text: applyReplaceRules(
getI18nText(item.text, currentLang),
replaceRules,
Expand All @@ -98,10 +101,12 @@ export function normalizeThemeConfig(
return {
text: applyReplaceRules(page?.title || '', replaceRules),
link: normalizedItem,
_fileKey: page._relativePath.replace(/\.mdx?$/, ''),
};
}

return {
...item,
text: applyReplaceRules(
getI18nText(item.text, currentLang),
replaceRules,
Expand Down
9 changes: 7 additions & 2 deletions packages/core/src/node/utils/flattenMdxContent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import fs from '@rspress/shared/fs-extra';
import { createProcessor } from '@mdx-js/mdx';
import type { Root } from 'hast';
import { MDX_REGEXP } from '@rspress/shared';
import { importStatementRegex } from '../constants';

const { CachedInputFileSystem, ResolverFactory } = enhancedResolve;
const processor = createProcessor();
Expand Down Expand Up @@ -60,6 +61,11 @@ export async function flattenMdxContent(
basePath: string,
alias: Record<string, string | string[]>,
): Promise<string> {
// Performance optimization: if the content does not contain any import statement, we can skip the parsing process

if (!importStatementRegex.test(content)) {
return content;
}
let result = content;
// We should update the resolver instanceof because the alias should be passed to it
// If we reuse the resolver instance in `detectReactVersion` method, the resolver will lose the alias info and cannot resolve path correctly in mdx files.
Expand All @@ -81,12 +87,10 @@ export async function flattenMdxContent(
const importNodes = ast.children
.filter(node => node.type === 'mdxjsEsm')
.map(node => {
result = result.replace((node as { value: string }).value, '');
return (node.data?.estree as { body: ImportNode[] })?.body || [];
})
.flat()
.filter(node => node.type === 'ImportDeclaration');

for (const importNode of importNodes) {
// import Comp from './a';
// id: Comp
Expand All @@ -104,6 +108,7 @@ export async function flattenMdxContent(
} catch (e) {
continue;
}

if (MDX_REGEXP.test(absoluteImportPath)) {
// replace import statement with the content of the imported file
const importedContent = fs.readFileSync(absoluteImportPath, 'utf-8');
Expand Down
16 changes: 2 additions & 14 deletions packages/core/src/runtime/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,7 @@ import {
} from '@rspress/runtime';
import { HelmetProvider } from 'react-helmet-async';
import React, { useContext, useLayoutEffect } from 'react';
import {
Header,
PageData,
cleanUrl,
isProduction,
MDX_REGEXP,
} from '@rspress/shared';
import { Header, PageData, cleanUrl, MDX_REGEXP } from '@rspress/shared';
import globalComponents from 'virtual-global-components';
import 'virtual-global-styles';

Expand Down Expand Up @@ -69,13 +63,7 @@ export async function initPageData(routePath: string): Promise<PageData> {
pageType: frontmatter?.pageType || 'doc',
title,
frontmatter,
// Trade off:
// 1. the `extractPageInfo` includes complete toc even if import doc fragments, because we use `flattenMdxContent` function to make all doc fragments' toc included.However, it is only computed once when build
// 2. the mod.toc is not complete toc, but it is computed every time through loader when doc changed
// We choose the better solutions for different environments:
// In production, we use the extractPageInfo.toc to ensure the toc is complete and accurate.
// In development, we use the mod.toc to ensure the toc is up to date to ensure DX.However, we cannot ensure the complete toc info when including doc fragments.
toc: isProduction() ? extractPageInfo?.toc : toc,
toc,
},
};
} else {
Expand Down
11 changes: 8 additions & 3 deletions packages/plugin-auto-nav-sidebar/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,13 +168,14 @@ function processLocales(
lang === defaultLang ? '' : `/${lang}`
}`,
);
return walk(path.join(root, version, lang), routePrefix);
return walk(path.join(root, version, lang), routePrefix, root);
}),
)
: [
await walk(
path.join(root, lang),
addTrailingSlash(lang === defaultLang ? '' : `/${lang}`),
root,
),
];
return combineWalkResult(walks, versions);
Expand Down Expand Up @@ -225,10 +226,14 @@ export function pluginAutoNavSidebar(): RspressPlugin {
const routePrefix = addTrailingSlash(
version === defaultVersion ? '' : `/${version}`,
);
return walk(path.join(config.root!, version), routePrefix);
return walk(
path.join(config.root!, version),
routePrefix,
config.root!,
);
}),
)
: [await walk(config.root!)];
: [await walk(config.root!, '/', config.root!)];

const combined = combineWalkResult(walks, versions);

Expand Down
Loading

0 comments on commit 24c03fc

Please sign in to comment.