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 no-invalid-position-at-import-rule #5202

Merged
merged 10 commits into from Mar 26, 2021
1 change: 1 addition & 0 deletions docs/user-guide/rules/list.md
Expand Up @@ -79,6 +79,7 @@ Grouped first by the following categories and then by the [_thing_](http://apps.
- [`no-empty-source`](../../../lib/rules/no-empty-source/README.md): Disallow empty sources.
- [`no-extra-semicolons`](../../../lib/rules/no-extra-semicolons/README.md): Disallow extra semicolons (Autofixable).
- [`no-invalid-double-slash-comments`](../../../lib/rules/no-invalid-double-slash-comments/README.md): Disallow double-slash comments (`//...`) which are not supported by CSS.
- [`no-invalid-position-at-import-rule`](../../../lib/rules/no-invalid-position-at-import-rule/README.md): Disallow invalid position `@import` rules within a stylesheet.

## Limit language features

Expand Down
3 changes: 3 additions & 0 deletions lib/rules/index.js
Expand Up @@ -242,6 +242,9 @@ const rules = {
'no-invalid-double-slash-comments': importLazy(() =>
require('./no-invalid-double-slash-comments'),
)(),
'no-invalid-position-at-import-rule': importLazy(() =>
require('./no-invalid-position-at-import-rule'),
)(),
'no-missing-end-of-source-newline': importLazy(() =>
require('./no-missing-end-of-source-newline'),
)(),
Expand Down
51 changes: 51 additions & 0 deletions lib/rules/no-invalid-position-at-import-rule/README.md
@@ -0,0 +1,51 @@
# no-invalid-position-at-import-rule

Disallow invalid position `@import` rules within a stylesheet.

<!-- prettier-ignore -->
```css
a {}
@import 'foo.css';
/** ↑
* This @import */
```

chuuddo marked this conversation as resolved.
Show resolved Hide resolved
Any `@import` rules must precede all other valid at-rules and style rules in a stylesheet (ignoring `@charset`), or else the `@import` rule is invalid.

## Options

### `true`

The following patterns are considered violations:

<!-- prettier-ignore -->
```css
a {}
@import 'foo.css';
```

<!-- prettier-ignore -->
```css
@media print {}
@import 'foo.css';
```

The following patterns are _not_ considered violations:

<!-- prettier-ignore -->
```css
@import 'foo.css';
a {}
```

<!-- prettier-ignore -->
```css
/* some comment */
@import 'foo.css';
```

<!-- prettier-ignore -->
```css
@charset 'utf-8';
@import 'foo.css';
```
113 changes: 113 additions & 0 deletions lib/rules/no-invalid-position-at-import-rule/__tests__/index.js
@@ -0,0 +1,113 @@
'use strict';

const stripIndent = require('common-tags').stripIndent;

const { messages, ruleName } = require('..');

testRule({
ruleName,
config: [true],

accept: [
{
code: stripIndent`
@import 'foo.css';
a {}
`,
description: '@import on first line',
},
{
code: stripIndent`
/* some comment */
@import 'foo.css';
`,
description: '@import after comment',
},
{
code: stripIndent`
@charset 'utf-8';
@import 'foo.css';
`,
description: '@import after @charset ',
},
{
code: stripIndent`
@import 'foo.css';
@import 'bar.css';
`,
description: '@import after another @import',
},
{
code: stripIndent`
@CHARSET 'utf-8';
@imPORT 'foo.css';
@import 'bar.css';
`,
description: 'case insensitive',
},
],

reject: [
chuuddo marked this conversation as resolved.
Show resolved Hide resolved
{
code: stripIndent`
a {}
@import 'foo.css';
`,
message: messages.rejected,
description: '@import after selector',
line: 2,
column: 1,
},
{
code: stripIndent`
@media print {}
@import url('foo.css');
`,
message: messages.rejected,
description: '@import after another at-rule',
line: 2,
column: 1,
},
{
code: stripIndent`
@media print {}
@imPort URl('foo.css');
`,
message: messages.rejected,
description: 'case insensitive',
line: 2,
column: 1,
},
{
code: stripIndent`
@import 'foo.css';
a {}
@import 'bar.css';
`,
message: messages.rejected,
description: 'only second @import reported',
line: 3,
column: 1,
},
{
code: stripIndent`
a {}
@import 'foo.css';
@import 'bar.css';
`,
warnings: [
{
message: messages.rejected,
line: 2,
column: 1,
},
{
message: messages.rejected,
line: 3,
column: 1,
},
],
description: 'all @import reported',
},
chuuddo marked this conversation as resolved.
Show resolved Hide resolved
],
});
52 changes: 52 additions & 0 deletions lib/rules/no-invalid-position-at-import-rule/index.js
@@ -0,0 +1,52 @@
// @ts-nocheck

'use strict';

const report = require('../../utils/report');
const ruleMessages = require('../../utils/ruleMessages');
const validateOptions = require('../../utils/validateOptions');

const ruleName = 'no-invalid-position-at-import-rule';

const messages = ruleMessages(ruleName, {
rejected: 'Unexpected invalid position @import rule',
});

function rule(actual) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, { actual });

if (!validOptions) {
return;
}

let invalidPosition = false;

root.walk((node) => {
const nodeName = node.name && node.name.toLowerCase();

if (node.type === 'comment' || (node.type === 'atrule' && nodeName === 'charset')) {
return;
}

if (node.type === 'atrule' && nodeName === 'import') {
if (invalidPosition) {
report({
message: messages.rejected,
node,
result,
ruleName,
});
}

return;
}

invalidPosition = true;
});
};
}

rule.ruleName = ruleName;
rule.messages = messages;
module.exports = rule;