@bobfrankston/npmglobalize 1.0.101 → 1.0.103
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/lib.js +95 -12
- package/package.json +1 -1
package/lib.js
CHANGED
|
@@ -752,6 +752,47 @@ export function transformDeps(pkg, baseDir, verbose = false, forcePublish = fals
|
|
|
752
752
|
}
|
|
753
753
|
return { transformed, unpublished };
|
|
754
754
|
}
|
|
755
|
+
/** Build and print a dependency tree of file: references.
|
|
756
|
+
* Recursively walks file: deps showing the full hierarchy with indentation. */
|
|
757
|
+
function printDepTree(baseDir, indent = 0, visited = new Set()) {
|
|
758
|
+
const realDir = fs.realpathSync(baseDir);
|
|
759
|
+
if (visited.has(realDir))
|
|
760
|
+
return;
|
|
761
|
+
visited.add(realDir);
|
|
762
|
+
let pkg;
|
|
763
|
+
try {
|
|
764
|
+
pkg = readPackageJson(baseDir);
|
|
765
|
+
}
|
|
766
|
+
catch {
|
|
767
|
+
return;
|
|
768
|
+
}
|
|
769
|
+
for (const key of DEP_KEYS) {
|
|
770
|
+
if (!pkg[key])
|
|
771
|
+
continue;
|
|
772
|
+
// Use the backup (.dependencies) if present — it has the original file: refs
|
|
773
|
+
const deps = pkg['.' + key] || pkg[key];
|
|
774
|
+
for (const [name, value] of Object.entries(deps)) {
|
|
775
|
+
if (!isFileRef(value))
|
|
776
|
+
continue;
|
|
777
|
+
const prefix = ' '.repeat(indent * 2);
|
|
778
|
+
let targetPath;
|
|
779
|
+
let version = '?';
|
|
780
|
+
try {
|
|
781
|
+
targetPath = resolveFilePath(value, baseDir);
|
|
782
|
+
const targetPkg = readPackageJson(targetPath);
|
|
783
|
+
version = targetPkg.version || '?';
|
|
784
|
+
}
|
|
785
|
+
catch {
|
|
786
|
+
console.log(`${prefix} ${name} → ${value} ${colors.red('(unresolvable)')}`);
|
|
787
|
+
continue;
|
|
788
|
+
}
|
|
789
|
+
const exists = checkVersionExists(name, version);
|
|
790
|
+
const marker = exists ? colors.green('✓') : colors.red('✗');
|
|
791
|
+
console.log(`${prefix} ${marker} ${name}@${version} → ${value}`);
|
|
792
|
+
printDepTree(targetPath, indent + 1, visited);
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
}
|
|
755
796
|
/** Restore file: dependencies from .dependencies */
|
|
756
797
|
export function restoreDeps(pkg, verbose = false) {
|
|
757
798
|
let restored = false;
|
|
@@ -1301,6 +1342,37 @@ const RECOMMENDED_GITIGNORE = [
|
|
|
1301
1342
|
'*.ldf',
|
|
1302
1343
|
'*.ndf'
|
|
1303
1344
|
];
|
|
1345
|
+
/** Extensions only recommended for .gitignore when matching files exist in the project */
|
|
1346
|
+
const PRESENCE_ONLY_EXTENSIONS = new Set(['.mdf', '.ldf', '.ndf']);
|
|
1347
|
+
/** Check if any files with the given extension exist in dir (skips node_modules, .git, prev) */
|
|
1348
|
+
function hasFilesWithExtension(dir, ext) {
|
|
1349
|
+
const skip = new Set(['node_modules', '.git', 'prev']);
|
|
1350
|
+
function check(d) {
|
|
1351
|
+
try {
|
|
1352
|
+
for (const entry of fs.readdirSync(d, { withFileTypes: true })) {
|
|
1353
|
+
if (skip.has(entry.name))
|
|
1354
|
+
continue;
|
|
1355
|
+
if (entry.isFile() && entry.name.endsWith(ext))
|
|
1356
|
+
return true;
|
|
1357
|
+
if (entry.isDirectory() && check(path.join(d, entry.name)))
|
|
1358
|
+
return true;
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
catch { }
|
|
1362
|
+
return false;
|
|
1363
|
+
}
|
|
1364
|
+
return check(dir);
|
|
1365
|
+
}
|
|
1366
|
+
/** Filter RECOMMENDED_GITIGNORE to skip presence-only patterns when no matching files exist */
|
|
1367
|
+
function getApplicableGitignorePatterns(cwd) {
|
|
1368
|
+
return RECOMMENDED_GITIGNORE.filter(pattern => {
|
|
1369
|
+
const extMatch = pattern.match(/^\*(\.\w+)$/);
|
|
1370
|
+
if (extMatch && PRESENCE_ONLY_EXTENSIONS.has(extMatch[1])) {
|
|
1371
|
+
return hasFilesWithExtension(cwd, extMatch[1]);
|
|
1372
|
+
}
|
|
1373
|
+
return true;
|
|
1374
|
+
});
|
|
1375
|
+
}
|
|
1304
1376
|
/** Recommended .npmignore patterns */
|
|
1305
1377
|
const RECOMMENDED_NPMIGNORE = [
|
|
1306
1378
|
'.git/',
|
|
@@ -1341,7 +1413,7 @@ function checkIgnoreFiles(cwd, options) {
|
|
|
1341
1413
|
if (fs.existsSync(gitignorePath)) {
|
|
1342
1414
|
const content = fs.readFileSync(gitignorePath, 'utf-8');
|
|
1343
1415
|
const lines = content.split('\n').map(l => l.trim());
|
|
1344
|
-
for (const pattern of
|
|
1416
|
+
for (const pattern of getApplicableGitignorePatterns(cwd)) {
|
|
1345
1417
|
if (!lines.some(line => line === pattern || line === pattern.replace('/', ''))) {
|
|
1346
1418
|
changes.push(` .gitignore missing: ${pattern}`);
|
|
1347
1419
|
}
|
|
@@ -1395,7 +1467,7 @@ function conformIgnoreFiles(cwd) {
|
|
|
1395
1467
|
const content = fs.readFileSync(gitignorePath, 'utf-8');
|
|
1396
1468
|
const lines = new Set(content.split('\n').map(l => l.trim()).filter(l => l && !l.startsWith('#')));
|
|
1397
1469
|
let updated = false;
|
|
1398
|
-
for (const pattern of
|
|
1470
|
+
for (const pattern of getApplicableGitignorePatterns(cwd)) {
|
|
1399
1471
|
const normalized = pattern.replace('/', '');
|
|
1400
1472
|
if (!lines.has(pattern) && !lines.has(normalized)) {
|
|
1401
1473
|
lines.add(pattern);
|
|
@@ -1468,9 +1540,9 @@ function ensureGitignore(cwd) {
|
|
|
1468
1540
|
// Update .gitignore if needed
|
|
1469
1541
|
if (needsUpdate) {
|
|
1470
1542
|
if (!content || content.trim() === '') {
|
|
1471
|
-
// Create new .gitignore from
|
|
1543
|
+
// Create new .gitignore from applicable patterns plus extras
|
|
1472
1544
|
const extras = ['*certs*/', 'configuration.json', 'cruft/', 'prev/', 'tests/'];
|
|
1473
|
-
content = [...
|
|
1545
|
+
content = [...getApplicableGitignorePatterns(cwd), ...extras].join('\n') + '\n';
|
|
1474
1546
|
}
|
|
1475
1547
|
else {
|
|
1476
1548
|
// Add node_modules to existing .gitignore
|
|
@@ -2477,13 +2549,16 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
2477
2549
|
console.log('');
|
|
2478
2550
|
}
|
|
2479
2551
|
// Check if target packages need to be published
|
|
2480
|
-
if (transformResult.unpublished.length > 0) {
|
|
2552
|
+
if (transformResult.unpublished.length > 0 && !noPublish) {
|
|
2481
2553
|
console.log('');
|
|
2482
2554
|
console.log(colors.red('⚠ WARNING: Some file: dependencies are not published on npm:'));
|
|
2483
2555
|
for (const { name, version, path } of transformResult.unpublished) {
|
|
2484
2556
|
console.log(colors.yellow(` ${name}@${version} (${path})`));
|
|
2485
2557
|
}
|
|
2486
2558
|
console.log('');
|
|
2559
|
+
console.log('Dependency tree (✓ = on npm, ✗ = needs publishing):');
|
|
2560
|
+
printDepTree(cwd);
|
|
2561
|
+
console.log('');
|
|
2487
2562
|
if (publishDeps) {
|
|
2488
2563
|
const action = forcePublish ? 'Publishing/updating' : 'Publishing';
|
|
2489
2564
|
console.log(`${action} file: dependencies first (--publish-deps)...`);
|
|
@@ -2508,7 +2583,8 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
2508
2583
|
fix,
|
|
2509
2584
|
conform, // Propagate conform to dependencies
|
|
2510
2585
|
publishDeps, // Propagate so transitive deps also get published
|
|
2511
|
-
forcePublish // Propagate so transitive deps get force-published too
|
|
2586
|
+
forcePublish, // Propagate so transitive deps get force-published too
|
|
2587
|
+
rebase // Propagate so behind-remote deps get rebased automatically
|
|
2512
2588
|
});
|
|
2513
2589
|
if (!depSuccess) {
|
|
2514
2590
|
console.error(colors.red(`Failed to publish ${name}`));
|
|
@@ -2529,6 +2605,7 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
2529
2605
|
console.log(colors.yellow(' 1. Publish them manually first'));
|
|
2530
2606
|
console.log(colors.yellow(' 2. Use --publish-deps to publish them automatically'));
|
|
2531
2607
|
console.log(colors.yellow(' 3. Use --force to continue anyway (NOT RECOMMENDED)'));
|
|
2608
|
+
console.log(colors.yellow(' 4. Use -npd (--no-publish-deps) to skip dependency publishing'));
|
|
2532
2609
|
console.log('');
|
|
2533
2610
|
if (!force) {
|
|
2534
2611
|
const shouldContinue = await confirm('Continue with unpublished dependencies?', false);
|
|
@@ -2610,10 +2687,13 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
2610
2687
|
const pkgName = pkg.name;
|
|
2611
2688
|
const pkgVersion = pkg.version;
|
|
2612
2689
|
if (!pkg.bin && (install || link || wsl)) {
|
|
2613
|
-
|
|
2614
|
-
|
|
2690
|
+
const proceed = await offerAddBin(cwd, pkg);
|
|
2691
|
+
if (!proceed) {
|
|
2692
|
+
console.log(colors.yellow('Skipping global install — library packages should not be installed globally.'));
|
|
2693
|
+
console.log(colors.yellow(`To use in projects: npm install ${pkgName}`));
|
|
2694
|
+
}
|
|
2615
2695
|
}
|
|
2616
|
-
|
|
2696
|
+
if (pkg.bin && (install || link || wsl)) {
|
|
2617
2697
|
if (link) {
|
|
2618
2698
|
console.log(`Installing ${pkgName} globally from local directory (link)...`);
|
|
2619
2699
|
const localInstallResult = runCommand('npm', ['install', '-g', '.'], { cwd, silent: false });
|
|
@@ -3104,10 +3184,13 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
3104
3184
|
const pkgName = updatedPkg.name;
|
|
3105
3185
|
const pkgVersion = updatedPkg.version;
|
|
3106
3186
|
if (!updatedPkg.bin && (install || link || wsl)) {
|
|
3107
|
-
|
|
3108
|
-
|
|
3187
|
+
const proceed = await offerAddBin(cwd, updatedPkg);
|
|
3188
|
+
if (!proceed) {
|
|
3189
|
+
console.log(colors.yellow('Skipping global install — library packages should not be installed globally.'));
|
|
3190
|
+
console.log(colors.yellow(`To use in projects: npm install ${pkgName}`));
|
|
3191
|
+
}
|
|
3109
3192
|
}
|
|
3110
|
-
|
|
3193
|
+
if (updatedPkg.bin && (install || link || wsl)) {
|
|
3111
3194
|
if (link) {
|
|
3112
3195
|
console.log(`Linking globally: ${pkgName}@${pkgVersion}...`);
|
|
3113
3196
|
if (!dryRun) {
|