next-compose-plugins: Incompatible with next.js 12.2.0: "Invalid next.config.js options detected"

When compiling with next.js 12.2.0, this warning is shown, even when an empty array is passed for the plugins parameter:

warn  - Invalid next.config.js options detected: 
[
  {
    "instancePath": "",
    "schemaPath": "#/additionalProperties",
    "keyword": "additionalProperties",
    "params": {
      "additionalProperty": "webpackDevMiddleware"
    },
    "message": "must NOT have additional properties"
  },
  {
    "instancePath": "",
    "schemaPath": "#/additionalProperties",
    "keyword": "additionalProperties",
    "params": {
      "additionalProperty": "configOrigin"
    },
    "message": "must NOT have additional properties"
  },
  {
    "instancePath": "",
    "schemaPath": "#/additionalProperties",
    "keyword": "additionalProperties",
    "params": {
      "additionalProperty": "target"
    },
    "message": "must NOT have additional properties"
  },
  {
    "instancePath": "",
    "schemaPath": "#/additionalProperties",
    "keyword": "additionalProperties",
    "params": {
      "additionalProperty": "analyticsId"
    },
    "message": "must NOT have additional properties"
  },
  {
    "instancePath": "",
    "schemaPath": "#/additionalProperties",
    "keyword": "additionalProperties",
    "params": {
      "additionalProperty": "webpack5"
    },
    "message": "must NOT have additional properties"
  },
  {
    "instancePath": "/amp/canonicalBase",
    "schemaPath": "#/properties/amp/properties/canonicalBase/minLength",
    "keyword": "minLength",
    "params": {
      "limit": 1
    },
    "message": "must NOT have fewer than 1 characters"
  },
  {
    "instancePath": "/basePath",
    "schemaPath": "#/properties/basePath/minLength",
    "keyword": "minLength",
    "params": {
      "limit": 1
    },
    "message": "must NOT have fewer than 1 characters"
  },
  {
    "instancePath": "/generateEtags",
    "schemaPath": "#/properties/generateEtags/isFunction",
    "keyword": "isFunction",
    "params": {},
    "message": "must pass \"isFunction\" keyword validation"
  },
  {
    "instancePath": "/i18n",
    "schemaPath": "#/properties/i18n/type",
    "keyword": "type",
    "params": {
      "type": "object"
    },
    "message": "must be object"
  },
  {
    "instancePath": "/webpack",
    "schemaPath": "#/properties/webpack/isFunction",
    "keyword": "isFunction",
    "params": {},
    "message": "must pass \"isFunction\" keyword validation"
  }
] 

About this issue

  • Original URL
  • State: open
  • Created 2 years ago
  • Reactions: 60
  • Comments: 26

Commits related to this issue

Most upvoted comments

I ditched next-compose-plugins since it’s unmaintained and replaced it with these lines:

module.exports = (_phase, { defaultConfig }) => {
    const plugins = [withStaticImport, withBundleAnalyzer, withCustomWebpack]
    return plugins.reduce((acc, plugin) => plugin(acc), { ...defaultConfig, ...config })
}

I ditched since it’s unmaintained and replaced it with these lines:next-compose-plugins

module.exports = (_phase, { defaultConfig }) => {
    const plugins = [withStaticImport, withBundleAnalyzer, withCustomWebpack]
    return plugins.reduce((acc, plugin) => plugin(acc), { ...defaultConfig, ...config })
}

Hey @fabb… using your example, I see the same warning message:

