@reliverse/publish 2.2.7
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/dist/mod.d.ts +76 -0
- package/dist/mod.js +1251 -0
- package/package.json +29 -0
package/dist/mod.d.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { type BumpType } from "@reliverse/bump";
|
|
2
|
+
import type { BaseConfig } from "@reliverse/config/impl/core";
|
|
3
|
+
import { type PackageKind, type RegistryType } from "@reliverse/config/impl/publish";
|
|
4
|
+
export interface PackagePublishConfig {
|
|
5
|
+
enable?: boolean;
|
|
6
|
+
dryRun?: boolean;
|
|
7
|
+
tag?: string;
|
|
8
|
+
access?: "public" | "restricted";
|
|
9
|
+
otp?: string;
|
|
10
|
+
authType?: "web" | "legacy";
|
|
11
|
+
concurrency?: number;
|
|
12
|
+
verbose?: boolean;
|
|
13
|
+
bump?: "major" | "minor" | "patch" | "premajor" | "preminor" | "prepatch" | "prerelease";
|
|
14
|
+
bumpDisable?: boolean;
|
|
15
|
+
registry?: RegistryType;
|
|
16
|
+
kind?: PackageKind;
|
|
17
|
+
}
|
|
18
|
+
export interface PublishConfig extends BaseConfig {
|
|
19
|
+
global?: PackagePublishConfig;
|
|
20
|
+
packages?: Record<string, PackagePublishConfig>;
|
|
21
|
+
patterns?: Array<{
|
|
22
|
+
pattern: string;
|
|
23
|
+
config: PackagePublishConfig;
|
|
24
|
+
}>;
|
|
25
|
+
}
|
|
26
|
+
export interface DlerConfig {
|
|
27
|
+
publish?: PublishConfig;
|
|
28
|
+
}
|
|
29
|
+
export interface PublishOptions {
|
|
30
|
+
dryRun?: boolean;
|
|
31
|
+
tag?: string;
|
|
32
|
+
access?: "public" | "restricted";
|
|
33
|
+
otp?: string;
|
|
34
|
+
authType?: "web" | "legacy";
|
|
35
|
+
verbose?: boolean;
|
|
36
|
+
bump?: BumpType;
|
|
37
|
+
bumpDisable?: boolean;
|
|
38
|
+
concurrency?: number;
|
|
39
|
+
registry?: RegistryType;
|
|
40
|
+
kind?: PackageKind;
|
|
41
|
+
withNpmLogs?: boolean;
|
|
42
|
+
gzipLevel?: string;
|
|
43
|
+
ca?: string;
|
|
44
|
+
cafile?: string;
|
|
45
|
+
ignoreScripts?: boolean;
|
|
46
|
+
silent?: boolean;
|
|
47
|
+
noProgress?: boolean;
|
|
48
|
+
noSummary?: boolean;
|
|
49
|
+
bunRegistry?: string;
|
|
50
|
+
skipTip2FA?: boolean;
|
|
51
|
+
filter?: string | string[];
|
|
52
|
+
}
|
|
53
|
+
export type { PackageKind, RegistryType, } from "@reliverse/config/impl/publish";
|
|
54
|
+
export interface PublishResult {
|
|
55
|
+
success: boolean;
|
|
56
|
+
packageName: string;
|
|
57
|
+
packagePath: string;
|
|
58
|
+
version?: string;
|
|
59
|
+
error?: string;
|
|
60
|
+
warning?: string;
|
|
61
|
+
}
|
|
62
|
+
export interface PublishAllResult {
|
|
63
|
+
results: PublishResult[];
|
|
64
|
+
hasErrors: boolean;
|
|
65
|
+
successCount: number;
|
|
66
|
+
errorCount: number;
|
|
67
|
+
warningCount: number;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Publish a single package
|
|
71
|
+
*/
|
|
72
|
+
export declare function publishPackage(packagePath: string, options?: PublishOptions, bumpedVersions?: Map<string, string>): Promise<PublishResult>;
|
|
73
|
+
/**
|
|
74
|
+
* Publish all workspace packages
|
|
75
|
+
*/
|
|
76
|
+
export declare function publishAllPackages(cwd?: string, ignore?: string | string[], options?: PublishOptions): Promise<PublishAllResult>;
|
package/dist/mod.js
ADDED
|
@@ -0,0 +1,1251 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import { transformExportsForBuild } from "@reliverse/build";
|
|
3
|
+
import { bumpVersion, getNextVersion } from "@reliverse/bump";
|
|
4
|
+
import {
|
|
5
|
+
filterPackages,
|
|
6
|
+
findMonorepoRoot,
|
|
7
|
+
getWorkspacePackages,
|
|
8
|
+
loadDlerConfig
|
|
9
|
+
} from "@reliverse/config/impl/discovery";
|
|
10
|
+
import {
|
|
11
|
+
getPackagePublishConfig,
|
|
12
|
+
mergePublishOptions
|
|
13
|
+
} from "@reliverse/config/impl/publish";
|
|
14
|
+
import { re } from "@reliverse/relico";
|
|
15
|
+
import { logger } from "@reliverse/relinka";
|
|
16
|
+
import { readPackageJSON, writePackageJSON } from "@reliverse/typerso";
|
|
17
|
+
const THROW_2FA_ERROR = false;
|
|
18
|
+
let hasShown2FATip = false;
|
|
19
|
+
async function runBunPublishCommand(packagePath, args, verbose, withNpmLogs) {
|
|
20
|
+
try {
|
|
21
|
+
if (verbose) {
|
|
22
|
+
logger.debug(`Spawning bun publish command: bun ${args.join(" ")}`);
|
|
23
|
+
logger.debug(`Working directory: ${packagePath}`);
|
|
24
|
+
if (withNpmLogs) {
|
|
25
|
+
logger.debug(
|
|
26
|
+
"With npm logs enabled - output will be displayed directly"
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const proc = Bun.spawn(args, {
|
|
31
|
+
cwd: packagePath,
|
|
32
|
+
stdout: withNpmLogs ? "inherit" : "pipe",
|
|
33
|
+
stderr: withNpmLogs ? "inherit" : "pipe",
|
|
34
|
+
stdin: withNpmLogs ? "inherit" : "ignore",
|
|
35
|
+
// Allow input when showing logs, prevent hangs when hiding logs
|
|
36
|
+
env: process.env
|
|
37
|
+
// Explicitly pass environment variables
|
|
38
|
+
});
|
|
39
|
+
const timeout = 5 * 60 * 1e3;
|
|
40
|
+
let timeoutFired = false;
|
|
41
|
+
const timeoutId = setTimeout(() => {
|
|
42
|
+
timeoutFired = true;
|
|
43
|
+
proc.kill();
|
|
44
|
+
}, timeout);
|
|
45
|
+
if (verbose) {
|
|
46
|
+
logger.debug(`Set timeout of ${timeout / 1e3}s for publish command`);
|
|
47
|
+
}
|
|
48
|
+
let stdout = "";
|
|
49
|
+
let stderr = "";
|
|
50
|
+
if (withNpmLogs) {
|
|
51
|
+
const exitCode2 = await proc.exited;
|
|
52
|
+
clearTimeout(timeoutId);
|
|
53
|
+
if (verbose) {
|
|
54
|
+
logger.debug(`Command exited with code: ${exitCode2}`);
|
|
55
|
+
}
|
|
56
|
+
if (timeoutFired) {
|
|
57
|
+
if (verbose) {
|
|
58
|
+
logger.debug("Publish command timed out after 5 minutes");
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
stdout: "",
|
|
62
|
+
stderr: "Error: Publishing timed out after 5 minutes. This may indicate an authentication issue or network problem.",
|
|
63
|
+
exitCode: 1
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
return { stdout, stderr, exitCode: exitCode2 };
|
|
67
|
+
}
|
|
68
|
+
[stdout, stderr] = await Promise.all([
|
|
69
|
+
new Response(proc.stdout).text(),
|
|
70
|
+
new Response(proc.stderr).text()
|
|
71
|
+
]);
|
|
72
|
+
const exitCode = await proc.exited;
|
|
73
|
+
clearTimeout(timeoutId);
|
|
74
|
+
if (verbose) {
|
|
75
|
+
logger.debug(`Command exited with code: ${exitCode}`);
|
|
76
|
+
if (stdout) {
|
|
77
|
+
logger.debug(`Stdout length: ${stdout.length} bytes`);
|
|
78
|
+
}
|
|
79
|
+
if (stderr) {
|
|
80
|
+
logger.debug(`Stderr length: ${stderr.length} bytes`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (timeoutFired) {
|
|
84
|
+
if (verbose) {
|
|
85
|
+
logger.debug("Publish command timed out after 5 minutes");
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
stdout,
|
|
89
|
+
stderr: `${stderr}
|
|
90
|
+
|
|
91
|
+
Error: Publishing timed out after 5 minutes. This may indicate an authentication issue or network problem.`,
|
|
92
|
+
exitCode: 1
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
const combinedOutput = `${stdout}${stderr}`;
|
|
96
|
+
if (THROW_2FA_ERROR && combinedOutput.includes("This operation requires a one-time password.")) {
|
|
97
|
+
throw new Error(
|
|
98
|
+
"This operation requires a one-time password. You have 2FA enabled in your npm account. Please set NPM_CONFIG_TOKEN environment variable with your npm token."
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
if (combinedOutput.includes("failed to read OTP input") || combinedOutput.includes("use your security key for authentication")) {
|
|
102
|
+
const hasToken = !!(process.env.NPM_CONFIG_TOKEN || process.env.NPM_TOKEN);
|
|
103
|
+
if (hasToken) {
|
|
104
|
+
throw new Error(
|
|
105
|
+
"2FA authentication required. NPM_CONFIG_TOKEN is set but authentication failed. Please verify:\n 1. The token is valid and not expired\n 2. The token has publish permissions\n 3. Use --with-npm-logs flag to allow interactive OTP input if token authentication is not working"
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
throw new Error(
|
|
109
|
+
"2FA authentication required. The publish command cannot read OTP input when output is captured. Please either:\n 1. Set NPM_CONFIG_TOKEN environment variable with your npm token (note: .env files are not automatically loaded - export it in your shell or use a tool like dotenv-cli), or\n 2. Use --with-npm-logs flag to allow interactive OTP input."
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
return { stdout, stderr, exitCode };
|
|
113
|
+
} catch (error) {
|
|
114
|
+
if (verbose) {
|
|
115
|
+
logger.debug(
|
|
116
|
+
`Failed to spawn bun publish: ${error instanceof Error ? error.message : String(error)}`
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
throw new Error(
|
|
120
|
+
`Failed to spawn bun publish: ${error instanceof Error ? error.message : String(error)}`
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
function validateKindRegistryCombination(kind, registry, verbose) {
|
|
125
|
+
if (verbose) {
|
|
126
|
+
logger.debug(
|
|
127
|
+
`Validating kind-registry combination: kind=${kind ?? "undefined"}, registry=${registry ?? "undefined"}`
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
if (!kind) {
|
|
131
|
+
if (verbose) {
|
|
132
|
+
logger.debug("No kind specified, allowing any registry");
|
|
133
|
+
}
|
|
134
|
+
return { valid: true };
|
|
135
|
+
}
|
|
136
|
+
if (!registry) {
|
|
137
|
+
if (verbose) {
|
|
138
|
+
logger.debug("No registry specified, allowing any kind");
|
|
139
|
+
}
|
|
140
|
+
return { valid: true };
|
|
141
|
+
}
|
|
142
|
+
const allowedCombinations = {
|
|
143
|
+
library: ["npm", "jsr", "npm-jsr", "none"],
|
|
144
|
+
"browser-app": ["vercel", "none"],
|
|
145
|
+
"native-app": ["none"],
|
|
146
|
+
cli: ["npm", "none"]
|
|
147
|
+
};
|
|
148
|
+
const allowedRegistries = allowedCombinations[kind];
|
|
149
|
+
if (verbose) {
|
|
150
|
+
logger.debug(
|
|
151
|
+
`Allowed registries for kind "${kind}": ${allowedRegistries.join(", ")}`
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
if (!allowedRegistries.includes(registry)) {
|
|
155
|
+
if (verbose) {
|
|
156
|
+
logger.debug(
|
|
157
|
+
`Validation failed: registry "${registry}" not allowed for kind "${kind}"`
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
return {
|
|
161
|
+
valid: false,
|
|
162
|
+
error: `Package kind "${kind}" is not compatible with registry "${registry}". Allowed registries for "${kind}": ${allowedRegistries.join(", ")}`
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
if (verbose) {
|
|
166
|
+
logger.debug("Kind-registry combination is valid");
|
|
167
|
+
}
|
|
168
|
+
return { valid: true };
|
|
169
|
+
}
|
|
170
|
+
async function validateDistFolder(packagePath, verbose) {
|
|
171
|
+
const distPath = resolve(packagePath, "dist");
|
|
172
|
+
if (verbose) {
|
|
173
|
+
logger.debug(`Checking dist folder at: ${distPath}`);
|
|
174
|
+
}
|
|
175
|
+
try {
|
|
176
|
+
const stat = await Bun.file(distPath).stat();
|
|
177
|
+
const isValid = stat.isDirectory();
|
|
178
|
+
if (verbose) {
|
|
179
|
+
logger.debug(
|
|
180
|
+
`Dist folder ${isValid ? "exists and is a directory" : "is not a directory"}`
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
return isValid;
|
|
184
|
+
} catch (error) {
|
|
185
|
+
if (verbose) {
|
|
186
|
+
logger.debug(
|
|
187
|
+
`Dist folder validation failed: ${error instanceof Error ? error.message : String(error)}`
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
function validatePackageJsonFields(pkg, _packageName, kind, verbose) {
|
|
194
|
+
if (verbose) {
|
|
195
|
+
logger.debug(`Validating package.json fields for package: ${pkg.name}`);
|
|
196
|
+
logger.debug(`Package kind: ${kind ?? "undefined"}`);
|
|
197
|
+
}
|
|
198
|
+
const errors = [];
|
|
199
|
+
if (!pkg.name) {
|
|
200
|
+
errors.push("Missing required field: 'name'");
|
|
201
|
+
}
|
|
202
|
+
if (!pkg.version) {
|
|
203
|
+
errors.push("Missing required field: 'version'");
|
|
204
|
+
} else if (verbose) {
|
|
205
|
+
logger.debug(`Package version: ${pkg.version}`);
|
|
206
|
+
}
|
|
207
|
+
if (pkg.private === true) {
|
|
208
|
+
errors.push(
|
|
209
|
+
"Package has 'private: true' - cannot publish. Run 'dler build' to prepare the package."
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
if (!pkg.files || !Array.isArray(pkg.files) || pkg.files.length === 0) {
|
|
213
|
+
errors.push("Missing or empty 'files' field - Run 'dler build' to add it.");
|
|
214
|
+
} else {
|
|
215
|
+
if (verbose) {
|
|
216
|
+
logger.debug(`Files field: ${JSON.stringify(pkg.files)}`);
|
|
217
|
+
}
|
|
218
|
+
const hasDist = pkg.files.includes("dist") || pkg.files.includes("dist/*");
|
|
219
|
+
if (!hasDist) {
|
|
220
|
+
errors.push("'files' field must include 'dist' directory");
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
const hasBinField = pkg.bin && typeof pkg.bin === "object" && Object.keys(pkg.bin).length > 0;
|
|
224
|
+
if (verbose) {
|
|
225
|
+
logger.debug(`Has bin field: ${hasBinField}`);
|
|
226
|
+
logger.debug(`Has exports field: ${!!pkg.exports}`);
|
|
227
|
+
}
|
|
228
|
+
if (!pkg.exports && !hasBinField) {
|
|
229
|
+
errors.push("Missing 'exports' field - Run 'dler build' to add it.");
|
|
230
|
+
}
|
|
231
|
+
if (!pkg.publishConfig) {
|
|
232
|
+
errors.push("Missing 'publishConfig' field - Run 'dler build' to add it.");
|
|
233
|
+
} else if (!pkg.publishConfig.access) {
|
|
234
|
+
errors.push(
|
|
235
|
+
"Missing 'publishConfig.access' field - Run 'dler build' to add it."
|
|
236
|
+
);
|
|
237
|
+
} else if (verbose) {
|
|
238
|
+
logger.debug(`PublishConfig access: ${pkg.publishConfig.access}`);
|
|
239
|
+
}
|
|
240
|
+
if (kind === "cli") {
|
|
241
|
+
if (!pkg.bin) {
|
|
242
|
+
errors.push(
|
|
243
|
+
"CLI package missing 'bin' field - Run 'dler build' to add it."
|
|
244
|
+
);
|
|
245
|
+
} else if (typeof pkg.bin === "object") {
|
|
246
|
+
const binEntries = Object.keys(pkg.bin);
|
|
247
|
+
if (verbose) {
|
|
248
|
+
logger.debug(`CLI bin entries: ${binEntries.join(", ")}`);
|
|
249
|
+
}
|
|
250
|
+
if (binEntries.length === 0) {
|
|
251
|
+
errors.push("CLI package has empty 'bin' field");
|
|
252
|
+
} else {
|
|
253
|
+
for (const [binName, binPath] of Object.entries(pkg.bin)) {
|
|
254
|
+
if (typeof binPath === "string" && !binPath.startsWith("dist/")) {
|
|
255
|
+
errors.push(
|
|
256
|
+
`CLI bin entry '${binName}' should point to 'dist/' directory, found: '${binPath}'`
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
if (verbose) {
|
|
264
|
+
logger.debug(
|
|
265
|
+
`Package validation ${errors.length === 0 ? "passed" : "failed"} with ${errors.length} error(s)`
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
return {
|
|
269
|
+
valid: errors.length === 0,
|
|
270
|
+
errors
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
async function restoreOriginalDependencies(packagePath, originalDependencies, originalDevDependencies, originalScripts, originalExports, verbose) {
|
|
274
|
+
if (verbose) {
|
|
275
|
+
logger.debug(`Restoring original package.json for: ${packagePath}`);
|
|
276
|
+
}
|
|
277
|
+
try {
|
|
278
|
+
const pkg = await readPackageJSON(packagePath);
|
|
279
|
+
if (pkg) {
|
|
280
|
+
if (originalDependencies !== void 0) {
|
|
281
|
+
if (verbose) {
|
|
282
|
+
logger.debug(
|
|
283
|
+
`Restoring ${Object.keys(originalDependencies).length} dependencies`
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
pkg.dependencies = originalDependencies;
|
|
287
|
+
}
|
|
288
|
+
if (originalDevDependencies !== void 0) {
|
|
289
|
+
if (verbose) {
|
|
290
|
+
logger.debug(
|
|
291
|
+
`Restoring ${Object.keys(originalDevDependencies).length} devDependencies`
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
pkg.devDependencies = originalDevDependencies;
|
|
295
|
+
}
|
|
296
|
+
if (originalScripts !== void 0) {
|
|
297
|
+
if (verbose) {
|
|
298
|
+
logger.debug(
|
|
299
|
+
`Restoring ${Object.keys(originalScripts).length} scripts`
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
pkg.scripts = originalScripts;
|
|
303
|
+
}
|
|
304
|
+
if (originalExports !== void 0) {
|
|
305
|
+
if (verbose) {
|
|
306
|
+
logger.debug("Restoring original exports field");
|
|
307
|
+
}
|
|
308
|
+
pkg.exports = originalExports;
|
|
309
|
+
}
|
|
310
|
+
await writePackageJSON(resolve(packagePath, "package.json"), pkg);
|
|
311
|
+
if (verbose) {
|
|
312
|
+
logger.debug("Successfully restored original package.json");
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
} catch (error) {
|
|
316
|
+
logger.warn(
|
|
317
|
+
`\u26A0\uFE0F Failed to restore original dependencies: ${error instanceof Error ? error.message : String(error)}`
|
|
318
|
+
);
|
|
319
|
+
if (verbose) {
|
|
320
|
+
logger.debug(
|
|
321
|
+
`Restore error details: ${error instanceof Error ? error.stack : String(error)}`
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
async function resolveWorkspaceVersion(packagePath, depName, workspacePackages, bumpedVersions, verbose) {
|
|
327
|
+
try {
|
|
328
|
+
if (verbose) {
|
|
329
|
+
logger.debug(`Resolving workspace version for: ${depName}`);
|
|
330
|
+
}
|
|
331
|
+
if (bumpedVersions?.has(depName)) {
|
|
332
|
+
const version = bumpedVersions.get(depName) || null;
|
|
333
|
+
if (verbose) {
|
|
334
|
+
logger.debug(`Found ${depName} in bumped versions map: ${version}`);
|
|
335
|
+
}
|
|
336
|
+
return version;
|
|
337
|
+
}
|
|
338
|
+
if (!workspacePackages) {
|
|
339
|
+
if (verbose) {
|
|
340
|
+
logger.debug("Workspace packages not provided, discovering...");
|
|
341
|
+
}
|
|
342
|
+
const monorepoRoot = await findMonorepoRoot(packagePath);
|
|
343
|
+
if (!monorepoRoot) {
|
|
344
|
+
if (verbose) {
|
|
345
|
+
logger.debug("Monorepo root not found");
|
|
346
|
+
}
|
|
347
|
+
return null;
|
|
348
|
+
}
|
|
349
|
+
workspacePackages = await getWorkspacePackages(monorepoRoot);
|
|
350
|
+
if (verbose) {
|
|
351
|
+
logger.debug(`Found ${workspacePackages.length} workspace packages`);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
const workspacePkg = workspacePackages.find((pkg) => pkg.name === depName);
|
|
355
|
+
if (workspacePkg?.pkg?.version) {
|
|
356
|
+
if (verbose) {
|
|
357
|
+
logger.debug(
|
|
358
|
+
`Resolved workspace version for ${depName}: ${workspacePkg.pkg.version}`
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
return workspacePkg.pkg.version;
|
|
362
|
+
}
|
|
363
|
+
if (verbose) {
|
|
364
|
+
logger.debug(`Workspace package ${depName} not found or has no version`);
|
|
365
|
+
}
|
|
366
|
+
return null;
|
|
367
|
+
} catch (error) {
|
|
368
|
+
if (verbose) {
|
|
369
|
+
logger.debug(
|
|
370
|
+
`Error resolving workspace version: ${error instanceof Error ? error.message : String(error)}`
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
return null;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
async function resolveCatalogVersion(packagePath, depName, verbose) {
|
|
377
|
+
try {
|
|
378
|
+
if (verbose) {
|
|
379
|
+
logger.debug(`Resolving catalog version for: ${depName}`);
|
|
380
|
+
}
|
|
381
|
+
const possibleRoots = [
|
|
382
|
+
resolve(packagePath, ".."),
|
|
383
|
+
resolve(packagePath, "..", ".."),
|
|
384
|
+
resolve(packagePath, "..", "..", "..")
|
|
385
|
+
];
|
|
386
|
+
if (verbose) {
|
|
387
|
+
logger.debug(`Checking ${possibleRoots.length} possible root paths`);
|
|
388
|
+
}
|
|
389
|
+
for (const possibleRoot of possibleRoots) {
|
|
390
|
+
try {
|
|
391
|
+
if (verbose) {
|
|
392
|
+
logger.debug(`Checking root path: ${possibleRoot}`);
|
|
393
|
+
}
|
|
394
|
+
const rootPkg = await readPackageJSON(possibleRoot);
|
|
395
|
+
if (rootPkg?.workspaces) {
|
|
396
|
+
const workspaces = rootPkg.workspaces;
|
|
397
|
+
if (typeof workspaces === "object" && workspaces !== null && "catalog" in workspaces) {
|
|
398
|
+
const catalog = workspaces.catalog;
|
|
399
|
+
if (catalog && typeof catalog === "object") {
|
|
400
|
+
const catalogVersion = catalog[depName];
|
|
401
|
+
if (catalogVersion) {
|
|
402
|
+
if (verbose) {
|
|
403
|
+
logger.debug(
|
|
404
|
+
`Resolved catalog version for ${depName}: ${catalogVersion}`
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
return catalogVersion;
|
|
408
|
+
} else if (verbose) {
|
|
409
|
+
logger.debug(`Catalog found but ${depName} not in catalog`);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
} catch (error) {
|
|
415
|
+
if (verbose) {
|
|
416
|
+
logger.debug(
|
|
417
|
+
`Error checking root ${possibleRoot}: ${error instanceof Error ? error.message : String(error)}`
|
|
418
|
+
);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
if (verbose) {
|
|
423
|
+
logger.debug(`Catalog version for ${depName} not found`);
|
|
424
|
+
}
|
|
425
|
+
return null;
|
|
426
|
+
} catch (error) {
|
|
427
|
+
if (verbose) {
|
|
428
|
+
logger.debug(
|
|
429
|
+
`Error resolving catalog version: ${error instanceof Error ? error.message : String(error)}`
|
|
430
|
+
);
|
|
431
|
+
}
|
|
432
|
+
return null;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
async function preparePackageForPublishing(packagePath, options, workspacePackages, bumpedVersions, shouldBumpVersion = true) {
|
|
436
|
+
try {
|
|
437
|
+
if (options.verbose) {
|
|
438
|
+
logger.debug(`Preparing package for publishing: ${packagePath}`);
|
|
439
|
+
}
|
|
440
|
+
const pkg = await readPackageJSON(packagePath);
|
|
441
|
+
if (!pkg) {
|
|
442
|
+
if (options.verbose) {
|
|
443
|
+
logger.debug("Failed to read package.json");
|
|
444
|
+
}
|
|
445
|
+
return { success: false, error: "Could not read package.json" };
|
|
446
|
+
}
|
|
447
|
+
if (options.verbose) {
|
|
448
|
+
logger.debug(`Package name: ${pkg.name}, version: ${pkg.version}`);
|
|
449
|
+
}
|
|
450
|
+
const originalPkg = { ...pkg };
|
|
451
|
+
const originalExports = pkg.exports ? JSON.parse(JSON.stringify(pkg.exports)) : void 0;
|
|
452
|
+
if (options.verbose) {
|
|
453
|
+
logger.debug("Created backup of original package.json");
|
|
454
|
+
}
|
|
455
|
+
if (shouldBumpVersion && options.bump && !options.bumpDisable && !options.dryRun && pkg.version) {
|
|
456
|
+
if (options.verbose) {
|
|
457
|
+
logger.debug(`Bumping version: ${pkg.version} -> ${options.bump}`);
|
|
458
|
+
}
|
|
459
|
+
const bumpResult = bumpVersion(pkg.version, options.bump);
|
|
460
|
+
if (!bumpResult) {
|
|
461
|
+
if (options.verbose) {
|
|
462
|
+
logger.debug(`Invalid version bump: ${options.bump}`);
|
|
463
|
+
}
|
|
464
|
+
return {
|
|
465
|
+
success: false,
|
|
466
|
+
error: `Invalid version bump: ${options.bump}`
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
if (options.verbose) {
|
|
470
|
+
logger.log(
|
|
471
|
+
re.blue(
|
|
472
|
+
` Bumping version from ${re.bold(pkg.version)} to ${re.bold(re.green(bumpResult.bumped))} (${options.bump})`
|
|
473
|
+
)
|
|
474
|
+
);
|
|
475
|
+
}
|
|
476
|
+
pkg.version = bumpResult.bumped;
|
|
477
|
+
} else if (options.verbose) {
|
|
478
|
+
logger.debug(
|
|
479
|
+
`Skipping version bump: shouldBumpVersion=${shouldBumpVersion}, bump=${options.bump}, bumpDisable=${options.bumpDisable}, dryRun=${options.dryRun}`
|
|
480
|
+
);
|
|
481
|
+
}
|
|
482
|
+
if (!pkg.publishConfig) {
|
|
483
|
+
pkg.publishConfig = {};
|
|
484
|
+
if (options.verbose) {
|
|
485
|
+
logger.debug("Created publishConfig object");
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
pkg.publishConfig.access = options.access || "public";
|
|
489
|
+
if (options.verbose) {
|
|
490
|
+
logger.debug(`Set publishConfig.access: ${pkg.publishConfig.access}`);
|
|
491
|
+
}
|
|
492
|
+
const originalDependencies = pkg.dependencies && Object.keys(pkg.dependencies).length > 0 ? { ...pkg.dependencies } : void 0;
|
|
493
|
+
const originalDevDependencies = pkg.devDependencies && Object.keys(pkg.devDependencies).length > 0 ? { ...pkg.devDependencies } : void 0;
|
|
494
|
+
const originalScripts = pkg.scripts && Object.keys(pkg.scripts).length > 0 ? { ...pkg.scripts } : void 0;
|
|
495
|
+
if (options.verbose) {
|
|
496
|
+
logger.debug(
|
|
497
|
+
`Stored originals: ${originalDependencies ? Object.keys(originalDependencies).length : 0} deps, ${originalDevDependencies ? Object.keys(originalDevDependencies).length : 0} devDeps, ${originalScripts ? Object.keys(originalScripts).length : 0} scripts`
|
|
498
|
+
);
|
|
499
|
+
}
|
|
500
|
+
delete pkg.devDependencies;
|
|
501
|
+
delete pkg.scripts;
|
|
502
|
+
if (options.verbose) {
|
|
503
|
+
logger.debug("Removed devDependencies and scripts for publishing");
|
|
504
|
+
}
|
|
505
|
+
if (pkg.dependencies) {
|
|
506
|
+
const depCount = Object.keys(pkg.dependencies).length;
|
|
507
|
+
if (options.verbose) {
|
|
508
|
+
logger.debug(`Resolving ${depCount} dependencies`);
|
|
509
|
+
}
|
|
510
|
+
for (const [depName, version] of Object.entries(pkg.dependencies)) {
|
|
511
|
+
if (typeof version === "string") {
|
|
512
|
+
if (version.startsWith("workspace:")) {
|
|
513
|
+
const workspaceVersion = await resolveWorkspaceVersion(
|
|
514
|
+
packagePath,
|
|
515
|
+
depName,
|
|
516
|
+
workspacePackages,
|
|
517
|
+
bumpedVersions,
|
|
518
|
+
options.verbose
|
|
519
|
+
);
|
|
520
|
+
if (workspaceVersion) {
|
|
521
|
+
if (options.verbose) {
|
|
522
|
+
logger.log(
|
|
523
|
+
re.blue(
|
|
524
|
+
` Resolving workspace dependency ${re.bold(depName)}: ${version} -> ${re.green(workspaceVersion)}`
|
|
525
|
+
)
|
|
526
|
+
);
|
|
527
|
+
}
|
|
528
|
+
pkg.dependencies[depName] = workspaceVersion;
|
|
529
|
+
} else {
|
|
530
|
+
if (options.verbose) {
|
|
531
|
+
logger.warn(
|
|
532
|
+
` \u26A0\uFE0F Cannot resolve workspace dependency ${re.bold(depName)}, removing it`
|
|
533
|
+
);
|
|
534
|
+
}
|
|
535
|
+
delete pkg.dependencies[depName];
|
|
536
|
+
}
|
|
537
|
+
} else if (version === "catalog:") {
|
|
538
|
+
const catalogVersion = await resolveCatalogVersion(
|
|
539
|
+
packagePath,
|
|
540
|
+
depName,
|
|
541
|
+
options.verbose
|
|
542
|
+
);
|
|
543
|
+
if (catalogVersion) {
|
|
544
|
+
if (options.verbose) {
|
|
545
|
+
logger.log(
|
|
546
|
+
re.blue(
|
|
547
|
+
` Resolving catalog dependency ${re.bold(depName)}: ${version} -> ${re.green(catalogVersion)}`
|
|
548
|
+
)
|
|
549
|
+
);
|
|
550
|
+
}
|
|
551
|
+
pkg.dependencies[depName] = catalogVersion;
|
|
552
|
+
} else {
|
|
553
|
+
if (options.verbose) {
|
|
554
|
+
logger.warn(
|
|
555
|
+
` \u26A0\uFE0F Cannot resolve catalog dependency ${re.bold(depName)}, removing it`
|
|
556
|
+
);
|
|
557
|
+
}
|
|
558
|
+
delete pkg.dependencies[depName];
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
if (options.verbose) {
|
|
564
|
+
logger.debug(
|
|
565
|
+
`After resolution: ${Object.keys(pkg.dependencies).length} dependencies remaining`
|
|
566
|
+
);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
if (pkg.exports) {
|
|
570
|
+
if (options.verbose) {
|
|
571
|
+
logger.debug("Transforming exports field for publishing");
|
|
572
|
+
}
|
|
573
|
+
pkg.exports = transformExportsForBuild(pkg.exports);
|
|
574
|
+
}
|
|
575
|
+
await writePackageJSON(resolve(packagePath, "package.json"), pkg);
|
|
576
|
+
if (options.verbose) {
|
|
577
|
+
logger.debug("Successfully wrote modified package.json");
|
|
578
|
+
}
|
|
579
|
+
return {
|
|
580
|
+
success: true,
|
|
581
|
+
version: pkg.version,
|
|
582
|
+
originalPkg,
|
|
583
|
+
originalDependencies,
|
|
584
|
+
originalDevDependencies,
|
|
585
|
+
originalScripts,
|
|
586
|
+
originalExports
|
|
587
|
+
};
|
|
588
|
+
} catch (error) {
|
|
589
|
+
if (options.verbose) {
|
|
590
|
+
logger.debug(
|
|
591
|
+
`Error preparing package: ${error instanceof Error ? error.message : String(error)}`
|
|
592
|
+
);
|
|
593
|
+
}
|
|
594
|
+
return {
|
|
595
|
+
success: false,
|
|
596
|
+
error: error instanceof Error ? error.message : String(error)
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
export async function publishPackage(packagePath, options = {}, bumpedVersions) {
|
|
601
|
+
if (options.verbose) {
|
|
602
|
+
logger.debug(`Starting publish for package at: ${packagePath}`);
|
|
603
|
+
}
|
|
604
|
+
const rootPkg = await readPackageJSON(packagePath);
|
|
605
|
+
const rootPackageName = rootPkg?.name || "unknown";
|
|
606
|
+
if (options.verbose) {
|
|
607
|
+
logger.debug(`Package name: ${rootPackageName}`);
|
|
608
|
+
logger.debug(`Publish options: ${JSON.stringify(options, null, 2)}`);
|
|
609
|
+
}
|
|
610
|
+
let originalDependencies = null;
|
|
611
|
+
let originalDevDependencies = null;
|
|
612
|
+
let originalScripts = null;
|
|
613
|
+
let originalExports = null;
|
|
614
|
+
let workspacePackages;
|
|
615
|
+
let monorepoRoot = null;
|
|
616
|
+
try {
|
|
617
|
+
if (options.verbose) {
|
|
618
|
+
logger.debug("Discovering monorepo root and workspace packages...");
|
|
619
|
+
}
|
|
620
|
+
monorepoRoot = await findMonorepoRoot(packagePath);
|
|
621
|
+
if (monorepoRoot) {
|
|
622
|
+
if (options.verbose) {
|
|
623
|
+
logger.debug(`Monorepo root: ${monorepoRoot}`);
|
|
624
|
+
}
|
|
625
|
+
workspacePackages = await getWorkspacePackages(monorepoRoot);
|
|
626
|
+
if (options.verbose) {
|
|
627
|
+
logger.log(
|
|
628
|
+
re.blue(
|
|
629
|
+
` Found ${re.bold(workspacePackages.length)} workspace packages`
|
|
630
|
+
)
|
|
631
|
+
);
|
|
632
|
+
}
|
|
633
|
+
} else if (options.verbose) {
|
|
634
|
+
logger.debug("Not in a monorepo (no root found)");
|
|
635
|
+
}
|
|
636
|
+
} catch (error) {
|
|
637
|
+
if (options.verbose) {
|
|
638
|
+
logger.debug(
|
|
639
|
+
`Error discovering workspace: ${error instanceof Error ? error.message : String(error)}`
|
|
640
|
+
);
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
const copiedFiles = [];
|
|
644
|
+
try {
|
|
645
|
+
if (rootPkg?.private === true) {
|
|
646
|
+
if (options.verbose) {
|
|
647
|
+
logger.debug("Package is private, skipping publish");
|
|
648
|
+
}
|
|
649
|
+
return {
|
|
650
|
+
success: true,
|
|
651
|
+
packageName: rootPackageName,
|
|
652
|
+
packagePath,
|
|
653
|
+
warning: `The package has "private: true", publishing skipped.`
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
if (options.verbose) {
|
|
657
|
+
logger.debug("Validating kind-registry combination...");
|
|
658
|
+
}
|
|
659
|
+
const validation = validateKindRegistryCombination(
|
|
660
|
+
options.kind,
|
|
661
|
+
options.registry,
|
|
662
|
+
options.verbose
|
|
663
|
+
);
|
|
664
|
+
if (!validation.valid) {
|
|
665
|
+
if (options.verbose) {
|
|
666
|
+
logger.debug(`Validation failed: ${validation.error}`);
|
|
667
|
+
}
|
|
668
|
+
return {
|
|
669
|
+
success: false,
|
|
670
|
+
packageName: rootPackageName,
|
|
671
|
+
packagePath,
|
|
672
|
+
error: validation.error
|
|
673
|
+
};
|
|
674
|
+
}
|
|
675
|
+
if (options.registry === "none") {
|
|
676
|
+
if (options.verbose) {
|
|
677
|
+
logger.debug("Registry is 'none', skipping publish");
|
|
678
|
+
}
|
|
679
|
+
return {
|
|
680
|
+
success: true,
|
|
681
|
+
packageName: rootPackageName,
|
|
682
|
+
packagePath,
|
|
683
|
+
version: (await readPackageJSON(packagePath))?.version
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
if (monorepoRoot) {
|
|
687
|
+
if (options.verbose) {
|
|
688
|
+
logger.debug("Checking for files to copy from monorepo root...");
|
|
689
|
+
}
|
|
690
|
+
const filesToCopy = ["README.md", "LICENSE"];
|
|
691
|
+
for (const fileName of filesToCopy) {
|
|
692
|
+
const sourcePath = resolve(monorepoRoot, fileName);
|
|
693
|
+
const targetPath = resolve(packagePath, fileName);
|
|
694
|
+
try {
|
|
695
|
+
const sourceFile = Bun.file(sourcePath);
|
|
696
|
+
const sourceExists = await sourceFile.exists();
|
|
697
|
+
if (sourceExists) {
|
|
698
|
+
const targetFile = Bun.file(targetPath);
|
|
699
|
+
const targetExists = await targetFile.exists();
|
|
700
|
+
if (!targetExists) {
|
|
701
|
+
const content = await sourceFile.text();
|
|
702
|
+
await Bun.write(targetPath, content);
|
|
703
|
+
copiedFiles.push(targetPath);
|
|
704
|
+
if (options.verbose) {
|
|
705
|
+
logger.log(
|
|
706
|
+
re.blue(` Copied ${re.bold(fileName)} from monorepo root`)
|
|
707
|
+
);
|
|
708
|
+
logger.debug(
|
|
709
|
+
`Copied ${fileName} from ${sourcePath} to ${targetPath}`
|
|
710
|
+
);
|
|
711
|
+
}
|
|
712
|
+
} else if (options.verbose) {
|
|
713
|
+
logger.debug(
|
|
714
|
+
`${fileName} already exists in package, skipping copy`
|
|
715
|
+
);
|
|
716
|
+
}
|
|
717
|
+
} else if (options.verbose) {
|
|
718
|
+
logger.debug(`${fileName} not found in monorepo root`);
|
|
719
|
+
}
|
|
720
|
+
} catch (error) {
|
|
721
|
+
if (options.verbose) {
|
|
722
|
+
logger.debug(
|
|
723
|
+
`Error copying ${fileName}: ${error instanceof Error ? error.message : String(error)}`
|
|
724
|
+
);
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
if (options.verbose) {
|
|
730
|
+
logger.debug("Validating package.json fields...");
|
|
731
|
+
}
|
|
732
|
+
const pkgValidation = validatePackageJsonFields(
|
|
733
|
+
rootPkg,
|
|
734
|
+
rootPackageName,
|
|
735
|
+
options.kind,
|
|
736
|
+
options.verbose
|
|
737
|
+
);
|
|
738
|
+
if (!pkgValidation.valid) {
|
|
739
|
+
if (options.verbose) {
|
|
740
|
+
logger.debug(
|
|
741
|
+
`Package validation failed with ${pkgValidation.errors.length} error(s)`
|
|
742
|
+
);
|
|
743
|
+
}
|
|
744
|
+
return {
|
|
745
|
+
success: false,
|
|
746
|
+
packageName: rootPackageName,
|
|
747
|
+
packagePath,
|
|
748
|
+
error: `Package.json validation failed:
|
|
749
|
+
${pkgValidation.errors.join("\n ")}`
|
|
750
|
+
};
|
|
751
|
+
}
|
|
752
|
+
if (options.verbose) {
|
|
753
|
+
logger.debug("Validating dist folder...");
|
|
754
|
+
}
|
|
755
|
+
const hasDist = await validateDistFolder(packagePath, options.verbose);
|
|
756
|
+
if (!hasDist) {
|
|
757
|
+
if (options.verbose) {
|
|
758
|
+
logger.debug("Dist folder validation failed");
|
|
759
|
+
}
|
|
760
|
+
return {
|
|
761
|
+
success: false,
|
|
762
|
+
packageName: rootPackageName,
|
|
763
|
+
packagePath,
|
|
764
|
+
error: "dist folder not found. Run 'dler build' first."
|
|
765
|
+
};
|
|
766
|
+
}
|
|
767
|
+
if (options.verbose) {
|
|
768
|
+
logger.debug("Preparing package for publishing...");
|
|
769
|
+
}
|
|
770
|
+
const prepResult = await preparePackageForPublishing(
|
|
771
|
+
packagePath,
|
|
772
|
+
options,
|
|
773
|
+
workspacePackages,
|
|
774
|
+
bumpedVersions
|
|
775
|
+
);
|
|
776
|
+
if (!prepResult.success) {
|
|
777
|
+
if (options.verbose) {
|
|
778
|
+
logger.debug(`Preparation failed: ${prepResult.error}`);
|
|
779
|
+
}
|
|
780
|
+
return {
|
|
781
|
+
success: false,
|
|
782
|
+
packageName: rootPackageName,
|
|
783
|
+
packagePath,
|
|
784
|
+
error: prepResult.error
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
if (options.verbose) {
|
|
788
|
+
logger.debug(
|
|
789
|
+
`Package prepared successfully, version: ${prepResult.version}`
|
|
790
|
+
);
|
|
791
|
+
}
|
|
792
|
+
originalDependencies = prepResult.originalDependencies;
|
|
793
|
+
originalDevDependencies = prepResult.originalDevDependencies;
|
|
794
|
+
originalScripts = prepResult.originalScripts;
|
|
795
|
+
originalExports = prepResult.originalExports;
|
|
796
|
+
const rootPackage = await readPackageJSON(packagePath);
|
|
797
|
+
const packageName = rootPackage?.name || rootPackageName;
|
|
798
|
+
const registry = options.registry || "npm";
|
|
799
|
+
if (options.verbose) {
|
|
800
|
+
logger.debug(`Publishing to registry: ${registry}`);
|
|
801
|
+
}
|
|
802
|
+
if (registry === "npm" || registry === "npm-jsr") {
|
|
803
|
+
const args = ["publish"];
|
|
804
|
+
if (options.dryRun) {
|
|
805
|
+
args.push("--dry-run");
|
|
806
|
+
}
|
|
807
|
+
if (options.tag) {
|
|
808
|
+
args.push("--tag", options.tag);
|
|
809
|
+
}
|
|
810
|
+
if (options.access) {
|
|
811
|
+
args.push("--access", options.access);
|
|
812
|
+
}
|
|
813
|
+
if (options.otp) {
|
|
814
|
+
args.push("--otp", options.otp);
|
|
815
|
+
}
|
|
816
|
+
if (options.authType) {
|
|
817
|
+
args.push("--auth-type", options.authType);
|
|
818
|
+
}
|
|
819
|
+
if (options.gzipLevel) {
|
|
820
|
+
args.push("--gzip-level", options.gzipLevel);
|
|
821
|
+
}
|
|
822
|
+
if (options.bunRegistry) {
|
|
823
|
+
args.push("--registry", options.bunRegistry);
|
|
824
|
+
}
|
|
825
|
+
if (options.ca) {
|
|
826
|
+
args.push("--ca", options.ca);
|
|
827
|
+
}
|
|
828
|
+
if (options.cafile) {
|
|
829
|
+
args.push("--cafile", options.cafile);
|
|
830
|
+
}
|
|
831
|
+
if (options.ignoreScripts) {
|
|
832
|
+
args.push("--ignore-scripts");
|
|
833
|
+
}
|
|
834
|
+
if (options.silent) {
|
|
835
|
+
args.push("--silent");
|
|
836
|
+
}
|
|
837
|
+
if (options.noProgress) {
|
|
838
|
+
args.push("--no-progress");
|
|
839
|
+
}
|
|
840
|
+
if (options.noSummary) {
|
|
841
|
+
args.push("--no-summary");
|
|
842
|
+
}
|
|
843
|
+
if (options.verbose) {
|
|
844
|
+
logger.debug(`Bun publish args: ${args.join(" ")}`);
|
|
845
|
+
}
|
|
846
|
+
if (options.withNpmLogs && options.skipTip2FA !== true && !hasShown2FATip) {
|
|
847
|
+
hasShown2FATip = true;
|
|
848
|
+
logger.log(
|
|
849
|
+
`
|
|
850
|
+
${re.cyan.bold("\u{1F4A1} 2FA Authentication Tip")}
|
|
851
|
+
If you have 2FA enabled on npm, you may be prompted for a one-time password during publishing (on each publish attempt).
|
|
852
|
+
${re.bold("Quick fix:")}
|
|
853
|
+
When prompted, check the box: "Do not challenge npm publish operations from your IP address for the next 5 minutes"
|
|
854
|
+
${re.bold("Permanent solution:")}
|
|
855
|
+
https://npmjs.com \u2192 Avatar \u2192 Account \u2192 Modify 2FA \u2192 Uncheck "Require two-factor authentication for write actions"
|
|
856
|
+
|
|
857
|
+
${re.bold(`Use --skip-tip-2fa to skip this information and the 3s wait.
|
|
858
|
+
|
|
859
|
+
`)}`
|
|
860
|
+
);
|
|
861
|
+
await new Promise((resolve2) => setTimeout(resolve2, 3e3));
|
|
862
|
+
}
|
|
863
|
+
try {
|
|
864
|
+
const result = await runBunPublishCommand(
|
|
865
|
+
packagePath,
|
|
866
|
+
["bun", ...args],
|
|
867
|
+
options.verbose,
|
|
868
|
+
options.withNpmLogs
|
|
869
|
+
);
|
|
870
|
+
if (result.exitCode !== 0) {
|
|
871
|
+
const errorOutput = result.stderr || result.stdout;
|
|
872
|
+
if (options.verbose) {
|
|
873
|
+
logger.debug(
|
|
874
|
+
`Publish command failed with exit code ${result.exitCode}`
|
|
875
|
+
);
|
|
876
|
+
logger.debug(`Error output: ${errorOutput}`);
|
|
877
|
+
}
|
|
878
|
+
if (THROW_2FA_ERROR && errorOutput.includes("This operation requires a one-time password.")) {
|
|
879
|
+
throw new Error(
|
|
880
|
+
"This operation requires a one-time password. You have 2FA enabled in your npm account. Please set NPM_CONFIG_TOKEN environment variable with your npm token."
|
|
881
|
+
);
|
|
882
|
+
}
|
|
883
|
+
if (errorOutput.includes("failed to read OTP input") || errorOutput.includes("use your security key for authentication")) {
|
|
884
|
+
const hasToken = !!(process.env.NPM_CONFIG_TOKEN || process.env.NPM_TOKEN);
|
|
885
|
+
if (hasToken) {
|
|
886
|
+
throw new Error(
|
|
887
|
+
"2FA authentication required. NPM_CONFIG_TOKEN is set but authentication failed. Please verify:\n 1. The token is valid and not expired\n 2. The token has publish permissions\n 3. Use --with-npm-logs flag to allow interactive OTP input if token authentication is not working"
|
|
888
|
+
);
|
|
889
|
+
}
|
|
890
|
+
throw new Error(
|
|
891
|
+
"2FA authentication required. The publish command cannot read OTP input when output is captured. Please either:\n 1. Set NPM_CONFIG_TOKEN environment variable with your npm token (note: .env files are not automatically loaded - export it in your shell or use a tool like dotenv-cli), or\n 2. Use --with-npm-logs flag to allow interactive OTP input."
|
|
892
|
+
);
|
|
893
|
+
}
|
|
894
|
+
throw new Error(
|
|
895
|
+
`Failed to publish ${packageName} (exit code ${result.exitCode}): ${errorOutput}`
|
|
896
|
+
);
|
|
897
|
+
}
|
|
898
|
+
const output = result.stdout || result.stderr;
|
|
899
|
+
if (options.verbose) {
|
|
900
|
+
logger.debug(`Publish command succeeded`);
|
|
901
|
+
logger.debug(`Output length: ${output.length} bytes`);
|
|
902
|
+
}
|
|
903
|
+
const versionMatch = output.match(/published\s+([^\s]+)/i) || output.match(/@([0-9]+\.[0-9]+\.[0-9]+)/);
|
|
904
|
+
const version = versionMatch ? versionMatch[1] : rootPackage?.version;
|
|
905
|
+
if (options.verbose) {
|
|
906
|
+
logger.debug(`Extracted version: ${version ?? "unknown"}`);
|
|
907
|
+
}
|
|
908
|
+
if (options.verbose) {
|
|
909
|
+
logger.log(
|
|
910
|
+
re.green(
|
|
911
|
+
`\u2713 Published ${re.bold(packageName)}@${re.bold(version || "unknown")}`
|
|
912
|
+
)
|
|
913
|
+
);
|
|
914
|
+
} else {
|
|
915
|
+
logger.log(
|
|
916
|
+
re.green(
|
|
917
|
+
`\u2713 Published ${re.bold(packageName)}${version ? `@${re.bold(version)}` : ""}`
|
|
918
|
+
)
|
|
919
|
+
);
|
|
920
|
+
}
|
|
921
|
+
} catch (error) {
|
|
922
|
+
if (options.verbose) {
|
|
923
|
+
logger.debug(
|
|
924
|
+
`Publish error: ${error instanceof Error ? error.message : String(error)}`
|
|
925
|
+
);
|
|
926
|
+
}
|
|
927
|
+
throw new Error(
|
|
928
|
+
`Failed to publish ${packageName}: ${error instanceof Error ? error.message : String(error)}`
|
|
929
|
+
);
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
if (registry === "jsr" || registry === "npm-jsr") {
|
|
933
|
+
if (options.verbose) {
|
|
934
|
+
logger.debug("JSR publishing not implemented, skipping");
|
|
935
|
+
}
|
|
936
|
+
logger.warn(
|
|
937
|
+
`\u26A0\uFE0F JSR publishing not yet implemented for ${re.bold(packageName)}. Skipping JSR publish.`
|
|
938
|
+
);
|
|
939
|
+
}
|
|
940
|
+
if (registry === "vercel") {
|
|
941
|
+
if (options.verbose) {
|
|
942
|
+
logger.debug("Vercel publishing not implemented, skipping");
|
|
943
|
+
}
|
|
944
|
+
logger.warn(
|
|
945
|
+
`\u26A0\uFE0F Vercel publishing not yet implemented for ${re.bold(packageName)}. Skipping Vercel publish.`
|
|
946
|
+
);
|
|
947
|
+
return {
|
|
948
|
+
success: true,
|
|
949
|
+
packageName,
|
|
950
|
+
packagePath,
|
|
951
|
+
version: rootPackage?.version
|
|
952
|
+
};
|
|
953
|
+
}
|
|
954
|
+
if (originalDependencies || originalDevDependencies || originalScripts || originalExports) {
|
|
955
|
+
if (options.verbose) {
|
|
956
|
+
logger.debug("Restoring original package.json...");
|
|
957
|
+
}
|
|
958
|
+
await restoreOriginalDependencies(
|
|
959
|
+
packagePath,
|
|
960
|
+
originalDependencies,
|
|
961
|
+
originalDevDependencies,
|
|
962
|
+
originalScripts,
|
|
963
|
+
originalExports,
|
|
964
|
+
options.verbose
|
|
965
|
+
);
|
|
966
|
+
}
|
|
967
|
+
return {
|
|
968
|
+
success: true,
|
|
969
|
+
packageName,
|
|
970
|
+
packagePath,
|
|
971
|
+
version: rootPackage?.version
|
|
972
|
+
};
|
|
973
|
+
} catch (error) {
|
|
974
|
+
if (options.verbose) {
|
|
975
|
+
logger.debug(
|
|
976
|
+
`Publish failed with error: ${error instanceof Error ? error.message : String(error)}`
|
|
977
|
+
);
|
|
978
|
+
}
|
|
979
|
+
if (originalDependencies || originalDevDependencies || originalScripts || originalExports) {
|
|
980
|
+
if (options.verbose) {
|
|
981
|
+
logger.debug("Restoring original package.json after error...");
|
|
982
|
+
}
|
|
983
|
+
await restoreOriginalDependencies(
|
|
984
|
+
packagePath,
|
|
985
|
+
originalDependencies,
|
|
986
|
+
originalDevDependencies,
|
|
987
|
+
originalScripts,
|
|
988
|
+
originalExports,
|
|
989
|
+
options.verbose
|
|
990
|
+
);
|
|
991
|
+
}
|
|
992
|
+
return {
|
|
993
|
+
success: false,
|
|
994
|
+
packageName: rootPackageName,
|
|
995
|
+
packagePath,
|
|
996
|
+
error: error instanceof Error ? error.message : String(error)
|
|
997
|
+
};
|
|
998
|
+
} finally {
|
|
999
|
+
if (options.verbose && copiedFiles.length > 0) {
|
|
1000
|
+
logger.debug(`Cleaning up ${copiedFiles.length} copied file(s)...`);
|
|
1001
|
+
}
|
|
1002
|
+
for (const copiedFile of copiedFiles) {
|
|
1003
|
+
try {
|
|
1004
|
+
await Bun.file(copiedFile).unlink();
|
|
1005
|
+
if (options.verbose) {
|
|
1006
|
+
const fileName = copiedFile.split(/[/\\]/).pop();
|
|
1007
|
+
logger.log(
|
|
1008
|
+
re.blue(` Removed copied file: ${re.bold(fileName || "")}`)
|
|
1009
|
+
);
|
|
1010
|
+
logger.debug(`Removed copied file: ${copiedFile}`);
|
|
1011
|
+
}
|
|
1012
|
+
} catch (error) {
|
|
1013
|
+
if (options.verbose) {
|
|
1014
|
+
logger.debug(
|
|
1015
|
+
`Error removing copied file ${copiedFile}: ${error instanceof Error ? error.message : String(error)}`
|
|
1016
|
+
);
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
export async function publishAllPackages(cwd, ignore, options = {}) {
|
|
1023
|
+
try {
|
|
1024
|
+
if (options.verbose) {
|
|
1025
|
+
logger.debug(`Starting publishAllPackages, cwd: ${cwd ?? "current"}`);
|
|
1026
|
+
if (options.filter) {
|
|
1027
|
+
logger.debug(
|
|
1028
|
+
`Filter patterns: ${Array.isArray(options.filter) ? options.filter.join(", ") : options.filter}`
|
|
1029
|
+
);
|
|
1030
|
+
} else {
|
|
1031
|
+
logger.debug(
|
|
1032
|
+
`Ignore patterns: ${ignore ? Array.isArray(ignore) ? ignore.join(", ") : ignore : "none"}`
|
|
1033
|
+
);
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
if (options.verbose) {
|
|
1037
|
+
logger.debug("Loading dler.ts configuration...");
|
|
1038
|
+
}
|
|
1039
|
+
const dlerConfig = await loadDlerConfig(cwd);
|
|
1040
|
+
if (options.verbose) {
|
|
1041
|
+
logger.debug("Discovering workspace packages...");
|
|
1042
|
+
}
|
|
1043
|
+
const packages = await getWorkspacePackages(cwd);
|
|
1044
|
+
if (options.verbose) {
|
|
1045
|
+
logger.debug(`Found ${packages.length} total workspace package(s)`);
|
|
1046
|
+
}
|
|
1047
|
+
const isSingleRepo = packages.length === 1 && !ignore && !options.filter;
|
|
1048
|
+
const filteredPackages = isSingleRepo ? packages : filterPackages(packages, ignore, options.filter);
|
|
1049
|
+
if (options.verbose) {
|
|
1050
|
+
logger.debug(
|
|
1051
|
+
`After filtering: ${filteredPackages.length} package(s) (single-repo mode: ${isSingleRepo})`
|
|
1052
|
+
);
|
|
1053
|
+
if (options.filter) {
|
|
1054
|
+
const filterPatterns = Array.isArray(options.filter) ? options.filter : [options.filter];
|
|
1055
|
+
logger.info(
|
|
1056
|
+
re.blue(
|
|
1057
|
+
` Filtering to ${re.bold(filteredPackages.length)} packages matching: ${re.bold(filterPatterns.join(", "))}`
|
|
1058
|
+
)
|
|
1059
|
+
);
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
if (filteredPackages.length === 0) {
|
|
1063
|
+
logger.warn("No packages found to publish");
|
|
1064
|
+
return {
|
|
1065
|
+
results: [],
|
|
1066
|
+
hasErrors: false,
|
|
1067
|
+
successCount: 0,
|
|
1068
|
+
errorCount: 0,
|
|
1069
|
+
warningCount: 0
|
|
1070
|
+
};
|
|
1071
|
+
}
|
|
1072
|
+
logger.info(
|
|
1073
|
+
re.blue(
|
|
1074
|
+
`Found ${re.bold(filteredPackages.length)} package(s) to publish`
|
|
1075
|
+
)
|
|
1076
|
+
);
|
|
1077
|
+
const results = [];
|
|
1078
|
+
const concurrency = options.concurrency || 3;
|
|
1079
|
+
if (options.verbose) {
|
|
1080
|
+
logger.debug(`Concurrency: ${concurrency}`);
|
|
1081
|
+
}
|
|
1082
|
+
const packagesToPublish = filteredPackages.filter((pkg) => {
|
|
1083
|
+
const packageConfig = getPackagePublishConfig(pkg.name, dlerConfig);
|
|
1084
|
+
if (packageConfig?.enable === false) {
|
|
1085
|
+
if (options.verbose) {
|
|
1086
|
+
logger.info(
|
|
1087
|
+
re.yellow(`Skipping ${re.bold(pkg.name)} (disabled in config)`)
|
|
1088
|
+
);
|
|
1089
|
+
logger.debug(
|
|
1090
|
+
`Package config for ${pkg.name}: ${JSON.stringify(packageConfig)}`
|
|
1091
|
+
);
|
|
1092
|
+
}
|
|
1093
|
+
return false;
|
|
1094
|
+
}
|
|
1095
|
+
return true;
|
|
1096
|
+
});
|
|
1097
|
+
if (options.verbose) {
|
|
1098
|
+
logger.debug(
|
|
1099
|
+
`After enable filter: ${packagesToPublish.length} package(s) enabled`
|
|
1100
|
+
);
|
|
1101
|
+
}
|
|
1102
|
+
if (packagesToPublish.length === 0) {
|
|
1103
|
+
logger.warn("No packages enabled for publishing");
|
|
1104
|
+
return {
|
|
1105
|
+
results: [],
|
|
1106
|
+
hasErrors: false,
|
|
1107
|
+
successCount: 0,
|
|
1108
|
+
errorCount: 0,
|
|
1109
|
+
warningCount: 0
|
|
1110
|
+
};
|
|
1111
|
+
}
|
|
1112
|
+
logger.info(
|
|
1113
|
+
re.blue(
|
|
1114
|
+
`Publishing ${re.bold(packagesToPublish.length)} enabled package(s)`
|
|
1115
|
+
)
|
|
1116
|
+
);
|
|
1117
|
+
const bumpedVersions = /* @__PURE__ */ new Map();
|
|
1118
|
+
if (options.verbose) {
|
|
1119
|
+
logger.info(re.blue("Pre-bumping package versions..."));
|
|
1120
|
+
}
|
|
1121
|
+
for (const pkg of packagesToPublish) {
|
|
1122
|
+
const mergedOptions = mergePublishOptions(options, pkg.name, dlerConfig);
|
|
1123
|
+
const bumpType = mergedOptions.bump || (mergedOptions.bumpDisable ? void 0 : "patch");
|
|
1124
|
+
if (options.verbose) {
|
|
1125
|
+
logger.debug(
|
|
1126
|
+
`Pre-bumping ${pkg.name}: current=${pkg.pkg.version}, bumpType=${bumpType ?? "none"}, bumpDisable=${mergedOptions.bumpDisable}`
|
|
1127
|
+
);
|
|
1128
|
+
}
|
|
1129
|
+
if (bumpType && !mergedOptions.bumpDisable && pkg.pkg.version) {
|
|
1130
|
+
try {
|
|
1131
|
+
const nextVersion = getNextVersion(pkg.pkg.version, bumpType);
|
|
1132
|
+
if (nextVersion) {
|
|
1133
|
+
bumpedVersions.set(pkg.name, nextVersion);
|
|
1134
|
+
if (options.verbose) {
|
|
1135
|
+
logger.log(
|
|
1136
|
+
re.blue(
|
|
1137
|
+
` ${re.bold(pkg.name)}: ${pkg.pkg.version} -> ${re.green.bold(nextVersion)}`
|
|
1138
|
+
)
|
|
1139
|
+
);
|
|
1140
|
+
}
|
|
1141
|
+
} else if (options.verbose) {
|
|
1142
|
+
logger.debug(`Failed to calculate next version for ${pkg.name}`);
|
|
1143
|
+
}
|
|
1144
|
+
} catch (error) {
|
|
1145
|
+
if (options.verbose) {
|
|
1146
|
+
logger.debug(
|
|
1147
|
+
`Error bumping ${pkg.name}: ${error instanceof Error ? error.message : String(error)}`
|
|
1148
|
+
);
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
} else if (pkg.pkg.version) {
|
|
1152
|
+
bumpedVersions.set(pkg.name, pkg.pkg.version);
|
|
1153
|
+
if (options.verbose) {
|
|
1154
|
+
logger.debug(
|
|
1155
|
+
`Using current version for ${pkg.name}: ${pkg.pkg.version}`
|
|
1156
|
+
);
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
if (options.verbose) {
|
|
1161
|
+
logger.debug(`Pre-bumped ${bumpedVersions.size} package version(s)`);
|
|
1162
|
+
}
|
|
1163
|
+
const totalBatches = Math.ceil(packagesToPublish.length / concurrency);
|
|
1164
|
+
if (options.verbose) {
|
|
1165
|
+
logger.debug(
|
|
1166
|
+
`Processing ${totalBatches} batch(es) with concurrency ${concurrency}`
|
|
1167
|
+
);
|
|
1168
|
+
}
|
|
1169
|
+
for (let i = 0; i < packagesToPublish.length; i += concurrency) {
|
|
1170
|
+
const batch = packagesToPublish.slice(i, i + concurrency);
|
|
1171
|
+
const batchNumber = Math.floor(i / concurrency) + 1;
|
|
1172
|
+
if (options.verbose) {
|
|
1173
|
+
logger.debug(
|
|
1174
|
+
`Processing batch ${batchNumber}/${totalBatches} with ${batch.length} package(s)`
|
|
1175
|
+
);
|
|
1176
|
+
}
|
|
1177
|
+
const batchPromises = batch.map(async (pkg) => {
|
|
1178
|
+
const mergedOptions = mergePublishOptions(
|
|
1179
|
+
options,
|
|
1180
|
+
pkg.name,
|
|
1181
|
+
dlerConfig
|
|
1182
|
+
);
|
|
1183
|
+
if (!mergedOptions.bump && !mergedOptions.bumpDisable) {
|
|
1184
|
+
mergedOptions.bump = "patch";
|
|
1185
|
+
}
|
|
1186
|
+
if (options.verbose) {
|
|
1187
|
+
logger.debug(
|
|
1188
|
+
`Merged options for ${pkg.name}: ${JSON.stringify(mergedOptions)}`
|
|
1189
|
+
);
|
|
1190
|
+
}
|
|
1191
|
+
if (mergedOptions.verbose) {
|
|
1192
|
+
logger.info(re.blue(`Publishing ${re.bold(pkg.name)}...`));
|
|
1193
|
+
}
|
|
1194
|
+
return await publishPackage(pkg.path, mergedOptions, bumpedVersions);
|
|
1195
|
+
});
|
|
1196
|
+
const batchResults = await Promise.all(batchPromises);
|
|
1197
|
+
results.push(...batchResults);
|
|
1198
|
+
if (options.verbose) {
|
|
1199
|
+
const batchSuccess = batchResults.filter((r) => r.success).length;
|
|
1200
|
+
const batchErrors = batchResults.filter((r) => !r.success).length;
|
|
1201
|
+
logger.debug(
|
|
1202
|
+
`Batch ${batchNumber} completed: ${batchSuccess} success, ${batchErrors} error(s)`
|
|
1203
|
+
);
|
|
1204
|
+
}
|
|
1205
|
+
const hasBatchErrors = batchResults.some((r) => !r.success);
|
|
1206
|
+
if (hasBatchErrors) {
|
|
1207
|
+
if (options.verbose) {
|
|
1208
|
+
logger.debug(
|
|
1209
|
+
`Batch ${batchNumber} had errors, stopping processing of remaining batches`
|
|
1210
|
+
);
|
|
1211
|
+
}
|
|
1212
|
+
break;
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
const successCount = results.filter(
|
|
1216
|
+
(r) => r.success && !r.warning?.includes('"private: true"')
|
|
1217
|
+
).length;
|
|
1218
|
+
const errorCount = results.filter((r) => !r.success).length;
|
|
1219
|
+
const warningCount = results.filter((r) => r.warning).length;
|
|
1220
|
+
const hasErrors = errorCount > 0;
|
|
1221
|
+
if (options.verbose) {
|
|
1222
|
+
logger.debug(
|
|
1223
|
+
`Publish all completed: ${successCount} success, ${errorCount} error(s), ${warningCount} warning(s)`
|
|
1224
|
+
);
|
|
1225
|
+
}
|
|
1226
|
+
return {
|
|
1227
|
+
results,
|
|
1228
|
+
hasErrors,
|
|
1229
|
+
successCount,
|
|
1230
|
+
errorCount,
|
|
1231
|
+
warningCount
|
|
1232
|
+
};
|
|
1233
|
+
} catch (error) {
|
|
1234
|
+
if (options.verbose) {
|
|
1235
|
+
logger.debug(
|
|
1236
|
+
`Publish all failed: ${error instanceof Error ? error.message : String(error)}`
|
|
1237
|
+
);
|
|
1238
|
+
}
|
|
1239
|
+
logger.error(
|
|
1240
|
+
re.red("Failed to publish packages:"),
|
|
1241
|
+
re.red(error instanceof Error ? error.message : String(error))
|
|
1242
|
+
);
|
|
1243
|
+
return {
|
|
1244
|
+
results: [],
|
|
1245
|
+
hasErrors: true,
|
|
1246
|
+
successCount: 0,
|
|
1247
|
+
errorCount: 1,
|
|
1248
|
+
warningCount: 0
|
|
1249
|
+
};
|
|
1250
|
+
}
|
|
1251
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@reliverse/publish",
|
|
3
|
+
"version": "2.2.7",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./dist/mod.d.ts",
|
|
9
|
+
"default": "./dist/mod.js"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"c12": "^3.3.3",
|
|
14
|
+
"@reliverse/typerso": "2.2.7",
|
|
15
|
+
"@reliverse/config": "2.2.7",
|
|
16
|
+
"@reliverse/relinka": "2.2.7",
|
|
17
|
+
"@reliverse/bump": "2.2.8",
|
|
18
|
+
"@reliverse/relico": "2.2.7",
|
|
19
|
+
"@reliverse/build": "2.2.7"
|
|
20
|
+
},
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"access": "public"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist",
|
|
26
|
+
"package.json"
|
|
27
|
+
],
|
|
28
|
+
"license": "MIT"
|
|
29
|
+
}
|