Skip to content

Commit 363db96

Browse files
authored
feat: experimental yarn2+ support (#5701)
1 parent 1c27c2b commit 363db96

16 files changed

+211
-14
lines changed

lib/base-package-manager.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ export abstract class BasePackageManager implements INodePackageManager {
130130
protected getFlagsString(config: any, asArray: boolean): any {
131131
const array: Array<string> = [];
132132
for (const flag in config) {
133-
if (flag === "global" && this.packageManager !== "yarn") {
133+
if (flag === "global" && this.packageManager !== "yarn" && this.packageManager !== "yarn2") {
134134
array.push(`--${flag}`);
135135
array.push(`${config[flag]}`);
136136
} else if (config[flag]) {
@@ -141,7 +141,12 @@ export abstract class BasePackageManager implements INodePackageManager {
141141
flag === "gradle" ||
142142
flag === "version_info"
143143
) {
144-
array.push(` ${flag}`);
144+
if (this.packageManager === "yarn2") {
145+
array.push(`--fields ${flag}`);
146+
} else {
147+
array.push(` ${flag}`);
148+
149+
}
145150
continue;
146151
}
147152
array.push(`--${flag}`);

lib/bootstrap.ts

+1
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ injector.requireCommand("setup|*", "./commands/setup");
219219
injector.requirePublic("packageManager", "./package-manager");
220220
injector.requirePublic("npm", "./node-package-manager");
221221
injector.requirePublic("yarn", "./yarn-package-manager");
222+
injector.requirePublic("yarn2", "./yarn2-package-manager");
222223
injector.requirePublic("pnpm", "./pnpm-package-manager");
223224
injector.requireCommand(
224225
"package-manager|*get",

lib/commands/preview.ts

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ export class PreviewCommand implements ICommand {
5151
installCommand = "npm install --save-dev @nativescript/preview-cli";
5252
break;
5353
case PackageManagers.yarn:
54+
case PackageManagers.yarn2:
5455
installCommand = "yarn add -D @nativescript/preview-cli";
5556
break;
5657
case PackageManagers.pnpm:

lib/common/dispatchers.ts

+1
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ export class CommandDispatcher implements ICommandDispatcher {
124124
updateCommand = "npm i -g nativescript";
125125
break;
126126
case PackageManagers.yarn:
127+
case PackageManagers.yarn2:
127128
updateCommand = "yarn global add nativescript";
128129
break;
129130
case PackageManagers.pnpm:

lib/constants.ts

+1
Original file line numberDiff line numberDiff line change
@@ -493,4 +493,5 @@ export enum PackageManagers {
493493
npm = "npm",
494494
pnpm = "pnpm",
495495
yarn = "yarn",
496+
yarn2 = "yarn2",
496497
}

lib/declarations.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -663,6 +663,7 @@ interface IOptions
663663
frameworkName: string;
664664
frameworkVersion: string;
665665
yarn: string;
666+
yarn2: string;
666667
pnpm: string;
667668
ipa: string;
668669
tsc: boolean;

lib/options.ts

+1
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ export class Options {
136136
ts: { type: OptionType.Boolean, hasSensitiveValue: false },
137137
typescript: { type: OptionType.Boolean, hasSensitiveValue: false },
138138
yarn: { type: OptionType.Boolean, hasSensitiveValue: false },
139+
yarn2: { type: OptionType.Boolean, hasSensitiveValue: false },
139140
pnpm: { type: OptionType.Boolean, hasSensitiveValue: false },
140141
androidTypings: { type: OptionType.Boolean, hasSensitiveValue: false },
141142
bundle: { type: OptionType.String, hasSensitiveValue: false },

lib/package-installation-manager.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ export class PackageInstallationManager implements IPackageInstallationManager {
7070
versions: true,
7171
});
7272

73-
return semver.maxSatisfying(data, versionRange);
73+
return semver.maxSatisfying(data?.versions ?? data, versionRange);
7474
}
7575

7676
public async getMaxSatisfyingVersionSafe(
@@ -320,9 +320,10 @@ export class PackageInstallationManager implements IPackageInstallationManager {
320320
packageName: string,
321321
version: string
322322
): Promise<string> {
323-
const data: any = await this.$packageManager.view(packageName, {
323+
let data: any = await this.$packageManager.view(packageName, {
324324
"dist-tags": true,
325325
});
326+
data = data?.["dist-tags"] ?? data;
326327
this.$logger.trace("Using version %s. ", data[version]);
327328

328329
return data[version];

lib/package-manager.ts

+4
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export class PackageManager implements IPackageManager {
2626
private $npm: INodePackageManager,
2727
private $options: IOptions,
2828
private $yarn: INodePackageManager,
29+
private $yarn2: INodePackageManager,
2930
private $pnpm: INodePackageManager,
3031
private $logger: ILogger,
3132
private $userSettingsService: IUserSettingsService,
@@ -164,6 +165,9 @@ export class PackageManager implements IPackageManager {
164165
if (pm === PackageManagers.yarn || this.$options.yarn) {
165166
this._packageManagerName = PackageManagers.yarn;
166167
return this.$yarn;
168+
} if (pm === PackageManagers.yarn2 || this.$options.yarn2) {
169+
this._packageManagerName = PackageManagers.yarn2;
170+
return this.$yarn2;
167171
} else if (pm === PackageManagers.pnpm || this.$options.pnpm) {
168172
this._packageManagerName = PackageManagers.pnpm;
169173
return this.$pnpm;

lib/services/android-plugin-build-service.ts

+8-3
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,7 @@ export class AndroidPluginBuildService implements IAndroidPluginBuildService {
375375
const allGradleTemplateFiles = path.join(gradleTemplatePath, "*");
376376
const buildGradlePath = path.join(pluginTempDir, "build.gradle");
377377
const settingsGradlePath = path.join(pluginTempDir, "settings.gradle");
378-
378+
379379
this.$fs.copyFile(allGradleTemplateFiles, pluginTempDir);
380380
const runtimeGradleVersions = await this.getRuntimeGradleVersions(
381381
projectDir
@@ -430,12 +430,13 @@ export class AndroidPluginBuildService implements IAndroidPluginBuildService {
430430
let runtimeVersion: string = null;
431431

432432
try {
433-
const result = await this.$packageManager.view(
433+
let result = await this.$packageManager.view(
434434
SCOPED_ANDROID_RUNTIME_NAME,
435435
{
436436
"dist-tags": true,
437437
}
438438
);
439+
result = result?.["dist-tags"] ?? result;
439440
runtimeVersion = result.latest;
440441
} catch (err) {
441442
this.$logger.trace(
@@ -529,6 +530,7 @@ export class AndroidPluginBuildService implements IAndroidPluginBuildService {
529530
`${SCOPED_ANDROID_RUNTIME_NAME}@${runtimeVersion}`,
530531
{ version_info: true }
531532
);
533+
output = output?.["version_info"] ?? output;
532534

533535
if (!output) {
534536
/**
@@ -543,6 +545,7 @@ export class AndroidPluginBuildService implements IAndroidPluginBuildService {
543545
`${SCOPED_ANDROID_RUNTIME_NAME}@${runtimeVersion}`,
544546
{ gradle: true }
545547
);
548+
output = output?.["gradle"] ?? output;
546549

547550
const { version, android } = output;
548551

@@ -709,7 +712,9 @@ export class AndroidPluginBuildService implements IAndroidPluginBuildService {
709712
projectDir: pluginBuildSettings.projectDir,
710713
});
711714
pluginBuildSettings.androidToolsInfo = this.$androidToolsInfo.getToolsInfo(
712-
{ projectDir: pluginBuildSettings.projectDir }
715+
{
716+
projectDir: pluginBuildSettings.projectDir,
717+
}
713718
);
714719
}
715720

lib/services/pacote-service.ts

+7-7
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ export class PacoteService implements IPacoteService {
2929
options?: IPacoteManifestOptions
3030
): Promise<any> {
3131
this.$logger.trace(
32-
`Calling pacoteService.manifest for packageName: '${packageName}' and options: ${options}`
32+
`Calling pacoteService.manifest for packageName: '${packageName}' and options: `,
33+
options
3334
);
3435
const manifestOptions: IPacoteBaseOptions = await this.getPacoteBaseOptions();
3536

@@ -39,13 +40,12 @@ export class PacoteService implements IPacoteService {
3940

4041
packageName = this.getRealPackageName(packageName);
4142
this.$logger.trace(
42-
`Calling pacote.manifest for packageName: ${packageName} and options: ${JSON.stringify(
43-
manifestOptions,
44-
null,
45-
2
46-
)}`
43+
`Calling pacote.manifest for packageName: ${packageName} and manifestOptions:`,
44+
manifestOptions
4745
);
48-
const result = pacote.manifest(packageName, manifestOptions);
46+
const result = await pacote.manifest(packageName, manifestOptions);
47+
48+
this.$logger.trace("pacote.manifest result:", result);
4949

5050
return result;
5151
}

lib/yarn2-package-manager.ts

+168
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
import * as path from "path";
2+
import * as _ from "lodash";
3+
import { BasePackageManager } from "./base-package-manager";
4+
import { exported } from "./common/decorators";
5+
import {
6+
INodePackageManagerInstallOptions,
7+
INpmInstallResultInfo,
8+
INpmsResult,
9+
} from "./declarations";
10+
import {
11+
IChildProcess,
12+
IErrors,
13+
IFileSystem,
14+
IHostInfo,
15+
Server,
16+
IDictionary,
17+
} from "./common/declarations";
18+
import { injector } from "./common/yok";
19+
20+
export class Yarn2PackageManager extends BasePackageManager {
21+
private $hostInfo_: IHostInfo;
22+
constructor(
23+
$childProcess: IChildProcess,
24+
private $errors: IErrors,
25+
$fs: IFileSystem,
26+
$hostInfo: IHostInfo,
27+
private $httpClient: Server.IHttpClient,
28+
private $logger: ILogger,
29+
$pacoteService: IPacoteService
30+
) {
31+
super($childProcess, $fs, $hostInfo, $pacoteService, "yarn2");
32+
this.$hostInfo_ = $hostInfo;
33+
}
34+
35+
protected getPackageManagerExecutableName(): string {
36+
let executableName = "yarn";
37+
38+
if (this.$hostInfo_.isWindows) {
39+
executableName += ".cmd";
40+
}
41+
42+
return executableName;
43+
}
44+
45+
@exported("yarn2")
46+
public async install(
47+
packageName: string,
48+
pathToSave: string,
49+
config: INodePackageManagerInstallOptions
50+
): Promise<INpmInstallResultInfo> {
51+
if (config.disableNpmInstall) {
52+
return;
53+
}
54+
if (config.ignoreScripts) {
55+
config["ignore-scripts"] = true;
56+
}
57+
58+
const packageJsonPath = path.join(pathToSave, "package.json");
59+
const jsonContentBefore = this.$fs.readJson(packageJsonPath);
60+
61+
// remove unsupported flags
62+
// todo: refactor all package managers to map typed flags to the actual flags
63+
const cleanedConfig = _.omit(config, ["save-dev", "save-exact"]);
64+
65+
const flags = this.getFlagsString(cleanedConfig, true);
66+
let params = [];
67+
const isInstallingAllDependencies = packageName === pathToSave;
68+
if (!isInstallingAllDependencies) {
69+
params.push("add", packageName);
70+
}
71+
72+
params = params.concat(flags);
73+
const cwd = pathToSave;
74+
75+
try {
76+
const result = await this.processPackageManagerInstall(
77+
packageName,
78+
params,
79+
{ cwd, isInstallingAllDependencies }
80+
);
81+
return result;
82+
} catch (e) {
83+
this.$fs.writeJson(packageJsonPath, jsonContentBefore);
84+
throw e;
85+
}
86+
}
87+
88+
@exported("yarn2")
89+
public uninstall(
90+
packageName: string,
91+
config?: IDictionary<string | boolean>,
92+
cwd?: string
93+
): Promise<string> {
94+
const flags = this.getFlagsString(config, false);
95+
return this.$childProcess.exec(`yarn remove ${packageName} ${flags}`, {
96+
cwd,
97+
});
98+
}
99+
100+
@exported("yarn2")
101+
public async view(packageName: string, config: Object): Promise<any> {
102+
const wrappedConfig = _.extend({}, config, { json: true });
103+
104+
const flags = this.getFlagsString(wrappedConfig, false);
105+
let viewResult: any;
106+
try {
107+
viewResult = await this.$childProcess.exec(
108+
`yarn npm info ${packageName} ${flags}`
109+
);
110+
} catch (e) {
111+
this.$errors.fail(e.message);
112+
}
113+
114+
try {
115+
return JSON.parse(viewResult);
116+
} catch (err) {
117+
this.$errors.fail(err.message);
118+
return null;
119+
}
120+
}
121+
122+
@exported("yarn2")
123+
public search(
124+
filter: string[],
125+
config: IDictionary<string | boolean>
126+
): Promise<string> {
127+
this.$errors.fail(
128+
"Method not implemented. Yarn does not support searching for packages in the registry."
129+
);
130+
return null;
131+
}
132+
133+
public async searchNpms(keyword: string): Promise<INpmsResult> {
134+
const httpRequestResult = await this.$httpClient.httpRequest(
135+
`https://api.npms.io/v2/search?q=keywords:${keyword}`
136+
);
137+
const result: INpmsResult = JSON.parse(httpRequestResult.body);
138+
return result;
139+
}
140+
141+
@exported("yarn2")
142+
public async getRegistryPackageData(packageName: string): Promise<any> {
143+
const registry = await this.$childProcess.exec(
144+
`yarn config get npmRegistryServer`
145+
);
146+
const url = `${registry.trim()}/${packageName}`;
147+
this.$logger.trace(
148+
`Trying to get data from yarn registry for package ${packageName}, url is: ${url}`
149+
);
150+
const responseData = (await this.$httpClient.httpRequest(url)).body;
151+
this.$logger.trace(
152+
`Successfully received data from yarn registry for package ${packageName}. Response data is: ${responseData}`
153+
);
154+
const jsonData = JSON.parse(responseData);
155+
this.$logger.trace(
156+
`Successfully parsed data from yarn registry for package ${packageName}.`
157+
);
158+
return jsonData;
159+
}
160+
161+
@exported("yarn2")
162+
public async getCachePath(): Promise<string> {
163+
const result = await this.$childProcess.exec(`yarn config get cacheFolder`);
164+
return result.toString().trim();
165+
}
166+
}
167+
168+
injector.register("yarn2", Yarn2PackageManager);

test/controllers/add-platform-controller.ts

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { AddPlaformErrors } from "../../lib/constants";
88
import { PackageManager } from "../../lib/package-manager";
99
import { NodePackageManager } from "../../lib/node-package-manager";
1010
import { YarnPackageManager } from "../../lib/yarn-package-manager";
11+
import { Yarn2PackageManager } from "../../lib/yarn2-package-manager";
1112
import { PnpmPackageManager } from "../../lib/pnpm-package-manager";
1213
import { MobileHelper } from "../../lib/common/mobile/mobile-helper";
1314

@@ -29,6 +30,7 @@ function createInjector(data?: { latestFrameworkVersion: string }) {
2930
injector.register("packageManager", PackageManager);
3031
injector.register("npm", NodePackageManager);
3132
injector.register("yarn", YarnPackageManager);
33+
injector.register("yarn2", Yarn2PackageManager);
3234
injector.register("pnpm", PnpmPackageManager);
3335
injector.register("userSettingsService", {
3436
getSettingValue: async (settingName: string): Promise<void> => undefined,

test/package-installation-manager.ts

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import * as HostInfoLib from "../lib/common/host-info";
66
import * as LoggerLib from "../lib/common/logger/logger";
77
import * as NpmLib from "../lib/node-package-manager";
88
import * as YarnLib from "../lib/yarn-package-manager";
9+
import * as Yarn2Lib from "../lib/yarn2-package-manager";
910
import * as PnpmLib from "../lib/pnpm-package-manager";
1011
import * as PackageManagerLib from "../lib/package-manager";
1112
import * as PackageInstallationManagerLib from "../lib/package-installation-manager";
@@ -46,6 +47,7 @@ function createTestInjector(): IInjector {
4647
});
4748
testInjector.register("npm", NpmLib.NodePackageManager);
4849
testInjector.register("yarn", YarnLib.YarnPackageManager);
50+
testInjector.register("yarn2", Yarn2Lib.Yarn2PackageManager);
4951
testInjector.register("pnpm", PnpmLib.PnpmPackageManager);
5052
testInjector.register("packageManager", PackageManagerLib.PackageManager);
5153
testInjector.register("projectConfigService", ProjectConfigServiceStub);

0 commit comments

Comments
 (0)