Skip to content

Instantly share code, notes, and snippets.

@felixSchl
Last active November 15, 2016 16:46
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save felixSchl/940a1a7aa10a7cf4240e72f448ea2376 to your computer and use it in GitHub Desktop.
Save felixSchl/940a1a7aa10a7cf4240e72f448ea2376 to your computer and use it in GitHub Desktop.
Typescript 2 -> ES6 -> Babel -> ES5 (+ watchable / NO gulpfile! - just unix pipes) https://asciinema.org/a/20h1t61ky43xvszkrcl8h24jo
#!/bin/bash
#
# Task wrapper. Runs various chores in a cross platform manner.
#
# Usage: run [-h] <command>
#
# Available commands are:
# build: Build the project
# <other>: Run any other command available in the NPM path
#
# Options:
# -h, --help Show this help and exit
################################################################################
# Automatic configuration
################################################################################
TSC_CMD=tsc
BABEL_CMD=babel
PATH=./node_modules/.bin:$PATH
IS_WINDOWS=false
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SCRIPT_DIR_LEN=${#SCRIPT_DIR}
# check if we're operating under windows (under mingw / git bash)
# on windows, the symlinked script files have an obnoxious .cmd file extension.
if [[ $(uname) == MINGW* ]]; then
IS_WINDOWS=true
TSC_CMD=tsc.cmd
BABEL_CMD=babel.cmd
fi
################################################################################
# Build scripts
################################################################################
# Typescript wrapper
#
# Typescript 2 ships with a flag to emit compiled files to stdout
# (`-listEmittedFiles`). This makes it possible to convert the emitted
# files from ES7+ to ES5 using, for example, the babel transpiler.
#
# The problem, however, is that these emitted files may be mangled with
# other output of the compiler, since it emits everything to the same fd.
# This script contains a `tsc` wrapper (`_tsc`) that addresses this issue
# by filtering out the lines that describe an emitted file and those that
# do not and re-emitting them to stdout and stderr respectively. Further,
# it emits "sets" of changed files, not just single files which is help-
# ful, for example, in the case of babel which seems to perform much better
# given multiple files in one go, rather than performing a cold start for
# each. It does so by interspering a null-byte after each set of files:
#
# file 1\nfile2\nfile3\n\0file4\nfile
#
# This script has been tested to run on git-bash under Windows as well as
# Bash 3 on OSX (default).
function _tsc {
local IFS=$'\n'
$TSC_CMD "$@" --listEmittedFiles | awk '
function flush() {
if (files != "") {
printf "%s%c", files, 0
files = ""
}
fflush()
}
BEGIN { files = "" }
/.*Compilation complete.*/ {
print $0 > "/dev/stderr"
flush()
next
}
/^TSFILE:.*\.js$/ {
if (files != "") {
files = files "\n"
}
files = files substr($0, 9)
}
{ print $0 > "/dev/stderr" }
END { flush() }
'
}
# Read filepaths on stdin and remove their common leading path to the root of
# the project, taking into account Windows pathing.
function mk_relpath {
while IFS=$'\n' read -r file; do
if $IS_WINDOWS; then
file=/$(echo "$file" | sed 's/\\/\//g' | sed 's/://')
fi
eval "echo .${file:$SCRIPT_DIR_LEN}"
done
}
# Build the project from start to finish. All typescript source files are
# compiled and the output is run through the babel compiler:
#
# Typescript -> ES6 -> Babel -> ES5
#
# Any adhoc options are passed to the typescript compiler only. Configure babel
# behavior using the .babelrc file. Likewise, prefer tsconfig.json to configure
# typescript and only use the adhoc options for e.g. watching (-w|--watch).
function build {
local IFS=$'\n'
local -r outdir=$SCRIPT_DIR
local relfiles
_tsc "$@" |\
while read -d '' -r files; do
relfiles=($(mk_relpath <<< "$files"))
echo >&2 "babel: compiling ${#relfiles[@]} files"
"$BABEL_CMD" >&2 --out-dir "$outdir" "${relfiles[@]}"
done
}
################################################################################
# Command line interface
################################################################################
# derive the help text from the first comment in a shell script
function derive_help {
awk '
f && !NF { exit }
/^#/ && NR>2 { f=1; sub("^# ?", "", $0); print $0 }
' < "$1"
}
# print the help text, derived from this file
function help {
derive_help "${BASH_SOURCE[0]}"
}
case "${1:-build}" in
-h|--help) help ;;
build) shift; build "$@" ;;
-*) echo >&2 "unknown option: $1"; exit 1 ;;
*) cmd=$1; shift; "$cmd" "$@"; ;;
esac
@felixSchl
Copy link
Author

felixSchl commented Nov 15, 2016

I am desperate not to rely on gulp anymore as it has cost me an incredible amount of time: gulp.watch falls over on every error in a pipeline, making it essentially useless to keep running to find compile errors (gulpjs/gulp#71) (and I had just as little luck with gulp 4). gulp.watch also has this annoying behavior that the project won't build initially, only after triggering a change (#1372). Also gulp takes half a decade to boot up, which is worse when relying on npm run scripts, since node has to do multiple cold starts. But the worst part is the resentful behavior of one of the gulp members, telling people essentially to get fucked. In summary, I have spent too much of my lifetime trying to work around theses issues. Then you wonder why. The script above presents a way to use typescript and babel w/o any JS intervention: simply pipe the output of typescript into babel using ordinary unix pipes (works under git-bash also).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment