Updating nextjs from 8 to 9.3.3 broke styling - reactjs

I just updated nextjs from 8 to 9.3.3, and some of my styling broke.
Previously I was importing .scss files and they were locally scoped to their respective components.
After updating to 9.3.3, it seems that components with classNames that are named the same are now sharing styles, so that leads me to believe that something with the sass-loader or css-loader in next.config.js seems off.
this is my next.config.js
const path = require('path');
const dotenv = require('dotenv');
const withSass = require('#zeit/next-sass');
const webpack = require('webpack');
const envConfig = dotenv.config();
const config = withSass({
cssModules : true,
cssLoaderOptions : {
modules : true,
sourceMap : true,
importLoaders : 1,
},
sassLoaderOptions: {
includePaths: [path.resolve(__dirname, './')], //eslint-disable-line
},
useFileSystemPublicRoutes: false,
webpack: (config, { buildId, dev, isServer, defaultLoaders }) => { // eslint-disable-line
const ENV = {};
for (let entry in envConfig.parsed) {
ENV[`process.env.${entry}`] = JSON.stringify(envConfig.parsed[entry]);
}
config.plugins.push(new webpack.DefinePlugin(ENV));
return config;
},
});
module.exports = config;
What I've tried
I've changing my config around like this:
const config = withSass({
cssModules : true,
cssLoaderOptions : {
modules : {
mode: 'local',
exportGlobals: true,
localIdentName: '[path][name]__[local]--[hash:base64:5]',
context: path.resolve(__dirname, 'src'), //eslint-disable-line
hashPrefix: 'my-custom-hash',
},
sourceMap : true,
importLoaders : 1,
},
sassLoaderOptions: {
includePaths: [path.resolve(__dirname, './')], //eslint-disable-line
},
useFileSystemPublicRoutes: false,
webpack: (config, { buildId, dev, isServer, defaultLoaders }) => { // eslint-disable-line
const ENV = {};
for (let entry in envConfig.parsed) {
ENV[`process.env.${entry}`] = JSON.stringify(envConfig.parsed[entry]);
}
config.plugins.push(new webpack.DefinePlugin(ENV));
return config;
},
});
module.exports = config;
which didn't work.
My last resort which does work is manually going into each .scss file and changing them to x.module.scss, which would take a lot of time going into each file. Is there something wrong with my config?

Related

Reactjs - Can xterm.js have problem with webpack configuration?

So far I am not able to properly integrate xterm.js with reactjs due to which my code breaks in production but works while development.
HELP !!!
import React, {useEffect} from 'react';
import {Terminal} from 'xterm';
import {FitAddon} from 'xterm-addon-fit';
const UITerminal = () => {
const term = new Terminal();
const fitAddon = new FitAddon();
term.loadAddon(fitAddon);
useEffect(() => {
let termDocument = document.getElementById('terminal')
if (termDocument) {
term.open(termDocument)
fitaddon.fit();
}
window.addEventListener('resize', () => {
fitaddon.fit();
})
}, [])
return (<div id="terminal"></div>)
}
Below is the error response from production code. clearly it fails to import xterm
react_devtools_backend.js:4012 ReferenceError: Cannot access 'r' before initialization
at new m (96209.72626fc1cc862aea477a.bundle.js:1:165467)
at new b (96209.72626fc1cc862aea477a.bundle.js:1:159758)
at new M (96209.72626fc1cc862aea477a.bundle.js:1:57572)
at new r.exports.i.Terminal (96209.72626fc1cc862aea477a.bundle.js:1:294972)
at w (96209.72626fc1cc862aea477a.bundle.js:1:15994)
at zo (main.71e827eabc798023c129.bundle.js:1:1260000)
at Ws (main.71e827eabc798023c129.bundle.js:1:1333492)
at Wi (main.71e827eabc798023c129.bundle.js:1:1294411)
at Ui (main.71e827eabc798023c129.bundle.js:1:1294336)
at Pi (main.71e827eabc798023c129.bundle.js:1:1291367)
UPDATE
I have found out that it is happening because of my production webpack configuration but still the root cause is unidentified. please help in soughting this out. I am adding my development and production webpack config here.
Please note that the dev webpack config absolutely works fine if build with it and serve.
webpack.dev.js
const fqdn = "some.fqdn.com"
const path = require("path");
const webpack = require("webpack")
const common = require("./webpack.common");
const { merge } = require("webpack-merge");
const fs = require('fs');
const jsonFormat = require('json-format');
var HtmlWebpackPlugin = require("html-webpack-plugin");
const jsonFromatConfig = {
type: 'space',
size: 4
}
module.exports = merge(common, {
mode: "development",
devtool: "source-map",
output: {
filename: "bundle.js",
publicPath: '/',
},
plugins: [
new HtmlWebpackPlugin({
template: "./public/index.html"
}),
new webpack.HotModuleReplacementPlugin()
],
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader"]
}
]
},
target: "web",
devServer: {
open: true,
static: {
directory: path.join(__dirname, '../public'),
},
historyApiFallback: true,
},
});
webpack.prod.js
const path = require("path");
const common = require("./webpack.common");
const { merge } = require("webpack-merge");
var MinifyPlugin = require('babel-minify-webpack-plugin')
var CompressionPlugin = require('compression-webpack-plugin');
const CleanWebpackPlugin = require("clean-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const TerserPlugin = require("terser-webpack-plugin");
var HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = merge(common, {
mode: "production",
output: {
filename: "naming.[name].contenthash.[contenthash].bundle.js",
path: path.resolve(__dirname, "../build")
},
optimization: {
minimizer: [
new TerserPlugin(),
new HtmlWebpackPlugin( {
template: "./public/index.html",
minify: {
removeAttributeQuotes: true,
collapseWhitespace: true,
removeComments: true
}
} ),
new MinifyPlugin({}, {
comments: false
})
],
splitChunks: {
chunks: 'all',
minChunks: 3
}
},
plugins: [
new CompressionPlugin({
test: /\.js$|\.css$|\.html$/
}),
new MiniCssExtractPlugin({ filename: "naming.[name].contenthash.[contenthash].css" }),
new CleanWebpackPlugin()
],
module: {
rules: [
{
test: /\.css/i,
use: [
MiniCssExtractPlugin.loader,
"css-loader"
]
},
]
}
});
The error shows that you might use Xterm before its initialization,
You might find it useful to use react-aptor or the idea behind it to connect pure js packages like Xterm.js into react world.
import useAptor from 'react-aptor';
const initializer = (node, params) => {
// user params for further configuration
const terminal = new Terminal();
term.open(node);
return terminal;
}
const getAPI = (terminal, params) => {
return () => ({ terminal })
}
const ReactXterm = (props, ref) => {
const aptorRef = useAptor(ref, {
getAPI,
instantiate,
/* params: anything */
});
return <div ref={aptorRef} />;
};
function App() {
const ref = useRef();
const writeToTerminal = () => {
ref.current.terminal?.write('Hello from \x1B[1;3;31mxterm.js\x1B[0m $ ');
};
return (
<div>
<ReactXterm ref={ref} />
<button onClick={writeToTerminal}>write to terminal</button>
</div>
);
}
Disclosure: I am the maintainer of react-aptor

Nextjs Error: Must use import to load ES Module

Hi i am creating a project using nextjs and reactjs ,i am facing an issue, when I am trying to import datetime from npm date-time package ,its throwing this error.I am using this npm package https://www.npmjs.com/package/date-time. The error in my code is shown in the picture attached
This is my next.config.js what i need to
const path = require("path");
const withCss = require("#zeit/next-css");
const withSass = require("#zeit/next-sass");
const withImages = require("next-images");
module.exports = withImages(
withSass(
withCss({
webpack: (config, { isServer }) => {
if (isServer) {
config.module.rules.push({
test: /\.(png|jpg|gif|svg|eot|ttf|woff|woff2)$/,
use: {
loader: "url-loader",
options: {
limit: 100000,
name: "[name].[ext]",
},
},
});
}
return config;
},
cssLoaderOptions: {
url: false,
},
})
)
);

Next.js assetPrefix breaks less loader

I'm new to Next.js. Any help will be appreciated.
The app works fine in local dev environment. However, as soon as I add the following to next.config.js, next.js thorw an error.
// next.config.js
const isProd = process.env.NODE_ENV === 'production'
module.exports = {
// Use the CDN in production and localhost for development.
assetPrefix: isProd ? 'https://cdn.mydomain.com' : '/example',
}
#### error message
error - ./styles/fonts.less 1:0
Module parse failed: Unexpected character '#' (1:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
> #font-face {
Not sure what the issue is. Any help is welcomed. The following is all the config I have with my app.
const withImages = require('next-images')
module.exports = withImages();
const withCSS = require('#zeit/next-css')
module.exports = withCSS({
cssLoaderOptions: {
url: false
}
})
const withLess = require('#zeit/next-less')
/* With CSS Modules */
module.exports = withLess({
cssModules: true,
cssLoaderOptions: {
sourceMap: true,
localIdentName: '[local]___[hash:base64:5]',
localsConvention: 'camelCase',
camelCase: 'dashes',
}
})
const isProd = process.env.NODE_ENV === 'production'
module.exports = {
// Use the CDN in production and localhost for development.
assetPrefix: isProd ? 'https://cdn.mydomain.com' : '/example',
}
I found out the root cause of the my problem. Multiple module.exports caused the issue since the last one will override all previous ones. It means there will be no loader to deal Less/CSS. I also decided to use next-compose-plugins plugin to help me deal with multiple plugins. Otherwise, I have to do something like withCSS(withLESS()).
const withImages = require('next-images');
const withPlugins = require('next-compose-plugins');
const withCSS = require('#zeit/next-css');
const withLess = require('#zeit/next-less');
const isProd = process.env.NODE_ENV === 'production';
module.exports = withPlugins(
[
[withLess, {
cssModules: true,
cssLoaderOptions: {
sourceMap: true,
localIdentName: '[local]___[hash:base64:5]',
localsConvention: 'camelCase',
camelCase: 'dashes',
}
}],
[withCSS],
[withImages],
],
{
/* global config here ... */
assetPrefix: isProd ? 'https://cdn.cloudfront.net/vpassets' : ''
},
);

How to set environment variables on React with custom webpack

My React app isn't run by create-react-app, but a custom Webpack config.
I"ve installed dotenv / dotenv-expand / and also dotenv-webpack.
I have .env / .env.development files with API_URL variable in it.
On my url file,
const { API_URL } = process.env
and use this API_URL to fetch data.
But on this file, when I console.log(process.env), it is empty.
I also have tried to update webpack.config.js file with
const Dotenv = require('dotenv-webpack');
and
new Dotenv()
in plugins array.
But still doesn't work.
I also tried having variable name REACT_APP_API_URL but was same result.
Could anyone help me to set the env vars?
Thank you.
webpack.config.js
const webpack = require('webpack')
const Dotenv = require('dotenv-webpack');
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const webpackMerge = require('webpack-merge')
const modeConfig = env => require(`./build-utils/webpack.${env}`)(env)
const presetConfig = require('./build-utils/loadPresets')
module.exports = ({ mode, presets } = { mode: 'production', presets: [] }) => {
console.log('mode', mode, 'presets', presets)
return webpackMerge(
{
mode,
module: {
rules: [
{
test: /\.(png|jpe?g|svg)$/,
use: {
loader: 'file-loader',
options: {
name: 'assets/[name].[ext]',
}
},
},
],
},
node: {
fs: 'empty'
},
resolve: {
extensions: ['.js', '.json'],
},
output: {
filename: 'bundle.js',
chunkFilename: '[name].lazy-chunk.js',
path: path.resolve(__dirname, 'build'),
publicPath: mode === 'development' ? '/' : './'
},
devServer: {
historyApiFallback: true
},
plugins: [
new HtmlWebpackPlugin({
template: './build-utils/template.html'
}),
new webpack.ProgressPlugin(),
new Dotenv()
]
},
modeConfig(mode),
presetConfig({ mode, presets })
)
}
If you're trying to access the env values in your JS files, you typically need to have the dotenv plugin.
const webpack = require('webpack')
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const webpackMerge = require('webpack-merge')
require('dotenv').config() // may need to set path to your .env file if it isn't at the root at the project
const modeConfig = env => require(`./build-utils/webpack.${env}`)(env)
const presetConfig = require('./build-utils/loadPresets')
module.exports = ({ mode, presets } = { mode: 'production', presets: [] }) => {
return webpackMerge(
...
plugins: [
new HtmlWebpackPlugin({
template: './build-utils/template.html'
}),
new webpack.ProgressPlugin(),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify(process.env.NODE_ENV),
SOME_VALUE: JSON.stringify(process.env.SOME_VALUE),
...
}
})
]
},
...
)
}
NOTE: With this implementation you won't be able to do
const { API_URL } = process.env because DefinePlugin does a search and replace of the JavaScript where it will look up any references to process.env.API_URL and replace it with whatever that value is. Therefore API_URL won't exist on porcess.env, so to use it just do process.env.API_URL
You could also use dotenv-webpack, I think you were close to getting it to work.
const webpack = require('webpack')
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const webpackMerge = require('webpack-merge')
const Dotenv = require('dotenv-webpack');
const modeConfig = env => require(`./build-utils/webpack.${env}`)(env)
const presetConfig = require('./build-utils/loadPresets')
module.exports = ({ mode, presets } = { mode: 'production', presets: [] }) => {
return webpackMerge(
...
plugins: [
new HtmlWebpackPlugin({
template: './build-utils/template.html'
}),
new webpack.ProgressPlugin(),
new Dotenv({
path: envPath
})
]
},
...
)
}
If you need more help please share more details in by creating a Minimal, Reproducible Example.

