@pinnacle0/pnpm-single-version 1.0.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/README.md +74 -0
- package/dist/checker/error-message.js +47 -0
- package/dist/checker/index.js +33 -0
- package/dist/command/checkDeps.js +62 -0
- package/dist/command/index.js +2 -0
- package/dist/command/install.js +51 -0
- package/dist/command/pnpmfile/.pnpmfile.cjs +7 -0
- package/dist/command/pnpmfile/hook.js +2 -0
- package/dist/command/setup.js +13 -0
- package/dist/constant.js +2 -0
- package/dist/error.js +37 -0
- package/dist/hook-bundle.js +29 -0
- package/dist/hook.js +55 -0
- package/dist/index.js +2 -0
- package/dist/parser-option/find-project-root.js +54 -0
- package/dist/parser-option/parse-option.js +62 -0
- package/dist/type.js +1 -0
- package/dist/util/array-value-map-Helper.js +18 -0
- package/dist/util/glob-util.js +16 -0
- package/dist/util/logger.js +19 -0
- package/dist/util/pipe.js +3 -0
- package/dist/util/pnpm-logger.js +15 -0
- package/package.json +47 -0
- package/src/checker/error-message.ts +58 -0
- package/src/checker/index.ts +46 -0
- package/src/command/checkDeps.ts +27 -0
- package/src/command/index.ts +2 -0
- package/src/command/install.ts +17 -0
- package/src/command/pnpmfile/.pnpmfile.cjs +7 -0
- package/src/command/pnpmfile/hook.ts +3 -0
- package/src/command/setup.ts +13 -0
- package/src/constant.ts +2 -0
- package/src/error.ts +43 -0
- package/src/hook.ts +23 -0
- package/src/index.ts +3 -0
- package/src/parser-option/find-project-root.ts +22 -0
- package/src/parser-option/parse-option.ts +38 -0
- package/src/type.ts +17 -0
- package/src/util/array-value-map-Helper.ts +19 -0
- package/src/util/glob-util.ts +25 -0
- package/src/util/logger.ts +24 -0
- package/src/util/pipe.ts +43 -0
- package/src/util/pnpm-logger.ts +15 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { minimatch } from "minimatch";
|
|
2
|
+
function isMatch(target, patterns) {
|
|
3
|
+
let matched = false;
|
|
4
|
+
for (const pattern of patterns){
|
|
5
|
+
matched = minimatch(target, pattern);
|
|
6
|
+
if (matched) return matched;
|
|
7
|
+
}
|
|
8
|
+
return matched;
|
|
9
|
+
}
|
|
10
|
+
function toRegex(patterns) {
|
|
11
|
+
return new RegExp(patterns.map((_)=>minimatch.makeRe(_)).filter((_)=>_ !== false).map((_)=>_.source).join("|"));
|
|
12
|
+
}
|
|
13
|
+
export const globUtil = Object.freeze({
|
|
14
|
+
isMatch,
|
|
15
|
+
toRegex
|
|
16
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { APP_NAME } from "../constant.js";
|
|
3
|
+
export function createCheckerMessage(message) {
|
|
4
|
+
return chalk.magenta(APP_NAME + ": ") + message;
|
|
5
|
+
}
|
|
6
|
+
function debug(object) {
|
|
7
|
+
console.info(object);
|
|
8
|
+
}
|
|
9
|
+
function message(value) {
|
|
10
|
+
console.info(createCheckerMessage(value));
|
|
11
|
+
}
|
|
12
|
+
function error(error) {
|
|
13
|
+
console.info(createCheckerMessage(`${chalk.bgRed(" ERROR ")} ${error.message}`));
|
|
14
|
+
}
|
|
15
|
+
export const Logger = Object.freeze({
|
|
16
|
+
debug,
|
|
17
|
+
message,
|
|
18
|
+
error
|
|
19
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { createCheckerMessage } from "./logger.js";
|
|
2
|
+
import { hookLogger } from "@pnpm/core-loggers";
|
|
3
|
+
function message(value) {
|
|
4
|
+
hookLogger.info({
|
|
5
|
+
message: createCheckerMessage(value),
|
|
6
|
+
prefix: ""
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
function error(error) {
|
|
10
|
+
hookLogger.error(error);
|
|
11
|
+
}
|
|
12
|
+
export const pnpmLogger = Object.freeze({
|
|
13
|
+
message,
|
|
14
|
+
error
|
|
15
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pinnacle0/pnpm-single-version",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"author": "Pinnacle",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/pinnacle0/frontend-libraries.git",
|
|
10
|
+
"directory": "packages/pnpm-single-version"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist",
|
|
14
|
+
"src"
|
|
15
|
+
],
|
|
16
|
+
"bin": {
|
|
17
|
+
"pnpm-single-version": "./dist/command/index.js",
|
|
18
|
+
"psv": "./dist/command/index.js"
|
|
19
|
+
},
|
|
20
|
+
"main": "dist/index.js",
|
|
21
|
+
"types": "dist/index.d.ts",
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@pnpm/find-workspace-dir": "1000.1.0",
|
|
24
|
+
"@pnpm/lockfile-file": "9.1.3",
|
|
25
|
+
"@pnpm/lockfile-utils": "11.0.4",
|
|
26
|
+
"@pnpm/logger": "1000.0.0",
|
|
27
|
+
"@pnpm/core-loggers": "1000.1.5",
|
|
28
|
+
"chalk": "5.4.1",
|
|
29
|
+
"commander": "10.0.1",
|
|
30
|
+
"find-up": "5.0.0",
|
|
31
|
+
"is-ci": "3.0.1",
|
|
32
|
+
"minimatch": "10.0.1"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@swc/cli": "0.1.62",
|
|
36
|
+
"@swc/core": "1.4.4",
|
|
37
|
+
"@types/is-ci": "^3.0.0",
|
|
38
|
+
"@types/minimatch": "5.1.2",
|
|
39
|
+
"@types/node": "22.10.1",
|
|
40
|
+
"esbuild": "0.18.15",
|
|
41
|
+
"@pinnacle0/devtool-util": "1.3.2"
|
|
42
|
+
},
|
|
43
|
+
"scripts": {
|
|
44
|
+
"build": "pnpm run test && pnpm tsx script/build.ts",
|
|
45
|
+
"test": "vitest --config config/vitest.config.ts --run"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import type {LoggerType, PackageInfo} from "../type.js";
|
|
3
|
+
|
|
4
|
+
const Emoji = {
|
|
5
|
+
sad: "😔",
|
|
6
|
+
cross: "❌",
|
|
7
|
+
happy: "🤗",
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const headerMessage = (num: number) => `${Emoji.cross} Found ${chalk.redBright(num)} Non-single version dependencies\n`;
|
|
11
|
+
|
|
12
|
+
const listVersionMessage = (name: string, infos: PackageInfo[]): string =>
|
|
13
|
+
`
|
|
14
|
+
- ${chalk.blueBright(name)} has ${chalk.redBright(infos.length)} distinct versions
|
|
15
|
+
${infos.map(_ => _.version + (_.peersSuffix ?? "")).join(", ")}
|
|
16
|
+
`;
|
|
17
|
+
|
|
18
|
+
const hintMessage = `
|
|
19
|
+
Well, here is what you can try to do:
|
|
20
|
+
Run ${chalk.blueBright(`$ pnpm why <dependency> -r `)} on project root to figure out the dependencies that contain conflicted version.
|
|
21
|
+
If the output is too long, try to redirect output, like ${chalk.blueBright(`$ pnpm why <dependency> -r > example.txt`)}
|
|
22
|
+
more about ${chalk.blueBright(`$ pnpm why`)} command, check out ${chalk.greenBright("https://pnpm.io/cli/why")}
|
|
23
|
+
`;
|
|
24
|
+
|
|
25
|
+
export function createErrorMessage(nonSingleVersionDeps: Map<string, PackageInfo[]>): string | null {
|
|
26
|
+
if (nonSingleVersionDeps.size === 0) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
let message = headerMessage(nonSingleVersionDeps.size);
|
|
30
|
+
for (const [name, infos] of nonSingleVersionDeps.entries()) {
|
|
31
|
+
message += listVersionMessage(name, infos);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
message += hintMessage;
|
|
35
|
+
|
|
36
|
+
return message;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const logInstallationInterruptedMessage = (logger: LoggerType) => {
|
|
40
|
+
logger.message(`${Emoji.sad} ${chalk.red("Installation Process interrupted.")}`);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
*
|
|
45
|
+
* @param {string | null} message
|
|
46
|
+
* @returns {boolean} Any Error message logged
|
|
47
|
+
*/
|
|
48
|
+
export const logErrorMessage =
|
|
49
|
+
(logger: LoggerType) =>
|
|
50
|
+
(message: string | null): boolean => {
|
|
51
|
+
if (!message) {
|
|
52
|
+
logger.message(`${Emoji.happy} All Passed!!!`);
|
|
53
|
+
return false;
|
|
54
|
+
} else {
|
|
55
|
+
logger.message(message);
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import {nameVerFromPkgSnapshot} from "@pnpm/lockfile-utils";
|
|
2
|
+
import {pipe} from "../util/pipe.js";
|
|
3
|
+
import {ArrayValueMapHelper} from "../util/array-value-map-Helper.js";
|
|
4
|
+
import {LockfilePackagesMissingError} from "../error.js";
|
|
5
|
+
import {globUtil} from "../util/glob-util.js";
|
|
6
|
+
import {createErrorMessage, logErrorMessage} from "./error-message.js";
|
|
7
|
+
import type {CheckerOptions, LoggerType, PackageInfo} from "../type.js";
|
|
8
|
+
import type {Lockfile, PackageSnapshots} from "@pnpm/lockfile-utils";
|
|
9
|
+
|
|
10
|
+
export const filterNonSingleVersionDependencies = (packageInfoMap: Map<string, PackageInfo[]>): Map<string, PackageInfo[]> => {
|
|
11
|
+
const nonSingleVersionPackageInfo = new Map<string, PackageInfo[]>();
|
|
12
|
+
|
|
13
|
+
for (const [path, snapshots] of packageInfoMap.entries()) {
|
|
14
|
+
if (snapshots.length > 1) {
|
|
15
|
+
nonSingleVersionPackageInfo.set(path, snapshots);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return nonSingleVersionPackageInfo;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const filterSnapshotNeededForChecking =
|
|
23
|
+
(snapshots: PackageSnapshots) =>
|
|
24
|
+
(depsPattern: string[]): Map<string, PackageInfo[]> => {
|
|
25
|
+
const matcher = globUtil.toRegex(depsPattern);
|
|
26
|
+
const packageInfos = ArrayValueMapHelper.create<PackageInfo>();
|
|
27
|
+
|
|
28
|
+
for (const [path, snapshot] of Object.entries(snapshots)) {
|
|
29
|
+
const nameVersionInfo: PackageInfo = nameVerFromPkgSnapshot(path, snapshot);
|
|
30
|
+
if (matcher.test(nameVersionInfo.name)) {
|
|
31
|
+
ArrayValueMapHelper.add(packageInfos, nameVersionInfo.name, nameVersionInfo);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return packageInfos;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const checker = (lockfile: Lockfile, options: CheckerOptions, logger: LoggerType) => {
|
|
39
|
+
if (!lockfile.packages) {
|
|
40
|
+
throw new LockfilePackagesMissingError();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
logger.message("Verify single version dependencies...");
|
|
44
|
+
|
|
45
|
+
return pipe(filterSnapshotNeededForChecking(lockfile.packages), filterNonSingleVersionDependencies, createErrorMessage, logErrorMessage(logger))(options.includes);
|
|
46
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import {readWantedLockfile} from "@pnpm/lockfile-file";
|
|
2
|
+
import {Logger} from "../util/logger.js";
|
|
3
|
+
import isCI from "is-ci";
|
|
4
|
+
import {checker} from "../checker/index.js";
|
|
5
|
+
import {parseOptions} from "../parser-option/parse-option.js";
|
|
6
|
+
import {findProjectRoot} from "../parser-option/find-project-root.js";
|
|
7
|
+
import chalk from "chalk";
|
|
8
|
+
import {logInstallationInterruptedMessage} from "../checker/error-message.js";
|
|
9
|
+
|
|
10
|
+
export const checkDeps = async () => {
|
|
11
|
+
try {
|
|
12
|
+
const projectRoot = await findProjectRoot();
|
|
13
|
+
const options = await parseOptions(projectRoot);
|
|
14
|
+
const lockfile = await readWantedLockfile(projectRoot, {ignoreIncompatible: false});
|
|
15
|
+
if (!lockfile) {
|
|
16
|
+
Logger.message(`Cannot find ${chalk.blueBright("pnpm-lock.yaml")}, try to run ${chalk.blueBright("$ pnpm install")} first`);
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
if (checker(lockfile, options, Logger) && options.failOnCI && isCI) {
|
|
20
|
+
logInstallationInterruptedMessage(Logger);
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
} catch (error) {
|
|
24
|
+
error instanceof Error ? Logger.error(error) : console.error("Unexpected error", error);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import {findProjectRoot} from "../parser-option/find-project-root.js";
|
|
4
|
+
import {Logger} from "../util/logger.js";
|
|
5
|
+
|
|
6
|
+
export const install = async () => {
|
|
7
|
+
const root = await findProjectRoot();
|
|
8
|
+
const psvDotFolderPath = path.join(root, ".psv");
|
|
9
|
+
if (fs.existsSync(psvDotFolderPath)) {
|
|
10
|
+
fs.rmSync(psvDotFolderPath, {force: true, recursive: true});
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
fs.mkdirSync(psvDotFolderPath);
|
|
14
|
+
fs.copyFileSync(path.join(import.meta.dirname, "../hook-bundle.js"), path.join(psvDotFolderPath, "hook.js"));
|
|
15
|
+
|
|
16
|
+
Logger.message("installed .psv");
|
|
17
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import {Command} from "commander";
|
|
2
|
+
import {checkDeps} from "./checkDeps.js";
|
|
3
|
+
import {install} from "./install.js";
|
|
4
|
+
import packageJSON from "../../package.json" with {type: "json"};
|
|
5
|
+
|
|
6
|
+
const program = new Command();
|
|
7
|
+
|
|
8
|
+
program.name("pnpm-single-version").alias("psv").version(packageJSON.version);
|
|
9
|
+
|
|
10
|
+
program.command("check", {isDefault: true}).description("Verify single version dependencies").action(checkDeps);
|
|
11
|
+
program.command("install").description("Install the checker file into .psv").action(install);
|
|
12
|
+
|
|
13
|
+
program.parse();
|
package/src/constant.ts
ADDED
package/src/error.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import {PACKAGE_JSON_OPTIONS_KEY} from "./constant.js";
|
|
3
|
+
|
|
4
|
+
export class ProjectRootError extends Error {
|
|
5
|
+
constructor() {
|
|
6
|
+
super("Can not find project root");
|
|
7
|
+
this.name = "ProjectRootError";
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class PackageJsonNotFoundError extends Error {
|
|
12
|
+
constructor() {
|
|
13
|
+
super(`Unable to read package.json of root project `);
|
|
14
|
+
this.name = "PackageJsonNotFoundError";
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class OptionNotFoundError extends Error {
|
|
19
|
+
constructor() {
|
|
20
|
+
super(`Unable to find option ${chalk.blueBright(`'${PACKAGE_JSON_OPTIONS_KEY}'`)} in package.json of root project `);
|
|
21
|
+
this.name = "OptionsNotFound";
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export class OptionSyntaxError extends Error {
|
|
26
|
+
constructor(message: string) {
|
|
27
|
+
super(`Invalid option: ${message}`);
|
|
28
|
+
this.name = "OptionSyntaxError";
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export class LockFileError extends Error {
|
|
33
|
+
constructor() {
|
|
34
|
+
super("Unable to read lock file");
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export class LockfilePackagesMissingError extends Error {
|
|
39
|
+
constructor() {
|
|
40
|
+
super(`'packages' in lockfile is missing`);
|
|
41
|
+
this.name = "LockfilePackagesMissingError";
|
|
42
|
+
}
|
|
43
|
+
}
|
package/src/hook.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type {Lockfile} from "@pnpm/lockfile-file";
|
|
2
|
+
import {checker} from "./checker/index.js";
|
|
3
|
+
import {parseOptions} from "./parser-option/parse-option.js";
|
|
4
|
+
import {findProjectRoot} from "./parser-option/find-project-root.js";
|
|
5
|
+
import {pnpmLogger} from "./util/pnpm-logger.js";
|
|
6
|
+
import {logInstallationInterruptedMessage} from "./checker/error-message.js";
|
|
7
|
+
import {logger} from "@pnpm/logger";
|
|
8
|
+
|
|
9
|
+
export const hook = async (lockfile: Lockfile) => {
|
|
10
|
+
try {
|
|
11
|
+
const logger = pnpmLogger;
|
|
12
|
+
const projectRoot = await findProjectRoot();
|
|
13
|
+
const options = await parseOptions(projectRoot);
|
|
14
|
+
if (checker(lockfile, options, logger)) {
|
|
15
|
+
logInstallationInterruptedMessage(logger);
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
return lockfile;
|
|
19
|
+
} catch (error) {
|
|
20
|
+
error instanceof Error ? logger.error(error) : console.error("Unexpected error", error);
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import {findWorkspaceDir} from "@pnpm/find-workspace-dir";
|
|
2
|
+
import {realpath} from "fs/promises";
|
|
3
|
+
import findUp from "find-up";
|
|
4
|
+
import {ProjectRootError} from "../error.js";
|
|
5
|
+
import path from "path";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Can find the root path of the package and monorepo
|
|
9
|
+
*/
|
|
10
|
+
export const findProjectRoot = async (): Promise<string> => {
|
|
11
|
+
let projectRoot = await findWorkspaceDir(process.cwd());
|
|
12
|
+
if (!projectRoot) {
|
|
13
|
+
const packageJSONPath = await findUp("package.json", {cwd: await realpath(process.cwd())});
|
|
14
|
+
projectRoot = packageJSONPath ? path.parse(packageJSONPath).dir : undefined;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (!projectRoot) {
|
|
18
|
+
throw new ProjectRootError();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return projectRoot;
|
|
22
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import fs from "fs/promises";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import {TextDecoder} from "util";
|
|
4
|
+
import {PACKAGE_JSON_OPTIONS_KEY} from "../constant.js";
|
|
5
|
+
import {OptionNotFoundError, OptionSyntaxError} from "../error.js";
|
|
6
|
+
import type {CheckerOptions} from "../type.js";
|
|
7
|
+
|
|
8
|
+
const validateOptions = (options: any): CheckerOptions => {
|
|
9
|
+
if (!options || typeof options !== "object") {
|
|
10
|
+
throw new OptionNotFoundError();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const o = options as Partial<CheckerOptions>;
|
|
14
|
+
|
|
15
|
+
if (!Array.isArray(o?.includes)) {
|
|
16
|
+
throw new OptionSyntaxError("Please make sure you have included single version dependencies in option.");
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (o?.failOnCI !== undefined && typeof o.failOnCI !== "boolean") {
|
|
20
|
+
throw new OptionSyntaxError("Please use boolean for failedOnCI option");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return options as CheckerOptions;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const parseOptions = async (projectRoot: string): Promise<CheckerOptions> => {
|
|
27
|
+
const packageJsonfile = await fs.readFile(path.join(projectRoot, "package.json"));
|
|
28
|
+
// @see https://github.dev/sindresorhus/load-json-file
|
|
29
|
+
const packageJSON = JSON.parse(new TextDecoder().decode(packageJsonfile));
|
|
30
|
+
|
|
31
|
+
const options = validateOptions((packageJSON as any)?.[PACKAGE_JSON_OPTIONS_KEY]);
|
|
32
|
+
|
|
33
|
+
if (options.failOnCI === undefined) {
|
|
34
|
+
options.failOnCI = true;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return options;
|
|
38
|
+
};
|
package/src/type.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type {nameVerFromPkgSnapshot} from "@pnpm/lockfile-utils";
|
|
2
|
+
|
|
3
|
+
export interface CheckerOptions {
|
|
4
|
+
includes: string[];
|
|
5
|
+
/**
|
|
6
|
+
* default: true
|
|
7
|
+
* In CI env, exit with status code 1 when checker found multiple version on specified dependencies.
|
|
8
|
+
*/
|
|
9
|
+
failOnCI?: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface LoggerType {
|
|
13
|
+
error(error: Error): void;
|
|
14
|
+
message(value: string): void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type PackageInfo = ReturnType<typeof nameVerFromPkgSnapshot>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
type ArrayValueMap<Value, Key = string> = Map<Key, Value[]>
|
|
2
|
+
|
|
3
|
+
function get<Value, Key = string>(map: ArrayValueMap<Value, Key>, key: Key): Value[] {
|
|
4
|
+
return map.get(key) ?? []
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function add<Value, Key = string>(map: ArrayValueMap<Value, Key>, key: Key, value: Value): void {
|
|
8
|
+
map.set(key, [...get(map, key), value])
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function create<Value, Key = string>(): Map<Key, Value[]> {
|
|
12
|
+
return new Map()
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const ArrayValueMapHelper = Object.freeze({
|
|
16
|
+
add,
|
|
17
|
+
get,
|
|
18
|
+
create
|
|
19
|
+
})
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import {minimatch} from "minimatch";
|
|
2
|
+
|
|
3
|
+
function isMatch(target: string, patterns: string[]): boolean {
|
|
4
|
+
let matched = false;
|
|
5
|
+
for (const pattern of patterns) {
|
|
6
|
+
matched = minimatch(target, pattern);
|
|
7
|
+
if (matched) return matched;
|
|
8
|
+
}
|
|
9
|
+
return matched;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function toRegex(patterns: string[]): RegExp {
|
|
13
|
+
return new RegExp(
|
|
14
|
+
patterns
|
|
15
|
+
.map(_ => minimatch.makeRe(_))
|
|
16
|
+
.filter((_): _ is RegExp => _ !== false)
|
|
17
|
+
.map(_ => _.source)
|
|
18
|
+
.join("|")
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const globUtil = Object.freeze({
|
|
23
|
+
isMatch,
|
|
24
|
+
toRegex,
|
|
25
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import {APP_NAME} from "../constant.js";
|
|
3
|
+
|
|
4
|
+
export function createCheckerMessage(message: string): string {
|
|
5
|
+
return chalk.magenta(APP_NAME + ": ") + message;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function debug(object: object) {
|
|
9
|
+
console.info(object);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function message(value: string) {
|
|
13
|
+
console.info(createCheckerMessage(value));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function error(error: Error) {
|
|
17
|
+
console.info(createCheckerMessage(`${chalk.bgRed(" ERROR ")} ${error.message}`));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const Logger = Object.freeze({
|
|
21
|
+
debug,
|
|
22
|
+
message,
|
|
23
|
+
error,
|
|
24
|
+
});
|
package/src/util/pipe.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
type OperationFunction<P, R> = (value: P) => R
|
|
2
|
+
|
|
3
|
+
export function pipe<P, A>(op1: OperationFunction<P, A>): (initial: P) => A
|
|
4
|
+
export function pipe<P, A, B>(op1: OperationFunction<P, A>, op2: OperationFunction<A, B>): (initial?: P) => B
|
|
5
|
+
export function pipe<P, A, B, C>(
|
|
6
|
+
op1: OperationFunction<P, A>,
|
|
7
|
+
op2: OperationFunction<A, B>,
|
|
8
|
+
op3: OperationFunction<B, C>
|
|
9
|
+
): (initial?: P) => C
|
|
10
|
+
export function pipe<P, A, B, C, D>(
|
|
11
|
+
op1: OperationFunction<P, A>,
|
|
12
|
+
op2: OperationFunction<A, B>,
|
|
13
|
+
op3: OperationFunction<B, C>,
|
|
14
|
+
op4: OperationFunction<C, D>
|
|
15
|
+
): (initial?: P) => D
|
|
16
|
+
export function pipe<P, A, B, C, D, E>(
|
|
17
|
+
op1: OperationFunction<P, A>,
|
|
18
|
+
op2: OperationFunction<A, B>,
|
|
19
|
+
op3: OperationFunction<B, C>,
|
|
20
|
+
op4: OperationFunction<C, D>,
|
|
21
|
+
op5: OperationFunction<D, E>
|
|
22
|
+
): (initial?: P) => E
|
|
23
|
+
export function pipe<P, A, B, C, D, E, F>(
|
|
24
|
+
op1: OperationFunction<P, A>,
|
|
25
|
+
op2: OperationFunction<A, B>,
|
|
26
|
+
op3: OperationFunction<B, C>,
|
|
27
|
+
op4: OperationFunction<C, D>,
|
|
28
|
+
op5: OperationFunction<D, E>,
|
|
29
|
+
op6: OperationFunction<E, F>
|
|
30
|
+
): (initial?: P) => F
|
|
31
|
+
export function pipe<P, A, B, C, D, E, F, G>(
|
|
32
|
+
op1: OperationFunction<P, A>,
|
|
33
|
+
op2: OperationFunction<A, B>,
|
|
34
|
+
op3: OperationFunction<B, C>,
|
|
35
|
+
op4: OperationFunction<C, D>,
|
|
36
|
+
op5: OperationFunction<D, E>,
|
|
37
|
+
op6: OperationFunction<E, F>,
|
|
38
|
+
op7: OperationFunction<F, G>
|
|
39
|
+
): (initial?: P) => G
|
|
40
|
+
export function pipe(...ops: Array<OperationFunction<any, any>>): (initial?: any) => any
|
|
41
|
+
export function pipe(...fns: any[]) {
|
|
42
|
+
return (initial: any) => fns.reduce((p, f) => f(p), initial)
|
|
43
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {createCheckerMessage} from "./logger.js";
|
|
2
|
+
import {hookLogger} from "@pnpm/core-loggers";
|
|
3
|
+
|
|
4
|
+
function message(value: string) {
|
|
5
|
+
hookLogger.info({message: createCheckerMessage(value), prefix: ""});
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function error(error: Error) {
|
|
9
|
+
hookLogger.error(error);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const pnpmLogger = Object.freeze({
|
|
13
|
+
message,
|
|
14
|
+
error,
|
|
15
|
+
});
|