Skip to content

Commit

Permalink
[v4] compile nextra/components, nextra/hooks and `nextra-theme-bl…
Browse files Browse the repository at this point in the history
…og` source code with react-compiler (#3742)

* improve

* aa

* aa

* aa

* aa

* more

* more

* more

* more

* react compiler

* react compiler

* react compiler

* react com

* react com

* react compiler

* react com

* react com

* react compiler

* react com

* more

* react com

* react com

* react com

* react com

* react com

* aa

* aa

* aa

* aa

* better name

* fix build

* fix xx

* moreee

* moreee

* Update packages/nextra/src/client/mdx-components/pre/copy-to-clipboard.tsx

* should log on windows now

* missed this

* check with use 'use no memo' comments either files were optimized or not

* more

* more

* more

* more

* more

* more

* more

* more

* more

* more

* more

* more

* more

* more

* more

* more

* yoyo

* pnpm dedupe

* aa

* `react-compiler-runtime` should be direct dependency for nextra/nextra-theme-docs/nextra-theme-blog
  • Loading branch information
dimaMachina authored Nov 27, 2024
1 parent f065137 commit 386b620
Show file tree
Hide file tree
Showing 91 changed files with 1,002 additions and 688 deletions.
5 changes: 5 additions & 0 deletions .changeset/violet-walls-cover.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'nextra': minor
---

compile `nextra/components`, `nextra/hooks`, `nextra-theme-docs` and `nextra-theme-blog` source code with react-compiler
16 changes: 15 additions & 1 deletion .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ const TAILWIND_CONFIG = {
}
}

const REACT_COMPILER_RESTRICT = {
name: 'react',
importNames: ['memo', 'useCallback', 'useMemo', 'forwardRef']
}

