'Monorepo Dependency Chain Overreaching
My Question:
What am I doing wrong when building my TypeScript monorepo with Yarn?
Steps I Have Tried:
FAILED Installed the
foreachWorkspace package, and ranyarn workspaces foreach --topological-dev run buildin which each TypeScript package has abuildscript that istsc -b tsconfig.jsonFAILED - Wrote a PowerShell and Bash script for performing the action
yarn workspace <scope>/<package name> buildPASSED Built the application using my IDE (WebStorm) to confirm the code works as intended. To re-iterate, I can build it locally with my IDE, but both Bash and PowerShell scripts fail to build it as stated above
Background
NOTE: This is a private codebase that uses some private libraries and packages that I cannot share. I have trimmed the contents of the question to exclude anything that might be considered confidential, but know that some things are intentionally vague.
NOTE #2: I tried tagging as yarn-berry since that's what I'm using, but I lack the reputation to create it. So as a heads-up, this uses the new Yarn v3 CLI tool.
The inspiration for this monorepo was to consolidate multiple code projects under a single banner, and re-use code that was otherwise duplicated between them. When I first started the monorepo, I did what most newbies probably do, and wrote it in order with no worry to building in a CI/CD solution, or on a new machine. Well, I got a new laptop, and was thrust head-first into the situation that my monorepo no longer builds. I wrote a PowerShell script that builds it in the correct order, but I have now been introduced to a build error that didn't happen during the initial development.
This is the console print-out for adding a prebuild script that announces what package called, running using the yarn workspaces foreach --topological-dev run prebuild command. Unlike NPM life-cycle scripts, yarn doesn't call the pre* and post* scripts in the foreach loop, which is why there are no errors listed.
PS C:\~\my-monorepo> yarn workspaces foreach --topological-dev run prebuild
➤ YN0000: Building @myscope/errors
➤ YN0000: Building @myscope/extensions
➤ YN0000: Building @myscope/logger
➤ YN0000: Building @myscope/steps-selenium
➤ YN0000: Building @types/continuum__continuum-javascript-professional
➤ YN0000: Building @myscope/unittest
➤ YN0000: Building @myscope/utilities
➤ YN0000: Building @myscope/common
➤ YN0000: Building @myscope/continuum
➤ YN0000: Building @myscope/http
➤ YN0000: Building @myscope/cli
➤ YN0000: Building @myscope/pages
➤ YN0000: Building @myscope/parsers
➤ YN0000: Building @myscope/report
➤ YN0000: Building @myscope/setup
➤ YN0000: Building @myscope/steps-continuum
➤ YN0000: Building @myscope/steps
➤ YN0000: Done in 1s 896ms
Here's the PowerShell script:
Write-Host "Removing all prior build artifacts"
Remove-Item -Recurse -Path ./packages/*/dist
Write-Host "Install dependencies"
yarn install
Write-Host "Building custom errors"
yarn workspace @myscope/errors build
Write-Host "Building extensions library"
yarn workspace @myscope/extensions build
Write-Host "Building Unit Test libraries"
yarn workspace @myscope/unittest build
Write-Host "Building custom logger utility"
yarn workspace @myscope/logger build
Write-Host "Building common utilities module"
yarn workspace @myscope/utilities build
Write-Host "Building shared Common module"
yarn workspace @myscope/common build
Write-Host "Building Selenium Page-Object module"
yarn workspace @myscope/pages build
Write-Host "Building the reporting module"
yarn workspace @myscope/report build
Write-Host "Building Continuum Interfaces and Data Classes"
yarn workspace @myscope/continuum build
Write-Host "Building QA CLI"
yarn workspace @myscope/cli build
Write-Host "Building the Cucumber-World Setup module"
yarn workspace @myscope/setup build
Write-Host "Building shared Selenium Cucumber step definitions module"
yarn workspace @myscope/steps-selenium build
Write-Host "Building various parser libraries"
yarn workspace @myscope/parsers build
Write-Host "Building HTTP Client"
yarn workspace @myscope/http build
Write-Host "Building shared Continuum Accessibility testing Cucumber steps"
yarn workspace @myscope/steps-continuum build
Write-Host "Building composite Cucumber step definitions module"
yarn workspace @myscope/steps build
The common package is our first offender. It is a package that defines common interfaces shared across multiple packages, as well as the location of multiple shared resource files (SSL cert locations for instance). Common imports the types-continuum (@types/continuum__continuum-javascript-professional) package, since the 3rd-party library @continuum/continuum-javascript-professional is JavaScript only and no type definitions. Common does this because the shared config object for the main library includes optional values that correspond to a couple enums in the Continuum library. When attempting to build common I get the following error:
error TS2688: Cannot find type definition file for 'cli'.
The file is in the program because:
Entry point for implicit type library 'cli'
error TS2688: Cannot find type definition file for 'common'.
The file is in the program because:
Entry point for implicit type library 'common'
error TS2688: Cannot find type definition file for 'continuum'.
The file is in the program because:
Entry point for implicit type library 'continuum'
error TS2688: Cannot find type definition file for 'http'.
The file is in the program because:
Entry point for implicit type library 'http'
error TS2688: Cannot find type definition file for 'pages'.
The file is in the program because:
Entry point for implicit type library 'pages'
error TS2688: Cannot find type definition file for 'parsers'.
The file is in the program because:
Entry point for implicit type library 'parsers'
error TS2688: Cannot find type definition file for 'report'.
The file is in the program because:
Entry point for implicit type library 'report'
error TS2688: Cannot find type definition file for 'setup'.
The file is in the program because:
Entry point for implicit type library 'setup'
error TS2688: Cannot find type definition file for 'steps'.
The file is in the program because:
Entry point for implicit type library 'steps'
error TS2688: Cannot find type definition file for 'steps-continuum'.
The file is in the program because:
Entry point for implicit type library 'steps-continuum'
error TS2688: Cannot find type definition file for 'steps-selenium'.
The file is in the program because:
Entry point for implicit type library 'steps-selenium'
Found 11 errors.
What's interesting is, per a suggestion from a coworker, we added a types property to the package.json, and each subsequent TypeScript build results in fewer errors. The errors are reported for Common (11 errors), Continuum (8 errors), and Continuum-Steps (3 errors). Unsurprisingly, these all have a dependency on the Continuum types package. Here is what my project structure looks like:
├───.idea
│ ├───codeStyles
│ └───inspectionProfiles
├───.vscode
├───.yarn
│ ├───cache
│ ├───plugins
│ │ └───@yarnpkg
│ ├───releases
│ ├───sdks
│ └───unplugged
├───packages
│ ├───cli
│ │ └───src
│ │ ├───data
│ │ └───interfaces
│ ├───common
│ │ ├───dist
│ │ │ ├───src
│ │ │ │ └───interfaces
│ │ │ │ └───cucumber
│ │ │ └───tests
│ │ ├───src
│ │ │ └───interfaces
│ │ │ └───cucumber
│ │ └───tests
│ ├───continuum
│ │ └───src
│ │ └───interfaces
│ ├───errors
│ │ └───src
│ │ └───interfaces
│ ├───extensions
│ │ ├───src
│ │ │ └───interfaces
│ │ └───tests
│ ├───http
│ │ ├───src
│ │ │ └───interfaces
│ │ └───tests
│ ├───logger
│ │ ├───src
│ │ │ ├───interfaces
│ │ │ └───table
│ │ └───tests
│ ├───pages
│ │ └───src
│ │ ├───v1
│ │ └───v2
│ ├───parsers
│ │ ├───src
│ │ │ └───interfaces
│ │ ├───tests
│ │ └───test_resources
│ ├───report
│ │ └───src
│ │ └───interfaces
│ ├───setup
│ │ └───src
│ │ └───interfaces
│ ├───steps
│ ├───steps-continuum
│ │ └───src
│ ├───steps-selenium
│ │ └───src
│ ├───types-continuum
│ │ └───src
│ │ └───interfaces
│ ├───unittest
│ │ └───src
│ │ ├───interfaces
│ │ └───internals
│ └───utilities
│ ├───src
│ │ └───interfaces
│ └───tests
└───ssl
And lastly, this is what my tsconfig.json looks like (locally defined within each package, since extending a top-level tsconfig.json gave me errors in which packages didn't resolve properly).
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig.json to read more about this file */
/* Projects */
"incremental": true, /* Enable incremental compilation */
"composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
/* Language and Environment */
"target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
/* Modules */
"module": "commonjs", /* Specify what module code is generated. */
"moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
"baseUrl": "./", /* Specify the base directory to resolve non number of files TypeScript should add to a project. */
/* JavaScript Support */
/* Emit */
"declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
"declarationMap": true, /* Create sourcemaps for d.ts files. */
"sourceMap": true, /* Create source map files for emitted JavaScript files. */
"outDir": "./dist", /* Specify an output folder for all emitted files. */
/* Interop Constraints */
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
/* Type Checking */
"strict": true, /* Enable all strict type-checking options. */
"noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */
"noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */
"alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
"noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */
"noUnusedParameters": true, /* Raise an error when a function parameter isn't read */
/* Completeness */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}
There are a couple packages that use es2018 or es2020 as targets. While I don't remember why es2018 was required, I remember I needed es2020 for BigInt support in the logger module to implement the Console.prototype.time and Console.prototype.timeEnd methods using process.hrtime.bigint(). Most target es2016 as listed above
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
