prettier 1.5.5 → 1.6.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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +17 -1
  3. data/CONTRIBUTING.md +2 -2
  4. data/README.md +31 -12
  5. data/node_modules/prettier/bin-prettier.js +13702 -11629
  6. data/node_modules/prettier/index.js +19198 -16572
  7. data/node_modules/prettier/parser-angular.js +61 -40
  8. data/node_modules/prettier/parser-babel.js +22 -1
  9. data/node_modules/prettier/parser-espree.js +22 -1
  10. data/node_modules/prettier/parser-flow.js +22 -1
  11. data/node_modules/prettier/parser-glimmer.js +1 -1
  12. data/node_modules/prettier/parser-graphql.js +1 -1
  13. data/node_modules/prettier/parser-html.js +82 -63
  14. data/node_modules/prettier/parser-markdown.js +24 -9
  15. data/node_modules/prettier/parser-meriyah.js +22 -1
  16. data/node_modules/prettier/parser-postcss.js +22 -1
  17. data/node_modules/prettier/parser-typescript.js +22 -1
  18. data/node_modules/prettier/parser-yaml.js +2 -2
  19. data/node_modules/prettier/third-party.js +1042 -833
  20. data/package.json +3 -3
  21. data/rubocop.yml +9 -0
  22. data/src/haml/parser.js +5 -4
  23. data/src/haml/printer.js +428 -18
  24. data/src/parser/parseSync.js +8 -6
  25. data/src/plugin.js +1 -1
  26. data/src/rbs/parser.js +1 -3
  27. data/src/rbs/printer.js +35 -7
  28. data/src/ruby/nodes/args.js +66 -22
  29. data/src/ruby/nodes/calls.js +8 -1
  30. data/src/ruby/nodes/conditionals.js +47 -45
  31. data/src/ruby/nodes/hashes.js +5 -14
  32. data/src/ruby/nodes/params.js +2 -9
  33. data/src/ruby/nodes/strings.js +95 -2
  34. data/src/ruby/parser.js +1 -3
  35. data/src/ruby/parser.rb +52 -29
  36. data/src/ruby/printer.js +10 -1
  37. data/src/utils/inlineEnsureParens.js +1 -0
  38. data/src/utils/skipAssignIndent.js +8 -1
  39. metadata +3 -12
  40. data/src/haml/nodes/comment.js +0 -27
  41. data/src/haml/nodes/doctype.js +0 -34
  42. data/src/haml/nodes/filter.js +0 -16
  43. data/src/haml/nodes/hamlComment.js +0 -21
  44. data/src/haml/nodes/plain.js +0 -6
  45. data/src/haml/nodes/root.js +0 -8
  46. data/src/haml/nodes/script.js +0 -33
  47. data/src/haml/nodes/silentScript.js +0 -59
  48. data/src/haml/nodes/tag.js +0 -232
