La restriction create-react-app importe en dehors du répertoire src

151

J'utilise create-react-app. J'essaye d'appeler une image de mon dossier public à partir d'un fichier dans mon src/components. Je reçois ce message d'erreur.

./src/components/website_index.js Module non trouvé: Vous avez tenté d'importer ../../public/images/logo/WC-BlackonWhite.jpg qui se trouve en dehors du répertoire src / du projet. Les importations relatives en dehors de src / ne sont pas prises en charge. Vous pouvez soit le déplacer dans src /, soit y ajouter un lien symbolique à partir de node_modules / du projet.

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

J'ai lu beaucoup de choses disant que vous pouvez faire une importation sur le chemin, mais cela ne fonctionne toujours pas pour moi. Toute aide serait grandement appréciée. Je sais qu'il y a beaucoup de questions comme celle-ci, mais elles me disent toutes d'importer un logo ou une image si clairement qu'il me manque quelque chose dans la vue d'ensemble.

David Brierton
la source
2
Vous avez besoin ../public/images/logo_2016.pngVous êtes monté deux fois, d'abord hors du dossier des composants, puis hors du dossier src.
Chris G
./src/components/website_index.js Module non trouvé: Vous avez tenté d'importer ../../public/images/logo/WC-BlackonWhite.jpg qui se trouve en dehors du répertoire src / du projet. Les importations relatives en dehors de src / ne sont pas prises en charge. Vous pouvez soit le déplacer dans src /, soit y ajouter un lien symbolique à partir de node_modules / du projet.
David Brierton
Mon commentaire suppose que votre publicdossier se trouve directement dans votre srcdossier. Votre commentaire sans commentaire présente l'ancien chemin commençant par ../..donc vous ne savez pas quel est votre point de vue?
Chris G
3
aucun public n'est au même niveau que src
David Brierton
Que veulent-ils dire par "ou ajouter un lien symbolique vers celui-ci à partir des node_modules / du projet"?
Julha

Réponses:

128

Il s'agit d'une restriction spéciale ajoutée par les développeurs de create-react-app. Il est implémenté dans ModuleScopePluginpour garantir que les fichiers résident dans src/. Ce plugin garantit que les importations relatives depuis le répertoire source de l'application n'atteignent pas en dehors de celui-ci.

Vous pouvez désactiver cette fonctionnalité mais uniquement après l' ejectopération du projet create-react-app.

La plupart des fonctionnalités et ses mises à jour sont cachées dans les composants internes du système create-react-app. Si vous faites ejectvous n'aurez plus certaines fonctionnalités et sa mise à jour. Donc, si vous n'êtes pas prêt à gérer et configurer l'application incluse pour configurer Webpack et ainsi de suite, n'effectuez pas l' ejectopération.

Jouez avec les règles existantes (passez à src). Mais maintenant vous pouvez savoir comment supprimer la restriction: faire ejectet supprimer ModuleScopePlugindu fichier de configuration Webpack .


Depuis create- react -app v0.4.0, la NODE_PATHvariable d'environnement permet de spécifier un chemin pour l'importation absolue. Et puisque la v3.0.0 NODE_PATH est obsolète au profit de la configuration baseUrlde jsconfig.jsonou tsconfig.json.

L'importation absolue permet d'utiliser à la import App from 'App'place par import App from './App'rapport à la valeur spécifiée dans l'URL de base.

Cette fonctionnalité est particulièrement utile pour les monorepos ou autres questions de configuration, mais pas pour importer des images ou quoi que ce soit d'autre à partir d'un publicdossier.

Le contenu du publicdossier sera placé dans le builddossier et disponible par URL relative. De plus, tout ce qui est importé sera traité par webpack et sera également placé dans un builddossier.

Si vous importez quelque chose à partir d'un publicdossier, ce dernier sera probablement dupliqué dans le builddossier et sera disponible par deux URL différentes (ou avec différentes façons de charger), ce qui finira par aggraver la taille de téléchargement du package.

L'importation depuis le dossier src est préférable et présente des avantages. Tout sera emballé par webpack dans le bundle avec des morceaux de taille optimale et pour une meilleure efficacité de chargement .


Il existe des solutions intermédiaires, à savoir le système rewire qui vous permet de modifier par programmation la configuration du webpack. Mais supprimer le ModuleScopePluginplugin n'est pas une bonne solution; il est préférable d'ajouter des répertoires supplémentaires entièrement fonctionnels similaires à src.

Actuellement, create-react-appne prend pas en charge les répertoires supplémentaires autres que srcdans le dossier racine. Cela peut être fait en utilisant react-app-rewire-alias

oklas
la source
3
si vous créez un lien symbolique dans ./src, et importez à partir de là - la construction ne fonctionne pas (le plugin babel ne transforme pas les sources en dossiers liés par un lien symbolique). Donc, avec cette restriction, sur et sans lien symbolique sous src, vous êtes effectivement placé dans une prison `` sans partage de code avec d'autres projets '' (à moins que vous ne choisissiez complètement d'émigrer / éjecter hors de l'ARC)
VP
2
@VP mais l'erreur dit "ajoutez un lien symbolique vers celui-ci à partir des node_modules / du projet", cela ne fonctionne pas?
adrianmc
Ils devraient créer un indicateur pour désactiver cela lors de l'exécution create-react-apppour ceux qui ne veulent pas éjecter la configuration de webpack. Personnellement, j'éjecte toujours, mais certaines personnes ne se sentent pas à l'aise encore avec une configuration intimidante de webpacks.
TetraDev
2
@adrianmc. l'ajout d'un lien symbolique à partir des node_modules du projet ne fonctionne pas, car de nombreux projets utilisent des composants / code de partage qui ne sont pas des node_modules. Par exemple, je partage du code entre React Native et React natif web. Ces 'snippets' ne sont pas des node_modules, et ces 2 projets ont en fait des node_modules différents.
VP
1
Comment est-ce la réponse acceptée? Cette fausse restriction est éliminée de manière triviale en définissant simplement NODE_PATH=./src/..dans le .envfichier. Ce faisant, vous pouvez importer depuis l'extérieur du dossier src sans passer par la douleur associée à l'éjection de votre application.
Flaom
47

Le package react-app-rewired peut être utilisé pour supprimer le plugin. De cette façon, vous n'avez pas à vous éjecter.

Suivez les étapes de la page du package npm (installez le package et retournez les appels dans le fichier package.json) et utilisez un config-overrides.jsfichier similaire à celui-ci:

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

Cela supprimera le ModuleScopePlugin des plugins WebPack utilisés, mais laissera le reste tel quel et supprimera la nécessité de l'éjecter.