warn  - Invalid next.config.js options detected: 
[
  {
    "instancePath": "",
    "schemaPath": "#/additionalProperties",
    "keyword": "additionalProperties",
    "params": {
      "additionalProperty": "webpackDevMiddleware"
    },
    "message": "must NOT have additional properties"
  },

...

I faced the same problem, but it worked fine by removing defaultConfig.

/** @type {import('next').NextConfig} */
const nextConfig = {
  // ...
};

module.exports = (_phase, { defaultConfig }) => {
    const plugins = [/* ... */]
    return plugins.reduce((acc, plugin) => plugin(acc), { ...nextConfig })
}

Used Fix: @meiblorn @comment.

From:

const withPlugins = require('next-compose-plugins');
const nextTranslate = require('next-translate');

const nextConfig = {
  reactStrictMode: true,
};

module.exports = withPlugins([[nextTranslate]], nextConfig);

To:

const withPlugins = require('next-compose-plugins');
const nextTranslate = require('next-translate');

const nextConfig = {
  reactStrictMode: true,
};

module.exports = async (phase) => withPlugins([nextTranslate], nextConfig)(phase, { undefined });

Just ignore default config. Seems, its NextJS related issue.

Even using defaultConfig as is throws an error:

module.exports = async (phase, { defaultConfig }) => {
    return defaultConfig
};

Hence, its not a next-compose-plugins package error.

So, as a workaround, use it like below (just ignore defaultConfig existence):

module.exports = async (phase) => {
    /** @type {import('next').NextConfig} */
    const nextConfig = {
        reactStrictMode: true,
        swcMinify: true
    };

    const defaultConfig = {}
    return withPlugins([], nextConfig)(phase, { defaultConfig });
    // return withPlugins([], nextConfig)(phase, { undefined }); // also works
};

If you want to keep the default config you could do the following:

module.exports = async (phase, { defaultConfig }) => 
    withPlugins([nextTranslate], nextConfig)(phase, { ...defaultConfig, ...nextConfig });

I am using next-compose-plugins with next-pwa, @sentry/nextjs and @next/bundle-analyzer and I get similar warnings

const nextPlugins = [
  [withBundleAnalyzer],
  [
    withPWA,
    {
      pwa: {
        dest: 'public',
        register: true,
        skipWaiting: true,
        disable: process.env.NODE_ENV === 'development',
        buildExcludes: [/middleware-manifest.json$/],
      },
    },
  ],
  (nextConfig) => withSentryConfig(nextConfig, sentryWebpackPluginOptions),
];
...
module.exports = withPlugins(nextPlugins, nextConfig);

I ditched next-compose-plugins since it’s unmaintained and replaced it with these lines:

module.exports = (_phase, { defaultConfig }) => {
    const plugins = [withStaticImport, withBundleAnalyzer, withCustomWebpack]
    return plugins.reduce((acc, plugin) => plugin(acc), { ...defaultConfig, ...config })
}

why i got like this

image

with config

const withPWA = require('next-pwa');

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: false,
  swcMinify: true,
  compiler: {
    removeConsole: true,
  },
};

module.exports = (_phase, { defaultConfig }) => {
  const plugins = [
    withPWA({
      pwa: {
        dest: 'public',
        disable: process.env.NODE_ENV === 'development',
        register: true,
      },
    }),
  ];
  return plugins.reduce((acc, plugin) => plugin(acc), { ...defaultConfig, ...nextConfig });
};

For anyone looking for a full next.config.js solution that removes the warnings, here’s what I use:

/* eslint-disable @typescript-eslint/no-var-requires */
const CircularDependencyPlugin = require('circular-dependency-plugin')
const withPWA = require('next-pwa')({
  dest: 'public',
  disable: process.env.NODE_ENV === 'development',
})

const plugins = []

if (process.env.ANALYZE === 'true') {
  // only load dependency if env `ANALYZE` was set
  const withBundleAnalyzer = require('@next/bundle-analyzer')({
    enabled: true,
  })

  plugins.push(withBundleAnalyzer)
}

plugins.push(withPWA)

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  webpack: (config) => {
    config.plugins.push(
      new CircularDependencyPlugin({
        exclude: /a\.js|node_modules/,
        include: /src/,
        failOnError: true,
        allowAsyncCycles: false,
        cwd: process.cwd(),
      }),
    )

    return config
  },
}

module.exports = () => plugins.reduce((acc, next) => next(acc), nextConfig)

Just ignore default config. Seems, its NextJS related issue.

Even using defaultConfig as is throws an error:

module.exports = async (phase, { defaultConfig }) => {
    return defaultConfig
};

Hence, its not a next-compose-plugins package error.

So, as a workaround, use it like below (just ignore defaultConfig existence):

module.exports = async (phase) => {
    /** @type {import('next').NextConfig} */
    const nextConfig = {
        reactStrictMode: true,
        swcMinify: true
    };

    const defaultConfig = {}
    return withPlugins([], nextConfig)(phase, { defaultConfig });
    // return withPlugins([], nextConfig)(phase, { undefined }); // also works
};

What you can do instead is:

import nextComposePlugins from 'next-compose-plugins'
const { withPlugins } = nextComposePlugins.extend(() => ({}))

and then as before

const config = withPlugins(
  [...],
  nextConfig
)

export default config

I think all of the codes above make the things complex, you can just use one reduce line for composing all the plugins.

const bundleAnalyzer = require('@next/bundle-analyzer')({
  enabled: process.env.ANALYZE === 'true'
});

const nextTranslate = require('next-translate');

// Compose all the plugins one by one.
const plugins = [bundleAnalyzer, nextTranslate];

/** @type {import('next').NextConfig} */
const bookstairsConfig = {
  reactStrictMode: true,
};

module.exports = plugins.reduce((config, plugin) => plugin(config), bookstairsConfig)

For anyone who switched to @gendaineko2222’s suggested solution (like I did) and ran into a TypeError: plugin is not a function when passing a plugin as an array with some plugin-specific configuration (as outline here: https://github.com/cyrilwanner/next-compose-plugins/#configuration-object), this is the change that I had to make to handle that case:

module.exports = (_phase, { defaultConfig }) => {
  return plugins.reduce(
    (acc, plugin) => {
      if (Array.isArray(plugin)) {
        return plugin[0](acc, plugin[1]);
      }
      return plugin(acc);
    },
    { ...nextConfig }
  );
};

I ditched next-compose-plugins since it’s unmaintained and replaced it with these lines:

module.exports = (_phase, { defaultConfig }) => {
    const plugins = [withStaticImport, withBundleAnalyzer, withCustomWebpack]
    return plugins.reduce((acc, plugin) => plugin(acc), { ...defaultConfig, ...config })
}

For those using the .reduce() strategy, please be aware that the return value of plugin(acc) isn’t always a function (depending on the plugin you use: https://github.com/getsentry/sentry-javascript/issues/6447#issuecomment-1340671081

I’d suggest the following edit:

const bundleAnalyzer = require(https://github.com/vercel/next.js/tree/master/packages/next-bundle-analyzer)({
  enabled: process.env.ANALYZE === 'true'
});

module.exports = (phase, defaultConfig) => {
  const plugins = [
    // other plugins
    bundleAnalyzer,
    (config) => withSentryConfig(config, sentryWebpackPluginOptions),
    // other plugins
  ];

  const config = plugins.reduce((acc, plugin) => {
    const update = plugin(acc);
    return typeof update === "function" ? update(phase, defaultConfig) : update;
  }, { ...nextConfig });

  return config;
};

I think all of the codes above make the things complex, you can just use one reduce line for composing all the plugins.

const bundleAnalyzer = require('@next/bundle-analyzer')({
  enabled: process.env.ANALYZE === 'true'
});

const nextTranslate = require('next-translate');

// Compose all the plugins one by one.
const plugins = [bundleAnalyzer, nextTranslate];

/** @type {import('next').NextConfig} */
const bookstairsConfig = {
  reactStrictMode: true,
};

module.exports = plugins.reduce((config, plugin) => plugin(config), bookstairsConfig)

it keeps telling me TypeError : plugin is not a function

i got fixing config next-pwa (v 5.5.5) with this config

const withPWA = require('next-pwa');

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  swcMinify: true,
  compiler: {
    removeConsole: process.env.NODE_ENV !== 'development',
  },
  pwa: {
    dest: 'public',
    disable: process.env.NODE_ENV === 'development',
    register: true,
  },
};

module.exports = () => {
  const plugins = [withPWA];
  const config = plugins.reduce((acc, next) => next(acc), {
    ...nextConfig,
  });
  return config;
};

but i go twice print [PWA] Compile server like this

image

or [PWA] PWA support is disabled

image

I’m using next-pwa, @sentry/nextjs and @next/bundle-analyzer . Did this without next-compose-plugins

module.exports = withSentryConfig(withPWA(withBundleAnalyzer(yourNextConfig)), { silent: true });

Works fine 👍🏻

I ditched next-compose-plugins since it’s unmaintained and replaced it with these lines:

module.exports = (_phase, { defaultConfig }) => {
    const plugins = [withStaticImport, withBundleAnalyzer, withCustomWebpack]
    return plugins.reduce((acc, plugin) => plugin(acc), { ...defaultConfig, ...config })
}

I was still seeing the warnings after this change because of what was in defaultConfig. This workaround resolved them for me, and still maintained the defaultConfig usage. Still seeing the warning about .assetPrefix as it errors if you remove the field.

module.exports = (_phase, { defaultConfig }) => {
    // Workaround
    delete defaultConfig.webpackDevMiddleware;
    delete defaultConfig.configOrigin;
    delete defaultConfig.target;
    delete defaultConfig.webpack5;
    delete defaultConfig.amp.canonicalBase;
    delete defaultConfig.experimental.outputFileTracingRoot;
    delete defaultConfig.i18n;

    const plugins = [withStaticImport, withBundleAnalyzer, withCustomWebpack]
    return plugins.reduce((acc, plugin) => plugin(acc), { ...defaultConfig, ...config })
}

