Provides a test CLI for ES Module (ESM) changes to @oclif v2 against the widest coverage of Node 12.0.0 - 15.x using
--experimental-modules
& CommonJS (CJS) interop for named exports.
There are three test suites for ESM support for Oclif v2:
- test-cli-modern / everything works as expected
- test-cli-cjs-interop / workaround for CJS named exports
- test-cli-experimental-modules (this one) / usage of --experimental-modules + workaround for CJS named exports
This test CLI and Github Action CI / CD test suite covers the largest swath of Node versions starting at 12.0.0
.
A discussion issue about ESM support has concluded with a merge of ESM
support on the @oclif/core
repo. Please see this comment
on updated details on how to publish an ESM Oclif v2 CLI before the full launch of Oclif v2.
Click here to view the latest Action CI / CD run
(requires a valid Github login). The test suite is run in a matrix support macos-latest
, ubuntu-latest
, windows-latest
on Node versions 12.0.0
, 12.17.0
, 12.x
, 14.0.0
, 14.x
, 16.0.0
and 16.x
.
All the test suites use @oclif/core 0.5.10+
.
For testing the CLI is invoked locally via cross-spawn only. This is a limitation due to ESM tests via the esm
module
on Node 12.0.0
. Please see test-cli-modern
and test-cli-cjs-interop
for examples on API / programmatic tests that
run on Node 12.17.0+
.
It should be noted that everything is ESM from the test CLI to the test suite itself. Most of the complications w/ ESM
revolves around support in Mocha for Node versions that require --experimental-modules
(12.0 - 12.16
). This is due
to not being able to invoke Mocha with --experimental-modules
. To get the test suite to function on Node versions
prior to 12.17.0
the esm module is loaded in the Github Action after dependencies are
installed and
Mocha is invoked requiring esm.
In the Github Action there are two build jobs build-exp
for Node prior to 12.17.0
and build-node
for Node versions 12.17.0
and higher.
This test suite demonstrates that it is possible to launch an Oclif ESM CLI with very wide support for the Node
ecosystem version 12.0.0+
, however for practical purposes the test-cli-cjs-interop
for Node 12.17+ or
test-cli-modern
version for Node 12.20.0+
& 14.13.0+
is the recommended solution for Oclif v2. It is not likely
that the Oclif CLI / project generator should support this --experimental-modules version, but is made available as a
proof of concept and guide for anyone who manually wishes to work with such a configuration and absolutely support
Node 12.0.0+
.
Take note that in package.json
"type": "module"
is set. As things go this requires the bin bootstrap file
./bin/run
to be renamed to ./bin/run.js
to support ESM. There is a complication with the bootstrap file insofar that
Node must be invoked with --experimental-modules
, however shebangs do not support multiple arguments.
#!/usr/bin/env node --experimental-modules
does not work, so a non-obvious workaround
is required.
#!/usr/bin/env sh
// 2>/dev/null; exec /usr/bin/env node --experimental-modules "$0" "$@"
There are a couple of articles one might find when researching how to invoke Node with multiple arguments.
The first lays out the general idea, but is incomplete
insofar that in the second line //#
in bash is interpreted as an invalid path thus it needs to be further modified to
the solution presented above. The suggestion to use 2>/dev/null
was found in a similar discussion
about shebang arguments. Please refer to both of these sources for a more thorough explanation. Do note that the Windows
run.cmd
simply adds --experimental-modules
.
The next area of note regarding a workaround is that until Node version 12.20.0+
and 14.13.0+
that one can not
import via standard ESM mechanisms named exports from CJS modules. Since Oclif is a CJS module one must import Oclif
as the entire package as a default export.
Instead of import { Command } from '@oclif/core'
one must do the following import oclif from '@oclif/core'
then
subsequently reference Command as oclif.Command
.
For more information on CJS named exports please see this Node / Modules issue.
Since Mocha can not be invoked with --experimental-modules
and the public @oclif/test
testing tools for Oclif is not
updated for v2 with the ESM additions to @oclif/core
, and the test source is ESM itself the tests must be conducted
by using spawn and invoking the bootstrap code. To accomplish this cross-platform with Windows cross-spawn
is utilized. The local bootstrap code, ./bin/run.js
is invoked.
nyc does not support code coverage for ESM based tests in Mocha presently. The
solution is to use c8 which does work with ESM tests and is a drop in replacement
for nyc
. This repo uses Codecov to publish a coverage report
in the GH Action. When running tests locally a ./coverage
directory is created that contains the coverage report. As
can be seen in the report full coverage of both the CLI command / init files and bin bootstrap occurs.
While there is newly added ESM support to @oclif/core v0.5.10+
the rest of the Oclif v2 infrastructure and plugins are
not updated to use @oclif/core
yet. This is somewhat problematic in using @oclif/dev-cli v1.26.0
and in particular the
oclif-dev manifest
CLI command in the prepack
or prepublishOnly
NPM scripts. There is a workaround though to
publish ESM Oclif v2 CLIs using the oclif-dev manifest
command. It requires installing all dependencies from Oclif
then manually updating @oclif/config
which is the v1 version depended on by @oclif/dev-cli
. ESM support has been
back-ported with a hard fork of @oclif/config v1
in this repository.
The contents of the lib directory from the above repository needs to be copied into node_modules/@oclif/config/lib
as
it adds ESM config loading support to @oclif/config v1
Note: This workaround only works with oclif-dev manifest
command and not the README command.
A comment tracking the current best practice or procedure to publish an ESM Oclif v2 CLI is posted here. When Oclif v2 fully launches no workarounds will be necessary.