Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ESM support #113

Merged
merged 1 commit into from
Apr 3, 2019
Merged

ESM support #113

merged 1 commit into from
Apr 3, 2019

Conversation

Avaq
Copy link
Collaborator

@Avaq Avaq commented Mar 21, 2019

Motivation

To close #99. I started this a long time ago, but never finished it. Today I spent a few minutes adding the finishing touches, although completely untested and written by hand.

Changes

Adds a new choice to the module option: esm. When running doctest with Node version 9 or up available, setting module to esm allows for the documentation comments to be embedded in ECMAScript modules, and the usage of import to load dependencies.

The approach included in this commit has the following benefits and consequences:

  1. The CLI transparently switches between ESM and non-ESM enabled based on the Node version running it. This means the CLI is fully backwards compatible.
  2. The programmatic version also transparently switches between ESM and non-ESM depending on whether it's loaded via import or via require.
  3. The programmatic version has a breaking change, in that its primary function returns a Promise now.

TODO

  • Working proof of concept
  • Find a linting solution
  • Update and extend the unit tests
  • Polish and maximize code reuse

lib/doctest.js Outdated Show resolved Hide resolved
lib/doctest.js Outdated Show resolved Hide resolved
lib/doctest.js Outdated Show resolved Hide resolved
Copy link
Collaborator

@danse danse left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's great work, thanks for submitting! I wouldn't know how to assess the impact on the users although supporting more idioms is definitely welcome!

lib/doctest.js Outdated Show resolved Hide resolved
@Avaq
Copy link
Collaborator Author

Avaq commented Mar 25, 2019

Since import() is not available in cjs modules, and require() is not available in esm modules, it takes quite a bit of hackery getting this working, especially whilst keeping support for older Node versions (it would be only newer node versions that support esm).

The only way to interop between cjs and esm is to use esm to import a cjs module directly. So the idea is to create an esm module that dispatches to the cjs module for all functionality except esm support. I've started work on it, but it's slow going.

lib/command.js Outdated Show resolved Hide resolved
.eslintignore Outdated Show resolved Hide resolved
@@ -2,6 +2,9 @@
"root": true,
"extends": ["./node_modules/sanctuary-style/eslint-es3.json"],
"env": {"node": true},
"globals": {
"Promise": "readonly"
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm using native Promises in the cjs version to align its output type with the mjs version. If you don't align these types, tooling tends to get confused, not to mention people.

bin/doctest Show resolved Hide resolved
lib/command.mjs Outdated Show resolved Hide resolved
// unlines :: Array String -> String
exports.unlines = function(lines) {
return lines.reduce (function(s, line) { return s + line + '\n'; }, '');
};
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I realize that common.js might be a badly chosen name, seeing as it already means something within the very domain we're working with. I intended it to have "common functionality", which is where it gets the name.

lib/doctest.js Outdated Show resolved Hide resolved
lib/doctest.js Show resolved Hide resolved
lib/command.js Outdated Show resolved Hide resolved
test/index.js Show resolved Hide resolved
bin/doctest Outdated Show resolved Hide resolved
bin/doctest Outdated Show resolved Hide resolved
bin/doctest Outdated Show resolved Hide resolved
lib/command.js Show resolved Hide resolved
{
"files": ["*.mjs"],
"env": {"es6": true},
"parser": "babel-eslint"
Copy link
Collaborator Author

@Avaq Avaq Apr 1, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need a special parser for our files, because import() isn't an officially supported part of the language yet. The work around here is that we "pretend" to have written our source for Babel, while in reality we've written it for node --experimental-modules.

test/index.js Outdated
try {
stdout = execSync (command, {encoding: 'utf8', stdio: 'pipe'});
stdout = execSync (
command + (noWarnings ? ' --nodejs --no-warnings' : ''),
Copy link
Collaborator Author

@Avaq Avaq Apr 1, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm passing --no-warnings to the Node executable via the --nodejs flag. This is to suppress the warning that Node gives when using --experimental-modules, which causes stderr assertions to fail.

status: 1,
stdout: '',
stderr: unlines ([
'error: No files for doctesting provided'
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because the errors are now generated by the doctest function, giving no files would mean seeing no errors. This error ensures that you'll still see what went wrong when no files are provided.

@Avaq Avaq marked this pull request as ready for review April 1, 2019 11:56
test/index.js Show resolved Hide resolved
@Avaq
Copy link
Collaborator Author

Avaq commented Apr 1, 2019

@davidchambers @danse I've marked the PR as ready for review. Since opening this PR, I've had to change the approach completely, but things appear to be working great now! I've already discussed some of the changes with David in Gitter. I'll paste some here:

  • bin/doctest now always spawns a sub-process with the --experimental-modules flag, if Node has version 9 or up. Depending on the presence or absence of that flag, Node automatically has a preference for .mjs or .js files.
    • If --experimental-modules is absent, the sub-command is resolved to lib/doctest.js, and the old process continues from there.
    • If --experimental-modules is present, the sub-command is resolved to lib/doctest.mjs, which can make a choice based on the --module setting.
  • lib/doctest.js has remained nearly unchanged, except that it returns a Promise now, and emits a new error message when --module is esm.
  • lib/doctest.mjs contains the esm parser and evaluator, and delegates work to lib/doctest.js in case --module is not esm.

This approach means that:

  1. The CLI transparently switches between ESM and non-ESM enabled version based on the Node version running it. This means the CLI is fully backwards compatible.
  2. The programmatic version also transparently switches between ESM and non-ESM depending on whether it's loaded via import or via require.
  3. The programmatic version has a breaking change, in that it returns a Promise now.

@davidchambers
Copy link
Owner

Fantastic work, Aldwin!

lib/common.js Outdated Show resolved Hide resolved
lib/doctest.mjs Outdated Show resolved Hide resolved
package.json Outdated Show resolved Hide resolved
test/esm/index.mjs Outdated Show resolved Hide resolved
test/index.js Outdated Show resolved Hide resolved
@Avaq Avaq force-pushed the avaq/esm branch 3 times, most recently from 2e8f940 to e7a389f Compare April 2, 2019 09:45
README.md Outdated
@@ -48,14 +48,13 @@ The exit code is 0 if all tests pass, 1 otherwise.

### AMD and CommonJS modules
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should generalize this heading.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bin/doctest Outdated Show resolved Hide resolved
lib/command.js Outdated Show resolved Hide resolved
lib/common.js Outdated Show resolved Hide resolved
bin/doctest Outdated Show resolved Hide resolved
README.md Outdated Show resolved Hide resolved
@Avaq
Copy link
Collaborator Author

Avaq commented Apr 2, 2019

(I stopped doing amendments once I saw you push a commit.)

README.md Outdated Show resolved Hide resolved
bin/doctest Show resolved Hide resolved
@Avaq
Copy link
Collaborator Author

Avaq commented Apr 3, 2019

@danse @davidchambers I'm very happy with this in its current form. Let me know if I can squash! :D

lib/command.js Outdated Show resolved Hide resolved
@davidchambers
Copy link
Owner

Looking good! Please squash. :)

This commit adds a new choice to the 'module' option: 'esm'. When
running doctest with Node version 9 or up available, setting 'module'
to 'esm' allows for the documentation comments to be embedded in
ECMAScript modules, and use 'import' to load dependencies.

The approach included in this commit has the following consequences:

1. The CLI transparently switches between ESM and non-ESM enabled
   based on the Node version running it. This means the CLI is
   fully backwards compatible.
2. The programmatic version also transparently switches between
   ESM and non-ESM depending on whether it's loaded via import
   or via require.
3. The programmatic version has a breaking change, in that its
   primary function returns a Promise now.

Co-Authored-By: David Chambers <dc@davidchambers.me>
@Avaq
Copy link
Collaborator Author

Avaq commented Apr 3, 2019

⚠️ Don't forget that this is a breaking change to the programmatic API.

Copy link
Owner

@davidchambers davidchambers left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🌳

@davidchambers davidchambers merged commit 2f8940b into davidchambers:master Apr 3, 2019
@Avaq Avaq deleted the avaq/esm branch April 3, 2019 11:15
@danse
Copy link
Collaborator

danse commented Apr 5, 2019

thanks @Avaq for your contribution and great work you two, the project keeps developing and the code keeps getting cleaner :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

support es6 modules
3 participants