@mnci/cli 4.2.4 → 4.3.0

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/dist/cli.js CHANGED
@@ -860,7 +860,7 @@ var VERIFY_TARGETS = "lint,typecheck,test,build";
860
860
  var AFFECTED_OR_ALL_GUARD = `node -e "const cp=require('node:child_process');const T='${VERIFY_TARGETS}';const all=()=>process.exit(cp.spawnSync('npx nx run-many -t '+T,{stdio:'inherit',shell:true}).status ?? 1);const ref=process.env.GITHUB_BASE_REF||process.env.SYSTEM_PULLREQUEST_TARGETBRANCH||'';if(!ref){console.log('Not a pull request - verifying EVERY project.');all()}const target=ref.replace('refs/heads/','');const mergeBase=r=>{const o=cp.spawnSync('git',['merge-base',r,'HEAD'],{encoding:'utf8'});return o.status===0?o.stdout.trim():''};let base=mergeBase('origin/'+target);if(!base){console.log('No origin/'+target+' ref - fetching it to resolve a merge-base.');cp.spawnSync('git',['fetch','--no-tags','origin',target],{stdio:'inherit'});base=mergeBase('FETCH_HEAD')}if(!base){console.log('Could not resolve a merge-base with '+target+' - verifying EVERY project.');all()}console.log('Pull request against '+target+' - verifying projects affected since '+base);process.exit(cp.spawnSync('npx nx affected -t '+T+' --base='+base,{stdio:'inherit',shell:true}).status ?? 1)"`;
861
861
  var PACK_APPS_GUARD = `node -e "const fs=require('node:fs');fs.mkdirSync('dist/drop',{recursive:true});const hasProjectJson=fs.globSync('apps/*/project.json').length>0;const hasInlineNx=fs.globSync('apps/*/package.json').some((f)=>{try{return Boolean(JSON.parse(fs.readFileSync(f,'utf8')).nx)}catch{return false}});const hasCsproj=fs.globSync('apps/*/*.csproj').length>0;if(!hasProjectJson&&!hasInlineNx&&!hasCsproj){console.log('No apps to pack - skipping.');process.exit(0)}process.exit(require('node:child_process').spawnSync('npx nx run-many -t package',{stdio:'inherit',shell:true}).status ?? 1)"`;
862
862
  function releaseGuard(pythonPublishEnv, nugetPublishEnv) {
863
- return `node -e "const fs=require('node:fs'),cp=require('node:child_process');const hasNpm=fs.globSync('packages/*/package.json').length>0;const hasPython=fs.globSync('python-packages/*/pyproject.toml').length>0;const hasCsharp=fs.globSync('packages/*/*.csproj').length>0;if(!hasNpm&&!hasPython&&!hasCsharp){console.log('Nothing to release - skipping.');process.exit(0)}const env={...process.env};${pythonPublishEnv}${nugetPublishEnv}process.exit(cp.spawnSync('npx nx release --yes',{stdio:'inherit',shell:true,env}).status ?? 1)"`;
863
+ return String.raw`node -e "const fs=require('node:fs'),cp=require('node:child_process');const npmCount=fs.globSync('packages/*/package.json').length;const csharpCount=fs.globSync('packages/*/*.csproj').length;const pythonCount=fs.globSync('python-packages/*/pyproject.toml').length;const hasNpm=npmCount>0;const hasPython=pythonCount>0;const hasCsharp=csharpCount>0;if(!hasNpm&&!hasPython&&!hasCsharp){console.log('Nothing to release - skipping.');process.exit(0)}const specifier=process.env.RELEASE_SPECIFIER||'';let releaseCmd='npx nx release --yes';if(specifier){if(!/^(major|minor|patch|\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?)$/.test(specifier)){console.error('RELEASE_SPECIFIER value \''+specifier+'\' is invalid - use major, minor, patch, or an exact version like 1.2.3.');process.exit(1)}const releaseProjectCount=npmCount+csharpCount+pythonCount;if(/^(major|minor|patch)$/.test(specifier)&&releaseProjectCount>1){console.error('RELEASE_SPECIFIER is a keyword (\''+specifier+'\') but this workspace has '+releaseProjectCount+' releasable packages - a keyword under-bumps interdependent packages, because nx computes the dependency-bump pass from a stale cached version. Set RELEASE_SPECIFIER to an exact version instead, or clear it.');process.exit(1)}releaseCmd='npx nx release '+specifier+' --yes'}const env={...process.env};${pythonPublishEnv}${nugetPublishEnv}process.exit(cp.spawnSync(releaseCmd,{stdio:'inherit',shell:true,env}).status ?? 1)"`;
864
864
  }
