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

Backport implementation of satisfies #13786

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 4 additions & 4 deletions package.json
Expand Up @@ -24,10 +24,10 @@
"dependencies": {
"@angular/compiler": "12.2.16",
"@babel/code-frame": "7.16.7",
"@babel/parser": "7.18.0",
"@babel/parser": "7.20.1",
"@glimmer/syntax": "0.84.2",
"@iarna/toml": "2.2.5",
"@typescript-eslint/typescript-estree": "5.30.0",
"@typescript-eslint/typescript-estree": "5.36.2",
"acorn": "8.8.0",
"acorn-jsx": "5.3.2",
"angular-estree-parser": "2.5.1",
Expand Down Expand Up @@ -85,7 +85,7 @@
"semver": "7.3.7",
"string-width": "5.0.1",
"strip-ansi": "7.0.1",
"typescript": "4.7.2",
"typescript": "4.8.2",
"unicode-regex": "3.0.0",
"unified": "9.2.1",
"vnopts": "1.0.2",
Expand All @@ -102,7 +102,7 @@
"@types/file-entry-cache": "5.0.2",
"@types/find-cache-dir": "3.2.1",
"@types/jest": "27.4.1",
"@typescript-eslint/eslint-plugin": "5.20.0",
"@typescript-eslint/eslint-plugin": "5.36.2",
"babel-jest": "27.5.1",
"benchmark": "2.1.4",
"browserslist-to-esbuild": "1.1.1",
Expand Down
16 changes: 6 additions & 10 deletions src/language-js/loc.js
Expand Up @@ -6,20 +6,16 @@ const isNonEmptyArray = require("../utils/is-non-empty-array.js");
* @typedef {import("./types/estree").Node} Node
*/

function locStart(node, opts) {
const { ignoreDecorators } = opts || {};
function locStart(node) {
const start = node.range ? node.range[0] : node.start;

// Handle nodes with decorators. They should start at the first decorator
if (!ignoreDecorators) {
const decorators =
(node.declaration && node.declaration.decorators) || node.decorators;

if (isNonEmptyArray(decorators)) {
return locStart(decorators[0]);
}
const decorators = node.declaration?.decorators ?? node.decorators;
if (isNonEmptyArray(decorators)) {
return Math.min(locStart(decorators[0]), start);
}

return node.range ? node.range[0] : node.start;
return start;
}

function locEnd(node) {
Expand Down
14 changes: 10 additions & 4 deletions src/language-js/needs-parens.js
Expand Up @@ -15,6 +15,7 @@ const {
isCallExpression,
isMemberExpression,
isObjectProperty,
isTSTypeExpression,
} = require("./utils/index.js");

function needsParens(path, options) {
Expand Down Expand Up @@ -244,14 +245,16 @@ function needsParens(path, options) {
// fallthrough
case "TSTypeAssertion":
case "TSAsExpression":
case "TSSatisfiesExpression":
case "LogicalExpression":
switch (parent.type) {
case "TSSatisfiesExpression":
case "TSAsExpression":
// example: foo as unknown as Bar
return node.type !== "TSAsExpression";
return !isTSTypeExpression(node);

case "ConditionalExpression":
return node.type === "TSAsExpression";
return isTSTypeExpression(node);

case "CallExpression":
case "NewExpression":
Expand Down Expand Up @@ -282,7 +285,7 @@ function needsParens(path, options) {
case "AssignmentPattern":
return (
name === "left" &&
(node.type === "TSTypeAssertion" || node.type === "TSAsExpression")
(node.type === "TSTypeAssertion" || isTSTypeExpression(node))
);

case "LogicalExpression":
Expand Down Expand Up @@ -363,7 +366,7 @@ function needsParens(path, options) {
if (
parent.type === "UnaryExpression" ||
parent.type === "AwaitExpression" ||
parent.type === "TSAsExpression" ||
isTSTypeExpression(parent) ||
parent.type === "TSNonNullExpression"
) {
return true;
Expand All @@ -377,6 +380,7 @@ function needsParens(path, options) {
case "SpreadElement":
case "SpreadProperty":
case "TSAsExpression":
case "TSSatisfiesExpression":
case "TSNonNullExpression":
case "BindExpression":
return true;
Expand Down Expand Up @@ -612,6 +616,7 @@ function needsParens(path, options) {
case "TSTypeAssertion":
case "TypeCastExpression":
case "TSAsExpression":
case "TSSatisfiesExpression":
case "TSNonNullExpression":
return true;

Expand Down Expand Up @@ -661,6 +666,7 @@ function needsParens(path, options) {
return name === "object";

case "TSAsExpression":
case "TSSatisfiesExpression":
case "TSNonNullExpression":
case "BindExpression":
case "TaggedTemplateExpression":
Expand Down
47 changes: 25 additions & 22 deletions src/language-js/parse/postprocess/typescript.js
@@ -1,33 +1,36 @@
"use strict";

const isNonEmptyArray = require("../../../utils/is-non-empty-array.js");
const visitNode = require("./visit-node.js");
const throwSyntaxError = require("./throw-syntax-error.js");

// Copied from https://unpkg.com/typescript@4.8.2/lib/typescript.js
function getSourceFileOfNode(node) {
while (node && node.kind !== 305 /* SyntaxKind.SourceFile */) {
node = node.parent;
}
return node;
}

// Invalid decorators are removed since `@typescript-eslint/typescript-estree` v4
// https://github.com/typescript-eslint/typescript-eslint/pull/2375
function throwErrorForInvalidDecorator(
tsNode,
esTreeNode,
tsNodeToESTreeNodeMap
) {
const tsDecorators = tsNode.decorators;
if (!Array.isArray(tsDecorators)) {
// There is a `checkGrammarDecorators` in `typescript` package, consider use it directly in future
function throwErrorForInvalidDecorator(tsNode) {
const { illegalDecorators } = tsNode;
if (!isNonEmptyArray(illegalDecorators)) {
return;
}
const esTreeDecorators = esTreeNode.decorators;
if (
!Array.isArray(esTreeDecorators) ||
esTreeDecorators.length !== tsDecorators.length ||
tsDecorators.some((tsDecorator) => {
const esTreeDecorator = tsNodeToESTreeNodeMap.get(tsDecorator);
return !esTreeDecorator || !esTreeDecorators.includes(esTreeDecorator);
})
) {
throwSyntaxError(
esTreeNode,
"Leading decorators must be attached to a class declaration"
);
}

const [{ expression }] = illegalDecorators;

const sourceFile = getSourceFileOfNode(expression);
const [start, end] = [expression.pos, expression.end].map((position) => {
const { line, character: column } =
sourceFile.getLineAndCharacterOfPosition(position);
return { line: line + 1, column };
});

throwSyntaxError({ loc: { start, end } }, "Decorators are not valid here.");
}

// Values of abstract property is removed since `@typescript-eslint/typescript-estree` v5
Expand Down Expand Up @@ -66,7 +69,7 @@ function throwErrorForInvalidNodes(ast, options) {
return;
}

throwErrorForInvalidDecorator(tsNode, esTreeNode, tsNodeToESTreeNodeMap);
throwErrorForInvalidDecorator(tsNode);
throwErrorForInvalidAbstractProperty(tsNode, esTreeNode);
});
}
Expand Down
3 changes: 2 additions & 1 deletion src/language-js/print/call-arguments.js
Expand Up @@ -16,6 +16,7 @@ const {
isCallExpression,
isStringLiteral,
isObjectProperty,
isTSTypeExpression,
} = require("../utils/index.js");

const {
Expand Down Expand Up @@ -190,7 +191,7 @@ function couldGroupArg(arg, arrowChainRecursion = false) {
(arg.type === "ArrayExpression" &&
(arg.elements.length > 0 || hasComment(arg))) ||
(arg.type === "TSTypeAssertion" && couldGroupArg(arg.expression)) ||
(arg.type === "TSAsExpression" && couldGroupArg(arg.expression)) ||
(isTSTypeExpression(arg) && couldGroupArg(arg.expression)) ||
arg.type === "FunctionExpression" ||
(arg.type === "ArrowFunctionExpression" &&
// we want to avoid breaking inside composite return types but not simple keywords
Expand Down
3 changes: 1 addition & 2 deletions src/language-js/print/decorators.js
Expand Up @@ -72,8 +72,7 @@ function hasDecoratorsBeforeExport(node) {
const decorators = node.declaration && node.declaration.decorators;

return (
isNonEmptyArray(decorators) &&
locStart(node, { ignoreDecorators: true }) > locStart(decorators[0])
isNonEmptyArray(decorators) && locStart(node) === locStart(decorators[0])
);
}

Expand Down
3 changes: 2 additions & 1 deletion src/language-js/print/template-literal.js
Expand Up @@ -22,6 +22,7 @@ const {
isSimpleTemplateLiteral,
hasComment,
isMemberExpression,
isTSTypeExpression,
} = require("../utils/index.js");

function printTemplateLiteral(path, print, options) {
Expand Down Expand Up @@ -90,7 +91,7 @@ function printTemplateLiteral(path, print, options) {
isMemberExpression(expression) ||
expression.type === "ConditionalExpression" ||
expression.type === "SequenceExpression" ||
expression.type === "TSAsExpression" ||
isTSTypeExpression(expression) ||
isBinaryish(expression)
) {
printed = [indent([softline, printed]), softline];
Expand Down
3 changes: 2 additions & 1 deletion src/language-js/print/ternary.js
Expand Up @@ -6,6 +6,7 @@ const {
getComments,
isCallExpression,
isMemberExpression,
isTSTypeExpression,
} = require("../utils/index.js");
const { locStart, locEnd } = require("../loc.js");
const isBlockComment = require("../utils/is-block-comment.js");
Expand Down Expand Up @@ -165,7 +166,7 @@ function shouldExtraIndentForConditionalExpression(path) {

if (
(node.type === "NewExpression" && node.callee === child) ||
(node.type === "TSAsExpression" && node.expression === child)
(isTSTypeExpression(node) && node.expression === child)
) {
parent = path.getParentNode(ancestorCount + 1);
child = node;
Expand Down
4 changes: 3 additions & 1 deletion src/language-js/print/typescript.js
Expand Up @@ -148,8 +148,10 @@ function printTypescript(path, options, print) {
return printTypeParameters(path, options, print, "params");
case "TSTypeParameter":
return printTypeParameter(path, options, print);
case "TSSatisfiesExpression":
case "TSAsExpression": {
parts.push(print("expression"), " as ", print("typeAnnotation"));
const operator = node.type === "TSAsExpression" ? "as" : "satisfies";
parts.push(print("expression"), ` ${operator} `, print("typeAnnotation"));
const parent = path.getParentNode();
if (
(isCallExpression(parent) && parent.callee === node) ||
Expand Down
11 changes: 10 additions & 1 deletion src/language-js/utils/index.js
Expand Up @@ -106,7 +106,7 @@ function hasNakedLeftSide(node) {
node.type === "TaggedTemplateExpression" ||
node.type === "BindExpression" ||
(node.type === "UpdateExpression" && !node.prefix) ||
node.type === "TSAsExpression" ||
isTSTypeExpression(node) ||
node.type === "TSNonNullExpression"
);
}
Expand Down Expand Up @@ -998,6 +998,8 @@ function startsWithNoLookaheadToken(node, forbidFunctionClassAndDoExpr) {
node.expressions[0],
forbidFunctionClassAndDoExpr
);
// @ts-expect-error
case "TSSatisfiesExpression":
case "TSAsExpression":
case "TSNonNullExpression":
return startsWithNoLookaheadToken(
Expand Down Expand Up @@ -1305,6 +1307,12 @@ const markerForIfWithoutBlockAndSameLineComment = Symbol(
"ifWithoutBlockAndSameLineComment"
);

function isTSTypeExpression(node) {
return (
node.type === "TSAsExpression" || node.type === "TSSatisfiesExpression"
);
}

module.exports = {
getFunctionParameters,
iterateFunctionParametersPath,
Expand Down Expand Up @@ -1369,4 +1377,5 @@ module.exports = {
getComments,
CommentCheckFlags,
markerForIfWithoutBlockAndSameLineComment,
isTSTypeExpression,
};