Permalink
Cannot retrieve contributors at this time
321 lines (281 sloc)
11.4 KB
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
codeql-action/node_modules/eslint/lib/rules/object-curly-newline.js
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @fileoverview Rule to require or disallow line breaks inside braces. | |
* @author Toru Nagashima | |
*/ | |
"use strict"; | |
//------------------------------------------------------------------------------ | |
// Requirements | |
//------------------------------------------------------------------------------ | |
const astUtils = require("./utils/ast-utils"); | |
//------------------------------------------------------------------------------ | |
// Helpers | |
//------------------------------------------------------------------------------ | |
// Schema objects. | |
const OPTION_VALUE = { | |
oneOf: [ | |
{ | |
enum: ["always", "never"] | |
}, | |
{ | |
type: "object", | |
properties: { | |
multiline: { | |
type: "boolean" | |
}, | |
minProperties: { | |
type: "integer", | |
minimum: 0 | |
}, | |
consistent: { | |
type: "boolean" | |
} | |
}, | |
additionalProperties: false, | |
minProperties: 1 | |
} | |
] | |
}; | |
/** | |
* Normalizes a given option value. | |
* @param {string|Object|undefined} value An option value to parse. | |
* @returns {{multiline: boolean, minProperties: number, consistent: boolean}} Normalized option object. | |
*/ | |
function normalizeOptionValue(value) { | |
let multiline = false; | |
let minProperties = Number.POSITIVE_INFINITY; | |
let consistent = false; | |
if (value) { | |
if (value === "always") { | |
minProperties = 0; | |
} else if (value === "never") { | |
minProperties = Number.POSITIVE_INFINITY; | |
} else { | |
multiline = Boolean(value.multiline); | |
minProperties = value.minProperties || Number.POSITIVE_INFINITY; | |
consistent = Boolean(value.consistent); | |
} | |
} else { | |
consistent = true; | |
} | |
return { multiline, minProperties, consistent }; | |
} | |
/** | |
* Checks if a value is an object. | |
* @param {any} value The value to check | |
* @returns {boolean} `true` if the value is an object, otherwise `false` | |
*/ | |
function isObject(value) { | |
return typeof value === "object" && value !== null; | |
} | |
/** | |
* Checks if an option is a node-specific option | |
* @param {any} option The option to check | |
* @returns {boolean} `true` if the option is node-specific, otherwise `false` | |
*/ | |
function isNodeSpecificOption(option) { | |
return isObject(option) || typeof option === "string"; | |
} | |
/** | |
* Normalizes a given option value. | |
* @param {string|Object|undefined} options An option value to parse. | |
* @returns {{ | |
* ObjectExpression: {multiline: boolean, minProperties: number, consistent: boolean}, | |
* ObjectPattern: {multiline: boolean, minProperties: number, consistent: boolean}, | |
* ImportDeclaration: {multiline: boolean, minProperties: number, consistent: boolean}, | |
* ExportNamedDeclaration : {multiline: boolean, minProperties: number, consistent: boolean} | |
* }} Normalized option object. | |
*/ | |
function normalizeOptions(options) { | |
if (isObject(options) && Object.values(options).some(isNodeSpecificOption)) { | |
return { | |
ObjectExpression: normalizeOptionValue(options.ObjectExpression), | |
ObjectPattern: normalizeOptionValue(options.ObjectPattern), | |
ImportDeclaration: normalizeOptionValue(options.ImportDeclaration), | |
ExportNamedDeclaration: normalizeOptionValue(options.ExportDeclaration) | |
}; | |
} | |
const value = normalizeOptionValue(options); | |
return { ObjectExpression: value, ObjectPattern: value, ImportDeclaration: value, ExportNamedDeclaration: value }; | |
} | |
/** | |
* Determines if ObjectExpression, ObjectPattern, ImportDeclaration or ExportNamedDeclaration | |
* node needs to be checked for missing line breaks | |
* @param {ASTNode} node Node under inspection | |
* @param {Object} options option specific to node type | |
* @param {Token} first First object property | |
* @param {Token} last Last object property | |
* @returns {boolean} `true` if node needs to be checked for missing line breaks | |
*/ | |
function areLineBreaksRequired(node, options, first, last) { | |
let objectProperties; | |
if (node.type === "ObjectExpression" || node.type === "ObjectPattern") { | |
objectProperties = node.properties; | |
} else { | |
// is ImportDeclaration or ExportNamedDeclaration | |
objectProperties = node.specifiers | |
.filter(s => s.type === "ImportSpecifier" || s.type === "ExportSpecifier"); | |
} | |
return objectProperties.length >= options.minProperties || | |
( | |
options.multiline && | |
objectProperties.length > 0 && | |
first.loc.start.line !== last.loc.end.line | |
); | |
} | |
//------------------------------------------------------------------------------ | |
// Rule Definition | |
//------------------------------------------------------------------------------ | |
/** @type {import('../shared/types').Rule} */ | |
module.exports = { | |
meta: { | |
type: "layout", | |
docs: { | |
description: "Enforce consistent line breaks after opening and before closing braces", | |
recommended: false, | |
url: "https://eslint.org/docs/latest/rules/object-curly-newline" | |
}, | |
fixable: "whitespace", | |
schema: [ | |
{ | |
oneOf: [ | |
OPTION_VALUE, | |
{ | |
type: "object", | |
properties: { | |
ObjectExpression: OPTION_VALUE, | |
ObjectPattern: OPTION_VALUE, | |
ImportDeclaration: OPTION_VALUE, | |
ExportDeclaration: OPTION_VALUE | |
}, | |
additionalProperties: false, | |
minProperties: 1 | |
} | |
] | |
} | |
], | |
messages: { | |
unexpectedLinebreakBeforeClosingBrace: "Unexpected line break before this closing brace.", | |
unexpectedLinebreakAfterOpeningBrace: "Unexpected line break after this opening brace.", | |
expectedLinebreakBeforeClosingBrace: "Expected a line break before this closing brace.", | |
expectedLinebreakAfterOpeningBrace: "Expected a line break after this opening brace." | |
} | |
}, | |
create(context) { | |
const sourceCode = context.sourceCode; | |
const normalizedOptions = normalizeOptions(context.options[0]); | |
/** | |
* Reports a given node if it violated this rule. | |
* @param {ASTNode} node A node to check. This is an ObjectExpression, ObjectPattern, ImportDeclaration or ExportNamedDeclaration node. | |
* @returns {void} | |
*/ | |
function check(node) { | |
const options = normalizedOptions[node.type]; | |
if ( | |
(node.type === "ImportDeclaration" && | |
!node.specifiers.some(specifier => specifier.type === "ImportSpecifier")) || | |
(node.type === "ExportNamedDeclaration" && | |
!node.specifiers.some(specifier => specifier.type === "ExportSpecifier")) | |
) { | |
return; | |
} | |
const openBrace = sourceCode.getFirstToken(node, token => token.value === "{"); | |
let closeBrace; | |
if (node.typeAnnotation) { | |
closeBrace = sourceCode.getTokenBefore(node.typeAnnotation); | |
} else { | |
closeBrace = sourceCode.getLastToken(node, token => token.value === "}"); | |
} | |
let first = sourceCode.getTokenAfter(openBrace, { includeComments: true }); | |
let last = sourceCode.getTokenBefore(closeBrace, { includeComments: true }); | |
const needsLineBreaks = areLineBreaksRequired(node, options, first, last); | |
const hasCommentsFirstToken = astUtils.isCommentToken(first); | |
const hasCommentsLastToken = astUtils.isCommentToken(last); | |
/* | |
* Use tokens or comments to check multiline or not. | |
* But use only tokens to check whether line breaks are needed. | |
* This allows: | |
* var obj = { // eslint-disable-line foo | |
* a: 1 | |
* } | |
*/ | |
first = sourceCode.getTokenAfter(openBrace); | |
last = sourceCode.getTokenBefore(closeBrace); | |
if (needsLineBreaks) { | |
if (astUtils.isTokenOnSameLine(openBrace, first)) { | |
context.report({ | |
messageId: "expectedLinebreakAfterOpeningBrace", | |
node, | |
loc: openBrace.loc, | |
fix(fixer) { | |
if (hasCommentsFirstToken) { | |
return null; | |
} | |
return fixer.insertTextAfter(openBrace, "\n"); | |
} | |
}); | |
} | |
if (astUtils.isTokenOnSameLine(last, closeBrace)) { | |
context.report({ | |
messageId: "expectedLinebreakBeforeClosingBrace", | |
node, | |
loc: closeBrace.loc, | |
fix(fixer) { | |
if (hasCommentsLastToken) { | |
return null; | |
} | |
return fixer.insertTextBefore(closeBrace, "\n"); | |
} | |
}); | |
} | |
} else { | |
const consistent = options.consistent; | |
const hasLineBreakBetweenOpenBraceAndFirst = !astUtils.isTokenOnSameLine(openBrace, first); | |
const hasLineBreakBetweenCloseBraceAndLast = !astUtils.isTokenOnSameLine(last, closeBrace); | |
if ( | |
(!consistent && hasLineBreakBetweenOpenBraceAndFirst) || | |
(consistent && hasLineBreakBetweenOpenBraceAndFirst && !hasLineBreakBetweenCloseBraceAndLast) | |
) { | |
context.report({ | |
messageId: "unexpectedLinebreakAfterOpeningBrace", | |
node, | |
loc: openBrace.loc, | |
fix(fixer) { | |
if (hasCommentsFirstToken) { | |
return null; | |
} | |
return fixer.removeRange([ | |
openBrace.range[1], | |
first.range[0] | |
]); | |
} | |
}); | |
} | |
if ( | |
(!consistent && hasLineBreakBetweenCloseBraceAndLast) || | |
(consistent && !hasLineBreakBetweenOpenBraceAndFirst && hasLineBreakBetweenCloseBraceAndLast) | |
) { | |
context.report({ | |
messageId: "unexpectedLinebreakBeforeClosingBrace", | |
node, | |
loc: closeBrace.loc, | |
fix(fixer) { | |
if (hasCommentsLastToken) { | |
return null; | |
} | |
return fixer.removeRange([ | |
last.range[1], | |
closeBrace.range[0] | |
]); | |
} | |
}); | |
} | |
} | |
} | |
return { | |
ObjectExpression: check, | |
ObjectPattern: check, | |
ImportDeclaration: check, | |
ExportNamedDeclaration: check | |
}; | |
} | |
}; |