Plainforge is a local-first fixture workbench for HTML-to-plain-text extraction. It helps developers and agents compare conversion behavior against small, reviewable fixtures before trusting extracted text in docs, crawlers, or automation.
HTML-to-text conversion often looks simple until links, tables, hidden script blocks, entities, and spacing collide. Plainforge gives those edge cases a tiny home:
- keep representative HTML snippets in
fixtures/ - record expected plain text beside each snippet
- run deterministic conversions locally
- export JSON and Markdown reports for review
Plainforge was inspired by the existence of adjacent html2text projects, including vincentkoc/html2text, but it is a fresh JavaScript implementation focused on fixture-driven testing rather than copying another project's implementation.
Plainforge 0.1.0 is not currently available from the npm registry. Install the published GitHub release tarball instead:
npm install https://github.com/rogerchappel/plainforge/releases/download/v0.1.0/plainforge-0.1.0.tgzFor local development you can run the CLI directly:
node bin/plainforge.js --helpThe installed package exposes a plainforge binary and the public API shown
below. For repository development, use npm ci instead of the release-tarball
install command and run the CLI directly with node bin/plainforge.js --help.
Inspect the bundled sample fixtures and write reports:
node bin/plainforge.js inspect fixtures/sample --output out/plainforgeConvert one local HTML file:
node bin/plainforge.js convert fixtures/sample/basic-link/input.htmlUse JSON output in automation:
node bin/plainforge.js inspect fixtures/sample --jsonThe CLI accepts these command forms:
plainforge convert <html-file> [--strategy readable|compact] [--json]
plainforge inspect <fixture-dir> [--output <dir>] [--strategy readable|compact] [--json]
--output (or -o) is available only for inspect. Options that require a
value, unknown options, and extra file or directory arguments are rejected with
a nonzero exit status and an actionable error message.
Each fixture is a directory with:
my-case/
input.html # source HTML
expected.txt # expected plain-text output
meta.json # optional id/title/tags/notes
See fixtures/sample for working examples.
Conversion decodes semicolon-terminated named references used by the bundled
fixtures, including common currency (£, ¢, ¥) and mathematical
(∉, ≤, ≥) references. It also supports the HTML 4 Greek-letter
set, common mathematical operators and relations, and directional arrows (for
example, α, ∑, ≠, and →). The complete HTML Latin-1
set is supported, so ½, ×, and ÷ become ½, ×, and ÷.
Named references are case-sensitive: defined aliases such as Á decode,
while undefined spellings such as &AACUTE; remain unchanged. Historical text
references amp, lt, gt, quot, nbsp, copy, and reg may omit the
semicolon when the following character is not alphanumeric. Unknown or
ambiguous references remain unchanged so fixture comparisons do not silently
discard source text.
When present, meta.json must contain a JSON object. id, title, and
notes must be strings, while tags must be an array containing only strings.
All fields may be omitted: the fixture directory name supplies id and
title, with [] and an empty string used for tags and notes. Invalid
metadata stops inspection with an error that names the fixture's meta.json
file and the field that needs correction. Malformed JSON likewise reports the
exact meta.json path together with the parser's syntax context, so a broken
fixture can be identified when inspecting a directory of fixtures. Pipes and
line breaks in these fields are escaped or normalized when Markdown reports
are rendered.
import { convertHtmlToText, inspectFixtures } from 'plainforge';
const conversion = convertHtmlToText('<h1>Hello</h1><p>World</p>');
console.log(conversion.text);
const report = await inspectFixtures('fixtures/sample');
console.log(report.summary);HTML numeric character references follow browser-style recovery: decimal and
hexadecimal forms may omit the trailing semicolon, null, surrogate, and
out-of-range values become �, and the HTML C1 replacement table is applied.
Common named references, including HTML Latin-1 names such as ç and
é, ½, ×, and ÷ and symbols such as ™,
€, and “, are supported alongside Greek letters, mathematical
operators and relations, and arrows. Named references normally require a
trailing semicolon and an exact case match; the legacy text references listed
above may omit their semicolon at an unambiguous boundary. Unknown, ambiguous,
or differently cased named references are preserved.
Plainforge is intentionally boring and local:
- reads local files you point it at
- writes reports only when
--outputis provided - makes no network calls
- sends no telemetry
- does not read credentials
- does not publish packages, releases, or reports
Treat fixture content as untrusted text. Plainforge does not execute HTML, JavaScript, CSS, or remote resources.
Hidden script, style, noscript, svg, template, and head blocks are omitted; if one is truncated without a closing tag, its content is omitted through the end of the input.
Tag and anchor boundaries respect single- and double-quoted attribute values, including > characters inside those values.
HTML comments are omitted through their closing marker or the end of truncated
input. Ordinary text comparisons containing < and > are retained rather
than treated as tags.
npm test
npm run check
npm run build
npm run smoke
npm run package:smoke
bash scripts/validate.shA real CLI smoke is also available:
node bin/plainforge.js inspect fixtures/sample --output out/smoke --jsonThis is a usable MVP: fixture discovery, deterministic conversion, comparison diffs, JSON/Markdown reports, tests, and CLI smokes are implemented. The roadmap is intentionally conservative until real users identify the next painful extraction cases.
MIT
Use the checked-in scripts before opening or publishing a release:
npm run check
npm test
npm run build
npm run smoke
npm run package:smoke
npm run release:checkThe package smoke uses npm pack --dry-run so the published file list can be reviewed without publishing.
It also installs the generated tarball in a temporary project and checks the public import, CLI help, conversion, and fixture inspection paths.
Tagged releases require the tag to exactly match the package version (for
example, v0.2.0 for plainforge@0.2.0). The release workflow is intended to
run the full gate, pack and validate one tarball, publish that artifact to npm
with trusted publishing, and then attach it to the GitHub release. Verify npm
registry availability independently: an existing GitHub release and attached
tarball do not prove that npm publication succeeded or remains available.
For a failed or partial release, inspect the workflow run and both distribution targets before retrying. Correct validation, trusted-publisher, or registry configuration first. Never republish a version that already exists on npm; recover only the missing distribution step with the already validated artifact, or issue a new version when the original npm publication did not complete.