Commit 0e509a3d authored by David Sveningsson's avatar David Sveningsson
Browse files

feat: support specifying a custom loader when using library

parent ae40706f
......@@ -15,13 +15,34 @@ While primarly developed as a NodeJS CLI/backend tool it is possible to run `htm
Improvements are welcome!
## Example
## Base implementation
There is an example project [try-online-repo] running at [try-online-url] showing that it can be done and the workarounds required.
This article assume you are trying to get something similar to this code to run in the browser.
```ts
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][try-online-repo] running at [online.html-validate.org][try-online-url] showing that it can be done and the workarounds required.
[try-online-repo]: https://gitlab.com/html-validate/try-online
[try-online-url]: https://online.html-validate.org/
## 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:
```diff
-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`).
......@@ -33,38 +54,44 @@ This will manifest itself with errors such as:
- `Cannot read property 'existsSync' of undefined`
- `fs_1.default.existsSync is not a function`
### Workaround 1: prevent loader from trying to access filesystem
By far the easiest method is to pass a config to the [[HtmlValidate]] constructor with the `root` property to `true`:
To get around this the [[StaticConfigLoader]] (or a custom loader) can be used:
```ts
import { HtmlValidate } from "html-validate";
```diff
-import { HtmlValidate } from "html-validate"
+import { StaticConfigLoader, HtmlValidate } from "html-validate/es/browser";
const htmlvalidate = new HtmlValidate({
root: true,
extends: ["html-validate:recommended"],
});
-const htmlvalidate = new HtmlValidate();
+const loader = new StaticConfigLoader();
+const htmlvalidate = new HtmlValidate(loader);
const report = htmlvalidate.validateString(markup, "my-file.html");
```
Do note that no default configuration will be loaded either so you must explicitly enable rules or extend a preset.
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:
### Workaround 2:
```diff
-const loader = new StaticConfigLoader();
+const loader = new StaticConfigLoader({
+ extends: ["html-validate:standard"],
+ elements: ["html5"],
+});
const htmlvalidate = new HtmlValidate(loader);
```
If you are emulating or providing virtual access to a filesystem you can ensure the `fs` module is implemented.
There is no exhaustive list of functions which must be added.
### Previous workaround
If you are using webpack you can use `resolve.alias` to implement this:
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:
```js
module.exports = {
resolve: {
alias: {
fs$: path.resolve(__dirname, "src/my-fs.js"),
},
},
};
```diff
-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`.
......@@ -80,17 +107,17 @@ This will manifest itself with errors such as:
This is typically archived by passing an object instead of a string when configuring `html-validate`:
```diff
import { HtmlValidate } from "html-validate";
import { StaticConfigLoader, HtmlValidate } from "html-validate/es/browser";
+// check your loader! it must return a plain object (not `default: { ... }`, a path/url, etc)
+// 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 htmlvalidate = new HtmlValidate({
root: true,
const loader = new StaticConfigLoader({
extends: ["html-validate:recommended"],
- elements: ["html5"],
+ elements: [html5]
});
+ elements: [html5],
});
const htmlvalidate = new HtmlValidate(loader);
```
## Webpack
......
......@@ -45,6 +45,38 @@ beforeEach(() => {
});
describe("HtmlValidate", () => {
it("should support using a custom config loader", () => {
expect.assertions(2);
const loader = new (class extends ConfigLoader {
public getConfigFor(): Config {
return Config.fromObject({
rules: {
foobar: "error",
},
});
}
public flushCache(): void {
/* do nothing */
}
protected defaultConfig(): Config {
return Config.defaultConfig();
}
})();
const getConfigFor = jest.spyOn(loader, "getConfigFor");
const htmlvalidate = new HtmlValidate(loader);
const filename = "/path/to/my-file.html";
const config = htmlvalidate.getConfigFor(filename);
expect(getConfigFor).toHaveBeenCalledWith(filename, undefined);
expect(config.get()).toEqual({
extends: [],
plugins: [],
rules: {
foobar: "error",
},
transform: {},
});
});
describe("validateString()", () => {
it("should validate given string", () => {
expect.assertions(2);
......
......@@ -35,11 +35,15 @@ class HtmlValidate {
/**
* Create a new validator.
*
* @param configLoader - Use a custom configuration loader.
* @param config - If set it provides the global default configuration. By
* default `Config.defaultConfig()` is used.
*/
public constructor(config?: ConfigData) {
this.configLoader = new FileSystemConfigLoader(config);
public constructor(config?: ConfigData);
public constructor(configLoader: ConfigLoader);
public constructor(arg?: ConfigLoader | ConfigData) {
const [loader, config] = arg instanceof ConfigLoader ? [arg, undefined] : [undefined, arg];
this.configLoader = loader ?? new FileSystemConfigLoader(config);
}
/**
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment