/** * Which directories the workspace ADMITS as packages (#2953 review). * * Reading manifests is not the same as trusting them. An earlier draft * registered every `package.json` the repo-wide scan found, which recreates the * false-positive half of #2953 from a different source: an app importing * registry package `foo` binds to an excluded fixture or example that happens * to declare `name: "foo"`. This repository is itself the example — * `test/fixtures/**` declares `@repo/utils` among others. * * So the boundary is the workspace declaration, and these arms are about where * it is read from and what it admits. */ import { describe, it, expect, afterAll } from 'vitest'; import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import { loadNodeWorkspacePackages } from '../../src/core/ingestion/import-resolvers/node-workspace-packages.js'; const roots: string[] = []; /** Write a throwaway repo from a `relativePath -> contents` map. */ function repo(files: Readonly>): string { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'gn-ws-')); roots.push(root); for (const [rel, contents] of Object.entries(files)) { const full = path.join(root, rel); fs.mkdirSync(path.dirname(full), { recursive: true }); fs.writeFileSync(full, contents); } return root; } const pkg = (name: string): string => JSON.stringify({ name, main: 'src/index.ts' }); afterAll(() => { for (const root of roots) fs.rmSync(root, { recursive: true, force: true }); }); describe('workspace boundary', () => { it('admits a package the pnpm globs declare', async () => { const root = repo({ 'pnpm-workspace.yaml': 'packages:\n - "packages/*"\n', 'package.json': JSON.stringify({ name: 'root', private: true }), 'packages/utils/package.json': pkg('@repo/utils'), }); const packages = await loadNodeWorkspacePackages(root); expect(packages?.byName.has('@repo/utils')).toBe(true); }); it('refuses a package outside those globs', async () => { const root = repo({ 'pnpm-workspace.yaml': 'packages:\n - "packages/*"\n', 'package.json': JSON.stringify({ name: 'root', private: true }), 'packages/utils/package.json': pkg('@repo/utils'), // A fixture or example. Declares a name, is not a workspace member. 'examples/demo/package.json': pkg('lodash'), }); const packages = await loadNodeWorkspacePackages(root); expect(packages?.byName.has('@repo/utils')).toBe(true); // The pointed case: an app importing registry `lodash` must not bind here. expect(packages?.byName.has('lodash')).toBe(false); }); it('reads npm/yarn `workspaces` from the root manifest', async () => { const root = repo({ 'package.json': JSON.stringify({ name: 'root', workspaces: ['apps/*'] }), 'apps/web/package.json': pkg('@repo/web'), 'vendored/copy/package.json': pkg('@repo/vendored'), }); const packages = await loadNodeWorkspacePackages(root); expect(packages?.byName.has('@repo/web')).toBe(true); expect(packages?.byName.has('@repo/vendored')).toBe(false); }); it('reads the yarn object form', async () => { const root = repo({ 'package.json': JSON.stringify({ name: 'root', workspaces: { packages: ['apps/*'] } }), 'apps/web/package.json': pkg('@repo/web'), }); const packages = await loadNodeWorkspacePackages(root); expect(packages?.byName.has('@repo/web')).toBe(true); }); it('prunes root artifact workspaces while keeping nested source directories', async () => { const root = repo({ 'package.json': JSON.stringify({ name: 'root', workspaces: ['generated/*', 'packages/*/generated'], }), 'generated/apiclient/package.json': pkg('@repo/root-artifact'), 'packages/api/generated/package.json': pkg('@repo/generated-source'), }); const packages = await loadNodeWorkspacePackages(root); expect(packages?.byName.has('@repo/root-artifact')).toBe(false); expect(packages?.byName.has('@repo/generated-source')).toBe(true); }); it('honours a `!` exclusion', async () => { const root = repo({ 'pnpm-workspace.yaml': 'packages:\n - "packages/*"\n - "!packages/internal"\n', 'package.json': JSON.stringify({ name: 'root' }), 'packages/utils/package.json': pkg('@repo/utils'), 'packages/internal/package.json': pkg('@repo/internal'), }); const packages = await loadNodeWorkspacePackages(root); expect(packages?.byName.has('@repo/utils')).toBe(true); expect(packages?.byName.has('@repo/internal')).toBe(false); }); it('matches `**` across segments', async () => { const root = repo({ 'pnpm-workspace.yaml': 'packages:\n - "packages/**"\n', 'package.json': JSON.stringify({ name: 'root' }), 'packages/group/nested/package.json': pkg('@repo/nested'), }); const packages = await loadNodeWorkspacePackages(root); expect(packages?.byName.has('@repo/nested')).toBe(true); }); it('admits only the root when the repo declares no workspace', async () => { const root = repo({ 'package.json': pkg('just-one-package'), // A nested manifest in a repo with no workspace declaration is not a // member of anything — which is exactly this repository's own shape, and // why its `test/fixtures/**` manifests must not register. 'test/fixtures/thing/package.json': pkg('@repo/utils'), }); const packages = await loadNodeWorkspacePackages(root); expect(packages?.byName.has('just-one-package')).toBe(true); expect(packages?.byName.has('@repo/utils')).toBe(false); }); it('gives a package with a root-less `exports` map no root entries', async () => { // The loader half of the rule: when `exports` is present it is the whole // interface, so `main` and the conventional `src/index` fallback must not // be offered for the bare package name. const root = repo({ 'pnpm-workspace.yaml': 'packages:\n - "packages/*"\n', 'package.json': JSON.stringify({ name: 'root' }), 'packages/inner/package.json': JSON.stringify({ name: '@repo/inner', main: 'src/index.ts', exports: { './nest': './src/nest.ts' }, }), }); const packages = await loadNodeWorkspacePackages(root); const inner = packages?.byName.get('@repo/inner'); expect(inner?.entries).toEqual([]); expect(inner?.subpathExports.get('nest')).toEqual(['packages/inner/src/nest']); }); it('keeps legacy and conventional root entries when there is no `exports`', async () => { const root = repo({ 'pnpm-workspace.yaml': 'packages:\n - "packages/*"\n', 'package.json': JSON.stringify({ name: 'root' }), 'packages/utils/package.json': JSON.stringify({ name: '@repo/utils', main: 'src/index.ts' }), }); const packages = await loadNodeWorkspacePackages(root); expect(packages?.byName.get('@repo/utils')?.entries).toContain('packages/utils/src/index'); }); it('returns null when the repo has no manifest at all', async () => { const root = repo({ 'src/main.ts': 'export const x = 1;\n' }); expect(await loadNodeWorkspacePackages(root)).toBeNull(); }); });