GitNexus/gitnexus/test/unit/node-workspace-scope.test.ts

188 lines
7.1 KiB
TypeScript

/**
* 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<Record<string, string>>): 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();
});
});