prettier 1.6.1 → 2.0.0.pre.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (149) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +316 -293
  3. data/CONTRIBUTING.md +6 -9
  4. data/LICENSE +1 -1
  5. data/README.md +11 -12
  6. data/dist/haml/embed.js +53 -0
  7. data/dist/haml/parser.js +31 -0
  8. data/{src → dist}/haml/parser.rb +0 -0
  9. data/dist/haml/printer.js +336 -0
  10. data/dist/parser/getInfo.js +17 -0
  11. data/{src → dist}/parser/netcat.js +1 -0
  12. data/dist/parser/parseSync.js +128 -0
  13. data/dist/parser/server.rb +140 -0
  14. data/dist/plugin.js +143 -0
  15. data/dist/prettier.js +15 -0
  16. data/dist/rbs/parser.js +34 -0
  17. data/{src → dist}/rbs/parser.rb +0 -0
  18. data/dist/rbs/printer.js +517 -0
  19. data/dist/ruby/embed.js +110 -0
  20. data/dist/ruby/nodes/alias.js +59 -0
  21. data/{src → dist}/ruby/nodes/aref.js +26 -35
  22. data/dist/ruby/nodes/args.js +165 -0
  23. data/dist/ruby/nodes/arrays.js +126 -0
  24. data/dist/ruby/nodes/assign.js +41 -0
  25. data/dist/ruby/nodes/blocks.js +68 -0
  26. data/dist/ruby/nodes/calls.js +220 -0
  27. data/dist/ruby/nodes/case.js +50 -0
  28. data/dist/ruby/nodes/class.js +54 -0
  29. data/dist/ruby/nodes/commands.js +124 -0
  30. data/dist/ruby/nodes/conditionals.js +242 -0
  31. data/dist/ruby/nodes/constants.js +38 -0
  32. data/dist/ruby/nodes/flow.js +66 -0
  33. data/dist/ruby/nodes/hashes.js +130 -0
  34. data/dist/ruby/nodes/heredocs.js +30 -0
  35. data/dist/ruby/nodes/hooks.js +35 -0
  36. data/dist/ruby/nodes/ints.js +27 -0
  37. data/dist/ruby/nodes/lambdas.js +69 -0
  38. data/dist/ruby/nodes/loops.js +73 -0
  39. data/dist/ruby/nodes/massign.js +73 -0
  40. data/dist/ruby/nodes/methods.js +70 -0
  41. data/dist/ruby/nodes/operators.js +70 -0
  42. data/dist/ruby/nodes/params.js +89 -0
  43. data/dist/ruby/nodes/patterns.js +109 -0
  44. data/dist/ruby/nodes/regexp.js +45 -0
  45. data/dist/ruby/nodes/rescue.js +82 -0
  46. data/dist/ruby/nodes/return.js +75 -0
  47. data/dist/ruby/nodes/statements.js +111 -0
  48. data/dist/ruby/nodes/strings.js +218 -0
  49. data/dist/ruby/nodes/super.js +30 -0
  50. data/dist/ruby/nodes/undef.js +26 -0
  51. data/dist/ruby/nodes.js +151 -0
  52. data/dist/ruby/parser.js +34 -0
  53. data/{src → dist}/ruby/parser.rb +1215 -252
  54. data/dist/ruby/printer.js +125 -0
  55. data/dist/ruby/toProc.js +93 -0
  56. data/dist/types/haml.js +4 -0
  57. data/dist/types/plugin.js +3 -0
  58. data/dist/types/rbs.js +4 -0
  59. data/dist/types/ruby.js +4 -0
  60. data/dist/types/utils.js +2 -0
  61. data/dist/types.js +30 -0
  62. data/dist/utils/containsAssignment.js +15 -0
  63. data/dist/utils/getTrailingComma.js +6 -0
  64. data/dist/utils/hasAncestor.js +15 -0
  65. data/{src → dist}/utils/inlineEnsureParens.js +16 -17
  66. data/dist/utils/isEmptyBodyStmt.js +10 -0
  67. data/dist/utils/isEmptyStmts.js +10 -0
  68. data/dist/utils/literal.js +8 -0
  69. data/dist/utils/literallineWithoutBreakParent.js +8 -0
  70. data/dist/utils/makeCall.js +13 -0
  71. data/dist/utils/noIndent.js +11 -0
  72. data/dist/utils/printEmptyCollection.js +44 -0
  73. data/dist/utils/skipAssignIndent.js +15 -0
  74. data/dist/utils.js +30 -0
  75. data/node_modules/prettier/bin-prettier.js +313 -190
  76. data/node_modules/prettier/doc.js +191 -323
  77. data/node_modules/prettier/index.js +2753 -3677
  78. data/node_modules/prettier/package.json +1 -1
  79. data/node_modules/prettier/parser-angular.js +13 -14
  80. data/node_modules/prettier/parser-babel.js +7 -7
  81. data/node_modules/prettier/parser-espree.js +7 -7
  82. data/node_modules/prettier/parser-flow.js +7 -7
  83. data/node_modules/prettier/parser-glimmer.js +1 -1
  84. data/node_modules/prettier/parser-graphql.js +1 -1
  85. data/node_modules/prettier/parser-html.js +17 -17
  86. data/node_modules/prettier/parser-markdown.js +9 -9
  87. data/node_modules/prettier/parser-meriyah.js +7 -7
  88. data/node_modules/prettier/parser-postcss.js +2 -2
  89. data/node_modules/prettier/parser-typescript.js +7 -7
  90. data/node_modules/prettier/parser-yaml.js +2 -2
  91. data/node_modules/prettier/third-party.js +143 -78
  92. data/package.json +26 -18
  93. metadata +74 -67
  94. data/src/haml/embed.js +0 -87
  95. data/src/haml/parser.js +0 -23
  96. data/src/haml/printer.js +0 -438
  97. data/src/parser/parseSync.js +0 -172
  98. data/src/parser/server.rb +0 -66
  99. data/src/plugin.js +0 -148
  100. data/src/prettier.js +0 -16
  101. data/src/rbs/parser.js +0 -37
  102. data/src/rbs/printer.js +0 -643
  103. data/src/ruby/embed.js +0 -142
  104. data/src/ruby/nodes/alias.js +0 -73
  105. data/src/ruby/nodes/args.js +0 -222
  106. data/src/ruby/nodes/arrays.js +0 -162
  107. data/src/ruby/nodes/assign.js +0 -47
  108. data/src/ruby/nodes/blocks.js +0 -90
  109. data/src/ruby/nodes/calls.js +0 -246
  110. data/src/ruby/nodes/case.js +0 -65
  111. data/src/ruby/nodes/class.js +0 -64
  112. data/src/ruby/nodes/commands.js +0 -131
  113. data/src/ruby/nodes/conditionals.js +0 -282
  114. data/src/ruby/nodes/constants.js +0 -43
  115. data/src/ruby/nodes/flow.js +0 -74
  116. data/src/ruby/nodes/hashes.js +0 -155
  117. data/src/ruby/nodes/heredocs.js +0 -36
  118. data/src/ruby/nodes/hooks.js +0 -34
  119. data/src/ruby/nodes/ints.js +0 -31
  120. data/src/ruby/nodes/lambdas.js +0 -76
  121. data/src/ruby/nodes/loops.js +0 -98
  122. data/src/ruby/nodes/massign.js +0 -98
  123. data/src/ruby/nodes/methods.js +0 -74
  124. data/src/ruby/nodes/operators.js +0 -83
  125. data/src/ruby/nodes/params.js +0 -106
  126. data/src/ruby/nodes/patterns.js +0 -157
  127. data/src/ruby/nodes/regexp.js +0 -56
  128. data/src/ruby/nodes/rescue.js +0 -101
  129. data/src/ruby/nodes/return.js +0 -94
  130. data/src/ruby/nodes/statements.js +0 -142
  131. data/src/ruby/nodes/strings.js +0 -272
  132. data/src/ruby/nodes/super.js +0 -35
  133. data/src/ruby/nodes/undef.js +0 -42
  134. data/src/ruby/nodes.js +0 -34
  135. data/src/ruby/parser.js +0 -37
  136. data/src/ruby/printer.js +0 -147
  137. data/src/ruby/toProc.js +0 -105
  138. data/src/utils/containsAssignment.js +0 -11
  139. data/src/utils/getTrailingComma.js +0 -5
  140. data/src/utils/hasAncestor.js +0 -17
  141. data/src/utils/isEmptyBodyStmt.js +0 -7
  142. data/src/utils/isEmptyStmts.js +0 -11
  143. data/src/utils/literal.js +0 -7
  144. data/src/utils/literallineWithoutBreakParent.js +0 -7
  145. data/src/utils/makeCall.js +0 -14
  146. data/src/utils/noIndent.js +0 -10
  147. data/src/utils/printEmptyCollection.js +0 -49
  148. data/src/utils/skipAssignIndent.js +0 -17
  149. data/src/utils.js +0 -13
