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 for util and rlp #1948

Closed
wants to merge 5 commits into from
Closed

ESM support for util and rlp #1948

wants to merge 5 commits into from

Conversation

acolytec3
Copy link
Contributor

@acolytec3 acolytec3 commented Jun 9, 2022

This is a proof of concept introducing the use of rollup to compile our libraries for both ESM and CommonJS, starting with just rlp and util since they have the least number of external dependencies.

Steps taken so far:

  • Add Rollup and associated typescript plugin as devDependencies for rlp and `util
  • Make following changes to package-json
    • Add new "module": "[packagename].es.js" key for ESM bundle
    • Remove types key per suggestion from @MicahZoltu
  • Change build step from npm run build to rollup -c which means that Rollup first compiles our code with the Typescript compiler and then outputs CommonJS and ESM bundles
  • Add ts-node key to each package ts-config to tell ts-node to use commonjs compilation or nodejs tests won't run
  • In karma config, override tsconfig to use module: "commonjs" in its Typescript compilation step since our code isn't written in ESM format and karma complains otherwise

Additional thoughts/questions:

  • I know I said previously I didn't want to add a build step for all our libraries but at least for the two I've done so far, adding rollup doesn't seem to have any impact on the build (though admittedly util and rlp are small code bases to begin with).
  • Going at this way makes it feel relatively painless in terms of adding ESM support. I'm sure there are gotchas I'll face trying to move other packages to support ESM but there's no need for us to add '.js' to our imports this way and we can continue to just write Typescript the way we like it and rollup takes care of the packaging part seamlessly (I think anyway).
  • If we do decide to introduce rollup across the entire monorepo, we can presumably replace webpack with it in the client build process though I'm sure that's a bigger beast than what I did in these two packages.
  • Are we doing anything disingenuous by having the tests run against commonjs compiled code versus ESM for the builds? I think the answer is no but I'm not an expert on this one.

@acolytec3 acolytec3 marked this pull request as draft June 9, 2022 10:21
@acolytec3 acolytec3 requested review from ryanio and holgerd77 June 9, 2022 10:25
@codecov
Copy link

codecov bot commented Jun 9, 2022

Codecov Report

Merging #1948 (d34ba0a) into master (8a65e19) will increase coverage by 0.11%.
The diff coverage is n/a.

Impacted file tree graph

Flag Coverage Δ
block 86.15% <ø> (ø)
blockchain 81.72% <ø> (ø)
client 78.40% <ø> (ø)
common 95.01% <ø> (ø)
devp2p 82.78% <ø> (?)
ethash 90.81% <ø> (ø)
rlp 90.55% <ø> (ø)
statemanager 84.55% <ø> (ø)
trie 80.19% <ø> (ø)
tx 92.18% <ø> (ø)
util 87.32% <ø> (ø)
vm 80.45% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

@acolytec3
Copy link
Contributor Author

Okay, I'm 99.9% confident that the esm build worked without issue. I've tested it locally importing RLP into a vanilla javascript react app and it had no issues (though I'm not 100% sure if that's because the React dev server - not sure if it's rollup or webpack). I also created a really simple HTML document and imported the module directly and was able to call the methods so I'm going to call it a success. Here's the HTML code I used for reference. I just saved this as index.html in the rlp package directly and then served it using npx http-serve .

<!DOCTYPE html>
<html lang="en">
    <head>
        Welcome to my Module Importer
    </head>
    <body>
        Here's the body
    </body>
    <script>
        import('./dist/index.es.js').then((RLP) => console.log(RLP.encode('hello')))
    </script>
</html>

Also, just for one further verification step, I logged the RLP object from the script and it produced this
image
So I'm maybe eve more confident 99.999999% sure it's working correctly.

WDYT?

@acolytec3
Copy link
Contributor Author

If we wanted, we could support ESM for just these two packages for now since they are relatively standalone and I don't think it's a breaking change for people trying to import them.

@acolytec3
Copy link
Contributor Author

Following the advice of @ricmoo, I've tested this code in a webworker as well.

Here's the html

<!DOCTYPE html>
<html lang="en">
    <head>
        Welcome to my Module Importer
    </head>
    <body>
        Here's the body
    </body>
    <script>
        new Worker('./worker.js')
    </script>
</html>

Here's the web worker

 import('./dist/index.es.js').then((RLP) => console.log(RLP.encode('hello')))

If you serve this with npx http-serve . and open the page, you'll see the RLP encode output in the conosle, indicating that the module version can be imported in a web worker.

@MicahZoltu
Copy link
Contributor

Change build step from npm run build to rollup -c which means that Rollup first compiles our code with the Typescript compiler and then outputs CommonJS and ESM bundles

I'm not a fan of libraries doing bundling. I really think that should be something left for the application to do, and each application will use a different bundler and some applications will use no bundlers at all.

@MicahZoltu
Copy link
Contributor

I recommend testing against https://github.com/MicahZoltu/preact-typescript-es2015-template if you want to see if your stuff is generally forward compatible and friendly to the future of ES modules on the web. This project uses TS (ultra strict mode) and native modules plus import maps (which is not yet available in all browsers, but a polyfill is included).

Hopefully the readme is clear enough for how to add a new dependency, but I would be happy to help walk someone through adding a new dependency to the project (it doesn't use a bundler, so requires a small manual step when a new dependency is added). Feel free to ping me on Discord if someone wants help with that step.

@acolytec3
Copy link
Contributor Author

Change build step from npm run build to rollup -c which means that Rollup first compiles our code with the Typescript compiler and then outputs CommonJS and ESM bundles

I'm not a fan of libraries doing bundling. I really think that should be something left for the application to do, and each application will use a different bundler and some applications will use no bundlers at all.

Sorry, I used the wrong term. I should have said maybe "packaging" since it's just compiling with typescript and then outputting the cjs and esm files. I'm intentionally not bundling any dependencies in our roll-up step and declared them all as external.

@acolytec3
Copy link
Contributor Author

I recommend testing against https://github.com/MicahZoltu/preact-typescript-es2015-template if you want to see if your stuff is generally forward compatible and friendly to the future of ES modules on the web. This project uses TS (ultra strict mode) and native modules plus import maps (which is not yet available in all browsers, but a polyfill is included).

Hopefully the readme is clear enough for how to add a new dependency, but I would be happy to help walk someone through adding a new dependency to the project (it doesn't use a bundler, so requires a small manual step when a new dependency is added). Feel free to ping me on Discord if someone wants help with that step.

Awesome, will take a look!

@MicahZoltu
Copy link
Contributor

MicahZoltu commented Jun 11, 2022

Sorry, I used the wrong term. I should have said maybe "packaging" since it's just compiling with typescript and then outputting the cjs and esm files. I'm intentionally not bundling any dependencies in our roll-up step and declared them all as external.

Why use rollup at all? Why not just run tsc directly and reduce the number of tools you need (lower build complexity)?

@acolytec3
Copy link
Contributor Author

Sorry, I used the wrong term. I should have said maybe "packaging" since it's just compiling with typescript and then outputting the cjs and esm files. I'm intentionally not bundling any dependencies in our roll-up step and declared them all as external.

Why use rollup at all? Why not just run tsc directly and reduce the number of tools you need (lower build complexity)?

Basically because @ryanio was having a fair amount of difficulty getting #1936 to work with tsc only (see the comment thread but my basic understanding is that the CJS build process wasn't working and there were also some issues with karma-typescript that were proving tricky to resolve).

Since I'm never done ESM builds before, Rollup is specifically designed for building ESM modules (as I understand it), and is a build-tool of choice for many projects, so it seemed reasonable to try using industry standard tools and see how it goes. I'm not opposed to native tsc only but if this proof of concept plays out when I add it to the rest of the packages, this approach feels pretty painless. We'd likely end up adding some sort of additional build step anyway if we use your typescript transformer to add all the ESM required file extensions. I'm not opposed to another approach but this seems to work pretty well. And if rollup proved faster than webpack in building the client browser distribution, that would be a big win in itself.

@MicahZoltu
Copy link
Contributor

Every additional tool in the build chain increases complexity and risk of something breaking, and the more complexity in the build toolchain the harder it is to debug things. TTSC + Transformer plugin is an incredibly thin addition to the toolchain, which helps minimize complexity increase and chance of failure and reduces debugging required when problems are encountered. Rollup on the other hand is a pretty heavyweight tool (though not as bad as webpack) and adds a lot of complexity, and when things break troubleshooting becomes much harder.

In #1936 I see the following comment, was there more details somewhere?

I haven't been able to get the commonjs typescript build working yet since it complains about some of the esm changes

@acolytec3
Copy link
Contributor Author

In #1936 I see the following comment, was there more details somewhere?

I haven't been able to get the commonjs typescript build working yet since it complains about some of the esm changes

You'd have to go back and build it locally again. I think there was maybe a place in ethash and then in devp2p where TSC wouldn't compile it with the ESM changes that were made. And karma was having issues as well (though here again, I don't remember the specifics).

@MicahZoltu
Copy link
Contributor

Generally speaking, my advise would be to resolve those issues if at all possible. Adding Rollup is likely covering up some underlying problem that may or may not come back to bite you later.

@acolytec3
Copy link
Contributor Author

I'm going to close this for now as an experiment worth having done. Will revisit down the road once we decide on how to proceed with regards to ESM support.

@acolytec3 acolytec3 closed this Jul 11, 2022
@holgerd77 holgerd77 deleted the rollup-esm branch March 2, 2023 09:52
@holgerd77 holgerd77 restored the rollup-esm branch March 2, 2023 09:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants