npm-needs-publish 1.0.3 → 1.0.5
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 +1 -0
- package/dist/cjs/cli.js.map +1 -1
- package/dist/cjs/comparators/file-content.js +45 -14
- package/dist/cjs/comparators/file-content.js.map +1 -1
- package/dist/cjs/comparators/version-specifier.js +5 -7
- package/dist/cjs/comparators/version-specifier.js.map +1 -1
- package/dist/cjs/needs-publish.js +1 -1
- package/dist/cjs/needs-publish.js.map +1 -1
- package/dist/esm/comparators/file-content.js +1 -11
- package/dist/esm/comparators/file-content.js.map +1 -1
- package/dist/esm/comparators/version-specifier.js +5 -7
- package/dist/esm/comparators/version-specifier.js.map +1 -1
- package/dist/esm/needs-publish.js +1 -1
- package/dist/esm/needs-publish.js.map +1 -1
- package/package.json +4 -2
package/dist/cjs/cli.js
CHANGED
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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"}
|
|
@@ -33,8 +33,8 @@ _export(exports, {
|
|
|
33
33
|
}
|
|
34
34
|
});
|
|
35
35
|
var _crypto = /*#__PURE__*/ _interop_require_default(require("crypto"));
|
|
36
|
-
var _module = /*#__PURE__*/ _interop_require_default(require("module"));
|
|
37
36
|
var _stream = require("stream");
|
|
37
|
+
var _tar = /*#__PURE__*/ _interop_require_wildcard(require("tar"));
|
|
38
38
|
var _util = require("util");
|
|
39
39
|
var _zlib = /*#__PURE__*/ _interop_require_default(require("zlib"));
|
|
40
40
|
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
|
|
@@ -71,6 +71,47 @@ function _interop_require_default(obj) {
|
|
|
71
71
|
default: obj
|
|
72
72
|
};
|
|
73
73
|
}
|
|
74
|
+
function _getRequireWildcardCache(nodeInterop) {
|
|
75
|
+
if (typeof WeakMap !== "function") return null;
|
|
76
|
+
var cacheBabelInterop = new WeakMap();
|
|
77
|
+
var cacheNodeInterop = new WeakMap();
|
|
78
|
+
return (_getRequireWildcardCache = function(nodeInterop) {
|
|
79
|
+
return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
|
|
80
|
+
})(nodeInterop);
|
|
81
|
+
}
|
|
82
|
+
function _interop_require_wildcard(obj, nodeInterop) {
|
|
83
|
+
if (!nodeInterop && obj && obj.__esModule) {
|
|
84
|
+
return obj;
|
|
85
|
+
}
|
|
86
|
+
if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
|
|
87
|
+
return {
|
|
88
|
+
default: obj
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
var cache = _getRequireWildcardCache(nodeInterop);
|
|
92
|
+
if (cache && cache.has(obj)) {
|
|
93
|
+
return cache.get(obj);
|
|
94
|
+
}
|
|
95
|
+
var newObj = {
|
|
96
|
+
__proto__: null
|
|
97
|
+
};
|
|
98
|
+
var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
|
|
99
|
+
for(var key in obj){
|
|
100
|
+
if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
101
|
+
var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
|
|
102
|
+
if (desc && (desc.get || desc.set)) {
|
|
103
|
+
Object.defineProperty(newObj, key, desc);
|
|
104
|
+
} else {
|
|
105
|
+
newObj[key] = obj[key];
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
newObj.default = obj;
|
|
110
|
+
if (cache) {
|
|
111
|
+
cache.set(obj, newObj);
|
|
112
|
+
}
|
|
113
|
+
return newObj;
|
|
114
|
+
}
|
|
74
115
|
function _ts_generator(thisArg, body) {
|
|
75
116
|
var f, y, t, _ = {
|
|
76
117
|
label: 0,
|
|
@@ -171,15 +212,6 @@ function _ts_generator(thisArg, body) {
|
|
|
171
212
|
}
|
|
172
213
|
}
|
|
173
214
|
var pipeline = (0, _util.promisify)(_stream.pipeline);
|
|
174
|
-
var _require = typeof require === 'undefined' ? _module.default.createRequire(require("url").pathToFileURL(__filename).toString()) : require;
|
|
175
|
-
// Lazy load tar
|
|
176
|
-
var _tar = null;
|
|
177
|
-
function getTar() {
|
|
178
|
-
if (!_tar) {
|
|
179
|
-
_tar = _require('tar');
|
|
180
|
-
}
|
|
181
|
-
return _tar;
|
|
182
|
-
}
|
|
183
215
|
function comparePackageFiles(localTarball, registryTarball) {
|
|
184
216
|
return _async_to_generator(function() {
|
|
185
217
|
var localHash, registryHash, localFiles, registryFiles, changes, allPaths, localKeys, i, registryKeys, i1, onlyPackageJsonDiffers, pathKeys, i2, filePath, isPackageJson, localContent, registryContent;
|
|
@@ -271,15 +303,14 @@ function comparePackageFiles(localTarball, registryTarball) {
|
|
|
271
303
|
* @returns Map of file paths to content buffers
|
|
272
304
|
*/ function extractTarball(tarball) {
|
|
273
305
|
return _async_to_generator(function() {
|
|
274
|
-
var files,
|
|
306
|
+
var files, parser;
|
|
275
307
|
return _ts_generator(this, function(_state) {
|
|
276
308
|
switch(_state.label){
|
|
277
309
|
case 0:
|
|
278
310
|
files = {};
|
|
279
|
-
tar = getTar();
|
|
280
311
|
// Create a parser that collects file contents
|
|
281
|
-
parser = new
|
|
282
|
-
onReadEntry: function(entry) {
|
|
312
|
+
parser = new _tar.Parser({
|
|
313
|
+
onReadEntry: function onReadEntry(entry) {
|
|
283
314
|
if (entry.type === 'File') {
|
|
284
315
|
var chunks = [];
|
|
285
316
|
entry.on('data', function(chunk) {
|
|
@@ -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 { pipeline as pipelineCb, Readable } from 'stream';\nimport * as tar from 'tar';\nimport { promisify } from 'util';\nimport zlib from 'zlib';\n\nconst pipeline = promisify(pipelineCb);\n\nimport type { FileChange, FileComparison } from '../types.ts';\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\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","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","parser","tar","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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"}
|
|
@@ -45,17 +45,15 @@ function _interop_require_default(obj) {
|
|
|
45
45
|
var _require = typeof require === 'undefined' ? _module.default.createRequire(require("url").pathToFileURL(__filename).toString()) : require;
|
|
46
46
|
// Lazy load dependencies
|
|
47
47
|
var _semver = null;
|
|
48
|
-
var _npa = null;
|
|
49
48
|
function getSemver() {
|
|
50
|
-
if (!_semver)
|
|
51
|
-
|
|
52
|
-
}
|
|
49
|
+
if (!_semver) _semver = _require('semver');
|
|
50
|
+
if (!_semver) throw new Error('Failed to load semver module');
|
|
53
51
|
return _semver;
|
|
54
52
|
}
|
|
53
|
+
var _npa = null;
|
|
55
54
|
function getNpa() {
|
|
56
|
-
if (!_npa)
|
|
57
|
-
|
|
58
|
-
}
|
|
55
|
+
if (!_npa) _npa = _require('npm-package-arg');
|
|
56
|
+
if (!_npa) throw new Error('Failed to load npm-package-arg module');
|
|
59
57
|
return _npa;
|
|
60
58
|
}
|
|
61
59
|
function parseVersionSpecifier(spec, where) {
|
|
@@ -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":"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"}
|
|
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;\nfunction getSemver(): typeof import('semver') {\n if (!_semver) _semver = _require('semver');\n if (!_semver) throw new Error('Failed to load semver module');\n return _semver;\n}\n\nlet _npa: typeof import('npm-package-arg') | null = null;\nfunction getNpa(): typeof import('npm-package-arg') {\n if (!_npa) _npa = _require('npm-package-arg');\n if (!_npa) throw new Error('Failed to load npm-package-arg module');\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","getSemver","Error","_npa","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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"}
|
|
@@ -600,7 +600,7 @@ function needsPublish() {
|
|
|
600
600
|
var options = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
|
|
601
601
|
return new Promise(function(resolve, reject) {
|
|
602
602
|
return needsPublishCb(options, function(err, result) {
|
|
603
|
-
return err ? reject(err) : resolve(result);
|
|
603
|
+
return err || !result ? reject(err !== null && err !== void 0 ? err : new Error('Unknown error')) : resolve(result);
|
|
604
604
|
});
|
|
605
605
|
});
|
|
606
606
|
}
|
|
@@ -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) {\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) => needsPublishCb(options, (err, result) => (err ? reject(err) : resolve(result))));\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"],"mappings":"AAAA;;;;;;;;;;CAUC;;;;;;;;;;;QAkQeA;eAAAA;;QAjBAC;eAAAA;;;yDA/OD;6DACI;2DACF;wBACgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAGjC,IAAMC,WAAW,OAAOC,YAAY,cAAcC,eAAM,CAACC,aAAa,CAAC,uDAAmBF;AAO1F,SAASG,iBAAiBC,OAA4B,EAAEC,QAA8B;IACpF,IAAMC,MAAMF,QAAQE,GAAG,IAAIC,QAAQD,GAAG;IAEtC,0BAA0B;IAC1B,IAAME,WAAwBJ,QAAQK,OAAO,IAAIC,KAAKC,KAAK,CAACC,WAAE,CAACC,YAAY,CAACC,aAAI,CAACC,IAAI,CAACT,KAAK,iBAAiB;IAE5G,wBAAwB;IACxB,IAAIE,SAASQ,OAAO,EAClB,OAAOX,SAAS,MAAM;QACpBR,cAAc;QACdoB,QAAQ;IACV;IAED,CAAA;;gBACOC,QACAC,UACAC,KACEC,UACAC,WACFC,eAG8E,MAA5EC,qBAAqBC,oBAAoBC,oBAAoBC,YAGjEC,UAEIC,OAGMC,gBAUVC,aACAC,iBAQoBC,qBA+BHF,mBAUNA,oBA9CPE,WAKAC,eA+BAC,YAYCC,KACDC,OAmBJC,cAEIC,MACAC,UAKCJ,MACDC,QASFI,WACAC,cAWAC,gBAeEC,iBACAC,oBAEAC,mBAuBAC;;;;wBAzKF7B,SAASnB,SAAS;wBAClBoB,WAAWpB,SAAS;wBACpBqB,MAAMrB,SAAS;wBACbsB,WAAatB,SAAS,iBAAtBsB;wBACAC,YAAcvB,SAAS,QAAvBuB;wBACFC,gBAAgBD,UAAUD;wBAGoD;;4BAAM;+EAAA,QAAO;;;;wBAAb,OAAA,eAA5EG,sBAA4E,KAA5EA,qBAAqBC,qBAAuD,KAAvDA,oBAAoBC,qBAAmC,KAAnCA,oBAAoBC,aAAe,KAAfA;wBAErE,uCAAuC;wBACnCC,WAA+BxB,QAAQwB,QAAQ;6BAC/C,CAACA,UAAD;;;;wBACIC,QAAQmB,IAAAA,0BAAgB,EAACxC,SAASyC,IAAI,EAAE,OAAOzC,SAASyC,IAAI,CAACC,KAAK,CAAC,IAAI,CAAC,EAAE,GAAGC;6BAC/EtB,OAAAA;;;;;;;;;;;;wBAEmB;;4BAAMN,cAAc;gCAAQ;gCAAU;gCAAQ,GAAQ,OAANM,OAAM;;;;wBAAjEC,SAAW,cAAXA;wBACRF,WAAWE,OAAOsB,IAAI;wBACtB,IAAIxB,aAAa,aAAaA,WAAWuB;;;;;;;;;;;;;;;;;;wBAY3B;;4BAAMjC,OAAOe,SAAS,CAACzB,SAASyC,IAAI,EAAE;gCACtD9B,UAAAA;+BACIS,YAAY;gCAAEA,UAAAA;4BAAS;;;wBAFvBK,YAAY;wBAKZC,iBAAgBD,sBAAAA,SAAS,CAAC,YAAY,cAAtBA,0CAAAA,oBAAwBoB,MAAM;wBACpD,IAAI,CAACnB,eAAe;4BAClB7B,SAAS,MAAM;gCACbR,cAAc;gCACdoB,QAAQ;gCACR8B,OAAO;oCAAG;wCAAEO,MAAM;wCAAiBC,cAAc;oCAAW;;4BAC9D;4BACA;;;wBACF;wBAEAxB,cAAcE,UAAUuB,QAAQ,CAACtB,cAAc;wBAE/C,yCAAyC;wBACzC,IAAI1B,SAASiD,OAAO,KAAKvB,eAAe;4BACtC7B,SAAS,MAAM;gCACbR,cAAc;gCACdoB,QAAQ,AAAC,2BAAyDiB,OAA/B1B,SAASiD,OAAO,EAAC,gBAA4B,OAAdvB,eAAc;gCAChFa,OAAO;oCACL;wCACEO,MAAM;wCACNI,OAAO;wCACPC,UAAUzB;wCACV0B,UAAUpD,SAASiD,OAAO;wCAC1BF,cAAc;oCAChB;;4BAEJ;4BACA;;;wBACF;wBAEA,wCAAwC;wBAClCpB,cAAaJ,oBAAAA,YAAY8B,IAAI,cAAhB9B,wCAAAA,kBAAkB+B,OAAO;wBAC5C,IAAI,CAAC3B,YACH;;4BAAO9B,SAAS,MAAM;gCACpBR,cAAc;gCACdoB,QAAQ;gCACR8B,OAAO;oCAAG;wCAAEO,MAAM;wCAAiBC,cAAc;oCAAW;;4BAC9D;;wBAEgB;;4BAAMrC,OAAO4C,OAAO,CAAC3B,YAAY;gCACjDhB,UAAAA;gCACA4C,SAAS,GAAEhC,qBAAAA,YAAY8B,IAAI,cAAhB9B,yCAAAA,mBAAkBgC,SAAS;4BACxC;;;wBAHA/B,kBAAkB;;;;;;wBAIXI;wBACDC,QAAQD;wBACd,gDAAgD;wBAChD,IAAIC,MAAM2B,IAAI,KAAK,QAAQ;4BACzB3D,SAAS,MAAM;gCACbR,cAAc;gCACdoB,QAAQ;gCACR8B,OAAO;oCAAG;wCAAEO,MAAM;wCAAiBC,cAAc;oCAAW;;4BAC9D;4BACA;;;wBACF;wBACA,4CAA4C;wBAC5ClD,SAAS,MAAM;4BACbR,cAAc;4BACdoB,QAAQ,AAAC,4BAA4D,OAAjCoB,MAAM4B,OAAO,IAAI;wBACvD;wBACA;;;;;;;;;;wBAMM1B,OAAOnB,IAAId;wBACA;;4BAAMY,OAAOsB,QAAQ,CAACD,MAAM;gCAAEpB,UAAAA;4BAAS;;;wBAAlDqB,WAAW;wBACF;;4BAAMtB,OAAO4C,OAAO,CAACtB,SAAS0B,SAAS,EAAE;gCACtD/C,UAAAA;gCACA4C,WAAWvB,SAAS2B,UAAU;4BAChC;;;wBAHA7B,eAAe;;;;;;wBAIRF;wBACDC,SAAQD;wBACd/B,SAAS,MAAM;4BACbR,cAAc;4BACdoB,QAAQ,AAAC,gCAAgE,OAAjCoB,OAAM4B,OAAO,IAAI;wBAC3D;wBACA;;;;wBAGF,+BAA+B;wBACzBxB,YAAYd,WAAWW;wBACvBI,eAAef,WAAWK;wBAEhC,IAAIS,cAAcC,cAAc;4BAC9BrC,SAAS,MAAM;gCACbR,cAAc;gCACdoB,QAAQ,AAAC,8BAAwD,OAA3BwB,UAAU2B,SAAS,CAAC,GAAG,KAAI;4BACnE;4BACA;;;wBACF;wBAGuB;;4BAAM5C,oBAAoBc,cAAcN;;;wBAAzDW,iBAAiB;wBAEvB,IAAIA,eAAe0B,SAAS,EAAE;4BAC5B,yEAAyE;4BACzEhE,SAAS,MAAM;gCACbR,cAAc;gCACdoB,QAAQ;4BACV;4BACA;;;wBACF;6BAGI0B,CAAAA,eAAe2B,eAAe,IAAI,CAAClE,QAAQkE,eAAe,AAAD,GAAzD3B;;;;wBAGuB;;4BAAMjB,mBAAmBY;;;wBAA5CM,kBAAmB;wBACG;;4BAAMlB,mBAAmBM;;;wBAA/Ca,qBAAsB;wBAEtBC,oBAAoBrB,mBAAmBmB,iBAAiBC,oBAAoB;4BAChF0B,qBAAqBnE,QAAQmE,mBAAmB;4BAChDC,6BAA6BpE,QAAQoE,2BAA2B;4BAChEC,cAAcrE,QAAQqE,YAAY;4BAClCC,4BAA4BtE,QAAQsE,0BAA0B;wBAChE;wBAEA,IAAI,CAAC5B,kBAAkB6B,qBAAqB,EAAE;4BAC5CtE,SAAS,MAAM;gCACbR,cAAc;gCACdoB,QAAQ;gCACR8B,SAASD,kBAAkB8B,YAAY,CAACC,GAAG,CAAC,SAACC;2CAAQ;wCACnDxB,MAAM;wCACNI,OAAOoB,GAAGpB,KAAK;wCACfC,UAAUmB,GAAGnB,QAAQ;wCACrBC,UAAUkB,GAAGlB,QAAQ;wCACrBL,cAAc;oCAChB;;4BACF;4BACA;;;wBACF;wBAEA,qBAAqB;wBACfR,UAA0B,AAC9B,qBAAGD,kBAAkB8B,YAAY,CAACC,GAAG,CAAC,SAACC;mCAAQ;gCAC7CxB,MAAM;gCACNI,OAAOoB,GAAGpB,KAAK;gCACfC,UAAUmB,GAAGnB,QAAQ;gCACrBC,UAAUkB,GAAGlB,QAAQ;gCACrBL,cAAcuB,GAAGvB,YAAY;4BAC/B;mCACA,qBAAGT,kBAAkBiC,iBAAiB,CACnCC,MAAM,CAAC,SAACC;mCAAOA,GAAGC,cAAc,KAAK,gBAAgBD,GAAGC,cAAc,KAAK;2BAC3EL,GAAG,CAAC,SAACI;mCAAQ;gCACZ3B,MAAM;gCACNI,OAAO,AAAC,GAAauB,OAAXA,GAAG3B,IAAI,EAAC,KAAW,OAAR2B,GAAGhC,IAAI;gCAC5BU,UAAUsB,GAAGE,OAAO;gCACpBvB,UAAUqB,GAAGG,OAAO;gCACpB7B,cAAc;4BAChB;;wBAGJlD,SAAS,MAAM;4BACbR,cAAc;4BACdoB,QAAQ6B,kBAAkBuC,OAAO;4BACjCtC,SAAAA;wBACF;wBACA;;;;wBAGF,8BAA8B;wBAC9B1C,SAAS,MAAM;4BACbR,cAAc;4BACdoB,QAAQ,AAAC,0BAA2D,OAAlC0B,eAAe2C,WAAW,CAACC,MAAM,EAAC;4BACpExC,SAASJ,eAAe2C,WAAW,CAACT,GAAG,CAAC,SAACC;uCAAQ;oCAC/CxB,MAAM;oCACNI,OAAOoB,GAAGhE,IAAI;oCACdyC,cAAc;gCAChB;;wBACF;;;;;;QACF;KAAA,IAAKiC,KAAK,CAACnF;AACb;AAKO,SAASP,eAAeM,OAA4B,EAAEC,QAA8B;IACzFF,iBAAiBC,SAASC;AAC5B;AAeO,SAASR;QAAaO,UAAAA,iEAA+B,CAAC;IAC3D,OAAO,IAAIqF,QAAQ,SAACC,SAASC;eAAW7F,eAAeM,SAAS,SAACgC,KAAKwD;mBAAYxD,MAAMuD,OAAOvD,OAAOsD,QAAQE;;;AAChH"}
|
|
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) => needsPublishCb(options, (err, result) => (err || !result ? reject(err ?? new Error('Unknown error')) : resolve(result))));\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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"}
|
|
@@ -6,20 +6,11 @@
|
|
|
6
6
|
* 2. If different: Extract and compare file-by-file
|
|
7
7
|
* 3. Special handling for package.json (semantic comparison elsewhere)
|
|
8
8
|
*/ import crypto from 'crypto';
|
|
9
|
-
import Module from 'module';
|
|
10
9
|
import { pipeline as pipelineCb, Readable } from 'stream';
|
|
10
|
+
import * as tar from 'tar';
|
|
11
11
|
import { promisify } from 'util';
|
|
12
12
|
import zlib from 'zlib';
|
|
13
13
|
const pipeline = promisify(pipelineCb);
|
|
14
|
-
const _require = typeof require === 'undefined' ? Module.createRequire(import.meta.url) : require;
|
|
15
|
-
// Lazy load tar
|
|
16
|
-
let _tar = null;
|
|
17
|
-
function getTar() {
|
|
18
|
-
if (!_tar) {
|
|
19
|
-
_tar = _require('tar');
|
|
20
|
-
}
|
|
21
|
-
return _tar;
|
|
22
|
-
}
|
|
23
14
|
/**
|
|
24
15
|
* Compare package files from two tarballs
|
|
25
16
|
*
|
|
@@ -94,7 +85,6 @@ function getTar() {
|
|
|
94
85
|
* @returns Map of file paths to content buffers
|
|
95
86
|
*/ async function extractTarball(tarball) {
|
|
96
87
|
const files = {};
|
|
97
|
-
const tar = getTar();
|
|
98
88
|
// Create a parser that collects file contents
|
|
99
89
|
const parser = new tar.Parser({
|
|
100
90
|
onReadEntry: (entry)=>{
|
|
@@ -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":["crypto","Module","pipeline","pipelineCb","Readable","promisify","zlib","_require","require","createRequire","url","_tar","getTar","comparePackageFiles","localTarball","registryTarball","localHash","hashBuffer","registryHash","identical","fileChanges","packageJsonOnly","localFiles","extractTarball","registryFiles","changes","allPaths","localKeys","Object","keys","i","length","registryKeys","onlyPackageJsonDiffers","pathKeys","filePath","isPackageJson","indexOf","localContent","registryContent","push","path","action","buffersEqual","tarball","files","tar","parser","Parser","onReadEntry","entry","type","chunks","on","chunk","Buffer","concat","resume","from","createGunzip","buffer","createHash","update","digest","a","b","timingSafeEqual","getFileChangeSummary","added","filter","c","removed","modified","parts","join","isOnlyPackageJsonChange","extractPackageJson","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 { pipeline as pipelineCb, Readable } from 'stream';\nimport * as tar from 'tar';\nimport { promisify } from 'util';\nimport zlib from 'zlib';\n\nconst pipeline = promisify(pipelineCb);\n\nimport type { FileChange, FileComparison } from '../types.ts';\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\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":["crypto","pipeline","pipelineCb","Readable","tar","promisify","zlib","comparePackageFiles","localTarball","registryTarball","localHash","hashBuffer","registryHash","identical","fileChanges","packageJsonOnly","localFiles","extractTarball","registryFiles","changes","allPaths","localKeys","Object","keys","i","length","registryKeys","onlyPackageJsonDiffers","pathKeys","filePath","isPackageJson","indexOf","localContent","registryContent","push","path","action","buffersEqual","tarball","files","parser","Parser","onReadEntry","entry","type","chunks","on","chunk","Buffer","concat","resume","from","createGunzip","buffer","createHash","update","digest","a","b","timingSafeEqual","getFileChangeSummary","added","filter","c","removed","modified","parts","join","isOnlyPackageJsonChange","extractPackageJson","pkgJsonPath","find","p","Error","JSON","parse","toString"],"mappings":"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"}
|
|
@@ -19,17 +19,15 @@
|
|
|
19
19
|
const _require = typeof require === 'undefined' ? Module.createRequire(import.meta.url) : require;
|
|
20
20
|
// Lazy load dependencies
|
|
21
21
|
let _semver = null;
|
|
22
|
-
let _npa = null;
|
|
23
22
|
function getSemver() {
|
|
24
|
-
if (!_semver)
|
|
25
|
-
|
|
26
|
-
}
|
|
23
|
+
if (!_semver) _semver = _require('semver');
|
|
24
|
+
if (!_semver) throw new Error('Failed to load semver module');
|
|
27
25
|
return _semver;
|
|
28
26
|
}
|
|
27
|
+
let _npa = null;
|
|
29
28
|
function getNpa() {
|
|
30
|
-
if (!_npa)
|
|
31
|
-
|
|
32
|
-
}
|
|
29
|
+
if (!_npa) _npa = _require('npm-package-arg');
|
|
30
|
+
if (!_npa) throw new Error('Failed to load npm-package-arg module');
|
|
33
31
|
return _npa;
|
|
34
32
|
}
|
|
35
33
|
/**
|
|
@@ -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":["Module","_require","require","createRequire","url","_semver","_npa","getSemver","getNpa","parseVersionSpecifier","spec","where","semver","npa","startsWith","workspaceRange","slice","type","raw","normalized","parsed","resolve","fetchSpec","rangeType","detectRangeSubtype","range","Range","loose","saveSpec","gitInfo","committish","gitCommittish","undefined","semverRange","gitRange","subSpec","aliasTarget","rawSpec","trimmed","trim","includes","test","valid","extractMajorMinor","minVersion","major","minor","compareVersionSpecifiers","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","comparisonToSemanticChange","comparison","treatNarrowingAsEquivalent"],"mappings":"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"}
|
|
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;\nfunction getSemver(): typeof import('semver') {\n if (!_semver) _semver = _require('semver');\n if (!_semver) throw new Error('Failed to load semver module');\n return _semver;\n}\n\nlet _npa: typeof import('npm-package-arg') | null = null;\nfunction getNpa(): typeof import('npm-package-arg') {\n if (!_npa) _npa = _require('npm-package-arg');\n if (!_npa) throw new Error('Failed to load npm-package-arg module');\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":["Module","_require","require","createRequire","url","_semver","getSemver","Error","_npa","getNpa","parseVersionSpecifier","spec","where","semver","npa","startsWith","workspaceRange","slice","type","raw","normalized","parsed","resolve","fetchSpec","rangeType","detectRangeSubtype","range","Range","loose","saveSpec","gitInfo","committish","gitCommittish","undefined","semverRange","gitRange","subSpec","aliasTarget","rawSpec","trimmed","trim","includes","test","valid","extractMajorMinor","minVersion","major","minor","compareVersionSpecifiers","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","comparisonToSemanticChange","comparison","treatNarrowingAsEquivalent"],"mappings":"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"}
|
|
@@ -250,5 +250,5 @@ function needsPublishImpl(options, callback) {
|
|
|
250
250
|
* }
|
|
251
251
|
* ```
|
|
252
252
|
*/ export function needsPublish(options = {}) {
|
|
253
|
-
return new Promise((resolve, reject)=>needsPublishCb(options, (err, result)=>err ? reject(err) : resolve(result)));
|
|
253
|
+
return new Promise((resolve, reject)=>needsPublishCb(options, (err, result)=>err || !result ? reject(err !== null && err !== void 0 ? err : new Error('Unknown error')) : resolve(result)));
|
|
254
254
|
}
|
|
@@ -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) {\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) => needsPublishCb(options, (err, result) => (err ? reject(err) : resolve(result))));\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"],"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) => needsPublishCb(options, (err, result) => (err || !result ? reject(err ?? new Error('Unknown error')) : resolve(result))));\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": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "Smart publish detection for npm packages - semantic package.json comparison with semver-aware dependency analysis",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"npm",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"fs-copy-compat": "^1.0.0",
|
|
60
60
|
"fs-remove-compat": "^1.0.0",
|
|
61
61
|
"mkdirp-classic": "^0.5.2",
|
|
62
|
-
"npm-package-arg": "^
|
|
62
|
+
"npm-package-arg": "^12.0.0",
|
|
63
63
|
"pacote": "^17.0.7",
|
|
64
64
|
"semver": "^7.7.3",
|
|
65
65
|
"tar": "^7.5.2",
|
|
@@ -69,7 +69,9 @@
|
|
|
69
69
|
"@types/mocha": "*",
|
|
70
70
|
"@types/node": "*",
|
|
71
71
|
"@types/pacote": "^11.1.8",
|
|
72
|
+
"@types/resolve": "^1.20.6",
|
|
72
73
|
"@types/semver": "^7.7.1",
|
|
74
|
+
"@types/short-hash": "^1.0.3",
|
|
73
75
|
"@types/tar": "^7.0.87",
|
|
74
76
|
"cr": "^0.1.0",
|
|
75
77
|
"is-version": "^1.0.9",
|