以 es2017 为目标的 Typescript 抛出错误 "You may need an appropriate loader" 但项目使用 es5 构建良好

Typescript targeting es2017 throws error "You may need an appropriate loader" but project builds fine with es5

我们有一个 typescript react 'main' 项目。在 package.json 中是对我们使用的另一个内部库的依赖。当两个项目都针对 es5 时,'main' 项目构建良好,但更改为 es2017 会引发以下错误:

You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file.

它为在依赖项目中声明枚举的所有文件抛出此错误。

这是主项目的 tsconfig.json 文件:

{
  "compilerOptions": {
    "resolveJsonModule": true,
    "noImplicitAny": false,
    "module": "commonjs",
    "noEmitOnError": true,
    "removeComments": false,
    "sourceMap": true,
    "target": "es2017",
    "jsx": "react",
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "experimentalDecorators": true,
    "downlevelIteration": true,
    "types": ["node", "jest"],
    "baseUrl": "./src"
  },
  "exclude": ["node_modules"],
  "includes": ["./src"]
}

和 webpack.config.js:

/* eslint no-use-before-define: 0 */
const path = require('path');
const webpack = require('webpack');

// plugins
const CopyPlugin = require('copy-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');

const isProduction = process.env.NODE_ENV === 'production';

//entry --> output
module.exports = {
  context: __dirname,
  devtool: isProduction ? 'source-map' : 'eval-source-map',
  entry: {
    app: ['./src/index.tsx'],
  },
  output: {
    path: path.join(__dirname, 'site'),
    filename: '[name].[contenthash].js',
    publicPath: '/',
  },
  devServer: {
    inline: true,
    hot: true,
    historyApiFallback: true,
    disableHostCheck: true,
  },
  resolve: {
    extensions: ['.ts', '.js', '.jsx', '.tsx', '.pdf'],
    alias: {
      api: path.resolve('./src/api'),
      assets: path.resolve('./src/assets'),
      components: path.resolve('./src/components'),
      context: path.resolve('./src/context'),
      hooks: path.resolve('./src/hooks'),
      models: path.resolve('./src/models'),
    },
    fallback: {
      path: require.resolve('path-browserify'),
      util: require.resolve('util'),
    },
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx|ts|tsx)$/,
        exclude: isProduction ? [] : /(node_modules)/,
        use: [
          {
            loader: 'babel-loader',
            options: {
              cacheDirectory: true,
            },
          },
        ],
      },
      {
        test: /\.(pdf)$/,
        exclude: /(node_modules)/,
        use: [
          {
            loader: 'file-loader',
            options: {
              name: '[name].[ext]',
            },
          },
        ],
      },
    ],
  },
  optimization: {
    usedExports: isProduction ? true : false,
    removeAvailableModules: isProduction ? true : false,
    minimize: isProduction ? true : false,
    minimizer: isProduction ? [new TerserPlugin()] : [],
    splitChunks: {
      chunks: 'all',
    },
  },
  plugins: [
    new ForkTsCheckerWebpackPlugin({
      typescript: {
        diagnosticOptions: {
          semantic: true,
          syntactic: true,
        },
      },
    }),
    new webpack.ProvidePlugin({
      process: 'process/browser',
    }),
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify(
        process.env.NODE_ENV || 'development',
      ),
      'process.env.SENTRY_DSN': `"https://67422e5eff614bc2890330a8825d5c58@o348527.ingest.sentry.io/2686852"`,
      'process.env.APP_VERSION': JSON.stringify(
        process.env.npm_package_version,
      ),
    }),
    new HtmlWebpackPlugin({
      template: 'htmlTemplates/index.wm.template',
      inject: 'body',
      title: 'TIPPEL',
    }),
    new CopyPlugin({
      patterns: [
        {
          from: 'public/images',
          to: 'images',
        },
      ],
      options: {
        concurrency: 100,
      },
    }),
  ],
};

这是babel.config.json:

{
  "sourceType": "unambiguous",
  "presets": [
    [
      "@babel/preset-env",
      {
        "useBuiltIns": "entry",
        "corejs": 3
      }
    ],
    "@babel/preset-typescript",
    "@babel/preset-react"
  ],
  "plugins": [
    [
      "@babel/plugin-transform-runtime",
      {
        "regenerator": true
      }
    ],
    "@babel/plugin-proposal-class-properties",
    ["@babel/plugin-proposal-object-rest-spread", { "useBuiltIns": true }],
    ["@babel/plugin-transform-destructuring", { "useBuiltIns": true }],
    ["@babel/plugin-transform-object-assign", { "useBuiltIns": true }],
    ["@babel/plugin-transform-arrow-functions", { "useBuiltIns": true }]
  ]
}

据我所知,针对 es2017 没有什么特别的要求,所以我想知道解决方案是什么。

重要更新

如果我将包 json 更改为指向相关存储库的本地副本(称为 'models'),那么一切都会编译。

例如这个有效:

在package.json中:“@tplp/models”:“../models”

但这会引发加载程序构建错误:

在package.json中:“@tplp/models”:“2.6.3”

供参考

如果您尝试使用具有导出枚举的 npm 包,create-react-app 也会出现同样的问题,您可以阅读它 here

对于那些感兴趣的人,有非 npm 解决方案可以在项目之间共享 typescript 代码,例如 and this

第二次更新 bable.config.json 文件有:

"@babel/preset-react"

这会导致 babel “删除打字稿类型”。参考 here。不太清楚他们的意思。

但是删除该行(即删除 preset-react)意味着枚举的构建错误消失了......但是当然现在 none 的 JSX 可以编译了。

2 件事让我找到了解决方案。首先:

bable.config.json 文件有:

"@babel/preset-react"

这会导致 babel “删除打字稿类型”。参考 here。删除它可以消除错误,因为显然所有类型都被删除了(但显然 babel 无法处理 JSX)。

因此我意识到枚举文件是环境文件,babel-loader(特别是 preset-react)不知道如何处理它们(不像 tsc)。

这让我想到了 ,我意识到我必须特别告诉 babel-loader 在哪里可以找到枚举文件,所以将它添加到我的 package.json:

  {
    test: /\.(ts|tsx)$/,
    include: path.resolve('./node_modules/@tplp/models/src/enumerations'),
    use: [
      {
        loader: 'babel-loader',
        options: {
          cacheDirectory: true,
        },
      },
    ]
  },