@@ -63,6 +63,8 @@ export class TemplateFs {
6363 this . destination = destination ;
6464 }
6565
66+ /** Private methods */
67+
6668 /**
6769 * Removes the temporary directory created by this Template instance.
6870 * @private
@@ -230,6 +232,20 @@ export class TemplateFs {
230232 await fs . writeFile ( fullPath , Buffer . isBuffer ( content ) ? content : Buffer . from ( content ) ) ;
231233 }
232234
235+ /**
236+ * Replaces placeholders in the given sheet with values from the replacements map.
237+ *
238+ * The function searches for placeholders in the format `${key}` within the sheet
239+ * content, where `key` corresponds to a path in the replacements object.
240+ * If a value is found for the key, it replaces the placeholder with the value.
241+ * If no value is found, the original placeholder remains unchanged.
242+ *
243+ * @param sheetName - The name of the sheet to be replaced.
244+ * @param replacements - An object where keys represent placeholder paths and values are the replacements.
245+ * @returns A promise that resolves when the substitution is complete.
246+ * @throws {Error } If the template instance has been destroyed.
247+ * @experimental This API is experimental and might change in future versions.
248+ */
233249 async #substitute( sheetName : string , replacements : Record < string , unknown > ) : Promise < void > {
234250 const sharedStringsPath = this . #excelKeys. sharedStrings ;
235251 const sheetPath = await this . #getSheetPathByName( sheetName ) ;
@@ -267,6 +283,79 @@ export class TemplateFs {
267283 }
268284 }
269285
286+ /**
287+ * Removes sheets from the workbook.
288+ *
289+ * @param {Object } data - The data for sheet removal.
290+ * @param {number[] } [data.sheetIndexes] - The 1-based indexes of the sheets to remove.
291+ * @param {string[] } [data.sheetNames] - The names of the sheets to remove.
292+ * @returns {void }
293+ *
294+ * @throws {Error } If the template instance has been destroyed.
295+ * @throws {Error } If the sheet does not exist.
296+ * @experimental This API is experimental and might change in future versions.
297+ */
298+ async #removeSheets( data : {
299+ sheetNames ?: string [ ] ;
300+ sheetIndexes ?: number [ ] ;
301+ } ) : Promise < void > {
302+ const { sheetIndexes = [ ] , sheetNames = [ ] } = data ;
303+
304+ // first get index of sheets to remove
305+ const sheetIndexesToRemove : Set < number > = new Set ( sheetIndexes ) ;
306+
307+ for ( const sheetName of sheetNames ) {
308+ const sheetPath = await this . #getSheetPathByName( sheetName ) ;
309+
310+ const sheetIndexMatch = sheetPath . match ( / s h e e t ( \d + ) \. x m l $ / ) ;
311+
312+ if ( ! sheetIndexMatch || ! sheetIndexMatch [ 1 ] ) {
313+ throw new Error ( `Sheet "${ sheetName } " not found` ) ;
314+ }
315+
316+ const sheetIndex = parseInt ( sheetIndexMatch [ 1 ] , 10 ) ;
317+
318+ sheetIndexesToRemove . add ( sheetIndex ) ;
319+ }
320+
321+ // Remove sheets by index
322+ for ( const sheetIndex of sheetIndexesToRemove . values ( ) ) {
323+ const sheetPath = `xl/worksheets/sheet${ sheetIndex } .xml` ;
324+
325+ if ( ! this . fileKeys . has ( sheetPath ) ) {
326+ continue ;
327+ }
328+
329+ // remove sheet file
330+ await fs . unlink ( path . join ( this . destination , ...sheetPath . split ( "/" ) ) ) ;
331+ this . fileKeys . delete ( sheetPath ) ;
332+
333+ // remove sheet from workbook
334+ if ( this . fileKeys . has ( this . #excelKeys. workbook ) ) {
335+ this . #set( this . #excelKeys. workbook , Buffer . from ( Utils . Common . removeSheetFromWorkbook (
336+ this . #readFile( this . #excelKeys. workbook ) . toString ( ) ,
337+ sheetIndex ,
338+ ) ) ) ;
339+ }
340+
341+ // remove sheet from workbook relations
342+ if ( this . fileKeys . has ( this . #excelKeys. workbookRels ) ) {
343+ this . #set( this . #excelKeys. workbookRels , Buffer . from ( Utils . Common . removeSheetFromRels (
344+ this . #readFile( this . #excelKeys. workbookRels ) . toString ( ) ,
345+ sheetIndex ,
346+ ) ) ) ;
347+ }
348+
349+ // remove sheet from content types
350+ if ( this . fileKeys . has ( this . #excelKeys. contentTypes ) ) {
351+ this . #set( this . #excelKeys. contentTypes , Buffer . from ( Utils . Common . removeSheetFromContentTypes (
352+ this . #readFile( this . #excelKeys. contentTypes ) . toString ( ) ,
353+ sheetIndex ,
354+ ) ) ) ;
355+ }
356+ }
357+ }
358+
270359 /**
271360 * Validates the template by checking all required files exist.
272361 *
@@ -286,6 +375,8 @@ export class TemplateFs {
286375 }
287376 }
288377
378+ /** Public methods */
379+
289380 /**
290381 * Copies a sheet from the template to a new name.
291382 *
@@ -401,14 +492,14 @@ export class TemplateFs {
401492 * @param replacements - An object where keys represent placeholder paths and values are the replacements.
402493 * @returns A promise that resolves when the substitution is complete.
403494 */
404- substitute ( sheetName : string , replacements : Record < string , unknown > ) : Promise < void > {
495+ async substitute ( sheetName : string , replacements : Record < string , unknown > ) : Promise < void > {
405496 this . #ensureNotProcessing( ) ;
406497 this . #ensureNotDestroyed( ) ;
407498
408499 this . #isProcessing = true ;
409500
410501 try {
411- return this . #substitute( sheetName , replacements ) ;
502+ await this . #substitute( sheetName , replacements ) ;
412503 } finally {
413504 this . #isProcessing = false ;
414505 }
@@ -617,7 +708,7 @@ export class TemplateFs {
617708
618709 const { dimension : newDimension , rowNumber : actualRowNumber } = await Utils . writeRowsToStream ( output , rows , maxRowNumber ) ;
619710
620- if ( compareColumns ( newDimension . maxColumn , dimension . maxColumn ) > 0 ) {
711+ if ( Utils . compareColumns ( newDimension . maxColumn , dimension . maxColumn ) > 0 ) {
621712 dimension . maxColumn = newDimension . maxColumn ;
622713 }
623714
@@ -680,7 +771,7 @@ export class TemplateFs {
680771 // new <row>
681772 const { dimension : newDimension , rowNumber : actualRowNumber } = await Utils . writeRowsToStream ( output , rows , maxRowNumber ) ;
682773
683- if ( compareColumns ( newDimension . maxColumn , dimension . maxColumn ) > 0 ) {
774+ if ( Utils . compareColumns ( newDimension . maxColumn , dimension . maxColumn ) > 0 ) {
684775 dimension . maxColumn = newDimension . maxColumn ;
685776 }
686777
@@ -727,7 +818,7 @@ export class TemplateFs {
727818 // Prepare the rows
728819 const { dimension : newDimension } = await Utils . writeRowsToStream ( output , rows , maxRowNumber ) ;
729820
730- if ( compareColumns ( newDimension . maxColumn , dimension . maxColumn ) > 0 ) {
821+ if ( Utils . compareColumns ( newDimension . maxColumn , dimension . maxColumn ) > 0 ) {
731822 dimension . maxColumn = newDimension . maxColumn ;
732823 }
733824
@@ -815,6 +906,30 @@ export class TemplateFs {
815906 }
816907 }
817908
909+ /**
910+ * Removes sheets from the workbook.
911+ *
912+ * @param {Object } data
913+ * @param {number[] } [data.sheetIndexes] - The 1-based indexes of the sheets to remove.
914+ * @param {string[] } [data.sheetNames] - The names of the sheets to remove.
915+ * @returns {void }
916+ */
917+ async removeSheets ( data : {
918+ sheetNames ?: string [ ] ;
919+ sheetIndexes ?: number [ ] ;
920+ } ) : Promise < void > {
921+ this . #ensureNotProcessing( ) ;
922+ this . #ensureNotDestroyed( ) ;
923+
924+ this . #isProcessing = true ;
925+
926+ try {
927+ await this . #removeSheets( data ) ;
928+ } finally {
929+ this . #isProcessing = false ;
930+ }
931+ }
932+
818933 /**
819934 * Saves the modified Excel template to a buffer.
820935 *
@@ -920,6 +1035,8 @@ export class TemplateFs {
9201035 }
9211036 }
9221037
1038+ /** Static methods */
1039+
9231040 /**
9241041 * Creates a Template instance from an Excel file source.
9251042 * Removes any existing files in the destination directory.
@@ -970,8 +1087,3 @@ export class TemplateFs {
9701087 return new TemplateFs ( new Set ( Object . keys ( files ) ) , destinationWithUuid ) ;
9711088 }
9721089}
973-
974- const compareColumns = ( a : string , b : string ) : number => {
975- if ( a === b ) return 0 ;
976- return a . length === b . length ? ( a < b ? - 1 : 1 ) : ( a . length < b . length ? - 1 : 1 ) ;
977- } ;
0 commit comments