New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@jsreport/jsreport-xlsx

Package Overview
Dependencies
Maintainers
2
Versions
18
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@jsreport/jsreport-xlsx - npm Package Compare versions

Comparing version 4.0.0 to 4.0.1

4

jsreport.config.js

@@ -27,5 +27,5 @@ const office = require('@jsreport/office')

requires: {
core: '3.x.x',
studio: '3.x.x'
core: '4.x.x',
studio: '4.x.x'
}
}

@@ -22,88 +22,2 @@ const { DOMParser } = require('@xmldom/xmldom')

const outOfLoopItems = new Map()
sheetFile.data = await recursiveStringReplaceAsync(
sheetFile.data.toString(),
'<outOfLoop>',
'</outOfLoop>',
'g',
async (val, content, hasNestedMatch) => {
if (hasNestedMatch) {
return val
}
const doc = new DOMParser().parseFromString(val)
const outOfLoopEl = doc.documentElement
const childNodes = nodeListToArray(outOfLoopEl.childNodes)
const pendingReplacements = []
let itemIndex
for (const childNode of childNodes) {
if (childNode.nodeName === 'item') {
itemIndex = parseInt(childNode.textContent, 10)
} else if (childNode.nodeName === 'data') {
const dataChildNodes = nodeListToArray(childNode.childNodes)
const generatedEls = []
for (const dataChildNode of dataChildNodes) {
generatedEls.push(dataChildNode.cloneNode(true))
}
pendingReplacements.push({
elements: generatedEls
})
}
}
if (!outOfLoopItems.has(itemIndex)) {
outOfLoopItems.set(itemIndex, { pendingReplacements: [] })
}
outOfLoopItems.get(itemIndex).pendingReplacements.push(...pendingReplacements)
return ''
}
)
sheetFile.data = await recursiveStringReplaceAsync(
sheetFile.data.toString(),
'<outOfLoopPlaceholder>',
'</outOfLoopPlaceholder>',
'g',
async (val, content, hasNestedMatch) => {
if (hasNestedMatch) {
return val
}
const doc = new DOMParser().parseFromString(val)
const itemEl = doc.documentElement.firstChild
const itemIndex = parseInt(itemEl.textContent, 10)
const pendingReplacements = outOfLoopItems.get(itemIndex)?.pendingReplacements
if (pendingReplacements == null) {
throw new Error(`outOfLoopPlaceholder can not find metadata with index "${itemIndex}"`)
}
const pendingReplacement = pendingReplacements.shift()
if (pendingReplacement == null) {
throw new Error('outOfLoopPlaceholder does not match with pending elements to replace')
}
const generatedEls = pendingReplacement.elements
let newContent = ''
if (generatedEls == null || generatedEls.length === 0) {
return newContent
}
for (const generatedEl of generatedEls) {
newContent += serializeXml(generatedEl)
}
return newContent
}
)
const updatedCalcChainCountMap = new Map()

@@ -110,0 +24,0 @@

@@ -30,324 +30,285 @@ const path = require('path')