/** @type {import('eslint').Linter.Config} */
module.exports = {
root: true,
Expand Down Expand Up @@ -144,6 +149,14 @@ module.exports = {
]
}
},
{
files: ['packages/**'],
plugins: ['eslint-plugin-react-compiler'],
rules: {
'no-restricted-imports': ['error', REACT_COMPILER_RESTRICT],
'react-compiler/react-compiler': 'error'
}
},
// ⚙️ nextra-theme-docs
{
...TAILWIND_CONFIG,
Expand All @@ -166,7 +179,8 @@ module.exports = {
...TAILWIND_CONFIG.rules,
'no-restricted-imports': [
'error',
{ name: 'next/link', message: 'Use `<Anchor>` instead' }
{ name: 'next/link', message: 'Use `<Anchor>` instead' },
REACT_COMPILER_RESTRICT
],
// False positive due Tailwind CSS v4
'tailwindcss/no-custom-classname': 'off'
Expand Down
11 changes: 3 additions & 8 deletions docs/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { NextraLogo, VercelLogo } from '@components/icons'
import cn from 'clsx'
import type { Metadata, Viewport } from 'next'
import { Footer, Layout, Link, Navbar } from 'nextra-theme-docs'
import { Footer, Layout, Navbar } from 'nextra-theme-docs'
import { Banner, Head } from 'nextra/components'
import { getPageMap } from 'nextra/page-map'
import type { FC, ReactNode } from 'react'
Expand Down Expand Up @@ -56,13 +56,8 @@ const RootLayout: FC<{
children: ReactNode
}> = async ({ children }) => {
const banner = (
<Banner storageKey="4.0-release">
<div className="before:content-['🎉_']">
Nextra 4.0 is released.{' '}
<Link href="#" className="after:content-['_→']">
Read more
</Link>
</div>
<Banner dismissible={false}>
🚧 This is WIP documentation for Nextra 4.0
</Banner>
)
const navbar = (
Expand Down
2 changes: 1 addition & 1 deletion docs/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const IndexPage: FC = () => {
</Link>
</p>
</div>
<div className="features-container x:border-b bordered">
<div className="features-container x:border-b nextra-border">
<div className="content-container">
<Features>
<Feature
Expand Down
2 changes: 1 addition & 1 deletion examples/blog/app/tags/[tag]/page.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export default async function TagPage(props) {
const params = await props.params
return (
<>
<h1>{generateMetadata({ params }).title}</h1>
<h1>{(await generateMetadata({ params })).title}</h1>
{(await getPosts())
.filter(post =>
post.frontMatter.tags.includes(decodeURIComponent(params.tag))
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"eslint-plugin-deprecation": "3.0.0",
"eslint-plugin-import": "2.31.0",
"eslint-plugin-react": "7.37.2",
"eslint-plugin-react-compiler": "19.0.0-beta-df7b47d-20241124",
"eslint-plugin-react-hooks": "5.0.0",
"eslint-plugin-sonarjs": "^2.0.4",
"eslint-plugin-tailwindcss": "3.17.3",
Expand Down
27 changes: 27 additions & 0 deletions packages/esbuild-react-compiler-plugin/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"name": "esbuild-react-compiler-plugin",
"version": "0.0.0",
"type": "module",
"description": "",
"author": "Dimitri POSTOLOV",
"license": "ISC",
"private": true,
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
},
"./package.json": "./package.json"
},
"types": "./dist/index.d.ts",
"scripts": {
"build": "tsup",
"dev": "tsup --watch"
},
"dependencies": {
"react-compiler-webpack": "0.1.2"
},
"devDependencies": {
"@types/node": "^22.0.0"
}
}
5 changes: 5 additions & 0 deletions packages/esbuild-react-compiler-plugin/src/env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
declare module 'react-compiler-webpack/dist/react-compiler-loader.js' {
export default function reactCompilerLoader(
source: string | Buffer
): Promise<void>
}
64 changes: 64 additions & 0 deletions packages/esbuild-react-compiler-plugin/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import fs from 'node:fs/promises'
import path from 'node:path'
import reactCompilerLoader from 'react-compiler-webpack/dist/react-compiler-loader.js'
import type { Options } from 'tsup'

const reactCompilerConfig = {
sources(_filename: string) {
return true
},
target: '18'
}

export const reactCompilerPlugin = (
filter: RegExp
): NonNullable<Options['esbuildPlugins']>[number] => ({
name: 'react-compiler',
setup(build) {
build.onLoad({ filter }, async args => {
// Read the file content
const code = await fs.readFile(args.path)
return new Promise<{
contents: string
loader: 'ts' | 'tsx'
}>((resolve, reject) => {
function callback(error: Error | null, result?: string) {
if (!result) {
reject(error)
return
}
// Mark the file as a ts/tsx file
const loader = path.extname(args.path).slice(1) as 'ts' | 'tsx'
const relativePath = path.relative(process.cwd(), args.path)

if (
/^import \{ c as _c } from "react-compiler-runtime";/m.test(result)
) {
console.info(
'🚀 File',
relativePath,
'was optimized with react-compiler'
)
} else if (!/^'use no memo'/m.test(result)) {
console.error(
'❌ File',
relativePath,
'was not optimized with react-compiler'
)
}

resolve({ contents: result, loader })
}

reactCompilerLoader.call(
{
async: () => callback,
getOptions: () => reactCompilerConfig,
resourcePath: args.path
},
code
)
})
})
}
})
16 changes: 16 additions & 0 deletions packages/esbuild-react-compiler-plugin/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"compilerOptions": {
"target": "es2022",
"module": "ESNext",
"declaration": true,
"noEmit": true,
"esModuleInterop": true,
"strict": true,
"skipLibCheck": true,
"strictNullChecks": true,
"lib": ["esnext", "dom"],
"moduleResolution": "node",
"resolveJsonModule": true
},
"exclude": ["dist"]
}
11 changes: 11 additions & 0 deletions packages/esbuild-react-compiler-plugin/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { defineConfig } from 'tsup'
import packageJson from './package.json'

export default defineConfig({
name: packageJson.name,
entry: ['src/**/*.ts'],
format: 'esm',
dts: true,
splitting: process.env.NODE_ENV === 'production',
bundle: false
})
4 changes: 3 additions & 1 deletion packages/nextra-theme-blog/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,14 @@
},
"dependencies": {
"next-themes": "^0.4.0",
"next-view-transitions": "^0.3.0"
"next-view-transitions": "^0.3.0",
"react-compiler-runtime": "19.0.0-beta-df7b47d-20241124"
},
"devDependencies": {
"@tailwindcss/postcss": "^4.0.0-beta.2",
"@tailwindcss/typography": "^0.5.15",
"@types/react": "^18.2.23",
"esbuild-react-compiler-plugin": "workspace:*",
"next": "^15.0.2",
"nextra": "workspace:*",
"postcss": "^8.4.33",
Expand Down
9 changes: 6 additions & 3 deletions packages/nextra-theme-blog/src/components/cusdis.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,21 @@ export const Comments: FC<{
useEffect(() => {
try {
// update the theme for the cusdis iframe when theme changed
window.CUSDIS?.setTheme(resolvedTheme as 'dark' | 'light')
if (window.CUSDIS) {
// window.CUSDIS? doesn't work with react-compiler
window.CUSDIS.setTheme(resolvedTheme as 'dark' | 'light')
}
} catch (error) {
console.error(error)
}
}, [resolvedTheme])

if (!appId) {
console.warn('[nextra/cusdis] `appId` is required')
return
return null
}
if (!mounted) {
return
return null
}

return (
Expand Down
2 changes: 1 addition & 1 deletion packages/nextra-theme-blog/src/components/go-back.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const GoBack: FC = () => {
const segments = usePathname().split('/')

const isNestedPage = segments.length > 2
if (!isNestedPage) return
if (!isNestedPage) return null
return (
<Button
onClick={router.back}
Expand Down
2 changes: 2 additions & 0 deletions packages/nextra-theme-blog/src/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
'use no memo'

export { Comments } from './cusdis'
export { Layout, Footer } from './layout'
export { Navbar } from './navbar'
Expand Down
13 changes: 4 additions & 9 deletions packages/nextra-theme-blog/src/components/navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import type { PageMapItem } from 'nextra'
import { useFSRoute } from 'nextra/hooks'
import { normalizePages } from 'nextra/normalize-pages'
import type { FC, ReactNode } from 'react'
import { useMemo } from 'react'

type NavbarProps = {
children?: ReactNode
Expand All @@ -14,14 +13,10 @@ type NavbarProps = {

export const Navbar: FC<NavbarProps> = ({ children, pageMap }) => {
const pathname = useFSRoute()
const { topLevelNavbarItems } = useMemo(
() =>
normalizePages({
list: pageMap,
route: pathname
}),
[pageMap, pathname]
)
const { topLevelNavbarItems } = normalizePages({
list: pageMap,
route: pathname
})
return (
<header
className="x:mb-8 x:flex x:items-center x:gap-3 x:justify-end"
Expand Down
2 changes: 2 additions & 0 deletions packages/nextra-theme-blog/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
'use no memo'

export { useMDXComponents } from './mdx-components'
export {
Comments,
Expand Down
2 changes: 2 additions & 0 deletions packages/nextra-theme-blog/src/is-valid-date.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
'use no memo'

const DATE_RE = /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2})?(:\d{2}\.\d{3}Z)?$/
const DATE_RE_WITH_SLASH = /^\d{4}\/\d{1,2}\/\d{1,2}( \d{1,2}:\d{1,2})?$/

Expand Down
3 changes: 3 additions & 0 deletions packages/nextra-theme-blog/src/mdx-components.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// should be used on server
'use no memo'

/* eslint sort-keys: error */
import {
Callout,
Expand Down
2 changes: 2 additions & 0 deletions packages/nextra-theme-blog/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
'use no memo'

/* eslint typescript-sort-keys/interface: error */
import type { ReadingTime } from 'nextra'

Expand Down
1 change: 1 addition & 0 deletions packages/nextra-theme-blog/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"esModuleInterop": true,
"strict": true,
"skipLibCheck": true,
"strictNullChecks": true,
"jsx": "react-jsx",
"moduleResolution": "bundler",
"types": ["vitest/globals"]
Expand Down
6 changes: 4 additions & 2 deletions packages/nextra-theme-blog/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import { reactCompilerPlugin } from 'esbuild-react-compiler-plugin'
import { defineConfig } from 'tsup'
import { defaultEntry } from '../nextra-theme-docs/tsup.config'
import packageJson from './package.json'

export default defineConfig([
{
name: packageJson.name,
entry: [...defaultEntry, '!src/types.ts'],
entry: defaultEntry,
format: 'esm',
dts: true,
outExtension: () => ({ js: '.js' }),
bundle: false
bundle: false,
esbuildPlugins: [reactCompilerPlugin(/\.tsx?$/)]
},
{
name: `${packageJson.name}/css`,
Expand Down
2 changes: 2 additions & 0 deletions packages/nextra-theme-docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"@headlessui/react": "^2.1.2",
"clsx": "^2.1.0",
"next-themes": "^0.4.0",
"react-compiler-runtime": "19.0.0-beta-df7b47d-20241124",
"scroll-into-view-if-needed": "^3.1.0",
"zod": "^3.22.3",
"zod-validation-error": "^3.0.0",
Expand All @@ -43,6 +44,7 @@
"@testing-library/react": "^16.0.0",
"@types/react": "^18.2.23",
"@vitejs/plugin-react": "^4.1.0",
"esbuild-react-compiler-plugin": "workspace:*",
"jsdom": "^25.0.0",
"next": "^15.0.2",
"nextra": "workspace:*",
Expand Down
4 changes: 2 additions & 2 deletions packages/nextra-theme-docs/src/components/404/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ type NotFoundPageProps = {
export const NotFoundPage: FC<NotFoundPageProps> = ({
content = 'Submit an issue about broken link',
labels = 'bug',
children = <H1>404: Page Not Found</H1>,
children,
className
}) => {
return (
Expand All @@ -23,7 +23,7 @@ export const NotFoundPage: FC<NotFoundPageProps> = ({
className
)}
>
{children}
{children || <H1>404: Page Not Found</H1>}
<NotFoundLink labels={labels}>{content}</NotFoundLink>
</div>
)
Expand Down
Loading

0 comments on commit 386b620

Please sign in to comment.