-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
/
ast-to-doc.js
129 lines (111 loc) · 3.64 KB
/
ast-to-doc.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
"use strict";
const assert = require("assert");
const comments = require("./comments");
const FastPath = require("../common/fast-path");
const multiparser = require("./multiparser");
const doc = require("../doc");
const docBuilders = doc.builders;
const concat = docBuilders.concat;
const hardline = docBuilders.hardline;
const addAlignmentToDoc = docBuilders.addAlignmentToDoc;
const docUtils = doc.utils;
/**
* Takes an abstract syntax tree (AST) and recursively converts it to a
* document (series of printing primitives).
*
* This is done by descending down the AST recursively. The recursion
* involves two functions that call each other:
*
* 1. printGenerically(), which is defined as an inner function here.
* It basically takes care of node caching.
* 2. callPluginPrintFunction(), which checks for some options, and
* ultimately calls the print() function provided by the plugin.
*
* The plugin function will call printGenerically() again for child nodes
* of the current node, which will do its housekeeping, then call the
* plugin function again, and so on.
*
* All the while, these functions pass a "path" variable around, which
* is a stack-like data structure (FastPath) that maintains the current
* state of the recursion. It is called "path", because it represents
* the path to the current node through the Abstract Syntax Tree.
*/
function printAstToDoc(ast, options, alignmentSize = 0) {
const printer = options.printer;
if (printer.preprocess) {
ast = printer.preprocess(ast, options);
}
const cache = new Map();
function printGenerically(path, args) {
const node = path.getValue();
const shouldCache = node && typeof node === "object" && args === undefined;
if (shouldCache && cache.has(node)) {
return cache.get(node);
}
// We let JSXElement print its comments itself because it adds () around
// UnionTypeAnnotation has to align the child without the comments
let res;
if (printer.willPrintOwnComments && printer.willPrintOwnComments(path)) {
res = callPluginPrintFunction(path, options, printGenerically, args);
} else {
// printComments will call the plugin print function and check for
// comments to print
res = comments.printComments(
path,
p => callPluginPrintFunction(p, options, printGenerically, args),
options,
args && args.needsSemi
);
}
if (shouldCache) {
cache.set(node, res);
}
return res;
}
let doc = printGenerically(new FastPath(ast));
if (alignmentSize > 0) {
// Add a hardline to make the indents take effect
// It should be removed in index.js format()
doc = addAlignmentToDoc(
concat([hardline, doc]),
alignmentSize,
options.tabWidth
);
}
docUtils.propagateBreaks(doc);
return doc;
}
function callPluginPrintFunction(path, options, printPath, args) {
assert.ok(path instanceof FastPath);
const node = path.getValue();
const printer = options.printer;
// Escape hatch
if (printer.hasPrettierIgnore && printer.hasPrettierIgnore(path)) {
return options.originalText.slice(
options.locStart(node),
options.locEnd(node)
);
}
if (node) {
try {
// Potentially switch to a different parser
const sub = multiparser.printSubtree(
path,
printPath,
options,
printAstToDoc
);
if (sub) {
return sub;
}
} catch (error) {
/* istanbul ignore if */
if (process.env.PRETTIER_DEBUG) {
throw error;
}
// Continue with current parser
}
}
return printer.print(path, options, printPath, args);
}
module.exports = printAstToDoc;