La aplicación create-react-app restringe las importaciones fuera del directorio src

149

Estoy usando create-react-app. Estoy tratando de llamar una imagen desde mi carpeta pública desde un archivo dentro de mi src/components. Estoy recibiendo este mensaje de error.

./src/components/website_index.js Módulo no encontrado: intentó importar ../../public/images/logo/WC-BlackonWhite.jpg que queda fuera del directorio src / del proyecto. Las importaciones relativas fuera de src / no son compatibles. Puede moverlo dentro de src / o agregarle un enlace simbólico desde los módulos_nodo / del proyecto.

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

He leído muchas cosas que dicen que puedes importar a la ruta, pero eso todavía no me funciona. Cualquier ayuda sería muy apreciada. Sé que hay muchas preguntas como esta, pero todas me dicen que importe el logotipo o la imagen con tanta claridad que me falta algo en el panorama general.

David Brierton
fuente
2
Necesitas ../public/images/logo_2016.pngSubiste dos veces, primero fuera de la carpeta de componentes, luego fuera de la carpeta src.
Chris G
./src/components/website_index.js Módulo no encontrado: intentó importar ../../public/images/logo/WC-BlackonWhite.jpg que queda fuera del directorio src / del proyecto. Las importaciones relativas fuera de src / no son compatibles. Puede moverlo dentro de src / o agregarle un enlace simbólico desde los módulos_nodo / del proyecto.
David Brierton
Mi comentario asume que su publiccarpeta está directamente dentro de su srccarpeta. Su comentario sin comentarios presenta el camino anterior que comienza, ../..así que ¿no está seguro de cuál es su punto?
Chris G
3
ningún público está en el mismo nivel que src
David Brierton
¿Qué quieren decir con "o agregarle un enlace simbólico desde los node_modules /" del proyecto?
Julha

Respuestas:

127

Esta es una restricción especial agregada por los desarrolladores de create-react-app. Se implementa ModuleScopePluginpara garantizar que los archivos residan src/. Ese complemento garantiza que las importaciones relativas del directorio de origen de la aplicación no lleguen fuera de él.

Puede deshabilitar esta función, pero solo después de la ejectoperación del proyecto create-react-app.

La mayoría de las funciones y sus actualizaciones están ocultas en las partes internas del sistema create-react-app. Si lo hace eject, ya no tendrá algunas características y su actualización. Entonces, si no está listo para administrar y configurar la aplicación incluida para configurar el paquete web, etc., no realice la ejectoperación.

Juega según las reglas existentes (muévete a src). Pero ahora puede saber cómo eliminar la restricción: hacer ejecty eliminar ModuleScopePlugindel archivo de configuración del paquete web .


Desde create-react-app v0.4.0, la NODE_PATHvariable de entorno permite especificar una ruta para la importación absoluta. Y desde v3.0.0 NODE_PATH está en desuso a favor de establecer baseUrlen jsconfig.jsono tsconfig.json.

La importación absoluta permite usar en import App from 'App'lugar import App from './App'del valor especificado en la URL base.

Esta característica es específicamente útil para monorepos u otras preguntas de configuración, pero no para importar imágenes o cualquier otra cosa desde la publiccarpeta.

El contenido de la publiccarpeta se colocará en la buildcarpeta y estará disponible por URL relativa. Además, todo lo importado será procesado por webpack y se colocará también en la buildcarpeta.

Si importa algo de la publiccarpeta, probablemente esa cosa se duplicará en la buildcarpeta y estará disponible por dos URL diferentes (o con diferentes formas de carga), lo que finalmente empeorará el tamaño de descarga del paquete.

Importar desde la carpeta src es preferible y tiene ventajas. Todo será empaquetado por paquete web con el paquete con trozos de tamaño óptimo y para la mejor eficiencia de carga .


Hay soluciones intermedias, a saber, el sistema de cableado que le permite modificar mediante programación la configuración del paquete web. Pero eliminar el ModuleScopePlugincomplemento no es una buena solución; es mejor agregar directorios adicionales totalmente funcionales similares a src.

