Closed KyleMit closed 4 years ago
"scripts": {
- "build": "set ELEVENTY_ENV=prod&& npx @11ty/eleventy",
- "serve": "set ELEVENTY_ENV=dev&& npx @11ty/eleventy --serve",
- "clean": "rm -rf _site"
+ "build": "npx cross-env ELEVENTY_ENV=prod npx @11ty/eleventy",
+ "serve": "npx cross-env ELEVENTY_ENV=dev npx @11ty/eleventy --serve",
+ "clean": "npx rimraf _site"
}
And if you don't want to use npx to invoke cross-env and rimraf, you can also install globally:
npm i rimraf -g
npm i cross-env -g
The fundamental challenge is that whatever string ends up in the script section will get sent directly to whatever shell you've selected. Which means for true cross compatibility, the only safe scripts require syntaxes and commands that perfectly overlap in both cmd and bash - which is a small venn diagram.
Here's an outline of some of the commands we'd like to run and several different implementations across environments to see if we can find something that works universally.
MY_NAME=Kyle echo Hi, \'$MY_NAME\'
MY_NAME=Kyle && echo Hi, \'$MY_NAME\'
set MY_NAME=Kyle&& echo Hi '%MY_NAME%'
Note 1: You cannot have any space* between the value being set and the &&
command continuation character
*Note 2: For this particular example, the variable expansion will occur immediately, so in order to set and use the variable in the same line you'll need to wrap in cmd
evaluation, pass in /V
for delayed environment variable expansion, and delimit with !
instead of $
. But it should be fine to consume the variable in a subsequent command.
Attach directly to process.env
process.env.MY_NAME = 'Kyle';
You can create a file named .env
MY_NAME=Kyle
And then load into the process with a library like dotEnv like this
require('dotenv').config()
You might also set some environment variables on your build server settings like in Netlify Environment Variables:
rm
with -f
force and -r
recursive
rm -rf _site
The windows equivalent is rd
remove directory with /s
to delete all subfolders and /q
to not prompt for y/n confirmation
rd /s /q _site
Use fs.rmdir
to remove entire directory as of Node v12.10.0
const fs = require('fs');
fs.rmdir("_site", { recursive: true });
RimRaf is a recursive delete package that has a CLI which can be brought in using npx
npx rimraf _site
How to run multiple bash commands - Docs on list of commands / control operators
Syntax | Description |
---|---|
A ; B |
Run A and then run B |
A && B |
Run A, if it succeeds then run B |
A \|\| B |
Run A, if it fails then run B |
echo hey && echo you
How to run two commands in one cmd line - Docs on redirection and conditional processing symbols
Syntax | Description |
---|---|
A & B |
Run A and then run B |
A && B |
Run A, if it succeeds then run B |
A \|\| B |
Run A, if it fails then run B |
echo hey && echo you
You can Run npm scripts sequentially or run npm scripts in parallel with a library called npm-run-all
If you had the following scripts in your package.json
:
"scripts": {
"lint": "eslint src",
"build": "babel src -o lib"
}
You could run them all like this:
$ npm-run-all -s lint build # sequentially
$ npm-run-all -p lint build # in parallel
One escape hatch seems to be to to invoke a script contained in a node.js file - because node is a prerequisite anyway and is cross-OS compatible. However, this also seems pretty heavy handed for a 1 line function to delete a directory. This is similar to the approach taken by Create React App with react-scripts, but they have much more complexity to handle.
Another challenge to this approach is when calling CLI / binary commands like eleventy --serve
. To do that we need to execute a command line binary with Node.js and also pipe the response to stdout
So it could work a little something like this:
File: Package.json
:
"scripts" : {
"build" : "node build.js"
}
File: Build.js
let { exec } = require('child_process');
let log = (err, stdout, stderr) => console.log(stdout)
exec("git config --global user.name", log);
If an OS agnostic node command wouldn't work, you can also use require("os")
to get "operating system-related utility methods and properties"
As outlined in package.json with OS specific script, there's a library called run-script-os that helps automagically toggle between named script versions
So if we had the following scripts:
"scripts": {
"build": "npx run-script-os",
"build:windows": "SET ELEVENTY_ENV=prod&& npx @11ty/eleventy",
"build:default": "export ELEVENTY_ENV=prod && npx @11ty/eleventy"
}
You could manually run a named instance:
$ npm run build:windows
Or you could let run-script-os decide for you:
$ npm run build
From How to set environment variables in a cross-platform way, there's a package by Kent C. Dodds called cross-env
that allows you to set environment variables across multiple platforms like this:
"scripts": {
"build": "npx cross-env ELEVENTY_ENV=prod npx @11ty/eleventy",
}
At the potential cost of additional dependencies (none of which are felt by the end client), this probably gives us a solution that is both robust & terse as long as a CLI exists for what you're trying to do
!Spoiler! There's probably an NPM package for it
Ideally, the commands would just work on either linux or windows (most work is done against windows, but netlify builds on a linux machine)
Here are some examples of scripts used in the
package.json
:Problems
References