vercel / next.js

The React Framework
https://nextjs.org
MIT License
124.87k stars 26.66k forks source link

False negative for "Global CSS cannot be imported from within node_modules" warning #39516

Open just-boris opened 2 years ago

just-boris commented 2 years ago

Verify canary release

Provide environment information

Operating System:
  Platform: darwin
  Arch: x64
  Version: Darwin Kernel Version 21.6.0: Sat Jun 18 17:07:25 PDT 2022; root:xnu-8020.140.41~1/RELEASE_X86_64
Binaries:
  Node: 14.19.3
  npm: 6.14.17
  Yarn: 1.22.15
  pnpm: 6.11.0
Relevant packages:
  next: 12.2.5-canary.5
  eslint-config-next: 12.2.4
  react: 18.2.0
  react-dom: 18.2.0

What browser are you using? (if relevant)

n/a

How are you deploying your application? (if relevant)

n/a

Describe the Bug

Our components library (https://github.com/cloudscape-design/components) exports scoped styles, but we still get the warning:

./node_modules/@cloudscape-design/components/app-layout/content-wrapper/styles.scoped.css
Global CSS cannot be imported from within node_modules.
Read more: https://nextjs.org/docs/messages/css-npm
Location: node_modules/@cloudscape-design/components/app-layout/content-wrapper/styles.css.js

We use zero-config CSS scoping (e.g. the files published on npm are already scoped), but next.js still claims we are doing something wrong (do we?)

Expected Behavior

The project starts successfully with the default next config

Link to reproduction

https://github.com/just-boris/cloudscape-nextjs

To Reproduce

Currently next.config.js contains a workaround: https://github.com/just-boris/cloudscape-nextjs/blob/main/next.config.js#L8

To reproduce the issue, you need to replace module.exports = withTM(nextConfig); with module.exports = nextConfig;

derreck503 commented 2 years ago

🚨 Bumping up this bug 🐛

mattcarlotta commented 2 years ago

As far as I know, Next doesn't handle any internal dependency stylesheet imports regardless if they're global or local.

In this case, the stylesheet naming convention that cloudscape-design is using ([name].scoped.css) is not common, which is why Next assumes it's global CSS -- a more common use case of local CSS would be [name].module.css, which Next adopted from the Create React App. The reason Next decided not to support any dependency stylesheets is that they couldn't guarantee their import order: If you have a component-level stylesheet that overrides a 3rd party stylesheet, their order couldn't be consistently determined; therefore, you'd end up with inconsistent styles per page and per page load. Instead, they recommend package maintainers separate their stylesheets imports so that developers can import them within the _app.js/component file, which allows the developer to set stylesheet import hierarchy (guaranteeing stylesheet overrides).

Stylesheets aside, the larger roadblock that you're running into is that @cloudscape-design/components ships esmodules (ESM). Therefore, you won't be able to use server-side rendering for these components -- on the server, it tries to require the JavaScript dependency which uses the import/export syntax. Node doesn't support a commonjs (CJS) file that requires an ESM file -- Node expects all CJS JavaScript files to adhere to the require(id)/module.exports syntax. That said, in an ESM Javascript file, you technically can import a CJS JavaScript file! Unfortunately, Next v12.x.x support for ESM is still experimental.

What next-transpile-modules does to make it work is that it extends babel transpiling to node_module packages AND, by default, adds support for node_module stylesheet imports (which isn't recommended, as explained above). On that note, I believe TM is actually treating all of the [name].scoped.css stylesheets as a global since it doesn't follow the [name].module.css naming convention!

TLDR: Next currently doesn't support importing stylesheets nor ESM in node_modules.

just-boris commented 2 years ago

A little bit context on why [name].scoped.css was chosen. Originally we started with .module.css but quickly figured out that tools like CRA automatically pick it up and attempt to modify already scoped class names, breaking the styles. So, we changed the name to avoid side effects on the tools. The current setup works great across all tools, except next.js restriction.

As a request for Next.js team here would be to rephrase the error message, because it confuses users about actual content of our module. Saying something like "Arbitrary CSS cannot be imported from within node_modules" would be more accurate.

jaywcjlove commented 1 year ago

https://github.com/uiwjs/next-remove-imports

zackshen commented 1 year ago

@jaywcjlove the plugin would remove the imported css ? if removed css, the component can not work correctly.

jaywcjlove commented 1 year ago

@zackshen Copy the css from the package and import into the project

Or directly import the css in the package in the project

+import "@uiw/react-markdown-preview/markdown.css";
import dynamic from "next/dynamic";
import { useState } from "react";

+ const MDEditor = dynamic(
+  () => import("@uiw/react-md-editor").then((mod) => mod.default),
+  { ssr: false }
+ );
zackshen commented 1 year ago

@jaywcjlove your solution is helpful, but I has to modify top level business code and the user should known the implementation detail of the dependencies,finally the package next-transpile-modules can resolve the css problem.

chaitanya71998 commented 1 year ago

Hi @zackshen , i was also facing similar issue can you share what steps have you done to resolve this issue.

zackshen commented 1 year ago

@chaitanya71998

example code in next.config.js

const withTM = require('next-transpile-modules')(['react-shadow-picker'])

module.exports = withTM(config)

in business code

import { ShadowPicker } from 'react-shadow-picker';
import styled from 'styled-components';

const StyledShadowPicker = styled(ShadowPicker)`
// bla bla bla 
// custom style here
`
imwangpan commented 1 month ago

Thank you very much for the solution, it helped me a lot.