Actualmente, create-react-appno admite directorios adicionales que no sean srcen la carpeta raíz. Esto se puede hacer usando react-app-rewire-alias

oklas
fuente
3
si crea un enlace simbólico dentro de ./src e importa desde allí, la compilación no funciona (el complemento de babel no transforma las fuentes en las carpetas enlazadas). Entonces, con esta restricción, en y sin enlace simbólico bajo src, se lo coloca efectivamente en una cárcel de 'no compartir código con otros proyectos' (a menos que elija emigrar / expulsar completamente de la CRA)
VP
2
@VP pero el error dice "agregarle un enlace simbólico desde los módulos_nodo / del proyecto", ¿no funciona?
adrianmc
Deben hacer una marca para deshabilitar esto cuando se ejecutan create-react-apppara aquellos que no desean expulsar la configuración del paquete web. Personalmente, siempre me expulsan, pero algunas personas no se sienten cómodas pero juegan con la desalentadora configuración de los paquetes web.
TetraDev
2
@adrianmc. agregar enlaces simbólicos desde los módulos_nodo del proyecto no funciona, porque muchos proyectos usan componentes / códigos compartidos que no son módulos_nodo. Por ejemplo, comparto código entre React Native y React native web. Esos 'fragmentos' no son módulos_nodo, y estos 2 proyectos en realidad tienen diferentes módulos_nodo.
VP
1
¿Cómo es esta la respuesta aceptada? Esta restricción falsa se elimina trivialmente simplemente configurando NODE_PATH=./src/..el .envarchivo. Al hacerlo, puede importar desde fuera de la carpeta src sin pasar por el dolor asociado con la expulsión de su aplicación.
Flaom
47

El paquete react-app-rewired se puede usar para eliminar el complemento. De esta manera no tienes que expulsar.

Siga los pasos en la página del paquete npm (instale el paquete y voltee las llamadas en el archivo package.json) y use un config-overrides.jsarchivo similar 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;
};

Esto eliminará el ModuleScopePlugin de los complementos de WebPack utilizados, pero dejará el resto como estaba y elimina la necesidad de expulsar.

