Skip to content

Instantly share code, notes, and snippets.

@pvspain
Last active March 3, 2024 09:39
Show Gist options
  • Save pvspain/ef74705b023dc76385aeadd3533eea81 to your computer and use it in GitHub Desktop.
Save pvspain/ef74705b023dc76385aeadd3533eea81 to your computer and use it in GitHub Desktop.
build-monorepo
#! /usr/bin/env bash
# This script follows the construction of a monorepo using the facilities
# within npm. The reference, below, is a blog article, walking through
# the incremental process.
# https://daveiscoding.com/nodejs-typescript-monorepo-via-npm-workspaces
# This script deviates from the blog, where we use ES2022 and
# ECMAScript modules, and the package source code is located in the 'src'
# folder from the get-go.
# We create a git repository, named for $MONOREPO (defined below).
# Each step in the process is committed, so you can examine the changes
# introduced by each step as git diffs.
# The monorepo packages are all "scoped" within @${MONOREPO}.
# This is not essential for a monorepo, but does provide
# a unique namespace for package names within the monorepo.
# The package is initially a Javascript NodeJS project (v16+, ECMAScript modules),
# then later converted to Typescript (targetting ES2022).
## Report and commit all changes
function snapshot () {
git add .
git status
# Combine all arguments into one string: "$*"
git commit -m"$*"
}
# Set name for monorepo
export MONOREPO=container
# Reset terminal
clear
# Clean up any previous execution
printf "\n1. Clean up any previous execution...\n\n"
if [ -d ${MONOREPO} ]
then
rm -rf ${MONOREPO}
fi
# Create MONOREPO and git repository
printf "\n2. Create monorepo directory, '${MONOREPO}', and git repository...\n\n"
mkdir ${MONOREPO} &&
pushd ${MONOREPO} > /dev/null || exit
git init
# Initialise the MONOREPO npm package
printf "\n3. Initialise the monorepo npm package, '${MONOREPO}'...\n\n"
npm init -y &&
npm pkg set main="src/index.js" type=module || exit
snapshot "Init monorepo package: '${MONOREPO}'."
# Initialise the workspaces
printf "\n4. Initialise the package workspaces...\n\n"
npm init --scope @${MONOREPO} -w packages/core -y &&
npm init --scope @${MONOREPO} -w packages/web -y &&
npm init --scope @${MONOREPO} -w packages/cli -y || exit
# Signal we are using modules in Node package
# Change package entry-point location to 'src/index.js'
npm pkg set main="src/index.js" type=module --ws || exit
snapshot Initialise the package workspaces.
# Define package dependencies
printf "\n5. Define package dependencies...\n\n"
npm install @${MONOREPO}/core -w @${MONOREPO}/web &&
npm install @${MONOREPO}/core -w @${MONOREPO}/cli || exit
snapshot Define package dependencies.
# Add package testing files
printf "\n6. Add package testing files in 'src' folders...\n\n"
# Create 'src'folders
mkdir packages/core/src &&
mkdir packages/web/src &&
mkdir packages/cli/src || exit
cat << EOF > packages/core/src/index.js
console.log("Hello from Core!");
EOF
cat << EOF > packages/cli/src/index.js
import "@${MONOREPO}/core";
console.log("Hello from CLI!");
EOF
cat << EOF > packages/web/src/index.js
import "@${MONOREPO}/core";
console.log("Hello from Web!");
EOF
snapshot Add package testing files.
# Run tests
printf "\n7. Run tests...\n\n"
node packages/cli/src/index.js &&
node packages/web/src/index.js || exit
# Add "typescript" npm package
printf "\n8. Add 'typescript' npm package...\n\n"
npm install -D typescript || exit
# Don't track typescript package in git repo
printf "node_modules/typescript/\n" > .gitignore
snapshot Add typescript npm package.
# Add typescript config files
printf "\n9. Add typescript config files...\n\n"
cat << EOF > tsconfig.base.json
{
"compilerOptions": {
"incremental": true,
"target": "es2022",
"module": "node16",
"declaration": true,
"sourceMap": true,
"strict": true,
"moduleResolution": "node16",
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"composite": true
}
}
EOF
cat << EOF > packages/core/tsconfig.json
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"rootDir": "src",
"outDir": "dist"
}
}
EOF
cat << EOF > packages/cli/tsconfig.json
{
"extends": "../../tsconfig.base.json",
"references": [{ "path": "../core" }],
"compilerOptions": {
"rootDir": "src",
"outDir": "dist"
}
}
EOF
cp packages/cli/tsconfig.json packages/web/tsconfig.json
# Empty "files" array tells TypeScript to ignore all files except those in the references.
cat << EOF > tsconfig.json
{
"files": [],
"references": [
{ "path": "packages/core" },
{ "path": "packages/cli" },
{ "path": "packages/web" }
]
}
EOF
snapshot Add typescript config files.
# Convert JS files to TS
printf "\n10. Convert JS files to TS...\n\n"
mv packages/core/src/index.js packages/core/src/index.ts
mv packages/web/src/index.js packages/web/src/index.ts
mv packages/cli/src/index.js packages/cli/src/index.ts
snapshot Convert JS files to TS.
printf "\n11. Change package 'main' (index.js) location for tsc output...\n\n"
npm pkg set main="dist/index.js" &&
npm pkg set main="dist/index.js" --ws || exit
snapshot "Change package 'main' (index.js) location for tsc output."
printf "\n12. Building...\n\n"
npx tsc --build &&
printf "\n13. Run tests...\n\n"
node packages/cli/dist/index.js &&
node packages/web/dist/index.js || exit
popd > /dev/null
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment