<p dir="auto">I don't really know how to classify this. Maybe just something that should be documented.</p>
<p dir="auto">I have a <code class="notranslate">postcss.config.js</code> file that returns a function (i.e takes <code class="notranslate">ctx</code> as a parameter, returns a config object). That works fine in Next.js 9.1. But in 9.2, I get this error:</p>
<blockquote>
<p dir="auto">Error: Your custom PostCSS configuration may not export a function. Please export a plain object instead.</p>
<p dir="auto">at getPostCssPlugins (/path/to/node_modules/next/dist/build/webpack/config/blocks/css/plugins.js:4:296)</p>
</blockquote>
<p dir="auto">Looking at the code for <code class="notranslate">plugin.ts</code>, this is obviously intentional behavior. I tried to find this breaking change documented somewhere (e.g. release notes, blog posts, etc.), but didn't find anything (the pull request with the change does provide some context, but it's hard to find). I get it, new version, things change. So possibly the only resolution to this is to document it somewhere in the release notes, maybe explain why it's changed.</p>
<p dir="auto">If it's helpful, this is what my postcss.config.js file looked like before. Granted, what I'm using <code class="notranslate">ctx</code> for is probably unnecessary (sourcemaps seem to be generated just fine using Next.js default behavior). So it's not a big deal for me to get rid of it.</p>
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="module.exports = ctx =&gt; ({
    map: ctx.env === 'production' ? false : {inline: true},
    plugins: {
        'postcss-import': {
            resolve: createResolver({
                alias: {
                    '@css': path.resolve(__dirname, 'client/style'),
                    '@client': path.resolve(__dirname, 'client'),
                },
            }),
        },
        'postcss-rem': {
            baseline: 16,
            fallback: false,
            precision: 5,
        },
        'postcss-mixins': {},
        'postcss-simple-vars': {},
        'postcss-nested-ancestors': {},
        'postcss-nested': {},
        'postcss-custom-media': {},
        'postcss-calc': {mediaQueries: true},
        'postcss-preset-env': {
            stage: 0,
            browsers: 'last 2 versions',
        },
        'cssnano': {
            zindex: false,
            // autoprefixer is included in postcss-preset-env, so no need to do it again in nano
            autoprefixer: false,
            discardComments: {removeAll: true}
        },
        'postcss-reporter': {clearReportedMessages: true},
    }
})"><pre class="notranslate"><span class="pl-smi">module</span><span class="pl-kos">.</span><span class="pl-en">exports</span> <span class="pl-c1">=</span> <span class="pl-s1">ctx</span> <span class="pl-c1">=&gt;</span> <span class="pl-kos">(</span><span class="pl-kos">{</span>
    <span class="pl-c1">map</span>: <span class="pl-s1">ctx</span><span class="pl-kos">.</span><span class="pl-c1">env</span> <span class="pl-c1">===</span> <span class="pl-s">'production'</span> ? <span class="pl-c1">false</span> : <span class="pl-kos">{</span><span class="pl-c1">inline</span>: <span class="pl-c1">true</span><span class="pl-kos">}</span><span class="pl-kos">,</span>
    <span class="pl-c1">plugins</span>: <span class="pl-kos">{</span>
        <span class="pl-s">'postcss-import'</span>: <span class="pl-kos">{</span>
            <span class="pl-c1">resolve</span>: <span class="pl-en">createResolver</span><span class="pl-kos">(</span><span class="pl-kos">{</span>
                <span class="pl-c1">alias</span>: <span class="pl-kos">{</span>
                    <span class="pl-s">'@css'</span>: <span class="pl-s1">path</span><span class="pl-kos">.</span><span class="pl-en">resolve</span><span class="pl-kos">(</span><span class="pl-s1">__dirname</span><span class="pl-kos">,</span> <span class="pl-s">'client/style'</span><span class="pl-kos">)</span><span class="pl-kos">,</span>
                    <span class="pl-s">'@client'</span>: <span class="pl-s1">path</span><span class="pl-kos">.</span><span class="pl-en">resolve</span><span class="pl-kos">(</span><span class="pl-s1">__dirname</span><span class="pl-kos">,</span> <span class="pl-s">'client'</span><span class="pl-kos">)</span><span class="pl-kos">,</span>
                <span class="pl-kos">}</span><span class="pl-kos">,</span>
            <span class="pl-kos">}</span><span class="pl-kos">)</span><span class="pl-kos">,</span>
        <span class="pl-kos">}</span><span class="pl-kos">,</span>
        <span class="pl-s">'postcss-rem'</span>: <span class="pl-kos">{</span>
            <span class="pl-c1">baseline</span>: <span class="pl-c1">16</span><span class="pl-kos">,</span>
            <span class="pl-c1">fallback</span>: <span class="pl-c1">false</span><span class="pl-kos">,</span>
            <span class="pl-c1">precision</span>: <span class="pl-c1">5</span><span class="pl-kos">,</span>
        <span class="pl-kos">}</span><span class="pl-kos">,</span>
        <span class="pl-s">'postcss-mixins'</span>: <span class="pl-kos">{</span><span class="pl-kos">}</span><span class="pl-kos">,</span>
        <span class="pl-s">'postcss-simple-vars'</span>: <span class="pl-kos">{</span><span class="pl-kos">}</span><span class="pl-kos">,</span>
        <span class="pl-s">'postcss-nested-ancestors'</span>: <span class="pl-kos">{</span><span class="pl-kos">}</span><span class="pl-kos">,</span>
        <span class="pl-s">'postcss-nested'</span>: <span class="pl-kos">{</span><span class="pl-kos">}</span><span class="pl-kos">,</span>
        <span class="pl-s">'postcss-custom-media'</span>: <span class="pl-kos">{</span><span class="pl-kos">}</span><span class="pl-kos">,</span>
        <span class="pl-s">'postcss-calc'</span>: <span class="pl-kos">{</span><span class="pl-c1">mediaQueries</span>: <span class="pl-c1">true</span><span class="pl-kos">}</span><span class="pl-kos">,</span>
        <span class="pl-s">'postcss-preset-env'</span>: <span class="pl-kos">{</span>
            <span class="pl-c1">stage</span>: <span class="pl-c1">0</span><span class="pl-kos">,</span>
            <span class="pl-c1">browsers</span>: <span class="pl-s">'last 2 versions'</span><span class="pl-kos">,</span>
        <span class="pl-kos">}</span><span class="pl-kos">,</span>
        <span class="pl-s">'cssnano'</span>: <span class="pl-kos">{</span>
            <span class="pl-c1">zindex</span>: <span class="pl-c1">false</span><span class="pl-kos">,</span>
            <span class="pl-c">// autoprefixer is included in postcss-preset-env, so no need to do it again in nano</span>
            <span class="pl-c1">autoprefixer</span>: <span class="pl-c1">false</span><span class="pl-kos">,</span>
            <span class="pl-c1">discardComments</span>: <span class="pl-kos">{</span><span class="pl-c1">removeAll</span>: <span class="pl-c1">true</span><span class="pl-kos">}</span>
        <span class="pl-kos">}</span><span class="pl-kos">,</span>
        <span class="pl-s">'postcss-reporter'</span>: <span class="pl-kos">{</span><span class="pl-c1">clearReportedMessages</span>: <span class="pl-c1">true</span><span class="pl-kos">}</span><span class="pl-kos">,</span>
    <span class="pl-kos">}</span>
