跳至正文
Wener Notes

ESM

约 4 分钟阅读
Can I UseChromeSafari
moduleChrome 61+
dynamic-importChrome 63+Safari 11.1+
import-mapsChrome 89+

Can I Use

TypeScript
// 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];

html
<!--
  JSPM Generator Import Map
  Edit URL: https://generator.jspm.io/#U2NhYGBkDM0rySzJSU1hKEpNTC5xMLTQM9QzAADeRmOTGwA
-->
<script type="importmap">
  {
    "imports": {
      "react": "https://ga.jspm.io/npm:[email protected]/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:[email protected]/dist/es-module-shims.js"
  crossorigin="anonymous"
></script>

import map

  • 目前只支持 web - 不支持 worker
  • 可通过脚本动态构建 element
  • 只能映射为本地
html
<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
TypeScript
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

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

__dirname is not defined in ES module scope

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

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

Failed to resolve module references must start with

text
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
TypeScript
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 要放在最后
json
{
  "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
  1. 避免 default exports

require() of ES Module not supported

  • type=module

__esModule

require

JavaScript
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

关联信息

反向链接、本文链接的其他页面和外部资料。

参考资料

GitHub25 条
其他外链17 条
最近更新commit 6dff7d5编辑