Importando Módulos ES6 em CommonJS

You are viewing an old revision of this post, from November 23, 2022 @ 12:07:14. See below for differences between this version and the current revision.

Aqui na Liferay, alguns dias atrás, necessitávamos utilizar o pacote p-map. Só tinha um problema: esta aplicação em específico utilizava módulos no padrão CommonJS, e p-map utiliza módulos ES6. Até algumas das melhores referências que encontrei (por exemplo, este post) deixavam claro que não seria possível importar módulos ES6 a partir de CommonJS.

A boa notícia é que isto não é mais verdade! Usando import dinâmico, podemos carregar módulos ES6 a partir de CommonJS. Vejamos um exemplo.

Neste projeto, o arquivo importer.js tenta utilizar require() para importar um módulo ES6:

const pmap = require('p-map');

exports.importer = () => {
  console.log('Yes, I could import p-map:', pmap);
}Code language: JavaScript (javascript)

Naturalmente, isto não funciona:

$ node index.js 
internal/modules/cjs/loader.js:1102
      throw new ERR_REQUIRE_ESM(filename, parentPath, packageJsonPath);
      ^

Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /home/adam/software/es6commonjs/node_modules/p-map/index.js
require() of ES modules is not supported.
require() of /home/adam/software/es6commonjs/node_modules/p-map/index.js from /home/adam/software/es6commonjs/importer.js is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules.
Instead rename index.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from /home/adam/software/es6commonjs/node_modules/p-map/package.json.

    at new NodeError (internal/errors.js:322:7)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1102:13)
    at Module.load (internal/modules/cjs/loader.js:950:32)
    at Function.Module._load (internal/modules/cjs/loader.js:790:12)
    at Module.require (internal/modules/cjs/loader.js:974:19)
    at require (internal/modules/cjs/helpers.js:101:18)
    at Object.<anonymous> (/home/adam/software/es6commonjs/importer.js:1:14)
    at Module._compile (internal/modules/cjs/loader.js:1085:14)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1114:10)
    at Module.load (internal/modules/cjs/loader.js:950:32) {
  code: 'ERR_REQUIRE_ESM'
}
Code language: Bash (bash)

A solução é converter o require() em um import dinâmico. Mas tem um detalhe: import dinâmicos retornam uma Promise. Há várias maneiras de lidar com isso; a mais simples provavelmente é tornar nossa função assíncrona, como nessa versão:

exports.importer = async () => {
  const pmap = await import('p-map');
  console.log('Yes, I could import p-map:', pmap);
}Code language: JavaScript (javascript)

Agora nossa pequena aplicação funciona!

$ node index.js 
ok
Yes, I could import p-map: [Module: null prototype] {
  AbortError: [class AbortError extends Error],
  default: [AsyncFunction: pMap],
  pMapSkip: Symbol(skip)
}Code language: Bash (bash)

Talvez outros ajustes sejam necessários; eu mesmo precisei ajustar as configurações do eslint). O importante é que isto é possível. E não é uma gambiarra: a própria documentação de Node recomenda essa abordagem.

Então, não se assuste com informações desatualizadas: você não vai precisar reescrever sua aplicação toda como módulos ES 6. Ao menos para nós, isto foi um alívio e tanto!

Post Revisions:

Changes:

