无法使用esm模块在nodejs中动态导入文件

问题描述 投票:0回答:1

我正在尝试将我的 commonjs 存储库转换为 ESM,但在使用

readFileSync
命令时遇到问题。看来我在节点 18 和节点 20 上得到了不同的行为。我尝试加载路径:

D:\dev\ts-command-line-args\README.MD

我收到一个错误:

错误 [ERR_UNSUPPORTED_ESM_URL_SCHEME]:默认 ESM 加载程序仅支持具有以下方案的 URL:文件和数据。在 Windows 上,绝对路径必须是有效的 file:// URL。收到协议“d:”

然后我给它一个带有模式的路径:

    const markdownPath = pathToFileURL(resolve(args.markdownPath)).href;
    console.log(`Loading existing file from '${chalk.blue(markdownPath)}'`);
    const markdownFileContent = readFileSync(markdownPath).toString();

然后我在节点 18 上收到此错误:

Loading existing file from 'file:///D:/dev/ts-command-line-args/README.MD'

node:fs:601
  handleErrorFromBinding(ctx);
  ^

Error: ENOENT: no such file or directory, open 'file:///D:/dev/ts-command-line-args/README.MD'
    at Object.openSync (node:fs:601:3)
    at readFileSync (node:fs:469:35)
    at writeMarkdown (file:///D:/dev/ts-command-line-args/dist/write-markdown.js:15:33)
    at file:///D:/dev/ts-command-line-args/dist/write-markdown.js:46:1
    at ModuleJob.run (node:internal/modules/esm/module_job:194:25) {
  errno: -4058,
  syscall: 'open',
  code: 'ENOENT',
  path: 'file:///D:/dev/ts-command-line-args/README.MD'
}

(文件肯定存在)

节点 20 上出现此错误:

Error: ENOENT: no such file or directory, open 'D:\dev\ts-command-line-args\file:\D:\dev\ts-command-line-args\README.MD'

更复杂的是,我的项目已经使用几乎相同的方法加载了一个文件:

        console.log(`Loading args from file ${resolve(parsedArgs[options.loadFromFileArg])}`);

        const configFromFile: Partial<Record<keyof T, any>> = JSON.parse(
            readFileSync(resolve(parsedArgs[options.loadFromFileArg])).toString(),
        );

从文件 D:\dev s-command-line-args\package.json 加载参数

此工作代码位于

parse.js
中。我正在运行此代码:

节点分布/写标记

write-markdown.js
进口
parse.js
:

import { parse } from './parse.js';

所以看起来(这是一个假设)在由节点直接运行的js文件中加载文件不起作用,但在导入的js文件中加载文件确实起作用......

我真的希望有人能为我解决这个问题!

node.js es6-modules node.js-fs
1个回答
1
投票

你在追逐一条红鲱鱼。检查错误堆栈并查看错误来自何处始终很重要!

您的

ERR_UNSUPPORTED_ESM_URL_SCHEME
错误的完整堆栈是这样的:

Error [ERR_UNSUPPORTED_ESM_URL_SCHEME]: Only URLs with a scheme in: file, data are supported by the default ESM loader. On Windows, absolute paths must be valid file:// URLs. Received protocol 'c:'
    at new NodeError (node:internal/errors:372:5)
    at throwIfUnsupportedURLScheme (node:internal/modules/esm/resolve:1078:11)
    at defaultResolve (node:internal/modules/esm/resolve:1158:3)
    at ESMLoader.resolve (node:internal/modules/esm/loader:605:30)
    at ESMLoader.getModuleJob (node:internal/modules/esm/loader:318:18)
    at ESMLoader.import (node:internal/modules/esm/loader:404:22)
    at importModuleDynamically (node:internal/modules/esm/translators:106:35)
    at importModuleDynamicallyCallback (node:internal/process/esm_loader:35:14)
    at loadArgConfig (file:///C:/Users/david/Temp/ts-command-line-args/dist/helpers/markdown.helper.js:121:23)
    at file:///C:/Users/david/Temp/ts-command-line-args/dist/helpers/markdown.helper.js:114:42 {
  code: 'ERR_UNSUPPORTED_ESM_URL_SCHEME'
}

重要的部分是:

at loadArgConfig (file:///C:/Users/david/Temp/ts-command-line-args/dist/helpers/markdown.helper.js:121:23)

...引用此代码:

export async function loadArgConfig(jsFile: string, importName: string): Promise<UsageGuideConfig | undefined> {
    const jsPath = join(process.cwd(), jsFile);
    // eslint-disable-next-line @typescript-eslint/no-var-requires
    const jsExports = await import(jsPath); // <------------------- ERROR HERE!!!

    const argConfig: UsageGuideConfig = jsExports[importName];

    if (argConfig == null) {
        console.warn(`Could not import ArgumentConfig named '${importName}' from jsFile '${jsFile}'`);
        return undefined;
    }

    return argConfig;
}

它与

write-markdown.ts
文件及其对
readFileSync
的使用无关!不过,您更改了另一个文件中的代码,这更改了错误,因为您在运行此文件之前之前的代码中引入了一个新错误,因此现在它更早失败了。

解决方案是保留与

readFileSync
相关的代码,因为它工作得很好(没有
pathToFileURL
),而是在
pathToFileURL
中使用
loadArgConfig
(错误抱怨的地方):

const jsExports = await import(pathToFileURL(jsPath).href);

查看我的拉取请求https://github.com/Roaders/ts-command-line-args/pull/45

© www.soinside.com 2019 - 2024. All rights reserved.