prettier 0.15.1 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +47 -1
- data/CONTRIBUTING.md +127 -96
- data/README.md +31 -7
- data/node_modules/prettier/bin-prettier.js +28420 -26950
- data/node_modules/prettier/index.js +28307 -26856
- data/node_modules/prettier/third-party.js +813 -580
- data/package.json +2 -2
- data/src/haml.js +21 -0
- data/src/haml/embed.js +58 -0
- data/src/haml/nodes/comment.js +27 -0
- data/src/haml/nodes/doctype.js +32 -0
- data/src/haml/nodes/filter.js +16 -0
- data/src/haml/nodes/hamlComment.js +21 -0
- data/src/haml/nodes/script.js +29 -0
- data/src/haml/nodes/silentScript.js +30 -0
- data/src/haml/nodes/tag.js +151 -0
- data/src/haml/parse.js +18 -0
- data/src/haml/parse.rb +65 -0
- data/src/haml/print.js +38 -0
- data/src/nodes.js +1 -0
- data/src/nodes/conditionals.js +45 -30
- data/src/nodes/flow.js +1 -34
- data/src/nodes/massign.js +16 -17
- data/src/nodes/return.js +60 -0
- data/src/nodes/statements.js +1 -4
- data/src/nodes/strings.js +57 -5
- data/src/ripper.rb +27 -1
- data/src/ruby.js +18 -0
- metadata +19 -6
data/src/haml/print.js
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
const { concat, hardline, join, markAsRoot } = require("../prettier");
|
2
|
+
|
3
|
+
const comment = require("./nodes/comment");
|
4
|
+
const doctype = require("./nodes/doctype");
|
5
|
+
const filter = require("./nodes/filter");
|
6
|
+
const hamlComment = require("./nodes/hamlComment");
|
7
|
+
const script = require("./nodes/script");
|
8
|
+
const silentScript = require("./nodes/silentScript");
|
9
|
+
const tag = require("./nodes/tag");
|
10
|
+
|
11
|
+
const nodes = {
|
12
|
+
comment,
|
13
|
+
doctype,
|
14
|
+
filter,
|
15
|
+
haml_comment: hamlComment,
|
16
|
+
plain: (path, _opts, _print) => {
|
17
|
+
const { value } = path.getValue();
|
18
|
+
|
19
|
+
return value.text.startsWith("=") ? `\\${value.text}` : value.text;
|
20
|
+
},
|
21
|
+
root: (path, opts, print) =>
|
22
|
+
markAsRoot(concat([join(hardline, path.map(print, "children")), hardline])),
|
23
|
+
script,
|
24
|
+
silent_script: silentScript,
|
25
|
+
tag
|
26
|
+
};
|
27
|
+
|
28
|
+
const genericPrint = (path, opts, print) => {
|
29
|
+
const { type } = path.getValue();
|
30
|
+
|
31
|
+
if (!(type in nodes)) {
|
32
|
+
throw new Error(`Unsupported node encountered: ${type}`);
|
33
|
+
}
|
34
|
+
|
35
|
+
return nodes[type](path, opts, print);
|
36
|
+
};
|
37
|
+
|
38
|
+
module.exports = genericPrint;
|
data/src/nodes.js
CHANGED
data/src/nodes/conditionals.js
CHANGED
@@ -10,34 +10,6 @@ const {
|
|
10
10
|
} = require("../prettier");
|
11
11
|
const { containsAssignment } = require("../utils");
|
12
12
|
|
13
|
-
const noTernary = [
|
14
|
-
"@comment",
|
15
|
-
"alias",
|
16
|
-
"assign",
|
17
|
-
"break",
|
18
|
-
"command",
|
19
|
-
"command_call",
|
20
|
-
"if_mod",
|
21
|
-
"ifop",
|
22
|
-
"lambda",
|
23
|
-
"massign",
|
24
|
-
"next",
|
25
|
-
"opassign",
|
26
|
-
"rescue_mod",
|
27
|
-
"return",
|
28
|
-
"return0",
|
29
|
-
"super",
|
30
|
-
"undef",
|
31
|
-
"unless_mod",
|
32
|
-
"until_mod",
|
33
|
-
"var_alias",
|
34
|
-
"void_stmt",
|
35
|
-
"while_mod",
|
36
|
-
"yield",
|
37
|
-
"yield0",
|
38
|
-
"zsuper"
|
39
|
-
];
|
40
|
-
|
41
13
|
const printWithAddition = (keyword, path, print, { breaking = false } = {}) =>
|
42
14
|
concat([
|
43
15
|
`${keyword} `,
|
@@ -152,11 +124,54 @@ const printSingle = keyword => (path, { inlineConditionals }, print) => {
|
|
152
124
|
return group(ifBreak(multiline, inline));
|
153
125
|
};
|
154
126
|
|
127
|
+
const noTernary = [
|
128
|
+
"@comment",
|
129
|
+
"alias",
|
130
|
+
"assign",
|
131
|
+
"break",
|
132
|
+
"command",
|
133
|
+
"command_call",
|
134
|
+
"if_mod",
|
135
|
+
"ifop",
|
136
|
+
"lambda",
|
137
|
+
"massign",
|
138
|
+
"next",
|
139
|
+
"opassign",
|
140
|
+
"rescue_mod",
|
141
|
+
"return",
|
142
|
+
"return0",
|
143
|
+
"super",
|
144
|
+
"undef",
|
145
|
+
"unless_mod",
|
146
|
+
"until_mod",
|
147
|
+
"var_alias",
|
148
|
+
"void_stmt",
|
149
|
+
"while_mod",
|
150
|
+
"yield",
|
151
|
+
"yield0",
|
152
|
+
"zsuper"
|
153
|
+
];
|
154
|
+
|
155
155
|
// Certain expressions cannot be reduced to a ternary without adding parens
|
156
156
|
// around them. In this case we say they cannot be ternaried and default instead
|
157
157
|
// to breaking them into multiple lines.
|
158
|
-
const canTernaryStmts = stmts =>
|
159
|
-
stmts.body.length
|
158
|
+
const canTernaryStmts = stmts => {
|
159
|
+
if (stmts.body.length !== 1) {
|
160
|
+
return false;
|
161
|
+
}
|
162
|
+
|
163
|
+
const stmt = stmts.body[0];
|
164
|
+
|
165
|
+
// If the user is using one of the lower precedence "and" or "or" operators,
|
166
|
+
// then we can't use a ternary expression as it would break the flow control.
|
167
|
+
if (stmt.type === "binary" && ["and", "or"].includes(stmt.body[1])) {
|
168
|
+
return false;
|
169
|
+
}
|
170
|
+
|
171
|
+
// Check against the blocklist of statement types that are not allowed to be
|
172
|
+
// a part of a ternary expression.
|
173
|
+
return !noTernary.includes(stmt.type);
|
174
|
+
};
|
160
175
|
|
161
176
|
// In order for an `if` or `unless` expression to be shortened to a ternary,
|
162
177
|
// there has to be one and only one "addition" (another clause attached) which
|
data/src/nodes/flow.js
CHANGED
@@ -1,11 +1,4 @@
|
|
1
|
-
const {
|
2
|
-
concat,
|
3
|
-
group,
|
4
|
-
ifBreak,
|
5
|
-
indent,
|
6
|
-
join,
|
7
|
-
softline
|
8
|
-
} = require("../prettier");
|
1
|
+
const { concat, join } = require("../prettier");
|
9
2
|
const { literal } = require("../utils");
|
10
3
|
|
11
4
|
module.exports = {
|
@@ -42,32 +35,6 @@ module.exports = {
|
|
42
35
|
|
43
36
|
return concat(["next ", join(", ", path.call(print, "body", 0))]);
|
44
37
|
},
|
45
|
-
return: (path, opts, print) => {
|
46
|
-
const args = path.getValue().body[0].body[0];
|
47
|
-
|
48
|
-
if (!args) {
|
49
|
-
return "return";
|
50
|
-
}
|
51
|
-
|
52
|
-
let value = join(", ", path.call(print, "body", 0));
|
53
|
-
|
54
|
-
// If the body of the return contains parens, then just skip directly to the
|
55
|
-
// content of the parens so that we can skip printing parens if we don't
|
56
|
-
// want them.
|
57
|
-
if (args.body[0] && args.body[0].type === "paren") {
|
58
|
-
value = path.call(print, "body", 0, "body", 0, "body", 0, "body", 0);
|
59
|
-
}
|
60
|
-
|
61
|
-
return group(
|
62
|
-
concat([
|
63
|
-
"return",
|
64
|
-
ifBreak("(", " "),
|
65
|
-
indent(concat([softline, value])),
|
66
|
-
concat([softline, ifBreak(")", "")])
|
67
|
-
])
|
68
|
-
);
|
69
|
-
},
|
70
|
-
return0: literal("return"),
|
71
38
|
yield: (path, opts, print) => {
|
72
39
|
if (path.getValue().body[0].type === "paren") {
|
73
40
|
return concat(["yield", path.call(print, "body", 0)]);
|
data/src/nodes/massign.js
CHANGED
@@ -13,12 +13,13 @@ module.exports = {
|
|
13
13
|
right = group(join(concat([",", line]), right));
|
14
14
|
}
|
15
15
|
|
16
|
+
const parts = [join(concat([",", line]), path.call(print, "body", 0))];
|
17
|
+
if (path.getValue().body[0].comma) {
|
18
|
+
parts.push(",");
|
19
|
+
}
|
20
|
+
|
16
21
|
return group(
|
17
|
-
concat([
|
18
|
-
group(join(concat([",", line]), path.call(print, "body", 0))),
|
19
|
-
" =",
|
20
|
-
indent(concat([line, right]))
|
21
|
-
])
|
22
|
+
concat([group(concat(parts)), " =", indent(concat([line, right]))])
|
22
23
|
);
|
23
24
|
},
|
24
25
|
mlhs: makeList,
|
@@ -40,18 +41,16 @@ module.exports = {
|
|
40
41
|
return path.call(print, "body", 0);
|
41
42
|
}
|
42
43
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
])
|
54
|
-
);
|
44
|
+
const parts = [
|
45
|
+
softline,
|
46
|
+
join(concat([",", line]), path.call(print, "body", 0))
|
47
|
+
];
|
48
|
+
|
49
|
+
if (path.getValue().body[0].comma) {
|
50
|
+
parts.push(",");
|
51
|
+
}
|
52
|
+
|
53
|
+
return group(concat(["(", indent(concat(parts)), concat([softline, ")"])]));
|
55
54
|
},
|
56
55
|
mrhs: makeList,
|
57
56
|
mrhs_add_star: (path, opts, print) =>
|
data/src/nodes/return.js
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
const {
|
2
|
+
concat,
|
3
|
+
group,
|
4
|
+
ifBreak,
|
5
|
+
indent,
|
6
|
+
line,
|
7
|
+
join,
|
8
|
+
softline
|
9
|
+
} = require("../prettier");
|
10
|
+
const { literal } = require("../utils");
|
11
|
+
|
12
|
+
const printReturn = (path, opts, print) => {
|
13
|
+
let args = path.getValue().body[0].body[0];
|
14
|
+
let steps = ["body", 0, "body", 0];
|
15
|
+
|
16
|
+
if (!args) {
|
17
|
+
return "return";
|
18
|
+
}
|
19
|
+
|
20
|
+
// If the body of the return contains parens, then just skip directly to the
|
21
|
+
// content of the parens so that we can skip printing parens if we don't
|
22
|
+
// want them.
|
23
|
+
if (args.body[0] && args.body[0].type === "paren") {
|
24
|
+
args = args.body[0].body[0];
|
25
|
+
steps = steps.concat("body", 0, "body", 0);
|
26
|
+
}
|
27
|
+
|
28
|
+
// If we're returning an array literal that isn't a special array, then we
|
29
|
+
// want to grab the arguments so that we can print them out as if they were
|
30
|
+
// normal return arguments.
|
31
|
+
if (
|
32
|
+
args.body[0] &&
|
33
|
+
args.body[0].type === "array" &&
|
34
|
+
["args", "args_add_star"].includes(args.body[0].body[0].type)
|
35
|
+
) {
|
36
|
+
steps = steps.concat("body", 0, "body", 0);
|
37
|
+
}
|
38
|
+
|
39
|
+
// Now that we've established which actual node is the arguments to return,
|
40
|
+
// we grab it out of the path by diving down the steps that we've set up.
|
41
|
+
const parts = path.call.apply(path, [print].concat(steps));
|
42
|
+
|
43
|
+
// If we got the value straight out of the parens, then `parts` would only
|
44
|
+
// be a singular doc as opposed to an array.
|
45
|
+
const value = Array.isArray(parts) ? join(concat([",", line]), parts) : parts;
|
46
|
+
|
47
|
+
return group(
|
48
|
+
concat([
|
49
|
+
"return",
|
50
|
+
ifBreak(parts.length > 1 ? " [" : "(", " "),
|
51
|
+
indent(concat([softline, value])),
|
52
|
+
concat([softline, ifBreak(parts.length > 1 ? "]" : ")", "")])
|
53
|
+
])
|
54
|
+
);
|
55
|
+
};
|
56
|
+
|
57
|
+
module.exports = {
|
58
|
+
return: printReturn,
|
59
|
+
return0: literal("return")
|
60
|
+
};
|
data/src/nodes/statements.js
CHANGED
@@ -7,7 +7,6 @@ const {
|
|
7
7
|
join,
|
8
8
|
line,
|
9
9
|
literalline,
|
10
|
-
markAsRoot,
|
11
10
|
softline,
|
12
11
|
trim
|
13
12
|
} = require("../prettier");
|
@@ -66,9 +65,7 @@ module.exports = {
|
|
66
65
|
);
|
67
66
|
},
|
68
67
|
program: (path, opts, print) =>
|
69
|
-
|
70
|
-
concat([join(literalline, path.map(print, "body")), literalline])
|
71
|
-
),
|
68
|
+
concat([join(hardline, path.map(print, "body")), hardline]),
|
72
69
|
stmts: (path, opts, print) => {
|
73
70
|
const stmts = path.getValue().body;
|
74
71
|
const parts = [];
|
data/src/nodes/strings.js
CHANGED
@@ -4,7 +4,8 @@ const {
|
|
4
4
|
hardline,
|
5
5
|
indent,
|
6
6
|
literalline,
|
7
|
-
softline
|
7
|
+
softline,
|
8
|
+
join
|
8
9
|
} = require("../prettier");
|
9
10
|
const { concatBody, empty, makeList, prefix, surround } = require("../utils");
|
10
11
|
const escapePattern = require("../escapePattern");
|
@@ -38,13 +39,14 @@ const getStringQuote = (string, preferSingleQuotes) => {
|
|
38
39
|
|
39
40
|
const quotePattern = new RegExp("\\\\([\\s\\S])|(['\"])", "g");
|
40
41
|
|
41
|
-
const makeString = (content, enclosingQuote) => {
|
42
|
+
const makeString = (content, enclosingQuote, originalQuote) => {
|
43
|
+
const replaceOther = ["'", '"'].includes(originalQuote);
|
42
44
|
const otherQuote = enclosingQuote === '"' ? "'" : '"';
|
43
45
|
|
44
46
|
// Escape and unescape single and double quotes as needed to be able to
|
45
47
|
// enclose `content` with `enclosingQuote`.
|
46
48
|
return content.replace(quotePattern, (match, escaped, quote) => {
|
47
|
-
if (escaped === otherQuote) {
|
49
|
+
if (replaceOther && escaped === otherQuote) {
|
48
50
|
return escaped;
|
49
51
|
}
|
50
52
|
|
@@ -60,6 +62,53 @@ const makeString = (content, enclosingQuote) => {
|
|
60
62
|
});
|
61
63
|
};
|
62
64
|
|
65
|
+
// The `parts` argument that comes into this function is an array of either
|
66
|
+
// printed embedded expressions or plain strings (that themselves can contain
|
67
|
+
// newlines). What we want to return is an array where each element represents a
|
68
|
+
// line in the original text. So we end up tracking every line as we go and
|
69
|
+
// pushing onto a `currentLine` variable, then when we hit a new line we push
|
70
|
+
// the current line onto the `lines` variable until we've used up every part.
|
71
|
+
const makeHeredocLines = parts => {
|
72
|
+
let lines = [];
|
73
|
+
let currentLine = [];
|
74
|
+
|
75
|
+
parts.forEach(part => {
|
76
|
+
if (part.type === "group" || !part.includes("\n")) {
|
77
|
+
// In this case we've either hit an embedded expression or the piece of
|
78
|
+
// the current line that we're looking at is in the middle of two of them,
|
79
|
+
// so we just push onto the current line and continue on.
|
80
|
+
currentLine.push(part);
|
81
|
+
return;
|
82
|
+
}
|
83
|
+
|
84
|
+
let splits = part.split("\n");
|
85
|
+
if (splits[splits.length - 1] === "") {
|
86
|
+
// If a line ends with a newline, then we end up with an empty string at
|
87
|
+
// the end of the splits, we just pop it off here since we'll handle the
|
88
|
+
// newlines later.
|
89
|
+
splits = splits.slice(0, -1);
|
90
|
+
}
|
91
|
+
|
92
|
+
if (currentLine.length > 0) {
|
93
|
+
lines.push(concat(currentLine.concat(splits[0])));
|
94
|
+
currentLine = [];
|
95
|
+
} else {
|
96
|
+
lines.push(splits[0]);
|
97
|
+
}
|
98
|
+
|
99
|
+
if (splits.length > 1) {
|
100
|
+
lines = lines.concat(splits.slice(1, -1));
|
101
|
+
currentLine.push(splits[splits.length - 1]);
|
102
|
+
}
|
103
|
+
});
|
104
|
+
|
105
|
+
if (currentLine.length > 0) {
|
106
|
+
lines = lines.concat(currentLine);
|
107
|
+
}
|
108
|
+
|
109
|
+
return lines;
|
110
|
+
};
|
111
|
+
|
63
112
|
module.exports = {
|
64
113
|
"@CHAR": (path, { preferSingleQuotes }, _print) => {
|
65
114
|
const { body } = path.getValue();
|
@@ -78,10 +127,13 @@ module.exports = {
|
|
78
127
|
},
|
79
128
|
heredoc: (path, opts, print) => {
|
80
129
|
const { beging, ending } = path.getValue();
|
130
|
+
const lines = makeHeredocLines(path.map(print, "body"));
|
81
131
|
|
82
132
|
return concat([
|
83
133
|
beging,
|
84
|
-
|
134
|
+
literalline,
|
135
|
+
join(literalline, lines),
|
136
|
+
literalline,
|
85
137
|
ending
|
86
138
|
]);
|
87
139
|
},
|
@@ -130,7 +182,7 @@ module.exports = {
|
|
130
182
|
string.body.forEach((part, index) => {
|
131
183
|
if (part.type === "@tstring_content") {
|
132
184
|
// In this case, the part of the string is just regular string content
|
133
|
-
parts.push(makeString(part.body, quote));
|
185
|
+
parts.push(makeString(part.body, quote, string.quote));
|
134
186
|
} else {
|
135
187
|
// In this case, the part of the string is an embedded expression
|
136
188
|
parts.push(path.call(print, "body", 0, "body", index));
|
data/src/ripper.rb
CHANGED
@@ -14,11 +14,12 @@ require 'json' unless defined?(JSON)
|
|
14
14
|
require 'ripper'
|
15
15
|
|
16
16
|
class RipperJS < Ripper
|
17
|
-
attr_reader :lines, :__end__
|
17
|
+
attr_reader :source, :lines, :__end__
|
18
18
|
|
19
19
|
def initialize(source, *args)
|
20
20
|
super(source, *args)
|
21
21
|
|
22
|
+
@source = source
|
22
23
|
@lines = source.split("\n")
|
23
24
|
@__end__ = nil
|
24
25
|
end
|
@@ -750,6 +751,31 @@ class RipperJS < Ripper
|
|
750
751
|
sexp.merge!(type: :lambda, body: [params, stmts])
|
751
752
|
end
|
752
753
|
end
|
754
|
+
|
755
|
+
# We need to track for `mlhs_paren` and `massign` nodes whether or not
|
756
|
+
# there was an extra comma at the end of the expression. For some reason
|
757
|
+
# it's not showing up in the AST in an obvious way. In this case we're
|
758
|
+
# just simplifying everything by adding an additional field to `mlhs`
|
759
|
+
# nodes called `comma` that indicates whether or not there was an extra.
|
760
|
+
def on_mlhs_paren(body)
|
761
|
+
super.tap do |node|
|
762
|
+
next unless body[:type] == :mlhs
|
763
|
+
|
764
|
+
ending = source.rindex(')', char_pos)
|
765
|
+
buffer = source[(node[:char_start] + 1)...ending]
|
766
|
+
|
767
|
+
body[:comma] = buffer.strip.end_with?(',')
|
768
|
+
end
|
769
|
+
end
|
770
|
+
|
771
|
+
def on_massign(left, right)
|
772
|
+
super.tap do
|
773
|
+
next unless left[:type] == :mlhs
|
774
|
+
|
775
|
+
range = left[:char_start]..left[:char_end]
|
776
|
+
left[:comma] = source[range].strip.end_with?(',')
|
777
|
+
end
|
778
|
+
end
|
753
779
|
end
|
754
780
|
)
|
755
781
|
end
|