Búsqueda de sitios web

reactjs - Diferencias de Webpack en la exportación por defecto entre Typescript y Javascript


Tengo una aplicación de React con una página que importa un componente desde otro archivo.

// code from IconPage.tsx

import {AccountBalanceIcon} from './icons';

<AccountBalanceIcon />
// Code from ./icons.ts
export { default as AccountBalanceIcon } from "@material-ui/icons/AccountBalance";

Esto funciona bien cuando el archivo de exportación es un archivo TypeScript (.ts), sin embargo, cuando renombro el archivo a .js, obtengo un comportamiento significativamente diferente.

// Code from ./icons.js
export { default as AccountBalanceIcon } from "@material-ui/icons/AccountBalance";

Esto provoca errores durante el paso de compilación de webpack

Module not found: Error: Can't resolve '@material-ui/icons/AccountBalance' in '.../src/pages'
Did you mean 'AccountBalance.js'?
BREAKING CHANGE: The request '@material-ui/icons/AccountBalance' failed to resolve only because it was resolved as fully specified
(probably because the origin is strict EcmaScript Module, e. g. a module with javascript mimetype, a '*.mjs' file, or a '*.js' file where the package.json contains '"type": "module"').
The extension in the request is mandatory for it to be fully specified.
Add the extension to the request.

Sí, estoy usando "type": "module" en mi package.json, así que reestructuré el código para que compile agregando la extensión .js.

// Code from ./icons.js
export { default as AccountBalanceIcon } from "@material-ui/icons/AccountBalance.js";

Una vez hecho esto, webpack compila, pero obtengo el error familiar de React en la consola del navegador cuando te equivocas en tu declaración de importación.

react-dom.development.js:25058 Uncaught Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.

Check the render method of `IconPage`.
    at createFiberFromTypeAndProps (react-dom.development.js:25058:1)
    at createFiberFromElement (react-dom.development.js:25086:1)
    at createChild (react-dom.development.js:13446:1)

Renombrar el archivo de nuevo a .ts (e incluso manteniendo el AccountBalance.js en su lugar) resuelve el error en tiempo de ejecución.

Análisis

La razón de la discrepancia se muestra en el código de construcción.

Como archivo fuente de JavaScript, el código resultante que produce webpack es el siguiente.

    __webpack_require__.d(__webpack_exports__, {
        "AccountBalanceIcon": () =>
            (_material_ui_icons_AccountBalance_js__WEBPACK_IMPORTED_MODULE_0__)
    });

Y la salida de TypeScript se ve así.

    __webpack_require__.d(__webpack_exports__, {
        "AccountBalanceIcon": () =>
           (_material_ui_icons_AccountBalance_js__WEBPACK_IMPORTED_MODULE_0__["default"])
    });

Tenga en cuenta que el código fuente de TypeScript hace referencia a la propiedad .default del módulo importado, mientras que el código fuente de JavaScript se refiere al módulo en sí.

tsc --showConfig

{
    "compilerOptions": {
        "target": "es2017",
        "jsx": "react",
        "experimentalDecorators": true,
        "module": "esnext",
        "moduleResolution": "node",
        "allowJs": true,
        "outDir": "./dist",
        "esModuleInterop": true,
        "forceConsistentCasingInFileNames": true,
        "strict": true,
        "skipLibCheck": true
    },
    "include": [
        "./src/**/*"
    ],
    "exclude": [
        "./test/**/*"
    ]
}

webpack.config.js

import HtmlWebpackPlugin from "html-webpack-plugin";
import MiniCssExtractPlugin from "mini-css-extract-plugin";

const pathResolve = (path) => new URL(path, import.meta.url).pathname;

export default {
  mode: process.env.NODE_ENV === "production" ? "production" : "development",
  devtool: "inline-source-map",
  entry: "./src/index.tsx",
  output: {
    path: pathResolve("dist"),
    filename: "bundle.js",
    publicPath: '/',
  },
  devServer: {
    historyApiFallback: true,
    hot: true,
    port: 3000,
  },
  resolve: {
    extensions: [".ts", ".tsx", ".js"]
  },
  module: {
    rules: [
      { test: /\.tsx?$/, exclude: /node_modules/, loader: "ts-loader"},
      { test: /\.js$/, use: ["source-map-loader"], enforce: "pre" },
      { test: /\.css$/, use: ['style-loader', 'css-loader']},
      {
        test: /\.(png|svg|jpg|jpeg|gif)$/i,
        type: 'asset/resource',
      },
    ],
  },
  plugins:[
    new HtmlWebpackPlugin({
      template: pathResolve("./public/index.html"),
      filename: "index.html"
    }),
    new MiniCssExtractPlugin({filename: "app.css"}),
  ],
  ignoreWarnings: [/Failed to parse source map/],
};

Espero que mi problema esté realmente relacionado con uno de estos archivos de configuración.

Pregunta

¿Cómo puedo hacer que el archivo de JavaScript exporte el componente de Material-UI de manera limpia para que los proyectos de JS y TS puedan consumir la exportación?

Pregunto porque el archivo icons.ts en este ejemplo es en realidad una simplificación para mostrar el problema. El código de los iconos es parte de una biblioteca de componentes más grande que se empaqueta y distribuye como archivos JS transpiligados.

Respuesta #1

en lugar de esto:

export { default as AccountBalanceIcon } from "@material-ui/icons/AccountBalance";

intentar :

export { AccountBalance as AccountBalanceIcon } from "@material-ui/icons";

Artículos relacionados