865
865
  function pythonPublishEnvFragment(pythonPublishUrl2) {
866
866
  return pythonPublishUrl2 ? `if(hasPython){env.TWINE_REPOSITORY_URL='${pythonPublishUrl2}';env.TWINE_USERNAME='AzureArtifacts';env.TWINE_PASSWORD=Buffer.from(process.env.PAT,'base64').toString()}` : "";
@@ -943,6 +943,18 @@ variables:
943
943
  # agent OS.
944
944
  - name: npm_config_cache
945
945
  value: $(Pipeline.Workspace)/.npm
946
+ # Overrides the bump nx would compute from conventional commits for one
947
+ # release run \u2014 'major', 'minor', 'patch', or an exact version ('1.2.3').
948
+ # Defined here (rather than left unset) so $(RELEASE_SPECIFIER) always
949
+ # expands: an Azure Pipelines macro that names an undefined variable is
950
+ # left as the literal, unexpanded text '$(RELEASE_SPECIFIER)' rather than
951
+ # empty, which would otherwise reach the release guard below as a bogus
952
+ # value. Override it per run in Pipelines -> Run pipeline -> Variables, or
953
+ # set a repo-level default in Pipeline -> Edit -> Variables. See the
954
+ # release step below for why a bare keyword is unreliable once more than
955
+ # one package is releasable.
956
+ - name: RELEASE_SPECIFIER
957
+ value: ''
946
958
 
947
959
  steps:
948
960
  - checkout: self
@@ -1106,11 +1118,24 @@ steps:
1106
1118
  # skip cleanly when there is nothing to release. When there are Python or
1107
1119
  # C# packages and an Azure feed, twine/NuGet publish credentials are
1108
1120
  # exported (raw PAT, decoded from the base64 variable).
1121
+ #
1122
+ # RELEASE_SPECIFIER (the pipeline variable above) overrides the bump nx
1123
+ # would compute from conventional commits for THIS run. In a workspace with
1124
+ # more than one releasable package, a bare keyword ('major'/'minor'/'patch')
1125
+ # is unreliable \u2014 nx versions interdependent packages in two passes (a
1126
+ # dependency-bump pass, then the specifier pass), and the second pass
1127
+ # computes from a version cached before the first pass ran, silently
1128
+ # landing back on the same patch bump either way. An exact version ('1.2.3')
1129
+ # does not have this problem, since both passes apply it verbatim \u2014 prefer
1130
+ # it whenever more than one package is releasable. The guard below fails
1131
+ # the run rather than under-bumping silently: clear the variable back to
1132
+ # '' once the override is no longer needed.
1109
1133
  - script: ${releaseGuard(pythonPublishEnvFragment(pythonPublishUrl2), nugetPublishEnvFragment(nugetFeedUrl2))}
1110
1134
  displayName: Release \u2014 version, tag and publish (npm + Python + C#)
1111
1135
  condition: ${onMain}
1112
1136
  env:
1113
1137
  ${npmAuthName}: ${npmAuthValue}
1138
+ RELEASE_SPECIFIER: $(RELEASE_SPECIFIER)
1114
1139
 
1115
1140
  # nx release's own git push (release.git.push) is deliberately left off: it
1116
1141
  # only runs when a remote GitHub/GitLab Release is configured, which this
@@ -1306,7 +1331,21 @@ jobs:
1306
1331
  # commits, tag-only push. Portable guard: nx release errors on an empty
1307
1332
  # scope, so skip cleanly when there is nothing to release. When there
1308
1333
  # are Python or C# packages and an Azure feed, twine/NuGet publish
1309
- # credentials are exported (raw PAT, decoded from the base64 secret).${githubReleases ? `
1334
+ # credentials are exported (raw PAT, decoded from the base64 secret).
1335
+ #
1336
+ # RELEASE_SPECIFIER (repository variable, Settings -> Secrets and
1337
+ # variables -> Actions -> Variables) overrides the bump nx would compute
1338
+ # from conventional commits for THIS run: 'major', 'minor', 'patch', or
1339
+ # an exact version ('1.2.3'). In a workspace with more than one
1340
+ # releasable package, a bare keyword is unreliable \u2014 nx versions
1341
+ # interdependent packages in two passes (a dependency-bump pass, then
1342
+ # the specifier pass), and the second pass computes from a version
1343
+ # cached before the first pass ran, silently landing back on the same
1344
+ # patch bump either way. An exact version does not have this problem,
1345
+ # since both passes apply it verbatim \u2014 prefer it whenever more than one
1346
+ # package is releasable. The guard below fails the run rather than
1347
+ # under-bumping silently: unset or clear the variable once the override
1348
+ # is no longer needed.${githubReleases ? `
1310
1349
  # This provider also creates a per-project GitHub Release (changelog
1311
1350
  # generated from conventional commits since that project's last tag),
1312
1351
  # which is why 'nx release' pushes the tag itself here \u2014 see
@@ -1316,7 +1355,8 @@ jobs:
1316
1355
  name: Release \u2014 version, tag${githubReleases ? ", publish and GitHub Release" : " and publish"} (npm + Python + C#)
1317
1356
  if: \${{ ${onMain} }}
1318
1357
  env:
1319
- ${npmAuthName}: ${npmAuthValue}${githubReleases ? `
1358
+ ${npmAuthName}: ${npmAuthValue}
1359
+ RELEASE_SPECIFIER: \${{ vars.RELEASE_SPECIFIER }}${githubReleases ? `
1320
1360
  GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }}` : ""}
1321
1361
  ${githubReleases ? "" : `
1322
1362
  # nx release's own git push (release.git.push) is deliberately left off: it
@@ -1675,31 +1715,9 @@ var SOURCEMAP_PATH_TRANSFORM = [
1675
1715
  " },",
1676
1716
  ""
1677
1717
  ].join("\n");
1678
- var ROLLUP_CONFIG_WITH_DTS_FIX = [
1679
- " {",
1680
- " // Added by MoNecromanCI. @nx/rollup's dts-bundle plugin writes dist/index.d.ts",
1681
- " // as a stub re-exporting the real declarations, and builds that specifier with",
1682
- " // path.relative() - an OS-NATIVE path. On Windows it emits",
1683
- String.raw` // export * from "./src\\index";`,
1684
- " // which is not a valid module specifier on ANY platform: a specifier is",
1685
- " // URL-style, so / is correct everywhere and a backslash nowhere. It resolves on",
1686
- " // Windows only because the resolver normalises separators there, leaving the",
1687
- " // package untyped on Linux and macOS.",
1688
- " //",
1689
- " // mnci also points `types` past this stub, so nothing depends on it being",
1690
- " // correct; this makes the emitted file correct too. Remove once Nx fixes the",
1691
- " // plugin - its own devkit already exports normalizePath for exactly this.",
1692
- " //",
1693
- " // The second half fixes a separate defect in the REAL declarations `types`",
1694
- ' // points at: tsconfig.lib.json declares under moduleResolution "bundler",',
1695
- ' // where a bare relative specifier ("./lib/align") is valid, so every emitted',
1696
- " // .d.ts keeps the source's own extensionless imports verbatim. A consumer on",
1697
- ' // "moduleResolution": "nodenext" - the workspace root default - requires an',
1698
- " // explicit extension on every relative specifier and gets TS2834 (or,",
1699
- " // combined with skipLibCheck, a silent zero-export module) instead. Declared",
1700
- " // extensions are left untouched; only a bare relative specifier gets .js",
1701
- " // appended, matching what tsc itself emits under node16/nodenext.",
1702
- " plugins: [",
1718
+ var DECLARATION_SPECIFIER_PLUGIN_MARKER = "name: 'mnci-normalise-declaration-specifiers'";
1719
+ var DECLARATION_SPECIFIER_EXTENSION_MARKER = "bareRelativeSpecifier";
1720
+ var DECLARATION_SPECIFIER_PLUGIN = [
1703
1721
  " {",
1704
1722
  " name: 'mnci-normalise-declaration-specifiers',",
1705
1723
  " writeBundle (outputOptions) {",
@@ -1721,7 +1739,7 @@ var ROLLUP_CONFIG_WITH_DTS_FIX = [
1721
1739
  " const normalised = source.replaceAll(separator, '/')",
1722
1740
  " if (normalised !== source) writeFileSync(stub, normalised)",
1723
1741
  "",
1724
- String.raw` const bareRelativeSpecifier = /from(\s+)(['"])(\.[^'"]+?)\2/g`,
1742
+ String.raw` const ${DECLARATION_SPECIFIER_EXTENSION_MARKER} = /from(\s+)(['"])(\.[^'"]+?)\2/g`,
1725
1743
  String.raw` const hasExtension = /\.(?:mjs|cjs|jsx?|json)$/`,
1726
1744
  " let entries",
1727
1745
  " try {",
@@ -1739,17 +1757,93 @@ var ROLLUP_CONFIG_WITH_DTS_FIX = [
1739
1757
  " continue",
1740
1758
  " }",
1741
1759
  " const withExtensions = declaration.replace(",
1742
- " bareRelativeSpecifier,",
1760
+ ` ${DECLARATION_SPECIFIER_EXTENSION_MARKER},`,
1743
1761
  " (match, space, quote, specifier) =>",
1744
1762
  " hasExtension.test(specifier) ? match : `from${space}${quote}${specifier}.js${quote}`,",
1745
1763
  " )",
1746
1764
  " if (withExtensions !== declaration) writeFileSync(filePath, withExtensions)",
1747
1765
  " }",
1748
1766
  " }",
1749
- " }",
1767
+ " }"
1768
+ ].join("\n");
1769
+ var ROLLUP_CONFIG_WITH_DTS_FIX = [
1770
+ " {",
1771
+ " // Added by MoNecromanCI. @nx/rollup's dts-bundle plugin writes dist/index.d.ts",
1772
+ " // as a stub re-exporting the real declarations, and builds that specifier with",
1773
+ " // path.relative() - an OS-NATIVE path. On Windows it emits",
1774
+ String.raw` // export * from "./src\\index";`,
1775
+ " // which is not a valid module specifier on ANY platform: a specifier is",
1776
+ " // URL-style, so / is correct everywhere and a backslash nowhere. It resolves on",
1777
+ " // Windows only because the resolver normalises separators there, leaving the",
1778
+ " // package untyped on Linux and macOS.",
1779
+ " //",
1780
+ " // mnci also points `types` past this stub, so nothing depends on it being",
1781
+ " // correct; this makes the emitted file correct too. Remove once Nx fixes the",
1782
+ " // plugin - its own devkit already exports normalizePath for exactly this.",
1783
+ " //",
1784
+ " // The second half fixes a separate defect in the REAL declarations `types`",
1785
+ ' // points at: tsconfig.lib.json declares under moduleResolution "bundler",',
1786
+ ' // where a bare relative specifier ("./lib/align") is valid, so every emitted',
1787
+ " // .d.ts keeps the source's own extensionless imports verbatim. A consumer on",
1788
+ ' // "moduleResolution": "nodenext" - the workspace root default - requires an',
1789
+ " // explicit extension on every relative specifier and gets TS2834 (or,",
1790
+ " // combined with skipLibCheck, a silent zero-export module) instead. Declared",
1791
+ " // extensions are left untouched; only a bare relative specifier gets .js",
1792
+ " // appended, matching what tsc itself emits under node16/nodenext.",
1793
+ " plugins: [",
1794
+ DECLARATION_SPECIFIER_PLUGIN,
1750
1795
  " ]",
1751
1796
  " }"
1752
1797
  ].join("\n");
1798
+ function findEnclosingBraceSpan(text, needle) {
1799
+ const needleIndex = text.indexOf(needle);
1800
+ if (needleIndex === -1) {
1801
+ return void 0;
1802
+ }
1803
+ const start = text.lastIndexOf("{", needleIndex);
1804
+ if (start === -1) {
1805
+ return void 0;
1806
+ }
1807
+ let depth = 0;
1808
+ for (let index = start; index < text.length; index += 1) {
1809
+ if (text[index] === "{") {
1810
+ depth += 1;
1811
+ } else if (text[index] === "}") {
1812
+ depth -= 1;
1813
+ if (depth === 0) {
1814
+ return [start, index + 1];
1815
+ }
1816
+ }
1817
+ }
1818
+ return void 0;
1819
+ }
1820
+ function withUpgradedDeclarationSpecifierPlugin(config) {
1821
+ if (!config.includes(DECLARATION_SPECIFIER_PLUGIN_MARKER) || config.includes(DECLARATION_SPECIFIER_EXTENSION_MARKER)) {
1822
+ return config;
1823
+ }
1824
+ const span = findEnclosingBraceSpan(config, DECLARATION_SPECIFIER_PLUGIN_MARKER);
1825
+ if (!span) {
1826
+ return config;
1827
+ }
1828
+ const [start, end] = span;
1829
+ return `${config.slice(0, start)}${DECLARATION_SPECIFIER_PLUGIN}${config.slice(end)}`;
1830
+ }
1831
+ function upgradeDeclarationSpecifierPlugins(workspaceRoot) {
1832
+ const changed = [];
1833
+ const configs = (0, import_node_fs3.globSync)(["packages/*/rollup.config.cjs", "libs/*/rollup.config.cjs"], {
1834
+ cwd: workspaceRoot
1835
+ });
1836
+ for (const relativePath of configs) {
1837
+ const configPath = (0, import_node_path3.join)(workspaceRoot, relativePath);
1838
+ const before = (0, import_node_fs3.readFileSync)(configPath, "utf8");
1839
+ const after = withUpgradedDeclarationSpecifierPlugin(before);
1840
+ if (after !== before) {
1841
+ writeFileEnsured(configPath, after);
1842
+ changed.push(relativePath.replaceAll("\\", "/"));
1843
+ }
1844
+ }
1845
+ return changed;
1846
+ }
1753
1847
  function repairDeclarationSpecifiers(projectRoot) {
1754
1848
  const configPath = (0, import_node_path3.join)(projectRoot, "rollup.config.cjs");
1755
1849
  if (!fileExists(configPath)) {
@@ -1812,6 +1906,22 @@ function repairPublishableManifest(manifestPath) {
1812
1906
  }
1813
1907
  writeFileEnsured(manifestPath, toJson(manifest));
1814
1908
  }
1909
+ function repairPublishableManifests(workspaceRoot) {
1910
+ const changed = [];
1911
+ const manifests = (0, import_node_fs3.globSync)(["packages/*/package.json", "libs/*/package.json"], {
1912
+ cwd: workspaceRoot
1913
+ });
1914
+ for (const relativePath of manifests) {
1915
+ const manifestPath = (0, import_node_path3.join)(workspaceRoot, relativePath);
1916
+ const before = (0, import_node_fs3.readFileSync)(manifestPath, "utf8");
1917
+ repairPublishableManifest(manifestPath);
1918
+ const after = (0, import_node_fs3.readFileSync)(manifestPath, "utf8");
1919
+ if (after !== before) {
1920
+ changed.push(relativePath.replaceAll("\\", "/"));
1921
+ }
1922
+ }
1923
+ return changed;
1924
+ }
1815
1925
  function ensureAdmZip(workspaceRoot) {
1816
1926
  if (hasPlugin(workspaceRoot, "adm-zip")) {
1817
1927
  return;
@@ -4318,6 +4428,20 @@ function runUpgrade(workspaceRoot, options) {
4318
4428
  logger.detail(`updated ${path}`);
4319
4429
  }
4320
4430
  }
4431
+ const repairedManifests = repairPublishableManifests(workspaceRoot);
4432
+ if (repairedManifests.length > 0) {
4433
+ logger.step("Repointing `types` at the real declaration file");
4434
+ for (const path of repairedManifests) {
4435
+ logger.detail(`updated ${path}`);
4436
+ }
4437
+ }
4438
+ const upgradedDtsPlugins = upgradeDeclarationSpecifierPlugins(workspaceRoot);
4439
+ if (upgradedDtsPlugins.length > 0) {
4440
+ logger.step("Upgrading declaration specifiers to resolve under nodenext");
4441
+ for (const path of upgradedDtsPlugins) {
4442
+ logger.detail(`updated ${path}`);
4443
+ }
4444
+ }
4321
4445
  logger.step(
4322
4446
  "Formatting the workspace (eslint --fix) \u2014 the slowest step, minutes on a large workspace"
4323
4447
  );