Next JS config multiple plugin configuration

const {
DEVELOPMENT_SERVER,
PRODUCTION_BUILD
} = require("next/constants");
require('dotenv').config()
const path = require('path')
const Dotenv = require('dotenv-webpack')
const nextConfig = {
webpack: config => ({ ...config, node: { fs: "empty" } })
};
module.exports = phase => {
if (phase === DEVELOPMENT_SERVER || phase === PRODUCTION_BUILD) {
const withCSS = require("#zeit/next-css");
return withCSS(nextConfig);
}
return nextConfig;
};
*module.exports = {
webpack: (config) => {
config.plugins = config.plugins || []
config.plugins = [
...config.plugins,
// Read the .env file
new Dotenv({
path: path.join(__dirname, '.env'),
systemvars: true
})
]
return config
}
}*
let prefix;
switch (process.env.NODE_ENV) {
case "test":
prefix = "https://test.domain.com/providers";
break;
case "stage":
prefix = "https://state.domain.com/providers";
break;
case "production":
prefix = "https://production.domain.com/providers";
break;
default:
prefix = "";
break;
}
module.exports = {
distDir: "build",
assetPrefix: prefix
};
Here my next.config.js configuration.
But when I am trying to run then getting the message like
Error! Network error: Unexpected token N in JSON at position 0
But when I am trying to run whatever into the bold(*) and kept only that thing into the next.config.js then working fine.
How to configure multiple plugin into the module.export
Here is a simple way to use multiple nested plugins in Next.js
const withImages = require('next-images');
const withCSS = require('#zeit/next-css');
module.exports = withCSS(withImages({
webpack(config, options) {
return config
}
}))
If you want to using single one plugin then do this:
const withImages = require('next-images');
module.export = withImages();
For further information about Next.js plugins and their documentation click here:
https://github.com/zeit/next-plugins/tree/master/packages
next-compose-plugins plugin provides a cleaner API for enabling and configuring plugins for next.js.
Install npm install --save next-compose-plugins
Use it in next.config.js:
// next.config.js
const withPlugins = require('next-compose-plugins');
const images = require('next-images');
const sass = require('#zeit/next-sass');
const typescript = require('#zeit/next-typescript');
// optional next.js configuration
const nextConfig = {
useFileSystemPublicRoutes: false,
distDir: 'build',
};
module.exports = withPlugins([
// add a plugin with specific configuration
[sass, {
cssModules: true,
cssLoaderOptions: {
localIdentName: '[local]___[hash:base64:5]',
},
}],
// add a plugin without a configuration
images,
// another plugin with a configuration
[typescript, {
typescriptLoaderOptions: {
transpileOnly: false,
},
}],
], nextConfig);
Next.js version >10.0.3
Images are now built-in, so to add plugin and image code below will work:
const withPlugins = require('next-compose-plugins');
const withCSS = require('#zeit/next-css');
const nextConfig = {
images: {
domains: ['sd.domain.com'], // your domain
},
};
module.exports = withPlugins([
[withCSS]
], nextConfig);
This worked for me:
// next.config.js
const withLess = require("next-with-less");
const withPWA = require('next-pwa')
const pwa = withPWA({
pwa: {
dest: 'public'
}
})
module.exports = withLess({
lessLoaderOptions: {
},
reactStrictMode: true,
typescript: {
ignoreBuildErrors: true,
},
eslint: {
ignoreBuildErrors: true,
},
...pwa // using spread to merge the objects
});

Resources