nx
Version:
261 lines (260 loc) • 11 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.PnpmCatalogManager = void 0;
const js_yaml_1 = require("@zkochan/js-yaml");
const node_fs_1 = require("node:fs");
const node_path_1 = require("node:path");
const fileutils_1 = require("../fileutils");
const output_1 = require("../output");
/**
* PNPM-specific catalog manager implementation
*/
class PnpmCatalogManager {
constructor() {
this.name = 'pnpm';
this.catalogProtocol = 'catalog:';
}
isCatalogReference(version) {
return version.startsWith(this.catalogProtocol);
}
parseCatalogReference(version) {
if (!this.isCatalogReference(version)) {
return null;
}
const catalogName = version.substring(this.catalogProtocol.length);
// Normalize both "catalog:" and "catalog:default" to the same representation
const isDefault = !catalogName || catalogName === 'default';
return {
catalogName: isDefault ? undefined : catalogName,
isDefaultCatalog: isDefault,
};
}
getCatalogDefinitionFilePaths() {
return ['pnpm-workspace.yaml'];
}
getCatalogDefinitions(treeOrRoot) {
if (typeof treeOrRoot === 'string') {
const pnpmWorkspacePath = (0, node_path_1.join)(treeOrRoot, 'pnpm-workspace.yaml');
if (!(0, node_fs_1.existsSync)(pnpmWorkspacePath)) {
return null;
}
return readYamlFileFromFs(pnpmWorkspacePath);
}
else {
if (!treeOrRoot.exists('pnpm-workspace.yaml')) {
return null;
}
return readYamlFileFromTree(treeOrRoot, 'pnpm-workspace.yaml');
}
}
resolveCatalogReference(treeOrRoot, packageName, version) {
const catalogRef = this.parseCatalogReference(version);
if (!catalogRef) {
return null;
}
const workspaceConfig = this.getCatalogDefinitions(treeOrRoot);
if (!workspaceConfig) {
return null;
}
let catalogToUse;
if (catalogRef.isDefaultCatalog) {
// Check both locations for default catalog
catalogToUse =
workspaceConfig.catalog ?? workspaceConfig.catalogs?.default;
}
else if (catalogRef.catalogName) {
catalogToUse = workspaceConfig.catalogs?.[catalogRef.catalogName];
}
return catalogToUse?.[packageName] || null;
}
validateCatalogReference(treeOrRoot, packageName, version) {
const catalogRef = this.parseCatalogReference(version);
if (!catalogRef) {
throw new Error(`Invalid catalog reference syntax: "${version}". Expected format: "catalog:" or "catalog:name"`);
}
const workspaceConfig = this.getCatalogDefinitions(treeOrRoot);
if (!workspaceConfig) {
throw new Error(formatCatalogError('Cannot get Pnpm catalog definitions. No pnpm-workspace.yaml found in workspace root.', ['Create a pnpm-workspace.yaml file in your workspace root']));
}
let catalogToUse;
if (catalogRef.isDefaultCatalog) {
const hasCatalog = !!workspaceConfig.catalog;
const hasCatalogsDefault = !!workspaceConfig.catalogs?.default;
// Error if both defined (matches pnpm behavior)
if (hasCatalog && hasCatalogsDefault) {
throw new Error("The 'default' catalog was defined multiple times. Use the 'catalog' field or 'catalogs.default', but not both.");
}
catalogToUse =
workspaceConfig.catalog ?? workspaceConfig.catalogs?.default;
if (!catalogToUse) {
const availableCatalogs = Object.keys(workspaceConfig.catalogs || {});
const suggestions = [
'Define a default catalog in pnpm-workspace.yaml under the "catalog" key',
];
if (availableCatalogs.length > 0) {
suggestions.push(`Or select from the available named catalogs: ${availableCatalogs
.map((c) => `"catalog:${c}"`)
.join(', ')}`);
}
throw new Error(formatCatalogError('No default catalog defined in pnpm-workspace.yaml', suggestions));
}
}
else if (catalogRef.catalogName) {
catalogToUse = workspaceConfig.catalogs?.[catalogRef.catalogName];
if (!catalogToUse) {
const availableCatalogs = Object.keys(workspaceConfig.catalogs || {}).filter((c) => c !== 'default');
const defaultCatalog = !!workspaceConfig.catalog
? 'catalog'
: !workspaceConfig.catalogs?.default
? 'catalogs.default'
: null;
const suggestions = [
'Define the catalog in pnpm-workspace.yaml under the "catalogs" key',
];
if (availableCatalogs.length > 0) {
suggestions.push(`Or select from the available named catalogs: ${availableCatalogs
.map((c) => `"catalog:${c}"`)
.join(', ')}`);
}
if (defaultCatalog) {
suggestions.push(`Or use the default catalog ("${defaultCatalog}")`);
}
throw new Error(formatCatalogError(`Catalog "${catalogRef.catalogName}" not found in pnpm-workspace.yaml`, suggestions));
}
}
if (!catalogToUse[packageName]) {
let catalogName;
if (catalogRef.isDefaultCatalog) {
// Context-aware messaging based on which location exists
const hasCatalog = !!workspaceConfig.catalog;
catalogName = hasCatalog
? 'default catalog ("catalog")'
: 'default catalog ("catalogs.default")';
}
else {
catalogName = `catalog '${catalogRef.catalogName}'`;
}
const availablePackages = Object.keys(catalogToUse);
const suggestions = [
`Add "${packageName}" to ${catalogName} in pnpm-workspace.yaml`,
];
if (availablePackages.length > 0) {
suggestions.push(`Or select from the available packages in ${catalogName}: ${availablePackages
.map((p) => `"${p}"`)
.join(', ')}`);
}
throw new Error(formatCatalogError(`Package "${packageName}" not found in ${catalogName}`, suggestions));
}
}
updateCatalogVersions(treeOrRoot, updates) {
let checkExists;
let readYaml;
let writeYaml;
if (typeof treeOrRoot === 'string') {
const workspaceYamlPath = (0, node_path_1.join)(treeOrRoot, 'pnpm-workspace.yaml');
checkExists = () => (0, node_fs_1.existsSync)(workspaceYamlPath);
readYaml = () => (0, node_fs_1.readFileSync)(workspaceYamlPath, 'utf-8');
writeYaml = (content) => (0, node_fs_1.writeFileSync)(workspaceYamlPath, content, 'utf-8');
}
else {
checkExists = () => treeOrRoot.exists('pnpm-workspace.yaml');
readYaml = () => treeOrRoot.read('pnpm-workspace.yaml', 'utf-8');
writeYaml = (content) => treeOrRoot.write('pnpm-workspace.yaml', content);
}
if (!checkExists()) {
output_1.output.warn({
title: 'No pnpm-workspace.yaml found',
bodyLines: [
'Cannot update catalog versions without a pnpm-workspace.yaml file.',
'Create a pnpm-workspace.yaml file to use catalogs.',
],
});
return;
}
try {
const workspaceContent = readYaml();
const workspaceData = (0, js_yaml_1.load)(workspaceContent) || {};
let hasChanges = false;
for (const update of updates) {
const { packageName, version, catalogName } = update;
const normalizedCatalogName = catalogName === 'default' ? undefined : catalogName;
let targetCatalog;
if (!normalizedCatalogName) {
// Default catalog - update whichever exists, prefer catalog over catalogs.default
if (workspaceData.catalog) {
targetCatalog = workspaceData.catalog;
}
else if (workspaceData.catalogs?.default) {
targetCatalog = workspaceData.catalogs.default;
}
else {
// Neither exists, create catalog (shorthand syntax)
workspaceData.catalog ??= {};
targetCatalog = workspaceData.catalog;
}
}
else {
// Named catalog
workspaceData.catalogs ??= {};
workspaceData.catalogs[normalizedCatalogName] ??= {};
targetCatalog = workspaceData.catalogs[normalizedCatalogName];
}
if (targetCatalog[packageName] !== version) {
targetCatalog[packageName] = version;
hasChanges = true;
}
}
if (hasChanges) {
writeYaml((0, js_yaml_1.dump)(workspaceData, {
indent: 2,
quotingType: '"',
forceQuotes: true,
}));
}
}
catch (error) {
output_1.output.error({
title: 'Failed to update catalog versions',
bodyLines: [error instanceof Error ? error.message : String(error)],
});
throw error;
}
}
}
exports.PnpmCatalogManager = PnpmCatalogManager;
function readYamlFileFromFs(path) {
try {
return (0, fileutils_1.readYamlFile)(path);
}
catch (error) {
output_1.output.warn({
title: 'Unable to parse pnpm-workspace.yaml',
bodyLines: [error.toString()],
});
return null;
}
}
function readYamlFileFromTree(tree, path) {
const content = tree.read(path, 'utf-8');
const { load } = require('@zkochan/js-yaml');
try {
return load(content, { filename: path });
}
catch (error) {
output_1.output.warn({
title: 'Unable to parse pnpm-workspace.yaml',
bodyLines: [error.toString()],
});
return null;
}
}
function formatCatalogError(error, suggestions) {
let message = error;
if (suggestions && suggestions.length > 0) {
message += '\n\nSuggestions:';
suggestions.forEach((suggestion) => {
message += `\n • ${suggestion}`;
});
}
return message;
}