Skip to main content

ESM

Can I UseChromeSafari
moduleChrome 61+
dynamic-importChrome 63+Safari 11.1+
import-mapsChrome 89+

Can I Use

caution
  • react 没有 esm
    • import React from "https://esm.sh/react@17"
  • electron 不支持 esm #21457
tip
// reexport default
export {default /* …, */} from 'module-name';

// import 为一个 Module
import * as Reaction from '@wener/reaction'
// 模块 reexport
export * from '@wener/reaction'

import 'data:text/javascript,console.log("hello!");';
import _ from 'data:application/json,"world!"' assert {type: 'json'};

import fs from 'node:fs/promises';

// dyanmic import json
const pkgJsonModule = await import('https://cdn.jsdelivr.net/npm/@wener/reaction@latest/package.json', {assert: {type: 'json'}});
// 通过 toStringTag 判断是否为 Module
console.assert(pkgJsonModule[Symbol.toStringTag] === 'Modoule')
cons {default:{version}} = pkgJsonModule
console.log(version)

// CSS Module
import styles from "./styles.css" assert { type: "css" };
document.adoptedStyleSheets = [...document.adoptedStyleSheets, styles];

<!--
JSPM Generator Import Map
Edit URL: https://generator.jspm.io/#U2NhYGBkDM0rySzJSU1hKEpNTC5xMLTQM9QzAADeRmOTGwA
-->
<script type="importmap">
{
"imports": {
"react": "https://ga.jspm.io/npm:react@18.1.0/index.js"
}
}
</script>

<!-- ES Module Shims: Import maps polyfill for modules browsers without import maps support (all except Chrome 89+) -->
<script
async
src="https://ga.jspm.io/npm:es-module-shims@1.5.1/dist/es-module-shims.js"
crossorigin="anonymous"
></script>

import map

tip
  • 目前只支持 web - 不支持 worker
  • 可通过脚本动态构建 element
  • 只能映射为本地
<script type="importmap">
{
"imports": {
"moment": "/node_modules/moment/src/moment.js",
"lodash": "/node_modules/lodash-es/lodash.js"
}
}
</script>
<!-- application/importmap+json -->
<script type="importmap" src="import-map.importmap"></script>
动态构建
<script>
if (HTMLScriptElement.supports && HTMLScriptElement.supports('importmap')) {
console.log('Your browser supports import maps.');
}

const importMap = {
imports: {
moment: '/moment.mjs',
lodash: someFeatureDetection() ? '/lodash.mjs' : '/lodash-legacy-browsers.mjs',
},
};

const im = document.createElement('script');
im.type = 'importmap';
im.textContent = JSON.stringify(importMap);
document.currentScript.after(im);
</script>

<script type="module">
import _ from 'lodash'; // will fetch the right URL for this browser
</script>
importmap 规范
{
"imports": {
// import moment from "moment";
"moment": "/node_modules/moment/src/moment.js",
// 自动映射
// import localeData from "moment/locale/zh-cn.js";
"moment/": "/node_modules/moment/src/",
"lodash": "/node_modules/lodash-es/lodash.js",
// import fp from "lodash/fp.js";
"lodash/": "/node_modules/lodash-es/",
// 无扩展名
// import fp from "lodash/fp"
"lodash/fp": "/node_modules/lodash-es/fp.js",

// remapping - 对 <script> 不生效
"https://www.unpkg.com/vue/dist/vue.runtime.esm.js": "/node_modules/vue/dist/vue.runtime.esm.js",
// 前缀 remmaping
"https://www.unpkg.com/vue/": "/node_modules/vue/",
"/app/helpers.mjs": "/app/helpers/index.mjs",

// 常见处理 hash 场景
"/js/app.mjs": "/js/app-8e0d62a03.mjs"
},
// 限定上下文,修改依赖
"scopes": {
"/scope2/": {
"a": "/a-2.mjs"
},
"/scope2/scope3/": {
"b": "/b-3.mjs"
}
}
}

import json

  • NodeJS v17.1+ 2021-11-09
import info from `./package.json` assert { type: `json` };

const { default: info } = await import("./package.json", {
assert: {
type: "json",
},
});

exports

NodeJS

Internal

import.meta.resolve

FAQ

Directory import is not supported resolving ES modules

// 不可以
const M = import('./modules/core');
// 可以
const M = import('./modules/core/index.js');

__dirname is not defined in ES module scope

  • 定义最好用 var 避免重复定义,有些 bundle 会加这两个定义
import * as url from 'url';
var __filename = url.fileURLToPath(import.meta.url);
var __dirname = url.fileURLToPath(new URL('.', import.meta.url));
import { fileURLToPath } from 'node:url';
import path from 'node:path';

var __filename = fileURLToPath(import.meta.url);
var __dirname = path.dirname(fileURLToPath(import.meta.url));
// 新的 NodeJS 也支持 URL,可以直接 resolve
const foo = new URL('foo.js', import.meta.url);

Failed to resolve module references must start with

Failed to resolve module specifier "app". Relative references must start with either "/", "./", or "../".

An import map is added after module script load was triggered.

process.env.NODE_ENV

  • import.meta.env.MODE
  • __DEV__ -> import.meta.env.DEV
  • import.meta.env.PROD
declare global {
interface ImportMetaEnv extends Readonly<Record<string, any>> {
readonly MODE: string;
readonly BASE_URL: string;
readonly DEV: boolean;
readonly PROD: boolean;
readonly SSR: boolean;
}

interface ImportMeta {
readonly env: ImportMetaEnv;
}
}

Module not found: Default condition should be last one

  • default 要放在最后
{
"exports": {
".": {
"types": "./src/index.ts",
"development": "./dist/esm/wener-reaction.development.js",
"import": "./lib/esm/index.js",
"require": "./lib/cjs/index.js",
"default": "./lib/esm/index.js"
}
}
}

NextJS Cannot find module without suffix

  1. 配置使用 bundle 后的文件 - 无法 tree-shake
  2. 使用 mjs 后缀 - 会忽略 type:module
  1. 避免 default exports

require() of ES Module not supported

  • type=module

__esModule

require

import { createRequire } from 'module';
const require = createRequire(import.meta.url);

[ERR_UNSUPPORTED_ESM_URL_SCHEME]: Only URLs with a scheme in: file, data, and node are supported by the default ESM loader. Received protocol 'https:'

  • nodejs,bun --experimental-network-imports