mirror of
https://github.com/sinanyuntu/trade-message-center.git
synced 2026-09-17 13:22:11 +08:00
103 lines
3.9 KiB
JavaScript
103 lines
3.9 KiB
JavaScript
// 读取并校验 workspace 根版本
|
|
|
|
import { existsSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
import { resolve } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const workspaceDir = resolve(fileURLToPath(new URL(".", import.meta.url)), "..");
|
|
const packageFileName = "package.json";
|
|
const packageVersionPattern = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/u;
|
|
const MAX_CHROME_VERSION_COMPONENT = 65535;
|
|
const releaseTypes = new Set(["major", "minor", "patch"]);
|
|
|
|
const readJsonFile = (filePath) => {
|
|
let source;
|
|
try {
|
|
source = readFileSync(filePath, "utf8");
|
|
} catch (error) {
|
|
throw new Error(`Unable to read ${filePath}`, { cause: error });
|
|
}
|
|
|
|
try {
|
|
return JSON.parse(source);
|
|
} catch (error) {
|
|
throw new Error(`Invalid JSON in ${filePath}`, { cause: error });
|
|
}
|
|
};
|
|
|
|
const workspacePackageDirectories = ["apps", "packages"];
|
|
|
|
/** 按 workspace 根目录顺序枚举可参与版本镜像的 package。 */
|
|
export const listWorkspacePackagePaths = (rootDir = workspaceDir) =>
|
|
workspacePackageDirectories.flatMap((directory) => {
|
|
const directoryPath = resolve(rootDir, directory);
|
|
if (!existsSync(directoryPath)) return [];
|
|
return readdirSync(directoryPath, { withFileTypes: true })
|
|
.filter((entry) => entry.isDirectory())
|
|
.map((entry) => resolve(directoryPath, entry.name, "package.json"))
|
|
.filter((packagePath) => existsSync(packagePath))
|
|
.sort();
|
|
});
|
|
|
|
/** 校验同时满足 npm 与 Chrome Manifest 的三段版本。 */
|
|
export const validatePackageVersion = (version, source = "package.json") => {
|
|
if (typeof version !== "string" || version.length === 0) {
|
|
throw new Error(`${source} version must be a non-empty string`);
|
|
}
|
|
if (!packageVersionPattern.test(version)) {
|
|
throw new Error(
|
|
`${source} version must be a three-component numeric SemVer without prerelease or build metadata`,
|
|
);
|
|
}
|
|
|
|
const components = version.split(".").map(Number);
|
|
if (components.some((component) => component > MAX_CHROME_VERSION_COMPONENT)) {
|
|
throw new Error(`${source} version components must be between 0 and 65535`);
|
|
}
|
|
if (components.every((component) => component === 0)) {
|
|
throw new Error(`${source} version must not be 0.0.0`);
|
|
}
|
|
|
|
return version;
|
|
};
|
|
|
|
/** 递增符合 Chrome 限制的 workspace 发布版本。 */
|
|
export const bumpPackageVersion = (version, releaseType = "patch") => {
|
|
validatePackageVersion(version);
|
|
if (!releaseTypes.has(releaseType)) {
|
|
throw new Error("Release type must be major, minor, or patch");
|
|
}
|
|
|
|
const [major, minor, patch] = version.split(".").map(Number);
|
|
const nextVersion =
|
|
releaseType === "major"
|
|
? `${major + 1}.0.0`
|
|
: releaseType === "minor"
|
|
? `${major}.${minor + 1}.0`
|
|
: `${major}.${minor}.${patch + 1}`;
|
|
|
|
return validatePackageVersion(nextVersion, "Next package");
|
|
};
|
|
|
|
/** 读取唯一的 workspace 根版本事实源。 */
|
|
export const readRootPackageVersion = (rootDir = workspaceDir) => {
|
|
const packagePath = resolve(rootDir, packageFileName);
|
|
const manifest = readJsonFile(packagePath);
|
|
return validatePackageVersion(manifest?.version, `${packagePath}`);
|
|
};
|
|
|
|
/** 只更新 workspace 根版本这一唯一事实源。 */
|
|
export const writeRootPackageVersion = (version, rootDir = workspaceDir) => {
|
|
const packagePath = resolve(rootDir, packageFileName);
|
|
const manifest = readJsonFile(packagePath);
|
|
manifest.version = validatePackageVersion(version, `${packagePath}`);
|
|
writeFileSync(packagePath, `${JSON.stringify(manifest, null, 4)}\n`);
|
|
};
|
|
|
|
/** 输出供 CI 和 shell 命令消费的根版本。 */
|
|
const printPackageVersion = () => {
|
|
console.log(readRootPackageVersion());
|
|
};
|
|
|
|
if (process.argv[1] === fileURLToPath(import.meta.url)) printPackageVersion();
|