Webpack - 防止对等依赖项中的字体文件(.woff、.eot、.ttf)成为构建的一部分

Posted

技术标签:

【中文标题】Webpack - 防止对等依赖项中的字体文件(.woff、.eot、.ttf)成为构建的一部分【英文标题】:Webpack - prevent font files (.woff, .eot, .ttf) in peer dependencies from being part of the build 【发布时间】:2021-10-15 07:14:49 【问题描述】:

我正在尝试修改我的 webpack 配置,以使来自对等依赖项的字体文件(.woff、.woff2 .eot、.ttf)不会包含在构建中。这是我的 Webpack 配置:

webpack.common.js

module.exports = 
  entry: './index.js',
  output: 
    path: path.resolve(__dirname, 'dist'),
    filename: 'index_bundle.js',
    publicPath: '/',
    libraryTarget: 'umd',
  ,
  plugins: [
    new CleanWebpackPlugin(),
    new htmlWebpackPlugin(
      template: path.resolve(__dirname, "public/index.html"),
    ),
    new CopyPlugin(
      patterns: [
         from: path.resolve(__dirname, 'src/assets'), to: path.resolve(__dirname, 'dist/assets') 
      ],
    ),
  ],
  externals: 
    lodash: 'lodash',
    '@scoped/scoped-ui': '@scoped/scoped-ui',
    '@scoped/scoped-web-common': '@scoped/scoped-web-common',
    '@scoped/scoped-rich-text-editor': '@scoped/scoped-rich-text-editor',
    '@ckeditor/ckeditor5-react': '@ckeditor/ckeditor5-react',
    bootstrap: 'bootstrap',
    'react-bootstrap': 'react-bootstrap'
  ,
  // also tried the array format
  // externals: ['lodash', '@scoped/scoped-ui', '@scoped/scoped-web-common', '@ckeditor/ckeditor5-react', 'bootstrap', 'react-bootstrap'], 
  module: 
    rules: [
      
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: [
          'babel-loader',
          
            loader: 'eslint-loader',
            options: 
              emitWarning: true,
              formatter: 'table',
            ,
          ,
        ],
      ,
      
        test: /\.css$/i,
        use: ['style-loader', 'css-loader'],
      ,
      
        test: /\.(png|svg|jpg|gif)$/,
        exclude: /node_modules/,
        use: [
          
            loader: 'file-loader',
            options: 
              name: '[name].[ext]',
              outputPath: 'static/img',
            ,
          ,
        ],
      ,
      
        test: /\.(woff(2)?|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/,
        use: [
          
            loader: 'file-loader',
            options: 
              name: '[name].[ext]',
              outputPath: '/assets/'
            ,
          ,
        ],
      ,
      
        test: /\.ttf$/,
        use: [
          
            loader: 'ttf-loader',
            options: 
              name: './font/[hash].[ext]',
              outputPath: '/assets/'
            ,
          ,
        ],
      ,
      
        test: /\.s[ac]ss$/i,
        use: [
          'style-loader',
          'css-loader',
          'sass-loader',
        ],
      ,
    ],
  ,
;

Webpack(及相关)版本:

"webpack": "^4.44.1",
"webpack-cli": "^3.3.12",
"webpack-dev-server": "^3.11.0",
"webpack-merge": "^5.1.3"

package.json 中的对等依赖项:

 "peerDependencies": 
    "@ckeditor/ckeditor5-react": "^2.1.0",
    "@scoped/scoped-rich-text-editor": "0.0.1-56",
    "@scoped/scoped-ui": "^1",
    "@scoped/scoped-web-common": "^1",
    "bootstrap": "^4.4.1",
    "react-bootstrap": "^1.0.0-beta.17",
    "lodash": "4.17.21"
 ,

我的构建脚本的结果是以下文件结构:

. ┠ 地区 ┠ 资产 ┠ [散列].ttf ┠ [散列].eot ┠ [散列].woff ┠ [散列].woff2 ┠ [someImage].jpg ┠ [someOtherImage].png ┠ 静态 ┠ index_bundle.js ┠ index_bundle.js.map ┠ index.html

我尝试过的事情:

    按照以下问题中的建议向 webpack 配置添加一个外部密钥:Webpack to build without including peer dependencies

    尝试从字体文件的规则中排除 node_modules。

    删除字体文件本身的规则。

2) 和 3) 都给我相同的结果,即构建脚本失败,错误如下所示:

ERROR in ./node_modules/@scoped/scoped-ui/build/assets/fonts/418e7417-47f3-40a1-8817-519a566f9d82.eot 1:1
Module parse failed: Unexpected character '@' (1:1)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
(Source code omitted for this binary file)
 @ ./node_modules/@scoped/scoped-ui/build/styles/font.css (./node_modules/css-loader/dist/cjs.js!./node_modules/@scoped/scoped-ui/build/styles/font.css) 4:0-101 33:73-102
 @ ./node_modules/css-loader/dist/cjs.js!./node_modules/@scoped/scoped-ui/build/styles/main.css
 @ ./node_modules/@scoped/scoped-ui/build/styles/main.css
 @ ./src/index.js
 @ ./index.js

ERROR in ./node_modules/@scoped/scoped-ui/build/assets/fonts/4cc8f5da-4e24-4929-8efb-866ffcb1fe7e.eot 1:0
Module parse failed: Unexpected character '�' (1:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
(Source code omitted for this binary file)
 @ ./node_modules/@scoped/scoped-ui/build/styles/font.css (./node_modules/css-loader/dist/cjs.js!./node_modules/@scoped/scoped-ui/build/styles/font.css) 28:0-102 57:74-104
 @ ./node_modules/css-loader/dist/cjs.js!./node_modules/@scoped/scoped-ui/build/styles/main.css
 @ ./node_modules/@scoped/scoped-ui/build/styles/main.css
 @ ./src/index.js
 @ ./index.js

【问题讨论】:

【参考方案1】:

我认为这可以通过使用自定义解析插件来解决:

webpack.config.js

const path = require('path');

const PreventFontsFromPeerDeps = 
  apply (resolver) 
    const beforeFinalFile = resolver.getHook('before-final-file');
    
    let peerDepsFromMainRepo;
    beforeFinalFile.tap("PreventFontsFromPeerDeps", (request, resolveContext) => 
      if (!peerDepsFromMainRepo) 
        peerDepsFromMainRepo = request.descriptionFileData.peerDependencies;

        return;
      

      const  path  = request;
      
      const isPathAFont = /* ... */;
      const isFromPeerDeps = Object.keys(peerDepsFromMainRepo).some(k => path.includes(`node_modules/$k`));
      if (isFromPeerDeps && isPathAFont) 
        // By returning this, this request will be ignored.
        return  path: false ;
      
    );
  ,
;

/**
 * @type import("webpack/types").Configuration
 */
const config = 
  /* ... */

  resolve: 
    plugins: [PreventFontsFromPeerDeps]
  ,

  /* ... */
;

module.exports = config;

这个自定义插件介入的捆绑过程部分可以被认为是解析过程。 这是解决资源的地方。这个解析过程由多个阶段组成,比如:判断所需模块的package.json(也叫描述文件),判断路径是指路径还是到目录等 这些 stages 在内部用 webpack 的说法表示为 hooks。其中一个钩子是final-file,它负责确定文件是否存在。通过使用before-final-file,我们添加了某种优先级,这意味着我们的自定义逻辑将首先运行。

peerDepsFromMainRepo 指的是要解析的第一个文件。该文件很可能是存储库的一部分,因此与之关联的package.json 将是根目录之一,这意味着我们将能够从那里获取peerDependencies 对象。request 对象包含我们需要的所有信息,正如我们从这张图片中看到的那样:

插件本质上是从项目根目录的package.json文件中获取对等依赖项,然后,对于每个传入的请求(请求=资源的路径),它检查该资源是否是对等依赖与否。

注意:在上图中,我举了一个更简单的例子,我添加了lodash 作为对等依赖项。您可以找到示例here。


我在this SO answer.

中更详细地谈到了解决过程

【讨论】:

以上是关于Webpack - 防止对等依赖项中的字体文件(.woff、.eot、.ttf)成为构建的一部分的主要内容,如果未能解决你的问题,请参考以下文章

Vue 3 和 webpack 5 - 错误:“模块属性已从依赖项中删除”

冲突的对等依赖关系:webpack@4.46.0 npm ERR!节点模块/webpack

eslint 'html-webpack-plugin' 应该列在项目的依赖项中,而不是 devDependencies 中。 (导入/无外部依赖项)

一次性支持Webpack,Parcel和Rollup的最佳方法是什么?

使用 webpack 在 monaco-editor 包中构建字体文件

什么是包依赖项中的平均文件:工作空间?