Skip to content

Instantly share code, notes, and snippets.

@hallettj
Last active December 10, 2023 13:32
Show Gist options
  • Star 117 You must be signed in to star a gist
  • Fork 7 You must be signed in to fork a gist
  • Save hallettj/29b8e7815b264c88a0a0ee9dcddb6210 to your computer and use it in GitHub Desktop.
Save hallettj/29b8e7815b264c88a0a0ee9dcddb6210 to your computer and use it in GitHub Desktop.
Makefile for transpiling with Babel & Flow in a Node app, or in a client- or server-side shared library
# Makefile for transpiling with Babel in a Node app, or in a client- or
# server-side shared library.
.PHONY: all clean
# Install `babel-cli` in a project to get the transpiler.
babel := node_modules/.bin/babel
# Identify modules to be transpiled by recursively searching the `src/`
# directory.
src_files := $(shell find src/ -name '*.js')
# Building will involve copying every `.js` file from `src/` to a corresponding
# file in `lib/` with a `.js.flow` extension. Then we will run `babel` to
# transpile copied files, where the transpiled file will get a `.js` extension.
# This assignment computes the list of transpiled `.js` that we expect to end up;
# and we will work backward from there to figure out how to build them.
transpiled_files := $(patsubst src/%,lib/%,$(src_files))
# Putting each generated file in the same directory with its corresponding
# source file is important when working with Flow: during type-checking Flow
# will look in npm packages for `.js.flow` files to find type definitions. So
# putting `.js` and `.js.flow` files side-by-side is how you export type
# definitions from a shared library.
# Compute the list of type-definition source files that we want to end up with.
# This is done by replacing the `.js` extension from every value in the
# `transpiled_files` list with a `.js.flow` extension.
flow_files := $(patsubst %.js,%.js.flow,$(transpiled_files))
# Ask `make` to build all of the transpiled `.js` and `.js.flow` files that we
# want to end up with in `lib/`.
#
# This target also depends on the `node_modules/` directory, so that `make`
# automatically runs `yarn install` if `package.json` has changed.
all: node_modules $(flow_files) $(transpiled_files)
# This rule tells `make` how to transpile a source file using `babel`.
# Transpiled files will be written to `lib/`
lib/%: src/%
mkdir -p $(dir $@)
$(babel) $< --out-file $@ --source-maps
# Transpiling one file at a time makes incremental transpilation faster:
# `make` will only transpile source files that have changed since the last
# invocation.
# This rule tells `make` how to produce a `.js.flow` file. It is just a copy of
# a source file - the rule copies a file from `src/` to `lib/` and changes the
# extension.
lib/%.js.flow: src/%.js
mkdir -p $(dir $@)
cp $< $@
clean:
rm -rf lib
# This rule informs `make` that the `node_modules/` directory is out-of-date
# after changes to `package.json` or `yarn.lock`, and instructs `make` on how to
# install modules to get back up-to-date.
node_modules: package.json yarn.lock
yarn install
@whphhg
Copy link

whphhg commented Feb 28, 2018

Thank you for this thoroughly commented approach (and the blog post) to using make with babel. I haven't set-up make so far and this was a great insight into how it works. I've been using the approach below to auto-transform the files on every change and copy other non .js files to lib/ as well.

Here's the command: babel --copy-files src/ --out-dir lib/ --watch.

@shmup
Copy link

shmup commented Mar 1, 2018

@hallettj I can't determine why yarn install would be running every time with this setup:

.PHONY: all clean

babel := node_modules/.bin/babel
src_files := $(shell find src/ -name '*.js')
transpiled_files := $(patsubst src/%,lib/%,$(src_files))

all: node_modules $(transpiled_files)

lib/%: src/%
	mkdir -p $(dir $@)
	$(babel) $< --out-file $@ --source-maps

clean:
	rm -rf lib

node_modules: package.json yarn.lock
	yarn install

I'm certain that the package.json and yarn.lock haven't been modified. Well besides, I can run make over and over and it always runs yarn install, and to be sure, I check the two files in question and they remain with their old modified stamp.

EDIT: the fix was touching node_modules, ala:

node_modules: package.json yarn.lock
	yarn install
	touch node_modules

@ethagnawl
Copy link

ethagnawl commented Mar 2, 2018

That's pretty odd, @shmup Have you tried adding node_modules to the PHONY list (and removing touch node_modules)?

@shmup
Copy link

shmup commented Mar 3, 2018

@ethagnawl I ended up abandoning the idea entirely and let Yarn manage itself, since it dawned on me it isn't very useful to check your own modification dates. That's what Yarn does!

https://github.com/shmup/react-makefile/blob/master/Makefile

@gibatronic
Copy link

Since npm@5.2.0 you can use npx to call local binaries:

babel := 'npx babel'

@gibatronic
Copy link

gibatronic commented Mar 3, 2018

I've been also using Makefile for some time, the only downside is not being able to list available targets.

So for that I wrote the make-help project, all you have to do is to decorate the target with a comment and make-help will pick it up and list.

@tbartels
Copy link

tbartels commented Mar 4, 2018

@gibatronic depending on your shell you should be able to use tab completion, just type make <tab> to get a list of all make targets with partial autocomplete make cl<tab> becomes make clean, hunt down your shell completion script for more details, or a little googling should reveal how to use the complete builtin to generate autocomplete rules.

@simon-movio
Copy link

@gibatronic, I have been using the following post for help with listing available make targets
https://marmelab.com/blog/2016/02/29/auto-documented-makefile.html

Hope it is of some value.

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