Open mehmetnyarar opened 5 years ago
Same here, webpack doesn't run
Is your app crashing when running the server in dev/prod mode?
I noticed that I had a custom server that had require('./next.config.js')
in it. next.config.js
doesn't export an object anymore after using next-compose-plugins
. It uses phases so it exports a function.
In my case, webpack wasn't running because it was crashing trying to access variables from the exported function, expecting it to be an object. If you still need the next config object, you can see my comment in another post.
const isDev = process.env.NODE_ENV !== 'production';
const { PHASE_DEVELOPMENT_SERVER, PHASE_PRODUCTION_SERVER } = require('next/constants');
const nextConfigModule = require('../next.config');
let nextConfig;
if (typeof nextConfigModule === 'function') {
nextConfig = nextConfigModule(isDev ? PHASE_DEVELOPMENT_SERVER : PHASE_PRODUCTION_SERVER, {});
} else {
nextConfig = nextConfigModule;
}
// useless because already know isDev.
nextConfig.serverRuntimeConfig.isDev;
Note that you have to determine isDev
to get the correct next config. isDev
is probably part of your environment configuration. So if you only need the environment configuration object, you probably would have already gotten it elsewhere (in my case from process.env
) and don't need to get the next config.
@mehmetnyarar the code you have posted should work as you expect it to work. If the solution which @dosentmatter posted does not work, can you share a minimal reproduction example so I can take a look?
same. is there a working work around ?
withTM
in that case defines a webpack
function in the settings it returns.
So maybe the custom webpack
function settings passed in withPlugins
is already overrided by the above plugin ( withTM
) and thus ignored
+1 I'm having this issue in a major way minimal reproduction here, it's just the typescript +css module starter from next.js. If you open next.config.js you'll see we try to log when our custom webpack "hook" runs
module.exports = withPlugins([[withCSS, cssConfig]],{
webpack: config =>{
console.log("This never runs :("); // <<<<<<<<<<<<<<<< Here's the issue
return config;
}
});
but if you run the dev script you'll never hit that line of code.
@antontsvil
You define the webpack
function twice in your file (once with the css plugin and once at the bottom) and the way next
works, they overwrite each other so only one gets executed. You can simply change your next.config.js
to only have one webpack
definition like this:
const withCSS = require('@zeit/next-css')
const withPlugins = require('next-compose-plugins');
/* With additional configuration on top of CSS Modules */
const cssConfig = {
cssModules: true,
cssLoaderOptions: {
camelCase: true,
namedExport: true
},
};
module.exports = withPlugins([[withCSS, cssConfig]],{
webpack: (config, options) =>{
console.log("This now runs :)");
if (!options.isServer) {
for (let entry of options.defaultLoaders.css) {
if (entry.loader === 'css-loader') {
entry.loader = 'typings-for-css-modules-loader'
break
}
}
}
return config;
}
});
I'm thinking if next-compose-plugins
should manually parse the webpack definitions, but I'm not sure if that could break some special use-cases..
@cyrilwanner thanks for clearing that up. What's odd is that I've since found a workaround for this issue without removing the webpack function in the sassConfig. In my full project my next.config looks like this:
const extendWebpack = require("./extend-webpack");
const withPlugins = require("next-compose-plugins");
const withImages = require("next-optimized-images");
const withFonts = require("next-fonts");
const withSASS = require("@zeit/next-sass");
const SASSConfig = {
cssModules: true,
cssLoaderOptions: {
importLoaders: 1,
camelCase: true,
namedExport: true
},
webpack(config, options) {
if (!options.isServer) {
for (let entry of options.defaultLoaders.sass) {
if (entry.loader === "css-loader") {
entry.loader = "typings-for-css-modules-loader";
}
}
}
return config;
}
};
module.exports = withPlugins([
[withSASS, SASSConfig],
[withFonts],
[withImages],
[extendWebpack]
]);
The additional changes to my webpack config are made with this extendWebpack "plugin", which is simply a mock of a plugin structure that accomplishes what I need it to do. It looks like this:
const webpackAliasSupport = require("./lib/dev/webpack-alias-support");
const webpackEnableGlobal = require("./lib/dev/webpack-enable-global-sass");
module.exports = (nextConfig = {}, nextComposePlugins = {}) => {
return Object.assign({}, nextConfig, {
webpack: (config, options) => {
config = webpackEnableGlobal(config);
return webpackAliasSupport(config, options, nextConfig);
}
});
};
I'd like to apply your solution, since I'm sure there is still something wrong with my approach. Unfortunately I think I'm misunderstanding the order of the plugins being executed or something? When I try your suggested approach, the options.defaultLoaders
object has no sass key, as if the withSASS plugin hasn't run yet. Am I incorrect in assuming that this final custom next config receives the webpack config at it's final state, after it's been transformed by all preceding plugins?
Ah, I see. The config gets passed to the plugins before they run so they are aware of the values set there (e.g. the publicPath
etc).
I think your current solution works well, but I'll see if I find a more convenient way for overwriting the webpack config.
As a workaround, I wrote simple compose
function:
function compose(plugins) {
let cfg = {};
return plugins.reduceRight(
(prevFn, plugin) => {
// console.log('-->', plugin);
if (plugin[1]) cfg = { ...cfg, ...plugin[1] };
return (...args) => plugin[0](prevFn(...args));
},
(value) => {
// console.log('Combined config:', { ...cfg, ...value });
return { ...cfg, ...value };
}
);
}
So your config will have the following view:
const plugins = const plugins = [
[
// https://github.com/zeit/next-plugins/tree/master/packages/next-css
require('@zeit/next-css'),
{
cssModules: false,
cssLoaderOptions: {},
},
],
[
// https://github.com/zeit/next-plugins/tree/master/packages/next-sass
require('@zeit/next-sass'),
{
sassLoaderOptions: {},
},
],
];
const nextConfig = {
distDir: '../dist',
};
module.export = compose(plugins)(nextConfig);
@antontsvil You define the
webpack
function twice in your file (once with the css plugin and once at the bottom) and the waynext
works, they overwrite each other so only one gets executed. You can simply change yournext.config.js
to only have onewebpack
definition like this:const withCSS = require('@zeit/next-css') const withPlugins = require('next-compose-plugins'); /* With additional configuration on top of CSS Modules */ const cssConfig = { cssModules: true, cssLoaderOptions: { camelCase: true, namedExport: true }, }; module.exports = withPlugins([[withCSS, cssConfig]],{ webpack: (config, options) =>{ console.log("This now runs :)"); if (!options.isServer) { for (let entry of options.defaultLoaders.css) { if (entry.loader === 'css-loader') { entry.loader = 'typings-for-css-modules-loader' break } } } return config; } });
I'm thinking if
next-compose-plugins
should manually parse the webpack definitions, but I'm not sure if that could break some special use-cases..
Thanks for this reply. My expectation was different: I thought with every plugin I use in the chain, I can define a dedicated webpack function and these functions would be composed onto each other. Having only one webpack functions for multiple plugins make this a mess over time. For instance, the following would be my desired usage:
const nextConfig = {
env: {
BASE_URL: process.env.BASE_URL,
},
};
const lessWithAntdConfig = {
lessLoaderOptions: {
javascriptEnabled: true,
modifyVars: lessToJS(
fs.readFileSync(
path.resolve(__dirname, './assets/antd-custom.less'),
'utf8'
)
),
},
webpack: (config, { isServer }) => {
if (isServer) {
const antStyles = /antd\/.*?\/style.*?/;
const origExternals = [...config.externals];
config.externals = [
(context, request, callback) => {
if (request.match(antStyles)) return callback();
if (typeof origExternals[0] === 'function') {
origExternals[0](context, request, callback);
} else {
callback();
}
},
...(typeof origExternals[0] === 'function'
? []
: origExternals),
];
config.module.rules.unshift({
test: antStyles,
use: 'null-loader',
});
console.log(config.node);
}
return config;
},
};
const mdxConfig = {
webpack: (config, { isServer }) => {
if (!isServer) {
config.node = {
fs: 'empty',
};
}
return config;
},
};
const withBundleAnalyzer = bundleAnalyzer({
enabled: process.env.ANALYZE === 'true',
});
module.exports = withPlugins(
[
[withMDX, mdxConfig],
[withLess, lessWithAntdConfig],
[withBundleAnalyzer],
],
nextConfig
);
Instead I have to group all webpack functions in one shared webpack function:
const nextConfig = {
env: {
BASE_URL: process.env.BASE_URL,
},
webpack: (config, { isServer }) => {
// Less with Antd
if (isServer) {
const antStyles = /antd\/.*?\/style.*?/;
const origExternals = [...config.externals];
config.externals = [
(context, request, callback) => {
if (request.match(antStyles)) return callback();
if (typeof origExternals[0] === 'function') {
origExternals[0](context, request, callback);
} else {
callback();
}
},
...(typeof origExternals[0] === 'function'
? []
: origExternals),
];
config.module.rules.unshift({
test: antStyles,
use: 'null-loader',
});
}
// MDX
if (!isServer) {
config.node = {
fs: 'empty',
};
}
return config;
},
};
const lessWithAntdConfig = {
lessLoaderOptions: {
javascriptEnabled: true,
modifyVars: lessToJS(
fs.readFileSync(
path.resolve(__dirname, './assets/antd-custom.less'),
'utf8'
)
),
},
};
const withBundleAnalyzer = bundleAnalyzer({
enabled: process.env.ANALYZE === 'true',
});
module.exports = withPlugins(
[[withLess, lessWithAntdConfig], [withMDX], [withBundleAnalyzer]],
nextConfig
);
This seems okay for a webpack function for only two plugins, but let's imagine this for 10 plugins. Just my thoughts on this! Thanks for this plugin and your work on it 🎉
In fact the config must be passed as array item together with plugins.
const cssConfig = {
cssModules: true,
cssLoaderOptions: {
camelCase: true,
namedExport: true
},
};
module.exports = withPlugins([[withCSS, cssConfig]])
When I use
next-compose-plugins
, my custom next config is ignored and I can't get environment variables. When I don't use it, there's no problem.Am I doing something wrong or is this a bug?