@ohos-ports/curlconverter 4.12.0-beta.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 (270) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +153 -0
  3. package/dist/src/Headers.d.ts +23 -0
  4. package/dist/src/Headers.js +287 -0
  5. package/dist/src/Headers.js.map +1 -0
  6. package/dist/src/Query.d.ts +8 -0
  7. package/dist/src/Query.js +136 -0
  8. package/dist/src/Query.js.map +1 -0
  9. package/dist/src/Request.d.ts +200 -0
  10. package/dist/src/Request.js +1191 -0
  11. package/dist/src/Request.js.map +1 -0
  12. package/dist/src/Warnings.d.ts +15 -0
  13. package/dist/src/Warnings.js +111 -0
  14. package/dist/src/Warnings.js.map +1 -0
  15. package/dist/src/cli.d.ts +2 -0
  16. package/dist/src/cli.js +333 -0
  17. package/dist/src/cli.js.map +1 -0
  18. package/dist/src/curl/auth.d.ts +12 -0
  19. package/dist/src/curl/auth.js +40 -0
  20. package/dist/src/curl/auth.js.map +1 -0
  21. package/dist/src/curl/form.d.ts +28 -0
  22. package/dist/src/curl/form.js +218 -0
  23. package/dist/src/curl/form.js.map +1 -0
  24. package/dist/src/curl/opts.d.ts +2052 -0
  25. package/dist/src/curl/opts.js +1000 -0
  26. package/dist/src/curl/opts.js.map +1 -0
  27. package/dist/src/curl/url.d.ts +14 -0
  28. package/dist/src/curl/url.js +218 -0
  29. package/dist/src/curl/url.js.map +1 -0
  30. package/dist/src/generators/ansible.d.ts +5 -0
  31. package/dist/src/generators/ansible.js +282 -0
  32. package/dist/src/generators/ansible.js.map +1 -0
  33. package/dist/src/generators/c.d.ts +12 -0
  34. package/dist/src/generators/c.js +1230 -0
  35. package/dist/src/generators/c.js.map +1 -0
  36. package/dist/src/generators/cfml.d.ts +5 -0
  37. package/dist/src/generators/cfml.js +134 -0
  38. package/dist/src/generators/cfml.js.map +1 -0
  39. package/dist/src/generators/clojure.d.ts +7 -0
  40. package/dist/src/generators/clojure.js +436 -0
  41. package/dist/src/generators/clojure.js.map +1 -0
  42. package/dist/src/generators/csharp.d.ts +8 -0
  43. package/dist/src/generators/csharp.js +356 -0
  44. package/dist/src/generators/csharp.js.map +1 -0
  45. package/dist/src/generators/dart.d.ts +5 -0
  46. package/dist/src/generators/dart.js +273 -0
  47. package/dist/src/generators/dart.js.map +1 -0
  48. package/dist/src/generators/elixir.d.ts +8 -0
  49. package/dist/src/generators/elixir.js +296 -0
  50. package/dist/src/generators/elixir.js.map +1 -0
  51. package/dist/src/generators/go.d.ts +5 -0
  52. package/dist/src/generators/go.js +228 -0
  53. package/dist/src/generators/go.js.map +1 -0
  54. package/dist/src/generators/har.d.ts +7 -0
  55. package/dist/src/generators/har.js +115 -0
  56. package/dist/src/generators/har.js.map +1 -0
  57. package/dist/src/generators/http.d.ts +7 -0
  58. package/dist/src/generators/http.js +165 -0
  59. package/dist/src/generators/http.js.map +1 -0
  60. package/dist/src/generators/httpie.d.ts +5 -0
  61. package/dist/src/generators/httpie.js +446 -0
  62. package/dist/src/generators/httpie.js.map +1 -0
  63. package/dist/src/generators/java/httpurlconnection.d.ts +5 -0
  64. package/dist/src/generators/java/httpurlconnection.js +123 -0
  65. package/dist/src/generators/java/httpurlconnection.js.map +1 -0
  66. package/dist/src/generators/java/java.d.ts +8 -0
  67. package/dist/src/generators/java/java.js +247 -0
  68. package/dist/src/generators/java/java.js.map +1 -0
  69. package/dist/src/generators/java/jsoup.d.ts +5 -0
  70. package/dist/src/generators/java/jsoup.js +152 -0
  71. package/dist/src/generators/java/jsoup.js.map +1 -0
  72. package/dist/src/generators/java/okhttp.d.ts +5 -0
  73. package/dist/src/generators/java/okhttp.js +232 -0
  74. package/dist/src/generators/java/okhttp.js.map +1 -0
  75. package/dist/src/generators/javascript/axios.d.ts +5 -0
  76. package/dist/src/generators/javascript/axios.js +332 -0
  77. package/dist/src/generators/javascript/axios.js.map +1 -0
  78. package/dist/src/generators/javascript/got.d.ts +5 -0
  79. package/dist/src/generators/javascript/got.js +277 -0
  80. package/dist/src/generators/javascript/got.js.map +1 -0
  81. package/dist/src/generators/javascript/http.d.ts +8 -0
  82. package/dist/src/generators/javascript/http.js +183 -0
  83. package/dist/src/generators/javascript/http.js.map +1 -0
  84. package/dist/src/generators/javascript/javascript.d.ts +37 -0
  85. package/dist/src/generators/javascript/javascript.js +676 -0
  86. package/dist/src/generators/javascript/javascript.js.map +1 -0
  87. package/dist/src/generators/javascript/jquery.d.ts +15 -0
  88. package/dist/src/generators/javascript/jquery.js +297 -0
  89. package/dist/src/generators/javascript/jquery.js.map +1 -0
  90. package/dist/src/generators/javascript/ky.d.ts +7 -0
  91. package/dist/src/generators/javascript/ky.js +245 -0
  92. package/dist/src/generators/javascript/ky.js.map +1 -0
  93. package/dist/src/generators/javascript/request.d.ts +5 -0
  94. package/dist/src/generators/javascript/request.js +113 -0
  95. package/dist/src/generators/javascript/request.js.map +1 -0
  96. package/dist/src/generators/javascript/superagent.d.ts +10 -0
  97. package/dist/src/generators/javascript/superagent.js +301 -0
  98. package/dist/src/generators/javascript/superagent.js.map +1 -0
  99. package/dist/src/generators/javascript/xhr.d.ts +8 -0
  100. package/dist/src/generators/javascript/xhr.js +147 -0
  101. package/dist/src/generators/javascript/xhr.js.map +1 -0
  102. package/dist/src/generators/json.d.ts +45 -0
  103. package/dist/src/generators/json.js +197 -0
  104. package/dist/src/generators/json.js.map +1 -0
  105. package/dist/src/generators/julia.d.ts +8 -0
  106. package/dist/src/generators/julia.js +365 -0
  107. package/dist/src/generators/julia.js.map +1 -0
  108. package/dist/src/generators/kotlin.d.ts +8 -0
  109. package/dist/src/generators/kotlin.js +325 -0
  110. package/dist/src/generators/kotlin.js.map +1 -0
  111. package/dist/src/generators/lua.d.ts +5 -0
  112. package/dist/src/generators/lua.js +192 -0
  113. package/dist/src/generators/lua.js.map +1 -0
  114. package/dist/src/generators/matlab/common.d.ts +16 -0
  115. package/dist/src/generators/matlab/common.js +238 -0
  116. package/dist/src/generators/matlab/common.js.map +1 -0
  117. package/dist/src/generators/matlab/httpinterface.d.ts +2 -0
  118. package/dist/src/generators/matlab/httpinterface.js +211 -0
  119. package/dist/src/generators/matlab/httpinterface.js.map +1 -0
  120. package/dist/src/generators/matlab/matlab.d.ts +5 -0
  121. package/dist/src/generators/matlab/matlab.js +29 -0
  122. package/dist/src/generators/matlab/matlab.js.map +1 -0
  123. package/dist/src/generators/matlab/webservices.d.ts +2 -0
  124. package/dist/src/generators/matlab/webservices.js +191 -0
  125. package/dist/src/generators/matlab/webservices.js.map +1 -0
  126. package/dist/src/generators/objectivec.d.ts +8 -0
  127. package/dist/src/generators/objectivec.js +269 -0
  128. package/dist/src/generators/objectivec.js.map +1 -0
  129. package/dist/src/generators/ocaml.d.ts +8 -0
  130. package/dist/src/generators/ocaml.js +150 -0
  131. package/dist/src/generators/ocaml.js.map +1 -0
  132. package/dist/src/generators/perl.d.ts +9 -0
  133. package/dist/src/generators/perl.js +209 -0
  134. package/dist/src/generators/perl.js.map +1 -0
  135. package/dist/src/generators/php/guzzle.d.ts +5 -0
  136. package/dist/src/generators/php/guzzle.js +328 -0
  137. package/dist/src/generators/php/guzzle.js.map +1 -0
  138. package/dist/src/generators/php/php.d.ts +8 -0
  139. package/dist/src/generators/php/php.js +217 -0
  140. package/dist/src/generators/php/php.js.map +1 -0
  141. package/dist/src/generators/php/requests.d.ts +5 -0
  142. package/dist/src/generators/php/requests.js +89 -0
  143. package/dist/src/generators/php/requests.js.map +1 -0
  144. package/dist/src/generators/powershell.d.ts +11 -0
  145. package/dist/src/generators/powershell.js +377 -0
  146. package/dist/src/generators/powershell.js.map +1 -0
  147. package/dist/src/generators/python/http.d.ts +5 -0
  148. package/dist/src/generators/python/http.js +128 -0
  149. package/dist/src/generators/python/http.js.map +1 -0
  150. package/dist/src/generators/python/python.d.ts +22 -0
  151. package/dist/src/generators/python/python.js +1693 -0
  152. package/dist/src/generators/python/python.js.map +1 -0
  153. package/dist/src/generators/r/httr.d.ts +10 -0
  154. package/dist/src/generators/r/httr.js +298 -0
  155. package/dist/src/generators/r/httr.js.map +1 -0
  156. package/dist/src/generators/r/httr2.d.ts +5 -0
  157. package/dist/src/generators/r/httr2.js +214 -0
  158. package/dist/src/generators/r/httr2.js.map +1 -0
  159. package/dist/src/generators/ruby/httparty.d.ts +6 -0
  160. package/dist/src/generators/ruby/httparty.js +308 -0
  161. package/dist/src/generators/ruby/httparty.js.map +1 -0
  162. package/dist/src/generators/ruby/ruby.d.ts +14 -0
  163. package/dist/src/generators/ruby/ruby.js +515 -0
  164. package/dist/src/generators/ruby/ruby.js.map +1 -0
  165. package/dist/src/generators/rust.d.ts +8 -0
  166. package/dist/src/generators/rust.js +177 -0
  167. package/dist/src/generators/rust.js.map +1 -0
  168. package/dist/src/generators/swift.d.ts +8 -0
  169. package/dist/src/generators/swift.js +219 -0
  170. package/dist/src/generators/swift.js.map +1 -0
  171. package/dist/src/generators/wget.d.ts +8 -0
  172. package/dist/src/generators/wget.js +472 -0
  173. package/dist/src/generators/wget.js.map +1 -0
  174. package/dist/src/index.d.ts +53 -0
  175. package/dist/src/index.js +54 -0
  176. package/dist/src/index.js.map +1 -0
  177. package/dist/src/parse.d.ts +13 -0
  178. package/dist/src/parse.js +43 -0
  179. package/dist/src/parse.js.map +1 -0
  180. package/dist/src/shell/Parser.d.ts +4 -0
  181. package/dist/src/shell/Parser.js +6 -0
  182. package/dist/src/shell/Parser.js.map +1 -0
  183. package/dist/src/shell/Word.d.ts +48 -0
  184. package/dist/src/shell/Word.js +486 -0
  185. package/dist/src/shell/Word.js.map +1 -0
  186. package/dist/src/shell/tokenizer.d.ts +3 -0
  187. package/dist/src/shell/tokenizer.js +610 -0
  188. package/dist/src/shell/tokenizer.js.map +1 -0
  189. package/dist/src/shell/webParser.d.ts +4 -0
  190. package/dist/src/shell/webParser.js +15 -0
  191. package/dist/src/shell/webParser.js.map +1 -0
  192. package/dist/src/utils.d.ts +6 -0
  193. package/dist/src/utils.js +21 -0
  194. package/dist/src/utils.js.map +1 -0
  195. package/dist/tools/compare-requests.d.ts +2 -0
  196. package/dist/tools/compare-requests.js +448 -0
  197. package/dist/tools/compare-requests.js.map +1 -0
  198. package/dist/tools/gen-test.d.ts +2 -0
  199. package/dist/tools/gen-test.js +114 -0
  200. package/dist/tools/gen-test.js.map +1 -0
  201. package/dist/tree-sitter-bash.wasm +0 -0
  202. package/index.d.ts +3 -0
  203. package/package.json +97 -0
  204. package/src/Headers.ts +334 -0
  205. package/src/Query.ts +147 -0
  206. package/src/Request.ts +1649 -0
  207. package/src/Warnings.ts +154 -0
  208. package/src/cli.ts +561 -0
  209. package/src/curl/auth.ts +45 -0
  210. package/src/curl/form.ts +297 -0
  211. package/src/curl/opts.ts +1417 -0
  212. package/src/curl/url.ts +241 -0
  213. package/src/generators/ansible.ts +364 -0
  214. package/src/generators/c.ts +1371 -0
  215. package/src/generators/cfml.ts +155 -0
  216. package/src/generators/clojure.ts +521 -0
  217. package/src/generators/csharp.ts +383 -0
  218. package/src/generators/dart.ts +293 -0
  219. package/src/generators/elixir.ts +349 -0
  220. package/src/generators/go.ts +269 -0
  221. package/src/generators/har.ts +147 -0
  222. package/src/generators/http.ts +194 -0
  223. package/src/generators/httpie.ts +506 -0
  224. package/src/generators/java/httpurlconnection.ts +151 -0
  225. package/src/generators/java/java.ts +280 -0
  226. package/src/generators/java/jsoup.ts +172 -0
  227. package/src/generators/java/okhttp.ts +257 -0
  228. package/src/generators/javascript/axios.ts +395 -0
  229. package/src/generators/javascript/got.ts +362 -0
  230. package/src/generators/javascript/http.ts +232 -0
  231. package/src/generators/javascript/javascript.ts +825 -0
  232. package/src/generators/javascript/jquery.ts +360 -0
  233. package/src/generators/javascript/ky.ts +315 -0
  234. package/src/generators/javascript/request.ts +152 -0
  235. package/src/generators/javascript/superagent.ts +373 -0
  236. package/src/generators/javascript/xhr.ts +192 -0
  237. package/src/generators/json.ts +293 -0
  238. package/src/generators/julia.ts +398 -0
  239. package/src/generators/kotlin.ts +355 -0
  240. package/src/generators/lua.ts +209 -0
  241. package/src/generators/matlab/common.ts +281 -0
  242. package/src/generators/matlab/httpinterface.ts +284 -0
  243. package/src/generators/matlab/matlab.ts +39 -0
  244. package/src/generators/matlab/webservices.ts +263 -0
  245. package/src/generators/objectivec.ts +297 -0
  246. package/src/generators/ocaml.ts +168 -0
  247. package/src/generators/perl.ts +237 -0
  248. package/src/generators/php/guzzle.ts +368 -0
  249. package/src/generators/php/php.ts +239 -0
  250. package/src/generators/php/requests.ts +103 -0
  251. package/src/generators/powershell.ts +430 -0
  252. package/src/generators/python/http.ts +167 -0
  253. package/src/generators/python/python.ts +1934 -0
  254. package/src/generators/r/httr.ts +328 -0
  255. package/src/generators/r/httr2.ts +268 -0
  256. package/src/generators/ruby/httparty.ts +351 -0
  257. package/src/generators/ruby/ruby.ts +564 -0
  258. package/src/generators/rust.ts +243 -0
  259. package/src/generators/swift.ts +235 -0
  260. package/src/generators/wget.ts +533 -0
  261. package/src/index.ts +89 -0
  262. package/src/parse.ts +72 -0
  263. package/src/shell/Parser.ts +8 -0
  264. package/src/shell/Word.ts +531 -0
  265. package/src/shell/tokenizer.ts +736 -0
  266. package/src/shell/webParser.ts +18 -0
  267. package/src/utils.ts +25 -0
  268. package/tools/compare-requests.ts +571 -0
  269. package/tools/extract_curl_args.py +511 -0
  270. package/tools/gen-test.ts +129 -0
@@ -0,0 +1,825 @@
1
+ import { warnIfPartsIgnored } from "../../Warnings.js";
2
+ import { Word, eq, joinWords } from "../../shell/Word.js";
3
+ import { parse, COMMON_SUPPORTED_ARGS } from "../../parse.js";
4
+ import type { Request, Warnings } from "../../parse.js";
5
+ import { parseQueryString } from "../../Query.js";
6
+ import type { QueryList, QueryDict } from "../../Query.js";
7
+ import type { FormParam } from "../../curl/form.js";
8
+
9
+ import jsescObj from "jsesc";
10
+
11
+ export const javaScriptSupportedArgs = new Set([
12
+ ...COMMON_SUPPORTED_ARGS,
13
+ "upload-file",
14
+ "form",
15
+ "form-string",
16
+ "digest",
17
+ "no-digest",
18
+ "next",
19
+
20
+ // --no-compressed (the default) is unsupported though
21
+ "compressed",
22
+ ]);
23
+
24
+ export const nodeSupportedArgs = new Set([...javaScriptSupportedArgs, "proxy"]);
25
+
26
+ // https://fetch.spec.whatwg.org/#forbidden-method
27
+ export const FORBIDDEN_METHODS = ["CONNECT", "TRACE", "TRACK"];
28
+ // https://fetch.spec.whatwg.org/#forbidden-request-header
29
+ export const FORBIDDEN_HEADERS = [
30
+ "Accept-Charset",
31
+ "Accept-Encoding",
32
+ "Access-Control-Request-Headers",
33
+ "Access-Control-Request-Method",
34
+ "Connection",
35
+ "Content-Length",
36
+ "Cookie",
37
+ "Cookie2",
38
+ "Date",
39
+ "DNT",
40
+ "Expect",
41
+ "Host",
42
+ "Keep-Alive",
43
+ "Origin",
44
+ "Referer",
45
+ "Set-Cookie",
46
+ "TE",
47
+ "Trailer",
48
+ "Transfer-Encoding",
49
+ "Upgrade",
50
+ "Via",
51
+ ].map((h) => h.toLowerCase());
52
+
53
+ // TODO: implement?
54
+ export function reprObj(value: object, indentLevel?: number): string {
55
+ const escaped = jsescObj(value, {
56
+ quotes: "single",
57
+ minimal: false,
58
+ compact: false,
59
+ indent: " ",
60
+ indentLevel: indentLevel ? indentLevel : 0,
61
+ });
62
+ if (typeof value === "string") {
63
+ return "'" + escaped + "'";
64
+ }
65
+ return escaped;
66
+ }
67
+
68
+ export function reprPairs(
69
+ d: [Word, Word][],
70
+ indentLevel = 0,
71
+ indent = " ",
72
+ list = true,
73
+ imports: JSImports,
74
+ ): string {
75
+ if (d.length === 0) {
76
+ return list ? "[]" : "{}";
77
+ }
78
+
79
+ let code = list ? "[\n" : "{\n";
80
+ for (const [i, [key, value]] of d.entries()) {
81
+ code += indent.repeat(indentLevel + 1);
82
+ if (list) {
83
+ code += "[" + repr(key, imports) + ", " + repr(value, imports) + "]";
84
+ } else {
85
+ code += repr(key, imports) + ": " + repr(value, imports);
86
+ }
87
+ code += i < d.length - 1 ? ",\n" : "\n";
88
+ }
89
+ code += indent.repeat(indentLevel) + (list ? "]" : "}");
90
+ return code;
91
+ }
92
+ export function reprAsStringToStringDict(
93
+ d: [Word, Word][],
94
+ indentLevel = 0,
95
+ imports: JSImports,
96
+ indent = " ",
97
+ ): string {
98
+ return reprPairs(d, indentLevel, indent, false, imports);
99
+ }
100
+
101
+ export function reprAsStringTuples(
102
+ d: [Word, Word][],
103
+ indentLevel = 0,
104
+ imports: JSImports,
105
+ indent = " ",
106
+ ): string {
107
+ return reprPairs(d, indentLevel, indent, true, imports);
108
+ }
109
+
110
+ export function reprStringToStringList(
111
+ d: [Word, Word | Word[]][],
112
+ indentLevel = 0,
113
+ imports: JSImports,
114
+ indent = " ",
115
+ list = true,
116
+ ): string {
117
+ if (d.length === 0) {
118
+ return list ? "[]" : "{}";
119
+ }
120
+
121
+ let code = "{\n";
122
+ for (const [i, [key, value]] of d.entries()) {
123
+ let valueStr;
124
+ if (Array.isArray(value)) {
125
+ valueStr = "[" + value.map((v) => repr(v, imports)).join(", ") + "]";
126
+ } else {
127
+ valueStr = repr(value, imports);
128
+ }
129
+
130
+ code += indent.repeat(indentLevel + 1);
131
+ code += repr(key, imports) + ": " + valueStr;
132
+ code += i < d.length - 1 ? ",\n" : "\n";
133
+ }
134
+ code += indent.repeat(indentLevel) + "}";
135
+ return code;
136
+ }
137
+
138
+ // Backtick quotes are not supported
139
+ const regexEscape = /'|"|\\|\p{C}|[^ \P{Z}]/gu;
140
+ const regexDigit = /[0-9]/;
141
+ export function esc(s: string, quote: "'" | '"' = "'"): string {
142
+ return s.replace(regexEscape, (c: string, index: number, string: string) => {
143
+ switch (c[0]) {
144
+ // https://mathiasbynens.be/notes/javascript-escapes#single
145
+ case "\\":
146
+ return "\\\\";
147
+ case "\b":
148
+ return "\\b";
149
+ case "\f":
150
+ return "\\f";
151
+ case "\n":
152
+ return "\\n";
153
+ case "\r":
154
+ return "\\r";
155
+ case "\t":
156
+ return "\\t";
157
+ case "\v":
158
+ return "\\v";
159
+ case "'":
160
+ case '"':
161
+ return c === quote ? "\\" + c : c;
162
+ case "\0":
163
+ // \0 is null but \01 is an octal escape
164
+ // if we have ['\0', '1', '2']
165
+ // and we converted it to '\\012', it would be interpreted as octal
166
+ // so it needs to be converted to '\\x0012'
167
+ if (!regexDigit.test(string.charAt(index + 1))) {
168
+ return "\\0";
169
+ }
170
+ break;
171
+ }
172
+
173
+ if (c.length === 2) {
174
+ const first = c.charCodeAt(0);
175
+ const second = c.charCodeAt(1);
176
+ return (
177
+ "\\u" +
178
+ first.toString(16).padStart(4, "0") +
179
+ "\\u" +
180
+ second.toString(16).padStart(4, "0")
181
+ );
182
+ }
183
+
184
+ const hex = c.charCodeAt(0).toString(16);
185
+ if (hex.length > 2) {
186
+ return "\\u" + hex.padStart(4, "0");
187
+ }
188
+ return "\\x" + hex.padStart(2, "0");
189
+ });
190
+ }
191
+
192
+ export function reprStr(s: string, quote?: "'" | '"'): string {
193
+ if (quote === undefined) {
194
+ quote = "'";
195
+ if (s.includes("'") && !s.includes('"')) {
196
+ quote = '"';
197
+ }
198
+ }
199
+ return quote + esc(s, quote) + quote;
200
+ }
201
+
202
+ export type JSImports = [string, string][];
203
+ export function addImport(imports: JSImports, name: string, from: string) {
204
+ // TODO: this is linear
205
+ for (const [n, f] of imports) {
206
+ if (n === name && f === from) return;
207
+ }
208
+ imports.push([name, from]);
209
+ }
210
+ export function reprImports(imports: JSImports): string {
211
+ let ret = "";
212
+ for (const [name, from] of imports.sort(bySecondElem)) {
213
+ if (name.startsWith("* as")) {
214
+ ret += `import ${name} from ${reprStr(from)};\n`;
215
+ } else {
216
+ ret += `import { ${name} } from ${reprStr(from)};\n`;
217
+ }
218
+ }
219
+ return ret;
220
+ }
221
+ export function reprImportsRequire(imports: JSImports): string {
222
+ const ret: string[] = [];
223
+
224
+ if (imports.length === 0) {
225
+ return "";
226
+ }
227
+
228
+ for (const [name, from] of imports.sort(bySecondElem)) {
229
+ if (name.startsWith("* as ")) {
230
+ ret.push(
231
+ `const ${name.slice("* as ".length)} = require(${reprStr(from)});`,
232
+ );
233
+ } else if (name.includes(".")) {
234
+ ret.push(`const ${name} = require(${reprStr(from)}).${name};`);
235
+ } else {
236
+ ret.push(`const ${name} = require(${reprStr(from)});`);
237
+ }
238
+ }
239
+ return ret.join("\n") + "\n";
240
+ }
241
+
242
+ export function repr(w: Word, imports: JSImports): string {
243
+ // Node
244
+ const ret: string[] = [];
245
+ for (const t of w.tokens) {
246
+ if (typeof t === "string") {
247
+ ret.push(reprStr(t));
248
+ } else if (t.type === "variable") {
249
+ ret.push("process.env[" + reprStr(t.value) + "]");
250
+ } else {
251
+ ret.push("execSync(" + reprStr(t.value) + ").stdout");
252
+ addImport(imports, "execSync", "node:child_process");
253
+ }
254
+ }
255
+ return ret.join(" + ");
256
+ }
257
+
258
+ export function reprBrowser(w: Word, warnings: [string, string][]): string {
259
+ const ret: string[] = [];
260
+ for (const t of w.tokens) {
261
+ if (typeof t === "string") {
262
+ ret.push(reprStr(t));
263
+ } else {
264
+ ret.push(reprStr(t.text));
265
+ if (t.type === "variable") {
266
+ warnings.push([
267
+ "browser-has-no-env",
268
+ "Can't access environment variable in browser JS: " +
269
+ JSON.stringify(t.value),
270
+ ]);
271
+ } else {
272
+ warnings.push([
273
+ "browser-has-no-shell",
274
+ "Can't run subcommands in browser JS: " + JSON.stringify(t.value),
275
+ ]);
276
+ }
277
+ }
278
+ }
279
+ return ret.join(" + ");
280
+ }
281
+
282
+ export function reprFetch(
283
+ w: Word,
284
+ isNode: boolean,
285
+ imports: JSImports,
286
+ ): string {
287
+ if (!isNode) {
288
+ // TODO: warn
289
+ return reprStr(w.toString());
290
+ }
291
+ return repr(w, imports);
292
+ }
293
+
294
+ export function asParseFloat(w: Word, imports: JSImports): string {
295
+ if (w.isString()) {
296
+ const originalValue = w.toString();
297
+ // TODO: reimplement curl's float parsing instead of parseFloat()
298
+ const asFloat = parseFloat(originalValue);
299
+ if (!isNaN(asFloat)) {
300
+ return originalValue;
301
+ }
302
+ }
303
+ return "parseFloat(" + repr(w, imports) + ")";
304
+ }
305
+ export function asParseFloatTimes1000(w: Word, imports: JSImports): string {
306
+ if (w.isString()) {
307
+ const originalValue = w.toString();
308
+ // TODO: reimplement curl's float parsing instead of parseFloat()
309
+ // TODO: check overflow
310
+ const asFloat = parseFloat(originalValue) * 1000;
311
+ if (!isNaN(asFloat)) {
312
+ return asFloat.toString();
313
+ }
314
+ }
315
+ return "parseFloat(" + repr(w, imports) + ") * 1000";
316
+ }
317
+ export function asParseInt(w: Word, imports: JSImports): string {
318
+ if (w.isString()) {
319
+ const originalValue = w.toString();
320
+ // TODO: reimplement curl's int parsing instead of parseInt()
321
+ const asInt = parseInt(originalValue);
322
+ if (!isNaN(asInt)) {
323
+ return originalValue;
324
+ }
325
+ }
326
+ return "parseInt(" + repr(w, imports) + ")";
327
+ }
328
+
329
+ export function bySecondElem(a: [string, string], b: [string, string]): number {
330
+ return a[1].localeCompare(b[1]);
331
+ }
332
+
333
+ export function toURLSearchParams(
334
+ query: [QueryList, QueryDict | null | undefined],
335
+ imports: JSImports,
336
+ indent = 1,
337
+ ): string {
338
+ const [queryList, queryDict] = query;
339
+ const queryObj =
340
+ queryDict && queryDict.every((q) => !Array.isArray(q[1]))
341
+ ? reprAsStringToStringDict(queryDict as [Word, Word][], indent, imports)
342
+ : reprAsStringTuples(queryList, indent, imports);
343
+ return "new URLSearchParams(" + queryObj + ")";
344
+ }
345
+
346
+ export function toDictOrURLSearchParams(
347
+ query: [QueryList, QueryDict | null | undefined],
348
+ imports: JSImports,
349
+ indent = 1,
350
+ ): string {
351
+ const [queryList, queryDict] = query;
352
+
353
+ if (queryDict && queryDict.every((v) => !Array.isArray(v[1]))) {
354
+ return reprAsStringToStringDict(
355
+ queryDict as [Word, Word][],
356
+ indent,
357
+ imports,
358
+ );
359
+ }
360
+
361
+ return (
362
+ "new URLSearchParams(" +
363
+ reprAsStringTuples(queryList, indent, imports) +
364
+ ")"
365
+ );
366
+ }
367
+
368
+ export function toFormData(
369
+ multipartUploads: FormParam[],
370
+ imports: JSImports,
371
+ fetchImports: Set<string>,
372
+ warnings: Warnings,
373
+ isNode = true,
374
+ ): string {
375
+ let code = "new FormData();\n";
376
+ for (const m of multipartUploads) {
377
+ // TODO: use .set() if all names are unique?
378
+ code += "form.append(" + reprFetch(m.name, isNode, imports) + ", ";
379
+ if ("contentFile" in m) {
380
+ if (isNode) {
381
+ if (eq(m.contentFile, "-")) {
382
+ addImport(imports, "* as fs", "fs");
383
+ code += "fs.readFileSync(0).toString()";
384
+ if (m.filename) {
385
+ code += ", " + reprFetch(m.filename, isNode, imports);
386
+ }
387
+ } else {
388
+ fetchImports.add("fileFromSync");
389
+ // TODO: do this in a way that doesn't set filename="" if we don't have filename
390
+ code +=
391
+ "fileFromSync(" + reprFetch(m.contentFile, isNode, imports) + ")";
392
+ }
393
+ } else {
394
+ // TODO: does the second argument get sent as filename="" ?
395
+ code +=
396
+ "File(['<data goes here>'], " +
397
+ reprFetch(m.contentFile, isNode, imports) +
398
+ ")";
399
+ // TODO: (massive todo) we could read the file if we're running in the command line
400
+ warnings.push([
401
+ "--form",
402
+ "you can't read a file for --form/-F in the browser",
403
+ ]);
404
+ }
405
+ } else {
406
+ code += reprFetch(m.content, isNode, imports);
407
+ }
408
+ code += ");\n";
409
+ }
410
+ return code;
411
+ }
412
+
413
+ function getDataString(
414
+ request: Request,
415
+ data: Word,
416
+ isNode: boolean,
417
+ imports: JSImports,
418
+ ): [string, string | null] {
419
+ const originalStringRepr = reprFetch(data, isNode, imports);
420
+
421
+ const contentType = request.headers.getContentType();
422
+ if (contentType === "application/json") {
423
+ try {
424
+ const dataStr = data.toString();
425
+ const parsed = JSON.parse(dataStr);
426
+ // Only bother for arrays and {}
427
+ if (typeof parsed !== "object" || parsed === null) {
428
+ return [originalStringRepr, null];
429
+ }
430
+ const roundtrips = JSON.stringify(parsed) === dataStr;
431
+ const jsonAsJavaScript = reprObj(parsed, 1);
432
+
433
+ const dataString = "JSON.stringify(" + jsonAsJavaScript + ")";
434
+ return [dataString, roundtrips ? null : originalStringRepr];
435
+ } catch {
436
+ return [originalStringRepr, null];
437
+ }
438
+ }
439
+ if (contentType === "application/x-www-form-urlencoded") {
440
+ try {
441
+ const [queryList, queryDict] = parseQueryString(data);
442
+ if (queryList) {
443
+ // Technically node-fetch sends
444
+ // application/x-www-form-urlencoded;charset=utf-8
445
+ // TODO: handle repeated content-type header
446
+ if (
447
+ eq(
448
+ request.headers.get("content-type"),
449
+ "application/x-www-form-urlencoded",
450
+ )
451
+ ) {
452
+ request.headers.delete("content-type");
453
+ }
454
+ // TODO: check roundtrip, add a comment
455
+ // TODO: this isn't a dict anymore
456
+ return [toURLSearchParams([queryList, queryDict], imports), null];
457
+ }
458
+ return [originalStringRepr, null];
459
+ } catch {
460
+ return [originalStringRepr, null];
461
+ }
462
+ }
463
+ return [originalStringRepr, null];
464
+ }
465
+
466
+ export function getData(
467
+ request: Request,
468
+ isNode: boolean,
469
+ imports: JSImports,
470
+ ): [string, string | null] {
471
+ if (!request.dataArray || request.multipartUploads) {
472
+ return ["", null];
473
+ }
474
+
475
+ if (
476
+ request.dataArray.length === 1 &&
477
+ request.dataArray[0] instanceof Word &&
478
+ request.dataArray[0].isString()
479
+ ) {
480
+ try {
481
+ return getDataString(request, request.dataArray[0], isNode, imports);
482
+ } catch {}
483
+ }
484
+
485
+ const parts = [];
486
+ const hasBinary = request.dataArray.some(
487
+ (d) => !(d instanceof Word) && d.filetype === "binary",
488
+ );
489
+ const encoding = hasBinary ? "" : ", 'utf-8'";
490
+ for (const d of request.dataArray) {
491
+ if (d instanceof Word) {
492
+ parts.push(repr(d, imports));
493
+ } else {
494
+ const { filetype, name, filename } = d;
495
+ if (filetype === "urlencode" && name) {
496
+ // TODO: add this to the previous Word
497
+ parts.push(reprFetch(name, isNode, imports));
498
+ }
499
+ // TODO: use the filetype
500
+ if (eq(filename, "-")) {
501
+ if (isNode) {
502
+ addImport(imports, "* as fs", "fs");
503
+ parts.push("fs.readFileSync(0" + encoding + ")");
504
+ } else {
505
+ // TODO: something else
506
+ // TODO: warn that file needs content
507
+ parts.push("new File([/* contents */], '<stdin>')");
508
+ }
509
+ } else {
510
+ if (isNode) {
511
+ addImport(imports, "* as fs", "fs");
512
+ parts.push(
513
+ "fs.readFileSync(" +
514
+ reprFetch(filename, isNode, imports) +
515
+ encoding +
516
+ ")",
517
+ );
518
+ } else {
519
+ // TODO: warn that file needs content
520
+ parts.push(
521
+ "new File([/* contents */], " +
522
+ reprFetch(filename, isNode, imports) +
523
+ ")",
524
+ );
525
+ }
526
+ }
527
+ }
528
+ }
529
+
530
+ if (parts.length === 0) {
531
+ return ["''", null];
532
+ }
533
+
534
+ if (parts.length === 1) {
535
+ return [parts[0], null];
536
+ }
537
+
538
+ let [start, joiner, end] = ["new ArrayBuffer(", ", ", ")"];
539
+ const totalLength = parts.reduce((a, b) => a + b.length, 0);
540
+ if (totalLength > 80) {
541
+ start += "\n ";
542
+ joiner = ",\n ";
543
+ end = "\n )";
544
+ }
545
+ return [start + parts.join(joiner) + end, null];
546
+ }
547
+
548
+ function requestToJavaScriptOrNode(
549
+ request: Request,
550
+ warnings: Warnings,
551
+ fetchImports: Set<string>,
552
+ imports: JSImports,
553
+ isNode: boolean,
554
+ ): string {
555
+ warnIfPartsIgnored(request, warnings, {
556
+ multipleUrls: true,
557
+ dataReadsFile: true,
558
+ // Not actually supported, just warned per-URL
559
+ queryReadsFile: true,
560
+ });
561
+
562
+ let code = "";
563
+
564
+ if (request.multipartUploads) {
565
+ if (isNode) {
566
+ // TODO: remove once Node 16 is EOL'd on 2023-09-11
567
+ fetchImports.add("FormData");
568
+ }
569
+ code +=
570
+ "const form = " +
571
+ toFormData(
572
+ request.multipartUploads,
573
+ imports,
574
+ fetchImports,
575
+ warnings,
576
+ isNode,
577
+ );
578
+ code += "\n";
579
+ }
580
+
581
+ // Can delete content-type header
582
+ const [dataString, commentedOutDataString] = getData(
583
+ request,
584
+ isNode,
585
+ imports,
586
+ );
587
+
588
+ let fn = "fetch";
589
+ if (request.urls[0].auth && request.authType === "digest") {
590
+ // TODO: if 'Authorization:' header is specified, don't set this
591
+ const [user, password] = request.urls[0].auth;
592
+ addImport(imports, "* as DigestFetch", "digest-fetch");
593
+ code +=
594
+ "const client = new DigestFetch(" +
595
+ reprFetch(user, isNode, imports) +
596
+ ", " +
597
+ reprFetch(password, isNode, imports) +
598
+ ");\n";
599
+ fn = "client.fetch";
600
+ }
601
+
602
+ for (const urlObj of request.urls) {
603
+ code += fn + "(" + reprFetch(urlObj.url, isNode, imports);
604
+ if (urlObj.queryReadsFile) {
605
+ warnings.push([
606
+ "unsafe-query",
607
+ // TODO: better wording
608
+ "the URL query string is not correct, " +
609
+ JSON.stringify("@" + urlObj.queryReadsFile) +
610
+ " means it should read the file " +
611
+ JSON.stringify(urlObj.queryReadsFile),
612
+ ]);
613
+ }
614
+ const method = urlObj.method.toLowerCase();
615
+ const methodStr = urlObj.method.toString();
616
+ if (method.isString() && FORBIDDEN_METHODS.includes(methodStr)) {
617
+ warnings.push([
618
+ "forbidden-method",
619
+ "the method " +
620
+ JSON.stringify(methodStr) +
621
+ " is not allowed in fetch()",
622
+ ]);
623
+ }
624
+
625
+ let optionsCode = "";
626
+ if (!eq(method, "get")) {
627
+ // TODO: If you pass a weird method to fetch() it won't uppercase it
628
+ // const methods = []
629
+ // const method = methods.includes(request.method.toLowerCase()) ? request.method.toUpperCase() : request.method
630
+ optionsCode +=
631
+ " method: " + reprFetch(urlObj.method, isNode, imports) + ",\n";
632
+ }
633
+ if (
634
+ request.headers.length ||
635
+ (urlObj.auth && request.authType === "basic")
636
+ ) {
637
+ optionsCode += " headers: {\n";
638
+ for (const [headerName, headerValue] of request.headers) {
639
+ optionsCode +=
640
+ " " +
641
+ reprFetch(headerName, isNode, imports) +
642
+ ": " +
643
+ reprFetch(headerValue || new Word(), isNode, imports) +
644
+ ",\n";
645
+ if (
646
+ !isNode &&
647
+ headerName.isString() &&
648
+ FORBIDDEN_HEADERS.includes(headerName.toString().toLowerCase())
649
+ ) {
650
+ warnings.push([
651
+ "forbidden-header",
652
+ JSON.stringify(headerName.toString()) +
653
+ " header is forbidden in fetch()",
654
+ ]);
655
+ }
656
+ }
657
+ if (urlObj.auth && request.authType === "basic") {
658
+ // TODO: if -H 'Authorization:' is passed, don't set this
659
+ optionsCode +=
660
+ " 'Authorization': 'Basic ' + btoa(" +
661
+ reprFetch(joinWords(urlObj.auth, ":"), isNode, imports) +
662
+ "),\n";
663
+ }
664
+
665
+ if (optionsCode.endsWith(",\n")) {
666
+ optionsCode = optionsCode.slice(0, -2);
667
+ optionsCode += "\n";
668
+ }
669
+ optionsCode += " },\n";
670
+ }
671
+
672
+ if (urlObj.uploadFile) {
673
+ if (isNode) {
674
+ fetchImports.add("fileFromSync");
675
+ optionsCode +=
676
+ " body: fileFromSync(" +
677
+ reprFetch(urlObj.uploadFile, isNode, imports) +
678
+ "),\n";
679
+ } else {
680
+ optionsCode +=
681
+ " body: File(['<data goes here>'], " +
682
+ reprFetch(urlObj.uploadFile, isNode, imports) +
683
+ "),\n";
684
+ warnings.push([
685
+ "--form",
686
+ "you can't read a file for --upload-file/-F in the browser",
687
+ ]);
688
+ }
689
+ } else if (request.multipartUploads) {
690
+ optionsCode += " body: form,\n";
691
+ } else if (request.data) {
692
+ if (commentedOutDataString) {
693
+ optionsCode += " // body: " + commentedOutDataString + ",\n";
694
+ }
695
+ optionsCode += " body: " + dataString + ",\n";
696
+ }
697
+
698
+ if (isNode && request.proxy) {
699
+ // TODO: do this parsing in utils.ts
700
+ const proxy = request.proxy.includes("://")
701
+ ? request.proxy
702
+ : request.proxy.prepend("http://");
703
+ // TODO: could be more accurate
704
+ let [protocol] = proxy.split("://", 2);
705
+ protocol = protocol.toLowerCase();
706
+
707
+ if (!protocol.toBool()) {
708
+ protocol = new Word("http");
709
+ }
710
+ if (eq(protocol, "socks")) {
711
+ protocol = new Word("socks4");
712
+ proxy.replace(/^socks/, "socks4");
713
+ }
714
+
715
+ if (
716
+ eq(protocol, "socks4") ||
717
+ eq(protocol, "socks5") ||
718
+ eq(protocol, "socks5h") ||
719
+ eq(protocol, "socks4a")
720
+ ) {
721
+ addImport(imports, "{ SocksProxyAgent }", "socks-proxy-agent");
722
+ optionsCode +=
723
+ " agent: new SocksProxyAgent(" +
724
+ reprFetch(proxy, isNode, imports) +
725
+ "),\n";
726
+ } else if (eq(protocol, "http") || eq(protocol, "https")) {
727
+ addImport(imports, "HttpsProxyAgent", "https-proxy-agent");
728
+ optionsCode +=
729
+ " agent: new HttpsProxyAgent(" +
730
+ reprFetch(proxy, isNode, imports) +
731
+ "),\n";
732
+ } else {
733
+ warnings.push([
734
+ "--proxy",
735
+ "failed to parse --proxy/-x or unknown protocol: " + protocol,
736
+ ]);
737
+ // or this?
738
+ // throw new CCError('Unsupported proxy scheme for ' + reprFetch(request.proxy))
739
+ }
740
+ }
741
+
742
+ if (optionsCode) {
743
+ if (optionsCode.endsWith(",\n")) {
744
+ optionsCode = optionsCode.slice(0, -2);
745
+ }
746
+ code += ", {\n";
747
+ code += optionsCode;
748
+ code += "\n}";
749
+ }
750
+
751
+ code += ");\n";
752
+ }
753
+
754
+ // TODO: generate some code for the output, like .json() if 'Accept': 'application/json'
755
+
756
+ return code;
757
+ }
758
+
759
+ export function _toJavaScriptOrNode(
760
+ requests: Request[],
761
+ warnings: Warnings,
762
+ isNode: boolean,
763
+ ): string {
764
+ const fetchImports = new Set<string>();
765
+ const imports: JSImports = [];
766
+
767
+ const code = requests
768
+ .map((r) =>
769
+ requestToJavaScriptOrNode(r, warnings, fetchImports, imports, isNode),
770
+ )
771
+ .join("\n");
772
+
773
+ let importCode = "";
774
+ if (isNode) {
775
+ importCode += "import fetch";
776
+ if (fetchImports.size) {
777
+ importCode += ", { " + Array.from(fetchImports).sort().join(", ") + " }";
778
+ }
779
+ importCode += " from 'node-fetch';\n";
780
+ }
781
+ if (imports.length) {
782
+ for (const [varName, imp] of Array.from(imports).sort(bySecondElem)) {
783
+ // TODO: check this
784
+ importCode += "import " + varName + " from " + reprStr(imp) + ";\n";
785
+ }
786
+ }
787
+
788
+ if (importCode) {
789
+ return importCode + "\n" + code;
790
+ }
791
+ return code;
792
+ }
793
+
794
+ export function _toJavaScript(
795
+ requests: Request[],
796
+ warnings: Warnings = [],
797
+ ): string {
798
+ return _toJavaScriptOrNode(requests, warnings, false);
799
+ }
800
+ export function _toNode(requests: Request[], warnings: Warnings = []): string {
801
+ return _toJavaScriptOrNode(requests, warnings, true);
802
+ }
803
+
804
+ export function toJavaScriptWarn(
805
+ curlCommand: string | string[],
806
+ warnings: Warnings = [],
807
+ ): [string, Warnings] {
808
+ const requests = parse(curlCommand, javaScriptSupportedArgs, warnings);
809
+ return [_toJavaScript(requests, warnings), warnings];
810
+ }
811
+ export function toJavaScript(curlCommand: string | string[]): string {
812
+ const [result] = toJavaScriptWarn(curlCommand);
813
+ return result;
814
+ }
815
+
816
+ export function toNodeWarn(
817
+ curlCommand: string | string[],
818
+ warnings: Warnings = [],
819
+ ): [string, Warnings] {
820
+ const requests = parse(curlCommand, nodeSupportedArgs, warnings);
821
+ return [_toNode(requests, warnings), warnings];
822
+ }
823
+ export function toNode(curlCommand: string | string[]): string {
824
+ return toNodeWarn(curlCommand)[0];
825
+ }