<span class="pl-kos">}</span><span class="pl-kos">)</span></pre></div>
<h2 dir="auto">System information</h2>
<ul dir="auto">
<li>OS: MacOS</li>
<li>Browser (if applies): NA</li>
<li>Version of Next.js: 9.2</li>
</ul>
<h1 dir="auto">Bug report</h1>
<h2 dir="auto">Describe the bug</h2>
<p dir="auto">With the introduction of the <em>marvelous</em> <a href="https://github.com/zeit/next.js/issues/8626" data-hovercard-type="issue" data-hovercard-url="/vercel/next.js/issues/8626/hovercard">Built-in CSS Support</a>, a new non-standard <a href="https://github.com/zeit/next.js/pull/9704" data-hovercard-type="pull_request" data-hovercard-url="/vercel/next.js/pull/9704/hovercard">PostCSS Config</a> plugin syntax was introduced.</p>
<p dir="auto">This syntax requires that plugins listed in <code class="notranslate">postcss.config.js</code> be <strong>strings</strong> rather than <strong><code class="notranslate">require</code> statements</strong>. This breaks backwards compatibility and differs from the standard set by <a href="https://github.com/michael-ciniawsky/postcss-load-config#array">existing PostCSS documentation</a>.</p>
<p dir="auto">Additionally, this makes it tough (or impossible) to share a common PostCSS configuration with additional tools like Storybook.js.</p>
<h2 dir="auto">To Reproduce</h2>
<p dir="auto">Steps to reproduce the behavior, please provide code snippets or a repository:</p>
<ol dir="auto">
<li><code class="notranslate">yarn add -D tailwindcss autoprefixer</code></li>
<li>Add the following <code class="notranslate">postcss.config.js</code>:</li>
</ol>
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="module.exports = {
  plugins: [
    require(&quot;tailwindcss&quot;),
    require(&quot;autoprefixer&quot;)
  ]
};"><pre class="notranslate"><span class="pl-smi">module</span><span class="pl-kos">.</span><span class="pl-c1">exports</span> <span class="pl-c1">=</span> <span class="pl-kos">{</span>
  <span class="pl-c1">plugins</span>: <span class="pl-kos">[</span>
    <span class="pl-en">require</span><span class="pl-kos">(</span><span class="pl-s">"tailwindcss"</span><span class="pl-kos">)</span><span class="pl-kos">,</span>
    <span class="pl-en">require</span><span class="pl-kos">(</span><span class="pl-s">"autoprefixer"</span><span class="pl-kos">)</span>
  <span class="pl-kos">]</span>
