@cellajs/cli 0.6.0 → 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -414,7 +414,8 @@ previous value, and a package.json without `type` gets upstream's. Set it yourse
414
414
 
415
415
  1. **Updates.** It lists the outdated packages and runtime pins in one table and asks once which to
416
416
  update. Rows with a high or critical advisory start ticked; submitting with nothing ticked skips
417
- this layer.
417
+ this layer. Above the prompt it prints what `pnpm audit` reports right now, including packages
418
+ that have no row, so a run you cancel has shown them too.
418
419
  2. **Security.** It audits the workspace as the picked updates leave it and brings three settings
419
420
  in `pnpm-workspace.yaml` in line with that audit.
420
421
 
package/dist/index.js CHANGED
@@ -228,7 +228,7 @@ import process5 from "process";
228
228
  // package.json
229
229
  var package_default = {
230
230
  name: "@cellajs/cli",
231
- version: "0.6.0",
231
+ version: "0.6.2",
232
232
  private: false,
233
233
  license: "MIT",
234
234
  description: "CLI for managing your monorepo and upgrading it with upstream Cella.",
@@ -1436,6 +1436,129 @@ function formatAlignmentsLine(total, open) {
1436
1436
  return `alignments: ${parts.join(" \xB7 ")}`;
1437
1437
  }
1438
1438
 
1439
+ // src/utils/upstream-overrides.ts
1440
+ import { Node, Project as Project2, SyntaxKind as SyntaxKind2 } from "ts-morph";
1441
+ var LIST_KEYS = ["pinned", "ignored"];
1442
+ function resolveExpression(node, sourceFile, depth = 0) {
1443
+ if (!node || depth > 8) return node;
1444
+ if (Node.isParenthesizedExpression(node) || Node.isAsExpression(node) || Node.isSatisfiesExpression(node)) {
1445
+ return resolveExpression(node.getExpression(), sourceFile, depth + 1);
1446
+ }
1447
+ if (Node.isIdentifier(node)) {
1448
+ return resolveExpression(
1449
+ sourceFile.getVariableDeclaration(node.getText())?.getInitializer(),
1450
+ sourceFile,
1451
+ depth + 1
1452
+ );
1453
+ }
1454
+ return node;
1455
+ }
1456
+ function findConfigObject(sourceFile) {
1457
+ for (const call of sourceFile.getDescendantsOfKind(SyntaxKind2.CallExpression)) {
1458
+ if (call.getExpression().getText() !== "defineConfig") continue;
1459
+ const arg = resolveExpression(call.getArguments()[0], sourceFile);
1460
+ if (Node.isObjectLiteralExpression(arg)) return arg;
1461
+ }
1462
+ const exported = sourceFile.getExportAssignment((assignment) => !assignment.isExportEquals());
1463
+ const value = resolveExpression(exported?.getExpression(), sourceFile);
1464
+ return Node.isObjectLiteralExpression(value) ? value : void 0;
1465
+ }
1466
+ function readProperty(object, name, sourceFile) {
1467
+ for (const property of object.getProperties()) {
1468
+ if (Node.isPropertyAssignment(property)) {
1469
+ if (property.getName().replace(/^['"`]|['"`]$/g, "") !== name) continue;
1470
+ return resolveExpression(property.getInitializer(), sourceFile) ?? null;
1471
+ }
1472
+ if (Node.isShorthandPropertyAssignment(property) && property.getName() === name) {
1473
+ return resolveExpression(property.getNameNode(), sourceFile) ?? null;
1474
+ }
1475
+ }
1476
+ return void 0;
1477
+ }
1478
+ function readStringEntries(node) {
1479
+ if (!Node.isArrayLiteralExpression(node)) return null;
1480
+ const entries = [];
1481
+ for (const element of node.getElements()) {
1482
+ if (Node.isStringLiteral(element) || Node.isNoSubstitutionTemplateLiteral(element)) {
1483
+ entries.push(element.getLiteralValue());
1484
+ }
1485
+ }
1486
+ return entries;
1487
+ }
1488
+ function readPackageJsonKeys(config, sourceFile) {
1489
+ const settings = readProperty(config, "settings", sourceFile);
1490
+ if (settings === void 0) return [...DEFAULT_PACKAGE_JSON_KEYS];
1491
+ if (!settings || !Node.isObjectLiteralExpression(settings)) return null;
1492
+ const current = readProperty(settings, "packageJsonKeys", sourceFile);
1493
+ const keys = current === void 0 ? readProperty(settings, "packageJsonSync", sourceFile) : current;
1494
+ if (keys === void 0) return [...DEFAULT_PACKAGE_JSON_KEYS];
1495
+ return keys ? readStringEntries(keys) : null;
1496
+ }
1497
+ function parseOverrideLists(source) {
1498
+ try {
1499
+ const project = new Project2({ useInMemoryFileSystem: true, skipLoadingLibFiles: true });
1500
+ const sourceFile = project.createSourceFile("cella.config.ts", source);
1501
+ const config = findConfigObject(sourceFile);
1502
+ if (!config) return null;
1503
+ const lists = {
1504
+ pinned: [],
1505
+ ignored: [],
1506
+ packageJsonKeys: readPackageJsonKeys(config, sourceFile)
1507
+ };
1508
+ const overrides = readProperty(config, "overrides", sourceFile);
1509
+ if (overrides === void 0) return lists;
1510
+ if (!overrides || !Node.isObjectLiteralExpression(overrides)) return null;
1511
+ for (const key of LIST_KEYS) {
1512
+ const list = readProperty(overrides, key, sourceFile);
1513
+ if (list === void 0) continue;
1514
+ const entries = list ? readStringEntries(list) : null;
1515
+ if (!entries) return null;
1516
+ lists[key] = entries;
1517
+ }
1518
+ return lists;
1519
+ } catch {
1520
+ return null;
1521
+ }
1522
+ }
1523
+ function diffOverrideLists(base, incoming, fork) {
1524
+ const diff = (key) => {
1525
+ const baseSet = new Set(base[key].map(normalizeEntry).filter(Boolean));
1526
+ const incomingSet = new Set(incoming[key].map(normalizeEntry).filter(Boolean));
1527
+ const forkEntries = fork[key].map(normalizeEntry).filter(Boolean);
1528
+ return {
1529
+ added: [...incomingSet].filter((entry) => !baseSet.has(entry) && !isUnderAnyFolder(entry, forkEntries)),
1530
+ removed: [...baseSet].filter((entry) => !incomingSet.has(entry) && forkEntries.includes(entry))
1531
+ };
1532
+ };
1533
+ const diffKeys = () => {
1534
+ const [baseKeys, incomingKeys, forkKeys] = [base.packageJsonKeys, incoming.packageJsonKeys, fork.packageJsonKeys];
1535
+ if (!baseKeys || !incomingKeys || !forkKeys) return { added: [], removed: [] };
1536
+ return {
1537
+ added: [...new Set(incomingKeys)].filter((key) => !baseKeys.includes(key) && !forkKeys.includes(key)),
1538
+ removed: [...new Set(baseKeys)].filter((key) => !incomingKeys.includes(key) && forkKeys.includes(key))
1539
+ };
1540
+ };
1541
+ return { pinned: diff("pinned"), ignored: diff("ignored"), packageJsonKeys: diffKeys() };
1542
+ }
1543
+ async function compareUpstreamOverrides(repoPath, mergeBaseRef, incomingRef, forkConfig) {
1544
+ const [baseSource, incomingSource] = await Promise.all(
1545
+ [mergeBaseRef, incomingRef].map((ref) => git(["show", `${ref}:${CONFIG_FILE}`], repoPath, { ignoreErrors: true }))
1546
+ );
1547
+ if (baseSource === incomingSource) return void 0;
1548
+ const base = baseSource ? parseOverrideLists(baseSource) : null;
1549
+ if (!base) return { kind: "unreadable", side: "base" };
1550
+ const incoming = incomingSource ? parseOverrideLists(incomingSource) : null;
1551
+ if (!incoming) return { kind: "unreadable", side: "incoming" };
1552
+ const fork = {
1553
+ pinned: forkConfig.overrides?.pinned ?? [],
1554
+ ignored: forkConfig.overrides?.ignored ?? [],
1555
+ packageJsonKeys: forkConfig.settings.packageJsonKeys ?? DEFAULT_PACKAGE_JSON_KEYS
1556
+ };
1557
+ const changes = diffOverrideLists(base, incoming, fork);
1558
+ const count3 = Object.values(changes).reduce((n, list) => n + list.added.length + list.removed.length, 0);
1559
+ return count3 > 0 ? { kind: "changes", ...changes } : void 0;
1560
+ }
1561
+
1439
1562
  // src/utils/overrides.ts
1440
1563
  function normalizeEntry(entry) {
1441
1564
  return entry.trim().replace(/^\.\//, "").replace(/\/+$/, "");
@@ -1491,8 +1614,11 @@ async function validateOverrides(config, forkPath, upstreamRef) {
1491
1614
  }
1492
1615
  if (upstream && pinsUpstreamHas.length > 0) {
1493
1616
  const differs = await listChangedSinceRef(forkPath, upstream, pinsUpstreamHas.map(normalizeEntry));
1617
+ const upstreamSource = await git(["show", `${upstream}:${CONFIG_FILE}`], forkPath, { ignoreErrors: true });
1618
+ const upstreamPins = upstreamSource && parseOverrideLists(upstreamSource)?.pinned.map(normalizeEntry) || [];
1494
1619
  for (const entry of pinsUpstreamHas) {
1495
1620
  if (differs.some((changed) => isUnderAnyFolder(changed, [entry]))) continue;
1621
+ if (upstreamPins.includes(normalizeEntry(entry))) continue;
1496
1622
  warnings.push(`pin is identical to upstream, it only drops upstream's next change: ${entry}`);
1497
1623
  }
1498
1624
  }
@@ -1814,129 +1940,6 @@ function cliVersionMismatch(running, range) {
1814
1940
  ].join("\n");
1815
1941
  }
1816
1942
 
1817
- // src/utils/upstream-overrides.ts
1818
- import { Node, Project as Project2, SyntaxKind as SyntaxKind2 } from "ts-morph";
1819
- var LIST_KEYS = ["pinned", "ignored"];
1820
- function resolveExpression(node, sourceFile, depth = 0) {
1821
- if (!node || depth > 8) return node;
1822
- if (Node.isParenthesizedExpression(node) || Node.isAsExpression(node) || Node.isSatisfiesExpression(node)) {
1823
- return resolveExpression(node.getExpression(), sourceFile, depth + 1);
1824
- }
1825
- if (Node.isIdentifier(node)) {
1826
- return resolveExpression(
1827
- sourceFile.getVariableDeclaration(node.getText())?.getInitializer(),
1828
- sourceFile,
1829
- depth + 1
1830
- );
1831
- }
1832
- return node;
1833
- }
1834
- function findConfigObject(sourceFile) {
1835
- for (const call of sourceFile.getDescendantsOfKind(SyntaxKind2.CallExpression)) {
1836
- if (call.getExpression().getText() !== "defineConfig") continue;
1837
- const arg = resolveExpression(call.getArguments()[0], sourceFile);
1838
- if (Node.isObjectLiteralExpression(arg)) return arg;
1839
- }
1840
- const exported = sourceFile.getExportAssignment((assignment) => !assignment.isExportEquals());
1841
- const value = resolveExpression(exported?.getExpression(), sourceFile);
1842
- return Node.isObjectLiteralExpression(value) ? value : void 0;
1843
- }
1844
- function readProperty(object, name, sourceFile) {
1845
- for (const property of object.getProperties()) {
1846
- if (Node.isPropertyAssignment(property)) {
1847
- if (property.getName().replace(/^['"`]|['"`]$/g, "") !== name) continue;
1848
- return resolveExpression(property.getInitializer(), sourceFile) ?? null;
1849
- }
1850
- if (Node.isShorthandPropertyAssignment(property) && property.getName() === name) {
1851
- return resolveExpression(property.getNameNode(), sourceFile) ?? null;
1852
- }
1853
- }
1854
- return void 0;
1855
- }
1856
- function readStringEntries(node) {
1857
- if (!Node.isArrayLiteralExpression(node)) return null;
1858
- const entries = [];
1859
- for (const element of node.getElements()) {
1860
- if (Node.isStringLiteral(element) || Node.isNoSubstitutionTemplateLiteral(element)) {
1861
- entries.push(element.getLiteralValue());
1862
- }
1863
- }
1864
- return entries;
1865
- }
1866
- function readPackageJsonKeys(config, sourceFile) {
1867
- const settings = readProperty(config, "settings", sourceFile);
1868
- if (settings === void 0) return [...DEFAULT_PACKAGE_JSON_KEYS];
1869
- if (!settings || !Node.isObjectLiteralExpression(settings)) return null;
1870
- const current = readProperty(settings, "packageJsonKeys", sourceFile);
1871
- const keys = current === void 0 ? readProperty(settings, "packageJsonSync", sourceFile) : current;
1872
- if (keys === void 0) return [...DEFAULT_PACKAGE_JSON_KEYS];
1873
- return keys ? readStringEntries(keys) : null;
1874
- }
1875
- function parseOverrideLists(source) {
1876
- try {
1877
- const project = new Project2({ useInMemoryFileSystem: true, skipLoadingLibFiles: true });
1878
- const sourceFile = project.createSourceFile("cella.config.ts", source);
1879
- const config = findConfigObject(sourceFile);
1880
- if (!config) return null;
1881
- const lists = {
1882
- pinned: [],
1883
- ignored: [],
1884
- packageJsonKeys: readPackageJsonKeys(config, sourceFile)
1885
- };
1886
- const overrides = readProperty(config, "overrides", sourceFile);
1887
- if (overrides === void 0) return lists;
1888
- if (!overrides || !Node.isObjectLiteralExpression(overrides)) return null;
1889
- for (const key of LIST_KEYS) {
1890
- const list = readProperty(overrides, key, sourceFile);
1891
- if (list === void 0) continue;
1892
- const entries = list ? readStringEntries(list) : null;
1893
- if (!entries) return null;
1894
- lists[key] = entries;
1895
- }
1896
- return lists;
1897
- } catch {
1898
- return null;
1899
- }
1900
- }
1901
- function diffOverrideLists(base, incoming, fork) {
1902
- const diff = (key) => {
1903
- const baseSet = new Set(base[key].map(normalizeEntry).filter(Boolean));
1904
- const incomingSet = new Set(incoming[key].map(normalizeEntry).filter(Boolean));
1905
- const forkEntries = fork[key].map(normalizeEntry).filter(Boolean);
1906
- return {
1907
- added: [...incomingSet].filter((entry) => !baseSet.has(entry) && !isUnderAnyFolder(entry, forkEntries)),
1908
- removed: [...baseSet].filter((entry) => !incomingSet.has(entry) && forkEntries.includes(entry))
1909
- };
1910
- };
1911
- const diffKeys = () => {
1912
- const [baseKeys, incomingKeys, forkKeys] = [base.packageJsonKeys, incoming.packageJsonKeys, fork.packageJsonKeys];
1913
- if (!baseKeys || !incomingKeys || !forkKeys) return { added: [], removed: [] };
1914
- return {
1915
- added: [...new Set(incomingKeys)].filter((key) => !baseKeys.includes(key) && !forkKeys.includes(key)),
1916
- removed: [...new Set(baseKeys)].filter((key) => !incomingKeys.includes(key) && forkKeys.includes(key))
1917
- };
1918
- };
1919
- return { pinned: diff("pinned"), ignored: diff("ignored"), packageJsonKeys: diffKeys() };
1920
- }
1921
- async function compareUpstreamOverrides(repoPath, mergeBaseRef, incomingRef, forkConfig) {
1922
- const [baseSource, incomingSource] = await Promise.all(
1923
- [mergeBaseRef, incomingRef].map((ref) => git(["show", `${ref}:${CONFIG_FILE}`], repoPath, { ignoreErrors: true }))
1924
- );
1925
- if (baseSource === incomingSource) return void 0;
1926
- const base = baseSource ? parseOverrideLists(baseSource) : null;
1927
- if (!base) return { kind: "unreadable", side: "base" };
1928
- const incoming = incomingSource ? parseOverrideLists(incomingSource) : null;
1929
- if (!incoming) return { kind: "unreadable", side: "incoming" };
1930
- const fork = {
1931
- pinned: forkConfig.overrides?.pinned ?? [],
1932
- ignored: forkConfig.overrides?.ignored ?? [],
1933
- packageJsonKeys: forkConfig.settings.packageJsonKeys ?? DEFAULT_PACKAGE_JSON_KEYS
1934
- };
1935
- const changes = diffOverrideLists(base, incoming, fork);
1936
- const count3 = Object.values(changes).reduce((n, list) => n + list.added.length + list.removed.length, 0);
1937
- return count3 > 0 ? { kind: "changes", ...changes } : void 0;
1938
- }
1939
-
1940
1943
  // src/services/analyze-core.ts
1941
1944
  function isReportableIgnored(filePath) {
1942
1945
  return !isManagedFile(filePath) && !isGeneratedFile(filePath);
@@ -4466,8 +4469,9 @@ async function runAudit(config) {
4466
4469
  }
4467
4470
  const runtimes = planRuntimeUpdates(pins, feeds, { releaseAgeMinutes, now });
4468
4471
  const localTools = planLocalTools(machine, { releaseAgeMinutes, now });
4472
+ const advisories = auditResult ? advisoriesFromAudit(auditResult) : null;
4469
4473
  const rows = [
4470
- ...packageRows(outdated, auditResult ? advisoriesFromAudit(auditResult) : []),
4474
+ ...packageRows(outdated, advisories ?? []),
4471
4475
  ...runtimes.updates.map(runtimeRow)
4472
4476
  ];
4473
4477
  if (rows.length === 0) {
@@ -4487,6 +4491,7 @@ async function runAudit(config) {
4487
4491
  await runSecurityPass(forkPath, { apply: false, releaseAgeMinutes });
4488
4492
  return;
4489
4493
  }
4494
+ printReported(advisories);
4490
4495
  const selected = await promptForUpdates(rows);
4491
4496
  if (!selected) return;
4492
4497
  const { edits, bumped } = await planEdits(forkPath, rows, selected, pins.nodeImageSuffixes);
@@ -4495,8 +4500,8 @@ async function runAudit(config) {
4495
4500
  spinner.stop();
4496
4501
  }
4497
4502
  }
4503
+ var worstSeverity = (advisories, name) => SEVERITIES.find((severity) => advisories.some((a) => a.module === name && a.severity === severity)) ?? null;
4498
4504
  function packageRows(outdated, advisories) {
4499
- const severityOf = (name) => SEVERITIES.find((severity) => advisories.some((a) => a.module === name && a.severity === severity)) ?? null;
4500
4505
  const readDependentPkg = createDependentPkgReader();
4501
4506
  const rows = Object.entries(outdated).map(([key, pkg]) => {
4502
4507
  const name = outdatedPackageName(key);
@@ -4520,7 +4525,7 @@ function packageRows(outdated, advisories) {
4520
4525
  pinnedIn,
4521
4526
  repoUrl,
4522
4527
  releasesUrl: getReleasesUrl(repoUrl),
4523
- severity: severityOf(name)
4528
+ severity: worstSeverity(advisories, name)
4524
4529
  };
4525
4530
  });
4526
4531
  return rows.sort((a, b) => inText(a).localeCompare(inText(b)) || a.name.localeCompare(b.name));
@@ -4626,6 +4631,40 @@ function printLocalTools(checks) {
4626
4631
  }
4627
4632
  console.info();
4628
4633
  }
4634
+ function printReported(advisories) {
4635
+ if (!advisories) {
4636
+ console.info(`${warningMark} pnpm audit gave no answer: a row in the table can lack its dot`);
4637
+ console.info();
4638
+ return;
4639
+ }
4640
+ if (advisories.length === 0) {
4641
+ console.info(`${checkMark} pnpm audit reports nothing ${colors_default.dim("\xB7 with the current overrides and ignores")}`);
4642
+ console.info();
4643
+ return;
4644
+ }
4645
+ const modules = [...new Set(advisories.map((a) => a.module))].map((name) => {
4646
+ const own = advisories.filter((a) => a.module === name);
4647
+ return {
4648
+ name,
4649
+ severity: worstSeverity(own, name) ?? own[0].severity,
4650
+ installed: [...new Set(own.flatMap((a) => a.installed))].join(", "),
4651
+ via: formatDependents([...new Set(own.flatMap((a) => a.via))]),
4652
+ direct: own.some((a) => a.direct)
4653
+ };
4654
+ });
4655
+ modules.sort((a, b) => SEVERITIES.indexOf(a.severity) - SEVERITIES.indexOf(b.severity));
4656
+ const nameWidth = Math.min(30, Math.max(...modules.map((m) => m.name.length)));
4657
+ const versionWidth = Math.max(...modules.map((m) => m.installed.length));
4658
+ console.info(`${colors_default.bold("pnpm audit")} ${colors_default.dim("\xB7 with the current overrides and ignores")}`);
4659
+ for (const m of modules) {
4660
+ const name = colors_default.white(middleTruncate(m.name, nameWidth).padEnd(nameWidth));
4661
+ const source = `${m.via}${m.direct ? " (direct)" : ""}`;
4662
+ console.info(
4663
+ `${getVulnIcon(m.severity)} ${name} ${m.severity.padEnd(8)} ${m.installed.padEnd(versionWidth)} ${colors_default.dim(source)}`
4664
+ );
4665
+ }
4666
+ console.info();
4667
+ }
4629
4668
  function buildUpdateChoices(rows) {
4630
4669
  const makeChoice = (row) => {
4631
4670
  const devTag = row.isDev ? colors_default.dim(" (dev)") : "";