forked from KaotoIO/vscode-kaoto
-
Notifications
You must be signed in to change notification settings - Fork 0
/
plugin-list.js
127 lines (104 loc) · 4.49 KB
/
plugin-list.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
// Workaround for https://github.com/microsoft/vscode-vsce/issues/517
// taken from https://gist.githubusercontent.com/arendjr/415747652a8c79f12b005ce3cfb2f808/raw/a4a227070319e1d4c61e982b74153765d6338694/list-plugin.js
const fs = require('fs');
// Setup: Place this file in `.yarn/plugins/list-plugin.js` and the following
// to `.yarnrc.yml`:
//
// ```
// plugins:
// - path: .yarn/plugins/plugin-list.js
// ```
module.exports = {
name: 'plugin-list',
factory: (require) => {
const { BaseCommand } = require('@yarnpkg/cli');
const { Command, Option } = require('clipanion');
const { parseSyml } = require('@yarnpkg/parsers');
const { Manifest } = require('@yarnpkg/core');
class ListCommand extends BaseCommand {
static paths = [['list']];
static usage = Command.Usage({
description: 'Lists installed packages.',
});
prod = Option.Boolean('--prod', false);
json = Option.Boolean('--json', false);
manifest = new Manifest();
trees = [];
async execute() {
await this.manifest.loadFile(Manifest.fileName, {});
if (!this.prod || !this.json) {
throw new Error(
'This command can only be used with the --prod and --json ' +
'args to match the behavior required by VSCE. See: ' +
'https://github.com/microsoft/vscode-vsce/blob/main/src/npm.ts'
);
}
const packageJsonContents = fs.readFileSync('package.json', 'utf-8');
const { dependencies, resolutions } = JSON.parse(packageJsonContents);
const lockContents = fs.readFileSync('yarn.lock', 'utf-8');
const resolved = parseSyml(lockContents);
this.addDependencies(dependencies, resolved, resolutions);
const output = {
type: 'tree',
data: { type: 'list', trees: this.trees },
};
this.context.stdout.write(JSON.stringify(output));
}
addDependencies(dependencies, resolved, resolutions) {
for (const [packageName, versionSpecifier] of Object.entries(dependencies)) {
this.addDependency(packageName, versionSpecifier, resolved, resolutions);
}
}
addDependency(packageName, versionSpecifier, resolved, resolutions) {
const packageInfo = this.lookup(resolved, packageName, versionSpecifier, resolutions);
if (!packageInfo) {
throw new Error(`Cannot resolve "${packageName}" with version range "${versionSpecifier}"`);
}
const { version, dependencies } = packageInfo;
const name = `${packageName}@${version}`;
if (this.trees.find((tree) => tree.name === name)) {
return; // Dependency already added as part of another tree.
}
if (dependencies) {
const children = Object.entries(dependencies).map(([name, range]) => ({
name: `${name}@${range}`,
}));
this.trees.push({ name, children });
this.addDependencies(dependencies, resolved, resolutions);
} else {
this.trees.push({ name, children: [] });
}
}
/**
* @param resolved All the resolved dependencies as found in the lock file.
* @param packageName The package name to look up.
* @param versionSpecifier The package version range as declared in the package.json.
* @param resolutions The resolutions override as declared in the package.json.
*/
lookup(resolved, packageName, versionSpecifier, resolutions) {
const dependencyKey = this.getLockFileKey(packageName, versionSpecifier, resolutions);
const packageInfo = resolved[dependencyKey];
if (packageInfo) {
return packageInfo;
}
// Fall back to slower iteration-based lookup for combined keys.
for (const [key, packageInfo] of Object.entries(resolved)) {
if (key.split(',').some((key) => key.trim() === dependencyKey)) {
return packageInfo;
}
}
}
getLockFileKey(packageName, versionSpecifier, resolutions) {
// If the package name is in the resolutions field, use the version from there.
const resolvedVersionSpecifier = resolutions[packageName] ?? versionSpecifier;
// If the version field contains a URL, don't attempt to use the NPM registry
return resolvedVersionSpecifier.includes(':')
? `${packageName}@${resolvedVersionSpecifier}`
: `${packageName}@npm:${resolvedVersionSpecifier}`;
}
}
return {
commands: [ListCommand],
};
},
};