Running in a browser

While primarly developed as a NodeJS CLI/backend tool it is possible to run html-validate in a browser as well.

Note

While it is possible to get html-validate running in a browser it is currently not supported and requires a few workarounds.

Improvements are welcome!

Base implementation

This article assume you are trying to get something similar to this code to run in the browser.

import { HtmlValidate } from "html-validate";

const htmlvalidate = new HtmlValidate();
const report = htmlvalidate.validateString(markup, "my-file.html");

Example

There is an example project try-online running at online.html-validate.org showing that it can be done and the workarounds required.

Browser bundle

The first step is to make sure the correct bundle is used. The library contains both a full build and a browser build, if your bundler fails to pick up the right one you need to be explicit:

-import { HtmlValidate } from "html-validate";
+import { HtmlValidate } from "html-validate/es/browser"; // replace es with cjs for commonjs

Configuration loading

By default html-validate will traverse the filesystem looking for configuration files (e.g. .htmlvalidate.json). This is true even when using validateString(..).

This will manifest itself with errors such as:

To get around this the StaticConfigLoader (or a custom loader) can be used:

-import { HtmlValidate } from "html-validate"
+import { StaticConfigLoader, HtmlValidate } from "html-validate/es/browser";

-const htmlvalidate = new HtmlValidate();
+const loader = new StaticConfigLoader();
+const htmlvalidate = new HtmlValidate(loader);
 const report = htmlvalidate.validateString(markup, "my-file.html");

The StaticConfigLoader will only load the configuration passed to the constructor or to validateString(..). By default it uses the html-validate:recommended preset but can be overridden by passing a different to the constructor:

-const loader = new StaticConfigLoader();
+const loader = new StaticConfigLoader({
+  extends: ["html-validate:standard"],
+  elements: ["html5"],
+});
 const htmlvalidate = new HtmlValidate(loader);

Previous workaround

The previous workaround was to pass a configuration to the HtmlValidate constructor with the root property set to true but this is no longer recommended for this purpose:

-const htmlvalidate = new HtmlValidate();
+const htmlvalidate = new HtmlValidate({
+  root: true,
+  extends: ["html-validate:recommended"],
+});

Note that no default configuration will be loaded either so you must explicitly enable rules or extend a preset.

Bundled files

The html-validate NPM package contains a few data files such as elements/html.json. These files are dynamically imported and will most likely not be picked up by your bundler. Either you need to ensure the bundler picks up the files or the configuration loader does not need to import thme.

This will manifest itself with errors such as:

This is typically archived by passing an object instead of a string when configuring html-validate:

 import { StaticConfigLoader, HtmlValidate } from "html-validate/es/browser";

+// check your webpack loader! it must return a plain object (not `default: { ... }`, a path/url, etc)
+import html5 from "html-validate/elements/html5.json";

 const loader = new StaticConfigLoader({
   extends: ["html-validate:recommended"],
-  elements: ["html5"],
+  elements: [html5],
 });
 const htmlvalidate = new HtmlValidate(loader);

Webpack

Internally there are many dynamic imports and fs access.

You will see warnings such as:

WARNING in ./node_modules/html-validate/dist/config/config.js 81:25-50
Critical dependency: the request of a dependency is an expression

In many cases there is no way to avoid the warning per se but the workaround above are implemented the code paths triggering these issues are not hit.