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
64 changes: 64 additions & 0 deletions TURBOPACK_SETUP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Turbopack Setup for JSON Schema Tour

This document explains the Turbopack configuration for faster development builds.

## What was changed

1. **Updated `next.config.mjs`** to include Turbopack configuration:
- Added experimental Turbopack rules for MDX processing
- Configured path aliases for better module resolution
- Maintained compatibility with existing MDX setup

2. **Updated `package.json`** with new scripts:
- `next-dev-turbo`: Runs Next.js with the `--turbo` flag
- `dev:turbo`: Runs both Turbopack dev server and content watching concurrently

## Usage

To use Turbopack for development:

```bash
# Run with Turbopack (faster builds)
pnpm run dev:turbo

# Or just the Next.js server with Turbopack
pnpm run next-dev-turbo
```

To use the regular Webpack build:
```bash
# Regular development (slower builds)
pnpm run dev
```

## Hydration Issues Fixed

The following hydration issues were resolved for Turbopack compatibility:

1. **IconLink Component**: Added proper SSR handling for `window.matchMedia`
2. **Chakra UI Theme**: Disabled `useSystemColorMode` to prevent color mode mismatches
3. **ContinueBtn Component**: Added hydration-safe localStorage access
4. **Monaco Editor**: Used dynamic import with `ssr: false` to prevent DOM mismatches

## Known Warnings

You may see this warning when using Turbopack:
```
⚠ ./lib/server-functions.ts:28:23
lint TP1004 fs.readFileSync(???*0*, "utf-8") is very dynamic
```

This is expected and doesn't affect functionality. It's related to dynamic file system operations in the server-side code generation functions.

## Benefits

- **Faster builds**: Turbopack provides significantly faster build times compared to Webpack
- **Better development experience**: Hot reload and incremental compilation are faster
- **Full compatibility**: All existing features work including MDX processing, content watching, and hot module replacement

## Configuration Details

The Turbopack configuration in `next.config.mjs`:
- Handles `.mdx` files with the `@mdx-js/loader`
- Provides path aliases for module resolution
- Maintains compatibility with the existing MDX and React setup
11 changes: 9 additions & 2 deletions app/components/CodeEditor/CodeEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,15 @@
import styles from "./CodeEditor.module.css";
import ctx from "classnames";
import { GeistMono } from "geist/font/mono";
import Editor, { Monaco } from "@monaco-editor/react";
import { Monaco } from "@monaco-editor/react";
import { Flex, useColorMode } from "@chakra-ui/react";
import dynamic from "next/dynamic";

