node-pptx-templater 1.1.2 → 1.1.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-pptx-templater",
3
- "version": "1.1.2",
3
+ "version": "1.1.3",
4
4
  "description": "High-performance, low-level PowerPoint (PPTX) OpenXML template engine for Node.js. Dynamically replace text, insert images, update charts (with Excel workbook data caching), and merge table cells without PowerPoint corruption or Repair Mode prompts.",
5
5
  "main": "./src/index.js",
6
6
  "type": "commonjs",
@@ -37,6 +37,7 @@
37
37
  "example:table-extraction": "node examples/table-extraction.js",
38
38
  "example:nested-rows": "node examples/nested-table-rows.js",
39
39
  "example:xml-folder": "node examples/xml-folder-workflow.js",
40
+ "example:all": "node scripts/run-examples-check.js",
40
41
  "prepublishOnly": "npm run lint && npm run test"
41
42
  },
42
43
  "keywords": [
@@ -161,11 +161,11 @@ class OutputWriter {
161
161
  : e.compressionMethod === 0
162
162
  ? 'STORE'
163
163
  : `UNKNOWN(${e.compressionMethod})`
164
- console.log(e.name)
165
- console.log(`compressed: ${e.compressedSize}`)
166
- console.log(`uncompressed: ${e.uncompressedSize}`)
167
- console.log(`crc: ${e.crc32}`)
168
- console.log(`method: ${methodStr}`)
164
+ logger.info(e.name)
165
+ logger.info(`compressed: ${e.compressedSize}`)
166
+ logger.info(`uncompressed: ${e.uncompressedSize}`)
167
+ logger.info(`crc: ${e.crc32}`)
168
+ logger.info(`method: ${methodStr}`)
169
169
  })
170
170
  logger.info('--- End of ZIP debug output ---')
171
171
  }
@@ -2654,6 +2654,16 @@ class PPTXTemplater {
2654
2654
  return this.#shapeManager.validateShape(options)
2655
2655
  }
2656
2656
 
