Skip to content

Commit

Permalink
feat!: refactor detection to support multi versions (#42)
Browse files Browse the repository at this point in the history
* feat!: add yarn berry support

* improve multi version handling and extend fixture

---------

Co-authored-by: Pooya Parsa <pooya@pi0.io>
  • Loading branch information
Intevel and pi0 authored Mar 29, 2023
1 parent 6db58c4 commit 23dc546
Show file tree
Hide file tree
Showing 13 changed files with 183 additions and 83 deletions.
10 changes: 5 additions & 5 deletions src/api.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import { detectPackageManager } from "./detect";
import { runCorepack } from "./spawn";
import { PackageManagerName } from "./types";
import { PackageManager } from "./types";

export interface APICallOptions {
cwd: string;
packageManager: PackageManagerName;
packageManager: PackageManager;
silent: boolean;
}

export interface AddDependencyOptions extends Partial<APICallOptions> { dev?: boolean; }
export async function addDependency (name: string, _options: AddDependencyOptions = {}) {
const options = await _resolveOptions(_options);
const command = options.packageManager === "npm" ? "install" : "add";
const command = options.packageManager.name === "npm" ? "install" : "add";
const argv = [options.dev ? "-D" : ""].filter(Boolean);
await runCorepack(options.packageManager, [command, ...argv, name], { cwd: options.cwd, silent: options.silent });
await runCorepack(options.packageManager.command, [command, ...argv, name], { cwd: options.cwd, silent: options.silent });
return {};
}

Expand All @@ -22,7 +22,7 @@ async function _resolveOptions<T extends APICallOptions> (options: Partial<T> =
options.silent = options.silent ?? (process.env.NODE_ENV === "test");
if (!options.packageManager) {
const detected = await detectPackageManager(options.cwd);
options.packageManager = detected?.name || "npm";
options.packageManager = detected;
}
return options as T;
}
76 changes: 58 additions & 18 deletions src/detect.ts
Original file line number Diff line number Diff line change
@@ -1,47 +1,87 @@
import { existsSync } from "node:fs";
import { readFile } from "node:fs/promises";
import { resolve } from "node:path";
import { join, normalize } from "pathe";
import type { PackageManagerName } from "./types";
import type { PackageManager } from "./types";

const packageManagerLocks: Record<string, PackageManagerName> = {
"yarn.lock": "yarn",
"package-lock.json": "npm",
"pnpm-lock.yaml": "pnpm"
};
const packageManagers: PackageManager[] = [
{ name: "npm", command: "npm", lockFile: "package-lock.json" },
{
name: "pnpm",
command: "pnpm",
lockFile: "pnpm-lock.yaml",
files: ["pnpm-workspace.yaml"],
},
{
name: "yarn",
command: "yarn",
majorVersion: "1.0.0",
lockFile: "yarn.lock",
},
{
name: "yarn",
command: "yarn",
majorVersion: "2.0.0",
lockFile: "yarn.lock",
files: [".yarnrc.yml"],
},
];

export interface DetectPackageManagerOptions {
ignoreLockFile?: boolean;
ignorePackageJSON?: boolean;
}

export async function detectPackageManager (cwd: string, options: DetectPackageManagerOptions = {}): Promise<{ name: PackageManagerName, version?: string }> {
export async function detectPackageManager(
cwd: string,
options: DetectPackageManagerOptions = {}
): Promise<PackageManager | undefined> {
const detected = await findup(cwd, async (path) => {
// 1. Use `packageManager` field from package.json
if (!options.ignorePackageJSON) {
const packageJSONPath = join(path, "package.json");
if (existsSync(packageJSONPath)) {
const packageJSON = JSON.parse(await readFile(packageJSONPath, "utf8"));
if (packageJSON?.packageManager) {
const [name, version] = packageJSON.packageManager.split("@");
return { name, version };
const [name, version = "0.0.0"] =
packageJSON.packageManager.split("@");
const majorVersion = version.split(".")[0];
const packageManager =
packageManagers.find(
(pm) => pm.name === name && pm.majorVersion === majorVersion
) || packageManagers.find((pm) => pm.name === name);
return {
...packageManager,
name,
command: name,
version,
majorVersion,
};
}
}
}
// 2. Use implicit file detection
if (!options.ignoreLockFile) {
for (const lockFile in packageManagerLocks) {
if (existsSync(join(path, lockFile))) {
return { name: packageManagerLocks[lockFile] };
for (const packageManager of packageManagers) {
const detectionsFiles = [
packageManager.lockFile,
...(packageManager.files || []),
].filter(Boolean) as string[];
if (detectionsFiles.some((file) => existsSync(resolve(path, file)))) {
return {
...packageManager,
};
}
}
}
});
return {
name: "npm",
version: "latest", // TODO
...detected
};
return detected;
}

async function findup<T> (cwd: string, match: (path: string) => T | Promise<T>): Promise<T | undefined> {
async function findup<T>(
cwd: string,
match: (path: string) => T | Promise<T>
): Promise<T | undefined> {
const segments = normalize(cwd).split("/");
while (segments.length > 0) {
const path = segments.join("/");
Expand Down
11 changes: 10 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
@@ -1 +1,10 @@
export type PackageManagerName = "npm" | "yarn" | "pnpm"
export type PackageManagerName = "npm" | "yarn" | "pnpm";

export interface PackageManager {
name: PackageManagerName;
command: string;
version?: string;
majorVersion?: string;
lockFile?: string;
files?: string[];
}
28 changes: 14 additions & 14 deletions test/fixtures/npm/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions test/fixtures/npm/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
"private": true,
"version": "0.0.0",
"dependencies": {
"pathe": "^1.0.0"
"pathe": "^1.1.0"
},
"packageManager": "npm@9.1.3",
"devDependencies": {
"ufo": "^1.0.1"
"ufo": "^1.1.1"
}
}
2 changes: 1 addition & 1 deletion test/fixtures/pnpm/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"dependencies": {
"pathe": "^1.0.0"
},
"packageManager": "pnpm@7.18.0",
"packageManager": "pnpm@8.0.0",
"devDependencies": {
"ufo": "^1.0.1"
}
Expand Down
18 changes: 9 additions & 9 deletions test/fixtures/pnpm/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
"private": true,
"version": "0.0.0",
"dependencies": {
"pathe": "^1.0.0"
"pathe": "^1.1.0"
},
"packageManager": "yarn@1.22.19",
"devDependencies": {
"ufo": "^1.0.1"
"ufo": "^1.1.1"
}
}
13 changes: 13 additions & 0 deletions test/fixtures/yarn-berry/yarn.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1


pathe@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.0.tgz#e2e13f6c62b31a3289af4ba19886c230f295ec03"
integrity sha512-ODbEPR0KKHqECXW1GoxdDb+AZvULmXjVPy4rt+pGo2+TnjJTIPJQSVS6N63n8T2Ip+syHhbn52OewKicV0373w==

ufo@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.1.1.tgz#e70265e7152f3aba425bd013d150b2cdf4056d7c"
integrity sha512-MvlCc4GHrmZdAllBc0iUDowff36Q9Ndw/UzqmEKyrfSzokTd9ZCy1i+IIk5hrYKkjoYVQyNbrw7/F8XJ2rEwTg==
12 changes: 12 additions & 0 deletions test/fixtures/yarn-classic/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "fixture-yarn",
"private": true,
"version": "0.0.0",
"dependencies": {
"pathe": "^1.1.0"
},
"packageManager": "yarn@1.22.19",
"devDependencies": {
"ufo": "^1.1.1"
}
}
13 changes: 13 additions & 0 deletions test/fixtures/yarn-classic/yarn.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1


pathe@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.0.tgz#e2e13f6c62b31a3289af4ba19886c230f295ec03"
integrity sha512-ODbEPR0KKHqECXW1GoxdDb+AZvULmXjVPy4rt+pGo2+TnjJTIPJQSVS6N63n8T2Ip+syHhbn52OewKicV0373w==

ufo@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.1.1.tgz#e70265e7152f3aba425bd013d150b2cdf4056d7c"
integrity sha512-MvlCc4GHrmZdAllBc0iUDowff36Q9Ndw/UzqmEKyrfSzokTd9ZCy1i+IIk5hrYKkjoYVQyNbrw7/F8XJ2rEwTg==
13 changes: 0 additions & 13 deletions test/fixtures/yarn/yarn.lock

This file was deleted.

Loading

0 comments on commit 23dc546

Please sign in to comment.