vercel / next-plugins

Official Next.js plugins
MIT License
2.68k stars 319 forks source link

New plugin "next-compose" #58

Closed JerryCauser closed 6 years ago

JerryCauser commented 6 years ago

Should it be in off package?

code is very simple:

module.exports = plugins => ({
  webpack(config, options) {
    return plugins.reduce((config, plugin) => {
      if (plugin instanceof Array) {
        const [_plugin, ...args] = plugin
        plugin = _plugin(...args)
      }
      if (plugin instanceof Function) {
        plugin =  plugin()
      }
      if (plugin && plugin.webpack instanceof Function) {
        return plugin.webpack(config, options)
      }
      return config
    }, config)
  },

  webpackDevMiddleware(config) {
    return plugins.reduce((config, plugin) => {
      if (plugin instanceof Array) {
        const [_plugin, ...args] = plugin
        plugin = _plugin(...args)
      }
      if (plugin instanceof Function) {
        plugin =  plugin()
      }
      if (plugin && plugin.webpackDevMiddleware instanceof Function) {
        return plugin.webpackDevMiddleware(config)
      }
      return config
    }, config)
  }
})

and using like that:

const compose = require('@zeit/next-compose')
const withTs = require('@zeit/next-typescript')
const withSass = require('@zeit/next-sass')

module.exports = compose([
  withTs,
  [
    withSass,
    { sassLoaderOptions: { includePaths: ['some/path/to/include'] } }
  ],
  {
    webpack(config, options) {
      /** custom code */
      return config
    }
  }
])
JerryCauser commented 6 years ago

@timneutkens any thoughts?

sebas-deedee commented 6 years ago

Does this work combining next-sass and next-less? I want https://ant.design/ with theming support and work my app with SASS.

timneutkens commented 6 years ago

I don't think we have to provide this as a plugin / next.js specific thing, as far as I know the current setup of how plugins work, which is:

const withTs = require('@zeit/next-typescript')
const withSass = require('@zeit/next-sass')

const config = {}

module.exports = withTs(withSass(config))

Where every plugin returns the configuration object. Is perfect for usage with for example: compose-function.

JerryCauser commented 6 years ago

@sfernandezsl, yes it should work. next-less and next-sass will work together bcs they are using same textExtractor. If you wanna split them, then add you custom extractors in each config.

@timneutkens, what happen if I add config for withTs? How it should look? With compose it will look like that:

const withTs = require('@zeit/next-typescript')
const withSass = require('@zeit/next-sass')
const compose = require('@zeit/next-compose')

const tsConfig = {}
const sassConfig = {}

module.exports = compose([
  [withTs, tsConfig],
  [withSass, sassConfig],
  {
    webpack: (config) => {
      /**some special code code*/
      return config
    }
  }
])

with surrent setupd it will looks like:

const withTs = require('@zeit/next-typescript')
const withSass = require('@zeit/next-sass')

const tsConfig = {}
const sassConfig = {}

module.exports = withTs({
  ...tsConfig,
  webpack(config, options) {
    return withSass({
      ...sassConfig,
      webpack: (config, options) => {
        /**some special code code*/
        return config
      }
    })
  }
})

And if we wanna to add new chunk between TS and Sass or shuffle it, we will rewrite it. It is a callback hell.

I used composing idea from babelrc, where we can add presets and plugins in same way: if we need pure plugin/preset - just add his name. If we wanna pass some arguments(options) in it, then pass plugin as an array where the first elem is a plugin and rest are arguments.

But if you really don't wan't add it as off pluginm then I can create comunity plugin. Is it okay?

timneutkens commented 6 years ago

You're wrong as to the configuration format:

The config is 1 object, taken my example:

const withTs = require('@zeit/next-typescript')
const withSass = require('@zeit/next-sass')

const config = {}

module.exports = withTs(withSass(config))

You can add sass and typescript config like:

const withTs = require('@zeit/next-typescript')
const withSass = require('@zeit/next-sass')

const config = {
  sassLoaderOptions: {},
  cssModules: true,
  typescriptLoaderOptions: {}
}

module.exports = withTs(withSass(config))

basically plugins always extend the main configuration.

JerryCauser commented 6 years ago

Yeah, my bad. I thought, we need to pass config for each plugin. But how can I use two textExtractors at same time then? Ah, I forgt what we can not pass the rule fields... And can't specify which folder will handle each rule... For myself I used modified next-sass. It can handle aditional rules. I created pull request for that feature and using in my projects.

One of my config:

const webpack = require('webpack')
const withSass = require('./scripts/next-sass')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const commonsChunkConfig = require('./scripts/next-sass/commons-chunk-config')
const compose = require('./scripts/next-compose')
const {join} = require('path')

const styleLoaderOptions = {
  cssLoaderOptions({dev}) {
    return {
      modules: true,
      localIdentName: dev ? '[local]-[hash:base64:5]' : '[hash:base64:5]',
      sourceMap: false
    }
  },
  sassLoaderOptions: {
    sourceMap: false,
    includePaths: ["./", "./styles/base"]
  }
}
const extractVendorCSSPlugin = new ExtractTextPlugin('static/vendor.css')
const extractAppCSSPlugin = new ExtractTextPlugin('static/app.css')

module.exports = compose([
  [withSass, {
    cssLoaderOptions: { modules: false, sourceMap: false },
    sassLoaderOptions: styleLoaderOptions.sassLoaderOptions,
    extractCSSPlugin: extractVendorCSSPlugin,
    rules: {
      include: [join(__dirname, "styles")]
    }
  }],
  [withSass, {
    ...styleLoaderOptions,
    extractCSSPlugin: extractAppCSSPlugin,
    rules: {
      exclude: [join(__dirname, "styles")]
    }
  }],
  {
    webpack(config, options) {
      const providePlugin = new webpack.ProvidePlugin({
        'fetch': 'isomorphic-unfetch',
        'React': 'react'
      })
      config.plugins.push(providePlugin)
      config.plugins.push(extractVendorCSSPlugin)
      config.plugins.push(extractAppCSSPlugin)
      if (!options.isServer) {
        config = commonsChunkConfig(config, /\.(scss|sass)$/)
      }

      return config
    }
  }
])

Yaeh, for current implemetation it is just another pattern of generating config. You are using sequince of decorators, I'm using composition.

So, is it okay to make it as community plugin?

JerryCauser commented 6 years ago

And one more question: how work webpackDevMiddleware? Is it calling webpack and use config that return? And when we can use webpackDevMiddleware?

timneutkens commented 6 years ago

It's a different configuration, for webpackDevMiddleware, you can check it out in the source code 👍