@ts-dev-tools/core
Version:
TS dev tools Core
216 lines (215 loc) • 7.87 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.YarnPackageManagerAdapter = void 0;
const node_path_1 = require("node:path");
const AbstractPackageManagerAdapter_1 = require("./AbstractPackageManagerAdapter");
const PackageManagerType_1 = require("./PackageManagerType");
class YarnPackageManagerAdapter extends AbstractPackageManagerAdapter_1.AbstractPackageManagerAdapter {
async addDevPackage(packageName, dirPath) {
const isMonorepo = await this.isMonorepo(dirPath);
const { major } = await this.getVersion(PackageManagerType_1.PackageManagerType.yarn, dirPath);
const args = ["yarn", "add", "--dev"];
if (isMonorepo) {
args.push(major >= 2 ? "-W" : "--ignore-workspace-root-check");
}
args.push(packageName);
try {
await this.execCommand(args, dirPath, true);
}
catch (error) {
if (isMonorepo &&
typeof error === "string" &&
error.includes("Unsupported option name")) {
const retryArgs = args
.filter((arg) => arg !== "--ignore-workspace-root-check" && arg !== "-W")
.concat(major >= 2 ? "--ignore-workspace-root-check" : "-W");
await this.execCommand(retryArgs, dirPath, true);
return;
}
throw error;
}
}
async isMonorepo(dirPath) {
const { major } = await this.getVersion(PackageManagerType_1.PackageManagerType.yarn, dirPath);
const primary = major >= 2 ? "list" : "info";
const fallback = primary === "list" ? "info" : "list";
const yarnCommands = [
["yarn", "workspaces", primary, "--json"],
["yarn", "workspaces", fallback, "--json"],
];
let lastOutput;
let hasRecognizedOutput = false;
let hasSuccessfulCommand = false;
for (const args of yarnCommands) {
try {
const output = await this.execCommand(args, dirPath, true);
hasSuccessfulCommand = true;
lastOutput = output;
const analysis = this.analyzeYarnWorkspacesOutput(output);
if (analysis.hasEntries) {
return true;
}
if (analysis.hasRecognizedData) {
hasRecognizedOutput = true;
}
}
catch {
// Try next command
}
}
if (hasSuccessfulCommand && !hasRecognizedOutput) {
throw new Error(`Unexpected output from "yarn workspaces": ${lastOutput ?? "<empty>"}`);
}
return false;
}
async isPackageInstalled(packageName, dirPath) {
try {
const output = await this.execCommand(["yarn", "list", "--depth=1", "--json", "--pattern", packageName], dirPath, true);
if (this.yarnListOutputHasPackage(output, packageName)) {
return true;
}
}
catch {
// Fallback for Yarn versions that don't support `yarn list`
}
try {
const output = await this.execCommand(["yarn", "why", "--json", packageName], dirPath, true);
return this.yarnListOutputHasPackage(output, packageName);
}
catch {
return false;
}
}
async getNodeModulesPath(dirPath) {
return (0, node_path_1.join)(dirPath, "node_modules");
}
analyzeYarnWorkspacesOutput(output) {
let entries = this.parseJsonLines(output);
if (entries.length === 0) {
const jsonBlock = this.extractJsonBlock(output);
if (jsonBlock && typeof jsonBlock === "object") {
entries = [jsonBlock];
}
}
if (entries.length === 0) {
return { hasEntries: false, hasRecognizedData: false };
}
let workspaceListCount = 0;
let hasRecognizedData = false;
for (const entry of entries) {
if (!entry || typeof entry !== "object") {
continue;
}
const type = entry.type;
if (type === "error" || type === "warning") {
continue;
}
const data = entry.data ?? entry;
const parsedData = this.parseMaybeJsonString(data);
if (parsedData && typeof parsedData === "object") {
hasRecognizedData = true;
if (this.hasWorkspaceMap(parsedData)) {
return { hasEntries: true, hasRecognizedData };
}
if (this.isWorkspaceInfoMap(parsedData)) {
return { hasEntries: true, hasRecognizedData };
}
if (this.isWorkspaceListEntry(parsedData)) {
workspaceListCount += 1;
}
}
}
if (workspaceListCount > 0) {
return { hasEntries: true, hasRecognizedData };
}
return { hasEntries: false, hasRecognizedData };
}
parseMaybeJsonString(value) {
if (typeof value !== "string") {
return value;
}
try {
return JSON.parse(value);
}
catch {
return undefined;
}
}
extractJsonBlock(output) {
const start = output.indexOf("{");
const end = output.lastIndexOf("}");
if (start === -1 || end === -1 || end <= start) {
return undefined;
}
const candidate = output.slice(start, end + 1).trim();
if (candidate.length === 0) {
return undefined;
}
try {
return JSON.parse(candidate);
}
catch {
return undefined;
}
}
hasWorkspaceMap(value) {
if (!value || typeof value !== "object") {
return false;
}
const workspaces = value
.workspaces;
return !!workspaces && Object.keys(workspaces).length > 0;
}
isWorkspaceListEntry(value) {
if (!value || typeof value !== "object") {
return false;
}
const entry = value;
return (typeof entry.name === "string" &&
typeof entry.location === "string" &&
entry.location !== ".");
}
isWorkspaceInfoMap(value) {
if (!value || typeof value !== "object") {
return false;
}
const entries = Object.values(value);
if (entries.length === 0) {
return false;
}
return entries.some((entry) => {
if (!entry || typeof entry !== "object") {
return false;
}
const location = entry.location;
return (typeof location === "string" && location.length > 0 && location !== ".");
});
}
yarnListOutputHasPackage(output, packageName) {
const entries = this.parseJsonLines(output);
for (const entry of entries) {
if (!entry || typeof entry !== "object") {
continue;
}
const data = entry
.data;
const trees = data?.trees ?? entry.trees;
if (!trees) {
const children = entry
.children;
if (children) {
const childKeys = Object.keys(children);
if (childKeys.some((key) => key.startsWith(`${packageName}@`))) {
return true;
}
}
continue;
}
if (trees.some((tree) => tree.name.startsWith(`${packageName}@`))) {
return true;
}
}
return false;
}
}
exports.YarnPackageManagerAdapter = YarnPackageManagerAdapter;