All files / scripts/package-release/lib workspace.js

0% Statements 0/49
0% Branches 0/24
0% Functions 0/7
0% Lines 0/43

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93                                                                                                                                                                                         
import { readFileSync, existsSync } from 'fs';
import path from 'path';
import { globSync } from 'glob';
import { getRepoRoot, getLernaJson, getConfig } from './config.js';
 
let cachedPackages = null;
 
export function discoverPackages() {
  if (cachedPackages) return cachedPackages;
 
  const root = getRepoRoot();
  const lerna = getLernaJson();
  if (!lerna || !lerna.packages) {
    throw new Error('lerna.json not found or missing "packages" field');
  }
 
  // Discovery sources: lerna.json's `packages` globs plus any `additionalPackages`
  // configured in package-release.config.json. The latter lets the release CLI
  // manage published Tier-2 packages that live OUTSIDE lerna's package list
  // (e.g. `design-tokens`, which is intentionally kept out of lerna.json so
  // SFCI's `lerna-publish` step never touches it) — so `detect-bumps`, `tag`,
  // `github`, and `changelog` still see them. Tier classification is unaffected:
  // classifyTier() reads `release-cycle-aligned-packages`, so an additional
  // package that isn't in that list stays Tier 2.
  const config = getConfig();
  const patterns = [...lerna.packages, ...(config.additionalPackages || [])];
 
  const pkgs = [];
  const seenPaths = new Set();
  for (const pattern of patterns) {
    const pkgJsonPath = path.join(root, pattern, 'package.json');
    const matches = globSync(pkgJsonPath);
 
    if (matches.length === 0 && existsSync(pkgJsonPath)) {
      matches.push(pkgJsonPath);
    }
 
    for (const match of matches) {
      if (seenPaths.has(match)) continue; // de-dupe across the two sources
      seenPaths.add(match);
      const manifest = JSON.parse(readFileSync(match, 'utf8'));
      const pkgDir = path.dirname(match);
      pkgs.push({
        name: manifest.name,
        version: manifest.version,
        private: manifest.private === true,
        path: pkgDir,
        relativePath: path.relative(root, pkgDir),
        manifestPath: match,
      });
    }
  }
 
  cachedPackages = pkgs;
  return pkgs;
}
 
export function resolvePackage(nameOrPath) {
  const pkgs = discoverPackages();
  const normalized = nameOrPath.replace(/\/$/, '');
 
  const byName = pkgs.find((p) => p.name === normalized);
  if (byName) return byName;
 
  const byShortName = pkgs.find((p) => {
    const shortName = p.name.split('/').pop();
    return shortName === normalized;
  });
  if (byShortName) return byShortName;
 
  const byPath = pkgs.find(
    (p) => p.relativePath === normalized || p.path === normalized,
  );
  if (byPath) return byPath;
 
  return null;
}
 
export function resolvePackages(nameOrPathList) {
  const resolved = [];
  for (const item of nameOrPathList) {
    const pkg = resolvePackage(item);
    if (!pkg) {
      throw new Error(
        `Package "${item}" not found in lerna.json packages. ` +
        `Available packages: ${discoverPackages().map((p) => p.name).join(', ')}`,
      );
    }
    resolved.push(pkg);
  }
  return resolved;
}