Next.js: Importando css com escopo correto de node_modules fora de _app

Criado em 21 abr. 2020  ·  49Comentários  ·  Fonte: vercel/next.js

Relatório de erro

Descreva o bug

Importar uma folha de estilo de um pacote não é possível em uma página, porque o próximo gera este erro:

Global CSS cannot be imported from files other than your Custom <App>. Please move all global CSS imports to pages/_app.tsx.
Read more: https://err.sh/next.js/css-global

Embora eu entenda de onde isso vem, torna a divisão de código impossível. Se eu importar um componente de uma biblioteca de componentes, também preciso importar o CSS. Pode haver bibliotecas que não definem o escopo correto de seus seletores, mas isso não deve me impedir de sobrescrever este aviso. O CSS importado de uma biblioteca não é inerentemente "global".

Reproduzir

  1. import "my-library/index.css"
  2. yarn dev
  3. Eu recebo o erro de cima

Comportamento esperado

O arquivo deve ser importado.

Eu vejo essas soluções possíveis:

  • Bandeira global em next.config.js
  • Anotação de comentário na importação
  • Avise em vez de bloqueá-lo completamente
  • Verifique se o CSS contém apenas seletores com escopo

Contexto adicional

Houve discussões anteriores sobre isso.

Do blog

Uma vez que as folhas de estilo são globais por natureza, elas devem ser importadas no Customcomponente. Isso é necessário para evitar conflitos de nome de classe e ordenação para estilos globais.

Não concordo com essa afirmação, o raciocínio é que uma biblioteca externa pode usar módulos CSS e empacotá-los como um arquivo CSS para importação. O que é uma prática perfeitamente válida e comum e não tem efeitos colaterais .

# 10059

Este problema foi resolvido porque a importação global em _app é a escolha correta aqui.
Este comentário descreve o problema exato, mas não houve resposta, pois o problema foi encerrado. O comentário teve muitas reações positivas, então suponho que não seja o único com esse problema.

# 10975

Parece não ter relação.

# 9830

Pode estar relacionado, mas não tenho certeza.

Meu caso de uso

Eu escrevo longos artigos com muitas artes personalizadas e ilustrações interativas. Os artigos usam pacotes npm privados com componentes react que renderizam SVG com um pouco de CSS. Esses pacotes usam módulos CSS e exportam index.js e index.css . Adicionar todos os arquivos CSS a _app faz com que todo o CSS seja carregado, mesmo se as pessoas estiverem na página inicial, no formulário de contato ou em qualquer outro artigo, mesmo que esteja 100% não utilizado. Também vai contra o sistema de arquivos cuidar de suas páginas porque quase todas as páginas correspondem a uma importação CSS em _app .

story 8 feature request

Comentários muito úteis

Vamos permitir a importação de CSS de node_modules para qualquer arquivo de componente na próxima semana (no canário)! Publicaremos aqui quando estiver pronto para teste.

Todos 49 comentários

Estou enfrentando o mesmo problema simplesmente tentando usar Linaria, que define seus próprios nomes de classe. Embora os arquivos css que ele produz não terminem em .module.css , eles são "módulos". Preciso de uma maneira fácil de me integrar com a biblioteca.

por que mudou para nextjs de novo?

Eu também gostaria de poder usar GlobalCSS fora de node_modules. Isso nos ajudaria a adotar módulos CSS de forma incremental

sim, isso é muito importante! muitos pacotes npm não funcionam com nextjs, mas funcionam com CRA ou outros frameworks

Para qualquer outra pessoa que esteja tentando usar isso com a implementação de dart sass 'js para coisas como @use support e módulos sass, se você tiver -qualquer- outro módulo de nó que dependa de node-sass, a próxima configuração padrão usará node-sass em vez de sass. Eu corrigi localmente fazendo o seguinte:

