npm-needs-publish 1.0.0 → 1.0.2
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/compat.js +1 -3
- package/dist/cjs/compat.js.map +1 -1
- package/dist/cjs/needs-publish.js +2 -4
- package/dist/cjs/needs-publish.js.map +1 -1
- package/dist/esm/compat.js +1 -3
- package/dist/esm/compat.js.map +1 -1
- package/dist/esm/needs-publish.js +1 -7
- package/dist/esm/needs-publish.js.map +1 -1
- package/package.json +3 -1
package/dist/cjs/compat.js
CHANGED
|
@@ -17,9 +17,7 @@ Object.defineProperty(exports, "stringStartsWith", {
|
|
|
17
17
|
* - Falls back to indexOf on Node 0.12-3.x
|
|
18
18
|
*/ var hasStartsWith = typeof String.prototype.startsWith === 'function';
|
|
19
19
|
function stringStartsWith(str, search, position) {
|
|
20
|
-
if (hasStartsWith)
|
|
21
|
-
return str.startsWith(search, position);
|
|
22
|
-
}
|
|
20
|
+
if (hasStartsWith) return str.startsWith(search, position);
|
|
23
21
|
position = position || 0;
|
|
24
22
|
return str.indexOf(search, position) === position;
|
|
25
23
|
}
|
package/dist/cjs/compat.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/npm/npm-needs-publish/src/compat.ts"],"sourcesContent":["/**\n * Compatibility Layer for Node.js 0.12+\n * Local to this package - contains only needed functions.\n */\n\n/**\n * String.prototype.startsWith wrapper for Node.js 0.12+\n * - Uses native startsWith on Node 4.0+ / ES2015+\n * - Falls back to indexOf on Node 0.12-3.x\n */\nconst hasStartsWith = typeof String.prototype.startsWith === 'function';\
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/npm/npm-needs-publish/src/compat.ts"],"sourcesContent":["/**\n * Compatibility Layer for Node.js 0.12+\n * Local to this package - contains only needed functions.\n */\n\n/**\n * String.prototype.startsWith wrapper for Node.js 0.12+\n * - Uses native startsWith on Node 4.0+ / ES2015+\n * - Falls back to indexOf on Node 0.12-3.x\n */\nconst hasStartsWith = typeof String.prototype.startsWith === 'function';\nexport function stringStartsWith(str: string, search: string, position?: number): boolean {\n if (hasStartsWith) return str.startsWith(search, position);\n position = position || 0;\n return str.indexOf(search, position) === position;\n}\n"],"names":["stringStartsWith","hasStartsWith","String","prototype","startsWith","str","search","position","indexOf"],"mappings":";;;;+BAWgBA;;;eAAAA;;;AAXhB;;;CAGC,GAED;;;;CAIC,GACD,IAAMC,gBAAgB,OAAOC,OAAOC,SAAS,CAACC,UAAU,KAAK;AACtD,SAASJ,iBAAiBK,GAAW,EAAEC,MAAc,EAAEC,QAAiB;IAC7E,IAAIN,eAAe,OAAOI,IAAID,UAAU,CAACE,QAAQC;IACjDA,WAAWA,YAAY;IACvB,OAAOF,IAAIG,OAAO,CAACF,QAAQC,cAAcA;AAC3C"}
|
|
@@ -599,10 +599,8 @@ function needsPublishCb(options, callback) {
|
|
|
599
599
|
function needsPublish() {
|
|
600
600
|
var options = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
|
|
601
601
|
return new Promise(function(resolve, reject) {
|
|
602
|
-
needsPublishCb(options, function(
|
|
603
|
-
|
|
604
|
-
else if (result) resolve(result);
|
|
605
|
-
else reject(new Error('No result returned'));
|
|
602
|
+
return needsPublishCb(options, function(err, result) {
|
|
603
|
+
return err ? reject(err) : resolve(result);
|
|
606
604
|
});
|
|
607
605
|
});
|
|
608
606
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/npm/npm-needs-publish/src/needs-publish.ts"],"sourcesContent":["/**\n * Main orchestration logic for npm-needs-publish\n *\n * Algorithm:\n * 1. Fetch registry packument → if E404, return needsPublish=true (first publish)\n * 2. Version check → if different, return needsPublish=true (intentional bump)\n * 3. Fast hash check → if identical, return needsPublish=false (no changes)\n * 4. Extract both tarballs, compare file-by-file (excluding package.json)\n * 5. If non-package.json files differ → return needsPublish=true\n * 6. If only package.json differs → do semantic comparison\n */\n\nimport fs from 'fs';\nimport Module from 'module';\nimport path from 'path';\nimport { stringStartsWith } from './compat.ts';\nimport type { ChangeDetail, NeedsPublishOptions, NeedsPublishResult, PackageJson } from './types.ts';\n\nconst _require = typeof require === 'undefined' ? Module.createRequire(import.meta.url) : require;\n\n/**\n * Callback type for needsPublish\n */\nexport type NeedsPublishCallback = (error: Error | null, result?: NeedsPublishResult) => void;\n\nfunction needsPublishImpl(options: NeedsPublishOptions, callback: NeedsPublishCallback) {\n const cwd = options.cwd || process.cwd();\n\n // Load local package.json\n const localPkg: PackageJson = options.package || JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8'));\n\n // Skip private packages\n if (localPkg.private)\n return callback(null, {\n needsPublish: false,\n reason: 'Package is private',\n });\n\n (async () => {\n const pacote = _require('pacote');\n const Arborist = _require('@npmcli/arborist');\n const npa = _require('npm-package-arg');\n const { execFile } = _require('child_process');\n const { promisify } = _require('util');\n const execFileAsync = promisify(execFile);\n\n // Dynamic import for comparators (they use modern features)\n const { comparePackageFiles, comparePackageJson, extractPackageJson, hashBuffer } = await import('./comparators/index.ts');\n\n // Get registry URL for scoped packages\n let registry: string | undefined = options.registry;\n if (!registry) {\n const scope = stringStartsWith(localPkg.name, '@') ? localPkg.name.split('/')[0] : undefined;\n if (scope) {\n try {\n const { stdout } = await execFileAsync('npm', ['config', 'get', `${scope}:registry`]);\n registry = stdout.trim();\n if (registry === 'undefined') registry = undefined;\n } catch {\n // Fallback to default registry\n }\n }\n }\n\n // Step 1: Try to fetch registry packument\n let registryPkg: PackageJson;\n let registryTarball: Buffer;\n\n try {\n const packument = await pacote.packument(localPkg.name, {\n Arborist,\n ...(registry && { registry }),\n });\n\n const latestVersion = packument['dist-tags']?.latest;\n if (!latestVersion) {\n callback(null, {\n needsPublish: true,\n reason: 'No latest version found in registry (first publish)',\n changes: [{ type: 'first-publish', significance: 'critical' }],\n });\n return;\n }\n\n registryPkg = packument.versions[latestVersion] as unknown as PackageJson;\n\n // Step 2: Version comparison (fast path)\n if (localPkg.version !== latestVersion) {\n callback(null, {\n needsPublish: true,\n reason: `Version differs (local: ${localPkg.version}, registry: ${latestVersion})`,\n changes: [\n {\n type: 'version',\n field: 'version',\n oldValue: latestVersion,\n newValue: localPkg.version,\n significance: 'critical',\n },\n ],\n });\n return;\n }\n\n // Fetch registry tarball for comparison\n const tarballUrl = registryPkg.dist?.tarball;\n if (!tarballUrl)\n return callback(null, {\n needsPublish: true,\n reason: 'Registry package has no tarball URL',\n changes: [{ type: 'first-publish', significance: 'critical' }],\n });\n\n registryTarball = await pacote.tarball(tarballUrl, {\n Arborist,\n integrity: registryPkg.dist?.integrity,\n });\n } catch (err: unknown) {\n const error = err as { code?: string; message?: string };\n // Package not found in registry (first publish)\n if (error.code === 'E404') {\n callback(null, {\n needsPublish: true,\n reason: 'Package not found in registry (first publish)',\n changes: [{ type: 'first-publish', significance: 'critical' }],\n });\n return;\n }\n // Unknown error - assume changed to be safe\n callback(null, {\n needsPublish: true,\n reason: `Error checking registry: ${error.message || 'Unknown error'}`,\n });\n return;\n }\n\n // Step 3: Pack local package\n let localTarball: Buffer;\n try {\n const spec = npa(cwd);\n const manifest = await pacote.manifest(spec, { Arborist });\n localTarball = await pacote.tarball(manifest._resolved, {\n Arborist,\n integrity: manifest._integrity,\n });\n } catch (err: unknown) {\n const error = err as { message?: string };\n callback(null, {\n needsPublish: true,\n reason: `Error packing local package: ${error.message || 'Unknown error'}`,\n });\n return;\n }\n\n // Step 4: Fast hash comparison\n const localHash = hashBuffer(localTarball);\n const registryHash = hashBuffer(registryTarball);\n\n if (localHash === registryHash) {\n callback(null, {\n needsPublish: false,\n reason: `No changes detected (hash: ${localHash.substring(0, 16)}...)`,\n });\n return;\n }\n\n // Step 5: File-by-file comparison\n const fileComparison = await comparePackageFiles(localTarball, registryTarball);\n\n if (fileComparison.identical) {\n // Hash mismatch but files identical - likely tarball metadata difference\n callback(null, {\n needsPublish: false,\n reason: 'Files identical (tarball metadata differs)',\n });\n return;\n }\n\n // Step 6: If only package.json differs, do semantic comparison\n if (fileComparison.packageJsonOnly && !options.packageJsonOnly) {\n // Extract package.json from both tarballs for accurate comparison\n // (packument metadata is missing fields like 'files')\n const localTarballPkg = (await extractPackageJson(localTarball)) as PackageJson;\n const registryTarballPkg = (await extractPackageJson(registryTarball)) as PackageJson;\n\n const pkgJsonComparison = comparePackageJson(localTarballPkg, registryTarballPkg, {\n includeOptionalDeps: options.includeOptionalDeps,\n additionalSignificantFields: options.additionalSignificantFields,\n ignoreFields: options.ignoreFields,\n treatNarrowingAsEquivalent: options.treatNarrowingAsEquivalent,\n });\n\n if (!pkgJsonComparison.hasSignificantChanges) {\n callback(null, {\n needsPublish: false,\n reason: 'Package.json changes are not significant for consumers',\n changes: pkgJsonComparison.fieldChanges.map((fc) => ({\n type: 'field' as const,\n field: fc.field,\n oldValue: fc.oldValue,\n newValue: fc.newValue,\n significance: 'informational' as const,\n })),\n });\n return;\n }\n\n // Build changes list\n const changes: ChangeDetail[] = [\n ...pkgJsonComparison.fieldChanges.map((fc) => ({\n type: 'field' as const,\n field: fc.field,\n oldValue: fc.oldValue,\n newValue: fc.newValue,\n significance: fc.significance,\n })),\n ...pkgJsonComparison.dependencyChanges\n .filter((dc) => dc.semanticChange !== 'equivalent' && dc.semanticChange !== 'none')\n .map((dc) => ({\n type: 'dependency' as const,\n field: `${dc.type}.${dc.name}`,\n oldValue: dc.oldSpec,\n newValue: dc.newSpec,\n significance: 'significant' as const,\n })),\n ];\n\n callback(null, {\n needsPublish: true,\n reason: pkgJsonComparison.summary,\n changes,\n });\n return;\n }\n\n // Step 7: Other files changed\n callback(null, {\n needsPublish: true,\n reason: `Code changes detected (${fileComparison.fileChanges.length} files changed)`,\n changes: fileComparison.fileChanges.map((fc) => ({\n type: 'file' as const,\n field: fc.path,\n significance: 'significant' as const,\n })),\n });\n })().catch(callback);\n}\n\n/**\n * Callback-based needsPublish\n */\nexport function needsPublishCb(options: NeedsPublishOptions, callback: NeedsPublishCallback) {\n needsPublishImpl(options, callback);\n}\n\n/**\n * Determine if a package needs to be published to npm.\n *\n * @example\n * ```ts\n * import { needsPublish } from 'npm-needs-publish';\n *\n * const result = await needsPublish({ cwd: process.cwd() });\n * if (result.needsPublish) {\n * console.log('Publish needed:', result.reason);\n * }\n * ```\n */\nexport function needsPublish(options: NeedsPublishOptions = {}): Promise<NeedsPublishResult> {\n return new Promise((resolve, reject) => {\n needsPublishCb(options, (error, result) => {\n if (error) reject(error);\n else if (result) resolve(result);\n else reject(new Error('No result returned'));\n });\n });\n}\n"],"names":["needsPublish","needsPublishCb","_require","require","Module","createRequire","needsPublishImpl","options","callback","cwd","process","localPkg","package","JSON","parse","fs","readFileSync","path","join","private","reason","pacote","Arborist","npa","execFile","promisify","execFileAsync","comparePackageFiles","comparePackageJson","extractPackageJson","hashBuffer","registry","scope","stdout","registryPkg","registryTarball","packument","latestVersion","tarballUrl","err","error","localTarball","spec","manifest","localHash","registryHash","fileComparison","localTarballPkg","registryTarballPkg","pkgJsonComparison","changes","stringStartsWith","name","split","undefined","trim","latest","type","significance","versions","version","field","oldValue","newValue","dist","tarball","integrity","code","message","_resolved","_integrity","substring","identical","packageJsonOnly","includeOptionalDeps","additionalSignificantFields","ignoreFields","treatNarrowingAsEquivalent","hasSignificantChanges","fieldChanges","map","fc","dependencyChanges","filter","dc","semanticChange","oldSpec","newSpec","summary","fileChanges","length","catch","Promise","resolve","reject","result","Error"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/npm/npm-needs-publish/src/needs-publish.ts"],"sourcesContent":["/**\n * Main orchestration logic for npm-needs-publish\n *\n * Algorithm:\n * 1. Fetch registry packument → if E404, return needsPublish=true (first publish)\n * 2. Version check → if different, return needsPublish=true (intentional bump)\n * 3. Fast hash check → if identical, return needsPublish=false (no changes)\n * 4. Extract both tarballs, compare file-by-file (excluding package.json)\n * 5. If non-package.json files differ → return needsPublish=true\n * 6. If only package.json differs → do semantic comparison\n */\n\nimport fs from 'fs';\nimport Module from 'module';\nimport path from 'path';\nimport { stringStartsWith } from './compat.ts';\nimport type { ChangeDetail, NeedsPublishOptions, NeedsPublishResult, PackageJson } from './types.ts';\n\nconst _require = typeof require === 'undefined' ? Module.createRequire(import.meta.url) : require;\n\n/**\n * Callback type for needsPublish\n */\nexport type NeedsPublishCallback = (error: Error | null, result?: NeedsPublishResult) => void;\n\nfunction needsPublishImpl(options: NeedsPublishOptions, callback: NeedsPublishCallback) {\n const cwd = options.cwd || process.cwd();\n\n // Load local package.json\n const localPkg: PackageJson = options.package || JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8'));\n\n // Skip private packages\n if (localPkg.private)\n return callback(null, {\n needsPublish: false,\n reason: 'Package is private',\n });\n\n (async () => {\n const pacote = _require('pacote');\n const Arborist = _require('@npmcli/arborist');\n const npa = _require('npm-package-arg');\n const { execFile } = _require('child_process');\n const { promisify } = _require('util');\n const execFileAsync = promisify(execFile);\n\n // Dynamic import for comparators (they use modern features)\n const { comparePackageFiles, comparePackageJson, extractPackageJson, hashBuffer } = await import('./comparators/index.ts');\n\n // Get registry URL for scoped packages\n let registry: string | undefined = options.registry;\n if (!registry) {\n const scope = stringStartsWith(localPkg.name, '@') ? localPkg.name.split('/')[0] : undefined;\n if (scope) {\n try {\n const { stdout } = await execFileAsync('npm', ['config', 'get', `${scope}:registry`]);\n registry = stdout.trim();\n if (registry === 'undefined') registry = undefined;\n } catch {\n // Fallback to default registry\n }\n }\n }\n\n // Step 1: Try to fetch registry packument\n let registryPkg: PackageJson;\n let registryTarball: Buffer;\n\n try {\n const packument = await pacote.packument(localPkg.name, {\n Arborist,\n ...(registry && { registry }),\n });\n\n const latestVersion = packument['dist-tags']?.latest;\n if (!latestVersion) {\n callback(null, {\n needsPublish: true,\n reason: 'No latest version found in registry (first publish)',\n changes: [{ type: 'first-publish', significance: 'critical' }],\n });\n return;\n }\n\n registryPkg = packument.versions[latestVersion] as unknown as PackageJson;\n\n // Step 2: Version comparison (fast path)\n if (localPkg.version !== latestVersion) {\n callback(null, {\n needsPublish: true,\n reason: `Version differs (local: ${localPkg.version}, registry: ${latestVersion})`,\n changes: [\n {\n type: 'version',\n field: 'version',\n oldValue: latestVersion,\n newValue: localPkg.version,\n significance: 'critical',\n },\n ],\n });\n return;\n }\n\n // Fetch registry tarball for comparison\n const tarballUrl = registryPkg.dist?.tarball;\n if (!tarballUrl)\n return callback(null, {\n needsPublish: true,\n reason: 'Registry package has no tarball URL',\n changes: [{ type: 'first-publish', significance: 'critical' }],\n });\n\n registryTarball = await pacote.tarball(tarballUrl, {\n Arborist,\n integrity: registryPkg.dist?.integrity,\n });\n } catch (err: unknown) {\n const error = err as { code?: string; message?: string };\n // Package not found in registry (first publish)\n if (error.code === 'E404') {\n callback(null, {\n needsPublish: true,\n reason: 'Package not found in registry (first publish)',\n changes: [{ type: 'first-publish', significance: 'critical' }],\n });\n return;\n }\n // Unknown error - assume changed to be safe\n callback(null, {\n needsPublish: true,\n reason: `Error checking registry: ${error.message || 'Unknown error'}`,\n });\n return;\n }\n\n // Step 3: Pack local package\n let localTarball: Buffer;\n try {\n const spec = npa(cwd);\n const manifest = await pacote.manifest(spec, { Arborist });\n localTarball = await pacote.tarball(manifest._resolved, {\n Arborist,\n integrity: manifest._integrity,\n });\n } catch (err: unknown) {\n const error = err as { message?: string };\n callback(null, {\n needsPublish: true,\n reason: `Error packing local package: ${error.message || 'Unknown error'}`,\n });\n return;\n }\n\n // Step 4: Fast hash comparison\n const localHash = hashBuffer(localTarball);\n const registryHash = hashBuffer(registryTarball);\n\n if (localHash === registryHash) {\n callback(null, {\n needsPublish: false,\n reason: `No changes detected (hash: ${localHash.substring(0, 16)}...)`,\n });\n return;\n }\n\n // Step 5: File-by-file comparison\n const fileComparison = await comparePackageFiles(localTarball, registryTarball);\n\n if (fileComparison.identical) {\n // Hash mismatch but files identical - likely tarball metadata difference\n callback(null, {\n needsPublish: false,\n reason: 'Files identical (tarball metadata differs)',\n });\n return;\n }\n\n // Step 6: If only package.json differs, do semantic comparison\n if (fileComparison.packageJsonOnly && !options.packageJsonOnly) {\n // Extract package.json from both tarballs for accurate comparison\n // (packument metadata is missing fields like 'files')\n const localTarballPkg = (await extractPackageJson(localTarball)) as PackageJson;\n const registryTarballPkg = (await extractPackageJson(registryTarball)) as PackageJson;\n\n const pkgJsonComparison = comparePackageJson(localTarballPkg, registryTarballPkg, {\n includeOptionalDeps: options.includeOptionalDeps,\n additionalSignificantFields: options.additionalSignificantFields,\n ignoreFields: options.ignoreFields,\n treatNarrowingAsEquivalent: options.treatNarrowingAsEquivalent,\n });\n\n if (!pkgJsonComparison.hasSignificantChanges) {\n callback(null, {\n needsPublish: false,\n reason: 'Package.json changes are not significant for consumers',\n changes: pkgJsonComparison.fieldChanges.map((fc) => ({\n type: 'field' as const,\n field: fc.field,\n oldValue: fc.oldValue,\n newValue: fc.newValue,\n significance: 'informational' as const,\n })),\n });\n return;\n }\n\n // Build changes list\n const changes: ChangeDetail[] = [\n ...pkgJsonComparison.fieldChanges.map((fc) => ({\n type: 'field' as const,\n field: fc.field,\n oldValue: fc.oldValue,\n newValue: fc.newValue,\n significance: fc.significance,\n })),\n ...pkgJsonComparison.dependencyChanges\n .filter((dc) => dc.semanticChange !== 'equivalent' && dc.semanticChange !== 'none')\n .map((dc) => ({\n type: 'dependency' as const,\n field: `${dc.type}.${dc.name}`,\n oldValue: dc.oldSpec,\n newValue: dc.newSpec,\n significance: 'significant' as const,\n })),\n ];\n\n callback(null, {\n needsPublish: true,\n reason: pkgJsonComparison.summary,\n changes,\n });\n return;\n }\n\n // Step 7: Other files changed\n callback(null, {\n needsPublish: true,\n reason: `Code changes detected (${fileComparison.fileChanges.length} files changed)`,\n changes: fileComparison.fileChanges.map((fc) => ({\n type: 'file' as const,\n field: fc.path,\n significance: 'significant' as const,\n })),\n });\n })().catch(callback);\n}\n\n/**\n * Callback-based needsPublish\n */\nexport function needsPublishCb(options: NeedsPublishOptions, callback: NeedsPublishCallback) {\n needsPublishImpl(options, callback);\n}\n\n/**\n * Determine if a package needs to be published to npm.\n *\n * @example\n * ```ts\n * import { needsPublish } from 'npm-needs-publish';\n *\n * const result = await needsPublish({ cwd: process.cwd() });\n * if (result.needsPublish) {\n * console.log('Publish needed:', result.reason);\n * }\n * ```\n */\nexport function needsPublish(options: NeedsPublishOptions = {}): Promise<NeedsPublishResult> {\n return new Promise((resolve, reject) => needsPublishCb(options, (err, result) => (err ? reject(err) : resolve(result))));\n}\n"],"names":["needsPublish","needsPublishCb","_require","require","Module","createRequire","needsPublishImpl","options","callback","cwd","process","localPkg","package","JSON","parse","fs","readFileSync","path","join","private","reason","pacote","Arborist","npa","execFile","promisify","execFileAsync","comparePackageFiles","comparePackageJson","extractPackageJson","hashBuffer","registry","scope","stdout","registryPkg","registryTarball","packument","latestVersion","tarballUrl","err","error","localTarball","spec","manifest","localHash","registryHash","fileComparison","localTarballPkg","registryTarballPkg","pkgJsonComparison","changes","stringStartsWith","name","split","undefined","trim","latest","type","significance","versions","version","field","oldValue","newValue","dist","tarball","integrity","code","message","_resolved","_integrity","substring","identical","packageJsonOnly","includeOptionalDeps","additionalSignificantFields","ignoreFields","treatNarrowingAsEquivalent","hasSignificantChanges","fieldChanges","map","fc","dependencyChanges","filter","dc","semanticChange","oldSpec","newSpec","summary","fileChanges","length","catch","Promise","resolve","reject","result"],"mappings":"AAAA;;;;;;;;;;CAUC;;;;;;;;;;;QAkQeA;eAAAA;;QAjBAC;eAAAA;;;yDA/OD;6DACI;2DACF;wBACgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAGjC,IAAMC,WAAW,OAAOC,YAAY,cAAcC,eAAM,CAACC,aAAa,CAAC,uDAAmBF;AAO1F,SAASG,iBAAiBC,OAA4B,EAAEC,QAA8B;IACpF,IAAMC,MAAMF,QAAQE,GAAG,IAAIC,QAAQD,GAAG;IAEtC,0BAA0B;IAC1B,IAAME,WAAwBJ,QAAQK,OAAO,IAAIC,KAAKC,KAAK,CAACC,WAAE,CAACC,YAAY,CAACC,aAAI,CAACC,IAAI,CAACT,KAAK,iBAAiB;IAE5G,wBAAwB;IACxB,IAAIE,SAASQ,OAAO,EAClB,OAAOX,SAAS,MAAM;QACpBR,cAAc;QACdoB,QAAQ;IACV;IAED,CAAA;;gBACOC,QACAC,UACAC,KACEC,UACAC,WACFC,eAG8E,MAA5EC,qBAAqBC,oBAAoBC,oBAAoBC,YAGjEC,UAEIC,OAGMC,gBAUVC,aACAC,iBAQoBC,qBA+BHF,mBAUNA,oBA9CPE,WAKAC,eA+BAC,YAYCC,KACDC,OAmBJC,cAEIC,MACAC,UAKCJ,MACDC,QASFI,WACAC,cAWAC,gBAeEC,iBACAC,oBAEAC,mBAuBAC;;;;wBAzKF7B,SAASnB,SAAS;wBAClBoB,WAAWpB,SAAS;wBACpBqB,MAAMrB,SAAS;wBACbsB,WAAatB,SAAS,iBAAtBsB;wBACAC,YAAcvB,SAAS,QAAvBuB;wBACFC,gBAAgBD,UAAUD;wBAGoD;;4BAAM;+EAAA,QAAO;;;;wBAAb,OAAA,eAA5EG,sBAA4E,KAA5EA,qBAAqBC,qBAAuD,KAAvDA,oBAAoBC,qBAAmC,KAAnCA,oBAAoBC,aAAe,KAAfA;wBAErE,uCAAuC;wBACnCC,WAA+BxB,QAAQwB,QAAQ;6BAC/C,CAACA,UAAD;;;;wBACIC,QAAQmB,IAAAA,0BAAgB,EAACxC,SAASyC,IAAI,EAAE,OAAOzC,SAASyC,IAAI,CAACC,KAAK,CAAC,IAAI,CAAC,EAAE,GAAGC;6BAC/EtB,OAAAA;;;;;;;;;;;;wBAEmB;;4BAAMN,cAAc;gCAAQ;gCAAU;gCAAQ,GAAQ,OAANM,OAAM;;;;wBAAjEC,SAAW,cAAXA;wBACRF,WAAWE,OAAOsB,IAAI;wBACtB,IAAIxB,aAAa,aAAaA,WAAWuB;;;;;;;;;;;;;;;;;;wBAY3B;;4BAAMjC,OAAOe,SAAS,CAACzB,SAASyC,IAAI,EAAE;gCACtD9B,UAAAA;+BACIS,YAAY;gCAAEA,UAAAA;4BAAS;;;wBAFvBK,YAAY;wBAKZC,iBAAgBD,sBAAAA,SAAS,CAAC,YAAY,cAAtBA,0CAAAA,oBAAwBoB,MAAM;wBACpD,IAAI,CAACnB,eAAe;4BAClB7B,SAAS,MAAM;gCACbR,cAAc;gCACdoB,QAAQ;gCACR8B,OAAO;oCAAG;wCAAEO,MAAM;wCAAiBC,cAAc;oCAAW;;4BAC9D;4BACA;;;wBACF;wBAEAxB,cAAcE,UAAUuB,QAAQ,CAACtB,cAAc;wBAE/C,yCAAyC;wBACzC,IAAI1B,SAASiD,OAAO,KAAKvB,eAAe;4BACtC7B,SAAS,MAAM;gCACbR,cAAc;gCACdoB,QAAQ,AAAC,2BAAyDiB,OAA/B1B,SAASiD,OAAO,EAAC,gBAA4B,OAAdvB,eAAc;gCAChFa,OAAO;oCACL;wCACEO,MAAM;wCACNI,OAAO;wCACPC,UAAUzB;wCACV0B,UAAUpD,SAASiD,OAAO;wCAC1BF,cAAc;oCAChB;;4BAEJ;4BACA;;;wBACF;wBAEA,wCAAwC;wBAClCpB,cAAaJ,oBAAAA,YAAY8B,IAAI,cAAhB9B,wCAAAA,kBAAkB+B,OAAO;wBAC5C,IAAI,CAAC3B,YACH;;4BAAO9B,SAAS,MAAM;gCACpBR,cAAc;gCACdoB,QAAQ;gCACR8B,OAAO;oCAAG;wCAAEO,MAAM;wCAAiBC,cAAc;oCAAW;;4BAC9D;;wBAEgB;;4BAAMrC,OAAO4C,OAAO,CAAC3B,YAAY;gCACjDhB,UAAAA;gCACA4C,SAAS,GAAEhC,qBAAAA,YAAY8B,IAAI,cAAhB9B,yCAAAA,mBAAkBgC,SAAS;4BACxC;;;wBAHA/B,kBAAkB;;;;;;wBAIXI;wBACDC,QAAQD;wBACd,gDAAgD;wBAChD,IAAIC,MAAM2B,IAAI,KAAK,QAAQ;4BACzB3D,SAAS,MAAM;gCACbR,cAAc;gCACdoB,QAAQ;gCACR8B,OAAO;oCAAG;wCAAEO,MAAM;wCAAiBC,cAAc;oCAAW;;4BAC9D;4BACA;;;wBACF;wBACA,4CAA4C;wBAC5ClD,SAAS,MAAM;4BACbR,cAAc;4BACdoB,QAAQ,AAAC,4BAA4D,OAAjCoB,MAAM4B,OAAO,IAAI;wBACvD;wBACA;;;;;;;;;;wBAMM1B,OAAOnB,IAAId;wBACA;;4BAAMY,OAAOsB,QAAQ,CAACD,MAAM;gCAAEpB,UAAAA;4BAAS;;;wBAAlDqB,WAAW;wBACF;;4BAAMtB,OAAO4C,OAAO,CAACtB,SAAS0B,SAAS,EAAE;gCACtD/C,UAAAA;gCACA4C,WAAWvB,SAAS2B,UAAU;4BAChC;;;wBAHA7B,eAAe;;;;;;wBAIRF;wBACDC,SAAQD;wBACd/B,SAAS,MAAM;4BACbR,cAAc;4BACdoB,QAAQ,AAAC,gCAAgE,OAAjCoB,OAAM4B,OAAO,IAAI;wBAC3D;wBACA;;;;wBAGF,+BAA+B;wBACzBxB,YAAYd,WAAWW;wBACvBI,eAAef,WAAWK;wBAEhC,IAAIS,cAAcC,cAAc;4BAC9BrC,SAAS,MAAM;gCACbR,cAAc;gCACdoB,QAAQ,AAAC,8BAAwD,OAA3BwB,UAAU2B,SAAS,CAAC,GAAG,KAAI;4BACnE;4BACA;;;wBACF;wBAGuB;;4BAAM5C,oBAAoBc,cAAcN;;;wBAAzDW,iBAAiB;wBAEvB,IAAIA,eAAe0B,SAAS,EAAE;4BAC5B,yEAAyE;4BACzEhE,SAAS,MAAM;gCACbR,cAAc;gCACdoB,QAAQ;4BACV;4BACA;;;wBACF;6BAGI0B,CAAAA,eAAe2B,eAAe,IAAI,CAAClE,QAAQkE,eAAe,AAAD,GAAzD3B;;;;wBAGuB;;4BAAMjB,mBAAmBY;;;wBAA5CM,kBAAmB;wBACG;;4BAAMlB,mBAAmBM;;;wBAA/Ca,qBAAsB;wBAEtBC,oBAAoBrB,mBAAmBmB,iBAAiBC,oBAAoB;4BAChF0B,qBAAqBnE,QAAQmE,mBAAmB;4BAChDC,6BAA6BpE,QAAQoE,2BAA2B;4BAChEC,cAAcrE,QAAQqE,YAAY;4BAClCC,4BAA4BtE,QAAQsE,0BAA0B;wBAChE;wBAEA,IAAI,CAAC5B,kBAAkB6B,qBAAqB,EAAE;4BAC5CtE,SAAS,MAAM;gCACbR,cAAc;gCACdoB,QAAQ;gCACR8B,SAASD,kBAAkB8B,YAAY,CAACC,GAAG,CAAC,SAACC;2CAAQ;wCACnDxB,MAAM;wCACNI,OAAOoB,GAAGpB,KAAK;wCACfC,UAAUmB,GAAGnB,QAAQ;wCACrBC,UAAUkB,GAAGlB,QAAQ;wCACrBL,cAAc;oCAChB;;4BACF;4BACA;;;wBACF;wBAEA,qBAAqB;wBACfR,UAA0B,AAC9B,qBAAGD,kBAAkB8B,YAAY,CAACC,GAAG,CAAC,SAACC;mCAAQ;gCAC7CxB,MAAM;gCACNI,OAAOoB,GAAGpB,KAAK;gCACfC,UAAUmB,GAAGnB,QAAQ;gCACrBC,UAAUkB,GAAGlB,QAAQ;gCACrBL,cAAcuB,GAAGvB,YAAY;4BAC/B;mCACA,qBAAGT,kBAAkBiC,iBAAiB,CACnCC,MAAM,CAAC,SAACC;mCAAOA,GAAGC,cAAc,KAAK,gBAAgBD,GAAGC,cAAc,KAAK;2BAC3EL,GAAG,CAAC,SAACI;mCAAQ;gCACZ3B,MAAM;gCACNI,OAAO,AAAC,GAAauB,OAAXA,GAAG3B,IAAI,EAAC,KAAW,OAAR2B,GAAGhC,IAAI;gCAC5BU,UAAUsB,GAAGE,OAAO;gCACpBvB,UAAUqB,GAAGG,OAAO;gCACpB7B,cAAc;4BAChB;;wBAGJlD,SAAS,MAAM;4BACbR,cAAc;4BACdoB,QAAQ6B,kBAAkBuC,OAAO;4BACjCtC,SAAAA;wBACF;wBACA;;;;wBAGF,8BAA8B;wBAC9B1C,SAAS,MAAM;4BACbR,cAAc;4BACdoB,QAAQ,AAAC,0BAA2D,OAAlC0B,eAAe2C,WAAW,CAACC,MAAM,EAAC;4BACpExC,SAASJ,eAAe2C,WAAW,CAACT,GAAG,CAAC,SAACC;uCAAQ;oCAC/CxB,MAAM;oCACNI,OAAOoB,GAAGhE,IAAI;oCACdyC,cAAc;gCAChB;;wBACF;;;;;;QACF;KAAA,IAAKiC,KAAK,CAACnF;AACb;AAKO,SAASP,eAAeM,OAA4B,EAAEC,QAA8B;IACzFF,iBAAiBC,SAASC;AAC5B;AAeO,SAASR;QAAaO,UAAAA,iEAA+B,CAAC;IAC3D,OAAO,IAAIqF,QAAQ,SAACC,SAASC;eAAW7F,eAAeM,SAAS,SAACgC,KAAKwD;mBAAYxD,MAAMuD,OAAOvD,OAAOsD,QAAQE;;;AAChH"}
|
package/dist/esm/compat.js
CHANGED
|
@@ -7,9 +7,7 @@
|
|
|
7
7
|
* - Falls back to indexOf on Node 0.12-3.x
|
|
8
8
|
*/ const hasStartsWith = typeof String.prototype.startsWith === 'function';
|
|
9
9
|
export function stringStartsWith(str, search, position) {
|
|
10
|
-
if (hasStartsWith)
|
|
11
|
-
return str.startsWith(search, position);
|
|
12
|
-
}
|
|
10
|
+
if (hasStartsWith) return str.startsWith(search, position);
|
|
13
11
|
position = position || 0;
|
|
14
12
|
return str.indexOf(search, position) === position;
|
|
15
13
|
}
|
package/dist/esm/compat.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/npm/npm-needs-publish/src/compat.ts"],"sourcesContent":["/**\n * Compatibility Layer for Node.js 0.12+\n * Local to this package - contains only needed functions.\n */\n\n/**\n * String.prototype.startsWith wrapper for Node.js 0.12+\n * - Uses native startsWith on Node 4.0+ / ES2015+\n * - Falls back to indexOf on Node 0.12-3.x\n */\nconst hasStartsWith = typeof String.prototype.startsWith === 'function';\
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/npm/npm-needs-publish/src/compat.ts"],"sourcesContent":["/**\n * Compatibility Layer for Node.js 0.12+\n * Local to this package - contains only needed functions.\n */\n\n/**\n * String.prototype.startsWith wrapper for Node.js 0.12+\n * - Uses native startsWith on Node 4.0+ / ES2015+\n * - Falls back to indexOf on Node 0.12-3.x\n */\nconst hasStartsWith = typeof String.prototype.startsWith === 'function';\nexport function stringStartsWith(str: string, search: string, position?: number): boolean {\n if (hasStartsWith) return str.startsWith(search, position);\n position = position || 0;\n return str.indexOf(search, position) === position;\n}\n"],"names":["hasStartsWith","String","prototype","startsWith","stringStartsWith","str","search","position","indexOf"],"mappings":"AAAA;;;CAGC,GAED;;;;CAIC,GACD,MAAMA,gBAAgB,OAAOC,OAAOC,SAAS,CAACC,UAAU,KAAK;AAC7D,OAAO,SAASC,iBAAiBC,GAAW,EAAEC,MAAc,EAAEC,QAAiB;IAC7E,IAAIP,eAAe,OAAOK,IAAIF,UAAU,CAACG,QAAQC;IACjDA,WAAWA,YAAY;IACvB,OAAOF,IAAIG,OAAO,CAACF,QAAQC,cAAcA;AAC3C"}
|
|
@@ -250,11 +250,5 @@ function needsPublishImpl(options, callback) {
|
|
|
250
250
|
* }
|
|
251
251
|
* ```
|
|
252
252
|
*/ export function needsPublish(options = {}) {
|
|
253
|
-
return new Promise((resolve, reject)=>
|
|
254
|
-
needsPublishCb(options, (error, result)=>{
|
|
255
|
-
if (error) reject(error);
|
|
256
|
-
else if (result) resolve(result);
|
|
257
|
-
else reject(new Error('No result returned'));
|
|
258
|
-
});
|
|
259
|
-
});
|
|
253
|
+
return new Promise((resolve, reject)=>needsPublishCb(options, (err, result)=>err ? reject(err) : resolve(result)));
|
|
260
254
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/npm/npm-needs-publish/src/needs-publish.ts"],"sourcesContent":["/**\n * Main orchestration logic for npm-needs-publish\n *\n * Algorithm:\n * 1. Fetch registry packument → if E404, return needsPublish=true (first publish)\n * 2. Version check → if different, return needsPublish=true (intentional bump)\n * 3. Fast hash check → if identical, return needsPublish=false (no changes)\n * 4. Extract both tarballs, compare file-by-file (excluding package.json)\n * 5. If non-package.json files differ → return needsPublish=true\n * 6. If only package.json differs → do semantic comparison\n */\n\nimport fs from 'fs';\nimport Module from 'module';\nimport path from 'path';\nimport { stringStartsWith } from './compat.ts';\nimport type { ChangeDetail, NeedsPublishOptions, NeedsPublishResult, PackageJson } from './types.ts';\n\nconst _require = typeof require === 'undefined' ? Module.createRequire(import.meta.url) : require;\n\n/**\n * Callback type for needsPublish\n */\nexport type NeedsPublishCallback = (error: Error | null, result?: NeedsPublishResult) => void;\n\nfunction needsPublishImpl(options: NeedsPublishOptions, callback: NeedsPublishCallback) {\n const cwd = options.cwd || process.cwd();\n\n // Load local package.json\n const localPkg: PackageJson = options.package || JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8'));\n\n // Skip private packages\n if (localPkg.private)\n return callback(null, {\n needsPublish: false,\n reason: 'Package is private',\n });\n\n (async () => {\n const pacote = _require('pacote');\n const Arborist = _require('@npmcli/arborist');\n const npa = _require('npm-package-arg');\n const { execFile } = _require('child_process');\n const { promisify } = _require('util');\n const execFileAsync = promisify(execFile);\n\n // Dynamic import for comparators (they use modern features)\n const { comparePackageFiles, comparePackageJson, extractPackageJson, hashBuffer } = await import('./comparators/index.ts');\n\n // Get registry URL for scoped packages\n let registry: string | undefined = options.registry;\n if (!registry) {\n const scope = stringStartsWith(localPkg.name, '@') ? localPkg.name.split('/')[0] : undefined;\n if (scope) {\n try {\n const { stdout } = await execFileAsync('npm', ['config', 'get', `${scope}:registry`]);\n registry = stdout.trim();\n if (registry === 'undefined') registry = undefined;\n } catch {\n // Fallback to default registry\n }\n }\n }\n\n // Step 1: Try to fetch registry packument\n let registryPkg: PackageJson;\n let registryTarball: Buffer;\n\n try {\n const packument = await pacote.packument(localPkg.name, {\n Arborist,\n ...(registry && { registry }),\n });\n\n const latestVersion = packument['dist-tags']?.latest;\n if (!latestVersion) {\n callback(null, {\n needsPublish: true,\n reason: 'No latest version found in registry (first publish)',\n changes: [{ type: 'first-publish', significance: 'critical' }],\n });\n return;\n }\n\n registryPkg = packument.versions[latestVersion] as unknown as PackageJson;\n\n // Step 2: Version comparison (fast path)\n if (localPkg.version !== latestVersion) {\n callback(null, {\n needsPublish: true,\n reason: `Version differs (local: ${localPkg.version}, registry: ${latestVersion})`,\n changes: [\n {\n type: 'version',\n field: 'version',\n oldValue: latestVersion,\n newValue: localPkg.version,\n significance: 'critical',\n },\n ],\n });\n return;\n }\n\n // Fetch registry tarball for comparison\n const tarballUrl = registryPkg.dist?.tarball;\n if (!tarballUrl)\n return callback(null, {\n needsPublish: true,\n reason: 'Registry package has no tarball URL',\n changes: [{ type: 'first-publish', significance: 'critical' }],\n });\n\n registryTarball = await pacote.tarball(tarballUrl, {\n Arborist,\n integrity: registryPkg.dist?.integrity,\n });\n } catch (err: unknown) {\n const error = err as { code?: string; message?: string };\n // Package not found in registry (first publish)\n if (error.code === 'E404') {\n callback(null, {\n needsPublish: true,\n reason: 'Package not found in registry (first publish)',\n changes: [{ type: 'first-publish', significance: 'critical' }],\n });\n return;\n }\n // Unknown error - assume changed to be safe\n callback(null, {\n needsPublish: true,\n reason: `Error checking registry: ${error.message || 'Unknown error'}`,\n });\n return;\n }\n\n // Step 3: Pack local package\n let localTarball: Buffer;\n try {\n const spec = npa(cwd);\n const manifest = await pacote.manifest(spec, { Arborist });\n localTarball = await pacote.tarball(manifest._resolved, {\n Arborist,\n integrity: manifest._integrity,\n });\n } catch (err: unknown) {\n const error = err as { message?: string };\n callback(null, {\n needsPublish: true,\n reason: `Error packing local package: ${error.message || 'Unknown error'}`,\n });\n return;\n }\n\n // Step 4: Fast hash comparison\n const localHash = hashBuffer(localTarball);\n const registryHash = hashBuffer(registryTarball);\n\n if (localHash === registryHash) {\n callback(null, {\n needsPublish: false,\n reason: `No changes detected (hash: ${localHash.substring(0, 16)}...)`,\n });\n return;\n }\n\n // Step 5: File-by-file comparison\n const fileComparison = await comparePackageFiles(localTarball, registryTarball);\n\n if (fileComparison.identical) {\n // Hash mismatch but files identical - likely tarball metadata difference\n callback(null, {\n needsPublish: false,\n reason: 'Files identical (tarball metadata differs)',\n });\n return;\n }\n\n // Step 6: If only package.json differs, do semantic comparison\n if (fileComparison.packageJsonOnly && !options.packageJsonOnly) {\n // Extract package.json from both tarballs for accurate comparison\n // (packument metadata is missing fields like 'files')\n const localTarballPkg = (await extractPackageJson(localTarball)) as PackageJson;\n const registryTarballPkg = (await extractPackageJson(registryTarball)) as PackageJson;\n\n const pkgJsonComparison = comparePackageJson(localTarballPkg, registryTarballPkg, {\n includeOptionalDeps: options.includeOptionalDeps,\n additionalSignificantFields: options.additionalSignificantFields,\n ignoreFields: options.ignoreFields,\n treatNarrowingAsEquivalent: options.treatNarrowingAsEquivalent,\n });\n\n if (!pkgJsonComparison.hasSignificantChanges) {\n callback(null, {\n needsPublish: false,\n reason: 'Package.json changes are not significant for consumers',\n changes: pkgJsonComparison.fieldChanges.map((fc) => ({\n type: 'field' as const,\n field: fc.field,\n oldValue: fc.oldValue,\n newValue: fc.newValue,\n significance: 'informational' as const,\n })),\n });\n return;\n }\n\n // Build changes list\n const changes: ChangeDetail[] = [\n ...pkgJsonComparison.fieldChanges.map((fc) => ({\n type: 'field' as const,\n field: fc.field,\n oldValue: fc.oldValue,\n newValue: fc.newValue,\n significance: fc.significance,\n })),\n ...pkgJsonComparison.dependencyChanges\n .filter((dc) => dc.semanticChange !== 'equivalent' && dc.semanticChange !== 'none')\n .map((dc) => ({\n type: 'dependency' as const,\n field: `${dc.type}.${dc.name}`,\n oldValue: dc.oldSpec,\n newValue: dc.newSpec,\n significance: 'significant' as const,\n })),\n ];\n\n callback(null, {\n needsPublish: true,\n reason: pkgJsonComparison.summary,\n changes,\n });\n return;\n }\n\n // Step 7: Other files changed\n callback(null, {\n needsPublish: true,\n reason: `Code changes detected (${fileComparison.fileChanges.length} files changed)`,\n changes: fileComparison.fileChanges.map((fc) => ({\n type: 'file' as const,\n field: fc.path,\n significance: 'significant' as const,\n })),\n });\n })().catch(callback);\n}\n\n/**\n * Callback-based needsPublish\n */\nexport function needsPublishCb(options: NeedsPublishOptions, callback: NeedsPublishCallback) {\n needsPublishImpl(options, callback);\n}\n\n/**\n * Determine if a package needs to be published to npm.\n *\n * @example\n * ```ts\n * import { needsPublish } from 'npm-needs-publish';\n *\n * const result = await needsPublish({ cwd: process.cwd() });\n * if (result.needsPublish) {\n * console.log('Publish needed:', result.reason);\n * }\n * ```\n */\nexport function needsPublish(options: NeedsPublishOptions = {}): Promise<NeedsPublishResult> {\n return new Promise((resolve, reject) => {\n needsPublishCb(options, (error, result) => {\n if (error) reject(error);\n else if (result) resolve(result);\n else reject(new Error('No result returned'));\n });\n });\n}\n"],"names":["fs","Module","path","stringStartsWith","_require","require","createRequire","url","needsPublishImpl","options","callback","cwd","process","localPkg","package","JSON","parse","readFileSync","join","private","needsPublish","reason","pacote","Arborist","npa","execFile","promisify","execFileAsync","comparePackageFiles","comparePackageJson","extractPackageJson","hashBuffer","registry","scope","name","split","undefined","stdout","trim","registryPkg","registryTarball","packument","latestVersion","latest","changes","type","significance","versions","version","field","oldValue","newValue","tarballUrl","dist","tarball","integrity","err","error","code","message","localTarball","spec","manifest","_resolved","_integrity","localHash","registryHash","substring","fileComparison","identical","packageJsonOnly","localTarballPkg","registryTarballPkg","pkgJsonComparison","includeOptionalDeps","additionalSignificantFields","ignoreFields","treatNarrowingAsEquivalent","hasSignificantChanges","fieldChanges","map","fc","dependencyChanges","filter","dc","semanticChange","oldSpec","newSpec","summary","fileChanges","length","catch","needsPublishCb","Promise","resolve","reject","result","Error"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/npm/npm-needs-publish/src/needs-publish.ts"],"sourcesContent":["/**\n * Main orchestration logic for npm-needs-publish\n *\n * Algorithm:\n * 1. Fetch registry packument → if E404, return needsPublish=true (first publish)\n * 2. Version check → if different, return needsPublish=true (intentional bump)\n * 3. Fast hash check → if identical, return needsPublish=false (no changes)\n * 4. Extract both tarballs, compare file-by-file (excluding package.json)\n * 5. If non-package.json files differ → return needsPublish=true\n * 6. If only package.json differs → do semantic comparison\n */\n\nimport fs from 'fs';\nimport Module from 'module';\nimport path from 'path';\nimport { stringStartsWith } from './compat.ts';\nimport type { ChangeDetail, NeedsPublishOptions, NeedsPublishResult, PackageJson } from './types.ts';\n\nconst _require = typeof require === 'undefined' ? Module.createRequire(import.meta.url) : require;\n\n/**\n * Callback type for needsPublish\n */\nexport type NeedsPublishCallback = (error: Error | null, result?: NeedsPublishResult) => void;\n\nfunction needsPublishImpl(options: NeedsPublishOptions, callback: NeedsPublishCallback) {\n const cwd = options.cwd || process.cwd();\n\n // Load local package.json\n const localPkg: PackageJson = options.package || JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8'));\n\n // Skip private packages\n if (localPkg.private)\n return callback(null, {\n needsPublish: false,\n reason: 'Package is private',\n });\n\n (async () => {\n const pacote = _require('pacote');\n const Arborist = _require('@npmcli/arborist');\n const npa = _require('npm-package-arg');\n const { execFile } = _require('child_process');\n const { promisify } = _require('util');\n const execFileAsync = promisify(execFile);\n\n // Dynamic import for comparators (they use modern features)\n const { comparePackageFiles, comparePackageJson, extractPackageJson, hashBuffer } = await import('./comparators/index.ts');\n\n // Get registry URL for scoped packages\n let registry: string | undefined = options.registry;\n if (!registry) {\n const scope = stringStartsWith(localPkg.name, '@') ? localPkg.name.split('/')[0] : undefined;\n if (scope) {\n try {\n const { stdout } = await execFileAsync('npm', ['config', 'get', `${scope}:registry`]);\n registry = stdout.trim();\n if (registry === 'undefined') registry = undefined;\n } catch {\n // Fallback to default registry\n }\n }\n }\n\n // Step 1: Try to fetch registry packument\n let registryPkg: PackageJson;\n let registryTarball: Buffer;\n\n try {\n const packument = await pacote.packument(localPkg.name, {\n Arborist,\n ...(registry && { registry }),\n });\n\n const latestVersion = packument['dist-tags']?.latest;\n if (!latestVersion) {\n callback(null, {\n needsPublish: true,\n reason: 'No latest version found in registry (first publish)',\n changes: [{ type: 'first-publish', significance: 'critical' }],\n });\n return;\n }\n\n registryPkg = packument.versions[latestVersion] as unknown as PackageJson;\n\n // Step 2: Version comparison (fast path)\n if (localPkg.version !== latestVersion) {\n callback(null, {\n needsPublish: true,\n reason: `Version differs (local: ${localPkg.version}, registry: ${latestVersion})`,\n changes: [\n {\n type: 'version',\n field: 'version',\n oldValue: latestVersion,\n newValue: localPkg.version,\n significance: 'critical',\n },\n ],\n });\n return;\n }\n\n // Fetch registry tarball for comparison\n const tarballUrl = registryPkg.dist?.tarball;\n if (!tarballUrl)\n return callback(null, {\n needsPublish: true,\n reason: 'Registry package has no tarball URL',\n changes: [{ type: 'first-publish', significance: 'critical' }],\n });\n\n registryTarball = await pacote.tarball(tarballUrl, {\n Arborist,\n integrity: registryPkg.dist?.integrity,\n });\n } catch (err: unknown) {\n const error = err as { code?: string; message?: string };\n // Package not found in registry (first publish)\n if (error.code === 'E404') {\n callback(null, {\n needsPublish: true,\n reason: 'Package not found in registry (first publish)',\n changes: [{ type: 'first-publish', significance: 'critical' }],\n });\n return;\n }\n // Unknown error - assume changed to be safe\n callback(null, {\n needsPublish: true,\n reason: `Error checking registry: ${error.message || 'Unknown error'}`,\n });\n return;\n }\n\n // Step 3: Pack local package\n let localTarball: Buffer;\n try {\n const spec = npa(cwd);\n const manifest = await pacote.manifest(spec, { Arborist });\n localTarball = await pacote.tarball(manifest._resolved, {\n Arborist,\n integrity: manifest._integrity,\n });\n } catch (err: unknown) {\n const error = err as { message?: string };\n callback(null, {\n needsPublish: true,\n reason: `Error packing local package: ${error.message || 'Unknown error'}`,\n });\n return;\n }\n\n // Step 4: Fast hash comparison\n const localHash = hashBuffer(localTarball);\n const registryHash = hashBuffer(registryTarball);\n\n if (localHash === registryHash) {\n callback(null, {\n needsPublish: false,\n reason: `No changes detected (hash: ${localHash.substring(0, 16)}...)`,\n });\n return;\n }\n\n // Step 5: File-by-file comparison\n const fileComparison = await comparePackageFiles(localTarball, registryTarball);\n\n if (fileComparison.identical) {\n // Hash mismatch but files identical - likely tarball metadata difference\n callback(null, {\n needsPublish: false,\n reason: 'Files identical (tarball metadata differs)',\n });\n return;\n }\n\n // Step 6: If only package.json differs, do semantic comparison\n if (fileComparison.packageJsonOnly && !options.packageJsonOnly) {\n // Extract package.json from both tarballs for accurate comparison\n // (packument metadata is missing fields like 'files')\n const localTarballPkg = (await extractPackageJson(localTarball)) as PackageJson;\n const registryTarballPkg = (await extractPackageJson(registryTarball)) as PackageJson;\n\n const pkgJsonComparison = comparePackageJson(localTarballPkg, registryTarballPkg, {\n includeOptionalDeps: options.includeOptionalDeps,\n additionalSignificantFields: options.additionalSignificantFields,\n ignoreFields: options.ignoreFields,\n treatNarrowingAsEquivalent: options.treatNarrowingAsEquivalent,\n });\n\n if (!pkgJsonComparison.hasSignificantChanges) {\n callback(null, {\n needsPublish: false,\n reason: 'Package.json changes are not significant for consumers',\n changes: pkgJsonComparison.fieldChanges.map((fc) => ({\n type: 'field' as const,\n field: fc.field,\n oldValue: fc.oldValue,\n newValue: fc.newValue,\n significance: 'informational' as const,\n })),\n });\n return;\n }\n\n // Build changes list\n const changes: ChangeDetail[] = [\n ...pkgJsonComparison.fieldChanges.map((fc) => ({\n type: 'field' as const,\n field: fc.field,\n oldValue: fc.oldValue,\n newValue: fc.newValue,\n significance: fc.significance,\n })),\n ...pkgJsonComparison.dependencyChanges\n .filter((dc) => dc.semanticChange !== 'equivalent' && dc.semanticChange !== 'none')\n .map((dc) => ({\n type: 'dependency' as const,\n field: `${dc.type}.${dc.name}`,\n oldValue: dc.oldSpec,\n newValue: dc.newSpec,\n significance: 'significant' as const,\n })),\n ];\n\n callback(null, {\n needsPublish: true,\n reason: pkgJsonComparison.summary,\n changes,\n });\n return;\n }\n\n // Step 7: Other files changed\n callback(null, {\n needsPublish: true,\n reason: `Code changes detected (${fileComparison.fileChanges.length} files changed)`,\n changes: fileComparison.fileChanges.map((fc) => ({\n type: 'file' as const,\n field: fc.path,\n significance: 'significant' as const,\n })),\n });\n })().catch(callback);\n}\n\n/**\n * Callback-based needsPublish\n */\nexport function needsPublishCb(options: NeedsPublishOptions, callback: NeedsPublishCallback) {\n needsPublishImpl(options, callback);\n}\n\n/**\n * Determine if a package needs to be published to npm.\n *\n * @example\n * ```ts\n * import { needsPublish } from 'npm-needs-publish';\n *\n * const result = await needsPublish({ cwd: process.cwd() });\n * if (result.needsPublish) {\n * console.log('Publish needed:', result.reason);\n * }\n * ```\n */\nexport function needsPublish(options: NeedsPublishOptions = {}): Promise<NeedsPublishResult> {\n return new Promise((resolve, reject) => needsPublishCb(options, (err, result) => (err ? reject(err) : resolve(result))));\n}\n"],"names":["fs","Module","path","stringStartsWith","_require","require","createRequire","url","needsPublishImpl","options","callback","cwd","process","localPkg","package","JSON","parse","readFileSync","join","private","needsPublish","reason","pacote","Arborist","npa","execFile","promisify","execFileAsync","comparePackageFiles","comparePackageJson","extractPackageJson","hashBuffer","registry","scope","name","split","undefined","stdout","trim","registryPkg","registryTarball","packument","latestVersion","latest","changes","type","significance","versions","version","field","oldValue","newValue","tarballUrl","dist","tarball","integrity","err","error","code","message","localTarball","spec","manifest","_resolved","_integrity","localHash","registryHash","substring","fileComparison","identical","packageJsonOnly","localTarballPkg","registryTarballPkg","pkgJsonComparison","includeOptionalDeps","additionalSignificantFields","ignoreFields","treatNarrowingAsEquivalent","hasSignificantChanges","fieldChanges","map","fc","dependencyChanges","filter","dc","semanticChange","oldSpec","newSpec","summary","fileChanges","length","catch","needsPublishCb","Promise","resolve","reject","result"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "npm-needs-publish",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Smart publish detection for npm packages - semantic package.json comparison with semver-aware dependency analysis",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"npm",
|
|
@@ -71,6 +71,8 @@
|
|
|
71
71
|
"@types/pacote": "^11.1.8",
|
|
72
72
|
"@types/semver": "^7.7.1",
|
|
73
73
|
"@types/tar": "^6.1.13",
|
|
74
|
+
"cr": "^0.1.0",
|
|
75
|
+
"is-version": "^1.0.9",
|
|
74
76
|
"module-link-unlink": "^1.0.11",
|
|
75
77
|
"node-version-use": "*",
|
|
76
78
|
"os-shim": "^0.1.0",
|