7

I'm struggling with multiple npm packages in a root git repository with custom dev scripts to handle launch, compile, build and so on. Now I came across npm workspaces and wanted to use this stunning new feature in my following project structure but I can't get it to work:

projectx (root)
- package.json
- apps   
 -- backend
   -- src
   -- package.json (name: @projectx/backend, scripts: "dev":"ts-node or whatever")
 -- common
   -- src
   -- package.json (name: @projectx/common)
 -- frontend
   -- src
   -- package.json (name: @projectx/frontend, scripts: "dev":"webpack")

My root package.json contains:

    {
  "name": "packagex",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "private": "true",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "back:dev": "npm workspace @projectx/backend dev",
    "front:dev": "npm workspace @projectx/frontend dev",
    "dev": "run-p back:dev front:dev"
  },
  "workspaces": [
    "apps/*"
  ],
  "repository": {
    "type": "git",
    "url": "git_url"
  },
  "author": "me",
  "license": "ISC",
  "devDependencies": {
    "npm-run-all": "^4.1.5"
  }
}

And now I want to start backend and frontend with npm-run-all and the command on root: npm run dev which results in:

Screenshot of terminal output

And I also want to share the common package with backend and frontend, which should be possible in this case. Maybe anobody else is facing the same problem or has some ideas what I am doing wrong here.

Alex Pab
  • 107
  • 1
  • 1
  • 5

4 Answers4

20

npm@7.7.0 added a way to call scripts from child packages/workspaces, here are some examples based on your original:

Running a script named "dev" in all workspaces located under apps/backend:

npm run dev -w apps/backend

Running a script named "dev" in all workspaces:

npm run dev --ws

Running a script named "dev" in a package named @projectx/frontend:

npm run dev -w @projectx/frontend

More info:

ruyadorno
  • 866
  • 5
  • 16
7

Your "workspaces" property in package.json looks right. I'm using NPM Workspaces and it's working well, but it's still missing a lot of features so you need to wire things up yourself. I also don't think npm worksace is a command (but maybe for the future?), so here's a checklist to get it to work:

  • Make sure you're using Node 15+ and NPM 7+
  • Set all package.json to "private": true,
  • Delete all package-lock.json inside of your project, go to the root, then npm install. It should generate one root level package-lock.json that contains all dependencies for your workspaces
  • Since you're using npm-run-all, add this to your scripts:
  "scripts": {
    "back:dev": "cd apps/backend && npm run dev",
    "front:dev": "cd apps/fontend && npm run dev",
    "dev": "npm-run-all build --parallel back:dev front:dev"
  }

Then start it with npm run dev.

Note, you may want to consider using start scripts instead of dev to shorten the command you need to type (e.g. npm start instead of npm run dev), but npm run dev will still be fine.

Johnny Oshika
  • 54,741
  • 40
  • 181
  • 275
  • I can't see any notice requirement for node15/16. Today LTS is still limited to node14, isn't it? – Craig Hicks Sep 12 '21 at 03:56
  • 1
    @CraigHicks NPM 7+ is required for workspaces support. Node 15+ come with NPM 7+. You can use an older version of Node, but then you'll need to explicitly upgrade NPM to 7+. – Johnny Oshika Oct 08 '21 at 00:35
  • 1
    Correct. I'm using NPM 7 with Node 14, because Node 16 (not yet LTS) was causing some errors. Didn't try Node 15. – Craig Hicks Oct 08 '21 at 07:35
4

In root package.json you can also add short name for each package:

  "scripts": {
    "api": "npm --workspace=@app/api run",
  }

@app/api is a name in package.json

And run scripts in ./packages/api folder from root like so:

npm run api lint
npm run api dev
ZiiMakc
  • 31,187
  • 24
  • 65
  • 105
0

I think you wish to:

  • keep scripts and dependencies separate (thus the 4 package.json files), for ease of maintenance

May I suggest a work-around without workspaces that might do what you're after:

{
  ...
  "scripts": {
    "//back:dev": "npm workspace @projectx/backend dev",
    "back:dev": "npm --prefix apps/backend dev",

    "//front:dev": "npm workspace @projectx/frontend dev",
    "front:dev": "npm --prefix apps/frontend dev",

    "dev": "run-p back:dev front:dev"
  },
  "//workspaces": [
    "apps/*"
  ],
  "devDependencies": {
    "@local/back": "file:apps/backend",
    "@local/front": "file:apps/frontend",
    "npm-run-all": "^4.1.5"
  }
}

The npm --prefix runs npm scripts in another folder than the current one.

The @local/back dependencies are not necessary for that, but I've found such useful if eg. a package depends on another. You might use that trick to reach for the common by:

  "dependencies": {
    "@local/common": "file:../common"
  }

I wished a week ago that workspaces would offer a better solution, but didn't find any benefit over the above mechanisms.

I would also like workspaces to:

  • only expose those files in the files entry of the particular package.json (now, all are shown)
  • only allow import to paths in the exports of the particular package.json, if it has one

See NPM Workspaces monorepo - share local package's distribution folder as root instead of the entire source files

akauppi
  • 17,018
  • 15
  • 95
  • 120