2657
+ /**
2658
+ * Adds a new shape to the targeted slide(s).
2659
+ *
2660
+ * @param {string|Object} typeOrOptions Either the shape type string (e.g., 'rect', 'ellipse') or a full options object.
2661
+ * @param {Object} [options={}] Additional configuration options if type was specified as a string.
2662
+ * @returns {Promise<PPTXTemplater>} The templater instance for chaining.
2663
+ *
2664
+ * @example
2665
+ * await ppt.addShape('rect', { id: 'MyShape', x: 100, y: 100, width: 200, height: 100 });
2666
+ */
2657
2667
  async addShape(typeOrOptions, options = {}) {
2658
2668
  this.#assertLoaded()
2659
2669
  let resolvedOptions = {}
@@ -2738,14 +2748,28 @@ class PPTXTemplater {
2738
2748
  */
2739
2749
  async addCellShape(tableId, rowIndex, colIndex, options) {
2740
2750
  this.#assertLoaded()
2751
+ let row = rowIndex
2752
+ let col = colIndex
2753
+ let opts = options
2754
+ if (rowIndex && typeof rowIndex === 'object') {
2755
+ row = rowIndex.row !== undefined ? rowIndex.row : rowIndex.rowIndex
2756
+ col =
2757
+ rowIndex.column !== undefined
2758
+ ? rowIndex.column
2759
+ : rowIndex.col !== undefined
2760
+ ? rowIndex.col
2761
+ : rowIndex.colIndex
2762
+ opts = rowIndex
2763
+ }
2764
+
2741
2765
  const targetIndices = this.#getTargetSlideIndices()
2742
2766
  for (const idx of targetIndices) {
2743
2767
  this.#tableManager.addCellShape(
2744
2768
  idx,
2745
2769
  tableId,
2746
- rowIndex,
2747
- colIndex,
2748
- options,
2770
+ row,
2771
+ col,
2772
+ opts,
2749
2773
  this.#slideManager,
2750
2774
  this.#shapeManager
2751
2775
  )
@@ -2765,15 +2789,36 @@ class PPTXTemplater {
2765
2789
  */
2766
2790
  async updateCellShape(tableId, rowIndex, colIndex, shapeIndex, options) {
2767
2791
  this.#assertLoaded()
2792
+ let row = rowIndex
2793
+ let col = colIndex
2794
+ let shpIdx = shapeIndex
2795
+ let opts = options
2796
+ if (rowIndex && typeof rowIndex === 'object') {
2797
+ row = rowIndex.row !== undefined ? rowIndex.row : rowIndex.rowIndex
2798
+ col =
2799
+ rowIndex.column !== undefined
2800
+ ? rowIndex.column
2801
+ : rowIndex.col !== undefined
2802
+ ? rowIndex.col
2803
+ : rowIndex.colIndex
2804
+ shpIdx =
2805
+ rowIndex.shapeIndex !== undefined
2806
+ ? rowIndex.shapeIndex
2807
+ : rowIndex.shape !== undefined
2808
+ ? rowIndex.shape
2809
+ : colIndex
2810
+ opts = options !== undefined ? options : rowIndex
2811
+ }
2812
+
2768
2813
  const targetIndices = this.#getTargetSlideIndices()
2769
2814
  for (const idx of targetIndices) {
2770
2815
  this.#tableManager.updateCellShape(
2771
2816
  idx,
2772
2817
  tableId,
2773
- rowIndex,
2774
- colIndex,
2775
- shapeIndex,
2776
- options,
2818
+ row,
2819
+ col,
2820
+ shpIdx,
2821
+ opts,
2777
2822
  this.#slideManager,
2778
2823
  this.#shapeManager
2779
2824
  )
@@ -2792,14 +2837,33 @@ class PPTXTemplater {
2792
2837
  */
2793
2838
  async removeCellShape(tableId, rowIndex, colIndex, shapeIndex) {
2794
2839
  this.#assertLoaded()
2840
+ let row = rowIndex
2841
+ let col = colIndex
2842
+ let shpIdx = shapeIndex
2843
+ if (rowIndex && typeof rowIndex === 'object') {
2844
+ row = rowIndex.row !== undefined ? rowIndex.row : rowIndex.rowIndex
2845
+ col =
2846
+ rowIndex.column !== undefined
2847
+ ? rowIndex.column
2848
+ : rowIndex.col !== undefined
2849
+ ? rowIndex.col
2850
+ : rowIndex.colIndex
2851
+ shpIdx =
2852
+ rowIndex.shapeIndex !== undefined
2853
+ ? rowIndex.shapeIndex
2854
+ : rowIndex.shape !== undefined
2855
+ ? rowIndex.shape
2856
+ : colIndex
2857
+ }
2858
+
2795
2859
  const targetIndices = this.#getTargetSlideIndices()
2796
2860
  for (const idx of targetIndices) {
2797
2861
  this.#tableManager.removeCellShape(
2798
2862
  idx,
2799
2863
  tableId,
2800
- rowIndex,
2801
- colIndex,
2802
- shapeIndex,
2864
+ row,
2865
+ col,
2866
+ shpIdx,
2803
2867
  this.#slideManager,
2804
2868
  this.#shapeManager
2805
2869
  )
@@ -2818,14 +2882,33 @@ class PPTXTemplater {
2818
2882
  */
2819
2883
  getCellShape(tableId, rowIndex, colIndex, shapeIndex) {
2820
2884
  this.#assertLoaded()
2885
+ let row = rowIndex
2886
+ let col = colIndex
2887
+ let shpIdx = shapeIndex
2888
+ if (rowIndex && typeof rowIndex === 'object') {
2889
+ row = rowIndex.row !== undefined ? rowIndex.row : rowIndex.rowIndex
2890
+ col =
2891
+ rowIndex.column !== undefined
2892
+ ? rowIndex.column
2893
+ : rowIndex.col !== undefined
2894
+ ? rowIndex.col
2895
+ : rowIndex.colIndex
2896
+ shpIdx =
2897
+ rowIndex.shapeIndex !== undefined
2898
+ ? rowIndex.shapeIndex
2899
+ : rowIndex.shape !== undefined
2900
+ ? rowIndex.shape
2901
+ : colIndex
2902
+ }
2903
+
2821
2904
  const targetIndices = this.#getTargetSlideIndices()
2822
2905
  for (const idx of targetIndices) {
2823
2906
  const shape = this.#tableManager.getCellShape(
2824
2907
  idx,
2825
2908
  tableId,
2826
- rowIndex,
2827
- colIndex,
2828
- shapeIndex,
2909
+ row,
2910
+ col,
2911
+ shpIdx,
2829
2912
  this.#slideManager,
2830
2913
  this.#shapeManager
2831
2914
  )
@@ -2848,16 +2931,22 @@ class PPTXTemplater {
2848
2931
  typeof tableIdOrObj === 'object'
2849
2932
  ? tableIdOrObj.id || tableIdOrObj.name || tableIdOrObj.tableId
2850
2933
  : tableIdOrObj
2934
+ let row = rowIndex
2935
+ let col = colIndex
2936
+ if (rowIndex && typeof rowIndex === 'object') {
2937
+ row = rowIndex.row !== undefined ? rowIndex.row : rowIndex.rowIndex
2938
+ col =
2939
+ rowIndex.column !== undefined
2940
+ ? rowIndex.column
2941
+ : rowIndex.col !== undefined
2942
+ ? rowIndex.col
2943
+ : rowIndex.colIndex
2944
+ }
2945
+
2851
2946
  const targetIndices = this.#getTargetSlideIndices()
2852
2947
  for (const idx of targetIndices) {
2853
2948
  try {
2854
- const bounds = this.#tableManager.getCellBounds(
2855
- idx,
2856
- tableId,
2857
- rowIndex,
2858
- colIndex,
2859
- this.#slideManager
2860
- )
2949
+ const bounds = this.#tableManager.getCellBounds(idx, tableId, row, col, this.#slideManager)
2861
2950
  if (bounds) return bounds
2862
2951
  } catch (err) {
2863
2952
  logger.debug(
@@ -2885,17 +2974,33 @@ class PPTXTemplater {
2885
2974
  typeof tableIdOrObj === 'object'
2886
2975
  ? tableIdOrObj.id || tableIdOrObj.name || tableIdOrObj.tableId
2887
2976
  : tableIdOrObj
2977
+ let row = rowIndex
2978
+ let col = colIndex
2979
+ let widthOrOpts = shapeWidthOrOptions
2980
+ let height = shapeHeight
2981
+ if (rowIndex && typeof rowIndex === 'object') {
2982
+ row = rowIndex.row !== undefined ? rowIndex.row : rowIndex.rowIndex
2983
+ col =
2984
+ rowIndex.column !== undefined
2985
+ ? rowIndex.column
2986
+ : rowIndex.col !== undefined
2987
+ ? rowIndex.col
2988
+ : rowIndex.colIndex
2989
+ widthOrOpts = colIndex
2990
+ height = shapeWidthOrOptions
2991
+ }
2992
+
2888
2993
  const targetIndices = this.#getTargetSlideIndices()
2889
2994
  for (const idx of targetIndices) {
2890
2995
  try {
2891
2996
  const pos = this.#tableManager.getCellPosition(
2892
2997
  idx,
2893
2998
  tableId,
2894
- rowIndex,
2895
- colIndex,
2999
+ row,
3000
+ col,
2896
3001
  this.#slideManager,
2897
- shapeWidthOrOptions,
2898
- shapeHeight
3002
+ widthOrOpts,
3003
+ height
2899
3004
  )
2900
3005
  if (pos) return pos
2901
3006
  } catch (err) {
@@ -1302,10 +1302,10 @@ class TableManager {
1302
1302
  }
1303
1303
 
1304
1304
  #expandCellShape(config, cellBounds) {
1305
- const cellLeft_px = Math.round(cellBounds.left / 9525)
1306
- const cellTop_px = Math.round(cellBounds.top / 9525)
1307
- const cellWidth_px = Math.round(cellBounds.width / 9525)
1308
- const cellHeight_px = Math.round(cellBounds.height / 9525)
1305
+ const cellLeft_px = cellBounds.x
1306
+ const cellTop_px = cellBounds.y
1307
+ const cellWidth_px = cellBounds.width
1308
+ const cellHeight_px = cellBounds.height
1309
1309
 
1310
1310
  const parseLength = (val, maxVal) => {
1311
1311
  if (typeof val === 'string' && val.endsWith('%')) {
@@ -1743,7 +1743,7 @@ class TableManager {
1743
1743
  slideManager,
1744
1744
  shapeManager,
1745
1745
  tblObj,
1746
- frameObj
1746
+ _frameObj
1747
1747
  ) {
1748
1748
  if (!cellShapes || !shapeManager) return
1749
1749
 
@@ -1761,62 +1761,16 @@ class TableManager {
1761
1761
  }
1762
1762
  }
1763
1763
 
1764
- const xfrm = frameObj['p:xfrm']
1765
- const tableX = xfrm?.['a:off']?.['@_x'] ? parseInt(xfrm['a:off']['@_x'], 10) : 0
1766
- const tableY = xfrm?.['a:off']?.['@_y'] ? parseInt(xfrm['a:off']['@_y'], 10) : 0
1767
-
1768
- const gridCols = tblObj['a:tblGrid']?.['a:gridCol'] || []
1769
- const gridColsArr = Array.isArray(gridCols) ? gridCols : [gridCols]
1770
- const colWidths = gridColsArr.map(col => parseInt(col['@_w'] || 0, 10))
1771
-
1772
- const trsArr = tblObj['a:tr'] || []
1773
- const rowHeights = trsArr.map(row => parseInt(row['@_h'] || 0, 10))
1774
-
1775
- const getCellBounds = (r, c) => {
1776
- const parent = this.getMergeParent(slideIndex, tableId, r, c, slideManager)
1777
- const pr = parent.row
1778
- const pc = parent.col
1779
-
1780
- let cellLeft = tableX
1781
- for (let idx = 0; idx < pc; idx++) {
1782
- cellLeft += colWidths[idx] || 0
1783
- }
1784
-
1785
- let cellTop = tableY
1786
- for (let idx = 0; idx < pr; idx++) {
1787
- cellTop += rowHeights[idx] || 0
1788
- }
1789
-
1790
- const parentCell = trsArr[pr]?.['a:tc']?.[pc]
1791
- const gridSpan = parentCell?.['@_gridSpan'] ? parseInt(parentCell['@_gridSpan'], 10) : 1
1792
- const rowSpan = parentCell?.['@_rowSpan'] ? parseInt(parentCell['@_rowSpan'], 10) : 1
1793
-
1794
- let cellWidth = 0
1795
- for (let idx = 0; idx < gridSpan; idx++) {
1796
- cellWidth += colWidths[pc + idx] || 0
1797
- }
1798
-
1799
- let cellHeight = 0
1800
- for (let idx = 0; idx < rowSpan; idx++) {
1801
- cellHeight += rowHeights[pr + idx] || 0
1802
- }
1803
-
1804
- return {
1805
- left: cellLeft,
1806
- top: cellTop,
1807
- width: cellWidth,
1808
- height: cellHeight,
1809
- }
1810
- }
1811
-
1812
1764
  const shapesToCreate = []
1813
- const headerNames = (trsArr[0]?.['a:tc'] || []).map(cell => this.#getCellText(cell).trim())
1765
+ const headerNames = (tblObj['a:tr']?.[0]?.['a:tc'] || []).map(cell =>
1766
+ this.#getCellText(cell).trim()
1767
+ )
1814
1768
 
1815
1769
  for (let i = 0; i < rowsData.length; i++) {
1816
1770
  const rowData = rowsData[i]
1817
1771
  const finalRowIndex = isObjectRows ? i + 1 : i
1818
1772
 
1819
- const numCols = trsArr[finalRowIndex]?.['a:tc']?.length || 0
1773
+ const numCols = tblObj['a:tr']?.[finalRowIndex]?.['a:tc']?.length || 0
1820
1774
  for (let j = 0; j < numCols; j++) {
1821
1775
  const headerName = headerNames[j]
1822
1776
  let shapeFn = null
@@ -1851,16 +1805,24 @@ class TableManager {
1851
1805
  shapesToCreate.sort((a, b) => (a.config.zIndex || 0) - (b.config.zIndex || 0))
1852
1806
 
1853
1807
  shapesToCreate.forEach(item => {
1854
- const bounds = getCellBounds(item.rowIndex, item.colIndex)
1855
- const expandedConfigs = this.#expandCellShape(item.config, bounds)
1808
+ const bounds = this.getCellBounds(
1809
+ slideIndex,
1810
+ tableId,
1811
+ item.rowIndex,
1812
+ item.colIndex,
1813
+ slideManager
1814
+ )
1815
+ if (bounds) {
1816
+ const expandedConfigs = this.#expandCellShape(item.config, bounds)
1856
1817
 
1857
- expandedConfigs.forEach((expandedConfig, expIdx) => {
1858
- const finalShapeIndex =
1859
- expandedConfigs.length > 1 ? `${item.shapeIndex}_${expIdx}` : item.shapeIndex
1860
- expandedConfig.id = `cellshape_${resolvedTableId}_${item.rowIndex}_${item.colIndex}_${finalShapeIndex}`
1818
+ expandedConfigs.forEach((expandedConfig, expIdx) => {
1819
+ const finalShapeIndex =
1820
+ expandedConfigs.length > 1 ? `${item.shapeIndex}_${expIdx}` : item.shapeIndex
1821
+ expandedConfig.id = `cellshape_${resolvedTableId}_${item.rowIndex}_${item.colIndex}_${finalShapeIndex}`
1861
1822
 
1862
- shapeManager.addShape(slideIndex, expandedConfig, slideManager)
1863
- })
1823
+ shapeManager.addShape(slideIndex, expandedConfig, slideManager)
1824
+ })
1825
+ }
1864
1826
  })
1865
1827
  }
1866
1828
 
@@ -1928,53 +1890,13 @@ class TableManager {
1928
1890
  }
1929
1891
 
1930
1892
  addCellShape(slideIndex, tableId, rowIndex, colIndex, options, slideManager, shapeManager) {
1931
- const { tblObj, frameObj, resolvedTableId } = this.#getTableContext(
1932
- slideIndex,
1933
- tableId,
1934
- slideManager
1935
- )
1936
-
1937
- const xfrm = frameObj['p:xfrm']
1938
- const tableX = xfrm?.['a:off']?.['@_x'] ? parseInt(xfrm['a:off']['@_x'], 10) : 0
1939
- const tableY = xfrm?.['a:off']?.['@_y'] ? parseInt(xfrm['a:off']['@_y'], 10) : 0
1940
-
1941
- const gridCols = tblObj['a:tblGrid']?.['a:gridCol'] || []
1942
- const gridColsArr = Array.isArray(gridCols) ? gridCols : [gridCols]
1943
- const colWidths = gridColsArr.map(col => parseInt(col['@_w'] || 0, 10))
1944
-
1945
- const trsArr = tblObj['a:tr'] || []
1946
- const rowHeights = this.#calculateRowHeights(slideIndex, tableId, slideManager, tblObj, false)
1947
-
1948
- const parent = this.getMergeParent(slideIndex, tableId, rowIndex, colIndex, slideManager)
1949
- const pr = parent.row
1950
- const pc = parent.col
1951
-
1952
- let cellLeft = tableX
1953
- for (let idx = 0; idx < pc; idx++) {
1954
- cellLeft += colWidths[idx] || 0
1955
- }
1956
-
1957
- let cellTop = tableY
1958
- for (let idx = 0; idx < pr; idx++) {
1959
- cellTop += rowHeights[idx] || 0
1960
- }
1961
-
1962
- const parentCell = trsArr[pr]?.['a:tc']?.[pc]
1963
- const gridSpan = parentCell?.['@_gridSpan'] ? parseInt(parentCell['@_gridSpan'], 10) : 1
1964
- const rowSpan = parentCell?.['@_rowSpan'] ? parseInt(parentCell['@_rowSpan'], 10) : 1
1965
-
1966
- let cellWidth = 0
1967
- for (let idx = 0; idx < gridSpan; idx++) {
1968
- cellWidth += colWidths[pc + idx] || 0
1969
- }
1893
+ const { resolvedTableId } = this.#getTableContext(slideIndex, tableId, slideManager)
1970
1894
 
1971
- let cellHeight = 0
1972
- for (let idx = 0; idx < rowSpan; idx++) {
1973
- cellHeight += rowHeights[pr + idx] || 0
1895
+ const bounds = this.getCellBounds(slideIndex, tableId, rowIndex, colIndex, slideManager)
1896
+ if (!bounds) {
1897
+ throw new PPTXError(`Could not calculate bounds for cell (${rowIndex}, ${colIndex})`)
1974
1898
  }
1975
1899
 
1976
- const bounds = { left: cellLeft, top: cellTop, width: cellWidth, height: cellHeight }
1977
-
1978
1900
  const shapes = shapeManager.getShapes(slideIndex, slideManager)
1979
1901
  const prefix = `cellshape_${resolvedTableId}_${rowIndex}_${colIndex}_`
1980
1902
  let maxShapeIndex = -1
@@ -2011,11 +1933,7 @@ class TableManager {
2011
1933
  slideManager,
2012
1934
  shapeManager
2013
1935
  ) {
2014
- const { tblObj, frameObj, resolvedTableId } = this.#getTableContext(
2015
- slideIndex,
2016
- tableId,
2017
- slideManager
2018
- )
1936
+ const { resolvedTableId } = this.#getTableContext(slideIndex, tableId, slideManager)
2019
1937
 
2020
1938
  const shapes = shapeManager.getShapes(slideIndex, slideManager)
2021
1939
  const prefix = `cellshape_${resolvedTableId}_${rowIndex}_${colIndex}_${shapeIndex}`
@@ -2031,47 +1949,11 @@ class TableManager {
2031
1949
  shapeManager.deleteShape(slideIndex, s.name, slideManager)
2032
1950
  }
2033
1951
 
2034
- const xfrm = frameObj['p:xfrm']
2035
- const tableX = xfrm?.['a:off']?.['@_x'] ? parseInt(xfrm['a:off']['@_x'], 10) : 0
2036
- const tableY = xfrm?.['a:off']?.['@_y'] ? parseInt(xfrm['a:off']['@_y'], 10) : 0
2037
-
2038
- const gridCols = tblObj['a:tblGrid']?.['a:gridCol'] || []
2039
- const gridColsArr = Array.isArray(gridCols) ? gridCols : [gridCols]
2040
- const colWidths = gridColsArr.map(col => parseInt(col['@_w'] || 0, 10))
2041
-
2042
- const trsArr = tblObj['a:tr'] || []
2043
- const rowHeights = this.#calculateRowHeights(slideIndex, tableId, slideManager, tblObj, false)
2044
-
2045
- const parent = this.getMergeParent(slideIndex, tableId, rowIndex, colIndex, slideManager)
2046
- const pr = parent.row
2047
- const pc = parent.col
2048
-
2049
- let cellLeft = tableX
2050
- for (let idx = 0; idx < pc; idx++) {
2051
- cellLeft += colWidths[idx] || 0
2052
- }
2053
-
2054
- let cellTop = tableY
2055
- for (let idx = 0; idx < pr; idx++) {
2056
- cellTop += rowHeights[idx] || 0
2057
- }
2058
-
2059
- const parentCell = trsArr[pr]?.['a:tc']?.[pc]
2060
- const gridSpan = parentCell?.['@_gridSpan'] ? parseInt(parentCell['@_gridSpan'], 10) : 1
2061
- const rowSpan = parentCell?.['@_rowSpan'] ? parseInt(parentCell['@_rowSpan'], 10) : 1
2062
-
2063
- let cellWidth = 0
2064
- for (let idx = 0; idx < gridSpan; idx++) {
2065
- cellWidth += colWidths[pc + idx] || 0
2066
- }
2067
-
2068
- let cellHeight = 0
2069
- for (let idx = 0; idx < rowSpan; idx++) {
2070
- cellHeight += rowHeights[pr + idx] || 0
1952
+ const bounds = this.getCellBounds(slideIndex, tableId, rowIndex, colIndex, slideManager)
1953
+ if (!bounds) {
1954
+ throw new PPTXError(`Could not calculate bounds for cell (${rowIndex}, ${colIndex})`)
2071
1955
  }
2072
1956
 
2073
- const bounds = { left: cellLeft, top: cellTop, width: cellWidth, height: cellHeight }
2074
-
2075
1957
  const expandedConfigs = this.#expandCellShape(options, bounds)
2076
1958
 
2077
1959
  expandedConfigs.forEach((expandedConfig, expIdx) => {
@@ -646,7 +646,11 @@ class ZipManager {
646
646
  await fs.writeFile(targetPath, this.#dirtyBinaryFiles.get(relPath))
647
647
  } else {
648
648
  const srcPath = path.join(this.#folderRoot, relPath)
649
- await fs.copy(srcPath, targetPath)
649
+ const resolvedSrc = path.resolve(srcPath)
650
+ const resolvedDest = path.resolve(targetPath)
651
+ if (resolvedSrc !== resolvedDest) {
652
+ await fs.copy(srcPath, targetPath)
653
+ }
650
654
  }
651
655
  }
652
656
  } else {