added all files to project

This commit is contained in:
2022-03-10 10:36:59 +01:00
parent 09dd957b33
commit 46a936d7de
25351 changed files with 3883356 additions and 0 deletions
+338
View File
@@ -0,0 +1,338 @@
# indentation
Specify indentation.
<!-- prettier-ignore -->
```css
|@media print {
| a {
| ↑ background-position: top left,
| ↑ ↑ top right;
| ↑}↑ ↑
|}↑ ↑ ↑
/** ↑ ↑ ↑
* The indentation at these three points */
```
The [`fix` option](../../../docs/user-guide/usage/options.md#fix) can automatically fix all of the problems reported by this rule.
## Options
`int|"tab"`, where `int` is the number of spaces
### `2`
Always indent at-rules, rules, comments, declarations, inside parentheses and multi-line values by 2 spaces.
The following patterns are considered violations:
<!-- prettier-ignore -->
```css
@media print {
a {
background-position: top left,
top right;
}
}
```
<!-- prettier-ignore -->
```css
@media print {
a {
background-position: top left,
top right;
}
}
```
<!-- prettier-ignore -->
```css
@media print {
a {
background-position: top left,
top right;
}
}
```
<!-- prettier-ignore -->
```css
@media print {
a,
b {
background-position: top left,
top right;
}
}
```
<!-- prettier-ignore -->
```css
a {
/* blergh */
color: pink;
}
/* blergh */
```
<!-- prettier-ignore -->
```css
@media print,
(-webkit-min-device-pixel-ratio: 1.25),
(min-resolution: 120dpi) {}
```
<!-- prettier-ignore -->
```css
a {
color: rgb(
255,
255,
255
);
top: 0;
}
```
The following patterns are _not_ considered violations:
<!-- prettier-ignore -->
```css
@media print {
a {
background-position: top left,
top right;
}
}
```
<!-- prettier-ignore -->
```css
@media print {
a,
b {
background-position: top left,
top right;
}
}
```
<!-- prettier-ignore -->
```css
a {
/* blergh */
color: pink;
}
/* blergh */
```
<!-- prettier-ignore -->
```css
@media print,
(-webkit-min-device-pixel-ratio: 1.25),
(min-resolution: 120dpi) {}
```
<!-- prettier-ignore -->
```css
a {
color: rgb(
255,
255,
255
);
top: 0;
}
```
## Optional secondary options
### `baseIndentLevel: int|"auto"`
By default, the indent level of the CSS code block in non-CSS-like files is determined by the shortest indent of non-empty line. The setting `baseIndentLevel` allows you to define a relative indent level based on CSS code block opening or closing line.
For example, with `[ 2, { baseIndentLevel: 1 } ]`, CSS should be indented 1 levels higher than `<style>` tag:
```html
<!DOCTYPE html>
<html lang="en">
<head>
<style>
a {
display: block;
}
</style>
</head>
</html>
```
### `indentInsideParens: "twice"|"once-at-root-twice-in-block"`
By default, _one extra_ indentation (of your specified type) is expected after newlines inside parentheses, and the closing parenthesis is expected to have no extra indentation.
If you would like to change the quantity of extra indentation inside parentheses, use this option.
`"twice"` means you expect two extra indentations (of your specified type) after newlines inside parentheses, and expect the closing parenthesis to have one extra indentation. For example:
<!-- prettier-ignore -->
```css
a {
color: rgb(
255,
255,
255
);
top: 0;
}
```
`"once-at-root-twice-in-block"` means two things: You want the behavior of `"once"`, as documented above, when the parenthetical expression is part of a node that is an immediate descendent of the root — i.e. not inside a block. And you want the behavior of `"twice"`, as documented above, when the parenthetical expression is part of a node that is inside a block. For example:
<!-- prettier-ignore -->
```css
@import (
"foo.css"
);
a {
color: rgb(
255,
255,
255
);
top: 0;
}
```
### `indentClosingBrace: true|false`
If `true`, the closing brace of a block (rule or at-rule) will be expected at the same indentation level as the block's inner nodes.
For example, with `indentClosingBrace: true`.
The following patterns are considered violations:
<!-- prettier-ignore -->
```css
a {
color: pink;
}
```
<!-- prettier-ignore -->
```css
@media print {
a {
color: pink;
}
}
```
The following patterns are _not_ considered violations:
<!-- prettier-ignore -->
```css
a {
color: pink;
}
```
<!-- prettier-ignore -->
```css
@media print {
a {
color: pink;
}
}
```
### `except: ["block", "param", "value"]`
Do _not_ indent for these things.
For example, with `2`.
The following patterns are considered violations:
<!-- prettier-ignore -->
```css
@media print,
(-webkit-min-device-pixel-ratio: 1.25),
(min-resolution: 120dpi) {
a {
background-position: top left,
top right;
}
}
```
The following patterns are _not_ considered violations:
<!-- prettier-ignore -->
```css
@media print,
(-webkit-min-device-pixel-ratio: 1.25),
(min-resolution: 120dpi) {
a {
background-position: top left,
top right;
}
}
```
### `ignore: ["inside-parens", "param", "value"]`
#### `"inside-parens"`
Ignore the indentation inside parentheses.
For example, with `2`.
The following patterns are _not_ considered violations:
<!-- prettier-ignore -->
```css
a {
color: rgb(
255,
255,
255
);
top: 0;
}
```
#### `"param"`
Ignore the indentation of at-rule params.
For example, with `2`.
The following patterns are _not_ considered violations:
<!-- prettier-ignore -->
```css
@media print,
(-webkit-min-device-pixel-ratio: 1.25),
(min-resolution: 120dpi) {
}
```
#### `"value"`
Ignore the indentation of values.
For example, with `2`.
The following patterns are _not_ considered violations:
<!-- prettier-ignore -->
```css
a {
background-position: top left,
top right,
bottom left,
bottom right;
}
```
+615
View File
@@ -0,0 +1,615 @@
// @ts-nocheck
'use strict';
const _ = require('lodash');
const beforeBlockString = require('../../utils/beforeBlockString');
const hasBlock = require('../../utils/hasBlock');
const optionsMatches = require('../../utils/optionsMatches');
const report = require('../../utils/report');
const ruleMessages = require('../../utils/ruleMessages');
const styleSearch = require('style-search');
const validateOptions = require('../../utils/validateOptions');
const ruleName = 'indentation';
const messages = ruleMessages(ruleName, {
expected: (x) => `Expected indentation of ${x}`,
});
/**
* @param {number|"tab"} space - Number of whitespaces to expect, or else
* keyword "tab" for single `\t`
* @param {object} [options]
*/
function rule(space, options = {}, context) {
const isTab = space === 'tab';
const indentChar = isTab ? '\t' : ' '.repeat(space);
const warningWord = isTab ? 'tab' : 'space';
return (root, result) => {
const validOptions = validateOptions(
result,
ruleName,
{
actual: space,
possible: [_.isNumber, 'tab'],
},
{
actual: options,
possible: {
baseIndentLevel: [_.isNumber, 'auto'],
except: ['block', 'value', 'param'],
ignore: ['value', 'param', 'inside-parens'],
indentInsideParens: ['twice', 'once-at-root-twice-in-block'],
indentClosingBrace: [_.isBoolean],
},
optional: true,
},
);
if (!validOptions) {
return;
}
// Cycle through all nodes using walk.
root.walk((node) => {
if (node.type === 'root') {
// Ignore nested template literals root in css-in-js lang
return;
}
const nodeLevel = indentationLevel(node);
// Cut out any * and _ hacks from `before`
const before = (node.raws.before || '').replace(/[*_]$/, '');
const after = node.raws.after || '';
const parent = node.parent;
const expectedOpeningBraceIndentation = indentChar.repeat(nodeLevel);
// Only inspect the spaces before the node
// if this is the first node in root
// or there is a newline in the `before` string.
// (If there is no newline before a node,
// there is no "indentation" to check.)
const isFirstChild = parent.type === 'root' && parent.first === node;
const lastIndexOfNewline = before.lastIndexOf('\n');
// Inspect whitespace in the `before` string that is
// *after* the *last* newline character,
// because anything besides that is not indentation for this node:
// it is some other kind of separation, checked by some separate rule
if (
(lastIndexOfNewline !== -1 ||
(isFirstChild && (!getDocument(parent) || parent.raws.beforeStart.endsWith('\n')))) &&
before.slice(lastIndexOfNewline + 1) !== expectedOpeningBraceIndentation
) {
if (context.fix) {
if (isFirstChild && _.isString(node.raws.before)) {
node.raws.before = node.raws.before.replace(
/^[ \t]*(?=\S|$)/,
expectedOpeningBraceIndentation,
);
}
node.raws.before = fixIndentation(node.raws.before, expectedOpeningBraceIndentation);
} else {
report({
message: messages.expected(legibleExpectation(nodeLevel)),
node,
result,
ruleName,
});
}
}
// Only blocks have the `after` string to check.
// Only inspect `after` strings that start with a newline;
// otherwise there's no indentation involved.
// And check `indentClosingBrace` to see if it should be indented an extra level.
const closingBraceLevel = options.indentClosingBrace ? nodeLevel + 1 : nodeLevel;
const expectedClosingBraceIndentation = indentChar.repeat(closingBraceLevel);
if (
hasBlock(node) &&
after &&
after.includes('\n') &&
after.slice(after.lastIndexOf('\n') + 1) !== expectedClosingBraceIndentation
) {
if (context.fix) {
node.raws.after = fixIndentation(node.raws.after, expectedClosingBraceIndentation);
} else {
report({
message: messages.expected(legibleExpectation(closingBraceLevel)),
node,
index: node.toString().length - 1,
result,
ruleName,
});
}
}
// If this is a declaration, check the value
if (node.value) {
checkValue(node, nodeLevel);
}
// If this is a rule, check the selector
if (node.selector) {
checkSelector(node, nodeLevel);
}
// If this is an at rule, check the params
if (node.type === 'atrule') {
checkAtRuleParams(node, nodeLevel);
}
});
function indentationLevel(node, level = 0) {
if (node.parent.type === 'root') {
return level + getRootBaseIndentLevel(node.parent, options.baseIndentLevel, space);
}
let calculatedLevel;
// Indentation level equals the ancestor nodes
// separating this node from root; so recursively
// run this operation
calculatedLevel = indentationLevel(node.parent, level + 1);
// If options.except includes "block",
// blocks are taken down one from their calculated level
// (all blocks are the same level as their parents)
if (
optionsMatches(options, 'except', 'block') &&
(node.type === 'rule' || node.type === 'atrule') &&
hasBlock(node)
) {
calculatedLevel--;
}
return calculatedLevel;
}
function checkValue(decl, declLevel) {
if (!decl.value.includes('\n')) {
return;
}
if (optionsMatches(options, 'ignore', 'value')) {
return;
}
const declString = decl.toString();
const valueLevel = optionsMatches(options, 'except', 'value') ? declLevel : declLevel + 1;
checkMultilineBit(declString, valueLevel, decl);
}
function checkSelector(ruleNode, ruleLevel) {
const selector = ruleNode.selector;
// Less mixins have params, and they should be indented extra
if (ruleNode.params) {
ruleLevel += 1;
}
checkMultilineBit(selector, ruleLevel, ruleNode);
}
function checkAtRuleParams(atRule, ruleLevel) {
if (optionsMatches(options, 'ignore', 'param')) {
return;
}
// @nest and SCSS's @at-root rules should be treated like regular rules, not expected
// to have their params (selectors) indented
const paramLevel =
optionsMatches(options, 'except', 'param') ||
atRule.name === 'nest' ||
atRule.name === 'at-root'
? ruleLevel
: ruleLevel + 1;
checkMultilineBit(beforeBlockString(atRule).trim(), paramLevel, atRule);
}
function checkMultilineBit(source, newlineIndentLevel, node) {
if (!source.includes('\n')) {
return;
}
// Data for current node fixing
const fixPositions = [];
// `outsideParens` because function arguments and also non-standard parenthesized stuff like
// Sass maps are ignored to allow for arbitrary indentation
let parentheticalDepth = 0;
styleSearch(
{
source,
target: '\n',
outsideParens: optionsMatches(options, 'ignore', 'inside-parens'),
},
(match, matchCount) => {
const precedesClosingParenthesis = /^[ \t]*\)/.test(source.slice(match.startIndex + 1));
if (
optionsMatches(options, 'ignore', 'inside-parens') &&
(precedesClosingParenthesis || match.insideParens)
) {
return;
}
let expectedIndentLevel = newlineIndentLevel;
// Modififications for parenthetical content
if (!optionsMatches(options, 'ignore', 'inside-parens') && match.insideParens) {
// If the first match in is within parentheses, reduce the parenthesis penalty
if (matchCount === 1) parentheticalDepth -= 1;
// Account for windows line endings
let newlineIndex = match.startIndex;
if (source[match.startIndex - 1] === '\r') {
newlineIndex--;
}
const followsOpeningParenthesis = /\([ \t]*$/.test(source.slice(0, newlineIndex));
if (followsOpeningParenthesis) {
parentheticalDepth += 1;
}
const followsOpeningBrace = /{[ \t]*$/.test(source.slice(0, newlineIndex));
if (followsOpeningBrace) {
parentheticalDepth += 1;
}
const startingClosingBrace = /^[ \t]*}/.test(source.slice(match.startIndex + 1));
if (startingClosingBrace) {
parentheticalDepth -= 1;
}
expectedIndentLevel += parentheticalDepth;
// Past this point, adjustments to parentheticalDepth affect next line
if (precedesClosingParenthesis) {
parentheticalDepth -= 1;
}
switch (options.indentInsideParens) {
case 'twice':
if (!precedesClosingParenthesis || options.indentClosingBrace) {
expectedIndentLevel += 1;
}
break;
case 'once-at-root-twice-in-block':
if (node.parent === node.root()) {
if (precedesClosingParenthesis && !options.indentClosingBrace) {
expectedIndentLevel -= 1;
}
break;
}
if (!precedesClosingParenthesis || options.indentClosingBrace) {
expectedIndentLevel += 1;
}
break;
default:
if (precedesClosingParenthesis && !options.indentClosingBrace) {
expectedIndentLevel -= 1;
}
}
}
// Starting at the index after the newline, we want to
// check that the whitespace characters (excluding newlines) before the first
// non-whitespace character equal the expected indentation
const afterNewlineSpaceMatches = /^([ \t]*)\S/.exec(source.slice(match.startIndex + 1));
if (!afterNewlineSpaceMatches) {
return;
}
const afterNewlineSpace = afterNewlineSpaceMatches[1];
const expectedIndentation = indentChar.repeat(
expectedIndentLevel > 0 ? expectedIndentLevel : 0,
);
if (afterNewlineSpace !== expectedIndentation) {
if (context.fix) {
// Adding fixes position in reverse order, because if we change indent in the beginning of the string it will break all following fixes for that string
fixPositions.unshift({
expectedIndentation,
currentIndentation: afterNewlineSpace,
startIndex: match.startIndex,
});
} else {
report({
message: messages.expected(legibleExpectation(expectedIndentLevel)),
node,
index: match.startIndex + afterNewlineSpace.length + 1,
result,
ruleName,
});
}
}
},
);
if (fixPositions.length) {
if (node.type === 'rule') {
fixPositions.forEach((fixPosition) => {
node.selector = replaceIndentation(
node.selector,
fixPosition.currentIndentation,
fixPosition.expectedIndentation,
fixPosition.startIndex,
);
});
}
if (node.type === 'decl') {
const declProp = node.prop;
const declBetween = node.raws.between;
fixPositions.forEach((fixPosition) => {
if (fixPosition.startIndex < declProp.length + declBetween.length) {
node.raws.between = replaceIndentation(
declBetween,
fixPosition.currentIndentation,
fixPosition.expectedIndentation,
fixPosition.startIndex - declProp.length,
);
} else {
node.value = replaceIndentation(
node.value,
fixPosition.currentIndentation,
fixPosition.expectedIndentation,
fixPosition.startIndex - declProp.length - declBetween.length,
);
}
});
}
if (node.type === 'atrule') {
const atRuleName = node.name;
const atRuleAfterName = node.raws.afterName;
const atRuleParams = node.params;
fixPositions.forEach((fixPosition) => {
// 1 — it's a @ length
if (fixPosition.startIndex < 1 + atRuleName.length + atRuleAfterName.length) {
node.raws.afterName = replaceIndentation(
atRuleAfterName,
fixPosition.currentIndentation,
fixPosition.expectedIndentation,
fixPosition.startIndex - atRuleName.length - 1,
);
} else {
node.params = replaceIndentation(
atRuleParams,
fixPosition.currentIndentation,
fixPosition.expectedIndentation,
fixPosition.startIndex - atRuleName.length - atRuleAfterName.length - 1,
);
}
});
}
}
}
};
function legibleExpectation(level) {
const count = isTab ? level : level * space;
const quantifiedWarningWord = count === 1 ? warningWord : `${warningWord}s`;
return `${count} ${quantifiedWarningWord}`;
}
}
function getRootBaseIndentLevel(root, baseIndentLevel, space) {
const document = getDocument(root);
if (!document) {
return 0;
}
let indentLevel = root.source.baseIndentLevel;
if (!Number.isSafeInteger(indentLevel)) {
indentLevel = inferRootIndentLevel(root, baseIndentLevel, () =>
inferDocIndentSize(document, space),
);
root.source.baseIndentLevel = indentLevel;
}
return indentLevel;
}
function getDocument(node) {
const document = node.document;
if (document) {
return document;
}
const root = node.root();
return root && root.document;
}
function inferDocIndentSize(document, space) {
let indentSize = document.source.indentSize;
if (Number.isSafeInteger(indentSize)) {
return indentSize;
}
const source = document.source.input.css;
const indents = source.match(/^ *(?=\S)/gm);
if (indents) {
const scores = {};
let lastIndentSize = 0;
let lastLeadingSpacesLength = 0;
const vote = (leadingSpacesLength) => {
if (leadingSpacesLength) {
lastIndentSize = Math.abs(leadingSpacesLength - lastLeadingSpacesLength) || lastIndentSize;
if (lastIndentSize > 1) {
if (scores[lastIndentSize]) {
scores[lastIndentSize]++;
} else {
scores[lastIndentSize] = 1;
}
}
} else {
lastIndentSize = 0;
}
lastLeadingSpacesLength = leadingSpacesLength;
};
indents.forEach((leadingSpaces) => {
vote(leadingSpaces.length);
});
let bestScore = 0;
for (const indentSizeDate in scores) {
if (Object.prototype.hasOwnProperty.call(scores, indentSizeDate)) {
const score = scores[indentSizeDate];
if (score > bestScore) {
bestScore = score;
indentSize = indentSizeDate;
}
}
}
}
indentSize = Number(indentSize) || (indents && indents[0].length) || Number(space) || 2;
document.source.indentSize = indentSize;
return indentSize;
}
function inferRootIndentLevel(root, baseIndentLevel, indentSize) {
function getIndentLevel(indent) {
let tabCount = indent.match(/\t/g);
tabCount = tabCount ? tabCount.length : 0;
let spaceCount = indent.match(/ /g);
spaceCount = spaceCount ? Math.round(spaceCount.length / indentSize()) : 0;
return tabCount + spaceCount;
}
if (!Number.isSafeInteger(baseIndentLevel)) {
let source = root.source.input.css;
source = source.replace(/^[^\r\n]+/, (firstLine) => {
if (/(?:^|\n)([ \t]*)$/.test(root.raws.beforeStart)) {
return RegExp.$1 + firstLine;
}
return '';
});
const indents = source.match(/^[ \t]*(?=\S)/gm);
if (indents) {
return Math.min(...indents.map(getIndentLevel));
}
baseIndentLevel = 1;
}
const indents = [];
const foundIndents = /(?:^|\n)([ \t]*)\S[^\r\n]*(?:\r?\n\s*)*$/m.exec(root.raws.beforeStart);
// The indent level of the CSS code block in non-CSS-like files is determined by the shortest indent of non-empty line.
if (foundIndents) {
let shortest = Number.MAX_SAFE_INTEGER;
let i = 0;
while (++i < foundIndents.length) {
const current = getIndentLevel(foundIndents[i]);
if (current < shortest) {
shortest = current;
if (shortest === 0) {
break;
}
}
}
if (shortest !== Number.MAX_SAFE_INTEGER) {
indents.push(new Array(shortest).fill(' ').join(''));
}
}
const after = root.raws.after;
if (after) {
let afterEnd;
if (after.endsWith('\n')) {
const document = root.document;
if (document) {
const nextRoot = document.nodes[document.nodes.indexOf(root) + 1];
afterEnd = nextRoot ? nextRoot.raws.beforeStart : document.raws.afterEnd;
} else {
// Nested root node in css-in-js lang
const parent = root.parent;
const nextRoot = parent.nodes[parent.nodes.indexOf(root) + 1];
afterEnd = nextRoot ? nextRoot.raws.beforeStart : root.raws.afterEnd;
}
} else {
afterEnd = after;
}
indents.push(afterEnd.match(/^[ \t]*/)[0]);
}
if (indents.length) {
return Math.max(...indents.map(getIndentLevel)) + baseIndentLevel;
}
return baseIndentLevel;
}
function fixIndentation(str, whitespace) {
if (!_.isString(str)) {
return str;
}
return str.replace(/\n[ \t]*(?=\S|$)/g, `\n${whitespace}`);
}
function replaceIndentation(input, searchString, replaceString, startIndex) {
const offset = startIndex + 1;
const stringStart = input.slice(0, offset);
const stringEnd = input.slice(offset + searchString.length);
return stringStart + replaceString + stringEnd;
}
rule.ruleName = ruleName;
rule.messages = messages;
module.exports = rule;