Define and maintain complex project configuration through code.
JOIN THE #templatesareevil MOVEMENT!
projen synthesizes project configuration files such as package.json
,
tsconfig.json
, .gitignore
, GitHub workflows, eslint, jest, etc from a
well-typed definition written in JavaScript.
Contrary to templating/scaffolding approaches, projen is not a one-off
generator. Synthesized configuration is not expected to ever be manually edited
(in fact, projen enforces that). The source of truth is always .projenrc.js
.
To create a new project, run the following command and follow the instructions:
$ npx projen new
Projen is all about project types. Project types are represented as well-typed TypeScript classes. The project
definition file (.projenrc.js
) is a simple JavaScript program which instantiates of one of the project type classes
and calls proj.synth()
on it.
Supported project types (create with npx projen new TYPE
):
Some examples for features built-in to these project types:
- Fully synthesize
package.json
- Standard npm scripts like
compile
,build
,test
,package
- eslint
- Jest
- jsii: compile, package, api compatibility checks, API.md
- Bump & release scripts with CHANGELOG generation based on Conventional Commits
- Automated PR builds
- Automated releases to npm, maven, NuGet and PyPI
- Mergify configuration
- LICENSE file generation
- gitignore + npmigonre management
- Node "engines" support with coupling to CI build environment and @types/node
- Anti-tamper: CI builds will fail if a synthesized file is modified manually
To give you a sense of how it works, let's walk through a simple example.
Create a new local git directory:
$ mkdir my-project && cd my-project
$ git init
Initialize a typescript project:
$ npx projen new jsii \
--name my-module \
--author-name "Elad Ben-Israel" \
--author-email "elad.benisrael@gmail.com" \
--repository "https://github.com/eladb/my-module.git"
If you run
npx projen new
you will get a list of supported projects. Usenpx projen new TYPE --help
to explore options supported by each project type.
This will generate a new .projenrc.js
file with the following contents:
const { JsiiProject } = require('projen');
const project = new JsiiProject({
"name": "my-module",
"authorName": "Elad Ben-Israel",
"authorEmail": "elad.benisrael@gmail.com",
"repository": "https://github.com/eladb/my-module.git"
});
project.synth();
Now run:
npx projen
From now on, we will refer to this command as
pj
. Every time you modify.projenrc.js
, just runpj
.Put this in your shell profile:
alias pj='npx projen'
OK, what just happened? If you examine your directory, you will notice that
projen generated a bunch of read only project configuration files like
package.json
, tsconfig.json
, .eslintrc
, etc.
Should I commit these generated files? Yes, you should commit those files. Although most files don't have to be committed, there is value in being able to review any changes to these files when you upgrade projen itself (
yarn projen:upgrade
).Since projen installs an "anti-tamper" check in your CI builds, if these files are manually modified, your CI build will fail.
It also generated sample code in src/
and test/
so your typescript project
is ready to be built using yarn build
:
$ yarn build
yarn run v1.22.4
$ yarn test && yarn compile && yarn run package
$ yarn eslint && rm -fr lib/ && jest --passWithNoTests
$ eslint . --ext .ts
PASS test/hello.test.ts
✓ hello (1 ms)
----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
index.ts | 100 | 100 | 100 | 100 |
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 2.731 s
Ran all test suites.
$ tsc
$ rm -fr dist && mkdir -p dist/js && yarn pack && mv *.tgz dist/js/
✨ Done in 13.68s.
This will run tests via jest
, compile through tsc
, lint through eslint
and
prepare an npm distribution tarball dist/js
.
This command synthesized a jsii project for you with the following features:
yarn compile
andyarn watch
: compile/watch jsii to jsyarn eslint
: run eslintyarn test
run eslint and jest tests with coverageyarn run package
:jsii-pacmak
yarn build
:compile
+test
+package
- Automatically generates
API.md
with reference docs after compilation (alsoyarn docgen
). yarn bump
bumps the module version and creates a CHANGELOG entry.yarn release
will bump and push with tags tomaster
.yarn projen
runsnpx projen
(good for updating dependencies)- PR builds: a GitHub workflow which auto-builds PR branches
- Releases: a GitHub workflow which builds all commits to
master
and releases new versions to all package managers (default is only npm) .gitignore
and.npmignore
are set up to commit only files that must exist in the repo (for example, by defaultpackage.json
is ignored)..mergify.yml
will auto-merge approved PRs that pass PR buildsLICENSE
defaults toApache-2.0
- CI builds also include an "anti tamper" check to verify no files where changed
during build. This ensures that generated-committed files (such as
API.md
and the GitHub workflows) have not been changed out-of-band up-to-date.
The
JsiiProject
class has a rich API that can be used to configure these features (submit a PR
if you are missing a degree of freedom).
Now, let's add a python target. Edit .projenrc.js
and add a python
section:
const { JsiiProject } = require('projen');
const project = new JsiiProject({
"name": "my-module",
"authorName": "Elad Ben-Israel",
"authorEmail": "elad.benisrael@gmail.com",
"repository": "https://github.com/eladb/my-module.git",
python: {
distName: 'my-python-dist-name',
module: 'my_python_module'
}
});
project.synth()
And re-run:
$ pj
And this will be added:
- The
jsii
section in yourpackage.json
file will now have apython
entry. - The
release.yml
GitHub workflow will include a release job that will release your module to PyPI.
See API Reference for API details.
src/
-.ts
files, after compilation they will go underlib/
.test/
-.ts
files for jest tests. Those will not be included in your npm module.bin/
- CLI executables (not .ts files!). See details below on how to define CLIs.
jest is used for unit tests. Write your unit test files under test/
so they won't be
included in the npm module.
IMPORTANT: your test code should reference your library code through src
and
not through lib
. To enforce that we will delete lib/
before running your
tests.
The recommended workflow for TDD is to:
- Checkout the repo
- Run
pj
- Run
yarn test:watch
That's it. You don't need to compile in order to run your tests because
ts-jest
takes care of compiling your code for you and test:watch
will
continuously watch your code for changes.
You should create executable scripts under bin/
, but do not include
typescript files there. Those must be under src/
or otherwise they won't be
compiled and included in your output module.
Let's walk through a simple example. Say my CLI should be called mycli
:
-
Create a file
lib/mycli.ts
with the actual code of the CLI. No need to export this file from yourindex.ts
file. -
Create a file
bin/mycli
with the following content:#!/usr/bin/env node require('../lib/mycli.js');
That's it. projen will auto-detect bin/mycli
and will add it to your
package.json
under the bin
section. You can disable this behavior by setting
autoDetectBin: false
.
projen takes a "batteries included" approach and aims to offer dozens of different project types out of
the box (we are just getting started). Think projen new react
, projen new angular
, projen new java-maven
,
projen new awscdk-typescript
, projen new cdk8s-python
(nothing in projen is tight to javascript or npm!)...
Adding new project types is as simple as submitting a pull request to this repo and exporting a class that
extends projen.Project
(or one of it's derivatives). Projen automatically discovers project types so your
type will immediately be available in projen new
.
Contributions of all kinds are welcome!
To check out a development environment:
$ git clone git@github.com:eladb/projen
$ cd projen
$ yarn boostrap # special boostrapping because projen uses itself
Distributed under the Apache-2.0 license.