Skip to Content
  • Website
Codsen
  • Home
  • Open Source
  • Articles
  • About

prevOpen Source→string-fix-broken-named-entitiesnext

string-fix-broken-named-entities7.2.0

Finds and fixes common and not so common broken named HTML entities, returns ranges array of fixes

Downloads per monthChangelogMIT Licenselibera manifesto
  • the top
  • Installation
  • Quick Take
  • Examples
  • PURPOSE
  • API — FIXENT()
  • OPTS — DECODE
  • OPTS — CB
  • OPTS — DECODE IN REL…
  • OPTS — ENTITYCATCHE…
  • OPTS — TEXTAMPERSAN…
  • OPTS — PROGRESSFN
  • API — ALLRULES
  • API — VERSION
  • API — TYPES
  • Changelog

Installation

Quick Take

Examples

  • Apply the returned ranges to repair the source string
  • Report healthy named-entity ranges
  • Decode healthy named entities
  • Decode decimal and hexadecimal numeric entities
  • Map findings into custom callback results
  • Repair repeatedly encoded entities
  • Observe scan progress
  • Access the complete public rule-name list
  • Sift raw ampersands in a string from broken character references

Purpose

This program detects and fixes broken named HTML entities (like  ). The algorithm is Levenshtein distance, for smaller entities, it matches by distance 1; for longer entities it allows distance 2.

In practice, this means it can catch errors like: &nbp; (mistyped  ).

This program also works as a healthy entities catcher — broken entities are fed to one callback (opts.cb), healthy entities are fed to another callback (opts.entityCatcherCb).

There is a decoding function; the algorithm is aware of numeric HTML entities as well.

API — fixEnt()

The main function fixEnt() is imported like this:

It’s a function which takes two input arguments:

Input argumentTypeObligatoryDescription
input
Type: String
Obligatory: yes
inputStringyesString, hopefully HTML code
opts
Type: Plain object
Obligatory: no
optsPlain objectnoOptional Options Object.

The Optional Options Object has the following shape:

KeyTypeDefaultDescription
decode
Type: Boolean
Default: false
decodeBooleanfalseFixed values are normally put as HTML-encoded. Set to true to get raw characters instead.
cb
Type: Function or null
Default: see below
cbFunction or nullsee belowCallback function which gives you granular control of the program’s output
entityCatcherCb
Type: Function or null
Default: null
entityCatcherCbFunction or nullnullIf you set a function here, every encountered entity will be passed to it, see a dedicated chapter below
textAmpersandCatcherCb
Type: Function or null
Default: null
textAmpersandCatcherCbFunction or nullnullEach raw text ampersand’s index will be pinged to this function. See more below.
progressFn
Type: Function or null
Default: null
progressFnFunction or nullnullUsed in web worker setups. You pass a function and it gets called once for each natural number 0 to 99, meaning a percentage of the work done so far. See more below.

Function will return ranges (Ranges type below) describing proposed fixes — a null (theoretically possible) or array of one or more range arrays:

For example, four fixed nbsp’s:

[
  [6, 11, " "],
  [11, 18, " "],
  [27, 34, " "],
  [34, 41, " "],
];

The output can be further processed by other range libraries: cropping, sorting, merging can be done on range arrays, instead of mutating the input string each time.

opts.decode

If you set opts.decode and there are healthy encoded entities, those will not be decoded. Only if there are broken entities, those will be set in ranges as decoded values. If you want full decoding, consider filtering the input with a dedicated decoding library right after filtering using this library.

For example, you’d first filter the string using this library, then you’d filter the same input skipping already recorded ranges, using ranges-ent-decode. Then you’d merge the ranges.

For example:

import { fixEnt } from "string-fix-broken-named-entities";
const result = fixEnt("zz nbsp;zz nbsp;", { decode: true });
console.log(JSON.stringify(result, null, 4));
// => [[3, 8, "\xA0"], [11, 16, "\xA0"]]

opts.cb

So, normally, the output of this library is an array of zero or more arrays (each meaning string index ranges), for example:

[
  [1, 2],
  [3, 4]
]

Above means, delete the string from index 1 to 2 and from 3 to 4.

However, for example, in emlint, we need slightly different format, not only ranges but also issue titles:

[
  {
    "name": "tag-generic-error",
    "position": [[1, 2]]
  },
  {
    "name": "tag-generic-error",
    "position": [[3, 4]]
  }
]

Callback function via opts.cb allows you to change the output of this library.

The concept is, you pass a function in options object’s key cb. That function will receive a plain object with all “ingredients” under various keys. Whatever you return, will be pushed into a results array. For each result application is about to push, it will call your function with findings, all neatly put in the plain object.

For example, to solve the example above, you would do:

import { fixEnt } from "string-fix-broken-named-entities";
const res = fixEnt("zzznbsp;zzznbsp;", {
  cb: (oodles) => {
    // "oodles" or whatever you name it, is a plain object.
    // Grab any content from any of its keys, for example:
    // {
    //   ruleName: "missing semicolon on π (don't confuse with ϖ)",
    //   entityName: "pi",
    //   rangeFrom: 3,
    //   rangeTo: 4,
    //   rangeValEncoded: "π",
    //   rangeValDecoded: "\u03C0"
    // }
    return {
      name: oodles.ruleName,
      position: oodles.rangeValEncoded != null ? [oodles.rangeFrom, oodles.rangeTo, oodles.rangeValEncoded] : [oodles.rangeFrom, oodles.rangeTo],
    };
  },
});
console.log(JSON.stringify(res, null, 4));
// => [
//      {
//        name: "malformed  ",
//        position: [3, 8, " "]
//      },
//      {
//        name: "malformed  ",
//        position: [11, 16, " "]
//      }
//    ]

Here’s the detailed description of all the keys, values and their types:

KeyExample valueTypeDescription
ruleName
Type: string
ruleNamemissing semicolon on π (don't confuse with ϖ)stringFull name of the issue, suitable for linters
entityName
Type: string
entityNamepistringJust the name of the entity, without ampersand or semicolon. Case sensitive
rangeFrom
Type: (natural) number (string index)
rangeFrom3(natural) number (string index)Shows from where to delete
rangeTo
Type: (natural) number (string index)
rangeTo8(natural) number (string index)Shows up to where to delete
rangeValEncoded
Type: string or null
rangeValEncodedπstring or nullEncoded entity or null if fix should just delete that index range and there’s nothing to insert
rangeValDecoded
Type: string or null
rangeValDecoded\u03C0string or nullDecoded entity or null if fix should just delete that index range and there’s nothing to insert

opts.decode in relation to opts.cb

Even though it might seem that when a callback is used, opts.decode does not matter (because both encoded and decoded values are served in a callback), but it does matter.

For example, consider this case, where there are non-breaking spaces without semicolons:

&nbsp,&nbsp,&nbsp

Since you get an option to choose between raw and encoded values, result can come in two ways:

When decoded entities are requested, the program replaces ranges [0, 5], [6, 11] and [12, 17]:

// ranges:
[
  [0, 5, "\xA0"],
  [6, 11, "\xA0"],
  [12, 17, "\xA0"],
];

But, when encoded entities are requested, it’s just a matter of sticking in the missing semicolon, at indexes 5, 11 and 17:

// ranges:
[
  [5, 5, ";"],
  [11, 11, ";"],
  [17, 17, ";"],
];

opts.entityCatcherCb

If broken entities are pinged to opts.cb() callback, all healthy entities are pinged to opts.entityCatcherCb. It’s either one or another:

const inp1 = "y   z &nsp;";
const gatheredEntityRanges = [];
fix(inp1, {
  entityCatcherCb: (from, to) => gatheredEntityRanges.push([from, to]),
});
console.log(`${`\u001b[${33}m${`gatheredEntityRanges`}\u001b[${39}m`} = ${JSON.stringify(gatheredEntityRanges, null, 4)}`);
// => [[2, 8]]

opts.textAmpersandCatcherCb

Sometimes input string can contain ampersands-as-text and ampersands-as-part-of-entities.

For example, consider a string abc& &xyz. What do you see here? There’s one named HTML entity,  , surrounded by two raw text ampersands. This callback, opts.textAmpersandCatcherCb can be used to catch raw ampersands, probably with aim to HTML-encode them. Specifically, this option would call your function twice, with numbers 3 and 10, positions of raw ampersands.

See the supplied example where broken entity is fixed and raw text ampersands are encoded, all within the same string, all done using this program.

opts.progressFn

In web workeropens in a new tab setups, a worker can return “in progress” values. When you put this package into a web worker, this callback function under opts.progress will be called with a string containing a natural number, showing the percentage of the work done so far.

It’s hard to show a minimal worker application here but at least here’s how the pinging progress works from the side of this npm package:

// let's define a variable on a higher scope:
let count = 0;

// call application as normal, pass opts.progressFn:
const result = fixEnt("text &ang text&ang text text &ang text&ang text text &ang text&ang text", {
  progressFn: (percentageDone) => {
    // console.log(`percentageDone = ${percentageDone}`);
    count++;
  },
});
// each time percentage is reported, "count" is incremented

// now imagine if instead of incrementing the count, you pinged the
// value out of the worker

API — allRules

Every rule name this program can report, as an array of strings:

import { allRules } from "string-fix-broken-named-entities";

console.log(allRules.length);
// => 4255

console.log(allRules.slice(0, 3));
// => [
//      "bad-html-entity-malformed-Aacute",
//      "bad-html-entity-malformed-aacute",
//      "bad-html-entity-malformed-Abreve",
//    ]

The list is assembled from all named HTML entities, twice over, plus five catch-all names:

Name patternRaised when
bad-html-entity-malformed-*
bad-html-entity-malformed-*A named entity is mistyped, for example &nbp; for  .
bad-html-entity-encoded-*
bad-html-entity-encoded-*A named entity is present but encoded, for example  .
bad-html-entity-unrecognised
bad-html-entity-unrecognisedAn entity-looking chunk matches no known entity.
bad-html-entity-multiple-encoding
bad-html-entity-multiple-encodingAn entity was encoded more than once.
bad-html-entity-encoded-numeric
bad-html-entity-encoded-numericA numeric entity is present but encoded.
bad-html-entity-malformed-numeric
bad-html-entity-malformed-numericA numeric entity is mistyped.
bad-html-entity-other
bad-html-entity-otherAnything else the algorithm flags.

The ruleName key of the opts.cb callback object is always one of these. emlint consumes allRules to register a rule per name, which is why the list is exported rather than kept private.

API — version

You can import version:

API — types

This package is written in TypeScript and exports the following types:

TypeDescription
Opts
Type: Opts
OptsThe Optional Options Object of fixEnt(), documented above.
cbObj
Type: cbObj
cbObjThe plain object opts.cb is called with.
Obj
Type: Obj
ObjA plain object with string keys.
Ranges
Type: Ranges
RangesZero or more ranges, or null — what fixEnt() returns by default.
import type { Obj, Opts, Ranges, cbObj } from "string-fix-broken-named-entities";

Changelog

Open Changelog
↑ back to top
prev next

Copyright

All rights reserved © Roy Revelt 2026
All our open source packages are under MIT licenceopens in a new tab

Activities

🐛 See a bug? Raise an issueopens in a new tab
💘 Check out the Indiewebopens in a new tab and Libera manifestoopens in a new tab