@bobfrankston/npmglobalize 1.0.12 → 1.0.14
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/cli.js +8 -0
- package/lib.d.ts +7 -3
- package/lib.js +358 -41
- package/package.json +2 -1
- package/.gitattributes +0 -10
- package/.globalize.jsonc +0 -8
- package/.vscode/settings.json +0 -22
- package/.vscode/spellright.dict +0 -2
- package/.vscode/tasks.json +0 -20
- package/cli.d.ts.map +0 -1
- package/cli.js.map +0 -1
- package/cli.ts +0 -197
- package/index.d.ts.map +0 -1
- package/index.js.map +0 -1
- package/index.ts +0 -12
- package/lib.d.ts.map +0 -1
- package/lib.js.map +0 -1
- package/lib.ts +0 -993
- package/token.env +0 -10
- package/token.env.example +0 -9
- package/tsconfig.json +0 -28
package/lib.ts
DELETED
|
@@ -1,993 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* npmglobalize - Transform file: dependencies to npm versions for publishing
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import fs from 'fs';
|
|
6
|
-
import path from 'path';
|
|
7
|
-
import { execSync, spawnSync } from 'child_process';
|
|
8
|
-
import readline from 'readline';
|
|
9
|
-
import libversion from 'libnpmversion';
|
|
10
|
-
|
|
11
|
-
/** ANSI color codes */
|
|
12
|
-
const colors = {
|
|
13
|
-
red: (text: string) => `\x1b[31m${text}\x1b[0m`,
|
|
14
|
-
yellow: (text: string) => `\x1b[33m${text}\x1b[0m`,
|
|
15
|
-
green: (text: string) => `\x1b[32m${text}\x1b[0m`,
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
/** Options for the globalize operation */
|
|
19
|
-
export interface GlobalizeOptions {
|
|
20
|
-
/** Bump type: patch (default), minor, major */
|
|
21
|
-
bump?: 'patch' | 'minor' | 'major';
|
|
22
|
-
/** Just transform, don't publish */
|
|
23
|
-
applyOnly?: boolean;
|
|
24
|
-
/** Restore from .dependencies */
|
|
25
|
-
cleanup?: boolean;
|
|
26
|
-
/** Global install after publish */
|
|
27
|
-
install?: boolean;
|
|
28
|
-
/** Also install in WSL */
|
|
29
|
-
wsl?: boolean;
|
|
30
|
-
/** Continue despite git errors */
|
|
31
|
-
force?: boolean;
|
|
32
|
-
/** Keep file: paths after publish (default true) */
|
|
33
|
-
files?: boolean;
|
|
34
|
-
/** Show what would happen */
|
|
35
|
-
dryRun?: boolean;
|
|
36
|
-
/** Suppress npm warnings (default true) */
|
|
37
|
-
quiet?: boolean;
|
|
38
|
-
/** Show verbose output */
|
|
39
|
-
verbose?: boolean;
|
|
40
|
-
/** Initialize git/npm if needed */
|
|
41
|
-
init?: boolean;
|
|
42
|
-
/** Git visibility: private (default) or public */
|
|
43
|
-
gitVisibility?: 'private' | 'public';
|
|
44
|
-
/** npm visibility: public (default) or private */
|
|
45
|
-
npmVisibility?: 'private' | 'public';
|
|
46
|
-
/** Custom commit message */
|
|
47
|
-
message?: string;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const DEP_KEYS = ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies'];
|
|
51
|
-
|
|
52
|
-
/** Read and parse package.json from a directory */
|
|
53
|
-
export function readPackageJson(dir: string): any {
|
|
54
|
-
const pkgPath = path.join(dir, 'package.json');
|
|
55
|
-
if (!fs.existsSync(pkgPath)) {
|
|
56
|
-
throw new Error(`package.json not found: ${pkgPath}`);
|
|
57
|
-
}
|
|
58
|
-
return JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/** Read .globalize.jsonc config file */
|
|
62
|
-
export function readConfig(dir: string): Partial<GlobalizeOptions> {
|
|
63
|
-
const configPath = path.join(dir, '.globalize.jsonc');
|
|
64
|
-
if (!fs.existsSync(configPath)) {
|
|
65
|
-
return {};
|
|
66
|
-
}
|
|
67
|
-
try {
|
|
68
|
-
const content = fs.readFileSync(configPath, 'utf-8');
|
|
69
|
-
// Strip comments for JSON parsing (simple implementation)
|
|
70
|
-
const jsonContent = content
|
|
71
|
-
.split('\n')
|
|
72
|
-
.map(line => line.replace(/\/\/.*$/, '').trim())
|
|
73
|
-
.filter(line => line.length > 0)
|
|
74
|
-
.join('\n');
|
|
75
|
-
return JSON.parse(jsonContent);
|
|
76
|
-
} catch (error: any) {
|
|
77
|
-
console.warn(`Warning: Could not parse .globalize.jsonc config: ${error.message}`);
|
|
78
|
-
return {};
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/** Write .globalize.jsonc config file */
|
|
83
|
-
export function writeConfig(dir: string, config: Partial<GlobalizeOptions>): void {
|
|
84
|
-
const configPath = path.join(dir, '.globalize.jsonc');
|
|
85
|
-
|
|
86
|
-
// Build content with comments
|
|
87
|
-
const lines = [
|
|
88
|
-
'{',
|
|
89
|
-
' // Only set options that differ from defaults',
|
|
90
|
-
' // Defaults: bump=patch, files=true, quiet=true, gitVisibility=private, npmVisibility=public',
|
|
91
|
-
''
|
|
92
|
-
];
|
|
93
|
-
|
|
94
|
-
// Add configured values
|
|
95
|
-
for (const [key, value] of Object.entries(config)) {
|
|
96
|
-
const jsonValue = typeof value === 'string' ? `"${value}"` : JSON.stringify(value);
|
|
97
|
-
lines.push(` "${key}": ${jsonValue},`);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// Add commented examples for other options
|
|
101
|
-
lines.push('');
|
|
102
|
-
lines.push(' // Available options:');
|
|
103
|
-
lines.push(' // "bump": "patch" | "minor" | "major"');
|
|
104
|
-
lines.push(' // "install": true | false // Auto-install globally after publish');
|
|
105
|
-
lines.push(' // "wsl": true | false // Also install in WSL');
|
|
106
|
-
lines.push(' // "files": true | false // Keep file: paths after publish');
|
|
107
|
-
lines.push(' // "force": true | false // Continue despite git errors');
|
|
108
|
-
lines.push(' // "quiet": true | false // Suppress npm warnings');
|
|
109
|
-
lines.push(' // "verbose": true | false // Show detailed output');
|
|
110
|
-
lines.push(' // "gitVisibility": "private" | "public"');
|
|
111
|
-
lines.push(' // "npmVisibility": "private" | "public"');
|
|
112
|
-
lines.push('}');
|
|
113
|
-
|
|
114
|
-
fs.writeFileSync(configPath, lines.join('\n') + '\n');
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/** Write package.json to a directory */
|
|
118
|
-
export function writePackageJson(dir: string, pkg: any): void {
|
|
119
|
-
const pkgPath = path.join(dir, 'package.json');
|
|
120
|
-
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/** Resolve a file: path to absolute path */
|
|
124
|
-
export function resolveFilePath(fileRef: string, baseDir: string): string {
|
|
125
|
-
const relativePath = fileRef.replace(/^file:/, '');
|
|
126
|
-
return path.resolve(baseDir, relativePath);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/** Check if a dependency value is a file: reference */
|
|
130
|
-
export function isFileRef(value: string): boolean {
|
|
131
|
-
return typeof value === 'string' && value.startsWith('file:');
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/** Get all file: dependencies from package.json */
|
|
135
|
-
export function getFileRefs(pkg: any): Map<string, { key: string; name: string; value: string }> {
|
|
136
|
-
const refs = new Map<string, { key: string; name: string; value: string }>();
|
|
137
|
-
for (const key of DEP_KEYS) {
|
|
138
|
-
if (!pkg[key]) continue;
|
|
139
|
-
for (const [name, value] of Object.entries(pkg[key])) {
|
|
140
|
-
if (isFileRef(value as string)) {
|
|
141
|
-
refs.set(`${key}:${name}`, { key, name, value: value as string });
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
return refs;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/** Transform file: dependencies to npm versions */
|
|
149
|
-
export function transformDeps(pkg: any, baseDir: string, verbose: boolean = false): boolean {
|
|
150
|
-
let transformed = false;
|
|
151
|
-
|
|
152
|
-
for (const key of DEP_KEYS) {
|
|
153
|
-
if (!pkg[key]) continue;
|
|
154
|
-
|
|
155
|
-
const dotKey = '.' + key;
|
|
156
|
-
const hasFileRefs = Object.values(pkg[key]).some(v => isFileRef(v as string));
|
|
157
|
-
|
|
158
|
-
if (!hasFileRefs) continue;
|
|
159
|
-
|
|
160
|
-
// If .dependencies already exists, sync any new file: refs to it
|
|
161
|
-
if (pkg[dotKey]) {
|
|
162
|
-
for (const [name, value] of Object.entries(pkg[key])) {
|
|
163
|
-
if (isFileRef(value as string)) {
|
|
164
|
-
pkg[dotKey][name] = value;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
} else {
|
|
168
|
-
// Backup original
|
|
169
|
-
pkg[dotKey] = { ...pkg[key] };
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// Transform file: refs to npm versions
|
|
173
|
-
for (const [name, value] of Object.entries(pkg[key])) {
|
|
174
|
-
if (isFileRef(value as string)) {
|
|
175
|
-
const targetPath = resolveFilePath(value as string, baseDir);
|
|
176
|
-
try {
|
|
177
|
-
const targetPkg = readPackageJson(targetPath);
|
|
178
|
-
const npmVersion = '^' + targetPkg.version;
|
|
179
|
-
pkg[key][name] = npmVersion;
|
|
180
|
-
if (verbose) {
|
|
181
|
-
console.log(` ${name}: ${value} -> ${npmVersion}`);
|
|
182
|
-
}
|
|
183
|
-
transformed = true;
|
|
184
|
-
}
|
|
185
|
-
catch (error: any) {
|
|
186
|
-
throw new Error(`Failed to resolve ${name} at ${targetPath}: ${error.message}`);
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
return transformed;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/** Restore file: dependencies from .dependencies */
|
|
196
|
-
export function restoreDeps(pkg: any, verbose: boolean = false): boolean {
|
|
197
|
-
let restored = false;
|
|
198
|
-
|
|
199
|
-
for (const key of DEP_KEYS) {
|
|
200
|
-
const dotKey = '.' + key;
|
|
201
|
-
if (pkg[dotKey]) {
|
|
202
|
-
pkg[key] = pkg[dotKey];
|
|
203
|
-
delete pkg[dotKey];
|
|
204
|
-
restored = true;
|
|
205
|
-
if (verbose) {
|
|
206
|
-
console.log(` Restored ${key} from ${dotKey}`);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
return restored;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
/** Check if .dependencies exist (already transformed) */
|
|
215
|
-
export function hasBackup(pkg: any): boolean {
|
|
216
|
-
return DEP_KEYS.some(key => pkg['.' + key]);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/** Run a command and return success status */
|
|
220
|
-
export function runCommand(cmd: string, args: string[], options: { silent?: boolean; cwd?: string } = {}): { success: boolean; output: string; stderr: string } {
|
|
221
|
-
const { silent = false, cwd } = options;
|
|
222
|
-
try {
|
|
223
|
-
const result = spawnSync(cmd, args, {
|
|
224
|
-
encoding: 'utf-8',
|
|
225
|
-
stdio: silent ? 'pipe' : 'inherit',
|
|
226
|
-
cwd,
|
|
227
|
-
shell: true // Use shell for better Windows compatibility and output capture
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
// For non-silent commands, we can't capture output when using 'inherit'
|
|
231
|
-
// So we return empty string for output, but the user sees it in the terminal
|
|
232
|
-
if (!silent) {
|
|
233
|
-
return {
|
|
234
|
-
success: result.status === 0,
|
|
235
|
-
output: '',
|
|
236
|
-
stderr: ''
|
|
237
|
-
};
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
const output = result.stdout || '';
|
|
241
|
-
const stderr = result.stderr || '';
|
|
242
|
-
return {
|
|
243
|
-
success: result.status === 0,
|
|
244
|
-
output: output,
|
|
245
|
-
stderr: stderr
|
|
246
|
-
};
|
|
247
|
-
}
|
|
248
|
-
catch (error: any) {
|
|
249
|
-
return { success: false, output: '', stderr: error.message };
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
/** Run a command and throw on failure */
|
|
254
|
-
export function runCommandOrThrow(cmd: string, args: string[], options: { silent?: boolean; cwd?: string } = {}): string {
|
|
255
|
-
// For commands that should throw, we need to capture output, so use silent=true
|
|
256
|
-
const captureOptions = { ...options, silent: true };
|
|
257
|
-
const result = spawnSync(cmd, args, {
|
|
258
|
-
encoding: 'utf-8',
|
|
259
|
-
stdio: 'pipe',
|
|
260
|
-
cwd: captureOptions.cwd,
|
|
261
|
-
shell: false
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
const stdout = result.stdout || '';
|
|
265
|
-
const stderr = result.stderr || '';
|
|
266
|
-
const output = stdout + stderr;
|
|
267
|
-
|
|
268
|
-
if (result.status !== 0) {
|
|
269
|
-
// Show the error output
|
|
270
|
-
if (stderr.trim()) {
|
|
271
|
-
console.error(colors.red(stderr.trim()));
|
|
272
|
-
}
|
|
273
|
-
if (stdout.trim()) {
|
|
274
|
-
console.log(stdout.trim());
|
|
275
|
-
}
|
|
276
|
-
throw new Error(`Command failed with exit code ${result.status}: ${cmd} ${args.join(' ')}`);
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
return output;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
/** Git status checks */
|
|
283
|
-
export interface GitStatus {
|
|
284
|
-
isRepo: boolean;
|
|
285
|
-
hasRemote: boolean;
|
|
286
|
-
hasUncommitted: boolean;
|
|
287
|
-
hasUnpushed: boolean;
|
|
288
|
-
hasMergeConflict: boolean;
|
|
289
|
-
isDetachedHead: boolean;
|
|
290
|
-
currentBranch: string;
|
|
291
|
-
remoteBranch: string;
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
export function getGitStatus(cwd: string): GitStatus {
|
|
295
|
-
const status: GitStatus = {
|
|
296
|
-
isRepo: false,
|
|
297
|
-
hasRemote: false,
|
|
298
|
-
hasUncommitted: false,
|
|
299
|
-
hasUnpushed: false,
|
|
300
|
-
hasMergeConflict: false,
|
|
301
|
-
isDetachedHead: false,
|
|
302
|
-
currentBranch: '',
|
|
303
|
-
remoteBranch: ''
|
|
304
|
-
};
|
|
305
|
-
|
|
306
|
-
// Check if git repo
|
|
307
|
-
const gitDir = path.join(cwd, '.git');
|
|
308
|
-
if (!fs.existsSync(gitDir)) {
|
|
309
|
-
return status;
|
|
310
|
-
}
|
|
311
|
-
status.isRepo = true;
|
|
312
|
-
|
|
313
|
-
// Check for merge conflicts
|
|
314
|
-
const mergeHead = path.join(gitDir, 'MERGE_HEAD');
|
|
315
|
-
status.hasMergeConflict = fs.existsSync(mergeHead);
|
|
316
|
-
|
|
317
|
-
// Get branch info
|
|
318
|
-
try {
|
|
319
|
-
const branch = execSync('git rev-parse --abbrev-ref HEAD 2>nul', { cwd, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'ignore'] }).trim();
|
|
320
|
-
status.currentBranch = branch;
|
|
321
|
-
status.isDetachedHead = branch === 'HEAD';
|
|
322
|
-
}
|
|
323
|
-
catch (error: any) {
|
|
324
|
-
// Ignore - might be no commits yet
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
// Check for remote
|
|
328
|
-
try {
|
|
329
|
-
const remote = execSync('git remote 2>nul', { cwd, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'ignore'] }).trim();
|
|
330
|
-
status.hasRemote = remote.length > 0;
|
|
331
|
-
}
|
|
332
|
-
catch (error: any) {
|
|
333
|
-
// Ignore
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
// Check for uncommitted changes
|
|
337
|
-
try {
|
|
338
|
-
const statusOutput = execSync('git status --porcelain 2>nul', { cwd, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'ignore'] });
|
|
339
|
-
status.hasUncommitted = statusOutput.trim().length > 0;
|
|
340
|
-
}
|
|
341
|
-
catch (error: any) {
|
|
342
|
-
// Ignore
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
// Check for unpushed commits
|
|
346
|
-
if (status.hasRemote && !status.isDetachedHead && status.currentBranch) {
|
|
347
|
-
try {
|
|
348
|
-
const unpushed = execSync(`git log origin/${status.currentBranch}..HEAD --oneline 2>nul`, {
|
|
349
|
-
cwd,
|
|
350
|
-
encoding: 'utf-8',
|
|
351
|
-
stdio: ['pipe', 'pipe', 'ignore']
|
|
352
|
-
}).trim();
|
|
353
|
-
status.hasUnpushed = unpushed.length > 0;
|
|
354
|
-
}
|
|
355
|
-
catch (error: any) {
|
|
356
|
-
// Ignore - might not have tracking branch
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
return status;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
/** Validate package.json for release */
|
|
364
|
-
export function validatePackageJson(pkg: any): string[] {
|
|
365
|
-
const errors: string[] = [];
|
|
366
|
-
const warnings: string[] = [];
|
|
367
|
-
|
|
368
|
-
if (!pkg.repository) {
|
|
369
|
-
errors.push('Missing repository field - required for npm packages');
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
if (!pkg.description) {
|
|
373
|
-
warnings.push('Missing description field');
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
if (!pkg.name) {
|
|
377
|
-
errors.push('Missing name field');
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
if (!pkg.version) {
|
|
381
|
-
errors.push('Missing version field');
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
// Print warnings
|
|
385
|
-
for (const w of warnings) {
|
|
386
|
-
console.log(` Warning: ${w}`);
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
return errors;
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
/** Prompt user for confirmation */
|
|
393
|
-
export async function confirm(message: string, defaultYes: boolean = false): Promise<boolean> {
|
|
394
|
-
const rl = readline.createInterface({
|
|
395
|
-
input: process.stdin,
|
|
396
|
-
output: process.stdout
|
|
397
|
-
});
|
|
398
|
-
|
|
399
|
-
const suffix = defaultYes ? '[Y/n]' : '[y/N]';
|
|
400
|
-
|
|
401
|
-
return new Promise((resolve) => {
|
|
402
|
-
rl.question(`${message} ${suffix} `, (answer) => {
|
|
403
|
-
rl.close();
|
|
404
|
-
const a = answer.trim().toLowerCase();
|
|
405
|
-
if (a === '') {
|
|
406
|
-
resolve(defaultYes);
|
|
407
|
-
} else {
|
|
408
|
-
resolve(a === 'y' || a === 'yes');
|
|
409
|
-
}
|
|
410
|
-
});
|
|
411
|
-
});
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
/** Check npm authentication status */
|
|
415
|
-
function checkNpmAuth(): { authenticated: boolean; username?: string; error?: string } {
|
|
416
|
-
try {
|
|
417
|
-
// Use shell:true on Windows for better compatibility
|
|
418
|
-
const result = spawnSync('npm', ['whoami'], {
|
|
419
|
-
encoding: 'utf-8',
|
|
420
|
-
shell: true,
|
|
421
|
-
stdio: ['ignore', 'pipe', 'pipe']
|
|
422
|
-
});
|
|
423
|
-
|
|
424
|
-
if (result.status === 0 && result.stdout && result.stdout.trim()) {
|
|
425
|
-
return { authenticated: true, username: result.stdout.trim() };
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
// Parse error message to determine the issue
|
|
429
|
-
const stderr = (result.stderr || '').toLowerCase();
|
|
430
|
-
if (stderr.includes('code eneedauth') || stderr.includes('not logged in')) {
|
|
431
|
-
return { authenticated: false, error: 'not logged in' };
|
|
432
|
-
} else if (stderr.includes('token') && (stderr.includes('expired') || stderr.includes('revoked'))) {
|
|
433
|
-
return { authenticated: false, error: 'token expired' };
|
|
434
|
-
} else if (stderr) {
|
|
435
|
-
return { authenticated: false, error: 'unknown auth error' };
|
|
436
|
-
} else {
|
|
437
|
-
// npm whoami failed but with no stderr - likely auth issue
|
|
438
|
-
return { authenticated: false, error: 'not logged in' };
|
|
439
|
-
}
|
|
440
|
-
} catch (error: any) {
|
|
441
|
-
return { authenticated: false, error: error.message };
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
/** Get authentication setup instructions */
|
|
446
|
-
function getAuthInstructions(): string {
|
|
447
|
-
const npmrcPath = path.join(process.env.USERPROFILE || process.env.HOME || '~', '.npmrc');
|
|
448
|
-
return `
|
|
449
|
-
Authentication Options:
|
|
450
|
-
|
|
451
|
-
1. ${colors.yellow('Create a Granular Access Token')} (recommended):
|
|
452
|
-
- Go to: https://www.npmjs.com/settings/[username]/tokens
|
|
453
|
-
- Click "Generate New Token" → "Granular Access Token"
|
|
454
|
-
- Set permissions: ${colors.green('Read and write')} for packages
|
|
455
|
-
- Enable: ${colors.green('Bypass 2FA requirement')} (if available)
|
|
456
|
-
- Copy the token and save it securely
|
|
457
|
-
|
|
458
|
-
2. ${colors.yellow('Set token via environment variable')}:
|
|
459
|
-
- Set: ${colors.green('NPM_TOKEN=npm_xxx...')}
|
|
460
|
-
- Or run: ${colors.green('$env:NPM_TOKEN="npm_xxx..."')} (PowerShell)
|
|
461
|
-
|
|
462
|
-
3. ${colors.yellow('Set token in .npmrc')}:
|
|
463
|
-
- Edit: ${colors.green(npmrcPath)}
|
|
464
|
-
- Add: ${colors.green('//registry.npmjs.org/:_authToken=npm_xxx...')}
|
|
465
|
-
|
|
466
|
-
4. ${colors.yellow('Use classic login')} (may require 2FA):
|
|
467
|
-
- Run: ${colors.green('npm login')}
|
|
468
|
-
- Follow interactive prompts
|
|
469
|
-
|
|
470
|
-
Note: npm now requires either 2FA or a granular token with bypass enabled.
|
|
471
|
-
`;
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
/** Ensure .gitignore exists and includes node_modules */
|
|
475
|
-
function ensureGitignore(cwd: string): void {
|
|
476
|
-
const gitignorePath = path.join(cwd, '.gitignore');
|
|
477
|
-
let content = '';
|
|
478
|
-
let needsUpdate = false;
|
|
479
|
-
|
|
480
|
-
// Read existing .gitignore if it exists
|
|
481
|
-
if (fs.existsSync(gitignorePath)) {
|
|
482
|
-
content = fs.readFileSync(gitignorePath, 'utf-8');
|
|
483
|
-
// Check if node_modules is already ignored
|
|
484
|
-
const lines = content.split('\n').map(l => l.trim());
|
|
485
|
-
const hasNodeModules = lines.some(line =>
|
|
486
|
-
line === 'node_modules' ||
|
|
487
|
-
line === 'node_modules/' ||
|
|
488
|
-
line === '/node_modules' ||
|
|
489
|
-
line === '/node_modules/'
|
|
490
|
-
);
|
|
491
|
-
if (!hasNodeModules) {
|
|
492
|
-
console.log(colors.yellow(' Warning: node_modules not found in .gitignore, adding it...'));
|
|
493
|
-
needsUpdate = true;
|
|
494
|
-
}
|
|
495
|
-
} else {
|
|
496
|
-
console.log(' Creating .gitignore...');
|
|
497
|
-
needsUpdate = true;
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
// Update .gitignore if needed
|
|
501
|
-
if (needsUpdate) {
|
|
502
|
-
if (!content || content.trim() === '') {
|
|
503
|
-
// Create new .gitignore with common patterns
|
|
504
|
-
content = `node_modules/
|
|
505
|
-
.env*
|
|
506
|
-
*cert*/
|
|
507
|
-
*certs*/
|
|
508
|
-
*.pem
|
|
509
|
-
*.key
|
|
510
|
-
*.p12
|
|
511
|
-
*.pfx
|
|
512
|
-
configuration.json
|
|
513
|
-
token
|
|
514
|
-
tokens
|
|
515
|
-
*.token
|
|
516
|
-
cruft/
|
|
517
|
-
prev/
|
|
518
|
-
tests/
|
|
519
|
-
*.log
|
|
520
|
-
.DS_Store
|
|
521
|
-
Thumbs.db
|
|
522
|
-
`;
|
|
523
|
-
} else {
|
|
524
|
-
// Add node_modules to existing .gitignore
|
|
525
|
-
if (!content.endsWith('\n')) {
|
|
526
|
-
content += '\n';
|
|
527
|
-
}
|
|
528
|
-
content = 'node_modules/\n' + content;
|
|
529
|
-
}
|
|
530
|
-
fs.writeFileSync(gitignorePath, content);
|
|
531
|
-
console.log(colors.green(' ✓ .gitignore updated'));
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
/** Initialize git repository */
|
|
536
|
-
export async function initGit(cwd: string, visibility: 'private' | 'public', dryRun: boolean): Promise<boolean> {
|
|
537
|
-
const pkg = readPackageJson(cwd);
|
|
538
|
-
const repoName = pkg.name.replace(/^@[^/]+\//, ''); // Remove scope
|
|
539
|
-
|
|
540
|
-
console.log('Initializing git repository...');
|
|
541
|
-
|
|
542
|
-
// Explicit visibility confirmation
|
|
543
|
-
if (visibility === 'public') {
|
|
544
|
-
const ok = await confirm('Create PUBLIC git repo - anyone can see your code. Continue?', false);
|
|
545
|
-
if (!ok) {
|
|
546
|
-
console.log('Aborted.');
|
|
547
|
-
return false;
|
|
548
|
-
}
|
|
549
|
-
} else {
|
|
550
|
-
console.log(`Creating PRIVATE git repo '${repoName}' (default)...`);
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
if (dryRun) {
|
|
554
|
-
console.log(' [dry-run] Would run: git init');
|
|
555
|
-
console.log(` [dry-run] Would run: gh repo create ${repoName} --${visibility} --source=. --push`);
|
|
556
|
-
return true;
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
// Ensure .gitignore exists and includes node_modules
|
|
560
|
-
ensureGitignore(cwd);
|
|
561
|
-
|
|
562
|
-
// git init
|
|
563
|
-
runCommandOrThrow('git', ['init'], { cwd });
|
|
564
|
-
runCommandOrThrow('git', ['add', '-A'], { cwd });
|
|
565
|
-
runCommandOrThrow('git', ['commit', '-m', 'Initial commit'], { cwd });
|
|
566
|
-
|
|
567
|
-
// Create GitHub repo
|
|
568
|
-
const visFlag = visibility === 'private' ? '--private' : '--public';
|
|
569
|
-
runCommandOrThrow('gh', ['repo', 'create', repoName, visFlag, '--source=.', '--push'], { cwd });
|
|
570
|
-
|
|
571
|
-
// Update package.json with repository field
|
|
572
|
-
try {
|
|
573
|
-
const remoteUrl = execSync('git remote get-url origin', { cwd, encoding: 'utf-8' }).trim();
|
|
574
|
-
pkg.repository = {
|
|
575
|
-
type: 'git',
|
|
576
|
-
url: remoteUrl
|
|
577
|
-
};
|
|
578
|
-
writePackageJson(cwd, pkg);
|
|
579
|
-
console.log(' Updated package.json with repository field');
|
|
580
|
-
}
|
|
581
|
-
catch (error: any) {
|
|
582
|
-
console.log(' Warning: Could not update repository field');
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
return true;
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
/** Main globalize function */
|
|
589
|
-
export async function globalize(cwd: string, options: GlobalizeOptions = {}): Promise<boolean> {
|
|
590
|
-
const {
|
|
591
|
-
bump = 'patch',
|
|
592
|
-
applyOnly = false,
|
|
593
|
-
cleanup = false,
|
|
594
|
-
install = false,
|
|
595
|
-
wsl = false,
|
|
596
|
-
force = false,
|
|
597
|
-
files = true,
|
|
598
|
-
dryRun = false,
|
|
599
|
-
quiet = true,
|
|
600
|
-
verbose = false,
|
|
601
|
-
init = false,
|
|
602
|
-
gitVisibility = 'private',
|
|
603
|
-
npmVisibility = 'public',
|
|
604
|
-
message
|
|
605
|
-
} = options;
|
|
606
|
-
|
|
607
|
-
// Handle cleanup mode
|
|
608
|
-
if (cleanup) {
|
|
609
|
-
console.log('Restoring dependencies from backup...');
|
|
610
|
-
const pkg = readPackageJson(cwd);
|
|
611
|
-
if (restoreDeps(pkg, verbose)) {
|
|
612
|
-
if (!dryRun) {
|
|
613
|
-
writePackageJson(cwd, pkg);
|
|
614
|
-
}
|
|
615
|
-
console.log('Dependencies restored.');
|
|
616
|
-
} else {
|
|
617
|
-
console.log('No backup found - nothing to restore.');
|
|
618
|
-
}
|
|
619
|
-
return true;
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
// Check npm authentication early (unless dry-run or apply-only)
|
|
623
|
-
if (!dryRun && !applyOnly) {
|
|
624
|
-
const authStatus = checkNpmAuth();
|
|
625
|
-
if (!authStatus.authenticated) {
|
|
626
|
-
console.error(colors.red('\n✗ npm authentication required'));
|
|
627
|
-
console.error('');
|
|
628
|
-
if (authStatus.error === 'token expired') {
|
|
629
|
-
console.error('Your npm access token has expired or been revoked.');
|
|
630
|
-
console.error('');
|
|
631
|
-
console.error('To fix this, run:');
|
|
632
|
-
console.error(colors.yellow(' npm logout'));
|
|
633
|
-
console.error(colors.yellow(' npm login'));
|
|
634
|
-
} else if (authStatus.error === 'not logged in') {
|
|
635
|
-
console.error('You are not logged in to npm.');
|
|
636
|
-
console.error('');
|
|
637
|
-
console.error('To fix this, run:');
|
|
638
|
-
console.error(colors.yellow(' npm login'));
|
|
639
|
-
} else {
|
|
640
|
-
console.error(`Authentication error: ${authStatus.error}`);
|
|
641
|
-
console.error('');
|
|
642
|
-
console.error('Try running:');
|
|
643
|
-
console.error(colors.yellow(' npm whoami'));
|
|
644
|
-
}
|
|
645
|
-
console.error('');
|
|
646
|
-
console.error('After logging in, try running this command again.');
|
|
647
|
-
return false;
|
|
648
|
-
}
|
|
649
|
-
if (verbose) {
|
|
650
|
-
console.log(colors.green(`✓ Authenticated as ${authStatus.username}`));
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
// Check git status
|
|
655
|
-
const gitStatus = getGitStatus(cwd);
|
|
656
|
-
|
|
657
|
-
// Handle init mode
|
|
658
|
-
if (!gitStatus.isRepo) {
|
|
659
|
-
console.log('No git repository found.');
|
|
660
|
-
if (dryRun) {
|
|
661
|
-
console.log(' [dry-run] Would initialize git repository');
|
|
662
|
-
} else if (!init) {
|
|
663
|
-
const ok = await confirm('Initialize git repository?', true);
|
|
664
|
-
if (!ok) {
|
|
665
|
-
console.log('Aborted. Run with --init to initialize.');
|
|
666
|
-
return false;
|
|
667
|
-
}
|
|
668
|
-
const success = await initGit(cwd, gitVisibility, dryRun);
|
|
669
|
-
if (!success) return false;
|
|
670
|
-
} else {
|
|
671
|
-
const success = await initGit(cwd, gitVisibility, dryRun);
|
|
672
|
-
if (!success) return false;
|
|
673
|
-
}
|
|
674
|
-
} else if (init && !gitStatus.hasRemote) {
|
|
675
|
-
// Git repo exists but no remote - create GitHub repo
|
|
676
|
-
const success = await initGit(cwd, gitVisibility, dryRun);
|
|
677
|
-
if (!success) return false;
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
// Re-check git status after potential init
|
|
681
|
-
const currentGitStatus = getGitStatus(cwd);
|
|
682
|
-
|
|
683
|
-
// Validate git state
|
|
684
|
-
if (currentGitStatus.hasMergeConflict) {
|
|
685
|
-
console.error(colors.red('ERROR: Merge conflict detected. Resolve before releasing.'));
|
|
686
|
-
return false;
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
if (currentGitStatus.isDetachedHead && !force) {
|
|
690
|
-
console.error(colors.red('ERROR: Detached HEAD state. Use --force to continue anyway.'));
|
|
691
|
-
return false;
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
if (currentGitStatus.isDetachedHead && force) {
|
|
695
|
-
console.log(colors.yellow('Warning: Detached HEAD state - continuing with --force'));
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
// Read package.json
|
|
699
|
-
const pkg = readPackageJson(cwd);
|
|
700
|
-
|
|
701
|
-
// Auto-add repository field if git exists but field is missing
|
|
702
|
-
if (currentGitStatus.isRepo && currentGitStatus.hasRemote && !pkg.repository) {
|
|
703
|
-
try {
|
|
704
|
-
const remoteUrl = execSync('git remote get-url origin', { cwd, encoding: 'utf-8' }).trim();
|
|
705
|
-
pkg.repository = {
|
|
706
|
-
type: 'git',
|
|
707
|
-
url: remoteUrl
|
|
708
|
-
};
|
|
709
|
-
writePackageJson(cwd, pkg);
|
|
710
|
-
console.log('Added repository field from git remote.');
|
|
711
|
-
}
|
|
712
|
-
catch (error: any) {
|
|
713
|
-
// Ignore - will be caught by validation
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
// Validate package.json
|
|
718
|
-
console.log('Validating package.json...');
|
|
719
|
-
const errors = validatePackageJson(pkg);
|
|
720
|
-
if (errors.length > 0) {
|
|
721
|
-
// Check if missing repository and no remote - suggest init
|
|
722
|
-
if (!pkg.repository && !currentGitStatus.hasRemote) {
|
|
723
|
-
console.error(colors.red('ERROR: Missing repository field and no git remote configured.'));
|
|
724
|
-
console.error(colors.red('Run with --init to set up GitHub repository.'));
|
|
725
|
-
return false;
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
for (const e of errors) {
|
|
729
|
-
console.error(colors.red(` ERROR: ${e}`));
|
|
730
|
-
}
|
|
731
|
-
if (!force) {
|
|
732
|
-
return false;
|
|
733
|
-
}
|
|
734
|
-
console.log(colors.yellow('Continuing with --force despite errors...'));
|
|
735
|
-
}
|
|
736
|
-
|
|
737
|
-
// Check npm visibility - explicit confirmation
|
|
738
|
-
if (npmVisibility === 'private') {
|
|
739
|
-
if (!pkg.private) {
|
|
740
|
-
const ok = await confirm('Mark npm package PRIVATE - will not publish to npm. Continue?', false);
|
|
741
|
-
if (!ok) {
|
|
742
|
-
console.log('Aborted.');
|
|
743
|
-
return false;
|
|
744
|
-
}
|
|
745
|
-
pkg.private = true;
|
|
746
|
-
}
|
|
747
|
-
} else {
|
|
748
|
-
// Default is public - just inform
|
|
749
|
-
console.log(`Will publish '${pkg.name}' to PUBLIC npm registry (default).`);
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
// Transform dependencies
|
|
753
|
-
console.log('Transforming file: dependencies...');
|
|
754
|
-
const alreadyTransformed = hasBackup(pkg);
|
|
755
|
-
if (alreadyTransformed) {
|
|
756
|
-
console.log(' Already transformed (found .dependencies)');
|
|
757
|
-
}
|
|
758
|
-
|
|
759
|
-
const transformed = transformDeps(pkg, cwd, verbose);
|
|
760
|
-
if (transformed) {
|
|
761
|
-
console.log(' Dependencies transformed.');
|
|
762
|
-
if (!dryRun) {
|
|
763
|
-
writePackageJson(cwd, pkg);
|
|
764
|
-
}
|
|
765
|
-
} else if (!alreadyTransformed) {
|
|
766
|
-
console.log(' No file: dependencies found.');
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
if (applyOnly) {
|
|
770
|
-
console.log('Transform complete (--apply mode).');
|
|
771
|
-
return true;
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
// Skip if private
|
|
775
|
-
if (pkg.private) {
|
|
776
|
-
console.log('Package is private - skipping npm publish.');
|
|
777
|
-
return true;
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
// Check if there are changes to commit or a custom message
|
|
781
|
-
if (!currentGitStatus.hasUncommitted && !message) {
|
|
782
|
-
console.log('');
|
|
783
|
-
console.log('No changes to commit and no custom message specified.');
|
|
784
|
-
console.log('Nothing to release. Use -m "message" to force a release.');
|
|
785
|
-
return true;
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
// Ensure node_modules is in .gitignore before any git operations
|
|
789
|
-
if (currentGitStatus.isRepo && !dryRun) {
|
|
790
|
-
ensureGitignore(cwd);
|
|
791
|
-
}
|
|
792
|
-
|
|
793
|
-
// Git operations
|
|
794
|
-
if (currentGitStatus.hasUncommitted) {
|
|
795
|
-
const commitMsg = message || 'Pre-release commit';
|
|
796
|
-
console.log(`Committing changes: ${commitMsg}`);
|
|
797
|
-
if (!dryRun) {
|
|
798
|
-
runCommand('git', ['add', '-A'], { cwd });
|
|
799
|
-
runCommand('git', ['commit', '-m', commitMsg], { cwd });
|
|
800
|
-
} else {
|
|
801
|
-
console.log(' [dry-run] Would commit changes');
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
// Version bump
|
|
806
|
-
console.log(`Bumping version (${bump})...`);
|
|
807
|
-
if (!dryRun) {
|
|
808
|
-
try {
|
|
809
|
-
// Use libnpmversion - it will create commit and tag automatically
|
|
810
|
-
const newVersion = await libversion(bump, {
|
|
811
|
-
path: cwd
|
|
812
|
-
});
|
|
813
|
-
|
|
814
|
-
console.log(colors.green(`Version bumped to ${newVersion}`));
|
|
815
|
-
} catch (error: any) {
|
|
816
|
-
console.error(colors.red('ERROR: Version bump failed:'), error.message);
|
|
817
|
-
if (!force) {
|
|
818
|
-
return false;
|
|
819
|
-
}
|
|
820
|
-
console.log(colors.yellow('Continuing with --force...'));
|
|
821
|
-
}
|
|
822
|
-
} else {
|
|
823
|
-
console.log(` [dry-run] Would run: npm version ${bump}`);
|
|
824
|
-
}
|
|
825
|
-
|
|
826
|
-
// Publish
|
|
827
|
-
console.log('Publishing to npm...');
|
|
828
|
-
const npmArgs = ['publish'];
|
|
829
|
-
if (quiet) {
|
|
830
|
-
npmArgs.push('--quiet');
|
|
831
|
-
}
|
|
832
|
-
if (!dryRun) {
|
|
833
|
-
// Run with silent:true to capture the error message
|
|
834
|
-
const publishResult = runCommand('npm', npmArgs, { cwd, silent: true });
|
|
835
|
-
if (!publishResult.success) {
|
|
836
|
-
console.error(colors.red('\nERROR: npm publish failed\n'));
|
|
837
|
-
|
|
838
|
-
// Combine stdout and stderr for error analysis
|
|
839
|
-
const errorOutput = (publishResult.output + '\n' + publishResult.stderr).toLowerCase();
|
|
840
|
-
|
|
841
|
-
// Check for specific error types
|
|
842
|
-
if (errorOutput.includes('e403') && (errorOutput.includes('two-factor') || errorOutput.includes('2fa'))) {
|
|
843
|
-
console.error(colors.red('⚠ 2FA Required'));
|
|
844
|
-
console.error('');
|
|
845
|
-
console.error('Your npm account requires two-factor authentication or a');
|
|
846
|
-
console.error('granular access token with "bypass 2FA" enabled.');
|
|
847
|
-
console.error('');
|
|
848
|
-
console.error(colors.yellow('Your options:'));
|
|
849
|
-
console.error('');
|
|
850
|
-
console.error('1. Enable 2FA on your npm account:');
|
|
851
|
-
console.error(colors.green(' https://www.npmjs.com/settings/[username]/tfa'));
|
|
852
|
-
console.error('');
|
|
853
|
-
console.error('2. Create a granular access token:');
|
|
854
|
-
console.error(colors.green(' https://www.npmjs.com/settings/[username]/tokens'));
|
|
855
|
-
console.error(' - Select "Granular Access Token"');
|
|
856
|
-
console.error(' - Set permissions: Read and write');
|
|
857
|
-
console.error(' - Enable "Bypass 2FA requirement"');
|
|
858
|
-
console.error(' - Then set via environment variable or .npmrc:');
|
|
859
|
-
console.error(colors.green(' $env:NPM_TOKEN="npm_xxx..."'));
|
|
860
|
-
console.error(' Or add to ~/.npmrc:');
|
|
861
|
-
console.error(colors.green(' //registry.npmjs.org/:_authToken=npm_xxx...'));
|
|
862
|
-
console.error('');
|
|
863
|
-
} else if (errorOutput.includes('e404') || errorOutput.includes('not found')) {
|
|
864
|
-
console.error(colors.yellow('Package not found or access denied.'));
|
|
865
|
-
console.error('');
|
|
866
|
-
console.error('Possible causes:');
|
|
867
|
-
console.error(' • Package name is not available');
|
|
868
|
-
console.error(' • You don\'t have permission to publish to this package');
|
|
869
|
-
console.error(' • Scope (@username/) requires organization access');
|
|
870
|
-
console.error('');
|
|
871
|
-
} else if (errorOutput.includes('e402') || errorOutput.includes('payment required')) {
|
|
872
|
-
console.error(colors.yellow('Payment required for private packages.'));
|
|
873
|
-
console.error('');
|
|
874
|
-
console.error('Private scoped packages require a paid npm account.');
|
|
875
|
-
console.error('Either upgrade your account or make the package public.');
|
|
876
|
-
console.error('');
|
|
877
|
-
} else {
|
|
878
|
-
// Show the actual error output
|
|
879
|
-
if (publishResult.stderr) {
|
|
880
|
-
console.error(publishResult.stderr);
|
|
881
|
-
}
|
|
882
|
-
if (publishResult.output) {
|
|
883
|
-
console.error(publishResult.output);
|
|
884
|
-
}
|
|
885
|
-
console.error('');
|
|
886
|
-
console.error(colors.yellow('Common causes:'));
|
|
887
|
-
console.error(colors.yellow(' 1. Not logged in - run: npm login'));
|
|
888
|
-
console.error(colors.yellow(' 2. Version already published'));
|
|
889
|
-
console.error(colors.yellow(' 3. Authentication token expired'));
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
if (transformed) {
|
|
893
|
-
console.log('Run --cleanup to restore file: dependencies');
|
|
894
|
-
}
|
|
895
|
-
return false;
|
|
896
|
-
}
|
|
897
|
-
console.log(colors.green('✓ Published to npm'));
|
|
898
|
-
} else {
|
|
899
|
-
console.log(` [dry-run] Would run: npm publish ${quiet ? '--quiet' : ''}`);
|
|
900
|
-
}
|
|
901
|
-
|
|
902
|
-
// Push to git
|
|
903
|
-
if (currentGitStatus.hasRemote) {
|
|
904
|
-
console.log('Pushing to git...');
|
|
905
|
-
if (!dryRun) {
|
|
906
|
-
runCommand('git', ['push'], { cwd });
|
|
907
|
-
runCommand('git', ['push', '--tags'], { cwd });
|
|
908
|
-
} else {
|
|
909
|
-
console.log(' [dry-run] Would push to git');
|
|
910
|
-
}
|
|
911
|
-
}
|
|
912
|
-
|
|
913
|
-
// Global install
|
|
914
|
-
const pkgName = pkg.name;
|
|
915
|
-
if (install) {
|
|
916
|
-
console.log(`Installing globally: ${pkgName}...`);
|
|
917
|
-
if (!dryRun) {
|
|
918
|
-
runCommand('npm', ['install', '-g', pkgName], { cwd });
|
|
919
|
-
} else {
|
|
920
|
-
console.log(` [dry-run] Would run: npm install -g ${pkgName}`);
|
|
921
|
-
}
|
|
922
|
-
}
|
|
923
|
-
|
|
924
|
-
if (wsl) {
|
|
925
|
-
console.log(`Installing in WSL: ${pkgName}...`);
|
|
926
|
-
if (!dryRun) {
|
|
927
|
-
const wslResult = runCommand('wsl', ['npm', 'install', '-g', pkgName], { cwd });
|
|
928
|
-
if (!wslResult.success) {
|
|
929
|
-
console.log(' Warning: WSL install failed (is npm installed in WSL?)');
|
|
930
|
-
}
|
|
931
|
-
} else {
|
|
932
|
-
console.log(` [dry-run] Would run: wsl npm install -g ${pkgName}`);
|
|
933
|
-
}
|
|
934
|
-
}
|
|
935
|
-
|
|
936
|
-
// Finalize - restore file: paths if --files mode (default)
|
|
937
|
-
if (files && transformed) {
|
|
938
|
-
console.log('Restoring file: dependencies (--files mode)...');
|
|
939
|
-
const finalPkg = readPackageJson(cwd);
|
|
940
|
-
restoreDeps(finalPkg, verbose);
|
|
941
|
-
if (!dryRun) {
|
|
942
|
-
writePackageJson(cwd, finalPkg);
|
|
943
|
-
// Commit the restore
|
|
944
|
-
runCommand('git', ['add', 'package.json'], { cwd });
|
|
945
|
-
runCommand('git', ['commit', '-m', 'Restore file: dependencies'], { cwd });
|
|
946
|
-
if (currentGitStatus.hasRemote) {
|
|
947
|
-
runCommand('git', ['push'], { cwd });
|
|
948
|
-
}
|
|
949
|
-
}
|
|
950
|
-
} else if (!files) {
|
|
951
|
-
console.log('Keeping npm versions (--nofiles mode).');
|
|
952
|
-
}
|
|
953
|
-
|
|
954
|
-
console.log('Done!');
|
|
955
|
-
|
|
956
|
-
// Print summary
|
|
957
|
-
console.log('');
|
|
958
|
-
console.log(colors.green('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
|
|
959
|
-
console.log(colors.green('✓ Release Summary'));
|
|
960
|
-
console.log(colors.green('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
|
|
961
|
-
const finalPkg = readPackageJson(cwd);
|
|
962
|
-
console.log(` Package: ${colors.green(finalPkg.name)}`);
|
|
963
|
-
console.log(` Version: ${colors.green('v' + finalPkg.version)}`);
|
|
964
|
-
console.log(` Published: ${colors.green('✓')}`);
|
|
965
|
-
console.log(` Git pushed: ${colors.green('✓')}`);
|
|
966
|
-
if (install) {
|
|
967
|
-
console.log(` Installed globally: ${colors.green('✓')}`);
|
|
968
|
-
}
|
|
969
|
-
if (wsl) {
|
|
970
|
-
console.log(` Installed in WSL: ${colors.green('✓')}`);
|
|
971
|
-
}
|
|
972
|
-
if (files && transformed) {
|
|
973
|
-
console.log(` Restored file: deps: ${colors.green('✓')}`);
|
|
974
|
-
}
|
|
975
|
-
console.log(colors.green('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
|
|
976
|
-
console.log('');
|
|
977
|
-
console.log(`Run: ${colors.green(finalPkg.name.includes('/') ? finalPkg.name.split('/')[1] : finalPkg.name)}`);
|
|
978
|
-
console.log('');
|
|
979
|
-
|
|
980
|
-
return true;
|
|
981
|
-
}
|
|
982
|
-
|
|
983
|
-
export default {
|
|
984
|
-
globalize,
|
|
985
|
-
transformDeps,
|
|
986
|
-
restoreDeps,
|
|
987
|
-
readPackageJson,
|
|
988
|
-
writePackageJson,
|
|
989
|
-
getGitStatus,
|
|
990
|
-
validatePackageJson,
|
|
991
|
-
confirm,
|
|
992
|
-
initGit
|
|
993
|
-
};
|