const graphicDataEl = drawingDoc.getElementsByTagName('a:graphicData')[0]
// drawing in xlsx are in separate files (not inline), this means that it is possible to
// have multiple charts in a single drawing,
// so we assume there is going to be more than one chart from the drawing.
// this was also validated by verifying the output in Excel by duplicating
// a chart, it always create a drawing with multiple chart definitions.
const graphicDataEls = nodeListToArray(drawingDoc.getElementsByTagName('a:graphicData'))
if (
graphicDataEl == null
) {
return
}
for (const graphicDataEl of graphicDataEls) {
if (
graphicDataEl.getAttribute('uri') !== 'http://schemas.openxmlformats.org/drawingml/2006/chart' &&
graphicDataEl.getAttribute('uri') !== 'http://schemas.microsoft.com/office/drawing/2014/chartex'
) {
continue
}
if (
graphicDataEl.getAttribute('uri') !== 'http://schemas.openxmlformats.org/drawingml/2006/chart' &&
graphicDataEl.getAttribute('uri') !== 'http://schemas.microsoft.com/office/drawing/2014/chartex'
) {
return
}
const graphicDataChartEl = nodeListToArray(graphicDataEl.childNodes).find((el) => {
let found = false
const graphicDataChartEl = nodeListToArray(graphicDataEl.childNodes).find((el) => {
let found = false
found = (
el.nodeName === 'c:chart' &&
el.getAttribute('xmlns:c') === 'http://schemas.openxmlformats.org/drawingml/2006/chart' &&
el.getAttribute('xmlns:r') === 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'
)
if (!found) {
found = (
el.nodeName === 'cx:chart' &&
el.getAttribute('xmlns:cx') === 'http://schemas.microsoft.com/office/drawing/2014/chartex' &&
el.nodeName === 'c:chart' &&
el.getAttribute('xmlns:c') === 'http://schemas.openxmlformats.org/drawingml/2006/chart' &&
el.getAttribute('xmlns:r') === 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'
)
}
return found
})
if (!found) {
found = (
el.nodeName === 'cx:chart' &&
el.getAttribute('xmlns:cx') === 'http://schemas.microsoft.com/office/drawing/2014/chartex' &&
el.getAttribute('xmlns:r') === 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'
)
}
if (graphicDataChartEl == null) {
return
}
return found
})
const chartRelId = graphicDataChartEl.getAttribute('r:id')
if (graphicDataChartEl == null) {
continue
}
const drawingRelsPath = path.posix.join(path.posix.dirname(drawingPath), '_rels', `${path.posix.basename(drawingPath)}.rels`)
const chartRelId = graphicDataChartEl.getAttribute('r:id')
const drawingRelsDoc = files.find((file) => file.path === drawingRelsPath)?.doc
const drawingRelsPath = path.posix.join(path.posix.dirname(drawingPath), '_rels', `${path.posix.basename(drawingPath)}.rels`)
if (drawingRelsDoc == null) {
return
}
const drawingRelsDoc = files.find((file) => file.path === drawingRelsPath)?.doc
const drawingRelationshipEls = nodeListToArray(drawingRelsDoc.getElementsByTagName('Relationship'))
if (drawingRelsDoc == null) {
continue
}
const chartRelationshipEl = drawingRelationshipEls.find((r) => r.getAttribute('Id') === chartRelId)
const drawingRelationshipEls = nodeListToArray(drawingRelsDoc.getElementsByTagName('Relationship'))
if (chartRelationshipEl == null) {
return
}
const chartRelationshipEl = drawingRelationshipEls.find((r) => r.getAttribute('Id') === chartRelId)
if (
graphicDataChartEl.prefix === 'cx' &&
chartRelationshipEl.getAttribute('Type') !== 'http://schemas.microsoft.com/office/2014/relationships/chartEx'
) {
return
}
if (chartRelationshipEl == null) {
continue
}
if (
graphicDataChartEl.prefix === 'c' &&
chartRelationshipEl.getAttribute('Type') !== 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart'
) {
return
}
if (
graphicDataChartEl.prefix === 'cx' &&
chartRelationshipEl.getAttribute('Type') !== 'http://schemas.microsoft.com/office/2014/relationships/chartEx'
) {
continue
}
const chartPath = path.posix.join(path.posix.dirname(sheetFilepath), chartRelationshipEl.getAttribute('Target'))
const chartFile = files.find((file) => file.path === chartPath)
const chartDoc = chartFile?.doc
if (
graphicDataChartEl.prefix === 'c' &&
chartRelationshipEl.getAttribute('Type') !== 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart'
) {
continue
}
if (chartDoc == null) {
return
}
const chartPath = path.posix.join(path.posix.dirname(sheetFilepath), chartRelationshipEl.getAttribute('Target'))
const chartFile = files.find((file) => file.path === chartPath)
const chartDoc = chartFile?.doc
let chartEl
if (chartDoc == null) {
continue
}
if (graphicDataChartEl.prefix === 'cx') {
chartEl = chartDoc.getElementsByTagName('cx:chart')[0]
} else {
chartEl = chartDoc.getElementsByTagName('c:chart')[0]
}
let chartEl
if (chartEl == null) {
return
}
if (graphicDataChartEl.prefix === 'cx') {
chartEl = chartDoc.getElementsByTagName('cx:chart')[0]
} else {
chartEl = chartDoc.getElementsByTagName('c:chart')[0]
}
const chartTitles = nodeListToArray(chartEl.getElementsByTagName(`${graphicDataChartEl.prefix}:title`))
const chartMainTitleEl = chartTitles[0]
if (chartEl == null) {
continue
}
if (chartMainTitleEl == null) {
return
}
const chartTitles = nodeListToArray(chartEl.getElementsByTagName(`${graphicDataChartEl.prefix}:title`))
const chartMainTitleEl = chartTitles[0]
const chartMainTitleTextElements = nodeListToArray(chartMainTitleEl.getElementsByTagName('a:t'))
for (const chartMainTitleTextEl of chartMainTitleTextElements) {
const textContent = chartMainTitleTextEl.textContent
if (!textContent.includes('$xlsxChart')) {
if (chartMainTitleEl == null) {
continue
}
const match = textContent.match(/\$xlsxChart([^$]*)\$/)
const chartConfig = JSON.parse(Buffer.from(match[1], 'base64').toString())
const chartMainTitleTextElements = nodeListToArray(chartMainTitleEl.getElementsByTagName('a:t'))
// remove chart helper text
chartMainTitleTextEl.textContent = chartMainTitleTextEl.textContent.replace(match[0], '')
for (const chartMainTitleTextEl of chartMainTitleTextElements) {
const textContent = chartMainTitleTextEl.textContent
if (graphicDataChartEl.prefix === 'cx') {
const chartSeriesEl = chartDoc.getElementsByTagName('cx:plotArea')[0].getElementsByTagName('cx:series')[0]
const chartType = chartSeriesEl.getAttribute('layoutId')
const supportedCharts = ['waterfall', 'treemap', 'sunburst', 'funnel', 'clusteredColumn']
if (!supportedCharts.includes(chartType)) {
throw new Error(`"${chartType}" type (chartEx) is not supported`)
if (!textContent.includes('$xlsxChart')) {
continue
}
const chartDataEl = chartDoc.getElementsByTagName('cx:chartData')[0]
const existingDataItemsElements = nodeListToArray(chartDataEl.getElementsByTagName('cx:data'))
const dataPlaceholderEl = chartDoc.createElement('xlsxChartexDataReplace')
const seriesPlaceholderEl = chartDoc.createElement('xlsxChartexSeriesReplace')
const match = textContent.match(/\$xlsxChart([^$]*)\$/)
const chartConfig = JSON.parse(Buffer.from(match[1], 'base64').toString())
dataPlaceholderEl.textContent = 'sample'
seriesPlaceholderEl.textContent = 'sample'
// remove chart helper text
chartMainTitleTextEl.textContent = chartMainTitleTextEl.textContent.replace(match[0], '')
chartDataEl.appendChild(dataPlaceholderEl)
chartSeriesEl.parentNode.insertBefore(seriesPlaceholderEl, chartSeriesEl.nextSibling)
if (graphicDataChartEl.prefix === 'cx') {
const chartSeriesEl = chartDoc.getElementsByTagName('cx:plotArea')[0].getElementsByTagName('cx:series')[0]
const chartType = chartSeriesEl.getAttribute('layoutId')
const supportedCharts = ['waterfall', 'treemap', 'sunburst', 'funnel', 'clusteredColumn']
existingDataItemsElements.forEach((dataItemEl) => {
dataItemEl.parentNode.removeChild(dataItemEl)
})
if (!supportedCharts.includes(chartType)) {
throw new Error(`"${chartType}" type (chartEx) is not supported`)
}
chartSeriesEl.parentNode.removeChild(chartSeriesEl)
const chartDataEl = chartDoc.getElementsByTagName('cx:chartData')[0]
const existingDataItemsElements = nodeListToArray(chartDataEl.getElementsByTagName('cx:data'))
const dataPlaceholderEl = chartDoc.createElement('xlsxChartexDataReplace')
const seriesPlaceholderEl = chartDoc.createElement('xlsxChartexSeriesReplace')
chartFile.data = serializeXml(chartFile.doc)
chartFile.serializeFromDoc = false
dataPlaceholderEl.textContent = 'sample'
seriesPlaceholderEl.textContent = 'sample'
let newDataItemElement = existingDataItemsElements[0].cloneNode(true)
chartDataEl.appendChild(dataPlaceholderEl)
chartSeriesEl.parentNode.insertBefore(seriesPlaceholderEl, chartSeriesEl.nextSibling)
newDataItemElement.setAttribute('id', 0)
existingDataItemsElements.forEach((dataItemEl) => {
dataItemEl.parentNode.removeChild(dataItemEl)
})
addChartexItem(chartDoc, {
name: 'cx:strDim',
type: chartType,
data: Array.isArray(chartConfig.data.labels[0]) ? chartConfig.data.labels.map((subLabels) => ({ items: subLabels })) : [{ items: chartConfig.data.labels }]
}, newDataItemElement)
chartSeriesEl.parentNode.removeChild(chartSeriesEl)
addChartexItem(chartDoc, { name: 'cx:numDim', type: chartType, data: [{ items: chartConfig.data.datasets[0].data || [] }] }, newDataItemElement)
chartFile.data = serializeXml(chartFile.doc)
chartFile.serializeFromDoc = false
let newChartSeriesElement = chartSeriesEl.cloneNode(true)
let newDataItemElement = existingDataItemsElements[0].cloneNode(true)
addChartexItem(chartDoc, { name: 'cx:tx', data: chartConfig.data.datasets[0].label || '' }, newChartSeriesElement)
addChartexItem(chartDoc, { name: 'cx:dataId', data: newDataItemElement.getAttribute('id') }, newChartSeriesElement)
newDataItemElement.setAttribute('id', 0)
newDataItemElement = serializeXml(newDataItemElement)
newChartSeriesElement = serializeXml(newChartSeriesElement)
addChartexItem(chartDoc, {
name: 'cx:strDim',
type: chartType,
data: Array.isArray(chartConfig.data.labels[0]) ? chartConfig.data.labels.map((subLabels) => ({ items: subLabels })) : [{ items: chartConfig.data.labels }]
}, newDataItemElement)
chartFile.data = chartFile.data.replace(/<xlsxChartexDataReplace[^>]*>[^]*?(?=<\/xlsxChartexDataReplace>)<\/xlsxChartexDataReplace>/g, newDataItemElement)
chartFile.data = chartFile.data.replace(/<xlsxChartexSeriesReplace[^>]*>[^]*?(?=<\/xlsxChartexSeriesReplace>)<\/xlsxChartexSeriesReplace>/g, newChartSeriesElement)
} else {
const chartPlotAreaEl = chartDoc.getElementsByTagName('c:plotArea')[0]
addChartexItem(chartDoc, { name: 'cx:numDim', type: chartType, data: [{ items: chartConfig.data.datasets[0].data || [] }] }, newDataItemElement)
const supportedCharts = [
'barChart', 'lineChart',
'stockChart', 'scatterChart', 'bubbleChart'
// 'areaChart', 'area3DChart', 'barChart', 'bar3DChart', 'lineChart', 'line3DChart',
// 'pieChart', 'pie3DChart', 'doughnutChart', 'stockChart', 'scatterChart', 'bubbleChart'
]
let newChartSeriesElement = chartSeriesEl.cloneNode(true)
const existingChartSeriesElements = nodeListToArray(chartDoc.getElementsByTagName('c:ser'))
addChartexItem(chartDoc, { name: 'cx:tx', data: chartConfig.data.datasets[0].label || '' }, newChartSeriesElement)
addChartexItem(chartDoc, { name: 'cx:dataId', data: newDataItemElement.getAttribute('id') }, newChartSeriesElement)
if (existingChartSeriesElements.length === 0) {
throw new Error(`Base chart in xlsx must have at least one data serie defined, ref: "${chartPath}"`)
}
newDataItemElement = serializeXml(newDataItemElement)
newChartSeriesElement = serializeXml(newChartSeriesElement)
if (chartConfig.options != null) {
const existingAxesNodes = []
chartFile.data = chartFile.data.replace(/<xlsxChartexDataReplace[^>]*>[^]*?(?=<\/xlsxChartexDataReplace>)<\/xlsxChartexDataReplace>/g, newDataItemElement)
chartFile.data = chartFile.data.replace(/<xlsxChartexSeriesReplace[^>]*>[^]*?(?=<\/xlsxChartexSeriesReplace>)<\/xlsxChartexSeriesReplace>/g, newChartSeriesElement)
} else {
const chartPlotAreaEl = chartDoc.getElementsByTagName('c:plotArea')[0]
for (let i = 0; i < chartPlotAreaEl.childNodes.length; i++) {
const currentNode = chartPlotAreaEl.childNodes[i]
const supportedCharts = [
'barChart', 'lineChart',
'stockChart', 'scatterChart', 'bubbleChart'
// 'areaChart', 'area3DChart', 'barChart', 'bar3DChart', 'lineChart', 'line3DChart',
// 'pieChart', 'pie3DChart', 'doughnutChart', 'stockChart', 'scatterChart', 'bubbleChart'
]
if (currentNode.nodeName === 'c:catAx' || currentNode.nodeName === 'c:valAx') {
existingAxesNodes.push(currentNode)
}
const existingChartSeriesElements = nodeListToArray(chartDoc.getElementsByTagName('c:ser'))
if (existingChartSeriesElements.length === 0) {
throw new Error(`Base chart in xlsx must have at least one data serie defined, ref: "${chartPath}"`)
}
if (chartConfig.options.scales && Array.isArray(chartConfig.options.scales.xAxes) && chartConfig.options.scales.xAxes.length > 0) {
const primaryXAxisConfig = chartConfig.options.scales.xAxes[0]
const secondaryXAxisConfig = chartConfig.options.scales.xAxes[1]
const primaryXAxisEl = existingAxesNodes[0]
const secondaryXAxisEl = existingAxesNodes[3]
if (chartConfig.options != null) {
const existingAxesNodes = []
if (primaryXAxisConfig && primaryXAxisEl) {
configureAxis(chartDoc, primaryXAxisConfig, primaryXAxisEl)
}
for (let i = 0; i < chartPlotAreaEl.childNodes.length; i++) {
const currentNode = chartPlotAreaEl.childNodes[i]
if (secondaryXAxisConfig && secondaryXAxisEl) {
configureAxis(chartDoc, secondaryXAxisConfig, secondaryXAxisEl)
if (currentNode.nodeName === 'c:catAx' || currentNode.nodeName === 'c:valAx') {
existingAxesNodes.push(currentNode)
}
}
}
if (chartConfig.options.scales && Array.isArray(chartConfig.options.scales.yAxes) && chartConfig.options.scales.yAxes.length > 0) {
const primaryYAxisConfig = chartConfig.options.scales.yAxes[0]
const secondaryYAxisConfig = chartConfig.options.scales.yAxes[1]
const primaryYAxisEl = existingAxesNodes[1]
const secondaryYAxisEl = existingAxesNodes[2]
if (chartConfig.options.scales && Array.isArray(chartConfig.options.scales.xAxes) && chartConfig.options.scales.xAxes.length > 0) {
const primaryXAxisConfig = chartConfig.options.scales.xAxes[0]
const secondaryXAxisConfig = chartConfig.options.scales.xAxes[1]
const primaryXAxisEl = existingAxesNodes[0]
const secondaryXAxisEl = existingAxesNodes[3]
if (primaryYAxisConfig && primaryYAxisEl) {
configureAxis(chartDoc, primaryYAxisConfig, primaryYAxisEl)
}
if (primaryXAxisConfig && primaryXAxisEl) {
configureAxis(chartDoc, primaryXAxisConfig, primaryXAxisEl)
}
if (secondaryYAxisConfig && secondaryYAxisEl) {
configureAxis(chartDoc, secondaryYAxisConfig, secondaryYAxisEl)
if (secondaryXAxisConfig && secondaryXAxisEl) {
configureAxis(chartDoc, secondaryXAxisConfig, secondaryXAxisEl)
}
}
}
// NOTE: option "storeDataInSheet" not supported for now
// it requires to complete the implementation to put
// cell references in chart series data
delete chartConfig.options.storeDataInSheet
if (chartConfig.options.scales && Array.isArray(chartConfig.options.scales.yAxes) && chartConfig.options.scales.yAxes.length > 0) {
const primaryYAxisConfig = chartConfig.options.scales.yAxes[0]
const secondaryYAxisConfig = chartConfig.options.scales.yAxes[1]
const primaryYAxisEl = existingAxesNodes[1]
const secondaryYAxisEl = existingAxesNodes[2]
if (chartConfig.options.storeDataInSheet === true) {
// creating new sheet to store the data for the chart
const newSheetInfo = getNewSheet(files)
if (primaryYAxisConfig && primaryYAxisEl) {
configureAxis(chartDoc, primaryYAxisConfig, primaryYAxisEl)
}
// transform the chart data to the order in which the sheet data
// expects to be
const sheetData = [
[null, ...chartConfig.data.datasets.map((d) => d.label)],
...chartConfig.data.labels.map((label, idx) => {
const arr = [label]
if (secondaryYAxisConfig && secondaryYAxisEl) {
configureAxis(chartDoc, secondaryYAxisConfig, secondaryYAxisEl)
}
}
for (const dataset of chartConfig.data.datasets) {
const datasetVal = dataset.data[idx]
// NOTE: option "storeDataInSheet" not supported for now
// it requires to complete the implementation to put
// cell references in chart series data
delete chartConfig.options.storeDataInSheet
if (datasetVal != null) {
arr.push(datasetVal)
} else {
arr.push(null)
}
}
if (chartConfig.options.storeDataInSheet === true) {
// creating new sheet to store the data for the chart
const newSheetInfo = getNewSheet(files)
return arr
})
]
// transform the chart data to the order in which the sheet data
// expects to be
const sheetData = [
[null, ...chartConfig.data.datasets.map((d) => d.label)],
...chartConfig.data.labels.map((label, idx) => {
const arr = [label]
addDataToSheet(newSheetInfo, sheetData)
addNewSheetToWorkbook(newSheetInfo, files)
}
}
for (const dataset of chartConfig.data.datasets) {
const datasetVal = dataset.data[idx]
const lastExistingChartSerieEl = existingChartSeriesElements[existingChartSeriesElements.length - 1]
let lastChartTypeContentEl
if (datasetVal != null) {
arr.push(datasetVal)
} else {
arr.push(null)
}
}
for (const [serieIdx, serieEl] of existingChartSeriesElements.entries()) {
const chartTypeContentEl = serieEl.parentNode
const chartType = chartTypeContentEl.localName
return arr
})
]
lastChartTypeContentEl = chartTypeContentEl
if (!supportedCharts.includes(chartType)) {
throw new Error(`Chart "${chartType}" type is not supported, ref: "${chartPath}"`)
addDataToSheet(newSheetInfo, sheetData)
addNewSheetToWorkbook(newSheetInfo, files)
}
}
const refNode = serieEl.nextSibling
const lastExistingChartSerieEl = existingChartSeriesElements[existingChartSeriesElements.length - 1]
let lastChartTypeContentEl
serieEl.parentNode.removeChild(serieEl)
for (const [serieIdx, serieEl] of existingChartSeriesElements.entries()) {
const chartTypeContentEl = serieEl.parentNode
const chartType = chartTypeContentEl.localName
const currentDataset = chartConfig.data.datasets[serieIdx]
lastChartTypeContentEl = chartTypeContentEl
if (!currentDataset) {
continue
}
if (!supportedCharts.includes(chartType)) {
throw new Error(`Chart "${chartType}" type is not supported, ref: "${chartPath}"`)
}
const newChartSerieNode = serieEl.cloneNode(true)
const refNode = serieEl.nextSibling
prepareChartSerie(chartDoc, chartType, newChartSerieNode, {
serieIdx,
serieLabel: currentDataset.label,
generalLabels: chartConfig.data.labels,
dataErrors: currentDataset.dataErrors,
dataLabels: currentDataset.dataLabels,
dataValues: currentDataset.data
})
serieEl.parentNode.removeChild(serieEl)
refNode.parentNode.insertBefore(newChartSerieNode, refNode)
}
const currentDataset = chartConfig.data.datasets[serieIdx]
if (chartConfig.data.datasets.length > existingChartSeriesElements.length) {
const lastSerieIdx = existingChartSeriesElements.length - 1
const seriesInLastChartNodes = nodeListToArray(lastChartTypeContentEl.getElementsByTagName('c:ser'))
const chartType = lastChartTypeContentEl.localName
const remainingDatasets = chartConfig.data.datasets.slice(existingChartSeriesElements.length)
const refNode = seriesInLastChartNodes[seriesInLastChartNodes.length - 1].nextSibling
for (const [remainingIdx, currentDataset] of remainingDatasets.entries()) {
// create based on the last serie, but without predefined shape properties
const newChartSerieNode = lastExistingChartSerieEl.cloneNode(true)
const shapePropertiesEl = findChildNode('c:spPr', newChartSerieNode)
if (shapePropertiesEl) {
shapePropertiesEl.parentNode.removeChild(shapePropertiesEl)
if (!currentDataset) {
continue
}
const markerEl = findChildNode('c:marker', newChartSerieNode)
const newChartSerieNode = serieEl.cloneNode(true)
if (markerEl) {
const symbolEl = findChildNode('c:symbol', markerEl)
if (symbolEl && symbolEl.getAttribute('val') !== 'none') {
symbolEl.setAttribute('val', 'none')
}
}
prepareChartSerie(chartDoc, chartType, newChartSerieNode, {
serieIdx: lastSerieIdx + remainingIdx + 1,
serieIdx,
serieLabel: currentDataset.label,

@@ -362,6 +323,46 @@ generalLabels: chartConfig.data.labels,

}
if (chartConfig.data.datasets.length > existingChartSeriesElements.length) {
const lastSerieIdx = existingChartSeriesElements.length - 1
const seriesInLastChartNodes = nodeListToArray(lastChartTypeContentEl.getElementsByTagName('c:ser'))
const chartType = lastChartTypeContentEl.localName
const remainingDatasets = chartConfig.data.datasets.slice(existingChartSeriesElements.length)
const refNode = seriesInLastChartNodes[seriesInLastChartNodes.length - 1].nextSibling
for (const [remainingIdx, currentDataset] of remainingDatasets.entries()) {
// create based on the last serie, but without predefined shape properties
const newChartSerieNode = lastExistingChartSerieEl.cloneNode(true)
const shapePropertiesEl = findChildNode('c:spPr', newChartSerieNode)
if (shapePropertiesEl) {
shapePropertiesEl.parentNode.removeChild(shapePropertiesEl)
}
const markerEl = findChildNode('c:marker', newChartSerieNode)
if (markerEl) {
const symbolEl = findChildNode('c:symbol', markerEl)
if (symbolEl && symbolEl.getAttribute('val') !== 'none') {
symbolEl.setAttribute('val', 'none')
}
}
prepareChartSerie(chartDoc, chartType, newChartSerieNode, {
serieIdx: lastSerieIdx + remainingIdx + 1,
serieLabel: currentDataset.label,
generalLabels: chartConfig.data.labels,
dataErrors: currentDataset.dataErrors,
dataLabels: currentDataset.dataLabels,
dataValues: currentDataset.data
})
refNode.parentNode.insertBefore(newChartSerieNode, refNode)
}
}
chartFile.data = serializeXml(chartFile.doc)
chartFile.serializeFromDoc = false
}
chartFile.data = serializeXml(chartFile.doc)
chartFile.serializeFromDoc = false
}

@@ -368,0 +369,0 @@ }

const path = require('path')
const { num2col } = require('xlsx-coordinates')
const { nodeListToArray, isWorksheetFile, isWorksheetRelsFile, getClosestEl, getSheetInfo } = require('../../utils')
const { nodeListToArray, isWorksheetFile, isWorksheetRelsFile, getSheetInfo } = require('../../utils')
const { parseCellRef, evaluateCellRefsFromExpression } = require('../../cellUtils')

@@ -490,5 +490,6 @@ const startLoopRegexp = /{{#each ([^{}]{0,500})}}/

const match = matches[0]
const shouldEscape = !match[0].startsWith('{{{')
const expressionValue = match[2]
cellValueWrapperEl.textContent = `{{#xlsxSData type='cellValue' value=${expressionValue.includes(' ') ? `(${expressionValue})` : expressionValue}`
cellValueWrapperEl.textContent = `{{#xlsxSData type='cellValue' value=${expressionValue.includes(' ') ? `(${expressionValue})` : expressionValue}${shouldEscape ? ' escape=true' : ''}`
} else {

@@ -538,14 +539,10 @@ cellValueWrapperEl.textContent = "{{#xlsxSData type='cellValue'"

for (const { loopDetected, startingRowEl, endingRowEl, types } of outOfLoopElsToHandle) {
const sortedOutOfLoopElsToHandle = getOutOfLoopElsSortedByHierarchy(outOfLoopElsToHandle)
for (const { loopDetected, startingRowEl, endingRowEl, types } of sortedOutOfLoopElsToHandle) {
const loopLevel = loopDetected.hierarchyId.split('#').length
const isOuterLevel = loopLevel === 1
if (loopDetected.type === 'row' && loopDetected.children.length !== 0) {
throw new Error('Multiple nested row loops are not supported')
}
for (const type of types) {
const outOfLoopElement = sheetDoc.createElement('outOfLoop')
let itemEl = sheetDoc.createElement('item')
itemEl.textContent = outLoopItemIndex
const outOfLoopEl = sheetDoc.createElement('outOfLoop')

@@ -559,3 +556,2 @@ const rowHandlebarsWrapperText = type === 'left' ? startingRowEl.previousSibling.textContent : endingRowEl.previousSibling.textContent

toCloneEls.push(...getCellElsAndWrappersFrom(currentEl, 'previous'))
toCloneEls.reverse()

@@ -568,46 +564,30 @@ } else {

const newEl = toCloneEl.cloneNode(true)
outOfLoopElement.appendChild(newEl)
outOfLoopEl.appendChild(newEl)
toCloneEl.parentNode.removeChild(toCloneEl)
}
processOpeningTag(sheetDoc, outOfLoopElement.firstChild, rowHandlebarsWrapperText)
processClosingTag(sheetDoc, outOfLoopElement.lastChild, '{{/xlsxSData}}')
processOpeningTag(sheetDoc, outOfLoopEl.firstChild, rowHandlebarsWrapperText)
processClosingTag(sheetDoc, outOfLoopEl.lastChild, '{{/xlsxSData}}')
outOfLoopElement.insertBefore(itemEl, outOfLoopElement.firstChild)
processOpeningTag(sheetDoc, outOfLoopEl.firstChild, `{{#xlsxSData type='outOfLoop' item='${outLoopItemIndex}' }}`)
processClosingTag(sheetDoc, outOfLoopEl.lastChild, '{{/xlsxSData}}')
processOpeningTag(sheetDoc, outOfLoopElement.firstChild.nextSibling, `{{#xlsxSData type='outOfLoop' item='${outLoopItemIndex}' }}`)
processClosingTag(sheetDoc, outOfLoopElement.lastChild, '{{/xlsxSData}}')
const loopEdgeEl = loopDetected.blockStartEl
const loopEdgeEl = loopDetected.blockEndEl
const outOfLoopItemContentEls = nodeListToArray(outOfLoopEl.childNodes)
// we get the last outOfLoop element
const lastOutOfLoopElOrNull = getClosestEl(loopEdgeEl, (n) => (
n.nodeName !== 'outOfLoop' ||
(n.nodeName === 'outOfLoop' && n.nextSibling?.nodeName !== 'outOfLoop')
), 'next')
for (const contentEl of outOfLoopItemContentEls) {
loopEdgeEl.parentNode.insertBefore(
contentEl,
loopEdgeEl
)
}
loopEdgeEl.parentNode.insertBefore(
outOfLoopElement,
lastOutOfLoopElOrNull?.nodeName === 'outOfLoop' ? lastOutOfLoopElOrNull?.nextSibling : lastOutOfLoopElOrNull
)
const outOfLoopPlaceholderEl = sheetDoc.createElement('outOfLoopPlaceholder')
const outOfLoopPlaceholderElement = sheetDoc.createElement('outOfLoopPlaceholder')
itemEl = sheetDoc.createElement('item')
itemEl.textContent = outLoopItemIndex
const contentEl = sheetDoc.createElement('xlsxRemove')
contentEl.textContent = `{{xlsxSData type='outOfLoopPlaceholder' item='${outLoopItemIndex}' }}`
outOfLoopPlaceholderElement.appendChild(itemEl)
outOfLoopPlaceholderEl.appendChild(contentEl)
processOpeningTag(sheetDoc, outOfLoopPlaceholderElement.firstChild, `{{#xlsxSData type='outOfLoopPlaceholder' item='${outLoopItemIndex}' }}`)
processClosingTag(sheetDoc, outOfLoopPlaceholderElement.lastChild, '{{/xlsxSData}}')
if (type === 'left') {
const cellAndWrappers = getCellElAndWrappers(loopDetected.start.el)
loopDetected.start.el.parentNode.insertBefore(outOfLoopPlaceholderElement, cellAndWrappers[0])
} else {
const cellAndWrappers = getCellElAndWrappers(loopDetected.end.el)
loopDetected.end.el.parentNode.insertBefore(outOfLoopPlaceholderElement, cellAndWrappers[cellAndWrappers.length - 1].nextSibling)
}
outLoopItemIndex++
// we only want to conditionally render the outOfLoopPlaceholder items if the loop

@@ -617,5 +597,21 @@ // is at the outer level

// we include a if condition to preserve the cells that are before/after the each
processOpeningTag(sheetDoc, outOfLoopPlaceholderElement, `{{#if ${loopDetected.type === 'block' && type === 'right' ? '@last' : '@first'}}}`)
processClosingTag(sheetDoc, outOfLoopPlaceholderElement, '{{/if}}')
processOpeningTag(sheetDoc, outOfLoopPlaceholderEl.firstChild, `{{#if ${loopDetected.type === 'block' && type === 'right' ? '@last' : '@first'}}}`)
processClosingTag(sheetDoc, outOfLoopPlaceholderEl.lastChild, '{{/if}}')
}
const outOfLoopPlaceholderContentEls = nodeListToArray(outOfLoopPlaceholderEl.childNodes)
const cellAndWrappers = getCellElAndWrappers(type === 'left' ? loopDetected.start.el : loopDetected.end.el)
if (type === 'right') {
outOfLoopPlaceholderContentEls.reverse()
}
for (const contentEl of outOfLoopPlaceholderContentEls) {
if (type === 'left') {
loopDetected.start.el.parentNode.insertBefore(contentEl, cellAndWrappers[0])
} else {
loopDetected.end.el.parentNode.insertBefore(contentEl, cellAndWrappers[cellAndWrappers.length - 1].nextSibling)
}
}
outLoopItemIndex++
}

@@ -766,2 +762,67 @@ }

function getOutOfLoopElsSortedByHierarchy (outOfLoopElsToHandle) {
const hierarchy = new Map()
const hierarchyIdElMap = new Map()
for (let idx = 0; idx < outOfLoopElsToHandle.length; idx++) {
const outOfLoopElToHandle = outOfLoopElsToHandle[idx]
const hierarchyId = outOfLoopElToHandle.loopDetected.hierarchyId
hierarchyIdElMap.set(hierarchyId, outOfLoopElToHandle)
const parts = hierarchyId.split('#')
let currentHierarchy = hierarchy
for (let partIdx = 0; partIdx < parts.length; partIdx++) {
const occurrenceIdx = parseInt(parts[partIdx], 10)
const isLast = partIdx === parts.length - 1
if (!currentHierarchy.has(occurrenceIdx)) {
currentHierarchy.set(occurrenceIdx, {
children: new Map()
})
}
const currentItem = currentHierarchy.get(occurrenceIdx)
if (isLast) {
currentItem.match = hierarchyId
}
currentHierarchy = currentItem.children
}
}
const toArraySortedByOccurrence = (targetMap) => {
const sortedKeysByOccurrence = [...targetMap.keys()].sort((a, b) => a - b)
return sortedKeysByOccurrence.map((key) => targetMap.get(key))
}
const pending = toArraySortedByOccurrence(hierarchy)
const sortedHierarchyIds = []
while (pending.length > 0) {
const currentPending = pending.shift()
if (typeof currentPending === 'string') {
sortedHierarchyIds.push(currentPending)
continue
}
const item = currentPending
if (item.children.size > 0) {
pending.unshift(...toArraySortedByOccurrence(item.children))
}
if (item.match != null) {
pending.unshift(item.match)
}
}
const result = sortedHierarchyIds.map((hierarchyId) => hierarchyIdElMap.get(hierarchyId))
return result
}
function getLatestNotClosedLoop (loopsDetected) {

@@ -768,0 +829,0 @@ let loopFound

@@ -23,135 +23,136 @@ const path = require('path')

const graphicDataEl = drawingDoc.getElementsByTagName('a:graphicData')[0]
// drawing in xlsx are in separate files (not inline), this means that it is possible to
// have multiple charts in a single drawing,
// so we assume there is going to be more than one chart from the drawing.
// this was also validated by verifying the output in Excel by duplicating
// a chart, it always create a drawing with multiple chart definitions.
const graphicDataEls = nodeListToArray(drawingDoc.getElementsByTagName('a:graphicData'))
if (
graphicDataEl == null
) {
return
}
for (const graphicDataEl of graphicDataEls) {
if (
graphicDataEl.getAttribute('uri') !== 'http://schemas.openxmlformats.org/drawingml/2006/chart' &&
graphicDataEl.getAttribute('uri') !== 'http://schemas.microsoft.com/office/drawing/2014/chartex'
) {
continue
}
if (
graphicDataEl.getAttribute('uri') !== 'http://schemas.openxmlformats.org/drawingml/2006/chart' &&
graphicDataEl.getAttribute('uri') !== 'http://schemas.microsoft.com/office/drawing/2014/chartex'
) {
return
}
const graphicDataChartEl = nodeListToArray(graphicDataEl.childNodes).find((el) => {
let found = false
const graphicDataChartEl = nodeListToArray(graphicDataEl.childNodes).find((el) => {
let found = false
found = (
el.nodeName === 'c:chart' &&
el.getAttribute('xmlns:c') === 'http://schemas.openxmlformats.org/drawingml/2006/chart' &&
el.getAttribute('xmlns:r') === 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'
)
if (!found) {
found = (
el.nodeName === 'cx:chart' &&
el.getAttribute('xmlns:cx') === 'http://schemas.microsoft.com/office/drawing/2014/chartex' &&
el.nodeName === 'c:chart' &&
el.getAttribute('xmlns:c') === 'http://schemas.openxmlformats.org/drawingml/2006/chart' &&
el.getAttribute('xmlns:r') === 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'
)
}
return found
})
if (!found) {
found = (
el.nodeName === 'cx:chart' &&
el.getAttribute('xmlns:cx') === 'http://schemas.microsoft.com/office/drawing/2014/chartex' &&
el.getAttribute('xmlns:r') === 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'
)
}
if (graphicDataChartEl == null) {
return
}
return found
})
const chartRelId = graphicDataChartEl.getAttribute('r:id')
if (graphicDataChartEl == null) {
continue
}
const drawingRelsPath = path.posix.join(path.posix.dirname(drawingPath), '_rels', `${path.posix.basename(drawingPath)}.rels`)
const chartRelId = graphicDataChartEl.getAttribute('r:id')
const drawingRelsDoc = files.find((file) => file.path === drawingRelsPath)?.doc
const drawingRelsPath = path.posix.join(path.posix.dirname(drawingPath), '_rels', `${path.posix.basename(drawingPath)}.rels`)
if (drawingRelsDoc == null) {
return
}
const drawingRelsDoc = files.find((file) => file.path === drawingRelsPath)?.doc
const drawingRelationshipEls = nodeListToArray(drawingRelsDoc.getElementsByTagName('Relationship'))
if (drawingRelsDoc == null) {
continue
}
const chartRelationshipEl = drawingRelationshipEls.find((r) => r.getAttribute('Id') === chartRelId)
const drawingRelationshipEls = nodeListToArray(drawingRelsDoc.getElementsByTagName('Relationship'))
if (chartRelationshipEl == null) {
return
}
const chartRelationshipEl = drawingRelationshipEls.find((r) => r.getAttribute('Id') === chartRelId)
if (
graphicDataChartEl.prefix === 'cx' &&
chartRelationshipEl.getAttribute('Type') !== 'http://schemas.microsoft.com/office/2014/relationships/chartEx'
) {
return
}
if (chartRelationshipEl == null) {
continue
}
if (
graphicDataChartEl.prefix === 'c' &&
chartRelationshipEl.getAttribute('Type') !== 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart'
) {
return
}
if (
graphicDataChartEl.prefix === 'cx' &&
chartRelationshipEl.getAttribute('Type') !== 'http://schemas.microsoft.com/office/2014/relationships/chartEx'
) {
continue
}
const chartPath = path.posix.join(path.posix.dirname(sheetFilepath), chartRelationshipEl.getAttribute('Target'))
if (
graphicDataChartEl.prefix === 'c' &&
chartRelationshipEl.getAttribute('Type') !== 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart'
) {
continue
}
const chartDoc = files.find((file) => file.path === chartPath)?.doc
const chartPath = path.posix.join(path.posix.dirname(sheetFilepath), chartRelationshipEl.getAttribute('Target'))
if (chartDoc == null) {
return
}
const chartDoc = files.find((file) => file.path === chartPath)?.doc
let chartEl
if (chartDoc == null) {
continue
}
if (graphicDataChartEl.prefix === 'cx') {
chartEl = chartDoc.getElementsByTagName('cx:chart')[0]
} else {
chartEl = chartDoc.getElementsByTagName('c:chart')[0]
}
let chartEl
if (chartEl == null) {
return
}
if (graphicDataChartEl.prefix === 'cx') {
chartEl = chartDoc.getElementsByTagName('cx:chart')[0]
} else {
chartEl = chartDoc.getElementsByTagName('c:chart')[0]
}
// ensuring the cached strings in xlsx are not processed by handlebars, because it will
// give errors otherwise
if (graphicDataChartEl.prefix === 'c') {
// it seems only the standard charts "c:" cache data in the chart definition,
// for the chartex it is not needed that we do something
const existingChartSeriesElements = nodeListToArray(chartDoc.getElementsByTagName('c:ser'))
if (chartEl == null) {
continue
}
for (const chartSerieEl of existingChartSeriesElements) {
const tagsToCheck = ['c:tx', 'c:cat', 'c:val', 'c:xVal', 'c:yVal', 'c:bubbleSize']
// ensuring the cached strings in xlsx are not processed by handlebars, because it will
// give errors otherwise
if (graphicDataChartEl.prefix === 'c') {
// it seems only the standard charts "c:" cache data in the chart definition,
// for the chartex it is not needed that we do something
const existingChartSeriesElements = nodeListToArray(chartDoc.getElementsByTagName('c:ser'))
for (const targetTag of tagsToCheck) {
const existingTagEl = findChildNode(targetTag, chartSerieEl)
for (const chartSerieEl of existingChartSeriesElements) {
const tagsToCheck = ['c:tx', 'c:cat', 'c:val', 'c:xVal', 'c:yVal', 'c:bubbleSize']
if (existingTagEl == null) {
continue
}
for (const targetTag of tagsToCheck) {
const existingTagEl = findChildNode(targetTag, chartSerieEl)
const strRefEl = findChildNode('c:strRef', existingTagEl)
const numRefEl = findChildNode('c:numRef', existingTagEl)
for (const targetInfo of [{ el: strRefEl, cacheTag: 'strCache' }, { el: numRefEl, cacheTag: 'numCache' }]) {
if (targetInfo.el == null) {
if (existingTagEl == null) {
continue
}
const cacheEl = findChildNode(`c:${targetInfo.cacheTag}`, targetInfo.el)
const strRefEl = findChildNode('c:strRef', existingTagEl)
const numRefEl = findChildNode('c:numRef', existingTagEl)
if (cacheEl == null) {
continue
}
for (const targetInfo of [{ el: strRefEl, cacheTag: 'strCache' }, { el: numRefEl, cacheTag: 'numCache' }]) {
if (targetInfo.el == null) {
continue
}
const ptEls = findChildNode('c:pt', cacheEl, true)
const cacheEl = findChildNode(`c:${targetInfo.cacheTag}`, targetInfo.el)
for (const ptEl of ptEls) {
const ptValueEl = findChildNode('c:v', ptEl)
if (ptValueEl == null) {
if (cacheEl == null) {
continue
}
if (ptValueEl.textContent.includes('{{') && ptValueEl.textContent.includes('}}')) {
ptValueEl.textContent = `{{{{xlsxSData type='raw'}}}}${ptValueEl.textContent}{{{{/xlsxSData}}}}`
const ptEls = findChildNode('c:pt', cacheEl, true)
for (const ptEl of ptEls) {
const ptValueEl = findChildNode('c:v', ptEl)
if (ptValueEl == null) {
continue
}
if (ptValueEl.textContent.includes('{{') && ptValueEl.textContent.includes('}}')) {
ptValueEl.textContent = `{{{{xlsxSData type='raw'}}}}${ptValueEl.textContent}{{{{/xlsxSData}}}}`
}
}

@@ -162,20 +163,20 @@ }

}
}
const chartTitles = nodeListToArray(chartEl.getElementsByTagName(`${graphicDataChartEl.prefix}:title`))
const chartMainTitleEl = chartTitles[0]
const chartTitles = nodeListToArray(chartEl.getElementsByTagName(`${graphicDataChartEl.prefix}:title`))
const chartMainTitleEl = chartTitles[0]
if (chartMainTitleEl == null) {
return
}
if (chartMainTitleEl == null) {
continue
}
if (graphicDataChartEl.prefix === 'cx') {
const chartMainTitleTxEl = nodeListToArray(chartMainTitleEl.childNodes).find((el) => el.nodeName === 'cx:tx')
const chartMainTitleTxDataEl = chartMainTitleTxEl != null ? nodeListToArray(chartMainTitleTxEl.childNodes).find((el) => el.nodeName === 'cx:txData') : undefined
const chartMainTitleTxDataValueEl = chartMainTitleTxDataEl != null ? nodeListToArray(chartMainTitleTxDataEl.childNodes).find((el) => el.nodeName === 'cx:v') : undefined
if (graphicDataChartEl.prefix === 'cx') {
const chartMainTitleTxEl = nodeListToArray(chartMainTitleEl.childNodes).find((el) => el.nodeName === 'cx:tx')
const chartMainTitleTxDataEl = chartMainTitleTxEl != null ? nodeListToArray(chartMainTitleTxEl.childNodes).find((el) => el.nodeName === 'cx:txData') : undefined
const chartMainTitleTxDataValueEl = chartMainTitleTxDataEl != null ? nodeListToArray(chartMainTitleTxDataEl.childNodes).find((el) => el.nodeName === 'cx:v') : undefined
if (chartMainTitleTxDataValueEl?.textContent.startsWith('{{xlsxChart')) {
chartMainTitleTxDataValueEl.textContent = ''
if (chartMainTitleTxDataValueEl?.textContent.startsWith('{{xlsxChart')) {
chartMainTitleTxDataValueEl.textContent = ''
}
}
}
}
const { DOMParser, XMLSerializer } = require('@xmldom/xmldom')
const decodeXML = require('unescape')
const { decode } = require('html-entities')
const { decompress, saveXmlsToOfficeFile } = require('@jsreport/office')

@@ -8,2 +8,4 @@ const preprocess = require('./preprocess/preprocess')

const decodeXML = (str) => decode(str, { level: 'xml' })
module.exports = (reporter) => async (inputs, req) => {

@@ -37,5 +39,16 @@ const { xlsxTemplateContent, options, outputPath } = inputs

const xmlStr = new XMLSerializer().serializeToString(f.doc, undefined, (node) => {
// we need to decode the xml entities for the attributes for handlebars to work ok
if (node.nodeType === 2 && node.nodeValue && node.nodeValue.includes('{{')) {
const str = new XMLSerializer().serializeToString(node)
return decodeXML(str)
} else if (
// we need to decode the xml entities in text nodes for handlebars to work ok with partials
node.nodeType === 3 && node.nodeValue &&
(node.nodeValue.includes('{{>') || node.nodeValue.includes('{{#>'))
) {
const str = new XMLSerializer().serializeToString(node)
return str.replace(/{{#?&gt;/g, (m) => {
return decodeXML(m)
})
}

@@ -60,3 +73,4 @@

// we remove NUL unicode characters, which is the only character that is illegal in XML
// we remove NUL, VERTICAL TAB unicode characters, which are characters that are illegal in XML.
// NOTE: we should likely find a way to remove illegal characters more generally, using some kind of unicode ranges
// eslint-disable-next-line no-control-regex

@@ -63,0 +77,0 @@ const contents = newContent.toString().replace(/\u0000|\u000b/g, '').split('$$$xlsxFile$$$')

{
"name": "@jsreport/jsreport-xlsx",
"version": "4.0.0",
"version": "4.0.1",
"description": "jsreport recipe rendering excels directly from open xml",

@@ -34,2 +34,3 @@ "keywords": [

"@xmldom/xmldom": "0.8.6",
"html-entities": "2.4.0",
"js-excel-date-convert": "1.0.2",

@@ -44,3 +45,2 @@ "lodash": "4.17.21",

"string-replace-async": "2.0.0",
"unescape": "1.0.1",
"uuid": "8.3.2",

@@ -51,9 +51,9 @@ "xlsx-coordinates": "1.0.1",

"devDependencies": {
"@jsreport/jsreport-assets": "4.0.0",
"@jsreport/jsreport-assets": "4.0.1",
"@jsreport/jsreport-components": "4.0.0",
"@jsreport/jsreport-core": "4.0.0",
"@jsreport/jsreport-core": "4.0.1",
"@jsreport/jsreport-data": "4.0.0",
"@jsreport/jsreport-handlebars": "4.0.0",
"@jsreport/jsreport-jsrender": "4.0.0",
"@jsreport/studio-dev": "3.2.1",
"@jsreport/studio-dev": "4.0.0",
"cross-env": "6.0.3",

@@ -60,0 +60,0 @@ "handlebars": "4.7.7",

@@ -10,2 +10,9 @@ # @jsreport/jsreport-xlsx

### 4.0.1
- fix nested loops with closing tags on single line
- fix xlsxChart not working when copy/paste charts found
- fix xml/html entities encode
- make handlebars partials to work
### 4.0.0

@@ -12,0 +19,0 @@

@@ -112,3 +112,3 @@ /* eslint no-unused-vars: 0 */

newData.evaluatedLoopsIds = []
newData.outOfLoopItems = Object.create(null)
newData.outOfLoopTemplates = Object.create(null)

@@ -264,3 +264,3 @@ return optionsToUse.fn(this, { data: newData })

arguments.length === 1 &&
type === 'outOfLoopPlaceholder'
type === 'outOfLoop'
) {

@@ -270,26 +270,18 @@ const item = optionsToUse.hash.item

if (item == null) {
throw new Error('xlsxSData type="outOfLoopPlaceholder" helper item arg is required')
throw new Error('xlsxSData type="outOfLoop" helper item arg is required')
}
const currentLoopId = optionsToUse.data.currentLoopId
optionsToUse.data.outOfLoopTemplates[item] = (currentLoopId, currentIdx) => {
const newData = Handlebars.createFrame(optionsToUse.data)
if (currentLoopId == null) {
throw new Error('xlsxSData type="outOfLoopPlaceholder" helper invalid usage, currentLoopId not found')
}
newData.currentLoopId = currentLoopId
let metaItem
if (currentIdx != null) {
newData.index = currentIdx
}
if (optionsToUse.data.outOfLoopItems[item] != null) {
metaItem = optionsToUse.data.outOfLoopItems[item]
} else {
metaItem = { pendingExecutions: [] }
optionsToUse.data.outOfLoopItems[item] = metaItem
return optionsToUse.fn(this, { data: newData })
}
metaItem.pendingExecutions.push({
loopId: currentLoopId,
index: optionsToUse.data.index
})
return optionsToUse.fn(this, { data: optionsToUse.data })
return new Handlebars.SafeString('')
}

@@ -299,3 +291,3 @@

arguments.length === 1 &&
type === 'outOfLoop'
type === 'outOfLoopPlaceholder'
) {

@@ -305,41 +297,22 @@ const item = optionsToUse.hash.item

if (item == null) {
throw new Error('xlsxSData type="outOfLoop" helper item arg is required')
throw new Error('xlsxSData type="outOfLoopPlaceholder" helper item arg is required')
}
const outOfLoopItem = optionsToUse.data.outOfLoopItems[item]
const outOfLoopTemplate = optionsToUse.data.outOfLoopTemplates[item]
if (outOfLoopItem == null) {
throw new Error('xlsxSData type="outOfLoop" helper invalid usage, outOfLoopItem was not found')
if (outOfLoopTemplate == null) {
throw new Error('xlsxSData type="outOfLoopPlaceholder" helper invalid usage, outOfLoopItem was not found')
}
const pendingExecutions = outOfLoopItem.pendingExecutions.splice(0, outOfLoopItem.pendingExecutions.length)
const currentLoopId = optionsToUse.data.currentLoopId
if (pendingExecutions == null || pendingExecutions.length === 0) {
throw new Error('xlsxSData type="outOfLoop" helper invalid usage, pendingExecution was not found')
if (currentLoopId == null) {
throw new Error('xlsxSData type="outOfLoopPlaceholder" helper invalid usage, currentLoopId not found')
}
const output = []
const currentIdx = optionsToUse.data.index
for (const pendingExecution of pendingExecutions) {
const currentLoopId = pendingExecution.loopId
const currentIdx = pendingExecution.index
const output = outOfLoopTemplate(currentLoopId, currentIdx)
if (currentLoopId == null) {
throw new Error('xlsxSData type="outOfLoop" helper invalid usage, loopId was not found')
}
const newData = Handlebars.createFrame(optionsToUse.data)
newData.currentLoopId = currentLoopId
if (currentIdx != null) {
newData.index = currentIdx
}
const content = optionsToUse.fn(this, { data: newData })
output.push(`<data>${content}</data>`)
}
return new Handlebars.SafeString(output.join(''))
return new Handlebars.SafeString(output)
}

@@ -676,2 +649,16 @@

newData.currentCellValueInfo.value = optionsToUse.hash.value
let toEscape = false
// escape should be there when the original handlebars expression was intended
// to be escaped, we preserve that intend here and escape it, we need to do this
// because handlebars does not escape automatically the helper parameter hash,
// which we use as an implementation detail of our auto detect cell type logic
if (Object.prototype.hasOwnProperty.call(optionsToUse.hash, 'escape')) {
toEscape = optionsToUse.hash.escape === true && typeof newData.currentCellValueInfo.value === 'string'
}
if (toEscape) {
newData.currentCellValueInfo.value = Handlebars.escapeExpression(newData.currentCellValueInfo.value)
}
}

@@ -678,0 +665,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc