From 11c7c7f12ea6d7eceb813f47edfe7013b350771e Mon Sep 17 00:00:00 2001 From: Marcos Caceres Date: Sun, 10 May 2026 17:35:32 +1000 Subject: [PATCH 1/4] feat(linter): warn when data-cite targets a spec available in xref MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When conf.xref is configured with a named profile (e.g. "web-platform") or an explicit spec list, authors sometimes write data-cite="SPEC#frag" for terms that xref could resolve automatically. This is fragile because fragment identifiers change between spec versions. The new prefer-xref linter rule detects a[data-cite*="#"] and dfn[data-cite*="#"] where the spec key matches a spec in the configured xref list, and warns with a hint to use [= term =] or {{ IDL }} shorthand instead. Elements with class="lint-ignore" are excluded. The check is opt-in (lint: { "prefer-xref": true }) and skips when xref: true is set (no specific spec list — would need a network call to determine coverage). Closes #3561 --- profiles/w3c.js | 1 + src/core/linter-rules/prefer-xref.js | 159 ++++++++++++ .../core/linter-rules/prefer-xref-spec.js | 243 ++++++++++++++++++ 3 files changed, 403 insertions(+) create mode 100644 src/core/linter-rules/prefer-xref.js create mode 100644 tests/spec/core/linter-rules/prefer-xref-spec.js diff --git a/profiles/w3c.js b/profiles/w3c.js index cdf270af71..434c549b00 100644 --- a/profiles/w3c.js +++ b/profiles/w3c.js @@ -80,6 +80,7 @@ const modules = [ import("../src/core/linter-rules/no-http-props.js"), import("../src/core/linter-rules/a11y.js"), import("../src/core/linter-rules/informative-dfn.js"), + import("../src/core/linter-rules/prefer-xref.js"), ]; Promise.all(modules) diff --git a/src/core/linter-rules/prefer-xref.js b/src/core/linter-rules/prefer-xref.js new file mode 100644 index 0000000000..93beb06c97 --- /dev/null +++ b/src/core/linter-rules/prefer-xref.js @@ -0,0 +1,159 @@ +// @ts-check +/** + * Linter rule "prefer-xref". + * + * Warns when an author uses `data-cite="SPEC#fragment"` to link to a term in a + * specification that is already covered by the configured xref database. In + * that case the xref shorthand syntax (`[= term =]`, `{{ IDL }}`, etc.) is + * both shorter and more robust than a hard-coded fragment identifier. + */ +import { docLink, getIntlData, showWarning } from "../utils.js"; + +const ruleName = "prefer-xref"; +export const name = "core/linter-rules/prefer-xref"; + +/** + * Named xref profiles and their spec lists. + * Keep in sync with the `profiles` object in `src/core/xref.js`. + * + * @type {Record} + */ +const PROFILES = { + "web-platform": ["HTML", "INFRA", "URL", "WEBIDL", "DOM", "FETCH"], +}; + +/** @satisfies {Record} */ +const localizationStrings = { + en: { + msg(specKey) { + return `Spec \`${specKey}\` is available in xref. Consider using shorthand syntax (e.g. \`[= term =]\`) instead of \`data-cite="${specKey}#…"\`.`; + }, + get hint() { + return docLink`Using ${"[xref]"} shorthand syntax is shorter, spec-version-agnostic, and lets ReSpec verify the term exists. To silence this warning for a specific element, add \`class="lint-ignore"\`. To disable this rule entirely, set \`lint: { "${ruleName}": false }\` in your \`respecConfig\`.`; + }, + }, +}; +const l10n = getIntlData(localizationStrings); + +/** + * Derive the set of spec shortnames (uppercased) that the author has + * configured xref to search, based on the raw `conf.xref` value. + * + * Returns `null` when xref is enabled but no specific spec list is + * configured (`conf.xref === true`), meaning we cannot determine coverage + * without a network call and therefore skip the check. + * + * @param {Conf["xref"]} xref + * @returns {Set | null} + */ +function getXrefSpecSet(xref) { + if (!xref) return null; + + /** @type {Set} */ + const specs = new Set(); + + if (xref === true) { + // No specific spec list — skip the check to avoid false positives. + return null; + } + + if (typeof xref === "string") { + const profile = xref.toLowerCase(); + if (profile in PROFILES) { + PROFILES[profile].forEach(s => specs.add(s.toUpperCase())); + } + return specs.size ? specs : null; + } + + if (Array.isArray(xref)) { + xref.forEach(s => specs.add(s.toUpperCase())); + return specs.size ? specs : null; + } + + if (typeof xref === "object") { + const { profile, specs: specList } = + /** @type {{ profile?: string; specs?: string[] }} */ (xref); + if (profile) { + const key = profile.toLowerCase(); + if (key in PROFILES) { + PROFILES[key].forEach(s => specs.add(s.toUpperCase())); + } + } + if (specList) { + specList.forEach(s => specs.add(s.toUpperCase())); + } + return specs.size ? specs : null; + } + + return null; +} + +/** + * Extract the spec shortname (the part before `#`, `/`, or any `?`/`!` prefix) + * from a raw `data-cite` value. + * + * Examples: + * "HTML#foo" → "HTML" + * "?HTML#foo" → "HTML" + * "HTML/path#foo" → "HTML" + * + * @param {string} rawCite + * @returns {string} + */ +function extractSpecKey(rawCite) { + return rawCite + .replace(/^[?!]/, "") // strip leading normative/informative marker + .split(/[/#]/)[0] // take the part before any "/" or "#" + .toUpperCase(); +} + +/** + * @param {Conf} conf + */ +export function run(conf) { + // @ts-expect-error -- LintConfig can be false; ?. only short-circuits null/undefined in TS + if (!conf.lint?.[ruleName]) { + return; + } + + if (!conf.xref) { + // xref is not enabled; data-cite is the only option. + return; + } + + const xrefSpecSet = getXrefSpecSet(conf.xref); + if (!xrefSpecSet) { + // Either xref===true (no spec list) or an unrecognised config shape. + // Skip to avoid false positives. + return; + } + + // Select elements where the author has written data-cite="SPEC#fragment" + // (the "#" is in the attribute value itself, not in data-cite-frag). + // Exclude self-referencing fragments (#only) and already lint-ignored elements. + /** @type {NodeListOf} */ + const elems = document.querySelectorAll( + ":is(a, dfn)[data-cite*='#']:not([data-cite^='#']):not(.lint-ignore)" + ); + + /** @type {Map} key → offending elements */ + const offenders = new Map(); + + elems.forEach(elem => { + const rawCite = elem.dataset.cite ?? ""; + const specKey = extractSpecKey(rawCite); + if (!specKey || !xrefSpecSet.has(specKey)) return; + + if (!offenders.has(specKey)) { + offenders.set(specKey, []); + } + offenders.get(specKey)?.push(elem); + }); + + offenders.forEach((elements, specKey) => { + showWarning(l10n.msg(specKey), name, { + hint: l10n.hint, + elements, + }); + }); +} diff --git a/tests/spec/core/linter-rules/prefer-xref-spec.js b/tests/spec/core/linter-rules/prefer-xref-spec.js new file mode 100644 index 0000000000..166d928058 --- /dev/null +++ b/tests/spec/core/linter-rules/prefer-xref-spec.js @@ -0,0 +1,243 @@ +"use strict"; + +import { + flushIframes, + makeRSDoc, + makeStandardOps, + warningFilters, +} from "../../SpecHelper.js"; + +describe("Core — linter-rules - prefer-xref", () => { + const name = "core/linter-rules/prefer-xref"; + const lintWarnings = warningFilters.filter(name); + + afterAll(() => { + flushIframes(); + }); + + it("does not warn when the rule is turned off", async () => { + const body = ` +
+

Test

+

a element

+
`; + const ops = makeStandardOps( + { lint: { "prefer-xref": false }, xref: "web-platform" }, + body + ); + const doc = await makeRSDoc(ops); + expect(lintWarnings(doc)).toHaveSize(0); + }); + + it("does not warn when xref is not enabled", async () => { + const body = ` +
+

Test

+

a element

+
`; + const ops = makeStandardOps( + { lint: { "prefer-xref": true }, xref: false }, + body + ); + const doc = await makeRSDoc(ops); + expect(lintWarnings(doc)).toHaveSize(0); + }); + + it("does not warn when xref is enabled with no spec list (xref: true)", async () => { + // When xref=true we cannot determine coverage without a network call, + // so we conservatively skip the check. + const body = ` +
+

Test

+

a element

+
`; + const ops = makeStandardOps( + { lint: { "prefer-xref": true }, xref: true }, + body + ); + const doc = await makeRSDoc(ops); + expect(lintWarnings(doc)).toHaveSize(0); + }); + + it("warns for data-cite with fragment on a spec in the xref profile", async () => { + const body = ` +
+

Test

+

a element

+
`; + const ops = makeStandardOps( + { lint: { "prefer-xref": true }, xref: "web-platform" }, + body + ); + const doc = await makeRSDoc(ops); + const warnings = lintWarnings(doc); + expect(warnings).toHaveSize(1); + expect(warnings[0].message).toContain("HTML"); + }); + + it("warns once per spec key, grouping all offending elements together", async () => { + const body = ` +
+

