cssnext.jsbuildmarkdown

Nextjs 15.2 Mdx, Shiki, Rehype-pretty-code Build error


So I'm trying to build my Next.js project, which, for context, was being deployed without a problem, up until my main branch. When I then tried to implement mdx, with rehype and shiki, and tried to deploy that's what I got the errors. So trying to push the shiki-lol branch, that's when I get the error.

npm run dev works perfectly btw.

Deploying to Vercel or locally running npm run build, whichever I run, it doesn't work. I'm getting the following output:

> santiu@0.1.0 build
> next build

   ▲ Next.js 15.2.1

   Creating an optimized production build ...
Failed to compile.

HookWebpackError: Cannot read properties of undefined (reading '0')
    at makeWebpackError (C:\Users\Santi\Documents\PERSONAL\code\WEB\santiu\node_modules\next\dist\compiled\webpack\bundle5.js:29:315788)
    at C:\Users\Santi\Documents\PERSONAL\code\WEB\santiu\node_modules\next\dist\compiled\webpack\bundle5.js:29:106487
    at eval (eval at create (C:\Users\Santi\Documents\PERSONAL\code\WEB\santiu\node_modules\next\dist\compiled\webpack\bundle5.js:14:9224), <anonymous>:44:1)
TypeError: Cannot read properties of undefined (reading '0')        
    at C:\Users\Santi\Documents\PERSONAL\code\WEB\santiu\static\css\f5a335e585685605.css:204:23247
    at Parser.attribute (C:\Users\Santi\Documents\PERSONAL\code\WEB\santiu\node_modules\next\dist\compiled\cssnano-simple\index.js:320:84899)
    at Parser.parse (C:\Users\Santi\Documents\PERSONAL\code\WEB\santiu\node_modules\next\dist\compiled\cssnano-simple\index.js:320:99055)
    at Parser.loop (C:\Users\Santi\Documents\PERSONAL\code\WEB\santiu\node_modules\next\dist\compiled\cssnano-simple\index.js:320:98727)
    at new Parser (C:\Users\Santi\Documents\PERSONAL\code\WEB\santiu\node_modules\next\dist\compiled\cssnano-simple\index.js:320:84494) 
    at Processor._root (C:\Users\Santi\Documents\PERSONAL\code\WEB\santiu\node_modules\next\dist\compiled\cssnano-simple\index.js:320:101743)
    at Processor._runSync (C:\Users\Santi\Documents\PERSONAL\code\WEB\santiu\node_modules\next\dist\compiled\cssnano-simple\index.js:320:102250)
    at Processor.processSync (C:\Users\Santi\Documents\PERSONAL\code\WEB\santiu\node_modules\next\dist\compiled\cssnano-simple\index.js:320:103005)
    at C:\Users\Santi\Documents\PERSONAL\code\WEB\santiu\node_modules\next\dist\compiled\cssnano-simple\index.js:320:39339
    at Array.every (<anonymous>)
    at ensureCompatibility (C:\Users\Santi\Documents\PERSONAL\code\WEB\santiu\node_modules\next\dist\compiled\cssnano-simple\index.js:320:38727)
caused by plugins in Compilation.hooks.processAssets
TypeError: Cannot read properties of undefined (reading '0')        
    at C:\Users\Santi\Documents\PERSONAL\code\WEB\santiu\static\css\f5a335e585685605.css:204:23247
    at Parser.attribute (C:\Users\Santi\Documents\PERSONAL\code\WEB\santiu\node_modules\next\dist\compiled\cssnano-simple\index.js:320:84899)
    at Parser.parse (C:\Users\Santi\Documents\PERSONAL\code\WEB\santiu\node_modules\next\dist\compiled\cssnano-simple\index.js:320:99055)
    at Parser.loop (C:\Users\Santi\Documents\PERSONAL\code\WEB\santiu\node_modules\next\dist\compiled\cssnano-simple\index.js:320:98727)
    at new Parser (C:\Users\Santi\Documents\PERSONAL\code\WEB\santiu\node_modules\next\dist\compiled\cssnano-simple\index.js:320:84494) 
    at Processor._root (C:\Users\Santi\Documents\PERSONAL\code\WEB\santiu\node_modules\next\dist\compiled\cssnano-simple\index.js:320:101743)
    at Processor._runSync (C:\Users\Santi\Documents\PERSONAL\code\WEB\santiu\node_modules\next\dist\compiled\cssnano-simple\index.js:320:102250)
    at Processor.processSync (C:\Users\Santi\Documents\PERSONAL\code\WEB\santiu\node_modules\next\dist\compiled\cssnano-simple\index.js:320:103005)
    at C:\Users\Santi\Documents\PERSONAL\code\WEB\santiu\node_modules\next\dist\compiled\cssnano-simple\index.js:320:39339
    at Array.every (<anonymous>)
    at ensureCompatibility (C:\Users\Santi\Documents\PERSONAL\code\WEB\santiu\node_modules\next\dist\compiled\cssnano-simple\index.js:320:38727)

It appears to be a problem with cssnano-simple, but I can't quite pinpoint why it's happening, anyone knows what the issue could be?? AI hasn't been useful either.

I will show relevant code snippets and package.json info, but regardless the full project is in the repo.

next.config.mjs

import nextMDX from '@next/mdx';
import createNextIntlPlugin from 'next-intl/plugin';
import process from 'node:process';
import rehypePrettyCode from 'rehype-pretty-code';
import remarkUnwrapImages from 'remark-unwrap-images';
Object.assign(process.env, { NEXT_TELEMETRY_DISABLED: '1' });

const plugins = [];

const nextConfig = {
    reactStrictMode: true,
    poweredByHeader: false,

    experimental: {
        optimizePackageImports: ['lucide-react', '@vercel/analytics'],
        optimizeCss: false,
    },
    pageExtensions: ['js', 'jsx', 'ts', 'tsx', 'md', 'mdx'],
    devIndicators: {
        position: 'bottom-right',
    },
    images: {
        formats: ['image/avif', 'image/webp'],
        deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
        imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
        minimumCacheTTL: 60,
        dangerouslyAllowSVG: true,
        contentDispositionType: 'attachment',
        contentSecurityPolicy: "default-src 'self'; script-src 'none'; sandbox;",
        remotePatterns: [
            { protocol: 'https', hostname: 'via.placeholder.com' },
            { protocol: 'https', hostname: 'robohash.org' },
        ],
    },
    env: {
        NEXT_TELEMETRY_DISABLED: '1',
    },
};

/** @type {import('rehype-pretty-code').Options} */
const rehypePrettyCodeOptions = {
    theme: 'catppuccin-macchiato',
    keepBackground: false,
    defaultLang: 'plaintext',

    // Optimize performance
    grid: false, // Disable grid layout for faster rendering

    // Prevent lines from collapsing
    onVisitLine(node) {
        if (node.children.length === 0) {
            node.children = [{ type: 'text', value: ' ' }];
        }
    },

    // Add class to highlighted lines
    onVisitHighlightedLine(node) {
        if (!node.properties.className) {
            node.properties.className = [];
        }
        node.properties.className.push('highlighted');
    },

    // Add class to highlighted words
    onVisitHighlightedChars(node) {
        node.properties.className = ['word'];
    },
};

// Push MDX plugin
plugins.push(
    nextMDX({
        extension: /\.(md|mdx)$/,
        options: {
            remarkPlugins: [remarkUnwrapImages],
            rehypePlugins: [[rehypePrettyCode, rehypePrettyCodeOptions]],
        },
    })
);

// Push next-intl plugin
plugins.push(createNextIntlPlugin());

export default () =>
    plugins.reduce((config, plugin) => plugin(config), nextConfig);

package.json

{
    "name": "santiu",
    "version": "0.1.0",
    "private": true,
    "type": "module",
    "scripts": {
        "dev": "next dev ",
        "build": "next build",
        "start": "next start",
        "lint": "next lint"
    },
    "dependencies": {
        "@heroicons/react": "^2.2.0",
        "@mdx-js/loader": "^3.1.1",
        "@mdx-js/react": "^3.1.1",
        "@next/mdx": "^16.0.1",
        "@tailwindcss/typography": "^0.5.16",
        "@types/mdx": "^2.0.13",
        "@vercel/analytics": "^1.5.0",
        "clsx": "^2.1.1",
        "framer-motion": "^12.4.11",
        "next": "^15.2.1",
        "next-intl": "^4.0.1",
        "next-themes": "^0.4.5",
        "react": "^19.0.0",
        "react-dom": "^19.0.0",
        "react-icons": "^5.5.0",
        "rehype-pretty-code": "^0.14.1",
        "remark-unwrap-images": "^4.0.1",
        "santiu": "file:",
        "sharp": "^0.34.5",
        "shiki": "^3.15.0",
        "tailwind-merge": "^3.0.2"
    },
    "devDependencies": {
        "@eslint/eslintrc": "^3",
        "@tailwindcss/postcss": "^4",
        "@types/node": "^20",
        "@types/react": "^19",
        "@types/react-dom": "^19",
        "autoprefixer": "^10.4.21",
        "eslint": "^9",
        "eslint-config-next": "15.2.1",
        "tailwindcss": "^4",
        "typescript": "^5"
    }
}

styles/globals.css

@import 'tailwindcss';

