Webpack 5:如何使用编写为 ES 模块的自定义加载器

Luc*_*sky 6 commonjs node.js webpack es6-modules webpack-5

我如何告诉 Webpack 它应该将我的加载器视为 ES 模块而不是 CommonJS?

我的目标是编写一个使用 ES Module 语法的 Loader ( export default...)

演示(不起作用):https://stackblitz.com/edit/webpack-webpack-js-org-7gmdc8 ?file=webpack.config.js

显然 Webpack 正在尝试将模块作为 CommonJS 加载。如果您查看loadLoader,则条件loader.type === "module"失败,因为loader.typeis undefined。


// webpack.conf.js:
const path = require('path');

module.exports = {
  mode: 'development',
  entry: './src/app.js',
  output: {
    filename: 'app.js',
    path: path.resolve(__dirname, 'dist'),
    clean: true,
  },

  module: {
    rules: [
      {
        test: /\.js$/,
        use: ['my-loader'],
      },
    ],
  },

  resolveLoader: {
    alias: {
      // WEBPACK IS TRYING TO LOAD THIS AS COMMONJS... WHY???
      'my-loader':
        '/home/projects/webpack-webpack-js-org-7gmdc8/my-loader/main.mjs',
    },
  },
};
Run Code Online (Sandbox Code Playgroud)

示例堆栈跟踪:

ERROR in ./index.js
Module build failed (from ../my-webpack-loader-esm/dist/index.bundle.mjs):
Error [ERR_REQUIRE_ESM]: require() of ES Module /workspaces/webpack-loader-lab/my-webpack-loader-esm/dist/index.bundle.mjs not supported.
Instead change the require of /workspaces/webpack-loader-lab/my-webpack-loader-esm/dist/index.bundle.mjs to a dynamic import() which is available in all CommonJS modules.
    at loadLoader (/workspaces/webpack-loader-lab/the-client/node_modules/loader-runner/lib/loadLoader.js:23:17)
Run Code Online (Sandbox Code Playgroud)

Igo*_*r B 2

更新:

该支持是在 webpack 版本 5.76 和 5.81 之间添加的,截至 2023 年 6 月版本 5.88.0 支持 ESM 模块作为加载器

旧答案:

由于未合并 PR,目前不可能将其开箱即用,但我设法为其开发了一个解决方法插件。

首先我查看了loadLoader从哪里获取数据。它来自LoaderRunner.js,webpack 使用中间对象并在调用堆栈中导航,我发现了以下代码:

try {
    hooks.beforeLoaders.call(this.loaders, this, loaderContext);
} catch (err) {
    processResult(err);
    return;
}
Run Code Online (Sandbox Code Playgroud)

这告诉我可以通过钩子更改中间对象,并通过在 webpack 代码中搜索beforeLoaders我能够找到为此目的调整的示例插件,结果如下:

import NormalModule from 'webpack/lib/NormalModule.js'

const pluginName = 'MjsLoaderPlugin'
export default class MjsLoaderPlugin {
    apply(compiler) {
        compiler.hooks.compilation.tap(pluginName, compilation => {
            NormalModule.getCompilationHooks(compilation).beforeLoaders.tap(
                pluginName,
                (loaders, normalModule) => {
                    /*
                    loader = {
                        loader: 'F:\\<blah blah>\\webpack-loaders\\my-module-loader.mjs',
                        options: 'name=value',
                        ident: undefined
                    }
                    */

                    for(let loader of loaders) {
                        if(loader.loader.endsWith(".mjs")) {
                            loader.type = 'module'
                        }
                    }
                }
            )
        })
    }
}
Run Code Online (Sandbox Code Playgroud)

也不确定是否相关,这是我的加载器文件夹的配置部分,它告诉 webpack 它可以自动在此文件夹中搜索 mjs 加载器


import { dirname, resolve } from 'path'
import { fileURLToPath } from 'url'
const __dirname = dirname(fileURLToPath(import.meta.url))

export default {
    resolveLoader: {
        modules: [
            'node_modules',
            resolve(__dirname, 'tools/webpack-loaders')
        ],
        extensions: ['.js', '.mjs']
    }
}
Run Code Online (Sandbox Code Playgroud)