yarnpkg/yarn

Yarn workspace fails to add local package as dependency

kohlikohl opened this issue · 21 comments

Do you want to request a feature or report a bug?

Bug. (could be expected behavior)

What is the current behavior?
When trying to install a local package using yarn workspace <workspace-name> add <package-name> adding the package fails. It however succeeds when specifying the exact version of the local package.

If the current behavior is a bug, please provide the steps to reproduce.

Example repo: https://github.com/kohlikohl/yarn-workspace-install-local-package

Failure case

$ yarn workspace @scope/a add @scope/b
yarn workspace v1.3.2
yarn add v1.3.2
[1/4] Resolving packages...
error An unexpected error occurred: "https://registry.yarnpkg.com/@scope%2fb: Not found".
info If you think this is a bug, please open a bug report with the information provided in "C:\\dev\\playground\\yarn-workspace-add-package\\packages\\a\\yarn-error.log".
info Visit https://yarnpkg.com/en/docs/cli/add for documentation about this command.
error Command failed.
Exit code: 1
Command: C:\Program Files\nodejs\node.exe
Arguments: C:\Users\maknoll\AppData\Roaming\nvm\v8.6.0\node_modules\yarn\lib\cli.js add @scope/b
Directory: C:\dev\playground\yarn-workspace-add-package\packages\a
Output:

info Visit https://yarnpkg.com/en/docs/cli/workspace for documentation about this command.

✔️ Working case (version of package is specified)

$ yarn workspace @scope/a add @scope/b@1.0.0
yarn workspace v1.3.2
yarn add v1.3.2
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Saved lockfile.
success Saved 2 new dependencies.
├─ @scope/a@1.0.0
└─ @scope/b@1.0.0
Done in 0.17s.
Done in 0.69s.

What is the expected behavior?
When running yarn workspace @scope/a add @scope/b and not specifying a version, I would expect yarn to resolve @scope/b to the locally available version and add it as dependency to @scope/a's package.json.

I can, however, see that installing the package from the registry by default is a nice functionality. In which case it might be good to add a flag that forces a local install. yarn workspace @scope/a add @scope/b --local 🤔

Please mention your node.js, yarn and operating system version.
Yarn version:
1.3.2

Node version:
8.6.0

Platform:
win32 x64 (windows 10)

This seems similar to #3973.

The workspace docs mention that cd'ing into a packages' folder and running yarn add [-D] whatever should add that dependency to the package's package.json [dev]Dependencies, but no file change occurs...

Still an issue in yarn 1.6.0 (MacOS).

Does not work (tries to find local workspace package in NPM):

yarn workspace x add y

[1/4] 🔍 Resolving packages...
error Couldn't find package "y" on the "npm" registry.

If you specify the version, it works just like you'd expect:

yarn workspace x add y@^1.0.0

Neither local @ scoped or unscoped packages work for me without a corresponding version.

As @kohlikohl noted, Yarn always tries to resolve from the registry when the version is missing (it tries to find the latest one and use it). If someone is willing to open a PR to first check whether there's a workspace of the specified name it would be appreciated 🙂

Had the same problem with npm + lerna + docker. I ended up writing a small script which checks for package.json's in the upper directories and copies the deps+right versions into package.json. Just run it inside your Dockerfile (or whatever you try to deploy).

First specify the needed packages inside your package.json: (the version is being ignored).

	"dependencies": {
		"micro": "^9.3.2",
		"mkdirp": "^0.5.1",
		"money": "^0.2.0",
		"open-exchange-rates": "^0.3.0",
		"winston": "3.0.0-rc5"
	},
	"workspaceDependencies": {
		"moment": "*",
		"dotenv": "*"
	}

This makes it also easy to keep track of used packages inside your project, which makes maintenance much easier. And the script:

const path = require('path');
const fs = require('fs');

const getTargetPackage = ({ targetPackagePath }) => {
  if (!fs.existsSync(targetPackagePath)) {
    return false;
  }
  const targetPackage = JSON.parse(fs.readFileSync(targetPackagePath, 'utf8'));
  if (typeof targetPackage.workspaceDependencies !== 'object') {
    return false;
  }
  if (typeof targetPackage.dependencies !== 'object') {
    targetPackage.dependencies = {};
  }
  return targetPackage;
};

const checkUpperPackageJson = ({ dir, targetPackage, neededWsDeps }) => {
  const pkgPath = path.join(dir, 'package.json');
  if (fs.existsSync(pkgPath)) {
    const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
    if (pkg.dependencies) {
      for (const [name, version] of Object.entries(neededWsDeps)) {
        if (pkg.dependencies[name]) {
          targetPackage.dependencies[name] = pkg.dependencies[name];
          delete neededWsDeps[name];
        }
      }
    }
  }
};

