cyrilwanner / next-compose-plugins

💡next-compose-plugins provides a cleaner API for enabling and configuring plugins for next.js
MIT License
736 stars 12 forks source link

Incompatible with next.js 12.2.0: "Invalid next.config.js options detected" #59

Open fabb opened 2 years ago

fabb commented 2 years ago

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"
  }
] 
theabdulmateen commented 2 years ago

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);
fabb commented 2 years ago

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 })
}
jordanlambrecht commented 2 years ago

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 })
}

How would you use the extend feature with your method? I have a ton of redirects so I put them in their own separate config file.

fabb commented 2 years ago

Just spread them too.

brenopolanski commented 2 years ago

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 })
}

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"
  },

...
fabb commented 2 years ago

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

brenopolanski commented 2 years ago

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

Strange... i am just using the withBundleAnalyzer plugin for now.

In my tests I tried this code below:

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

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

module.exports = (_phase, { defaultConfig }) => {
    const plugins = [withBundleAnalyzer]
    return plugins.reduce((acc, plugin) => plugin(acc), { ...defaultConfig, ...nextConfig })
}
shifenhutu commented 2 years ago

related issue https://github.com/vercel/next.js/issues/38909

d-string commented 2 years ago

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 });
};
damianobarbati commented 2 years ago

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
d-string commented 2 years ago

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

gendaineko2222 commented 2 years ago

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 })
}
vsfedorenko commented 2 years ago

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
};
raphaelsaunier commented 2 years ago

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 }
  );
};
brenopolanski commented 2 years ago

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)
ahmadyusri commented 1 year ago

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

This work for me

dmitriy-baltak commented 1 year ago

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
jakeorr commented 1 year ago

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

LaCocoRoco commented 1 year ago

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 });
rubek-joshi commented 1 year ago

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

module.exports = async (phase, { defaultConfig }) => 
    withPlugins([nextTranslate], nextConfig)(phase, { ...defaultConfig, ...nextConfig });
syhily commented 1 year ago

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)
JalalMitali commented 1 year ago

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

nghiepdev commented 1 year ago

I'm using next-pwa, @sentry/nextjs and @next/bundle-analyzer and completely removed next-compose-plugins. No warnings and works with @sentry/nextjs

const {PHASE_PRODUCTION_BUILD} = require('next/constants');

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

module.exports = phase => {
  const plugins = [];

  if (phase === PHASE_PRODUCTION_BUILD) {
    if (process.env.ANALYZE === '1') {
      plugins.push(
        require('@next/bundle-analyzer')({
          enabled: true
        }),
      );
    }

    plugins.push(
      require('next-pwa')({
        // your config
      }),
    );

    plugins.push(nextConfig =>
      require('@sentry/nextjs').withSentryConfig(
        {
          ...nextConfig,
          sentry: {
             // your config
          },
        },
        {silent: true},
      )(phase, {nextConfig}),
    );
  }

  return plugins.reduce((acc, plugin) => plugin(acc), nextConfig);
};
dalalRohit commented 1 year ago

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 👍🏻

kylemh commented 1 year ago

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;
};
dangvanduc1999gspit commented 1 year ago

I have the same issue, but i cannot find out anything about target

image image