xlsx-template-browser 0.1.5 → 0.1.8

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.
@@ -7,7 +7,7 @@ export const valueToString = (value) => {
7
7
  if (!value) return ''
8
8
  if (Array.isArray(value)) return value.map(valueToString).toString()
9
9
  if (typeof value === 'object') value = JSON.stringify(value)
10
- return value.toString()
10
+ return escapeXml(value.toString())
11
11
  }
12
12
 
13
13
  /**
@@ -27,7 +27,7 @@ const dateToExcel = (value) => {
27
27
  * or undefined if the data type is not recognized.
28
28
  */
29
29
  export const guessDataType = (value) => {
30
- if (typeof value === 'undefined') return undefined
30
+ if (typeof value === 'undefined') return { cellType: 's', value: '' }
31
31
  if (typeof value === 'number') {
32
32
  if (isFinite(value)) return { cellType: 'n', value }
33
33
  return undefined
@@ -37,7 +37,7 @@ export const guessDataType = (value) => {
37
37
  return { cellType: 'n', value: dateToExcel(value) }
38
38
  }
39
39
  if (typeof value === 'boolean') return { cellType: 'b', value: value + 0 }
40
- return { cellType: 's', value: valueToString(value )}
40
+ return { cellType: 's', value: valueToString(value) }
41
41
  }
42
42
 
43
43
  /**
@@ -65,6 +65,10 @@ export const createSharedString = (text, template) => {
65
65
  * @returns {Element} - The created cell element.
66
66
  */
67
67
  export const createCell = ({ value, row, column, template, cellType }) => {
68
+ // If the cell is not a string, then skip
69
+ if (!cellType && !value) {
70
+ return template
71
+ }
68
72
  // If the cell is formula, then skip
69
73
  if (cellType === 'f') {
70
74
  // Check precalculated value
@@ -118,3 +122,24 @@ export const getExcelColumnIndex = (columnName) => {
118
122
  return index
119
123
  }
120
124
 
125
+ /**
126
+ * Escapes unsafe XML characters in a given string to ensure its safe inclusion
127
+ * in XML documents, preventing parsing issues.
128
+ *
129
+ * @param {string} unsafe - The input string containing XML-unsafe characters.
130
+ * @returns {string} - A new string with XML-unsafe characters replaced by their
131
+ * corresponding HTML entities.
132
+ */
133
+
134
+ const unsafeChars = {
135
+ '<': '&lt;',
136
+ '>': '&gt;',
137
+ '&': '&amp;',
138
+ '\'': '&apos;',
139
+ '"': '&quot;'
140
+ }
141
+
142
+ export const escapeXml = (unsafe) => {
143
+ return unsafe.replace(/[<>&'"]/g, (c) => { return unsafeChars[c] })
144
+ }
145
+
package/lib/helpers.js CHANGED
@@ -57,6 +57,7 @@ export const getByNotation = (obj, props) => {
57
57
  let accessors = props.match(notationRegex)
58
58
  if (!accessors.length) return obj
59
59
  accessors = accessors.map(el => /^\d+$/.test(el) ? parseInt(el) : el)
60
- return getDeep(obj, accessors)
60
+ const value = getDeep(obj, accessors)
61
+ return value || ''
61
62
  }
62
63
 
package/lib/main.js CHANGED
@@ -50,6 +50,16 @@ const replace = async (template, data) => {
50
50
  // We need to replace only text values with the new ones, therefore we process shared strings first
51
51
  // Get all string items
52
52
  xml.querySelectorAll('si').forEach((si, i) => {
53
+ // Get all rich format tags
54
+ const r = si.querySelector('r')
55
+ if (r) {
56
+ const xmlString = si.innerHTML
57
+ const newString = xmlString.replace(globalAccessorRegex, s => valueToString(getByNotation(data, s.replace(/\$\{|}/g, ''))))
58
+ si.innerHTML = newString
59
+ newSharedStrings.push(si)
60
+ valuesToReplace[i] = { value: newSharedStrings.length - 1, cellType: 's' }
61
+ return false
62
+ }
53
63
  // Get all text tags
54
64
  const t = si.querySelector('t')
55
65
  if (!t) return false
@@ -70,7 +80,7 @@ const replace = async (template, data) => {
70
80
  // If we have table values, we need to process them in a separate way
71
81
  const isTable = typeof table === 'string'
72
82
  let value = getByNotation(data, accessor)
73
- if (!value) return false
83
+ if (typeof value === 'undefined') return false
74
84
  // Save tables for further processing
75
85
  if (isTable) {
76
86
  valuesToReplace[i] = { isTable, value: value.map(guessDataType) }
@@ -127,13 +137,19 @@ const replace = async (template, data) => {
127
137
  // Get the cell value tag
128
138
  let v = c.querySelector('v') || {}
129
139
  // Check if the cell contains formula, and skip
130
- let f = c.querySelector('f')
131
- if (f) {
140
+ let isFormula = c.querySelector('f')
141
+ if (isFormula) {
132
142
  const value = c
133
143
  const cellType = 'f'
134
144
  newCells[newIndex] = Object.assign({}, { value, cellType }, { template: c })
135
145
  return false
136
146
  }
147
+ // Check if the cell contains string
148
+ let isString = c.getAttribute('t')
149
+ if (!isString || isString !== 's') {
150
+ newCells[newIndex] = Object.assign({}, { template: c })
151
+ return false
152
+ }
137
153
  // Get the new value to replace
138
154
  let newValue = valuesToReplace[v.textContent] || {}
139
155
  if (!Array.isArray(newValue)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xlsx-template-browser",
3
- "version": "0.1.5",
3
+ "version": "0.1.8",
4
4
  "description": "On-the-fly generation of .xlsx (Excel) files directly within the browser using templates constructed in Excel.",
5
5
  "main": "./lib/index",
6
6
  "files": [