1414 * limitations under the License.
1515 */
1616
17+ import fs from 'node:fs' ;
18+ import os from 'node:os' ;
19+ import path from 'node:path' ;
1720import { Flags , loglevel , orgApiVersionFlagWithDeprecations , SfCommand } from '@salesforce/sf-plugins-core' ;
1821import {
1922 BundleVersionCreateOptions ,
@@ -24,9 +27,6 @@ import {
2427import { Messages , Lifecycle } from '@salesforce/core' ;
2528import { camelCaseToTitleCase , Duration } from '@salesforce/kit' ;
2629import { requiredHubFlag } from '../../../../utils/hubFlag.js' ;
27- import fs from 'node:fs' ;
28- import path from 'node:path' ;
29- import os from 'node:os' ;
3030
3131Messages . importMessagesDirectoryFromMetaUrl ( import . meta. url ) ;
3232// TODO: Update messages
@@ -148,31 +148,7 @@ export class PackageBundlesCreate extends SfCommand<BundleSObjects.PackageBundle
148148 }
149149
150150 // Read and normalize the definition file to handle 15-char package version IDs
151- let definitionFilePath = flags [ 'definition-file' ] ;
152- let tempFilePath : string | undefined ;
153-
154- try {
155- // Read the definition file
156- const definitionContent = await fs . promises . readFile ( definitionFilePath , 'utf8' ) ;
157- const definitionJson = JSON . parse ( definitionContent ) as unknown ;
158-
159- // Normalize any 15-character package version IDs to 18-character format
160- const normalizedJson = normalizePackageVersionIds ( definitionJson ) ;
161-
162- // Check if any normalization occurred by comparing stringified versions
163- if ( JSON . stringify ( definitionJson ) !== JSON . stringify ( normalizedJson ) ) {
164- // Create a temporary file with normalized content
165- const tempDir = await fs . promises . mkdtemp ( path . join ( os . tmpdir ( ) , 'sf-bundle-' ) ) ;
166- tempFilePath = path . join ( tempDir , 'normalized-definition.json' ) ;
167- await fs . promises . writeFile ( tempFilePath , JSON . stringify ( normalizedJson , null , 2 ) , 'utf8' ) ;
168- definitionFilePath = tempFilePath ;
169- this . debug ( `Normalized package version IDs in definition file. Using temporary file: ${ tempFilePath } ` ) ;
170- }
171- } catch ( error ) {
172- // If reading/parsing fails, let the packaging library handle the error
173- // This preserves the original error messages for invalid JSON, missing files, etc.
174- this . debug ( `Could not normalize definition file: ${ error instanceof Error ? error . message : String ( error ) } ` ) ;
175- }
151+ const { definitionFilePath, tempFilePath } = await this . normalizeDefinitionFile ( flags [ 'definition-file' ] ) ;
176152
177153 const options : BundleVersionCreateOptions = {
178154 connection : flags [ 'target-dev-hub' ] . getConnection ( flags [ 'api-version' ] ) ,
@@ -228,29 +204,57 @@ export class PackageBundlesCreate extends SfCommand<BundleSObjects.PackageBundle
228204 }
229205 throw error ;
230206 } finally {
231- // Clean up temporary file if it was created
232- if ( tempFilePath ) {
233- try {
234- const tempDir = path . dirname ( tempFilePath ) ;
235- await fs . promises . rm ( tempDir , { recursive : true , force : true } ) ;
236- this . debug ( `Cleaned up temporary definition file: ${ tempFilePath } ` ) ;
237- } catch ( cleanupError ) {
238- // Log but don't fail if cleanup fails
239- this . debug (
240- `Failed to clean up temporary file: ${ cleanupError instanceof Error ? cleanupError . message : String ( cleanupError ) } `
241- ) ;
242- }
243- }
207+ await this . cleanupTempFile ( tempFilePath ) ;
244208 }
245209
246- // Stop spinner only if it was started - stop it cleanly without a message
247210 if ( isSpinnerRunning ) {
248211 this . spinner . stop ( ) ;
249212 }
250213
214+ this . handleResult ( result ) ;
215+ return result ;
216+ }
217+
218+ private async normalizeDefinitionFile (
219+ definitionFilePath : string
220+ ) : Promise < { definitionFilePath : string ; tempFilePath ?: string } > {
221+ try {
222+ const definitionContent = await fs . promises . readFile ( definitionFilePath , 'utf8' ) ;
223+ const definitionJson = JSON . parse ( definitionContent ) as unknown ;
224+ const normalizedJson = normalizePackageVersionIds ( definitionJson ) ;
225+
226+ if ( JSON . stringify ( definitionJson ) !== JSON . stringify ( normalizedJson ) ) {
227+ const tempDir = await fs . promises . mkdtemp ( path . join ( os . tmpdir ( ) , 'sf-bundle-' ) ) ;
228+ const tempFilePath = path . join ( tempDir , 'normalized-definition.json' ) ;
229+ await fs . promises . writeFile ( tempFilePath , JSON . stringify ( normalizedJson , null , 2 ) , 'utf8' ) ;
230+ this . debug ( `Normalized package version IDs in definition file. Using temporary file: ${ tempFilePath } ` ) ;
231+ return { definitionFilePath : tempFilePath , tempFilePath } ;
232+ }
233+ } catch ( error ) {
234+ this . debug ( `Could not normalize definition file: ${ error instanceof Error ? error . message : String ( error ) } ` ) ;
235+ }
236+ return { definitionFilePath } ;
237+ }
238+
239+ private async cleanupTempFile ( tempFilePath : string | undefined ) : Promise < void > {
240+ if ( tempFilePath ) {
241+ try {
242+ const tempDir = path . dirname ( tempFilePath ) ;
243+ await fs . promises . rm ( tempDir , { recursive : true , force : true } ) ;
244+ this . debug ( `Cleaned up temporary definition file: ${ tempFilePath } ` ) ;
245+ } catch ( cleanupError ) {
246+ this . debug (
247+ `Failed to clean up temporary file: ${
248+ cleanupError instanceof Error ? cleanupError . message : String ( cleanupError )
249+ } `
250+ ) ;
251+ }
252+ }
253+ }
254+
255+ private handleResult ( result : BundleSObjects . PackageBundleVersionCreateRequestResult ) : void {
251256 switch ( result . RequestStatus ) {
252257 case BundleSObjects . PkgBundleVersionCreateReqStatus . error : {
253- // Collect all error messages from both Error array and ValidationError
254258 const errorMessages : string [ ] = [ ] ;
255259
256260 if ( result . Error && result . Error . length > 0 ) {
@@ -267,14 +271,12 @@ export class PackageBundlesCreate extends SfCommand<BundleSObjects.PackageBundle
267271 throw messages . createError ( 'multipleErrors' , [ errorText ] ) ;
268272 }
269273 case BundleSObjects . PkgBundleVersionCreateReqStatus . success : {
270- // Show the PackageBundleVersionId (1Q8) if available, otherwise show the request ID
271274 const displayId = result . PackageBundleVersionId || result . Id ;
272275 this . log ( `Successfully created bundle version with ID ${ displayId } ` ) ;
273276 break ;
274277 }
275278 default :
276279 this . log ( messages . getMessage ( 'InProgress' , [ camelCaseToTitleCase ( result . RequestStatus as string ) , result . Id ] ) ) ;
277280 }
278- return result ;
279281 }
280282}
0 commit comments