javascript-build
javascript-build is a lightweight GNU Make-based build system for best-practice JavaScript package development.
- Uses the official Docker Node image (configurable)
- Run unit tests with node --test
- Code coverage using c8
- 100% code coverage enforced (configurable)
- Static code analysis using eslint
- Package documentation using jsdoc
- Publish the package to npm
- Publish application or documentation to GitHub Pages
Contents
- Project Setup
- Make Targets
- Make Options
- Make Variables
- Extending javascript-build
- Make Tips and Tricks
Project Setup
The basic structure of a javascript-build project is as follows:
.
|-- .gitignore
|-- Makefile
|-- README.md
|-- lib
| `-- packageName.js
`-- test
`-- testPackageName.js
The basic javascript-build "Makefile" is as follows:
# Download javascript-build
define WGET
ifeq '$$(wildcard $(notdir $(1)))' ''
$$(info Downloading $(notdir $(1)))
_WGET := $$(shell $(call WGET_CMD, $(1)))
endif
endef
WGET_CMD = if which wget; then wget -q -c $(1); else curl -f -Os $(1); fi
$(eval $(call WGET, https://raw.githubusercontent.com/craigahobbs/javascript-build/main/Makefile.base))
$(eval $(call WGET, https://raw.githubusercontent.com/craigahobbs/javascript-build/main/jsdoc.json))
$(eval $(call WGET, https://raw.githubusercontent.com/craigahobbs/javascript-build/main/.eslintrc.cjs))
# Include javascript-build
include Makefile.base
clean:
rm -rf Makefile.base jsdoc.json .eslintrc.cjs
Note that the makefile automatically downloads "Makefile.base", "jsdoc.json", and ".eslintrc.cjs" from javascript-build. It continually updates its development dependencies to the latest stable versions.
Here is a typical javascript-build project ".gitignore" file:
/build/
/node_modules/
/.eslintrc.cjs
/Makefile.base
/jsdoc.json
/package-lock.json
Notice that "Makefile.base", ".eslintrc.cjs", "jsdoc.json", and are ignored because they are downloaded by the Makefile.
Make Targets
javascript-build exposes build commands as "phony" make targets. For example, to run all pre-commit
targets, use the commit
target:
make commit
The following targets are available:
commit
Execute the test, lint, doc, and cover targets. This target should be run prior to any commit.
test
Run the unit tests in the "test" directory.
To run a single unit test, use the "TEST" make variable:
make test TEST='My Test'
lint
Run eslint on JavaScript source files under the "lib" directory.
doc
Run jsdoc on JavaScript source files under the "lib" directory. The HTML documentation index is located at "build/doc/index.html".
cover
Run unit tests with coverage. By default, "make cover" fails if coverage is less than 100%. The HTML coverage report index is located at "build/coverage/index.html".
The "TEST" make variable is supported as described in the test target above.
clean
Delete all development artifacts.
superclean
Delete all development artifacts and downloaded docker images.
changelog
Create and update the project's changelog file.
publish
Publish the package to npm.
gh-pages
Publish the application or project documentation to GitHub Pages. It first executes the clean
and
commit
targets to produce a clean build.
The repository is then git-cloned (or pulled) to the "../<repository-name>.gh-pages" directory, the "gh-pages" branch is checked-out, and the directories and files defined by the "GHPAGES_SRC" make variable are rsync-ed there. Afterward, review the changes, commit, and push to publish.
To create a "gh-pages" branch, enter the following shell commands:
git checkout --orphan gh-pages
git reset --hard
git commit --allow-empty -m "initializing gh-pages branch"
git push origin gh-pages
Make Options
To view the commands of any make target without executing, use the "-n" make argument:
make -n test
To run targets in parallel, use the "-j" make argument. This can significantly decrease the time of the commit target.
make -j commit
Make Variables
javascript-build exposes several make variables that can be modified in your makefile following the base makefile include. For example, to change the Node image:
include Makefile.base
NODE_IMAGE := node:15
The following variables are supported:
-
NODE_IMAGE
- The node docker image. -
NODE_TEST_ARGS
- Thenode --test
command line arguments. Default is "--test-reporter spec test/". -
C8_VERSION
- The c8 package version. -
C8_ARGS
- The c8 tool's command line arguments. Default is "--100 --all --allowExternal --src lib/ --src test/". -
ESLINT_VERSION
- The eslint package version. -
ESLINT_ARGS
- The eslint tool's command line arguments. Default is "lib/ test/". -
JSDOC_VERSION
- The jsdoc package version. -
JSDOC_ARGS
- The jsdoc tool's command line arguments. Default is "-c jsdoc.json -r README.md lib/". -
JSDOM_VERSION
- The jsdom package version. -
USE_JSDOM
- If set, jsdom is added as a development dependency.
Pre-Include Make Variables
The following make variables must be defined prior to the inclusion of the base makefile. This is because they modify the make targets that javascript-build generates on include. For example, to override the gh-pages source directories and files:
GHPAGES_SRC := lib/my-static-application/
include Makefile.base
GHPAGES_SRC
- The gh-pages target's source directories and files. Directories must end with a slash ("/"). Default is "build/doc/".
Other Make Variables
NO_DOCKER
- Use the system node instead of docker. This is intended to be used from the command line:
make commit NO_DOCKER=1
Extending javascript-build
All of the javascript-build targets may be extended either by adding additional commands or adding a target dependency. Add additional commands to execute when a target (and all its dependencies) is complete:
commit:
@echo 'Build succeeded!'
Add a target dependency when you want the new dependency to execute in parallel (for parallel builds):
.PHONY: other-stuff
other-stuff:
# do stuff...
commit: other-stuff
Make Tips and Tricks
Embed JavaScript in a Makefile
JavaScript can be embedded in a makefile by first defining the JavaScript script, exporting the script, and executing the script with Node's "-e" argument. Make variables can even be incorporated into the JavaScript script. Here's an example:
TITLE := Hello, World!
COUNT := 3
define JAVASCRIPT_SCRIPT
console.log('$(TITLE)');
for (let x = 1; x < $(COUNT) + 1; x++) {
console.log(`x = $${x}`);
}
endef
export JAVASCRIPT_SCRIPT
.PHONY: javascript-script
javascript-script:
node --input-type=module -e "$$JAVASCRIPT_SCRIPT"
Running make yields the following output:
Hello, World!
x = 1
x = 2
x = 3