xlsform2lstsv 0.2.3 → 0.3.0

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.
@@ -129,14 +129,21 @@ function transpile(node, ctx) {
129
129
  return `endsWith(${transpile(node.args[0], ctx)}, ${transpile(node.args[1], ctx)})`;
130
130
  }
131
131
  break;
132
+ case 'normalize-space':
133
+ if (node.args?.length === 1) {
134
+ return `trim(${transpile(node.args[0], ctx)})`;
135
+ }
136
+ break;
132
137
  case 'not':
133
138
  if (node.args?.length === 1) {
134
139
  return `!(${transpile(node.args[0], ctx)})`;
135
140
  }
136
141
  break;
137
142
  case 'if':
143
+ // Use if() function instead of ternary (? :) because EM's ternary parser
144
+ // can misinterpret colons inside string literals (e.g. '2026-03-CHW: Chancenwerk')
138
145
  if (node.args?.length === 3) {
139
- return `(${transpile(node.args[0], ctx)} ? ${transpile(node.args[1], ctx)} : ${transpile(node.args[2], ctx)})`;
146
+ return `if(${transpile(node.args[0], ctx)}, ${transpile(node.args[1], ctx)}, ${transpile(node.args[2], ctx)})`;
140
147
  }
141
148
  break;
142
149
  case 'today':
@@ -166,7 +173,7 @@ function transpile(node, ctx) {
166
173
  const rawValue = rightNode.value;
167
174
  const rewritten = lookupAnswerCode(fieldName, rawValue);
168
175
  if (rewritten !== rawValue) {
169
- return `${fieldName} == "${rewritten}"`;
176
+ return `${fieldName} == '${rewritten}'`;
170
177
  }
171
178
  }
172
179
  return `${transpile(leftNode, ctx)} == ${transpile(rightNode, ctx)}`;
@@ -179,7 +186,7 @@ function transpile(node, ctx) {
179
186
  const rawValue = rightNode.value;
180
187
  const rewritten = lookupAnswerCode(fieldName, rawValue);
181
188
  if (rewritten !== rawValue) {
182
- return `${fieldName} != "${rewritten}"`;
189
+ return `${fieldName} != '${rewritten}'`;
183
190
  }
184
191
  }
185
192
  return `${transpile(leftNode, ctx)} != ${transpile(rightNode, ctx)}`;
@@ -20,11 +20,12 @@ export class TSVGenerator {
20
20
  'mandatory',
21
21
  'other',
22
22
  'default',
23
- 'same_default'
23
+ 'same_default',
24
+ 'hidden'
24
25
  ];
25
26
  const lines = [headers.join('\t')];
26
27
  for (const row of this.rows) {
27
- const values = headers.map((h) => this.escapeForTSV(row[h] || ''));
28
+ const values = headers.map((h) => this.escapeForTSV(row[h] ?? ''));
28
29
  lines.push(values.join('\t'));
29
30
  }
30
31
  return lines.join('\n');
@@ -553,7 +553,7 @@ export class XLSFormToTSVConverter {
553
553
  * Transpile an XLSForm calculation expression to a LimeSurvey EM expression.
554
554
  */
555
555
  async convertCalculation(calculation) {
556
- return await xpathToLimeSurvey(calculation);
556
+ return await xpathToLimeSurvey(calculation, this.buildTranspilerContext());
557
557
  }
558
558
  async addGroup(row) {
559
559
  // Auto-generate name if missing (matches LimeSurvey behavior)
@@ -572,10 +572,10 @@ export class XLSFormToTSVConverter {
572
572
  this.tsvGenerator.addRow({
573
573
  class: 'G',
574
574
  'type/scale': groupSeqKey,
575
- name: groupName,
575
+ name: this.getLanguageSpecificValue(row.label, lang) || groupName,
576
576
  relevance: await this.convertRelevance(row.relevant),
577
- text: this.getLanguageSpecificValue(row.label, lang) || groupName,
578
- help: this.getLanguageSpecificValue(row.hint, lang) || '',
577
+ text: this.getLanguageSpecificValue(row.hint, lang) || '',
578
+ help: '',
579
579
  language: lang,
580
580
  validation: '',
581
581
  em_validation_q: "",
@@ -659,7 +659,8 @@ export class XLSFormToTSVConverter {
659
659
  mandatory: (isNote || isCalculate) ? '' : (row.required === 'yes' || row.required === 'true' ? 'Y' : ''),
660
660
  other: (isNote || isCalculate) ? '' : (lsType.other ? 'Y' : ''),
661
661
  default: (isNote || isCalculate) ? '' : (row.default || ''),
662
- same_default: ''
662
+ same_default: '',
663
+ hidden: isCalculate ? '1' : '',
663
664
  });
664
665
  }
665
666
  // Reset answer sequence for this question
@@ -831,10 +832,8 @@ export class XLSFormToTSVConverter {
831
832
  return { code: choiceValue, listName };
832
833
  return { code: codeMap.get(choiceValue) ?? choiceValue, listName };
833
834
  }
834
- async convertRelevance(relevant) {
835
- if (!relevant)
836
- return '1';
837
- const ctx = {
835
+ buildTranspilerContext() {
836
+ return {
838
837
  lookupAnswerCode: (fieldName, choiceValue) => {
839
838
  return this.lookupAnswerCode(fieldName, choiceValue).code;
840
839
  },
@@ -843,11 +842,15 @@ export class XLSFormToTSVConverter {
843
842
  const { code } = this.lookupAnswerCode(fieldName, choiceValue);
844
843
  const baseType = this.questionBaseTypeMap.get(truncated);
845
844
  if (baseType === 'select_multiple') {
846
- return `(${truncated}_${code}.NAOK == "Y")`;
845
+ return `(${truncated}_${code}.NAOK == 'Y')`;
847
846
  }
848
- return `(${truncated}.NAOK=="${code}")`;
847
+ return `(${truncated}.NAOK=='${code}')`;
849
848
  },
850
849
  };
851
- return await convertRelevance(relevant, ctx);
850
+ }
851
+ async convertRelevance(relevant) {
852
+ if (!relevant)
853
+ return '1';
854
+ return await convertRelevance(relevant, this.buildTranspilerContext());
852
855
  }
853
856
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xlsform2lstsv",
3
- "version": "0.2.3",
3
+ "version": "0.3.0",
4
4
  "description": "Convert XLSForm surveys to LimeSurvey TSV format",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",