@layer base {
    :root {
        --background: #ffffff;
        --foreground: #171717;
        --foreground-rgb: 23, 23, 23;
        color-scheme: dark;
    }

    [data-theme='dark'] {
        --background: #171717;
        --foreground: #fff;
        --foreground-rgb: 237, 237, 237;
    }

    html {
        @apply max-h-screen antialiased;
        font-family: var(--font-inter), sans-serif;
        background: var(--background);
        color: var(--foreground);
        transition: background-color 0.3s ease-in-out;
        min-height: 100vh;
        min-height: -webkit-fill-available;
        will-change: background-color;
    }

    * {
        box-sizing: border-box;
        font-family: var(--font-inter), sans-serif;
    }

    body {
        @apply transition-colors duration-300 m-0 p-0;
        min-height: 100vh;
        min-height: -webkit-fill-available;
        font-family: var(--font-inter), sans-serif;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
    }

    /* Code font family */
    code,
    pre {
        font-family: 'SF Mono', 'Monaco', 'Menlo', 'Consolas', 'Liberation Mono',
            'Courier New', monospace !important;
    }

    /* Control tab size */
    pre {
        tab-size: 2;
        -moz-tab-size: 2;
        @apply !px-0 rounded-lg;
    }

    code {
        @apply text-sm md:text-base;
    }

    pre > code {
        counter-reset: line;
    }

    /* Enhanced scrollbar for code blocks */
    pre::-webkit-scrollbar {
        height: 8px;
    }

    pre::-webkit-scrollbar-track {
        background: #161b22;
    }

    pre::-webkit-scrollbar-thumb {
        background: #30363d;
        border-radius: 4px;
    }

    pre::-webkit-scrollbar-thumb:hover {
        background: #484f58;
    }

    .capsize::before {
        content: '';
        margin-bottom: -0.098em;
        display: table;
    }

    .capsize::after {
        content: '';
        margin-top: -0.219em;
        display: table;
    }
}

.font-menlo {
    font-family: var(--font-menlo);
    font-weight: 100;
    text-transform: uppercase;
}

.font-menlo-thin {
    font-family: var(--font-menlo);
    font-weight: 400;
    font-size: 0.825rem;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: rgba(var(--foreground-rgb), 0.55);
    opacity: 0.9;
}

@layer components {
    button {
        @apply select-none;
        -webkit-touch-callout: none;
        -webkit-user-select: none;
        -khtml-user-select: none;
        -moz-user-select: none;
        -ms-user-select: none;
        user-select: none;
        -webkit-tap-highlight-color: transparent;
    }
}

@layer utilities {
    .text-primary {
        color: var(--foreground);
    }
    .text-secondary {
        color: rgba(var(--foreground-rgb), 0.8);
    }
    .text-tertiary {
        color: rgba(var(--foreground-rgb), 0.6);
    }
    .text-quaternary {
        color: rgba(var(--foreground-rgb), 0.4);
    }
}

/* MDX prose container */
.prose-mdx {
    max-width: 78ch;
    margin: 0 auto;
}

.prose {
    @apply max-w-220 sm:text-lg md:text-xl !leading-7 sm:!leading-9;
}

/* ============================================ */
/* REHYPE-PRETTY-CODE THEME SUPPORT */
/* ============================================ */

/* Light/Dark theme handling with Shiki variables */
code[data-theme*=' '],
code[data-theme*=' '] span {
    color: var(--shiki-light);
    background-color: var(--shiki-light-bg);
}

@media (prefers-color-scheme: dark) {
    code[data-theme*=' '],
    code[data-theme*=' '] span {
        color: var(--shiki-dark);
        background-color: var(--shiki-dark-bg);
    }
}

/* ============================================ */
/* CODE BLOCK STRUCTURE */
/* ============================================ */

/* Figure wrapper for code blocks */
figure[data-rehype-pretty-code-figure] {
    @apply shadow-xl mb-6 mt-1 rounded-lg border border-neutral-700 overflow-hidden;
}

/* Pre styling */
figure[data-rehype-pretty-code-figure] pre {
    @apply m-0 p-6 bg-neutral-800/50;
}

/* Main code block styling */
pre [data-line] {
    @apply px-4 border-l-2 border-l-transparent;
    line-height: 1.4; /* Control line spacing here too */
}

/* ============================================ */
/* LINE NUMBERS */
/* ============================================ */

code[data-line-numbers] {
    counter-reset: line;
}

code[data-line-numbers] > [data-line]::before {
    counter-increment: line;
    content: counter(line);
    @apply inline-block w-4 mr-4 text-right text-gray-500;
}

/* ============================================ */
/* HIGHLIGHTING */
/* ============================================ */

/* Highlighted lines */
[data-highlighted-line] {
    background: rgba(200, 200, 255, 0.1);
    @apply border-l-blue-400;
}

