Add support for flow typecast comments (#5280)
* Add support for flow typecast comments * Allow whitespace between comment start and colon Also rename flow-comments.js to utils.js * fix bug where type annotations could be accidentally created * use better regex in detecting flow comment whitespace * fix comment in utils.js * simplify conditionals and improve comments * fix lint errormaster
parent
559831d08e
commit
4d4fab39fe
|
@ -4,6 +4,7 @@ const assert = require("assert");
|
|||
|
||||
const util = require("../common/util");
|
||||
const comments = require("./comments");
|
||||
const { hasFlowShorthandAnnotationComment } = require("./utils");
|
||||
|
||||
function hasClosureCompilerTypeCastComment(text, path, locStart, locEnd) {
|
||||
// https://github.com/google/closure-compiler/wiki/Annotating-Types#type-casts
|
||||
|
@ -74,6 +75,16 @@ function needsParens(path, options) {
|
|||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
// Preserve parens if we have a Flow annotation comment, unless we're using the Flow
|
||||
// parser. The Flow parser turns Flow comments into type annotation nodes in its
|
||||
// AST, which we handle separately.
|
||||
options.parser !== "flow" &&
|
||||
hasFlowShorthandAnnotationComment(path.getValue())
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Identifiers never need parentheses.
|
||||
if (node.type === "Identifier") {
|
||||
return false;
|
||||
|
|
|
@ -34,6 +34,7 @@ const insertPragma = require("./pragma").insertPragma;
|
|||
const handleComments = require("./comments");
|
||||
const pathNeedsParens = require("./needs-parens");
|
||||
const preprocess = require("./preprocess");
|
||||
const { hasFlowShorthandAnnotationComment } = require("./utils");
|
||||
|
||||
const {
|
||||
builders: {
|
||||
|
@ -166,6 +167,14 @@ function genericPrint(path, options, printPath, args) {
|
|||
parts.push(linesWithoutParens);
|
||||
|
||||
if (needsParens) {
|
||||
const node = path.getValue();
|
||||
if (hasFlowShorthandAnnotationComment(node)) {
|
||||
parts.push(" /*");
|
||||
parts.push(node.trailingComments[0].value.trimLeft());
|
||||
parts.push("*/");
|
||||
node.trailingComments[0].printed = true;
|
||||
}
|
||||
|
||||
parts.push(")");
|
||||
}
|
||||
|
||||
|
@ -2836,14 +2845,36 @@ function printPathNoParens(path, options, print, args) {
|
|||
|
||||
return group(concat(parts));
|
||||
}
|
||||
case "TypeCastExpression":
|
||||
case "TypeCastExpression": {
|
||||
const value = path.getValue();
|
||||
// Flow supports a comment syntax for specifying type annotations: https://flow.org/en/docs/types/comments/.
|
||||
// Unfortunately, its parser doesn't differentiate between comment annotations and regular
|
||||
// annotations when producing an AST. So to preserve parentheses around type casts that use
|
||||
// the comment syntax, we need to hackily read the source itself to see if the code contains
|
||||
// a type annotation comment.
|
||||
//
|
||||
// Note that we're able to use the normal whitespace regex here because the Flow parser has
|
||||
// already deemed this AST node to be a type cast. Only the Babylon parser needs the
|
||||
// non-line-break whitespace regex, which is why hasFlowShorthandAnnotationComment() is
|
||||
// implemented differently.
|
||||
const commentSyntax =
|
||||
value &&
|
||||
value.typeAnnotation &&
|
||||
value.typeAnnotation.range &&
|
||||
options.originalText
|
||||
.substring(value.typeAnnotation.range[0])
|
||||
.match(/^\/\*\s*:/);
|
||||
return concat([
|
||||
"(",
|
||||
path.call(print, "expression"),
|
||||
commentSyntax ? " /*" : "",
|
||||
": ",
|
||||
path.call(print, "typeAnnotation"),
|
||||
commentSyntax ? " */" : "",
|
||||
")"
|
||||
]);
|
||||
}
|
||||
|
||||
case "TypeParameterDeclaration":
|
||||
case "TypeParameterInstantiation":
|
||||
case "TSTypeParameterDeclaration":
|
||||
|
@ -5994,7 +6025,7 @@ function willPrintOwnComments(path) {
|
|||
const parent = path.getParentNode();
|
||||
|
||||
return (
|
||||
((node && isJSXNode(node)) ||
|
||||
((node && (isJSXNode(node) || hasFlowShorthandAnnotationComment(node))) ||
|
||||
(parent &&
|
||||
(parent.type === "JSXSpreadAttribute" ||
|
||||
parent.type === "JSXSpreadChild" ||
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
"use strict";
|
||||
|
||||
function hasFlowShorthandAnnotationComment(node) {
|
||||
// https://flow.org/en/docs/types/comments/
|
||||
// Syntax example: const r = new (window.Request /*: Class<Request> */)("");
|
||||
|
||||
return (
|
||||
node.extra &&
|
||||
node.extra.parenthesized &&
|
||||
node.trailingComments &&
|
||||
// We match any whitespace except line terminators because
|
||||
// Flow annotation comments cannot be split across lines. For example:
|
||||
//
|
||||
// (this /*
|
||||
// : any */).foo = 5;
|
||||
//
|
||||
// is not picked up by Flow (see https://github.com/facebook/flow/issues/7050), so
|
||||
// removing the newline would create a type annotation that the user did not intend
|
||||
// to create.
|
||||
node.trailingComments[0].value.match(/^(?:(?=.)\s)*:/)
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
hasFlowShorthandAnnotationComment
|
||||
};
|
|
@ -85,3 +85,32 @@ type Props = {
|
|||
};
|
||||
|
||||
`;
|
||||
|
||||
exports[`type_annotations.js - flow-verify 1`] = `
|
||||
new (window.Request/*: Class<Request> */)("");
|
||||
|
||||
(this/*: any */).foo = 5;
|
||||
|
||||
(this/* : any */).foo = 5;
|
||||
|
||||
// This next example illustrates that Prettier will not remove a line break
|
||||
// and unintentionally create a type annotation that was not there before.
|
||||
(this/*
|
||||
: any */).foo = 5;
|
||||
|
||||
const x = (input /*: string */) /*: string */ => {};
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
new (window.Request /*: Class<Request> */)("");
|
||||
|
||||
(this /*: any */).foo = 5;
|
||||
|
||||
(this /*: any */).foo = 5;
|
||||
|
||||
// This next example illustrates that Prettier will not remove a line break
|
||||
// and unintentionally create a type annotation that was not there before.
|
||||
this /*
|
||||
: any */.foo = 5;
|
||||
|
||||
const x = (input /*: string */) /*: string */ => {};
|
||||
|
||||
`;
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
new (window.Request/*: Class<Request> */)("");
|
||||
|
||||
(this/*: any */).foo = 5;
|
||||
|
||||
(this/* : any */).foo = 5;
|
||||
|
||||
// This next example illustrates that Prettier will not remove a line break
|
||||
// and unintentionally create a type annotation that was not there before.
|
||||
(this/*
|
||||
: any */).foo = 5;
|
||||
|
||||
const x = (input /*: string */) /*: string */ => {};
|
Loading…
Reference in New Issue