Add no-unbalanced-parentheses
See original GitHub issueThe Problem
Developers on my team occasionally add extra parenthesis to the end of custom-properties, media-queries, and urls which are not caught by other linting rules and which can cause errors when that CSS is used in other projects.
The Solution
A new rule that checks for extra parenthesis at the end of media queries and custom-properties
Notes
I have a branch in my local clone of Stylelint with this rule already partially written if it is something the community wants I’m happy to finish and submit the PR
Tape test (based off no-extra-semicolons)
"use strict";
const rule = require("..");
const { messages, ruleName } = rule;
testRule(rule, {
ruleName,
config: [true],
accept: [
{
code: "a { background: url() }"
},
{
code: "a { background: url('') }"
},
{
code: "a { background: url( ) }"
},
{
code: "a { color: var(--prop) }"
},
{
code: "a { color: (); }"
},
{
code: "a { color: var(--prop);}"
},
{
code: "@media (min-width: var(--prop)) {a { color: var(--prop); }}"
},
{
code: "/* a { color: var(--prop);} */"
},
{
code: "/* a { color: var(--prop)));}; */"
},
{
code: "/* ()comment)) words))(( */"
},
{
code: "a { content: ')'; }"
},
{
code: "a { content: '))'; }"
},
{
code: "a { content: '(\t) )'; }"
},
{
code: ":root { --foo: '('; --bar: '))'; }"
}
],
reject: [
{
code: ")",
message: messages.rejected,
line: 1,
column: 1
},
{
code: " )",
message: messages.rejected,
line: 1,
column: 2
},
{
code: " )",
message: messages.rejected,
line: 1,
column: 3
},
{
code: "\n)",
message: messages.rejected,
line: 2,
column: 1
},
{
code: "\n\n)",
message: messages.rejected,
line: 3,
column: 1
},
{
code: "\r\n)",
message: messages.rejected,
line: 2,
column: 1
},
{
code: "\r\n\r\n)",
message: messages.rejected,
line: 3,
column: 1
},
{
code: "\r)",
message: messages.rejected,
line: 1,
column: 2
},
{
code: "\r\r)",
message: messages.rejected,
line: 1,
column: 3
},
{
code: "\t)",
message: messages.rejected,
line: 1,
column: 2
},
{
code: "\t\t)",
message: messages.rejected,
line: 1,
column: 3
},
{
code: "a {)}",
message: messages.rejected,
line: 1,
column: 4
},
{
code: "a { )}",
message: messages.rejected,
line: 1,
column: 5
},
{
code: "a {) }",
message: messages.rejected,
line: 1,
column: 4
},
{
code: "a { ) }",
message: messages.rejected,
line: 1,
column: 5
},
{
code: "a { )}",
message: messages.rejected,
line: 1,
column: 6
},
{
code: "a {) }",
message: messages.rejected,
line: 1,
column: 4
},
{
code: "a { ) }",
message: messages.rejected,
line: 1,
column: 6
},
{
code: "a { ) }",
message: messages.rejected,
line: 1,
column: 7
},
{
code: "a {\n)}",
message: messages.rejected,
line: 2,
column: 1
},
{
code: "a {)\n}",
message: messages.rejected,
line: 1,
column: 4
},
{
code: "a {\n)\n}",
message: messages.rejected,
line: 2,
column: 1
},
{
code: "a {\r\n)}",
message: messages.rejected,
line: 2,
column: 1
},
{
code: "a {)\r\n}",
message: messages.rejected,
line: 1,
column: 4
},
{
code: "a {\r\n)\r\n}",
message: messages.rejected,
line: 2,
column: 1
},
{
code: "a {\t)}",
message: messages.rejected,
line: 1,
column: 5
},
{
code: "a {)\t}",
message: messages.rejected,
line: 1,
column: 4
},
{
code: "a {\t)\t}",
message: messages.rejected,
line: 1,
column: 5
},
{
code: "a { color: var(--prop); })",
message: messages.rejected,
line: 1,
column: 26
},
{
code: "a { color: var(--prop); } )",
message: messages.rejected,
line: 1,
column: 27
},
{
code: "a { color: var(--prop); }\n)",
message: messages.rejected,
line: 2,
column: 1
},
{
code: "a { color: var(--prop); }\n\n)",
message: messages.rejected,
line: 3,
column: 1
},
{
code: "a { color: var(--prop); }\r\n)",
message: messages.rejected,
line: 2,
column: 1
},
{
code: "a { color: var(--prop); }\r\n\r\n)",
message: messages.rejected,
line: 3,
column: 1
},
{
code: "a { color: var(--prop); }\r)",
message: messages.rejected,
line: 1,
column: 19
},
{
code: "a { color: var(--prop); }\r\r)",
message: messages.rejected,
line: 1,
column: 28
},
{
code: "a { color: var(--prop); }\t)",
message: messages.rejected,
line: 1,
column: 27
},
{
code: "a { color: var(--prop); }\t\t)",
message: messages.rejected,
line: 1,
column: 28
},
{
code: ")a { color: var(--prop); }",
message: messages.rejected,
line: 1,
column: 1
},
{
code: ") a { color: var(--prop); }",
message: messages.rejected,
line: 1,
column: 1
},
{
code: ") a { color: var(--prop); }",
message: messages.rejected,
line: 1,
column: 1
},
{
code: ")\na { color: var(--prop); }",
message: messages.rejected,
line: 1,
column: 1
},
{
code: ")\n\na { color: var(--prop); }",
message: messages.rejected,
line: 1,
column: 1
},
{
code: ")\r\na { color: var(--prop); }",
message: messages.rejected,
line: 1,
column: 1
},
{
code: ")\r\n\r\na { color: var(--prop); }",
message: messages.rejected,
line: 1,
column: 1
},
{
code: ")\ta { color: var(--prop); }",
message: messages.rejected,
line: 1,
column: 1
},
{
code: ")\t\ta { color: var(--prop); }",
message: messages.rejected,
line: 1,
column: 1
},
{
code: "@media ()) { }",
message: messages.rejected,
line: 1,
column: 10
},
{
code: "@media () ) { a { color: var(--prop); } }",
message: messages.rejected,
line: 1,
column: 11
},
{
code: "@media () ) { a { color: var(--prop); } }",
message: messages.rejected,
line: 1,
column: 12
},
{
code: "@media (var(--prop))) { a { color: var(--prop); } }",
message: messages.rejected,
line: 1,
column: 21
},
{
code: "@media (var(--prop) )) { a { color: var(--prop); } }",
message: messages.rejected,
line: 1,
column: 22
},
{
code: "@media ( var(--prop) )) { a { color: var(--prop); } }",
message: messages.rejected,
line: 1,
column: 23
},
{
code: "/* comment */)",
message: messages.rejected,
line: 1,
column: 14
},
{
code: "/* comment */ ) /*comment */",
message: messages.rejected,
line: 1,
column: 15
},
{
code: "/* comment */\n)\n/* comment */",
message: messages.rejected,
line: 2,
column: 1
},
{
code: "/* comment */\r\n)\r\n/* comment */",
message: messages.rejected,
line: 2,
column: 1
},
{
code: "/* comment */\t)\t/* comment */",
message: messages.rejected,
line: 1,
column: 15
},
{
code: "a { color: var(--prop)) }",
message: messages.rejected,
line: 1,
column: 23
},
{
code: "a { color: var(--prop) ) }",
message: messages.rejected,
line: 1,
column: 24
},
{
code: "a { color: var(--prop) ) }",
message: messages.rejected,
line: 1,
column: 25
},
{
code: "a { color: var(--prop)\n) }",
message: messages.rejected,
line: 2,
column: 1
},
{
code: "a { color: var(--prop)\n\n) }",
message: messages.rejected,
line: 3,
column: 1
},
{
code: "a { color: var(--prop)\r\n) }",
message: messages.rejected,
line: 2,
column: 1
},
{
code: "a { color: var(--prop)\r\n\r\n) }",
message: messages.rejected,
line: 3,
column: 1
},
{
code: "a { background: url()) }",
message: messages.rejected,
line: 1,
column: 22
},
{
code: "a { background: url( )) }",
message: messages.rejected,
line: 1,
column: 23
},
{
code: "a { background: url( ) ) }",
message: messages.rejected,
line: 1,
column: 24
},
{
code: "a { background: url('')) }",
message: messages.rejected,
line: 1,
column: 24
},
{
code: "a { background: url(' ') ) }",
message: messages.rejected,
line: 1,
column: 26
},
{
code: "a { @media (min-width: var(--prop))) { color: red; } }",
message: messages.rejected,
line: 1,
column: 36
},
{
code: "a { @media (min-width: var(--prop)) ) { color: red; } }",
message: messages.rejected,
line: 1,
column: 37
},
{
code: "a { @media (min-width: var(--prop) )) { color: red; } }",
message: messages.rejected,
line: 1,
column: 37
},
{
code: "a { @media (min-width: var(--prop ))) { color: red; } }",
message: messages.rejected,
line: 1,
column: 37
},
{
code: "a {\n@media (min-width: var(--prop))) { color: red; } }",
message: messages.rejected,
line: 2,
column: 32
},
{
code: "a {\n@media (\nmin-width: var(--prop))) { color: red; } }",
message: messages.rejected,
line: 3,
column: 24
},
{
code: "a {\n@media (\nmin-width: var(--prop)\n)) { color: red; } }",
message: messages.rejected,
line: 4,
column: 2
},
]
});
testRule(rule, {
ruleName,
config: [true],
syntax: "html",
accept: [
{
code: `<div>
<style>
a {
color: var(--prop);
}
</style>
</div>`
},
{
code: `<div>
<style>
a {
background: url();
}
</style>
</div>`
}
],
reject: [
{
code: `<div>
<style>
)
</style>
</div>`,
message: messages.rejected,
line: 3,
column: 1
},
{
code: `<div>
<style>
a {
color: var(--prop))
}
</style>
</div>`,
message: messages.rejected,
line: 4,
column: 21
}
]
});
testRule(rule, {
ruleName,
config: [true],
syntax: "less",
accept: [
{
code: "a { .mixin(); .mixin2; }"
},
],
reject: [
{
code: "a { .mixin());\ncolor: red; }",
message: messages.rejected,
line: 1,
column: 13
}
]
});
Issue Analytics
- State:
- Created 5 years ago
- Reactions:1
- Comments:7 (6 by maintainers)
Top Results From Across the Web
Batch File "Unbalanced Parenthesis" - Stack Overflow
As far as I know, mathematically speaking the parenthesis are completely balanced. I've tried: wrapping the set /a statement in both "" and...
Read more >Minimum number of Parentheses to be added to make it valid
A string is valid if its balance is 0, and also every prefix has non-negative balance. Now, consider the balance of every prefix...
Read more >org-ref "Unbalanced parentheses", 20306, 18248 error
I am new to org-mode and I am learning to use it to replace markdown for academic writing. M-x org-ref gave me this...
Read more >Unbalanced parentheses cause error [#3205436] | Drupal.org
The parentheses is not properly escaped. I am not sure if this is an Apace Solr module issue, or a core issue, but...
Read more >Why do I get the error message 'Unbalanced or unexpected ...
Solution: Count the number of left parentheses and right parentheses on the line of code.
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
@alisonailea Sound great idea for rule for me
PostCSS parses this as a selector of
)
.We can expand the scope of the rule to include selectors, so the complete scope becomes:
Please consider contributing the rule if you have time.
There are steps on how to add a new rule in the Developer guide.