diff --git a/.changeset/friendly-windows-count.md b/.changeset/friendly-windows-count.md new file mode 100644 index 00000000000..8cc7f1f3f1d --- /dev/null +++ b/.changeset/friendly-windows-count.md @@ -0,0 +1,5 @@ +--- +"nextra": patch +--- + +fix route group within `app/` dir crash the `convertToPageMap` diff --git a/docs/app/_components/i18n-demo.tsx b/docs/app/_components/i18n-demo.tsx index 43ba7ef74e6..3e47f857b04 100644 --- a/docs/app/_components/i18n-demo.tsx +++ b/docs/app/_components/i18n-demo.tsx @@ -34,7 +34,6 @@ export const I18n: FC = () => {
setActive(lang)} - // eslint-disable-next-line tailwindcss/no-custom-classname -- TODO: configure eslint-plugin-tailwindcss to import nextra-theme-docs styles so below classes could be removed className={cn( 'relative cursor-default px-4 py-1.5 whitespace-nowrap select-none', active === lang diff --git a/examples/docs/src/app/showcase/(overview)/page.jsx b/examples/docs/src/app/showcase/(overview)/page.jsx new file mode 100644 index 00000000000..f075557cbf1 --- /dev/null +++ b/examples/docs/src/app/showcase/(overview)/page.jsx @@ -0,0 +1,20 @@ +/** + * @see https://github.com/shuding/nextra/issues/4148 + */ + +export const metadata = {} + +export default function Page() { + return ( +

+ Showcase +

+ ) +} diff --git a/packages/eslint-config/src/index.ts b/packages/eslint-config/src/index.ts index d912f99a170..41512e0efdc 100644 --- a/packages/eslint-config/src/index.ts +++ b/packages/eslint-config/src/index.ts @@ -3,7 +3,7 @@ import { includeIgnoreFile } from '@eslint/compat' import js from '@eslint/js' // @ts-expect-error -- no types import eslintPluginNext from '@next/eslint-plugin-next' -import type { Linter } from 'eslint' +// import type { Linter } from 'eslint' // @ts-expect-error -- no types import eslintConfigPrettier from 'eslint-config-prettier' import eslintPluginImport from 'eslint-plugin-import-x' @@ -13,24 +13,24 @@ import * as eslintPluginReactCompiler from 'eslint-plugin-react-compiler' // @ts-expect-error -- no types import eslintPluginReactHooks from 'eslint-plugin-react-hooks' import eslintPluginSonarJs from 'eslint-plugin-sonarjs' -// @ts-expect-error -- no types -import eslintPluginTailwindCss from 'eslint-plugin-tailwindcss' +// import eslintPluginTailwindCss from 'eslint-plugin-tailwindcss' // @ts-expect-error -- no types import eslintPluginTsSortKeys from 'eslint-plugin-typescript-sort-keys' import eslintPluginUnicorn from 'eslint-plugin-unicorn' import tseslint from 'typescript-eslint' import type { Config } from 'typescript-eslint' -const TAILWIND_CONFIG = { - extends: [eslintPluginTailwindCss.configs['flat/recommended']], - rules: { - 'tailwindcss/classnames-order': 'off', // conflicts with prettier-plugin-tailwindcss - 'tailwindcss/enforces-negative-arbitrary-values': 'error', - 'tailwindcss/enforces-shorthand': 'error', - 'tailwindcss/migration-from-tailwind-2': 'error', - 'tailwindcss/no-custom-classname': 'error' - } satisfies Linter.RulesRecord -} +// TODO: Enable once `eslint-plugin-tailwindcss` will support Tailwind CSS v4 +// const TAILWIND_CONFIG = { +// extends: [eslintPluginTailwindCss.configs['flat/recommended']], +// rules: { +// 'tailwindcss/classnames-order': 'off', // conflicts with prettier-plugin-tailwindcss +// 'tailwindcss/enforces-negative-arbitrary-values': 'error', +// 'tailwindcss/enforces-shorthand': 'error', +// 'tailwindcss/migration-from-tailwind-2': 'error', +// 'tailwindcss/no-custom-classname': 'error' +// } satisfies Linter.RulesRecord +// } const REACT_COMPILER_RESTRICT = { name: 'react', @@ -201,7 +201,7 @@ const config: Config = tseslint.config( }, // ⚙️ nextra-theme-docs { - ...TAILWIND_CONFIG, + // ...TAILWIND_CONFIG, files: ['packages/nextra-theme-docs/**'], settings: { tailwindcss: { @@ -217,7 +217,7 @@ const config: Config = tseslint.config( } }, rules: { - ...TAILWIND_CONFIG.rules, + // ...TAILWIND_CONFIG.rules, 'no-restricted-imports': [ 'error', { name: 'next/link', message: 'Use `` instead' }, @@ -229,10 +229,10 @@ const config: Config = tseslint.config( }, // ⚙️ nextra-theme-blog { - ...TAILWIND_CONFIG, + // ...TAILWIND_CONFIG, files: ['packages/nextra-theme-blog/**'], rules: { - ...TAILWIND_CONFIG.rules, + // ...TAILWIND_CONFIG.rules, 'no-restricted-imports': [ 'error', { @@ -252,7 +252,7 @@ const config: Config = tseslint.config( }, // ⚙️ nextra { - ...TAILWIND_CONFIG, + // ...TAILWIND_CONFIG, files: ['packages/nextra/**'], settings: { tailwindcss: { @@ -267,7 +267,7 @@ const config: Config = tseslint.config( } }, rules: { - ...TAILWIND_CONFIG.rules, + // ...TAILWIND_CONFIG.rules, 'import/extensions': ['error', 'ignorePackages'], // False positive due Tailwind CSS v4 'tailwindcss/no-custom-classname': 'off' @@ -275,7 +275,7 @@ const config: Config = tseslint.config( }, // ⚙️ Docs { - ...TAILWIND_CONFIG, + // ...TAILWIND_CONFIG, files: ['docs/**'], settings: { tailwindcss: { @@ -307,7 +307,7 @@ const config: Config = tseslint.config( }, // ⚙️ SWR-site example { - ...TAILWIND_CONFIG, + // ...TAILWIND_CONFIG, files: ['examples/swr-site/**'], settings: { tailwindcss: { diff --git a/packages/nextra/src/server/__tests__/to-page-map.test.ts b/packages/nextra/src/server/__tests__/to-page-map.test.ts index ee10c2436f2..758b7c8795b 100644 --- a/packages/nextra/src/server/__tests__/to-page-map.test.ts +++ b/packages/nextra/src/server/__tests__/to-page-map.test.ts @@ -873,6 +873,7 @@ describe('generatePageMap()', () => { "src/app/_meta.js", "src/app/blog/page.jsx", "src/app/page.jsx", + "src/app/showcase/(overview)/page.jsx", "src/content/_meta.js", "src/content/advanced/code-highlighting.mdx", "src/content/features/_meta.js", @@ -1011,6 +1012,11 @@ describe('generatePageMap()', () => { "name": "index", "route": "/", }, + { + "__pagePath": "src/app/showcase/(overview)/page.jsx", + "name": "showcase", + "route": "/showcase", + }, { "children": [ { @@ -1220,6 +1226,11 @@ describe('generatePageMap()', () => { "name": "index", "route": "/", }, + { + "__pagePath": "src/app/showcase/(overview)/page.jsx", + "name": "showcase", + "route": "/showcase", + }, ] `) }) diff --git a/packages/nextra/src/server/page-map/to-page-map.ts b/packages/nextra/src/server/page-map/to-page-map.ts index a17bd036184..676030561cb 100644 --- a/packages/nextra/src/server/page-map/to-page-map.ts +++ b/packages/nextra/src/server/page-map/to-page-map.ts @@ -1,4 +1,5 @@ import path from 'node:path' +import { normalizeAppPath } from 'next/dist/shared/lib/router/utils/app-paths.js' import type { TItem } from '../../types.js' interface NestedMap { @@ -54,7 +55,9 @@ export function convertToPageMap({ // // will be normalized to: // app/posts/aaron-swartz-a-programmable-web/page.mdx - dir.replaceAll(/\(.*?\)(\/|$)/g, '') + // + // The `normalizeAppPath` function ensures a leading slash is present, so we slice it off. + normalizeAppPath(dir).slice(1) : [dir, name !== 'index' && name].filter(Boolean).join('/') pages[key] = filePath }