@@ -135,15 +135,17 @@ function parseSync(parser, source, opts) {
135
135
  // using unix sockets.
136
136
  if (
137
137
  stderr.includes("invalid option -- U") ||
138
+ stderr.includes("invalid option -- 'u'") ||
138
139
  stderr.includes("Protocol not supported")
139
140
  ) {
140
141
  throw new Error(`
141
- @prettier/plugin-ruby uses netcat to communicate over unix sockets between
142
- the node.js process running prettier and an underlying Ruby process used
143
- for parsing. Unfortunately the version of netcat that you have installed
144
- does not support unix sockets. To solve this either uninstall the version
145
- of netcat that you're using and use a different implementation, or change
146
- the value of the rubyNetcatCommand option in your prettier configuration.
142
+ @prettier/plugin-ruby uses unix sockets to communicate between the node.js
143
+ process running prettier and an underlying Ruby process used for parsing.
144
+ Unfortunately the command that it tried to use to do that
145
+ (${netcat.command}) does not support unix sockets. To solve this either
146
+ uninstall the version of ${netcat.command} that you're using and use a
147
+ different implementation, or change the value of the rubyNetcatCommand
148
+ option in your prettier configuration.
147
149
  `);
148
150
  }
149
151
 
data/src/plugin.js CHANGED
@@ -10,7 +10,7 @@ const hamlParser = require("./haml/parser");
10
10
  /*
11
11
  * metadata mostly pulled from linguist and rubocop:
12
12
  * https://github.com/github/linguist/blob/master/lib/linguist/languages.yml
13
- * https://github.com/rubocop-hq/rubocop/blob/master/spec/rubocop/target_finder_spec.rb
13
+ * https://github.com/rubocop/rubocop/blob/master/spec/rubocop/target_finder_spec.rb
14
14
  */
15
15
 
16
16
  module.exports = {
data/src/rbs/parser.js CHANGED
@@ -8,12 +8,10 @@ function parse(text, _parsers, opts) {
8
8
  return parseSync("rbs", text, opts);
9
9
  }
10
10
 
11
- const pragmaPattern = /#\s*@(prettier|format)/;
12
-
13
11
  // This function handles checking whether or not the source string has the
14
12
  // pragma for prettier. This is an optional workflow for incremental adoption.
15
13
  function hasPragma(text) {
16
- return pragmaPattern.test(text);
14
+ return /^\s*#[^\S\n]*@(format|prettier)\s*(\n|$)/.test(text);
17
15
  }
18
16
 
19
17
  // This function is critical for comments and cursor support, and is responsible
data/src/rbs/printer.js CHANGED
@@ -147,7 +147,7 @@ function printNode(path, opts, print) {
147
147
 
148
148
  // This is the big function that prints out any individual type, which can
149
149
  // look like all kinds of things, listed in the case statement below.
150
- function printType(path, { forceUnionParens = false } = {}) {
150
+ function printType(path, { forceParens = false } = {}) {
151
151
  const node = path.getValue();
152
152
 
153
153
  switch (node.class) {
@@ -157,7 +157,13 @@ function printNode(path, opts, print) {
157
157
  }
158
158
  return node.literal;
159
159
  case "optional":
160
- return concat([path.call(printType, "type"), "?"]);
160
+ return concat([
161
+ path.call(
162
+ (typePath) => printType(typePath, { forceParens: true }),
163
+ "type"
164
+ ),
165
+ "?"
166
+ ]);
161
167
  case "tuple":
162
168
  // If we don't have any sub types, we explicitly need the space in between
163
169
  // the brackets to not confuse the parser.
@@ -173,14 +179,29 @@ function printNode(path, opts, print) {
173
179
  join(concat([line, "| "]), path.map(printType, "types"))
174
180
  );
175
181
 
176
- if (forceUnionParens || path.getParentNode().class === "intersection") {
182
+ if (forceParens) {
183
+ return concat(["(", doc, ")"]);
184
+ }
185
+
186
+ return doc;
187
+ }
188
+ case "intersection": {
189
+ const doc = group(
190
+ join(
191
+ concat([line, "& "]),
192
+ path.map(
193
+ (typePath) => printType(typePath, { forceParens: true }),
194
+ "types"
195
+ )
196
+ )
197
+ );
198
+
199
+ if (forceParens) {
177
200
  return concat(["(", doc, ")"]);
178
201
  }
179
202
 
180
203
  return doc;
181
204
  }
182
- case "intersection":
183
- return group(join(concat([line, "& "]), path.map(printType, "types")));
184
205
  case "class_singleton":
185
206
  return concat(["singleton(", node.name, ")"]);
186
207
  case "proc":
@@ -521,7 +542,7 @@ function printNode(path, opts, print) {
521
542
  parts.push(
522
543
  "-> ",
523
544
  path.call(
524
- (typePath) => printType(typePath, { forceUnionParens: true }),
545
+ (typePath) => printType(typePath, { forceParens: true }),
525
546
  "type",
526
547
  "return_type"
527
548
  )
@@ -609,7 +630,14 @@ function hasPrettierIgnore(path) {
609
630
  return node.comment && node.comment.string.includes("prettier-ignore");
610
631
  }
611
632
 
633
+ // This function handles adding the format pragma to a source string. This is an
634
+ // optional workflow for incremental adoption.
635
+ function insertPragma(text) {
636
+ return `# @format\n${text}`;
637
+ }
638
+
612
639
  module.exports = {
613
640
  print: printNode,
614
- hasPrettierIgnore
641
+ hasPrettierIgnore,
642
+ insertPragma
615
643
  };
@@ -108,12 +108,18 @@ function printArgs(path, { rubyToProc }, print) {
108
108
 
109
109
  function printArgsAddBlock(path, opts, print) {
110
110
  const node = path.getValue();
111
+ const blockNode = node.body[1];
112
+
111
113
  const parts = path.call(print, "body", 0);
112
114
 
113
- if (node.body[1]) {
115
+ if (blockNode) {
114
116
  let blockDoc = path.call(print, "body", 1);
115
117
 
116
- if (node.body[1].comments) {
118
+ if (!(blockNode.comments || []).some(({ leading }) => leading)) {
119
+ // If we don't have any leading comments, we can just prepend the
120
+ // operator.
121
+ blockDoc = concat(["&", blockDoc]);
122
+ } else if (Array.isArray(blockDoc[0])) {
117
123
  // If we have a method call like:
118
124
  //
119
125
  // foo(
@@ -123,10 +129,20 @@ function printArgsAddBlock(path, opts, print) {
123
129
  //
124
130
  // then we need to make sure we don't accidentally prepend the operator
125
131
  // before the comment.
126
- blockDoc.parts[2] = concat(["&", blockDoc.parts[2]]);
132
+ //
133
+ // In prettier >= 2.3.0, the comments are printed as an array before the
134
+ // content. I don't love this kind of reflection, but it's the simplest
135
+ // way at the moment to get this right.
136
+ blockDoc = blockDoc[0].concat(
137
+ concat(["&", blockDoc[1]]),
138
+ blockDoc.slice(2)
139
+ );
127
140
  } else {
128
- // If we don't have any comments, we can just prepend the operator
129
- blockDoc = concat(["&", blockDoc]);
141
+ // In prettier < 2.3.0, the comments are printed as part of a concat, so
142
+ // we can reflect on how many leading comments there are to determine
143
+ // which doc node we should modify.
144
+ const index = blockNode.comments.filter(({ leading }) => leading).length;
145
+ blockDoc.parts[index] = concat(["&", blockDoc.parts[index]]);
130
146
  }
131
147
 
132
148
  parts.push(blockDoc);
@@ -136,10 +152,34 @@ function printArgsAddBlock(path, opts, print) {
136
152
  }
137
153
 
138
154
  function printArgsAddStar(path, opts, print) {
139
- const node = path.getValue();
140
- const docs = path.map(print, "body");
155
+ let docs = [];
156
+
157
+ path.each((argPath, argIndex) => {
158
+ const doc = print(argPath);
159
+
160
+ // If it's the first child, then it's an array of args, so we're going to
161
+ // concat that onto the existing docs if there are any.
162
+ if (argIndex === 0) {
163
+ if (doc.length > 0) {
164
+ docs = docs.concat(doc);
165
+ }
166
+ return;
167
+ }
168
+
169
+ // If it's after the splat, then it's an individual arg, so we're just going
170
+ // to push it onto the array.
171
+ if (argIndex !== 1) {
172
+ docs.push(doc);
173
+ return;
174
+ }
175
+
176
+ // If we don't have any leading comments, we can just prepend the operator.
177
+ const argsNode = argPath.getValue();
178
+ if (!(argsNode.comments || []).some(({ leading }) => leading)) {
179
+ docs.push(concat(["*", doc]));
180
+ return;
181
+ }
141
182
 
142
- if (node.body[1].comments) {
143
183
  // If we have an array like:
144
184
  //
145
185
  // [
@@ -147,22 +187,26 @@ function printArgsAddStar(path, opts, print) {
147
187
  // *values
148
188
  // ]
149
189
  //
150
- // or if we have an array like:
151
- //
152
- // [
153
- // *values # comment
154
- // ]
155
- //
156
190
  // then we need to make sure we don't accidentally prepend the operator
157
- // before the comment.
158
- const index = node.body[1].comments.filter(({ leading }) => leading).length;
159
- docs[1].parts[index] = concat(["*", docs[1].parts[index]]);
160
- } else {
161
- // If we don't have any comments, we can just prepend the operator
162
- docs[1] = concat(["*", docs[1]]);
163
- }
191
+ // before the comment(s).
192
+ //
193
+ // In prettier >= 2.3.0, the comments are printed as an array before the
194
+ // content. I don't love this kind of reflection, but it's the simplest way
195
+ // at the moment to get this right.
196
+ if (Array.isArray(doc[0])) {
197
+ docs.push(doc[0].concat(concat(["*", doc[1]]), doc.slice(2)));
198
+ return;
199
+ }
200
+
201
+ // In prettier < 2.3.0, the comments are printed as part of a concat, so
202
+ // we can reflect on how many leading comments there are to determine which
203
+ // doc node we should modify.
204
+ const index = argsNode.comments.filter(({ leading }) => leading).length;
205
+ doc.parts[index] = concat(["*", doc.parts[index]]);
206
+ docs = docs.concat(doc);
207
+ }, "body");
164
208
 
165
- return docs[0].concat(docs[1]).concat(docs.slice(2));
209
+ return docs;
166
210
  }
167
211
 
168
212
  function printBlockArg(path, opts, print) {
@@ -4,6 +4,7 @@ const {
4
4
  hardline,
5
5
  ifBreak,
6
6
  indent,
7
+ join,
7
8
  softline
8
9
  } = require("../../prettier");
9
10
  const { makeCall, noIndent } = require("../../utils");
@@ -133,7 +134,13 @@ function printMethodAddArg(path, opts, print) {
133
134
  );
134
135
  }
135
136
 
136
- return concat([methodDoc, argsDoc]);
137
+ // If there are already parentheses, then we can just use the doc that's
138
+ // already printed.
139
+ if (argNode.type == "arg_paren") {
140
+ return concat([methodDoc, argsDoc]);
141
+ }
142
+
143
+ return concat([methodDoc, " ", join(", ", argsDoc), " "]);
137
144
  }
138
145
 
139
146
  function printMethodAddBlock(path, opts, print) {
@@ -190,57 +190,59 @@ const canTernary = (path) => {
190
190
  };
191
191
 
192
192
  // A normalized print function for both `if` and `unless` nodes.
193
- const printConditional = (keyword) => (path, { rubyModifier }, print) => {
194
- if (canTernary(path)) {
195
- let ternaryParts = [path.call(print, "body", 0), " ? "].concat(
196
- printTernaryClauses(
197
- keyword,
198
- path.call(print, "body", 1),
199
- path.call(print, "body", 2, "body", 0)
200
- )
201
- );
202
-
203
- if (["binary", "call"].includes(path.getParentNode().type)) {
204
- ternaryParts = ["("].concat(ternaryParts).concat(")");
193
+ const printConditional =
194
+ (keyword) =>
195
+ (path, { rubyModifier }, print) => {
196
+ if (canTernary(path)) {
197
+ let ternaryParts = [path.call(print, "body", 0), " ? "].concat(
198
+ printTernaryClauses(
199
+ keyword,
200
+ path.call(print, "body", 1),
201
+ path.call(print, "body", 2, "body", 0)
202
+ )
203
+ );
204
+
205
+ if (["binary", "call"].includes(path.getParentNode().type)) {
206
+ ternaryParts = ["("].concat(ternaryParts).concat(")");
207
+ }
208
+
209
+ return group(
210
+ ifBreak(printWithAddition(keyword, path, print), concat(ternaryParts))
211
+ );
205
212
  }
206
213
 
207
- return group(
208
- ifBreak(printWithAddition(keyword, path, print), concat(ternaryParts))
209
- );
210
- }
211
-
212
- const [predicate, statements, addition] = path.getValue().body;
214
+ const [predicate, statements, addition] = path.getValue().body;
213
215
 
214
- // If there's an additional clause that wasn't matched earlier, we know we
215
- // can't go for the inline option.
216
- if (addition) {
217
- return group(printWithAddition(keyword, path, print, { breaking: true }));
218
- }
216
+ // If there's an additional clause that wasn't matched earlier, we know we
217
+ // can't go for the inline option.
218
+ if (addition) {
219
+ return group(printWithAddition(keyword, path, print, { breaking: true }));
220
+ }
219
221
 
220
- // If the body of the conditional is empty, then we explicitly have to use the
221
- // block form.
222
- if (isEmptyStmts(statements)) {
223
- return concat([
224
- `${keyword} `,
225
- align(keyword.length + 1, path.call(print, "body", 0)),
226
- concat([hardline, "end"])
227
- ]);
228
- }
222
+ // If the body of the conditional is empty, then we explicitly have to use the
223
+ // block form.
224
+ if (isEmptyStmts(statements)) {
225
+ return concat([
226
+ `${keyword} `,
227
+ align(keyword.length + 1, path.call(print, "body", 0)),
228
+ concat([hardline, "end"])
229
+ ]);
230
+ }
229
231
 
230
- // If the predicate of the conditional contains an assignment, then we can't
231
- // know for sure that it doesn't impact the body of the conditional, so we
232
- // have to default to the block form.
233
- if (containsAssignment(predicate)) {
234
- return concat([
235
- `${keyword} `,
236
- align(keyword.length + 1, path.call(print, "body", 0)),
237
- indent(concat([hardline, path.call(print, "body", 1)])),
238
- concat([hardline, "end"])
239
- ]);
240
- }
232
+ // If the predicate of the conditional contains an assignment, then we can't
233
+ // know for sure that it doesn't impact the body of the conditional, so we
234
+ // have to default to the block form.
235
+ if (containsAssignment(predicate)) {
236
+ return concat([
237
+ `${keyword} `,
238
+ align(keyword.length + 1, path.call(print, "body", 0)),
239
+ indent(concat([hardline, path.call(print, "body", 1)])),
240
+ concat([hardline, "end"])
241
+ ]);
242
+ }
241
243
 
242
- return printSingle(keyword)(path, { rubyModifier }, print);
243
- };
244
+ return printSingle(keyword)(path, { rubyModifier }, print);
245
+ };
244
246
 
245
247
  module.exports = {
246
248
  else: (path, opts, print) => {
@@ -56,28 +56,19 @@ function printHashKeyLabel(path, print) {
56
56
  case "symbol_literal":
57
57
  return concat([path.call(print, "body", 0), ":"]);
58
58
  case "dyna_symbol": {
59
- const { parts } = print(path);
60
-
61
- // We're going to slice off the starting colon character so that we can
62
- // move it to the end. If there are comments, then we're going to go
63
- // further into the printed doc nodes.
64
- if (parts[0] === ":") {
65
- parts.splice(0, 1);
66
- } else {
67
- parts[1].parts.splice(0, 1);
68
- }
69
-
70
- return concat(parts.concat(":"));
59
+ return concat([print(path), ":"]);
71
60
  }
72
61
  }
73
62
  }
74
63
 
75
64
  function printHashKeyRocket(path, print) {
76
65
  const node = path.getValue();
77
- const doc = print(path);
66
+ let doc = print(path);
78
67
 
79
68
  if (node.type === "@label") {
80
- return `:${doc.slice(0, doc.length - 1)} =>`;
69
+ doc = concat([":", doc.slice(0, doc.length - 1)]);
70
+ } else if (node.type === "dyna_symbol") {
71
+ doc = concat([":", doc]);
81
72
  }
82
73
 
83
74
  return concat([doc, " =>"]);
@@ -17,15 +17,8 @@ function printRestParam(symbol) {
17
17
  }
18
18
 
19
19
  function printParams(path, opts, print) {
20
- const [
21
- reqs,
22
- optls,
23
- rest,
24
- post,
25
- kwargs,
26
- kwargRest,
27
- block
28
- ] = path.getValue().body;
20
+ const [reqs, optls, rest, post, kwargs, kwargRest, block] =
21
+ path.getValue().body;
29
22
  let parts = [];
30
23
 
31
24
  if (reqs) {
@@ -82,13 +82,106 @@ function printChar(path, { rubySingleQuote }, _print) {
82
82
  return concat([quote, body.slice(1), quote]);
83
83
  }
84
84
 
85
+ function printPercentSDynaSymbol(path, opts, print) {
86
+ const node = path.getValue();
87
+ const parts = [];
88
+
89
+ // Push on the quote, which includes the opening character.
90
+ parts.push(node.quote);
91
+
92
+ path.each((childPath) => {
93
+ const childNode = childPath.getValue();
94
+
95
+ if (childNode.type !== "@tstring_content") {
96
+ // Here we are printing an embedded variable or expression.
97
+ parts.push(print(childPath));
98
+ } else {
99
+ // Here we are printing plain string content.
100
+ parts.push(join(literalline, childNode.body.split("\n")));
101
+ }
102
+ }, "body");
103
+
104
+ // Push on the closing character, which is the opposite of the third
105
+ // character from the opening.
106
+ parts.push(quotePairs[node.quote[2]]);
107
+
108
+ return concat(parts);
109
+ }
110
+
111
+ // We don't actually want to print %s symbols, as they're much more rarely seen
112
+ // in the wild. But we're going to be forced into it if it's a multi-line symbol
113
+ // or if the quoting would get super complicated.
114
+ function shouldPrintPercentSDynaSymbol(node) {
115
+ // We shouldn't print a %s dyna symbol if it was not already that way in the
116
+ // original source.
117
+ if (node.quote[0] !== "%") {
118
+ return false;
119
+ }
120
+
121
+ // Here we're going to check if there is a closing character, a new line, or a
122
+ // quote in the content of the dyna symbol. If there is, then quoting could
123
+ // get weird, so just bail out and stick to the original bounds in the source.
124
+ const closing = quotePairs[node.quote[2]];
125
+
126
+ return node.body.some(
127
+ (child) =>
128
+ child.type === "@tstring_content" &&
129
+ (child.body.includes("\n") ||
130
+ child.body.includes(closing) ||
131
+ child.body.includes("'") ||
132
+ child.body.includes('"'))
133
+ );
134
+ }
135
+
85
136
  // Prints a dynamic symbol. Assumes there's a quote property attached to the
86
137
  // node that will tell us which quote to use when printing. We're just going to
87
138
  // use whatever quote was provided.
139
+ //
140
+ // In the case of a plain dyna symbol, node.quote will be either :" or :'
141
+ // For %s dyna symbols, node.quote will be %s[, %s(, %s{, or %s<
88
142
  function printDynaSymbol(path, opts, print) {
89
- const { quote } = path.getValue();
143
+ const node = path.getValue();
144
+
145
+ if (shouldPrintPercentSDynaSymbol(node)) {
146
+ return printPercentSDynaSymbol(path, opts, print);
147
+ }
148
+
149
+ const parts = [];
150
+ let quote;
151
+
152
+ if (isQuoteLocked(node)) {
153
+ if (node.quote.startsWith("%")) {
154
+ quote = opts.rubySingleQuote ? "'" : '"';
155
+ } else {
156
+ quote = node.quote.slice(1);
157
+ }
158
+ } else {
159
+ quote = opts.rubySingleQuote && isSingleQuotable(node) ? "'" : '"';
160
+ }
161
+
162
+ parts.push(quote);
163
+ path.each((childPath) => {
164
+ const child = childPath.getValue();
165
+
166
+ if (child.type !== "@tstring_content") {
167
+ parts.push(print(childPath));
168
+ } else {
169
+ parts.push(
170
+ join(literalline, normalizeQuotes(child.body, quote).split("\n"))
171
+ );
172
+ }
173
+ }, "body");
174
+
175
+ parts.push(quote);
176
+
177
+ // If we're inside of an assoc_new node as the key, then it will handle
178
+ // printing the : on its own since it could change sides.
179
+ const parentNode = path.getParentNode();
180
+ if (parentNode.type !== "assoc_new" || parentNode.body[0] !== node) {
181
+ parts.unshift(":");
182
+ }
90
183
 
91
- return concat([":", quote].concat(path.map(print, "body")).concat(quote));
184
+ return concat(parts);
92
185
  }
93
186
 
94
187
  function printStringConcat(path, opts, print) {