I have a basic Node.js console application set up to use Fable via Webpack. My config file has the basic setup, with the fable-loader configured to handle F# files (I'm also using the BundleAnalyzerPlugin to help visualize what is in my bundle):
let path = require("path");
let webpack = require("webpack");
let fableUtils = require("fable-utils");
let BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const resolve = filePath => path.join(__dirname, filePath);
let babelOptions = fableUtils.resolveBabelOptions({
presets: [["env", { "modules": false }]],
plugins: ["transform-runtime"]
});
module.exports = {
entry: resolve("./src/Console.fsproj"),
output: {
path: resolve("./app"),
filename: "Console.js"
},
resolve: { modules: [resolve("node_modules")] },
module: {
rules: [
{
test: /\.fs(x|proj)?$/,
use: {
loader: "fable-loader",
options: { babel: babelOptions, define: ["DEBUG"] }
}
}
]
},
devtool: "source-map",
mode: "development",
watch: true,
target: "node",
externals: {
serialport: {
commonjs: "serialport"
}
},
plugins: [new BundleAnalyzerPlugin()]
};
I am trying to use node-serialport (https://github.com/node-serialport/node-serialport). From their documentation and various GitHub issues, it seems they do not support Webpack, and in fact, Webpack in general seems to choke on Native Node.js modules. The solution seems to be to use the Webpack externals option as I have used it above (although, admittedly, I am pretty confused on what exactly it is supposed to do, other than simply exclude the module from my bundle).
From this point, I get a successful Webpack compilation and try to run my code:
module Console
open Fable.Core.JsInterop
let serialPort: obj = importDefault "serialport"
printfn "%A" serialPort
However, it prints undefined. I've tried a few other of the import functions as well as trying to use Emit and jsNative to generate a standard require call, but none of that works either, likely because Webpack has completely changed the meaning of require. I've even tried some shenanigans with the webpack.IgnorePlugin (https://webpack.js.org/plugins/ignore-plugin/) or trying to Emit a call like eval('require(\"serialport\")'); but no luck :^)
So what's the story on this? Ultimately, I need to get this up and running with Electron as well, but I figured testing it out in a Node.js console application would be easier to start. Surely it's possible to use Fable + Webpack + Native Node.js modules?
Hmm, I haven't worked with Node native modules, so I'm not sure. But if you're writing a Node app and have problems with webpack, you may want to try fable-splitter just using Babel commonjs plugin as in here (you can omit the fableCore part in that config).
I'll give that a shot, thanks. It'll be a bummer to not be able to use Webpack (won't be able to use HMR), but I think it's really a lack of documentation on Webpack's end at this point. I will probably play around with some other solutions and see what works and what doesn't
I made it work with this change:
externals: {
serialport: "commonjs serialport"
}
I followed this blog post: https://jlongster.com/Backend-Apps-with-Webpack--Part-I
Thanks that works great! I am still hopelessly confused as to _why_ it works, since it doesn't match any of the outlined techniques in their docs (https://webpack.js.org/configuration/externals/), but ¯\_(ツ)_/¯
As a more general solution, I am going to use webpack-node-externals (https://www.npmjs.com/package/webpack-node-externals) which does exactly this,
but for every package you have, forcing you to manually white-list the ones you want bundled rather than the other way around.
Most helpful comment
I made it work with this change:
I followed this blog post: https://jlongster.com/Backend-Apps-with-Webpack--Part-I