| const getOption = require("../lib/util.js").getOption; | ||
| module.exports = function (options) { | ||
| // The goal here is to tolerate as much syntax as possible, since Recast | ||
| // is not in the business of forbidding anything. If you want your | ||
| // parser to be more restrictive for some reason, you can always pass | ||
| // your own parser object to recast.parse. | ||
| return { | ||
| sourceType: getOption(options, "sourceType", "module"), | ||
| strictMode: getOption(options, "strictMode", false), | ||
| allowImportExportEverywhere: true, | ||
| allowReturnOutsideFunction: true, | ||
| startLine: 1, | ||
| tokens: true, | ||
| plugins: [ | ||
| "asyncGenerators", | ||
| "bigInt", | ||
| "classPrivateMethods", | ||
| "classPrivateProperties", | ||
| "classProperties", | ||
| "decorators-legacy", | ||
| "doExpressions", | ||
| "dynamicImport", | ||
| "exportDefaultFrom", | ||
| "exportExtensions", | ||
| "exportNamespaceFrom", | ||
| "functionBind", | ||
| "functionSent", | ||
| "importMeta", | ||
| "nullishCoalescingOperator", | ||
| "numericSeparator", | ||
| "objectRestSpread", | ||
| "optionalCatchBinding", | ||
| "optionalChaining", | ||
| ["pipelineOperator", { proposal: "minimal" }], | ||
| "throwExpressions", | ||
| ] | ||
| }; | ||
| }; |
| "use strict"; | ||
| // Prefer the new @babel/parser package, but fall back to babylon if | ||
| // that's what's available. | ||
| const parser = exports.parser = function () { | ||
| try { | ||
| return require("@babel/parser"); | ||
| } catch (e) { | ||
| return require("babylon"); | ||
| } | ||
| }(); | ||
| // This module is suitable for passing as options.parser when calling | ||
| // recast.parse to process JavaScript code with Babel: | ||
| // | ||
| // const ast = recast.parse(source, { | ||
| // parser: require("recast/parsers/babel") | ||
| // }); | ||
| // | ||
| exports.parse = function (source, options) { | ||
| options = require("./_babel_options.js")(options); | ||
| options.plugins.push("jsx", "flow"); | ||
| return parser.parse(source, options); | ||
| }; |
+81
-37
@@ -7,2 +7,3 @@ var assert = require("assert"); | ||
| var isNumber = types.builtInTypes.number; | ||
| var util = require("./util.js"); | ||
@@ -184,44 +185,85 @@ function FastPath(value) { | ||
| function expressionStartsWithCurlyBrace(node) { | ||
| // TODO detect when node is already wrapped in parentheses to avoid (admittedly harmless) wrapping in extra parentheses. | ||
| if(node == null) return false; | ||
| switch(node.type) { | ||
| // expressions guaranteed to start with a { | ||
| case "ObjectExpression": | ||
| case "ObjectPattern": | ||
| return true; | ||
| // Returns true if the node at the tip of the path is wrapped with | ||
| // parentheses, OR if the only reason the node needed parentheses was that | ||
| // it couldn't be the first expression in the enclosing statement (see | ||
| // FastPath#canBeFirstInStatement), and it has an opening `(` character. | ||
| // For example, the FunctionExpression in `(function(){}())` appears to | ||
| // need parentheses only because it's the first expression in the AST, but | ||
| // since it happens to be preceded by a `(` (which is not apparent from | ||
| // the AST but can be determined using FastPath#getPrevToken), there is no | ||
| // ambiguity about how to parse it, so it counts as having parentheses, | ||
| // even though it is not immediately followed by a `)`. | ||
| FPp.hasParens = function () { | ||
| const node = this.getNode(); | ||
| // All expressions that comprise nested expressions | ||
| case "BinaryExpression": | ||
| case "AssignmentExpression": | ||
| case "LogicalExpression": | ||
| return expressionStartsWithCurlyBrace(node.left); | ||
| const prevToken = this.getPrevToken(node); | ||
| if (! prevToken) { | ||
| return false; | ||
| } | ||
| case "MemberExpression": | ||
| case "BindExpression": | ||
| return expressionStartsWithCurlyBrace(node.object); | ||
| const nextToken = this.getNextToken(node); | ||
| if (! nextToken) { | ||
| return false; | ||
| } | ||
| case "CallExpression": | ||
| return expressionStartsWithCurlyBrace(node.callee); | ||
| if (prevToken.value === "(") { | ||
| if (nextToken.value === ")") { | ||
| // If the node preceded by a `(` token and followed by a `)` token, | ||
| // then of course it has parentheses. | ||
| return true; | ||
| } | ||
| case "UpdateExpression": | ||
| case "UnaryExpression": | ||
| return node.prefix === false && expressionStartsWithCurlyBrace(node.argument); | ||
| // If this is one of the few Expression types that can't come first in | ||
| // the enclosing statement because of parsing ambiguities (namely, | ||
| // FunctionExpression, ObjectExpression, and ClassExpression) and | ||
| // this.firstInStatement() returns true, and the node would not need | ||
| // parentheses in an expression context because this.needsParens(true) | ||
| // returns false, then it just needs an opening parenthesis to resolve | ||
| // the parsing ambiguity that made it appear to need parentheses. | ||
| const justNeedsOpeningParen = | ||
| ! this.canBeFirstInStatement() && | ||
| this.firstInStatement() && | ||
| ! this.needsParens(true); | ||
| case "TSNonNullExpression": | ||
| return expressionStartsWithCurlyBrace(node.expression); | ||
| if (justNeedsOpeningParen) { | ||
| return true; | ||
| } | ||
| } | ||
| case "ConditionalExpression": | ||
| return expressionStartsWithCurlyBrace(node.test); | ||
| return false; | ||
| }; | ||
| case "SequenceExpression": | ||
| return expressionStartsWithCurlyBrace(node.expressions[0]); | ||
| FPp.getPrevToken = function (node) { | ||
| node = node || this.getNode(); | ||
| const loc = node && node.loc; | ||
| const tokens = loc && loc.tokens; | ||
| if (tokens && loc.start.token > 0) { | ||
| const token = tokens[loc.start.token - 1]; | ||
| if (token) { | ||
| // Do not return tokens that fall outside the root subtree. | ||
| const rootLoc = this.getRootValue().loc; | ||
| if (util.comparePos(rootLoc.start, token.loc.start) <= 0) { | ||
| return token; | ||
| } | ||
| } | ||
| } | ||
| return null; | ||
| }; | ||
| case "TaggedTemplateExpression": | ||
| return expressionStartsWithCurlyBrace(node.tag); | ||
| default: | ||
| false; | ||
| FPp.getNextToken = function (node) { | ||
| node = node || this.getNode(); | ||
| const loc = node && node.loc; | ||
| const tokens = loc && loc.tokens; | ||
| if (tokens && loc.end.token < tokens.length) { | ||
| const token = tokens[loc.end.token]; | ||
| if (token) { | ||
| // Do not return tokens that fall outside the root subtree. | ||
| const rootLoc = this.getRootValue().loc; | ||
| if (util.comparePos(token.loc.end, rootLoc.end) <= 0) { | ||
| return token; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| return null; | ||
| }; | ||
@@ -268,6 +310,2 @@ // Inspired by require("ast-types").NodePath.prototype.needsParens, but | ||
| if (parent.type === "ArrowFunctionExpression" && name === "body") { | ||
| return expressionStartsWithCurlyBrace(node); | ||
| } | ||
| switch (node.type) { | ||
@@ -544,2 +582,8 @@ case "UnaryExpression": | ||
| if (n.ArrowFunctionExpression.check(parent) && | ||
| childName === "body") { | ||
| assert.strictEqual(parent.body, child); | ||
| return true; | ||
| } | ||
| if (n.SequenceExpression.check(parent) && | ||
@@ -546,0 +590,0 @@ parentName === "expressions" && |
+105
-102
| var defaults = { | ||
| // If you want to use a different branch of esprima, or any other | ||
| // module that supports a .parse function, pass that module object to | ||
| // recast.parse as options.parser (legacy synonym: options.esprima). | ||
| parser: require("../parsers/esprima"), | ||
| // If you want to use a different branch of esprima, or any other module | ||
| // that supports a .parse function, pass that module object to | ||
| // recast.parse as options.parser (legacy synonym: options.esprima). | ||
| parser: require("../parsers/esprima"), | ||
| // Number of spaces the pretty-printer should use per tab for | ||
| // indentation. If you do not pass this option explicitly, it will be | ||
| // (quite reliably!) inferred from the original code. | ||
| tabWidth: 4, | ||
| // Number of spaces the pretty-printer should use per tab for | ||
| // indentation. If you do not pass this option explicitly, it will be | ||
| // (quite reliably!) inferred from the original code. | ||
| tabWidth: 4, | ||
| // If you really want the pretty-printer to use tabs instead of | ||
| // spaces, make this option true. | ||
| useTabs: false, | ||
| // If you really want the pretty-printer to use tabs instead of spaces, | ||
| // make this option true. | ||
| useTabs: false, | ||
| // The reprinting code leaves leading whitespace untouched unless it | ||
| // has to reindent a line, or you pass false for this option. | ||
| reuseWhitespace: true, | ||
| // The reprinting code leaves leading whitespace untouched unless it has | ||
| // to reindent a line, or you pass false for this option. | ||
| reuseWhitespace: true, | ||
| // Override this option to use a different line terminator, e.g. \r\n. | ||
| lineTerminator: require("os").EOL || "\n", | ||
| // Override this option to use a different line terminator, e.g. \r\n. | ||
| lineTerminator: require("os").EOL || "\n", | ||
| // Some of the pretty-printer code (such as that for printing function | ||
| // parameter lists) makes a valiant attempt to prevent really long | ||
| // lines. You can adjust the limit by changing this option; however, | ||
| // there is no guarantee that line length will fit inside this limit. | ||
| wrapColumn: 74, // Aspirational for now. | ||
| // Some of the pretty-printer code (such as that for printing function | ||
| // parameter lists) makes a valiant attempt to prevent really long | ||
| // lines. You can adjust the limit by changing this option; however, | ||
| // there is no guarantee that line length will fit inside this limit. | ||
| wrapColumn: 74, // Aspirational for now. | ||
| // Pass a string as options.sourceFileName to recast.parse to tell the | ||
| // reprinter to keep track of reused code so that it can construct a | ||
| // source map automatically. | ||
| sourceFileName: null, | ||
| // Pass a string as options.sourceFileName to recast.parse to tell the | ||
| // reprinter to keep track of reused code so that it can construct a | ||
| // source map automatically. | ||
| sourceFileName: null, | ||
| // Pass a string as options.sourceMapName to recast.print, and | ||
| // (provided you passed options.sourceFileName earlier) the | ||
| // PrintResult of recast.print will have a .map property for the | ||
| // generated source map. | ||
| sourceMapName: null, | ||
| // Pass a string as options.sourceMapName to recast.print, and (provided | ||
| // you passed options.sourceFileName earlier) the PrintResult of | ||
| // recast.print will have a .map property for the generated source map. | ||
| sourceMapName: null, | ||
| // If provided, this option will be passed along to the source map | ||
| // generator as a root directory for relative source file paths. | ||
| sourceRoot: null, | ||
| // If provided, this option will be passed along to the source map | ||
| // generator as a root directory for relative source file paths. | ||
| sourceRoot: null, | ||
| // If you provide a source map that was generated from a previous call | ||
| // to recast.print as options.inputSourceMap, the old source map will | ||
| // be composed with the new source map. | ||
| inputSourceMap: null, | ||
| // If you provide a source map that was generated from a previous call | ||
| // to recast.print as options.inputSourceMap, the old source map will be | ||
| // composed with the new source map. | ||
| inputSourceMap: null, | ||
| // If you want esprima to generate .range information (recast only | ||
| // uses .loc internally), pass true for this option. | ||
| range: false, | ||
| // If you want esprima to generate .range information (recast only uses | ||
| // .loc internally), pass true for this option. | ||
| range: false, | ||
| // If you want esprima not to throw exceptions when it encounters | ||
| // non-fatal errors, keep this option true. | ||
| tolerant: true, | ||
| // If you want esprima not to throw exceptions when it encounters | ||
| // non-fatal errors, keep this option true. | ||
| tolerant: true, | ||
| // If you want to override the quotes used in string literals, specify | ||
| // either "single", "double", or "auto" here ("auto" will select the one | ||
| // which results in the shorter literal) | ||
| // Otherwise, double quotes are used. | ||
| quote: null, | ||
| // If you want to override the quotes used in string literals, specify | ||
| // either "single", "double", or "auto" here ("auto" will select the one | ||
| // which results in the shorter literal) Otherwise, use double quotes. | ||
| quote: null, | ||
| // Controls the printing of trailing commas in object literals, | ||
| // array expressions and function parameters. | ||
| // | ||
| // This option could either be: | ||
| // * Boolean - enable/disable in all contexts (objects, arrays and function params). | ||
| // * Object - enable/disable per context. | ||
| // | ||
| // Example: | ||
| // trailingComma: { | ||
| // objects: true, | ||
| // arrays: true, | ||
| // parameters: false, | ||
| // } | ||
| trailingComma: false, | ||
| // Controls the printing of trailing commas in object literals, array | ||
| // expressions and function parameters. | ||
| // | ||
| // This option could either be: | ||
| // * Boolean - enable/disable in all contexts (objects, arrays and function params). | ||
| // * Object - enable/disable per context. | ||
| // | ||
| // Example: | ||
| // trailingComma: { | ||
| // objects: true, | ||
| // arrays: true, | ||
| // parameters: false, | ||
| // } | ||
| trailingComma: false, | ||
| // Controls the printing of spaces inside array brackets. | ||
| // See: http://eslint.org/docs/rules/array-bracket-spacing | ||
| arrayBracketSpacing: false, | ||
| // Controls the printing of spaces inside array brackets. | ||
| // See: http://eslint.org/docs/rules/array-bracket-spacing | ||
| arrayBracketSpacing: false, | ||
| // Controls the printing of spaces inside object literals, | ||
| // destructuring assignments, and import/export specifiers. | ||
| // See: http://eslint.org/docs/rules/object-curly-spacing | ||
| objectCurlySpacing: true, | ||
| // Controls the printing of spaces inside object literals, | ||
| // destructuring assignments, and import/export specifiers. | ||
| // See: http://eslint.org/docs/rules/object-curly-spacing | ||
| objectCurlySpacing: true, | ||
| // If you want parenthesis to wrap single-argument arrow function parameter | ||
| // lists, pass true for this option. | ||
| arrowParensAlways: false, | ||
| // If you want parenthesis to wrap single-argument arrow function | ||
| // parameter lists, pass true for this option. | ||
| arrowParensAlways: false, | ||
| // There are 2 supported syntaxes (`,` and `;`) in Flow Object Types; | ||
| // The use of commas is in line with the more popular style and matches | ||
| // how objects are defined in JS, making it a bit more natural to write. | ||
| flowObjectCommas: true, | ||
| // There are 2 supported syntaxes (`,` and `;`) in Flow Object Types; | ||
| // The use of commas is in line with the more popular style and matches | ||
| // how objects are defined in JS, making it a bit more natural to write. | ||
| flowObjectCommas: true, | ||
| // Whether to return an array of .tokens on the root AST node. | ||
| tokens: true | ||
| }, hasOwn = defaults.hasOwnProperty; | ||
@@ -99,30 +101,31 @@ | ||
| exports.normalize = function(options) { | ||
| options = options || defaults; | ||
| options = options || defaults; | ||
| function get(key) { | ||
| return hasOwn.call(options, key) | ||
| ? options[key] | ||
| : defaults[key]; | ||
| } | ||
| function get(key) { | ||
| return hasOwn.call(options, key) | ||
| ? options[key] | ||
| : defaults[key]; | ||
| } | ||
| return { | ||
| tabWidth: +get("tabWidth"), | ||
| useTabs: !!get("useTabs"), | ||
| reuseWhitespace: !!get("reuseWhitespace"), | ||
| lineTerminator: get("lineTerminator"), | ||
| wrapColumn: Math.max(get("wrapColumn"), 0), | ||
| sourceFileName: get("sourceFileName"), | ||
| sourceMapName: get("sourceMapName"), | ||
| sourceRoot: get("sourceRoot"), | ||
| inputSourceMap: get("inputSourceMap"), | ||
| parser: get("esprima") || get("parser"), | ||
| range: get("range"), | ||
| tolerant: get("tolerant"), | ||
| quote: get("quote"), | ||
| trailingComma: get("trailingComma"), | ||
| arrayBracketSpacing: get("arrayBracketSpacing"), | ||
| objectCurlySpacing: get("objectCurlySpacing"), | ||
| arrowParensAlways: get("arrowParensAlways"), | ||
| flowObjectCommas: get("flowObjectCommas"), | ||
| }; | ||
| return { | ||
| tabWidth: +get("tabWidth"), | ||
| useTabs: !!get("useTabs"), | ||
| reuseWhitespace: !!get("reuseWhitespace"), | ||
| lineTerminator: get("lineTerminator"), | ||
| wrapColumn: Math.max(get("wrapColumn"), 0), | ||
| sourceFileName: get("sourceFileName"), | ||
| sourceMapName: get("sourceMapName"), | ||
| sourceRoot: get("sourceRoot"), | ||
| inputSourceMap: get("inputSourceMap"), | ||
| parser: get("esprima") || get("parser"), | ||
| range: get("range"), | ||
| tolerant: get("tolerant"), | ||
| quote: get("quote"), | ||
| trailingComma: get("trailingComma"), | ||
| arrayBracketSpacing: get("arrayBracketSpacing"), | ||
| objectCurlySpacing: get("objectCurlySpacing"), | ||
| arrowParensAlways: get("arrowParensAlways"), | ||
| flowObjectCommas: get("flowObjectCommas"), | ||
| tokens: !!get("tokens") | ||
| }; | ||
| }; |
+94
-2
@@ -40,2 +40,22 @@ "use strict"; | ||
| // Use ast.tokens if possible, and otherwise fall back to the Esprima | ||
| // tokenizer. All the preconfigured ../parsers/* expose ast.tokens | ||
| // automatically, but custom parsers might need additional configuration | ||
| // to avoid this fallback. | ||
| const tokens = Array.isArray(ast.tokens) | ||
| ? ast.tokens | ||
| : require("esprima").tokenize(sourceWithoutTabs, { | ||
| loc: true | ||
| }); | ||
| // We will reattach the tokens array to the file object below. | ||
| delete ast.tokens; | ||
| // Make sure every token has a token.value string. | ||
| tokens.forEach(function (token) { | ||
| if (typeof token.value !== "string") { | ||
| token.value = lines.sliceString(token.loc.start, token.loc.end); | ||
| } | ||
| }); | ||
| if (Array.isArray(ast.comments)) { | ||
@@ -80,2 +100,7 @@ comments = ast.comments; | ||
| // Expose file.tokens unless the caller passed false for options.tokens. | ||
| if (options.tokens) { | ||
| file.tokens = tokens; | ||
| } | ||
| // Expand the Program's .loc to include all comments (not just those | ||
@@ -105,8 +130,11 @@ // attached to the Program node, as its children may have comments as | ||
| // compared to the original. | ||
| return new TreeCopier(lines).copy(file); | ||
| return new TreeCopier(lines, tokens).copy(file); | ||
| }; | ||
| function TreeCopier(lines) { | ||
| function TreeCopier(lines, tokens) { | ||
| assert.ok(this instanceof TreeCopier); | ||
| this.lines = lines; | ||
| this.tokens = tokens; | ||
| this.startTokenIndex = 0; | ||
| this.endTokenIndex = tokens.length; | ||
| this.indent = 0; | ||
@@ -153,2 +181,5 @@ this.seen = new Map; | ||
| const oldStartTokenIndex = this.startTokenIndex; | ||
| const oldEndTokenIndex = this.endTokenIndex; | ||
| if (loc) { | ||
@@ -166,4 +197,12 @@ // When node is a comment, we set node.loc.indent to | ||
| // Every node.loc has a reference to the original source lines as well | ||
| // as a complete list of source tokens. | ||
| loc.lines = this.lines; | ||
| loc.tokens = this.tokens; | ||
| loc.indent = newIndent; | ||
| // Set loc.start.token and loc.end.token such that | ||
| // loc.tokens.slice(loc.start.token, loc.end.token) returns a list of | ||
| // all the tokens that make up this node. | ||
| this.findTokenRange(loc); | ||
| } | ||
@@ -188,4 +227,57 @@ | ||
| this.indent = oldIndent; | ||
| this.startTokenIndex = oldStartTokenIndex; | ||
| this.endTokenIndex = oldEndTokenIndex; | ||
| return copy; | ||
| }; | ||
| // If we didn't have any idea where in loc.tokens to look for tokens | ||
| // contained by this loc, a binary search would be appropriate, but | ||
| // because we maintain this.startTokenIndex and this.endTokenIndex as we | ||
| // traverse the AST, we only need to make small (linear) adjustments to | ||
| // those indexes with each recursive iteration. | ||
| TCp.findTokenRange = function (loc) { | ||
| // In the unlikely event that loc.tokens[this.startTokenIndex] starts | ||
| // *after* loc.start, we need to rewind this.startTokenIndex first. | ||
| while (this.startTokenIndex > 0) { | ||
| const token = loc.tokens[this.startTokenIndex]; | ||
| if (util.comparePos(loc.start, token.loc.start) < 0) { | ||
| --this.startTokenIndex; | ||
| } else break; | ||
| } | ||
| // In the unlikely event that loc.tokens[this.endTokenIndex - 1] ends | ||
| // *before* loc.end, we need to fast-forward this.endTokenIndex first. | ||
| while (this.endTokenIndex < loc.tokens.length) { | ||
| const token = loc.tokens[this.endTokenIndex]; | ||
| if (util.comparePos(token.loc.end, loc.end) < 0) { | ||
| ++this.endTokenIndex; | ||
| } else break; | ||
| } | ||
| // Increment this.startTokenIndex until we've found the first token | ||
| // contained by this node. | ||
| while (this.startTokenIndex < this.endTokenIndex) { | ||
| const token = loc.tokens[this.startTokenIndex]; | ||
| if (util.comparePos(token.loc.start, loc.start) < 0) { | ||
| ++this.startTokenIndex; | ||
| } else break; | ||
| } | ||
| // Index into loc.tokens of the first token within this node. | ||
| loc.start.token = this.startTokenIndex; | ||
| // Decrement this.endTokenIndex until we've found the first token after | ||
| // this node (not contained by the node). | ||
| while (this.endTokenIndex > this.startTokenIndex) { | ||
| const token = loc.tokens[this.endTokenIndex - 1]; | ||
| if (util.comparePos(loc.end, token.loc.end) < 0) { | ||
| --this.endTokenIndex; | ||
| } else break; | ||
| } | ||
| // Index into loc.tokens of the first token *after* this node. | ||
| // If loc.start.token === loc.end.token, the node contains no tokens, | ||
| // and the index is that of the next token following this node. | ||
| loc.end.token = this.endTokenIndex; | ||
| }; |
+19
-97
@@ -193,6 +193,12 @@ var assert = require("assert"); | ||
| var newLines = print( | ||
| reprint.newPath, | ||
| needToPrintNewPathWithComments | ||
| ).indentTail(oldNode.loc.indent); | ||
| var newLines = print(reprint.newPath, { | ||
| includeComments: needToPrintNewPathWithComments, | ||
| // If the oldNode we're replacing already had parentheses, we may | ||
| // not need to print the new node with any extra parentheses, | ||
| // because the existing parentheses will suffice. However, if the | ||
| // newNode has a different type than the oldNode, let the printer | ||
| // decide if reprint.newPath needs parentheses, as usual. | ||
| avoidRootParens: (oldNode.type === newNode.type && | ||
| reprint.oldPath.hasParens()) | ||
| }).indentTail(oldNode.loc.indent); | ||
@@ -220,3 +226,9 @@ var nls = needsLeadingSpace(lines, oldNode.loc, newLines); | ||
| // guaranteed to contain all the reprinted nodes and comments. | ||
| return patcher.get(origLoc).indentTail(-orig.loc.indent); | ||
| const patchedLines = patcher.get(origLoc).indentTail(-orig.loc.indent); | ||
| if (path.needsParens()) { | ||
| return linesModule.concat(["(", patchedLines, ")"]); | ||
| } | ||
| return patchedLines; | ||
| }; | ||
@@ -408,74 +420,2 @@ }; | ||
| // This object is reused in hasOpeningParen and hasClosingParen to avoid | ||
| // having to allocate a temporary object. | ||
| var reusablePos = { line: 1, column: 0 }; | ||
| var nonSpaceExp = /\S/; | ||
| function hasOpeningParen(oldPath) { | ||
| var oldNode = oldPath.getValue(); | ||
| var loc = oldNode.loc; | ||
| var lines = loc && loc.lines; | ||
| if (lines) { | ||
| var pos = reusablePos; | ||
| pos.line = loc.start.line; | ||
| pos.column = loc.start.column; | ||
| while (lines.prevPos(pos)) { | ||
| var ch = lines.charAt(pos); | ||
| if (ch === "(") { | ||
| // If we found an opening parenthesis but it occurred before the | ||
| // start of the original subtree for this reprinting, then we must | ||
| // not return true for hasOpeningParen(oldPath). | ||
| return comparePos(oldPath.getRootValue().loc.start, pos) <= 0; | ||
| } | ||
| if (nonSpaceExp.test(ch)) { | ||
| return false; | ||
| } | ||
| } | ||
| } | ||
| return false; | ||
| } | ||
| function hasClosingParen(oldPath) { | ||
| var oldNode = oldPath.getValue(); | ||
| var loc = oldNode.loc; | ||
| var lines = loc && loc.lines; | ||
| if (lines) { | ||
| var pos = reusablePos; | ||
| pos.line = loc.end.line; | ||
| pos.column = loc.end.column; | ||
| do { | ||
| var ch = lines.charAt(pos); | ||
| if (ch === ")") { | ||
| // If we found a closing parenthesis but it occurred after the end | ||
| // of the original subtree for this reprinting, then we must not | ||
| // return true for hasClosingParen(oldPath). | ||
| return comparePos(pos, oldPath.getRootValue().loc.end) <= 0; | ||
| } | ||
| if (nonSpaceExp.test(ch)) { | ||
| return false; | ||
| } | ||
| } while (lines.nextPos(pos)); | ||
| } | ||
| return false; | ||
| } | ||
| function hasParens(oldPath) { | ||
| // This logic can technically be fooled if the node has parentheses but | ||
| // there are comments intervening between the parentheses and the | ||
| // node. In such cases the node will be harmlessly wrapped in an | ||
| // additional layer of parentheses. | ||
| return hasOpeningParen(oldPath) && hasClosingParen(oldPath); | ||
| } | ||
| function findChildReprints(newPath, oldPath, reprints) { | ||
@@ -493,25 +433,7 @@ var newNode = newPath.getValue(); | ||
| // If this type of node cannot come lexically first in its enclosing | ||
| // statement (e.g. a function expression or object literal), and it | ||
| // seems to be doing so, then the only way we can ignore this problem | ||
| // and save ourselves from falling back to the pretty printer is if an | ||
| // opening parenthesis happens to precede the node. For example, | ||
| // (function(){ ... }()); does not need to be reprinted, even though the | ||
| // FunctionExpression comes lexically first in the enclosing | ||
| // ExpressionStatement and fails the hasParens test, because the parent | ||
| // CallExpression passes the hasParens test. If we relied on the | ||
| // path.needsParens() && !hasParens(oldNode) check below, the absence of | ||
| // a closing parenthesis after the FunctionExpression would trigger | ||
| // pretty-printing unnecessarily. | ||
| if (Node.check(newNode) && | ||
| !newPath.canBeFirstInStatement() && | ||
| newPath.firstInStatement() && | ||
| !hasOpeningParen(oldPath)) { | ||
| return false; | ||
| } | ||
| // If this node needs parentheses and will not be wrapped with | ||
| // parentheses when reprinted, then return false to skip reprinting and | ||
| // let it be printed generically. | ||
| if (newPath.needsParens(true) && !hasParens(oldPath)) { | ||
| if (newPath.needsParens() && | ||
| ! oldPath.hasParens()) { | ||
| return false; | ||
@@ -518,0 +440,0 @@ } |
+4
-4
| { | ||
| "author": "Ben Newman <bn@cs.stanford.edu>", | ||
| "name": "recast", | ||
| "version": "0.16.0", | ||
| "description": "JavaScript syntax tree transformer, nondestructive pretty-printer, and automatic source map generator", | ||
@@ -15,3 +16,2 @@ "keywords": [ | ||
| ], | ||
| "version": "0.15.5", | ||
| "homepage": "http://github.com/benjamn/recast", | ||
@@ -32,3 +32,3 @@ "repository": { | ||
| "dependencies": { | ||
| "ast-types": "0.11.5", | ||
| "ast-types": "0.11.6", | ||
| "esprima": "~4.0.0", | ||
@@ -43,6 +43,6 @@ "private": "~0.1.5", | ||
| "esprima-fb": "^15001.1001.0-dev-harmony-fb", | ||
| "flow-parser": "^0.80.0", | ||
| "flow-parser": "^0.83.0", | ||
| "glob": "^7.1.2", | ||
| "mocha": "~5.2.0", | ||
| "reify": "^0.17.3" | ||
| "reify": "^0.18.0" | ||
| }, | ||
@@ -49,0 +49,0 @@ "engines": { |
+1
-23
| "use strict"; | ||
| // Prefer the new @babel/parser package, but fall back to babylon if | ||
| // that's what's available. | ||
| const parser = exports.parser = function () { | ||
| try { | ||
| return require("@babel/parser"); | ||
| } catch (e) { | ||
| return require("babylon"); | ||
| } | ||
| }(); | ||
| // This module is suitable for passing as options.parser when calling | ||
| // recast.parse to process JavaScript code with Babel: | ||
| // | ||
| // const ast = recast.parse(source, { | ||
| // parser: require("recast/parsers/babylon") | ||
| // }); | ||
| // | ||
| exports.parse = function (source, options) { | ||
| options = require("./_babylon_options.js")(options); | ||
| options.plugins.push("jsx", "flow"); | ||
| return parser.parse(source, options); | ||
| }; | ||
| Object.assign(exports, require("./babel.js")); |
@@ -21,3 +21,3 @@ "use strict"; | ||
| tolerant: getOption(options, "tolerant", true), | ||
| tokens: getOption(options, "tokens", true) | ||
| tokens: true | ||
| }); | ||
@@ -24,0 +24,0 @@ |
+2
-2
| "use strict"; | ||
| const parser = require("./babylon.js").parser; | ||
| const parser = require("./babel.js").parser; | ||
@@ -13,5 +13,5 @@ // This module is suitable for passing as options.parser when calling | ||
| exports.parse = function parse(source, options) { | ||
| options = require("./_babylon_options.js")(options); | ||
| options = require("./_babel_options.js")(options); | ||
| options.plugins.push("jsx", "flow"); | ||
| return parser.parse(source, options); | ||
| }; |
| "use strict"; | ||
| const parser = require("./babylon.js").parser; | ||
| const parser = require("./babel.js").parser; | ||
@@ -13,5 +13,5 @@ // This module is suitable for passing as options.parser when calling | ||
| exports.parse = function parse(source, options) { | ||
| options = require("./_babylon_options.js")(options); | ||
| options = require("./_babel_options.js")(options); | ||
| options.plugins.push("typescript"); | ||
| return parser.parse(source, options); | ||
| }; |
+1
-1
@@ -45,3 +45,3 @@ # recast, _v_. [](https://travis-ci.org/benjamn/recast) [](https://gitter.im/benjamn/recast?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [](https://greenkeeper.io/) | ||
| See [ast-types](https://github.com/benjamn/ast-types) (especially the [def/core.js](https://github.com/benjamn/ast-types/blob/master/def/core.js)) module for a thorough overview of the `ast` api. | ||
| See [ast-types](https://github.com/benjamn/ast-types) (especially the [def/core.js](https://github.com/benjamn/ast-types/blob/master/def/core.js)) module for a thorough overview of the `ast` API. | ||
| ```js | ||
@@ -48,0 +48,0 @@ // Grab a reference to the function declaration we just parsed. |
| const getOption = require("../lib/util.js").getOption; | ||
| module.exports = function (options) { | ||
| // The goal here is to tolerate as much syntax as possible, since Recast | ||
| // is not in the business of forbidding anything. If you want your | ||
| // parser to be more restrictive for some reason, you can always pass | ||
| // your own parser object to recast.parse. | ||
| return { | ||
| sourceType: getOption(options, "sourceType", "module"), | ||
| strictMode: getOption(options, "strictMode", false), | ||
| allowImportExportEverywhere: true, | ||
| allowReturnOutsideFunction: true, | ||
| startLine: 1, | ||
| tokens: getOption(options, "tokens", true), | ||
| plugins: [ | ||
| "asyncGenerators", | ||
| "bigInt", | ||
| "classPrivateMethods", | ||
| "classPrivateProperties", | ||
| "classProperties", | ||
| "decorators-legacy", | ||
| "doExpressions", | ||
| "dynamicImport", | ||
| "exportDefaultFrom", | ||
| "exportExtensions", | ||
| "exportNamespaceFrom", | ||
| "functionBind", | ||
| "functionSent", | ||
| "importMeta", | ||
| "nullishCoalescingOperator", | ||
| "numericSeparator", | ||
| "objectRestSpread", | ||
| "optionalCatchBinding", | ||
| "optionalChaining", | ||
| ["pipelineOperator", { proposal: "minimal" }], | ||
| "throwExpressions", | ||
| ] | ||
| }; | ||
| }; |
Sorry, the diff of this file is too big to display
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
198339
1.96%27
3.85%5358
1.88%+ Added
- Removed
Updated