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

fix: esm json import #416

Merged
merged 10 commits into from Nov 14, 2021
Merged

fix: esm json import #416

merged 10 commits into from Nov 14, 2021

Conversation

jly36963
Copy link
Contributor

@jly36963 jly36963 commented Oct 10, 2021

Addresses: yargs/yargs#2040

Problem

The esm require for json files wasn't working properly. I offered a workaround in a comment on the issue above (passing a custom ConfigCallback to yargs.config()), but the underlying issue was the way yargs-parser handles the importing of the json file.

Solution

readFileSync(path, 'utf8') reads the json file as a string, but it needs to be a parsed object. To make it behave the same way as the cjs require, there are two easy solutions.

// use module createRequire
import { createRequire } from "module";
const esmRequire = createRequire(import.meta.url);

const config = esmRequire(path);
// parse the json string
const config = JSON.parse(readFileSync(path, 'utf8'));

I chose the createRequire solution, which required me to change the modules from es2015 to es2020 in tsconfig. I believe it would also require dropping support for Node 10, as createRequire was implemented in node 12. If that's a deal-breaker, I'm cool with switching to the JSON.parse solution instead.

Note

It looks like optimize and typescript are at odds now. Because of the TS 4.4 breaking change, I need to add types to the catch block errors, but optimize doesn't like it.

@jly36963 jly36963 requested a review from bcoe October 10, 2021 20:48
@bcoe
Copy link
Member

bcoe commented Oct 13, 2021

@jly36963 let's make a separate PR that drops Node 10 in yargs-parser?

@jly36963
Copy link
Contributor Author

@bcoe sounds good

@bcoe
Copy link
Member

bcoe commented Oct 20, 2021

@jly36963 a little buy this week, but have this on my mind.

If you don't get to it, I'll work on dropping Node 10 later in the week so we can merge.

@jly36963
Copy link
Contributor Author

jly36963 commented Oct 20, 2021

@bcoe Let me know what else I can do on #421

Copy link
Member

@bcoe bcoe left a comment

Choose a reason for hiding this comment

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

Left a note about adding a couple tests.

I believe the failing tests are due to a regression with @types/node, upgrading the dependency should help.

lib/index.ts Outdated
@@ -42,7 +45,7 @@ const parser = new YargsParser({
if (typeof require !== 'undefined') {
return require(path)
} else if (path.match(/\.json$/)) {
return readFileSync(path, 'utf8')
return esmRequire(path)
Copy link
Member

Choose a reason for hiding this comment

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

It would be good to add one test that exercises this line.

Perhaps we can add a yargs-parser.mjs test to exercise this path? I believe it might also be good to add a test in yargs-parser.cjs, to confirm that we haven't broken JSON importing for CommonJS.

@jly36963 jly36963 marked this pull request as draft October 24, 2021 21:48
@jly36963
Copy link
Contributor Author

@bcoe I'm not very familiar with tscc / tsickle / closure compiler. optimize is failing because tsickle overrides module flag as commonjs, which breaks import.meta. Do you know how to fix this?

failed run

@bcoe
Copy link
Member

bcoe commented Oct 27, 2021

@jkrems can you help figure this out, I want to make sure we don't break Google's use case.

@jkrems
Copy link
Contributor

jkrems commented Oct 28, 2021

This may break in general in environments without native import.meta which unfortunately includes code targeting CJS and also the closure compiler toolchain. We use a different index.ts inside of Google (because some of the dynamic expression in the current one already prevent optimization), so it will not actually break us in this case though.

Anyhow, drive-by comment on the PR: I'm not sure if you want the require to be relative to import.meta.url because that's relative to the yaqs-parser file somewhere in node_modules or relative to whatever ESM bundle it is in (which may not be the same as the bundle defining the config path). Since this is node only, maybe the correct value is actually createRequire('file:///') ("the file system root") or createRequire(pathToURL(join(process.cwd(), "_yargs-parser")))?

P.S.: If you're using createRequire (which I think is great and better than using the contextual require of the file!), maybe you could even do it consistently / independent of ESM or not? The non-import.meta.url variant I mentioned above would work equally well in ESM and CJS and could replace the current implementation in general.

@bcoe
Copy link
Member

bcoe commented Nov 8, 2021

@jkrems thank you for the review.

@bcoe bcoe marked this pull request as ready for review November 14, 2021 23:54
Copy link
Member

@bcoe bcoe left a comment

Choose a reason for hiding this comment

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

I like where this work landed 👍

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.

None yet

4 participants