npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

next-recompose-plugins

v3.0.0

Published

[![npm](https://img.shields.io/npm/v/next-recompose-plugins?style=for-the-badge&color=blue)](https://www.npmjs.com/package/next-recompose-plugins) [![license](https://img.shields.io/badge/license-mit-purple?style=for-the-badge)](./LICENSE)

Downloads

3,120

Readme

next-recompose-plugins

npm license

The default way Next.js suggests to enable and configure plugins is unclear and confusing when you have many plugins. Sometimes we even use plugins that do not have a standardized API, so our code becomes even more obscure.

In order to address that next-recompose-plugins provides a clean 🫧 and easyAPI for Next.js's plugins configuration and composition.

Table of contents

Quick showcase

For those who don't like to read a lot.

const config = new Config(async () => {
    await something();
  
    return {...};
  })
  .applyPlugin((phase, args, config) => {
    return plugin1(config);
  }, 'plugin-1')
  .applyPlugin((phase, args, config) => {
    return plugin2(config);
  }, 'plugin-2')
  .applyPlugin((phase, args, config) => {
    return plugin3(config);
  }, 'plugin-3')
  .build();

Also make sure to see Real world example

Installation

npm install --save next-recompose-plugins

or

yarn add next-recompose-plugins

Building basic configuration

Pass config object directly to Config class constructor:

// next.config.js
const {Config} = require('next-recompose-plugins');

module.exports = new Config({
    // Next.js's config options goes here ...
  })
  .build();

You can also use a function. This way you have possibility to define different options based on current phase or args provided by Next.

// next.config.js
const {Config} = require('next-recompose-plugins');

module.exports = new Config((phase, args) => {
    return {
      reactStrictMode: true,
      experimental: {},
    };
  })
  .build();

Configuration function can also be async (supported in Next.js 12.1+).

// next.config.js
const {Config} = require('next-recompose-plugins');

module.exports = new Config(async (phase, args) => {
    await something();

    return {
      reactStrictMode: true,
      experimental: {},
    };
  })
  .build();

Applying plugins

Let's apply @next/bundle-analyzer plugin by chaining applyPlugin method. Pass a function to applyPlugin(...) method which accepts the following arguments:

  • phase - current configuration phase as one of the constants of next/constants;
  • args - args provided by Next;
  • config - the source config object.

Important: Your plugin applying function should take the config object, (optionally) enhance it using a desired plugin then return the object back!

// next.config.js
const {Config} = require('next-recompose-plugins');
const withBundleAnalyzer = require('@next/bundle-analyzer');

module.exports = new Config({...})
  .applyPlugin((phase, args, config) => {
    // enhance the config with the desired plugin and return it back  
    return withBundleAnalyzer({})(config);
  })
  .build();

Let's say that you want to apply the plugin only in production build, you can do this easily this way:

// next.config.js
const {Config} = require('next-recompose-plugins');
const {PHASE_PRODUCTION_BUILD} = require('next/constants');
const withBundleAnalyzer = require('@next/bundle-analyzer');

module.exports = new Config({...})
  .applyPlugin((phase, args, config) => {
    // determine current configuration phase
    if(phase === PHASE_PRODUCTION_BUILD) {
        // enhance the config with the desired plugin and return it back
        return withBundleAnalyzer({})(config);
    }
    
    // it's important to always return the config even if no plugins were applied
    return config;
  })
  .build();

Plugin applying function can also be async (support in Next.js 12.1+).

// next.config.js
const {Config} = require('next-recompose-plugins');
const withBundleAnalyzer = require('@next/bundle-analyzer');

module.exports = new Config({...})
  .applyPlugin(async (phase, args, config) => {
    await something();
  
    // enhance the config with the desired plugin and return it back  
    return withBundleAnalyzer({})(config);
  })
  .build();

It's a good technique to annotate your plugin applying functions with a name. When an error occur there will be a detailed information provided indicating which applyMethod invocation function is failing.

// next.config.js
const {Config} = require('next-recompose-plugins');
const withBundleAnalyzer = require('@next/bundle-analyzer');

module.exports = new Config({...})
  .applyPlugin((phase, args, config) => {
    // Uhh.. what's going on here!?
    throw new Error('Test');
  
    // enhance the config with the desired plugin and return it back  
    return withBundleAnalyzer({})(config);
  }, 'bundle-analyzer') // Pass an annotation as a last argument
  .build();
ready - started server on 0.0.0.0:3000, url: http://localhost:3000
An error occurred while applying the plugin (bundle-analyzer).
Error: Test
    at path-to-project-dir/next.config.js:11:11
    at path-to-project-dir/node_modules/next-recompose-plugins/dist/cjs/main.js:130:44
    at async Object.normalizeConfig (path-to-project-dir/node_modules/next/dist/server/config-shared.js:130:12)
    at async Object.loadConfig [as default] (path-to-project-dir/node_modules/next/dist/server/config.js:87:28)
    at async NextServer.prepare (path-to-project-dir/node_modules/next/dist/server/next.js:134:24)
    at async path-to-project-dir/node_modules/next/dist/cli/next-dev.js:585:17

Remember that you can chain applyPlugin(...) methods as many times as you would like to.

// next.config.js
const {Config} = require('next-recompose-plugins');

module.exports = new Config({...})
  .applyPlugin(async (phase, args, config) => {...}, 'plugin 1')
  .applyPlugin(async (phase, args, config) => {...}, 'plugin 2')
  .applyPlugin(async (phase, args, config) => {...}, 'plugin 3')
  .build();

Real world example

const path = require('path');
const {Config} = require('next-recompose-plugins');
const {PHASE_PRODUCTION_BUILD} = require('next/constants');
const withBundleAnalyzer = require('@next/bundle-analyzer');
const withExportImages = require('next-export-optimize-images');
const {withSentryConfig} = require('@sentry/nextjs');

const config = new Config(async () => {
  await something();

  return {
    reactStrictMode: true
  };
})
  .applyPlugin((phase, args, config) => {
    return withBundleAnalyzer({enabled: phase === PHASE_PRODUCTION_BUILD})(config);
  }, '@next/bundle-analyzer')
  .applyPlugin((phase, args, config) => {
    return withExportImages(config, {
      configPath: 'next-export-optimize-images.config.js',
    });
  }, 'next-export-optimize-images')
  .applyPlugin((phase, args, config) => {
    // Sentry plugin does not follow community guidelines according valid plugin shape

    config.sentry = {
      disableServerWebpackPlugin: true,
      hideSourceMaps: true,
    };

    let newConfig = withSentryConfig(config, {
      silent: true,
      configFile: path.resolve(__dirname, './.config/sentry/sentry.properties'),
    });

    if (typeof newConfig === 'function') newConfig = newConfig(phase, args);

    return newConfig;
  }, '@sentry/nextjs')
  .build();

module.exports = config;

Migration guides

v1 -> v2 🔗