// example next.config.js
module.exports = {
webpack(config, options) {
  config.module.rules.forEach(rule => {
          if (rule.oneOf) {
            const nestedScss = rule.oneOf.find((one) => {
              return one.test
                && 'some.scss'.match(one.test) 
                && one.issuer 
                && one.issuer.include 
                && one.issuer.include.includes('_app');
            });
            if (nestedScss) {
              const sassLoader = nestedScss.use.find(u => u.loader.includes('sass-loader'));
              // Set implementation to sass instead of node-sass here.
              sassLoader.options.implementation = require('sass');
            }
          }
        })
  }
}

Em seguida, você precisará importar seus arquivos scss em _app.js .

@smurrayatwork isto é hackear, não codificar, desculpe

Além disso, a restrição de ser exclusivamente _app.js é um pouco complicada.

Se não vamos oferecer suporte a referências CSS em todos os lugares, poderíamos fazê-lo de forma que o CSS também possa ser referenciado por dependências diretas de _app (que não são referenciadas em nenhum outro lugar)?
ou seja, Tudo bem se for exigido por _app (e em nenhum outro lugar), o que daria ao CSS uma ordem determinística baseada nas importações.

Não é ideal, no entanto, o caso de uso que tenho é que tenho uma base de código compartilhada por vários aplicativos que importam um módulo compartilhado que importa CSS compartilhado. Eu odiaria duplicar essas importações CSS compartilhadas em _app.js para cada aplicativo. Atualmente, para contornar isso, eu teria que fazer uma metaprogramação js sofisticada porque não podemos exigir css em outros módulos.

Em vez disso, gostaria que minha abordagem atual funcionasse, que é ter um "App Factory" que importa todo o CSS compartilhado. _app então usa a fábrica e importa seu próprio CSS sobre os compartilhados.

Estou adicionando https://github.com/vercel/next.js/discussions/13991, pois acho que está relacionado a esse problema.

+100 para isso. Estou tendo que copiar e colar arquivos css de módulo de nó em meu projeto e adicionar um .module.css a eles

Aqui está outro exemplo.

No caso do pacote pdf-viewer-reactjs suas dependências requerem CSS que também precisa ser importado de _app.js .

Isso está sobrecarregando o CSS de todo o aplicativo e não tenho certeza sobre os conflitos neste estágio.

importar 'react-quill / dist / quill.snow.css';
importar 'react-image-crop / dist / ReactCrop.css';
import '../../node_modules/material-design-icons/iconfont/material-icons.css';
import '../../node_modules/bulma/css/bulma.css';
import '../../node_modules/bulma-helpers/css/bulma-helpers.min.css';

Além disso, o seguinte é enviado para o console:

avisar - ./node_modules/material-design-icons/iconfont/material-icons.css
CSS global não pode ser importado de node_modules.
Leia mais: https://err.sh/next.js/css-npm
Localização: node_modules / pdf-viewer-reactjs / dist / pdf-viewer-reactjs.js

./node_modules/bulma/css/bulma.css
CSS global não pode ser importado de node_modules.
Leia mais: https://err.sh/next.js/css-npm
Localização: node_modules / pdf-viewer-reactjs / dist / pdf-viewer-reactjs.js

./node_modules/bulma-helpers/css/bulma-helpers.min.css
CSS global não pode ser importado de node_modules.
Leia mais: https://err.sh/next.js/css-npm
Localização: node_modules / pdf-viewer-reactjs / dist / pdf-viewer-reactjs.js

./node_modules/material-design-icons/iconfont/material-icons.css
A construção do módulo falhou: Erro: o carregador final (./node_modules/next/dist/build/webpack/loaders/error-loader.js) não retornou um buffer ou string

./node_modules/bulma/css/bulma.css
A construção do módulo falhou: Erro: o carregador final (./node_modules/next/dist/build/webpack/loaders/error-loader.js) não retornou um buffer ou string

./node_modules/bulma-helpers/css/bulma-helpers.min.css
A construção do módulo falhou: Erro: o carregador final (./node_modules/next/dist/build/webpack/loaders/error-loader.js) não retornou um buffer ou string

Oi ! Alguém resolveu isso e como? Muitos módulos de nó que não consigo importar por causa disso.

Talvez o uso de estilos globais em componentes possa ser ativado por meio de next.config.js , ou um aviso de console feio contra estilos globais possa ser mostrado, caso haja preocupação em quebrar as melhores práticas / opiniões do NextJS.

Mas isso é importante para usuários que estão convertendo de CRA> NextJS. É um bloqueador para nós b / c não podemos mudar e, em seguida, adotar coisas como módulos CSS.

Ainda não consigo contornar isso. Para minhas próprias necessidades, usei um manipulador CSS personalizado, mas isso desabilita o suporte a CSS embutido, mas pode não ser uma boa solução para todos os casos. O seguinte é desencorajado , use apenas até que os autores do pacote resolvam

next.config.js

const withCSS = require('@zeit/next-css');
const withPlugins = require('next-compose-plugins');
...
module.exports = withPlugins([
...
withCSS,
]);

@abdelrahmantoptal 's Você sabe como fazer isso funcionar para o SASS?

Parece que funcionaria para CSS, mas gera um erro ao encontrar uma importação SASS:

error - ./src/components/layouts/Footer.scss 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
> <strong i="8">@import</strong> 'styles/vars';
| 
| footer {

então tentei adicionar um carregador SASS à configuração do webpack antes de usar o plug-in withCSS:

      config.module.rules.push({
        test: /\.s[ac]ss$/i,
        use: [
          // Creates `style` nodes from JS strings
          'style-loader',
          // Translates CSS into CommonJS
          'css-loader',
          // Compiles Sass to CSS
          'sass-loader'
        ]
      });

Mas isso causou:

error - ./src/components/App.scss
ReferenceError: self is not defined

Também tentei substituir @zeit/next-sass , mas isso causou o mesmo erro:

error - ./src/components/App.scss
ReferenceError: self is not defined

Alguma sugestão sobre como ajustar seu código para usar o SASS?

Eu agora descartei Gatsby e logo Next.js por causa de seus recursos opinativos pequenos, mas muito bloqueadores, como este. Agora não consigo usar o plug-in CodeBlock para CKEditor 5 porque não consigo evitar esse erro. Sempre deve haver alguma maneira de contornar essas configurações.

Seria muito útil ter notícias do @Timer ou de alguém da Vercel sobre este problema. Este é um grande problema com Next.js. Algo está planejado para consertar isso?

Acordado! É incrivelmente comum ter css em módulos de nó. Como desenvolvedor, não tenho controle sobre como outros desenvolvedores estruturam seus módulos de nó, e outros desenvolvedores não esperam que colocar css em um módulo de nó interrompa uma estrutura da web.

@OssiPesonen você já viu isso ? Essa solução alternativa não é ideal, mas resolveu o problema para mim nesse ínterim.

@OssiPesonen você já viu isso ? Essa solução alternativa não é ideal, mas resolveu o problema para mim nesse ínterim.

Não vejo como isso ajuda? O problema não é eu ter que importar manualmente alguns arquivos CSS de módulos de nó. O problema são os pacotes npm que fazem a importação de CSS por si próprios. Um pacote que inclui uma linha como esta:

import '../theme/stylesheet.css'

Fará com que next.js trave com força total. E, aparentemente, o conselho do mantenedor é:

Entre em contato com o mantenedor e peça que publique uma versão compilada de sua dependência.

Em que tipo de mundo da fantasia as pessoas vivem, onde imaginam que você pode simplesmente contatar os mantenedores e pedir-lhes que recompilem seus pacotes para você em um ritmo muito rápido? Isso vai atrapalhar qualquer pessoa por semanas! Este ingresso está aberto há 4 meses. Isso é inaceitável ao trabalhar em projetos de movimentação rápida.

Vamos permitir a importação de CSS de node_modules para qualquer arquivo de componente na próxima semana (no canário)! Publicaremos aqui quando estiver pronto para teste.

Se alguém precisar disso antes do lançamento, fui capaz de usar o plugin next-transpile-modules para transpilar o módulo de node_modules que estava importando CSS. Caiu como uma luva para mim.

@BrandonE parece que next-transpile-modules ainda precisa ter os módulos chamados *.module.css . Você encontrou uma maneira de contornar isso?

@rjoaopereira Não posso dizer que tenho um profundo entendimento de como isso funciona, mas a maior parte do meu node_modules que importou CSS só funcionou com o plug-in @zeit/next-css . Apenas um não, e nesse ponto a transpilação resolveu o problema. Longe de ser uma solução elegante, e espero que as versões futuras do Next.js nos permitam gastar menos tempo na alquimia Babel / Webpack e mais na criação de aplicativos da web.

Consegui isso quase funcionando com as seguintes alterações.

próximo 9.5.3
next-transpile-modules 4.1.0
Componentes de primeira parte com emoção.
Componentes de terceiros com uma mistura de módulos css e css global

scopedcomponents deve ser substituído pelos componentes de terceiros usados

//next.config.js
const withCustomWebpack = require("./webpack-custom.config");
const withNextCSSOverride = require("./next.config.css");
const withTM = require("next-transpile-modules")(["@scopedcomponents"]);

module.exports = withCustomWebpack(
  withTM(
    withNextCSSOverride({
      poweredByHeader: false
    })
  )
);

///next.config.css.js
const {
  getCssModuleLocalIdent
} = require("next/dist/build/webpack/config/blocks/css/loaders/getCssModuleLocalIdent");
const path = require("path");
/**
 * Stolen from https://stackoverflow.com/questions/10776600/testing-for-equality-of-regular-expressions
 */
const regexEqual = (x, y) => {
  return (
    x instanceof RegExp &&
    y instanceof RegExp &&
    x.source === y.source &&
    x.global === y.global &&
    x.ignoreCase === y.ignoreCase &&
    x.multiline === y.multiline
  );
};

module.exports = (nextConfig = {}) => {
  return Object.assign({}, nextConfig, {
    webpack(config, options) {
      const nextCssLoaders = config.module.rules.find(
        rule => typeof rule.oneOf === "object"
      );

      if (nextCssLoaders) {
        const nextCssLoader = nextCssLoaders.oneOf.find(
          rule =>
            rule.sideEffects === false &&
            regexEqual(rule.test, /\.module\.css$/)
        );

        if (nextCssLoader) {
          /***********************************************************
           * change the rule to match all scopedcomponents css files
           ***********************************************************/
          nextCssLoader.test = /(@scopedcomponents|react\-virtualized)\/.*\.css$/;

          const cssLoader = nextCssLoader.use.find(({ loader }) =>
            loader.includes("css-loader")
          );

          if (cssLoader) {
            /***********************************************************
             * Override the default behaviour for CSS modules discovery
             * auto = true makes webpack search for *.module.css
             * https://webpack.js.org/loaders/css-loader/#auto
             ***********************************************************/
            cssLoader.options.modules.auto = /@scopedcomponents\/.*\.css$/;
            /***********************************************************
             * Nextjs overrides the default mode to "Pure"
             * https://github.com/vercel/next.js/blob/v9.5.2/packages/next/build/webpack/config/blocks/css/loaders/modules.ts#L35
             * Put it back to normal
             ***********************************************************/
            cssLoader.options.modules.mode = "local";
            /***********************************************************************************************************************
             * There is a problem when using components built with css-modules with Nextjs.                                        *
             * NextJS will consume code from `lib` on the server side and from `es` on the client.                                 *
             * https://github.com/vercel/next.js/blob/v9.5.2/packages/next/build/webpack-config.ts#L374                            *
             * This raises a problem when generating the classes for different environments,                                       *
             * throwing an error of className mismatch due to the hash created being based on the file path                        *
             * https://github.com/vercel/next.js/blob/v9.5.2/packages/next/build/webpack/config/blocks/css/loaders/modules.ts#L26  *
             * https://github.com/webpack/loader-utils/blob/v1.4.0/lib/interpolateName.js#L39                                      *
             * To solve this, when generating the classNames for 3rd party components,                                                 *
             * we need to tell cssloader to always use the same path                                                               *                                                                          *
             *                                                                                                                     *
             *  https://github.com/zeit/next-plugins/issues/595                                                                    *
             ***********************************************************************************************************************/
            cssLoader.options.modules.getLocalIdent = (
              context,
              localIdentName,
              localName,
              options
            ) => {
              const newContext = { ...context };
              if (newContext.resourcePath.includes("@scopedcomponents")) {
                newContext.resourcePath = newContext.resourcePath.replace(
                  `${path.sep}es${path.sep}`,
                  `${path.sep}lib${path.sep}`
                );
              }
              return getCssModuleLocalIdent(
                newContext,
                localIdentName,
                localName,
                options
              );
            };
          }
        }
      }

      if (typeof nextConfig.webpack === "function") {
        return nextConfig.webpack(config, options);
      }

      return config;
    }
  });
};

Problemas:

@Timer alguma atualização sobre isso?

Vamos permitir a importação de CSS de node_modules para qualquer arquivo de componente na próxima semana (no canário)! Publicaremos aqui quando estiver pronto para teste.

Haverá importação dinâmica de CSS de um componente após essa correção?

Muito obrigado @Timer

next@^9.5.4-canary.10 agora permite que você importe CSS global de node_modules em qualquer lugar em seu aplicativo. Isso melhora a interoperabilidade com bibliotecas React de terceiros que exigem a importação de CSS, mas não querem aumentar o tamanho do pacote para todo o seu aplicativo.

@Timer Mal posso esperar por esse lançamento, gostei muito do seu trabalho 💯 ❤️

Obrigado @Timer !

Este tem sido um problema de bloqueio para mim atualmente, no entanto, quando testei isso hoje, ainda vejo a mesma mensagem de erro. Existe algo mais do que simplesmente atualizar para 9.5.4-canary-10? Este exemplo está tentando usar lib @rmwc de terceiros

image

@johmike Você está importando usando a seguinte sintaxe ??

import "@rmwc/avatar/avatar.css";

Você tentou reiniciar o servidor de desenvolvimento depois de instalar a versão mais recente de next ?

@Timer Muito obrigado por este recurso. Funciona muito bem para importar arquivos CSS da pasta node_modules .

import 'prism-themes/themes/prism-darcula.css';

Algum plano para suportar a importação de css global fora do node_modules dir?

@sasivarnan
Isso está vindo de outra biblioteca que está importando os componentes @rmwc . Essa biblioteca está usando @require("@rmwc/avatar/avatar.css") . Estou importando import {Avatar} from "library/Avatar" e isso está falhando.

@sasivarnan

Isso está vindo de outra biblioteca que está importando os componentes @rmwc . Essa biblioteca está usando @require("@rmwc/avatar/avatar.css") . Estou importando import {Avatar} from "library/Avatar" e isso está falhando.

Entendi. Achei que tivesse sido importado diretamente em seu aplicativo. Foi mal.

A julgar pelos comentários aqui, isso na verdade não foi resolvido ou foi resolvido, mas muitas pessoas aqui relatam um problema diferente. Muitas pessoas ainda não conseguem importar módulos, que importam CSS do próprio pacote (uma instrução import style.css dentro de um arquivo de pacote).

A correção parece permitir que o aplicativo importe CSS do caminho node_modules/ , mas há uma maneira bem fácil de contornar isso: apenas copie o CSS para o seu aplicativo por enquanto até que seja corrigido. Não é um problema de nível de bloqueador. Portanto, isso não resolveu realmente o problema do bloqueador, para o qual não há solução fácil. Se você importar um componente que possui uma instrução de importação para um arquivo CSS que o próprio pacote contém, o aplicativo falha.

@sasivarnan @OssiPesonen, vocês dois parecem estar falando sobre um problema diferente do que estava sendo discutido e corrigido neste problema do OP.

Isso corrige especificamente as bibliotecas que exigem a importação de CSS em seu aplicativo, por exemplo:

// components/MySlider.tsx
import { Slider } from "@reach/slider";
import "@reach/slider/styles.css";

function Example() {
  return <Slider min={0} max={200} step={10} />;
}

Você está falando sobre uma duplicata de # 706 e # 13282, ou a capacidade de tratar node_modules como código primário.

@Timer Acabei de testar o caso de uso esperado e realmente funciona bem.

Quando eu importo o css em um componente diretamente como parte da próxima estrutura, ele funciona conforme o esperado, sem erros.
No entanto, se eu mover esse componente para outro pacote fora da próxima estrutura, construir e instalar esse pacote, ele retornará ao mesmo erro de antes.

É possível que outra coisa esteja acontecendo, já que nem estou usando o componente Avatar neste exemplo, estou importando Button e, ainda assim, Avatar é o erro que está falhando.

image

Também adicionei next-transpile-modules porque estamos trabalhando em um monorepo, mas isso não parece ajudar neste problema em particular.

Eu consegui fazer isso funcionar por meio de um arquivo de configuração estranho, vasculhando vários outros problemas em torno de next-transpile-modules .

const withCSS = require("@zeit/next-css");
module.exports = withCSS();
require.extensions[".css"] = () => {
  return;
};

Eliminei next-transpile-modules e isso funciona. Não tenho ideia do porquê, parece que não deveria fazer nada?

Falei muito cedo! Embora isso funcione para next dev , next build falha com um erro unknown token . (ponto) de um dos arquivos CSS.

@Timer Alguma

Você pode seguir https://github.com/vercel/next.js/issues/13282 para esse comportamento.

Usando [email protected] é possível importar css em qualquer lugar em meu aplicativo. Mas será o mesmo possível para arquivos scss? Gostaria de importar apenas os arquivos scss que estou realmente usando em uma página .

// pages / _app.tsx
import '../styles/common.scss'

// pages / index.tsx Eu uso um botão
import '@mynpm/custom-ui/_Button.scss'

// pages / about.tsx Eu uso um carrossel
import '@mynpm/custom-ui/_Carousel.scss'

O exemplo em https://nextjs.org/docs/basic-features/built-in-css-support
Schermata 2020-10-13 alle 16 43 19

Retorna o erro:
erro - /Users/gp/dev/next-kolumbus/node_modules/@reach/dialog/styles.css
CSS global não pode ser importado de node_modules.
Leia mais: https://err.sh/next.js/css-npm

O exemplo em https://nextjs.org/docs/basic-features/built-in-css-support
Schermata 2020-10-13 alle 16 43 19

Retorna o erro:
erro - /Users/gp/dev/next-kolumbus/node_modules/@reach/dialog/styles.css
CSS global não pode ser importado de node_modules.
Leia mais: https://err.sh/next.js/css-npm

Certifique-se de que está usando a versão mais recente do Next.js.

Desculpe, eu não especifiquei no comentário anterior. Usei a versão 9.5.5. acabou de ser atualizado do npm.

Limpei todo o cache .next e agora funciona conforme o esperado.

O erro ainda existe na versão 9.5.5 , em _app -> import "react-gauge-chart-nextjs-support/dist/GaugeChart/style.css";

Screenshot 2020-11-12 at 14 12 11

Esta página foi útil?
0 / 5 - 0 avaliações