Babel.js 如何有条件地修改ES6 JavaScript模块的导出

14ifxucb  于 11个月前  发布在  Babel
关注(0)|答案(2)|浏览(133)

我想知道是否有可能仍然从模块内部访问ES6模块的模块导出,就像在CommonJS中使用module. exports一样。
为了清楚起见,我有一个js模块(Config.js),我用它来导出所有的配置变量。

export const DatabaseName = "myDbName";
export const DatabasePort = 3000;
export const DatabaseHosts = ["174.292.292.32"];
export const MaxWebRequest = 50;
export const MaxImageRequests = 50;
export const WebRequestTimeout = 30;
etc...

字符串
然后我有一个单独的Dev.Config.js文件,它只包含我的开发环境的覆盖。

export const DatabaseHosts = ["localhost"];
export const DatabasePort = 5500;


在我的主Config.js文件中,我在底部有这样的逻辑。

try {
    var environmentConfig = `./${process.env.NODE_ENV}.Config.js`;
    var localConfig = require(environmentConfig)
    module.exports = Object.assign(module.exports, localConfig)
} catch (error) {
    console.log("Error overriding config with local values. " + error)
}


最后,在我的消费代码中,我可以像这样导入我的js文件,

import * as Config from "./Config.js";

console.log(Config.DatabaseHosts) // Gives me the correct "overridden" value on my dev environment


目前,我一直在使用babel将我的代码全部转回到CommonJS中,我想这就是我如何能够混合和匹配导入/导出语法的方式,并且仍然像我上面所做的那样引用模块。
我的问题是,我如何在一个纯ES6模块中复制这个模式,而不需要使用babel转换它,因为我不能从模块本身修改我的模块。

zaq34kh6

zaq34kh61#

ESM不支持条件导出模式。
为了使用dynamic import修改从另一个模块导出的值,该模块的指定符是从环境变量派生的(在try...catch语句中,这样失败的尝试就不会在顶层抛出未捕获的异常),您可以修改导出的结构,以便它们作为对象的属性公开。下面是一个可重现的示例来演示:
./package.json

{
  "name": "so-77465699",
  "version": "0.1.0",
  "type": "module",
  "scripts": {
    "dev": "NODE_ENV=Dev node main.js",
    "prod": "NODE_ENV=production node main.js"
  },
  "license": "MIT"
}

字符串
./Dev.Config.js

export const Config = {
  DatabaseHosts: ["localhost"],
  DatabasePort: 5500,
};

export default Config;


./Config.js

const Config = {
  DatabaseName: "myDbName",
  DatabasePort: 3000,
  DatabaseHosts: ["174.292.292.32"],
  MaxWebRequest: 50,
  MaxImageRequests: 50,
  WebRequestTimeout: 30,
};

try {
  // Import a module specifier based on
  // the value of the NODE_ENV environemnt variable.
  // Destructure and rename the default export:
  const { default: envConfig } = await import(
    import.meta.resolve(`./${process.env.NODE_ENV}.Config.js`)
  );

  // Iterate the keys and values, updating the existing Config object:
  for (const [key, value] of Object.entries(envConfig)) {
    Config[key] = value;
  }
} catch (cause) {
  console.log(`Error overriding config with local values: ${cause}`);
}

export { Config, Config as default };


./main.js

// Import named export:
import { Config } from "./Config.js";

// Alternatively, since it's also the default export:
// import { default as Config } from "./Config.js";

// Or, using syntax sugar:
// import Config from "./Config.js";

console.log(Config.DatabaseHosts);


在终端:

% node --version
v20.9.0

% npm run dev

> [email protected] dev
> NODE_ENV=Dev node main.js

[ 'localhost' ]

% npm run prod

> [email protected] prod
> NODE_ENV=production node main.js

Error overriding config with local values: Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/Users/node/so-77465699/production.Config.js' imported from /Users/node/so-77465699/Config.js
[ '174.292.292.32' ]

gpnt7bae

gpnt7bae2#

在ES6模块中没有办法动态构建/覆盖exports对象。即使是条件导入也需要顶级的await或工具支持。
然而,你可以做的是混乱的let声明和eval

export const DatabaseName = "myDbName"; // keep things as `const` to prevent overriding them
export let DatabasePort = 3000;
export let DatabaseHosts = ["174.292.292.32"];
export let MaxWebRequest = 50;
export let MaxImageRequests = 50;
export const WebRequestTimeout = 30;
… // etc

try {
    var environmentConfig = `./${process.env.NODE_ENV}.Config.js`;
    var localConfig = await import(environmentConfig)
    for (const [name, value] of Object.entries(localConfig)) {
        eval(`${name} = value;`);
    }
} catch (error) {
    console.log("Error overriding config with local values. " + error)
}

字符串
不过我不推荐这样做。只有当你无法改变 * Config.js * 模块的使用方式时,或者当有太多的导出变量使得替代方案不可行时,才使用这种方法。
相反,我建议您创建一个单独的模块来合并配置,尽管这需要拼写两次配置名称:

// Default.Config.js
export const DatabaseName = "myDbName";
export const DatabasePort = 3000;
export const DatabaseHosts = ["174.292.292.32"];
export const MaxWebRequest = 50;
export const MaxImageRequests = 50;
export const WebRequestTimeout = 30;
// Dev.Config.js
export const DatabaseHosts = ["localhost"];
export const DatabasePort = 5500;
// Config.js
import * as defaultConfig from "./Default.Config.js";

const localConfig = await import(`./${process.env.NODE_ENV}.Config.js`).catch(error => {
    console.log("Error overriding config with local values. " + error);
    return {};
});

export const {
    DatabaseName,
    DatabasePort,
    DatabaseHosts,
    MaxWebRequest,
    MaxImageRequests,
    WebRequestTimeout,
    … // etc
} = Object.assign({}, defaultConfig, localConfig);

或者将配置模块改为默认导出对象,这样你就可以任意操作这些对象了,但是你失去了使用命名导入和静态验证的能力。

相关问题