Migration guide
Upgrading to v8
Dependency changes
- Minimum required NodeJS version is v16.
- Minimum required Jest version is v27.
All users
- The
void
rule has been removed after being deprecated a long time, it is replaced with the separatevoid-content
,void-style
andno-self-closing
rules. - Deprecated severity alias
disabled
removed. If you use this in your configuration you need to update it tooff
.
{
"rules": {
- "my-awesome-rule": "disabled"
+ "my-awesome-rule": "off"
}
}
API changes
Promise-based API
The HtmlValidate
class now has a Promise
based API where most methods return a Promise
.
The old synchronous methods has been renamed to *Sync(..)
, .e.g validateString(..)
is now validateStringSync(..)
.
To migrate either use the new asynchronous API with await
:
-const result = htmlvalidate.validateFile("my-awesome-file.html");
+const result = await htmlvalidate.validateFile("my-awesome-file.html");
or use the synchronous API:
-const result = htmlvalidate.validateFile("my-awesome-file.html");
+const result = htmlvalidate.validateFileSync("my-awesome-file.html");
For unittesting with Jest it is recommended to make the entire test-case async (but the matchers will handle passing in a Promise
as well):
-it("my awesome test", () => {
+it("my awesome test", async () => {
const htmlvalidate = new HtmlValidate();
- const report = htmlvalidate.validateString("...");
+ const report = await htmlvalidate.validateString("...");
expect(report).toMatchCodeFrame();
});
@html-validate/plugin-utils
The TemplateExtractor
class has been moved to the @html-validate/plugin-utils
package.
This change only affects API users who use the TemplateExtractor
class, typically this is only used when writing plugins.
The rationale for this is to cut down on the API surface and the number of required dependencies.
getContextualDocumentation
replaces getRuleDocumentation
A new getContextualDocumentation
replaces the now deprecated getRuleDocumentation
method.
The context parameter to getRuleDocumentation
is now required and must not be omitted.
For rule authors this means you can now rely on the context
parameter being set in the documentation
callback.
For IDE integration and toolchain authors this means you should migrate to use getContextualDocumentation
as soon as possible or if you are continuing to use getRuleDocumentation
you are now required to pass the config
and context
field from the reported message.
Configuration API changes
Many breaking changes has been introduced to the configuration API.
ConfigLoader
must returnResolvedConfig
In the simplest case this only requires to call Config.resolve()
:
-return config;
+return config.resolve();
A resolved configuration cannot further reference any new files to extend, plugins to load, etc.
- Add
Resolver
classes as a mean to separatefs
from browser build
This change affect API users only, specifically API users directly using the Config
class.
Additionally when using the StaticConfigLoader
no modules will be resolved using require(..)
by default any longer.
If you want to resolve modules using require
you must use the NodeJSResolver
.
Instructions for running in a browser is also updated.
To create a Config
instance you must now pass in a Resolver
(single or array):
+const resolvers = [ /* ... */ ];
-const config = new Config( /* ... */ );
+const config = new Config(resolvers, /* ... */ );
This applies to calls to Config.fromObject(..)
as well.
The default resolvers for StaticConfigLoader
is StaticResolver
and for FileSystemConfigLoader
is NodeJSResolver
.
Both can optionally take a new set of resolvers (including custom ones).
Each resolver will, in order, try to load things by name.
For instance, when using the NodeJSResolver
it uses require(..)
to load new items.
StaticResolver
- uses a predefined set of items.NodeJSResolver
- usesrequire(..)
ConfigFactory
removed
The ConfigFactory
parameter to ConfigLoader
(and its child classes StaticConfigLoader
and FileSystemConfigLoader
) has been removed.
No replacement.
If you are using this you are probably better off implementing a fully custom loader later returning a ResolvedConfig
.
Upgrading to v7
Dependency changes
- Minimum required NodeJS version is v14.
Upgrading to v6
- CLI users: No required changes but if custom element metadata is present it can benefit from upgrading format.
- API users: Breaking changes!
Configuration changes
The format for specifying attribute metadata has changed.
This will probably not affect most users but if you have custom element metadata (e.g. elements.json
) and specify attribute restrictions you should migrate to the new format.
If you do not use custom element metadata you can safely upgrade to this version without any changes.
If you need to maintain backwards compatibility with older versions of html-validate
you can safely hold of the migration (e.g. you publish a component framework with bundled element metadata and don't want to force an update for end users).
The old format is deprecated but will continue to be supported for now.
Previously the attributes was specified as an array of possible values (strings or regular expressions).
Boolean attributes was specified as []
and when value could be omitted it used the magic value [""]
.
{
"my-custom-input": {
"attributes": {
/* enumeration: must have one of the specified values */
"type": ["text", "email", "tel"],
/* boolean: should not have a value */
"disabled": [],
/* allow omitting value, e.g. it can be set as a boolean or it should be "true" or "false" */
"multiline": ["", "true", "false"],
},
},
}
To migrate the array is changed to an object with the properties enum
, boolean
and omit
:
{
"my-custom-input": {
"attributes": {
/* enumeration: must have one of the specified values */
- "type": ["text", "email", "tel"],
+ "type": {
+ "enum": ["text", "email", "tel"]
+ },
/* boolean: should not have a value */
- "disabled": [],
+ "disabled": {
+ "boolean": true
+ },
/* allow omitting value, e.g. it can be set as a boolean or it should be "true" or "false" */
- "multiline": ["", "true", "false"]
+ "multiline": {
+ "omit": true,
+ "enum": ["true", "false"] // the value "" is no longer specified in the enumeration
+ }
}
}
}
The properties requiredAttributes
and deprecatedAttributes
have been integrated into the same object:
{
"my-custom-input": {
- "requiredAttributes": ["type"],
- "deprecatedAttributes": ["autocorrect"]
+ "attributes": {
+ "type": {
+ "required": true
+ },
+ "autocorrect": {
+ "deprecated": true
+ }
+ }
}
}
It is perfectly valid to specify attributes as an empty object which is used to signal that an attribute is exists. When #68 (validate know attributes) is implemented it will be required to list all known attributes but for now no validation will happen without any properties set.
{
"my-custom-input": {
"attributes": {
/* signal that the "foobar" attribute exists but no validation will occur */
"foobar": {},
},
},
}
API changes
If you use MetaElement
to query attribute metadata you must use the new object.
Typically this should only be if you have a custom rule dealing with attributes.
While the old format is supported in userland internally it is converted to the new format.
For instance, given a rule such as:
function myCustomRule(node: DOMNode, attr: Attribute, rule: string[]): void {
/* ... */
}
const meta = node.meta.attributes;
for (const attr of node.attributes) {
if (meta[attr.key]) {
myCustomRule(node, attr, meta[attr.key]);
}
}
The signature of the function must be changed to:
-function myCustomRule(node: DOMNode, attr: Attribute, rule: string[]): void {
+function myCustomRule(node: DOMNode, attr: Attribute, rule: MetaAttribute): void {
/* ... */
}
If you want backwards compatibility you must handle both string[]
and MetaAttribute
, Array.isArray
can be used to distinguish between the two:
function myCustomRule(node: DOMNode, attr: Attribute, rule: string[] | MetaAttribute): void {
if (Array.isArray(rule)) {
/* legacy code path */
} else {
/* modern code path */
}
}
If the rule used logic to determine if the attribute is boolean it must be changed to use the boolean
property:
-const isBoolean = rule.length === 0;
+const isBoolean = rule.boolean;
If the rule used logic to determine if the attribute value can be omitted it must be changed to use the omitted
property:
-const canOmit = rule.includes("");
+const canOmit = rule.omit;
The list of allowed values are must be read from the enum
property but rules must take care to ensure they work even if enum
is not set (undefined
):
-const valid = rule.includes(attr.value);
+const valid = !rule.enum || rule.enum.includes(attr.value);
If you used requiredAttributes
or deprecatedAttributes
these have now been integrated into the same object:
-const isDeprecated = meta.deprecatedAttributes.includes(attr.key);
+const isDeprecated = meta.attribute[attr.key]?.deprecated;
ConfigReadyEvent
Only affects API users.
If you have a rule or plugin listening to the ConfigReadyEvent
event the datatype of the config
property has changed from ConfigData
to ResolvedConfig
.
For most part it contains the same information but is normalized, for instance rules are now always passed as Record<RuleID, [Severity, Options]>
.
Configured transformers, plugins etc are resolved instances and fields suchs as root
and extends
will never be present.
StaticConfigLoader
Only affects API users.
The default configuration loader has changed from FileSystemConfigLoader
to StaticConfigLoader
, i.e. the directory traversal looking for .htmlvalidate.json
configuration files must now be explicitly enabled.
This will reduce the dependency on the NodeJS fs
module and make it easier to use the library in browsers.
To restore the previous behaviour you must now enable FileSystemConfigLoader
:
import { HtmlValidate, FileSystemConfigLoader } from "html-validate";
-const htmlvalidate = new HtmlValidate();
+const loader = new FileSystemConfigLoader();
+const htmlvalidate = new HtmlValidate(loader);
If you pass configuration to the constructor you now pass it to the loader instead:
import { HtmlValidate, FileSystemConfigLoader } from "html-validate";
-const htmlvalidate = new HtmlValidate({ ... });
+const loader = new FileSystemConfigLoader({ ... });
+const htmlvalidate = new HtmlValidate(loader);
If you use the root
property as a workaround for the directory traversal you can now drop the workaround and rely on StaticConfigLoader
:
import { HtmlValidate } from "html-validate";
-const htmlvalidate = new HtmlValidate({
- root: true,
-});
+const htmlvalidate = new HtmlValidate();
The CLI class is not affected as it will enable FileSystemConfigLoader
automatically, so the following code will continue to work as expected:
const cli = new CLI();
const htmlvalidate = cli.getValidator();