Commit 168b786c authored by David Sveningsson's avatar David Sveningsson
Browse files

feat(rules): add autofix support to `doctype-style`

parent 12ed8b83
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@ exports[`docs/rules/doctype-style.md inline validation: incorrect 1`] = `
        "context": {
          "style": "uppercase",
        },
        "fix": [Function],
        "line": 1,
        "message": "DOCTYPE should be uppercase",
        "offset": 0,
+18 −0
Original line number Diff line number Diff line
@@ -54,6 +54,15 @@ describe("rule doctype-style", () => {
			const docs = await htmlvalidate.getRuleDocumentation("doctype-style", null, context);
			expect(docs).toMatchSnapshot();
		});

		it("should fix doctype to uppercase", async () => {
			expect.assertions(1);
			const markup = "<!doctype html>";
			const report = await htmlvalidate.validateString(markup);
			const [message] = report.results[0].messages;
			const result = await htmlvalidate.autofixString("inline", markup, message.fix!);
			expect(result).toBe("<!DOCTYPE html>");
		});
	});

	describe("configured with lowercase", () => {
@@ -105,5 +114,14 @@ describe("rule doctype-style", () => {
			const docs = await htmlvalidate.getRuleDocumentation("doctype-style", null, context);
			expect(docs).toMatchSnapshot();
		});

		it("should fix doctype to lowercase", async () => {
			expect.assertions(1);
			const markup = "<!DOCTYPE html>";
			const report = await htmlvalidate.validateString(markup);
			const [message] = report.results[0].messages;
			const result = await htmlvalidate.autofixString("inline", markup, message.fix!);
			expect(result).toBe("<!doctype html>");
		});
	});
});
+24 −2
Original line number Diff line number Diff line
import { type DoctypeEvent } from "../event";
import { sliceLocation } from "../location";
import { type RuleDocumentation, type SchemaObject, Rule, ruleDocumentationUrl } from "../rule";

interface RuleContext {
@@ -18,6 +19,8 @@ export default class DoctypeStyle extends Rule<RuleContext, RuleOptions> {
		super({ ...defaults, ...options });
	}

	public static override readonly fixable = true;

	public static override schema(): SchemaObject {
		return {
			style: {
@@ -36,11 +39,30 @@ export default class DoctypeStyle extends Rule<RuleContext, RuleOptions> {

	public setup(): void {
		this.on("doctype", (event: DoctypeEvent) => {
			/* event.location covers "<!" + keyword + a single trailing whitespace,
			 * the keyword itself is always 7 characters regardless of casing */
			const keywordLocation = sliceLocation(event.location, 2, 9);
			if (this.options.style === "uppercase" && event.tag !== "DOCTYPE") {
				this.report(null, "DOCTYPE should be uppercase", event.location, this.options);
				this.report({
					node: null,
					message: "DOCTYPE should be uppercase",
					location: event.location,
					context: this.options,
					fix(fixer) {
						fixer.replaceText(keywordLocation, "DOCTYPE");
					},
				});
			}
			if (this.options.style === "lowercase" && event.tag !== "doctype") {
				this.report(null, "DOCTYPE should be lowercase", event.location, this.options);
				this.report({
					node: null,
					message: "DOCTYPE should be lowercase",
					location: event.location,
					context: this.options,
					fix(fixer) {
						fixer.replaceText(keywordLocation, "doctype");
					},
				});
			}
		});
	}