A restrição create-react-app importa fora do diretório src

149

Estou usando o create-react-app. Estou tentando chamar uma imagem da minha pasta pública de um arquivo dentro do meu src/components. Estou recebendo esta mensagem de erro.

./src/components/website_index.js Módulo não encontrado: você tentou importar ../../public/images/logo/WC-BlackonWhite.jpg que fica fora do diretório src / do projeto. As importações relativas fora de src / não são suportadas. Você pode movê-lo para dentro de src / ou adicionar um link simbólico a partir de node_modules / do projeto.

import logo from '../../public/images/logo_2016.png'; <img className="Header-logo" src={logo} alt="Logo" />

Eu li muitas coisas dizendo que você pode fazer uma importação para o caminho, mas isso ainda não está funcionando para mim. Qualquer ajuda seria muito apreciada. Sei que há muitas perguntas como essa, mas todas estão me dizendo para importar o logotipo ou a imagem, de modo que estou perdendo algo no quadro geral.

David Brierton
fonte
2
Você precisa ../public/images/logo_2016.pngSubiu duas vezes, primeiro na pasta de componentes e depois na pasta src.
Chris G
./src/components/website_index.js Módulo não encontrado: você tentou importar ../../public/images/logo/WC-BlackonWhite.jpg que fica fora do diretório src / do projeto. As importações relativas fora de src / não são suportadas. Você pode movê-lo para dentro de src / ou adicionar um link simbólico a partir de node_modules / do projeto.
David Brierton
Meu comentário pressupõe que sua publicpasta esteja diretamente dentro srcdela. O seu comentário sem comentários apresenta o caminho antigo, começando com, ../..para não ter certeza de qual é o seu ponto?
Chris G
3
Nenhum público está no mesmo nível que src
David Brierton
O que eles querem dizer com "ou adicionar um link simbólico a partir de node_modules /" do projeto?
Julha

Respostas:

127

Essa é uma restrição especial adicionada pelos desenvolvedores do create-react-app. Ele é implementado ModuleScopePluginpara garantir que os arquivos residam src/. Esse plug-in garante que as importações relativas do diretório de origem do aplicativo não cheguem fora dele.

Você pode desativar esse recurso, mas somente após a ejectoperação do projeto create-react-app.

A maioria dos recursos e suas atualizações estão ocultos nas partes internas do sistema create-react-app. Se você criar eject, não terá mais alguns recursos e sua atualização. Portanto, se você não estiver pronto para gerenciar e configurar o aplicativo incluído para configurar o webpack e assim por diante - não faça a ejectoperação.

Jogue de acordo com as regras existentes (vá para src). Mas agora você pode saber como remover restrições: faça ejecte remova ModuleScopePlugindo arquivo de configuração do webpack .


Desde create- react -app v0.4.0, a NODE_PATHvariável de ambiente permite especificar um caminho para importação absoluta. E desde que a v3.0.0 NODE_PATH foi descontinuada em favor da configuração baseUrlem jsconfig.jsonou tsconfig.json.

A importação absoluta permite o uso import App from 'App'em import App from './App'relação ao valor especificado no URL base.

Esse recurso é especificamente útil para monorepos ou outras questões de configuração, mas não para importar imagens ou qualquer outra coisa da publicpasta.

O conteúdo da publicpasta será colocado na buildpasta e disponível por URL relativo. Além disso, tudo o que for importado será processado pelo webpack e também será colocado na buildpasta.

Se você importar algo da publicpasta, provavelmente esse item será duplicado na buildpasta e estará disponível por dois URLs diferentes (ou com formas diferentes de carregar), o que piorará o tamanho do download do pacote.

A importação da pasta src é preferível e possui vantagens. Tudo será empacotado pelo webpack no pacote com tamanho ideal para os pedaços e para a melhor eficiência de carregamento .


Existem soluções intermediárias, a saber, o sistema de religação , que permite modificar programaticamente a configuração do webpack. Mas remover o ModuleScopePluginplugin não é uma boa solução; é melhor adicionar diretórios adicionais totalmente funcionais semelhantes a src.