// Dynamic import of Monaco Editor to prevent SSR hydration issues
const MonacoEditor = dynamic(() => import("@monaco-editor/react"), {
ssr: false,
loading: () => <div style={{ height: "400px", display: "flex", alignItems: "center", justifyContent: "center" }}>Loading editor...</div>
});
import { useEffect, useState, useRef } from "react";
import MyBtn from "../MyBtn";
import { tryFormattingCode, validateCode } from "@/lib/client-functions";
Expand Down Expand Up @@ -219,7 +226,7 @@ export default function CodeEditor({
return (
<>
<div className={ctx(styles.codeEditor, GeistMono.className)}>
<Editor
<MonacoEditor
language="json"
defaultValue={codeString}
theme={colorMode === "light" ? "light" : "my-theme"}
Expand Down
13 changes: 10 additions & 3 deletions app/components/ContinueBtn/ContinueBtn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,28 @@ import { getCheckPoint } from "@/lib/progressSaving";
import styles from "./ContinueBtn.module.css";
import RightArrow from "@/app/styles/icons/RightArrow";
import { Button } from "@chakra-ui/react";
import { useState, useEffect } from "react";

export default function ContinueBtn() {
const router = useRouter();
const checkpoint = getCheckPoint();
const [checkpoint, setCheckpoint] = useState<string | null>(null);
const [isClient, setIsClient] = useState(false);

useEffect(() => {
setIsClient(true);
const savedCheckpoint = getCheckPoint();
setCheckpoint(savedCheckpoint);
}, []);

const handleClick = () => {
const checkpoint = getCheckPoint();
if (checkpoint) {
router.push(`/${checkpoint}`);
}
};

return (
<>
{checkpoint && (
{isClient && checkpoint && (
<Button
variant={"default"}
onClick={handleClick}
Expand Down
24 changes: 17 additions & 7 deletions app/components/IconLink/IconLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,33 @@ import React from "react";

export default function Icon() {
const [isDarkMode, setIsDarkMode] = React.useState(false);
const [isClient, setIsClient] = React.useState(false);

const onUpdate = React.useCallback((matcher: MediaQueryList) => {
setIsDarkMode(matcher.matches);
}, []);

React.useEffect(() => {
const matcher: MediaQueryList = window.matchMedia(
"(prefers-color-scheme: dark)",
);
matcher.addEventListener("change", () => onUpdate(matcher));
onUpdate(matcher);
}, []);
// Set client flag first to prevent hydration mismatch
setIsClient(true);

if (typeof window !== 'undefined') {
const matcher: MediaQueryList = window.matchMedia(
"(prefers-color-scheme: dark)",
);
matcher.addEventListener("change", () => onUpdate(matcher));
onUpdate(matcher);
}
}, [onUpdate]);

// During SSR and initial hydration, always use the light icon to prevent mismatch
const iconHref = isClient && isDarkMode ? "/logos/icon.ico" : "/logos/icon-black.ico";

return (
<link
rel="icon"
type="image/svg+xml"
href={isDarkMode ? "/logos/icon.ico" : "/logos/icon-black.ico"}
href={iconHref}
id="light-scheme-icon"
/>
);
Expand Down
5 changes: 1 addition & 4 deletions app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@ import cx from "classnames";
import { interFont, outfitFont } from "./styles/fonts";
import CompanyLogos from "./components/CommunityLinks";
import HomePageLinks from "./components/HomePageLinks";
import dynamic from "next/dynamic";
const ContinueBtn = dynamic(() => import("./components/ContinueBtn"), {
ssr: false,
});
import ContinueBtn from "./components/ContinueBtn";
export default function Home() {
return (
<div className={cx(styles.main, outfitFont.className)}>
Expand Down
2 changes: 1 addition & 1 deletion app/styles/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ const Modal = {
export const theme = extendTheme({
config: {
initialColorMode: "light",
useSystemColorMode: true,
useSystemColorMode: false, // Disabled to prevent hydration mismatches with Turbopack
},
styles: {
global: {
Expand Down
21 changes: 21 additions & 0 deletions next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,27 @@ const nextConfig = {
compiler: {
styledComponents: true,
},

// Turbopack configuration
experimental: {
turbo: {
rules: {
// Configure how Turbopack handles different file types
'*.mdx': {
loaders: ['@mdx-js/loader'],
as: '*.js',
},
},
resolveAlias: {
// Add any necessary path aliases for Turbopack
'@': './',
'@/app': './app',
'@/lib': './lib',
'@/content': './content',
'@/public': './public',
},
},
},
};

export default withMDX(nextConfig);
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
"type": "module",
"scripts": {
"next-dev": "next dev",
"next-dev-turbo": "next dev --turbo",
"content-watch": "nodemon --ext ts,mdx --watch ./content --ignore ./content/outline.json --exec \"tsx ./scripts/generateOutline.ts\"",
"dev": "concurrently \"pnpm run next-dev\" \"pnpm run content-watch\"",
"dev:turbo": "concurrently \"pnpm run next-dev-turbo\" \"pnpm run content-watch\"",
"build": "tsx ./scripts/generateOutline.ts && next build",
"start": "next start",
"lint": "next lint",
Expand Down