xslt-processor 5.0.8 → 5.0.10

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/dist/index.d.mts CHANGED
@@ -285,7 +285,7 @@ declare class ExprContext {
285
285
  ignoreAttributesWithoutValue: any;
286
286
  returnOnFirstMatch: any;
287
287
  ignoreNonElementNodesForNTA: any;
288
- parent: ExprContext;
288
+ parent: ExprContext | null;
289
289
  root: XNode;
290
290
  decimalFormatSettings: XsltDecimalFormatSettings;
291
291
  inApplyTemplates: boolean;
@@ -348,8 +348,8 @@ declare class ExprContext {
348
348
  * @returns A new ExprContext instance with the specified node list and position, and the current context as its parent.
349
349
  */
350
350
  clone(opt_nodeList?: XNode[], opt_position?: number): ExprContext;
351
- setVariable(name?: string, value?: NodeValue | string): void;
352
- getVariable(name: string): NodeValue;
351
+ setVariable(name: string, value?: NodeValue | string): void;
352
+ getVariable(name: string): NodeValue | null;
353
353
  /**
354
354
  * Gets a regex group from xsl:analyze-string context.
355
355
  * Searches up the parent chain for regexGroups.
@@ -612,6 +612,13 @@ declare class NodeConverter {
612
612
  * followed by the evaluated function arguments.
613
613
  */
614
614
  private createCustomFunctions;
615
+ /**
616
+ * Minimal XSLT/XPath format-number() picture formatter.
617
+ * Supports grouping, decimal precision, percent/per-mille scaling and positive/negative subpictures.
618
+ */
619
+ private formatXsltNumberPicture;
620
+ private applyGrouping;
621
+ private leftPad;
615
622
  /**
616
623
  * Convert an XPathNode interface tree to actual XNode objects.
617
624
  * This is needed to convert json-to-xml() output to XSLT-compatible nodes.
package/dist/index.d.ts CHANGED
@@ -285,7 +285,7 @@ declare class ExprContext {
285
285
  ignoreAttributesWithoutValue: any;
286
286
  returnOnFirstMatch: any;
287
287
  ignoreNonElementNodesForNTA: any;
288
- parent: ExprContext;
288
+ parent: ExprContext | null;
289
289
  root: XNode;
290
290
  decimalFormatSettings: XsltDecimalFormatSettings;
291
291
  inApplyTemplates: boolean;
@@ -348,8 +348,8 @@ declare class ExprContext {
348
348
  * @returns A new ExprContext instance with the specified node list and position, and the current context as its parent.
349
349
  */
350
350
  clone(opt_nodeList?: XNode[], opt_position?: number): ExprContext;
351
- setVariable(name?: string, value?: NodeValue | string): void;
352
- getVariable(name: string): NodeValue;
351
+ setVariable(name: string, value?: NodeValue | string): void;
352
+ getVariable(name: string): NodeValue | null;
353
353
  /**
354
354
  * Gets a regex group from xsl:analyze-string context.
355
355
  * Searches up the parent chain for regexGroups.
@@ -612,6 +612,13 @@ declare class NodeConverter {
612
612
  * followed by the evaluated function arguments.
613
613
  */
614
614
  private createCustomFunctions;
615
+ /**
616
+ * Minimal XSLT/XPath format-number() picture formatter.
617
+ * Supports grouping, decimal precision, percent/per-mille scaling and positive/negative subpictures.
618
+ */
619
+ private formatXsltNumberPicture;
620
+ private applyGrouping;
621
+ private leftPad;
615
622
  /**
616
623
  * Convert an XPathNode interface tree to actual XNode objects.
617
624
  * This is needed to convert json-to-xml() output to XSLT-compatible nodes.
package/dist/index.js CHANGED
@@ -9084,13 +9084,9 @@ var XPathBaseParser = class {
9084
9084
  return expr;
9085
9085
  }
9086
9086
  parsePrimaryExpr() {
9087
- var _a, _b, _c;
9087
+ var _a, _b;
9088
9088
  if (this.match("DOLLAR")) {
9089
- const next = this.peek();
9090
- if (!next || !this.isNcNameToken(next.type)) {
9091
- throw grammarViolation(`Expected variable name after $. Got: ${(_a = next == null ? void 0 : next.lexeme) != null ? _a : "EOF"}`);
9092
- }
9093
- const name = this.advance().lexeme;
9089
+ const name = this.parseVariableReferenceName();
9094
9090
  return new XPathVariableReference(name);
9095
9091
  }
9096
9092
  if (this.match("OPEN_PAREN")) {
@@ -9114,7 +9110,7 @@ var XPathBaseParser = class {
9114
9110
  return this.parseFunctionCall();
9115
9111
  }
9116
9112
  throw grammarViolation(
9117
- `Unexpected token in primary expression: ${(_c = (_b = this.peek()) == null ? void 0 : _b.lexeme) != null ? _c : "EOF"}`
9113
+ `Unexpected token in primary expression: ${(_b = (_a = this.peek()) == null ? void 0 : _a.lexeme) != null ? _b : "EOF"}`
9118
9114
  );
9119
9115
  }
9120
9116
  parseFunctionCall() {
@@ -9186,6 +9182,27 @@ var XPathBaseParser = class {
9186
9182
  isFunctionNameToken(type) {
9187
9183
  return type === "IDENTIFIER" || type === "FUNCTION" || type === "OPERATOR" || type === "LOCATION" || type === "EQNAME";
9188
9184
  }
9185
+ parseVariableReferenceName() {
9186
+ var _a, _b;
9187
+ if (this.check("EQNAME")) {
9188
+ return this.advance().lexeme;
9189
+ }
9190
+ const next = this.peek();
9191
+ if (!next || !this.isNcNameToken(next.type)) {
9192
+ throw grammarViolation(`Expected variable name after $. Got: ${(_a = next == null ? void 0 : next.lexeme) != null ? _a : "EOF"}`);
9193
+ }
9194
+ let name = this.advance().lexeme;
9195
+ if (this.match("COLON")) {
9196
+ const local = this.peek();
9197
+ if (!local || !this.isNcNameToken(local.type)) {
9198
+ throw grammarViolation(
9199
+ `Expected local name after namespace prefix in variable reference. Got: ${(_b = local == null ? void 0 : local.lexeme) != null ? _b : "EOF"}`
9200
+ );
9201
+ }
9202
+ name = `${name}:${this.advance().lexeme}`;
9203
+ }
9204
+ return name;
9205
+ }
9189
9206
  isNcNameToken(type) {
9190
9207
  return type === "IDENTIFIER" || type === "FUNCTION" || type === "OPERATOR" || type === "LOCATION" || type === "NODE_TYPE";
9191
9208
  }
@@ -9298,16 +9315,12 @@ var XPath20Parser = class extends XPathBaseParser {
9298
9315
  return left;
9299
9316
  }
9300
9317
  parsePrimaryExpr() {
9301
- var _a, _b;
9302
9318
  if (this.check("RESERVED_WORD") && this.peek().lexeme === "if") {
9303
9319
  return this.parseIfExpr();
9304
9320
  }
9305
9321
  if (this.check("DOLLAR")) {
9306
9322
  this.advance();
9307
- if (!this.isNameToken()) {
9308
- throw new Error(`Expected variable name after $. Got: ${(_b = (_a = this.peek()) == null ? void 0 : _a.lexeme) != null ? _b : "EOF"}`);
9309
- }
9310
- const name = this.advance().lexeme;
9323
+ const name = this.parseVariableReferenceName();
9311
9324
  return new XPathVariableReference(name);
9312
9325
  }
9313
9326
  return super.parsePrimaryExpr();
@@ -9684,7 +9697,7 @@ var XPath30Parser = class extends XPath20Parser {
9684
9697
  let args = [];
9685
9698
  if (this.check("DOLLAR")) {
9686
9699
  this.advance();
9687
- const varName = this.consume("IDENTIFIER", "Expected variable name after $").lexeme;
9700
+ const varName = this.parseVariableReferenceName();
9688
9701
  funcExpr = new XPathVariableReference(varName);
9689
9702
  this.consume(
9690
9703
  "OPEN_PAREN",
@@ -9752,8 +9765,7 @@ var XPath30Parser = class extends XPath20Parser {
9752
9765
  return this.parseStringTemplateFromLexeme(template);
9753
9766
  }
9754
9767
  if (this.match("DOLLAR")) {
9755
- const nameToken = this.consumeNameTokenInternal("Expected variable name after $");
9756
- return new XPathVariableReference(nameToken.lexeme);
9768
+ return new XPathVariableReference(this.parseVariableReferenceName());
9757
9769
  }
9758
9770
  if (this.checkReservedWordInternal("function")) {
9759
9771
  return this.parseInlineFunction();
@@ -11846,7 +11858,9 @@ var NodeConverter = class {
11846
11858
  * We add missing properties but keep the original XNode reference.
11847
11859
  */
11848
11860
  adaptXNode(node) {
11849
- if (!node) return null;
11861
+ if (!node) {
11862
+ throw new Error("Cannot adapt undefined XPath node.");
11863
+ }
11850
11864
  const adapted = node;
11851
11865
  if (!("textContent" in adapted)) {
11852
11866
  Object.defineProperty(adapted, "textContent", {
@@ -11932,7 +11946,12 @@ var NodeConverter = class {
11932
11946
  }
11933
11947
  for (let i = contexts.length - 1; i >= 0; i--) {
11934
11948
  const current = contexts[i];
11935
- for (const [name, value] of Object.entries(current.variables || {})) {
11949
+ const currentVariables = current.variables || {};
11950
+ for (const name in currentVariables) {
11951
+ if (!Object.prototype.hasOwnProperty.call(currentVariables, name)) {
11952
+ continue;
11953
+ }
11954
+ const value = currentVariables[name];
11936
11955
  if (value && typeof value === "object" && "stringValue" in value) {
11937
11956
  const nodeValue = value;
11938
11957
  if (nodeValue.type === "node-set") {
@@ -11981,7 +12000,7 @@ var NodeConverter = class {
11981
12000
  };
11982
12001
  functions["format-number"] = (_context, number, format, decimalFormatName) => {
11983
12002
  const settings = exprContext.decimalFormatSettings;
11984
- return number.toLocaleString();
12003
+ return this.formatXsltNumberPicture(Number(number), String(format), settings);
11985
12004
  };
11986
12005
  functions["xml-to-json"] = (_context, nodes) => {
11987
12006
  if (exprContext.xsltVersion !== "3.0") {
@@ -12062,7 +12081,7 @@ var NodeConverter = class {
12062
12081
  "xsl:with-param"
12063
12082
  ];
12064
12083
  const normalizedName = name.startsWith("xsl:") ? name : `xsl:${name}`;
12065
- return xsltElements.includes(normalizedName) || xsltElements.includes(name);
12084
+ return xsltElements.indexOf(normalizedName) >= 0 || xsltElements.indexOf(name) >= 0;
12066
12085
  };
12067
12086
  functions["function-available"] = (_context, functionName2) => {
12068
12087
  const name = String(functionName2);
@@ -12113,7 +12132,7 @@ var NodeConverter = class {
12113
12132
  "json-to-xml"
12114
12133
  ];
12115
12134
  const allFunctions = [...xpathCoreFunctions, ...xsltFunctions, ...additionalFunctions];
12116
- return allFunctions.includes(name);
12135
+ return allFunctions.indexOf(name) >= 0;
12117
12136
  };
12118
12137
  functions["document"] = (_context, uriOrNodeSet, _baseNode) => {
12119
12138
  var _a, _b;
@@ -12157,6 +12176,95 @@ var NodeConverter = class {
12157
12176
  }
12158
12177
  return functions;
12159
12178
  }
12179
+ /**
12180
+ * Minimal XSLT/XPath format-number() picture formatter.
12181
+ * Supports grouping, decimal precision, percent/per-mille scaling and positive/negative subpictures.
12182
+ */
12183
+ formatXsltNumberPicture(value, picture, settings) {
12184
+ if (Number.isNaN(value)) {
12185
+ return settings.naN;
12186
+ }
12187
+ if (!Number.isFinite(value)) {
12188
+ return value < 0 ? `${settings.minusSign}${settings.infinity}` : settings.infinity;
12189
+ }
12190
+ const subPictures = picture.split(settings.patternSeparator);
12191
+ const isNegative = value < 0;
12192
+ const activePicture = (isNegative && subPictures.length > 1 ? subPictures[1] : subPictures[0]) || "";
12193
+ const absValue = Math.abs(value);
12194
+ const placeholders = /* @__PURE__ */ new Set([settings.digit, settings.zeroDigit, settings.groupingSeparator, settings.decimalSeparator]);
12195
+ let firstPatternIndex = -1;
12196
+ let lastPatternIndex = -1;
12197
+ for (let i = 0; i < activePicture.length; i++) {
12198
+ if (placeholders.has(activePicture[i])) {
12199
+ if (firstPatternIndex === -1) {
12200
+ firstPatternIndex = i;
12201
+ }
12202
+ lastPatternIndex = i;
12203
+ }
12204
+ }
12205
+ if (firstPatternIndex === -1) {
12206
+ return activePicture;
12207
+ }
12208
+ const prefix = activePicture.slice(0, firstPatternIndex);
12209
+ const suffix = activePicture.slice(lastPatternIndex + 1);
12210
+ const numericPicture = activePicture.slice(firstPatternIndex, lastPatternIndex + 1);
12211
+ let scaledValue = absValue;
12212
+ if (activePicture.includes(settings.percent)) {
12213
+ scaledValue *= 100;
12214
+ }
12215
+ if (activePicture.includes(settings.perMille)) {
12216
+ scaledValue *= 1e3;
12217
+ }
12218
+ const decimalIndex = numericPicture.indexOf(settings.decimalSeparator);
12219
+ const integerPattern = decimalIndex >= 0 ? numericPicture.slice(0, decimalIndex) : numericPicture;
12220
+ const fractionPattern = decimalIndex >= 0 ? numericPicture.slice(decimalIndex + 1) : "";
12221
+ const minIntegerDigits = integerPattern.split("").filter((c) => c === settings.zeroDigit).length;
12222
+ const minFractionDigits = fractionPattern.split("").filter((c) => c === settings.zeroDigit).length;
12223
+ const maxFractionDigits = fractionPattern.split("").filter((c) => c === settings.zeroDigit || c === settings.digit).length;
12224
+ const rounded = maxFractionDigits > 0 ? scaledValue.toFixed(maxFractionDigits) : Math.round(scaledValue).toString();
12225
+ let [integerPart, fractionPart = ""] = rounded.split(".");
12226
+ if (integerPart.length < minIntegerDigits) {
12227
+ integerPart = this.leftPad(integerPart, minIntegerDigits, settings.zeroDigit);
12228
+ }
12229
+ while (fractionPart.length > minFractionDigits && fractionPart.endsWith(settings.zeroDigit)) {
12230
+ fractionPart = fractionPart.slice(0, -1);
12231
+ }
12232
+ const lastGroupingPos = integerPattern.lastIndexOf(settings.groupingSeparator);
12233
+ if (lastGroupingPos >= 0) {
12234
+ const groupSize = integerPattern.slice(lastGroupingPos + 1).split("").filter((c) => c === settings.zeroDigit || c === settings.digit).length;
12235
+ if (groupSize > 0) {
12236
+ integerPart = this.applyGrouping(integerPart, settings.groupingSeparator, groupSize);
12237
+ }
12238
+ }
12239
+ const numberText = fractionPart.length > 0 ? `${integerPart}${settings.decimalSeparator}${fractionPart}` : integerPart;
12240
+ if (isNegative && subPictures.length < 2) {
12241
+ return `${settings.minusSign}${prefix}${numberText}${suffix}`;
12242
+ }
12243
+ return `${prefix}${numberText}${suffix}`;
12244
+ }
12245
+ applyGrouping(numberText, separator, groupSize) {
12246
+ if (groupSize <= 0 || numberText.length <= groupSize) {
12247
+ return numberText;
12248
+ }
12249
+ const chunks = [];
12250
+ for (let i = numberText.length; i > 0; i -= groupSize) {
12251
+ const start = Math.max(0, i - groupSize);
12252
+ chunks.unshift(numberText.slice(start, i));
12253
+ }
12254
+ return chunks.join(separator);
12255
+ }
12256
+ leftPad(value, width, fillChar) {
12257
+ if (value.length >= width) {
12258
+ return value;
12259
+ }
12260
+ const padChar = fillChar && fillChar.length > 0 ? fillChar.charAt(0) : "0";
12261
+ const missing = width - value.length;
12262
+ let padding = "";
12263
+ for (let i = 0; i < missing; i++) {
12264
+ padding += padChar;
12265
+ }
12266
+ return padding + value;
12267
+ }
12160
12268
  /**
12161
12269
  * Convert an XPathNode interface tree to actual XNode objects.
12162
12270
  * This is needed to convert json-to-xml() output to XSLT-compatible nodes.
@@ -12169,7 +12277,11 @@ var NodeConverter = class {
12169
12277
  if (xpathNode.nodeType === DOM_DOCUMENT_NODE) {
12170
12278
  if (xpathNode.childNodes && xpathNode.childNodes.length > 0) {
12171
12279
  const rootChild = xpathNode.childNodes[0];
12172
- node = this.convertXPathNodeToXNode(rootChild, ownerDoc);
12280
+ const convertedNode = this.convertXPathNodeToXNode(rootChild, ownerDoc);
12281
+ if (!convertedNode) {
12282
+ return null;
12283
+ }
12284
+ node = convertedNode;
12173
12285
  return node;
12174
12286
  }
12175
12287
  return null;
@@ -15402,7 +15514,7 @@ var Xslt = class {
15402
15514
  );
15403
15515
  if (onCompletionElements.length > 0) {
15404
15516
  const onCompletion = onCompletionElements[0];
15405
- const completionContext = context.clone([], 0);
15517
+ const completionContext = context.clone();
15406
15518
  for (const accName in accumulators) {
15407
15519
  completionContext.variables[accName] = accumulators[accName];
15408
15520
  }