November 23, 2022 @ 12:07:14Current Revision
Content
Unchanged: <!-- wp:paragraph -->Unchanged: <!-- wp:paragraph -->
Unchanged: <p>Aqui na Liferay, alguns dias atrás, necessitávamos utilizar o pacote <a href="https:/ /www.npmjs.com/package/p- map">p-map</a>. Só tinha um problema: esta aplicação em específico utilizava módulos no padrão <a href="https:/ /nodejs.org/docs/latest/api/ modules.html" >CommonJS</a>, e p-map utiliza <a href="https:/ /developer.mozilla.org/en- US/docs/Web/JavaScript/Guide/ Modules">módulos ES6</a>. Até algumas das melhores referências que encontrei (por exemplo, <a href="https:/ /reflectoring.io/ nodejs-modules- imports/">este post</a>) deixavam claro que não seria possível importar módulos ES6 a partir de CommonJS.</p>Unchanged: <p>Aqui na Liferay, alguns dias atrás, necessitávamos utilizar o pacote <a href="https:/ /www.npmjs.com/package/p- map">p-map</a>. Só tinha um problema: esta aplicação em específico utilizava módulos no padrão <a href="https:/ /nodejs.org/docs/latest/api/ modules.html" >CommonJS</a>, e p-map utiliza <a href="https:/ /developer.mozilla.org/en- US/docs/Web/JavaScript/Guide/ Modules">módulos ES6</a>. Até algumas das melhores referências que encontrei (por exemplo, <a href="https:/ /reflectoring.io/ nodejs-modules- imports/">este post</a>) deixavam claro que não seria possível importar módulos ES6 a partir de CommonJS.</p>
Unchanged: <!-- /wp:paragraph -->Unchanged: <!-- /wp:paragraph -->
Unchanged: <!-- wp:paragraph -->Unchanged: <!-- wp:paragraph -->
Deleted: <p>A boa notícia é que isto não é mais verdade! Usando <code>import</code> dinâmico, podemos carregar módulos ES6 a partir de CommonJS. Vejamos um exemplo.</p> Added: <p>A boa notícia é que isto não é mais verdade! Usando <a href="https:/ /developer.mozilla.org/en- US/docs/Web/JavaScript/Reference/ Operators/import" ><code>import</code> dinâmico</a>, podemos carregar módulos ES6 a partir de CommonJS. Vejamos um exemplo.</p>
Unchanged: <!-- /wp:paragraph -->Unchanged: <!-- /wp:paragraph -->
Unchanged: <!-- wp:paragraph -->Unchanged: <!-- wp:paragraph -->
Unchanged: <p>Neste <a href="https:/ /github.com/brandizzi/ es6commonjs/ ">projeto</a>, o arquivo <a href="https:/ /github.com/brandizzi/ es6commonjs/ blob/d7c3e3a69ef7828e6dcf714a5e83e77892294490/ importer.js"> importer.js</a> tenta utilizar <code>require()</code> para importar um módulo ES6:</p>Unchanged: <p>Neste <a href="https:/ /github.com/brandizzi/ es6commonjs/ ">projeto</a>, o arquivo <a href="https:/ /github.com/brandizzi/ es6commonjs/ blob/d7c3e3a69ef7828e6dcf714a5e83e77892294490/ importer.js"> importer.js</a> tenta utilizar <code>require()</code> para importar um módulo ES6:</p>
Unchanged: <!-- /wp:paragraph -->Unchanged: <!-- /wp:paragraph -->
Unchanged: <!-- wp:code {"language":" javascript"} -->Unchanged: <!-- wp:code {"language":" javascript"} -->
Unchanged: <pre class="wp-block- code"><code>const pmap = require('p-map');Unchanged: <pre class="wp-block- code"><code>const pmap = require('p-map');
Unchanged: exports.importer = () =&gt; {Unchanged: exports.importer = () =&gt; {
Unchanged: console.log('Yes, I could import p-map:', pmap);Unchanged: console.log('Yes, I could import p-map:', pmap);
Unchanged: }</code></pre>Unchanged: }</code></pre>
Unchanged: <!-- /wp:code -->Unchanged: <!-- /wp:code -->
Unchanged: <!-- wp:paragraph -->Unchanged: <!-- wp:paragraph -->
Unchanged: <p>Naturalmente, isto não funciona:</p>Unchanged: <p>Naturalmente, isto não funciona:</p>
Unchanged: <!-- /wp:paragraph -->Unchanged: <!-- /wp:paragraph -->
Unchanged: <!-- wp:code {"language":"bash"} -->Unchanged: <!-- wp:code {"language":"bash"} -->
Unchanged: <pre class="wp-block- code"><code>$ node index.js Unchanged: <pre class="wp-block- code"><code>$ node index.js
Unchanged: internal/modules/ cjs/loader.js:1102Unchanged: internal/modules/ cjs/loader.js:1102
Unchanged: throw new ERR_REQUIRE_ESM(filename, parentPath, packageJsonPath);Unchanged: throw new ERR_REQUIRE_ESM(filename, parentPath, packageJsonPath);
Unchanged: ^Unchanged: ^
Unchanged: Error &#91;ERR_REQUIRE_ESM]: Must use import to load ES Module: /home/adam/software/ es6commonjs/node_modules/ p-map/index.jsUnchanged: Error &#91;ERR_REQUIRE_ESM]: Must use import to load ES Module: /home/adam/software/ es6commonjs/node_modules/ p-map/index.js
Unchanged: require() of ES modules is not supported.Unchanged: require() of ES modules is not supported.
Unchanged: require() of /home/adam/software/ es6commonjs/node_modules/ p-map/index.js from /home/adam/software/ es6commonjs/importer.js is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules.Unchanged: require() of /home/adam/software/ es6commonjs/node_modules/ p-map/index.js from /home/adam/software/ es6commonjs/importer.js is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules.
Unchanged: Instead rename index.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from /home/adam/software/ es6commonjs/node_modules/ p-map/package.json.Unchanged: Instead rename index.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from /home/adam/software/ es6commonjs/node_modules/ p-map/package.json.
Unchanged: at new NodeError (internal/errors.js:322:7)Unchanged: at new NodeError (internal/errors.js:322:7)
Unchanged: at Object.Module._ extensions..js (internal/modules/ cjs/loader.js:1102:13)Unchanged: at Object.Module._ extensions..js (internal/modules/ cjs/loader.js:1102:13)
Unchanged: at Module.load (internal/modules/ cjs/loader.js:950:32)Unchanged: at Module.load (internal/modules/ cjs/loader.js:950:32)
Unchanged: at Function.Module._load (internal/modules/ cjs/loader.js:790:12)Unchanged: at Function.Module._load (internal/modules/ cjs/loader.js:790:12)
Unchanged: at Module.require (internal/modules/ cjs/loader.js:974:19)Unchanged: at Module.require (internal/modules/ cjs/loader.js:974:19)
Unchanged: at require (internal/modules/ cjs/helpers.js:101:18)Unchanged: at require (internal/modules/ cjs/helpers.js:101:18)
Unchanged: at Object.&lt;anonymous&gt; (/home/adam/software/ es6commonjs/importer.js:1:14)Unchanged: at Object.&lt;anonymous&gt; (/home/adam/software/ es6commonjs/importer.js:1:14)
Unchanged: at Module._compile (internal/modules/ cjs/loader.js:1085:14)Unchanged: at Module._compile (internal/modules/ cjs/loader.js:1085:14)
Unchanged: at Object.Module._ extensions..js (internal/modules/ cjs/loader.js:1114:10)Unchanged: at Object.Module._ extensions..js (internal/modules/ cjs/loader.js:1114:10)
Unchanged: at Module.load (internal/modules/ cjs/loader.js:950:32) {Unchanged: at Module.load (internal/modules/ cjs/loader.js:950:32) {
Unchanged: code: 'ERR_REQUIRE_ESM'Unchanged: code: 'ERR_REQUIRE_ESM'
Unchanged: }Unchanged: }
Unchanged: </code></pre>Unchanged: </code></pre>
Unchanged: <!-- /wp:code -->Unchanged: <!-- /wp:code -->
Unchanged: <!-- wp:paragraph -->Unchanged: <!-- wp:paragraph -->
Unchanged: <p>A solução é converter o <code>require()</code> em um <code>import</code> dinâmico. Mas tem um detalhe: <code>import</code> dinâmicos retornam uma <code>Promise</code>. Há várias maneiras de lidar com isso; a mais simples provavelmente é tornar nossa função assíncrona, como <a href="https:/ /github.com/brandizzi/ es6commonjs/ blob/be491a9/ importer.js">nessa versão</a>:</p>Unchanged: <p>A solução é converter o <code>require()</code> em um <code>import</code> dinâmico. Mas tem um detalhe: <code>import</code> dinâmicos retornam uma <code>Promise</code>. Há várias maneiras de lidar com isso; a mais simples provavelmente é tornar nossa função assíncrona, como <a href="https:/ /github.com/brandizzi/ es6commonjs/ blob/be491a9/ importer.js">nessa versão</a>:</p>
Unchanged: <!-- /wp:paragraph -->Unchanged: <!-- /wp:paragraph -->
Unchanged: <!-- wp:code {"language":" javascript"} -->Unchanged: <!-- wp:code {"language":" javascript"} -->
Unchanged: <pre class="wp-block- code"><code>exports.importer = async () =&gt; {Unchanged: <pre class="wp-block- code"><code>exports.importer = async () =&gt; {
Unchanged: const pmap = await import('p-map');Unchanged: const pmap = await import('p-map');
Unchanged: console.log('Yes, I could import p-map:', pmap);Unchanged: console.log('Yes, I could import p-map:', pmap);
Unchanged: }</code></pre>Unchanged: }</code></pre>
Unchanged: <!-- /wp:code -->Unchanged: <!-- /wp:code -->
Unchanged: <!-- wp:paragraph -->Unchanged: <!-- wp:paragraph -->
Unchanged: <p>Agora nossa pequena aplicação funciona!</p>Unchanged: <p>Agora nossa pequena aplicação funciona!</p>
Unchanged: <!-- /wp:paragraph -->Unchanged: <!-- /wp:paragraph -->
Unchanged: <!-- wp:code {"language":"bash"} -->Unchanged: <!-- wp:code {"language":"bash"} -->
Unchanged: <pre class="wp-block- code"><code>$ node index.js Unchanged: <pre class="wp-block- code"><code>$ node index.js
Unchanged: okUnchanged: ok
Unchanged: Yes, I could import p-map: &#91;Module: null prototype] {Unchanged: Yes, I could import p-map: &#91;Module: null prototype] {
Unchanged: AbortError: &#91;class AbortError extends Error],Unchanged: AbortError: &#91;class AbortError extends Error],
Unchanged: default: &#91;AsyncFunction: pMap],Unchanged: default: &#91;AsyncFunction: pMap],
Unchanged: pMapSkip: Symbol(skip)Unchanged: pMapSkip: Symbol(skip)
Unchanged: }</code></pre>Unchanged: }</code></pre>
Unchanged: <!-- /wp:code -->Unchanged: <!-- /wp:code -->
Unchanged: <!-- wp:paragraph -->Unchanged: <!-- wp:paragraph -->
Deleted: <p>Talvez outros ajustes sejam necessários; eu mesmo precisei ajustar as configurações do <a href="https:/ /stackoverflow.com/questions/ 47815775/dynamic-imports- for-code-splitting-cause- eslint-parsing- error-import" >eslint</a>). O importante é que isto é possível. E não é uma gambiarra: <a href="https:/ /nodejs.org/api/ esm.html#import- expressions">a própria documentação de Node recomenda essa abordagem</a>.</p> Added: <p>Outros ajustes podem ser necessários; eu mesmo precisei ajustar as configurações do <a href="https:/ /stackoverflow.com/questions/ 47815775/dynamic-imports- for-code-splitting-cause- eslint-parsing- error-import" >eslint</a>. O importante é que isto é possível. E não é uma gambiarra: <a href="https:/ /nodejs.org/api/ esm.html#import- expressions">a própria documentação de Node recomenda essa abordagem</a>.</p>
Unchanged: <!-- /wp:paragraph -->Unchanged: <!-- /wp:paragraph -->
Unchanged: <!-- wp:paragraph -->Unchanged: <!-- wp:paragraph -->
Deleted: <p>Então, não se assuste com informações desatualizadas: você <em>não</em> vai precisar reescrever sua aplicação toda como módulos ES 6. Ao menos para nós, isto foi um alívio e tanto!</p> Added: <p>Então, não se assuste com informações desatualizadas: você <em>não</em> vai precisar reescrever sua aplicação toda como módulos ES 6, ao menos por enquanto.</p>
Unchanged: <!-- /wp:paragraph -->Unchanged: <!-- /wp:paragraph -->

Note: Spaces may be added to comparison text to allow better line wrapping.

Post a Comment

Your email is never shared. Required fields are marked *

*
*

%d bloggers like this: