Skip to main content

AST Changes

This page covers breaking changes to the internal AST layer. It affects parser plugin developers and custom rule authors who access AST-level token properties directly.

Custom rule authors

If your rules only use the DOM layer (MLElement, MLToken, etc.), no changes are needed. The DOM layer public API is unchanged. See DOM Layer Impact below.

Summary

ChangeWho is affected
Token position properties renamedParser plugin developers
End position properties removedParser plugin developers
selfClosingSolidus removedParser plugin developers
conditionalType replaced by blockBehaviorParser plugin developers
MLMarkupLanguageParser / Parse types removedParser plugin developers
getNamespace moved to parser-utilsParser plugin developers

Token position properties renamed

MLASTToken position properties have been simplified. The start prefix is dropped.

v4v5
startOffsetoffset
startLineline
startColcol

End position properties removed

Breaking Change

endOffset, endLine, and endCol have been removed from MLASTToken.

Calculate them from the start position and raw string instead.

End offset:

// v4
const end = token.endOffset;

// v5
const end = token.offset + token.raw.length;

End line/column -- use helpers from @markuplint/parser-utils:

import { getEndLine, getEndCol, getEndPosition } from '@markuplint/parser-utils/location';

const endLine = getEndLine(token.raw, token.line);
const endCol = getEndCol(token.raw, token.col);

// Or get all end positions at once:
const { endOffset, endLine, endCol } = getEndPosition(token.raw, token.offset, token.line, token.col);

selfClosingSolidus removed

Use tagCloseChar instead of selfClosingSolidus.

// v4
if (element.selfClosingSolidus) {
// self-closing element
}

// v5
if (element.tagCloseChar.startsWith('/')) {
// self-closing element (tagCloseChar is "/>")
}

conditionalType replaced by blockBehavior

conditionalType has been replaced by blockBehavior. The new property is an object with type and expression fields. It is also available on MLASTElement.

// v4
if (block.conditionalType === 'if:else') {
// ...
}

// v5
if (block.blockBehavior?.type === 'if:else') {
// ...
}

The blockBehavior interface:

interface MLASTBlockBehavior {
readonly type: MLASTBlockBehaviorType;
readonly expression: string;
}

MLMarkupLanguageParser type removed

The legacy MLMarkupLanguageParser and Parse types have been removed. Use MLParser instead.

// v4
import type { MLMarkupLanguageParser } from '@markuplint/ml-ast';
const parser: MLMarkupLanguageParser = { ... };

// v5
import type { MLParser } from '@markuplint/ml-ast';
const parser: MLParser = { ... };

getNamespace moved to parser-utils

getNamespace() has been removed from @markuplint/html-parser. It now lives in @markuplint/parser-utils with a new signature.

// v4
import { getNamespace } from '@markuplint/html-parser';
const ns = getNamespace(tagName, parentNamespace);

// v5
import { getNamespace } from '@markuplint/parser-utils';
const ns = getNamespace(currentNodeName, parentNode);
v4v5
Package@markuplint/html-parser@markuplint/parser-utils
First parametertagName: stringcurrentNodeName: string | null
Second parameterparentNamespace?: stringparentNode: MLASTParentNode | null
note

The base Parser class in @markuplint/parser-utils now handles namespace detection automatically. Most parsers no longer need to call getNamespace directly.

DOM Layer Impact

The DOM layer public API (MLToken, MLElement, etc.) is unchanged. These getters still work:

  • startLine, startCol, startOffset
  • endLine, endCol, endOffset
  • raw, fixed

If your custom rules only use the DOM layer, no migration is needed.