jayphelps / core-decorators

Library of stage-0 JavaScript decorators (aka ES2016/ES7 decorators but not accurate) inspired by languages that come with built-ins like @​override, @​deprecate, @​autobind, @​mixin and more. Popular with React/Angular, but is framework agnostic.
MIT License
4.52k stars 263 forks source link

Support Node 10 ECMAScript Modules #151

Open dantman opened 6 years ago

dantman commented 6 years ago

Node has implemented native support for ES2015 modules with an .mjs extension. True modules that function according to the spec.

One limitation of the spec differences between real ES modules and CommonJS modules is that imports from CommonJS modules cannot use named imports, the entire exports/module.exports object is just exposed as the default export.

For core-decorators this results in the following: https://github.com/dantman/core-decorators-esm-test

core-decorators-modules daniel$ npm start

> core-decorators-modules@1.0.0 start /private/tmp/core-decorators-modules
> npm run run:babel && npm run run:esm

> core-decorators-modules@1.0.0 run:babel /private/tmp/core-decorators-modules
> babel test.js > babel-test.js && node babel-test.js

{ override: [Getter],
  deprecate: [Getter],
  deprecated: [Getter],
  suppressWarnings: [Getter],
  memoize: [Getter],
  autobind: [Getter],
  readonly: [Getter],
  enumerable: [Getter],
  nonenumerable: [Getter],
  nonconfigurable: [Getter],
  debounce: [Getter],
  throttle: [Getter],
  decorate: [Getter],
  mixin: [Getter],
  mixins: [Getter],
  lazyInitialize: [Getter],
  time: [Getter],
  extendDescriptor: [Getter],
  profile: [Getter],
  applyDecorators: [Getter] }

> core-decorators-modules@1.0.0 run:esm /private/tmp/core-decorators-modules
> node --experimental-modules test.mjs

(node:22402) ExperimentalWarning: The ESM module loader is experimental.
{ default:
   { override: [Getter],
     deprecate: [Getter],
     deprecated: [Getter],
     suppressWarnings: [Getter],
     memoize: [Getter],
     autobind: [Getter],
     readonly: [Getter],
     enumerable: [Getter],
     nonenumerable: [Getter],
     nonconfigurable: [Getter],
     debounce: [Getter],
     throttle: [Getter],
     decorate: [Getter],
     mixin: [Getter],
     mixins: [Getter],
     lazyInitialize: [Getter],
     time: [Getter],
     extendDescriptor: [Getter],
     profile: [Getter],
     applyDecorators: [Getter] } }

You cannot import {autobind} from 'core-decorators'; inside of ESM code. Your only option is to import CD from 'core-decorators'; const {autobind} = CD;.

To make it possible to use named imports from core-decorators I think it would be a good idea to support Node 10's ES modules.

Doing this should be pretty simple for core-decorators, as core-decorators is a leaf package (a package with no dependencies). We just need to export another build in the package that uses ES modules like es/ does but uses the .mjs file extension. Also ESM modules use the same main as normal Node modules, you just specify main without an extension and Node 9- will get your CommonJS .js and Node 10/--experimental-modules will get the ESM .mjs. So either the .mjs build will need to be in lib/ or you'll need an index.js and index.mjs in the package root to switch between module.exports = require('./lib'); and export * from './esm';.

Given how ESM works, I think the ecosystem would adapt to ESM easiest if leaf packages like core-decorators are the first to support ESM. (Then packages with dependencies on leaf packages don't need to worry about importing CommonJS versions of leaf packages)

jayphelps commented 6 years ago

This is a an awesome ticket and example, thank you for taking the time. I'm not focusing much effort on this library at the moment since the decorator spec is still in flux and babel doesn't yet have support for the new spec. Of course if someone else is interested in fixing this they can, but I unfortunately don't have the cycles to prioritize. 👍