yarn-why-duplicate 1.0.0 → 1.2.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/.editorconfig ADDED
@@ -0,0 +1,13 @@
1
+ # EditorConfig helps developers define and maintain consistent
2
+ # coding styles between different editors and IDEs
3
+ # http://editorconfig.org
4
+
5
+ root = true
6
+
7
+ [*]
8
+ indent_style = space
9
+ indent_size = 2
10
+ end_of_line = lf
11
+ charset = utf-8
12
+ trim_trailing_whitespace = true
13
+ insert_final_newline = true
package/.eslintrc.json ADDED
@@ -0,0 +1,4 @@
1
+ {
2
+ "root": true,
3
+ "extends": ["@pob/eslint-config/root-module"]
4
+ }
@@ -0,0 +1,2 @@
1
+ CHANGELOG.md
2
+ /.yarn/**/*
package/CHANGELOG.md ADDED
@@ -0,0 +1,30 @@
1
+ # Changelog
2
+
3
+ ## [1.2.0](https://github.com/christophehurpeau/yarn-why-duplicate/compare/v1.1.0...v1.2.0) (2023-04-25)
4
+
5
+
6
+ ### Features
7
+
8
+ * force 1.2.0 ([72688d8](https://github.com/christophehurpeau/yarn-why-duplicate/commit/72688d83af568ebb83fe8ee261b6ac18adf44f0e))
9
+
10
+ ## [1.1.0](https://github.com/christophehurpeau/yarn-why-duplicate/compare/v1.0.0...v1.1.0) (2023-04-25)
11
+
12
+
13
+ ### Features
14
+
15
+ * force 1.1.0 ([d333635](https://github.com/christophehurpeau/yarn-why-duplicate/commit/d3336353ffe709910577310116dab70dd8f25f5c))
16
+
17
+ ## 1.0.0 (2023-04-25)
18
+
19
+
20
+ ### Features
21
+
22
+ * **deps:** update dependency semver to v7.5.0 ([#4](https://github.com/christophehurpeau/yarn-why-duplicate/issues/4)) ([f9eee30](https://github.com/christophehurpeau/yarn-why-duplicate/commit/f9eee30e153dea0dc8dabb69b2484dd6dd3be0dc))
23
+ * first commit ([c3a6354](https://github.com/christophehurpeau/yarn-why-duplicate/commit/c3a6354ae139baffc75c4582af439db4f496329c))
24
+
25
+
26
+ ### Bug Fixes
27
+
28
+ * add try/catch on compareRange ([357eed7](https://github.com/christophehurpeau/yarn-why-duplicate/commit/357eed715012b156d4abd447f9d7232fdc066d72))
29
+ * use virtuals ([340d56a](https://github.com/christophehurpeau/yarn-why-duplicate/commit/340d56a8abc6c81b9973ed5ecb80604e6905990b))
30
+ * virtual child ([8798439](https://github.com/christophehurpeau/yarn-why-duplicate/commit/8798439ccf4f7ee40292ce9fb734e573a37c1d71))
package/README.md ADDED
@@ -0,0 +1,22 @@
1
+ <h3 align="center">
2
+ yarn-why-duplicate
3
+ </h3>
4
+
5
+ <p align="center">
6
+ Simple bin to know why a package is duplicated
7
+ </p>
8
+
9
+ <p align="center">
10
+ <a href="https://npmjs.org/package/yarn-why-duplicate"><img src="https://img.shields.io/npm/v/yarn-why-duplicate.svg?style=flat-square"></a>
11
+ <a href="https://npmjs.org/package/yarn-why-duplicate"><img src="https://img.shields.io/npm/dw/yarn-why-duplicate.svg?style=flat-square"></a>
12
+ <a href="https://npmjs.org/package/yarn-why-duplicate"><img src="https://img.shields.io/node/v/yarn-why-duplicate.svg?style=flat-square"></a>
13
+ <a href="https://npmjs.org/package/yarn-why-duplicate"><img src="https://img.shields.io/npm/types/yarn-why-duplicate.svg?style=flat-square"></a>
14
+ </p>
15
+
16
+ ## Usage
17
+
18
+ With yarn berry:
19
+
20
+ ```
21
+ yarn dlx yarn-why-duplicate <dependencyName>
22
+ ```
@@ -1,31 +1,22 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { execSync } from 'child_process';
3
+ import { displayDuplicates } from '../lib/displayDuplicates.js';
4
+ import { identifyDuplicates } from '../lib/identifyDuplicates.js';
5
+ import { readAndParseYarnWhy } from '../lib/readAndParseYarnWhy.js';
4
6
 
5
7
  const pkgName = process.argv.slice(2)[0];
6
8
 
7
- const yarnWhyResult = execSync(`yarn why ${pkgName} --json`, { encoding: 'utf8' })
8
- .split('\n')
9
- .filter(Boolean)
10
- .map((line) => JSON.parse(line));
11
-
12
- const groupByVersions = new Map();
9
+ const yarnWhyResult = readAndParseYarnWhy(pkgName);
13
10
 
14
11
  for (const line of yarnWhyResult) {
15
- for (const [key, child] of Object.entries(line.children)) {
16
- let values = groupByVersions.get(child.locator);
17
- if (!values) {
18
- values = [];
19
- groupByVersions.set(child.locator, values);
20
- }
21
- values.push(line.value);
12
+ if (line.type === 'error') {
13
+ console.error(`yarn error: ${line.data}`);
14
+ process.exit(1);
15
+ } else if (line.type === 'step') {
16
+ console.error('yarn 1 is not supported, use yarn berry (>=2)');
17
+ process.exit(1);
22
18
  }
23
19
  }
24
20
 
25
- for (const [version, values] of groupByVersions.entries()) {
26
- console.log(version);
27
- for (const value of values) {
28
- console.log(`- ${value}`);
29
- }
30
- console.log();
31
- }
21
+ const groupByVersions = identifyDuplicates(yarnWhyResult);
22
+ displayDuplicates(groupByVersions);
@@ -0,0 +1,4 @@
1
+ {
2
+ "root": true,
3
+ "extends": ["@pob/eslint-config/node-module"]
4
+ }
@@ -0,0 +1,32 @@
1
+ import { structUtils, Configuration } from '@yarnpkg/core';
2
+
3
+ export const displayDuplicates = (
4
+ groupByVersions,
5
+ log = console.log,
6
+ configuration = Configuration.create(process.cwd()),
7
+ ) => {
8
+ if (groupByVersions.length === 0) {
9
+ log('No version found');
10
+ return;
11
+ }
12
+
13
+ log(
14
+ `Found ${groupByVersions.length} version${
15
+ groupByVersions.length === 1 ? '' : 's'
16
+ }:`,
17
+ );
18
+ log();
19
+
20
+ for (const { descriptor, sources } of groupByVersions) {
21
+ log(structUtils.prettyDescriptor(configuration, descriptor));
22
+ for (const { source, descriptor } of sources.values()) {
23
+ log(
24
+ `- ${structUtils.prettyDescriptor(
25
+ configuration,
26
+ source,
27
+ )} (${structUtils.prettyDescriptor(configuration, descriptor)})`,
28
+ ); // TODO prettyDescriptor from structUtils
29
+ }
30
+ log();
31
+ }
32
+ };
@@ -0,0 +1,58 @@
1
+ import { structUtils } from '@yarnpkg/core';
2
+ import { compare as compareRange } from 'semver';
3
+
4
+ export const identifyDuplicates = (yarnWhyResult) => {
5
+ const groupByVersions = new Map();
6
+
7
+ for (const line of yarnWhyResult) {
8
+ const descriptor = structUtils.parseDescriptor(line.value);
9
+ const devirtualizedDescriptor = structUtils.isVirtualDescriptor(descriptor)
10
+ ? structUtils.devirtualizeDescriptor(descriptor)
11
+ : descriptor;
12
+
13
+ for (const [key, child] of Object.entries(line.children)) {
14
+ const childDescriptor = structUtils.parseDescriptor(key);
15
+ const devirtualizedChildDescriptor = structUtils.isVirtualDescriptor(
16
+ childDescriptor,
17
+ )
18
+ ? structUtils.devirtualizeDescriptor(childDescriptor)
19
+ : childDescriptor;
20
+
21
+ let byVersion = groupByVersions.get(
22
+ devirtualizedChildDescriptor.descriptorHash,
23
+ );
24
+ if (!byVersion) {
25
+ byVersion = {
26
+ descriptor: devirtualizedChildDescriptor,
27
+ sources: new Map(),
28
+ };
29
+ groupByVersions.set(
30
+ devirtualizedChildDescriptor.descriptorHash,
31
+ byVersion,
32
+ );
33
+ }
34
+
35
+ if (!byVersion.sources.has(devirtualizedDescriptor.descriptorHash)) {
36
+ byVersion.sources.set(devirtualizedDescriptor.descriptorHash, {
37
+ source: devirtualizedDescriptor, // source of the dependency
38
+ descriptor: structUtils.parseDescriptor(child.descriptor), // value parsed from package.json eg date-fns@npm:^1.29.0
39
+ locator: structUtils.parseLocator(child.locator), // value resolved by yarn eg date-fns@npm:1.30.1
40
+ });
41
+ }
42
+ }
43
+ }
44
+
45
+ const groups = [...groupByVersions.values()];
46
+ groups.sort((a, b) => {
47
+ const aRange = structUtils.parseRange(a.descriptor.range);
48
+ const bRange = structUtils.parseRange(b.descriptor.range);
49
+
50
+ try {
51
+ return -compareRange(aRange.selector, bRange.selector);
52
+ } catch {
53
+ return 0;
54
+ }
55
+ });
56
+
57
+ return groups;
58
+ };
@@ -0,0 +1,24 @@
1
+ import { spawnSync } from 'child_process';
2
+
3
+ export const parseYarnWhy = (content) => {
4
+ return content
5
+ .split('\n')
6
+ .filter(Boolean)
7
+ .map((line) => JSON.parse(line));
8
+ };
9
+
10
+ export const readAndParseYarnWhy = (pkgName) => {
11
+ const { stdout, stderr } = spawnSync('yarn', ['why', pkgName, '--json'], {
12
+ encoding: 'utf8',
13
+ stdio: 'pipe',
14
+ pwd: process.cwd(),
15
+ });
16
+ if (stderr) {
17
+ try {
18
+ return parseYarnWhy(stderr);
19
+ } catch {
20
+ throw new Error(`yarn failed: ${stderr}`);
21
+ }
22
+ }
23
+ return parseYarnWhy(stdout);
24
+ };
package/package.json CHANGED
@@ -1,26 +1,58 @@
1
1
  {
2
2
  "name": "yarn-why-duplicate",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "Simple bin to know why a package is duplicated",
5
- "main": "bin/yarn-why-duplicate.mjs",
6
- "scripts": {
7
- "test": "echo \"Error: no test specified\" && exit 1"
8
- },
9
- "repository": {
10
- "type": "git",
11
- "url": "git+https://github.com/christophehurpeau/yarn-why-duplicate.git"
12
- },
13
- "bin": {
14
- "yarn-why-duplicate": "./bin/yarn-why-duplicate.mjs"
15
- },
16
5
  "keywords": [
17
6
  "yarn",
18
7
  "berry"
19
8
  ],
20
- "author": "Christophe Hurpeau",
9
+ "author": "Christophe Hurpeau <christophe@hurpeau.com> (https://christophe.hurpeau.com)",
21
10
  "license": "ISC",
11
+ "repository": "https://github.com/christophehurpeau/yarn-why-duplicate.git",
12
+ "homepage": "https://github.com/christophehurpeau/yarn-why-duplicate#readme",
22
13
  "bugs": {
23
14
  "url": "https://github.com/christophehurpeau/yarn-why-duplicate/issues"
24
15
  },
25
- "homepage": "https://github.com/christophehurpeau/yarn-why-duplicate#readme"
16
+ "type": "module",
17
+ "engines": {
18
+ "node": ">=18.0.0"
19
+ },
20
+ "packageManager": "yarn@3.5.0",
21
+ "main": "./lib/index.js",
22
+ "exports": {
23
+ ".": "./lib/index.js",
24
+ "./package.json": "./package.json"
25
+ },
26
+ "sideEffects": false,
27
+ "bin": "./bin/yarn-why-duplicate.mjs",
28
+ "scripts": {
29
+ "checks": "node scripts/check-package.js",
30
+ "clean:build": "true",
31
+ "lint": "yarn run lint:prettier && yarn run lint:eslint",
32
+ "lint:eslint": "eslint --report-unused-disable-directives --resolve-plugins-relative-to . --quiet *.{js,cjs,mjs} lib bin scripts",
33
+ "lint:prettier": "pob-root-prettier --check .",
34
+ "lint:prettier:fix": "pob-root-prettier --write .",
35
+ "postinstallDev": "pob-root-postinstall",
36
+ "test": "node --test",
37
+ "test:coverage": "node --test",
38
+ "test:watch": "node --test --watch"
39
+ },
40
+ "prettier": "@pob/root/prettier-config",
41
+ "commitlint": {
42
+ "extends": [
43
+ "@pob/commitlint-config"
44
+ ]
45
+ },
46
+ "pob": {},
47
+ "dependencies": {
48
+ "@yarnpkg/core": "4.0.0-rc.42",
49
+ "semver": "7.5.0"
50
+ },
51
+ "devDependencies": {
52
+ "@pob/commitlint-config": "5.1.1",
53
+ "@pob/eslint-config": "50.0.4",
54
+ "@pob/root": "7.8.0",
55
+ "check-package-dependencies": "6.3.2",
56
+ "eslint": "8.39.0"
57
+ }
26
58
  }
@@ -0,0 +1,3 @@
1
+ import { createCheckPackage } from 'check-package-dependencies';
2
+
3
+ await createCheckPackage().checkRecommended({}).run();