Next.js assetPrefix breaks less loader - reactjs

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' : ''
},
);

Related

How to fix Error 400 Image not displaying Next App firebase ssr

Still in my journey of deploying my ssr app on firebase. Almost there.
I only have an issue with my images. I am getting a 400 error message on the console. Pictures are from an external url. The data fetched is correct but it does display somehow. You will see below my server.js file and next.config.js.
Can someone tell me what is missing, please?
server.js
const { https } = require('firebase-functions');
const { default: next } = require('next');
const isDev = process.env.NODE_ENV !== 'production';
const server = next({
dev: isDev,
//location of .next generated after running -> yarn build
conf: { distDir: '.next' },
image :{ domain :['assets.coingecko.com'],}
});
const nextjsHandle = server.getRequestHandler();
exports.nextServer = https.onRequest((req, res) => {
return server.prepare()
.then(() => {
return nextjsHandle(req, res)
});
});
next.config.js
const webpack = require('webpack');
const path = require('path');
//const withPlugins = require('next-compose-plugins');
//const optimizedImages = require('next-optimized-images');
module.exports = {
images: {
domains: ['assets.coingecko.com'],
loader: 'imgix',
path: 'https://assets.coingecko.com/',
},
reactStrictMode: true,
entry: './src/index.js',
sassOptions: {
includePaths: [path.join(__dirname, 'styles')],
},
module: {
rules: [
//...
{
test: /\.(png|jp(e*)g|svg|gif)$/,
use: [
{
loader: 'file-loader',
options: {
name: 'images/[hash]-[name].[ext]',
},
},
],
},
],
},
//...
}
first i wanted to thank you for your help #juliomalves. I found answer to my issue. I have answered the questions in details here [1][ https://stackoverflow.com/questions/69974890/how-to-set-up-next-image-loader-url-correctly-for-external-url/70052871#70052871]
if anyone find himself in the same situation.

Updating nextjs from 8 to 9.3.3 broke styling

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?

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

React production shows blank page

i have a problem with a webpack build, the command i run is
cross-env NODE_ENV=production webpack --config webpack.prod.js
and the file webpack prod is:
var webpack = require('webpack');
var webpackMerge = require('webpack-merge');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var commonConfig = require('./webpack.common.js');
var path = require('path');
const ENV = process.env.NODE_ENV = process.env.ENV = 'production';
module.exports = webpackMerge(commonConfig, {
devtool: 'source-map',
output: {
path: path.join(process.cwd(), '/public'),
publicPath: '/',
filename: '[name].[hash].js'
},
plugins: [
//new webpack.NoErrorsPlugin(),
new webpack.optimize.DedupePlugin(),
new webpack.optimize.UglifyJsPlugin({
mangle: {
keep_fnames: true,
except: ['$super']
}
}),
new ExtractTextPlugin('[name].[hash].css'),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('production')
}
})
]
});
webpack.common.js is
var webpack = require('webpack');
var CopyWebpackPlugin = require('copy-webpack-plugin');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var path = require('path');
var pkgBower = require('./package.json');
module.exports = {
entry: {
'app': './main.js'
},
resolve: {
root: path.join(__dirname, ''),
modulesDirectories: ['node_modules', 'bower_components'],
extensions: ['', '.js', '.jsx']
},
module: {
loaders: [...]
},
devServer: {
outputPath: path.join(__dirname, 'public')
},
plugins: [
new HtmlWebpackPlugin({
template: process.env.NODE_ENV == 'development' ? 'index.html' : 'index.prod.html',
baseUrl: process.env.NODE_ENV == 'development' ? '/' : '/'
})
]
};
i can run the build but unfortunatelly when i open index.html it shows a blank page to me, i want to include this SPA in a spring boot project but I do not know how ;)
Thanks!
Solved! was a problem of "browserHistory"
If you use the "browserHistory" as history manager your webpack needs a node.js server to run, using a "hashHistory" you can use it as a normal web page!
See
Why is React Webpack production build showing Blank page?
First things first, which version of webpack are you using Lorenzo? Probably due to syntax, it's 1.X.
First, check in your package.json file which version is. Then, if it's really 1.x you can build in debug mode.
export default {
entry: [
'./src/Client.js'
],
debug: true
}
Inside your webpack.common.js
Check the error, paste here.
I hope it helps, welcome to StackOverflow, here are a few notes that can improve our community =)
Try to put "main":"index.js" in package.json file
That solved my problem.
"main": "index.js",
"scripts": {
...
},
Happy to help.

Resources