Search code examples
yarnpkgmonorepoyarn-workspaces

Yarn Workspaces Not Building Local Dependency


I deleted my old question, to provide more info. I'm using Yarn workspaces (v1.22) on a monorepo with both an Angular app and library. I have the library declared as a dependency of the app.

root
   |
   ---projects
         |
         ---lib (components)
         |
         ---app

Each project has its associated build scripts. If I run yarn on a fresh checkout at the root or yarn workspace app install, it installs everything and links to the lib's project folder, but it doesn't perform the build, which is necessary, since my tsconfig paths includes the output of the build dist/lib. The last step in the install says "Building Fresh Packages", but it doesn't trigger the build for the lib for some reason.

tsconfig.json (base)

{
  "compileOnSave": false,
  "compilerOptions": {
    "baseUrl": "./",
    "outDir": "./dist/out-tsc",
    "forceConsistentCasingInFileNames": true,
    "noFallthroughCasesInSwitch": true,
    "sourceMap": true,
    "declaration": false,
    "downlevelIteration": true,
    "experimentalDecorators": true,
    "moduleResolution": "node",
    "importHelpers": true,
    "target": "es2015",
    "module": "es2020",
    "lib": [
      "es2018",
      "dom"
    ],
    "paths": {
      "@fabric/components/*": [
        "./dist/components/*"
      ]
    }
  },
  "angularCompilerOptions": {
    "strictInjectionParameters": true,
    "strictInputAccessModifiers": true,
    "strictTemplates": true
  }
}

root package

{
  "name": "fabric",
  "private": true,
  "license": "UNLICENSED",
  "workspaces": [
    "projects/*"
  ],
  "engines": {
    "node": ">=12.0.0 < 16.0.0",
    "yarn": ">= 1.0.0",
    "npm": "Please use Yarn instead of NPM to install dependencies. See: https://yarnpkg.com/lang/en/docs/install/"
  },
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "components": "yarn workspace @fabric/components",
    "guide-app": "yarn workspace @fabric/guide-app",
    "test": "yarn guide-app test && yarn components test",
    "postinstall": "ngcc"
  },
  "dependencies": {
    "@angular/animations": "12.0.2",
    "@angular/cdk": "12.0.2",
    "@angular/common": "12.0.2",
    "@angular/compiler": "12.0.2",
    "@angular/core": "12.0.2",
    "@angular/flex-layout": "12.0.0-beta.34",
    "@angular/forms": "12.0.2",
    "@angular/localize": "12.0.2",
    "@angular/material": "12.0.2",
    "@angular/platform-browser": "12.0.2",
    "@angular/platform-browser-dynamic": "12.0.2",
    "@angular/router": "12.0.2",
    "rxjs": "6.6.7",
    "tslib": "2.2.0",
    "zone.js": "0.11.4"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "12.0.2",
    "@angular-devkit/core": "12.0.2",
    "@angular-devkit/schematics": "12.0.2",
    "@angular/cli": "12.0.2",
    "@angular/compiler-cli": "12.0.2",
    "@angular/language-service": "12.0.2",
    "@schematics/angular": "12.0.2",
    "@types/jest": "26.0.23",
    "@types/node": "14.14.37",
    "codelyzer": "6.0.2",
    "jest": "26.6.3",
    "jest-junit": "12.1.0",
    "jest-preset-angular": "8.4.0",
    "jest-transform-stub": "2.0.0",
    "ng-packagr": "12.0.2",
    "protractor": "7.0.0",
    "tslint": "6.1.3",
    "typescript": "4.2.4"
  }
}

lib package

{
  "name": "@fabric/components",
  "version": "5.0.2",
  "license": "UNLICENSED",
  "engines": {
    "node": ">=12.0.0 < 16.0.0",
    "yarn": ">= 1.0.0",
    "npm": "Please use Yarn instead of NPM to install dependencies. See: https://yarnpkg.com/lang/en/docs/install/"
  },
  "scripts": {
    "build": "ng build components --configuration production",
    "watch": "ng build components --watch",
    "test": "jest --config ./jest.config.js"
  },
  "dependencies": {
    "@fontsource/roboto": "^4.4.0",
    "tslib": "^2.2.0"
  },
  "peerDependencies": {
    "@angular/cdk": "^12.0.0",
    "@angular/common": "^12.0.0",
    "@angular/core": "^12.0.0",
    "@angular/flex-layout": "^12.0.0-beta.34",
    "@angular/localize": "^12.0.0",
    "@angular/material": "^12.0.0"
  }
}

app package

{
  "name": "@fabric/guide-app",
  "version": "5.0.1",
  "license": "UNLICENSED",
  "engines": {
    "node": ">=12.0.0 < 16.0.0",
    "yarn": ">= 1.0.0",
    "npm": "Please use Yarn instead of NPM to install dependencies. See: https://yarnpkg.com/lang/en/docs/install/"
  },
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build --configuration production",
    "watch": "ng build --watch --configuration development",
    "test": "jest --config ./jest.config.js",
    "lint": "ng lint",
    "e2e": "ng e2e",
  },
  "private": true,
  "dependencies": {
    "@fabric/components": "^5.0.0",
    "@ngx-translate/core": "~13.0.0",
    "@ngx-translate/http-loader": "~6.0.0",
    "ngx-highlightjs": "~4.1.3"
  }
}

So I guess, first, is my assumption correct, that Yarn should be building the dependent package? And if so, is there something in my config that isn't correct? If that's not part of yarn's offering, do you have any suggestion to what could be added to support this?


Solution

  • Your folder structure, workspaces and dependencies are correctly set up. For yarn to invoke your build script you will have to add it to the postinstall and/or prepare script of your lib package:

    {
      "scripts": {
        "build": "...",
        "postinstall": "yarn build"
      }
    }
    

    When installing the app package, it should run the post-install scripts of all its dependencies. Mind however, that this side-effect may not be desired when using lib in a different context e.g. as a standalone package.

    Note that, when making changes to lib, you have to bump its version and that version has to be in the range app specifies in its dependencies. Only then yarn will re-execute its postinstall script when re-installing the app.


    Apart from that I noticed you used tsconfig's compilerOptions.paths to refer to lib. There is another way to do this using typescript references. A good starting point is this article.