$
This commit is contained in:
250
node_modules/recast/lib/parser.js
generated
vendored
Normal file
250
node_modules/recast/lib/parser.js
generated
vendored
Normal file
@@ -0,0 +1,250 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.parse = void 0;
|
||||
var tslib_1 = require("tslib");
|
||||
var assert_1 = tslib_1.__importDefault(require("assert"));
|
||||
var types = tslib_1.__importStar(require("ast-types"));
|
||||
var b = types.builders;
|
||||
var isObject = types.builtInTypes.object;
|
||||
var isArray = types.builtInTypes.array;
|
||||
var options_1 = require("./options");
|
||||
var lines_1 = require("./lines");
|
||||
var comments_1 = require("./comments");
|
||||
var util = tslib_1.__importStar(require("./util"));
|
||||
function parse(source, options) {
|
||||
options = options_1.normalize(options);
|
||||
var lines = lines_1.fromString(source, options);
|
||||
var sourceWithoutTabs = lines.toString({
|
||||
tabWidth: options.tabWidth,
|
||||
reuseWhitespace: false,
|
||||
useTabs: false,
|
||||
});
|
||||
var comments = [];
|
||||
var ast = options.parser.parse(sourceWithoutTabs, {
|
||||
jsx: true,
|
||||
loc: true,
|
||||
locations: true,
|
||||
range: options.range,
|
||||
comment: true,
|
||||
onComment: comments,
|
||||
tolerant: util.getOption(options, "tolerant", true),
|
||||
ecmaVersion: 6,
|
||||
sourceType: util.getOption(options, "sourceType", "module"),
|
||||
});
|
||||
// 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.
|
||||
var 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)) {
|
||||
comments = ast.comments;
|
||||
delete ast.comments;
|
||||
}
|
||||
if (ast.loc) {
|
||||
// If the source was empty, some parsers give loc.{start,end}.line
|
||||
// values of 0, instead of the minimum of 1.
|
||||
util.fixFaultyLocations(ast, lines);
|
||||
}
|
||||
else {
|
||||
ast.loc = {
|
||||
start: lines.firstPos(),
|
||||
end: lines.lastPos(),
|
||||
};
|
||||
}
|
||||
ast.loc.lines = lines;
|
||||
ast.loc.indent = 0;
|
||||
var file;
|
||||
var program;
|
||||
if (ast.type === "Program") {
|
||||
program = ast;
|
||||
// In order to ensure we reprint leading and trailing program
|
||||
// comments, wrap the original Program node with a File node. Only
|
||||
// ESTree parsers (Acorn and Esprima) return a Program as the root AST
|
||||
// node. Most other (Babylon-like) parsers return a File.
|
||||
file = b.file(ast, options.sourceFileName || null);
|
||||
file.loc = {
|
||||
start: lines.firstPos(),
|
||||
end: lines.lastPos(),
|
||||
lines: lines,
|
||||
indent: 0,
|
||||
};
|
||||
}
|
||||
else if (ast.type === "File") {
|
||||
file = ast;
|
||||
program = file.program;
|
||||
}
|
||||
// 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
|
||||
// attached to the Program node, as its children may have comments as
|
||||
// well), since sometimes program.loc.{start,end} will coincide with the
|
||||
// .loc.{start,end} of the first and last *statements*, mistakenly
|
||||
// excluding comments that fall outside that region.
|
||||
var trueProgramLoc = util.getTrueLoc({
|
||||
type: program.type,
|
||||
loc: program.loc,
|
||||
body: [],
|
||||
comments: comments,
|
||||
}, lines);
|
||||
program.loc.start = trueProgramLoc.start;
|
||||
program.loc.end = trueProgramLoc.end;
|
||||
// Passing file.program here instead of just file means that initial
|
||||
// comments will be attached to program.body[0] instead of program.
|
||||
comments_1.attach(comments, program.body.length ? file.program : file, lines);
|
||||
// Return a copy of the original AST so that any changes made may be
|
||||
// compared to the original.
|
||||
return new TreeCopier(lines, tokens).copy(file);
|
||||
}
|
||||
exports.parse = parse;
|
||||
var TreeCopier = function TreeCopier(lines, tokens) {
|
||||
assert_1.default.ok(this instanceof TreeCopier);
|
||||
this.lines = lines;
|
||||
this.tokens = tokens;
|
||||
this.startTokenIndex = 0;
|
||||
this.endTokenIndex = tokens.length;
|
||||
this.indent = 0;
|
||||
this.seen = new Map();
|
||||
};
|
||||
var TCp = TreeCopier.prototype;
|
||||
TCp.copy = function (node) {
|
||||
if (this.seen.has(node)) {
|
||||
return this.seen.get(node);
|
||||
}
|
||||
if (isArray.check(node)) {
|
||||
var copy_1 = new Array(node.length);
|
||||
this.seen.set(node, copy_1);
|
||||
node.forEach(function (item, i) {
|
||||
copy_1[i] = this.copy(item);
|
||||
}, this);
|
||||
return copy_1;
|
||||
}
|
||||
if (!isObject.check(node)) {
|
||||
return node;
|
||||
}
|
||||
util.fixFaultyLocations(node, this.lines);
|
||||
var copy = Object.create(Object.getPrototypeOf(node), {
|
||||
original: {
|
||||
// Provide a link from the copy to the original.
|
||||
value: node,
|
||||
configurable: false,
|
||||
enumerable: false,
|
||||
writable: true,
|
||||
},
|
||||
});
|
||||
this.seen.set(node, copy);
|
||||
var loc = node.loc;
|
||||
var oldIndent = this.indent;
|
||||
var newIndent = oldIndent;
|
||||
var oldStartTokenIndex = this.startTokenIndex;
|
||||
var oldEndTokenIndex = this.endTokenIndex;
|
||||
if (loc) {
|
||||
// When node is a comment, we set node.loc.indent to
|
||||
// node.loc.start.column so that, when/if we print the comment by
|
||||
// itself, we can strip that much whitespace from the left margin of
|
||||
// the comment. This only really matters for multiline Block comments,
|
||||
// but it doesn't hurt for Line comments.
|
||||
if (node.type === "Block" ||
|
||||
node.type === "Line" ||
|
||||
node.type === "CommentBlock" ||
|
||||
node.type === "CommentLine" ||
|
||||
this.lines.isPrecededOnlyByWhitespace(loc.start)) {
|
||||
newIndent = this.indent = loc.start.column;
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
var keys = Object.keys(node);
|
||||
var keyCount = keys.length;
|
||||
for (var i = 0; i < keyCount; ++i) {
|
||||
var key = keys[i];
|
||||
if (key === "loc") {
|
||||
copy[key] = node[key];
|
||||
}
|
||||
else if (key === "tokens" && node.type === "File") {
|
||||
// Preserve file.tokens (uncopied) in case client code cares about
|
||||
// it, even though Recast ignores it when reprinting.
|
||||
copy[key] = node[key];
|
||||
}
|
||||
else {
|
||||
copy[key] = this.copy(node[key]);
|
||||
}
|
||||
}
|
||||
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) {
|
||||
var 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) {
|
||||
var 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) {
|
||||
var 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) {
|
||||
var 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;
|
||||
};
|
Reference in New Issue
Block a user