elado / next-with-less

Next.js + Less CSS Support
MIT License
143 stars 24 forks source link

Global CSS cannot be imported from within node_modules. #6

Open JesperWe opened 3 years ago

JesperWe commented 3 years ago

Hi!

Thanks for trying to contibute to solving the Next 10 / Webpack 5 / Ant Design mess ;-)

I am trying you solution, but ending up with

info  - Creating an optimized production build  
Failed to compile.

./node_modules/antd/lib/affix/style/index.less
Global CSS cannot be imported from within node_modules.
Read more: https://nextjs.org/docs/messages/css-npm
Location: node_modules/antd/lib/affix/style/index.js
...
Error: > Build failed because of webpack errors

This does not happen if I try to add the component to your example code. I cannot quite figure what the difference is though. Any tips?

elado commented 3 years ago

See https://github.com/elado/next-with-less/issues/5

JesperWe commented 3 years ago

The solution is to import the .less files in your pages/_app instead of importing a JS file that imports them:

Not quite understanding this. You are saying that the normal way, like

import { Affix, Button, Col, Row } from "antd"

doesn't work, because it is importing a JS file that imports a Less file.

And that I need to import ALL less files of the components used across the app in _app.js to fix this?

Seems I am in for a bit of work... :-)

elado commented 3 years ago

Can you share some more code? I'll take a look later

JesperWe commented 3 years ago

Sure, I'll try to show the relevant pieces. The project is huge ;-)

next.config.js:

const lessToJS = require( 'less-vars-to-js' )
const fs = require( 'fs' )
const path = require( 'path' )
const withLess = require("next-with-less");
const MomentLocalesPlugin = require( 'moment-locales-webpack-plugin' )

// Where your antd-custom.less file lives
const theme = lessToJS(
    fs.readFileSync( path.resolve( __dirname, './antd-custom.less' ), 'utf8' )
)

const withBundleAnalyzer = require( '@next/bundle-analyzer' )( {
    enabled: process.env.ANALYZE === 'true',
} )

module.exports = withBundleAnalyzer( withLess( {
    lessLoaderOptions: {
        lessOptions: {
            javascriptEnabled: true,
            modifyVars: theme,
        }
    },
    productionBrowserSourceMaps: false,
    publicRuntimeConfig: { theme },
    webpack: ( config, { buildId, dev, isServer, defaultLoaders, webpack } ) => {
        config.resolve.modules.push( path.resolve( './' ) )

        config.plugins.push(
            new MomentLocalesPlugin( {
                localesToKeep: [ 'sv' ]
            } ) )

        return config
    },
    i18n: {
        locales: [ 'en', 'sv' ],
        defaultLocale: 'sv'
    },
    future: {
        webpack5: true
    }
} ) )

antd-custom.less just has a set of theme variable overrides:

@text-color: #151716;
@component-background: #fff;
 .... etc

Then there is a root app.less file that gets imported to _app.js. This is the only Ant css related import in the .js components. For the rest of the app we just import the ant components as shown in my post above.

app.less imports the default theme, then our theme customization and then some overrides to the default theme that is not exposed through the Less variables:

app.less:

@import "node_modules/antd/lib/style/themes/default.less";
@import "antd-custom.less";
@import "antd-overrides.less";

... and then lots of ordinary global (non component specific) css/less ...

This setup has served us fine since Next 9.2 or so but since the Next team seems inclined to drop the ball on Less support I found your "let's mimic their Sass setup" approach interesting ;-)

elado commented 3 years ago

I'm not quite sure where it's coming from. Can you try converting

@import "node_modules/antd/lib/style/themes/default.less";

into

@import "~antd/lib/style/themes/default.less";

?

~ prefix is the common way of importing CSS with @import from node_modules.

If this still fails, try replicating the issue in the examples dir in a fork.

akornato commented 3 years ago

@JesperWe removing .babelrc that I had from Next.js example fixed this for me.

JesperWe commented 3 years ago

Good catch @akornato ! I could not remove .babelrc completely since it uses other (non-Ant) stuff, but removing the Ant plugin made the above error go away.

build runs a bit longer with this....however it now fails at

info  - Creating an optimized production build  
Failed to compile.

./app.less

// https://github.com/ant-design/ant-motion/issues/44
.bezierEasingMixin();
^
Inline JavaScript is not enabled. Is it set in your options?
      in /...../node_modules/antd/lib/style/color/bezierEasing.less (line 110, column 0)
    at runMicrotasks (<anonymous>)

even though I can see in the source line 51 has javascriptEnabled: true,

akornato commented 3 years ago

@JesperWe hmm not sure why, I no longer even have javascriptEnabled: true - my next.config.js is pretty much just as in this repo's example.

JesperWe commented 3 years ago

Yes, this plugin seems to always enable javascript but it somehow it is not working for me. Maybe we are using components that you are not...

joelbqz commented 3 years ago

@JesperWe removing .babelrc that I had from Next.js example fixed this for me.

This issue was happening to me as well, did what @JesperWe suggested and it worked! Thanks!

jaywcjlove commented 3 years ago

I created a nextjs package next-remove-imports to solve the problem.

Example: https://codesandbox.io/s/nextjs-example-react-md-editor-qjhn7?file=/pages/index.js

// next.config.js
const removeImports = require("next-remove-imports")();

module.exports = removeImports({});
// pages/index.js

import Head from 'next/head'
import MDEditor from '@uiw/react-md-editor';
import "@uiw/react-md-editor/dist/markdown-editor.css";
import "@uiw/react-markdown-preview/dist/markdown.css";

export default function Home() {
  return (
    <div>
      <Head>
        <title>Create Next App</title>
        <meta name="description" content="Generated by create next app" />
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <MDEditor value="**Hello world!!!**" />
    </div>
  )
}

@JesperWe