data/CONTRIBUTING.md CHANGED
@@ -78,16 +78,14 @@ Effectively, it walks the AST in the reverse direction from the way `Ripper` bui
78
78
  As the nodes are printing themselves and their children, they're additionally building up a second AST. That AST is built using the `builder` commands from prettier core, described [here](https://github.com/prettier/prettier/blob/main/commands.md). As an example, below is how a `binary` node (like the one representing the `1 + 1` above) would handle printing itself:
79
79
 
80
80
  ```javascript
81
- const { concat, group, indent, line } = require("prettier").doc.builders;
81
+ const { group, indent, line } = require("prettier").doc.builders;
82
82
 
83
83
  const printBinary = (path, opts, print) =>
84
- group(
85
- concat([
86
- concat([path.call(print, "body", 0), " "]),
87
- path.getValue().body[1],
88
- indent(concat([line, path.call(print, "body", 2)]))
89
- ])
90
- );
84
+ group([
85
+ [path.call(print, "body", 0), " "],
86
+ path.getValue().body[1],
87
+ indent([line, path.call(print, "body", 2)])
88
+ ]);
91
89
  ```
92
90
 
93
91
  Recall that the `binary` node looks like this:
@@ -100,7 +98,6 @@ This means that there is a node in the `0` position of the array that represents
100
98
 
