• npm/yarn/pnpm workspaces

    • Makes each workspace available in node_modules/foo, as a symlink
    • Good article on dependency resolution details:
      • https://blog.logrocket.com/javascript-package-managers-compared/
      • https://blog.logrocket.com/advanced-package-manager-features-npm-yarn-pnpm/
  • When you install, will try to look for local version that satisfies, and falls back to npm

    • So if you have a/package.json version 1.0.0 and b that depends on a: ^1.0.0, it will install from local as expected, but if you depend on ^2.0.0, then the a package from npm will get installed!
    • You can alternatively specify “*” but not sure how Lerna interacts with this on publish
  • yarn supports link: but npm removed support for it, can only do file:

    • Only works if not hoisted
    • In workspaces/hoisting, no difference, even yarn just does a copy and doesn’t create a symlink
  • yarn supports nohoist, which results in more duplicate packages and no global sharing, but prevents certain dependency version hoisting issues

  • Issues: phantom dependencies, doppelgangers - good write-up on rushjs

  • Which version gets hoisted? TODO

    • yarn: whichever version has the most dependents
  • Is version resolution greedy? TODO

  • yarn-deduplicate has several strategies: highest, fewer

  • Issues

    • You can have deps as shown, but in effect something different gets hoisted to root node_modules. Build actually uses the version in root

      npm list @types/socket.io-client
      plasmic-monorepo@ /Users/yang/proj/tmp/fullws
      ├─┬ @plasmicapp/[email protected] -> ./packages/watcher
      │ └── @types/[email protected]
      ├─┬ [email protected] -> ./packages/create-plasmic-app
      │ └─┬ @plasmicapp/[email protected]
      │   └── @types/[email protected] deduped
      └─┬ [email protected] -> ./platform/wab
        └── @types/[email protected]
      
      # working
      
      rg version packages/watcher/node_modules/@types/socket.io-client/package.json
      packages/watcher/node_modules/@types/socket.io-client/package.json: No such file or directory (os error 2)
      
      rg version node_modules/@types/socket.io-client/package.json
      3:    "version": "3.0.0",
      
      # (builds)
      
      # not working
      
      rg version packages/watcher/node_modules/@types/socket.io-client/package.json
      3:    "version": "3.0.0",
      
      rg version node_modules/@types/socket.io-client/package.json # I think this is what gets used
      3:    "version": "1.4.36",
      
      ✖  nx run @plasmicapp/watcher:build
      $ yarn build:types && yarn build:index
      $ yarn tsc
      $ /Users/yang/proj/tmp/fullws/node_modules/.bin/tsc
             src/watcher.ts(1,1): error TS6133: 'Socket' is declared but its value is never read.
             src/watcher.ts(12,19): error TS2749: 'Socket' refers to a value, but is being used as a type here. Did you mean 'typeof Socket'?
             src/watcher.ts(81,25): error TS7006: Parameter 'data' implicitly has an 'any' type.
      error Command failed with exit code 2.
      info Visit <https://yarnpkg.com/en/docs/cli/run> for documentation about this command.
      error Command failed with exit code 2.
      info Visit <https://yarnpkg.com/en/docs/cli/run> for documentation about this command.
      error Command failed with exit code 2.
      info Visit <https://yarnpkg.com/en/docs/cli/run> for documentation about this command.
      
  • Misc gotchas

    • Can’t represent true DAG; either hoist to common version, or if there’s a second (potentially also common) version they will be duplicated, unless you’re using pnpm
    • yarn (and maybe others) cannot resolve same range to multiple different versions
    • Must have version number in all subpackages (at least for yarn)
    • If you try to have packages not part of the workspace but using file:../, then these are distinct from other dependencies. Say you have @plasmicapp/react-web depending on @plasmicapp/host: 1.0.0 (the current version). Then wab depends on both as file:. Then you’ll end up with duplicate host even though they’re both 1.0.0! (@plasmicpkgs/foo that has devDep/peerDep on host will be deduped fine though.) yarn-deduplicate has no effect.
    • yarn resolutions don’t work on nested deps of file:
  • NX

    • Something in it allows it to know what the package.json deps are of any project, so if you try to build a downstream library, it will know to first rebuild any upstream libraries that have been changed
  • Lerna

    • lerna publish also increments the versions in dependent packages’ package.json’s.
  • Open questions

    • Typical NX/Lerna guides use “*” as internal package version, which resolves to the local package.
    • Any practical best way to avoid needing build across packages, and to use the latest saved source files - i.e., DX as if it’s single package? Often package.json’s are pointing to some dist/ which requires building, and downstream packages might not expect to have to transpile upstream sources, so I’m not sure there’s a clean way out of this.