Test

+

a element

+

p element

+

list

+
`; + const ops = makeStandardOps( + { lint: { "prefer-xref": true }, xref: "web-platform" }, + body + ); + const doc = await makeRSDoc(ops); + const warnings = lintWarnings(doc); + // One warning per spec key (HTML and INFRA each get one warning). + expect(warnings).toHaveSize(2); + const specKeys = warnings.map(w => w.message).join(" "); + expect(specKeys).toContain("HTML"); + expect(specKeys).toContain("INFRA"); + }); + + it("does not warn for data-cite without a fragment (context-only use)", async () => { + // data-cite="HTML" (no #) is a legitimate context hint — xref uses it. + const body = ` +
+

Test

+

event handler

+
`; + const ops = makeStandardOps( + { lint: { "prefer-xref": true }, xref: "web-platform" }, + body + ); + const doc = await makeRSDoc(ops); + expect(lintWarnings(doc)).toHaveSize(0); + }); + + it("does not warn for specs not in the configured xref list", async () => { + const body = ` +
+

Test

+

some term

+
`; + const ops = makeStandardOps( + { lint: { "prefer-xref": true }, xref: "web-platform" }, + body + ); + const doc = await makeRSDoc(ops); + expect(lintWarnings(doc)).toHaveSize(0); + }); + + it("warns for specs in a custom xref array", async () => { + const body = ` +
+

Test

+

some term

+
`; + const ops = makeStandardOps( + { lint: { "prefer-xref": true }, xref: ["MYSPEC"] }, + body + ); + const doc = await makeRSDoc(ops); + const warnings = lintWarnings(doc); + expect(warnings).toHaveSize(1); + expect(warnings[0].message).toContain("MYSPEC"); + }); + + it("warns for specs in a custom xref object with specs array", async () => { + const body = ` +
+

Test

+

EventTarget

+
`; + const ops = makeStandardOps( + { + lint: { "prefer-xref": true }, + xref: { profile: "web-platform", specs: ["DOM"] }, + }, + body + ); + const doc = await makeRSDoc(ops); + const warnings = lintWarnings(doc); + expect(warnings).toHaveSize(1); + expect(warnings[0].message).toContain("DOM"); + }); + + it("ignores elements with class='lint-ignore'", async () => { + const body = ` +
+

Test

+

a element

+

p element

+
`; + const ops = makeStandardOps( + { lint: { "prefer-xref": true }, xref: "web-platform" }, + body + ); + const doc = await makeRSDoc(ops); + const warnings = lintWarnings(doc); + // Only the second element (without lint-ignore) should warn. + expect(warnings).toHaveSize(1); + expect(warnings[0].elements).toHaveSize(1); + }); + + it("handles case-insensitive spec key matching", async () => { + // Authors may write lowercase "html" — should still match "HTML" in the profile. + const body = ` +
+

Test

+

a element

+
`; + const ops = makeStandardOps( + { lint: { "prefer-xref": true }, xref: "web-platform" }, + body + ); + const doc = await makeRSDoc(ops); + const warnings = lintWarnings(doc); + expect(warnings).toHaveSize(1); + }); + + it("handles informative prefix '?' in data-cite value", async () => { + const body = ` +
+

Test

+

a element

+
`; + const ops = makeStandardOps( + { lint: { "prefer-xref": true }, xref: "web-platform" }, + body + ); + const doc = await makeRSDoc(ops); + const warnings = lintWarnings(doc); + expect(warnings).toHaveSize(1); + expect(warnings[0].message).toContain("HTML"); + }); + + it("warns for dfn elements with data-cite fragment in xref specs", async () => { + const body = ` +
+