101
99
  So, the `printBinary` function is going to use the following `prettier` builders to build up the intermediate represention:
102
100
 
103
- - `concat` - puts multiple nodes together and prints them without breaking them apart
104
101
  - `group` - marks places where `prettier` could split text if the line gets too long; if the max line length is hit, `prettier` will break apart the outermost `group` node first
105
102
  - `indent` - increases the current print indent for the contents of the node if the parent node is broken, e.g., if the `binary` node is too long to fit on one line, it will indent the right-hand operand
106
103
  - `line` - puts a space if the group is not broken, otherwise puts a newline
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2019-present Kevin Deisz
3
+ Copyright (c) 2019-present Kevin Newton
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -128,18 +128,17 @@ The `prettier` executable is now installed and ready for use:
128
128
 
129
129
  Below are the options (from [`src/plugin.js`](src/plugin.js)) that `@prettier/plugin-ruby` currently supports:
130
130
 
131
- | API Option | CLI Option | Default | Description |
132
- | ------------------- | ----------------------- | :------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
133
- | `printWidth` | `--print-width` | `80` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#print-width)). |
134
- | `requirePragma` | `--require-pragma` | `false` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#require-pragma)). |
135
- | `rubyArrayLiteral` | `--ruby-array-literal` | `true` | When possible, favor the use of string and symbol array literals. |
136
- | `rubyHashLabel` | `--ruby-hash-label` | `true` | When possible, uses the shortened hash key syntax, as opposed to hash rockets. |
137
- | `rubyModifier` | `--ruby-modifier` | `true` | When it fits on one line, allows while and until statements to use the modifier form. |
138
- | `rubyNetcatCommand` | `--ruby-netcat-command` | | The prefix of the command to execute to communicate between the node.js process and the Ruby process. (For example, `"nc -U"` or `"telnet -u"`) Normally you should not set this option. |
139
- | `rubySingleQuote` | `--ruby-single-quote` | `true` | When double quotes are not necessary for interpolation, prefers the use of single quotes for string literals. |
140
- | `rubyToProc` | `--ruby-to-proc` | `false` | When possible, convert blocks to the more concise `Symbol#to_proc` syntax. |
141
- | `tabWidth` | `--tab-width` | `2` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#tab-width)). |
142
- | `trailingComma` | `--trailing-comma` | `"none"` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#trailing-comma)). `"es5"` is equivalent to `true`. |
131
+ | API Option | CLI Option | Default | Description |
132
+ | ------------------ | ---------------------- | :------: | ------------------------------------------------------------------------------------------------------------------------------------ |
133
+ | `printWidth` | `--print-width` | `80` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#print-width)). |
134
+ | `requirePragma` | `--require-pragma` | `false` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#require-pragma)). |
135
+ | `rubyArrayLiteral` | `--ruby-array-literal` | `true` | When possible, favor the use of string and symbol array literals. |
136
+ | `rubyHashLabel` | `--ruby-hash-label` | `true` | When possible, uses the shortened hash key syntax, as opposed to hash rockets. |
137
+ | `rubyModifier` | `--ruby-modifier` | `true` | When it fits on one line, allows while and until statements to use the modifier form. |
138
+ | `rubySingleQuote` | `--ruby-single-quote` | `true` | When double quotes are not necessary for interpolation, prefers the use of single quotes for string literals. |
139
+ | `rubyToProc` | `--ruby-to-proc` | `false` | When possible, convert blocks to the more concise `Symbol#to_proc` syntax. |
140
+ | `tabWidth` | `--tab-width` | `2` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#tab-width)). |
141
+ | `trailingComma` | `--trailing-comma` | `"none"` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#trailing-comma)). `"es5"` is equivalent to `true`. |
143
142
 
