Using TypeScript Project References to Solve Common Package Issues in a Monorepo
The article explains how a frontend team tackled monorepo challenges—such as ignored package.json, tsconfig, and phantom dependencies—by adopting TypeScript Project References and minimal Git‑hook compilation, providing step‑by‑step configuration and code examples for seamless common package integration.
The Dali Intelligent Frontend team shares their monorepo journey, starting from the first commit in December 2018 to a repository now containing 154 business packages, 11 shared packages, and 7 tooling packages, and uses this article to record the lessons learned.
In their monorepo, developers often import common packages directly, adding them to the build tool's include list and treating their source as part of the business code. This leads to several hidden problems: the common package's package.json entry is ignored, its tsconfig.json is overridden by the consuming package, and dependencies declared only in the common package become phantom dependencies, causing version uncertainty.
Two practical solutions are discussed. The first, an automatic compilation via a Git pull hook, compiles all common packages on each pull but requires developers to restart dev processes when both common and business packages change. The second, leveraging TypeScript Project References , allows each package to be compiled independently while respecting their own tsconfig files, and is supported by ts-loader from version 5.2.0 onward.
Implementation steps include:
Ensuring the common package has correct TypeScript configuration, possibly providing separate tsconfig.es.json and tsconfig.lib.json files.
Adding a proper package.json with fields like exports, main, module, and typings (see code example below).
Adjusting the consuming package's build configuration: removing unnecessary includes, enabling projectReferences in ts-loader, and clearing conflicting compilerOptions.
Declaring the common package as a dependency in the business package's package.json so the package manager resolves it correctly.
Adding the common package's tsconfig paths to the references array of the business package's tsconfig.json (see code example below).
// package.json of a common package
{
"name": "@monorepo_workspace/common-a",
"version": "1.0.0",
"description": "A common package",
"sideEffects": false,
"exports": {
".": {
"import": "./es/index.js",
"require": "./lib/index.js"
}
},
"main": "./lib/index.js",
"module": "./es/index.js",
"typings": "./es/index.d.ts"
} // ts-loader configuration snippet
tsLoader: (config) => {
config.projectReferences = true;
config.compilerOptions = undefined; // override framework defaults that may break projectReferences
} // tsconfig.json with projectReferences
{
"references": [
{ "path": "../../common/common-a/tsconfig.es.json" },
{ "path": "../../common/common-b/tsconfig.json" }
]
}After these configurations, running the development server triggers a one‑time TypeScript build of the referenced common packages before the main build, allowing the bundler to treat them as real packages. The team reports stable results and plans to further automate reference addition via a custom webpack plugin or enhanced ts‑loader features.
Future work includes addressing other monorepo pain points such as node deployment, yarn.lock hell, and resolution overload, which will be shared in upcoming posts.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
ByteDance Dali Intelligent Technology Team
Technical practice sharing from the ByteDance Dali Intelligent Technology Team
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