Test

+

interface

+
`; + const ops = makeStandardOps( + { lint: { "prefer-xref": true }, xref: "web-platform" }, + body + ); + const doc = await makeRSDoc(ops); + const warnings = lintWarnings(doc); + expect(warnings).toHaveSize(1); + expect(warnings[0].message).toContain("WEBIDL"); + }); + + it("does not warn for self-referencing data-cite (data-cite='#foo')", async () => { + const body = ` +
+

Test

+ something +

something

+
`; + const ops = makeStandardOps( + { lint: { "prefer-xref": true }, xref: "web-platform" }, + body + ); + const doc = await makeRSDoc(ops); + expect(lintWarnings(doc)).toHaveSize(0); + }); +}); From 831ee209230b397802d61cab2b39de10418f43c9 Mon Sep 17 00:00:00 2001 From: Marcos Caceres Date: Sun, 10 May 2026 18:44:24 +1000 Subject: [PATCH 2/4] refactor(linter/prefer-xref): simplify and fix severity support - Import profiles from xref.js instead of duplicating the constant - Add error severity support (conf.lint["prefer-xref"] = "error") - Merge falsy and true xref guards into single early return - Remove dead ?? "" fallback (selector guarantees data-cite exists) - Remove unnecessary ?. on Map.get() after guaranteed set - Trim verbose JSDoc to essential type annotations --- src/core/linter-rules/prefer-xref.js | 71 +++++----------------------- src/core/xref.js | 2 +- 2 files changed, 14 insertions(+), 59 deletions(-) diff --git a/src/core/linter-rules/prefer-xref.js b/src/core/linter-rules/prefer-xref.js index 93beb06c97..0ccdaf0aca 100644 --- a/src/core/linter-rules/prefer-xref.js +++ b/src/core/linter-rules/prefer-xref.js @@ -1,27 +1,10 @@ // @ts-check -/** - * Linter rule "prefer-xref". - * - * Warns when an author uses `data-cite="SPEC#fragment"` to link to a term in a - * specification that is already covered by the configured xref database. In - * that case the xref shorthand syntax (`[= term =]`, `{{ IDL }}`, etc.) is - * both shorter and more robust than a hard-coded fragment identifier. - */ -import { docLink, getIntlData, showWarning } from "../utils.js"; +import { docLink, getIntlData, showError, showWarning } from "../utils.js"; +import { profiles } from "../xref.js"; const ruleName = "prefer-xref"; export const name = "core/linter-rules/prefer-xref"; -/** - * Named xref profiles and their spec lists. - * Keep in sync with the `profiles` object in `src/core/xref.js`. - * - * @type {Record} - */ -const PROFILES = { - "web-platform": ["HTML", "INFRA", "URL", "WEBIDL", "DOM", "FETCH"], -}; - /** @satisfies {Record} */ const localizationStrings = { en: { @@ -36,31 +19,19 @@ const localizationStrings = { const l10n = getIntlData(localizationStrings); /** - * Derive the set of spec shortnames (uppercased) that the author has - * configured xref to search, based on the raw `conf.xref` value. - * - * Returns `null` when xref is enabled but no specific spec list is - * configured (`conf.xref === true`), meaning we cannot determine coverage - * without a network call and therefore skip the check. - * * @param {Conf["xref"]} xref * @returns {Set | null} */ function getXrefSpecSet(xref) { - if (!xref) return null; + if (!xref || xref === true) return null; /** @type {Set} */ const specs = new Set(); - if (xref === true) { - // No specific spec list — skip the check to avoid false positives. - return null; - } - if (typeof xref === "string") { const profile = xref.toLowerCase(); - if (profile in PROFILES) { - PROFILES[profile].forEach(s => specs.add(s.toUpperCase())); + if (profile in profiles) { + profiles[profile].forEach(s => specs.add(s.toUpperCase())); } return specs.size ? specs : null; } @@ -75,8 +46,8 @@ function getXrefSpecSet(xref) { /** @type {{ profile?: string; specs?: string[] }} */ (xref); if (profile) { const key = profile.toLowerCase(); - if (key in PROFILES) { - PROFILES[key].forEach(s => specs.add(s.toUpperCase())); + if (key in profiles) { + profiles[key].forEach(s => specs.add(s.toUpperCase())); } } if (specList) { @@ -89,22 +60,11 @@ function getXrefSpecSet(xref) { } /** - * Extract the spec shortname (the part before `#`, `/`, or any `?`/`!` prefix) - * from a raw `data-cite` value. - * - * Examples: - * "HTML#foo" → "HTML" - * "?HTML#foo" → "HTML" - * "HTML/path#foo" → "HTML" - * * @param {string} rawCite * @returns {string} */ function extractSpecKey(rawCite) { - return rawCite - .replace(/^[?!]/, "") // strip leading normative/informative marker - .split(/[/#]/)[0] // take the part before any "/" or "#" - .toUpperCase(); + return rawCite.replace(/^[?!]/, "").split(/[/#]/)[0].toUpperCase(); } /** @@ -117,41 +77,36 @@ export function run(conf) { } if (!conf.xref) { - // xref is not enabled; data-cite is the only option. return; } const xrefSpecSet = getXrefSpecSet(conf.xref); if (!xrefSpecSet) { - // Either xref===true (no spec list) or an unrecognised config shape. - // Skip to avoid false positives. return; } - // Select elements where the author has written data-cite="SPEC#fragment" - // (the "#" is in the attribute value itself, not in data-cite-frag). - // Exclude self-referencing fragments (#only) and already lint-ignored elements. /** @type {NodeListOf} */ const elems = document.querySelectorAll( ":is(a, dfn)[data-cite*='#']:not([data-cite^='#']):not(.lint-ignore)" ); - /** @type {Map} key → offending elements */ + /** @type {Map} */ const offenders = new Map(); elems.forEach(elem => { - const rawCite = elem.dataset.cite ?? ""; + const rawCite = elem.dataset.cite; const specKey = extractSpecKey(rawCite); if (!specKey || !xrefSpecSet.has(specKey)) return; if (!offenders.has(specKey)) { offenders.set(specKey, []); } - offenders.get(specKey)?.push(elem); + offenders.get(specKey).push(elem); }); + const logger = conf.lint?.[ruleName] === "error" ? showError : showWarning; offenders.forEach((elements, specKey) => { - showWarning(l10n.msg(specKey), name, { + logger(l10n.msg(specKey), name, { hint: l10n.hint, elements, }); diff --git a/src/core/xref.js b/src/core/xref.js index 459af7e97e..e9bd8baeab 100644 --- a/src/core/xref.js +++ b/src/core/xref.js @@ -32,7 +32,7 @@ import { sub } from "./pubsubhub.js"; export const name = "core/xref"; -const profiles = { +export const profiles = { "web-platform": ["HTML", "INFRA", "URL", "WEBIDL", "DOM", "FETCH"], }; From 8858f2fabe939c4e57557b41c4f5ede9de099a01 Mon Sep 17 00:00:00 2001 From: Marcos Caceres Date: Sun, 10 May 2026 20:08:45 +1000 Subject: [PATCH 3/4] fix(linter/prefer-xref): add JSDoc type casts for TypeScript strict mode --- src/core/linter-rules/prefer-xref.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/core/linter-rules/prefer-xref.js b/src/core/linter-rules/prefer-xref.js index 0ccdaf0aca..1520781e9c 100644 --- a/src/core/linter-rules/prefer-xref.js +++ b/src/core/linter-rules/prefer-xref.js @@ -31,7 +31,9 @@ function getXrefSpecSet(xref) { if (typeof xref === "string") { const profile = xref.toLowerCase(); if (profile in profiles) { - profiles[profile].forEach(s => specs.add(s.toUpperCase())); + /** @type {Record} */ (profiles)[profile].forEach( + (/** @type {string} */ s) => specs.add(s.toUpperCase()) + ); } return specs.size ? specs : null; } @@ -47,7 +49,9 @@ function getXrefSpecSet(xref) { if (profile) { const key = profile.toLowerCase(); if (key in profiles) { - profiles[key].forEach(s => specs.add(s.toUpperCase())); + /** @type {Record} */ (profiles)[key].forEach( + (/** @type {string} */ s) => specs.add(s.toUpperCase()) + ); } } if (specList) { @@ -94,16 +98,17 @@ export function run(conf) { const offenders = new Map(); elems.forEach(elem => { - const rawCite = elem.dataset.cite; + const rawCite = /** @type {string} */ (elem.dataset.cite); const specKey = extractSpecKey(rawCite); if (!specKey || !xrefSpecSet.has(specKey)) return; if (!offenders.has(specKey)) { offenders.set(specKey, []); } - offenders.get(specKey).push(elem); + /** @type {HTMLElement[]} */ (offenders.get(specKey)).push(elem); }); + // @ts-expect-error -- ruleName is a string literal but LintConfig index signature doesn't cover it const logger = conf.lint?.[ruleName] === "error" ? showError : showWarning; offenders.forEach((elements, specKey) => { logger(l10n.msg(specKey), name, { From 96974dc44cb2f9b1ff8766545d65009d97ece161 Mon Sep 17 00:00:00 2001 From: Marcos Caceres Date: Sun, 10 May 2026 20:46:09 +1000 Subject: [PATCH 4/4] feat(linter/prefer-xref): register in all profiles, add Czech translation - Register prefer-xref in geonovum, aom, and dini profiles - Add Czech (cs) localization for msg and hint strings --- profiles/aom.js | 1 + profiles/dini.js | 1 + profiles/geonovum.js | 1 + src/core/linter-rules/prefer-xref.js | 8 ++++++++ 4 files changed, 11 insertions(+) diff --git a/profiles/aom.js b/profiles/aom.js index db075aa1d6..c9df5652bc 100644 --- a/profiles/aom.js +++ b/profiles/aom.js @@ -54,6 +54,7 @@ const modules = [ import("../src/core/linter-rules/no-unused-vars.js"), import("../src/core/linter-rules/privsec-section.js"), import("../src/core/linter-rules/no-http-props.js"), + import("../src/core/linter-rules/prefer-xref.js"), ]; Promise.all(modules) diff --git a/profiles/dini.js b/profiles/dini.js index 0315e6d46e..e94aebbf5b 100644 --- a/profiles/dini.js +++ b/profiles/dini.js @@ -54,6 +54,7 @@ const modules = [ import("../src/core/linter-rules/no-unused-vars.js"), import("../src/core/linter-rules/privsec-section.js"), import("../src/core/linter-rules/no-http-props.js"), + import("../src/core/linter-rules/prefer-xref.js"), ]; Promise.all(modules) diff --git a/profiles/geonovum.js b/profiles/geonovum.js index 798a323b35..da072db60b 100644 --- a/profiles/geonovum.js +++ b/profiles/geonovum.js @@ -52,6 +52,7 @@ const modules = [ import("../src/core/linter-rules/no-unused-vars.js"), import("../src/core/linter-rules/privsec-section.js"), import("../src/core/linter-rules/no-http-props.js"), + import("../src/core/linter-rules/prefer-xref.js"), ]; Promise.all(modules) diff --git a/src/core/linter-rules/prefer-xref.js b/src/core/linter-rules/prefer-xref.js index 1520781e9c..2b42952da7 100644 --- a/src/core/linter-rules/prefer-xref.js +++ b/src/core/linter-rules/prefer-xref.js @@ -15,6 +15,14 @@ const localizationStrings = { return docLink`Using ${"[xref]"} shorthand syntax is shorter, spec-version-agnostic, and lets ReSpec verify the term exists. To silence this warning for a specific element, add \`class="lint-ignore"\`. To disable this rule entirely, set \`lint: { "${ruleName}": false }\` in your \`respecConfig\`.`; }, }, + cs: { + msg(specKey) { + return `Specifikace \`${specKey}\` je dostupná v xref. Zvažte použití zkráceného zápisu (např. \`[= pojem =]\`) místo \`data-cite="${specKey}#…"\`.`; + }, + get hint() { + return docLink`Zkrácený zápis ${"[xref]"} je kratší, nezávislý na verzi specifikace a umožňuje ReSpecu ověřit existenci pojmu. Pro potlačení tohoto varování u konkrétního prvku přidejte \`class="lint-ignore"\`. Pro úplné vypnutí pravidla nastavte \`lint: { "${ruleName}": false }\` ve vašem \`respecConfig\`.`; + }, + }, }; const l10n = getIntlData(localizationStrings);