Fable: Inconsistent file extension for node (console) targeting when specifying the extension for Fable 3

Created on 2 Nov 2020  路  7Comments  路  Source: fable-compiler/Fable

Description

Compile App.fs with Fable 3 with extension .fs.mjs without specifying the outdir and the output file will be App.fs.mjs (extension .fs.mjs). However the fable library extension will be still .js. I would like to use mjs as extension (with or without the .fs. prefix) because I target node console and not the browser using beta-005. Manually renaming the fable library (rename each file to .mjs extension) output and updating the references in the first few lines makes it work with node 12.x and 14.x. Would it be possible to specify the fable library output extension separately or with the extension too? e.g.: --use-extension-for-fable-core or --fable-core-extension. I may be able to help with a PR, once I get more familiar with Fable3 internals.

The mjs renamed fable library looks like this (each file updated manually to have proper references):

./.fable/fable-library.3.0.0-nagareyama-beta-005
./.fable/fable-library.3.0.0-nagareyama-beta-005/Double.mjs
./.fable/fable-library.3.0.0-nagareyama-beta-005/Int32.mjs
./.fable/fable-library.3.0.0-nagareyama-beta-005/DateOffset.mjs
./.fable/fable-library.3.0.0-nagareyama-beta-005/Util.mjs
./.fable/fable-library.3.0.0-nagareyama-beta-005/MailboxProcessor.mjs
./.fable/fable-library.3.0.0-nagareyama-beta-005/String.mjs
./.fable/fable-library.3.0.0-nagareyama-beta-005/Types.mjs
./.fable/fable-library.3.0.0-nagareyama-beta-005/Async.mjs
./.fable/fable-library.3.0.0-nagareyama-beta-005/Uri.mjs
./.fable/fable-library.3.0.0-nagareyama-beta-005/Set.mjs
./.fable/fable-library.3.0.0-nagareyama-beta-005/Option.mjs
./.fable/fable-library.3.0.0-nagareyama-beta-005/Guid.mjs
./.fable/fable-library.3.0.0-nagareyama-beta-005/BigInt
./.fable/fable-library.3.0.0-nagareyama-beta-005/BigInt/z.mjs
./.fable/fable-library.3.0.0-nagareyama-beta-005/BigInt/n.mjs
./.fable/fable-library.3.0.0-nagareyama-beta-005/Observable.mjs
./.fable/fable-library.3.0.0-nagareyama-beta-005/System.Collections.Generic.mjs
./.fable/fable-library.3.0.0-nagareyama-beta-005/RegExp.mjs
./.fable/fable-library.3.0.0-nagareyama-beta-005/lib
./.fable/fable-library.3.0.0-nagareyama-beta-005/lib/long.mjs
./.fable/fable-library.3.0.0-nagareyama-beta-005/lib/big.mjs
./.fable/fable-library.3.0.0-nagareyama-beta-005/Encoding.mjs
./.fable/fable-library.3.0.0-nagareyama-beta-005/Unicode.9.0.0.mjs
./.fable/fable-library.3.0.0-nagareyama-beta-005/MutableSet.mjs
./.fable/fable-library.3.0.0-nagareyama-beta-005/Date.mjs
./.fable/fable-library.3.0.0-nagareyama-beta-005/BigInt.mjs
./.fable/fable-library.3.0.0-nagareyama-beta-005/Map.mjs
./.fable/fable-library.3.0.0-nagareyama-beta-005/Seq.mjs
./.fable/fable-library.3.0.0-nagareyama-beta-005/FSharp.Core.mjs
./.fable/fable-library.3.0.0-nagareyama-beta-005/Boolean.mjs
./.fable/fable-library.3.0.0-nagareyama-beta-005/TimeSpan.mjs
./.fable/fable-library.3.0.0-nagareyama-beta-005/MapUtil.mjs
./.fable/fable-library.3.0.0-nagareyama-beta-005/AsyncBuilder.mjs
./.fable/fable-library.3.0.0-nagareyama-beta-005/Long.mjs
./.fable/fable-library.3.0.0-nagareyama-beta-005/Timer.mjs
./.fable/fable-library.3.0.0-nagareyama-beta-005/List.mjs
./.fable/fable-library.3.0.0-nagareyama-beta-005/MutableMap.mjs
./.fable/fable-library.3.0.0-nagareyama-beta-005/Event.mjs
./.fable/fable-library.3.0.0-nagareyama-beta-005/Array.mjs
./.fable/fable-library.3.0.0-nagareyama-beta-005/Char.mjs
./.fable/fable-library.3.0.0-nagareyama-beta-005/Global.mjs
./.fable/fable-library.3.0.0-nagareyama-beta-005/Reflection.mjs
./.fable/fable-library.3.0.0-nagareyama-beta-005/FSharp.Collections.mjs
./.fable/fable-library.3.0.0-nagareyama-beta-005/Decimal.mjs
./.fable/fable-library.3.0.0-nagareyama-beta-005/BitConverter.mjs
./.fable/fable-library.3.0.0-nagareyama-beta-005/System.Text.mjs

Related information

  • Fable version: beta-005
  • Operating system: Ubuntu 20.04

All 7 comments

@zpodlovics

Would using "type": "module" in your package.json work for you instead of using .mjs extensions?

We can probably improve the support for .mjs extensions, but there will always be corner cases when importing other libraries since we're no longer walking the import chain (as opposed to, say, Babel or TypeScript). It's probably better to use a post-build processing step to do that.

As mentioned in a comment on the same topic for another compiler, TypeScript also does not want to concern itself with managing .mjs extensions, as it contradicts one of the TypeScript non-goals of not trying to:

Provide an end-to-end build pipeline. Instead, make the system extensible so that external tools can use the compiler for more complex build workflows.

But by all means, feel free to suggest or contribute a solution if you feel strongly about it.
See also #2233.

Actually, I had trouble with "type": "module" in package.json because in that case all .js files must be ES2015 modules, while most of the npm packages are still commonjs. So it may make sense to use the specified extension (.mjs) for the files from fable-library. This would make it consistent too with the other packages in .fable folder. I will give it a try.

@alfonsogarciacaro I think you can use "type": "module" to local scope folders with ES modules, so it may be enough to just add a package.json with "type": "module" in the fable-library folder.

The biggest issue with allowing for custom extensions is that the import extension can't always match the actual file extension, as some extensions are not valid for imports. So we need some mapping between the two, and that complicates things.

@ncave You're right, this is tricky. Actually I just realized some packages have F# and JS files next to each other with the same name, so I'm using automatically .fs.js extension in this case to avoid conflicts. Also we don't know if the JS file is using ES2015 modules so...

@zpodlovics I'm still having problems with "type": "module" in package.json but I'm using the esm loader in a production app and works great. You just need to write/generate an index.js file with the following content:

require = require('esm')(module);
module.exports = require('./Main.js'); // Points to the generated entry JS file

Thanks a lot! It seems that the "type": "module" indeed generate output files that are directly usable by console node using a single self contained project.

Great to hear it's working! Can the issue be closed?

Thanks a lot! Yes, the issue could be closed. I will revisit later if I run into issues with this package.json trick.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

stkb picture stkb  路  3Comments

alfonsogarciacaro picture alfonsogarciacaro  路  3Comments

MangelMaxime picture MangelMaxime  路  3Comments

SirUppyPancakes picture SirUppyPancakes  路  3Comments

et1975 picture et1975  路  3Comments