const copyWorkspaceDepsIntoDeps = () => {
  let dir = path.join(process.cwd());
  const targetPackagePath = path.join(dir, 'package.json');
  const targetPackage = getTargetPackage({ targetPackagePath });
  if (targetPackage) {
    const neededWsDeps = { ...targetPackage.workspaceDependencies };
    do {
      checkUpperPackageJson({ dir, targetPackage, neededWsDeps });
      dir = path.resolve(dir, '..');
    } while (dir !== '/' && Object.keys(neededWsDeps).length);
    fs.writeFileSync(targetPackagePath, JSON.stringify(targetPackage, null, "\t"), 'utf8');
    console.log(
      `Copying .workspaceDependencies finished. Not copied: ${Object.keys(neededWsDeps).length}.`,
      neededWsDeps
    );
  }
};

copyWorkspaceDepsIntoDeps();

Results into:

	"dependencies": {
		"micro": "^9.3.2",
		"mkdirp": "^0.5.1",
		"money": "^0.2.0",
		"open-exchange-rates": "^0.3.0",
		"winston": "3.0.0-rc5",
		"moment": "^2.22.2",
		"dotenv": "^6.0.0"
	},
	"workspaceDependencies": {
		"moment": "*",
		"dotenv": "*"
	}

However this should be somehow supported by default. (workspaceDependencies + yarn command for copying lock/versions) Has there ever been some feature requests regarding the change of the "dependencies syntax"? It would be great if we could change it to something like this:

"dependencies": {
  "micro": {
    "dev": false, //default: false
    "optional": false, //default: false
    "peer": false, //default: false
    "version": "^9.3.2", //can be omitted
  },
  "mkdirp": {
    "dev": true,
    "optional": false,
    "workspace": true, //default: false
  },
  "money": "^0.2.0"
},

The current syntax doesn't reflect the existing complexity, like optional dev dependencies, or the fact that peer deps are often also dev deps, etc.

I had a similar (same?) issue where the registry was contacted although I was expecting yarn to link locally. In my case, the issue was a leading ./ in the top-level workspaces-field.

I.e.:
Wrong:

{
  "workspaces": [
    "./packages/*"
  ]
}

Right:

{
  "workspaces": [
    "packages/*"
  ]
}

I have the same issue. Everything was working fine then I took a holiday, came back and tried to run yarn outdated to see what packages need updating. I get an error message saying:

Outdated lockfile. Please run `yarn install` and try again.

Then when I run yarn install, I get the error:

Error: Couldn't find package "@scope/package*" required by "@scope/anotherPackage" on the "npm" registry.

@scope/package and @scope/anotherPackage are both workspaces, so they shouldn't be trying to find them on the npm registry.

The only real change I made to my computer between the time it was working and now was that I installed Firefox but I don't see how that would affect this issue.

Any advice?

same issue here.
in @scoped/somelib

somelib/ > yarn link
success use yarn link "@scoped/somelib"
someproject/ > yarn link "@scoped/somelib"
success using linked package
someproject/ > yarn install
can't find "@scoped/somelib" on npm

I hit a similar issue to @leoselig with their comment above while using yarn 1.22.4. My issue was with a trailing slash that was causing yarn to reach out to the registry to find a local package during install time.

Changing:

{
  "workspaces": {
    "packages": ["packages/*/"]
  }
}

to:

{
  "workspaces": {
    "packages": ["packages/*"]
  }
}

fixed the issue for me.

you can always use a unpublish @scope/b version. only the version is in the local package.json

m4rvr commented

Is there a workaround yet for the issue that it can't find local packages with latest?

jgod commented

Is there a workaround yet for the issue that it can't find local packages with latest?

Yeah, I was migrating a project to use yarn workspaces and figured I could use latest to refer to these internal-packages and I guess not...

Struggled with this one for half an hour and it turns out the workspaces glob in your root package.json file must not start with a ./.

Bad:

  "workspaces": [
    "./packages/*"
  ],

Works:

  "workspaces": [
    "packages/*"
  ],

@bengotow exactly the same thing was posted by @leoselig 4 years ago #4878 (comment)

I migrated from pnpm to yarn, and switching from
"@acme/api": "workspace:^",
to
"@acme/api": "*",

did the trick for me.

I migrated from pnpm to yarn, and switching from "@acme/api": "workspace:^", to "@acme/api": "*",

did the trick for me.

Somehow this worked, thanks.

@Tobjoern worked for me as well. Thanks!

yarn workspace @scope/a add @scope/b --peer worked for me

foges commented

🤦 I spent 6 hours on a Saturday trying to get this to work and all that was needed was to add @1.0.0 to the end of yarn workspace x add y -- Would definitely be great to fix this since I'm sure some ridiculous amount of dev-hours have been spent on this😅

@chenbrian worked for me. Thanks

why... 😂 It didn't worked for me....