Skip to content

feat!: Align namespace-switching logic to the HTML living standard tree construction rules#18

Open
FlippingBinary wants to merge 11 commits into
syntax-tree:mainfrom
FlippingBinary:feat/living-standard-namespaces
Open

feat!: Align namespace-switching logic to the HTML living standard tree construction rules#18
FlippingBinary wants to merge 11 commits into
syntax-tree:mainfrom
FlippingBinary:feat/living-standard-namespaces

Conversation

@FlippingBinary

Copy link
Copy Markdown

Initial checklist

  • I read the support docs
  • I read the contributing guide
  • I agree to follow the code of conduct
  • I searched issues and discussions and couldn’t find anything or linked relevant results below
  • I made sure the docs are up to date
  • I included tests (or that’s not needed)

Description of changes

This pull request is intended to align the namespace-switching logic to the HTML Living Standard § 13.2.6. I needed this done because I was running into a problem when child elements of foreignObject were being created in the SVG namespace instead of the HTML namespace. Since I needed this fixed for my own project whether it gets accepted to the official release or not, I wrote the code without opening an issue. Now that I've got it working, I'm sharing what I've got so far. I hope that's okay.

In particular, this changes namespace selection in the following cases:

  • SVG: In the SVG namespace, the HTML namespace is selected for child elements of <foreignObject>, <desc>, and <title>.

  • MathML: In the HTML namespace, the MathML namespace is selected for <math> elements. In the MathML namespace, the HTML namespace is selected for <annotation-xml> elements that have an encoding attribute that matches either text/html or application/xhtml+xml. The standard calls for a text integration point on <mi>, <mo>, <mn>, <ms>, and <mtext>, but I couldn't confidently say if that means the namespace should switch to HTML, so I didn't add special handling for those.

  • Explicit Overrides: The xmlns property values on individual elements override the implicitly inherited namespace, not just the root element.

Two existing tests would fail because of this work, but I tried to follow the convention that was established in the serializeNodeToHtmlString() helper function by adding the MathML namespace to the string replacement chain. Downstream code that is closely coupled to the previous namespace selection behavior could break, so I would consider this to be a breaking change. I also added a few new tests to assert the intended namespaces rather than modifying existing tests to do the same. Maybe the tests could be consolidated, but I wanted to leave the existing tests as-is to improve confidence in the impact of this pull request.

@github-actions github-actions Bot added 👋 phase/new Post is being triaged automatically 🤞 phase/open Post is being triaged manually and removed 👋 phase/new Post is being triaged automatically labels May 24, 2026

@wooorm wooorm left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your patience!

Looks like a good addition. Some code nits. I also fixed CI, so you can rebase, then it should work!

Comment thread lib/index.js Outdated
Comment thread lib/index.js Outdated
Comment thread lib/index.js
tagName,
currentNamespace,
properties
)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there is such tight coupling, between the call site and the utility function, 3 parameters, 2 return fields, plus only called once, does it really make sense to split them?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC I split this to reduce the function's complexity. Merged together, the function becomes quite large. That's really the only reason to keep it split, but I can merge it in if you prefer that.

Comment thread test/index.js
@FlippingBinary FlippingBinary marked this pull request as draft June 9, 2026 16:31

@FlippingBinary FlippingBinary left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I've made changes based on your feedback. The instrumented document is gone, I refactored the trees in just my tests to use the hast convenience functions when able, and created a helper function to serialize the tree into pairs of namespace and localname strings. That all made the tests so much cleaner.

Also, I had neglected to explicitly test whether html elements are created with createElementNS() when the namespace is set and createElement() when it isn't, so I added tests for that. That will protect against accidentally using createElementNS() for every element, which might cause breaking behavior downstream. Those tests avoid instrumenting document by inferring the creation method from whether the tag's local name became lowercased. That matches the spec, but it's not necessarily intuitive so I mention it in a comment.

I think it's ready for your review again whenever you're available!

Comment thread lib/index.js Outdated
Comment thread lib/index.js Outdated
Comment thread lib/index.js
tagName,
currentNamespace,
properties
)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC I split this to reduce the function's complexity. Merged together, the function becomes quite large. That's really the only reason to keep it split, but I can merge it in if you prefer that.

Comment thread test/index.js
@FlippingBinary FlippingBinary marked this pull request as ready for review June 9, 2026 19:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🤞 phase/open Post is being triaged manually

Development

Successfully merging this pull request may close these issues.

2 participants