-
Notifications
You must be signed in to change notification settings - Fork 23
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
Convert to a hybrid (CJS + ESM) package #99
base: main
Are you sure you want to change the base?
Conversation
Thanks for your pull request. I will review during the weekend 😉 |
Sorry for waiting, I will comment on Friday 🙏 |
So, dear, @hamishfagg I got the runner working as ESM edition based on your PR!
import { fileURLToPath } from 'url'
import path from 'node:path'
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename) Did you get co2.js working? Other than that, I'd be happy to merge |
I got co2 working but I needed to add a ts declaration first If this PR is merged won't that mean that stepci itself needs to be converted to ESM as well? |
Yes, I suppose then we will have to convert anything into ESM (Cannot use "module" outside of "module" error will occur otherwise). Meanwhile, cannot you just compile Try to change |
We could, though I suspect that compiling got will be more difficult than just compiling the stepCI runner now that we've figured that out :) (we only use the runner). So is the easiest solution perhaps making the runner support commonJS and ESM like I mentioned? |
Hey, ESM is not a priority currently, but rather "nice to have" I don't think compiling My issue with ESM is that you can use CommonJS modules in an ESM module, but not the other way around Another way may be to bundle all the dependencies. With this, we would have a full control of the output format. I'm not sure however, whether an NPM package can export both ESM and CommonJS entry points Anyways, |
I understand, I'm going to try to get the runner to support both - so that it can be used by stepCI, and also by our project.
Which file did you add this section to? Thanks |
src/index.ts |
Take a look at the changes now, I've managed to get the stepCI runner to compile as both commonJS and ESM at the same time - following the link I posted above. The ESM version uses got-ssrf to stop SSRF attacks which was the purpose of these changes. I wasn't able to add your polyfil methods above as they seem to be esm-specific in part. But we can solve that if you like this approach. I'm able to use this code to run our project as both commonJS and esm without any issues. |
Will check out on Friday, thanks! Hopefully that have solved the issue for you 😄 |
So, I've decided the best path forward would be to move to ESM modules and then use a module bundler to compile it to both ESM and CommonJS. Otherwise, I could compile Thanks a lot for your input on that one, I really appreciate that 🙏 |
Isn't this what I've already done, other than using a separate bundler program to compile? My changes compile the runner as both ESM and CommonJS, stores both in separate folders under |
Hey, Unfortunately the issue is more nuanced than that. TypeScript compiler only converts (transpiles) your TypeScript code to JavaScript, however this has no impact on third-party dependencies, such as the The work needed to complete the ESM transition is beyond the scope of this pull request Our best path forward would be to bundle Hopefully, you could get your issue resolved already and I'll update you here on Friday 🙏 |
Here's how you can convert
{
"devDependencies": {
"esbuild": "^0.17.12",
"got": "^12.6.0"
},
"scripts": {
"build": "esbuild got/source/index.ts --bundle --platform=node --outfile=dist/index.js"
}
}
The only downside is that type-declarations are not bundled in the output |
I don't really understand your insistence on converting got to CommonJS. The ONLY issue with converting this lib to ESM is that some projects use the commonJS version - namely the main stepci project. And that problem is fixed by providing the current commonJS version of the runner (which still uses old got) alongside the ESM version. Once that's done, you don't need to convert anything else. That's what I've already done, so we don't need to touch got or any other dependency. So I don't understand why you're talking about how I need a bundler to convert got. |
The problem is that you cannot require ESM modules from a cjs package I have done an experiment:
const { run } = require('./index.js')
"got": "^12.6.0", Here's the error I'm getting:
I will reopen in case you want to pull a more solid fix |
Yes, you can't import the new versions of got into the runner. That is the problem that this PR solves. I'm going to go back to the start because I don't feel like I'm being understood. What we have now:
Everything is fine here and it works great. However... The problemWe would like to use features from got v12.x. And if we do this:
Then we run into the problem that you described. You can't import ESM from CommonJS. Solution 1We could convert all of your projects to ESM and upgrade to got v12.x. This would solve the problem:
But as you have pointed out, this is labour intensive for stepci (and I agree it's too hard). Solution 2You are suggesting this solution:
Imo this is just kicking the can down the road, as any changes to got might break your bundler or cause vulnerabilities that we would be unaware of. Solution 3THIS IS WHAT THIS PR IMPLEMENTS
Both versions of the runner can be distributed via npm simultaneously, so that the correct version for any project is used automatically (this will require zero changes for anyone importing the runner into their project). This 'hybrid' type of package is becoming common because of the mess with commonJS/ESM, so it is not something special. Please take a look at the changes in this PR and see that it compiles a ESM version of the runner as well as a commonJS version, and configures them to be used based on the import type used. |
Regarding Solution 3: |
You don't, you build the runner twice - one with got 11 and one with got 12. This link I provided at the very start of this PR explains all of this, it is not something I've invented: https://www.sensedeep.com/blog/posts/2021/how-to-create-single-source-npm-module.html |
Doesn't make sense to me. Have you tried my example above? The runner is a package. How can a package have "got" as a dependency in package.json with 2 different versions 11 and 12? You do realize, that transpiling from TS > JS does not bundle the packages? They're still downloaded from package.json IMO the cleanest way to resolve the issue would be to convert to ESM. But we don't have any plans to do that currently and this comes with its own downsides |
Yes. Have you tried this PR?
It doesn't. It is built twice. Please read the contents of this PR.
Yes. Please read the contents of package.json in this PR:
Depending on how Again, this is not something new and it is used all over the place. The co2 package in |
Thanks for the explanation! My question was actually how you could have two version of got in a package.json as a dependency, simultaneously. That's suboptimal for us, as we want to depend on the latest version of So the issue here is really: we want both cjs and esm export, got v12 only works with esm, got v11 works on both What I'm proposing:
Does this sound good to you? Ps. Sorry I was getting heated in my previous responses 😄 |
Likewise :)
So in this step 3, got v12 would be transpiled/converted to CJS? Just so I understand right There's also the possibility of 'dynamic importing' got v12 in CJS, but TBH I don't understand enough about js to know if that's a viable solution: https://github.com/sindresorhus/got#install |
As per discussions with @AndrewFarley this is a proof of concept of converting the runner to ESM.
Tests are failing with
ERR_MODULE_NOT_FOUND
but TS is not my forte so not sure what's going on here.It's worth noting that there is a path to supporting both commonJS and ESM: https://www.sensedeep.com/blog/posts/2021/how-to-create-single-source-npm-module.html