Atualmente, create-react-appnão suporta diretórios adicionais além srcda pasta raiz. Isso pode ser feito usando react-app-rewire-alias

oklas
fonte
3
se você criar um link simbólico em ./src e importar a partir daí - a compilação não funcionará (o plug-in babel não transforma fontes em pastas com links simbólicos). Portanto, com essa restrição, on e no-symlink sob src, você é efetivamente colocado em uma prisão 'sem código de compartilhamento com outros projetos' (a menos que você escolha emigrar / ejetar completamente do CRA)
VP
2
@VP, mas o erro diz "adicionar um link simbólico a partir de node_modules /." Do projeto, isso não funciona?
Adrianmc
Eles devem fazer um sinalizador para desativá-lo ao executar create-react-apppara quem não deseja ejetar a configuração do webpack. Pessoalmente, eu sempre ejeto, mas algumas pessoas ainda não se sentem confortáveis ​​ao mexer com os webpacks assustadores da configuração.
TetraDev 9/0818
2
@adrianmc. adicionar link simbólico a partir de node_modules do projeto não funciona, porque muitos projetos usam componentes / códigos de compartilhamento que não são node_modules. Por exemplo, compartilho código entre o React Native e o React Native Web. Esses 'snippets' não são node_modules, e esses 2 projetos têm realmente node_modules diferentes.
VP
1
Como é essa a resposta aceita? Essa restrição falsa é trivialmente eliminada simplesmente configurando NODE_PATH=./src/..o .envarquivo. Ao fazer isso, você pode importar de fora da pasta src sem passar pela dor associada à ejeção do aplicativo.
Flaom
47

O pacote react-app-rewired pode ser usado para remover o plugin. Dessa forma, você não precisa ejetar.

Siga as etapas na página do pacote npm (instale o pacote e ative as chamadas no arquivo package.json) e use um config-overrides.jsarquivo semelhante a este:

const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');

module.exports = function override(config, env) {
    config.resolve.plugins = config.resolve.plugins.filter(plugin => !(plugin instanceof ModuleScopePlugin));

    return config;
};

Isso removerá o ModuleScopePlugin dos plugins usados ​​do WebPack, mas deixará o resto como estava e remove a necessidade de ejetar.

