如何欺骗 Node.js 将 .js 文件加载为 ES6 模块?

Posted

技术标签:

【中文标题】如何欺骗 Node.js 将 .js 文件加载为 ES6 模块?【英文标题】:How to trick Node.js to load .js files as ES6 modules? 【发布时间】:2018-11-23 13:32:52 【问题描述】:

Node.JS 10 添加了对加载 ES6 模块的实验性支持,这些模块已经在浏览器中运行。这意味着我们最终可以为 Node.JS 和浏览器使用完全相同的文件,而无需任何转译或 polyfill。

除非我们不能。 Node.js 需要 .mjs 扩展名才能将文件作为模块加载。我尝试使用符号链接来欺骗节点,但节点绕过了它:

D:\web\lines>node --experimental-modules ES6test.mjs
(node:7464) ExperimentalWarning: The ESM module loader is experimental.
D:\web\lines\ES6test.js:6
import myLibrary from "./MyFile.mjs";
       ^^^^^^^^^^^^^^^

我想不出任何其他的解决方法来完成这项工作 - 这真的会使整个 ES6 模块支持变得无用。

其他人能想出一些技巧让 Node.js 忽略扩展吗?

【问题讨论】:

这能回答你的问题吗? "Uncaught SyntaxError: Cannot use import statement outside a module" when importing ECMAScript 6 【参考方案1】:

您现在可以在节点 v12.x 中导入 .js 文件,分两步:

在您的 package.json 文件中添加以下行:
// package.json

  "type": "module"

在脚本之前添加--experimental-modules标志
node --experimental-modules index.js

参考:https://nodejs.org/api/esm.html

【讨论】:

我关注了你,但仍然遇到同样的错误。节点版本为 12.19.0【参考方案2】:

Node.js 要求所有 ES 模块都应具有 .mjs 扩展名。由于 ES 模块的 Node.js 支持是实验性的,因此可能会发生变化。 A proposal 和 open pull request 有望通过 package.json esm 标志和 --mode 选项解决此问题。

目前这可以通过自定义ES module loader 解决,该ES module loader 挂钩到默认模块解析器并更改某些模块的模块类型:

custom-loader.mjs

import path from 'path';

const ESM_WITH_JS_EXT = './MyFile.js'; // relative to loader path
const ESM_WITH_JS_EXT_URL = new URL(path.dirname(import.meta.url) + `/$ESM_WITH_JS_EXT`).href;

export function resolve(specifier, parentModuleURL, defaultResolver) 
    const resolvedModule = defaultResolver(specifier, parentModuleURL);

    if (resolvedModule.url === ESM_WITH_JS_EXT_URL)
        resolvedModule.format = 'esm';

    return resolvedModule;

它被用作:

node --experimental-modules --loader ./custom-loader.mjs ./index.mjs

由于 ES 和 CommonJS 模块的评估方式存在根本差异,因此更改应仅限于需要它们的模块。

【讨论】:

看起来很有希望,我会试试的。【参考方案3】:

我用绝妙的esm 包解决了这个问题。您可以启用动态(智能) esm 模块加载包范围,或使用如下标志每次运行:

node -r esm your/es6/module.js

它还具有将每个文件视为 es6 模块的选项,或者仅将那些以 '.mjs' 结尾的文件。那里还有其他软件包,但这个刚刚好用。

【讨论】:

【参考方案4】:

使用适用于 Node.js 的 ES6 导入和导出模块

使用.mjs 扩展名而不是.js 命名文件

创建文件

touch main.mjs lib.mjs

main.js

import  add  from './lib.mjs';
console.log(add(40, 2));

lib.mjs

export let add = (x,y) => 
  return x + y

运行

node --experimental-modules main.js

【讨论】:

【参考方案5】:

这里有一个模块可以满足您的需求esmjs.mjs

import  readFileSync  from 'fs'
import  fileURLToPath, pathToFileURL  from 'url'
import  dirname, join  from 'path'

export const jsmodule = (test_url_or_path, module_path) => 

    const __filename = test_url_or_path.toLowerCase().startsWith('file:')
        ? fileURLToPath(test_url_or_path)
        : test_url_or_path

    const __dirname = dirname(__filename)

    const abs_path = join(__dirname, module_path)
    const file_url = pathToFileURL(abs_path)

    const file_buf = readFileSync(file_url)
    const b64 = file_buf.toString('base64')

    const moduleData = "data:text/javascript;base64," + b64

    return import(moduleData)

来自.mjs模块的用法:

const  hey  = await jsmodule(import.meta.url, '../../test-data/mjs.js')

用法,来自.js文件:

const  hey  = await jsmodule(__filename, '../../test-data/mjs.js')

Reference & tests on Github

【讨论】:

【参考方案6】:

你可以这样做:

创建一个文件module2.mjs(名称由你决定)

'use strict';

export function foo() 
    return 'foo';

创建 index.mjs 文件:

'use strict';

import  foo  from './module2.mjs';

console.log(foo());

使用节点 8.11.1(或 10),您可以运行它(提供命令和输出):

node --experimental-modules index.mjs
(node:60428) ExperimentalWarning: The ESM module loader is experimental.
foo

【讨论】:

我假设目标是导入现有的 .js ES 模块(一个包或公共代码)而不将其扩展名更改为 .mjs。 那么我猜是不可能的,至少对于节点本机功能,如果 babel 是一个选项,那么我猜是可能的 我发现这是可能的,但很麻烦,请参阅我的回答。

以上是关于如何欺骗 Node.js 将 .js 文件加载为 ES6 模块?的主要内容,如果未能解决你的问题,请参考以下文章

如何将 node.js 变量传递/访问到 html 文件/模板

从内存中的字符串加载 node.js 模块

Node.js:如何在不上传文件列表的情况下检查文件夹是不是为空

node.js

puppeteer / node.js - 进入页面,点击加载更多直到所有评论加载,将页面保存为mhtml

如何使用 Node.js 将 CSV 文件转换为 JSON 脚本?