11const path = require ( 'path' ) ;
22const { promises : fs } = require ( 'fs' ) ;
33const chalk = require ( 'chalk' ) ;
4+ const pacote = require ( 'pacote' ) ;
45const { runInDir } = require ( './run-in-dir' ) ;
6+ const { forEachPackage } = require ( './monorepo/for-each-package' ) ;
57const { updatePackageJson } = require ( './monorepo/update-package-json' ) ;
68const { withProgress } = require ( './monorepo/with-progress' ) ;
79const {
@@ -11,6 +13,8 @@ const {
1113} = require ( './workspace-dependencies' ) ;
1214const { calculateReplacements, intersects } = require ( './semver-helpers' ) ;
1315
16+ const DEPENDENCY_GROUPS = [ 'peerDependencies' , 'dependencies' , 'devDependencies' ] ;
17+
1418const USAGE = `Check for dependency alignment issues.
1519
1620USAGE: depalign.js [--skip-deduped] [--json] [--autofix] [--type peer,optional]
@@ -23,6 +27,8 @@ Options:
2327 --types-only Will only include a dependency in the report if all packages have this dependency as one of the provided with --type argument.
2428 --autofix Output a list of replacements to normalize ranges and align everything to the highest possible range.
2529 --autofix-only Will only autofix dependencies provided with this option
30+ --align Update peerDepencencies, dependencies and devDependencies that specify the package
31+ --range The range to use when aligning (default latest)
2632 --dangerously-include-mismatched Include mismatched dependencies into autofix.
2733 --config Path to the config. Default is .depalignrc.json
2834 --validate-config Check that 'ignore' option in the config doesn't include extraneous dependencies and versions
@@ -67,13 +73,20 @@ async function main(args) {
6773
6874 const outputJson = args . json ;
6975 const shouldApplyFixes = args . autofix ;
76+ const alignPackage = args . align ;
77+ const alignToRange = args . range || 'latest' ;
7078 const shouldCheckConfig = args [ 'validate-config' ] ;
7179 const fixOnly = new Set ( args [ 'autofix-only' ] ) ;
7280 const includeTypes = args . type ;
7381 const includeTypesOnly = args [ 'types-only' ] ;
7482 const includeDeduped = ! args [ 'skip-deduped' ] ;
7583 const includeMismatched = args [ 'dangerously-include-mismatched' ] ;
7684
85+ if ( alignPackage ) {
86+ await alignPackageToRange ( alignPackage , alignToRange ) ;
87+ return ;
88+ }
89+
7790 const workspaces = await collectWorkspacesMeta ( ) ;
7891 const dependencies = collectWorkspacesDependencies ( workspaces ) ;
7992 const report = generateReport ( dependencies , {
@@ -187,6 +200,47 @@ async function main(args) {
187200 process . exitCode = report . mismatched . size ;
188201}
189202
203+ async function alignPackageToRange ( packageName , range ) {
204+ range = range === 'latest'
205+ // resolve in registry so we don't update package.json to literally "latest"
206+ ? `^${ ( await pacote . manifest ( `${ packageName } @${ range } ` ) ) . version } `
207+ : range
208+
209+ await forEachPackage ( async ( { location, packageJson } ) => {
210+ if ( ! hasDep ( packageJson , packageName ) ) {
211+ return ;
212+ }
213+
214+ await updatePackageJson ( location , ( pkgJson ) => {
215+ return updateDepToRange ( pkgJson , packageName , range ) ;
216+ } ) ;
217+ } ) ;
218+
219+ await runInDir ( 'npm install' ) ;
220+ }
221+
222+ function hasDep ( packageJson , packageName ) {
223+ for ( const group of DEPENDENCY_GROUPS ) {
224+ if ( packageJson [ group ] && packageJson [ group ] [ packageName ] ) {
225+ return true ;
226+ }
227+ }
228+
229+ return false ;
230+ }
231+
232+ function updateDepToRange ( packageJson , packageName , range ) {
233+ const updated = { ...packageJson } ;
234+
235+ for ( const group of DEPENDENCY_GROUPS ) {
236+ if ( packageJson [ group ] && packageJson [ group ] [ packageName ] ) {
237+ updated [ group ] = { ...packageJson [ group ] , [ packageName ] : range } ;
238+ }
239+ }
240+
241+ return updated ;
242+ }
243+
190244function generateReport (
191245 dependencies ,
192246 { includeTypes, includeTypesOnly, ignore = { } } = { ignore : { } }
0 commit comments