Lukas Bach
fonte
5
Ótima resposta. Você pode aproveitar ainda mais o react-app-rewired[ github.com/arackaf/customize-craunette(customize-cra) . Então a configuração usará apenas babelInclude([path.resolve('src'), path.resolve('../common')])e removeModuleScopePlugin().
Ivoh 20/04/19
você pode confirmar onde devo colocar esse trecho de código?
bijayshrestha
1
@bijayshrestha Leia o arquivo leia-me do projeto react-app-rewired, onde ele explica como configurá-lo. Durante a instalação, você criará um config-overrides.jsarquivo no qual poderá inserir o código.
Lukas Bach
Remover o ModuleScopePluginplugin não é uma boa ideia. É melhor adicionar diretórios adicionais totalmente funcionais, semelhantes ao srcuso de
react
1
Esta solução funciona com o Typecript? Não consigo fazê-lo funcionar com o TS.
user3053247 2/07
27

Para oferecer um pouco mais de informação às respostas de outras pessoas. Você tem duas opções sobre como entregar o arquivo .png ao usuário. A estrutura do arquivo deve estar de acordo com o método escolhido. As duas opções são:

  1. Use o módulo system ( import x from y) fornecido com o react-create-app e empacote-o com o seu JS. Coloque a imagem dentro da srcpasta.

  2. Sirva-o da publicpasta e deixe o Node servir o arquivo. O create-react-app também aparentemente vem com uma variável de ambiente, por exemplo <img src={process.env.PUBLIC_URL + '/img/logo.png'} />;. Isso significa que você pode referenciá-lo no aplicativo React, mas ainda o veicular no Node, com o navegador solicitando-o separadamente em uma solicitação GET normal.

Fonte: create-react-app

danielgormly
fonte
A sugestão 2 é exatamente o que causa o seguinte erro para mim: Módulo não encontrado: você tentou importar ./../../../public/CheersBar-Drinks-147.jpg que fica fora do diretório src / do projeto . As importações relativas fora de src / não são suportadas. Você pode movê-lo para dentro de src / ou adicionar um link simbólico a partir de node_modules / do projeto.
Amos Long
25

Se suas imagens estiverem na pasta pública, você deverá usar

"/images/logo_2016.png"

no seu em <img> srcvez de importar

'../../public/images/logo_2016.png'; 

Isso vai funcionar

<img className="Header-logo" src="/images/logo_2016.png" alt="Logo" />
Abhinav bhardwaj
fonte
2
Não está trabalhando para mim. Recebendo a mesma mensagem - 'fora do diretório src / do projeto. As importações relativas fora de src / não são suportadas. '
Arkady
A sua resposta está correta IMO, mas tomei a liberdade de esclarecer que você está falando sobre o caminho no <img src="...">, não importá-lo
Dmitry Yudakov
1
Era exatamente isso que eu estava procurando! Simplesmente adicionei uma pasta "images" ao diretório "src" do meu CRA e pude usá-lo.<img src="/images/logo.png" alt="Logo" />
Amos Long
12

Você precisa entrar WC-BlackonWhite.jpgno seu srcdiretório. O publicdiretório é para arquivos estáticos que serão vinculados diretamente no HTML (como o favicon), não para itens que você importará diretamente para o seu pacote.

Joe Clay
fonte
Eu já vi outros fazerem isso dessa maneira. É por isso que eu pensei que esta é a maneira correta
David Brierton
@DavidBrierton: Essa precaução pode ter sido adicionada em uma versão mais recente do create-react-app.
Joe Clay
8

Existem algumas respostas que fornecem soluções react-app-rewired, mas customize-craexpõem uma removeModuleScopePlugin()API especial que é um pouco mais elegante. (É a mesma solução, mas abstraída pelo customize-crapacote.)

npm i --save-dev react-app-rewired customize-cra

package.json

"scripts": {
    - "start": "react-scripts start"
    + "start": "react-app-rewired start",
    ...
},

config-overrides.js

const { removeModuleScopePlugin } = require('customize-cra')

module.exports = removeModuleScopePlugin()
Avi Nerenberg
fonte
1
você salvou meu dia!
lmiguelvargasf
5

Remova-o usando o Craco:

module.exports = {
  webpack: {
    configure: webpackConfig => {
      const scopePluginIndex = webpackConfig.resolve.plugins.findIndex(
        ({ constructor }) => constructor && constructor.name === 'ModuleScopePlugin'
      );

      webpackConfig.resolve.plugins.splice(scopePluginIndex, 1);
      return webpackConfig;
    }
  }
};
nevace
fonte
Vote no craco! Craco suporta CRA 3.x
Roman Podlinov
4

Essa restrição garante que todos os arquivos ou módulos (exportações) estejam dentro do src/diretório em que a implementação está ./node_modules/react-dev-utils/ModuleScopePlugin.js, nas seguintes linhas de código.

// Resolve the issuer from our appSrc and make sure it's one of our files
// Maybe an indexOf === 0 would be better?
     const relative = path.relative(appSrc, request.context.issuer);
// If it's not in src/ or a subdirectory, not our request!
     if (relative.startsWith('../') || relative.startsWith('..\\')) {
        return callback();
      }

Você pode remover esta restrição

  1. alterando este trecho de código (não recomendado)
  2. ou nãoeject , em seguida, remover ModuleScopePlugin.jsa partir do diretório.
  3. ou comentar / remover const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');de./node_modules/react-scripts/config/webpack.config.dev.js

PS: cuidado com as conseqüências de ejeção .

its4zahoor
fonte
Obrigado @SurajRao, mudei para aqui. Espero que seja melhor.
its4zahoor
@PattycakeJr Sim, é por isso que abaixo eu disse para ter cuidado com as consequências da ejeção.
its4zahoor
3

instale esses dois pacotes

npm i --save-dev react-app-rewired customize-cra

package.json

"scripts": {
    - "start": "react-scripts start"
    + "start": "react-app-rewired start"
},

config-overrides.js

const { removeModuleScopePlugin } = require('customize-cra');

module.exports = function override(config, env) {
    if (!config.plugins) {
        config.plugins = [];
    }
    removeModuleScopePlugin()(config);

    return config;
};
Muhammad Numan
fonte
2

Você não precisa ejetar, você pode modificar a react-scriptsconfiguração com a biblioteca de rescripts

Isso funcionaria então:

module.exports = config => {
  const scopePluginIndex = config.resolve.plugins.findIndex(
    ({ constructor }) => constructor && constructor.name === "ModuleScopePlugin"
  );

  config.resolve.plugins.splice(scopePluginIndex, 1);

  return config;
};
Lukas
fonte
2

Acho que a solução Lukas Bach para usar o react -app- rewired para modificar a configuração do webpack é um bom caminho, no entanto, não excluiria todo o ModuleScopePlugin, mas coloque na lista branca o arquivo específico que pode ser importado fora do src:

config-overrides.js

const ModuleScopePlugin = require("react-dev-utils/ModuleScopePlugin");
const path = require("path");

module.exports = function override(config) {
  config.resolve.plugins.forEach(plugin => {
    if (plugin instanceof ModuleScopePlugin) {
      plugin.allowedFiles.add(path.resolve("./config.json"));
    }
  });

  return config;
};
Bartek Maciejiczek
fonte
Essa é a melhor solução até agora.
adi518 17/02
2

Imagem dentro da pasta pública

  use image inside html extension
  <img src="%PUBLIC_URL%/resumepic.png"/>

  use image inside  js extension
  <img src={process.env.PUBLIC_URL+"/resumepic.png"}/>
  • usar imagem dentro da extensão js
Yziaf_07
fonte
1

Se você precisar importar apenas um único arquivo, como README.md ou package.json, isso poderá ser explicitamente adicionado ao ModuleScopePlugin ()

config / paths.js

const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
module.exports = {
  appPackageJson: resolveApp('package.json'),
  appReadmeMD:    resolveApp('README.md'),
};

config / webpack.config.dev.js + config / webpack.config.prod.js

module.exports = {
  resolve: {
    plugins: [
      // Prevents users from importing files from outside of src/ (or node_modules/).
      // This often causes confusion because we only process files within src/ with babel.
      // To fix this, we prevent you from importing files out of src/ -- if you'd like to,
      // please link the files into your node_modules/ and let module-resolution kick in.
      // Make sure your source files are compiled, as they will not be processed in any way.
      new ModuleScopePlugin(paths.appSrc, [
          paths.appPackageJson,
          paths.appReadmeMD         // README.md lives outside of ./src/ so needs to be explicitly included in ModuleScopePlugin()
      ]),
    ]
  }
}
James McGuigan
fonte
3
Isso requer a primeira ejeção do create-react-app, não?
Beau Smith
1

a melhor solução é bifurcar react-scripts, isso é realmente mencionado na documentação oficial, consulte: Alternativas à ejeção

Alnamrouti com tarifa
fonte
1

Se você precisar de várias modificações, como ao usar o design de formigas , poderá combinar várias funções como esta:

const {
  override,
  removeModuleScopePlugin,
  fixBabelImports,
} = require('customize-cra');

module.exports = override(
  fixBabelImports('import', {
    libraryName: 'antd',
    libraryDirectory: 'es',
    style: 'css',
  }),
  removeModuleScopePlugin(),
);
Denis Beklarov
fonte
1

Adicionando à resposta de Bartek Maciejiczek, é assim que fica com o Craco:

const ModuleScopePlugin = require("react-dev-utils/ModuleScopePlugin");
const path = require("path");

module.exports = {
  webpack: {
    configure: webpackConfig => {
      webpackConfig.resolve.plugins.forEach(plugin => {
        if (plugin instanceof ModuleScopePlugin) {
          plugin.allowedFiles.add(path.resolve("./config.json"));
        }
      });
      return webpackConfig;
    }
  }
};

adi518
fonte