144
143
  Any of these can be added to your existing [prettier configuration
145
144
  file](https://prettier.io/docs/en/configuration.html). For example:
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const prettier_1 = __importDefault(require("../prettier"));
7
+ const { hardline, indent, literalline, markAsRoot, mapDoc, stripTrailingHardline } = prettier_1.default;
8
+ // This function is in here because it handles embedded parser values. I don't
9
+ // have a test that exercises it because I'm not sure for which parser it is
10
+ // necessary, but since it's in prettier core I'm keeping it here.
11
+ /* istanbul ignore next */
12
+ function replaceNewlines(doc) {
13
+ return mapDoc(doc, (currentDoc) => typeof currentDoc === "string" && currentDoc.includes("\n")
14
+ ? currentDoc.split(/(\n)/g).map((v, i) => (i % 2 === 0 ? v : literalline))
15
+ : currentDoc);
16
+ }
17
+ const embed = (path, _print, textToDoc, opts) => {
18
+ const node = path.getValue();
19
+ // We're only going to embed other languages on filter nodes.
20
+ if (node.type !== "filter") {
21
+ return null;
22
+ }
23
+ let parser = node.value.name;
24
+ // We don't want to deal with some weird recursive parser situation, so we
25
+ // need to explicitly call out the HAML parser here and just return null.
26
+ if (parser === "haml") {
27
+ return null;
28
+ }
29
+ // In HAML the name of the JS filter is :javascript, whereas in prettier the
30
+ // name of the JS parser is babel. Here we explicitly handle that conversion.
31
+ if (parser === "javascript") {
32
+ parser = "babel";
33
+ }
34
+ // If there aren't any plugins that look like the name of the filter, then we
35
+ // will just exit early.
36
+ if (!opts.plugins.some((plugin) => typeof plugin !== "string" &&
37
+ plugin.parsers &&
38
+ Object.prototype.hasOwnProperty.call(plugin.parsers, parser))) {
39
+ return null;
40
+ }
41
+ // If there is a plugin that has a parser that matches the name of this
42
+ // filter, then we're going to assume that's correct for embedding and go
43
+ // ahead and switch to that parser.
44
+ return markAsRoot([
45
+ ":",
46
+ node.value.name,
47
+ indent([
48
+ hardline,
49
+ replaceNewlines(stripTrailingHardline(textToDoc(node.value.text, { parser })))
50
+ ])
51
+ ]);
52
+ };
53
+ exports.default = embed;
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const parseSync_1 = __importDefault(require("../parser/parseSync"));
7
+ const parser = {
8
+ // This function is responsible for taking an input string of text and
9
+ // returning to prettier a JavaScript object that is the equivalent AST that
10
+ // represents the code stored in that string. We accomplish this by spawning a
11
+ // new process and reading JSON off STDOUT.
12
+ parse(text) {
13
+ return (0, parseSync_1.default)("haml", text);
14
+ },
15
+ astFormat: "haml",
16
+ // This function handles checking whether or not the source string has the
17
+ // pragma for prettier. This is an optional workflow for incremental adoption.
18
+ hasPragma(text) {
19
+ return /^\s*-#\s*@(prettier|format)/.test(text);
20
+ },
21
+ // These functions are just placeholders until we can actually perform this
22
+ // properly. The functions are necessary otherwise the format with cursor
23
+ // functions break.
24
+ locStart() {
25
+ return 0;
26
+ },
27
+ locEnd() {
28
+ return 0;
29
+ }
30
+ };
31
+ exports.default = parser;
File without changes
@@ -0,0 +1,336 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const prettier_1 = __importDefault(require("../prettier"));
7
+ const embed_1 = __importDefault(require("./embed"));
8
+ const { align, fill, group, hardline, ifBreak, indent, join, line, makeString, softline } = prettier_1.default;
9
+ const docTypes = {
10
+ basic: "Basic",
11
+ frameset: "Frameset",
12
+ mobile: "Mobile",
13
+ rdfa: "RDFa",
14
+ strict: "Strict",
15
+ xml: "XML"
16
+ };
17
+ const docVersions = ["1.1", "5"];
18
+ // Prints out a hash key according to the configured prettier options.
19
+ function printHashKey(key, opts) {
20
+ let quoted = key;
21
+ const joiner = opts.rubyHashLabel ? ":" : " =>";
22
+ if (key.includes(":") || key.includes("-")) {
23
+ const quote = opts.rubySingleQuote ? "'" : '"';
24
+ quoted = `${quote}${key}${quote}`;
25
+ }
26
+ return `${opts.rubyHashLabel ? "" : ":"}${quoted}${joiner}`;
27
+ }
28
+ // Prints out the value inside of a hash key-value pair according to the
29
+ // configured prettier options.
30
+ function printHashValue(value, opts) {
31
+ if (typeof value !== "string") {
32
+ return value.toString();
33
+ }
34
+ // This is a very special syntax created by the parser to let us know that
35
+ // this should be printed literally instead of as a string.
36
+ if (value.startsWith("&")) {
37
+ return value.slice(1);
38
+ }
39
+ const quote = opts.rubySingleQuote && !value.includes("#{") && !value.includes("'")
40
+ ? "'"
41
+ : '"';
42
+ return makeString(value, quote);
43
+ }
44
+ // This will print an attributes object to a Doc node. It handles nesting on
45
+ // multiple levels and will print out according to whether or not the version of
46
+ // HAML being used supports multi-line attributes.
47
+ function printAttributes(object, opts, level = 0) {
48
+ if (typeof object !== "object") {
49
+ return printHashValue(object, opts);
50
+ }
51
+ const boundary = level === 0 ? softline : line;
52
+ const parts = Object.keys(object).map((key) => [
53
+ printHashKey(key, opts),
54
+ " ",
55
+ printAttributes(object[key], opts, level + 1)
56
+ ]);
57
+ // If we have support for multi-line attributes laid out like a regular hash,
58
+ // then we print them that way here.
59
+ if (opts.supportsMultiline) {
60
+ return group([
61
+ "{",
62
+ indent(group([boundary, join([",", line], parts)])),
63
+ boundary,
64
+ "}"
65
+ ]);
66
+ }
67
+ // Otherwise, if we only have one attribute, then just print it inline
68
+ // regardless of how long it is.
69
+ if (parts.length === 0) {
70
+ return group(["{", parts[0], "}"]);
71
+ }
72
+ // Otherwise, depending on how long the line is it will split the content into
73
+ // multi-line attributes that old Haml understands.
74
+ return group([
75
+ "{",
76
+ parts[0],
77
+ ",",
78
+ align(opts.headerLength + 1, [line, join([",", line], parts.slice(1))]),
79
+ "}"
80
+ ]);
81
+ }
82
+ // A utility function used in a silent script that is meant to determine if a
83
+ // child node is a continuation of a parent node (as in a when clause within a
84
+ // case statement or an else clause within an if).
85
+ function isContinuation(parentNode, childNode) {
86
+ if (childNode.type !== "silent_script") {
87
+ return false;
88
+ }
89
+ const parent = parentNode.value.keyword;
90
+ const child = childNode.value.keyword;
91
+ return ((parent === "case" && ["when", "else"].includes(child)) ||
92
+ (["if", "unless"].includes(parent) && ["elsif", "else"].includes(child)));
93
+ }
94
+ const printer = {
95
+ embed: embed_1.default,
96
+ // This is our printer's main print function that will switch on the type of
97
+ // node and print it out by returning a Doc tree.
98
+ print(path, opts, print) {
99
+ const node = path.getValue();
100
+ switch (node.type) {
101
+ // https://haml.info/docs/yardoc/file.REFERENCE.html#html-comments-
102
+ case "comment": {
103
+ const { value } = node;
104
+ const parts = ["/"];
105
+ if (value.revealed) {
106
+ parts.push("!");
107
+ }
108
+ if (value.conditional) {
109
+ parts.push(value.conditional);
110
+ }
111
+ else if (value.text) {
112
+ parts.push(" ", value.text);
113
+ }
114
+ return printWithChildren(node, group(parts));
115
+ }
116
+ // https://haml.info/docs/yardoc/file.REFERENCE.html#doctype-
117
+ case "doctype": {
118
+ const { value } = node;
119
+ const parts = ["!!!"];
120
+ if (value.type in docTypes) {
121
+ parts.push(docTypes[value.type]);
122
+ }
123
+ else if (value.version && docVersions.includes(value.version)) {
124
+ parts.push(value.version);
125
+ }
126
+ else {
127
+ parts.push(value.type);
128
+ }
129
+ if (value.encoding) {
130
+ parts.push(value.encoding);
131
+ }
132
+ return group(join(" ", parts));
133
+ }
134
+ // https://haml.info/docs/yardoc/file.REFERENCE.html#filters
135
+ case "filter":
136
+ return group([
137
+ ":",
138
+ node.value.name,
139
+ indent([hardline, join(hardline, node.value.text.trim().split("\n"))])
140
+ ]);
141
+ // https://haml.info/docs/yardoc/file.REFERENCE.html#haml-comments--
142
+ case "haml_comment": {
143
+ const { value } = node;
144
+ const parts = ["-#"];
145
+ if (value.text) {
146
+ if (opts.originalText.split("\n")[node.line - 1].trim() === "-#") {
147
+ const lines = value.text.trim().split("\n");
148
+ parts.push(indent([hardline, join(hardline, lines)]));
149
+ }
150
+ else {
151
+ parts.push(" ", value.text.trim());
152
+ }
153
+ }
154
+ return parts;
155
+ }
156
+ // https://haml.info/docs/yardoc/file.REFERENCE.html#plain-text
157
+ case "plain":
158
+ return node.value.text;
159
+ // The root node in the AST that we build in the parser.
160
+ case "root": {
161
+ const nodePath = path;
162
+ return [join(hardline, nodePath.map(print, "children")), hardline];
163
+ }
164
+ // https://haml.info/docs/yardoc/file.REFERENCE.html#inserting_ruby
165
+ case "script": {
166
+ const { value } = node;
167
+ const parts = [];
168
+ if (value.escape_html) {
169
+ parts.unshift("&");
170
+ }
171
+ if (value.preserve) {
172
+ parts.push("~");
173
+ }
174
+ else if (!value.interpolate) {
175
+ parts.push("=");
176
+ }
177
+ if (value.escape_html && !value.preserve && value.interpolate) {
178
+ parts.push(" ", value.text.trim().slice(1, -1));
179
+ }
180
+ else {
181
+ parts.push(" ", value.text.trim());
182
+ }
183
+ return printWithChildren(node, group(parts));
184
+ }
185
+ // https://haml.info/docs/yardoc/file.REFERENCE.html#running-ruby--
186
+ case "silent_script": {
187
+ const parts = [`- ${node.value.text.trim()}`];
188
+ if (node.children.length > 0) {
189
+ const nodePath = path;
190
+ parts.push(nodePath.map((childPath) => {
191
+ const child = childPath.getValue();
192
+ const concated = [hardline, print(childPath)];
193
+ return isContinuation(node, child) ? concated : indent(concated);
194
+ }, "children"));
195
+ }
196
+ return group(parts);
197
+ }
198
+ // https://haml.info/docs/yardoc/file.REFERENCE.html#element-name-
199
+ case "tag": {
200
+ const { value } = node;
201
+ const { attributes, dynamic_attributes } = value;
202
+ const parts = [];
203
+ // If we have a tag that isn't a div, then we need to print out that
204
+ // name of that tag first. If it is a div, first we'll check if there
205
+ // are any other things that would force us to print out the div
206
+ // explicitly, and otherwise we'll leave it off.
207
+ if (value.name !== "div") {
208
+ parts.push(`%${value.name}`);
209
+ }
210
+ // If we have a class attribute, then we're going to print that here
211
+ // using the special class syntax.
212
+ if (attributes.class) {
213
+ parts.push(`.${attributes.class.replace(/ /g, ".")}`);
214
+ }
215
+ // If we have an id attribute, then we're going to print that here using
216
+ // the special id syntax.
217
+ if (attributes.id) {
218
+ parts.push(`#${attributes.id}`);
219
+ }
220
+ // If we're using dynamic attributes on this tag, then they come in as a
221
+ // string that looks like the output of Hash#inspect from Ruby. So here
222
+ // we're going to split it all up and print it out nicely.
223
+ if (dynamic_attributes.new) {
224
+ const docs = [];
225
+ dynamic_attributes.new
226
+ .slice(1, -2)
227
+ .split(",")
228
+ .forEach((pair, index) => {
229
+ if (index !== 0) {
230
+ docs.push(line);
231
+ }
232
+ docs.push(join("=", pair.slice(1).split('" => ')));
233
+ });
234
+ parts.push(group(["(", align(parts.join("").length + 1, fill(docs)), ")"]));
235
+ }
236
+ // If there are any static attributes that are not class or id (because
237
+ // we already took care of those), then we're going to print them out
238
+ // here.
239
+ const staticAttributes = Object.keys(attributes).filter((name) => !["class", "id"].includes(name));
240
+ if (staticAttributes.length > 0) {
241
+ const docs = staticAttributes.reduce((accum, key) => {
242
+ const doc = `${printHashKey(key, opts)} ${printHashValue(attributes[key], opts)}`;
243
+ return accum.length === 0 ? [doc] : [...accum, ",", line, doc];
244
+ }, []);
245
+ parts.push(group(["{", align(parts.join("").length + 1, fill(docs)), "}"]));
246
+ }
247
+ // If there are dynamic attributes that don't use the newer syntax, then
248
+ // we're going to print them out here.
249
+ if (dynamic_attributes.old) {
250
+ if (parts.length === 0) {
251
+ parts.push("%div");
252
+ }
253
+ if (typeof dynamic_attributes.old === "string") {
254
+ parts.push(dynamic_attributes.old);
255
+ }
256
+ else {
257
+ // This is kind of a total hack in that I don't think you're
258
+ // really supposed to directly use `path.stack`, but it's the
259
+ // easiest way to get the root node without having to know how
260
+ // many levels deep we are.
261
+ const root = path.stack[0];
262
+ parts.push(printAttributes(dynamic_attributes.old, {
263
+ ...opts,
264
+ supportsMultiline: root.supports_multiline,
265
+ headerLength: parts.join("").length
266
+ }));
267
+ }
268
+ }
269
+ // https://haml.info/docs/yardoc/file.REFERENCE.html#object-reference-
270
+ if (value.object_ref) {
271
+ if (parts.length === 0) {
272
+ parts.push("%div");
273
+ }
274
+ parts.push(value.object_ref);
275
+ }
276
+ // https://haml.info/docs/yardoc/file.REFERENCE.html#whitespace-removal--and-
277
+ if (value.nuke_outer_whitespace) {
278
+ parts.push(">");
279
+ }
280
+ if (value.nuke_inner_whitespace) {
281
+ parts.push("<");
282
+ }
283
+ // https://haml.info/docs/yardoc/file.REFERENCE.html#empty-void-tags-
284
+ if (value.self_closing) {
285
+ parts.push("/");
286
+ }
287
+ if (value.value) {
288
+ let contents;
289
+ if (value.parse && value.value.match(/#[{$@]/)) {
290
+ // There's a weird case here where if the value includes
291
+ // interpolation and it's marked as { parse: true }, then we don't
292
+ // actually want the = prefix, and we want to remove extra escaping.
293
+ contents = [
294
+ ifBreak("", " "),
295
+ value.value.slice(1, -1).replace(/\\"/g, '"')
296
+ ];
297
+ }
298
+ else if (value.parse) {
299
+ contents = ["= ", value.value];
300
+ }
301
+ else {
302
+ contents = [ifBreak("", " "), value.value];
303
+ }
304
+ return printWithChildren(node, group([group(parts), indent([softline, ...contents])]));
305
+ }
306
+ // In case none of the other if statements have matched and we're
307
+ // printing a div, we need to explicitly add it back into the array.
308
+ if (parts.length === 0 && value.name === "div") {
309
+ parts.push("%div");
310
+ }
311
+ return printWithChildren(node, group(parts));
312
+ }
313
+ default:
314
+ throw new Error(`Unsupported node encountered: ${node.type}`);
315
+ }
316
+ // It's common to a couple of nodes to attach nested child nodes on the
317
+ // children property. This utility prints them out grouped together with
318
+ // their parent node docs.
319
+ function printWithChildren(node, docs) {
320
+ if (node.children.length === 0) {
321
+ return docs;
322
+ }
323
+ const nodePath = path;
324
+ return group([
325
+ docs,
326
+ indent([hardline, join(hardline, nodePath.map(print, "children"))])
327
+ ]);
328
+ }
329
+ },
330
+ // This function handles adding the format pragma to a source string. This is
331
+ // an optional workflow for incremental adoption.
332
+ insertPragma(text) {
333
+ return `-# @format${text.startsWith("-#") ? "\n" : "\n\n"}${text}`;
334
+ }
335
+ };
336
+ exports.default = printer;
@@ -0,0 +1,17 @@
1
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
2
+ const { existsSync, readFileSync } = require("fs");
3
+
4
+ const filepath = process.argv[process.argv.length - 1];
5
+
6
+ const timeout = setTimeout(() => {
7
+ clearInterval(interval);
8
+ throw new Error("Failed to get information from parse server in time.");
9
+ }, 5000);
10
+
11
+ const interval = setInterval(() => {
12
+ if (existsSync(filepath)) {
13
+ process.stdout.write(readFileSync(filepath).toString("utf8"));
14
+ clearTimeout(timeout);
15
+ clearInterval(interval);
16
+ }
17
+ }, 100);
@@ -2,6 +2,7 @@
2
2
  // On average, this is 2-3x slower than netcat, but still much faster than
3
3
  // spawning a new Ruby process.
4
4
 
5
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
5
6
  const { createConnection } = require("net");
6
7
 
7
8
  const sock = process.argv[process.argv.length - 1];
@@ -0,0 +1,128 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getInfoFilepath = exports.getLang = void 0;
7
+ const child_process_1 = require("child_process");
8
+ const fs_1 = require("fs");
9
+ const os_1 = __importDefault(require("os"));
10
+ const path_1 = __importDefault(require("path"));
11
+ const process_1 = __importDefault(require("process"));
12
+ let parserArgs;
13
+ if (process_1.default.env.PRETTIER_RUBY_HOST) {
14
+ const [cmd, ...args] = process_1.default.env.PRETTIER_RUBY_HOST.split(" ");
15
+ parserArgs = { cmd, args };
16
+ }
17
+ // In order to properly parse ruby code, we need to tell the ruby process to
18
+ // parse using UTF-8. Unfortunately, the way that you accomplish this looks
19
+ // differently depending on your platform.
20
+ /* istanbul ignore next */
21
+ function getLang() {
22
+ const { env, platform } = process_1.default;
23
+ const envValue = env.LC_ALL || env.LC_CTYPE || env.LANG;
24
+ // If an env var is set for the locale that already includes UTF-8 in the
25
+ // name, then assume we can go with that.
26
+ if (envValue && envValue.includes("UTF-8")) {
27
+ return envValue;
28
+ }
29
+ // Otherwise, we're going to guess which encoding to use based on the system.
30
+ // This is probably not the best approach in the world, as you could be on
31
+ // linux and not have C.UTF-8, but in that case you're probably passing an env
32
+ // var for it. This object below represents all of the possible values of
33
+ // process.platform per:
34
+ // https://nodejs.org/api/process.html#process_process_platform
35
+ return {
36
+ aix: "C.UTF-8",
37
+ android: "C.UTF-8",
38
+ cygwin: "C.UTF-8",
39
+ darwin: "en_US.UTF-8",
40
+ freebsd: "C.UTF-8",
41
+ haiku: "C.UTF-8",
42
+ linux: "C.UTF-8",
43
+ netbsd: "C.UTF-8",
44
+ openbsd: "C.UTF-8",
45
+ sunos: "C.UTF-8",
46
+ win32: ".UTF-8"
47
+ }[platform];
48
+ }
49
+ exports.getLang = getLang;
50
+ // Generate the filepath that should be used to communicate the connection
51
+ // information between this process and the parser server.
52
+ function getInfoFilepath() {
53
+ return path_1.default.join(os_1.default.tmpdir(), `prettier-ruby-parser-${process_1.default.pid}.info`);
54
+ }
55
+ exports.getInfoFilepath = getInfoFilepath;
56
+ // Create a file that will act as a communication mechanism, spawn a parser
57
+ // server with that filepath as an argument, then spawn another process that
58
+ // will read that information in order to enable us to connect to it in the
59
+ // spawnSync function.
60
+ function spawnServer() {
61
+ const filepath = getInfoFilepath();
62
+ const server = (0, child_process_1.spawn)("ruby", [path_1.default.join(__dirname, "./server.rb"), filepath], {
63
+ env: Object.assign({}, process_1.default.env, { LANG: getLang() }),
64
+ detached: true,
65
+ stdio: "inherit"
66
+ });
67
+ server.unref();
68
+ process_1.default.on("exit", () => {
69
+ if ((0, fs_1.existsSync)(filepath)) {
70
+ (0, fs_1.unlinkSync)(filepath);
71
+ }
72
+ try {
73
+ if (server.pid) {
74
+ process_1.default.kill(-server.pid);
75
+ }
76
+ }
77
+ catch (e) {
78
+ if (process_1.default.env.PLUGIN_RUBY_CI) {
79
+ throw new Error(`Failed to kill the parser server: ${e}`);
80
+ }
81
+ }
82
+ });
83
+ const info = (0, child_process_1.spawnSync)("node", [
84
+ path_1.default.join(__dirname, "./getInfo.js"),
85
+ filepath
86
+ ]);
87
+ if (info.status !== 0) {
88
+ throw new Error(`
89
+ We failed to spawn our parser server. Please report this error on GitHub
90
+ at https://github.com/prettier/plugin-ruby. The error message was:
91
+
92
+ ${info.stderr.toString()}.
93
+ `);
94
+ }
95
+ const [cmd, ...args] = info.stdout.toString().split(" ");
96
+ return { cmd, args };
97
+ }
98
+ // Formats and sends a request to the parser server. We use netcat (or something
99
+ // like it) here since Prettier requires the results of `parse` to be
100
+ // synchronous and Node.js does not offer a mechanism for synchronous socket
101
+ // requests.
102
+ function parseSync(parser, source) {
103
+ if (!parserArgs) {
104
+ parserArgs = spawnServer();
105
+ }
106
+ const response = (0, child_process_1.spawnSync)(parserArgs.cmd, parserArgs.args, {
107
+ input: `${parser}|${source}`,
108
+ maxBuffer: 15 * 1024 * 1024
109
+ });
110
+ const stdout = response.stdout.toString();
111
+ const stderr = response.stderr.toString();
112
+ const { status } = response;
113
+ // If we didn't receive anything over stdout or we have a bad exit status,
114
+ // then throw whatever we can.
115
+ if (stdout.length === 0 || (status !== null && status !== 0)) {
116
+ throw new Error(stderr || "An unknown error occurred");
117
+ }
118
+ const parsed = JSON.parse(stdout);
119
+ if (parsed.error) {
120
+ const error = new Error(parsed.error);
121
+ if (parsed.loc) {
122
+ error.loc = parsed.loc;
123
+ }
124
+ throw error;
125
+ }
126
+ return parsed;
127
+ }
128
+ exports.default = parseSync;