diff --git a/yarn.config.cjs b/yarn.config.cjs index 5dfe8f8a3..1666ae474 100644 --- a/yarn.config.cjs +++ b/yarn.config.cjs @@ -126,6 +126,11 @@ module.exports = defineConfig({ expectWorkspaceLicense(workspace); } + if (!isPrivate) { + // Non-private packages must not depend on private packages. + expectNoPrivateWorkspaceProductionDependencies(Yarn, workspace); + } + if (!isPrivate && !exportsExceptions.includes(workspaceBasename)) { // The entrypoints for all published packages must be the same. expectWorkspaceField(workspace, 'module', './dist/index.mjs'); @@ -827,6 +832,36 @@ function expectConsistentDependenciesAndDevDependencies(Yarn) { } } +/** + * Expect that non-private workspace packages do not have production + * dependencies (anything except `devDependencies`) using the `workspace:` + * protocol that resolve to private packages. + * + * @param {Yarn} Yarn - The Yarn "global". + * @param {Workspace} workspace - The workspace to check. + */ +function expectNoPrivateWorkspaceProductionDependencies(Yarn, workspace) { + for (const dependency of Yarn.dependencies({ workspace })) { + if (dependency.type === 'devDependencies') { + continue; + } + + if (!dependency.range.startsWith('workspace:')) { + continue; + } + + const dependencyWorkspace = Yarn.workspace({ ident: dependency.ident }); + if ( + dependencyWorkspace !== null && + dependencyWorkspace.manifest.private === true + ) { + dependency.error( + `Non-private package "${workspace.manifest.name}" must not depend on private package "${dependency.ident}" in "${dependency.type}"`, + ); + } + } +} + /** * Expect that the workspace has a README.md file, and that it is a non-empty * string. The README.md is expected to: