Skip to content

Commit

Permalink
feat: add attr special rule to enforce attributes ordering (#47)
Browse files Browse the repository at this point in the history
Closes #46

* Add attr special rule to enforce attributes ordering

* Use "soft" ordering by default and `true` for alphabetical order

* Fix lint error

* Faster and safer implementation
  • Loading branch information
mondeja authored and birjj committed Dec 1, 2021
1 parent 8fd0e21 commit 8e77613
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 1 deletion.
38 changes: 37 additions & 1 deletion src/rules/attr.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const logger = require("../lib/logger")("rule:attr");
* The following special configs are allowed:
* - `{ "rule::selector": {String} }` Default "*". The matching elements must fulfill the other configs.
* - `{ "rule::whitelist": {Boolean} }` Default `false`. If true, no other attributes can exist than those specified by the other configs.
* - `{ "rule::order": {Array<String> | Boolean} }` Default `null`. As array, attributes must be defined in the provided order. As `true`, attributes must be defined in alphabetical order.
*/

/**
Expand All @@ -30,7 +31,7 @@ const logger = require("../lib/logger")("rule:attr");
* - If whitelist is true, error if there are attributes left
*/

const SPECIAL_ATTRIBS = ["rule::selector", "rule::whitelist"];
const SPECIAL_ATTRIBS = ["rule::selector", "rule::whitelist", "rule::order"];

/**
* Executes on a single element.
Expand Down Expand Up @@ -63,6 +64,41 @@ function executeOnElm($elm, config, reporter, ast) {
}
}
);

if (config["rule::order"]) {
const attributes = Object.keys(attrs);
if (attributes.length > 0) {
let order;
if (config["rule::order"] === true) {
// alphabetical ordering
order = attributes.slice();
order.sort();
} else {
order = config["rule::order"];
}

let prevIndex = order.indexOf(attributes[0]);
for (let i = 1; i < attributes.length; i++) {
const index = order.indexOf(attributes[i]);
if (index === -1) {
// this attribute doesn't need ordering, ignore it
return;
}

if (prevIndex !== -1 && index < prevIndex) {
reporter.error(
`Wrong ordering of attributes, found "${
attributes.join(", ")}", expected "${order.join(", ")}"`,
$elm,
ast
);
break;
}
prevIndex = index;
}
}
}

// check that all configs are met
Object.keys(attrs).forEach(
attrib => {
Expand Down
46 changes: 46 additions & 0 deletions test/attr.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const testSVG = `<svg role="img" viewBox="0 0 24 24">
</g>
<g></g>
<circle></circle>
<rect height="100" width="300" style="fill:black;" />
</svg>`;

function inspect(obj) {
Expand Down Expand Up @@ -197,4 +198,49 @@ describe("Rule: attr", function(){
"rule::whitelist": true,
});
});
it("should succeed enforcing right attributes ordering", function() {
return testSucceeds({
"rule::selector": "rect",
"rule::order": ["height", "width", "style"],
});
});
it("should fail enforcing wrong attributes ordering", function() {
return testFails({
"rule::selector": "rect",
"rule::order": ["width", "style", "height"],
});
});
it("should succeed enforcing ordering of first attributes", function() {
return testSucceeds({
"rule::selector": "rect",
"rule::order": ["height", "width"],
});
});
it("should succeed enforcing soft ordering of some attributes", function() {
return testSucceeds({
"rule::selector": "rect",
"rule::order": ["height", "style"],
});
});
it("should succeed enforcing alphabetical ordering with true", function() {
return testSucceeds({
"rule::selector": "svg",
"rule::order": true,
});
});
it("should fail enforcing alphabetical ordering", function() {
return testFails({
"rule::selector": "rect",
"rule::order": true,
});
});
it("should succeed enforcing hard ordering with whitelist", function() {
return testSucceeds({
"role": true,
"viewBox": true,
"rule::selector": "svg",
"rule::whitelist": true,
"rule::order": true,
});
});
});

0 comments on commit 8e77613

Please sign in to comment.