Many popular node packages support writing configuration files in JS or TS, like webpack, vite. Now I'm also trying to create a package supporting JS and TS configuration file, which would be used as: my-package --config path/to/config.ts.
I first tried using require directly, which failed for TS (that's quite an obvious result, since no loader for TS is specified).
So I then tried using typescript package to transpile the config file and using require-from-string to load the module, which doesn't work either when the specified config file import some other modules.
So my current problem is: how to get the configuration module resolved under current context.
Note that this package is designed to work like webpack-cli, being added to devDependencies by other packages and used as a tool for development. So the current context refers to the package that installs this package.
Here are some relative posts I've looked through and tried (so don't propose a duplicate to these questions):

推荐答案

在探究了webpack-cli的来源后,我终于在rechoir的帮助下做到了这一点.代码如下:

import interpret from "interpret";
import rechoir from "rechoir";
import AsyncFs from "fs/promises";
import Path from "path";
import { pathToFileURL } from "URL";

// My package is build with `webpack`, so I need the following to bypass its dynamic require constraint.
declare var __non_webpack_require__: ((id: string) => any) | undefined;
const dynamicRequire = typeof __non_webpack_require__ === "function" ? __non_webpack_require__ : require;

async function tryRequireThenImport(module: string): Promise<any> {
    let result;
    try {
        result = dynamicRequire(module);
    } catch (error: any) {
        let importEsm: ((module: string) => Promise<{ default: any }>) | undefined;
        try {
            importEsm = new Function("id", "return import(id);") as any;
        } catch (e) {
            importEsm = undefined;
        }
        if (error.code === "ERR_REQUIRE_ESM" && importEsm) {
            const urlForConfig = pathToFileURL(module).href;
            result = (await importEsm(urlForConfig)).default;
            return result;
        }
        throw error;
    }
    // For babel/typescript
    if (result && typeof result === "object" && "default" in result)
        result = result.default || {};
    return result || {};
}

async function loadModule(path: string): Promise<any> {
    const ext = Path.extname(path);
    if (ext === ".json") {
        const content = await AsyncFs.readFile(path, "utf-8");
        return JSON.parse(content);
    }
    if (!Object.keys(interpret.jsVariants).includes(ext))
        throw new Error(`Unsupported file type: ${ext}`);
    // This is the key point that gets the configuration module loaded under current context
    rechoir.prepare(interpret.jsVariants, path);
    return await tryRequireThenImport(path);
}

实现方式主要是borrow webpack-cli.以下是消息来源:

Node.js相关问答推荐

如何在MongoDB中更新嵌套数组

使用Node.js中的";类型";:";模块";不导入文件

如何使用 Node.js 连接到 Cloud SQL?

如何使用对象中的常量值验证字符串字段?

在 Node JS 中使用 url 链接而不是文件路径

当我try 从本地主机发布新产品时收到错误消息

用户与mongoose 的完美搭配

TypeError:changeChecked 不是函数

使用 firebase 函数 api 运行套接字(相同的端口创建问题)

如何限制 cron 表单将消息推送到 RabbitMQ?

Ansible 将以什么用户身份运行我的命令?

如何使用 UglifyJS 缩小文件夹中的多个 Javascript 文件?

node.js 是否支持yields ?

使用 Socket.io 将客户端连接到服务器

代理后面的凉亭

Node应用程序中相同npm包的两个版本

nodeJS - 如何使用 express 创建和读取会话

如何在 NodeJS 中拆分和修改字符串?

`return function *(){...}` 是什么意思?

node/nodemon 中是否有对 typescript 的源映射支持?