Related issue: https://github.com/vercel/next.js/issues/39161

I don’t understand, I don’t have webpack customisations but I get lots of warnings:

/** @type {import('next').NextConfig} */
const config = {
  reactStrictMode: true,
};

const withBundleAnalyzer = require('@next/bundle-analyzer')({
  enabled: process.env.ANALYZE === 'true',
});

const withMDX = require('@next/mdx')({
  extension: /\.mdx?$/,
  pageExtensions: ['js', 'jsx', 'ts', 'tsx', 'mdx'],
});

const plugins = [withBundleAnalyzer, withMDX];

module.exports = (_phase, { defaultConfig }) => {
  const finalConfig = { ...defaultConfig, config };
  plugins.forEach(plugin => plugin(finalConfig));
  return finalConfig;
}

Next yells about:

$ next dev -p 80
ready - started server on 0.0.0.0:80, url: http://localhost:80
info  - Loaded env from /Users/damians/Desktop/webapp/.env
warn  - Invalid next.config.js options detected:
[
  {
    "instancePath": "",
    "schemaPath": "#/additionalProperties",
    "keyword": "additionalProperties",
    "params": {
      "additionalProperty": "webpackDevMiddleware"
    },
    "message": "must NOT have additional properties"
  },
  {
    "instancePath": "",
    "schemaPath": "#/additionalProperties",
    "keyword": "additionalProperties",
    "params": {
      "additionalProperty": "configOrigin"
    },
    "message": "must NOT have additional properties"
  },
  {
    "instancePath": "",
    "schemaPath": "#/additionalProperties",
    "keyword": "additionalProperties",
    "params": {
      "additionalProperty": "target"
    },
    "message": "must NOT have additional properties"
  },
  {
    "instancePath": "",
    "schemaPath": "#/additionalProperties",
    "keyword": "additionalProperties",
    "params": {
      "additionalProperty": "analyticsId"
    },
    "message": "must NOT have additional properties"
  },
  {
    "instancePath": "",
    "schemaPath": "#/additionalProperties",
    "keyword": "additionalProperties",
    "params": {
      "additionalProperty": "webpack5"
    },
    "message": "must NOT have additional properties"
  },
  {
    "instancePath": "",
    "schemaPath": "#/additionalProperties",
    "keyword": "additionalProperties",
    "params": {
      "additionalProperty": "config"
    },
    "message": "must NOT have additional properties"
  },
  {
    "instancePath": "/amp/canonicalBase",
    "schemaPath": "#/properties/amp/properties/canonicalBase/minLength",
    "keyword": "minLength",
    "params": {
      "limit": 1
    },
    "message": "must NOT have fewer than 1 characters"
  },
  {
    "instancePath": "/assetPrefix",
    "schemaPath": "#/properties/assetPrefix/minLength",
    "keyword": "minLength",
    "params": {
      "limit": 1
    },
    "message": "must NOT have fewer than 1 characters"
  },
  {
    "instancePath": "/basePath",
    "schemaPath": "#/properties/basePath/minLength",
    "keyword": "minLength",
    "params": {
      "limit": 1
    },
    "message": "must NOT have fewer than 1 characters"
  },
  {
    "instancePath": "/experimental/outputFileTracingRoot",
    "schemaPath": "#/properties/experimental/properties/outputFileTracingRoot/minLength",
    "keyword": "minLength",
    "params": {
      "limit": 1
    },
    "message": "must NOT have fewer than 1 characters"
  },
  {
    "instancePath": "/generateEtags",
    "schemaPath": "#/properties/generateEtags/isFunction",
    "keyword": "isFunction",
    "params": {},
    "message": "must pass \"isFunction\" keyword validation"
  },
  {
    "instancePath": "/i18n",
    "schemaPath": "#/properties/i18n/type",
    "keyword": "type",
    "params": {
      "type": "object"
    },
    "message": "must be object"
  },
  {
    "instancePath": "/webpack",
    "schemaPath": "#/properties/webpack/isFunction",
    "keyword": "isFunction",
    "params": {},
    "message": "must pass \"isFunction\" keyword validation"
  }
]
See more info here: https://nextjs.org/docs/messages/invalid-next-config

For me the warnings are gone, so I guess one of your plugins or your config sets webpackDevMiddleware etc. somewhere?

Just spread them too.