A monorepo starter kit for Typescript code.
-
NPM Workspaces to managing multiple packages in a monorepo
-
Turborepo a build system with caching and monorepo support
-
TypeScript for static type checking
-
Prettier for code formatting
-
Jest a test framework
-
a sample of the Next.js implementation
-
Commitizen + Commitlint working together with Git Hooks via Husky
-
lint-staged with Git Hooks via Husky
-
TODO: add a sample of the Webpack Module Federation implementation over Next.js
Packages must be one of the types: sites
, services
and packages
.
A site package is a client-side application. Typically, this generates, at build or runtime (SSR), at least one HTML and/or static assets entrypoint.
A service package is a server-side application. Typically, exposes an API.
Any other is a general purpose package. Can be a library, configuration presets, helpers packages, utils packages, dev packages, etc.
npm init --yes --scope='@acme' --workspace='<packages | services>/<package-dirname>'
- TODO: make a npm init template of package and service
Any package is completely isolated. To integrate a package with this repository, follow the steps below:
Make sure the package has the package.json
file configured correctly.
Given the
specifities of how Node.js handles module resolution
all packages inside workspaces can be used as devDependencies
without
any configuration. To packages that must be included in the bundle, we
recommended that you install these packages in the main bundle package. See an
example that uses @acme/logger
as dependency in
./services/api-service/package.json
Configure the jest
to preset @acme/jest-preset/<some-preset-file>
:
{
"jest": {
"preset": "@acme/jest-preset/node"
}
}
Configure the common scripts
to run with Turborepo:
{
"scripts": {
"build": "...",
"dev": "...",
"lint": "npm run lint:code && npm run lint:styles",
"lint:code": "TIMING=1 eslint 'src/**/*.{js,jsx,ts,tsx}'",
"lint:styles": "TIMING=1 stylelint 'src/**/*.{html,css,scss}'",
"test": "jest"
}
}
⚠️ Watch out for linters. Ifstylelint
is not needed, do not install or configure it.
See an example without stylelint in ./packages/logger/package.json
See an example with stylelint in ./packages/ui/package.json
Make sure the package configures this file extending the @acme/tsconfig
.
Sites, services and packages may depend on another package. For Typescript
to correctly interpret these files, the compilerOptions.composite
and
references
properties must be defined.
In the package to be imported, make sure that compilerOptions.outDir
,
compilerOptions.rootDir
, compilerOptions.composite
and includes
are
defined in tsconfig.json
:
{
"extends": "@acme/tsconfig/<some-config-file>",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src",
"composite": true
},
"includes": [
"src"
]
}
⚠️ Packages that use tsup to build do not work according tocomposite
option. Removecomposite
in this case and use@acme/lint-staged-config/with-tsup
to configure.lintstagedrc.js
. See this tsup issue.
See an example with composite option in ./packages/logger/tsconfig.json
See an example without composite option in ./packages/ui/tsconfig.json
See an example of the lint-staged configuration in ./packages/ui/.lintstagedrc.js
In the package that will import another package, make sure that
compilerOptions.composite
and references
are defined in tsconfig.json
:
{
"extends": "@acme/tsconfig/<some-config-file>",
"compilerOptions": {
"composite": true
},
"references": [
{
"path": "<relative-path-to-another-package>"
}
]
}
See an example in ./services/api-service/tsconfig.json
⚠️ For correct type checking a build of imported package must be generated, or a declaration type of module should be defined. See an example of the declaration type in ./services/api-service/src/modules.d.ts
Make sure the package configures this file extending the
@acme/lint-staged-config
.
See an example without stylelint in ./packages/logger/.lintstagedrc.js
See an example with stylelint (and tsup) in ./packages/ui/.lintstagedrc.js
Make sure the package configures this file extending the @acme/eslint-config
.
The .eslintrc.js
file must be marked with the root
property.
See an example in ./packages/ui/.eslintrc.js
If needed, make sure the package configures this file extending the
@acme/stylelint-config
.
The .stylelintrc.js
file must be marked with the root
property.
See an example in ./packages/ui/.stylelintrc.js
If needed, make sure the package has this file configured correctly.
See an example in ./packages/ui/.stylelintignore
To run a site in DEV mode without run your dependencies in DEV mode:
npm run dev -- --filter='<site-package-name>'
To run a site in DEV mode with all your dependencies in DEV mode too:
npm run dev -- --filter='<site-package-name>...'
⚠️ Pay attention to run two or more sites with all dependencies when the dependencies is shared. If you run two or more sites in separated terminal, you will get two or more compilers of the same shared dependencies running at same time. Utilize the--filter
instead run in multiple terminals.
Example:
npm run dev -- --filter='@acme/home-site...' --filter='@acme/root-site...'
To use more filter syntaxes, see Turborepo Filtering Workspaces .