barrelsby
barrelsby copied to clipboard
Support for ES modules
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:
- https://nodejs.org/api/packages.html#--input-type-flag
- https://nodejs.org/api/packages.html#packages_package_json_and_file_extensions
- https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-5.html#ecmascript-module-support-in-nodejs
- https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-5.html#new-file-extensions
Please let me know if you have any feedback/corrections/suggestions 😄
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.
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
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 😉
No activity has been seen recently, marking as stale. If this is a mistake please reach out to a collaborator
No activity has been seen recently, marking as stale. If this is a mistake please reach out to a collaborator
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.
Work has been started here: https://github.com/bencoveney/barrelsby/tree/v3
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.
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
No activity has been seen recently, marking as stale. If this is a mistake please reach out to a collaborator
Still would be nice if this had ESM support
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)
No activity has been seen recently, marking as stale. If this is a mistake please reach out to a collaborator
please add ESM support, thanks
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