@bobfrankston/npmglobalize 1.0.143 → 1.0.145
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 +2 -1
- package/ignorepatterns.json5 +2 -1
- package/lib.d.ts +5 -1
- package/lib.js +161 -44
- package/package.json +19 -5
package/cli.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* npmglobalize CLI - Transform file: dependencies to npm versions for publishing
|
|
4
4
|
*/
|
|
5
|
-
import { globalize, globalizeWorkspace, readConfig, readPackageJson, readUserNpmConfig, writeConfig, writePackageJson, confirm, getBuildIssues, clearBuildIssues, recordBuildIssue, extractFirstTscError } from './lib.js';
|
|
5
|
+
import { globalize, globalizeWorkspace, installCleanupHandlers, readConfig, readPackageJson, readUserNpmConfig, writeConfig, writePackageJson, confirm, getBuildIssues, clearBuildIssues, recordBuildIssue, extractFirstTscError } from './lib.js';
|
|
6
6
|
import fs from 'fs';
|
|
7
7
|
import path from 'path';
|
|
8
8
|
import { styleText } from 'util';
|
|
@@ -343,6 +343,7 @@ function printBuildSummary() {
|
|
|
343
343
|
console.log('');
|
|
344
344
|
}
|
|
345
345
|
export async function main() {
|
|
346
|
+
installCleanupHandlers();
|
|
346
347
|
// Show version at the very start
|
|
347
348
|
const ownPkgPath = path.join(__dirname, 'package.json');
|
|
348
349
|
const ownPkg = JSON.parse(fs.readFileSync(ownPkgPath, 'utf-8'));
|
package/ignorepatterns.json5
CHANGED
package/lib.d.ts
CHANGED
|
@@ -132,6 +132,8 @@ export declare function readConfig(dir: string): Partial<GlobalizeOptions>;
|
|
|
132
132
|
export declare function writeConfig(dir: string, config: Partial<GlobalizeOptions>, explicitKeys?: Set<string>): void;
|
|
133
133
|
/** Write package.json to a directory */
|
|
134
134
|
export declare function writePackageJson(dir: string, pkg: any): void;
|
|
135
|
+
/** Install signal/exit handlers that restore `.dependencies` backups on abnormal exit. */
|
|
136
|
+
export declare function installCleanupHandlers(): void;
|
|
135
137
|
/** Resolve a file: path to absolute path */
|
|
136
138
|
export declare function resolveFilePath(fileRef: string, baseDir: string): string;
|
|
137
139
|
/** Check if a dependency value is a file: reference */
|
|
@@ -188,7 +190,8 @@ export declare function transformDeps(pkg: any, baseDir: string, verbose?: boole
|
|
|
188
190
|
path: string;
|
|
189
191
|
}>;
|
|
190
192
|
};
|
|
191
|
-
/** Restore file: dependencies from .dependencies
|
|
193
|
+
/** Restore file: dependencies from .dependencies using a three-way merge that
|
|
194
|
+
* preserves any external modifications made since the last transform. */
|
|
192
195
|
export declare function restoreDeps(pkg: any, verbose?: boolean): boolean;
|
|
193
196
|
/** Check if .dependencies exist (already transformed) */
|
|
194
197
|
export declare function hasBackup(pkg: any): boolean;
|
|
@@ -268,6 +271,7 @@ declare const _default: {
|
|
|
268
271
|
validatePackageJson: typeof validatePackageJson;
|
|
269
272
|
confirm: typeof confirm;
|
|
270
273
|
initGit: typeof initGit;
|
|
274
|
+
installCleanupHandlers: typeof installCleanupHandlers;
|
|
271
275
|
};
|
|
272
276
|
export default _default;
|
|
273
277
|
//# sourceMappingURL=lib.d.ts.map
|
package/lib.js
CHANGED
|
@@ -313,10 +313,111 @@ export function writeConfig(dir, config, explicitKeys) {
|
|
|
313
313
|
lines.push('}');
|
|
314
314
|
fs.writeFileSync(configPath, lines.join('\n') + '\n');
|
|
315
315
|
}
|
|
316
|
+
/** Package.json paths with a live `.dependencies` backup on disk.
|
|
317
|
+
* Tracked so the exit handler can restore them if the process dies mid-run. */
|
|
318
|
+
const dirtyPackageJsons = new Set();
|
|
316
319
|
/** Write package.json to a directory */
|
|
317
320
|
export function writePackageJson(dir, pkg) {
|
|
318
321
|
const pkgPath = path.join(dir, 'package.json');
|
|
319
322
|
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
|
|
323
|
+
if (hasBackup(pkg)) {
|
|
324
|
+
dirtyPackageJsons.add(pkgPath);
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
dirtyPackageJsons.delete(pkgPath);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
/** Three-way merge that restores `.dependencies` backup into `dependencies` while
|
|
331
|
+
* preserving external modifications made since the last transform.
|
|
332
|
+
*
|
|
333
|
+
* Uses `.transformedSnapshot` (written by transformDeps) to distinguish
|
|
334
|
+
* "untouched transform output" from "externally modified":
|
|
335
|
+
* - current[name] === snapshot[name] → untouched, restore to backup value
|
|
336
|
+
* - current[name] !== snapshot[name] → modified externally, keep current
|
|
337
|
+
* - name in backup but missing from current → removed externally, leave removed
|
|
338
|
+
* - name in current but not in backup → added externally, keep
|
|
339
|
+
*
|
|
340
|
+
* If no snapshot exists (legacy backup from before this mechanism), falls back to
|
|
341
|
+
* blind restore plus new-dep merge, matching the old behavior. */
|
|
342
|
+
function mergeRestore(pkg, verbose = false) {
|
|
343
|
+
const snapshot = pkg['.transformedSnapshot'];
|
|
344
|
+
let restored = false;
|
|
345
|
+
for (const key of DEP_KEYS) {
|
|
346
|
+
const dotKey = '.' + key;
|
|
347
|
+
if (!pkg[dotKey])
|
|
348
|
+
continue;
|
|
349
|
+
const backup = pkg[dotKey];
|
|
350
|
+
const current = pkg[key] || {};
|
|
351
|
+
const snap = snapshot?.[key];
|
|
352
|
+
const merged = { ...current };
|
|
353
|
+
for (const [name, backupValue] of Object.entries(backup)) {
|
|
354
|
+
if (!(name in current))
|
|
355
|
+
continue; // removed externally — leave removed
|
|
356
|
+
if (snap && name in snap) {
|
|
357
|
+
if (current[name] === snap[name]) {
|
|
358
|
+
merged[name] = backupValue; // untouched — restore
|
|
359
|
+
}
|
|
360
|
+
else if (verbose) {
|
|
361
|
+
console.log(colors.yellow(` Preserving externally-modified ${key}.${name}: ${current[name]} (transform wrote ${snap[name]})`));
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
else {
|
|
365
|
+
merged[name] = backupValue; // legacy: no snapshot, blind restore
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
pkg[key] = merged;
|
|
369
|
+
delete pkg[dotKey];
|
|
370
|
+
restored = true;
|
|
371
|
+
}
|
|
372
|
+
if (restored && snapshot) {
|
|
373
|
+
delete pkg['.transformedSnapshot'];
|
|
374
|
+
}
|
|
375
|
+
return restored;
|
|
376
|
+
}
|
|
377
|
+
/** Synchronous emergency restore — called from signal/exit handlers.
|
|
378
|
+
* Reads each tracked package.json, runs the three-way merge, writes it back. */
|
|
379
|
+
function emergencyRestoreDeps() {
|
|
380
|
+
if (dirtyPackageJsons.size === 0)
|
|
381
|
+
return;
|
|
382
|
+
console.error(colors.yellow(`\nRestoring file: dependencies in ${dirtyPackageJsons.size} package.json file(s)...`));
|
|
383
|
+
for (const pkgPath of dirtyPackageJsons) {
|
|
384
|
+
try {
|
|
385
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
386
|
+
if (mergeRestore(pkg)) {
|
|
387
|
+
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
|
|
388
|
+
console.error(colors.green(` ✓ ${pkgPath}`));
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
catch (err) {
|
|
392
|
+
console.error(colors.red(` ✗ ${pkgPath}: ${err.message}`));
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
dirtyPackageJsons.clear();
|
|
396
|
+
}
|
|
397
|
+
let cleanupHandlersInstalled = false;
|
|
398
|
+
/** Install signal/exit handlers that restore `.dependencies` backups on abnormal exit. */
|
|
399
|
+
export function installCleanupHandlers() {
|
|
400
|
+
if (cleanupHandlersInstalled)
|
|
401
|
+
return;
|
|
402
|
+
cleanupHandlersInstalled = true;
|
|
403
|
+
process.on('exit', emergencyRestoreDeps);
|
|
404
|
+
const signalHandler = (signal) => {
|
|
405
|
+
emergencyRestoreDeps();
|
|
406
|
+
process.exit(128 + (signal === 'SIGINT' ? 2 : signal === 'SIGTERM' ? 15 : 1));
|
|
407
|
+
};
|
|
408
|
+
process.on('SIGINT', signalHandler);
|
|
409
|
+
process.on('SIGTERM', signalHandler);
|
|
410
|
+
process.on('SIGHUP', signalHandler);
|
|
411
|
+
process.on('uncaughtException', (err) => {
|
|
412
|
+
console.error(colors.red('\nUncaught exception:'), err);
|
|
413
|
+
emergencyRestoreDeps();
|
|
414
|
+
process.exit(1);
|
|
415
|
+
});
|
|
416
|
+
process.on('unhandledRejection', (reason) => {
|
|
417
|
+
console.error(colors.red('\nUnhandled rejection:'), reason);
|
|
418
|
+
emergencyRestoreDeps();
|
|
419
|
+
process.exit(1);
|
|
420
|
+
});
|
|
320
421
|
}
|
|
321
422
|
/** Resolve a file: path to absolute path */
|
|
322
423
|
export function resolveFilePath(fileRef, baseDir) {
|
|
@@ -766,36 +867,21 @@ function hasLocalChanges(packageName, version, targetPath, verbose) {
|
|
|
766
867
|
export function transformDeps(pkg, baseDir, verbose = false, forcePublish = false) {
|
|
767
868
|
let transformed = false;
|
|
768
869
|
const unpublished = [];
|
|
870
|
+
// If re-running on a previously-transformed pkg, merge-restore first.
|
|
871
|
+
// This preserves any external modifications (AI tools, npm install, manual edits)
|
|
872
|
+
// made since the last transform, then proceeds to re-transform from a clean slate.
|
|
873
|
+
if (hasBackup(pkg)) {
|
|
874
|
+
mergeRestore(pkg, verbose);
|
|
875
|
+
}
|
|
769
876
|
for (const key of DEP_KEYS) {
|
|
770
877
|
if (!pkg[key])
|
|
771
878
|
continue;
|
|
772
879
|
const dotKey = '.' + key;
|
|
773
|
-
// If .dependencies already exists, restore it and merge any new dependencies
|
|
774
|
-
if (pkg[dotKey]) {
|
|
775
|
-
// Save any new dependencies that aren't in .dependencies
|
|
776
|
-
const currentDeps = { ...pkg[key] };
|
|
777
|
-
// Restore .dependencies back to dependencies
|
|
778
|
-
pkg[key] = { ...pkg[dotKey] };
|
|
779
|
-
// Merge in any NEW dependencies that were added since transformation
|
|
780
|
-
for (const [name, value] of Object.entries(currentDeps)) {
|
|
781
|
-
if (!(name in pkg[dotKey])) {
|
|
782
|
-
// This is a new dependency, add it
|
|
783
|
-
pkg[key][name] = value;
|
|
784
|
-
// Also add to .dependencies backup
|
|
785
|
-
pkg[dotKey][name] = value;
|
|
786
|
-
if (verbose) {
|
|
787
|
-
console.log(` Merged new dependency: ${name}`);
|
|
788
|
-
}
|
|
789
|
-
}
|
|
790
|
-
}
|
|
791
|
-
}
|
|
792
880
|
const hasFileRefs = Object.values(pkg[key]).some(v => isFileRef(v));
|
|
793
881
|
if (!hasFileRefs)
|
|
794
882
|
continue;
|
|
795
|
-
// Backup original
|
|
796
|
-
|
|
797
|
-
pkg[dotKey] = { ...pkg[key] };
|
|
798
|
-
}
|
|
883
|
+
// Backup original
|
|
884
|
+
pkg[dotKey] = { ...pkg[key] };
|
|
799
885
|
// Transform file: refs to npm versions
|
|
800
886
|
for (const [name, value] of Object.entries(pkg[key])) {
|
|
801
887
|
if (isFileRef(value)) {
|
|
@@ -846,6 +932,17 @@ export function transformDeps(pkg, baseDir, verbose = false, forcePublish = fals
|
|
|
846
932
|
}
|
|
847
933
|
}
|
|
848
934
|
}
|
|
935
|
+
// Record snapshot of transform output so restore can distinguish untouched
|
|
936
|
+
// entries from those modified externally since the transform ran.
|
|
937
|
+
if (transformed) {
|
|
938
|
+
const snapshot = {};
|
|
939
|
+
for (const key of DEP_KEYS) {
|
|
940
|
+
if (pkg['.' + key]) {
|
|
941
|
+
snapshot[key] = { ...pkg[key] };
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
pkg['.transformedSnapshot'] = snapshot;
|
|
945
|
+
}
|
|
849
946
|
return { transformed, unpublished };
|
|
850
947
|
}
|
|
851
948
|
/** Build and print a dependency tree of file: references.
|
|
@@ -889,21 +986,10 @@ function printDepTree(baseDir, indent = 0, visited = new Set()) {
|
|
|
889
986
|
}
|
|
890
987
|
}
|
|
891
988
|
}
|
|
892
|
-
/** Restore file: dependencies from .dependencies
|
|
989
|
+
/** Restore file: dependencies from .dependencies using a three-way merge that
|
|
990
|
+
* preserves any external modifications made since the last transform. */
|
|
893
991
|
export function restoreDeps(pkg, verbose = false) {
|
|
894
|
-
|
|
895
|
-
for (const key of DEP_KEYS) {
|
|
896
|
-
const dotKey = '.' + key;
|
|
897
|
-
if (pkg[dotKey]) {
|
|
898
|
-
pkg[key] = pkg[dotKey];
|
|
899
|
-
delete pkg[dotKey];
|
|
900
|
-
restored = true;
|
|
901
|
-
if (verbose) {
|
|
902
|
-
console.log(` Restored ${key} from ${dotKey}`);
|
|
903
|
-
}
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
|
-
return restored;
|
|
992
|
+
return mergeRestore(pkg, verbose);
|
|
907
993
|
}
|
|
908
994
|
/** Check if .dependencies exist (already transformed) */
|
|
909
995
|
export function hasBackup(pkg) {
|
|
@@ -3475,8 +3561,22 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
3475
3561
|
console.log(colors.green(`✓ Installed globally: ${pkgName}@${pkgVersion}`));
|
|
3476
3562
|
}
|
|
3477
3563
|
else {
|
|
3478
|
-
|
|
3479
|
-
|
|
3564
|
+
// Registry install failed — fall back to local install
|
|
3565
|
+
const auth = checkNpmAuth();
|
|
3566
|
+
if (!auth.authenticated) {
|
|
3567
|
+
console.log(colors.yellow(` Registry install failed (${auth.error || 'auth issue'}) — falling back to local install...`));
|
|
3568
|
+
}
|
|
3569
|
+
else {
|
|
3570
|
+
console.log(colors.yellow(` Registry install failed — falling back to local install...`));
|
|
3571
|
+
}
|
|
3572
|
+
const localFallback = runCommand('npm', ['install', '-g', '.'], { cwd, silent: false, showCommand: true });
|
|
3573
|
+
if (localFallback.success) {
|
|
3574
|
+
console.log(colors.green(`✓ Installed globally from local: ${pkgName}@${pkgVersion}`));
|
|
3575
|
+
}
|
|
3576
|
+
else {
|
|
3577
|
+
console.error(colors.red(`✗ Global install failed`));
|
|
3578
|
+
console.error(colors.yellow(` Try running manually: npm install -g ${pkgName}@${pkgVersion}`));
|
|
3579
|
+
}
|
|
3480
3580
|
}
|
|
3481
3581
|
}
|
|
3482
3582
|
else {
|
|
@@ -4026,7 +4126,7 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
4026
4126
|
}
|
|
4027
4127
|
}
|
|
4028
4128
|
catch (e) {
|
|
4029
|
-
|
|
4129
|
+
console.error(colors.yellow(` Warning: could not delete tarball ${tarballName} — remove manually`));
|
|
4030
4130
|
}
|
|
4031
4131
|
if (!publishResult.success) {
|
|
4032
4132
|
// Check for specific error types before recording
|
|
@@ -4180,9 +4280,25 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
4180
4280
|
console.log(colors.green(`✓ Installed globally: ${pkgName}@${pkgVersion}`));
|
|
4181
4281
|
}
|
|
4182
4282
|
else {
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
|
|
4283
|
+
// Registry install failed — may be auth/token issue for private packages.
|
|
4284
|
+
// Fall back to local install which doesn't need registry auth.
|
|
4285
|
+
const auth = checkNpmAuth();
|
|
4286
|
+
if (!auth.authenticated) {
|
|
4287
|
+
console.log(colors.yellow(` Registry install failed (${auth.error || 'auth issue'}) — falling back to local install...`));
|
|
4288
|
+
}
|
|
4289
|
+
else {
|
|
4290
|
+
console.log(colors.yellow(` Registry install failed — falling back to local install...`));
|
|
4291
|
+
}
|
|
4292
|
+
const localFallback = runCommand('npm', ['install', '-g', '.'], { cwd, silent: false, showCommand: true });
|
|
4293
|
+
if (localFallback.success) {
|
|
4294
|
+
globalInstallOk = true;
|
|
4295
|
+
console.log(colors.green(`✓ Installed globally from local: ${pkgName}@${pkgVersion}`));
|
|
4296
|
+
}
|
|
4297
|
+
else {
|
|
4298
|
+
console.error(colors.red(`✗ Global install failed`));
|
|
4299
|
+
console.error(colors.yellow(` Try running manually: npm install -g ${pkgName}@${pkgVersion}`));
|
|
4300
|
+
recordBuildIssue(pkgName, 'warning', 'Global install failed');
|
|
4301
|
+
}
|
|
4186
4302
|
}
|
|
4187
4303
|
}
|
|
4188
4304
|
else {
|
|
@@ -4563,6 +4679,7 @@ export default {
|
|
|
4563
4679
|
getGitStatus,
|
|
4564
4680
|
validatePackageJson,
|
|
4565
4681
|
confirm,
|
|
4566
|
-
initGit
|
|
4682
|
+
initGit,
|
|
4683
|
+
installCleanupHandlers
|
|
4567
4684
|
};
|
|
4568
4685
|
//# sourceMappingURL=lib.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobfrankston/npmglobalize",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.145",
|
|
4
4
|
"description": "Transform file: dependencies to npm versions for publishing",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -31,10 +31,10 @@
|
|
|
31
31
|
"url": "https://github.com/BobFrankston/npmglobalize.git"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@bobfrankston/freezepak": "^0.1.
|
|
35
|
-
"@bobfrankston/importgen": "^0.1.
|
|
36
|
-
"@bobfrankston/themecolors": "^0.1.
|
|
37
|
-
"@bobfrankston/userconfig": "^1.0.
|
|
34
|
+
"@bobfrankston/freezepak": "^0.1.7",
|
|
35
|
+
"@bobfrankston/importgen": "^0.1.33",
|
|
36
|
+
"@bobfrankston/themecolors": "^0.1.5",
|
|
37
|
+
"@bobfrankston/userconfig": "^1.0.7",
|
|
38
38
|
"@npmcli/package-json": "^7.0.4",
|
|
39
39
|
"json5": "^2.2.3",
|
|
40
40
|
"libnpmversion": "^8.0.3",
|
|
@@ -56,5 +56,19 @@
|
|
|
56
56
|
"npm-registry-fetch": "^19.1.1",
|
|
57
57
|
"pacote": "^21.0.4",
|
|
58
58
|
"simple-git": "^3.30.0"
|
|
59
|
+
},
|
|
60
|
+
".transformedSnapshot": {
|
|
61
|
+
"dependencies": {
|
|
62
|
+
"@bobfrankston/freezepak": "^0.1.7",
|
|
63
|
+
"@bobfrankston/importgen": "^0.1.33",
|
|
64
|
+
"@bobfrankston/themecolors": "^0.1.5",
|
|
65
|
+
"@bobfrankston/userconfig": "^1.0.7",
|
|
66
|
+
"@npmcli/package-json": "^7.0.4",
|
|
67
|
+
"json5": "^2.2.3",
|
|
68
|
+
"libnpmversion": "^8.0.3",
|
|
69
|
+
"npm-registry-fetch": "^19.1.1",
|
|
70
|
+
"pacote": "^21.0.4",
|
|
71
|
+
"simple-git": "^3.30.0"
|
|
72
|
+
}
|
|
59
73
|
}
|
|
60
74
|
}
|