bun-push 0.1.9
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/LICENSE +21 -0
- package/README.md +320 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +1459 -0
- package/dist/cli.js.map +22 -0
- package/dist/i18n/index.d.ts +21 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/dist/i18n/index.js +126 -0
- package/dist/i18n/locales/en.d.ts +119 -0
- package/dist/i18n/locales/en.d.ts.map +1 -0
- package/dist/i18n/locales/en.js +125 -0
- package/dist/i18n/locales/ja.d.ts +119 -0
- package/dist/i18n/locales/ja.d.ts.map +1 -0
- package/dist/i18n/locales/ja.js +125 -0
- package/dist/i18n/locales/ko.d.ts +119 -0
- package/dist/i18n/locales/ko.d.ts.map +1 -0
- package/dist/i18n/locales/ko.js +125 -0
- package/dist/i18n/locales/zh.d.ts +119 -0
- package/dist/i18n/locales/zh.d.ts.map +1 -0
- package/dist/i18n/locales/zh.js +125 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1114 -0
- package/dist/index.js.map +20 -0
- package/dist/index.test.d.ts +2 -0
- package/dist/index.test.d.ts.map +1 -0
- package/dist/index.test.js +90 -0
- package/dist/logo.d.ts +9 -0
- package/dist/logo.d.ts.map +1 -0
- package/dist/logo.js +58 -0
- package/dist/prompts.d.ts +42 -0
- package/dist/prompts.d.ts.map +1 -0
- package/dist/prompts.js +312 -0
- package/dist/publisher.d.ts +7 -0
- package/dist/publisher.d.ts.map +1 -0
- package/dist/publisher.js +110 -0
- package/dist/publisher.test.d.ts +2 -0
- package/dist/publisher.test.d.ts.map +1 -0
- package/dist/publisher.test.js +92 -0
- package/dist/types.d.ts +78 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +4 -0
- package/dist/utils/changelog.d.ts +32 -0
- package/dist/utils/changelog.d.ts.map +1 -0
- package/dist/utils/changelog.js +210 -0
- package/dist/utils/changelog.test.d.ts +2 -0
- package/dist/utils/changelog.test.d.ts.map +1 -0
- package/dist/utils/changelog.test.js +126 -0
- package/dist/utils/scripts.d.ts +13 -0
- package/dist/utils/scripts.d.ts.map +1 -0
- package/dist/utils/scripts.js +30 -0
- package/dist/utils/scripts.test.d.ts +2 -0
- package/dist/utils/scripts.test.d.ts.map +1 -0
- package/dist/utils/scripts.test.js +58 -0
- package/dist/utils/version.d.ts +24 -0
- package/dist/utils/version.d.ts.map +1 -0
- package/dist/utils/version.js +71 -0
- package/dist/utils/version.test.d.ts +2 -0
- package/dist/utils/version.test.d.ts.map +1 -0
- package/dist/utils/version.test.js +78 -0
- package/dist/utils/workspace.d.ts +14 -0
- package/dist/utils/workspace.d.ts.map +1 -0
- package/dist/utils/workspace.js +174 -0
- package/dist/utils/workspace.test.d.ts +2 -0
- package/dist/utils/workspace.test.d.ts.map +1 -0
- package/dist/utils/workspace.test.js +124 -0
- package/package.json +56 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace 相关工具函数
|
|
3
|
+
*/
|
|
4
|
+
import { readFileSync, existsSync, readdirSync, statSync } from "fs";
|
|
5
|
+
import { join, resolve } from "path";
|
|
6
|
+
import { t } from "../i18n";
|
|
7
|
+
/**
|
|
8
|
+
* 读取 package.json 文件
|
|
9
|
+
*/
|
|
10
|
+
function readPackageJson(path) {
|
|
11
|
+
const packageJsonPath = join(path, "package.json");
|
|
12
|
+
if (!existsSync(packageJsonPath)) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
const content = readFileSync(packageJsonPath, "utf-8");
|
|
17
|
+
return JSON.parse(content);
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* 检查是否是 monorepo 项目
|
|
25
|
+
*/
|
|
26
|
+
function isMonorepo(packageJson) {
|
|
27
|
+
// 检查是否有 workspaces 字段
|
|
28
|
+
if (packageJson.workspaces && Array.isArray(packageJson.workspaces)) {
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
// 检查是否有 workspaces.packages 字段(pnpm/npm 7+)
|
|
32
|
+
if (packageJson.workspaces?.packages && Array.isArray(packageJson.workspaces.packages)) {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* 解析 workspace 模式
|
|
39
|
+
*/
|
|
40
|
+
function parseWorkspacePatterns(workspaces) {
|
|
41
|
+
if (Array.isArray(workspaces)) {
|
|
42
|
+
return workspaces;
|
|
43
|
+
}
|
|
44
|
+
if (workspaces?.packages) {
|
|
45
|
+
return workspaces.packages;
|
|
46
|
+
}
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* 匹配包路径是否满足 workspace 模式
|
|
51
|
+
*/
|
|
52
|
+
function matchesPattern(path, pattern) {
|
|
53
|
+
// 简单的 glob 匹配实现
|
|
54
|
+
const regex = pattern
|
|
55
|
+
.replace(/\*\*/g, ".*")
|
|
56
|
+
.replace(/\*/g, "[^/]*")
|
|
57
|
+
.replace(/\//g, "\\/");
|
|
58
|
+
return new RegExp(`^${regex}$`).test(path);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* 扫描目录下的包
|
|
62
|
+
*/
|
|
63
|
+
function scanPackages(rootPath, patterns, found = []) {
|
|
64
|
+
const rootPackageJson = readPackageJson(rootPath);
|
|
65
|
+
if (!rootPackageJson) {
|
|
66
|
+
return found;
|
|
67
|
+
}
|
|
68
|
+
// 递归扫描目录
|
|
69
|
+
function scanDirectory(dir, depth = 0) {
|
|
70
|
+
if (depth > 10)
|
|
71
|
+
return; // 防止无限递归
|
|
72
|
+
try {
|
|
73
|
+
const entries = readdirSync(dir);
|
|
74
|
+
for (const entry of entries) {
|
|
75
|
+
const fullPath = join(dir, entry);
|
|
76
|
+
// 计算相对路径,使用 path.relative 确保跨平台兼容
|
|
77
|
+
const relativePath = fullPath.startsWith(rootPath)
|
|
78
|
+
? fullPath.slice(rootPath.length).replace(/^[/\\]/, "")
|
|
79
|
+
: fullPath;
|
|
80
|
+
// 跳过 node_modules 和隐藏目录
|
|
81
|
+
if (entry.startsWith(".") || entry === "node_modules") {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
const stat = statSync(fullPath);
|
|
85
|
+
if (stat.isDirectory()) {
|
|
86
|
+
// 检查是否匹配 workspace 模式
|
|
87
|
+
const matches = patterns.some((pattern) => matchesPattern(relativePath, pattern));
|
|
88
|
+
if (matches) {
|
|
89
|
+
const packageJson = readPackageJson(fullPath);
|
|
90
|
+
if (packageJson && packageJson.name) {
|
|
91
|
+
found.push({
|
|
92
|
+
name: packageJson.name,
|
|
93
|
+
version: packageJson.version || "0.0.0",
|
|
94
|
+
path: fullPath,
|
|
95
|
+
packageJson,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
// 继续递归扫描
|
|
101
|
+
scanDirectory(fullPath, depth + 1);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
// 忽略权限错误等
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
scanDirectory(rootPath);
|
|
111
|
+
return found;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* 获取 workspace 信息
|
|
115
|
+
*/
|
|
116
|
+
export function getWorkspaceInfo(workingDir = process.cwd()) {
|
|
117
|
+
const rootPath = resolve(workingDir);
|
|
118
|
+
const rootPackageJson = readPackageJson(rootPath);
|
|
119
|
+
if (!rootPackageJson) {
|
|
120
|
+
throw new Error(t("workspace.packageJsonNotFound"));
|
|
121
|
+
}
|
|
122
|
+
const isMonorepoProject = isMonorepo(rootPackageJson);
|
|
123
|
+
if (!isMonorepoProject) {
|
|
124
|
+
// 单仓库项目
|
|
125
|
+
return {
|
|
126
|
+
isMonorepo: false,
|
|
127
|
+
packages: [
|
|
128
|
+
{
|
|
129
|
+
name: rootPackageJson.name || "unknown",
|
|
130
|
+
version: rootPackageJson.version || "0.0.0",
|
|
131
|
+
path: rootPath,
|
|
132
|
+
packageJson: rootPackageJson,
|
|
133
|
+
},
|
|
134
|
+
],
|
|
135
|
+
rootPackageJson,
|
|
136
|
+
rootPath,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
// Monorepo 项目
|
|
140
|
+
const workspaceConfig = rootPackageJson.workspaces;
|
|
141
|
+
const patterns = parseWorkspacePatterns(workspaceConfig);
|
|
142
|
+
if (patterns.length === 0) {
|
|
143
|
+
throw new Error(t("workspace.workspacesConfigInvalid"));
|
|
144
|
+
}
|
|
145
|
+
const packages = scanPackages(rootPath, patterns);
|
|
146
|
+
// 如果没有找到包,至少包含根包
|
|
147
|
+
if (packages.length === 0 && rootPackageJson.name) {
|
|
148
|
+
packages.push({
|
|
149
|
+
name: rootPackageJson.name,
|
|
150
|
+
version: rootPackageJson.version || "0.0.0",
|
|
151
|
+
path: rootPath,
|
|
152
|
+
packageJson: rootPackageJson,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
isMonorepo: true,
|
|
157
|
+
packages,
|
|
158
|
+
rootPackageJson,
|
|
159
|
+
rootPath,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* 根据包名查找包信息
|
|
164
|
+
*/
|
|
165
|
+
export function findPackageByName(workspaceInfo, packageName) {
|
|
166
|
+
return workspaceInfo.packages.find((pkg) => pkg.name === packageName);
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* 根据路径查找包信息
|
|
170
|
+
*/
|
|
171
|
+
export function findPackageByPath(workspaceInfo, packagePath) {
|
|
172
|
+
const resolvedPath = resolve(packagePath);
|
|
173
|
+
return workspaceInfo.packages.find((pkg) => resolve(pkg.path) === resolvedPath);
|
|
174
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace.test.d.ts","sourceRoot":"","sources":["../../src/utils/workspace.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace 工具函数测试
|
|
3
|
+
*/
|
|
4
|
+
import { describe, test, expect, beforeEach, afterEach } from "bun:test";
|
|
5
|
+
import { mkdir, writeFile, rm } from "fs/promises";
|
|
6
|
+
import { join } from "path";
|
|
7
|
+
import { getWorkspaceInfo, findPackageByName, findPackageByPath } from "./workspace";
|
|
8
|
+
// 临时测试目录
|
|
9
|
+
const TEST_DIR = join(process.cwd(), ".test-tmp");
|
|
10
|
+
describe("Workspace 工具函数", () => {
|
|
11
|
+
beforeEach(async () => {
|
|
12
|
+
// 清理并创建测试目录
|
|
13
|
+
try {
|
|
14
|
+
await rm(TEST_DIR, { recursive: true, force: true });
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
// 忽略错误
|
|
18
|
+
}
|
|
19
|
+
await mkdir(TEST_DIR, { recursive: true });
|
|
20
|
+
});
|
|
21
|
+
afterEach(async () => {
|
|
22
|
+
// 清理测试目录
|
|
23
|
+
try {
|
|
24
|
+
await rm(TEST_DIR, { recursive: true, force: true });
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
// 忽略错误
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
describe("getWorkspaceInfo - 单仓库项目", () => {
|
|
31
|
+
test("应该正确识别单仓库项目", async () => {
|
|
32
|
+
const packageJson = {
|
|
33
|
+
name: "test-package",
|
|
34
|
+
version: "1.0.0",
|
|
35
|
+
};
|
|
36
|
+
await writeFile(join(TEST_DIR, "package.json"), JSON.stringify(packageJson, null, 2));
|
|
37
|
+
const workspaceInfo = getWorkspaceInfo(TEST_DIR);
|
|
38
|
+
expect(workspaceInfo.isMonorepo).toBe(false);
|
|
39
|
+
expect(workspaceInfo.packages).toHaveLength(1);
|
|
40
|
+
expect(workspaceInfo.packages[0].name).toBe("test-package");
|
|
41
|
+
expect(workspaceInfo.packages[0].version).toBe("1.0.0");
|
|
42
|
+
});
|
|
43
|
+
test("没有 package.json 应该抛出错误", () => {
|
|
44
|
+
expect(() => getWorkspaceInfo(join(TEST_DIR, "nonexistent"))).toThrow("未找到 package.json");
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
describe("getWorkspaceInfo - Monorepo 项目", () => {
|
|
48
|
+
test("应该正确识别数组格式的 workspaces", async () => {
|
|
49
|
+
const rootPackageJson = {
|
|
50
|
+
name: "root",
|
|
51
|
+
version: "1.0.0",
|
|
52
|
+
workspaces: ["packages/*"],
|
|
53
|
+
};
|
|
54
|
+
await writeFile(join(TEST_DIR, "package.json"), JSON.stringify(rootPackageJson, null, 2));
|
|
55
|
+
// 创建子包目录
|
|
56
|
+
const packagesDir = join(TEST_DIR, "packages");
|
|
57
|
+
await mkdir(packagesDir, { recursive: true });
|
|
58
|
+
const pkg1Dir = join(packagesDir, "pkg1");
|
|
59
|
+
await mkdir(pkg1Dir, { recursive: true });
|
|
60
|
+
await writeFile(join(pkg1Dir, "package.json"), JSON.stringify({ name: "pkg1", version: "1.0.0" }, null, 2));
|
|
61
|
+
const workspaceInfo = getWorkspaceInfo(TEST_DIR);
|
|
62
|
+
expect(workspaceInfo.isMonorepo).toBe(true);
|
|
63
|
+
expect(workspaceInfo.packages.length).toBeGreaterThanOrEqual(1);
|
|
64
|
+
});
|
|
65
|
+
test("应该正确识别对象格式的 workspaces", async () => {
|
|
66
|
+
const rootPackageJson = {
|
|
67
|
+
name: "root",
|
|
68
|
+
version: "1.0.0",
|
|
69
|
+
workspaces: {
|
|
70
|
+
packages: ["packages/*"],
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
await writeFile(join(TEST_DIR, "package.json"), JSON.stringify(rootPackageJson, null, 2));
|
|
74
|
+
const workspaceInfo = getWorkspaceInfo(TEST_DIR);
|
|
75
|
+
expect(workspaceInfo.isMonorepo).toBe(true);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
describe("findPackageByName", () => {
|
|
79
|
+
test("应该根据包名查找包", async () => {
|
|
80
|
+
const packageJson = {
|
|
81
|
+
name: "test-package",
|
|
82
|
+
version: "1.0.0",
|
|
83
|
+
};
|
|
84
|
+
await writeFile(join(TEST_DIR, "package.json"), JSON.stringify(packageJson, null, 2));
|
|
85
|
+
const workspaceInfo = getWorkspaceInfo(TEST_DIR);
|
|
86
|
+
const found = findPackageByName(workspaceInfo, "test-package");
|
|
87
|
+
expect(found).toBeDefined();
|
|
88
|
+
expect(found?.name).toBe("test-package");
|
|
89
|
+
});
|
|
90
|
+
test("找不到包应该返回 undefined", async () => {
|
|
91
|
+
const packageJson = {
|
|
92
|
+
name: "test-package",
|
|
93
|
+
version: "1.0.0",
|
|
94
|
+
};
|
|
95
|
+
await writeFile(join(TEST_DIR, "package.json"), JSON.stringify(packageJson, null, 2));
|
|
96
|
+
const workspaceInfo = getWorkspaceInfo(TEST_DIR);
|
|
97
|
+
const found = findPackageByName(workspaceInfo, "nonexistent");
|
|
98
|
+
expect(found).toBeUndefined();
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
describe("findPackageByPath", () => {
|
|
102
|
+
test("应该根据路径查找包", async () => {
|
|
103
|
+
const packageJson = {
|
|
104
|
+
name: "test-package",
|
|
105
|
+
version: "1.0.0",
|
|
106
|
+
};
|
|
107
|
+
await writeFile(join(TEST_DIR, "package.json"), JSON.stringify(packageJson, null, 2));
|
|
108
|
+
const workspaceInfo = getWorkspaceInfo(TEST_DIR);
|
|
109
|
+
const found = findPackageByPath(workspaceInfo, TEST_DIR);
|
|
110
|
+
expect(found).toBeDefined();
|
|
111
|
+
expect(found?.name).toBe("test-package");
|
|
112
|
+
});
|
|
113
|
+
test("找不到路径应该返回 undefined", async () => {
|
|
114
|
+
const packageJson = {
|
|
115
|
+
name: "test-package",
|
|
116
|
+
version: "1.0.0",
|
|
117
|
+
};
|
|
118
|
+
await writeFile(join(TEST_DIR, "package.json"), JSON.stringify(packageJson, null, 2));
|
|
119
|
+
const workspaceInfo = getWorkspaceInfo(TEST_DIR);
|
|
120
|
+
const found = findPackageByPath(workspaceInfo, "/nonexistent/path");
|
|
121
|
+
expect(found).toBeUndefined();
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "bun-push",
|
|
3
|
+
"version": "0.1.9",
|
|
4
|
+
"description": "一个支持 monorepo 和单仓库的 npm 包发布工具,支持 CLI 和 JS API 两种使用方式",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"private": false,
|
|
9
|
+
"bin": {
|
|
10
|
+
"bun-push": "./dist/cli.js"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "bun test && bun run build.config.ts",
|
|
17
|
+
"dev": "bun run --watch src/cli.ts",
|
|
18
|
+
"test": "bun test"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"npm",
|
|
22
|
+
"publish",
|
|
23
|
+
"monorepo",
|
|
24
|
+
"workspace",
|
|
25
|
+
"cli",
|
|
26
|
+
"release"
|
|
27
|
+
],
|
|
28
|
+
"author": "ccode",
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "https://github.com/ccode/bun-push.git"
|
|
33
|
+
},
|
|
34
|
+
"bugs": {
|
|
35
|
+
"url": "https://github.com/ccode/bun-push/issues"
|
|
36
|
+
},
|
|
37
|
+
"homepage": "https://github.com/ccode/bun-push#readme",
|
|
38
|
+
"engines": {
|
|
39
|
+
"bun": ">=1.3.0"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"prompts": "^2.4.2",
|
|
43
|
+
"chalk": "^5.3.0",
|
|
44
|
+
"ora": "^8.1.1",
|
|
45
|
+
"semver": "^7.7.1",
|
|
46
|
+
"zod": "^3.24.1",
|
|
47
|
+
"figlet": "^1.7.0"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@types/bun": "latest",
|
|
51
|
+
"@types/prompts": "^2.4.9",
|
|
52
|
+
"@types/semver": "^7.5.8",
|
|
53
|
+
"@types/figlet": "^1.7.0",
|
|
54
|
+
"typescript": "^5.7.3"
|
|
55
|
+
}
|
|
56
|
+
}
|