npm-needs-publish 0.1.4 → 1.0.0
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/cjs/cli.js +12 -4
- package/dist/cjs/cli.js.map +1 -1
- package/dist/cjs/comparators/dependency.js +1 -1
- package/dist/cjs/comparators/file-content.js +12 -4
- package/dist/cjs/comparators/file-content.js.map +1 -1
- package/dist/cjs/comparators/index.js +1 -1
- package/dist/cjs/comparators/package-json.js +1 -1
- package/dist/cjs/comparators/version-specifier.js +9 -9
- package/dist/cjs/comparators/version-specifier.js.map +1 -1
- package/dist/cjs/compat.js +1 -1
- package/dist/cjs/fs-compat.js +1 -1
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/needs-publish.js +22 -19
- package/dist/cjs/needs-publish.js.map +1 -1
- package/dist/cjs/types.js +1 -1
- package/dist/esm/needs-publish.js +14 -20
- package/dist/esm/needs-publish.js.map +1 -1
- package/package.json +16 -16
package/dist/cjs/cli.js
CHANGED
|
@@ -69,9 +69,17 @@ function _ts_generator(thisArg, body) {
|
|
|
69
69
|
},
|
|
70
70
|
trys: [],
|
|
71
71
|
ops: []
|
|
72
|
-
}, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
73
|
-
return
|
|
74
|
-
|
|
72
|
+
}, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype), d = Object.defineProperty;
|
|
73
|
+
return d(g, "next", {
|
|
74
|
+
value: verb(0)
|
|
75
|
+
}), d(g, "throw", {
|
|
76
|
+
value: verb(1)
|
|
77
|
+
}), d(g, "return", {
|
|
78
|
+
value: verb(2)
|
|
79
|
+
}), typeof Symbol === "function" && d(g, Symbol.iterator, {
|
|
80
|
+
value: function() {
|
|
81
|
+
return this;
|
|
82
|
+
}
|
|
75
83
|
}), g;
|
|
76
84
|
function verb(n) {
|
|
77
85
|
return function(v) {
|
|
@@ -315,4 +323,4 @@ function cli(argv) {
|
|
|
315
323
|
});
|
|
316
324
|
})();
|
|
317
325
|
}
|
|
318
|
-
/* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (
|
|
326
|
+
/* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (var key in exports) { exports.default[key] = exports[key]; } } catch (_) {}; module.exports = exports.default; }
|
package/dist/cjs/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/npm/npm-needs-publish/src/cli.ts"],"sourcesContent":["/**\n * CLI for npm-needs-publish\n *\n * Usage:\n * npm-needs-publish [options]\n * nnp [options]\n *\n * Exit codes:\n * 0 - Package does NOT need publishing\n * 1 - Package NEEDS publishing\n * 2 - Error occurred\n */\n\nimport { readFileSync } from 'fs';\nimport { dirname, join } from 'path';\nimport { fileURLToPath } from 'url';\nimport { parseArgs } from 'util';\nimport { needsPublish } from './needs-publish.ts';\nimport type { NeedsPublishOptions, NeedsPublishResult } from './types.ts';\n\nconst __dirname = dirname(typeof __filename !== 'undefined' ? __filename : fileURLToPath(import.meta.url));\n\nfunction getVersion(): string {\n const packagePath = join(__dirname, '..', '..', 'package.json');\n const packageJson = JSON.parse(readFileSync(packagePath, 'utf8'));\n return packageJson.version;\n}\n\nfunction showHelp(): void {\n console.log(`\nnpm-needs-publish - Smart publish detection for npm packages\n\nUsage: npm-needs-publish [options]\n nnp [options]\n\nDetermine if a package needs to be published based on semantic comparison\nof package.json fields and content changes.\n\nOptions:\n --help, -h Show this help message\n --version, -V Show version number\n --cwd <path> Working directory (default: current directory)\n --registry <url> Registry URL override\n --json Output result as JSON\n --verbose, -v Show detailed change breakdown\n --package-json-only Only compare package.json, skip file comparison\n --no-optional-deps Exclude optionalDependencies from comparison\n\nExit codes:\n 0 - Package does NOT need publishing\n 1 - Package NEEDS publishing\n 2 - Error occurred\n\nExamples:\n # Check if current directory needs publishing\n npm-needs-publish\n\n # Check with JSON output\n npm-needs-publish --json\n\n # Check specific directory with verbose output\n npm-needs-publish --cwd ./packages/my-package --verbose\n\n # Skip optionalDependencies comparison\n npm-needs-publish --no-optional-deps\n`);\n}\n\nfunction formatResult(result: NeedsPublishResult, verbose: boolean): string {\n const lines: string[] = [];\n\n if (result.needsPublish) {\n lines.push('✓ Package NEEDS publishing');\n lines.push(` Reason: ${result.reason}`);\n } else {\n lines.push('✗ Package does NOT need publishing');\n lines.push(` Reason: ${result.reason}`);\n }\n\n if (verbose && result.changes && result.changes.length > 0) {\n lines.push('');\n lines.push('Changes detected:');\n for (const change of result.changes) {\n const icon = change.significance === 'critical' ? '[!]' : change.significance === 'significant' ? '[*]' : '[ ]';\n\n if (change.type === 'first-publish') {\n lines.push(` ${icon} First publish`);\n } else if (change.type === 'version') {\n lines.push(` ${icon} Version: ${change.oldValue} -> ${change.newValue}`);\n } else if (change.type === 'dependency') {\n lines.push(` ${icon} Dependency ${change.field}: ${change.oldValue || '(none)'} -> ${change.newValue || '(none)'}`);\n } else if (change.type === 'field') {\n lines.push(` ${icon} Field ${change.field} changed`);\n } else if (change.type === 'file') {\n lines.push(` ${icon} File: ${change.field}`);\n }\n }\n }\n\n return lines.join('\\n');\n}\n\nexport default async function cli(argv: string[]): Promise<void> {\n const { values, positionals } = parseArgs({\n args: argv,\n options: {\n help: {\n type: 'boolean',\n short: 'h',\n default: false,\n },\n version: {\n type: 'boolean',\n short: 'V',\n default: false,\n },\n cwd: {\n type: 'string',\n default: process.cwd(),\n },\n registry: {\n type: 'string',\n },\n json: {\n type: 'boolean',\n default: false,\n },\n verbose: {\n type: 'boolean',\n short: 'v',\n default: false,\n },\n 'package-json-only': {\n type: 'boolean',\n default: false,\n },\n 'no-optional-deps': {\n type: 'boolean',\n default: false,\n },\n },\n allowPositionals: true,\n });\n\n if (values.version) {\n console.log(getVersion());\n process.exit(0);\n }\n\n if (values.help) {\n showHelp();\n process.exit(0);\n }\n\n // Use positional argument as cwd if provided\n const cwd = positionals[0] || values.cwd || process.cwd();\n\n try {\n const options: NeedsPublishOptions = {\n cwd,\n registry: values.registry,\n packageJsonOnly: values['package-json-only'],\n includeOptionalDeps: !values['no-optional-deps'],\n };\n\n const result = await needsPublish(options);\n\n if (values.json) {\n console.log(JSON.stringify(result, null, 2));\n } else {\n console.log(formatResult(result, values.verbose || false));\n }\n\n process.exit(result.needsPublish ? 1 : 0);\n } catch (error) {\n if (values.json) {\n console.log(\n JSON.stringify(\n {\n error: true,\n message: error instanceof Error ? error.message : String(error),\n },\n null,\n 2\n )\n );\n } else {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n }\n process.exit(2);\n }\n}\n"],"names":["cli","__dirname","dirname","__filename","fileURLToPath","getVersion","packagePath","join","packageJson","JSON","parse","readFileSync","version","showHelp","console","log","formatResult","result","verbose","lines","needsPublish","push","reason","changes","length","change","icon","significance","type","oldValue","newValue","field","argv","parseArgs","values","positionals","cwd","options","error","args","help","short","default","process","registry","json","allowPositionals","exit","packageJsonOnly","includeOptionalDeps","stringify","message","Error","String"],"mappings":"AAAA;;;;;;;;;;;CAWC;;;;+BA2FD;;;eAA8BA;;;kBAzFD;oBACC;mBACA;oBACJ;8BACG
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/npm/npm-needs-publish/src/cli.ts"],"sourcesContent":["/**\n * CLI for npm-needs-publish\n *\n * Usage:\n * npm-needs-publish [options]\n * nnp [options]\n *\n * Exit codes:\n * 0 - Package does NOT need publishing\n * 1 - Package NEEDS publishing\n * 2 - Error occurred\n */\n\nimport { readFileSync } from 'fs';\nimport { dirname, join } from 'path';\nimport { fileURLToPath } from 'url';\nimport { parseArgs } from 'util';\nimport { needsPublish } from './needs-publish.ts';\nimport type { NeedsPublishOptions, NeedsPublishResult } from './types.ts';\n\nconst __dirname = dirname(typeof __filename !== 'undefined' ? __filename : fileURLToPath(import.meta.url));\n\nfunction getVersion(): string {\n const packagePath = join(__dirname, '..', '..', 'package.json');\n const packageJson = JSON.parse(readFileSync(packagePath, 'utf8'));\n return packageJson.version;\n}\n\nfunction showHelp(): void {\n console.log(`\nnpm-needs-publish - Smart publish detection for npm packages\n\nUsage: npm-needs-publish [options]\n nnp [options]\n\nDetermine if a package needs to be published based on semantic comparison\nof package.json fields and content changes.\n\nOptions:\n --help, -h Show this help message\n --version, -V Show version number\n --cwd <path> Working directory (default: current directory)\n --registry <url> Registry URL override\n --json Output result as JSON\n --verbose, -v Show detailed change breakdown\n --package-json-only Only compare package.json, skip file comparison\n --no-optional-deps Exclude optionalDependencies from comparison\n\nExit codes:\n 0 - Package does NOT need publishing\n 1 - Package NEEDS publishing\n 2 - Error occurred\n\nExamples:\n # Check if current directory needs publishing\n npm-needs-publish\n\n # Check with JSON output\n npm-needs-publish --json\n\n # Check specific directory with verbose output\n npm-needs-publish --cwd ./packages/my-package --verbose\n\n # Skip optionalDependencies comparison\n npm-needs-publish --no-optional-deps\n`);\n}\n\nfunction formatResult(result: NeedsPublishResult, verbose: boolean): string {\n const lines: string[] = [];\n\n if (result.needsPublish) {\n lines.push('✓ Package NEEDS publishing');\n lines.push(` Reason: ${result.reason}`);\n } else {\n lines.push('✗ Package does NOT need publishing');\n lines.push(` Reason: ${result.reason}`);\n }\n\n if (verbose && result.changes && result.changes.length > 0) {\n lines.push('');\n lines.push('Changes detected:');\n for (const change of result.changes) {\n const icon = change.significance === 'critical' ? '[!]' : change.significance === 'significant' ? '[*]' : '[ ]';\n\n if (change.type === 'first-publish') {\n lines.push(` ${icon} First publish`);\n } else if (change.type === 'version') {\n lines.push(` ${icon} Version: ${change.oldValue} -> ${change.newValue}`);\n } else if (change.type === 'dependency') {\n lines.push(` ${icon} Dependency ${change.field}: ${change.oldValue || '(none)'} -> ${change.newValue || '(none)'}`);\n } else if (change.type === 'field') {\n lines.push(` ${icon} Field ${change.field} changed`);\n } else if (change.type === 'file') {\n lines.push(` ${icon} File: ${change.field}`);\n }\n }\n }\n\n return lines.join('\\n');\n}\n\nexport default async function cli(argv: string[]): Promise<void> {\n const { values, positionals } = parseArgs({\n args: argv,\n options: {\n help: {\n type: 'boolean',\n short: 'h',\n default: false,\n },\n version: {\n type: 'boolean',\n short: 'V',\n default: false,\n },\n cwd: {\n type: 'string',\n default: process.cwd(),\n },\n registry: {\n type: 'string',\n },\n json: {\n type: 'boolean',\n default: false,\n },\n verbose: {\n type: 'boolean',\n short: 'v',\n default: false,\n },\n 'package-json-only': {\n type: 'boolean',\n default: false,\n },\n 'no-optional-deps': {\n type: 'boolean',\n default: false,\n },\n },\n allowPositionals: true,\n });\n\n if (values.version) {\n console.log(getVersion());\n process.exit(0);\n }\n\n if (values.help) {\n showHelp();\n process.exit(0);\n }\n\n // Use positional argument as cwd if provided\n const cwd = positionals[0] || values.cwd || process.cwd();\n\n try {\n const options: NeedsPublishOptions = {\n cwd,\n registry: values.registry,\n packageJsonOnly: values['package-json-only'],\n includeOptionalDeps: !values['no-optional-deps'],\n };\n\n const result = await needsPublish(options);\n\n if (values.json) {\n console.log(JSON.stringify(result, null, 2));\n } else {\n console.log(formatResult(result, values.verbose || false));\n }\n\n process.exit(result.needsPublish ? 1 : 0);\n } catch (error) {\n if (values.json) {\n console.log(\n JSON.stringify(\n {\n error: true,\n message: error instanceof Error ? error.message : String(error),\n },\n null,\n 2\n )\n );\n } else {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n }\n process.exit(2);\n }\n}\n"],"names":["cli","__dirname","dirname","__filename","fileURLToPath","getVersion","packagePath","join","packageJson","JSON","parse","readFileSync","version","showHelp","console","log","formatResult","result","verbose","lines","needsPublish","push","reason","changes","length","change","icon","significance","type","oldValue","newValue","field","argv","parseArgs","values","positionals","cwd","options","error","args","help","short","default","process","registry","json","allowPositionals","exit","packageJsonOnly","includeOptionalDeps","stringify","message","Error","String"],"mappings":"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"}
|
|
@@ -210,4 +210,4 @@ function getDependencyChangeSummary(changes) {
|
|
|
210
210
|
var summary = parts.join(', ');
|
|
211
211
|
return "Dependencies: ".concat(summary, " (").concat(significant.length, " significant)");
|
|
212
212
|
}
|
|
213
|
-
/* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (
|
|
213
|
+
/* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (var key in exports) { exports.default[key] = exports[key]; } } catch (_) {}; module.exports = exports.default; }
|
|
@@ -80,9 +80,17 @@ function _ts_generator(thisArg, body) {
|
|
|
80
80
|
},
|
|
81
81
|
trys: [],
|
|
82
82
|
ops: []
|
|
83
|
-
}, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
84
|
-
return
|
|
85
|
-
|
|
83
|
+
}, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype), d = Object.defineProperty;
|
|
84
|
+
return d(g, "next", {
|
|
85
|
+
value: verb(0)
|
|
86
|
+
}), d(g, "throw", {
|
|
87
|
+
value: verb(1)
|
|
88
|
+
}), d(g, "return", {
|
|
89
|
+
value: verb(2)
|
|
90
|
+
}), typeof Symbol === "function" && d(g, Symbol.iterator, {
|
|
91
|
+
value: function() {
|
|
92
|
+
return this;
|
|
93
|
+
}
|
|
86
94
|
}), g;
|
|
87
95
|
function verb(n) {
|
|
88
96
|
return function(v) {
|
|
@@ -372,4 +380,4 @@ function extractPackageJson(tarball) {
|
|
|
372
380
|
});
|
|
373
381
|
})();
|
|
374
382
|
}
|
|
375
|
-
/* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (
|
|
383
|
+
/* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (var key in exports) { exports.default[key] = exports[key]; } } catch (_) {}; module.exports = exports.default; }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/npm/npm-needs-publish/src/comparators/file-content.ts"],"sourcesContent":["/**\n * File content comparison for package tarballs\n *\n * Compares files between local and registry tarballs:\n * 1. Fast path: Compare tarball hashes\n * 2. If different: Extract and compare file-by-file\n * 3. Special handling for package.json (semantic comparison elsewhere)\n */\n\nimport crypto from 'crypto';\nimport Module from 'module';\nimport { pipeline as pipelineCb, Readable } from 'stream';\nimport { promisify } from 'util';\nimport zlib from 'zlib';\n\nconst pipeline = promisify(pipelineCb);\n\nimport type { FileChange, FileComparison } from '../types.ts';\n\nconst _require = typeof require === 'undefined' ? Module.createRequire(import.meta.url) : require;\n\n// Lazy load tar\nlet _tar: typeof import('tar') | null = null;\n\nfunction getTar(): typeof import('tar') {\n if (!_tar) {\n _tar = _require('tar');\n }\n return _tar;\n}\n\n/**\n * Compare package files from two tarballs\n *\n * @param localTarball - Local package tarball as Buffer\n * @param registryTarball - Registry package tarball as Buffer\n * @returns File comparison result\n */\nexport async function comparePackageFiles(localTarball: Buffer, registryTarball: Buffer): Promise<FileComparison> {\n // Fast path: identical tarballs\n const localHash = hashBuffer(localTarball);\n const registryHash = hashBuffer(registryTarball);\n\n if (localHash === registryHash) {\n return {\n identical: true,\n fileChanges: [],\n packageJsonOnly: false,\n };\n }\n\n // Extract both tarballs to memory\n const localFiles = await extractTarball(localTarball);\n const registryFiles = await extractTarball(registryTarball);\n\n const changes: FileChange[] = [];\n\n // Build combined set of all paths\n const allPaths: Record<string, boolean> = {};\n const localKeys = Object.keys(localFiles);\n for (let i = 0; i < localKeys.length; i++) {\n allPaths[localKeys[i]] = true;\n }\n const registryKeys = Object.keys(registryFiles);\n for (let i = 0; i < registryKeys.length; i++) {\n allPaths[registryKeys[i]] = true;\n }\n\n let onlyPackageJsonDiffers = true;\n\n const pathKeys = Object.keys(allPaths);\n for (let i = 0; i < pathKeys.length; i++) {\n const filePath = pathKeys[i];\n const isPackageJson = filePath === 'package/package.json' || filePath.indexOf('/package.json') === filePath.length - 13;\n const localContent = localFiles[filePath];\n const registryContent = registryFiles[filePath];\n\n if (!registryContent) {\n // File added\n changes.push({ path: filePath, action: 'added' });\n if (!isPackageJson) onlyPackageJsonDiffers = false;\n } else if (!localContent) {\n // File removed\n changes.push({ path: filePath, action: 'removed' });\n if (!isPackageJson) onlyPackageJsonDiffers = false;\n } else if (!buffersEqual(localContent, registryContent)) {\n // File modified\n changes.push({ path: filePath, action: 'modified' });\n if (!isPackageJson) onlyPackageJsonDiffers = false;\n }\n }\n\n return {\n identical: changes.length === 0,\n fileChanges: changes,\n packageJsonOnly: onlyPackageJsonDiffers && changes.length > 0,\n };\n}\n\n/**\n * Extract tarball contents to memory\n *\n * @param tarball - Tarball as Buffer (gzipped)\n * @returns Map of file paths to content buffers\n */\nasync function extractTarball(tarball: Buffer): Promise<Record<string, Buffer>> {\n const files: Record<string, Buffer> = {};\n const tar = getTar();\n\n // Create a parser that collects file contents\n const parser = new tar.Parser({\n onReadEntry: (entry) => {\n if (entry.type === 'File') {\n const chunks: Buffer[] = [];\n\n entry.on('data', (chunk: Buffer) => {\n chunks.push(chunk);\n });\n\n entry.on('end', () => {\n files[entry.path] = Buffer.concat(chunks);\n });\n } else {\n // Drain non-file entries (directories, etc.)\n entry.resume();\n }\n },\n });\n\n // Process the tarball\n await pipeline(Readable.from(tarball), zlib.createGunzip(), parser);\n\n return files;\n}\n\n/**\n * Hash a buffer using SHA-512\n */\nexport function hashBuffer(buffer: Buffer): string {\n return crypto.createHash('sha512').update(buffer).digest('base64');\n}\n\n/**\n * Compare two buffers for equality\n */\nfunction buffersEqual(a: Buffer, b: Buffer): boolean {\n if (a.length !== b.length) return false;\n return crypto.timingSafeEqual(a, b);\n}\n\n/**\n * Get a summary of file changes\n */\nexport function getFileChangeSummary(changes: FileChange[]): string {\n if (changes.length === 0) {\n return 'No file changes';\n }\n\n const added = changes.filter((c) => c.action === 'added');\n const removed = changes.filter((c) => c.action === 'removed');\n const modified = changes.filter((c) => c.action === 'modified');\n\n const parts: string[] = [];\n\n if (added.length > 0) {\n parts.push(`${added.length} added`);\n }\n if (removed.length > 0) {\n parts.push(`${removed.length} removed`);\n }\n if (modified.length > 0) {\n parts.push(`${modified.length} modified`);\n }\n\n return `Files: ${parts.join(', ')} (${changes.length} total)`;\n}\n\n/**\n * Check if changes are only in package.json\n */\nexport function isOnlyPackageJsonChange(changes: FileChange[]): boolean {\n if (changes.length === 0) return false;\n\n for (let i = 0; i < changes.length; i++) {\n const path = changes[i].path;\n if (path !== 'package/package.json' && path.indexOf('/package.json') !== path.length - 13) {\n return false;\n }\n }\n return true;\n}\n\n/**\n * Extract package.json from a tarball\n *\n * @param tarball - Tarball as Buffer (gzipped)\n * @returns Parsed package.json object\n */\nexport async function extractPackageJson(tarball: Buffer): Promise<unknown> {\n const files = await extractTarball(tarball);\n\n // Look for package.json in the tarball\n const pkgJsonPath = Object.keys(files).find((p) => p === 'package/package.json' || p.indexOf('/package.json') === p.length - 13);\n\n if (!pkgJsonPath || !files[pkgJsonPath]) {\n throw new Error('package.json not found in tarball');\n }\n\n return JSON.parse(files[pkgJsonPath].toString('utf8'));\n}\n"],"names":["comparePackageFiles","extractPackageJson","getFileChangeSummary","hashBuffer","isOnlyPackageJsonChange","pipeline","promisify","pipelineCb","_require","require","Module","createRequire","_tar","getTar","localTarball","registryTarball","localHash","registryHash","localFiles","registryFiles","changes","allPaths","localKeys","i","registryKeys","onlyPackageJsonDiffers","pathKeys","filePath","isPackageJson","localContent","registryContent","identical","fileChanges","packageJsonOnly","extractTarball","Object","keys","length","indexOf","push","path","action","buffersEqual","tarball","files","tar","parser","Parser","onReadEntry","entry","type","chunks","on","chunk","Buffer","concat","resume","Readable","from","zlib","createGunzip","buffer","crypto","createHash","update","digest","a","b","timingSafeEqual","added","filter","c","removed","modified","parts","join","pkgJsonPath","find","p","Error","JSON","parse","toString"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/npm/npm-needs-publish/src/comparators/file-content.ts"],"sourcesContent":["/**\n * File content comparison for package tarballs\n *\n * Compares files between local and registry tarballs:\n * 1. Fast path: Compare tarball hashes\n * 2. If different: Extract and compare file-by-file\n * 3. Special handling for package.json (semantic comparison elsewhere)\n */\n\nimport crypto from 'crypto';\nimport Module from 'module';\nimport { pipeline as pipelineCb, Readable } from 'stream';\nimport { promisify } from 'util';\nimport zlib from 'zlib';\n\nconst pipeline = promisify(pipelineCb);\n\nimport type { FileChange, FileComparison } from '../types.ts';\n\nconst _require = typeof require === 'undefined' ? Module.createRequire(import.meta.url) : require;\n\n// Lazy load tar\nlet _tar: typeof import('tar') | null = null;\n\nfunction getTar(): typeof import('tar') {\n if (!_tar) {\n _tar = _require('tar');\n }\n return _tar;\n}\n\n/**\n * Compare package files from two tarballs\n *\n * @param localTarball - Local package tarball as Buffer\n * @param registryTarball - Registry package tarball as Buffer\n * @returns File comparison result\n */\nexport async function comparePackageFiles(localTarball: Buffer, registryTarball: Buffer): Promise<FileComparison> {\n // Fast path: identical tarballs\n const localHash = hashBuffer(localTarball);\n const registryHash = hashBuffer(registryTarball);\n\n if (localHash === registryHash) {\n return {\n identical: true,\n fileChanges: [],\n packageJsonOnly: false,\n };\n }\n\n // Extract both tarballs to memory\n const localFiles = await extractTarball(localTarball);\n const registryFiles = await extractTarball(registryTarball);\n\n const changes: FileChange[] = [];\n\n // Build combined set of all paths\n const allPaths: Record<string, boolean> = {};\n const localKeys = Object.keys(localFiles);\n for (let i = 0; i < localKeys.length; i++) {\n allPaths[localKeys[i]] = true;\n }\n const registryKeys = Object.keys(registryFiles);\n for (let i = 0; i < registryKeys.length; i++) {\n allPaths[registryKeys[i]] = true;\n }\n\n let onlyPackageJsonDiffers = true;\n\n const pathKeys = Object.keys(allPaths);\n for (let i = 0; i < pathKeys.length; i++) {\n const filePath = pathKeys[i];\n const isPackageJson = filePath === 'package/package.json' || filePath.indexOf('/package.json') === filePath.length - 13;\n const localContent = localFiles[filePath];\n const registryContent = registryFiles[filePath];\n\n if (!registryContent) {\n // File added\n changes.push({ path: filePath, action: 'added' });\n if (!isPackageJson) onlyPackageJsonDiffers = false;\n } else if (!localContent) {\n // File removed\n changes.push({ path: filePath, action: 'removed' });\n if (!isPackageJson) onlyPackageJsonDiffers = false;\n } else if (!buffersEqual(localContent, registryContent)) {\n // File modified\n changes.push({ path: filePath, action: 'modified' });\n if (!isPackageJson) onlyPackageJsonDiffers = false;\n }\n }\n\n return {\n identical: changes.length === 0,\n fileChanges: changes,\n packageJsonOnly: onlyPackageJsonDiffers && changes.length > 0,\n };\n}\n\n/**\n * Extract tarball contents to memory\n *\n * @param tarball - Tarball as Buffer (gzipped)\n * @returns Map of file paths to content buffers\n */\nasync function extractTarball(tarball: Buffer): Promise<Record<string, Buffer>> {\n const files: Record<string, Buffer> = {};\n const tar = getTar();\n\n // Create a parser that collects file contents\n const parser = new tar.Parser({\n onReadEntry: (entry) => {\n if (entry.type === 'File') {\n const chunks: Buffer[] = [];\n\n entry.on('data', (chunk: Buffer) => {\n chunks.push(chunk);\n });\n\n entry.on('end', () => {\n files[entry.path] = Buffer.concat(chunks);\n });\n } else {\n // Drain non-file entries (directories, etc.)\n entry.resume();\n }\n },\n });\n\n // Process the tarball\n await pipeline(Readable.from(tarball), zlib.createGunzip(), parser);\n\n return files;\n}\n\n/**\n * Hash a buffer using SHA-512\n */\nexport function hashBuffer(buffer: Buffer): string {\n return crypto.createHash('sha512').update(buffer).digest('base64');\n}\n\n/**\n * Compare two buffers for equality\n */\nfunction buffersEqual(a: Buffer, b: Buffer): boolean {\n if (a.length !== b.length) return false;\n return crypto.timingSafeEqual(a, b);\n}\n\n/**\n * Get a summary of file changes\n */\nexport function getFileChangeSummary(changes: FileChange[]): string {\n if (changes.length === 0) {\n return 'No file changes';\n }\n\n const added = changes.filter((c) => c.action === 'added');\n const removed = changes.filter((c) => c.action === 'removed');\n const modified = changes.filter((c) => c.action === 'modified');\n\n const parts: string[] = [];\n\n if (added.length > 0) {\n parts.push(`${added.length} added`);\n }\n if (removed.length > 0) {\n parts.push(`${removed.length} removed`);\n }\n if (modified.length > 0) {\n parts.push(`${modified.length} modified`);\n }\n\n return `Files: ${parts.join(', ')} (${changes.length} total)`;\n}\n\n/**\n * Check if changes are only in package.json\n */\nexport function isOnlyPackageJsonChange(changes: FileChange[]): boolean {\n if (changes.length === 0) return false;\n\n for (let i = 0; i < changes.length; i++) {\n const path = changes[i].path;\n if (path !== 'package/package.json' && path.indexOf('/package.json') !== path.length - 13) {\n return false;\n }\n }\n return true;\n}\n\n/**\n * Extract package.json from a tarball\n *\n * @param tarball - Tarball as Buffer (gzipped)\n * @returns Parsed package.json object\n */\nexport async function extractPackageJson(tarball: Buffer): Promise<unknown> {\n const files = await extractTarball(tarball);\n\n // Look for package.json in the tarball\n const pkgJsonPath = Object.keys(files).find((p) => p === 'package/package.json' || p.indexOf('/package.json') === p.length - 13);\n\n if (!pkgJsonPath || !files[pkgJsonPath]) {\n throw new Error('package.json not found in tarball');\n }\n\n return JSON.parse(files[pkgJsonPath].toString('utf8'));\n}\n"],"names":["comparePackageFiles","extractPackageJson","getFileChangeSummary","hashBuffer","isOnlyPackageJsonChange","pipeline","promisify","pipelineCb","_require","require","Module","createRequire","_tar","getTar","localTarball","registryTarball","localHash","registryHash","localFiles","registryFiles","changes","allPaths","localKeys","i","registryKeys","onlyPackageJsonDiffers","pathKeys","filePath","isPackageJson","localContent","registryContent","identical","fileChanges","packageJsonOnly","extractTarball","Object","keys","length","indexOf","push","path","action","buffersEqual","tarball","files","tar","parser","Parser","onReadEntry","entry","type","chunks","on","chunk","Buffer","concat","resume","Readable","from","zlib","createGunzip","buffer","crypto","createHash","update","digest","a","b","timingSafeEqual","added","filter","c","removed","modified","parts","join","pkgJsonPath","find","p","Error","JSON","parse","toString"],"mappings":"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"}
|
|
@@ -55,4 +55,4 @@ var _dependencyts = require("./dependency.js");
|
|
|
55
55
|
var _filecontentts = require("./file-content.js");
|
|
56
56
|
var _packagejsonts = require("./package-json.js");
|
|
57
57
|
var _versionspecifierts = require("./version-specifier.js");
|
|
58
|
-
/* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (
|
|
58
|
+
/* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (var key in exports) { exports.default[key] = exports[key]; } } catch (_) {}; module.exports = exports.default; }
|
|
@@ -322,4 +322,4 @@ function isSignificantField(field, options) {
|
|
|
322
322
|
// Default significant fields
|
|
323
323
|
return !!SIGNIFICANT_FIELDS[field];
|
|
324
324
|
}
|
|
325
|
-
/* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (
|
|
325
|
+
/* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (var key in exports) { exports.default[key] = exports[key]; } } catch (_) {}; module.exports = exports.default; }
|
|
@@ -98,7 +98,7 @@ function parseVersionSpecifier(spec, where) {
|
|
|
98
98
|
loose: true
|
|
99
99
|
});
|
|
100
100
|
normalized = range.range || spec;
|
|
101
|
-
} catch (
|
|
101
|
+
} catch (unused) {
|
|
102
102
|
// Keep original if normalization fails
|
|
103
103
|
}
|
|
104
104
|
return {
|
|
@@ -166,7 +166,7 @@ function parseVersionSpecifier(spec, where) {
|
|
|
166
166
|
raw: spec,
|
|
167
167
|
normalized: range1.range || spec
|
|
168
168
|
};
|
|
169
|
-
} catch (
|
|
169
|
+
} catch (unused) {
|
|
170
170
|
// Unknown type
|
|
171
171
|
return {
|
|
172
172
|
type: 'tag',
|
|
@@ -175,7 +175,7 @@ function parseVersionSpecifier(spec, where) {
|
|
|
175
175
|
};
|
|
176
176
|
}
|
|
177
177
|
}
|
|
178
|
-
} catch (
|
|
178
|
+
} catch (unused) {
|
|
179
179
|
// Try to parse as semver range
|
|
180
180
|
try {
|
|
181
181
|
var range2 = new semver.Range(spec, {
|
|
@@ -186,7 +186,7 @@ function parseVersionSpecifier(spec, where) {
|
|
|
186
186
|
raw: spec,
|
|
187
187
|
normalized: range2.range || spec
|
|
188
188
|
};
|
|
189
|
-
} catch (
|
|
189
|
+
} catch (unused) {
|
|
190
190
|
// Treat as tag
|
|
191
191
|
return {
|
|
192
192
|
type: 'tag',
|
|
@@ -225,7 +225,7 @@ function parseVersionSpecifier(spec, where) {
|
|
|
225
225
|
major: minVersion.major,
|
|
226
226
|
minor: minVersion.minor
|
|
227
227
|
};
|
|
228
|
-
} catch (
|
|
228
|
+
} catch (unused) {
|
|
229
229
|
return null;
|
|
230
230
|
}
|
|
231
231
|
}
|
|
@@ -321,7 +321,7 @@ function compareVersionSpecifiers(specA, specB, options) {
|
|
|
321
321
|
rangeB = new semver.Range(specB, {
|
|
322
322
|
loose: true
|
|
323
323
|
});
|
|
324
|
-
} catch (
|
|
324
|
+
} catch (unused) {
|
|
325
325
|
// One or both are not valid semver ranges
|
|
326
326
|
return {
|
|
327
327
|
equivalent: false,
|
|
@@ -370,14 +370,14 @@ function compareVersionSpecifiers(specA, specB, options) {
|
|
|
370
370
|
aSubsetB = semver.subset(rangeA, rangeB, {
|
|
371
371
|
includePrerelease: true
|
|
372
372
|
});
|
|
373
|
-
} catch (
|
|
373
|
+
} catch (unused) {
|
|
374
374
|
// subset can throw on complex ranges
|
|
375
375
|
}
|
|
376
376
|
try {
|
|
377
377
|
bSubsetA = semver.subset(rangeB, rangeA, {
|
|
378
378
|
includePrerelease: true
|
|
379
379
|
});
|
|
380
|
-
} catch (
|
|
380
|
+
} catch (unused) {
|
|
381
381
|
// subset can throw on complex ranges
|
|
382
382
|
}
|
|
383
383
|
if (aSubsetB && bSubsetA) {
|
|
@@ -574,4 +574,4 @@ function comparisonToSemanticChange(comparison, options) {
|
|
|
574
574
|
return 'incompatible';
|
|
575
575
|
}
|
|
576
576
|
}
|
|
577
|
-
/* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (
|
|
577
|
+
/* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (var key in exports) { exports.default[key] = exports[key]; } } catch (_) {}; module.exports = exports.default; }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/npm/npm-needs-publish/src/comparators/version-specifier.ts"],"sourcesContent":["/**\n * Version specifier parsing and comparison\n *\n * Handles ALL npm version specifier formats:\n * - Exact: 1.2.3\n * - Caret: ^1.2.3\n * - Tilde: ~1.2.3\n * - Range: >=1.0.0 <2.0.0\n * - X-range: 1.x, 1.2.x, *\n * - Hyphen: 1.2.3 - 2.3.4\n * - OR: >=1.0.0 || >=2.0.0\n * - Git: git+https://...\n * - File: file:../local\n * - Alias: npm:package@version\n * - Workspace: workspace:*, workspace:^\n * - Tag: latest, next\n * - URL: http(s) URLs to tarballs\n */\n\nimport Module from 'module';\nimport type { CompareSpecifierOptions, ParsedVersionSpecifier, SemanticChange, SpecifierComparison, VersionSpecifierType } from '../types.ts';\n\nconst _require = typeof require === 'undefined' ? Module.createRequire(import.meta.url) : require;\n\n// Lazy load dependencies\nlet _semver: typeof import('semver') | null = null;\nlet _npa: typeof import('npm-package-arg') | null = null;\n\nfunction getSemver(): typeof import('semver') {\n if (!_semver) {\n _semver = _require('semver');\n }\n return _semver;\n}\n\nfunction getNpa(): typeof import('npm-package-arg') {\n if (!_npa) {\n _npa = _require('npm-package-arg');\n }\n return _npa;\n}\n\n/**\n * Parse a version specifier into its components\n */\nexport function parseVersionSpecifier(spec: string, where?: string): ParsedVersionSpecifier {\n const semver = getSemver();\n const npa = getNpa();\n\n // Handle workspace protocol first (not handled by npa)\n if (spec.startsWith('workspace:')) {\n const workspaceRange = spec.slice(10);\n return {\n type: 'workspace',\n raw: spec,\n normalized: spec,\n workspaceRange: workspaceRange || '*',\n };\n }\n\n // Handle empty string\n if (!spec || spec === '') {\n return {\n type: 'x-range',\n raw: spec,\n normalized: '*',\n };\n }\n\n try {\n // Use npm-package-arg for standard parsing\n const parsed = npa.resolve('pkg', spec, where);\n\n switch (parsed.type) {\n case 'version':\n return {\n type: 'exact',\n raw: spec,\n normalized: parsed.fetchSpec || spec,\n };\n\n case 'range': {\n const rangeType = detectRangeSubtype(spec);\n let normalized = spec;\n try {\n const range = new semver.Range(parsed.fetchSpec || spec, { loose: true });\n normalized = range.range || spec;\n } catch {\n // Keep original if normalization fails\n }\n return {\n type: rangeType,\n raw: spec,\n normalized,\n };\n }\n\n case 'tag':\n return {\n type: 'tag',\n raw: spec,\n normalized: parsed.fetchSpec || spec,\n };\n\n case 'git':\n return {\n type: 'git',\n raw: spec,\n normalized: parsed.saveSpec || spec,\n gitInfo: {\n url: parsed.fetchSpec || '',\n committish: parsed.gitCommittish || undefined,\n semverRange: parsed.gitRange || undefined,\n },\n };\n\n case 'remote':\n return {\n type: 'url',\n raw: spec,\n normalized: parsed.fetchSpec || spec,\n };\n\n case 'file':\n case 'directory':\n return {\n type: 'file',\n raw: spec,\n normalized: parsed.saveSpec || spec,\n };\n\n case 'alias': {\n // Recursively parse the underlying spec\n const subSpec = parsed.subSpec;\n if (subSpec) {\n return {\n type: 'alias',\n raw: spec,\n normalized: spec,\n aliasTarget: parseVersionSpecifier(subSpec.rawSpec, where),\n };\n }\n return {\n type: 'alias',\n raw: spec,\n normalized: spec,\n };\n }\n\n default:\n // Try to parse as semver range\n try {\n const range = new semver.Range(spec, { loose: true });\n return {\n type: detectRangeSubtype(spec),\n raw: spec,\n normalized: range.range || spec,\n };\n } catch {\n // Unknown type\n return {\n type: 'tag',\n raw: spec,\n normalized: spec,\n };\n }\n }\n } catch {\n // Try to parse as semver range\n try {\n const range = new semver.Range(spec, { loose: true });\n return {\n type: detectRangeSubtype(spec),\n raw: spec,\n normalized: range.range || spec,\n };\n } catch {\n // Treat as tag\n return {\n type: 'tag',\n raw: spec,\n normalized: spec,\n };\n }\n }\n}\n\n/**\n * Detect the subtype of a semver range\n */\nfunction detectRangeSubtype(spec: string): VersionSpecifierType {\n const trimmed = spec.trim();\n\n if (trimmed.startsWith('^')) return 'caret';\n if (trimmed.startsWith('~')) return 'tilde';\n if (trimmed.includes(' - ')) return 'hyphen';\n if (trimmed.includes('||')) return 'or';\n if (/[xX*]/.test(trimmed) || trimmed === '') return 'x-range';\n\n // Check if it's an exact version\n const semver = getSemver();\n if (semver.valid(trimmed)) return 'exact';\n\n return 'range';\n}\n\n/**\n * Extract major and minor version from a semver range\n * Uses semver.minVersion to get the minimum satisfying version\n */\nfunction extractMajorMinor(spec: string): { major: number; minor: number } | null {\n const semver = getSemver();\n try {\n const range = new semver.Range(spec, { loose: true });\n const minVersion = semver.minVersion(range);\n if (!minVersion) return null;\n return { major: minVersion.major, minor: minVersion.minor };\n } catch {\n return null;\n }\n}\n\n/**\n * Compare two version specifiers for semantic equivalence\n *\n * @returns Whether the two specifiers would resolve to the same set of versions\n */\nexport function compareVersionSpecifiers(specA: string, specB: string, options?: CompareSpecifierOptions): SpecifierComparison {\n // Fast path: identical strings\n if (specA === specB) {\n return { equivalent: true, relation: 'identical' };\n }\n\n // Parse both specifiers\n const parsedA = parseVersionSpecifier(specA, options?.where);\n const parsedB = parseVersionSpecifier(specB, options?.where);\n\n // Handle workspace protocol specially\n if (parsedA.type === 'workspace' || parsedB.type === 'workspace') {\n return compareWorkspaceSpecs(parsedA, parsedB);\n }\n\n // Handle alias types - compare underlying specs\n if (parsedA.type === 'alias' || parsedB.type === 'alias') {\n return compareAliasSpecs(parsedA, parsedB, options);\n }\n\n // Different types are generally not equivalent\n if (getSpecCategory(parsedA.type) !== getSpecCategory(parsedB.type)) {\n return { equivalent: false, relation: 'incompatible-types' };\n }\n\n // Type-specific comparison\n switch (parsedA.type) {\n case 'exact':\n case 'caret':\n case 'tilde':\n case 'range':\n case 'x-range':\n case 'hyphen':\n case 'or':\n return compareSemverSpecs(specA, specB);\n\n case 'git':\n return compareGitSpecs(parsedA, parsedB);\n\n case 'file':\n return compareFileSpecs(parsedA, parsedB);\n\n case 'tag':\n return compareTagSpecs(parsedA, parsedB);\n\n case 'url':\n return compareUrlSpecs(parsedA, parsedB);\n\n default:\n return { equivalent: false, relation: 'unknown-type' };\n }\n}\n\n/**\n * Get the category of a specifier type for comparison purposes\n */\nfunction getSpecCategory(type: VersionSpecifierType): string {\n switch (type) {\n case 'exact':\n case 'caret':\n case 'tilde':\n case 'range':\n case 'x-range':\n case 'hyphen':\n case 'or':\n return 'semver';\n case 'git':\n return 'git';\n case 'file':\n return 'file';\n case 'tag':\n return 'tag';\n case 'url':\n return 'url';\n case 'alias':\n return 'alias';\n case 'workspace':\n return 'workspace';\n default:\n return 'unknown';\n }\n}\n\n/**\n * Compare two semver-based specifiers\n */\nfunction compareSemverSpecs(specA: string, specB: string): SpecifierComparison {\n const semver = getSemver();\n\n let rangeA: InstanceType<typeof semver.Range>;\n let rangeB: InstanceType<typeof semver.Range>;\n\n try {\n rangeA = new semver.Range(specA, { loose: true });\n rangeB = new semver.Range(specB, { loose: true });\n } catch {\n // One or both are not valid semver ranges\n return { equivalent: false, relation: 'incompatible-types' };\n }\n\n // Normalize to canonical form for string comparison\n const normalizedA = rangeA.range;\n const normalizedB = rangeB.range;\n\n if (normalizedA === normalizedB) {\n return { equivalent: true, relation: 'normalized-equal' };\n }\n\n // Check if both are caret ranges with same major version\n // This handles ncu -u scenarios like ^4.17.0 → ^4.17.21 or ^4.17.0 → ^4.18.0\n if (specA.trim().startsWith('^') && specB.trim().startsWith('^')) {\n const boundsA = extractMajorMinor(specA);\n const boundsB = extractMajorMinor(specB);\n if (boundsA && boundsB && boundsA.major === boundsB.major) {\n return { equivalent: true, relation: 'same-major-caret' };\n }\n }\n\n // Check if both are tilde ranges with same major.minor version\n // This handles scenarios like ~4.17.0 → ~4.17.5\n if (specA.trim().startsWith('~') && specB.trim().startsWith('~')) {\n const boundsA = extractMajorMinor(specA);\n const boundsB = extractMajorMinor(specB);\n if (boundsA && boundsB && boundsA.major === boundsB.major && boundsA.minor === boundsB.minor) {\n return { equivalent: true, relation: 'same-minor-tilde' };\n }\n }\n\n // Use semver.subset to determine relationship\n // A is subset of B means A is more restrictive (all versions in A are in B)\n // We need to check both directions to determine equivalence\n let aSubsetB = false;\n let bSubsetA = false;\n\n try {\n aSubsetB = semver.subset(rangeA, rangeB, { includePrerelease: true });\n } catch {\n // subset can throw on complex ranges\n }\n\n try {\n bSubsetA = semver.subset(rangeB, rangeA, { includePrerelease: true });\n } catch {\n // subset can throw on complex ranges\n }\n\n if (aSubsetB && bSubsetA) {\n // Equivalent ranges (different syntax, same meaning)\n return { equivalent: true, relation: 'semantically-equal' };\n }\n\n if (bSubsetA) {\n // New (B) is subset of old (A) → new is more restrictive (narrowed)\n // Example: * → ^4.17.0 (constrains to v4.x)\n return { equivalent: false, relation: 'narrowed', detail: 'new range is subset of old' };\n }\n\n if (aSubsetB) {\n // Old (A) is subset of new (B) → new is less restrictive (widened)\n // Example: ^4.17.0 → * (allows any version)\n return { equivalent: false, relation: 'widened', detail: 'old range is subset of new' };\n }\n\n // Check if they intersect at all\n const intersects = rangeA.intersects(rangeB);\n return {\n equivalent: false,\n relation: intersects ? 'partially-overlapping' : 'disjoint',\n };\n}\n\n/**\n * Compare git specifiers\n */\nfunction compareGitSpecs(parsedA: ParsedVersionSpecifier, parsedB: ParsedVersionSpecifier): SpecifierComparison {\n // For git specs, compare URL and committish\n const gitA = parsedA.gitInfo;\n const gitB = parsedB.gitInfo;\n\n if (!gitA || !gitB) {\n return { equivalent: false, relation: 'incompatible-types' };\n }\n\n // URLs must match\n if (gitA.url !== gitB.url) {\n return { equivalent: false, relation: 'disjoint', detail: 'Different git URLs' };\n }\n\n // If both have committish, compare them\n if (gitA.committish && gitB.committish) {\n if (gitA.committish === gitB.committish) {\n return { equivalent: true, relation: 'identical' };\n }\n return { equivalent: false, relation: 'disjoint', detail: 'Different committish' };\n }\n\n // If both have semver ranges, compare them\n if (gitA.semverRange && gitB.semverRange) {\n return compareSemverSpecs(gitA.semverRange, gitB.semverRange);\n }\n\n // Mixed or missing - treat as different\n return { equivalent: false, relation: 'incompatible-types' };\n}\n\n/**\n * Compare file/directory specifiers\n * File specs are always treated as different since we can't compare their contents\n */\nfunction compareFileSpecs(parsedA: ParsedVersionSpecifier, parsedB: ParsedVersionSpecifier): SpecifierComparison {\n // File paths must be identical to be considered equivalent\n if (parsedA.normalized === parsedB.normalized) {\n return { equivalent: true, relation: 'identical' };\n }\n // Different file paths - always treat as different\n return { equivalent: false, relation: 'disjoint', detail: 'Different file paths' };\n}\n\n/**\n * Compare workspace specifiers\n */\nfunction compareWorkspaceSpecs(parsedA: ParsedVersionSpecifier, parsedB: ParsedVersionSpecifier): SpecifierComparison {\n // Both must be workspace\n if (parsedA.type !== 'workspace' || parsedB.type !== 'workspace') {\n return { equivalent: false, relation: 'incompatible-types' };\n }\n\n // Compare the underlying workspace ranges\n const rangeA = parsedA.workspaceRange || '*';\n const rangeB = parsedB.workspaceRange || '*';\n\n if (rangeA === rangeB) {\n return { equivalent: true, relation: 'identical' };\n }\n\n // Workspace ranges like * and ^ have specific meanings\n // * = use exact version from workspace\n // ^ = use caret range\n // ~ = use tilde range\n // For comparison purposes, treat different symbols as different\n return { equivalent: false, relation: 'disjoint', detail: 'Different workspace specifiers' };\n}\n\n/**\n * Compare tag specifiers (latest, next, etc.)\n * Tags are resolved at install time, so we can't compare them semantically\n */\nfunction compareTagSpecs(parsedA: ParsedVersionSpecifier, parsedB: ParsedVersionSpecifier): SpecifierComparison {\n // Tags must be identical to be considered equivalent\n if (parsedA.normalized === parsedB.normalized) {\n return { equivalent: true, relation: 'identical' };\n }\n // Different tags - always treat as potentially different\n return { equivalent: false, relation: 'disjoint', detail: 'Different tags' };\n}\n\n/**\n * Compare URL specifiers\n */\nfunction compareUrlSpecs(parsedA: ParsedVersionSpecifier, parsedB: ParsedVersionSpecifier): SpecifierComparison {\n // URLs must be identical to be considered equivalent\n if (parsedA.normalized === parsedB.normalized) {\n return { equivalent: true, relation: 'identical' };\n }\n return { equivalent: false, relation: 'disjoint', detail: 'Different URLs' };\n}\n\n/**\n * Compare alias specifiers by comparing their underlying targets\n */\nfunction compareAliasSpecs(parsedA: ParsedVersionSpecifier, parsedB: ParsedVersionSpecifier, options?: CompareSpecifierOptions): SpecifierComparison {\n // If both are aliases, compare their targets\n if (parsedA.type === 'alias' && parsedB.type === 'alias') {\n if (parsedA.aliasTarget && parsedB.aliasTarget) {\n return compareVersionSpecifiers(parsedA.aliasTarget.raw, parsedB.aliasTarget.raw, options);\n }\n }\n\n // If only one is alias, compare the alias target with the other spec\n if (parsedA.type === 'alias' && parsedA.aliasTarget) {\n return compareVersionSpecifiers(parsedA.aliasTarget.raw, parsedB.raw, options);\n }\n\n if (parsedB.type === 'alias' && parsedB.aliasTarget) {\n return compareVersionSpecifiers(parsedA.raw, parsedB.aliasTarget.raw, options);\n }\n\n return { equivalent: false, relation: 'incompatible-types' };\n}\n\n/**\n * Options for comparisonToSemanticChange\n */\nexport interface SemanticChangeOptions {\n /** Treat narrowed ranges as equivalent @default true */\n treatNarrowingAsEquivalent?: boolean;\n}\n\n/**\n * Map a specifier comparison to a semantic change type\n */\nexport function comparisonToSemanticChange(comparison: SpecifierComparison, options?: SemanticChangeOptions): SemanticChange {\n const treatNarrowingAsEquivalent = options?.treatNarrowingAsEquivalent !== false;\n\n if (comparison.equivalent) {\n return comparison.relation === 'identical' ? 'none' : 'equivalent';\n }\n\n switch (comparison.relation) {\n case 'narrowed':\n // When treatNarrowingAsEquivalent is true (default), narrowing is safe\n return treatNarrowingAsEquivalent ? 'equivalent' : 'narrowed';\n case 'widened':\n return 'widened';\n default:\n return 'incompatible';\n }\n}\n"],"names":["compareVersionSpecifiers","comparisonToSemanticChange","parseVersionSpecifier","_require","require","Module","createRequire","_semver","_npa","getSemver","getNpa","spec","where","semver","npa","startsWith","workspaceRange","slice","type","raw","normalized","parsed","resolve","fetchSpec","rangeType","detectRangeSubtype","range","Range","loose","saveSpec","gitInfo","url","committish","gitCommittish","undefined","semverRange","gitRange","subSpec","aliasTarget","rawSpec","trimmed","trim","includes","test","valid","extractMajorMinor","minVersion","major","minor","specA","specB","options","equivalent","relation","parsedA","parsedB","compareWorkspaceSpecs","compareAliasSpecs","getSpecCategory","compareSemverSpecs","compareGitSpecs","compareFileSpecs","compareTagSpecs","compareUrlSpecs","rangeA","rangeB","normalizedA","normalizedB","boundsA","boundsB","aSubsetB","bSubsetA","subset","includePrerelease","detail","intersects","gitA","gitB","comparison","treatNarrowingAsEquivalent"],"mappings":"AAAA;;;;;;;;;;;;;;;;;CAiBC;;;;;;;;;;;QAkNeA;eAAAA;;QA6SAC;eAAAA;;QAneAC;eAAAA;;;6DA1BG;;;;;;AAGnB,IAAMC,WAAW,OAAOC,YAAY,cAAcC,eAAM,CAACC,aAAa,CAAC,uDAAmBF;AAE1F,yBAAyB;AACzB,IAAIG,UAA0C;AAC9C,IAAIC,OAAgD;AAEpD,SAASC;IACP,IAAI,CAACF,SAAS;QACZA,UAAUJ,SAAS;IACrB;IACA,OAAOI;AACT;AAEA,SAASG;IACP,IAAI,CAACF,MAAM;QACTA,OAAOL,SAAS;IAClB;IACA,OAAOK;AACT;AAKO,SAASN,sBAAsBS,IAAY,EAAEC,KAAc;IAChE,IAAMC,SAASJ;IACf,IAAMK,MAAMJ;IAEZ,uDAAuD;IACvD,IAAIC,KAAKI,UAAU,CAAC,eAAe;QACjC,IAAMC,iBAAiBL,KAAKM,KAAK,CAAC;QAClC,OAAO;YACLC,MAAM;YACNC,KAAKR;YACLS,YAAYT;YACZK,gBAAgBA,kBAAkB;QACpC;IACF;IAEA,sBAAsB;IACtB,IAAI,CAACL,QAAQA,SAAS,IAAI;QACxB,OAAO;YACLO,MAAM;YACNC,KAAKR;YACLS,YAAY;QACd;IACF;IAEA,IAAI;QACF,2CAA2C;QAC3C,IAAMC,SAASP,IAAIQ,OAAO,CAAC,OAAOX,MAAMC;QAExC,OAAQS,OAAOH,IAAI;YACjB,KAAK;gBACH,OAAO;oBACLA,MAAM;oBACNC,KAAKR;oBACLS,YAAYC,OAAOE,SAAS,IAAIZ;gBAClC;YAEF,KAAK;gBAAS;oBACZ,IAAMa,YAAYC,mBAAmBd;oBACrC,IAAIS,aAAaT;oBACjB,IAAI;wBACF,IAAMe,QAAQ,IAAIb,OAAOc,KAAK,CAACN,OAAOE,SAAS,IAAIZ,MAAM;4BAAEiB,OAAO;wBAAK;wBACvER,aAAaM,MAAMA,KAAK,IAAIf;oBAC9B,EAAE,UAAM;oBACN,uCAAuC;oBACzC;oBACA,OAAO;wBACLO,MAAMM;wBACNL,KAAKR;wBACLS,YAAAA;oBACF;gBACF;YAEA,KAAK;gBACH,OAAO;oBACLF,MAAM;oBACNC,KAAKR;oBACLS,YAAYC,OAAOE,SAAS,IAAIZ;gBAClC;YAEF,KAAK;gBACH,OAAO;oBACLO,MAAM;oBACNC,KAAKR;oBACLS,YAAYC,OAAOQ,QAAQ,IAAIlB;oBAC/BmB,SAAS;wBACPC,KAAKV,OAAOE,SAAS,IAAI;wBACzBS,YAAYX,OAAOY,aAAa,IAAIC;wBACpCC,aAAad,OAAOe,QAAQ,IAAIF;oBAClC;gBACF;YAEF,KAAK;gBACH,OAAO;oBACLhB,MAAM;oBACNC,KAAKR;oBACLS,YAAYC,OAAOE,SAAS,IAAIZ;gBAClC;YAEF,KAAK;YACL,KAAK;gBACH,OAAO;oBACLO,MAAM;oBACNC,KAAKR;oBACLS,YAAYC,OAAOQ,QAAQ,IAAIlB;gBACjC;YAEF,KAAK;gBAAS;oBACZ,wCAAwC;oBACxC,IAAM0B,UAAUhB,OAAOgB,OAAO;oBAC9B,IAAIA,SAAS;wBACX,OAAO;4BACLnB,MAAM;4BACNC,KAAKR;4BACLS,YAAYT;4BACZ2B,aAAapC,sBAAsBmC,QAAQE,OAAO,EAAE3B;wBACtD;oBACF;oBACA,OAAO;wBACLM,MAAM;wBACNC,KAAKR;wBACLS,YAAYT;oBACd;gBACF;YAEA;gBACE,+BAA+B;gBAC/B,IAAI;oBACF,IAAMe,SAAQ,IAAIb,OAAOc,KAAK,CAAChB,MAAM;wBAAEiB,OAAO;oBAAK;oBACnD,OAAO;wBACLV,MAAMO,mBAAmBd;wBACzBQ,KAAKR;wBACLS,YAAYM,OAAMA,KAAK,IAAIf;oBAC7B;gBACF,EAAE,UAAM;oBACN,eAAe;oBACf,OAAO;wBACLO,MAAM;wBACNC,KAAKR;wBACLS,YAAYT;oBACd;gBACF;QACJ;IACF,EAAE,UAAM;QACN,+BAA+B;QAC/B,IAAI;YACF,IAAMe,SAAQ,IAAIb,OAAOc,KAAK,CAAChB,MAAM;gBAAEiB,OAAO;YAAK;YACnD,OAAO;gBACLV,MAAMO,mBAAmBd;gBACzBQ,KAAKR;gBACLS,YAAYM,OAAMA,KAAK,IAAIf;YAC7B;QACF,EAAE,UAAM;YACN,eAAe;YACf,OAAO;gBACLO,MAAM;gBACNC,KAAKR;gBACLS,YAAYT;YACd;QACF;IACF;AACF;AAEA;;CAEC,GACD,SAASc,mBAAmBd,IAAY;IACtC,IAAM6B,UAAU7B,KAAK8B,IAAI;IAEzB,IAAID,QAAQzB,UAAU,CAAC,MAAM,OAAO;IACpC,IAAIyB,QAAQzB,UAAU,CAAC,MAAM,OAAO;IACpC,IAAIyB,QAAQE,QAAQ,CAAC,QAAQ,OAAO;IACpC,IAAIF,QAAQE,QAAQ,CAAC,OAAO,OAAO;IACnC,IAAI,QAAQC,IAAI,CAACH,YAAYA,YAAY,IAAI,OAAO;IAEpD,iCAAiC;IACjC,IAAM3B,SAASJ;IACf,IAAII,OAAO+B,KAAK,CAACJ,UAAU,OAAO;IAElC,OAAO;AACT;AAEA;;;CAGC,GACD,SAASK,kBAAkBlC,IAAY;IACrC,IAAME,SAASJ;IACf,IAAI;QACF,IAAMiB,QAAQ,IAAIb,OAAOc,KAAK,CAAChB,MAAM;YAAEiB,OAAO;QAAK;QACnD,IAAMkB,aAAajC,OAAOiC,UAAU,CAACpB;QACrC,IAAI,CAACoB,YAAY,OAAO;QACxB,OAAO;YAAEC,OAAOD,WAAWC,KAAK;YAAEC,OAAOF,WAAWE,KAAK;QAAC;IAC5D,EAAE,UAAM;QACN,OAAO;IACT;AACF;AAOO,SAAShD,yBAAyBiD,KAAa,EAAEC,KAAa,EAAEC,OAAiC;IACtG,+BAA+B;IAC/B,IAAIF,UAAUC,OAAO;QACnB,OAAO;YAAEE,YAAY;YAAMC,UAAU;QAAY;IACnD;IAEA,wBAAwB;IACxB,IAAMC,UAAUpD,sBAAsB+C,OAAOE,oBAAAA,8BAAAA,QAASvC,KAAK;IAC3D,IAAM2C,UAAUrD,sBAAsBgD,OAAOC,oBAAAA,8BAAAA,QAASvC,KAAK;IAE3D,sCAAsC;IACtC,IAAI0C,QAAQpC,IAAI,KAAK,eAAeqC,QAAQrC,IAAI,KAAK,aAAa;QAChE,OAAOsC,sBAAsBF,SAASC;IACxC;IAEA,gDAAgD;IAChD,IAAID,QAAQpC,IAAI,KAAK,WAAWqC,QAAQrC,IAAI,KAAK,SAAS;QACxD,OAAOuC,kBAAkBH,SAASC,SAASJ;IAC7C;IAEA,+CAA+C;IAC/C,IAAIO,gBAAgBJ,QAAQpC,IAAI,MAAMwC,gBAAgBH,QAAQrC,IAAI,GAAG;QACnE,OAAO;YAAEkC,YAAY;YAAOC,UAAU;QAAqB;IAC7D;IAEA,2BAA2B;IAC3B,OAAQC,QAAQpC,IAAI;QAClB,KAAK;QACL,KAAK;QACL,KAAK;QACL,KAAK;QACL,KAAK;QACL,KAAK;QACL,KAAK;YACH,OAAOyC,mBAAmBV,OAAOC;QAEnC,KAAK;YACH,OAAOU,gBAAgBN,SAASC;QAElC,KAAK;YACH,OAAOM,iBAAiBP,SAASC;QAEnC,KAAK;YACH,OAAOO,gBAAgBR,SAASC;QAElC,KAAK;YACH,OAAOQ,gBAAgBT,SAASC;QAElC;YACE,OAAO;gBAAEH,YAAY;gBAAOC,UAAU;YAAe;IACzD;AACF;AAEA;;CAEC,GACD,SAASK,gBAAgBxC,IAA0B;IACjD,OAAQA;QACN,KAAK;QACL,KAAK;QACL,KAAK;QACL,KAAK;QACL,KAAK;QACL,KAAK;QACL,KAAK;YACH,OAAO;QACT,KAAK;YACH,OAAO;QACT,KAAK;YACH,OAAO;QACT,KAAK;YACH,OAAO;QACT,KAAK;YACH,OAAO;QACT,KAAK;YACH,OAAO;QACT,KAAK;YACH,OAAO;QACT;YACE,OAAO;IACX;AACF;AAEA;;CAEC,GACD,SAASyC,mBAAmBV,KAAa,EAAEC,KAAa;IACtD,IAAMrC,SAASJ;IAEf,IAAIuD;IACJ,IAAIC;IAEJ,IAAI;QACFD,SAAS,IAAInD,OAAOc,KAAK,CAACsB,OAAO;YAAErB,OAAO;QAAK;QAC/CqC,SAAS,IAAIpD,OAAOc,KAAK,CAACuB,OAAO;YAAEtB,OAAO;QAAK;IACjD,EAAE,UAAM;QACN,0CAA0C;QAC1C,OAAO;YAAEwB,YAAY;YAAOC,UAAU;QAAqB;IAC7D;IAEA,oDAAoD;IACpD,IAAMa,cAAcF,OAAOtC,KAAK;IAChC,IAAMyC,cAAcF,OAAOvC,KAAK;IAEhC,IAAIwC,gBAAgBC,aAAa;QAC/B,OAAO;YAAEf,YAAY;YAAMC,UAAU;QAAmB;IAC1D;IAEA,yDAAyD;IACzD,6EAA6E;IAC7E,IAAIJ,MAAMR,IAAI,GAAG1B,UAAU,CAAC,QAAQmC,MAAMT,IAAI,GAAG1B,UAAU,CAAC,MAAM;QAChE,IAAMqD,UAAUvB,kBAAkBI;QAClC,IAAMoB,UAAUxB,kBAAkBK;QAClC,IAAIkB,WAAWC,WAAWD,QAAQrB,KAAK,KAAKsB,QAAQtB,KAAK,EAAE;YACzD,OAAO;gBAAEK,YAAY;gBAAMC,UAAU;YAAmB;QAC1D;IACF;IAEA,+DAA+D;IAC/D,gDAAgD;IAChD,IAAIJ,MAAMR,IAAI,GAAG1B,UAAU,CAAC,QAAQmC,MAAMT,IAAI,GAAG1B,UAAU,CAAC,MAAM;QAChE,IAAMqD,WAAUvB,kBAAkBI;QAClC,IAAMoB,WAAUxB,kBAAkBK;QAClC,IAAIkB,YAAWC,YAAWD,SAAQrB,KAAK,KAAKsB,SAAQtB,KAAK,IAAIqB,SAAQpB,KAAK,KAAKqB,SAAQrB,KAAK,EAAE;YAC5F,OAAO;gBAAEI,YAAY;gBAAMC,UAAU;YAAmB;QAC1D;IACF;IAEA,8CAA8C;IAC9C,4EAA4E;IAC5E,4DAA4D;IAC5D,IAAIiB,WAAW;IACf,IAAIC,WAAW;IAEf,IAAI;QACFD,WAAWzD,OAAO2D,MAAM,CAACR,QAAQC,QAAQ;YAAEQ,mBAAmB;QAAK;IACrE,EAAE,UAAM;IACN,qCAAqC;IACvC;IAEA,IAAI;QACFF,WAAW1D,OAAO2D,MAAM,CAACP,QAAQD,QAAQ;YAAES,mBAAmB;QAAK;IACrE,EAAE,UAAM;IACN,qCAAqC;IACvC;IAEA,IAAIH,YAAYC,UAAU;QACxB,qDAAqD;QACrD,OAAO;YAAEnB,YAAY;YAAMC,UAAU;QAAqB;IAC5D;IAEA,IAAIkB,UAAU;QACZ,oEAAoE;QACpE,4CAA4C;QAC5C,OAAO;YAAEnB,YAAY;YAAOC,UAAU;YAAYqB,QAAQ;QAA6B;IACzF;IAEA,IAAIJ,UAAU;QACZ,mEAAmE;QACnE,4CAA4C;QAC5C,OAAO;YAAElB,YAAY;YAAOC,UAAU;YAAWqB,QAAQ;QAA6B;IACxF;IAEA,iCAAiC;IACjC,IAAMC,aAAaX,OAAOW,UAAU,CAACV;IACrC,OAAO;QACLb,YAAY;QACZC,UAAUsB,aAAa,0BAA0B;IACnD;AACF;AAEA;;CAEC,GACD,SAASf,gBAAgBN,OAA+B,EAAEC,OAA+B;IACvF,4CAA4C;IAC5C,IAAMqB,OAAOtB,QAAQxB,OAAO;IAC5B,IAAM+C,OAAOtB,QAAQzB,OAAO;IAE5B,IAAI,CAAC8C,QAAQ,CAACC,MAAM;QAClB,OAAO;YAAEzB,YAAY;YAAOC,UAAU;QAAqB;IAC7D;IAEA,kBAAkB;IAClB,IAAIuB,KAAK7C,GAAG,KAAK8C,KAAK9C,GAAG,EAAE;QACzB,OAAO;YAAEqB,YAAY;YAAOC,UAAU;YAAYqB,QAAQ;QAAqB;IACjF;IAEA,wCAAwC;IACxC,IAAIE,KAAK5C,UAAU,IAAI6C,KAAK7C,UAAU,EAAE;QACtC,IAAI4C,KAAK5C,UAAU,KAAK6C,KAAK7C,UAAU,EAAE;YACvC,OAAO;gBAAEoB,YAAY;gBAAMC,UAAU;YAAY;QACnD;QACA,OAAO;YAAED,YAAY;YAAOC,UAAU;YAAYqB,QAAQ;QAAuB;IACnF;IAEA,2CAA2C;IAC3C,IAAIE,KAAKzC,WAAW,IAAI0C,KAAK1C,WAAW,EAAE;QACxC,OAAOwB,mBAAmBiB,KAAKzC,WAAW,EAAE0C,KAAK1C,WAAW;IAC9D;IAEA,wCAAwC;IACxC,OAAO;QAAEiB,YAAY;QAAOC,UAAU;IAAqB;AAC7D;AAEA;;;CAGC,GACD,SAASQ,iBAAiBP,OAA+B,EAAEC,OAA+B;IACxF,2DAA2D;IAC3D,IAAID,QAAQlC,UAAU,KAAKmC,QAAQnC,UAAU,EAAE;QAC7C,OAAO;YAAEgC,YAAY;YAAMC,UAAU;QAAY;IACnD;IACA,mDAAmD;IACnD,OAAO;QAAED,YAAY;QAAOC,UAAU;QAAYqB,QAAQ;IAAuB;AACnF;AAEA;;CAEC,GACD,SAASlB,sBAAsBF,OAA+B,EAAEC,OAA+B;IAC7F,yBAAyB;IACzB,IAAID,QAAQpC,IAAI,KAAK,eAAeqC,QAAQrC,IAAI,KAAK,aAAa;QAChE,OAAO;YAAEkC,YAAY;YAAOC,UAAU;QAAqB;IAC7D;IAEA,0CAA0C;IAC1C,IAAMW,SAASV,QAAQtC,cAAc,IAAI;IACzC,IAAMiD,SAASV,QAAQvC,cAAc,IAAI;IAEzC,IAAIgD,WAAWC,QAAQ;QACrB,OAAO;YAAEb,YAAY;YAAMC,UAAU;QAAY;IACnD;IAEA,uDAAuD;IACvD,uCAAuC;IACvC,sBAAsB;IACtB,sBAAsB;IACtB,gEAAgE;IAChE,OAAO;QAAED,YAAY;QAAOC,UAAU;QAAYqB,QAAQ;IAAiC;AAC7F;AAEA;;;CAGC,GACD,SAASZ,gBAAgBR,OAA+B,EAAEC,OAA+B;IACvF,qDAAqD;IACrD,IAAID,QAAQlC,UAAU,KAAKmC,QAAQnC,UAAU,EAAE;QAC7C,OAAO;YAAEgC,YAAY;YAAMC,UAAU;QAAY;IACnD;IACA,yDAAyD;IACzD,OAAO;QAAED,YAAY;QAAOC,UAAU;QAAYqB,QAAQ;IAAiB;AAC7E;AAEA;;CAEC,GACD,SAASX,gBAAgBT,OAA+B,EAAEC,OAA+B;IACvF,qDAAqD;IACrD,IAAID,QAAQlC,UAAU,KAAKmC,QAAQnC,UAAU,EAAE;QAC7C,OAAO;YAAEgC,YAAY;YAAMC,UAAU;QAAY;IACnD;IACA,OAAO;QAAED,YAAY;QAAOC,UAAU;QAAYqB,QAAQ;IAAiB;AAC7E;AAEA;;CAEC,GACD,SAASjB,kBAAkBH,OAA+B,EAAEC,OAA+B,EAAEJ,OAAiC;IAC5H,6CAA6C;IAC7C,IAAIG,QAAQpC,IAAI,KAAK,WAAWqC,QAAQrC,IAAI,KAAK,SAAS;QACxD,IAAIoC,QAAQhB,WAAW,IAAIiB,QAAQjB,WAAW,EAAE;YAC9C,OAAOtC,yBAAyBsD,QAAQhB,WAAW,CAACnB,GAAG,EAAEoC,QAAQjB,WAAW,CAACnB,GAAG,EAAEgC;QACpF;IACF;IAEA,qEAAqE;IACrE,IAAIG,QAAQpC,IAAI,KAAK,WAAWoC,QAAQhB,WAAW,EAAE;QACnD,OAAOtC,yBAAyBsD,QAAQhB,WAAW,CAACnB,GAAG,EAAEoC,QAAQpC,GAAG,EAAEgC;IACxE;IAEA,IAAII,QAAQrC,IAAI,KAAK,WAAWqC,QAAQjB,WAAW,EAAE;QACnD,OAAOtC,yBAAyBsD,QAAQnC,GAAG,EAAEoC,QAAQjB,WAAW,CAACnB,GAAG,EAAEgC;IACxE;IAEA,OAAO;QAAEC,YAAY;QAAOC,UAAU;IAAqB;AAC7D;AAaO,SAASpD,2BAA2B6E,UAA+B,EAAE3B,OAA+B;IACzG,IAAM4B,6BAA6B5B,CAAAA,oBAAAA,8BAAAA,QAAS4B,0BAA0B,MAAK;IAE3E,IAAID,WAAW1B,UAAU,EAAE;QACzB,OAAO0B,WAAWzB,QAAQ,KAAK,cAAc,SAAS;IACxD;IAEA,OAAQyB,WAAWzB,QAAQ;QACzB,KAAK;YACH,uEAAuE;YACvE,OAAO0B,6BAA6B,eAAe;QACrD,KAAK;YACH,OAAO;QACT;YACE,OAAO;IACX;AACF"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/npm/npm-needs-publish/src/comparators/version-specifier.ts"],"sourcesContent":["/**\n * Version specifier parsing and comparison\n *\n * Handles ALL npm version specifier formats:\n * - Exact: 1.2.3\n * - Caret: ^1.2.3\n * - Tilde: ~1.2.3\n * - Range: >=1.0.0 <2.0.0\n * - X-range: 1.x, 1.2.x, *\n * - Hyphen: 1.2.3 - 2.3.4\n * - OR: >=1.0.0 || >=2.0.0\n * - Git: git+https://...\n * - File: file:../local\n * - Alias: npm:package@version\n * - Workspace: workspace:*, workspace:^\n * - Tag: latest, next\n * - URL: http(s) URLs to tarballs\n */\n\nimport Module from 'module';\nimport type { CompareSpecifierOptions, ParsedVersionSpecifier, SemanticChange, SpecifierComparison, VersionSpecifierType } from '../types.ts';\n\nconst _require = typeof require === 'undefined' ? Module.createRequire(import.meta.url) : require;\n\n// Lazy load dependencies\nlet _semver: typeof import('semver') | null = null;\nlet _npa: typeof import('npm-package-arg') | null = null;\n\nfunction getSemver(): typeof import('semver') {\n if (!_semver) {\n _semver = _require('semver');\n }\n return _semver;\n}\n\nfunction getNpa(): typeof import('npm-package-arg') {\n if (!_npa) {\n _npa = _require('npm-package-arg');\n }\n return _npa;\n}\n\n/**\n * Parse a version specifier into its components\n */\nexport function parseVersionSpecifier(spec: string, where?: string): ParsedVersionSpecifier {\n const semver = getSemver();\n const npa = getNpa();\n\n // Handle workspace protocol first (not handled by npa)\n if (spec.startsWith('workspace:')) {\n const workspaceRange = spec.slice(10);\n return {\n type: 'workspace',\n raw: spec,\n normalized: spec,\n workspaceRange: workspaceRange || '*',\n };\n }\n\n // Handle empty string\n if (!spec || spec === '') {\n return {\n type: 'x-range',\n raw: spec,\n normalized: '*',\n };\n }\n\n try {\n // Use npm-package-arg for standard parsing\n const parsed = npa.resolve('pkg', spec, where);\n\n switch (parsed.type) {\n case 'version':\n return {\n type: 'exact',\n raw: spec,\n normalized: parsed.fetchSpec || spec,\n };\n\n case 'range': {\n const rangeType = detectRangeSubtype(spec);\n let normalized = spec;\n try {\n const range = new semver.Range(parsed.fetchSpec || spec, { loose: true });\n normalized = range.range || spec;\n } catch {\n // Keep original if normalization fails\n }\n return {\n type: rangeType,\n raw: spec,\n normalized,\n };\n }\n\n case 'tag':\n return {\n type: 'tag',\n raw: spec,\n normalized: parsed.fetchSpec || spec,\n };\n\n case 'git':\n return {\n type: 'git',\n raw: spec,\n normalized: parsed.saveSpec || spec,\n gitInfo: {\n url: parsed.fetchSpec || '',\n committish: parsed.gitCommittish || undefined,\n semverRange: parsed.gitRange || undefined,\n },\n };\n\n case 'remote':\n return {\n type: 'url',\n raw: spec,\n normalized: parsed.fetchSpec || spec,\n };\n\n case 'file':\n case 'directory':\n return {\n type: 'file',\n raw: spec,\n normalized: parsed.saveSpec || spec,\n };\n\n case 'alias': {\n // Recursively parse the underlying spec\n const subSpec = parsed.subSpec;\n if (subSpec) {\n return {\n type: 'alias',\n raw: spec,\n normalized: spec,\n aliasTarget: parseVersionSpecifier(subSpec.rawSpec, where),\n };\n }\n return {\n type: 'alias',\n raw: spec,\n normalized: spec,\n };\n }\n\n default:\n // Try to parse as semver range\n try {\n const range = new semver.Range(spec, { loose: true });\n return {\n type: detectRangeSubtype(spec),\n raw: spec,\n normalized: range.range || spec,\n };\n } catch {\n // Unknown type\n return {\n type: 'tag',\n raw: spec,\n normalized: spec,\n };\n }\n }\n } catch {\n // Try to parse as semver range\n try {\n const range = new semver.Range(spec, { loose: true });\n return {\n type: detectRangeSubtype(spec),\n raw: spec,\n normalized: range.range || spec,\n };\n } catch {\n // Treat as tag\n return {\n type: 'tag',\n raw: spec,\n normalized: spec,\n };\n }\n }\n}\n\n/**\n * Detect the subtype of a semver range\n */\nfunction detectRangeSubtype(spec: string): VersionSpecifierType {\n const trimmed = spec.trim();\n\n if (trimmed.startsWith('^')) return 'caret';\n if (trimmed.startsWith('~')) return 'tilde';\n if (trimmed.includes(' - ')) return 'hyphen';\n if (trimmed.includes('||')) return 'or';\n if (/[xX*]/.test(trimmed) || trimmed === '') return 'x-range';\n\n // Check if it's an exact version\n const semver = getSemver();\n if (semver.valid(trimmed)) return 'exact';\n\n return 'range';\n}\n\n/**\n * Extract major and minor version from a semver range\n * Uses semver.minVersion to get the minimum satisfying version\n */\nfunction extractMajorMinor(spec: string): { major: number; minor: number } | null {\n const semver = getSemver();\n try {\n const range = new semver.Range(spec, { loose: true });\n const minVersion = semver.minVersion(range);\n if (!minVersion) return null;\n return { major: minVersion.major, minor: minVersion.minor };\n } catch {\n return null;\n }\n}\n\n/**\n * Compare two version specifiers for semantic equivalence\n *\n * @returns Whether the two specifiers would resolve to the same set of versions\n */\nexport function compareVersionSpecifiers(specA: string, specB: string, options?: CompareSpecifierOptions): SpecifierComparison {\n // Fast path: identical strings\n if (specA === specB) {\n return { equivalent: true, relation: 'identical' };\n }\n\n // Parse both specifiers\n const parsedA = parseVersionSpecifier(specA, options?.where);\n const parsedB = parseVersionSpecifier(specB, options?.where);\n\n // Handle workspace protocol specially\n if (parsedA.type === 'workspace' || parsedB.type === 'workspace') {\n return compareWorkspaceSpecs(parsedA, parsedB);\n }\n\n // Handle alias types - compare underlying specs\n if (parsedA.type === 'alias' || parsedB.type === 'alias') {\n return compareAliasSpecs(parsedA, parsedB, options);\n }\n\n // Different types are generally not equivalent\n if (getSpecCategory(parsedA.type) !== getSpecCategory(parsedB.type)) {\n return { equivalent: false, relation: 'incompatible-types' };\n }\n\n // Type-specific comparison\n switch (parsedA.type) {\n case 'exact':\n case 'caret':\n case 'tilde':\n case 'range':\n case 'x-range':\n case 'hyphen':\n case 'or':\n return compareSemverSpecs(specA, specB);\n\n case 'git':\n return compareGitSpecs(parsedA, parsedB);\n\n case 'file':\n return compareFileSpecs(parsedA, parsedB);\n\n case 'tag':\n return compareTagSpecs(parsedA, parsedB);\n\n case 'url':\n return compareUrlSpecs(parsedA, parsedB);\n\n default:\n return { equivalent: false, relation: 'unknown-type' };\n }\n}\n\n/**\n * Get the category of a specifier type for comparison purposes\n */\nfunction getSpecCategory(type: VersionSpecifierType): string {\n switch (type) {\n case 'exact':\n case 'caret':\n case 'tilde':\n case 'range':\n case 'x-range':\n case 'hyphen':\n case 'or':\n return 'semver';\n case 'git':\n return 'git';\n case 'file':\n return 'file';\n case 'tag':\n return 'tag';\n case 'url':\n return 'url';\n case 'alias':\n return 'alias';\n case 'workspace':\n return 'workspace';\n default:\n return 'unknown';\n }\n}\n\n/**\n * Compare two semver-based specifiers\n */\nfunction compareSemverSpecs(specA: string, specB: string): SpecifierComparison {\n const semver = getSemver();\n\n let rangeA: InstanceType<typeof semver.Range>;\n let rangeB: InstanceType<typeof semver.Range>;\n\n try {\n rangeA = new semver.Range(specA, { loose: true });\n rangeB = new semver.Range(specB, { loose: true });\n } catch {\n // One or both are not valid semver ranges\n return { equivalent: false, relation: 'incompatible-types' };\n }\n\n // Normalize to canonical form for string comparison\n const normalizedA = rangeA.range;\n const normalizedB = rangeB.range;\n\n if (normalizedA === normalizedB) {\n return { equivalent: true, relation: 'normalized-equal' };\n }\n\n // Check if both are caret ranges with same major version\n // This handles ncu -u scenarios like ^4.17.0 → ^4.17.21 or ^4.17.0 → ^4.18.0\n if (specA.trim().startsWith('^') && specB.trim().startsWith('^')) {\n const boundsA = extractMajorMinor(specA);\n const boundsB = extractMajorMinor(specB);\n if (boundsA && boundsB && boundsA.major === boundsB.major) {\n return { equivalent: true, relation: 'same-major-caret' };\n }\n }\n\n // Check if both are tilde ranges with same major.minor version\n // This handles scenarios like ~4.17.0 → ~4.17.5\n if (specA.trim().startsWith('~') && specB.trim().startsWith('~')) {\n const boundsA = extractMajorMinor(specA);\n const boundsB = extractMajorMinor(specB);\n if (boundsA && boundsB && boundsA.major === boundsB.major && boundsA.minor === boundsB.minor) {\n return { equivalent: true, relation: 'same-minor-tilde' };\n }\n }\n\n // Use semver.subset to determine relationship\n // A is subset of B means A is more restrictive (all versions in A are in B)\n // We need to check both directions to determine equivalence\n let aSubsetB = false;\n let bSubsetA = false;\n\n try {\n aSubsetB = semver.subset(rangeA, rangeB, { includePrerelease: true });\n } catch {\n // subset can throw on complex ranges\n }\n\n try {\n bSubsetA = semver.subset(rangeB, rangeA, { includePrerelease: true });\n } catch {\n // subset can throw on complex ranges\n }\n\n if (aSubsetB && bSubsetA) {\n // Equivalent ranges (different syntax, same meaning)\n return { equivalent: true, relation: 'semantically-equal' };\n }\n\n if (bSubsetA) {\n // New (B) is subset of old (A) → new is more restrictive (narrowed)\n // Example: * → ^4.17.0 (constrains to v4.x)\n return { equivalent: false, relation: 'narrowed', detail: 'new range is subset of old' };\n }\n\n if (aSubsetB) {\n // Old (A) is subset of new (B) → new is less restrictive (widened)\n // Example: ^4.17.0 → * (allows any version)\n return { equivalent: false, relation: 'widened', detail: 'old range is subset of new' };\n }\n\n // Check if they intersect at all\n const intersects = rangeA.intersects(rangeB);\n return {\n equivalent: false,\n relation: intersects ? 'partially-overlapping' : 'disjoint',\n };\n}\n\n/**\n * Compare git specifiers\n */\nfunction compareGitSpecs(parsedA: ParsedVersionSpecifier, parsedB: ParsedVersionSpecifier): SpecifierComparison {\n // For git specs, compare URL and committish\n const gitA = parsedA.gitInfo;\n const gitB = parsedB.gitInfo;\n\n if (!gitA || !gitB) {\n return { equivalent: false, relation: 'incompatible-types' };\n }\n\n // URLs must match\n if (gitA.url !== gitB.url) {\n return { equivalent: false, relation: 'disjoint', detail: 'Different git URLs' };\n }\n\n // If both have committish, compare them\n if (gitA.committish && gitB.committish) {\n if (gitA.committish === gitB.committish) {\n return { equivalent: true, relation: 'identical' };\n }\n return { equivalent: false, relation: 'disjoint', detail: 'Different committish' };\n }\n\n // If both have semver ranges, compare them\n if (gitA.semverRange && gitB.semverRange) {\n return compareSemverSpecs(gitA.semverRange, gitB.semverRange);\n }\n\n // Mixed or missing - treat as different\n return { equivalent: false, relation: 'incompatible-types' };\n}\n\n/**\n * Compare file/directory specifiers\n * File specs are always treated as different since we can't compare their contents\n */\nfunction compareFileSpecs(parsedA: ParsedVersionSpecifier, parsedB: ParsedVersionSpecifier): SpecifierComparison {\n // File paths must be identical to be considered equivalent\n if (parsedA.normalized === parsedB.normalized) {\n return { equivalent: true, relation: 'identical' };\n }\n // Different file paths - always treat as different\n return { equivalent: false, relation: 'disjoint', detail: 'Different file paths' };\n}\n\n/**\n * Compare workspace specifiers\n */\nfunction compareWorkspaceSpecs(parsedA: ParsedVersionSpecifier, parsedB: ParsedVersionSpecifier): SpecifierComparison {\n // Both must be workspace\n if (parsedA.type !== 'workspace' || parsedB.type !== 'workspace') {\n return { equivalent: false, relation: 'incompatible-types' };\n }\n\n // Compare the underlying workspace ranges\n const rangeA = parsedA.workspaceRange || '*';\n const rangeB = parsedB.workspaceRange || '*';\n\n if (rangeA === rangeB) {\n return { equivalent: true, relation: 'identical' };\n }\n\n // Workspace ranges like * and ^ have specific meanings\n // * = use exact version from workspace\n // ^ = use caret range\n // ~ = use tilde range\n // For comparison purposes, treat different symbols as different\n return { equivalent: false, relation: 'disjoint', detail: 'Different workspace specifiers' };\n}\n\n/**\n * Compare tag specifiers (latest, next, etc.)\n * Tags are resolved at install time, so we can't compare them semantically\n */\nfunction compareTagSpecs(parsedA: ParsedVersionSpecifier, parsedB: ParsedVersionSpecifier): SpecifierComparison {\n // Tags must be identical to be considered equivalent\n if (parsedA.normalized === parsedB.normalized) {\n return { equivalent: true, relation: 'identical' };\n }\n // Different tags - always treat as potentially different\n return { equivalent: false, relation: 'disjoint', detail: 'Different tags' };\n}\n\n/**\n * Compare URL specifiers\n */\nfunction compareUrlSpecs(parsedA: ParsedVersionSpecifier, parsedB: ParsedVersionSpecifier): SpecifierComparison {\n // URLs must be identical to be considered equivalent\n if (parsedA.normalized === parsedB.normalized) {\n return { equivalent: true, relation: 'identical' };\n }\n return { equivalent: false, relation: 'disjoint', detail: 'Different URLs' };\n}\n\n/**\n * Compare alias specifiers by comparing their underlying targets\n */\nfunction compareAliasSpecs(parsedA: ParsedVersionSpecifier, parsedB: ParsedVersionSpecifier, options?: CompareSpecifierOptions): SpecifierComparison {\n // If both are aliases, compare their targets\n if (parsedA.type === 'alias' && parsedB.type === 'alias') {\n if (parsedA.aliasTarget && parsedB.aliasTarget) {\n return compareVersionSpecifiers(parsedA.aliasTarget.raw, parsedB.aliasTarget.raw, options);\n }\n }\n\n // If only one is alias, compare the alias target with the other spec\n if (parsedA.type === 'alias' && parsedA.aliasTarget) {\n return compareVersionSpecifiers(parsedA.aliasTarget.raw, parsedB.raw, options);\n }\n\n if (parsedB.type === 'alias' && parsedB.aliasTarget) {\n return compareVersionSpecifiers(parsedA.raw, parsedB.aliasTarget.raw, options);\n }\n\n return { equivalent: false, relation: 'incompatible-types' };\n}\n\n/**\n * Options for comparisonToSemanticChange\n */\nexport interface SemanticChangeOptions {\n /** Treat narrowed ranges as equivalent @default true */\n treatNarrowingAsEquivalent?: boolean;\n}\n\n/**\n * Map a specifier comparison to a semantic change type\n */\nexport function comparisonToSemanticChange(comparison: SpecifierComparison, options?: SemanticChangeOptions): SemanticChange {\n const treatNarrowingAsEquivalent = options?.treatNarrowingAsEquivalent !== false;\n\n if (comparison.equivalent) {\n return comparison.relation === 'identical' ? 'none' : 'equivalent';\n }\n\n switch (comparison.relation) {\n case 'narrowed':\n // When treatNarrowingAsEquivalent is true (default), narrowing is safe\n return treatNarrowingAsEquivalent ? 'equivalent' : 'narrowed';\n case 'widened':\n return 'widened';\n default:\n return 'incompatible';\n }\n}\n"],"names":["compareVersionSpecifiers","comparisonToSemanticChange","parseVersionSpecifier","_require","require","Module","createRequire","_semver","_npa","getSemver","getNpa","spec","where","semver","npa","startsWith","workspaceRange","slice","type","raw","normalized","parsed","resolve","fetchSpec","rangeType","detectRangeSubtype","range","Range","loose","saveSpec","gitInfo","url","committish","gitCommittish","undefined","semverRange","gitRange","subSpec","aliasTarget","rawSpec","trimmed","trim","includes","test","valid","extractMajorMinor","minVersion","major","minor","specA","specB","options","equivalent","relation","parsedA","parsedB","compareWorkspaceSpecs","compareAliasSpecs","getSpecCategory","compareSemverSpecs","compareGitSpecs","compareFileSpecs","compareTagSpecs","compareUrlSpecs","rangeA","rangeB","normalizedA","normalizedB","boundsA","boundsB","aSubsetB","bSubsetA","subset","includePrerelease","detail","intersects","gitA","gitB","comparison","treatNarrowingAsEquivalent"],"mappings":"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"}
|
package/dist/cjs/compat.js
CHANGED
|
@@ -23,4 +23,4 @@ function stringStartsWith(str, search, position) {
|
|
|
23
23
|
position = position || 0;
|
|
24
24
|
return str.indexOf(search, position) === position;
|
|
25
25
|
}
|
|
26
|
-
/* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (
|
|
26
|
+
/* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (var key in exports) { exports.default[key] = exports[key]; } } catch (_) {}; module.exports = exports.default; }
|
package/dist/cjs/fs-compat.js
CHANGED
|
@@ -54,4 +54,4 @@ function mkdtempSync(prefix) {
|
|
|
54
54
|
mkdirpSync(dir);
|
|
55
55
|
return dir;
|
|
56
56
|
}
|
|
57
|
-
/* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (
|
|
57
|
+
/* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (var key in exports) { exports.default[key] = exports[key]; } } catch (_) {}; module.exports = exports.default; }
|
package/dist/cjs/index.js
CHANGED
|
@@ -58,4 +58,4 @@ var _filecontentts = require("./comparators/file-content.js");
|
|
|
58
58
|
var _packagejsonts = require("./comparators/package-json.js");
|
|
59
59
|
var _versionspecifierts = require("./comparators/version-specifier.js");
|
|
60
60
|
var _needspublishts = require("./needs-publish.js");
|
|
61
|
-
/* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (
|
|
61
|
+
/* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (var key in exports) { exports.default[key] = exports[key]; } } catch (_) {}; module.exports = exports.default; }
|
|
@@ -167,9 +167,17 @@ function _ts_generator(thisArg, body) {
|
|
|
167
167
|
},
|
|
168
168
|
trys: [],
|
|
169
169
|
ops: []
|
|
170
|
-
}, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
171
|
-
return
|
|
172
|
-
|
|
170
|
+
}, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype), d = Object.defineProperty;
|
|
171
|
+
return d(g, "next", {
|
|
172
|
+
value: verb(0)
|
|
173
|
+
}), d(g, "throw", {
|
|
174
|
+
value: verb(1)
|
|
175
|
+
}), d(g, "return", {
|
|
176
|
+
value: verb(2)
|
|
177
|
+
}), typeof Symbol === "function" && d(g, Symbol.iterator, {
|
|
178
|
+
value: function() {
|
|
179
|
+
return this;
|
|
180
|
+
}
|
|
173
181
|
}), g;
|
|
174
182
|
function verb(n) {
|
|
175
183
|
return function(v) {
|
|
@@ -255,16 +263,13 @@ function needsPublishImpl(options, callback) {
|
|
|
255
263
|
// Load local package.json
|
|
256
264
|
var localPkg = options.package || JSON.parse(_fs.default.readFileSync(_path.default.join(cwd, 'package.json'), 'utf8'));
|
|
257
265
|
// Skip private packages
|
|
258
|
-
if (localPkg.private) {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
});
|
|
263
|
-
return;
|
|
264
|
-
}
|
|
266
|
+
if (localPkg.private) return callback(null, {
|
|
267
|
+
needsPublish: false,
|
|
268
|
+
reason: 'Package is private'
|
|
269
|
+
});
|
|
265
270
|
(function() {
|
|
266
271
|
return _async_to_generator(function() {
|
|
267
|
-
var pacote, Arborist, npa, execFile, promisify, execFileAsync, _ref, comparePackageFiles, comparePackageJson, extractPackageJson, hashBuffer, registry, scope, stdout,
|
|
272
|
+
var pacote, Arborist, npa, execFile, promisify, execFileAsync, _ref, comparePackageFiles, comparePackageJson, extractPackageJson, hashBuffer, registry, scope, stdout, unused, registryPkg, registryTarball, _packument_disttags, _registryPkg_dist, _registryPkg_dist1, packument, latestVersion, tarballUrl, err, error, localTarball, spec, manifest, err1, error1, localHash, registryHash, fileComparison, localTarballPkg, registryTarballPkg, pkgJsonComparison, changes;
|
|
268
273
|
return _ts_generator(this, function(_state) {
|
|
269
274
|
switch(_state.label){
|
|
270
275
|
case 0:
|
|
@@ -318,7 +323,7 @@ function needsPublishImpl(options, callback) {
|
|
|
318
323
|
5
|
|
319
324
|
];
|
|
320
325
|
case 4:
|
|
321
|
-
|
|
326
|
+
unused = _state.sent();
|
|
322
327
|
return [
|
|
323
328
|
3,
|
|
324
329
|
5
|
|
@@ -378,7 +383,8 @@ function needsPublishImpl(options, callback) {
|
|
|
378
383
|
}
|
|
379
384
|
// Fetch registry tarball for comparison
|
|
380
385
|
tarballUrl = (_registryPkg_dist = registryPkg.dist) === null || _registryPkg_dist === void 0 ? void 0 : _registryPkg_dist.tarball;
|
|
381
|
-
if (!tarballUrl)
|
|
386
|
+
if (!tarballUrl) return [
|
|
387
|
+
2,
|
|
382
388
|
callback(null, {
|
|
383
389
|
needsPublish: true,
|
|
384
390
|
reason: 'Registry package has no tarball URL',
|
|
@@ -388,11 +394,8 @@ function needsPublishImpl(options, callback) {
|
|
|
388
394
|
significance: 'critical'
|
|
389
395
|
}
|
|
390
396
|
]
|
|
391
|
-
})
|
|
392
|
-
|
|
393
|
-
2
|
|
394
|
-
];
|
|
395
|
-
}
|
|
397
|
+
})
|
|
398
|
+
];
|
|
396
399
|
return [
|
|
397
400
|
4,
|
|
398
401
|
pacote.tarball(tarballUrl, {
|
|
@@ -603,4 +606,4 @@ function needsPublish() {
|
|
|
603
606
|
});
|
|
604
607
|
});
|
|
605
608
|
}
|
|
606
|
-
/* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (
|
|
609
|
+
/* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (var key in exports) { exports.default[key] = exports[key]; } } catch (_) {}; module.exports = exports.default; }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/npm/npm-needs-publish/src/needs-publish.ts"],"sourcesContent":["/**\n * Main orchestration logic for npm-needs-publish\n *\n * Algorithm:\n * 1. Fetch registry packument → if E404, return needsPublish=true (first publish)\n * 2. Version check → if different, return needsPublish=true (intentional bump)\n * 3. Fast hash check → if identical, return needsPublish=false (no changes)\n * 4. Extract both tarballs, compare file-by-file (excluding package.json)\n * 5. If non-package.json files differ → return needsPublish=true\n * 6. If only package.json differs → do semantic comparison\n */\n\nimport fs from 'fs';\nimport Module from 'module';\nimport path from 'path';\nimport { stringStartsWith } from './compat.ts';\nimport type { ChangeDetail, NeedsPublishOptions, NeedsPublishResult, PackageJson } from './types.ts';\n\nconst _require = typeof require === 'undefined' ? Module.createRequire(import.meta.url) : require;\n\n/**\n * Callback type for needsPublish\n */\nexport type NeedsPublishCallback = (error: Error | null, result?: NeedsPublishResult) => void;\n\nfunction needsPublishImpl(options: NeedsPublishOptions, callback: NeedsPublishCallback): undefined {\n const cwd = options.cwd || process.cwd();\n\n // Load local package.json\n const localPkg: PackageJson = options.package || JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8'));\n\n // Skip private packages\n if (localPkg.private) {\n callback(null, {\n needsPublish: false,\n reason: 'Package is private',\n });\n return;\n }\n\n (async () => {\n const pacote = _require('pacote');\n const Arborist = _require('@npmcli/arborist');\n const npa = _require('npm-package-arg');\n const { execFile } = _require('child_process');\n const { promisify } = _require('util');\n const execFileAsync = promisify(execFile);\n\n // Dynamic import for comparators (they use modern features)\n const { comparePackageFiles, comparePackageJson, extractPackageJson, hashBuffer } = await import('./comparators/index.ts');\n\n // Get registry URL for scoped packages\n let registry: string | undefined = options.registry;\n if (!registry) {\n const scope = stringStartsWith(localPkg.name, '@') ? localPkg.name.split('/')[0] : undefined;\n if (scope) {\n try {\n const { stdout } = await execFileAsync('npm', ['config', 'get', `${scope}:registry`]);\n registry = stdout.trim();\n if (registry === 'undefined') registry = undefined;\n } catch {\n // Fallback to default registry\n }\n }\n }\n\n // Step 1: Try to fetch registry packument\n let registryPkg: PackageJson;\n let registryTarball: Buffer;\n\n try {\n const packument = await pacote.packument(localPkg.name, {\n Arborist,\n ...(registry && { registry }),\n });\n\n const latestVersion = packument['dist-tags']?.latest;\n if (!latestVersion) {\n callback(null, {\n needsPublish: true,\n reason: 'No latest version found in registry (first publish)',\n changes: [{ type: 'first-publish', significance: 'critical' }],\n });\n return;\n }\n\n registryPkg = packument.versions[latestVersion] as unknown as PackageJson;\n\n // Step 2: Version comparison (fast path)\n if (localPkg.version !== latestVersion) {\n callback(null, {\n needsPublish: true,\n reason: `Version differs (local: ${localPkg.version}, registry: ${latestVersion})`,\n changes: [\n {\n type: 'version',\n field: 'version',\n oldValue: latestVersion,\n newValue: localPkg.version,\n significance: 'critical',\n },\n ],\n });\n return;\n }\n\n // Fetch registry tarball for comparison\n const tarballUrl = registryPkg.dist?.tarball;\n if (!tarballUrl) {\n callback(null, {\n needsPublish: true,\n reason: 'Registry package has no tarball URL',\n changes: [{ type: 'first-publish', significance: 'critical' }],\n });\n return;\n }\n\n registryTarball = await pacote.tarball(tarballUrl, {\n Arborist,\n integrity: registryPkg.dist?.integrity,\n });\n } catch (err: unknown) {\n const error = err as { code?: string; message?: string };\n // Package not found in registry (first publish)\n if (error.code === 'E404') {\n callback(null, {\n needsPublish: true,\n reason: 'Package not found in registry (first publish)',\n changes: [{ type: 'first-publish', significance: 'critical' }],\n });\n return;\n }\n // Unknown error - assume changed to be safe\n callback(null, {\n needsPublish: true,\n reason: `Error checking registry: ${error.message || 'Unknown error'}`,\n });\n return;\n }\n\n // Step 3: Pack local package\n let localTarball: Buffer;\n try {\n const spec = npa(cwd);\n const manifest = await pacote.manifest(spec, { Arborist });\n localTarball = await pacote.tarball(manifest._resolved, {\n Arborist,\n integrity: manifest._integrity,\n });\n } catch (err: unknown) {\n const error = err as { message?: string };\n callback(null, {\n needsPublish: true,\n reason: `Error packing local package: ${error.message || 'Unknown error'}`,\n });\n return;\n }\n\n // Step 4: Fast hash comparison\n const localHash = hashBuffer(localTarball);\n const registryHash = hashBuffer(registryTarball);\n\n if (localHash === registryHash) {\n callback(null, {\n needsPublish: false,\n reason: `No changes detected (hash: ${localHash.substring(0, 16)}...)`,\n });\n return;\n }\n\n // Step 5: File-by-file comparison\n const fileComparison = await comparePackageFiles(localTarball, registryTarball);\n\n if (fileComparison.identical) {\n // Hash mismatch but files identical - likely tarball metadata difference\n callback(null, {\n needsPublish: false,\n reason: 'Files identical (tarball metadata differs)',\n });\n return;\n }\n\n // Step 6: If only package.json differs, do semantic comparison\n if (fileComparison.packageJsonOnly && !options.packageJsonOnly) {\n // Extract package.json from both tarballs for accurate comparison\n // (packument metadata is missing fields like 'files')\n const localTarballPkg = (await extractPackageJson(localTarball)) as PackageJson;\n const registryTarballPkg = (await extractPackageJson(registryTarball)) as PackageJson;\n\n const pkgJsonComparison = comparePackageJson(localTarballPkg, registryTarballPkg, {\n includeOptionalDeps: options.includeOptionalDeps,\n additionalSignificantFields: options.additionalSignificantFields,\n ignoreFields: options.ignoreFields,\n treatNarrowingAsEquivalent: options.treatNarrowingAsEquivalent,\n });\n\n if (!pkgJsonComparison.hasSignificantChanges) {\n callback(null, {\n needsPublish: false,\n reason: 'Package.json changes are not significant for consumers',\n changes: pkgJsonComparison.fieldChanges.map((fc) => ({\n type: 'field' as const,\n field: fc.field,\n oldValue: fc.oldValue,\n newValue: fc.newValue,\n significance: 'informational' as const,\n })),\n });\n return;\n }\n\n // Build changes list\n const changes: ChangeDetail[] = [\n ...pkgJsonComparison.fieldChanges.map((fc) => ({\n type: 'field' as const,\n field: fc.field,\n oldValue: fc.oldValue,\n newValue: fc.newValue,\n significance: fc.significance,\n })),\n ...pkgJsonComparison.dependencyChanges\n .filter((dc) => dc.semanticChange !== 'equivalent' && dc.semanticChange !== 'none')\n .map((dc) => ({\n type: 'dependency' as const,\n field: `${dc.type}.${dc.name}`,\n oldValue: dc.oldSpec,\n newValue: dc.newSpec,\n significance: 'significant' as const,\n })),\n ];\n\n callback(null, {\n needsPublish: true,\n reason: pkgJsonComparison.summary,\n changes,\n });\n return;\n }\n\n // Step 7: Other files changed\n callback(null, {\n needsPublish: true,\n reason: `Code changes detected (${fileComparison.fileChanges.length} files changed)`,\n changes: fileComparison.fileChanges.map((fc) => ({\n type: 'file' as const,\n field: fc.path,\n significance: 'significant' as const,\n })),\n });\n })().catch(callback);\n}\n\n/**\n * Callback-based needsPublish\n */\nexport function needsPublishCb(options: NeedsPublishOptions, callback: NeedsPublishCallback): void {\n needsPublishImpl(options, callback);\n}\n\n/**\n * Determine if a package needs to be published to npm.\n *\n * @example\n * ```ts\n * import { needsPublish } from 'npm-needs-publish';\n *\n * const result = await needsPublish({ cwd: process.cwd() });\n * if (result.needsPublish) {\n * console.log('Publish needed:', result.reason);\n * }\n * ```\n */\nexport function needsPublish(options: NeedsPublishOptions = {}): Promise<NeedsPublishResult> {\n return new Promise((resolve, reject) => {\n needsPublishCb(options, (error, result) => {\n if (error) reject(error);\n else if (result) resolve(result);\n else reject(new Error('No result returned'));\n });\n });\n}\n"],"names":["needsPublish","needsPublishCb","_require","require","Module","createRequire","needsPublishImpl","options","callback","cwd","process","localPkg","package","JSON","parse","fs","readFileSync","path","join","private","reason","pacote","Arborist","npa","execFile","promisify","execFileAsync","comparePackageFiles","comparePackageJson","extractPackageJson","hashBuffer","registry","scope","stdout","registryPkg","registryTarball","packument","latestVersion","tarballUrl","err","error","localTarball","spec","manifest","localHash","registryHash","fileComparison","localTarballPkg","registryTarballPkg","pkgJsonComparison","changes","stringStartsWith","name","split","undefined","trim","latest","type","significance","versions","version","field","oldValue","newValue","dist","tarball","integrity","code","message","_resolved","_integrity","substring","identical","packageJsonOnly","includeOptionalDeps","additionalSignificantFields","ignoreFields","treatNarrowingAsEquivalent","hasSignificantChanges","fieldChanges","map","fc","dependencyChanges","filter","dc","semanticChange","oldSpec","newSpec","summary","fileChanges","length","catch","Promise","resolve","reject","result","Error"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/npm/npm-needs-publish/src/needs-publish.ts"],"sourcesContent":["/**\n * Main orchestration logic for npm-needs-publish\n *\n * Algorithm:\n * 1. Fetch registry packument → if E404, return needsPublish=true (first publish)\n * 2. Version check → if different, return needsPublish=true (intentional bump)\n * 3. Fast hash check → if identical, return needsPublish=false (no changes)\n * 4. Extract both tarballs, compare file-by-file (excluding package.json)\n * 5. If non-package.json files differ → return needsPublish=true\n * 6. If only package.json differs → do semantic comparison\n */\n\nimport fs from 'fs';\nimport Module from 'module';\nimport path from 'path';\nimport { stringStartsWith } from './compat.ts';\nimport type { ChangeDetail, NeedsPublishOptions, NeedsPublishResult, PackageJson } from './types.ts';\n\nconst _require = typeof require === 'undefined' ? Module.createRequire(import.meta.url) : require;\n\n/**\n * Callback type for needsPublish\n */\nexport type NeedsPublishCallback = (error: Error | null, result?: NeedsPublishResult) => void;\n\nfunction needsPublishImpl(options: NeedsPublishOptions, callback: NeedsPublishCallback) {\n const cwd = options.cwd || process.cwd();\n\n // Load local package.json\n const localPkg: PackageJson = options.package || JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8'));\n\n // Skip private packages\n if (localPkg.private)\n return callback(null, {\n needsPublish: false,\n reason: 'Package is private',\n });\n\n (async () => {\n const pacote = _require('pacote');\n const Arborist = _require('@npmcli/arborist');\n const npa = _require('npm-package-arg');\n const { execFile } = _require('child_process');\n const { promisify } = _require('util');\n const execFileAsync = promisify(execFile);\n\n // Dynamic import for comparators (they use modern features)\n const { comparePackageFiles, comparePackageJson, extractPackageJson, hashBuffer } = await import('./comparators/index.ts');\n\n // Get registry URL for scoped packages\n let registry: string | undefined = options.registry;\n if (!registry) {\n const scope = stringStartsWith(localPkg.name, '@') ? localPkg.name.split('/')[0] : undefined;\n if (scope) {\n try {\n const { stdout } = await execFileAsync('npm', ['config', 'get', `${scope}:registry`]);\n registry = stdout.trim();\n if (registry === 'undefined') registry = undefined;\n } catch {\n // Fallback to default registry\n }\n }\n }\n\n // Step 1: Try to fetch registry packument\n let registryPkg: PackageJson;\n let registryTarball: Buffer;\n\n try {\n const packument = await pacote.packument(localPkg.name, {\n Arborist,\n ...(registry && { registry }),\n });\n\n const latestVersion = packument['dist-tags']?.latest;\n if (!latestVersion) {\n callback(null, {\n needsPublish: true,\n reason: 'No latest version found in registry (first publish)',\n changes: [{ type: 'first-publish', significance: 'critical' }],\n });\n return;\n }\n\n registryPkg = packument.versions[latestVersion] as unknown as PackageJson;\n\n // Step 2: Version comparison (fast path)\n if (localPkg.version !== latestVersion) {\n callback(null, {\n needsPublish: true,\n reason: `Version differs (local: ${localPkg.version}, registry: ${latestVersion})`,\n changes: [\n {\n type: 'version',\n field: 'version',\n oldValue: latestVersion,\n newValue: localPkg.version,\n significance: 'critical',\n },\n ],\n });\n return;\n }\n\n // Fetch registry tarball for comparison\n const tarballUrl = registryPkg.dist?.tarball;\n if (!tarballUrl)\n return callback(null, {\n needsPublish: true,\n reason: 'Registry package has no tarball URL',\n changes: [{ type: 'first-publish', significance: 'critical' }],\n });\n\n registryTarball = await pacote.tarball(tarballUrl, {\n Arborist,\n integrity: registryPkg.dist?.integrity,\n });\n } catch (err: unknown) {\n const error = err as { code?: string; message?: string };\n // Package not found in registry (first publish)\n if (error.code === 'E404') {\n callback(null, {\n needsPublish: true,\n reason: 'Package not found in registry (first publish)',\n changes: [{ type: 'first-publish', significance: 'critical' }],\n });\n return;\n }\n // Unknown error - assume changed to be safe\n callback(null, {\n needsPublish: true,\n reason: `Error checking registry: ${error.message || 'Unknown error'}`,\n });\n return;\n }\n\n // Step 3: Pack local package\n let localTarball: Buffer;\n try {\n const spec = npa(cwd);\n const manifest = await pacote.manifest(spec, { Arborist });\n localTarball = await pacote.tarball(manifest._resolved, {\n Arborist,\n integrity: manifest._integrity,\n });\n } catch (err: unknown) {\n const error = err as { message?: string };\n callback(null, {\n needsPublish: true,\n reason: `Error packing local package: ${error.message || 'Unknown error'}`,\n });\n return;\n }\n\n // Step 4: Fast hash comparison\n const localHash = hashBuffer(localTarball);\n const registryHash = hashBuffer(registryTarball);\n\n if (localHash === registryHash) {\n callback(null, {\n needsPublish: false,\n reason: `No changes detected (hash: ${localHash.substring(0, 16)}...)`,\n });\n return;\n }\n\n // Step 5: File-by-file comparison\n const fileComparison = await comparePackageFiles(localTarball, registryTarball);\n\n if (fileComparison.identical) {\n // Hash mismatch but files identical - likely tarball metadata difference\n callback(null, {\n needsPublish: false,\n reason: 'Files identical (tarball metadata differs)',\n });\n return;\n }\n\n // Step 6: If only package.json differs, do semantic comparison\n if (fileComparison.packageJsonOnly && !options.packageJsonOnly) {\n // Extract package.json from both tarballs for accurate comparison\n // (packument metadata is missing fields like 'files')\n const localTarballPkg = (await extractPackageJson(localTarball)) as PackageJson;\n const registryTarballPkg = (await extractPackageJson(registryTarball)) as PackageJson;\n\n const pkgJsonComparison = comparePackageJson(localTarballPkg, registryTarballPkg, {\n includeOptionalDeps: options.includeOptionalDeps,\n additionalSignificantFields: options.additionalSignificantFields,\n ignoreFields: options.ignoreFields,\n treatNarrowingAsEquivalent: options.treatNarrowingAsEquivalent,\n });\n\n if (!pkgJsonComparison.hasSignificantChanges) {\n callback(null, {\n needsPublish: false,\n reason: 'Package.json changes are not significant for consumers',\n changes: pkgJsonComparison.fieldChanges.map((fc) => ({\n type: 'field' as const,\n field: fc.field,\n oldValue: fc.oldValue,\n newValue: fc.newValue,\n significance: 'informational' as const,\n })),\n });\n return;\n }\n\n // Build changes list\n const changes: ChangeDetail[] = [\n ...pkgJsonComparison.fieldChanges.map((fc) => ({\n type: 'field' as const,\n field: fc.field,\n oldValue: fc.oldValue,\n newValue: fc.newValue,\n significance: fc.significance,\n })),\n ...pkgJsonComparison.dependencyChanges\n .filter((dc) => dc.semanticChange !== 'equivalent' && dc.semanticChange !== 'none')\n .map((dc) => ({\n type: 'dependency' as const,\n field: `${dc.type}.${dc.name}`,\n oldValue: dc.oldSpec,\n newValue: dc.newSpec,\n significance: 'significant' as const,\n })),\n ];\n\n callback(null, {\n needsPublish: true,\n reason: pkgJsonComparison.summary,\n changes,\n });\n return;\n }\n\n // Step 7: Other files changed\n callback(null, {\n needsPublish: true,\n reason: `Code changes detected (${fileComparison.fileChanges.length} files changed)`,\n changes: fileComparison.fileChanges.map((fc) => ({\n type: 'file' as const,\n field: fc.path,\n significance: 'significant' as const,\n })),\n });\n })().catch(callback);\n}\n\n/**\n * Callback-based needsPublish\n */\nexport function needsPublishCb(options: NeedsPublishOptions, callback: NeedsPublishCallback) {\n needsPublishImpl(options, callback);\n}\n\n/**\n * Determine if a package needs to be published to npm.\n *\n * @example\n * ```ts\n * import { needsPublish } from 'npm-needs-publish';\n *\n * const result = await needsPublish({ cwd: process.cwd() });\n * if (result.needsPublish) {\n * console.log('Publish needed:', result.reason);\n * }\n * ```\n */\nexport function needsPublish(options: NeedsPublishOptions = {}): Promise<NeedsPublishResult> {\n return new Promise((resolve, reject) => {\n needsPublishCb(options, (error, result) => {\n if (error) reject(error);\n else if (result) resolve(result);\n else reject(new Error('No result returned'));\n });\n });\n}\n"],"names":["needsPublish","needsPublishCb","_require","require","Module","createRequire","needsPublishImpl","options","callback","cwd","process","localPkg","package","JSON","parse","fs","readFileSync","path","join","private","reason","pacote","Arborist","npa","execFile","promisify","execFileAsync","comparePackageFiles","comparePackageJson","extractPackageJson","hashBuffer","registry","scope","stdout","registryPkg","registryTarball","packument","latestVersion","tarballUrl","err","error","localTarball","spec","manifest","localHash","registryHash","fileComparison","localTarballPkg","registryTarballPkg","pkgJsonComparison","changes","stringStartsWith","name","split","undefined","trim","latest","type","significance","versions","version","field","oldValue","newValue","dist","tarball","integrity","code","message","_resolved","_integrity","substring","identical","packageJsonOnly","includeOptionalDeps","additionalSignificantFields","ignoreFields","treatNarrowingAsEquivalent","hasSignificantChanges","fieldChanges","map","fc","dependencyChanges","filter","dc","semanticChange","oldSpec","newSpec","summary","fileChanges","length","catch","Promise","resolve","reject","result","Error"],"mappings":"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"}
|
package/dist/cjs/types.js
CHANGED
|
@@ -6,4 +6,4 @@
|
|
|
6
6
|
Object.defineProperty(exports, "__esModule", {
|
|
7
7
|
value: true
|
|
8
8
|
});
|
|
9
|
-
/* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (
|
|
9
|
+
/* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (var key in exports) { exports.default[key] = exports[key]; } } catch (_) {}; module.exports = exports.default; }
|
|
@@ -18,13 +18,10 @@ function needsPublishImpl(options, callback) {
|
|
|
18
18
|
// Load local package.json
|
|
19
19
|
const localPkg = options.package || JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8'));
|
|
20
20
|
// Skip private packages
|
|
21
|
-
if (localPkg.private) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
});
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
21
|
+
if (localPkg.private) return callback(null, {
|
|
22
|
+
needsPublish: false,
|
|
23
|
+
reason: 'Package is private'
|
|
24
|
+
});
|
|
28
25
|
(async ()=>{
|
|
29
26
|
const pacote = _require('pacote');
|
|
30
27
|
const Arborist = _require('@npmcli/arborist');
|
|
@@ -97,19 +94,16 @@ function needsPublishImpl(options, callback) {
|
|
|
97
94
|
}
|
|
98
95
|
// Fetch registry tarball for comparison
|
|
99
96
|
const tarballUrl = (_registryPkg_dist = registryPkg.dist) === null || _registryPkg_dist === void 0 ? void 0 : _registryPkg_dist.tarball;
|
|
100
|
-
if (!tarballUrl) {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
});
|
|
111
|
-
return;
|
|
112
|
-
}
|
|
97
|
+
if (!tarballUrl) return callback(null, {
|
|
98
|
+
needsPublish: true,
|
|
99
|
+
reason: 'Registry package has no tarball URL',
|
|
100
|
+
changes: [
|
|
101
|
+
{
|
|
102
|
+
type: 'first-publish',
|
|
103
|
+
significance: 'critical'
|
|
104
|
+
}
|
|
105
|
+
]
|
|
106
|
+
});
|
|
113
107
|
registryTarball = await pacote.tarball(tarballUrl, {
|
|
114
108
|
Arborist,
|
|
115
109
|
integrity: (_registryPkg_dist1 = registryPkg.dist) === null || _registryPkg_dist1 === void 0 ? void 0 : _registryPkg_dist1.integrity
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/npm/npm-needs-publish/src/needs-publish.ts"],"sourcesContent":["/**\n * Main orchestration logic for npm-needs-publish\n *\n * Algorithm:\n * 1. Fetch registry packument → if E404, return needsPublish=true (first publish)\n * 2. Version check → if different, return needsPublish=true (intentional bump)\n * 3. Fast hash check → if identical, return needsPublish=false (no changes)\n * 4. Extract both tarballs, compare file-by-file (excluding package.json)\n * 5. If non-package.json files differ → return needsPublish=true\n * 6. If only package.json differs → do semantic comparison\n */\n\nimport fs from 'fs';\nimport Module from 'module';\nimport path from 'path';\nimport { stringStartsWith } from './compat.ts';\nimport type { ChangeDetail, NeedsPublishOptions, NeedsPublishResult, PackageJson } from './types.ts';\n\nconst _require = typeof require === 'undefined' ? Module.createRequire(import.meta.url) : require;\n\n/**\n * Callback type for needsPublish\n */\nexport type NeedsPublishCallback = (error: Error | null, result?: NeedsPublishResult) => void;\n\nfunction needsPublishImpl(options: NeedsPublishOptions, callback: NeedsPublishCallback): undefined {\n const cwd = options.cwd || process.cwd();\n\n // Load local package.json\n const localPkg: PackageJson = options.package || JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8'));\n\n // Skip private packages\n if (localPkg.private) {\n callback(null, {\n needsPublish: false,\n reason: 'Package is private',\n });\n return;\n }\n\n (async () => {\n const pacote = _require('pacote');\n const Arborist = _require('@npmcli/arborist');\n const npa = _require('npm-package-arg');\n const { execFile } = _require('child_process');\n const { promisify } = _require('util');\n const execFileAsync = promisify(execFile);\n\n // Dynamic import for comparators (they use modern features)\n const { comparePackageFiles, comparePackageJson, extractPackageJson, hashBuffer } = await import('./comparators/index.ts');\n\n // Get registry URL for scoped packages\n let registry: string | undefined = options.registry;\n if (!registry) {\n const scope = stringStartsWith(localPkg.name, '@') ? localPkg.name.split('/')[0] : undefined;\n if (scope) {\n try {\n const { stdout } = await execFileAsync('npm', ['config', 'get', `${scope}:registry`]);\n registry = stdout.trim();\n if (registry === 'undefined') registry = undefined;\n } catch {\n // Fallback to default registry\n }\n }\n }\n\n // Step 1: Try to fetch registry packument\n let registryPkg: PackageJson;\n let registryTarball: Buffer;\n\n try {\n const packument = await pacote.packument(localPkg.name, {\n Arborist,\n ...(registry && { registry }),\n });\n\n const latestVersion = packument['dist-tags']?.latest;\n if (!latestVersion) {\n callback(null, {\n needsPublish: true,\n reason: 'No latest version found in registry (first publish)',\n changes: [{ type: 'first-publish', significance: 'critical' }],\n });\n return;\n }\n\n registryPkg = packument.versions[latestVersion] as unknown as PackageJson;\n\n // Step 2: Version comparison (fast path)\n if (localPkg.version !== latestVersion) {\n callback(null, {\n needsPublish: true,\n reason: `Version differs (local: ${localPkg.version}, registry: ${latestVersion})`,\n changes: [\n {\n type: 'version',\n field: 'version',\n oldValue: latestVersion,\n newValue: localPkg.version,\n significance: 'critical',\n },\n ],\n });\n return;\n }\n\n // Fetch registry tarball for comparison\n const tarballUrl = registryPkg.dist?.tarball;\n if (!tarballUrl) {\n callback(null, {\n needsPublish: true,\n reason: 'Registry package has no tarball URL',\n changes: [{ type: 'first-publish', significance: 'critical' }],\n });\n return;\n }\n\n registryTarball = await pacote.tarball(tarballUrl, {\n Arborist,\n integrity: registryPkg.dist?.integrity,\n });\n } catch (err: unknown) {\n const error = err as { code?: string; message?: string };\n // Package not found in registry (first publish)\n if (error.code === 'E404') {\n callback(null, {\n needsPublish: true,\n reason: 'Package not found in registry (first publish)',\n changes: [{ type: 'first-publish', significance: 'critical' }],\n });\n return;\n }\n // Unknown error - assume changed to be safe\n callback(null, {\n needsPublish: true,\n reason: `Error checking registry: ${error.message || 'Unknown error'}`,\n });\n return;\n }\n\n // Step 3: Pack local package\n let localTarball: Buffer;\n try {\n const spec = npa(cwd);\n const manifest = await pacote.manifest(spec, { Arborist });\n localTarball = await pacote.tarball(manifest._resolved, {\n Arborist,\n integrity: manifest._integrity,\n });\n } catch (err: unknown) {\n const error = err as { message?: string };\n callback(null, {\n needsPublish: true,\n reason: `Error packing local package: ${error.message || 'Unknown error'}`,\n });\n return;\n }\n\n // Step 4: Fast hash comparison\n const localHash = hashBuffer(localTarball);\n const registryHash = hashBuffer(registryTarball);\n\n if (localHash === registryHash) {\n callback(null, {\n needsPublish: false,\n reason: `No changes detected (hash: ${localHash.substring(0, 16)}...)`,\n });\n return;\n }\n\n // Step 5: File-by-file comparison\n const fileComparison = await comparePackageFiles(localTarball, registryTarball);\n\n if (fileComparison.identical) {\n // Hash mismatch but files identical - likely tarball metadata difference\n callback(null, {\n needsPublish: false,\n reason: 'Files identical (tarball metadata differs)',\n });\n return;\n }\n\n // Step 6: If only package.json differs, do semantic comparison\n if (fileComparison.packageJsonOnly && !options.packageJsonOnly) {\n // Extract package.json from both tarballs for accurate comparison\n // (packument metadata is missing fields like 'files')\n const localTarballPkg = (await extractPackageJson(localTarball)) as PackageJson;\n const registryTarballPkg = (await extractPackageJson(registryTarball)) as PackageJson;\n\n const pkgJsonComparison = comparePackageJson(localTarballPkg, registryTarballPkg, {\n includeOptionalDeps: options.includeOptionalDeps,\n additionalSignificantFields: options.additionalSignificantFields,\n ignoreFields: options.ignoreFields,\n treatNarrowingAsEquivalent: options.treatNarrowingAsEquivalent,\n });\n\n if (!pkgJsonComparison.hasSignificantChanges) {\n callback(null, {\n needsPublish: false,\n reason: 'Package.json changes are not significant for consumers',\n changes: pkgJsonComparison.fieldChanges.map((fc) => ({\n type: 'field' as const,\n field: fc.field,\n oldValue: fc.oldValue,\n newValue: fc.newValue,\n significance: 'informational' as const,\n })),\n });\n return;\n }\n\n // Build changes list\n const changes: ChangeDetail[] = [\n ...pkgJsonComparison.fieldChanges.map((fc) => ({\n type: 'field' as const,\n field: fc.field,\n oldValue: fc.oldValue,\n newValue: fc.newValue,\n significance: fc.significance,\n })),\n ...pkgJsonComparison.dependencyChanges\n .filter((dc) => dc.semanticChange !== 'equivalent' && dc.semanticChange !== 'none')\n .map((dc) => ({\n type: 'dependency' as const,\n field: `${dc.type}.${dc.name}`,\n oldValue: dc.oldSpec,\n newValue: dc.newSpec,\n significance: 'significant' as const,\n })),\n ];\n\n callback(null, {\n needsPublish: true,\n reason: pkgJsonComparison.summary,\n changes,\n });\n return;\n }\n\n // Step 7: Other files changed\n callback(null, {\n needsPublish: true,\n reason: `Code changes detected (${fileComparison.fileChanges.length} files changed)`,\n changes: fileComparison.fileChanges.map((fc) => ({\n type: 'file' as const,\n field: fc.path,\n significance: 'significant' as const,\n })),\n });\n })().catch(callback);\n}\n\n/**\n * Callback-based needsPublish\n */\nexport function needsPublishCb(options: NeedsPublishOptions, callback: NeedsPublishCallback): void {\n needsPublishImpl(options, callback);\n}\n\n/**\n * Determine if a package needs to be published to npm.\n *\n * @example\n * ```ts\n * import { needsPublish } from 'npm-needs-publish';\n *\n * const result = await needsPublish({ cwd: process.cwd() });\n * if (result.needsPublish) {\n * console.log('Publish needed:', result.reason);\n * }\n * ```\n */\nexport function needsPublish(options: NeedsPublishOptions = {}): Promise<NeedsPublishResult> {\n return new Promise((resolve, reject) => {\n needsPublishCb(options, (error, result) => {\n if (error) reject(error);\n else if (result) resolve(result);\n else reject(new Error('No result returned'));\n });\n });\n}\n"],"names":["fs","Module","path","stringStartsWith","_require","require","createRequire","url","needsPublishImpl","options","callback","cwd","process","localPkg","package","JSON","parse","readFileSync","join","private","needsPublish","reason","pacote","Arborist","npa","execFile","promisify","execFileAsync","comparePackageFiles","comparePackageJson","extractPackageJson","hashBuffer","registry","scope","name","split","undefined","stdout","trim","registryPkg","registryTarball","packument","latestVersion","latest","changes","type","significance","versions","version","field","oldValue","newValue","tarballUrl","dist","tarball","integrity","err","error","code","message","localTarball","spec","manifest","_resolved","_integrity","localHash","registryHash","substring","fileComparison","identical","packageJsonOnly","localTarballPkg","registryTarballPkg","pkgJsonComparison","includeOptionalDeps","additionalSignificantFields","ignoreFields","treatNarrowingAsEquivalent","hasSignificantChanges","fieldChanges","map","fc","dependencyChanges","filter","dc","semanticChange","oldSpec","newSpec","summary","fileChanges","length","catch","needsPublishCb","Promise","resolve","reject","result","Error"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/npm/npm-needs-publish/src/needs-publish.ts"],"sourcesContent":["/**\n * Main orchestration logic for npm-needs-publish\n *\n * Algorithm:\n * 1. Fetch registry packument → if E404, return needsPublish=true (first publish)\n * 2. Version check → if different, return needsPublish=true (intentional bump)\n * 3. Fast hash check → if identical, return needsPublish=false (no changes)\n * 4. Extract both tarballs, compare file-by-file (excluding package.json)\n * 5. If non-package.json files differ → return needsPublish=true\n * 6. If only package.json differs → do semantic comparison\n */\n\nimport fs from 'fs';\nimport Module from 'module';\nimport path from 'path';\nimport { stringStartsWith } from './compat.ts';\nimport type { ChangeDetail, NeedsPublishOptions, NeedsPublishResult, PackageJson } from './types.ts';\n\nconst _require = typeof require === 'undefined' ? Module.createRequire(import.meta.url) : require;\n\n/**\n * Callback type for needsPublish\n */\nexport type NeedsPublishCallback = (error: Error | null, result?: NeedsPublishResult) => void;\n\nfunction needsPublishImpl(options: NeedsPublishOptions, callback: NeedsPublishCallback) {\n const cwd = options.cwd || process.cwd();\n\n // Load local package.json\n const localPkg: PackageJson = options.package || JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8'));\n\n // Skip private packages\n if (localPkg.private)\n return callback(null, {\n needsPublish: false,\n reason: 'Package is private',\n });\n\n (async () => {\n const pacote = _require('pacote');\n const Arborist = _require('@npmcli/arborist');\n const npa = _require('npm-package-arg');\n const { execFile } = _require('child_process');\n const { promisify } = _require('util');\n const execFileAsync = promisify(execFile);\n\n // Dynamic import for comparators (they use modern features)\n const { comparePackageFiles, comparePackageJson, extractPackageJson, hashBuffer } = await import('./comparators/index.ts');\n\n // Get registry URL for scoped packages\n let registry: string | undefined = options.registry;\n if (!registry) {\n const scope = stringStartsWith(localPkg.name, '@') ? localPkg.name.split('/')[0] : undefined;\n if (scope) {\n try {\n const { stdout } = await execFileAsync('npm', ['config', 'get', `${scope}:registry`]);\n registry = stdout.trim();\n if (registry === 'undefined') registry = undefined;\n } catch {\n // Fallback to default registry\n }\n }\n }\n\n // Step 1: Try to fetch registry packument\n let registryPkg: PackageJson;\n let registryTarball: Buffer;\n\n try {\n const packument = await pacote.packument(localPkg.name, {\n Arborist,\n ...(registry && { registry }),\n });\n\n const latestVersion = packument['dist-tags']?.latest;\n if (!latestVersion) {\n callback(null, {\n needsPublish: true,\n reason: 'No latest version found in registry (first publish)',\n changes: [{ type: 'first-publish', significance: 'critical' }],\n });\n return;\n }\n\n registryPkg = packument.versions[latestVersion] as unknown as PackageJson;\n\n // Step 2: Version comparison (fast path)\n if (localPkg.version !== latestVersion) {\n callback(null, {\n needsPublish: true,\n reason: `Version differs (local: ${localPkg.version}, registry: ${latestVersion})`,\n changes: [\n {\n type: 'version',\n field: 'version',\n oldValue: latestVersion,\n newValue: localPkg.version,\n significance: 'critical',\n },\n ],\n });\n return;\n }\n\n // Fetch registry tarball for comparison\n const tarballUrl = registryPkg.dist?.tarball;\n if (!tarballUrl)\n return callback(null, {\n needsPublish: true,\n reason: 'Registry package has no tarball URL',\n changes: [{ type: 'first-publish', significance: 'critical' }],\n });\n\n registryTarball = await pacote.tarball(tarballUrl, {\n Arborist,\n integrity: registryPkg.dist?.integrity,\n });\n } catch (err: unknown) {\n const error = err as { code?: string; message?: string };\n // Package not found in registry (first publish)\n if (error.code === 'E404') {\n callback(null, {\n needsPublish: true,\n reason: 'Package not found in registry (first publish)',\n changes: [{ type: 'first-publish', significance: 'critical' }],\n });\n return;\n }\n // Unknown error - assume changed to be safe\n callback(null, {\n needsPublish: true,\n reason: `Error checking registry: ${error.message || 'Unknown error'}`,\n });\n return;\n }\n\n // Step 3: Pack local package\n let localTarball: Buffer;\n try {\n const spec = npa(cwd);\n const manifest = await pacote.manifest(spec, { Arborist });\n localTarball = await pacote.tarball(manifest._resolved, {\n Arborist,\n integrity: manifest._integrity,\n });\n } catch (err: unknown) {\n const error = err as { message?: string };\n callback(null, {\n needsPublish: true,\n reason: `Error packing local package: ${error.message || 'Unknown error'}`,\n });\n return;\n }\n\n // Step 4: Fast hash comparison\n const localHash = hashBuffer(localTarball);\n const registryHash = hashBuffer(registryTarball);\n\n if (localHash === registryHash) {\n callback(null, {\n needsPublish: false,\n reason: `No changes detected (hash: ${localHash.substring(0, 16)}...)`,\n });\n return;\n }\n\n // Step 5: File-by-file comparison\n const fileComparison = await comparePackageFiles(localTarball, registryTarball);\n\n if (fileComparison.identical) {\n // Hash mismatch but files identical - likely tarball metadata difference\n callback(null, {\n needsPublish: false,\n reason: 'Files identical (tarball metadata differs)',\n });\n return;\n }\n\n // Step 6: If only package.json differs, do semantic comparison\n if (fileComparison.packageJsonOnly && !options.packageJsonOnly) {\n // Extract package.json from both tarballs for accurate comparison\n // (packument metadata is missing fields like 'files')\n const localTarballPkg = (await extractPackageJson(localTarball)) as PackageJson;\n const registryTarballPkg = (await extractPackageJson(registryTarball)) as PackageJson;\n\n const pkgJsonComparison = comparePackageJson(localTarballPkg, registryTarballPkg, {\n includeOptionalDeps: options.includeOptionalDeps,\n additionalSignificantFields: options.additionalSignificantFields,\n ignoreFields: options.ignoreFields,\n treatNarrowingAsEquivalent: options.treatNarrowingAsEquivalent,\n });\n\n if (!pkgJsonComparison.hasSignificantChanges) {\n callback(null, {\n needsPublish: false,\n reason: 'Package.json changes are not significant for consumers',\n changes: pkgJsonComparison.fieldChanges.map((fc) => ({\n type: 'field' as const,\n field: fc.field,\n oldValue: fc.oldValue,\n newValue: fc.newValue,\n significance: 'informational' as const,\n })),\n });\n return;\n }\n\n // Build changes list\n const changes: ChangeDetail[] = [\n ...pkgJsonComparison.fieldChanges.map((fc) => ({\n type: 'field' as const,\n field: fc.field,\n oldValue: fc.oldValue,\n newValue: fc.newValue,\n significance: fc.significance,\n })),\n ...pkgJsonComparison.dependencyChanges\n .filter((dc) => dc.semanticChange !== 'equivalent' && dc.semanticChange !== 'none')\n .map((dc) => ({\n type: 'dependency' as const,\n field: `${dc.type}.${dc.name}`,\n oldValue: dc.oldSpec,\n newValue: dc.newSpec,\n significance: 'significant' as const,\n })),\n ];\n\n callback(null, {\n needsPublish: true,\n reason: pkgJsonComparison.summary,\n changes,\n });\n return;\n }\n\n // Step 7: Other files changed\n callback(null, {\n needsPublish: true,\n reason: `Code changes detected (${fileComparison.fileChanges.length} files changed)`,\n changes: fileComparison.fileChanges.map((fc) => ({\n type: 'file' as const,\n field: fc.path,\n significance: 'significant' as const,\n })),\n });\n })().catch(callback);\n}\n\n/**\n * Callback-based needsPublish\n */\nexport function needsPublishCb(options: NeedsPublishOptions, callback: NeedsPublishCallback) {\n needsPublishImpl(options, callback);\n}\n\n/**\n * Determine if a package needs to be published to npm.\n *\n * @example\n * ```ts\n * import { needsPublish } from 'npm-needs-publish';\n *\n * const result = await needsPublish({ cwd: process.cwd() });\n * if (result.needsPublish) {\n * console.log('Publish needed:', result.reason);\n * }\n * ```\n */\nexport function needsPublish(options: NeedsPublishOptions = {}): Promise<NeedsPublishResult> {\n return new Promise((resolve, reject) => {\n needsPublishCb(options, (error, result) => {\n if (error) reject(error);\n else if (result) resolve(result);\n else reject(new Error('No result returned'));\n });\n });\n}\n"],"names":["fs","Module","path","stringStartsWith","_require","require","createRequire","url","needsPublishImpl","options","callback","cwd","process","localPkg","package","JSON","parse","readFileSync","join","private","needsPublish","reason","pacote","Arborist","npa","execFile","promisify","execFileAsync","comparePackageFiles","comparePackageJson","extractPackageJson","hashBuffer","registry","scope","name","split","undefined","stdout","trim","registryPkg","registryTarball","packument","latestVersion","latest","changes","type","significance","versions","version","field","oldValue","newValue","tarballUrl","dist","tarball","integrity","err","error","code","message","localTarball","spec","manifest","_resolved","_integrity","localHash","registryHash","substring","fileComparison","identical","packageJsonOnly","localTarballPkg","registryTarballPkg","pkgJsonComparison","includeOptionalDeps","additionalSignificantFields","ignoreFields","treatNarrowingAsEquivalent","hasSignificantChanges","fieldChanges","map","fc","dependencyChanges","filter","dc","semanticChange","oldSpec","newSpec","summary","fileChanges","length","catch","needsPublishCb","Promise","resolve","reject","result","Error"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "npm-needs-publish",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Smart publish detection for npm packages - semantic package.json comparison with semver-aware dependency analysis",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"npm",
|
|
@@ -56,32 +56,32 @@
|
|
|
56
56
|
},
|
|
57
57
|
"dependencies": {
|
|
58
58
|
"@npmcli/arborist": "^7.5.4",
|
|
59
|
-
"fs-copy-compat": "^0.
|
|
60
|
-
"fs-remove-compat": "^0.
|
|
61
|
-
"mkdirp-classic": "^0.5.
|
|
59
|
+
"fs-copy-compat": "^1.0.0",
|
|
60
|
+
"fs-remove-compat": "^1.0.0",
|
|
61
|
+
"mkdirp-classic": "^0.5.2",
|
|
62
62
|
"npm-package-arg": "^13.0.2",
|
|
63
63
|
"pacote": "^17.0.7",
|
|
64
64
|
"semver": "^7.7.3",
|
|
65
65
|
"tar": "^7.5.2",
|
|
66
|
-
"temp-suffix": "^1.0.
|
|
66
|
+
"temp-suffix": "^1.0.10"
|
|
67
67
|
},
|
|
68
68
|
"devDependencies": {
|
|
69
|
-
"@types/mocha": "
|
|
70
|
-
"@types/node": "
|
|
69
|
+
"@types/mocha": "*",
|
|
70
|
+
"@types/node": "*",
|
|
71
71
|
"@types/pacote": "^11.1.8",
|
|
72
72
|
"@types/semver": "^7.7.1",
|
|
73
73
|
"@types/tar": "^6.1.13",
|
|
74
|
-
"module-link-unlink": "^1.0.
|
|
75
|
-
"node-version-use": "
|
|
76
|
-
"os-shim": "^0.1.
|
|
77
|
-
"queue-cb": "^1.
|
|
78
|
-
"resolve": "^1.
|
|
74
|
+
"module-link-unlink": "^1.0.11",
|
|
75
|
+
"node-version-use": "*",
|
|
76
|
+
"os-shim": "^0.1.0",
|
|
77
|
+
"queue-cb": "^1.0.0",
|
|
78
|
+
"resolve": "^1.3.1",
|
|
79
79
|
"short-hash": "^1.0.0",
|
|
80
|
-
"ts-dev-stack": "
|
|
81
|
-
"tsds-config": "
|
|
82
|
-
"tsds-lib-test": "^1.19.
|
|
80
|
+
"ts-dev-stack": "*",
|
|
81
|
+
"tsds-config": "*",
|
|
82
|
+
"tsds-lib-test": "^1.19.7"
|
|
83
83
|
},
|
|
84
84
|
"engines": {
|
|
85
|
-
"node": ">=16
|
|
85
|
+
"node": ">=16"
|
|
86
86
|
}
|
|
87
87
|
}
|