Lukas Bach
la source
5
Très bonne réponse. Vous pouvez en tirer davantage parti react-app-rewiredavec [ github.com/arackaf/customize-cra unity ( customize-cra) . Ensuite, la configuration utilisera juste babelInclude([path.resolve('src'), path.resolve('../common')])et removeModuleScopePlugin().
ivosh
pouvez-vous s'il vous plaît confirmer où dois-je placer cet extrait de code?
bijayshrestha
1
@bijayshrestha Lisez le fichier readme du projet react-app-rewired, il explique comment le configurer. Lors de l'installation, vous allez créer un config-overrides.jsfichier dans lequel vous pourrez placer le code.
Lukas Bach
La suppression du ModuleScopePluginplugin n'est pas une bonne idée. Il est préférable d'ajouter des répertoires supplémentaires entièrement fonctionnels similaires à l' srcutilisation de react-app-rewire-alias
oklas
1
Cette solution fonctionne-t-elle avec Typescript? Je ne peux pas le faire fonctionner avec TS.
user3053247 le
27

Pour offrir un peu plus d'informations aux réponses des autres. Vous avez deux options concernant la façon de fournir le fichier .png à l'utilisateur. La structure du fichier doit être conforme à la méthode choisie. Les deux options sont:

  1. Utilisez le module system ( import x from y) fourni avec react-create-app et associez-le à votre JS. Placez l'image dans le srcdossier.

  2. Servez-le à partir du publicdossier et laissez Node servir le fichier. create-react-app est également apparemment livré avec une variable d'environnement, par exemple <img src={process.env.PUBLIC_URL + '/img/logo.png'} />;. Cela signifie que vous pouvez le référencer dans votre application React tout en le faisant servir via Node, votre navigateur le demandant séparément dans une requête GET normale.

Source: create-react-app

danielgormly
la source
La suggestion n ° 2 est exactement ce qui cause l'erreur suivante pour moi: Module non trouvé: vous avez tenté d'importer ./../../../public/CheersBar-Drinks-147.jpg qui ne relève pas du répertoire src / du projet . Les importations relatives en dehors de src / ne sont pas prises en charge. Vous pouvez soit le déplacer dans src /, soit y ajouter un lien symbolique à partir de node_modules / du projet.
Amos Long
25

Si vos images sont dans le dossier public, vous devez utiliser

"/images/logo_2016.png"

dans votre <img> srcau lieu d'importer

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

Cela fonctionnera

<img className="Header-logo" src="/images/logo_2016.png" alt="Logo" />
Abhinav Bhardwaj
la source
2
Ne travaille pas pour moi. Obtenir le même message - 'en dehors du répertoire src / du projet. Les importations relatives en dehors de src / ne sont pas prises en charge. '
Arkady
Votre réponse est correcte IMO mais j'ai pris la liberté de préciser que vous parlez du chemin <img src="...">, pas de l'importer
Dmitry Yudakov
1
C'était exactement ce que je cherchais! J'ai simplement ajouté un dossier "images" au répertoire "src" de mon ARC, puis j'ai pu utiliser<img src="/images/logo.png" alt="Logo" />
Amos Long
12

Vous devez vous déplacer WC-BlackonWhite.jpgdans votre srcrépertoire. Le publicrépertoire est destiné aux fichiers statiques qui seront liés directement dans le HTML (comme le favicon), et non aux fichiers que vous allez importer directement dans votre bundle.

Joe Clay
la source
J'ai vu d'autres le faire de cette façon. C'est pourquoi j'ai pensé que c'était la bonne manière
David Brierton
@DavidBrierton: Cette précaution a peut-être été ajoutée dans une version plus récente de create-react-app.
Joe Clay
8

Il y a quelques réponses qui fournissent des solutions react-app-rewired, mais qui customize-craexpose une removeModuleScopePlugin()API spéciale qui est un peu plus élégante. (C'est la même solution, mais abstraite par le customize-crapackage.)

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
la source
1
tu as sauvé ma journée!
lmiguelvargasf
6

Retirez-le en utilisant 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
la source
Votez pour le craco! Craco soutient CRA 3.x
Roman Podlinov
4

Cette restriction garantit que tous les fichiers ou modules (exportations) sont à l'intérieur du src/répertoire, l'implémentation est dans ./node_modules/react-dev-utils/ModuleScopePlugin.js, dans les lignes de code suivantes.

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

Vous pouvez supprimer cette restriction en

  1. soit en changeant ce morceau de code (non recommandé)
  2. ou neeject puis retirer ModuleScopePlugin.jsdu répertoire.
  3. ou commenter / supprimer const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');de./node_modules/react-scripts/config/webpack.config.dev.js

PS: attention aux conséquences de l' éjection .

its4zahoor
la source
Merci @SurajRao, je l'ai déplacé ici. J'espère que c'est mieux.
its4zahoor
@PattycakeJr Oui, c'est pourquoi ci-dessous j'ai dit d'être conscient des conséquences de l'éjection.
its4zahoor
3

installez ces deux packages

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
la source
2

Vous n'avez pas besoin d'éjecter, vous pouvez modifier la react-scriptsconfiguration avec la bibliothèque de rescripts

Cela fonctionnerait alors:

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

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

  return config;
};
Lukas
la source
2

Je pense que la solution de Lukas Bach pour utiliser react-app-rewired afin de modifier la configuration de webpack est un bon moyen de procéder, cependant, je n'exclurais pas tout le ModuleScopePlugin mais mettrais plutôt en liste blanche le fichier spécifique qui peut être importé en dehors de 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
la source
C'est la meilleure solution à ce jour.
adi518 le
2

Image dans le dossier public

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

  use image inside  js extension
  <img src={process.env.PUBLIC_URL+"/resumepic.png"}/>
  • utiliser l'image dans l'extension js
Yziaf_07
la source
1

Si vous n'avez besoin d'importer qu'un seul fichier, tel que README.md ou package.json, cela peut être explicitement ajouté à 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
la source
3
Cela nécessite d'abord l'éjection de create-react-app, non?
Beau Smith
1

la meilleure solution est de fork react-scripts, cela est en fait mentionné dans la documentation officielle, voir: Alternatives à l'éjection

Tarif Alnamrouti
la source
1

Si vous avez besoin de plusieurs modifications, comme lors de l'utilisation de la conception de fourmis , vous pouvez combiner plusieurs fonctions comme celle-ci:

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

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

Pour ajouter à la réponse de Bartek Maciejiczek, voici à quoi ça ressemble avec 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
la source