question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Add no-unbalanced-parentheses

See original GitHub issue

The 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:open
  • Created 5 years ago
  • Reactions:1
  • Comments:7 (6 by maintainers)

github_iconTop GitHub Comments

2reactions
alexander-akaitcommented, Jul 18, 2018

@alisonailea Sound great idea for rule for me

0reactions
jeddy3commented, Mar 26, 2021

Would this fix the following synthetic case

PostCSS parses this as a selector of ).

We can expand the scope of the rule to include selectors, so the complete scope becomes:

  • selectors of rules
  • params of at-rules
  • values of declarations

Please consider contributing the rule if you have time.

There are steps on how to add a new rule in the Developer guide.

Read more comments on GitHub >

github_iconTop 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 >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found