@igorskyflyer/rawelement
v1.3.0
Published
A utility that lets you manipulate HTML elements, their attributes and innerHTML as strings, on the go and then render the modified HTML. Very useful in SSG projects.
Maintainers
Readme
Table of Contents
- ✨ Features
- 🕵🏼 Usage
- 🤹🏼 API
constructor()- 🗒️ Examples
- ⚙️ Implementation
- 🎯 Motivation
- 📝 Changelog
- 🪪 License
- 💖 Support
- 🧬 Related
- 👨🏻💻 Author
Features
- 🏗 Create and manage raw HTML element wrappers from plain strings
- 🛡 Validates matching start/end tags for consistency
- ✂ Strips wrapper tags while keeping inner content intact
- 🎨 Optional formatting to remove leading indentation
- 🏷 Add, update, check, or remove wrapper element attributes
- 📦 Returns full wrapper or just inner source on demand
- ⚡ Precompiled regex for fast whitespace and tag matching
- 🚫 Throws clear errors on invalid data or attribute usage without a wrapper
- 🧹 Escapes attributes safely
Usage
Install it by executing any of the following, depending on the preferred package manager:
bun add @igorskyflyer/rawelementpnpm add @igorskyflyer/rawelementyarn add @igorskyflyer/rawelementnpm i @igorskyflyer/rawelementAPI
RawElement(options: IRawElementOptions)
Creates a RawElement instance.
If no options are specified or the required data property is not passed, it will throw an error.
IRawElementOptions
Options are an interface IRawElementOptions of the following structure:
interface IRawElementOptions {
data: string
tag?: keyof HTMLElementTagNameMap // = string
format?: boolean
}data: string
Required, the actual HTML element/data to process, as a String.
Data will be normalized, i.e. CRLF (\r\n) replaced with LF (\n).
tag?: string
Optional, a wrapper HTML tag for the element that will contain the text content and optional attributes set by the setAttribute() method.
The provided HTML element MUST have a matching start and an end tag that correspond to the value of the tag property, otherwise an error is thrown.
ℹ️ NOTE
setAttributeonly works when thetagproperty is defined.
format?: boolean
Optional, indicates whether to format the text content inside of the element.
💡 TIP
It is highly recommended to leave the property
formatto its default value oftrueotherwise thesourceproperty of an instance ofRawElementmight contain a lot of leading whitespace.
wrapper
Gets the whole wrapper element made of:
source
Gets the text content of the element.
💡 TIP
The text content can be formatted (the default behavior), set
formatin theoptionsto false to disable formatting.
hasAttribute(name: string): boolean
Checks whether the wrapper element has an attribute.
🛑 CAUTION
If the wrapper element is not set, i.e.
tagis not defined, attributes cannot be used and this method will throw an Error.
Returns a Boolean indicating whether the provided attribute exists.
setAttribute(name: string, value: string | null): boolean
Sets an attribute and its value on the wrapper element.
🛑 CAUTION
If the wrapper element is not set, i.e.
tagis not defined, attributes cannot be used and this method will throw an Error.
Passing the value of null removes the attribute (see removeAttribute) as well.
Returns a Boolean indicating whether the action succeeded.
removeAttribute(name: string): boolean
Removes an attribute and its value from the wrapper element.
🛑 CAUTION
If the wrapper element is not set, i.e.
tagis not defined, attributes cannot be used and this method will throw an Error.
Returns a Boolean with the value of:
true- if the attribute was found and removed,false- if the attribute does not exist or there was an error in removing it.
Examples
EncodedComponent.astro
---
import { Encoder } from '@igorskyflyer/encode-entities'
import { RawElement } from '@igorskyflyer/rawelement'
// here we are using Astro (the SSG)
// but the data can come from any source
const slot: string = await Astro.slots.render('default')
const element: RawElement = new RawElement({
tag: 'div',
data: slot
})
const encoder: Encoder = new Encoder()
const source: string = encoder.encode(element.source) // this component will always output encoded content
---
<Fragment set:html={source} />my-page.astro
import EncodedComponent from './EncodedComponent.astro'
<EncodedComponent>
<div is:raw>
Mitochondria are known as the "powerhouses" of the cell because they generate most of the cell's supply of ATP & CO<sub>2</sub>, which is used as a source of chemical energy.
</div>
</EncodedComponent>
{/*
Will get rendered as:
Mitochondria are known as the "powerhouses" of the cell because they generate most of the cell's supply of ATP & CO<sub>2</sub>, which is used as a source of chemical energy.
*/}Implementation
RawElement isolates the content of a single wrapped tag from a string of markup, then lets you rebuild that wrapper with attributes attached.
- Wrapper validation - a single anchored regex (
^\s*<(\w+)...<\/(\1)>$) matches the opening tag, its content, and its closing tag as one unit, using a backreference to guarantee the start and end tag names actually match. This also correctly handles content that contains further tags of the same name nested inside - the match spans the true outer boundary rather than stopping at the first matching closing tag. - Whitespace normalization - once the wrapper is stripped away, the inner content is dedented based on its first line's leading whitespace and trimmed, so indentation inherited from the surrounding source file doesn't leak into the extracted content.
- Attribute management -
setAttribute,hasAttribute, andremoveAttributeoperate on an internal attribute list, which is serialized back onto the wrapper only when thewrappergetter is read. - Safe serialization - attribute values are HTML-escaped (
&,",<,>) before being written back into the wrapper, so a value can't break out of its"..."boundary and inject markup or additional attributes. - No dependencies, no DOM - the whole class is a single small file built on native string/regex operations; it doesn't parse a full DOM tree, only a single tag-content-tag unit.
Motivation
Extracting the exact content of a wrapped tag sounds trivial until you need it to be correct: nested tags of the same name, inconsistent indentation from the surrounding source, and unescaped attribute values are all easy to get subtly wrong with a quick regex or string split.
RawElement exists to handle that one job properly - validate a wrapper, extract its content without being fooled by same-name nesting, normalize whitespace predictably, and read/write attributes back onto it safely. It's deliberately narrow: not a DOM parser, not a templating engine, just a small, dependable primitive for the "isolate this tag's content" problem that shows up anywhere source needs to be transformed, regenerated, or hashed - for example in astro-saferesource, where deterministic extraction is what makes CSP integrity hashes reliable.
Changelog
Read about the latest changes in the CHANGELOG.
License
Licensed under the MIT license.
Support
Related
@igorskyflyer/astro-post-excerpt
⭐ An Astro component that renders post excerpts for your Astro blog - directly from your Markdown and MDX files. Astro v2+ collections are supported as well! 💎
@igorskyflyer/extendable-string
🦀 ExtendableString allows you to create strings on steroids that have custom transformations applied to them, unlike common, plain strings. 🪀
✒ DúöScríbî allows you to convert letters with diacritics to regular letters. 🤓
@igorskyflyer/magic-queryselector
🪄 A TypeScript-types patch for querySelector/querySelectorAll, make them return types you expect them to! 🔮
⛸ Strips Markdown headings!🏹
Author
Created by Igor Dimitrijević (@igorskyflyer).