Lukas Bach
fuente
55
Gran respuesta. Puede aprovechar aún más react-app-rewiredcon [ github.com/arackaf/customize-crafont>(customize-cra) . Entonces la configuración usará just babelInclude([path.resolve('src'), path.resolve('../common')])y removeModuleScopePlugin().
ivosh
¿pueden confirmar dónde debo colocar este fragmento de código?
bijayshrestha
1
@bijayshrestha Lea el archivo readme del proyecto react-app-rewired, allí explica cómo configurarlo. Durante la configuración, creará un config-overrides.jsarchivo en el que puede colocar el código.
Lukas Bach
Eliminar el ModuleScopePlugincomplemento no es una buena idea. Es mejor agregar directorios adicionales que funcionen de manera similar al srcuso de react-app-rewire-alias
oklas
1
¿Esta solución funciona con Typecript? No puedo hacerlo funcionar con TS.
user3053247
27

Ofrecer un poco más de información a las respuestas de otros. Tiene dos opciones con respecto a cómo entregar el archivo .png al usuario. La estructura del archivo debe ajustarse al método que elija. Las dos opciones son:

  1. Use el sistema de módulos ( import x from y) provisto con react-create-app y combínelo con su JS. Coloque la imagen dentro de la srccarpeta.

  2. Servirlo desde la publiccarpeta y dejar que Node sirva el archivo. create-react-app también aparentemente viene con una variable de entorno, por ejemplo <img src={process.env.PUBLIC_URL + '/img/logo.png'} />;. Esto significa que puede hacer referencia a él en su aplicación React, pero aún así se puede servir a través de Node, con su navegador solicitándolo por separado en una solicitud GET normal.

Fuente: create-react-app

danielgormly
fuente
La sugerencia n. ° 2 es exactamente lo que me causa el siguiente error: Módulo no encontrado: intentó importar ./../../../public/CheersBar-Drinks-147.jpg que queda fuera del directorio del proyecto src / . Las importaciones relativas fuera de src / no son compatibles. Puede moverlo dentro de src / o agregarle un enlace simbólico desde el nodo_módulos / del proyecto.
Amos Long
25

Si sus imágenes están en la carpeta pública, entonces debe usar

"/images/logo_2016.png"

en su <img> srclugar de importar

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

Esto funcionará

<img className="Header-logo" src="/images/logo_2016.png" alt="Logo" />
Abhinav bhardwaj
fuente
2
No funciona para mi Obteniendo el mismo mensaje - 'fuera del directorio src / del proyecto. No se admiten las importaciones relativas fuera de src /.
Arkady
Su respuesta es IMO correcta, pero me tomé la libertad de aclarar que está hablando sobre el camino <img src="...">, no importarlo
Dmitry Yudakov
1
¡Esto era exactamente lo que estaba buscando! Simplemente agregué una carpeta de "imágenes" al directorio "src" de mi CRA, y luego pude usar<img src="/images/logo.png" alt="Logo" />
Amos Long el
12

Necesita moverse WC-BlackonWhite.jpga su srcdirectorio. El publicdirectorio es para archivos estáticos que se vincularán directamente en el HTML (como el favicon), no cosas que va a importar directamente en su paquete.

Joe Clay
fuente
He visto a otros hacerlo de esta manera. Es por eso que pensé que esta es la forma correcta
David Brierton,
@DavidBrierton: esta precaución podría haberse agregado en una versión más reciente de create-react-app.
Joe Clay
8

Hay algunas respuestas que brindan soluciones react-app-rewired, pero customize-craexpone una removeModuleScopePlugin()API especial que es un poco más elegante. (Es la misma solución, pero abstraída por el customize-crapaquete).

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
fuente
1
¡Salvaste mi día!
lmiguelvargasf
5

Eliminarlo con 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
fuente
¡Vota por el craco! Craco admite CRA 3.x
Roman Podlinov
4

Esta restricción asegura que todos los archivos o módulos (exportaciones) estén dentro del src/directorio, la implementación está ./node_modules/react-dev-utils/ModuleScopePlugin.jsen las siguientes líneas 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();
      }

Puede eliminar esta restricción mediante

  1. ya sea cambiando este fragmento de código (no recomendado)
  2. o noeject luego retire ModuleScopePlugin.jsdel directorio.
  3. o comentar / eliminar const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');de./node_modules/react-scripts/config/webpack.config.dev.js

PD: cuidado con las consecuencias de la expulsión .

its4zahoor
fuente
Gracias @SurajRao, lo mudé aquí. Espero que sea mejor
its4zahoor
@PattycakeJr Sí, es por eso que a continuación le he dicho que tenga cuidado con las consecuencias de expulsión.
its4zahoor
3

instale estos dos paquetes

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
fuente
2

No necesita expulsar, puede modificar la react-scriptsconfiguración con la biblioteca de rescripts

Esto funcionaría entonces:

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

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

  return config;
};
Lukas
fuente
2

Creo que la solución de Lukas Bach para usar react-app- rewired para modificar la configuración del paquete web es una buena manera de hacerlo, sin embargo, no excluiría todo el ModuleScopePlugin, sino que incluiría en la lista blanca el archivo específico que se puede importar fuera 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
fuente
Esa es la mejor solución hasta ahora.
adi518
2

Imagen dentro de la carpeta 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 imagen dentro de la extensión js
revs Yziaf_07
fuente
1

Si solo necesita importar un solo archivo, como README.md o package.json, esto se puede agregar explícitamente a 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
fuente
3
Esto requiere primero expulsar de create-react-app, ¿no?
Beau Smith el
1

Si necesita múltiples modificaciones, como cuando usa el diseño de hormigas , puede combinar múltiples funciones como esta:

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

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

Agregando a la respuesta de Bartek Maciejiczek, así es como se ve con 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
fuente