Running nx target only if file doesn't exist - nrwl-nx

I have a project which has a build step, however, I need to make sure that the file firebase.config.json exists before running the build command.
With that, I have two NPM scripts:
// package.json
{
...,
"nx": {
"targets": {
"prepare": {
"outputs": ["firebase.config.json"]
},
"build": {
"outputs": ["dist"],
"dependsOn": [
{
"target": "prepare",
"projects": "self"
}
]
}
}
},
"scripts": {
"prepare": "firebase apps:sdkconfig web $FIREBASE_APP_ID_SHOP --json | jq .result.sdkConfig > firebase.config.json",
"build": "VITE_FIREBASE_CONFIG=$(cat ./firebase.config.json) vite build",
},
...
}
So with the above, every time I run nx build app it will first run prepare and build the firebase.config.json file.
However, every time I make a change to any of the source files inside my project, prepare re-runs even though the firebase.config.json is already present.
Is it possible for nx to only run a target if the file declared under outputs is not present?

If you are in a bash environment you can modify your prepare script to be the following (note the original command has been shortened with ellipses for readability).
// package.json
{
"scripts":{
"prepare": "CONFIG=firebase.config.json; [ -f \"$CONFIG\" ] || firebase apps:sdkconfig ... | jq ... > \"$CONFIG\""
}
}
The above prepare script will still run, but it should not spend any time reproducing the configuration file if it already exists.
CONFIG=firebase.config.json is just putting our file in a bash environment variable so we can use it in multiple places (helps prevent typos). [ -f "$CONFIG" ] will return true if $CONFIG holds a filename which corresponds to an existing file. If it returns true, it will short-circuit the || (OR) command.
If you want further verification of this technique, you can test this concept at the terminal with the command [ -f somefile.txt ] || echo "File does not exist". If somefile.txt does not exist, then the echo will run. If the file does exist, then the echo will not run.
A slightly-related side-note: while you clearly can do this all in the package.json configuration, if your nx workspace is going to grow to include other libraries or applications, I highly recommend splitting up all your workspace configuration into the default nx configuration files: nx.json, workspace.json, and the per-project project.json files for the sake of readability/maintainability.
Best of luck!

Related

Make Babel transpile a group of files independently without "Couldn't find intersection" error

I have a set of Javascript files in src directory. These files are completely independent from one another, i.e. nothing requires anything, they are not modules and intended to be used old-fashinally in different html files in <script> tags. I have set up babel using package.json (name, version, description etc omitted):
{
"scripts": {
"build": "babel src --out-dir ./ --source-maps"
},
"babel": {
"presets": [
"#babel/preset-env",
"babel-preset-minify"
],
"comments": false
},
"devDependencies": {
"#babel/cli": "^7.20.7",
"#babel/core": "^7.20.12",
"#babel/preset-env": "^7.20.2",
"babel-preset-minify": "^0.5.2"
}
}
When two certain js files are put in src directory, Babel fails to build, producing an error:
Error: D:\temp\babel\src\some_script.js: Couldn't find intersection
at NodePath.getDeepestCommonAncestorFrom (D:\temp\babel\node_modules\#babel\traverse\lib\path\ancestry.js:113:11)
at getSegmentedSubPaths (D:\temp\babel\node_modules\babel-plugin-minify-builtins\lib\index.js:244:14)
at BuiltInReplacer.replace (D:\temp\babel\node_modules\babel-plugin-minify-builtins\lib\index.js:92:31)
at PluginPass.exit (D:\temp\babel\node_modules\babel-plugin-minify-builtins\lib\index.js:205:27)
at newFn (D:\temp\babel\node_modules\#babel\traverse\lib\visitors.js:143:21)
at NodePath._call (D:\temp\babel\node_modules\#babel\traverse\lib\path\context.js:45:20)
at NodePath.call (D:\temp\babel\node_modules\#babel\traverse\lib\path\context.js:35:17)
at NodePath.visit (D:\temp\babel\node_modules\#babel\traverse\lib\path\context.js:88:8)
at TraversalContext.visitQueue (D:\temp\babel\node_modules\#babel\traverse\lib\context.js:86:16)
at TraversalContext.visitSingle (D:\temp\babel\node_modules\#babel\traverse\lib\context.js:65:19) {
code: 'BABEL_TRANSFORM_ERROR'
}
This error does not occur if I delete any one of those files and npm run build again, it also doesn't occur every time when there's more than one file inside the src directory, it only occurs for this particular pair of files. From which it can be assumed that the files are not transpiled independently and Babel somehow analyzes the content of the files, looking for "intersections" or whatever — a behavior that I never asked for. How to ask Babel to stop doing that, if possible?

Copy files by extension in VS Code tasks.json

I want to copy, recursively, all files from /src with the extension .json to my /out directory. I currently copy all files in my static folder (regardless of extension) like this, in tasks.json
{
"version": "2.0.0",
"tasks": [
{
"type": "shell",
"label": "copyStatic",
"command" : "cp",
"args": ["-f", "-r", "${workspaceFolder}/src/static", "${workspaceFolder}/out/"],
}
]
}
I tried using the /**/ notation I'd seen elsewhere like this
{
"version": "2.0.0",
"tasks": [
{
"type": "shell",
"label": "copyJson",
"command" : "cp",
"args": ["-f", "-r", "${workspaceFolder}/src/**/*.json", "${workspaceFolder}/out/"],
}
]
}
But it didn't work - got an error cp: /src/**/*.json: No such file or directory
Any ideas how to do this in tasks.json? I want to deep copy so include files like
/src/foo.json --> /out/foo.json
/src/folder/bar.json --> /out/folder/bar.json
Thanks
A gulp solution is quite easy:
const gulp = require("gulp");
function copyJSONFiles() {
return gulp.src('src/**/*.json') // get all the json files from 'src' directory
.pipe(gulp.dest('out')); // move them to the workspace/out directory
}
exports.default = copyJSONFiles;
// /src/foo.json --> /out/foo.json
// /src/folder/bar.json --> /out/folder/bar.json
This file, called gulpfile.js, goes into your workspace folder at the top level. It is triggered with just the gulp command in the terminal.
The out folder will be created and the folder structure under src will be preserved within it.
As I said in my comment on October 13
"command" : "find ./async -name '*.json' -exec cp --parents {} out/ ';'",
will preserve the folder structure of the src directory (here async) but unfortunately under out/async. That is the purpose of the --parents option.
However, not using the --parents options results in just a flat folder of json files which it doesn't seem you want.
There is probably a pure script version that will flatten that parent folder removing the src folder therein. But the gulp version is awfully easy.
Complex queries can be quite hard to achieve with cp. Fortunately,find searches recursively by default, and can be used in combination with an -exec cp to actually copy these files.
The following command does the trick:
"command" : "find src/ -name "*.json" -exec cp {} out/ \;"

file copy on save or upload in vs code

I work frequently providing custom java script solutions to SharePoint. The annoying part at the moment is that either I need to setup a watcher to check file changes and overwrite it in the directory where the files will be in SharePoint or I just move then manually. Is there anyway we can setup something with a config file, like it is with the watch tsc compiler.
All I want is, for example to run robocopy when I save a file.
Or even just overwrite the same file specified location, preferable not dependent on extensions.
Thank you all.
For those wondering. I was able to achieve it with the simplest way I can think of: by using fs.watch or node-watch package.
created a js file with the code below to watch file changes in the src dir and act depending on the event.
watch.js:
//if you are using fs:
//var fs = require('fs');
//
var watch = require('node-watch');
//fs.watch if using the node fs.
watch('./src/',{recursive: true}, async function(evt, name) {
if (evt == 'remove') {
// on delete runs rest call to delete the file
}
if (evt == 'update') {
// on create or modify runs rest to upload the file
}
}
with that you can either run the cmd "node watch.js"
or create a task to run on folderopen.
./.vs_code/tasks.json
{
"type": "npm",
"script": "watch",
"label": "wathever label you want",
"isBackground": true,
"runOptions": {
"runOn": "folderOpen"
}
}
package.json:
"scripts": {
"watch": "node watch.js"
}
For the task to run on folderopen you also need to allow it in the folder. you can search on the command pallete (ctrl+shift+p), "Tasks:Manage Automatic Tasks in Folder" to enable it.

Run flutter app on multiple connected devices/emulators simultaneously [duplicate]

This question already has answers here:
Flutter Hot Reload to multiple devices
(6 answers)
Closed 3 years ago.
How can I run my flutter app on multiple devices at the same time without having to go through the sequential procedure of: select a device -> run, select other device -> run, etc.?
Using:
Android Studio 3.2.1
Flutter 1.0.0
Dart 2.1.0
Run command in terminal:
flutter run -d all
or create a script (e.g. runall.sh in root):
#!/usr/bin/env bash
flutter run -d all
and go to "Run" -> "Edit Configurations". Press "+" in upper left corner -> select "Bash". Then set:
Name: runall
Script: [path to runall.sh script]
Interpreter Path: /bin/bash
Select "runall" instead of "main.dart" beside run icon. Performing run (also through shortcut) will now run app on all devices.
Drawback: You'll have to enter "r" followed by Enter in run terminal for hot reload. Icon and shortcut does not work. Hot reload is performed on all devices though.
Just a workaround for now. I'm pretty sure the flutter plugin will cover this soon.
There are many ways to do this as previously answered. If you use VS Code instead of Android Studio as your Flutter IDE, this is how you can use the VSC launch configuration and tasks to run concurrently from a single launch and have hot reload enabled for all devices.
If you have an issue with executing flutter run -d all this is an alternative solution will which let you specify the devices that should run.
Ensure that the devices you specify are available when running flutter devices.
Your current launch.json file may look something like this:
{
"version": "0.2.0",
"configurations": [
{
"name": "Flutter",
"type": "dart",
"request": "launch",
"flutterMode": "debug"
}
]
}
Setup
You will need to update this launch.json file and create tasks.json in the same .vscode folder that is in your application's root directory.
Paste only the below code into launch.json
{
"version": "0.2.0",
"configurations": [
{
"name": "Flutter-All",
"preLaunchTask": "Flutter-Launch-All",
"type": "dart",
},
{
"name": "Flutter-iOS",
"preLaunchTask": "Flutter-Launch-iOS",
"type": "dart",
},
{
"name": "Flutter-Android",
"preLaunchTask": "Flutter-Launch-Android",
"type": "dart",
},
{
"name": "Flutter-Web",
"preLaunchTask": "Flutter-Launch-Web",
"type": "dart",
}
],
}
Paste only the below code into tasks.json
{
"version": "2.0.0",
"tasks": [
{
"label": "Flutter-Launch-All",
"dependsOn": [
"Flutter-Launch-iOS",
"Flutter-Launch-Android",
"Flutter-Launch-Web"
]
},
{
"label": "Flutter-Launch-iOS",
"type": "shell",
"command": "flutter run -d 'iPhone 11' "
},
{
"label": "Flutter-Launch-Android",
"type": "shell",
"command": "flutter run -d 'AOSP on IA Emulator' "
},
{
"label": "Flutter-Launch-Web",
"type": "shell",
"command": "flutter run -d 'Chrome' "
}
]
}
Replace the device names accordingly ('iPhone 11', 'AOSP on IA Emulator', 'Chrome').
Firing up all devices
Press the F5 key.
And you're done.
If the F5 shortcut to Start Debugging does not work for you, navigate to Debug & Run on the side panel and select the Flutter-All Configuration you've just created and then Run.
You will then see the terminal window appear and will able to switch between the individual hot-reload sessions running (as Tasks in their own shell).
Some Background
We use 'Compound Tasks' by way of the dependsOn option on a Task and not 'Compounds' which are for Configurations.
As it is not possible to launch Configurations concurrently, only sequentially, we use tasks which can run concurrently.
Hence, the "Flutter-All" Configuration executes the tasks of the iOS, Android and Web Configurations.
If using Compounds, a Configuration will need to complete before the next runs which is not what we want.
With Tasks we can choose to execute them sequentially however by default they will execute concurrently when using the dependsOn option.
//Do not use this unless you want to use Configurations only by testing them sequentially and not tasks
"compounds": [
{
"name": "Flutter-All",
"configurations": ["Flutter-iOS", "Flutter-Android", "Flutter-Web"],
}
]
If you don't want to use the command line directly each time, you can do the following workaround:
Download bashsupport plugin (link)
Create new configuration of bash, leave the script field empty, and in the interperter options cell insert:flutter run -d all. It should look something like:
If step two didn't work, create a file call something like run_all.sh in your root project. and put the next lines there(assuming that bin/bash is the path to your bash):
#!/bin/bash
flutter run -d all
type in your terminal:chmod 755 run_all.sh.
Specify run_all.sh in your configuration and bin/bash as your interprter path.
Remove flutter run -d all from interperter options.
It should look something like:
You can always have an external tool watch the files for you and trigger a hot reload.
Flutter supports certain signals to trigger a hot reload natively
--pid-file Specify a file to write the process id to. You can send SIGUSR1 to trigger a hot reload and SIGUSR2 to trigger a hot restart.
Here is an fast example:
#!/usr/bin/env bash
set -euo pipefail
# Remove previous pid files
rm -f /tmp/flutter.pid
# Run in a loop a hot reload call in a subshell
(while true
do
# Wait for flutter to start before monitoring pid
while [[ ! -f /tmp/flutter.pid ]]; do sleep 1; done;
# Send hot reload signal when files change
find lib/ -name '*.dart' | entr -n -d -p kill -USR1 $(cat /tmp/flutter.pid)
done) &
# Run all devices under 1 pid
flutter run -d all --pid-file /tmp/flutter.pid
Idea from: https://medium.com/#kikap/how-to-automatically-hot-reload-flutter-when-dart-source-files-change-6e8fdb523004
For more details on entr: http://eradman.com/entrproject/entr.1.html

How do I define an extension for coffeeify with Budo dev server?

I'm trying to use coffeeify with budo so I do not have to add the extension to my require statements. I have tried passing these commands through budo's browserify options
budo src/app.coffee --live --serve bundle.js -- -t coffeeify --extension=".coffee"
budo src/app.coffee --live --serve bundle.js -- -t [coffeeify --extension=".coffee"]
I also tried inserting the browserify transform into my package.json
"browserify: {
"transform": ["coffeeify", {"extension": ".coffee"}]
}
Here is something that works for me (took me forever to figure it out, the hard part being getting watchify to work with coffeescript). Everything is in the package.yaml. Invoke npm start from your top folder and it will do the trick. npm puts all the locally installed node binaries in your PATH for you (they normally live under node_modules/.bin).
{
"name": "my-package",
"version": "1.0.0",
"private": true,
"scripts": {
"start": "(cd src; budo app.coffee:bundle.js --dir . --live --verbose -- --extension=.coffee | garnish)"
},
"browserify": {
"extension": [ ".coffee" ],
"transform": [ ["coffeeify"], ["brfs"] ]
},
"devDependencies": {
"brfs": "1.4.1",
"browserify": "11.1.0",
"budo": "^5.1.5",
"coffee-script": "latest",
"coffeeify": "^1.1.0",
"garnish": "^3.2.1",
"watchify": "3.4.0"
}
}
I have my source code under the src folder, and a file named app.coffee which includes (or require in node.js terms) my whole application. I have an index.html in my src folder which reference the bundle.js through from an html script tag.
The command to start budo is inside my package.json. It does cd into my src folder first.
The trick is to specify some configuration in the browserify block: the extension .coffee needs to be present, and a list of transforms as well. I tried to have everything on the command line but never got it to work
After npm start is invoked, since I pass the --live argument to budo everything works like magic and edit/saves to my documents do trigger a browser reload/refresh.
To deploy or release you'll probably need another target to minify with uglify.js. I still have a script that does that manually in 2 steps, the first step calls browserify and the second step calls uglify.js explicitely.
As a remark, recent version of budo do the piping into garnish for you I've heard.
Another tip is to look at what the React folks are doing to transform their .jsx files, as it is in theory extremely close to what the coffeescript folks need to do. There seems to be a huge momentum around React so hopefully React people will have figured those build problems first.