Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions packages/mdxe/bin/mdxe.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ const runNextCommand = async (command, args = []) => {
const readmePath = resolve(userCwd, 'README.md')
const hasReadme = existsSync(readmePath)

const { tempDir, cleanup } = await createTempNextConfig(userCwd)
tempConfigInfo = { cleanup }

const localNextBin = resolve(userCwd, 'node_modules', '.bin', 'next')
const mdxeNextBin = resolve(mdxeRoot, 'node_modules', '.bin', 'next')

Expand All @@ -72,13 +75,17 @@ const runNextCommand = async (command, args = []) => {
activeProcess = spawn(cmd, cmdArgs, {
stdio: 'inherit',
shell: true,
cwd: embeddedAppPath,
cwd: tempDir, // Use the temporary directory with proper App Router configuration
env: {
...process.env,
PAYLOAD_DB_PATH: resolve(userCwd, 'mdx.db'),
NEXT_DIST_DIR: resolve(userCwd, '.next'),
USER_CWD: userCwd,
README_PATH: hasReadme ? readmePath : ''
README_PATH: hasReadme ? readmePath : '',
MDXE_CONTENT_DIR: userCwd,
NEXT_PAGES_DIR: '',
NEXT_SKIP_PAGES_ROUTER: 'true',
NEXT_TELEMETRY_DISABLED: '1'
}
})

Expand Down
23 changes: 23 additions & 0 deletions packages/mdxe/src/app/404.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from 'react'

export default function NotFound() {
return (
<div className="flex flex-col items-center justify-center min-h-screen py-2">
<main className="flex flex-col items-center justify-center w-full flex-1 px-20 text-center">
<h1 className="text-6xl font-bold">404</h1>
<h2 className="text-2xl mt-3">Page Not Found</h2>
<p className="mt-3 text-lg">
The page you are looking for does not exist or has been moved.
</p>
<div className="mt-6">
<a
href="/"
className="px-4 py-2 text-sm font-medium text-white bg-blue-600 rounded-md hover:bg-blue-700"
>
Go back home
</a>
</div>
</main>
</div>
)
}
35 changes: 20 additions & 15 deletions packages/mdxe/src/app/mdx-components.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,38 @@
import React from 'react'
import type { ComponentType } from 'react'
import type { ComponentType, ReactNode } from 'react'

type MDXComponents = Record<string, ComponentType<React.ComponentProps<any>>>
interface MDXComponentProps {
children: ReactNode // Changed from optional to required
[key: string]: any
}

type MDXComponents = Record<string, ComponentType<MDXComponentProps>>

const layouts = {
ArticleLayout: ({ children }: { children: React.ReactNode }) => <div className="article-layout">{children}</div>,
PostLayout: ({ children }: { children: React.ReactNode }) => <div className="post-layout">{children}</div>,
DocsLayout: ({ children }: { children: React.ReactNode }) => <div className="docs-layout">{children}</div>,
ThingLayout: ({ children }: { children: React.ReactNode }) => <div className="thing-layout">{children}</div>,
ArticleLayout: ({ children }: MDXComponentProps) => <div className="article-layout">{children}</div>,
PostLayout: ({ children }: MDXComponentProps) => <div className="post-layout">{children}</div>,
DocsLayout: ({ children }: MDXComponentProps) => <div className="docs-layout">{children}</div>,
ThingLayout: ({ children }: MDXComponentProps) => <div className="thing-layout">{children}</div>,
}

export function useMDXComponents(components: MDXComponents): MDXComponents {
export async function useMDXComponents(components: MDXComponents): Promise<MDXComponents> {
let userComponents = {}
try {
const userMdxComponents = require(process.cwd() + '/mdx-components.js')
if (userMdxComponents.default) {
userComponents = userMdxComponents.default
} else if (typeof userMdxComponents === 'function') {
const userMdxComponentsModule = await import(process.cwd() + '/mdx-components.js')
const userMdxComponents = userMdxComponentsModule.default || userMdxComponentsModule

if (typeof userMdxComponents === 'function') {
userComponents = userMdxComponents(components)
} else if (typeof userMdxComponents === 'object') {
userComponents = userMdxComponents
}
} catch (_) { // Underscore indicates intentionally unused parameter
} catch (error) {
}

const defaultComponents = {
h1: ({ children }: { children: React.ReactNode }) => <h1 className="text-2xl font-bold">{children}</h1>,
h2: ({ children }: { children: React.ReactNode }) => <h2 className="text-xl font-bold">{children}</h2>,
p: ({ children }: { children: React.ReactNode }) => <p className="my-2">{children}</p>,
h1: ({ children }: MDXComponentProps) => <h1 className="text-2xl font-bold">{children}</h1>,
h2: ({ children }: MDXComponentProps) => <h2 className="text-xl font-bold">{children}</h2>,
p: ({ children }: MDXComponentProps) => <p className="my-2">{children}</p>,
}

return {
Expand Down
23 changes: 23 additions & 0 deletions packages/mdxe/src/app/not-found.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from 'react'

export default function NotFound() {
return (
<div className="flex flex-col items-center justify-center min-h-screen py-2">
<main className="flex flex-col items-center justify-center w-full flex-1 px-20 text-center">
<h1 className="text-6xl font-bold">404</h1>
<h2 className="text-2xl mt-3">Page Not Found</h2>
<p className="mt-3 text-lg">
The page you are looking for does not exist or has been moved.
</p>
<div className="mt-6">
<a
href="/"
className="px-4 py-2 text-sm font-medium text-white bg-blue-600 rounded-md hover:bg-blue-700"
>
Go back home
</a>
</div>
</main>
</div>
)
}
12 changes: 7 additions & 5 deletions packages/mdxe/src/config/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,9 @@
*/
const nextConfig = {
reactStrictMode: true,
experimental: {
turbo: {
resolveAlias: {
'next-mdx-import-source-file': './mdx-components.js',
},
turbopack: {
resolveAlias: {
'next-mdx-import-source-file': './mdx-components.js',
},
},
distDir: '.next',
Expand All @@ -16,6 +14,10 @@ const nextConfig = {
images: {
domains: (process.env.NEXT_IMAGE_DOMAINS || '').split(',').filter(Boolean),
},
pageExtensions: ['js', 'jsx', 'ts', 'tsx', 'mdx'],
useFileSystemPublicRoutes: true,
skipMiddlewareUrlNormalize: true,
skipTrailingSlashRedirect: true
}

module.exports = nextConfig
1 change: 0 additions & 1 deletion packages/mdxe/src/config/tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { join } from 'path'
export default {
content: [
'./app/**/*.{js,ts,jsx,tsx,md,mdx}',
'./pages/**/*.{js,ts,jsx,tsx,md,mdx}',
'./components/**/*.{js,ts,jsx,tsx,md,mdx}',
'./content/**/*.{js,ts,jsx,tsx,md,mdx}',
'./src/**/*.{js,ts,jsx,tsx,md,mdx}',
Expand Down
1 change: 1 addition & 0 deletions packages/mdxe/src/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const nextConfig = {
images: {
domains: (process.env.NEXT_IMAGE_DOMAINS || '').split(',').filter(Boolean),
},
pageExtensions: ['js', 'jsx', 'ts', 'tsx', 'mdx'],
webpack: (config) => {
config.module.rules.push({
test: /\.(md|mdx)$/,
Expand Down
5 changes: 3 additions & 2 deletions packages/mdxe/src/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@
"include": [
"**/*.ts",
"**/*.tsx",
"/Users/nathanclevenger/Projects/mdx/examples/minimal/.next/types/**/*.ts",
"../../examples/minimal/.next/types/**/*.ts",
".next/types/**/*.ts",
"next-env.d.ts",
"/Users/nathanclevenger/Projects/mdx/packages/mdxe/.next/types/**/*.ts"
"/home/ubuntu/repos/mdx/examples/minimal/.next/types/**/*.ts"
],
"exclude": [
"node_modules"
Expand Down
113 changes: 19 additions & 94 deletions packages/mdxe/src/utils/temp-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,111 +54,36 @@ export default function RootLayout({ children }) {
`
)

// Create the API route for content listing
await fs.writeFile(
join(contentApiDir, 'route.js'),
`import { readdir } from 'fs/promises'
import { join } from 'path'

export async function GET() {
try {
const contentDir = process.env.MDXE_CONTENT_DIR || '.'
const filePaths = []

async function findMarkdownFiles(dir) {
const entries = await readdir(dir, { withFileTypes: true })

for (const entry of entries) {
const fullPath = join(dir, entry.name)

if (entry.isDirectory()) {
await findMarkdownFiles(fullPath)
} else if (entry.name.endsWith('.md') || entry.name.endsWith('.mdx')) {
filePaths.push(fullPath)
}
}
}

await findMarkdownFiles(contentDir)

return Response.json({ files: filePaths })
} catch (error) {
console.error('Error loading content:', error)
return Response.json({ files: [], error: error.message }, { status: 500 })
}
}
`
// copy the not-found.tsx template to the app directory
const notFoundTemplatePath = resolve(__dirname, 'templates', 'not-found.tsx')
if (existsSync(notFoundTemplatePath)) {
await fs.copyFile(notFoundTemplatePath, join(appDir, 'not-found.tsx'))
} else {
await fs.writeFile(
join(appDir, 'not-found.tsx'),
`export default function NotFound() {
return (
<div>
<h1>404 - Page Not Found</h1>
<p>The page you are looking for does not exist.</p>
<a href="/">Go back home</a>
</div>
)
}`
)
}

// Create the client page component
await fs.writeFile(
join(appDir, 'page.tsx'),
`"use client"

import { useEffect, useState } from 'react'

export default function Page() {
const [contentPaths, setContentPaths] = useState([])
const [isLoading, setIsLoading] = useState(true)
const [error, setError] = useState(null)

useEffect(() => {
async function fetchContent() {
try {
setIsLoading(true)
const response = await fetch('/api/content')

if (!response.ok) {
throw new Error('Failed to fetch content')
}

const data = await response.json()
setContentPaths(data.files)
} catch (err) {
console.error('Error fetching content:', err)
setError(err.message)
} finally {
setIsLoading(false)
}
}

fetchContent()
}, [])

if (isLoading) {
return <div className="p-4">Loading content...</div>
}

if (error) {
return <div className="p-4 text-red-500">Error: {error}</div>
}

return (
<div className="p-4">
<h1 className="text-2xl font-bold mb-4">MDXE Content</h1>
{contentPaths.length === 0 ? (
<p>No markdown content found.</p>
) : (
<ul className="list-disc pl-6">
{contentPaths.map((path, index) => (
<li key={index} className="mb-2">
<a href={'/content/' + path.replace(process.env.MDXE_CONTENT_DIR || '.', '')} className="text-blue-500 hover:underline">
{path.split('/').pop()}
</a>
</li>
))}
</ul>
)}
</div>
)
}`
'"use client"\n\nimport { useEffect, useState } from \'react\'\n\nexport default function Page() {\n const [contentPaths, setContentPaths] = useState([])\n const [isLoading, setIsLoading] = useState(true)\n const [error, setError] = useState(null)\n \n useEffect(() => {\n async function fetchContent() {\n try {\n setIsLoading(true)\n const response = await fetch(\'/api/content\')\n \n if (!response.ok) {\n throw new Error(\'Failed to fetch content\')\n }\n \n const data = await response.json()\n setContentPaths(data.files || [])\n } catch (err) {\n console.error(\'Error fetching content:\', err)\n setError(err.message)\n } finally {\n setIsLoading(false)\n }\n }\n \n fetchContent()\n }, [])\n \n if (isLoading) {\n return <div className="p-4">Loading content...</div>\n }\n \n if (error) {\n return <div className="p-4 text-red-500">Error: {error}</div>\n }\n \n return (\n <div className="p-4">\n <h1 className="text-2xl font-bold mb-4">MDXE Content</h1>\n {contentPaths.length === 0 ? (\n <p>No markdown content found.</p>\n ) : (\n <ul className="list-disc pl-6">\n {contentPaths.map((path, index) => (\n <li key={index} className="mb-2">\n <a href={\'/content/\' + path.split(\'/\').pop()} className="text-blue-500 hover:underline">\n {path.split(\'/\').pop()}\n </a>\n </li>\n ))}\n </ul>\n )}\n </div>\n )\n}'
)

const cleanup = async () => {
try {
await fs.rm(tempDir, { recursive: true, force: true })
} catch (error) {
console.warn(`Warning: Could not remove temporary directory ${tempDir}: ${error.message}`)
console.warn('Warning: Could not remove temporary directory ' + tempDir + ': ' + error.message)
}
}

Expand Down
23 changes: 23 additions & 0 deletions packages/mdxe/src/utils/templates/not-found.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from 'react'

export default function NotFound() {
return (
<div className="flex flex-col items-center justify-center min-h-screen py-2">
<main className="flex flex-col items-center justify-center w-full flex-1 px-20 text-center">
<h1 className="text-6xl font-bold">404</h1>
<h2 className="text-2xl mt-3">Page Not Found</h2>
<p className="mt-3 text-lg">
The page you are looking for does not exist or has been moved.
</p>
<div className="mt-6">
<a
href="/"
className="px-4 py-2 text-sm font-medium text-white bg-blue-600 rounded-md hover:bg-blue-700"
>
Go back home
</a>
</div>
</main>
</div>
)
}
Loading
Loading