<span class="pl-kos">}</span><span class="pl-kos">;</span></pre></div>
<ol start="3" dir="auto">
<li><code class="notranslate">yarn next build</code></li>
</ol>
<h2 dir="auto">Expected behavior</h2>
<ul dir="auto">
<li>The build succeeds, and either plugins imported with <code class="notranslate">require()</code> are supported</li>
<li>Or the build fails, and a more descriptive error is shown in the build log.</li>
</ul>
<h2 dir="auto">Screenshots</h2>
<p dir="auto">The following is shown when running <code class="notranslate">next build</code>:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="Error: An unknown PostCSS plugin was provided (function creator() {
--
02:32:09 PM | var transformer = initializer.apply(void 0, arguments);
02:32:09 PM | transformer.postcssPlugin = name;
02:32:09 PM | transformer.postcssVersion = new _processor.default().version;
02:32:09 PM | return transformer;
02:32:09 PM | }).
02:32:09 PM | &gt; Build error occurred"><pre class="notranslate"><code class="notranslate">Error: An unknown PostCSS plugin was provided (function creator() {
--
02:32:09 PM | var transformer = initializer.apply(void 0, arguments);
02:32:09 PM | transformer.postcssPlugin = name;
02:32:09 PM | transformer.postcssVersion = new _processor.default().version;
02:32:09 PM | return transformer;
02:32:09 PM | }).
02:32:09 PM | &gt; Build error occurred
</code></pre></div>
<h2 dir="auto">System information</h2>
<ul dir="auto">
<li>OS: macOS</li>
<li>Version of Next.js: 9.2</li>
</ul>
<h2 dir="auto">Additional context</h2>
<p dir="auto">This work was likely done to be able to identify, dedupe, and acknowledge Next's default PostCSS plugins — and it's likely the solution to this issue is nothing more than better documentation.</p>
<p dir="auto">Here are a couple proposed solutions:</p>
<h3 dir="auto">1. Support <code class="notranslate">require()</code> statements in PostCSS config</h3>
<p dir="auto">I'd rely on <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/Timer/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/Timer">@Timer</a>'s knowledge here, as there are likely other consequences (notably: not being able to inspect and dedupe plugins).</p>
<h3 dir="auto">2. Display a warning/error that indicates deprecated behavior</h3>
<p dir="auto">This would be a simple check in <code class="notranslate">packages/next/build/webpack/config/blocks/css/plugins.ts</code> to see if the plugin is a function:</p>
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="else if (typeof plugin === 'function') {
  console.error(
    `${chalk.red.bold(
      'Error'
    )}: A PostCSS Plugin must be provided as a ${chalk.bold(
      'string'
    )}. Instead, we got a function imported with require().`
  )
}"><pre class="notranslate"><span class="pl-s1">else</span> <span class="pl-k">if</span> <span class="pl-kos">(</span><span class="pl-k">typeof</span> <span class="pl-s1">plugin</span> <span class="pl-c1">===</span> <span class="pl-s">'function'</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
  <span class="pl-smi">console</span><span class="pl-kos">.</span><span class="pl-en">error</span><span class="pl-kos">(</span>
    <span class="pl-s">`<span class="pl-s1"><span class="pl-kos">${</span><span class="pl-s1">chalk</span><span class="pl-kos">.</span><span class="pl-c1">red</span><span class="pl-kos">.</span><span class="pl-en">bold</span><span class="pl-kos">(</span></span></span>
<span class="pl-s"><span class="pl-s1">      <span class="pl-s">'Error'</span></span></span>
<span class="pl-s"><span class="pl-s1">    <span class="pl-kos">)</span><span class="pl-kos">}</span></span>: A PostCSS Plugin must be provided as a <span class="pl-s1"><span class="pl-kos">${</span><span class="pl-s1">chalk</span><span class="pl-kos">.</span><span class="pl-en">bold</span><span class="pl-kos">(</span></span></span>
<span class="pl-s"><span class="pl-s1">      <span class="pl-s">'string'</span></span></span>
<span class="pl-s"><span class="pl-s1">    <span class="pl-kos">)</span><span class="pl-kos">}</span></span>. Instead, we got a function imported with require().`</span>
  <span class="pl-kos">)</span>
<span class="pl-kos">}</span></pre></div>
<p dir="auto">I'm happy to submit PRs for either of these solutions! But looking for guidance from the core team.</p>