/* Highlighted characters/words */
[data-highlighted-chars] {
    @apply bg-zinc-600/50 rounded;
    box-shadow: 0 0 0 4px rgb(82 82 91 / 0.5);
}

/* Custom character highlighting with IDs */
[data-chars-id] {
    @apply shadow-none p-1 border-b-2;
}

[data-chars-id] span {
    @apply !text-inherit;
}

[data-chars-id='v'] {
    @apply !text-pink-300 bg-rose-800/50 border-b-pink-600 font-bold;
}

[data-chars-id='s'] {
    @apply !text-yellow-300 bg-yellow-800/50 border-b-yellow-600 font-bold;
}

[data-chars-id='i'] {
    @apply !text-purple-200 bg-purple-800/50 border-b-purple-600 font-bold;
}

/* ============================================ */
/* CODE BLOCK TITLE */
/* ============================================ */

[data-rehype-pretty-code-title] {
    @apply border-b border-neutral-700/20 bg-neutral-800/50 text-neutral-100 rounded-t-lg py-2 pb-2.5 pl-5 text-sm underline;
}

/* Remove top border radius from pre when title exists */
figure[data-rehype-pretty-code-figure]:has(> [data-rehype-pretty-code-title])
    pre {
    @apply !rounded-t-none;
}

/* ============================================ */
/* INLINE CODE */
/* ============================================ */
/* text-blue-300 */
:not(pre) > code {
    @apply font-mono text-sm bg-neutral-800/70 text-blue-300 px-1.5 py-0.5 rounded border border-neutral-700/50;
}

/* h3 inline code sizing */
h3 code {
    @apply !text-lg md:!text-xl;
}

/* ============================================ */
/* OVERFLOW HANDLING */
/* ============================================ */

pre,
code,
figure {
    @apply overflow-x-auto;
}

/* p {
    @apply text-xl;
}

article p {
    @apply leading-9;
} */

[data-rehype-pretty-code-figure] span {
    font-family: 'SF Mono', 'Monaco', 'Menlo', 'Consolas', 'Liberation Mono',
        'Courier New', monospace !important;
    @apply text-sm;
}

mdx-components.tsx

import type { MDXComponents } from 'mdx/types';
import Link from 'next/link';

export function useMDXComponents(components: MDXComponents): MDXComponents {
    return {
        h1: ({ children }) => (
            <h1 className='text-lg font-bold text-neutral-100 leading-tight'>
                {children}
            </h1>
        ),
        h2: ({ children }) => (
            <h2 className='font-menlo-thin scroll-mt-20 !text-xs mt-30 mb-6 leading-tight'>
                {children}
            </h2>
        ),
        p: ({ children }) => (
            <p className='mb-6 leading-[1.75] text-base md:text-lg text-neutral-300'>
                {children}
            </p>
        ),
        a: ({ href, children }) => {
            if (href?.startsWith('http')) {
                return (
                    <a
                        href={href}
                        className='text-blue-400 underline underline-offset-2 hover:text-blue-300 transition-colors'
                        target='_blank'
                        rel='noopener noreferrer'
                    >
                        {children}
                    </a>
                );
            }
            return (
                <Link
                    href={href || ''}
                    className='text-blue-400 underline underline-offset-2 hover:text-blue-300 transition-colors'
                >
                    {children}
                </Link>
            );
        },
        ul: ({ children }) => (
            <ul className='list-disc list-inside mb-6 space-y-2 text-base md:text-lg text-neutral-300'>
                {children}
            </ul>
        ),
        ol: ({ children }) => (
            <ol className='list-decimal list-inside mb-6 space-y-2 text-base md:text-lg text-neutral-300'>
                {children}
            </ol>
        ),
        li: ({ children }) => <li className='leading-[1.75]'>{children}</li>,
        blockquote: ({ children }) => (
            <blockquote className='border-l-4 border-neutral-600 pl-6 py-2 my-6 italic text-neutral-400'>
                {children}
            </blockquote>
        ),
        img: (props) => (
            <figure className='my-12 -mx-4 md:-mx-12 lg:-mx-24'>
                <img
                    {...props}
                    className='w-full h-auto rounded-none md:rounded-lg shadow-2xl'
                    loading='lazy'
                />
                {props.alt && (
                    <figcaption className='text-center text-xs text-neutral-500 mt-4 px-4'>
                        {props.alt}
                    </figcaption>
                )}
            </figure>
        ),
        hr: () => <hr className='my-12 border-t border-neutral-800' />,
        ...components,
    };
}

Solution

  • Unfortunately the only fix I found was adding this to my next.config.mjs

    webpack: (config) => {
        config.optimization.minimize = false;
        return config;
    },
    

    And the code runs properly with no build errors, or console errors or warnings. I don't know what causes this settings being turned on, to not function.