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

Support for ES modules #178

Open
bencoveney opened this issue Nov 1, 2021 · 15 comments
Open

Support for ES modules #178

bencoveney opened this issue Nov 1, 2021 · 15 comments

Comments

@bencoveney
Copy link
Owner

bencoveney commented Nov 1, 2021

Support for ES modules

Barrelsby is a stand-in for the developer (writing code) and should be able to write equivalent code to that which the user would write - this now includes code structured as ES modules.

Detecting CommonJS Modules or ES Modules

When building each barrel file, we should perform the following steps:

  • Starting at the directory where the barrel file would be placed, look for a package.json file.
  • If one is not found, repeat the process for each parent directory until one is found.

Once this has been done, we will be able to work out if we are using CommonJS os ES Modules:

  • If a package.json file was found and it contains a "type" field with the value "module", then we are using ES Modules.
  • Otherwise, we are using Common JS.

This matches both the TypeScript and Node logic for determining whether a file should be treated as CommonJS or ESM.

This needs to be done for each barrel file, as we cannot safely assume that all barrels are part of the same package, for example somebody may be using barrelsby in a monorepo containing multiple projects.

Overriding the module type

We can add an additional configuration option to barrelsby to allow the module type to be manually specified.

  • If --input-type is not provided, then the default behaviour will be to use the detection logic described above.
  • --input-type=commonjs will force Barrelsby to create CommonJS modules.
  • --input-type=module will force Barrelsby to create ES modules.

This argument aligns with how node have named their argument: https://nodejs.org/api/packages.html#--input-type-flag

It could be nice to align with how TypeScript names their argument, however TypeScript allows many more values that are probably overkill for what we want to support.
https://www.typescriptlang.org/tsconfig#module

Support for new file extensions

Barrelsby should be updated so that it also considers .mts and .cts files to be TypeScript files, and will include them when working out which files to add to barrels.

Transformation of file extensions

Barrelsby should perform the following manipulation of import paths when writing import statements in barrel files:

File extension CommonJS Modification ESM Modification
.ts Extension removed Extension replaced with .js
.tsx Extension removed Extension replaced with .js
.mts Extension removed Extension replaced with .mjs
.cts Extension removed Extension replaced with .cjs

For CommonJS modules, the extensions could optionally be preserved, however this would represent a change to existing barrelsby output so is perhaps best left for another time.

While barrelsby does not currently support arbitrary file extensions, it could do so in the future - A possible example might be .json or .css files. In any code that processes import paths it would be great to leave any unknown file extensions in place to support this in the future.

Reading material:

Please let me know if you have any feedback/corrections/suggestions 😄

@BitForger
Copy link
Collaborator

BitForger commented Nov 2, 2021

I think this is a good plan but I'd recommend a couple tweaks.

  • Look for package.json in the directory that it's being run in
  • If none found, assume commonjs mode

I see where you're coming from with stepping up to find a package.json but I'm not sure it's worth the effort to try to figure out how high and where to stop so that you don't get the tool running outside the intended scope of the CWD

I think until some of the major frameworks start moving to ESM we should assume commonjs since that'll be the most common use case for now. I'd use a flag like -E to enable ESM support.

@BitForger
Copy link
Collaborator

Also, it seems that ESM support isn't ready to go mainstream yet unless everyone decides to shift off TypeScript.
https://www.infoworld.com/article/3637149/typescript-delays-esm-support-for-nodejs.html

@bencoveney
Copy link
Owner Author

Waiting for official support from TypeScript sounds like a good plan to me 👍

I think I understand your point regarding looking outside the CWD - It does feel a bit funny. For me I think it would be useful to double check exactly what checks the TypeScript compiler would be performing and under what circumstances.

If the TypeScript compiler will traverse all the way up the directory tree anyway, then I don't think its egregious for us to replicate that and do the same check, and copying whatever they do would give a more consistent solution.

I haven't had chance to run it through completely myself, but would like to:

  • Follow the code path through in the TypeScript compiler and see if there's any checks or caveats we've missed. Looks like this is where the check originates from: program.getImpliedNodeFormatForFile().
  • Sanity check with an experiment using a TypeScript compiler with the ESM feature support, and see if the compiler behaviour really will change depending on the presence/absence of the package.json at the root of my filesystem.

Supporting a flag to manually set the behaviour also sounds like a good idea 👍 The opening post could be amended to:

If --input-type is not provided, assume we are using CommonJS modules (until some point in the future if/when we can detect the module type automatically).
--input-type=commonjs will force Barrelsby to create CommonJS modules.
--input-type=module will force Barrelsby to create ES modules.

Unfortuntely -E has already been used for a different arg but happy to bikeshed alternatives 😉

@github-actions
Copy link

No activity has been seen recently, marking as stale. If this is a mistake please reach out to a collaborator

@github-actions
Copy link

No activity has been seen recently, marking as stale. If this is a mistake please reach out to a collaborator

@mschering
Copy link

mschering commented Jul 15, 2022

I had the problem that the output was missing a .js extension. So I worked around by doing:

barrelsby --config barrelsby.json && sed -i .bak 's/";/.js";/' script/mybarrel.ts && rm script/mybarrel.ts.bak

The sed command replaces "; with .js";

Maybe this helps someone else too.

@BitForger
Copy link
Collaborator

Work has been started here: https://github.com/bencoveney/barrelsby/tree/v3

@KevinNovak
Copy link

Any updates on this? I want to use barrelsby for my project but since I'm using ESM modules I need the ".js" extension added to the end of imports/exports.

@BitForger
Copy link
Collaborator

Any updates on this? I want to use barrelsby for my project but since I'm using ESM modules I need the ".js" extension added to the end of imports/exports.

It's mostly ported in the v3 branch. I haven't been able to get it to run correctly and have been working on getting the integration tests working. I don't have a timeline for you. Sorry

@github-actions
Copy link

No activity has been seen recently, marking as stale. If this is a mistake please reach out to a collaborator

@KevinNovak
Copy link

Still would be nice if this had ESM support

@linonetwo
Copy link

I really need this to fix

Relative import paths need explicit file extensions in EcmaScript imports when '--moduleResolution' is 'node16' or 'nodenext'. Did you mean './errors/index.js'?ts(2835)

@github-actions
Copy link

No activity has been seen recently, marking as stale. If this is a mistake please reach out to a collaborator

@phenomenal-hardy
Copy link

please add ESM support, thanks

@alfonsoar
Copy link

I had the problem that the output was missing a .js extension. So I worked around by doing:

barrelsby --config barrelsby.json && sed -i .bak 's/";/.js";/' script/mybarrel.ts && rm script/mybarrel.ts.bak

The sed command replaces "; with .js";

Maybe this helps someone else too.

For those of you using single quotes:

sed -i.bak 's/\\x27;/.js\\x27;/' src/index.ts && rm src/index.ts.bak

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants