Open-Attestation / open-attestation

Meta framework for providing digital provenance and integrity to documents.
https://openattestation.com
Apache License 2.0
54 stars 18 forks source link

fix: revert esm changes #197

Closed Nebulis closed 3 years ago

Nebulis commented 3 years ago

closes #195

This PR reverts all the changes to make OA an ESM-only library. The reasons for the revert are detailed below.

Disclaimer

Once OA v6.0.0 was released, I tried to use it in another library. I faced many problems, specifically with Jest, and decided to dig more and check whether there were issues with other tools.

I noticed that the library built didn't work directly (without specific configuration) with the following tools:

Node

Path

When trying to use the library in a node project, the following error happened:

Directory import '/home/nebulis/IdeaProjects/test-esm/node_modules/@govtechsg/open-attestation/dist/shared/validate' is not supported resolving ES modules imported from /home/nebulis/IdeaProjects/test-esm/node_modules/@govtechsg/open-attestation/dist/index.js

The reason is simple: we didn't use full path to import files. We relied on common js behavior, which doesn't require the extension (.js) and which automatically loads index files if it exists.

To fix there is a node flag:

❯ node --experimental-specifier-resolution=node src/index.js

CommonJS

Once the path is fixed for node, a new error happened:

SyntaxError: Named export 'identity' not found. The requested module 'lodash' is a CommonJS module, which may not support all module.exports as named exports.

There is no way to fix this with a flag. We must fix our library and changed all imports that are from CJS libraries. To manage to test our library with the rest of the tools, I fixed manually, inside the node_modules folder. Once fixed, the library was usable with node.

Additional readings:

Jest

To have Jest working, the first thing is to follow the documentation:

And additionally we need a custom resolver (answer found somewhere tin the Jest issues:

module.exports = function (request, options) { if (request === "crypto") { return "crypto"; } return resolver(options.basedir, request); };


Additional readings:
- https://github.com/facebook/jest/issues/9771
- https://github.com/facebook/jest/issues/9771#issuecomment-841624042
- https://github.com/facebook/jest/issues/9430

### ts-kest

To have ts-jest working, the first thing is to follow [the documentation](https://kulshekhar.github.io/ts-jest/docs/guides/esm-support), in addition to the steps used for Jest above:
```json
{
  // jest config
  "preset": "ts-jest",
  "testEnvironment": "node",
  "extensionsToTreatAsEsm": [".ts"],
  "globals": {
    "ts-jest": {
      "useESM": true
    }
  }
}

ts-node

When using ts-node, the following error happened:

Unknown file extension ".ts"

ts-node doesn't support ESM for the moment. The progress can be followed in https://github.com/TypeStrong/ts-node/issues/1007. There is an experimental loader that can be used by following the direction in the issue:

It's important to use ts-node as a loader.

Webpack

Webpack didn't work for the same reason as node. The imports are not valid. There is a flag to set, to force webpack to infer the path:

// webpack.config.js
{
  test: /\.m?js/,
  resolve: {
    fullySpecified: false
  }
}

Additional reading:

Issues

Overall there are 2 issues

  1. Our import paths are wrong. All our paths should directly target the files, without any inference required.
  2. We must import differently CJS modules:
    • lodash
    • hs-sha3
    • flatley

Path

We could expect typescript to help us, but no. Like for custom paths, maintainers decided that it's not their job. So we are left alone. You can find a discussion on the topic below:

There are tools to help:

I tried to find a lint rule to help, but it looks like ESM and Typescript don't move together in the same direction:

If you read carefully the discussion, you will find out that you can import .js files, inside typescript files, even if the file you import is a typescript file (and not a javascript file). Heresy? :)

This solution works, however, I faced issues with ts-jest. Some people made it work, but at this point, I was over with this, and decided to not dig more

CJS modules

The fix is quite simple, but I tried to find a way to trigger an error on build or on lint. Unsuccessfully.

Conclusion

With all this information, we decided to revert back to our old process. ESM and TS don't play well together and we may reconsider in the future if there are improvements with the tooling. The goal must be to help library users and developers but right now, it will just add a burden for both.

john-dot-oa commented 3 years ago

:tada: This PR is included in version 6.0.1 :tada:

The release is available on:

Your semantic-release bot :package::rocket: