解决 ES10 中 import 扩展名丢失的 bug

在 ES6 中引入了 import 这个语法,用来导入 JavaScript 模块。而在 ES10 中,我们可以在 import 语句中省略掉扩展名,比如:

import { foo } from './bar';

上面的代码中,省略了 .js 扩展名。这样虽然可以减少代码冗余,但同时也可能会带来一些问题,比如当我们要导入不同类型的文件时,需要手动设置好扩展名。

在本文中,我们将介绍一个解决 ES10 中 import 扩展名丢失的 bug 的方法,并提供示例代码,帮助读者更好地理解和应用这个知识点。

问题描述

当我们在 ES10 中使用 import 导入文件时,如果文件中省略了扩展名,系统会自动查找同名的文件,并导入第一个找到的文件。举个例子:

如果我们有如下文件结构:

而在 bar.js 中,我们使用了 import 导入 foo.js

import { foo } from './foo';

这时,如果我们把 foo.js 改为 foo.jsxfoo.ts,那么导入操作仍然会执行成功,因为在查找同名文件时,系统会优先选择 .js 文件。

这可能会在多人协作或者多个项目共用同一个依赖时产生问题,因为不同文件有不同的扩展名,在省略扩展名时会导致程序出现意想不到的错误。

解决方案

为了解决这个问题,我们可以通过调整 Node.js 中 resolve 函数的逻辑,让其在查找同名文件时,优先选择与导入文件的扩展名相同的文件。

以下是一个实现示例:

const { resolve } = require('path');
const { statSync, readFileSync } = require('fs');

// 获取扩展名
const getExtension = filename => {
  const match = /\.([^.]*)$/.exec(filename);
  return match ? match[1] : '';
};

const resolveWithExtension = (filePathWithoutExt, extensions) => {
  const filePathsWithExts = extensions.map(ext => filePathWithoutExt + '.' + ext);
  
  for (let i = 0; i < filePathsWithExts.length; i++) {
    const filePathWithExt = filePathsWithExts[i];
    const resolvedPath = resolve(filePathWithExt);
    const stats = statSync(resolvedPath);
    
    if (stats.isFile()) {
      return resolvedPath;
    }
  }
  
  // 抛出文件不存在的错误
  throw new Error(`Cannot find file "${filePathWithoutExt}" with extensions: ${extensions.join(', ')}`);
};

// 重写 resolve 函数
const originalResolve = require.resolve;
require.resolve = (moduleName, options) => {
  let ext = options && options.extensions && options.extensions[0];
  if (ext) {
    return originalResolve(moduleName, options);
  }
  try {
    const filePathWithoutExt = originalResolve(moduleName, { ...options, extensions: ['.js'] }).slice(0, -3);
  
    // 获取当前文件的扩展名
    const currentExt = getExtension(module.parent.filename);
  
    // 优先选择同扩展名的文件
    const extensions = currentExt ? [currentExt, ...options.extensions] : options.extensions;
    
    return resolveWithExtension(filePathWithoutExt, extensions);
  } catch (err) {
    if (err.code !== 'MODULE_NOT_FOUND') {
      throw err; // 如果不是 "模块未找到" 错误,则抛出异常
    }
    // 抛出错误
    throw new Error(`Cannot resolve module '${moduleName}'`);
  }
};

在上述代码中,我们通过重写 Node.js 中的 require.resolve 函数,让其优先选择与导入文件扩展名相同的文件。具体的实现细节和注释已经在代码中进行了说明。

总结

在本文中,我们介绍了 ES10 中 import 扩展名丢失的 bug,并提供了一种解决方案。这个方案通过调整 Node.js 中 resolve 函数的逻辑,优先选择与导入文件扩展名相同的文件,从而避免了这个问题带来的潜在风险。我们还提供了示例代码,帮助读者更好地理解和应用这个知识点。

来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/65a21350add4f0e0ffa24b05


纠错反馈