npm-needs-publish 0.1.0 → 0.1.1
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/comparators/file-content.d.cts +7 -0
- package/dist/cjs/comparators/file-content.d.ts +7 -0
- package/dist/cjs/comparators/file-content.js +30 -0
- package/dist/cjs/comparators/file-content.js.map +1 -1
- package/dist/cjs/comparators/index.d.cts +1 -1
- package/dist/cjs/comparators/index.d.ts +1 -1
- package/dist/cjs/comparators/index.js +3 -0
- package/dist/cjs/comparators/index.js.map +1 -1
- package/dist/cjs/index.d.cts +1 -0
- package/dist/cjs/index.d.ts +1 -0
- package/dist/cjs/index.js +4 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/needs-publish.js +65 -51
- package/dist/cjs/needs-publish.js.map +1 -1
- package/dist/esm/comparators/file-content.d.ts +7 -0
- package/dist/esm/comparators/file-content.js +14 -0
- package/dist/esm/comparators/file-content.js.map +1 -1
- package/dist/esm/comparators/index.d.ts +1 -1
- package/dist/esm/comparators/index.js +1 -1
- package/dist/esm/comparators/index.js.map +1 -1
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/needs-publish.js +6 -2
- package/dist/esm/needs-publish.js.map +1 -1
- package/package.json +1 -1
|
@@ -27,3 +27,10 @@ export declare function getFileChangeSummary(changes: FileChange[]): string;
|
|
|
27
27
|
* Check if changes are only in package.json
|
|
28
28
|
*/
|
|
29
29
|
export declare function isOnlyPackageJsonChange(changes: FileChange[]): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Extract package.json from a tarball
|
|
32
|
+
*
|
|
33
|
+
* @param tarball - Tarball as Buffer (gzipped)
|
|
34
|
+
* @returns Parsed package.json object
|
|
35
|
+
*/
|
|
36
|
+
export declare function extractPackageJson(tarball: Buffer): Promise<unknown>;
|
|
@@ -27,3 +27,10 @@ export declare function getFileChangeSummary(changes: FileChange[]): string;
|
|
|
27
27
|
* Check if changes are only in package.json
|
|
28
28
|
*/
|
|
29
29
|
export declare function isOnlyPackageJsonChange(changes: FileChange[]): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Extract package.json from a tarball
|
|
32
|
+
*
|
|
33
|
+
* @param tarball - Tarball as Buffer (gzipped)
|
|
34
|
+
* @returns Parsed package.json object
|
|
35
|
+
*/
|
|
36
|
+
export declare function extractPackageJson(tarball: Buffer): Promise<unknown>;
|
|
@@ -19,6 +19,9 @@ _export(exports, {
|
|
|
19
19
|
get comparePackageFiles () {
|
|
20
20
|
return comparePackageFiles;
|
|
21
21
|
},
|
|
22
|
+
get extractPackageJson () {
|
|
23
|
+
return extractPackageJson;
|
|
24
|
+
},
|
|
22
25
|
get getFileChangeSummary () {
|
|
23
26
|
return getFileChangeSummary;
|
|
24
27
|
},
|
|
@@ -341,4 +344,31 @@ function isOnlyPackageJsonChange(changes) {
|
|
|
341
344
|
}
|
|
342
345
|
return true;
|
|
343
346
|
}
|
|
347
|
+
function extractPackageJson(tarball) {
|
|
348
|
+
return _async_to_generator(function() {
|
|
349
|
+
var files, pkgJsonPath;
|
|
350
|
+
return _ts_generator(this, function(_state) {
|
|
351
|
+
switch(_state.label){
|
|
352
|
+
case 0:
|
|
353
|
+
return [
|
|
354
|
+
4,
|
|
355
|
+
extractTarball(tarball)
|
|
356
|
+
];
|
|
357
|
+
case 1:
|
|
358
|
+
files = _state.sent();
|
|
359
|
+
// Look for package.json in the tarball
|
|
360
|
+
pkgJsonPath = Object.keys(files).find(function(p) {
|
|
361
|
+
return p === 'package/package.json' || p.indexOf('/package.json') === p.length - 13;
|
|
362
|
+
});
|
|
363
|
+
if (!pkgJsonPath || !files[pkgJsonPath]) {
|
|
364
|
+
throw new Error('package.json not found in tarball');
|
|
365
|
+
}
|
|
366
|
+
return [
|
|
367
|
+
2,
|
|
368
|
+
JSON.parse(files[pkgJsonPath].toString('utf8'))
|
|
369
|
+
];
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
})();
|
|
373
|
+
}
|
|
344
374
|
/* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (var key in exports) { exports.default[key] = exports[key]; } } catch (_) {}; module.exports = exports.default; }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/npm/npm-needs-publish/src/comparators/file-content.ts"],"sourcesContent":["/**\n * File content comparison for package tarballs\n *\n * Compares files between local and registry tarballs:\n * 1. Fast path: Compare tarball hashes\n * 2. If different: Extract and compare file-by-file\n * 3. Special handling for package.json (semantic comparison elsewhere)\n */\n\nimport crypto from 'crypto';\nimport Module from 'module';\nimport { Readable } from 'stream';\nimport { pipeline } from 'stream/promises';\nimport zlib from 'zlib';\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"],"names":["comparePackageFiles","getFileChangeSummary","hashBuffer","isOnlyPackageJsonChange","_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","pipeline","Readable","from","zlib","createGunzip","buffer","crypto","createHash","update","digest","a","b","timingSafeEqual","added","filter","c","removed","modified","parts","join"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/npm/npm-needs-publish/src/comparators/file-content.ts"],"sourcesContent":["/**\n * File content comparison for package tarballs\n *\n * Compares files between local and registry tarballs:\n * 1. Fast path: Compare tarball hashes\n * 2. If different: Extract and compare file-by-file\n * 3. Special handling for package.json (semantic comparison elsewhere)\n */\n\nimport crypto from 'crypto';\nimport Module from 'module';\nimport { Readable } from 'stream';\nimport { pipeline } from 'stream/promises';\nimport zlib from 'zlib';\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","_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","pipeline","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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"}
|
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
* Comparators - Core comparison logic for npm-needs-publish
|
|
3
3
|
*/
|
|
4
4
|
export { compareDependencies, getDependencyChangeSummary, hasSignificantDependencyChanges, } from './dependency.js';
|
|
5
|
-
export { comparePackageFiles, getFileChangeSummary, hashBuffer, isOnlyPackageJsonChange, } from './file-content.js';
|
|
5
|
+
export { comparePackageFiles, extractPackageJson, getFileChangeSummary, hashBuffer, isOnlyPackageJsonChange, } from './file-content.js';
|
|
6
6
|
export { comparePackageJson, isSignificantField, } from './package-json.js';
|
|
7
7
|
export { compareVersionSpecifiers, comparisonToSemanticChange, parseVersionSpecifier, type SemanticChangeOptions, } from './version-specifier.js';
|
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
* Comparators - Core comparison logic for npm-needs-publish
|
|
3
3
|
*/
|
|
4
4
|
export { compareDependencies, getDependencyChangeSummary, hasSignificantDependencyChanges, } from './dependency.js';
|
|
5
|
-
export { comparePackageFiles, getFileChangeSummary, hashBuffer, isOnlyPackageJsonChange, } from './file-content.js';
|
|
5
|
+
export { comparePackageFiles, extractPackageJson, getFileChangeSummary, hashBuffer, isOnlyPackageJsonChange, } from './file-content.js';
|
|
6
6
|
export { comparePackageJson, isSignificantField, } from './package-json.js';
|
|
7
7
|
export { compareVersionSpecifiers, comparisonToSemanticChange, parseVersionSpecifier, type SemanticChangeOptions, } from './version-specifier.js';
|
|
@@ -26,6 +26,9 @@ _export(exports, {
|
|
|
26
26
|
get comparisonToSemanticChange () {
|
|
27
27
|
return _versionspecifierts.comparisonToSemanticChange;
|
|
28
28
|
},
|
|
29
|
+
get extractPackageJson () {
|
|
30
|
+
return _filecontentts.extractPackageJson;
|
|
31
|
+
},
|
|
29
32
|
get getDependencyChangeSummary () {
|
|
30
33
|
return _dependencyts.getDependencyChangeSummary;
|
|
31
34
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/npm/npm-needs-publish/src/comparators/index.ts"],"sourcesContent":["/**\n * Comparators - Core comparison logic for npm-needs-publish\n */\n\nexport {\n compareDependencies,\n getDependencyChangeSummary,\n hasSignificantDependencyChanges,\n} from './dependency.ts';\nexport {\n comparePackageFiles,\n getFileChangeSummary,\n hashBuffer,\n isOnlyPackageJsonChange,\n} from './file-content.ts';\n\nexport {\n comparePackageJson,\n isSignificantField,\n} from './package-json.ts';\nexport {\n compareVersionSpecifiers,\n comparisonToSemanticChange,\n parseVersionSpecifier,\n type SemanticChangeOptions,\n} from './version-specifier.ts';\n"],"names":["compareDependencies","comparePackageFiles","comparePackageJson","compareVersionSpecifiers","comparisonToSemanticChange","getDependencyChangeSummary","getFileChangeSummary","hasSignificantDependencyChanges","hashBuffer","isOnlyPackageJsonChange","isSignificantField","parseVersionSpecifier"],"mappings":"AAAA;;CAEC;;;;;;;;;;;QAGCA;eAAAA,iCAAmB;;QAKnBC;eAAAA,kCAAmB;;
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/npm/npm-needs-publish/src/comparators/index.ts"],"sourcesContent":["/**\n * Comparators - Core comparison logic for npm-needs-publish\n */\n\nexport {\n compareDependencies,\n getDependencyChangeSummary,\n hasSignificantDependencyChanges,\n} from './dependency.ts';\nexport {\n comparePackageFiles,\n extractPackageJson,\n getFileChangeSummary,\n hashBuffer,\n isOnlyPackageJsonChange,\n} from './file-content.ts';\n\nexport {\n comparePackageJson,\n isSignificantField,\n} from './package-json.ts';\nexport {\n compareVersionSpecifiers,\n comparisonToSemanticChange,\n parseVersionSpecifier,\n type SemanticChangeOptions,\n} from './version-specifier.ts';\n"],"names":["compareDependencies","comparePackageFiles","comparePackageJson","compareVersionSpecifiers","comparisonToSemanticChange","extractPackageJson","getDependencyChangeSummary","getFileChangeSummary","hasSignificantDependencyChanges","hashBuffer","isOnlyPackageJsonChange","isSignificantField","parseVersionSpecifier"],"mappings":"AAAA;;CAEC;;;;;;;;;;;QAGCA;eAAAA,iCAAmB;;QAKnBC;eAAAA,kCAAmB;;QAQnBC;eAAAA,iCAAkB;;QAIlBC;eAAAA,4CAAwB;;QACxBC;eAAAA,8CAA0B;;QAZ1BC;eAAAA,iCAAkB;;QALlBC;eAAAA,wCAA0B;;QAM1BC;eAAAA,mCAAoB;;QALpBC;eAAAA,6CAA+B;;QAM/BC;eAAAA,yBAAU;;QACVC;eAAAA,sCAAuB;;QAKvBC;eAAAA,iCAAkB;;QAKlBC;eAAAA,yCAAqB;;;4BAhBhB;6BAOA;6BAKA;kCAMA"}
|
package/dist/cjs/index.d.cts
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
* ```
|
|
19
19
|
*/
|
|
20
20
|
export { compareDependencies } from './comparators/dependency.js';
|
|
21
|
+
export { extractPackageJson } from './comparators/file-content.js';
|
|
21
22
|
export { comparePackageJson } from './comparators/package-json.js';
|
|
22
23
|
export { compareVersionSpecifiers, comparisonToSemanticChange, parseVersionSpecifier, type SemanticChangeOptions } from './comparators/version-specifier.js';
|
|
23
24
|
export { type NeedsPublishCallback, needsPublish, needsPublishCb } from './needs-publish.js';
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
* ```
|
|
19
19
|
*/
|
|
20
20
|
export { compareDependencies } from './comparators/dependency.js';
|
|
21
|
+
export { extractPackageJson } from './comparators/file-content.js';
|
|
21
22
|
export { comparePackageJson } from './comparators/package-json.js';
|
|
22
23
|
export { compareVersionSpecifiers, comparisonToSemanticChange, parseVersionSpecifier, type SemanticChangeOptions } from './comparators/version-specifier.js';
|
|
23
24
|
export { type NeedsPublishCallback, needsPublish, needsPublishCb } from './needs-publish.js';
|
package/dist/cjs/index.js
CHANGED
|
@@ -40,6 +40,9 @@ _export(exports, {
|
|
|
40
40
|
get comparisonToSemanticChange () {
|
|
41
41
|
return _versionspecifierts.comparisonToSemanticChange;
|
|
42
42
|
},
|
|
43
|
+
get extractPackageJson () {
|
|
44
|
+
return _filecontentts.extractPackageJson;
|
|
45
|
+
},
|
|
43
46
|
get needsPublish () {
|
|
44
47
|
return _needspublishts.needsPublish;
|
|
45
48
|
},
|
|
@@ -51,6 +54,7 @@ _export(exports, {
|
|
|
51
54
|
}
|
|
52
55
|
});
|
|
53
56
|
var _dependencyts = require("./comparators/dependency.js");
|
|
57
|
+
var _filecontentts = require("./comparators/file-content.js");
|
|
54
58
|
var _packagejsonts = require("./comparators/package-json.js");
|
|
55
59
|
var _versionspecifierts = require("./comparators/version-specifier.js");
|
|
56
60
|
var _needspublishts = require("./needs-publish.js");
|
package/dist/cjs/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/npm/npm-needs-publish/src/index.ts"],"sourcesContent":["/**\n * npm-needs-publish\n *\n * Smart publish detection for npm packages - semantic package.json comparison\n * with semver-aware dependency analysis\n *\n * @example\n * ```typescript\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 * // Proceed with npm publish\n * } else {\n * console.log('No publish needed:', result.reason);\n * }\n * ```\n */\n\n// Comparators\nexport { compareDependencies } from './comparators/dependency.ts';\nexport { comparePackageJson } from './comparators/package-json.ts';\nexport { compareVersionSpecifiers, comparisonToSemanticChange, parseVersionSpecifier, type SemanticChangeOptions } from './comparators/version-specifier.ts';\n// Main API\nexport { type NeedsPublishCallback, needsPublish, needsPublishCb } from './needs-publish.ts';\n\n// Types\nexport type {\n ChangeDetail,\n CompareOptions,\n CompareSpecifierOptions,\n DependencyChange,\n DependencyCompareOptions,\n DependencyComparison,\n FieldChange,\n FileChange,\n FileComparison,\n NeedsPublishOptions,\n NeedsPublishResult,\n PackageJson,\n PackageJsonComparison,\n ParsedVersionSpecifier,\n SemanticChange,\n SpecifierComparison,\n VersionSpecifierType,\n} from './types.ts';\n"],"names":["compareDependencies","comparePackageJson","compareVersionSpecifiers","comparisonToSemanticChange","needsPublish","needsPublishCb","parseVersionSpecifier"],"mappings":"AAAA;;;;;;;;;;;;;;;;;;CAkBC,GAED,cAAc;;;;;;;;;;;;QACLA;eAAAA,iCAAmB;;
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/npm/npm-needs-publish/src/index.ts"],"sourcesContent":["/**\n * npm-needs-publish\n *\n * Smart publish detection for npm packages - semantic package.json comparison\n * with semver-aware dependency analysis\n *\n * @example\n * ```typescript\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 * // Proceed with npm publish\n * } else {\n * console.log('No publish needed:', result.reason);\n * }\n * ```\n */\n\n// Comparators\nexport { compareDependencies } from './comparators/dependency.ts';\nexport { extractPackageJson } from './comparators/file-content.ts';\nexport { comparePackageJson } from './comparators/package-json.ts';\nexport { compareVersionSpecifiers, comparisonToSemanticChange, parseVersionSpecifier, type SemanticChangeOptions } from './comparators/version-specifier.ts';\n// Main API\nexport { type NeedsPublishCallback, needsPublish, needsPublishCb } from './needs-publish.ts';\n\n// Types\nexport type {\n ChangeDetail,\n CompareOptions,\n CompareSpecifierOptions,\n DependencyChange,\n DependencyCompareOptions,\n DependencyComparison,\n FieldChange,\n FileChange,\n FileComparison,\n NeedsPublishOptions,\n NeedsPublishResult,\n PackageJson,\n PackageJsonComparison,\n ParsedVersionSpecifier,\n SemanticChange,\n SpecifierComparison,\n VersionSpecifierType,\n} from './types.ts';\n"],"names":["compareDependencies","comparePackageJson","compareVersionSpecifiers","comparisonToSemanticChange","extractPackageJson","needsPublish","needsPublishCb","parseVersionSpecifier"],"mappings":"AAAA;;;;;;;;;;;;;;;;;;CAkBC,GAED,cAAc;;;;;;;;;;;;QACLA;eAAAA,iCAAmB;;QAEnBC;eAAAA,iCAAkB;;QAClBC;eAAAA,4CAAwB;;QAAEC;eAAAA,8CAA0B;;QAFpDC;eAAAA,iCAAkB;;QAISC;eAAAA,4BAAY;;QAAEC;eAAAA,8BAAc;;QAFDC;eAAAA,yCAAqB;;;4BAHhD;6BACD;6BACA;kCACqF;8BAEhD"}
|
|
@@ -274,7 +274,7 @@ var workerWrapper = (0, _tsdslib.wrapWorker)(_path.default.join(dist, 'cjs', 'ne
|
|
|
274
274
|
}
|
|
275
275
|
(function() {
|
|
276
276
|
return _async_to_generator(function() {
|
|
277
|
-
var pacote, Arborist, npa, execFile, promisify, execFileAsync, _ref, comparePackageFiles, comparePackageJson, hashBuffer, registry, scope, stdout, e, registryPkg, registryTarball, _packument_disttags, _registryPkg_dist, _registryPkg_dist1, packument, latestVersion, tarballUrl, err, error, localTarball, spec, manifest, err1, error1, localHash, registryHash, fileComparison, pkgJsonComparison, changes;
|
|
277
|
+
var pacote, Arborist, npa, execFile, promisify, execFileAsync, _ref, comparePackageFiles, comparePackageJson, extractPackageJson, hashBuffer, registry, scope, stdout, e, registryPkg, registryTarball, _packument_disttags, _registryPkg_dist, _registryPkg_dist1, packument, latestVersion, tarballUrl, err, error, localTarball, spec, manifest, err1, error1, localHash, registryHash, fileComparison, localTarballPkg, registryTarballPkg, pkgJsonComparison, changes;
|
|
278
278
|
return _ts_generator(this, function(_state) {
|
|
279
279
|
switch(_state.label){
|
|
280
280
|
case 0:
|
|
@@ -291,7 +291,7 @@ var workerWrapper = (0, _tsdslib.wrapWorker)(_path.default.join(dist, 'cjs', 'ne
|
|
|
291
291
|
})
|
|
292
292
|
];
|
|
293
293
|
case 1:
|
|
294
|
-
_ref = _state.sent(), comparePackageFiles = _ref.comparePackageFiles, comparePackageJson = _ref.comparePackageJson, hashBuffer = _ref.hashBuffer;
|
|
294
|
+
_ref = _state.sent(), comparePackageFiles = _ref.comparePackageFiles, comparePackageJson = _ref.comparePackageJson, extractPackageJson = _ref.extractPackageJson, hashBuffer = _ref.hashBuffer;
|
|
295
295
|
// Get registry URL for scoped packages
|
|
296
296
|
registry = options.registry;
|
|
297
297
|
if (!!registry) return [
|
|
@@ -511,61 +511,75 @@ var workerWrapper = (0, _tsdslib.wrapWorker)(_path.default.join(dist, 'cjs', 'ne
|
|
|
511
511
|
2
|
|
512
512
|
];
|
|
513
513
|
}
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
2
|
|
538
|
-
];
|
|
539
|
-
}
|
|
540
|
-
// Build changes list
|
|
541
|
-
changes = _to_consumable_array(pkgJsonComparison.fieldChanges.map(function(fc) {
|
|
542
|
-
return {
|
|
543
|
-
type: 'field',
|
|
544
|
-
field: fc.field,
|
|
545
|
-
oldValue: fc.oldValue,
|
|
546
|
-
newValue: fc.newValue,
|
|
547
|
-
significance: fc.significance
|
|
548
|
-
};
|
|
549
|
-
})).concat(_to_consumable_array(pkgJsonComparison.dependencyChanges.filter(function(dc) {
|
|
550
|
-
return dc.semanticChange !== 'equivalent' && dc.semanticChange !== 'none';
|
|
551
|
-
}).map(function(dc) {
|
|
552
|
-
return {
|
|
553
|
-
type: 'dependency',
|
|
554
|
-
field: "".concat(dc.type, ".").concat(dc.name),
|
|
555
|
-
oldValue: dc.oldSpec,
|
|
556
|
-
newValue: dc.newSpec,
|
|
557
|
-
significance: 'significant'
|
|
558
|
-
};
|
|
559
|
-
})));
|
|
514
|
+
if (!(fileComparison.packageJsonOnly && !options.packageJsonOnly)) return [
|
|
515
|
+
3,
|
|
516
|
+
17
|
|
517
|
+
];
|
|
518
|
+
return [
|
|
519
|
+
4,
|
|
520
|
+
extractPackageJson(localTarball)
|
|
521
|
+
];
|
|
522
|
+
case 15:
|
|
523
|
+
localTarballPkg = _state.sent();
|
|
524
|
+
return [
|
|
525
|
+
4,
|
|
526
|
+
extractPackageJson(registryTarball)
|
|
527
|
+
];
|
|
528
|
+
case 16:
|
|
529
|
+
registryTarballPkg = _state.sent();
|
|
530
|
+
pkgJsonComparison = comparePackageJson(localTarballPkg, registryTarballPkg, {
|
|
531
|
+
includeOptionalDeps: options.includeOptionalDeps,
|
|
532
|
+
additionalSignificantFields: options.additionalSignificantFields,
|
|
533
|
+
ignoreFields: options.ignoreFields,
|
|
534
|
+
treatNarrowingAsEquivalent: options.treatNarrowingAsEquivalent
|
|
535
|
+
});
|
|
536
|
+
if (!pkgJsonComparison.hasSignificantChanges) {
|
|
560
537
|
callback(null, {
|
|
561
|
-
needsPublish:
|
|
562
|
-
reason:
|
|
563
|
-
changes:
|
|
538
|
+
needsPublish: false,
|
|
539
|
+
reason: 'Package.json changes are not significant for consumers',
|
|
540
|
+
changes: pkgJsonComparison.fieldChanges.map(function(fc) {
|
|
541
|
+
return {
|
|
542
|
+
type: 'field',
|
|
543
|
+
field: fc.field,
|
|
544
|
+
oldValue: fc.oldValue,
|
|
545
|
+
newValue: fc.newValue,
|
|
546
|
+
significance: 'informational'
|
|
547
|
+
};
|
|
548
|
+
})
|
|
564
549
|
});
|
|
565
550
|
return [
|
|
566
551
|
2
|
|
567
552
|
];
|
|
568
553
|
}
|
|
554
|
+
// Build changes list
|
|
555
|
+
changes = _to_consumable_array(pkgJsonComparison.fieldChanges.map(function(fc) {
|
|
556
|
+
return {
|
|
557
|
+
type: 'field',
|
|
558
|
+
field: fc.field,
|
|
559
|
+
oldValue: fc.oldValue,
|
|
560
|
+
newValue: fc.newValue,
|
|
561
|
+
significance: fc.significance
|
|
562
|
+
};
|
|
563
|
+
})).concat(_to_consumable_array(pkgJsonComparison.dependencyChanges.filter(function(dc) {
|
|
564
|
+
return dc.semanticChange !== 'equivalent' && dc.semanticChange !== 'none';
|
|
565
|
+
}).map(function(dc) {
|
|
566
|
+
return {
|
|
567
|
+
type: 'dependency',
|
|
568
|
+
field: "".concat(dc.type, ".").concat(dc.name),
|
|
569
|
+
oldValue: dc.oldSpec,
|
|
570
|
+
newValue: dc.newSpec,
|
|
571
|
+
significance: 'significant'
|
|
572
|
+
};
|
|
573
|
+
})));
|
|
574
|
+
callback(null, {
|
|
575
|
+
needsPublish: true,
|
|
576
|
+
reason: pkgJsonComparison.summary,
|
|
577
|
+
changes: changes
|
|
578
|
+
});
|
|
579
|
+
return [
|
|
580
|
+
2
|
|
581
|
+
];
|
|
582
|
+
case 17:
|
|
569
583
|
// Step 7: Other files changed
|
|
570
584
|
callback(null, {
|
|
571
585
|
needsPublish: true,
|
|
@@ -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 { wrapWorker } from 'tsds-lib';\nimport url from 'url';\nimport { stringStartsWith } from './compat.ts';\nimport type { ChangeDetail, NeedsPublishOptions, NeedsPublishResult, PackageJson } from './types.ts';\n\n// Version check for worker pattern - use worker for Node < 14\nconst major = +process.versions.node.split('.')[0];\nconst version = major > 14 ? 'local' : 'stable';\nconst _require = typeof require === 'undefined' ? Module.createRequire(import.meta.url) : require;\nconst __dirname = path.dirname(typeof __filename === 'undefined' ? url.fileURLToPath(import.meta.url) : __filename);\nconst dist = path.join(__dirname, '..');\nconst workerWrapper = wrapWorker(path.join(dist, 'cjs', 'needs-publish.js'));\n\n/**\n * Callback type for needsPublish\n */\nexport type NeedsPublishCallback = (error: Error | null, result?: NeedsPublishResult) => void;\n\n/**\n * Worker function that runs on Node 14+\n */\nfunction worker(options: NeedsPublishOptions, callback: NeedsPublishCallback): undefined {\n const cwd = options.cwd || process.cwd();\n\n // Load local package.json\n const localPkg: PackageJson = options.package || JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8'));\n\n // Skip private packages\n if (localPkg.private) {\n callback(null, {\n needsPublish: false,\n reason: 'Package is private',\n });\n return;\n }\n\n (async () => {\n const pacote = _require('pacote');\n const Arborist = _require('@npmcli/arborist');\n const npa = _require('npm-package-arg');\n const { execFile } = _require('child_process');\n const { promisify } = _require('util');\n const execFileAsync = promisify(execFile);\n\n // Dynamic import for comparators (they use modern features)\n const { comparePackageFiles, comparePackageJson, hashBuffer } = await import('./comparators/index.ts');\n\n // Get registry URL for scoped packages\n let registry: string | undefined = options.registry;\n if (!registry) {\n const scope = stringStartsWith(localPkg.name, '@') ? localPkg.name.split('/')[0] : undefined;\n if (scope) {\n try {\n const { stdout } = await execFileAsync('npm', ['config', 'get', `${scope}:registry`]);\n registry = stdout.trim();\n if (registry === 'undefined') registry = undefined;\n } catch {\n // Fallback to default registry\n }\n }\n }\n\n // Step 1: Try to fetch registry packument\n let registryPkg: PackageJson;\n let registryTarball: Buffer;\n\n try {\n const packument = await pacote.packument(localPkg.name, {\n Arborist,\n ...(registry && { registry }),\n });\n\n const latestVersion = packument['dist-tags']?.latest;\n if (!latestVersion) {\n callback(null, {\n needsPublish: true,\n reason: 'No latest version found in registry (first publish)',\n changes: [{ type: 'first-publish', significance: 'critical' }],\n });\n return;\n }\n\n registryPkg = packument.versions[latestVersion] as unknown as PackageJson;\n\n // Step 2: Version comparison (fast path)\n if (localPkg.version !== latestVersion) {\n callback(null, {\n needsPublish: true,\n reason: `Version differs (local: ${localPkg.version}, registry: ${latestVersion})`,\n changes: [\n {\n type: 'version',\n field: 'version',\n oldValue: latestVersion,\n newValue: localPkg.version,\n significance: 'critical',\n },\n ],\n });\n return;\n }\n\n // Fetch registry tarball for comparison\n const tarballUrl = registryPkg.dist?.tarball;\n if (!tarballUrl) {\n callback(null, {\n needsPublish: true,\n reason: 'Registry package has no tarball URL',\n changes: [{ type: 'first-publish', significance: 'critical' }],\n });\n return;\n }\n\n registryTarball = await pacote.tarball(tarballUrl, {\n Arborist,\n integrity: registryPkg.dist?.integrity,\n });\n } catch (err: unknown) {\n const error = err as { code?: string; message?: string };\n // Package not found in registry (first publish)\n if (error.code === 'E404') {\n callback(null, {\n needsPublish: true,\n reason: 'Package not found in registry (first publish)',\n changes: [{ type: 'first-publish', significance: 'critical' }],\n });\n return;\n }\n // Unknown error - assume changed to be safe\n callback(null, {\n needsPublish: true,\n reason: `Error checking registry: ${error.message || 'Unknown error'}`,\n });\n return;\n }\n\n // Step 3: Pack local package\n let localTarball: Buffer;\n try {\n const spec = npa(cwd);\n const manifest = await pacote.manifest(spec, { Arborist });\n localTarball = await pacote.tarball(manifest._resolved, {\n Arborist,\n integrity: manifest._integrity,\n });\n } catch (err: unknown) {\n const error = err as { message?: string };\n callback(null, {\n needsPublish: true,\n reason: `Error packing local package: ${error.message || 'Unknown error'}`,\n });\n return;\n }\n\n // Step 4: Fast hash comparison\n const localHash = hashBuffer(localTarball);\n const registryHash = hashBuffer(registryTarball);\n\n if (localHash === registryHash) {\n callback(null, {\n needsPublish: false,\n reason: `No changes detected (hash: ${localHash.substring(0, 16)}...)`,\n });\n return;\n }\n\n // Step 5: File-by-file comparison\n const fileComparison = await comparePackageFiles(localTarball, registryTarball);\n\n if (fileComparison.identical) {\n // Hash mismatch but files identical - likely tarball metadata difference\n callback(null, {\n needsPublish: false,\n reason: 'Files identical (tarball metadata differs)',\n });\n return;\n }\n\n // Step 6: If only package.json differs, do semantic comparison\n if (fileComparison.packageJsonOnly && !options.packageJsonOnly) {\n const pkgJsonComparison = comparePackageJson(localPkg, registryPkg, {\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 - works on Node 0.8+\n * Uses worker for older Node versions, runs locally on Node 14+\n */\nexport function needsPublishCb(options: NeedsPublishOptions, callback: NeedsPublishCallback): void {\n version !== 'local' ? workerWrapper(version, options, callback) : worker(options, callback);\n}\n\n/**\n * Determine if a package needs to be published to npm.\n *\n * @example\n * ```ts\n * import { needsPublish } from 'npm-needs-publish';\n *\n * const result = await needsPublish({ cwd: process.cwd() });\n * if (result.needsPublish) {\n * console.log('Publish needed:', result.reason);\n * }\n * ```\n */\nexport function needsPublish(options: NeedsPublishOptions = {}): Promise<NeedsPublishResult> {\n return new Promise((resolve, reject) => {\n needsPublishCb(options, (error, result) => {\n if (error) reject(error);\n else if (result) resolve(result);\n else reject(new Error('No result returned'));\n });\n });\n}\n"],"names":["needsPublish","needsPublishCb","major","process","versions","node","split","version","_require","require","Module","createRequire","__dirname","path","dirname","__filename","url","fileURLToPath","dist","join","workerWrapper","wrapWorker","worker","options","callback","cwd","localPkg","package","JSON","parse","fs","readFileSync","private","reason","pacote","Arborist","npa","execFile","promisify","execFileAsync","comparePackageFiles","comparePackageJson","hashBuffer","registry","scope","stdout","registryPkg","registryTarball","packument","latestVersion","tarballUrl","err","error","localTarball","spec","manifest","localHash","registryHash","fileComparison","pkgJsonComparison","changes","stringStartsWith","name","undefined","trim","latest","type","significance","field","oldValue","newValue","tarball","integrity","code","message","_resolved","_integrity","substring","identical","packageJsonOnly","includeOptionalDeps","additionalSignificantFields","ignoreFields","treatNarrowingAsEquivalent","hasSignificantChanges","fieldChanges","map","fc","dependencyChanges","filter","dc","semanticChange","oldSpec","newSpec","summary","fileChanges","length","catch","Promise","resolve","reject","result","Error"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/npm/npm-needs-publish/src/needs-publish.ts"],"sourcesContent":["/**\n * Main orchestration logic for npm-needs-publish\n *\n * Algorithm:\n * 1. Fetch registry packument → if E404, return needsPublish=true (first publish)\n * 2. Version check → if different, return needsPublish=true (intentional bump)\n * 3. Fast hash check → if identical, return needsPublish=false (no changes)\n * 4. Extract both tarballs, compare file-by-file (excluding package.json)\n * 5. If non-package.json files differ → return needsPublish=true\n * 6. If only package.json differs → do semantic comparison\n */\n\nimport fs from 'fs';\nimport Module from 'module';\nimport path from 'path';\nimport { wrapWorker } from 'tsds-lib';\nimport url from 'url';\nimport { stringStartsWith } from './compat.ts';\nimport type { ChangeDetail, NeedsPublishOptions, NeedsPublishResult, PackageJson } from './types.ts';\n\n// Version check for worker pattern - use worker for Node < 14\nconst major = +process.versions.node.split('.')[0];\nconst version = major > 14 ? 'local' : 'stable';\nconst _require = typeof require === 'undefined' ? Module.createRequire(import.meta.url) : require;\nconst __dirname = path.dirname(typeof __filename === 'undefined' ? url.fileURLToPath(import.meta.url) : __filename);\nconst dist = path.join(__dirname, '..');\nconst workerWrapper = wrapWorker(path.join(dist, 'cjs', 'needs-publish.js'));\n\n/**\n * Callback type for needsPublish\n */\nexport type NeedsPublishCallback = (error: Error | null, result?: NeedsPublishResult) => void;\n\n/**\n * Worker function that runs on Node 14+\n */\nfunction worker(options: NeedsPublishOptions, callback: NeedsPublishCallback): undefined {\n const cwd = options.cwd || process.cwd();\n\n // Load local package.json\n const localPkg: PackageJson = options.package || JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8'));\n\n // Skip private packages\n if (localPkg.private) {\n callback(null, {\n needsPublish: false,\n reason: 'Package is private',\n });\n return;\n }\n\n (async () => {\n const pacote = _require('pacote');\n const Arborist = _require('@npmcli/arborist');\n const npa = _require('npm-package-arg');\n const { execFile } = _require('child_process');\n const { promisify } = _require('util');\n const execFileAsync = promisify(execFile);\n\n // Dynamic import for comparators (they use modern features)\n const { comparePackageFiles, comparePackageJson, extractPackageJson, hashBuffer } = await import('./comparators/index.ts');\n\n // Get registry URL for scoped packages\n let registry: string | undefined = options.registry;\n if (!registry) {\n const scope = stringStartsWith(localPkg.name, '@') ? localPkg.name.split('/')[0] : undefined;\n if (scope) {\n try {\n const { stdout } = await execFileAsync('npm', ['config', 'get', `${scope}:registry`]);\n registry = stdout.trim();\n if (registry === 'undefined') registry = undefined;\n } catch {\n // Fallback to default registry\n }\n }\n }\n\n // Step 1: Try to fetch registry packument\n let registryPkg: PackageJson;\n let registryTarball: Buffer;\n\n try {\n const packument = await pacote.packument(localPkg.name, {\n Arborist,\n ...(registry && { registry }),\n });\n\n const latestVersion = packument['dist-tags']?.latest;\n if (!latestVersion) {\n callback(null, {\n needsPublish: true,\n reason: 'No latest version found in registry (first publish)',\n changes: [{ type: 'first-publish', significance: 'critical' }],\n });\n return;\n }\n\n registryPkg = packument.versions[latestVersion] as unknown as PackageJson;\n\n // Step 2: Version comparison (fast path)\n if (localPkg.version !== latestVersion) {\n callback(null, {\n needsPublish: true,\n reason: `Version differs (local: ${localPkg.version}, registry: ${latestVersion})`,\n changes: [\n {\n type: 'version',\n field: 'version',\n oldValue: latestVersion,\n newValue: localPkg.version,\n significance: 'critical',\n },\n ],\n });\n return;\n }\n\n // Fetch registry tarball for comparison\n const tarballUrl = registryPkg.dist?.tarball;\n if (!tarballUrl) {\n callback(null, {\n needsPublish: true,\n reason: 'Registry package has no tarball URL',\n changes: [{ type: 'first-publish', significance: 'critical' }],\n });\n return;\n }\n\n registryTarball = await pacote.tarball(tarballUrl, {\n Arborist,\n integrity: registryPkg.dist?.integrity,\n });\n } catch (err: unknown) {\n const error = err as { code?: string; message?: string };\n // Package not found in registry (first publish)\n if (error.code === 'E404') {\n callback(null, {\n needsPublish: true,\n reason: 'Package not found in registry (first publish)',\n changes: [{ type: 'first-publish', significance: 'critical' }],\n });\n return;\n }\n // Unknown error - assume changed to be safe\n callback(null, {\n needsPublish: true,\n reason: `Error checking registry: ${error.message || 'Unknown error'}`,\n });\n return;\n }\n\n // Step 3: Pack local package\n let localTarball: Buffer;\n try {\n const spec = npa(cwd);\n const manifest = await pacote.manifest(spec, { Arborist });\n localTarball = await pacote.tarball(manifest._resolved, {\n Arborist,\n integrity: manifest._integrity,\n });\n } catch (err: unknown) {\n const error = err as { message?: string };\n callback(null, {\n needsPublish: true,\n reason: `Error packing local package: ${error.message || 'Unknown error'}`,\n });\n return;\n }\n\n // Step 4: Fast hash comparison\n const localHash = hashBuffer(localTarball);\n const registryHash = hashBuffer(registryTarball);\n\n if (localHash === registryHash) {\n callback(null, {\n needsPublish: false,\n reason: `No changes detected (hash: ${localHash.substring(0, 16)}...)`,\n });\n return;\n }\n\n // Step 5: File-by-file comparison\n const fileComparison = await comparePackageFiles(localTarball, registryTarball);\n\n if (fileComparison.identical) {\n // Hash mismatch but files identical - likely tarball metadata difference\n callback(null, {\n needsPublish: false,\n reason: 'Files identical (tarball metadata differs)',\n });\n return;\n }\n\n // Step 6: If only package.json differs, do semantic comparison\n if (fileComparison.packageJsonOnly && !options.packageJsonOnly) {\n // Extract package.json from both tarballs for accurate comparison\n // (packument metadata is missing fields like 'files')\n const localTarballPkg = (await extractPackageJson(localTarball)) as PackageJson;\n const registryTarballPkg = (await extractPackageJson(registryTarball)) as PackageJson;\n\n const pkgJsonComparison = comparePackageJson(localTarballPkg, registryTarballPkg, {\n includeOptionalDeps: options.includeOptionalDeps,\n additionalSignificantFields: options.additionalSignificantFields,\n ignoreFields: options.ignoreFields,\n treatNarrowingAsEquivalent: options.treatNarrowingAsEquivalent,\n });\n\n if (!pkgJsonComparison.hasSignificantChanges) {\n callback(null, {\n needsPublish: false,\n reason: 'Package.json changes are not significant for consumers',\n changes: pkgJsonComparison.fieldChanges.map((fc) => ({\n type: 'field' as const,\n field: fc.field,\n oldValue: fc.oldValue,\n newValue: fc.newValue,\n significance: 'informational' as const,\n })),\n });\n return;\n }\n\n // Build changes list\n const changes: ChangeDetail[] = [\n ...pkgJsonComparison.fieldChanges.map((fc) => ({\n type: 'field' as const,\n field: fc.field,\n oldValue: fc.oldValue,\n newValue: fc.newValue,\n significance: fc.significance,\n })),\n ...pkgJsonComparison.dependencyChanges\n .filter((dc) => dc.semanticChange !== 'equivalent' && dc.semanticChange !== 'none')\n .map((dc) => ({\n type: 'dependency' as const,\n field: `${dc.type}.${dc.name}`,\n oldValue: dc.oldSpec,\n newValue: dc.newSpec,\n significance: 'significant' as const,\n })),\n ];\n\n callback(null, {\n needsPublish: true,\n reason: pkgJsonComparison.summary,\n changes,\n });\n return;\n }\n\n // Step 7: Other files changed\n callback(null, {\n needsPublish: true,\n reason: `Code changes detected (${fileComparison.fileChanges.length} files changed)`,\n changes: fileComparison.fileChanges.map((fc) => ({\n type: 'file' as const,\n field: fc.path,\n significance: 'significant' as const,\n })),\n });\n })().catch(callback);\n}\n\n/**\n * Callback-based needsPublish - works on Node 0.8+\n * Uses worker for older Node versions, runs locally on Node 14+\n */\nexport function needsPublishCb(options: NeedsPublishOptions, callback: NeedsPublishCallback): void {\n version !== 'local' ? workerWrapper(version, options, callback) : worker(options, callback);\n}\n\n/**\n * Determine if a package needs to be published to npm.\n *\n * @example\n * ```ts\n * import { needsPublish } from 'npm-needs-publish';\n *\n * const result = await needsPublish({ cwd: process.cwd() });\n * if (result.needsPublish) {\n * console.log('Publish needed:', result.reason);\n * }\n * ```\n */\nexport function needsPublish(options: NeedsPublishOptions = {}): Promise<NeedsPublishResult> {\n return new Promise((resolve, reject) => {\n needsPublishCb(options, (error, result) => {\n if (error) reject(error);\n else if (result) resolve(result);\n else reject(new Error('No result returned'));\n });\n });\n}\n"],"names":["needsPublish","needsPublishCb","major","process","versions","node","split","version","_require","require","Module","createRequire","__dirname","path","dirname","__filename","url","fileURLToPath","dist","join","workerWrapper","wrapWorker","worker","options","callback","cwd","localPkg","package","JSON","parse","fs","readFileSync","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","undefined","trim","latest","type","significance","field","oldValue","newValue","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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"}
|
|
@@ -27,3 +27,10 @@ export declare function getFileChangeSummary(changes: FileChange[]): string;
|
|
|
27
27
|
* Check if changes are only in package.json
|
|
28
28
|
*/
|
|
29
29
|
export declare function isOnlyPackageJsonChange(changes: FileChange[]): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Extract package.json from a tarball
|
|
32
|
+
*
|
|
33
|
+
* @param tarball - Tarball as Buffer (gzipped)
|
|
34
|
+
* @returns Parsed package.json object
|
|
35
|
+
*/
|
|
36
|
+
export declare function extractPackageJson(tarball: Buffer): Promise<unknown>;
|
|
@@ -159,3 +159,17 @@ function getTar() {
|
|
|
159
159
|
}
|
|
160
160
|
return true;
|
|
161
161
|
}
|
|
162
|
+
/**
|
|
163
|
+
* Extract package.json from a tarball
|
|
164
|
+
*
|
|
165
|
+
* @param tarball - Tarball as Buffer (gzipped)
|
|
166
|
+
* @returns Parsed package.json object
|
|
167
|
+
*/ export async function extractPackageJson(tarball) {
|
|
168
|
+
const files = await extractTarball(tarball);
|
|
169
|
+
// Look for package.json in the tarball
|
|
170
|
+
const pkgJsonPath = Object.keys(files).find((p)=>p === 'package/package.json' || p.indexOf('/package.json') === p.length - 13);
|
|
171
|
+
if (!pkgJsonPath || !files[pkgJsonPath]) {
|
|
172
|
+
throw new Error('package.json not found in tarball');
|
|
173
|
+
}
|
|
174
|
+
return JSON.parse(files[pkgJsonPath].toString('utf8'));
|
|
175
|
+
}
|
|
@@ -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 { Readable } from 'stream';\nimport { pipeline } from 'stream/promises';\nimport zlib from 'zlib';\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"],"names":["crypto","Module","Readable","pipeline","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"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/npm/npm-needs-publish/src/comparators/file-content.ts"],"sourcesContent":["/**\n * File content comparison for package tarballs\n *\n * Compares files between local and registry tarballs:\n * 1. Fast path: Compare tarball hashes\n * 2. If different: Extract and compare file-by-file\n * 3. Special handling for package.json (semantic comparison elsewhere)\n */\n\nimport crypto from 'crypto';\nimport Module from 'module';\nimport { Readable } from 'stream';\nimport { pipeline } from 'stream/promises';\nimport zlib from 'zlib';\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","Readable","pipeline","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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"}
|
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
* Comparators - Core comparison logic for npm-needs-publish
|
|
3
3
|
*/
|
|
4
4
|
export { compareDependencies, getDependencyChangeSummary, hasSignificantDependencyChanges, } from './dependency.js';
|
|
5
|
-
export { comparePackageFiles, getFileChangeSummary, hashBuffer, isOnlyPackageJsonChange, } from './file-content.js';
|
|
5
|
+
export { comparePackageFiles, extractPackageJson, getFileChangeSummary, hashBuffer, isOnlyPackageJsonChange, } from './file-content.js';
|
|
6
6
|
export { comparePackageJson, isSignificantField, } from './package-json.js';
|
|
7
7
|
export { compareVersionSpecifiers, comparisonToSemanticChange, parseVersionSpecifier, type SemanticChangeOptions, } from './version-specifier.js';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Comparators - Core comparison logic for npm-needs-publish
|
|
3
3
|
*/ export { compareDependencies, getDependencyChangeSummary, hasSignificantDependencyChanges } from './dependency.js';
|
|
4
|
-
export { comparePackageFiles, getFileChangeSummary, hashBuffer, isOnlyPackageJsonChange } from './file-content.js';
|
|
4
|
+
export { comparePackageFiles, extractPackageJson, getFileChangeSummary, hashBuffer, isOnlyPackageJsonChange } from './file-content.js';
|
|
5
5
|
export { comparePackageJson, isSignificantField } from './package-json.js';
|
|
6
6
|
export { compareVersionSpecifiers, comparisonToSemanticChange, parseVersionSpecifier } from './version-specifier.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/npm/npm-needs-publish/src/comparators/index.ts"],"sourcesContent":["/**\n * Comparators - Core comparison logic for npm-needs-publish\n */\n\nexport {\n compareDependencies,\n getDependencyChangeSummary,\n hasSignificantDependencyChanges,\n} from './dependency.ts';\nexport {\n comparePackageFiles,\n getFileChangeSummary,\n hashBuffer,\n isOnlyPackageJsonChange,\n} from './file-content.ts';\n\nexport {\n comparePackageJson,\n isSignificantField,\n} from './package-json.ts';\nexport {\n compareVersionSpecifiers,\n comparisonToSemanticChange,\n parseVersionSpecifier,\n type SemanticChangeOptions,\n} from './version-specifier.ts';\n"],"names":["compareDependencies","getDependencyChangeSummary","hasSignificantDependencyChanges","comparePackageFiles","getFileChangeSummary","hashBuffer","isOnlyPackageJsonChange","comparePackageJson","isSignificantField","compareVersionSpecifiers","comparisonToSemanticChange","parseVersionSpecifier"],"mappings":"AAAA;;CAEC,GAED,SACEA,mBAAmB,EACnBC,0BAA0B,EAC1BC,+BAA+B,QAC1B,kBAAkB;AACzB,SACEC,mBAAmB,EACnBC,oBAAoB,EACpBC,UAAU,EACVC,uBAAuB,QAClB,oBAAoB;AAE3B,SACEC,kBAAkB,EAClBC,kBAAkB,QACb,oBAAoB;AAC3B,SACEC,wBAAwB,EACxBC,0BAA0B,EAC1BC,qBAAqB,QAEhB,yBAAyB"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/npm/npm-needs-publish/src/comparators/index.ts"],"sourcesContent":["/**\n * Comparators - Core comparison logic for npm-needs-publish\n */\n\nexport {\n compareDependencies,\n getDependencyChangeSummary,\n hasSignificantDependencyChanges,\n} from './dependency.ts';\nexport {\n comparePackageFiles,\n extractPackageJson,\n getFileChangeSummary,\n hashBuffer,\n isOnlyPackageJsonChange,\n} from './file-content.ts';\n\nexport {\n comparePackageJson,\n isSignificantField,\n} from './package-json.ts';\nexport {\n compareVersionSpecifiers,\n comparisonToSemanticChange,\n parseVersionSpecifier,\n type SemanticChangeOptions,\n} from './version-specifier.ts';\n"],"names":["compareDependencies","getDependencyChangeSummary","hasSignificantDependencyChanges","comparePackageFiles","extractPackageJson","getFileChangeSummary","hashBuffer","isOnlyPackageJsonChange","comparePackageJson","isSignificantField","compareVersionSpecifiers","comparisonToSemanticChange","parseVersionSpecifier"],"mappings":"AAAA;;CAEC,GAED,SACEA,mBAAmB,EACnBC,0BAA0B,EAC1BC,+BAA+B,QAC1B,kBAAkB;AACzB,SACEC,mBAAmB,EACnBC,kBAAkB,EAClBC,oBAAoB,EACpBC,UAAU,EACVC,uBAAuB,QAClB,oBAAoB;AAE3B,SACEC,kBAAkB,EAClBC,kBAAkB,QACb,oBAAoB;AAC3B,SACEC,wBAAwB,EACxBC,0BAA0B,EAC1BC,qBAAqB,QAEhB,yBAAyB"}
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
* ```
|
|
19
19
|
*/
|
|
20
20
|
export { compareDependencies } from './comparators/dependency.js';
|
|
21
|
+
export { extractPackageJson } from './comparators/file-content.js';
|
|
21
22
|
export { comparePackageJson } from './comparators/package-json.js';
|
|
22
23
|
export { compareVersionSpecifiers, comparisonToSemanticChange, parseVersionSpecifier, type SemanticChangeOptions } from './comparators/version-specifier.js';
|
|
23
24
|
export { type NeedsPublishCallback, needsPublish, needsPublishCb } from './needs-publish.js';
|
package/dist/esm/index.js
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
* ```
|
|
19
19
|
*/ // Comparators
|
|
20
20
|
export { compareDependencies } from './comparators/dependency.js';
|
|
21
|
+
export { extractPackageJson } from './comparators/file-content.js';
|
|
21
22
|
export { comparePackageJson } from './comparators/package-json.js';
|
|
22
23
|
export { compareVersionSpecifiers, comparisonToSemanticChange, parseVersionSpecifier } from './comparators/version-specifier.js';
|
|
23
24
|
// Main API
|
package/dist/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/npm/npm-needs-publish/src/index.ts"],"sourcesContent":["/**\n * npm-needs-publish\n *\n * Smart publish detection for npm packages - semantic package.json comparison\n * with semver-aware dependency analysis\n *\n * @example\n * ```typescript\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 * // Proceed with npm publish\n * } else {\n * console.log('No publish needed:', result.reason);\n * }\n * ```\n */\n\n// Comparators\nexport { compareDependencies } from './comparators/dependency.ts';\nexport { comparePackageJson } from './comparators/package-json.ts';\nexport { compareVersionSpecifiers, comparisonToSemanticChange, parseVersionSpecifier, type SemanticChangeOptions } from './comparators/version-specifier.ts';\n// Main API\nexport { type NeedsPublishCallback, needsPublish, needsPublishCb } from './needs-publish.ts';\n\n// Types\nexport type {\n ChangeDetail,\n CompareOptions,\n CompareSpecifierOptions,\n DependencyChange,\n DependencyCompareOptions,\n DependencyComparison,\n FieldChange,\n FileChange,\n FileComparison,\n NeedsPublishOptions,\n NeedsPublishResult,\n PackageJson,\n PackageJsonComparison,\n ParsedVersionSpecifier,\n SemanticChange,\n SpecifierComparison,\n VersionSpecifierType,\n} from './types.ts';\n"],"names":["compareDependencies","comparePackageJson","compareVersionSpecifiers","comparisonToSemanticChange","parseVersionSpecifier","needsPublish","needsPublishCb"],"mappings":"AAAA;;;;;;;;;;;;;;;;;;CAkBC,GAED,cAAc;AACd,SAASA,mBAAmB,QAAQ,8BAA8B;AAClE,SAASC,kBAAkB,QAAQ,gCAAgC;AACnE,SAASC,wBAAwB,EAAEC,0BAA0B,EAAEC,qBAAqB,QAAoC,qCAAqC;AAC7J,WAAW;AACX,SAAoCC,YAAY,EAAEC,cAAc,QAAQ,qBAAqB"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/npm/npm-needs-publish/src/index.ts"],"sourcesContent":["/**\n * npm-needs-publish\n *\n * Smart publish detection for npm packages - semantic package.json comparison\n * with semver-aware dependency analysis\n *\n * @example\n * ```typescript\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 * // Proceed with npm publish\n * } else {\n * console.log('No publish needed:', result.reason);\n * }\n * ```\n */\n\n// Comparators\nexport { compareDependencies } from './comparators/dependency.ts';\nexport { extractPackageJson } from './comparators/file-content.ts';\nexport { comparePackageJson } from './comparators/package-json.ts';\nexport { compareVersionSpecifiers, comparisonToSemanticChange, parseVersionSpecifier, type SemanticChangeOptions } from './comparators/version-specifier.ts';\n// Main API\nexport { type NeedsPublishCallback, needsPublish, needsPublishCb } from './needs-publish.ts';\n\n// Types\nexport type {\n ChangeDetail,\n CompareOptions,\n CompareSpecifierOptions,\n DependencyChange,\n DependencyCompareOptions,\n DependencyComparison,\n FieldChange,\n FileChange,\n FileComparison,\n NeedsPublishOptions,\n NeedsPublishResult,\n PackageJson,\n PackageJsonComparison,\n ParsedVersionSpecifier,\n SemanticChange,\n SpecifierComparison,\n VersionSpecifierType,\n} from './types.ts';\n"],"names":["compareDependencies","extractPackageJson","comparePackageJson","compareVersionSpecifiers","comparisonToSemanticChange","parseVersionSpecifier","needsPublish","needsPublishCb"],"mappings":"AAAA;;;;;;;;;;;;;;;;;;CAkBC,GAED,cAAc;AACd,SAASA,mBAAmB,QAAQ,8BAA8B;AAClE,SAASC,kBAAkB,QAAQ,gCAAgC;AACnE,SAASC,kBAAkB,QAAQ,gCAAgC;AACnE,SAASC,wBAAwB,EAAEC,0BAA0B,EAAEC,qBAAqB,QAAoC,qCAAqC;AAC7J,WAAW;AACX,SAAoCC,YAAY,EAAEC,cAAc,QAAQ,qBAAqB"}
|
|
@@ -43,7 +43,7 @@ const workerWrapper = wrapWorker(path.join(dist, 'cjs', 'needs-publish.js'));
|
|
|
43
43
|
const { promisify } = _require('util');
|
|
44
44
|
const execFileAsync = promisify(execFile);
|
|
45
45
|
// Dynamic import for comparators (they use modern features)
|
|
46
|
-
const { comparePackageFiles, comparePackageJson, hashBuffer } = await import('./comparators/index.js');
|
|
46
|
+
const { comparePackageFiles, comparePackageJson, extractPackageJson, hashBuffer } = await import('./comparators/index.js');
|
|
47
47
|
// Get registry URL for scoped packages
|
|
48
48
|
let registry = options.registry;
|
|
49
49
|
if (!registry) {
|
|
@@ -188,7 +188,11 @@ const workerWrapper = wrapWorker(path.join(dist, 'cjs', 'needs-publish.js'));
|
|
|
188
188
|
}
|
|
189
189
|
// Step 6: If only package.json differs, do semantic comparison
|
|
190
190
|
if (fileComparison.packageJsonOnly && !options.packageJsonOnly) {
|
|
191
|
-
|
|
191
|
+
// Extract package.json from both tarballs for accurate comparison
|
|
192
|
+
// (packument metadata is missing fields like 'files')
|
|
193
|
+
const localTarballPkg = await extractPackageJson(localTarball);
|
|
194
|
+
const registryTarballPkg = await extractPackageJson(registryTarball);
|
|
195
|
+
const pkgJsonComparison = comparePackageJson(localTarballPkg, registryTarballPkg, {
|
|
192
196
|
includeOptionalDeps: options.includeOptionalDeps,
|
|
193
197
|
additionalSignificantFields: options.additionalSignificantFields,
|
|
194
198
|
ignoreFields: options.ignoreFields,
|
|
@@ -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 { wrapWorker } from 'tsds-lib';\nimport url from 'url';\nimport { stringStartsWith } from './compat.ts';\nimport type { ChangeDetail, NeedsPublishOptions, NeedsPublishResult, PackageJson } from './types.ts';\n\n// Version check for worker pattern - use worker for Node < 14\nconst major = +process.versions.node.split('.')[0];\nconst version = major > 14 ? 'local' : 'stable';\nconst _require = typeof require === 'undefined' ? Module.createRequire(import.meta.url) : require;\nconst __dirname = path.dirname(typeof __filename === 'undefined' ? url.fileURLToPath(import.meta.url) : __filename);\nconst dist = path.join(__dirname, '..');\nconst workerWrapper = wrapWorker(path.join(dist, 'cjs', 'needs-publish.js'));\n\n/**\n * Callback type for needsPublish\n */\nexport type NeedsPublishCallback = (error: Error | null, result?: NeedsPublishResult) => void;\n\n/**\n * Worker function that runs on Node 14+\n */\nfunction worker(options: NeedsPublishOptions, callback: NeedsPublishCallback): undefined {\n const cwd = options.cwd || process.cwd();\n\n // Load local package.json\n const localPkg: PackageJson = options.package || JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8'));\n\n // Skip private packages\n if (localPkg.private) {\n callback(null, {\n needsPublish: false,\n reason: 'Package is private',\n });\n return;\n }\n\n (async () => {\n const pacote = _require('pacote');\n const Arborist = _require('@npmcli/arborist');\n const npa = _require('npm-package-arg');\n const { execFile } = _require('child_process');\n const { promisify } = _require('util');\n const execFileAsync = promisify(execFile);\n\n // Dynamic import for comparators (they use modern features)\n const { comparePackageFiles, comparePackageJson, hashBuffer } = await import('./comparators/index.ts');\n\n // Get registry URL for scoped packages\n let registry: string | undefined = options.registry;\n if (!registry) {\n const scope = stringStartsWith(localPkg.name, '@') ? localPkg.name.split('/')[0] : undefined;\n if (scope) {\n try {\n const { stdout } = await execFileAsync('npm', ['config', 'get', `${scope}:registry`]);\n registry = stdout.trim();\n if (registry === 'undefined') registry = undefined;\n } catch {\n // Fallback to default registry\n }\n }\n }\n\n // Step 1: Try to fetch registry packument\n let registryPkg: PackageJson;\n let registryTarball: Buffer;\n\n try {\n const packument = await pacote.packument(localPkg.name, {\n Arborist,\n ...(registry && { registry }),\n });\n\n const latestVersion = packument['dist-tags']?.latest;\n if (!latestVersion) {\n callback(null, {\n needsPublish: true,\n reason: 'No latest version found in registry (first publish)',\n changes: [{ type: 'first-publish', significance: 'critical' }],\n });\n return;\n }\n\n registryPkg = packument.versions[latestVersion] as unknown as PackageJson;\n\n // Step 2: Version comparison (fast path)\n if (localPkg.version !== latestVersion) {\n callback(null, {\n needsPublish: true,\n reason: `Version differs (local: ${localPkg.version}, registry: ${latestVersion})`,\n changes: [\n {\n type: 'version',\n field: 'version',\n oldValue: latestVersion,\n newValue: localPkg.version,\n significance: 'critical',\n },\n ],\n });\n return;\n }\n\n // Fetch registry tarball for comparison\n const tarballUrl = registryPkg.dist?.tarball;\n if (!tarballUrl) {\n callback(null, {\n needsPublish: true,\n reason: 'Registry package has no tarball URL',\n changes: [{ type: 'first-publish', significance: 'critical' }],\n });\n return;\n }\n\n registryTarball = await pacote.tarball(tarballUrl, {\n Arborist,\n integrity: registryPkg.dist?.integrity,\n });\n } catch (err: unknown) {\n const error = err as { code?: string; message?: string };\n // Package not found in registry (first publish)\n if (error.code === 'E404') {\n callback(null, {\n needsPublish: true,\n reason: 'Package not found in registry (first publish)',\n changes: [{ type: 'first-publish', significance: 'critical' }],\n });\n return;\n }\n // Unknown error - assume changed to be safe\n callback(null, {\n needsPublish: true,\n reason: `Error checking registry: ${error.message || 'Unknown error'}`,\n });\n return;\n }\n\n // Step 3: Pack local package\n let localTarball: Buffer;\n try {\n const spec = npa(cwd);\n const manifest = await pacote.manifest(spec, { Arborist });\n localTarball = await pacote.tarball(manifest._resolved, {\n Arborist,\n integrity: manifest._integrity,\n });\n } catch (err: unknown) {\n const error = err as { message?: string };\n callback(null, {\n needsPublish: true,\n reason: `Error packing local package: ${error.message || 'Unknown error'}`,\n });\n return;\n }\n\n // Step 4: Fast hash comparison\n const localHash = hashBuffer(localTarball);\n const registryHash = hashBuffer(registryTarball);\n\n if (localHash === registryHash) {\n callback(null, {\n needsPublish: false,\n reason: `No changes detected (hash: ${localHash.substring(0, 16)}...)`,\n });\n return;\n }\n\n // Step 5: File-by-file comparison\n const fileComparison = await comparePackageFiles(localTarball, registryTarball);\n\n if (fileComparison.identical) {\n // Hash mismatch but files identical - likely tarball metadata difference\n callback(null, {\n needsPublish: false,\n reason: 'Files identical (tarball metadata differs)',\n });\n return;\n }\n\n // Step 6: If only package.json differs, do semantic comparison\n if (fileComparison.packageJsonOnly && !options.packageJsonOnly) {\n const pkgJsonComparison = comparePackageJson(localPkg, registryPkg, {\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 - works on Node 0.8+\n * Uses worker for older Node versions, runs locally on Node 14+\n */\nexport function needsPublishCb(options: NeedsPublishOptions, callback: NeedsPublishCallback): void {\n version !== 'local' ? workerWrapper(version, options, callback) : worker(options, callback);\n}\n\n/**\n * Determine if a package needs to be published to npm.\n *\n * @example\n * ```ts\n * import { needsPublish } from 'npm-needs-publish';\n *\n * const result = await needsPublish({ cwd: process.cwd() });\n * if (result.needsPublish) {\n * console.log('Publish needed:', result.reason);\n * }\n * ```\n */\nexport function needsPublish(options: NeedsPublishOptions = {}): Promise<NeedsPublishResult> {\n return new Promise((resolve, reject) => {\n needsPublishCb(options, (error, result) => {\n if (error) reject(error);\n else if (result) resolve(result);\n else reject(new Error('No result returned'));\n });\n });\n}\n"],"names":["fs","Module","path","wrapWorker","url","stringStartsWith","major","process","versions","node","split","version","_require","require","createRequire","__dirname","dirname","__filename","fileURLToPath","dist","join","workerWrapper","worker","options","callback","cwd","localPkg","package","JSON","parse","readFileSync","private","needsPublish","reason","pacote","Arborist","npa","execFile","promisify","execFileAsync","comparePackageFiles","comparePackageJson","hashBuffer","registry","scope","name","undefined","stdout","trim","registryPkg","registryTarball","packument","latestVersion","latest","changes","type","significance","field","oldValue","newValue","tarballUrl","tarball","integrity","err","error","code","message","localTarball","spec","manifest","_resolved","_integrity","localHash","registryHash","substring","fileComparison","identical","packageJsonOnly","pkgJsonComparison","includeOptionalDeps","additionalSignificantFields","ignoreFields","treatNarrowingAsEquivalent","hasSignificantChanges","fieldChanges","map","fc","dependencyChanges","filter","dc","semanticChange","oldSpec","newSpec","summary","fileChanges","length","catch","needsPublishCb","Promise","resolve","reject","result","Error"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/npm/npm-needs-publish/src/needs-publish.ts"],"sourcesContent":["/**\n * Main orchestration logic for npm-needs-publish\n *\n * Algorithm:\n * 1. Fetch registry packument → if E404, return needsPublish=true (first publish)\n * 2. Version check → if different, return needsPublish=true (intentional bump)\n * 3. Fast hash check → if identical, return needsPublish=false (no changes)\n * 4. Extract both tarballs, compare file-by-file (excluding package.json)\n * 5. If non-package.json files differ → return needsPublish=true\n * 6. If only package.json differs → do semantic comparison\n */\n\nimport fs from 'fs';\nimport Module from 'module';\nimport path from 'path';\nimport { wrapWorker } from 'tsds-lib';\nimport url from 'url';\nimport { stringStartsWith } from './compat.ts';\nimport type { ChangeDetail, NeedsPublishOptions, NeedsPublishResult, PackageJson } from './types.ts';\n\n// Version check for worker pattern - use worker for Node < 14\nconst major = +process.versions.node.split('.')[0];\nconst version = major > 14 ? 'local' : 'stable';\nconst _require = typeof require === 'undefined' ? Module.createRequire(import.meta.url) : require;\nconst __dirname = path.dirname(typeof __filename === 'undefined' ? url.fileURLToPath(import.meta.url) : __filename);\nconst dist = path.join(__dirname, '..');\nconst workerWrapper = wrapWorker(path.join(dist, 'cjs', 'needs-publish.js'));\n\n/**\n * Callback type for needsPublish\n */\nexport type NeedsPublishCallback = (error: Error | null, result?: NeedsPublishResult) => void;\n\n/**\n * Worker function that runs on Node 14+\n */\nfunction worker(options: NeedsPublishOptions, callback: NeedsPublishCallback): undefined {\n const cwd = options.cwd || process.cwd();\n\n // Load local package.json\n const localPkg: PackageJson = options.package || JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8'));\n\n // Skip private packages\n if (localPkg.private) {\n callback(null, {\n needsPublish: false,\n reason: 'Package is private',\n });\n return;\n }\n\n (async () => {\n const pacote = _require('pacote');\n const Arborist = _require('@npmcli/arborist');\n const npa = _require('npm-package-arg');\n const { execFile } = _require('child_process');\n const { promisify } = _require('util');\n const execFileAsync = promisify(execFile);\n\n // Dynamic import for comparators (they use modern features)\n const { comparePackageFiles, comparePackageJson, extractPackageJson, hashBuffer } = await import('./comparators/index.ts');\n\n // Get registry URL for scoped packages\n let registry: string | undefined = options.registry;\n if (!registry) {\n const scope = stringStartsWith(localPkg.name, '@') ? localPkg.name.split('/')[0] : undefined;\n if (scope) {\n try {\n const { stdout } = await execFileAsync('npm', ['config', 'get', `${scope}:registry`]);\n registry = stdout.trim();\n if (registry === 'undefined') registry = undefined;\n } catch {\n // Fallback to default registry\n }\n }\n }\n\n // Step 1: Try to fetch registry packument\n let registryPkg: PackageJson;\n let registryTarball: Buffer;\n\n try {\n const packument = await pacote.packument(localPkg.name, {\n Arborist,\n ...(registry && { registry }),\n });\n\n const latestVersion = packument['dist-tags']?.latest;\n if (!latestVersion) {\n callback(null, {\n needsPublish: true,\n reason: 'No latest version found in registry (first publish)',\n changes: [{ type: 'first-publish', significance: 'critical' }],\n });\n return;\n }\n\n registryPkg = packument.versions[latestVersion] as unknown as PackageJson;\n\n // Step 2: Version comparison (fast path)\n if (localPkg.version !== latestVersion) {\n callback(null, {\n needsPublish: true,\n reason: `Version differs (local: ${localPkg.version}, registry: ${latestVersion})`,\n changes: [\n {\n type: 'version',\n field: 'version',\n oldValue: latestVersion,\n newValue: localPkg.version,\n significance: 'critical',\n },\n ],\n });\n return;\n }\n\n // Fetch registry tarball for comparison\n const tarballUrl = registryPkg.dist?.tarball;\n if (!tarballUrl) {\n callback(null, {\n needsPublish: true,\n reason: 'Registry package has no tarball URL',\n changes: [{ type: 'first-publish', significance: 'critical' }],\n });\n return;\n }\n\n registryTarball = await pacote.tarball(tarballUrl, {\n Arborist,\n integrity: registryPkg.dist?.integrity,\n });\n } catch (err: unknown) {\n const error = err as { code?: string; message?: string };\n // Package not found in registry (first publish)\n if (error.code === 'E404') {\n callback(null, {\n needsPublish: true,\n reason: 'Package not found in registry (first publish)',\n changes: [{ type: 'first-publish', significance: 'critical' }],\n });\n return;\n }\n // Unknown error - assume changed to be safe\n callback(null, {\n needsPublish: true,\n reason: `Error checking registry: ${error.message || 'Unknown error'}`,\n });\n return;\n }\n\n // Step 3: Pack local package\n let localTarball: Buffer;\n try {\n const spec = npa(cwd);\n const manifest = await pacote.manifest(spec, { Arborist });\n localTarball = await pacote.tarball(manifest._resolved, {\n Arborist,\n integrity: manifest._integrity,\n });\n } catch (err: unknown) {\n const error = err as { message?: string };\n callback(null, {\n needsPublish: true,\n reason: `Error packing local package: ${error.message || 'Unknown error'}`,\n });\n return;\n }\n\n // Step 4: Fast hash comparison\n const localHash = hashBuffer(localTarball);\n const registryHash = hashBuffer(registryTarball);\n\n if (localHash === registryHash) {\n callback(null, {\n needsPublish: false,\n reason: `No changes detected (hash: ${localHash.substring(0, 16)}...)`,\n });\n return;\n }\n\n // Step 5: File-by-file comparison\n const fileComparison = await comparePackageFiles(localTarball, registryTarball);\n\n if (fileComparison.identical) {\n // Hash mismatch but files identical - likely tarball metadata difference\n callback(null, {\n needsPublish: false,\n reason: 'Files identical (tarball metadata differs)',\n });\n return;\n }\n\n // Step 6: If only package.json differs, do semantic comparison\n if (fileComparison.packageJsonOnly && !options.packageJsonOnly) {\n // Extract package.json from both tarballs for accurate comparison\n // (packument metadata is missing fields like 'files')\n const localTarballPkg = (await extractPackageJson(localTarball)) as PackageJson;\n const registryTarballPkg = (await extractPackageJson(registryTarball)) as PackageJson;\n\n const pkgJsonComparison = comparePackageJson(localTarballPkg, registryTarballPkg, {\n includeOptionalDeps: options.includeOptionalDeps,\n additionalSignificantFields: options.additionalSignificantFields,\n ignoreFields: options.ignoreFields,\n treatNarrowingAsEquivalent: options.treatNarrowingAsEquivalent,\n });\n\n if (!pkgJsonComparison.hasSignificantChanges) {\n callback(null, {\n needsPublish: false,\n reason: 'Package.json changes are not significant for consumers',\n changes: pkgJsonComparison.fieldChanges.map((fc) => ({\n type: 'field' as const,\n field: fc.field,\n oldValue: fc.oldValue,\n newValue: fc.newValue,\n significance: 'informational' as const,\n })),\n });\n return;\n }\n\n // Build changes list\n const changes: ChangeDetail[] = [\n ...pkgJsonComparison.fieldChanges.map((fc) => ({\n type: 'field' as const,\n field: fc.field,\n oldValue: fc.oldValue,\n newValue: fc.newValue,\n significance: fc.significance,\n })),\n ...pkgJsonComparison.dependencyChanges\n .filter((dc) => dc.semanticChange !== 'equivalent' && dc.semanticChange !== 'none')\n .map((dc) => ({\n type: 'dependency' as const,\n field: `${dc.type}.${dc.name}`,\n oldValue: dc.oldSpec,\n newValue: dc.newSpec,\n significance: 'significant' as const,\n })),\n ];\n\n callback(null, {\n needsPublish: true,\n reason: pkgJsonComparison.summary,\n changes,\n });\n return;\n }\n\n // Step 7: Other files changed\n callback(null, {\n needsPublish: true,\n reason: `Code changes detected (${fileComparison.fileChanges.length} files changed)`,\n changes: fileComparison.fileChanges.map((fc) => ({\n type: 'file' as const,\n field: fc.path,\n significance: 'significant' as const,\n })),\n });\n })().catch(callback);\n}\n\n/**\n * Callback-based needsPublish - works on Node 0.8+\n * Uses worker for older Node versions, runs locally on Node 14+\n */\nexport function needsPublishCb(options: NeedsPublishOptions, callback: NeedsPublishCallback): void {\n version !== 'local' ? workerWrapper(version, options, callback) : worker(options, callback);\n}\n\n/**\n * Determine if a package needs to be published to npm.\n *\n * @example\n * ```ts\n * import { needsPublish } from 'npm-needs-publish';\n *\n * const result = await needsPublish({ cwd: process.cwd() });\n * if (result.needsPublish) {\n * console.log('Publish needed:', result.reason);\n * }\n * ```\n */\nexport function needsPublish(options: NeedsPublishOptions = {}): Promise<NeedsPublishResult> {\n return new Promise((resolve, reject) => {\n needsPublishCb(options, (error, result) => {\n if (error) reject(error);\n else if (result) resolve(result);\n else reject(new Error('No result returned'));\n });\n });\n}\n"],"names":["fs","Module","path","wrapWorker","url","stringStartsWith","major","process","versions","node","split","version","_require","require","createRequire","__dirname","dirname","__filename","fileURLToPath","dist","join","workerWrapper","worker","options","callback","cwd","localPkg","package","JSON","parse","readFileSync","private","needsPublish","reason","pacote","Arborist","npa","execFile","promisify","execFileAsync","comparePackageFiles","comparePackageJson","extractPackageJson","hashBuffer","registry","scope","name","undefined","stdout","trim","registryPkg","registryTarball","packument","latestVersion","latest","changes","type","significance","field","oldValue","newValue","tarballUrl","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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"}
|