yarn-frozen-dependencies
Version:
A yarn plugin who freeze the workspace dependencies to prevent undesired updates.
79 lines (63 loc) • 3.71 kB
text/typescript
/** @C3-MONOREPO/YARN-PLUGIN-SEALED-DEPENDENCIES
* This plugin allows marking dependencies as sealed in a workspace to prevent undesired changes.
* When you use a yarn command to manage dependencies, the plugin will automatically check if the operation is permitted
* and report a message otherwise.
*
* To configure a workspace as sealed, add the `sealed` property in package.json. Please see the `SealedOptions` type to
* known more about the possible value of the `sealed` property.
*/
import type {Plugin, Workspace, Descriptor} from "@yarnpkg/core";
import {Operation, OperationReporter, OperationReporterOptions} from "./operation-reporters/operation-reporter";
import {ConfirmOperationReporter} from "./operation-reporters/confirm.operation-reporter";
import {WarnOperationReporter} from "./operation-reporters/warn.operation-reporter";
import {ErrorOperationReporter} from "./operation-reporters/error.operation-reporter";
const checkOperationIsPossible = (operation: Operation) => async (workspace: Workspace, target: string, descriptor: Descriptor, destinationDescriptor: Descriptor) => {
const {manifest: {raw: {sealed: sealedEntry = false}}} = workspace;
const operationReportersOptions: OperationReporterOptions = {
operation,
workspace,
target,
descriptor,
destinationDescriptor
};
const operationReporters: Record<string, OperationReporter> = {
'confirm': new ConfirmOperationReporter(operationReportersOptions),
'warn': new WarnOperationReporter(operationReportersOptions),
'error': new ErrorOperationReporter(operationReportersOptions),
}
if (!sealedEntry) {
return;
}
const sealedOptions = resolveSealedOptions(sealedEntry);
if (sealedOptions[target].includes(operation)) {
await operationReporters[sealedOptions.level].reportOperation();
}
}
////////////////////////////////////////////////////////////////////////////////
// SEALED OPTIONS
////////////////////////////////////////////////////////////////////////////////
type SealedOptions = boolean & {
level?: 'error' | 'warn' | 'confirm',
operations?: Array<Operation>,
dependencies?: boolean | Array<Operation>,
devDependencies?: boolean | Array<Operation>,
peerDependencies?: boolean | Array<Operation>
};
const resolveSealedOptions = (sealedEntry: SealedOptions) => ({
level: (sealedEntry === true && 'error') || (sealedEntry.level) || 'error',
operations: (sealedEntry.operations instanceof Array && sealedEntry.operations) || ['add', 'remove', 'replace'],
dependencies: (sealedEntry.dependencies instanceof Array && sealedEntry.dependencies) || (sealedEntry.dependencies === false && []) || sealedEntry.operations || ['add', 'remove', 'replace'],
devDependencies: (sealedEntry.devDependencies instanceof Array && sealedEntry.devDependencies) || (sealedEntry.dependencies === false && []) || sealedEntry.operations || ['add', 'remove', 'replace'],
peerDependencies: (sealedEntry.peerDependencies instanceof Array && sealedEntry.peerDependencies) || (sealedEntry.dependencies === false && []) || sealedEntry.operations || ['add', 'remove', 'replace'],
});
////////////////////////////////////////////////////////////////////////////////
// PLUGIN DECLARATION AND EXPORT
////////////////////////////////////////////////////////////////////////////////
const plugin: Plugin = {
hooks: {
afterWorkspaceDependencyAddition: checkOperationIsPossible('add'),
afterWorkspaceDependencyRemoval: checkOperationIsPossible('remove'),
afterWorkspaceDependencyReplacement: checkOperationIsPossible('replace'),
},
};
export default plugin;