yarn-deduplicate
Advanced tools
Comparing version 2.0.0 to 2.1.0
@@ -8,2 +8,17 @@ # Changelog | ||
## [2.1.0] - 2020-07-10 | ||
### Chores | ||
- Updated dependencies | ||
- Move from CircleCI to GitHub actions | ||
- Clean up and dedupe `yarn.lock` | ||
### Added | ||
- Option `--scopes` to limit changes to a list of scopes (thanks to @sgomes) | ||
- Improve documentation for `--strategy` (thanks to @KubaJastrz) | ||
- Clean up .npmignore (thanks to @bluelovers) | ||
## [2.0.0] - 2020-02-29 | ||
@@ -10,0 +25,0 @@ |
27
cli.js
@@ -18,10 +18,20 @@ #!/usr/bin/env node | ||
.option('-l, --list', 'do not change yarn.lock, just output the diagnosis') | ||
.option('-f, --fail', 'if there are duplicates in yarn.lock, exit 1 for failure') | ||
.option( | ||
'-f, --fail', | ||
'if there are duplicates in yarn.lock, terminate the script wuth exit status 1' | ||
) | ||
.option( | ||
'--scopes <scopes>', | ||
'a comma separated list of scopes to deduplicate. Defaults to all packages.', | ||
(val) => val.split(',').map((v) => v.trim()) | ||
) | ||
.option( | ||
'--packages <packages>', | ||
'a comma separated list of packages to deduplicate. Defaults to all packages.', | ||
val => val.split(',').map(v => v.trim()) | ||
(val) => val.split(',').map((v) => v.trim()) | ||
) | ||
.option('--exclude <exclude>', 'a comma separated list of packages not to deduplicate.', val => | ||
val.split(',').map(v => v.trim()) | ||
.option( | ||
'--exclude <exclude>', | ||
'a comma separated list of packages not to deduplicate.', | ||
(val) => val.split(',').map((v) => v.trim()) | ||
) | ||
@@ -32,2 +42,7 @@ .option('--print', 'instead of saving the deduplicated yarn.lock, print the result in stdout'); | ||
if (commander.scopes && commander.packages) { | ||
console.error('Please specify either scopes or packages, not both.'); | ||
commander.help(); | ||
} | ||
if (commander.strategy !== 'highest' && commander.strategy !== 'fewer') { | ||
@@ -47,6 +62,7 @@ console.error(`Invalid strategy ${commander.strategy}`); | ||
useMostCommon, | ||
includeScopes: commander.scopes, | ||
includePackages: commander.packages, | ||
excludePackages: commander.exclude, | ||
}); | ||
duplicates.forEach(logLine => console.log(logLine)); | ||
duplicates.forEach((logLine) => console.log(logLine)); | ||
if (commander.fail && duplicates.length > 0) { | ||
@@ -58,2 +74,3 @@ process.exit(1); | ||
useMostCommon, | ||
includeScopes: commander.scopes, | ||
includePackages: commander.packages, | ||
@@ -60,0 +77,0 @@ excludePackages: commander.exclude, |
61
index.js
const lockfile = require('@yarnpkg/lockfile'); | ||
const semver = require('semver'); | ||
const parseYarnLock = file => lockfile.parse(file).object; | ||
const parseYarnLock = (file) => lockfile.parse(file).object; | ||
const extractPackages = (json, includePackages = [], excludePackages = []) => { | ||
const extractPackages = (json, includeScopes = [], includePackages = [], excludePackages = []) => { | ||
const packages = {}; | ||
const re = /^(.*)@([^@]*?)$/; | ||
Object.keys(json).forEach(name => { | ||
Object.keys(json).forEach((name) => { | ||
const pkg = json[name]; | ||
@@ -29,2 +29,10 @@ const match = name.match(re); | ||
// If there is a list of scopes, only process those. | ||
if ( | ||
includeScopes.length > 0 && | ||
!includeScopes.find((scope) => packageName.startsWith(`${scope}/`)) | ||
) { | ||
return; | ||
} | ||
// If there is a list of package names, only process those. | ||
@@ -62,5 +70,5 @@ if (includePackages.length > 0 && !includePackages.includes(packageName)) return; | ||
// Link each package instance with all the versions it could satisfy. | ||
Object.keys(versions).forEach(version => { | ||
Object.keys(versions).forEach((version) => { | ||
const satisfies = versions[version].satisfies; | ||
packageInstances.forEach(packageInstance => { | ||
packageInstances.forEach((packageInstance) => { | ||
// We can assume that the installed version always satisfied the requested version. | ||
@@ -81,3 +89,3 @@ packageInstance.satisfiedBy.add(packageInstance.installedVersion); | ||
// Sort the list of satisfied versions | ||
packageInstances.forEach(packageInstance => { | ||
packageInstances.forEach((packageInstance) => { | ||
// Save all versions for future reference | ||
@@ -107,4 +115,7 @@ packageInstance.versions = versions; | ||
const getDuplicatedPackages = (json, { includePackages, excludePackages, useMostCommon }) => { | ||
const packages = extractPackages(json, includePackages, excludePackages); | ||
const getDuplicatedPackages = ( | ||
json, | ||
{ includeScopes, includePackages, excludePackages, useMostCommon } | ||
) => { | ||
const packages = extractPackages(json, includeScopes, includePackages, excludePackages); | ||
return Object.keys(packages) | ||
@@ -120,3 +131,3 @@ .reduce( | ||
yarnLock, | ||
{ includePackages = [], excludePackages = [], useMostCommon = false } = {} | ||
{ includeScopes = [], includePackages = [], excludePackages = [], useMostCommon = false } = {} | ||
) => { | ||
@@ -126,9 +137,12 @@ const json = parseYarnLock(yarnLock); | ||
getDuplicatedPackages(json, { includePackages, excludePackages, useMostCommon }).forEach( | ||
({ bestVersion, name, installedVersion, requestedVersion }) => { | ||
result.push( | ||
`Package "${name}" wants ${requestedVersion} and could get ${bestVersion}, but got ${installedVersion}` | ||
); | ||
} | ||
); | ||
getDuplicatedPackages(json, { | ||
includeScopes, | ||
includePackages, | ||
excludePackages, | ||
useMostCommon, | ||
}).forEach(({ bestVersion, name, installedVersion, requestedVersion }) => { | ||
result.push( | ||
`Package "${name}" wants ${requestedVersion} and could get ${bestVersion}, but got ${installedVersion}` | ||
); | ||
}); | ||
@@ -140,13 +154,16 @@ return result; | ||
yarnLock, | ||
{ includePackages = [], excludePackages = [], useMostCommon = false } = {} | ||
{ includeScopes = [], includePackages = [], excludePackages = [], useMostCommon = false } = {} | ||
) => { | ||
const json = parseYarnLock(yarnLock); | ||
getDuplicatedPackages(json, { includePackages, excludePackages, useMostCommon }).forEach( | ||
({ bestVersion, name, versions, requestedVersion }) => { | ||
json[`${name}@${requestedVersion}`] = versions[bestVersion].pkg; | ||
} | ||
); | ||
getDuplicatedPackages(json, { | ||
includeScopes, | ||
includePackages, | ||
excludePackages, | ||
useMostCommon, | ||
}).forEach(({ bestVersion, name, versions, requestedVersion }) => { | ||
json[`${name}@${requestedVersion}`] = versions[bestVersion].pkg; | ||
}); | ||
return lockfile.stringify(json); | ||
}; |
{ | ||
"name": "yarn-deduplicate", | ||
"version": "2.0.0", | ||
"version": "2.1.0", | ||
"bin": "./cli.js", | ||
@@ -16,3 +16,3 @@ "description": "Deduplication tool for yarn.lock files", | ||
"scripts": { | ||
"test": "eslint . && jest", | ||
"test": "jest", | ||
"lint": "eslint ." | ||
@@ -23,3 +23,3 @@ }, | ||
"license": "Apache-2.0", | ||
"author": "Sergio Cinos <scinos@atlassian.com>", | ||
"author": "Sergio Cinos <sergio.cinos@gmail.com>", | ||
"main": "index.js", | ||
@@ -32,24 +32,23 @@ "repository": { | ||
"node": ">=10", | ||
"yarn": "^1.0.0" | ||
"yarn": "^1.22.4" | ||
}, | ||
"dependencies": { | ||
"@yarnpkg/lockfile": "^1.1.0", | ||
"commander": "^4.1.1", | ||
"semver": "7.1.3" | ||
"commander": "^5.1.0", | ||
"semver": "^7.3.2" | ||
}, | ||
"devDependencies": { | ||
"eslint": "^6.8.0", | ||
"eslint-config-prettier": "^6.10.0", | ||
"eslint-plugin-jest": "^23.8.0", | ||
"eslint-plugin-prettier": "^3.0.0", | ||
"jest": "^25.1.0", | ||
"outdent": "0.7.0", | ||
"prettier": "^1.15.3" | ||
"eslint": "^7.4.0", | ||
"eslint-config-prettier": "^6.11.0", | ||
"eslint-plugin-jest": "^23.18.0", | ||
"eslint-plugin-prettier": "^3.1.4", | ||
"jest": "^26.1.0", | ||
"outdent": "^0.7.1", | ||
"prettier": "^2.0.5" | ||
}, | ||
"jest": { | ||
"testPathIgnorePatterns": [ | ||
"<rootDir>/node_modules", | ||
"<rootDir>/.history" | ||
"<rootDir>/node_modules" | ||
] | ||
} | ||
} |
@@ -1,3 +0,4 @@ | ||
Builds: [![CircleCI](https://circleci.com/gh/atlassian/yarn-deduplicate.svg?style=svg)](https://circleci.com/gh/atlassian/yarn-deduplicate) | ||
Builds: ![Node.js CI](https://github.com/atlassian/yarn-deduplicate/workflows/Node.js%20CI/badge.svg) | ||
# Yarn deduplicate | ||
@@ -98,2 +99,4 @@ | ||
`--strategy <strategy>` | ||
`highest` | ||
@@ -142,3 +145,3 @@ It will try to use the highest installed version. For example, with the following `yarn.lock`: | ||
`--packages <package1> <package2> <packageN>` | ||
`--packages <package1>,<package2>,<packageN>` | ||
@@ -150,2 +153,22 @@ Receives a list of packages to deduplicate. It will ignore any other duplicated package not in the | ||
`--scopes <scope1>,<scope2>,<scopeN>` | ||
Receives a list of scopes to deduplicate. It will ignore any other duplicated package not in the | ||
list. This option is recommended when deduplicating a large number of inter-dependent packages | ||
from a single scope, such as @babel. This will allow for a more controlled and progressive | ||
deduplication of `yarn.lock` without specifying each package individually. | ||
### Usage in CI | ||
This tool can be used as part of a CI workflow. Adding the flag `--fail` will force the process to | ||
exit with status 1 if there are duplicated packages. Example: | ||
```bash | ||
# Print the list of duplicated packages and exit with status 1 | ||
yarn-deduplicate --list --fail | ||
# Deduplicate yarn.lock and exit with status 1 if changes were required | ||
yarn-deduplicate --fail | ||
``` | ||
--- | ||
@@ -190,3 +213,2 @@ | ||
### Limit packages to deduplicate yarn.lock | ||
@@ -202,3 +224,2 @@ ```bash | ||
## Contributors | ||
@@ -228,4 +249,4 @@ | ||
* [CLA for corporate contributors](https://na2.docusign.net/Member/PowerFormSigning.aspx?PowerFormId=e1c17c66-ca4d-4aab-a953-2c231af4a20b) | ||
* [CLA for individuals](https://na2.docusign.net/Member/PowerFormSigning.aspx?PowerFormId=3f94fbdc-2fbe-46ac-b14c-5d152700ae5d) | ||
* [CLA for corporate contributors](https://opensource.atlassian.com/corporate) | ||
* [CLA for individuals](https://opensource.atlassian.com/individual) | ||
@@ -232,0 +253,0 @@ ## License |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
251
1
1
35401
8
231
+ Addedcommander@5.1.0(transitive)
+ Addedsemver@7.6.3(transitive)
- Removedcommander@4.1.1(transitive)
- Removedsemver@7.1.3(transitive)
Updatedcommander@^5.1.0
Updatedsemver@^7.3.2