04

May 12, 2026 · View on GitHub


Stop Nodes

A stop node is a tag whose inner content is captured as a raw string without any further XML parsing. Useful for <script>, <style>, embedded HTML, or any tag whose content isn't valid XML.

const parser = new XMLParser({
  tags: {
    stopNodes: [
      '..script',   // any <script> tag anywhere in the tree
      'root.raw',   // only <raw> directly inside <root>
    ],
  },
  onStopNode(tagDetail, rawContent, matcher) {
    console.log(tagDetail.name, rawContent);
  },
});

The onStopNode callback receives the tag details, the captured raw string, and a ReadOnlyMatcher for path inspection. If you don't provide a callback the raw content is still available in the output through the output builder.


Default Enclosure Behaviour

By default, stop-node collection ends at the first matching close tag, regardless of context. So <!-- </script> --> inside a stop node will end the <script> collection unless you tell the parser to skip XML comments.

To control this, use the object form with skipEnclosures:

import { xmlEnclosures, quoteEnclosures } from '@nodable/flexible-xml-parser';

const parser = new XMLParser({
  tags: {
    stopNodes: [
      // plain string — ends at first </script>
      '..script',

      // skip XML comments and CDATA when looking for the close tag
      { expression: 'body..pre',   skipEnclosures: [...xmlEnclosures] },

      // skip XML + quote enclosures (good for <style> with string literals)
      { expression: 'head..style', skipEnclosures: [...xmlEnclosures, ...quoteEnclosures] },

      // explicitly no skipping
      { expression: 'root.raw',    skipEnclosures: [] },
    ],
  },
});

xmlEnclosures covers XML comments (<!-- -->) and CDATA (<![CDATA[...]]>).
quoteEnclosures covers single quotes, double quotes, and template literals.


Nested

By default, stop-node collection ends at the first matching close tag. But you can set nested flag to true to automatically skip the nested stop node.

{ expression: 'root.raw', nested: true,   skipEnclosures: [] },

Eg

<root>
  <raw>stop node</raw>
  <raw>stop node <raw>nested stop node</raw></raw>
</root>

If nested:false then above XML will error until auto close is enabled. Because 2nd stopnode will be set to stop node <raw>nested stop node

If nested:true then above XML will be parsed as

{
  root: {
    raw: [
      "stop node",
      "stop node <raw>nested stop node</raw>"
    ]
  }
}

Skip Tags

Skip tags drop a tag and its entire subtree from the output silently. Content is consumed but never forwarded to the output builder.

const parser = new XMLParser({
  skip: {
    tags: [
      '..script',   // drop all <script> tags anywhere
      'root.debug', // drop <debug> only inside <root>
    ],
  },
});

Like stop nodes, entries can be plain strings or objects with skipEnclosures:

import { xmlEnclosures } from '@nodable/flexible-xml-parser';

skip: {
  tags: [
    '..script',
    { expression: 'body..pre', skipEnclosures: [...xmlEnclosures] },
  ],
}

Stop Node vs Skip Tag

Stop nodeSkip tag
Content captured?Yes — as raw stringNo — silently discarded
Callback available?Yes — onStopNodeNo
Use whenYou need the raw inner textYou want to ignore a subtree entirely

Path Expression Syntax

Both stopNodes and skip.tags use path expression strings. Key patterns:

PatternMatches
'..tag'Any <tag> at any depth
'root.tag'<tag> directly inside <root>
'*.tag'<tag> as a direct child of any parent
'root..tag'<tag> anywhere inside <root>
'tag[attr=val]'<tag> with a specific attribute value

See 09-path-expressions.md for the full syntax reference.


➡ Next: 05 — Output Builders