Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add named-grid-areas-no-invalid #5167

Merged
merged 46 commits into from Mar 5, 2021
Merged
Show file tree
Hide file tree
Changes from 43 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
f189843
adds README.md
evrom Feb 21, 2021
ceeb4a1
adds rules for `grid-template-areas`
evrom Feb 23, 2021
13fd708
remove comment from value before parsing grid
evrom Feb 23, 2021
b930a18
Do not use `matchAll`
evrom Feb 23, 2021
75cad76
use `_.flatten` instead of `Array.prototype.flat`
evrom Feb 23, 2021
60aac69
Update lib/rules/named-grid-areas-no-invalid/README.md
evrom Feb 23, 2021
90173e3
Update lib/rules/named-grid-areas-no-invalid/README.md
evrom Feb 23, 2021
3e76615
Update lib/rules/named-grid-areas-no-invalid/README.md
evrom Feb 23, 2021
061fb27
Update lib/rules/named-grid-areas-no-invalid/README.md
evrom Feb 23, 2021
9a0f36f
Update lib/rules/named-grid-areas-no-invalid/__tests__/index.js
evrom Feb 23, 2021
3581dca
use `a` tag in css examples
evrom Feb 23, 2021
0312bd8
use valueParser from 'postcss-value-parser'
evrom Feb 23, 2021
a973f20
move rule function above helper functions
evrom Feb 23, 2021
15c743d
adds use of validateOptions
evrom Feb 23, 2021
835e732
use correct columns
evrom Feb 23, 2021
8148fde
make message clearer
evrom Feb 23, 2021
5977236
use stripIndent and format testcase
evrom Feb 24, 2021
c4d30c1
do not continue testing if errors were reported
evrom Feb 24, 2021
e2045b0
avoid iterating over nodes multiple times
evrom Feb 24, 2021
9cb4f96
add test for multiple non-contiguous areas
evrom Feb 24, 2021
c1179a4
use `a` tag in all test cases
evrom Feb 24, 2021
7b9ad63
use tabs to indent css
evrom Feb 24, 2021
75e46d6
indent css strings one tab
evrom Feb 24, 2021
422bad3
adds test for `NONE` and `none` with comments
evrom Feb 24, 2021
676bd36
do not use value parser in simple case of `none`
evrom Feb 24, 2021
79091e9
remove testing of invalid symbols
evrom Feb 24, 2021
15edac8
use `complain()`
evrom Feb 24, 2021
fae6be4
adds one more test for `NONE`
evrom Feb 24, 2021
54e718d
changes warning messages
evrom Feb 24, 2021
ff23bb2
do not continue testing grid if non-rectangular
evrom Feb 24, 2021
60f87ab
make if statements more readable
evrom Feb 24, 2021
8ec63fa
Update lib/rules/named-grid-areas-no-invalid/__tests__/index.js
evrom Feb 25, 2021
e20dba0
Update lib/rules/named-grid-areas-no-invalid/__tests__/index.js
evrom Feb 25, 2021
9167731
Update lib/rules/named-grid-areas-no-invalid/__tests__/index.js
evrom Feb 25, 2021
5ce08a6
use object destructuring. `return` instead of...
evrom Feb 25, 2021
79be6b6
use destructuring
evrom Feb 25, 2021
5fe13b4
do not define const parsedValue
evrom Feb 25, 2021
35c67b8
check properties by case insensitive name
evrom Feb 25, 2021
e6e6992
move `complain()` to bottom of scope
evrom Feb 25, 2021
f8fd4c6
adds `named-grid-areas-no-invalid` rule to list...
evrom Feb 25, 2021
475f571
move utility functions out of index.js
evrom Mar 1, 2021
403cf68
adds tests to utils
evrom Mar 1, 2021
fd93b97
refractor utils
evrom Mar 1, 2021
224ffef
Update docs/user-guide/rules/list.md
evrom Mar 4, 2021
24ab53b
Update lib/rules/named-grid-areas-no-invalid/utils/isRectangular.js
evrom Mar 4, 2021
992a83e
Update lib/rules/named-grid-areas-no-invalid/README.md
evrom Mar 4, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/user-guide/rules/list.md
Expand Up @@ -17,6 +17,10 @@ Grouped first by the following categories and then by the [_thing_](http://apps.
- [`font-family-no-duplicate-names`](../../../lib/rules/font-family-no-duplicate-names/README.md): Disallow duplicate font family names.
- [`font-family-no-missing-generic-family-keyword`](../../../lib/rules/font-family-no-missing-generic-family-keyword/README.md): Disallow missing generic families in lists of font family names.

### Named Grid Areas
evrom marked this conversation as resolved.
Show resolved Hide resolved

- [`named-grid-areas-no-invalid`](../../../lib/rules/named-grid-areas-no-invalid/README.md): Disallow invalid named grid areas.

### Function

- [`function-calc-no-invalid`](../../../lib/rules/function-calc-no-invalid/README.md): Disallow an invalid expression within `calc` functions.
Expand Down
1 change: 1 addition & 0 deletions lib/rules/index.js
Expand Up @@ -231,6 +231,7 @@ const rules = {
'media-query-list-comma-space-before': importLazy(() =>
require('./media-query-list-comma-space-before'),
)(),
'named-grid-areas-no-invalid': importLazy(() => require('./named-grid-areas-no-invalid'))(),
'no-descending-specificity': importLazy(() => require('./no-descending-specificity'))(),
'no-duplicate-at-import-rules': importLazy(() => require('./no-duplicate-at-import-rules'))(),
'no-duplicate-selectors': importLazy(() => require('./no-duplicate-selectors'))(),
Expand Down
58 changes: 58 additions & 0 deletions lib/rules/named-grid-areas-no-invalid/README.md
@@ -0,0 +1,58 @@
# named-grid-areas-no-invalid

Disallow invalid named grid areas.

<!-- prettier-ignore -->
```css
a { grid-template-areas:
"a a a"
"b b b"; }
/** ↑
* This named grid area */
evrom marked this conversation as resolved.
Show resolved Hide resolved

## Options

### `true`

The following patterns are considered violations:

All strings must define the same number of cell tokens.

<!-- prettier-ignore -->
```css
a { grid-template-areas: "a a a"
"b b b b"; }
```

All strings must define at least one cell token.

<!-- prettier-ignore -->
```css
a { grid-template-areas: "" }
```

All named grid areas that spans multiple grid cells must form a single filled-in rectangle.

<!-- prettier-ignore -->
```css
a { grid-template-areas: "a a a"
"b b a"; }
```

The following patterns are _not_ considered violations:

<!-- prettier-ignore -->
```css
a { grid-template-areas: "a a a"
"b b b"; }
```

<!-- prettier-ignore -->
```css
a { grid-template-areas: "a a a" "b b b"; }
```

<!-- prettier-ignore -->
```css
a { grid-template-areas: none; }
```
138 changes: 138 additions & 0 deletions lib/rules/named-grid-areas-no-invalid/__tests__/index.js
@@ -0,0 +1,138 @@
'use strict';

const stripIndent = require('common-tags').stripIndent;
const { messages, ruleName } = require('..');

testRule({
ruleName,

config: [true],

accept: [
{
code: 'a { grid-template-areas: "a a a" "b b b"; }',
},
{
code: 'a { GRID-TEMPLATE-AREAS: "a a a" "b b b"; }',
},
{
code: stripIndent`
evrom marked this conversation as resolved.
Show resolved Hide resolved
a {
grid-template-areas: "head head"
evrom marked this conversation as resolved.
Show resolved Hide resolved
"nav main"
"nav foot";
}`,
},
{
code: stripIndent`
a {
grid-template-areas:
/* "" */ "head head"
"nav main" /* "a b c" */
"nav foot" /* "" */;
}`,
},
{
code: 'a { grid-template-areas: none; }',
},
{
code: 'a { grid-template-areas: none; }',
},
{
code: 'a { grid-template-areas: /*comment*/ none /* comment */; }',
},
{
code: 'a { grid-template-areas: /*comment*/ NONE /* comment */; }',
},
{
code: 'a { grid-template-areas: NONE; }',
},
{
code: 'a { grid-template-areas: /* comment "" " */ none; }',
},
{
code: stripIndent`
a {
grid-template-areas: /* "comment" */ none /* "comment " */;
}`,
},
],

reject: [
{
code: 'a { grid-template-areas: "a a a" "b b"; }',
message: messages.expectedSameNumber(),
line: 1,
column: 26,
},
{
code: 'a { grid-template-areas: "a a a" "b b a"; }',
message: messages.expectedRectangle('a'),
line: 1,
column: 26,
},
{
code: 'a { grid-template-areas: "a c a" "c b a"; }',
warnings: [
{ message: messages.expectedRectangle('a'), line: 1, column: 26 },
{ message: messages.expectedRectangle('c'), line: 1, column: 26 },
],
},
{
code: stripIndent`
a {
grid-template-areas: "header header header header"
"main main . sidebar"
"footer footer footer header";
}`,
message: messages.expectedRectangle('header'),
line: 2,
column: 23,
},
{
code: 'a { grid-template-areas: ""; }',
message: messages.expectedToken(),
line: 1,
column: 26,
},
{
code: 'a { grid-template-areas: /* "comment" */ ""; }',
message: messages.expectedToken(),
line: 1,
column: 42,
},
{
code: stripIndent`
a { grid-template-areas:
"";
}`,
message: messages.expectedToken(),
line: 2,
column: 4,
},
{
code: 'a { grid-template-areas: "" "" ""; }',
warnings: [
{ message: messages.expectedToken(), line: 1, column: 26 },
{ message: messages.expectedToken(), line: 1, column: 29 },
{ message: messages.expectedToken(), line: 1, column: 32 },
],
},
{
code: stripIndent`
a {
grid-template-areas: /* none */
"" /* none */;
}`,
message: messages.expectedToken(),
line: 3,
column: 4,
},
{
code: 'a { GRID-TEMPLATE-AREAS: ""; }',
message: messages.expectedToken(),
line: 1,
column: 26,
},
],
});
evrom marked this conversation as resolved.
Show resolved Hide resolved
80 changes: 80 additions & 0 deletions lib/rules/named-grid-areas-no-invalid/index.js
@@ -0,0 +1,80 @@
// @ts-nocheck

'use strict';

const _ = require('lodash');
const declarationValueIndex = require('../../utils/declarationValueIndex');
const findNotContiguousOrRectangular = require('./utils/findNotContiguousOrRectangular');
const isRectangular = require('./utils/isRectangular');
const report = require('../../utils/report');
const ruleMessages = require('../../utils/ruleMessages');
const validateOptions = require('../../utils/validateOptions');
const valueParser = require('postcss-value-parser');

const ruleName = 'named-grid-areas-no-invalid';

const messages = ruleMessages(ruleName, {
expectedToken: () => 'Expected cell token within string',
expectedSameNumber: () => 'Expected same number of cell tokens in each string',
expectedRectangle: (name) => `Expected single filled-in rectangle for "${name}"`,
});

function rule(actual) {
evrom marked this conversation as resolved.
Show resolved Hide resolved
return (root, result) => {
const validOptions = validateOptions(result, ruleName, { actual });

if (!validOptions) {
return;
}

root.walkDecls(/^grid-template-areas$/i, (decl) => {
const { value } = decl;

if (value.toLowerCase().trim() === 'none') return;

const areas = [];
let reportSent = false;

valueParser(value).walk(({ sourceIndex, type, value: tokenValue }) => {
if (type !== 'string') return;

if (tokenValue === '') {
complain(messages.expectedToken(), sourceIndex);
reportSent = true;

return;
}

areas.push(_.compact(tokenValue.trim().split(' ')));
});

if (reportSent) return;

if (!isRectangular(areas)) {
complain(messages.expectedSameNumber());
evrom marked this conversation as resolved.
Show resolved Hide resolved

return;
}

const notContiguousOrRectangular = findNotContiguousOrRectangular(areas);

notContiguousOrRectangular.sort().forEach((name) => {
complain(messages.expectedRectangle(name));
});

function complain(message, sourceIndex = 0) {
report({
evrom marked this conversation as resolved.
Show resolved Hide resolved
message,
node: decl,
index: declarationValueIndex(decl) + sourceIndex,
result,
ruleName,
});
}
});
};
}

rule.ruleName = ruleName;
rule.messages = messages;
module.exports = rule;
@@ -0,0 +1,35 @@
'use strict';

const findNotContiguousOrRectangular = require('../findNotContiguousOrRectangular');

describe('findNotContiguousOrRectangular finds', () => {
test('nothing', () => {
expect(
findNotContiguousOrRectangular([
['a', 'a', '.'],
['a', 'a', '.'],
['.', 'b', 'c'],
]),
).toEqual([]);
});

test('non-contiguous area', () => {
expect(
findNotContiguousOrRectangular([
['a', 'a', '.'],
['a', 'a', '.'],
['.', 'b', 'a'],
]),
).toEqual(['a']);
});

test('non-contiguous area when area is not in an adjacent row', () => {
expect(
findNotContiguousOrRectangular([
['header', 'header', 'header', 'header'],
['main', 'main', '.', 'sidebar'],
['footer', 'footer', 'footer', 'header'],
]),
).toEqual(['header']);
});
});
@@ -0,0 +1,25 @@
'use strict';

const isRectangular = require('../isRectangular');

describe('isRectangular is', () => {
test('true when rectangular', () => {
expect(
isRectangular([
['a', 'a', '.'],
['a', 'a', '.'],
['.', 'b', 'c'],
]),
).toEqual(true);
});

test('false when not-rectangular', () => {
expect(
isRectangular([
['a', 'a', '.'],
['a', 'a'],
['.', 'b', 'a'],
]),
).toEqual(false);
});
});