arraybuffer-xml-parser
Advanced tools
Comparing version 0.6.1 to 1.0.0
@@ -1,2 +0,2 @@ | ||
export declare function arrayTrim(array: Uint8Array, arg?: unknown): Uint8Array; | ||
export declare function arrayTrim(array: Uint8Array, arg?: unknown): Uint8Array<ArrayBufferLike>; | ||
//# sourceMappingURL=arrayTrim.d.ts.map |
@@ -1,6 +0,8 @@ | ||
import { ParseOptions } from './traversable/defaultOptions'; | ||
import type { ParseOptions } from './traversable/defaultOptions'; | ||
/** | ||
* Parse an ArrayBuffer or Uint8Array representing an XML and return an object | ||
* @param xmlData | ||
* @param options | ||
*/ | ||
export declare function parse(xmlData: string | Uint8Array | ArrayBufferLike, options?: ParseOptions): import("./XMLNode").XMLNodeValue | Record<string, import("./XMLNode").XMLNodeValue>; | ||
//# sourceMappingURL=parse.d.ts.map |
@@ -6,2 +6,4 @@ import { defaultOptions } from './traversable/defaultOptions'; | ||
* Parse an ArrayBuffer or Uint8Array representing an XML and return an object | ||
* @param xmlData | ||
* @param options | ||
*/ | ||
@@ -16,6 +18,6 @@ export function parse(xmlData, options = {}) { | ||
} | ||
options = { ...defaultOptions, ...options }; | ||
const traversable = getTraversable(xmlData, options); | ||
return traversableToJSON(traversable, options); | ||
const realOptions = { ...defaultOptions, ...options }; | ||
const traversable = getTraversable(xmlData, realOptions); | ||
return traversableToJSON(traversable, realOptions); | ||
} | ||
//# sourceMappingURL=parse.js.map |
@@ -1,6 +0,9 @@ | ||
import { StreamParseOptions } from './traversable/defaultOptions'; | ||
import type { StreamParseOptions } from './traversable/defaultOptions'; | ||
/** | ||
* Parse a web stream representing an XML and emit objects | ||
* @param readableStream | ||
* @param lookupTagName | ||
* @param options | ||
*/ | ||
export declare function parseStream(readableStream: ReadableStream, lookupTagName: string, options?: StreamParseOptions): AsyncGenerator<import("./XMLNode").XMLNodeValue | Record<string, import("./XMLNode").XMLNodeValue>, void, unknown>; | ||
//# sourceMappingURL=parseStream.d.ts.map |
@@ -1,2 +0,2 @@ | ||
import { defaultOptions, } from './traversable/defaultOptions'; | ||
import { defaultStreamOptions } from './traversable/defaultOptions'; | ||
import { getTraversableGenerator } from './traversable/getTraversableGenerator'; | ||
@@ -6,9 +6,12 @@ import { traversableToJSON } from './traversableToJSON'; | ||
* Parse a web stream representing an XML and emit objects | ||
* @param readableStream | ||
* @param lookupTagName | ||
* @param options | ||
*/ | ||
export async function* parseStream(readableStream, lookupTagName, options = {}) { | ||
options = { ...defaultOptions, ...options }; | ||
for await (const traversableEntry of getTraversableGenerator(readableStream, lookupTagName, options)) { | ||
yield traversableToJSON(traversableEntry, options); | ||
const realOptions = { ...defaultStreamOptions, ...options }; | ||
for await (const traversableEntry of getTraversableGenerator(readableStream, lookupTagName, realOptions)) { | ||
yield traversableToJSON(traversableEntry, realOptions); | ||
} | ||
} | ||
//# sourceMappingURL=parseStream.js.map |
@@ -1,2 +0,2 @@ | ||
import { XMLNode } from '../XMLNode'; | ||
import type { XMLAttributeValue, XMLNode } from '../XMLNode'; | ||
export declare const decoder: { | ||
@@ -17,2 +17,3 @@ decode: (array: Uint8Array) => string; | ||
} | ||
export type TagValueProcessor = (value: Uint8Array, currentNode: XMLNode) => any; | ||
export interface ParseOptions { | ||
@@ -25,9 +26,9 @@ /** | ||
/** | ||
* @default '$' | ||
* @default '' | ||
*/ | ||
attributeNamePrefix?: string; | ||
attributesNodeName?: string; | ||
/** | ||
* @default ''' | ||
* Tag values can be modified during parsing. By default we decode the tag value (a Uint8Array) using TextDecoder | ||
*/ | ||
attributesNodeName?: string; | ||
tagValueProcessor?: TagValueProcessor; | ||
/** | ||
@@ -47,8 +48,2 @@ * skip attributes | ||
/** | ||
* Parse attribute values that looks like number or boolean | ||
* @default true | ||
*/ | ||
dynamicTypingAttributeValue?: boolean; | ||
tagNameProcessor?: (arg: string) => string; | ||
/** | ||
* @default '#text' | ||
@@ -58,7 +53,9 @@ */ | ||
/** | ||
* @default true | ||
* Callback to process tag names | ||
* @default (name:string) => name | ||
*/ | ||
cdataPositddionChar?: string; | ||
tagNameProcessor?: (name: string) => string; | ||
/** | ||
* @default true | ||
* Callback to process attribute names | ||
* @default (name:string) => `$${name}` | ||
*/ | ||
@@ -71,7 +68,2 @@ attributeNameProcessor?: (name: string) => string; | ||
/** | ||
* Parse tag values that looks like number or boolean | ||
* @default true | ||
*/ | ||
dynamicTypingNodeValue?: boolean; | ||
/** | ||
* @default false | ||
@@ -85,10 +77,6 @@ */ | ||
/** | ||
* Tag values can be modified during parsing. By default we decode the tag value (a Uint8Array) using TextDecoder | ||
*/ | ||
tagValueProcessor?: (value: Uint8Array, currentNode: XMLNode, tagName?: string) => string | Uint8Array; | ||
/** | ||
* Attribute values can be modified during parsing | ||
* @default (value:string)=>value | ||
*/ | ||
attributeValueProcessor?: (value: string, attrName: string) => string; | ||
attributeValueProcessor?: (value: string, name: string) => XMLAttributeValue; | ||
/** | ||
@@ -100,3 +88,6 @@ * prevent further parsing | ||
} | ||
export declare const defaultOptions: ParseOptions; | ||
export type RealParseOptions = Required<ParseOptions>; | ||
export type RealStreamParseOptions = Required<StreamParseOptions>; | ||
export declare const defaultOptions: RealParseOptions; | ||
export declare const defaultStreamOptions: RealStreamParseOptions; | ||
//# sourceMappingURL=defaultOptions.d.ts.map |
@@ -0,1 +1,2 @@ | ||
import { parseString } from 'dynamic-typing'; | ||
const utf8Decoder = new TextDecoder(); | ||
@@ -9,3 +10,2 @@ export const decoder = { | ||
trimValues: true, | ||
attributeNamePrefix: '$', | ||
attributesNodeName: '', | ||
@@ -15,13 +15,20 @@ ignoreAttributes: false, | ||
allowBooleanAttributes: false, | ||
dynamicTypingAttributeValue: true, | ||
parseAttributesString: true, | ||
textNodeName: '#text', | ||
dynamicTypingNodeValue: true, | ||
arrayMode: false, | ||
cdataTagName: false, | ||
tagNameProcessor: (name) => name, | ||
attributeNameProcessor: (name) => `$${name}`, | ||
tagValueProcessor: (value) => { | ||
return decoder.decode(value).replace(/\r/g, ''); | ||
const string = decoder.decode(value).replaceAll('\r', ''); | ||
return parseString(string); | ||
}, | ||
attributeValueProcessor: (value) => value, | ||
attributeValueProcessor: (value) => parseString(value), | ||
stopNodes: [], | ||
}; | ||
export const defaultStreamOptions = { | ||
...defaultOptions, | ||
maxEntrySize: 1e7, | ||
maxBufferSize: 2e8, | ||
}; | ||
//# sourceMappingURL=defaultOptions.js.map |
import { XMLNode } from '../XMLNode'; | ||
import { ParseOptions } from './defaultOptions'; | ||
export declare function getTraversable(xmlData: Uint8Array, options: ParseOptions): XMLNode; | ||
import type { RealParseOptions } from './defaultOptions'; | ||
export declare function getTraversable(xmlData: Uint8Array, options: RealParseOptions): XMLNode; | ||
//# sourceMappingURL=getTraversable.d.ts.map |
@@ -7,7 +7,7 @@ import { XMLNode } from '../XMLNode'; | ||
import { parseAttributesString } from './parseAttributesString'; | ||
import { concat } from './utils/concat'; | ||
import { removeNameSpaceIfNeeded } from './utils/removeNameSpaceIfNeeded'; | ||
import { decoder } from './utils/utf8Decoder'; | ||
export function getTraversable(xmlData, options) { | ||
const traversable = new XMLNode('!xml'); | ||
const { tagValueProcessor } = options; | ||
const traversable = new XMLNode('!xml', undefined, new Uint8Array(0), tagValueProcessor); | ||
let currentNode = traversable; | ||
@@ -28,11 +28,5 @@ let dataSize = 0; | ||
if (currentNode) { | ||
const value = options.trimValues | ||
currentNode.append(options.trimValues | ||
? arrayTrim(xmlData.subarray(dataIndex, dataIndex + dataSize)) | ||
: xmlData.subarray(dataIndex, dataIndex + dataSize); | ||
if (currentNode.value === undefined) { | ||
currentNode.value = value; | ||
} | ||
else { | ||
currentNode.value = concat(currentNode.value, value); | ||
} | ||
: xmlData.subarray(dataIndex, dataIndex + dataSize)); | ||
} | ||
@@ -45,3 +39,3 @@ if (options.stopNodes?.length && | ||
} | ||
currentNode.value = xmlData.subarray(currentNode.startIndex + 1, i); | ||
currentNode.bytes = xmlData.subarray(currentNode.startIndex + 1, i); | ||
} | ||
@@ -64,8 +58,6 @@ currentNode = currentNode.parent; | ||
i, 'Comment is not closed.'); | ||
if (currentNode && dataSize !== 0) { | ||
if (currentNode.tagName !== '!xml') { | ||
currentNode.value = concat(currentNode.value, options.trimValues | ||
? arrayTrim(xmlData.subarray(dataIndex, dataSize + dataIndex)) | ||
: xmlData.subarray(dataIndex, dataSize + dataIndex)); | ||
} | ||
if (currentNode && dataSize !== 0 && currentNode.tagName !== '!xml') { | ||
currentNode.append(options.trimValues | ||
? arrayTrim(xmlData.subarray(dataIndex, dataSize + dataIndex)) | ||
: xmlData.subarray(dataIndex, dataSize + dataIndex)); | ||
} | ||
@@ -100,15 +92,15 @@ dataSize = 0; | ||
: xmlData.subarray(dataIndex, dataIndex + dataSize); | ||
currentNode.value = concat(currentNode.value, value); | ||
currentNode.append(value); | ||
} | ||
if (options.cdataTagName) { | ||
//add cdata node | ||
const childNode = new XMLNode(options.cdataTagName, currentNode, tagExp); | ||
const childNode = new XMLNode(options.cdataTagName, currentNode, tagExp, tagValueProcessor); | ||
currentNode.addChild(childNode); | ||
//add rest value to parent node | ||
if (tagExp) { | ||
childNode.value = tagExp; | ||
childNode.bytes = tagExp; | ||
} | ||
} | ||
else { | ||
currentNode.value = concat(currentNode.value, tagExp); | ||
currentNode.append(tagExp); | ||
} | ||
@@ -122,25 +114,23 @@ i = closeIndex + 2; | ||
const parsedOpeningTag = closingIndexForOpeningTag(xmlData, i + 1); | ||
const tagData = parsedOpeningTag.data.replace(/\r?\n|\t/g, ' '); | ||
const tagData = parsedOpeningTag.data.replaceAll(/\r?\n|\t/g, ' '); | ||
const closeIndex = parsedOpeningTag.index; | ||
const separatorIndex = tagData.indexOf(' '); | ||
let shouldBuildAttributesMap = true; | ||
let tagName = separatorIndex >= 0 | ||
? tagData.substr(0, separatorIndex).replace(/\s+$/, '') | ||
let tagName = separatorIndex !== -1 | ||
? tagData.slice(0, Math.max(0, separatorIndex)).replace(/\s+$/, '') | ||
: tagData; | ||
let tagAttributes = separatorIndex >= 0 ? tagData.substr(separatorIndex + 1) : ''; | ||
let tagAttributes = separatorIndex !== -1 ? tagData.slice(separatorIndex + 1) : ''; | ||
if (options.ignoreNameSpace) { | ||
const colonIndex = tagName.indexOf(':'); | ||
if (colonIndex !== -1) { | ||
tagName = tagName.substr(colonIndex + 1); | ||
tagName = tagName.slice(colonIndex + 1); | ||
shouldBuildAttributesMap = | ||
tagName !== parsedOpeningTag.data.substr(colonIndex + 1); | ||
tagName !== parsedOpeningTag.data.slice(colonIndex + 1); | ||
} | ||
} | ||
//save text to parent node | ||
if (currentNode && dataSize !== 0) { | ||
if (currentNode.tagName !== '!xml') { | ||
currentNode.value = concat(currentNode.value, options.trimValues | ||
? arrayTrim(xmlData.subarray(dataIndex, dataIndex + dataSize)) | ||
: xmlData.subarray(dataIndex, dataIndex + dataSize)); | ||
} | ||
if (currentNode && dataSize !== 0 && currentNode.tagName !== '!xml') { | ||
currentNode.append(options.trimValues | ||
? arrayTrim(xmlData.subarray(dataIndex, dataIndex + dataSize)) | ||
: xmlData.subarray(dataIndex, dataIndex + dataSize)); | ||
} | ||
@@ -151,9 +141,9 @@ if (tagData.length > 0 && tagData.endsWith('/')) { | ||
// <abc def="123"/> | ||
tagAttributes = tagAttributes.substr(0, tagAttributes.length - 1); | ||
tagAttributes = tagAttributes.slice(0, Math.max(0, tagAttributes.length - 1)); | ||
} | ||
else { | ||
// <abc/> | ||
tagName = tagName.substr(0, tagName.length - 1); | ||
tagName = tagName.slice(0, Math.max(0, tagName.length - 1)); | ||
} | ||
const childNode = new XMLNode(tagName, currentNode, ''); | ||
const childNode = new XMLNode(tagName, currentNode, new Uint8Array(0), tagValueProcessor); | ||
if (tagAttributes) { | ||
@@ -166,3 +156,3 @@ childNode.attributes = parseAttributesString(tagAttributes, options); | ||
//opening tag | ||
const childNode = new XMLNode(tagName, currentNode); | ||
const childNode = new XMLNode(tagName, currentNode, new Uint8Array(0), tagValueProcessor); | ||
if (options.stopNodes?.length && | ||
@@ -169,0 +159,0 @@ options.stopNodes.includes(childNode.tagName)) { |
import { XMLNode } from '../XMLNode'; | ||
import { StreamParseOptions } from './defaultOptions'; | ||
export declare function getTraversableGenerator(readableStream: ReadableStream, lookupTagName: string, options: StreamParseOptions): AsyncGenerator<XMLNode, void, unknown>; | ||
import type { RealStreamParseOptions } from './defaultOptions'; | ||
export declare function getTraversableGenerator(readableStream: ReadableStream, lookupTagName: string, options: RealStreamParseOptions): AsyncGenerator<XMLNode, void, unknown>; | ||
//# sourceMappingURL=getTraversableGenerator.d.ts.map |
@@ -7,6 +7,10 @@ import { XMLNode } from '../XMLNode'; | ||
import { parseAttributesString } from './parseAttributesString'; | ||
import { concat } from './utils/concat'; | ||
import { removeNameSpaceIfNeeded } from './utils/removeNameSpaceIfNeeded'; | ||
import { decoder } from './utils/utf8Decoder'; | ||
export async function* getTraversableGenerator(readableStream, lookupTagName, options) { | ||
export async function* getTraversableGenerator(readableStream, lookupTagName, | ||
// tagValueProcessor is not mandatory so I need to change StreamParseOptions to Partial<StreamParseOptions> | ||
// to avoid the error: Property 'tagValueProcessor' is missing in type 'StreamParseOptions' but required in type 'Partial<StreamParseOptions>' | ||
// streamParseOptions: StreamParseOptions, | ||
options) { | ||
const { tagValueProcessor } = options; | ||
let dataSize = 0; | ||
@@ -62,11 +66,5 @@ let dataIndex = 0; | ||
if (currentNode) { | ||
const value = options.trimValues | ||
currentNode.append(options.trimValues | ||
? arrayTrim(xmlData.subarray(dataIndex, dataIndex + dataSize)) | ||
: xmlData.subarray(dataIndex, dataIndex + dataSize); | ||
if (currentNode.value === undefined) { | ||
currentNode.value = value; | ||
} | ||
else { | ||
currentNode.value = concat(currentNode.value, value); | ||
} | ||
: xmlData.subarray(dataIndex, dataIndex + dataSize)); | ||
if (options.stopNodes?.length && | ||
@@ -78,3 +76,3 @@ options.stopNodes.includes(currentNode.tagName)) { | ||
} | ||
currentNode.value = xmlData.subarray(currentNode.startIndex + 1, i); | ||
currentNode.bytes = xmlData.subarray(currentNode.startIndex + 1, i); | ||
} | ||
@@ -102,8 +100,6 @@ if (tagName === lookupTagName) { | ||
i, 'Comment is not closed.'); | ||
if (currentNode && dataSize !== 0) { | ||
if (currentNode.tagName !== '!xml') { | ||
currentNode.value = concat(currentNode.value, options.trimValues | ||
? arrayTrim(xmlData.subarray(dataIndex, dataSize + dataIndex)) | ||
: xmlData.subarray(dataIndex, dataSize + dataIndex)); | ||
} | ||
if (currentNode && dataSize !== 0 && currentNode.tagName !== '!xml') { | ||
currentNode.append(options.trimValues | ||
? arrayTrim(xmlData.subarray(dataIndex, dataSize + dataIndex)) | ||
: xmlData.subarray(dataIndex, dataSize + dataIndex)); | ||
} | ||
@@ -138,17 +134,15 @@ dataSize = 0; | ||
: xmlData.subarray(dataIndex, dataIndex + dataSize); | ||
if (currentNode) | ||
currentNode.value = concat(currentNode.value, value); | ||
currentNode?.append(value); | ||
} | ||
if (options.cdataTagName) { | ||
//add cdata node | ||
const childNode = new XMLNode(options.cdataTagName, currentNode, tagExp); | ||
if (currentNode) | ||
currentNode.addChild(childNode); | ||
const childNode = new XMLNode(options.cdataTagName, currentNode, tagExp, tagValueProcessor); | ||
currentNode?.addChild(childNode); | ||
//add rest value to parent node | ||
if (tagExp) { | ||
childNode.value = tagExp; | ||
childNode.bytes = tagExp; | ||
} | ||
} | ||
else if (currentNode) { | ||
currentNode.value = concat(currentNode.value, tagExp); | ||
currentNode.append(tagExp); | ||
} | ||
@@ -162,25 +156,23 @@ i = closeIndex + 2; | ||
const parsedOpeningTag = closingIndexForOpeningTag(xmlData, i + 1); | ||
const tagData = parsedOpeningTag.data.replace(/\r?\n|\t/g, ' '); | ||
const tagData = parsedOpeningTag.data.replaceAll(/\r?\n|\t/g, ' '); | ||
const closeIndex = parsedOpeningTag.index; | ||
const separatorIndex = tagData.indexOf(' '); | ||
let shouldBuildAttributesMap = true; | ||
let tagName = separatorIndex >= 0 | ||
? tagData.substr(0, separatorIndex).replace(/\s+$/, '') | ||
let tagName = separatorIndex !== -1 | ||
? tagData.slice(0, Math.max(0, separatorIndex)).replace(/\s+$/, '') | ||
: tagData; | ||
let tagAttributes = separatorIndex >= 0 ? tagData.substr(separatorIndex + 1) : ''; | ||
let tagAttributes = separatorIndex !== -1 ? tagData.slice(separatorIndex + 1) : ''; | ||
if (options.ignoreNameSpace) { | ||
const colonIndex = tagName.indexOf(':'); | ||
if (colonIndex !== -1) { | ||
tagName = tagName.substr(colonIndex + 1); | ||
tagName = tagName.slice(colonIndex + 1); | ||
shouldBuildAttributesMap = | ||
tagName !== parsedOpeningTag.data.substr(colonIndex + 1); | ||
tagName !== parsedOpeningTag.data.slice(colonIndex + 1); | ||
} | ||
} | ||
//save text to parent node | ||
if (currentNode && dataSize !== 0) { | ||
if (currentNode.tagName !== '!xml') { | ||
currentNode.value = concat(currentNode.value, options.trimValues | ||
? arrayTrim(xmlData.subarray(dataIndex, dataIndex + dataSize)) | ||
: xmlData.subarray(dataIndex, dataIndex + dataSize)); | ||
} | ||
if (currentNode && dataSize !== 0 && currentNode.tagName !== '!xml') { | ||
currentNode.append(options.trimValues | ||
? arrayTrim(xmlData.subarray(dataIndex, dataIndex + dataSize)) | ||
: xmlData.subarray(dataIndex, dataIndex + dataSize)); | ||
} | ||
@@ -193,9 +185,9 @@ if (tagData.length > 0 && tagData.endsWith('/')) { | ||
// <abc def="123"/> | ||
tagAttributes = tagAttributes.substr(0, tagAttributes.length - 1); | ||
tagAttributes = tagAttributes.slice(0, Math.max(0, tagAttributes.length - 1)); | ||
} | ||
else { | ||
// <abc/> | ||
tagName = tagName.substr(0, tagName.length - 1); | ||
tagName = tagName.slice(0, Math.max(0, tagName.length - 1)); | ||
} | ||
const childNode = new XMLNode(tagName, currentNode, ''); | ||
const childNode = new XMLNode(tagName, currentNode, new Uint8Array(0), tagValueProcessor); | ||
if (tagAttributes) { | ||
@@ -211,3 +203,3 @@ childNode.attributes = parseAttributesString(tagAttributes, options); | ||
if (currentNode || tagName === lookupTagName) { | ||
const childNode = new XMLNode(tagName, currentNode); | ||
const childNode = new XMLNode(tagName, currentNode, new Uint8Array(0), tagValueProcessor); | ||
if (options.stopNodes?.length && | ||
@@ -214,0 +206,0 @@ options.stopNodes.includes(childNode.tagName)) { |
@@ -1,4 +0,3 @@ | ||
import { XMLNode } from '../XMLNode'; | ||
import { ParseOptions } from './defaultOptions'; | ||
export declare function parseAttributesString(string: string, options: ParseOptions): Record<string, boolean | XMLNode> | undefined; | ||
import type { RealParseOptions } from './defaultOptions'; | ||
export declare function parseAttributesString(string: string, options: RealParseOptions): Record<string, string | number | boolean> | undefined; | ||
//# sourceMappingURL=parseAttributesString.d.ts.map |
@@ -1,17 +0,17 @@ | ||
import { getAllMatches, isEmptyObject } from '../util'; | ||
// eslint-disable-next-line @typescript-eslint/no-var-requires | ||
const { parseString } = require('dynamic-typing'); | ||
const newLocal = '([^\\s=]+)\\s*(=\\s*([\'"])(.*?)\\3)?'; | ||
import { getAllMatches, isEmptySimpleObject } from '../util'; | ||
const newLocal = String.raw `([^\s=]+)\s*(=\s*(['"])(.*?)\3)?`; | ||
const attrsRegx = new RegExp(newLocal, 'g'); | ||
//Attributes are strings so no point in using arrayBuffers here | ||
export function parseAttributesString(string, options) { | ||
if (options.ignoreAttributes) { | ||
const { ignoreAttributes } = options; | ||
if (ignoreAttributes) { | ||
return; | ||
} | ||
string = string.replace(/\r?\n/g, ' '); | ||
string = string.replaceAll(/\r?\n/g, ' '); | ||
const matches = getAllMatches(string, attrsRegx); | ||
// argument 1 is the key, argument 4 is the value | ||
const attributes = {}; | ||
for (const match of matches) { | ||
const attrName = resolveNameSpace(match[1], options); | ||
if (attrName.length) { | ||
const attributeName = resolveNameSpace(match[1], options); | ||
if (attributeName.length > 0) { | ||
if (match[4] !== undefined) { | ||
@@ -22,23 +22,14 @@ if (options.trimValues) { | ||
if (options.attributeValueProcessor) { | ||
match[4] = options.attributeValueProcessor(match[4], attrName); | ||
attributes[attrName] = stringParseValue(match[4], options.dynamicTypingAttributeValue); | ||
attributes[attributeName] = options.attributeValueProcessor(match[4], attributeName); | ||
} | ||
} | ||
else if (options.allowBooleanAttributes) { | ||
attributes[attrName] = true; | ||
attributes[attributeName] = true; | ||
} | ||
} | ||
} | ||
if (isEmptyObject(attributes)) | ||
if (isEmptySimpleObject(attributes)) | ||
return; | ||
return attributes; | ||
} | ||
function stringParseValue(value, shouldParse) { | ||
if (shouldParse && typeof value === 'string') { | ||
return parseString(value); | ||
} | ||
else { | ||
return value === undefined ? '' : value; | ||
} | ||
} | ||
function resolveNameSpace(tagName, options) { | ||
@@ -45,0 +36,0 @@ if (options.ignoreNameSpace) { |
@@ -1,3 +0,3 @@ | ||
import { ParseOptions } from '../defaultOptions'; | ||
import type { ParseOptions } from '../defaultOptions'; | ||
export declare function removeNameSpaceIfNeeded(tagName: string, options: ParseOptions): string; | ||
//# sourceMappingURL=removeNameSpaceIfNeeded.d.ts.map |
@@ -7,3 +7,3 @@ export function removeNameSpaceIfNeeded(tagName, options) { | ||
if (colonIndex !== -1) { | ||
tagName = tagName.substr(colonIndex + 1); | ||
tagName = tagName.slice(colonIndex + 1); | ||
} | ||
@@ -10,0 +10,0 @@ return tagName; |
@@ -1,11 +0,11 @@ | ||
import { XMLNode, XMLNodeValue } from './XMLNode'; | ||
import { ParseOptions } from './traversable/defaultOptions'; | ||
import type { XMLNode, XMLNodeValue } from './XMLNode'; | ||
import type { RealParseOptions } from './traversable/defaultOptions'; | ||
/** | ||
* | ||
* @param {*} node | ||
* @param {*} options | ||
* @param {*} parentTagName | ||
* @param node | ||
* @param options | ||
* @param parentTagName | ||
* @returns | ||
*/ | ||
export declare function traversableToJSON(node: XMLNode, options: ParseOptions, parentTagName?: string): XMLNodeValue | Record<string, XMLNodeValue>; | ||
export declare function traversableToJSON(node: XMLNode, options: RealParseOptions, parentTagName?: string): XMLNodeValue | Record<string, XMLNodeValue>; | ||
//# sourceMappingURL=traversableToJSON.d.ts.map |
@@ -1,46 +0,30 @@ | ||
import { parseString } from 'dynamic-typing'; | ||
import { isTagNameInArrayMode, merge, isEmptyObject } from './util'; | ||
import { isTagNameInArrayMode, merge, isEmptyObject, isEmptySimpleObject, } from './util'; | ||
/** | ||
* | ||
* @param {*} node | ||
* @param {*} options | ||
* @param {*} parentTagName | ||
* @param node | ||
* @param options | ||
* @param parentTagName | ||
* @returns | ||
*/ | ||
export function traversableToJSON(node, options, parentTagName) { | ||
const { dynamicTypingNodeValue, tagValueProcessor, arrayMode, tagNameProcessor, attributeNameProcessor, } = options; | ||
const { arrayMode, tagNameProcessor, attributeNameProcessor, textNodeName } = options; | ||
const result = {}; | ||
if (tagValueProcessor) { | ||
node.value = | ||
node.value && tagValueProcessor(node.value, node); | ||
} | ||
if (typeof node.value === 'string' && dynamicTypingNodeValue) { | ||
node.value = parseString(node.value); | ||
} | ||
// when no child node or attr is present | ||
if ((!node.children || isEmptyObject(node.children)) && | ||
(!node.attributes || isEmptyObject(node.attributes))) { | ||
return node.value === undefined ? '' : node.value; | ||
(!node.attributes || isEmptySimpleObject(node.attributes))) { | ||
return node.value; | ||
} | ||
// otherwise create a textnode if node has some text | ||
if (node.value !== undefined && | ||
(typeof node.value === 'number' || | ||
typeof node.value === 'boolean' || | ||
node.value.length !== 0)) { | ||
if (node.bytes.length > 0) { | ||
const asArray = isTagNameInArrayMode(node.tagName, arrayMode, parentTagName); | ||
result[options.textNodeName] = asArray | ||
? [node.value] | ||
: node.value; | ||
result[textNodeName] = asArray ? [node.value] : node.value; | ||
} | ||
if (node.attributes && !isEmptyObject(node.attributes)) { | ||
if (node.attributes && !isEmptySimpleObject(node.attributes)) { | ||
let attributes = options.parseAttributesString ? {} : node.attributes; | ||
if (options.attributeNamePrefix) { | ||
if (attributeNameProcessor) { | ||
// need to rename the attributes | ||
const renamedAttributes = {}; | ||
for (const attributeName in node.attributes) { | ||
const newAttributeName = attributeNameProcessor | ||
? attributeNameProcessor(attributeName) | ||
: attributeName; | ||
renamedAttributes[options.attributeNamePrefix + newAttributeName] = | ||
node.attributes[attributeName]; | ||
const newAttributeName = attributeNameProcessor(attributeName); | ||
renamedAttributes[newAttributeName] = node.attributes[attributeName]; | ||
} | ||
@@ -54,2 +38,3 @@ attributes = renamedAttributes; | ||
} | ||
//@ts-expect-error Should fix this type issue | ||
merge(result, attributes, arrayMode); | ||
@@ -56,0 +41,0 @@ } |
@@ -1,9 +0,11 @@ | ||
import { XMLNode } from './XMLNode'; | ||
import type { XMLAttributeValue, XMLNode } from './XMLNode'; | ||
export declare function getAllMatches(string: string, regex: RegExp): RegExpExecArray[]; | ||
export declare function isName(string: string): boolean; | ||
export declare function isEmptyObject(obj: Record<string, boolean | XMLNode | XMLNode[]>): boolean; | ||
export declare function isEmptySimpleObject(object: Record<string, XMLAttributeValue>): boolean; | ||
export declare function isEmptyObject(object: Record<string, boolean | XMLNode | XMLNode[]>): boolean; | ||
/** | ||
* Copy all the properties of a into b. | ||
* @param {object} target | ||
* @param {object} source | ||
* @param target | ||
* @param source | ||
* @param arrayMode | ||
*/ | ||
@@ -13,9 +15,8 @@ export declare function merge(target: Record<string, boolean | XMLNode | Array<XMLNode | boolean>>, source: Record<string, boolean | XMLNode>, arrayMode: ((tagName: string, parentTagName: string) => boolean) | string | boolean | RegExp): void; | ||
* Check if a tag name should be treated as array | ||
* | ||
* @param tagName the node tagName | ||
* @param arrayMode the array mode option | ||
* @param parentTagName the parent tag name | ||
* @returns {boolean} true if node should be parsed as array | ||
* @param tagName - the node tagName | ||
* @param arrayMode - the array mode option | ||
* @param parentTagName - the parent tag name | ||
* @returns true if node should be parsed as array | ||
*/ | ||
export declare function isTagNameInArrayMode(tagName: string, arrayMode: ((tagName: string, parentTagName: string) => boolean) | string | boolean | RegExp | undefined, parentTagName: string): boolean; | ||
//# sourceMappingURL=util.d.ts.map |
@@ -1,2 +0,2 @@ | ||
const nameStartChar = ':A-Za-z_\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD'; | ||
const nameStartChar = String.raw `:A-Za-z_\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD`; | ||
const nameChar = `${nameStartChar}\\-.\\d\\u00B7\\u0300-\\u036F\\u203F-\\u2040`; | ||
@@ -12,6 +12,6 @@ const nameRegexp = `[${nameStartChar}][${nameChar}]*`; | ||
} | ||
export function isEmptyObject(obj) { | ||
export function isEmptySimpleObject(object) { | ||
// fastest implementation: https://jsbench.me/qfkqv692c8/1 | ||
// eslint-disable-next-line no-unreachable-loop | ||
for (const key in obj) { | ||
for (const key in object) { | ||
return false; | ||
@@ -21,6 +21,15 @@ } | ||
} | ||
export function isEmptyObject(object) { | ||
// fastest implementation: https://jsbench.me/qfkqv692c8/1 | ||
// eslint-disable-next-line no-unreachable-loop | ||
for (const key in object) { | ||
return false; | ||
} | ||
return true; | ||
} | ||
/** | ||
* Copy all the properties of a into b. | ||
* @param {object} target | ||
* @param {object} source | ||
* @param target | ||
* @param source | ||
* @param arrayMode | ||
*/ | ||
@@ -41,7 +50,6 @@ export function merge(target, source, arrayMode) { | ||
* Check if a tag name should be treated as array | ||
* | ||
* @param tagName the node tagName | ||
* @param arrayMode the array mode option | ||
* @param parentTagName the parent tag name | ||
* @returns {boolean} true if node should be parsed as array | ||
* @param tagName - the node tagName | ||
* @param arrayMode - the array mode option | ||
* @param parentTagName - the parent tag name | ||
* @returns true if node should be parsed as array | ||
*/ | ||
@@ -48,0 +56,0 @@ export function isTagNameInArrayMode(tagName, arrayMode, parentTagName) { |
@@ -0,2 +1,4 @@ | ||
import type { TagValueProcessor } from './traversable/defaultOptions'; | ||
export type XMLNodeValue = string | Uint8Array | number | boolean; | ||
export type XMLAttributeValue = string | number | boolean; | ||
export declare class XMLNode { | ||
@@ -6,8 +8,12 @@ tagName: string; | ||
children: Record<string, XMLNode[]>; | ||
attributes?: Record<string, XMLNode | boolean>; | ||
value?: XMLNodeValue; | ||
attributes?: Record<string, XMLAttributeValue>; | ||
bytes: Uint8Array; | ||
startIndex: number; | ||
constructor(tagName: string, parent?: XMLNode, value?: Uint8Array | string | undefined | number); | ||
private tagValueProcessor; | ||
private cachedValue?; | ||
constructor(tagName: string, parent: XMLNode | undefined, bytes: Uint8Array, tagValueProcessor: TagValueProcessor); | ||
append(toAppend: Uint8Array): void; | ||
get value(): any; | ||
addChild(child: XMLNode): void; | ||
} | ||
//# sourceMappingURL=XMLNode.d.ts.map |
@@ -6,5 +6,7 @@ export class XMLNode { | ||
attributes; | ||
value; | ||
bytes; | ||
startIndex; | ||
constructor(tagName, parent, value) { | ||
tagValueProcessor; | ||
cachedValue; | ||
constructor(tagName, parent, bytes, tagValueProcessor) { | ||
this.tagName = tagName; | ||
@@ -14,5 +16,23 @@ this.parent = parent; | ||
this.attributes = Object.create(null); //attributes map | ||
this.value = value; //text only | ||
this.bytes = bytes; //text only | ||
this.tagValueProcessor = tagValueProcessor; | ||
this.startIndex = -1; | ||
} | ||
append(toAppend) { | ||
if (this.bytes.length === 0) { | ||
this.bytes = toAppend; | ||
return; | ||
} | ||
const arrayConcat = new Uint8Array(this.bytes.length + toAppend.length); | ||
arrayConcat.set(this.bytes); | ||
arrayConcat.set(toAppend, this.bytes.length); | ||
this.bytes = arrayConcat; | ||
} | ||
get value() { | ||
if (this.cachedValue === undefined) { | ||
const value = this.tagValueProcessor(this.bytes, this); | ||
this.cachedValue = value; | ||
} | ||
return this.cachedValue; | ||
} | ||
addChild(child) { | ||
@@ -19,0 +39,0 @@ if (Array.isArray(this.children[child.tagName])) { |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.arrayIndexOf = void 0; | ||
exports.arrayIndexOf = arrayIndexOf; | ||
function arrayIndexOf(array, referenceArray, index = 0) { | ||
@@ -36,3 +36,2 @@ let found = 0; | ||
} | ||
exports.arrayIndexOf = arrayIndexOf; | ||
//# sourceMappingURL=arrayIndexOf.js.map |
@@ -1,2 +0,2 @@ | ||
export declare function arrayTrim(array: Uint8Array, arg?: unknown): Uint8Array; | ||
export declare function arrayTrim(array: Uint8Array, arg?: unknown): Uint8Array<ArrayBufferLike>; | ||
//# sourceMappingURL=arrayTrim.d.ts.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.arrayTrim = void 0; | ||
exports.arrayTrim = arrayTrim; | ||
/* eslint-disable @typescript-eslint/no-unused-vars */ | ||
@@ -16,3 +16,2 @@ function arrayTrim(array, arg) { | ||
} | ||
exports.arrayTrim = arrayTrim; | ||
//# sourceMappingURL=arrayTrim.js.map |
@@ -1,6 +0,8 @@ | ||
import { ParseOptions } from './traversable/defaultOptions'; | ||
import type { ParseOptions } from './traversable/defaultOptions'; | ||
/** | ||
* Parse an ArrayBuffer or Uint8Array representing an XML and return an object | ||
* @param xmlData | ||
* @param options | ||
*/ | ||
export declare function parse(xmlData: string | Uint8Array | ArrayBufferLike, options?: ParseOptions): import("./XMLNode").XMLNodeValue | Record<string, import("./XMLNode").XMLNodeValue>; | ||
//# sourceMappingURL=parse.d.ts.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.parse = void 0; | ||
exports.parse = parse; | ||
const defaultOptions_1 = require("./traversable/defaultOptions"); | ||
@@ -9,2 +9,4 @@ const getTraversable_1 = require("./traversable/getTraversable"); | ||
* Parse an ArrayBuffer or Uint8Array representing an XML and return an object | ||
* @param xmlData | ||
* @param options | ||
*/ | ||
@@ -19,7 +21,6 @@ function parse(xmlData, options = {}) { | ||
} | ||
options = { ...defaultOptions_1.defaultOptions, ...options }; | ||
const traversable = (0, getTraversable_1.getTraversable)(xmlData, options); | ||
return (0, traversableToJSON_1.traversableToJSON)(traversable, options); | ||
const realOptions = { ...defaultOptions_1.defaultOptions, ...options }; | ||
const traversable = (0, getTraversable_1.getTraversable)(xmlData, realOptions); | ||
return (0, traversableToJSON_1.traversableToJSON)(traversable, realOptions); | ||
} | ||
exports.parse = parse; | ||
//# sourceMappingURL=parse.js.map |
@@ -1,6 +0,9 @@ | ||
import { StreamParseOptions } from './traversable/defaultOptions'; | ||
import type { StreamParseOptions } from './traversable/defaultOptions'; | ||
/** | ||
* Parse a web stream representing an XML and emit objects | ||
* @param readableStream | ||
* @param lookupTagName | ||
* @param options | ||
*/ | ||
export declare function parseStream(readableStream: ReadableStream, lookupTagName: string, options?: StreamParseOptions): AsyncGenerator<import("./XMLNode").XMLNodeValue | Record<string, import("./XMLNode").XMLNodeValue>, void, unknown>; | ||
//# sourceMappingURL=parseStream.d.ts.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.parseStream = void 0; | ||
exports.parseStream = parseStream; | ||
const defaultOptions_1 = require("./traversable/defaultOptions"); | ||
@@ -9,10 +9,12 @@ const getTraversableGenerator_1 = require("./traversable/getTraversableGenerator"); | ||
* Parse a web stream representing an XML and emit objects | ||
* @param readableStream | ||
* @param lookupTagName | ||
* @param options | ||
*/ | ||
async function* parseStream(readableStream, lookupTagName, options = {}) { | ||
options = { ...defaultOptions_1.defaultOptions, ...options }; | ||
for await (const traversableEntry of (0, getTraversableGenerator_1.getTraversableGenerator)(readableStream, lookupTagName, options)) { | ||
yield (0, traversableToJSON_1.traversableToJSON)(traversableEntry, options); | ||
const realOptions = { ...defaultOptions_1.defaultStreamOptions, ...options }; | ||
for await (const traversableEntry of (0, getTraversableGenerator_1.getTraversableGenerator)(readableStream, lookupTagName, realOptions)) { | ||
yield (0, traversableToJSON_1.traversableToJSON)(traversableEntry, realOptions); | ||
} | ||
} | ||
exports.parseStream = parseStream; | ||
//# sourceMappingURL=parseStream.js.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.closingIndexForOpeningTag = void 0; | ||
exports.closingIndexForOpeningTag = closingIndexForOpeningTag; | ||
const utf8Decoder_1 = require("./utils/utf8Decoder"); | ||
@@ -36,3 +36,2 @@ /** | ||
} | ||
exports.closingIndexForOpeningTag = closingIndexForOpeningTag; | ||
//# sourceMappingURL=closingIndexForOpeningTag.js.map |
@@ -1,2 +0,2 @@ | ||
import { XMLNode } from '../XMLNode'; | ||
import type { XMLAttributeValue, XMLNode } from '../XMLNode'; | ||
export declare const decoder: { | ||
@@ -17,2 +17,3 @@ decode: (array: Uint8Array) => string; | ||
} | ||
export type TagValueProcessor = (value: Uint8Array, currentNode: XMLNode) => any; | ||
export interface ParseOptions { | ||
@@ -25,9 +26,9 @@ /** | ||
/** | ||
* @default '$' | ||
* @default '' | ||
*/ | ||
attributeNamePrefix?: string; | ||
attributesNodeName?: string; | ||
/** | ||
* @default ''' | ||
* Tag values can be modified during parsing. By default we decode the tag value (a Uint8Array) using TextDecoder | ||
*/ | ||
attributesNodeName?: string; | ||
tagValueProcessor?: TagValueProcessor; | ||
/** | ||
@@ -47,8 +48,2 @@ * skip attributes | ||
/** | ||
* Parse attribute values that looks like number or boolean | ||
* @default true | ||
*/ | ||
dynamicTypingAttributeValue?: boolean; | ||
tagNameProcessor?: (arg: string) => string; | ||
/** | ||
* @default '#text' | ||
@@ -58,7 +53,9 @@ */ | ||
/** | ||
* @default true | ||
* Callback to process tag names | ||
* @default (name:string) => name | ||
*/ | ||
cdataPositddionChar?: string; | ||
tagNameProcessor?: (name: string) => string; | ||
/** | ||
* @default true | ||
* Callback to process attribute names | ||
* @default (name:string) => `$${name}` | ||
*/ | ||
@@ -71,7 +68,2 @@ attributeNameProcessor?: (name: string) => string; | ||
/** | ||
* Parse tag values that looks like number or boolean | ||
* @default true | ||
*/ | ||
dynamicTypingNodeValue?: boolean; | ||
/** | ||
* @default false | ||
@@ -85,10 +77,6 @@ */ | ||
/** | ||
* Tag values can be modified during parsing. By default we decode the tag value (a Uint8Array) using TextDecoder | ||
*/ | ||
tagValueProcessor?: (value: Uint8Array, currentNode: XMLNode, tagName?: string) => string | Uint8Array; | ||
/** | ||
* Attribute values can be modified during parsing | ||
* @default (value:string)=>value | ||
*/ | ||
attributeValueProcessor?: (value: string, attrName: string) => string; | ||
attributeValueProcessor?: (value: string, name: string) => XMLAttributeValue; | ||
/** | ||
@@ -100,3 +88,6 @@ * prevent further parsing | ||
} | ||
export declare const defaultOptions: ParseOptions; | ||
export type RealParseOptions = Required<ParseOptions>; | ||
export type RealStreamParseOptions = Required<StreamParseOptions>; | ||
export declare const defaultOptions: RealParseOptions; | ||
export declare const defaultStreamOptions: RealStreamParseOptions; | ||
//# sourceMappingURL=defaultOptions.d.ts.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.defaultOptions = exports.decoder = void 0; | ||
exports.defaultStreamOptions = exports.defaultOptions = exports.decoder = void 0; | ||
const dynamic_typing_1 = require("dynamic-typing"); | ||
const utf8Decoder = new TextDecoder(); | ||
@@ -12,3 +13,2 @@ exports.decoder = { | ||
trimValues: true, | ||
attributeNamePrefix: '$', | ||
attributesNodeName: '', | ||
@@ -18,13 +18,20 @@ ignoreAttributes: false, | ||
allowBooleanAttributes: false, | ||
dynamicTypingAttributeValue: true, | ||
parseAttributesString: true, | ||
textNodeName: '#text', | ||
dynamicTypingNodeValue: true, | ||
arrayMode: false, | ||
cdataTagName: false, | ||
tagNameProcessor: (name) => name, | ||
attributeNameProcessor: (name) => `$${name}`, | ||
tagValueProcessor: (value) => { | ||
return exports.decoder.decode(value).replace(/\r/g, ''); | ||
const string = exports.decoder.decode(value).replaceAll('\r', ''); | ||
return (0, dynamic_typing_1.parseString)(string); | ||
}, | ||
attributeValueProcessor: (value) => value, | ||
attributeValueProcessor: (value) => (0, dynamic_typing_1.parseString)(value), | ||
stopNodes: [], | ||
}; | ||
exports.defaultStreamOptions = { | ||
...exports.defaultOptions, | ||
maxEntrySize: 1e7, | ||
maxBufferSize: 2e8, | ||
}; | ||
//# sourceMappingURL=defaultOptions.js.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.findClosingIndex = void 0; | ||
exports.findClosingIndex = findClosingIndex; | ||
const arrayIndexOf_1 = require("../bufferUtils/arrayIndexOf"); | ||
@@ -14,3 +14,2 @@ function findClosingIndex(xmlData, str, i, errMsg) { | ||
} | ||
exports.findClosingIndex = findClosingIndex; | ||
//# sourceMappingURL=findClosingIndex.js.map |
import { XMLNode } from '../XMLNode'; | ||
import { ParseOptions } from './defaultOptions'; | ||
export declare function getTraversable(xmlData: Uint8Array, options: ParseOptions): XMLNode; | ||
import type { RealParseOptions } from './defaultOptions'; | ||
export declare function getTraversable(xmlData: Uint8Array, options: RealParseOptions): XMLNode; | ||
//# sourceMappingURL=getTraversable.d.ts.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.getTraversable = void 0; | ||
exports.getTraversable = getTraversable; | ||
const XMLNode_1 = require("../XMLNode"); | ||
@@ -10,7 +10,7 @@ const arrayIndexOf_1 = require("../bufferUtils/arrayIndexOf"); | ||
const parseAttributesString_1 = require("./parseAttributesString"); | ||
const concat_1 = require("./utils/concat"); | ||
const removeNameSpaceIfNeeded_1 = require("./utils/removeNameSpaceIfNeeded"); | ||
const utf8Decoder_1 = require("./utils/utf8Decoder"); | ||
function getTraversable(xmlData, options) { | ||
const traversable = new XMLNode_1.XMLNode('!xml'); | ||
const { tagValueProcessor } = options; | ||
const traversable = new XMLNode_1.XMLNode('!xml', undefined, new Uint8Array(0), tagValueProcessor); | ||
let currentNode = traversable; | ||
@@ -31,11 +31,5 @@ let dataSize = 0; | ||
if (currentNode) { | ||
const value = options.trimValues | ||
currentNode.append(options.trimValues | ||
? (0, arrayTrim_1.arrayTrim)(xmlData.subarray(dataIndex, dataIndex + dataSize)) | ||
: xmlData.subarray(dataIndex, dataIndex + dataSize); | ||
if (currentNode.value === undefined) { | ||
currentNode.value = value; | ||
} | ||
else { | ||
currentNode.value = (0, concat_1.concat)(currentNode.value, value); | ||
} | ||
: xmlData.subarray(dataIndex, dataIndex + dataSize)); | ||
} | ||
@@ -48,3 +42,3 @@ if (options.stopNodes?.length && | ||
} | ||
currentNode.value = xmlData.subarray(currentNode.startIndex + 1, i); | ||
currentNode.bytes = xmlData.subarray(currentNode.startIndex + 1, i); | ||
} | ||
@@ -67,8 +61,6 @@ currentNode = currentNode.parent; | ||
i, 'Comment is not closed.'); | ||
if (currentNode && dataSize !== 0) { | ||
if (currentNode.tagName !== '!xml') { | ||
currentNode.value = (0, concat_1.concat)(currentNode.value, options.trimValues | ||
? (0, arrayTrim_1.arrayTrim)(xmlData.subarray(dataIndex, dataSize + dataIndex)) | ||
: xmlData.subarray(dataIndex, dataSize + dataIndex)); | ||
} | ||
if (currentNode && dataSize !== 0 && currentNode.tagName !== '!xml') { | ||
currentNode.append(options.trimValues | ||
? (0, arrayTrim_1.arrayTrim)(xmlData.subarray(dataIndex, dataSize + dataIndex)) | ||
: xmlData.subarray(dataIndex, dataSize + dataIndex)); | ||
} | ||
@@ -103,15 +95,15 @@ dataSize = 0; | ||
: xmlData.subarray(dataIndex, dataIndex + dataSize); | ||
currentNode.value = (0, concat_1.concat)(currentNode.value, value); | ||
currentNode.append(value); | ||
} | ||
if (options.cdataTagName) { | ||
//add cdata node | ||
const childNode = new XMLNode_1.XMLNode(options.cdataTagName, currentNode, tagExp); | ||
const childNode = new XMLNode_1.XMLNode(options.cdataTagName, currentNode, tagExp, tagValueProcessor); | ||
currentNode.addChild(childNode); | ||
//add rest value to parent node | ||
if (tagExp) { | ||
childNode.value = tagExp; | ||
childNode.bytes = tagExp; | ||
} | ||
} | ||
else { | ||
currentNode.value = (0, concat_1.concat)(currentNode.value, tagExp); | ||
currentNode.append(tagExp); | ||
} | ||
@@ -125,25 +117,23 @@ i = closeIndex + 2; | ||
const parsedOpeningTag = (0, closingIndexForOpeningTag_1.closingIndexForOpeningTag)(xmlData, i + 1); | ||
const tagData = parsedOpeningTag.data.replace(/\r?\n|\t/g, ' '); | ||
const tagData = parsedOpeningTag.data.replaceAll(/\r?\n|\t/g, ' '); | ||
const closeIndex = parsedOpeningTag.index; | ||
const separatorIndex = tagData.indexOf(' '); | ||
let shouldBuildAttributesMap = true; | ||
let tagName = separatorIndex >= 0 | ||
? tagData.substr(0, separatorIndex).replace(/\s+$/, '') | ||
let tagName = separatorIndex !== -1 | ||
? tagData.slice(0, Math.max(0, separatorIndex)).replace(/\s+$/, '') | ||
: tagData; | ||
let tagAttributes = separatorIndex >= 0 ? tagData.substr(separatorIndex + 1) : ''; | ||
let tagAttributes = separatorIndex !== -1 ? tagData.slice(separatorIndex + 1) : ''; | ||
if (options.ignoreNameSpace) { | ||
const colonIndex = tagName.indexOf(':'); | ||
if (colonIndex !== -1) { | ||
tagName = tagName.substr(colonIndex + 1); | ||
tagName = tagName.slice(colonIndex + 1); | ||
shouldBuildAttributesMap = | ||
tagName !== parsedOpeningTag.data.substr(colonIndex + 1); | ||
tagName !== parsedOpeningTag.data.slice(colonIndex + 1); | ||
} | ||
} | ||
//save text to parent node | ||
if (currentNode && dataSize !== 0) { | ||
if (currentNode.tagName !== '!xml') { | ||
currentNode.value = (0, concat_1.concat)(currentNode.value, options.trimValues | ||
? (0, arrayTrim_1.arrayTrim)(xmlData.subarray(dataIndex, dataIndex + dataSize)) | ||
: xmlData.subarray(dataIndex, dataIndex + dataSize)); | ||
} | ||
if (currentNode && dataSize !== 0 && currentNode.tagName !== '!xml') { | ||
currentNode.append(options.trimValues | ||
? (0, arrayTrim_1.arrayTrim)(xmlData.subarray(dataIndex, dataIndex + dataSize)) | ||
: xmlData.subarray(dataIndex, dataIndex + dataSize)); | ||
} | ||
@@ -154,9 +144,9 @@ if (tagData.length > 0 && tagData.endsWith('/')) { | ||
// <abc def="123"/> | ||
tagAttributes = tagAttributes.substr(0, tagAttributes.length - 1); | ||
tagAttributes = tagAttributes.slice(0, Math.max(0, tagAttributes.length - 1)); | ||
} | ||
else { | ||
// <abc/> | ||
tagName = tagName.substr(0, tagName.length - 1); | ||
tagName = tagName.slice(0, Math.max(0, tagName.length - 1)); | ||
} | ||
const childNode = new XMLNode_1.XMLNode(tagName, currentNode, ''); | ||
const childNode = new XMLNode_1.XMLNode(tagName, currentNode, new Uint8Array(0), tagValueProcessor); | ||
if (tagAttributes) { | ||
@@ -169,3 +159,3 @@ childNode.attributes = (0, parseAttributesString_1.parseAttributesString)(tagAttributes, options); | ||
//opening tag | ||
const childNode = new XMLNode_1.XMLNode(tagName, currentNode); | ||
const childNode = new XMLNode_1.XMLNode(tagName, currentNode, new Uint8Array(0), tagValueProcessor); | ||
if (options.stopNodes?.length && | ||
@@ -192,3 +182,2 @@ options.stopNodes.includes(childNode.tagName)) { | ||
} | ||
exports.getTraversable = getTraversable; | ||
//# sourceMappingURL=getTraversable.js.map |
import { XMLNode } from '../XMLNode'; | ||
import { StreamParseOptions } from './defaultOptions'; | ||
export declare function getTraversableGenerator(readableStream: ReadableStream, lookupTagName: string, options: StreamParseOptions): AsyncGenerator<XMLNode, void, unknown>; | ||
import type { RealStreamParseOptions } from './defaultOptions'; | ||
export declare function getTraversableGenerator(readableStream: ReadableStream, lookupTagName: string, options: RealStreamParseOptions): AsyncGenerator<XMLNode, void, unknown>; | ||
//# sourceMappingURL=getTraversableGenerator.d.ts.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.getTraversableGenerator = void 0; | ||
exports.getTraversableGenerator = getTraversableGenerator; | ||
const XMLNode_1 = require("../XMLNode"); | ||
@@ -10,6 +10,10 @@ const arrayIndexOf_1 = require("../bufferUtils/arrayIndexOf"); | ||
const parseAttributesString_1 = require("./parseAttributesString"); | ||
const concat_1 = require("./utils/concat"); | ||
const removeNameSpaceIfNeeded_1 = require("./utils/removeNameSpaceIfNeeded"); | ||
const utf8Decoder_1 = require("./utils/utf8Decoder"); | ||
async function* getTraversableGenerator(readableStream, lookupTagName, options) { | ||
async function* getTraversableGenerator(readableStream, lookupTagName, | ||
// tagValueProcessor is not mandatory so I need to change StreamParseOptions to Partial<StreamParseOptions> | ||
// to avoid the error: Property 'tagValueProcessor' is missing in type 'StreamParseOptions' but required in type 'Partial<StreamParseOptions>' | ||
// streamParseOptions: StreamParseOptions, | ||
options) { | ||
const { tagValueProcessor } = options; | ||
let dataSize = 0; | ||
@@ -65,11 +69,5 @@ let dataIndex = 0; | ||
if (currentNode) { | ||
const value = options.trimValues | ||
currentNode.append(options.trimValues | ||
? (0, arrayTrim_1.arrayTrim)(xmlData.subarray(dataIndex, dataIndex + dataSize)) | ||
: xmlData.subarray(dataIndex, dataIndex + dataSize); | ||
if (currentNode.value === undefined) { | ||
currentNode.value = value; | ||
} | ||
else { | ||
currentNode.value = (0, concat_1.concat)(currentNode.value, value); | ||
} | ||
: xmlData.subarray(dataIndex, dataIndex + dataSize)); | ||
if (options.stopNodes?.length && | ||
@@ -81,3 +79,3 @@ options.stopNodes.includes(currentNode.tagName)) { | ||
} | ||
currentNode.value = xmlData.subarray(currentNode.startIndex + 1, i); | ||
currentNode.bytes = xmlData.subarray(currentNode.startIndex + 1, i); | ||
} | ||
@@ -105,8 +103,6 @@ if (tagName === lookupTagName) { | ||
i, 'Comment is not closed.'); | ||
if (currentNode && dataSize !== 0) { | ||
if (currentNode.tagName !== '!xml') { | ||
currentNode.value = (0, concat_1.concat)(currentNode.value, options.trimValues | ||
? (0, arrayTrim_1.arrayTrim)(xmlData.subarray(dataIndex, dataSize + dataIndex)) | ||
: xmlData.subarray(dataIndex, dataSize + dataIndex)); | ||
} | ||
if (currentNode && dataSize !== 0 && currentNode.tagName !== '!xml') { | ||
currentNode.append(options.trimValues | ||
? (0, arrayTrim_1.arrayTrim)(xmlData.subarray(dataIndex, dataSize + dataIndex)) | ||
: xmlData.subarray(dataIndex, dataSize + dataIndex)); | ||
} | ||
@@ -141,17 +137,15 @@ dataSize = 0; | ||
: xmlData.subarray(dataIndex, dataIndex + dataSize); | ||
if (currentNode) | ||
currentNode.value = (0, concat_1.concat)(currentNode.value, value); | ||
currentNode?.append(value); | ||
} | ||
if (options.cdataTagName) { | ||
//add cdata node | ||
const childNode = new XMLNode_1.XMLNode(options.cdataTagName, currentNode, tagExp); | ||
if (currentNode) | ||
currentNode.addChild(childNode); | ||
const childNode = new XMLNode_1.XMLNode(options.cdataTagName, currentNode, tagExp, tagValueProcessor); | ||
currentNode?.addChild(childNode); | ||
//add rest value to parent node | ||
if (tagExp) { | ||
childNode.value = tagExp; | ||
childNode.bytes = tagExp; | ||
} | ||
} | ||
else if (currentNode) { | ||
currentNode.value = (0, concat_1.concat)(currentNode.value, tagExp); | ||
currentNode.append(tagExp); | ||
} | ||
@@ -165,25 +159,23 @@ i = closeIndex + 2; | ||
const parsedOpeningTag = (0, closingIndexForOpeningTag_1.closingIndexForOpeningTag)(xmlData, i + 1); | ||
const tagData = parsedOpeningTag.data.replace(/\r?\n|\t/g, ' '); | ||
const tagData = parsedOpeningTag.data.replaceAll(/\r?\n|\t/g, ' '); | ||
const closeIndex = parsedOpeningTag.index; | ||
const separatorIndex = tagData.indexOf(' '); | ||
let shouldBuildAttributesMap = true; | ||
let tagName = separatorIndex >= 0 | ||
? tagData.substr(0, separatorIndex).replace(/\s+$/, '') | ||
let tagName = separatorIndex !== -1 | ||
? tagData.slice(0, Math.max(0, separatorIndex)).replace(/\s+$/, '') | ||
: tagData; | ||
let tagAttributes = separatorIndex >= 0 ? tagData.substr(separatorIndex + 1) : ''; | ||
let tagAttributes = separatorIndex !== -1 ? tagData.slice(separatorIndex + 1) : ''; | ||
if (options.ignoreNameSpace) { | ||
const colonIndex = tagName.indexOf(':'); | ||
if (colonIndex !== -1) { | ||
tagName = tagName.substr(colonIndex + 1); | ||
tagName = tagName.slice(colonIndex + 1); | ||
shouldBuildAttributesMap = | ||
tagName !== parsedOpeningTag.data.substr(colonIndex + 1); | ||
tagName !== parsedOpeningTag.data.slice(colonIndex + 1); | ||
} | ||
} | ||
//save text to parent node | ||
if (currentNode && dataSize !== 0) { | ||
if (currentNode.tagName !== '!xml') { | ||
currentNode.value = (0, concat_1.concat)(currentNode.value, options.trimValues | ||
? (0, arrayTrim_1.arrayTrim)(xmlData.subarray(dataIndex, dataIndex + dataSize)) | ||
: xmlData.subarray(dataIndex, dataIndex + dataSize)); | ||
} | ||
if (currentNode && dataSize !== 0 && currentNode.tagName !== '!xml') { | ||
currentNode.append(options.trimValues | ||
? (0, arrayTrim_1.arrayTrim)(xmlData.subarray(dataIndex, dataIndex + dataSize)) | ||
: xmlData.subarray(dataIndex, dataIndex + dataSize)); | ||
} | ||
@@ -196,9 +188,9 @@ if (tagData.length > 0 && tagData.endsWith('/')) { | ||
// <abc def="123"/> | ||
tagAttributes = tagAttributes.substr(0, tagAttributes.length - 1); | ||
tagAttributes = tagAttributes.slice(0, Math.max(0, tagAttributes.length - 1)); | ||
} | ||
else { | ||
// <abc/> | ||
tagName = tagName.substr(0, tagName.length - 1); | ||
tagName = tagName.slice(0, Math.max(0, tagName.length - 1)); | ||
} | ||
const childNode = new XMLNode_1.XMLNode(tagName, currentNode, ''); | ||
const childNode = new XMLNode_1.XMLNode(tagName, currentNode, new Uint8Array(0), tagValueProcessor); | ||
if (tagAttributes) { | ||
@@ -214,3 +206,3 @@ childNode.attributes = (0, parseAttributesString_1.parseAttributesString)(tagAttributes, options); | ||
if (currentNode || tagName === lookupTagName) { | ||
const childNode = new XMLNode_1.XMLNode(tagName, currentNode); | ||
const childNode = new XMLNode_1.XMLNode(tagName, currentNode, new Uint8Array(0), tagValueProcessor); | ||
if (options.stopNodes?.length && | ||
@@ -238,3 +230,2 @@ options.stopNodes.includes(childNode.tagName)) { | ||
} | ||
exports.getTraversableGenerator = getTraversableGenerator; | ||
//# sourceMappingURL=getTraversableGenerator.js.map |
@@ -1,4 +0,3 @@ | ||
import { XMLNode } from '../XMLNode'; | ||
import { ParseOptions } from './defaultOptions'; | ||
export declare function parseAttributesString(string: string, options: ParseOptions): Record<string, boolean | XMLNode> | undefined; | ||
import type { RealParseOptions } from './defaultOptions'; | ||
export declare function parseAttributesString(string: string, options: RealParseOptions): Record<string, string | number | boolean> | undefined; | ||
//# sourceMappingURL=parseAttributesString.d.ts.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.parseAttributesString = void 0; | ||
exports.parseAttributesString = parseAttributesString; | ||
const util_1 = require("../util"); | ||
// eslint-disable-next-line @typescript-eslint/no-var-requires | ||
const { parseString } = require('dynamic-typing'); | ||
const newLocal = '([^\\s=]+)\\s*(=\\s*([\'"])(.*?)\\3)?'; | ||
const newLocal = String.raw `([^\s=]+)\s*(=\s*(['"])(.*?)\3)?`; | ||
const attrsRegx = new RegExp(newLocal, 'g'); | ||
//Attributes are strings so no point in using arrayBuffers here | ||
function parseAttributesString(string, options) { | ||
if (options.ignoreAttributes) { | ||
const { ignoreAttributes } = options; | ||
if (ignoreAttributes) { | ||
return; | ||
} | ||
string = string.replace(/\r?\n/g, ' '); | ||
string = string.replaceAll(/\r?\n/g, ' '); | ||
const matches = (0, util_1.getAllMatches)(string, attrsRegx); | ||
// argument 1 is the key, argument 4 is the value | ||
const attributes = {}; | ||
for (const match of matches) { | ||
const attrName = resolveNameSpace(match[1], options); | ||
if (attrName.length) { | ||
const attributeName = resolveNameSpace(match[1], options); | ||
if (attributeName.length > 0) { | ||
if (match[4] !== undefined) { | ||
@@ -25,24 +25,14 @@ if (options.trimValues) { | ||
if (options.attributeValueProcessor) { | ||
match[4] = options.attributeValueProcessor(match[4], attrName); | ||
attributes[attrName] = stringParseValue(match[4], options.dynamicTypingAttributeValue); | ||
attributes[attributeName] = options.attributeValueProcessor(match[4], attributeName); | ||
} | ||
} | ||
else if (options.allowBooleanAttributes) { | ||
attributes[attrName] = true; | ||
attributes[attributeName] = true; | ||
} | ||
} | ||
} | ||
if ((0, util_1.isEmptyObject)(attributes)) | ||
if ((0, util_1.isEmptySimpleObject)(attributes)) | ||
return; | ||
return attributes; | ||
} | ||
exports.parseAttributesString = parseAttributesString; | ||
function stringParseValue(value, shouldParse) { | ||
if (shouldParse && typeof value === 'string') { | ||
return parseString(value); | ||
} | ||
else { | ||
return value === undefined ? '' : value; | ||
} | ||
} | ||
function resolveNameSpace(tagName, options) { | ||
@@ -49,0 +39,0 @@ if (options.ignoreNameSpace) { |
@@ -1,3 +0,3 @@ | ||
import { ParseOptions } from '../defaultOptions'; | ||
import type { ParseOptions } from '../defaultOptions'; | ||
export declare function removeNameSpaceIfNeeded(tagName: string, options: ParseOptions): string; | ||
//# sourceMappingURL=removeNameSpaceIfNeeded.d.ts.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.removeNameSpaceIfNeeded = void 0; | ||
exports.removeNameSpaceIfNeeded = removeNameSpaceIfNeeded; | ||
function removeNameSpaceIfNeeded(tagName, options) { | ||
@@ -10,7 +10,6 @@ if (!options.ignoreNameSpace) { | ||
if (colonIndex !== -1) { | ||
tagName = tagName.substr(colonIndex + 1); | ||
tagName = tagName.slice(colonIndex + 1); | ||
} | ||
return tagName; | ||
} | ||
exports.removeNameSpaceIfNeeded = removeNameSpaceIfNeeded; | ||
//# sourceMappingURL=removeNameSpaceIfNeeded.js.map |
@@ -1,11 +0,11 @@ | ||
import { XMLNode, XMLNodeValue } from './XMLNode'; | ||
import { ParseOptions } from './traversable/defaultOptions'; | ||
import type { XMLNode, XMLNodeValue } from './XMLNode'; | ||
import type { RealParseOptions } from './traversable/defaultOptions'; | ||
/** | ||
* | ||
* @param {*} node | ||
* @param {*} options | ||
* @param {*} parentTagName | ||
* @param node | ||
* @param options | ||
* @param parentTagName | ||
* @returns | ||
*/ | ||
export declare function traversableToJSON(node: XMLNode, options: ParseOptions, parentTagName?: string): XMLNodeValue | Record<string, XMLNodeValue>; | ||
export declare function traversableToJSON(node: XMLNode, options: RealParseOptions, parentTagName?: string): XMLNodeValue | Record<string, XMLNodeValue>; | ||
//# sourceMappingURL=traversableToJSON.d.ts.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.traversableToJSON = void 0; | ||
const dynamic_typing_1 = require("dynamic-typing"); | ||
exports.traversableToJSON = traversableToJSON; | ||
const util_1 = require("./util"); | ||
/** | ||
* | ||
* @param {*} node | ||
* @param {*} options | ||
* @param {*} parentTagName | ||
* @param node | ||
* @param options | ||
* @param parentTagName | ||
* @returns | ||
*/ | ||
function traversableToJSON(node, options, parentTagName) { | ||
const { dynamicTypingNodeValue, tagValueProcessor, arrayMode, tagNameProcessor, attributeNameProcessor, } = options; | ||
const { arrayMode, tagNameProcessor, attributeNameProcessor, textNodeName } = options; | ||
const result = {}; | ||
if (tagValueProcessor) { | ||
node.value = | ||
node.value && tagValueProcessor(node.value, node); | ||
} | ||
if (typeof node.value === 'string' && dynamicTypingNodeValue) { | ||
node.value = (0, dynamic_typing_1.parseString)(node.value); | ||
} | ||
// when no child node or attr is present | ||
if ((!node.children || (0, util_1.isEmptyObject)(node.children)) && | ||
(!node.attributes || (0, util_1.isEmptyObject)(node.attributes))) { | ||
return node.value === undefined ? '' : node.value; | ||
(!node.attributes || (0, util_1.isEmptySimpleObject)(node.attributes))) { | ||
return node.value; | ||
} | ||
// otherwise create a textnode if node has some text | ||
if (node.value !== undefined && | ||
(typeof node.value === 'number' || | ||
typeof node.value === 'boolean' || | ||
node.value.length !== 0)) { | ||
if (node.bytes.length > 0) { | ||
const asArray = (0, util_1.isTagNameInArrayMode)(node.tagName, arrayMode, parentTagName); | ||
result[options.textNodeName] = asArray | ||
? [node.value] | ||
: node.value; | ||
result[textNodeName] = asArray ? [node.value] : node.value; | ||
} | ||
if (node.attributes && !(0, util_1.isEmptyObject)(node.attributes)) { | ||
if (node.attributes && !(0, util_1.isEmptySimpleObject)(node.attributes)) { | ||
let attributes = options.parseAttributesString ? {} : node.attributes; | ||
if (options.attributeNamePrefix) { | ||
if (attributeNameProcessor) { | ||
// need to rename the attributes | ||
const renamedAttributes = {}; | ||
for (const attributeName in node.attributes) { | ||
const newAttributeName = attributeNameProcessor | ||
? attributeNameProcessor(attributeName) | ||
: attributeName; | ||
renamedAttributes[options.attributeNamePrefix + newAttributeName] = | ||
node.attributes[attributeName]; | ||
const newAttributeName = attributeNameProcessor(attributeName); | ||
renamedAttributes[newAttributeName] = node.attributes[attributeName]; | ||
} | ||
@@ -57,2 +41,3 @@ attributes = renamedAttributes; | ||
} | ||
//@ts-expect-error Should fix this type issue | ||
(0, util_1.merge)(result, attributes, arrayMode); | ||
@@ -80,3 +65,2 @@ } | ||
} | ||
exports.traversableToJSON = traversableToJSON; | ||
//# sourceMappingURL=traversableToJSON.js.map |
@@ -1,9 +0,11 @@ | ||
import { XMLNode } from './XMLNode'; | ||
import type { XMLAttributeValue, XMLNode } from './XMLNode'; | ||
export declare function getAllMatches(string: string, regex: RegExp): RegExpExecArray[]; | ||
export declare function isName(string: string): boolean; | ||
export declare function isEmptyObject(obj: Record<string, boolean | XMLNode | XMLNode[]>): boolean; | ||
export declare function isEmptySimpleObject(object: Record<string, XMLAttributeValue>): boolean; | ||
export declare function isEmptyObject(object: Record<string, boolean | XMLNode | XMLNode[]>): boolean; | ||
/** | ||
* Copy all the properties of a into b. | ||
* @param {object} target | ||
* @param {object} source | ||
* @param target | ||
* @param source | ||
* @param arrayMode | ||
*/ | ||
@@ -13,9 +15,8 @@ export declare function merge(target: Record<string, boolean | XMLNode | Array<XMLNode | boolean>>, source: Record<string, boolean | XMLNode>, arrayMode: ((tagName: string, parentTagName: string) => boolean) | string | boolean | RegExp): void; | ||
* Check if a tag name should be treated as array | ||
* | ||
* @param tagName the node tagName | ||
* @param arrayMode the array mode option | ||
* @param parentTagName the parent tag name | ||
* @returns {boolean} true if node should be parsed as array | ||
* @param tagName - the node tagName | ||
* @param arrayMode - the array mode option | ||
* @param parentTagName - the parent tag name | ||
* @returns true if node should be parsed as array | ||
*/ | ||
export declare function isTagNameInArrayMode(tagName: string, arrayMode: ((tagName: string, parentTagName: string) => boolean) | string | boolean | RegExp | undefined, parentTagName: string): boolean; | ||
//# sourceMappingURL=util.d.ts.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.isTagNameInArrayMode = exports.merge = exports.isEmptyObject = exports.isName = exports.getAllMatches = void 0; | ||
const nameStartChar = ':A-Za-z_\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD'; | ||
exports.getAllMatches = getAllMatches; | ||
exports.isName = isName; | ||
exports.isEmptySimpleObject = isEmptySimpleObject; | ||
exports.isEmptyObject = isEmptyObject; | ||
exports.merge = merge; | ||
exports.isTagNameInArrayMode = isTagNameInArrayMode; | ||
const nameStartChar = String.raw `:A-Za-z_\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD`; | ||
const nameChar = `${nameStartChar}\\-.\\d\\u00B7\\u0300-\\u036F\\u203F-\\u2040`; | ||
@@ -12,11 +17,9 @@ const nameRegexp = `[${nameStartChar}][${nameChar}]*`; | ||
} | ||
exports.getAllMatches = getAllMatches; | ||
function isName(string) { | ||
return regexName.exec(string) !== null; | ||
} | ||
exports.isName = isName; | ||
function isEmptyObject(obj) { | ||
function isEmptySimpleObject(object) { | ||
// fastest implementation: https://jsbench.me/qfkqv692c8/1 | ||
// eslint-disable-next-line no-unreachable-loop | ||
for (const key in obj) { | ||
for (const key in object) { | ||
return false; | ||
@@ -26,7 +29,15 @@ } | ||
} | ||
exports.isEmptyObject = isEmptyObject; | ||
function isEmptyObject(object) { | ||
// fastest implementation: https://jsbench.me/qfkqv692c8/1 | ||
// eslint-disable-next-line no-unreachable-loop | ||
for (const key in object) { | ||
return false; | ||
} | ||
return true; | ||
} | ||
/** | ||
* Copy all the properties of a into b. | ||
* @param {object} target | ||
* @param {object} source | ||
* @param target | ||
* @param source | ||
* @param arrayMode | ||
*/ | ||
@@ -45,10 +56,8 @@ function merge(target, source, arrayMode) { | ||
} | ||
exports.merge = merge; | ||
/** | ||
* Check if a tag name should be treated as array | ||
* | ||
* @param tagName the node tagName | ||
* @param arrayMode the array mode option | ||
* @param parentTagName the parent tag name | ||
* @returns {boolean} true if node should be parsed as array | ||
* @param tagName - the node tagName | ||
* @param arrayMode - the array mode option | ||
* @param parentTagName - the parent tag name | ||
* @returns true if node should be parsed as array | ||
*/ | ||
@@ -67,3 +76,2 @@ function isTagNameInArrayMode(tagName, arrayMode, parentTagName) { | ||
} | ||
exports.isTagNameInArrayMode = isTagNameInArrayMode; | ||
//# sourceMappingURL=util.js.map |
@@ -0,2 +1,4 @@ | ||
import type { TagValueProcessor } from './traversable/defaultOptions'; | ||
export type XMLNodeValue = string | Uint8Array | number | boolean; | ||
export type XMLAttributeValue = string | number | boolean; | ||
export declare class XMLNode { | ||
@@ -6,8 +8,12 @@ tagName: string; | ||
children: Record<string, XMLNode[]>; | ||
attributes?: Record<string, XMLNode | boolean>; | ||
value?: XMLNodeValue; | ||
attributes?: Record<string, XMLAttributeValue>; | ||
bytes: Uint8Array; | ||
startIndex: number; | ||
constructor(tagName: string, parent?: XMLNode, value?: Uint8Array | string | undefined | number); | ||
private tagValueProcessor; | ||
private cachedValue?; | ||
constructor(tagName: string, parent: XMLNode | undefined, bytes: Uint8Array, tagValueProcessor: TagValueProcessor); | ||
append(toAppend: Uint8Array): void; | ||
get value(): any; | ||
addChild(child: XMLNode): void; | ||
} | ||
//# sourceMappingURL=XMLNode.d.ts.map |
@@ -9,5 +9,7 @@ "use strict"; | ||
attributes; | ||
value; | ||
bytes; | ||
startIndex; | ||
constructor(tagName, parent, value) { | ||
tagValueProcessor; | ||
cachedValue; | ||
constructor(tagName, parent, bytes, tagValueProcessor) { | ||
this.tagName = tagName; | ||
@@ -17,5 +19,23 @@ this.parent = parent; | ||
this.attributes = Object.create(null); //attributes map | ||
this.value = value; //text only | ||
this.bytes = bytes; //text only | ||
this.tagValueProcessor = tagValueProcessor; | ||
this.startIndex = -1; | ||
} | ||
append(toAppend) { | ||
if (this.bytes.length === 0) { | ||
this.bytes = toAppend; | ||
return; | ||
} | ||
const arrayConcat = new Uint8Array(this.bytes.length + toAppend.length); | ||
arrayConcat.set(this.bytes); | ||
arrayConcat.set(toAppend, this.bytes.length); | ||
this.bytes = arrayConcat; | ||
} | ||
get value() { | ||
if (this.cachedValue === undefined) { | ||
const value = this.tagValueProcessor(this.bytes, this); | ||
this.cachedValue = value; | ||
} | ||
return this.cachedValue; | ||
} | ||
addChild(child) { | ||
@@ -22,0 +42,0 @@ if (Array.isArray(this.children[child.tagName])) { |
{ | ||
"name": "arraybuffer-xml-parser", | ||
"version": "0.6.1", | ||
"version": "1.0.0", | ||
"description": "Parse XML files contained in an array buffer", | ||
@@ -23,3 +23,3 @@ "main": "./lib/index.js", | ||
"test": "npm run test-only && npm run eslint && npm run prettier && npm run check-types", | ||
"test-only": "jest --coverage", | ||
"test-only": "vitest run --coverage", | ||
"tsc": "npm run clean && npm run tsc-cjs && npm run tsc-esm", | ||
@@ -42,15 +42,15 @@ "tsc-cjs": "tsc --project tsconfig.cjs.json", | ||
"@types/he": "^1.2.3", | ||
"@types/jest": "^29.5.12", | ||
"@vitest/coverage-v8": "2.1.7", | ||
"cheminfo-build": "^1.2.0", | ||
"eslint": "^8.57.0", | ||
"eslint-config-cheminfo-typescript": "^12.4.0", | ||
"eslint": "^9.16.0", | ||
"eslint-config-cheminfo-typescript": "^17.0.0", | ||
"globals": "^15.13.0", | ||
"he": "^1.2.0", | ||
"iobuffer": "^5.3.2", | ||
"jest": "^29.7.0", | ||
"pako": "^2.1.0", | ||
"prettier": "^3.2.5", | ||
"rimraf": "^5.0.7", | ||
"ts-jest": "^29.1.2", | ||
"typescript": "^5.4.5", | ||
"uint8-base64": "^0.1.1" | ||
"prettier": "^3.4.1", | ||
"rimraf": "^6.0.1", | ||
"typescript": "^5.7.2", | ||
"uint8-base64": "^0.1.1", | ||
"vitest": "^2.1.7" | ||
}, | ||
@@ -57,0 +57,0 @@ "dependencies": { |
@@ -1,2 +0,6 @@ | ||
import { defaultOptions, ParseOptions } from './traversable/defaultOptions'; | ||
import type { | ||
ParseOptions, | ||
RealParseOptions, | ||
} from './traversable/defaultOptions'; | ||
import { defaultOptions } from './traversable/defaultOptions'; | ||
import { getTraversable } from './traversable/getTraversable'; | ||
@@ -7,2 +11,4 @@ import { traversableToJSON } from './traversableToJSON'; | ||
* Parse an ArrayBuffer or Uint8Array representing an XML and return an object | ||
* @param xmlData | ||
* @param options | ||
*/ | ||
@@ -22,7 +28,7 @@ export function parse( | ||
options = { ...defaultOptions, ...options }; | ||
const realOptions: RealParseOptions = { ...defaultOptions, ...options }; | ||
const traversable = getTraversable(xmlData as Uint8Array, options); | ||
const traversable = getTraversable(xmlData as Uint8Array, realOptions); | ||
return traversableToJSON(traversable, options); | ||
return traversableToJSON(traversable, realOptions); | ||
} |
@@ -1,5 +0,3 @@ | ||
import { | ||
defaultOptions, | ||
StreamParseOptions, | ||
} from './traversable/defaultOptions'; | ||
import type { StreamParseOptions } from './traversable/defaultOptions'; | ||
import { defaultStreamOptions } from './traversable/defaultOptions'; | ||
import { getTraversableGenerator } from './traversable/getTraversableGenerator'; | ||
@@ -10,2 +8,5 @@ import { traversableToJSON } from './traversableToJSON'; | ||
* Parse a web stream representing an XML and emit objects | ||
* @param readableStream | ||
* @param lookupTagName | ||
* @param options | ||
*/ | ||
@@ -17,3 +18,3 @@ export async function* parseStream( | ||
) { | ||
options = { ...defaultOptions, ...options }; | ||
const realOptions = { ...defaultStreamOptions, ...options }; | ||
@@ -23,6 +24,6 @@ for await (const traversableEntry of getTraversableGenerator( | ||
lookupTagName, | ||
options, | ||
realOptions, | ||
)) { | ||
yield traversableToJSON(traversableEntry, options); | ||
yield traversableToJSON(traversableEntry, realOptions); | ||
} | ||
} |
@@ -1,3 +0,5 @@ | ||
import { XMLNode } from '../XMLNode'; | ||
import { parseString } from 'dynamic-typing'; | ||
import type { XMLAttributeValue, XMLNode } from '../XMLNode'; | ||
const utf8Decoder = new TextDecoder(); | ||
@@ -24,2 +26,7 @@ | ||
export type TagValueProcessor = ( | ||
value: Uint8Array, | ||
currentNode: XMLNode, | ||
) => any; | ||
export interface ParseOptions { | ||
@@ -32,9 +39,9 @@ /** | ||
/** | ||
* @default '$' | ||
* @default '' | ||
*/ | ||
attributeNamePrefix?: string; | ||
attributesNodeName?: string; | ||
/** | ||
* @default ''' | ||
* Tag values can be modified during parsing. By default we decode the tag value (a Uint8Array) using TextDecoder | ||
*/ | ||
attributesNodeName?: string; | ||
tagValueProcessor?: TagValueProcessor; | ||
/** | ||
@@ -54,8 +61,2 @@ * skip attributes | ||
/** | ||
* Parse attribute values that looks like number or boolean | ||
* @default true | ||
*/ | ||
dynamicTypingAttributeValue?: boolean; | ||
tagNameProcessor?: (arg: string) => string; | ||
/** | ||
* @default '#text' | ||
@@ -65,7 +66,9 @@ */ | ||
/** | ||
* @default true | ||
* Callback to process tag names | ||
* @default (name:string) => name | ||
*/ | ||
cdataPositddionChar?: string; | ||
tagNameProcessor?: (name: string) => string; | ||
/** | ||
* @default true | ||
* Callback to process attribute names | ||
* @default (name:string) => `$${name}` | ||
*/ | ||
@@ -78,7 +81,2 @@ attributeNameProcessor?: (name: string) => string; | ||
/** | ||
* Parse tag values that looks like number or boolean | ||
* @default true | ||
*/ | ||
dynamicTypingNodeValue?: boolean; | ||
/** | ||
* @default false | ||
@@ -96,14 +94,6 @@ */ | ||
/** | ||
* Tag values can be modified during parsing. By default we decode the tag value (a Uint8Array) using TextDecoder | ||
*/ | ||
tagValueProcessor?: ( | ||
value: Uint8Array, | ||
currentNode: XMLNode, | ||
tagName?: string, | ||
) => string | Uint8Array; | ||
/** | ||
* Attribute values can be modified during parsing | ||
* @default (value:string)=>value | ||
*/ | ||
attributeValueProcessor?: (value: string, attrName: string) => string; | ||
attributeValueProcessor?: (value: string, name: string) => XMLAttributeValue; | ||
/** | ||
@@ -116,5 +106,8 @@ * prevent further parsing | ||
export const defaultOptions: ParseOptions = { | ||
export type RealParseOptions = Required<ParseOptions>; | ||
export type RealStreamParseOptions = Required<StreamParseOptions>; | ||
export const defaultOptions: RealParseOptions = { | ||
trimValues: true, | ||
attributeNamePrefix: '$', | ||
attributesNodeName: '', | ||
@@ -124,14 +117,22 @@ ignoreAttributes: false, | ||
allowBooleanAttributes: false, | ||
dynamicTypingAttributeValue: true, | ||
parseAttributesString: true, | ||
textNodeName: '#text', | ||
dynamicTypingNodeValue: true, | ||
arrayMode: false, | ||
cdataTagName: false as unknown as string, | ||
tagNameProcessor: (name: string) => name, | ||
attributeNameProcessor: (name: string) => `$${name}`, | ||
tagValueProcessor: (value: Uint8Array) => { | ||
return decoder.decode(value).replace(/\r/g, ''); | ||
const string = decoder.decode(value).replaceAll('\r', ''); | ||
return parseString(string); | ||
}, | ||
attributeValueProcessor: (value: string) => value, | ||
attributeValueProcessor: (value: string) => parseString(value), | ||
stopNodes: [], | ||
}; | ||
export const defaultStreamOptions: RealStreamParseOptions = { | ||
...defaultOptions, | ||
maxEntrySize: 1e7, | ||
maxBufferSize: 2e8, | ||
}; |
@@ -6,11 +6,16 @@ import { XMLNode } from '../XMLNode'; | ||
import { closingIndexForOpeningTag } from './closingIndexForOpeningTag'; | ||
import { ParseOptions } from './defaultOptions'; | ||
import type { RealParseOptions } from './defaultOptions'; | ||
import { findClosingIndex } from './findClosingIndex'; | ||
import { parseAttributesString } from './parseAttributesString'; | ||
import { concat } from './utils/concat'; | ||
import { removeNameSpaceIfNeeded } from './utils/removeNameSpaceIfNeeded'; | ||
import { decoder } from './utils/utf8Decoder'; | ||
export function getTraversable(xmlData: Uint8Array, options: ParseOptions) { | ||
const traversable = new XMLNode('!xml'); | ||
export function getTraversable(xmlData: Uint8Array, options: RealParseOptions) { | ||
const { tagValueProcessor } = options; | ||
const traversable = new XMLNode( | ||
'!xml', | ||
undefined, | ||
new Uint8Array(0), | ||
tagValueProcessor, | ||
); | ||
let currentNode = traversable; | ||
@@ -38,10 +43,7 @@ let dataSize = 0; | ||
if (currentNode) { | ||
const value = options.trimValues | ||
? arrayTrim(xmlData.subarray(dataIndex, dataIndex + dataSize)) | ||
: xmlData.subarray(dataIndex, dataIndex + dataSize); | ||
if (currentNode.value === undefined) { | ||
currentNode.value = value; | ||
} else { | ||
currentNode.value = concat(currentNode.value, value); | ||
} | ||
currentNode.append( | ||
options.trimValues | ||
? arrayTrim(xmlData.subarray(dataIndex, dataIndex + dataSize)) | ||
: xmlData.subarray(dataIndex, dataIndex + dataSize), | ||
); | ||
} | ||
@@ -56,3 +58,3 @@ if ( | ||
} | ||
currentNode.value = xmlData.subarray(currentNode.startIndex + 1, i); | ||
currentNode.bytes = xmlData.subarray(currentNode.startIndex + 1, i); | ||
} | ||
@@ -78,11 +80,8 @@ currentNode = currentNode.parent as XMLNode; | ||
); | ||
if (currentNode && dataSize !== 0) { | ||
if (currentNode.tagName !== '!xml') { | ||
currentNode.value = concat( | ||
currentNode.value, | ||
options.trimValues | ||
? arrayTrim(xmlData.subarray(dataIndex, dataSize + dataIndex)) | ||
: xmlData.subarray(dataIndex, dataSize + dataIndex), | ||
); | ||
} | ||
if (currentNode && dataSize !== 0 && currentNode.tagName !== '!xml') { | ||
currentNode.append( | ||
options.trimValues | ||
? arrayTrim(xmlData.subarray(dataIndex, dataSize + dataIndex)) | ||
: xmlData.subarray(dataIndex, dataSize + dataIndex), | ||
); | ||
} | ||
@@ -125,3 +124,3 @@ dataSize = 0; | ||
currentNode.value = concat(currentNode.value, value); | ||
currentNode.append(value); | ||
} | ||
@@ -135,2 +134,3 @@ | ||
tagExp, | ||
tagValueProcessor, | ||
); | ||
@@ -140,6 +140,6 @@ currentNode.addChild(childNode); | ||
if (tagExp) { | ||
childNode.value = tagExp; | ||
childNode.bytes = tagExp; | ||
} | ||
} else { | ||
currentNode.value = concat(currentNode.value, tagExp); | ||
currentNode.append(tagExp); | ||
} | ||
@@ -153,3 +153,3 @@ | ||
const parsedOpeningTag = closingIndexForOpeningTag(xmlData, i + 1); | ||
const tagData = parsedOpeningTag.data.replace(/\r?\n|\t/g, ' '); | ||
const tagData = parsedOpeningTag.data.replaceAll(/\r?\n|\t/g, ' '); | ||
const closeIndex = parsedOpeningTag.index; | ||
@@ -159,13 +159,13 @@ const separatorIndex = tagData.indexOf(' '); | ||
let tagName = | ||
separatorIndex >= 0 | ||
? tagData.substr(0, separatorIndex).replace(/\s+$/, '') | ||
separatorIndex !== -1 | ||
? tagData.slice(0, Math.max(0, separatorIndex)).replace(/\s+$/, '') | ||
: tagData; | ||
let tagAttributes = | ||
separatorIndex >= 0 ? tagData.substr(separatorIndex + 1) : ''; | ||
separatorIndex !== -1 ? tagData.slice(separatorIndex + 1) : ''; | ||
if (options.ignoreNameSpace) { | ||
const colonIndex = tagName.indexOf(':'); | ||
if (colonIndex !== -1) { | ||
tagName = tagName.substr(colonIndex + 1); | ||
tagName = tagName.slice(colonIndex + 1); | ||
shouldBuildAttributesMap = | ||
tagName !== parsedOpeningTag.data.substr(colonIndex + 1); | ||
tagName !== parsedOpeningTag.data.slice(colonIndex + 1); | ||
} | ||
@@ -175,11 +175,8 @@ } | ||
//save text to parent node | ||
if (currentNode && dataSize !== 0) { | ||
if (currentNode.tagName !== '!xml') { | ||
currentNode.value = concat( | ||
currentNode.value, | ||
options.trimValues | ||
? arrayTrim(xmlData.subarray(dataIndex, dataIndex + dataSize)) | ||
: xmlData.subarray(dataIndex, dataIndex + dataSize), | ||
); | ||
} | ||
if (currentNode && dataSize !== 0 && currentNode.tagName !== '!xml') { | ||
currentNode.append( | ||
options.trimValues | ||
? arrayTrim(xmlData.subarray(dataIndex, dataIndex + dataSize)) | ||
: xmlData.subarray(dataIndex, dataIndex + dataSize), | ||
); | ||
} | ||
@@ -192,9 +189,17 @@ | ||
// <abc def="123"/> | ||
tagAttributes = tagAttributes.substr(0, tagAttributes.length - 1); | ||
tagAttributes = tagAttributes.slice( | ||
0, | ||
Math.max(0, tagAttributes.length - 1), | ||
); | ||
} else { | ||
// <abc/> | ||
tagName = tagName.substr(0, tagName.length - 1); | ||
tagName = tagName.slice(0, Math.max(0, tagName.length - 1)); | ||
} | ||
const childNode = new XMLNode(tagName, currentNode, ''); | ||
const childNode = new XMLNode( | ||
tagName, | ||
currentNode, | ||
new Uint8Array(0), | ||
tagValueProcessor, | ||
); | ||
if (tagAttributes) { | ||
@@ -210,3 +215,8 @@ childNode.attributes = parseAttributesString( | ||
const childNode = new XMLNode(tagName, currentNode); | ||
const childNode = new XMLNode( | ||
tagName, | ||
currentNode, | ||
new Uint8Array(0), | ||
tagValueProcessor, | ||
); | ||
if ( | ||
@@ -213,0 +223,0 @@ options.stopNodes?.length && |
@@ -6,6 +6,5 @@ import { XMLNode } from '../XMLNode'; | ||
import { closingIndexForOpeningTag } from './closingIndexForOpeningTag'; | ||
import { StreamParseOptions } from './defaultOptions'; | ||
import type { RealStreamParseOptions } from './defaultOptions'; | ||
import { findClosingIndex } from './findClosingIndex'; | ||
import { parseAttributesString } from './parseAttributesString'; | ||
import { concat } from './utils/concat'; | ||
import { removeNameSpaceIfNeeded } from './utils/removeNameSpaceIfNeeded'; | ||
@@ -17,4 +16,8 @@ import { decoder } from './utils/utf8Decoder'; | ||
lookupTagName: string, | ||
options: StreamParseOptions, | ||
// tagValueProcessor is not mandatory so I need to change StreamParseOptions to Partial<StreamParseOptions> | ||
// to avoid the error: Property 'tagValueProcessor' is missing in type 'StreamParseOptions' but required in type 'Partial<StreamParseOptions>' | ||
// streamParseOptions: StreamParseOptions, | ||
options: RealStreamParseOptions, | ||
) { | ||
const { tagValueProcessor } = options; | ||
let dataSize = 0; | ||
@@ -81,10 +84,7 @@ let dataIndex = 0; | ||
if (currentNode) { | ||
const value = options.trimValues | ||
? arrayTrim(xmlData.subarray(dataIndex, dataIndex + dataSize)) | ||
: xmlData.subarray(dataIndex, dataIndex + dataSize); | ||
if (currentNode.value === undefined) { | ||
currentNode.value = value; | ||
} else { | ||
currentNode.value = concat(currentNode.value, value); | ||
} | ||
currentNode.append( | ||
options.trimValues | ||
? arrayTrim(xmlData.subarray(dataIndex, dataIndex + dataSize)) | ||
: xmlData.subarray(dataIndex, dataIndex + dataSize), | ||
); | ||
if ( | ||
@@ -98,3 +98,3 @@ options.stopNodes?.length && | ||
} | ||
currentNode.value = xmlData.subarray(currentNode.startIndex + 1, i); | ||
currentNode.bytes = xmlData.subarray(currentNode.startIndex + 1, i); | ||
} | ||
@@ -125,11 +125,8 @@ if (tagName === lookupTagName) { | ||
); | ||
if (currentNode && dataSize !== 0) { | ||
if (currentNode.tagName !== '!xml') { | ||
currentNode.value = concat( | ||
currentNode.value, | ||
options.trimValues | ||
? arrayTrim(xmlData.subarray(dataIndex, dataSize + dataIndex)) | ||
: xmlData.subarray(dataIndex, dataSize + dataIndex), | ||
); | ||
} | ||
if (currentNode && dataSize !== 0 && currentNode.tagName !== '!xml') { | ||
currentNode.append( | ||
options.trimValues | ||
? arrayTrim(xmlData.subarray(dataIndex, dataSize + dataIndex)) | ||
: xmlData.subarray(dataIndex, dataSize + dataIndex), | ||
); | ||
} | ||
@@ -172,3 +169,3 @@ dataSize = 0; | ||
if (currentNode) currentNode.value = concat(currentNode.value, value); | ||
currentNode?.append(value); | ||
} | ||
@@ -182,10 +179,11 @@ | ||
tagExp, | ||
tagValueProcessor, | ||
); | ||
if (currentNode) currentNode.addChild(childNode); | ||
currentNode?.addChild(childNode); | ||
//add rest value to parent node | ||
if (tagExp) { | ||
childNode.value = tagExp; | ||
childNode.bytes = tagExp; | ||
} | ||
} else if (currentNode) { | ||
currentNode.value = concat(currentNode.value, tagExp); | ||
currentNode.append(tagExp); | ||
} | ||
@@ -199,3 +197,3 @@ | ||
const parsedOpeningTag = closingIndexForOpeningTag(xmlData, i + 1); | ||
const tagData = parsedOpeningTag.data.replace(/\r?\n|\t/g, ' '); | ||
const tagData = parsedOpeningTag.data.replaceAll(/\r?\n|\t/g, ' '); | ||
const closeIndex = parsedOpeningTag.index; | ||
@@ -205,13 +203,13 @@ const separatorIndex = tagData.indexOf(' '); | ||
let tagName = | ||
separatorIndex >= 0 | ||
? tagData.substr(0, separatorIndex).replace(/\s+$/, '') | ||
separatorIndex !== -1 | ||
? tagData.slice(0, Math.max(0, separatorIndex)).replace(/\s+$/, '') | ||
: tagData; | ||
let tagAttributes = | ||
separatorIndex >= 0 ? tagData.substr(separatorIndex + 1) : ''; | ||
separatorIndex !== -1 ? tagData.slice(separatorIndex + 1) : ''; | ||
if (options.ignoreNameSpace) { | ||
const colonIndex = tagName.indexOf(':'); | ||
if (colonIndex !== -1) { | ||
tagName = tagName.substr(colonIndex + 1); | ||
tagName = tagName.slice(colonIndex + 1); | ||
shouldBuildAttributesMap = | ||
tagName !== parsedOpeningTag.data.substr(colonIndex + 1); | ||
tagName !== parsedOpeningTag.data.slice(colonIndex + 1); | ||
} | ||
@@ -221,11 +219,8 @@ } | ||
//save text to parent node | ||
if (currentNode && dataSize !== 0) { | ||
if (currentNode.tagName !== '!xml') { | ||
currentNode.value = concat( | ||
currentNode.value, | ||
options.trimValues | ||
? arrayTrim(xmlData.subarray(dataIndex, dataIndex + dataSize)) | ||
: xmlData.subarray(dataIndex, dataIndex + dataSize), | ||
); | ||
} | ||
if (currentNode && dataSize !== 0 && currentNode.tagName !== '!xml') { | ||
currentNode.append( | ||
options.trimValues | ||
? arrayTrim(xmlData.subarray(dataIndex, dataIndex + dataSize)) | ||
: xmlData.subarray(dataIndex, dataIndex + dataSize), | ||
); | ||
} | ||
@@ -239,9 +234,17 @@ | ||
// <abc def="123"/> | ||
tagAttributes = tagAttributes.substr(0, tagAttributes.length - 1); | ||
tagAttributes = tagAttributes.slice( | ||
0, | ||
Math.max(0, tagAttributes.length - 1), | ||
); | ||
} else { | ||
// <abc/> | ||
tagName = tagName.substr(0, tagName.length - 1); | ||
tagName = tagName.slice(0, Math.max(0, tagName.length - 1)); | ||
} | ||
const childNode = new XMLNode(tagName, currentNode, ''); | ||
const childNode = new XMLNode( | ||
tagName, | ||
currentNode, | ||
new Uint8Array(0), | ||
tagValueProcessor, | ||
); | ||
if (tagAttributes) { | ||
@@ -260,3 +263,8 @@ childNode.attributes = parseAttributesString( | ||
if (currentNode || tagName === lookupTagName) { | ||
const childNode = new XMLNode(tagName, currentNode); | ||
const childNode = new XMLNode( | ||
tagName, | ||
currentNode, | ||
new Uint8Array(0), | ||
tagValueProcessor, | ||
); | ||
if ( | ||
@@ -263,0 +271,0 @@ options.stopNodes?.length && |
@@ -1,24 +0,25 @@ | ||
import { XMLNode } from '../XMLNode'; | ||
import { getAllMatches, isEmptyObject } from '../util'; | ||
import { getAllMatches, isEmptySimpleObject } from '../util'; | ||
import { ParseOptions } from './defaultOptions'; | ||
import type { RealParseOptions } from './defaultOptions'; | ||
// eslint-disable-next-line @typescript-eslint/no-var-requires | ||
const { parseString } = require('dynamic-typing'); | ||
const newLocal = '([^\\s=]+)\\s*(=\\s*([\'"])(.*?)\\3)?'; | ||
const newLocal = String.raw`([^\s=]+)\s*(=\s*(['"])(.*?)\3)?`; | ||
const attrsRegx = new RegExp(newLocal, 'g'); | ||
//Attributes are strings so no point in using arrayBuffers here | ||
export function parseAttributesString(string: string, options: ParseOptions) { | ||
if (options.ignoreAttributes) { | ||
export function parseAttributesString( | ||
string: string, | ||
options: RealParseOptions, | ||
) { | ||
const { ignoreAttributes } = options; | ||
if (ignoreAttributes) { | ||
return; | ||
} | ||
string = string.replace(/\r?\n/g, ' '); | ||
string = string.replaceAll(/\r?\n/g, ' '); | ||
const matches = getAllMatches(string, attrsRegx); | ||
const attributes: Record<string, XMLNode | boolean> = {}; | ||
// argument 1 is the key, argument 4 is the value | ||
const attributes: Record<string, string | number | boolean> = {}; | ||
for (const match of matches) { | ||
const attrName = resolveNameSpace(match[1], options); | ||
if (attrName.length) { | ||
const attributeName = resolveNameSpace(match[1], options); | ||
if (attributeName.length > 0) { | ||
if (match[4] !== undefined) { | ||
@@ -29,27 +30,17 @@ if (options.trimValues) { | ||
if (options.attributeValueProcessor) { | ||
match[4] = options.attributeValueProcessor(match[4], attrName); | ||
attributes[attrName] = stringParseValue( | ||
attributes[attributeName] = options.attributeValueProcessor( | ||
match[4], | ||
options.dynamicTypingAttributeValue as boolean, | ||
attributeName, | ||
); | ||
} | ||
} else if (options.allowBooleanAttributes) { | ||
attributes[attrName] = true; | ||
attributes[attributeName] = true; | ||
} | ||
} | ||
} | ||
if (isEmptyObject(attributes)) return; | ||
if (isEmptySimpleObject(attributes)) return; | ||
return attributes; | ||
} | ||
function stringParseValue(value: string, shouldParse: boolean) { | ||
if (shouldParse && typeof value === 'string') { | ||
return parseString(value); | ||
} else { | ||
return value === undefined ? '' : value; | ||
} | ||
} | ||
function resolveNameSpace(tagName: string, options: ParseOptions) { | ||
function resolveNameSpace(tagName: string, options: RealParseOptions) { | ||
if (options.ignoreNameSpace) { | ||
@@ -56,0 +47,0 @@ const tags = tagName.split(':'); |
@@ -1,2 +0,2 @@ | ||
import { ParseOptions } from '../defaultOptions'; | ||
import type { ParseOptions } from '../defaultOptions'; | ||
@@ -12,5 +12,5 @@ export function removeNameSpaceIfNeeded( | ||
if (colonIndex !== -1) { | ||
tagName = tagName.substr(colonIndex + 1); | ||
tagName = tagName.slice(colonIndex + 1); | ||
} | ||
return tagName; | ||
} |
@@ -1,12 +0,15 @@ | ||
import { parseString } from 'dynamic-typing'; | ||
import type { XMLAttributeValue, XMLNode, XMLNodeValue } from './XMLNode'; | ||
import type { RealParseOptions } from './traversable/defaultOptions'; | ||
import { | ||
isTagNameInArrayMode, | ||
merge, | ||
isEmptyObject, | ||
isEmptySimpleObject, | ||
} from './util'; | ||
import { XMLNode, XMLNodeValue } from './XMLNode'; | ||
import { ParseOptions } from './traversable/defaultOptions'; | ||
import { isTagNameInArrayMode, merge, isEmptyObject } from './util'; | ||
/** | ||
* | ||
* @param {*} node | ||
* @param {*} options | ||
* @param {*} parentTagName | ||
* @param node | ||
* @param options | ||
* @param parentTagName | ||
* @returns | ||
@@ -16,36 +19,19 @@ */ | ||
node: XMLNode, | ||
options: ParseOptions, | ||
options: RealParseOptions, | ||
parentTagName?: string, | ||
): XMLNodeValue | Record<string, XMLNodeValue> { | ||
const { | ||
dynamicTypingNodeValue, | ||
tagValueProcessor, | ||
arrayMode, | ||
tagNameProcessor, | ||
attributeNameProcessor, | ||
} = options; | ||
const { arrayMode, tagNameProcessor, attributeNameProcessor, textNodeName } = | ||
options; | ||
const result: Record<string, any> = {}; | ||
if (tagValueProcessor) { | ||
node.value = | ||
node.value && tagValueProcessor(node.value as Uint8Array, node); | ||
} | ||
if (typeof node.value === 'string' && dynamicTypingNodeValue) { | ||
node.value = parseString(node.value); | ||
} | ||
// when no child node or attr is present | ||
if ( | ||
(!node.children || isEmptyObject(node.children)) && | ||
(!node.attributes || isEmptyObject(node.attributes)) | ||
(!node.attributes || isEmptySimpleObject(node.attributes)) | ||
) { | ||
return node.value === undefined ? '' : node.value; | ||
return node.value; | ||
} | ||
// otherwise create a textnode if node has some text | ||
if ( | ||
node.value !== undefined && | ||
(typeof node.value === 'number' || | ||
typeof node.value === 'boolean' || | ||
node.value.length !== 0) | ||
) { | ||
if (node.bytes.length > 0) { | ||
const asArray = isTagNameInArrayMode( | ||
@@ -57,18 +43,13 @@ node.tagName, | ||
result[options.textNodeName as string] = asArray | ||
? [node.value] | ||
: node.value; | ||
result[textNodeName] = asArray ? [node.value] : node.value; | ||
} | ||
if (node.attributes && !isEmptyObject(node.attributes)) { | ||
if (node.attributes && !isEmptySimpleObject(node.attributes)) { | ||
let attributes = options.parseAttributesString ? {} : node.attributes; | ||
if (options.attributeNamePrefix) { | ||
if (attributeNameProcessor) { | ||
// need to rename the attributes | ||
const renamedAttributes: Record<string, boolean | XMLNode> = {}; | ||
const renamedAttributes: Record<string, XMLAttributeValue> = {}; | ||
for (const attributeName in node.attributes) { | ||
const newAttributeName = attributeNameProcessor | ||
? attributeNameProcessor(attributeName) | ||
: attributeName; | ||
renamedAttributes[options.attributeNamePrefix + newAttributeName] = | ||
node.attributes[attributeName]; | ||
const newAttributeName = attributeNameProcessor(attributeName); | ||
renamedAttributes[newAttributeName] = node.attributes[attributeName]; | ||
} | ||
@@ -82,2 +63,3 @@ attributes = renamedAttributes; | ||
} | ||
//@ts-expect-error Should fix this type issue | ||
merge(result, attributes, arrayMode as string); | ||
@@ -84,0 +66,0 @@ } |
@@ -1,5 +0,4 @@ | ||
import { XMLNode } from './XMLNode'; | ||
import type { XMLAttributeValue, XMLNode } from './XMLNode'; | ||
const nameStartChar = | ||
':A-Za-z_\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD'; | ||
const nameStartChar = String.raw`:A-Za-z_\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD`; | ||
const nameChar = `${nameStartChar}\\-.\\d\\u00B7\\u0300-\\u036F\\u203F-\\u2040`; | ||
@@ -18,8 +17,17 @@ const nameRegexp = `[${nameStartChar}][${nameChar}]*`; | ||
export function isEmptySimpleObject(object: Record<string, XMLAttributeValue>) { | ||
// fastest implementation: https://jsbench.me/qfkqv692c8/1 | ||
// eslint-disable-next-line no-unreachable-loop | ||
for (const key in object) { | ||
return false; | ||
} | ||
return true; | ||
} | ||
export function isEmptyObject( | ||
obj: Record<string, boolean | XMLNode | XMLNode[]>, | ||
object: Record<string, boolean | XMLNode | XMLNode[]>, | ||
) { | ||
// fastest implementation: https://jsbench.me/qfkqv692c8/1 | ||
// eslint-disable-next-line no-unreachable-loop | ||
for (const key in obj) { | ||
for (const key in object) { | ||
return false; | ||
@@ -32,4 +40,5 @@ } | ||
* Copy all the properties of a into b. | ||
* @param {object} target | ||
* @param {object} source | ||
* @param target | ||
* @param source | ||
* @param arrayMode | ||
*/ | ||
@@ -57,7 +66,6 @@ export function merge( | ||
* Check if a tag name should be treated as array | ||
* | ||
* @param tagName the node tagName | ||
* @param arrayMode the array mode option | ||
* @param parentTagName the parent tag name | ||
* @returns {boolean} true if node should be parsed as array | ||
* @param tagName - the node tagName | ||
* @param arrayMode - the array mode option | ||
* @param parentTagName - the parent tag name | ||
* @returns true if node should be parsed as array | ||
*/ | ||
@@ -64,0 +72,0 @@ export function isTagNameInArrayMode( |
@@ -0,2 +1,5 @@ | ||
import type { TagValueProcessor } from './traversable/defaultOptions'; | ||
export type XMLNodeValue = string | Uint8Array | number | boolean; | ||
export type XMLAttributeValue = string | number | boolean; | ||
@@ -7,9 +10,12 @@ export class XMLNode { | ||
public children: Record<string, XMLNode[]>; | ||
public attributes?: Record<string, XMLNode | boolean>; | ||
public value?: XMLNodeValue; | ||
public attributes?: Record<string, XMLAttributeValue>; | ||
public bytes: Uint8Array; | ||
public startIndex: number; | ||
private tagValueProcessor: TagValueProcessor; | ||
private cachedValue?: XMLNodeValue; | ||
public constructor( | ||
tagName: string, | ||
parent?: XMLNode, | ||
value?: Uint8Array | string | undefined | number, | ||
parent: XMLNode | undefined, | ||
bytes: Uint8Array, | ||
tagValueProcessor: TagValueProcessor, | ||
) { | ||
@@ -20,5 +26,23 @@ this.tagName = tagName; | ||
this.attributes = Object.create(null); //attributes map | ||
this.value = value; //text only | ||
this.bytes = bytes; //text only | ||
this.tagValueProcessor = tagValueProcessor; | ||
this.startIndex = -1; | ||
} | ||
public append(toAppend: Uint8Array): void { | ||
if (this.bytes.length === 0) { | ||
this.bytes = toAppend; | ||
return; | ||
} | ||
const arrayConcat = new Uint8Array(this.bytes.length + toAppend.length); | ||
arrayConcat.set(this.bytes); | ||
arrayConcat.set(toAppend, this.bytes.length); | ||
this.bytes = arrayConcat; | ||
} | ||
public get value(): any { | ||
if (this.cachedValue === undefined) { | ||
const value = this.tagValueProcessor(this.bytes, this); | ||
this.cachedValue = value; | ||
} | ||
return this.cachedValue; | ||
} | ||
public addChild(child: XMLNode) { | ||
@@ -25,0 +49,0 @@ if (Array.isArray(this.children[child.tagName])) { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
0
205627
156
3064