This is a polyglot monorepo boilerplate for The Palmer Group. It is a starter monorepo comprised of TypeScript apps/packages, placeholder JVM and Python apps and an example deployment workflow.
The repository is powered by Lerna and Yarn. Lerna is responsible for bootstrapping, installing, symlinking all of the packages/apps together.
This repo includes multiple packages and applications for a hypothetical project called mono
. Here's a rundown of the folders:
mono-common
: Shared utilities (TypeScript)mono-ui
: Component library (TypeScript x Storybook) (depends onmono-common
)mono-cra
: Create React App x TypeScript (depends onmono-common
+mono-ui
)mono-razzle
: Razzle x TypeScript (depends onmono-common
+mono-ui
)mono-jvm
: Dropwizard x Kotlin.circle
: Some example CircleCI v2 workflows for various monorepo setups (including polyglot)
You should run a search and replace on the word mono
and replace with your project name. Rename folders from mono-xxx
as well. Lastly, -razzle
and -cra
are just placeholders. You should carefully replace them with their proper names as well like -web
,-admin
, -api
, or -whatever-makes-sense
.
Each package can be referenced within other packages/app files by importing from @<name>/<folder>
(kind of like an npm scoped package).
import * as React from 'react';
import './App.css';
import { Button } from '@mono/ui';
class App extends React.Component<any> {
render() {
return (
<div className="App">
<header className="App-header">
<h1 className="App-title">Welcome to React</h1>
</header>
<Button>Hello</Button>
<p className="App-intro">
To get started, edit <code>src/App.tsx</code> and save to reload.
</p>
</div>
);
}
}
export default App;
IMPORTANT: YOU DO NOT NEED TO CREATE/OWN THE NPM ORGANIZATION OF YOUR PROJECT NAME BECAUSE NOTHING IS EVER PUBLISHED TO NPM.
For more info, see the section on package versioning
Install lerna globally.
npm i -g lerna
git clone [email protected]:palmerhq/typescript-monorepo-starter.git
cd typescript-monorepo-starter
rm -rf .git
yarn install
Lerna allows some pretty nifty development stuff. Here's a quick rundown of stuff you can do:
yarn start
: Run's theyarn start
command in every project and pipe output to the console. This will do the following:- mono-cra: Starts the app in dev mode on port 3000
- mono-razzle: Starts the app in dev mode on port 8082
- mono-ui: Starts TypeScript watch task, and also launches react-storybook on port 6006
- mono-common: Starts TypeScript watch task
yarn test
: Run's theyarn test
command in every project and pipe output to the console. Because of how jest works, this cannot be run in watch mode...womp.womp.yarn build
: Build all projectslerna clean
: Clean up all node_moduleslerna bootstrap
: Rerun lerna's bootstrap command
**IF you run yarn add <module>
or npm install <module>
from inside a project folder, you will break your symlinks.** To manage package modules, please refer to the following instructions:
To add a new npm module to ALL packages, run
lerna add <module>
To add a new npm module(s) to just one package
lerna add <module> --scope=<package-name> <other yarn-flags>
# Examples (if your project name was `mono`)
lerna add classnames --scope=@mono/ui
lerna add @types/classnames @types/jest --scope=@mono/ui --dev
Unfortunately, there is no lerna remove
or lerna uninstall
command. Instead, you should remove the target module from the relevant package or packages' package.json
and then run lerna bootstrap
again.
Reference issue: lerna/lerna#1229 (comment)
None of the packages in this setup are ever published to NPM. Instead, each shared packages' (like mono-common
and mono-ui
) have build steps (which are run via yarn prepare
) and get built locally and then symlinked. This symlinking solves some problems often faced with monorepos:
- All projects/apps are always on the latest version of other packages in the monorepo
- You don't actually need to version things (unless you actually want to publish to NPM)
- You don't need to do special stuff in the CI or Cloud to work with private NPM packages
Somewhat confusingly, (and amazingly), the TypeScript setup for this thing for both mono-cra
and mono-razzle
directly reference source code (<name>/src/**
) of mono-ui
and mono-common
by messing with paths
in tsconfig.json
.
{
"extends": "./tsconfig.base.json",
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@mono/common/*": ["./mono/common/src"],
"@mono/ui/*": ["./mono/ui/src"]
}
}
}
Long story short, this means that you can open up this whole thing at the root directory and VSCode will understand what is going on.
You are welcome.
If you don't like the @mono/<folder>
you can change it to whatever you want to. For example, you may want to change it to mono-<folder>
so it exactly matches the folder names.
To do this, you need to (search and replace @mono/
with mono-
). Or in other words:
- Edit
./tsconfig.json
'spaths
- Edit each package's
name
inpackage.json
- Update references to related packages in
dependencies
in eachpackage.json
- Update references in code in each package.
Again, search and replace will work.
Important: If you do this, then your Lerna installation scoping will also change because it uses the package name.
# old
lerna add axios --scope=@mono/common
# new (changed to mono-common)
lerna add axios --scope=mono-common
Refer to internal Palmer Group documentation. Deployment workflow for the mono-jvm
folder is a stub.
Inside of the mono-python
folder is a "Hello World" Flask application and Docker setup.
Set up Python environment:
pipenv install
To create a virtual environment you just execute the $ pipenv shell
.
Run a development server:
FLASK_APP=helloworld flask run
Build the docker image:
docker build -t flask-pipenv-helloworld .
Run the Docker Container:
docker run -p 8000:8000 flask-pipenv-helloworld
You can find the container runtime details as shown below:
docker ps
# ...
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
80af0bce64c7 flask-pipenv-helloworld "gunicorn -b0.0.0.0:…" 1 second ago Up Less than a second 0.0.0.0:8000->8000/tcp silly_goldstine
Install the prerequisites:
pipenv install --dev
Runs tests:
pipenv run python -m pytest
A LOT of this has been shameless taken from Quramy/lerna-yarn-workspaces-example. So much so, in fact, you can read the original README.md in WORKSPACES.md