We had some discussion in https://github.com/SAFE-Stack/SAFE-BookStore/issues/313 about the use of -- to pass arguments to the subprocess, which is understandably confusing for new users (npm run does it like this, but yarn run has removed the need of it). So I thought Fable 2 could be an opportunity to make some breaking changes in the CLI if they help improve the user experience. I'd like to discuss two things: passing arguments and Fable commands.
Right now, the way to tell apart Fable arguments and those that should be send to Webpack, etc, is to separate them with -- but it looks a bit funny. I can think of two other possibilities, please say which one you prefer or whether there's another alternative:
dotnet fable [FABLE_ARGS] [COMMAND] [OTHER_ARGS] as other dotnet CLI tools like "dotnet watch":dotnet fable --port free webpack --mode development
dotnet fable webpack --port free --mode development
The second option can be convenient but I'm afraid there may be corner cases that produce unexpected behaviour. Fable 1 supports three parameters: --port, --verbose and --timeout. Fable 2 removes --timeout and adds --fable-core. Of them, I guess --verbose is the one with more chances to collide (maybe also --port with webpack-dev-server and similar).
Fable 1 offers a variety of commands to start (or not) the subprocess, but I think most of them are not used:
start Start Fable daemon
npm-run Run Fable while an npm script is running
yarn-run Run Fable while a yarn script is running
node-run Run Fable while a node script is running
shell-run Run Fable while a shell script is running
webpack Start Fable daemon, invoke Webpack and shut it down
webpack-dev-server Run Fable while Webpack development server is running
Maybe we can reduce the number and make them simpler to make invoking the tool easier. We could try to mimic npm run and yarn run so for example typing dotnet fable build first looks for a script named "build" in package.json and if not found, tries to run node_modules/.bin/build.
BTW,
webpackis probably broken for Webpack 4 as we have to usewebpack-clinow.
If we do this, we'll likely have to change the start command (not sure if someone uses it, @jgrund?) to something more complex with less possibilities of colliding, like dotnet fable standalone-daemon.
The other consequence I don't like very much is that if somebody has a script like this in package.json:
"scripts": {
"start": "cd tools && dotnet fable webpack-dev-server"
...so we can start the dev server just by typing yarn start. In this case yarn first will look into the package.json, then call Fable, which will look into package.json again and finally invoke the script in node_modules/.bin/webpack-dev-server. It doesn't feel quite right to add unnecessary steps to the workflow willingly.
If you are thinking to do the opposite: make the JS process, like fable-loader, start the Fable daemon as a subprocess, I tried to do that at the beginning with Fable 1, but I had difficulties to send a close signal to Fable daemon because the
exitevent in Node only lets you perform synchronous action, and the API to communicate with a TCP server is only asynchronous.
I would keep the -- way to pass arguments to subprocess. I don't find it robust to filter arguments on the condition they are known or no by fable.
For example, both fable and webpack-dev-server know --port arguments.
The first alternative could be a solution indeed even if I prefer to explicitly write --
Also -- is the standard way to pass arguments to subprocess on Unix world. There is some info to found in this issue https://github.com/fsprojects/Argu/issues/106#issuecomment-370645255
I'd vote for either keeping -- or going with option 1.
In either case, I think this is something that can be solved with docs explaining how to invoke the cli / pass extra args.
If we do this, we'll likely have to change the start command (not sure if someone uses it, @jgrund?)
I use fable start to spin up the server in the background so my jest tests run quickly after initial compile.
I do so manually, so I'd have no issue calling some other name.
I am not very knowledgeable but FWIW option #2 seems brittle. Option #1 seems fine and so does the current "--" method. The downside to "--" is that it's not very intuitive or discoverable, but if it is standard in the UNIX world that helps some. (Now that I think about it this explains why I sometimes see "--" in git commands.)
Here's the Yarn change where they implemented this: https://github.com/yarnpkg/yarn/pull/4152 and lots of discussion, and perceived bugs linked to it.
I like the look of 1. now I understand -- I'm not against sticking with it. Currently what we have to write is:
fable webpack-dev-server --port free -- --mode development
To make that easier to visually parse it would be good to be able to write:
fable --port free webpack-dev-server -- --mode development
the other order is what needed fixing here https://github.com/SAFE-Stack/SAFE-BookStore/pull/312
I agree that -- but it looks a bit funny.
I like the first proposition.
dotnet fable [FABLE_ARGS] [COMMAND] [OTHER_ARGS] looks pretty clear to me.
About commands:
start is not the default one ?-run so keep : npm Run Fable while an npm script is running
yarn Run Fable while a yarn script is running
node Run Fable while a node script is running
shell Run Fable while a shell script is running
I just want this to be super explicit without no magic at all.
Hi there,
Some questions and feedback.
The flexibility of fable command make it not so easy to understand :
Do you launch using yarn start or using dotnet fable yarn-start
So sometime people are lost and then you need to look in package.json to understand.
Maybe having default convention can be good.
In debug fable use 'fable-dev' script from package.json
For production use 'fable-prod'
Just call dotnet fable or dotnet fable - p
Another question, any plan for new 2.1 global tooling?
I don't think global tooling is a good solution because you want to control the version of Fable you are using for the project.
IHMO the confusion between the commands mostly comes because the projects they use don't have a build system included in it. And call one of another really depends on how the project is build. For example, if you can yarn start then you need to have dotnet fable xxxx inside the start script.
If you call dotnet fable yarn-start then you don't need to call fable as the server is already running.
If we add a default behavior to fable then it will probably need to call npm instead of yarn because yarn isn't always installed on the user system.
You're for the global tooling but in same way the truth is that you use a global yarn :p
Both should be possible.
I love my build system to be Kiss. So I use a lot the dotnet CLI and docker.
Checking if yarn exists in path is not so hard, but I see what you mean.
I would prefer yarn to be local :)
And dotnet-fable receive more updates than yarn in general.
Speaking about Fable 2 here...
I do have yarn (and npm) globally installed. I guess it doesn't matter either if you have dotnet-fable installed in your local .fsproj or globally (I haven't tried it yet). Fable 2 will locate package.json in the same or a parent directory where dotnet fable is run and that will become the working directory.
I know there's been some confusion about the commands to run both Fable and the JS client (usually webpack + fable loader). I'm trying to fix that in Fable 2, so I'll explain here my current idea. Suggestions are appreciated :)
Calling a yarn/npm script from Fable should NOT be recommended now. This is because it's possible now to call a script in node_modules/.bin directly. So for example, for Webpack we can just do:
dotnet fable webpack-dev-sever #development
dotnet fable webpack-cli -- --mode production #production
However, it's tedious to type the full command every time (and move to the folder where the .fsproj is) so we usually have a build script (as @MangelMaxime says) or a shortcut to quickly call it from the CLI (as @evilz says). I personally like scripts in the package.json, because they're fast to start, easy to setup and cross-platform. However, I understand this is confusing for some users because the flow is like this:
yarn start (JS tooling)cd src && dotnet fable webpack-dev-server (dotnet SDK tooling)node_modules/.bin/webpack-dev-serverFor people new to JS tooling is difficult to understand the difference between: yarn start > dotnet fable webpack-dev-server and dotnet fable yarn-start > webpack-dev-server. So I'm not sure what's the best way to communicate the CLI commands for Fable 2. Maybe just show the basic commands (dotnet fable webpack-dev-server) and then let people use their preferred build tool? Or should we just standardize a build system (FAKE 5) in tutorials and templates?
I do like the fable 2 ability to call node_modules/.bin. 馃憤
I am in favor with including FAKE 5. :)
If we want to reduce the number of config file in the template build.proj, fake.sh, fake.cmd. We can install it globally and only include the build.fsx in it.
Then user can call fake build.fsx or something like that directly. Because FAKE5 general a build.fsx.lock then it's ok to have in global as the dependencies used inside the build.fsx are locked.
@alfonsogarciacaro
I love that fable can call npm module directly, it's look powerful and simple.
Don't you think fable cli should have a flag for production ?
dotnet fable webpack-dev-sever #development
dotnet fable webpack -p #production
About Fake, I know it's a great tool !!! BUT it's important to not be dependant of this.
Also not being dependant of paket could be nice.
I whould not said that without dotnet cli... I'm a F# tooling rebel :p
Let see an actual dockerfile (can be better) :
# BUID
FROM evilz/node-mono-dotnet as build-env
WORKDIR /app
# copy csproj and restore as distinct layers
COPY *.sln ./
COPY paket* ./
COPY .paket/paket.exe ./.paket/
COPY .paket/Paket.Restore.targets ./.paket/
COPY .paket/paket.targets ./.paket/
COPY yarn* ./
COPY package.json ./
COPY src/*.fsproj src/
RUN yarn install
# copy everything else and build app
COPY ./ ./
WORKDIR /app/src
RUN dotnet restore
CMD ["sh"]
# RUN dotnet fable yarn-build
# # production environment
# FROM nginx:1.13.8-alpine
# COPY --from=build-env ./public /usr/share/nginx/html
# EXPOSE 80
# CMD ["nginx", "-g", "daemon off;"]
To many copies, and confusing.
Don't you think fable cli should have a flag for production ?
I'd rather not, because this means Fable needs to make assumptions on the JS tooling. Fable 1 does it, but Fable 2 doesn't know anything about webpack, it will just check a script with that name in the node_modules/.bin folder. We're doing something for development, but it's in the config file. Maybe we can add an additional check: if the mode is not set, and webpack-dev-server is not active, we assume production mode by default.
Also not being dependant of paket could be nice.
Fable 2 doesn't depend on Paket ;)
Closing as Fable 2 has already been released. Also from Fable 2.1 on the cli won't be main way anymore to interact with Fable but the JS clients like fable-loader or fable-splitter.
Most helpful comment
I would keep the
--way to pass arguments to subprocess. I don't find it robust to filter arguments on the condition they are known or no by fable.For example, both fable and webpack-dev-server know
--portarguments.The first alternative could be a solution indeed even if I prefer to explicitly write
--Also
--is the standard way to pass arguments to subprocess on Unix world. There is some info to found in this issue https://github.com/fsprojects/Argu/issues/106#issuecomment-370645255