├── .github └── workflows │ └── test.yml ├── .gitignore ├── Gemfile ├── Gemfile.lock ├── LICENSE.txt ├── README.md ├── index.html ├── package.json ├── resources ├── css │ ├── color-brewer.css │ └── common.css └── js │ ├── curl-to-ruby.js │ └── default.js ├── src ├── curl-to-ruby.js ├── curlToRuby.js ├── default.js ├── highlight.pack.js ├── jsonToRuby.js └── parseCommand.js ├── test.rb ├── webpack.config.js └── yarn.lock /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | branches: 6 | - gh-pages 7 | pull_request: 8 | branches: 9 | - gh-pages 10 | 11 | jobs: 12 | test: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v1 16 | - name: Set up Ruby 2.6 17 | uses: actions/setup-ruby@v1 18 | with: 19 | ruby-version: 2.6.x 20 | - name: Set up Node.js 8.x 21 | uses: actions/setup-node@v1 22 | with: 23 | node-version: 8.x 24 | - name: Install dependencies 25 | run: | 26 | gem install bundler 27 | bundle install --jobs 4 --retry 3 28 | yarn 29 | - name: Build and run tests 30 | run: | 31 | yarn run compile 32 | bundle exec ruby test.rb 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | Thumbs.db 3 | _gitignore 4 | node_modules 5 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gem 'execjs' 3 | gem 'execjs-fastnode' 4 | gem 'minitest' 5 | gem 'pry' 6 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | coderay (1.1.2) 5 | execjs (2.7.0) 6 | execjs-fastnode (0.2.1) 7 | execjs (~> 2.0) 8 | method_source (0.9.2) 9 | minitest (5.11.3) 10 | pry (0.12.2) 11 | coderay (~> 1.1.0) 12 | method_source (~> 0.9.0) 13 | 14 | PLATFORMS 15 | ruby 16 | 17 | DEPENDENCIES 18 | execjs 19 | execjs-fastnode 20 | minitest 21 | pry 22 | 23 | BUNDLED WITH 24 | 1.17.3 25 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Matthew Holt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | curl-to-ruby 2 | ============ 3 | 4 | curl-to-ruby is a tool to instantly convert [curl](http://curl.haxx.se) commands to ruby code using [net/http](http://ruby-doc.org/stdlib-2.1.1/libdoc/net/http/rdoc/Net/HTTP.html) in the browser. It does *not* guarantee high-fidelity conversions, but it's good enough for most API docs that have curl samples. 5 | 6 | ### Try it 7 | 8 | **[Check it out!](https://jhawthorn.github.io/curl-to-ruby)** It works inside your browser. 9 | 10 | 11 | ### FAQ 12 | 13 | #### Does any curl command work? 14 | 15 | Any curl command should work, but only certain flags are understood and converted into ruby code. The rest of the flags will be ignored. 16 | 17 | #### Which kinds of curl commands are understood? 18 | 19 | Mostly simple HTTP commands (headers, basic auth, body, etc). 20 | 21 | #### Will you consider supporting *this-or-that* flag? 22 | 23 | curl has like a bajillion options, so don't expect all of them to be implemented; just the most common/important ones to stub out code from API samples and docs, etc. But feel free to open an issue or submit a pull request! 24 | 25 | 26 | 27 | ### Credits 28 | 29 | Updated to ruby by John Hawthorn ([jhawthorn](https://twitter.com/jhawthorn)) 30 | 31 | Based on [curl-to-Go](https://github.com/mholt/curl-to-go) by Matt Holt ([mholt6](https://twitter.com/mholt6)). 32 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | curl-to-ruby: Convert curl commands to ruby's net/http 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 |

curl-to-ruby

13 |

Instantly convert curl commands to Ruby's net/http

14 | 15 |

16 | Ruby's net/http is notorious for not having the friendliest API, but it isn't all that bad. 17 | Ruby has great gems like faraday, but in libraries and small utilities it's better to kill your dependencies and use what the stdlib provides. 18 |

19 |

20 | This tool turns a curl command into ruby (2.0+) code using net/http. Currently, it knows the following options: -d/--data, -H/--header, -I/--head, -u/--user, -k/--insecure, --url, and -X/--request. 21 |

22 |

23 | There's probably bugs; please contribute on GitHub. Based on curl-to-go. 24 |

25 | 26 |

27 | Simple · 28 | Basic Auth · 29 | JSON · 30 | Complex JSON · 31 | Form Data 32 |

33 | 34 |
35 |
36 | 37 | 42 | 45 | 48 | 57 | 58 | 59 |
60 |
61 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "curl-to-ruby", 3 | "version": "1.0.0", 4 | "private": true, 5 | "description": "curl-to-ruby ============", 6 | "scripts": { 7 | "watch": "webpack --watch --colors --progress --display-error-details", 8 | "compile": "webpack --no-colors -p --display-error-details", 9 | "eslint": "eslint src" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/jhawthorn/curl-to-ruby.git" 14 | }, 15 | "author": "John Hawthorn", 16 | "license": "MIT", 17 | "bugs": { 18 | "url": "https://github.com/jhawthorn/curl-to-ruby/issues" 19 | }, 20 | "homepage": "https://github.com/jhawthorn/curl-to-ruby#readme", 21 | "dependencies": { 22 | "@babel/core": "^7.5.5", 23 | "@babel/preset-env": "^7.5.5", 24 | "babel-loader": "^8.0.6", 25 | "highlight.js": "^9.4.0", 26 | "query-string": "^6.8.2", 27 | "shell-quote": "^1.6.0", 28 | "webpack": "^4.39.2" 29 | }, 30 | "devDependencies": { 31 | "babel-eslint": "^10.0.2", 32 | "expose-loader": "^0.7.1", 33 | "webpack-cli": "^3.3.6" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /resources/css/color-brewer.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Colorbrewer theme 4 | Original: https://github.com/mbostock/colorbrewer-theme (c) Mike Bostock 5 | Ported by Fabrício Tavares de Oliveira 6 | 7 | */ 8 | 9 | .hljs { 10 | display: block; 11 | overflow-x: auto; 12 | padding: 0.5em; 13 | background: #fff; 14 | } 15 | 16 | .hljs, 17 | .hljs-subst { 18 | color: #000; 19 | } 20 | 21 | .hljs-string, 22 | .hljs-meta, 23 | .hljs-symbol, 24 | .hljs-template-tag, 25 | .hljs-template-variable, 26 | .hljs-addition { 27 | color: #756bb1; 28 | } 29 | 30 | .hljs-comment, 31 | .hljs-quote { 32 | color: #636363; 33 | } 34 | 35 | .hljs-number, 36 | .hljs-regexp, 37 | .hljs-literal, 38 | .hljs-bullet, 39 | .hljs-link { 40 | color: #31a354; 41 | } 42 | 43 | .hljs-deletion, 44 | .hljs-variable { 45 | color: #88f; 46 | } 47 | 48 | 49 | 50 | .hljs-keyword, 51 | .hljs-selector-tag, 52 | .hljs-title, 53 | .hljs-section, 54 | .hljs-built_in, 55 | .hljs-doctag, 56 | .hljs-type, 57 | .hljs-tag, 58 | .hljs-name, 59 | .hljs-selector-id, 60 | .hljs-selector-class, 61 | .hljs-strong { 62 | color: #3182bd; 63 | } 64 | 65 | .hljs-emphasis { 66 | font-style: italic; 67 | } 68 | 69 | .hljs-attribute { 70 | color: #e6550d; 71 | } 72 | -------------------------------------------------------------------------------- /resources/css/common.css: -------------------------------------------------------------------------------- 1 | /* 2 | Eric Meyer's Reset CSS v2.0 3 | 4 | NOTICE: This copy of the CSS reset has been modified 5 | to make main tag "display: block" because the default in 6 | even IE 11 is "display: inline", which is not correct. 7 | */ 8 | html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{border:0;font-size:100%;font:inherit;vertical-align:baseline;margin:0;padding:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section{display:block}body{line-height:1}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:before,blockquote:after,q:before,q:after{content:none}table{border-collapse:collapse;border-spacing:0} 9 | 10 | * { 11 | box-sizing: border-box; 12 | } 13 | 14 | body { 15 | font: 14px/1.5em sans-serif; 16 | } 17 | 18 | header, 19 | main, 20 | footer { 21 | width: 85%; 22 | max-width: 900px; 23 | margin: 0 auto; 24 | } 25 | 26 | header { 27 | margin-bottom: 50px; 28 | } 29 | 30 | h1 { 31 | font-size: 48px; 32 | font-weight: bold; 33 | line-height: 1.5em; 34 | text-align: center; 35 | } 36 | 37 | h2 { 38 | font-size: 18px; 39 | line-height: 1.3em; 40 | text-align: center; 41 | margin-bottom: 25px; 42 | } 43 | 44 | p { 45 | max-width: 625px; 46 | margin: 10px auto; 47 | } 48 | 49 | code { 50 | font-family: 'Consolas', 'Menlo', 'Monaco', 'Courier New', monospace; 51 | padding: 1px 3px; 52 | background-color: #EEE; 53 | font-size: 12px; 54 | } 55 | 56 | .examples { 57 | max-width: none; 58 | text-align: center; 59 | } 60 | 61 | .examples a { 62 | margin: 0 3px; 63 | } 64 | 65 | .clr-red { 66 | color: #CC0000; 67 | } 68 | 69 | #input, 70 | #output { 71 | border: 1px solid #CCC; 72 | padding: 10px; 73 | outline: none; 74 | font: 16px/1.5em Consolas, Menlo, Monaco, 'Courier New', monospace; 75 | white-space: pre; 76 | tab-size: 4; 77 | } 78 | 79 | #input { 80 | width: 100%; 81 | margin-bottom: 50px; 82 | overflow-y: auto; 83 | resize: vertical; 84 | } 85 | 86 | #output { 87 | background-color: #F0F8FF; /* "aliceblue" - happened on that by chance choosing a color in chrome inspector tools */ 88 | word-wrap: break-word; 89 | font-size: 14px; 90 | } 91 | 92 | footer { 93 | margin: 50px auto; 94 | text-align: center; 95 | } 96 | -------------------------------------------------------------------------------- /resources/js/curl-to-ruby.js: -------------------------------------------------------------------------------- 1 | !function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=8)}([function(e,t,r){"use strict";const n=r(3),o=r(4),s=r(5);function i(e,t){return t.encode?t.strict?n(e):encodeURIComponent(e):e}function a(e,t){return t.decode?o(e):e}function u(e){const t=e.indexOf("#");return-1!==t&&(e=e.slice(0,t)),e}function c(e){const t=(e=u(e)).indexOf("?");return-1===t?"":e.slice(t+1)}function l(e,t){const r=function(e){let t;switch(e.arrayFormat){case"index":return(e,r,n)=>{t=/\[(\d*)\]$/.exec(e),e=e.replace(/\[\d*\]$/,""),t?(void 0===n[e]&&(n[e]={}),n[e][t[1]]=r):n[e]=r};case"bracket":return(e,r,n)=>{t=/(\[\])$/.exec(e),e=e.replace(/\[\]$/,""),t?void 0!==n[e]?n[e]=[].concat(n[e],r):n[e]=[r]:n[e]=r};case"comma":return(e,t,r)=>{const n="string"==typeof t&&t.split("").indexOf(",")>-1?t.split(","):t;r[e]=n};default:return(e,t,r)=>{void 0!==r[e]?r[e]=[].concat(r[e],t):r[e]=t}}}(t=Object.assign({decode:!0,sort:!0,arrayFormat:"none",parseNumbers:!1,parseBooleans:!1},t)),n=Object.create(null);if("string"!=typeof e)return n;if(!(e=e.trim().replace(/^[?#&]/,"")))return n;for(const o of e.split("&")){let[e,i]=s(o.replace(/\+/g," "),"=");i=void 0===i?null:a(i,t),t.parseNumbers&&!Number.isNaN(Number(i))&&"string"==typeof i&&""!==i.trim()?i=Number(i):!t.parseBooleans||null===i||"true"!==i.toLowerCase()&&"false"!==i.toLowerCase()||(i="true"===i.toLowerCase()),r(a(e,t),i,n)}return!1===t.sort?n:(!0===t.sort?Object.keys(n).sort():Object.keys(n).sort(t.sort)).reduce((e,t)=>{const r=n[t];return Boolean(r)&&"object"==typeof r&&!Array.isArray(r)?e[t]=function e(t){return Array.isArray(t)?t.sort():"object"==typeof t?e(Object.keys(t)).sort((e,t)=>Number(e)-Number(t)).map(e=>t[e]):t}(r):e[t]=r,e},Object.create(null))}t.extract=c,t.parse=l,t.stringify=(e,t)=>{if(!e)return"";const r=function(e){switch(e.arrayFormat){case"index":return t=>(r,n)=>{const o=r.length;return void 0===n?r:null===n?[...r,[i(t,e),"[",o,"]"].join("")]:[...r,[i(t,e),"[",i(o,e),"]=",i(n,e)].join("")]};case"bracket":return t=>(r,n)=>void 0===n?r:null===n?[...r,[i(t,e),"[]"].join("")]:[...r,[i(t,e),"[]=",i(n,e)].join("")];case"comma":return t=>(r,n,o)=>null==n||0===n.length?r:0===o?[[i(t,e),"=",i(n,e)].join("")]:[[r,i(n,e)].join(",")];default:return t=>(r,n)=>void 0===n?r:null===n?[...r,i(t,e)]:[...r,[i(t,e),"=",i(n,e)].join("")]}}(t=Object.assign({encode:!0,strict:!0,arrayFormat:"none"},t)),n=Object.keys(e);return!1!==t.sort&&n.sort(t.sort),n.map(n=>{const o=e[n];return void 0===o?"":null===o?i(n,t):Array.isArray(o)?o.reduce(r(n),[]).join("&"):i(n,t)+"="+i(o,t)}).filter(e=>e.length>0).join("&")},t.parseUrl=(e,t)=>({url:u(e).split("?")[0]||"",query:l(c(e),t)})},function(e,t){t.quote=function(e){return e.map(function(e){return e&&"object"==typeof e?e.op.replace(/(.)/g,"\\$1"):/["\s]/.test(e)&&!/'/.test(e)?"'"+e.replace(/(['\\])/g,"\\$1")+"'":/["'\s]/.test(e)?'"'+e.replace(/(["\\$`!])/g,"\\$1")+'"':e=(e=String(e).replace(/([A-z]:)?([#!"$&'()*,:;<=>?@\[\\\]^`{|}])/g,"$1\\$2")).replace(/\\\\/g,"\\")}).join(" ")};for(var r="(?:"+["\\|\\|","\\&\\&",";;","\\|\\&","\\<\\(",">>",">\\&","[&;()|<>]"].join("|")+")",n="(\\\\['\"|&;()<> \\t]|[^\\s'\"|&;()<> \\t])+",o='"((\\\\"|[^"])*?)"',s="'((\\\\'|[^'])*?)'",i="",a=0;a<4;a++)i+=(Math.pow(16,8)*Math.random()).toString(16);t.parse=function(e,t,a){var u=function(e,t,a){var u=new RegExp(["("+r+")","("+n+"|"+o+"|"+s+")*"].join("|"),"g"),c=e.match(u).filter(Boolean),l=!1;if(!c)return[];t||(t={});a||(a={});return c.map(function(e,n){if(!l){if(RegExp("^"+r+"$").test(e))return{op:e};for(var o=a.escape||"\\",s=!1,u=!1,f="",p=!1,d=0,h=e.length;d0?t.url=e.url[0]:e._.length>1&&(t.url=e._[1]);t.url=function(e){return e&&!new RegExp("^https?://","i").test(e)?"http://"+e:e}(t.url),e.H&&(t.headers=t.headers.concat(e.H));e.header&&(t.headers=t.headers.concat(e.header));(e.I||e.head)&&(t.method="HEAD");e.request&&e.request.length>0?t.method=e.request[e.request.length-1].toUpperCase():e.X&&e.X.length>0&&(t.method=e.X[e.X.length-1].toUpperCase());var r=[],n=[],o=function(e){t.method||(t.method="POST");for(var o=0;o0&&"@"==e[o][0]?n.push(e[o].substr(1)):r.push(e[o])};e.d&&o(e.d);e.data&&o(e.data);e["data-binary"]&&o(e["data-binary"]);r.length>0&&(t.data.ascii=r.join("&"));n.length>0&&(t.data.files=n);var s="";e.user&&e.user.length>0?s=e.user[e.user.length-1]:e.u&&e.u.length>0&&(s=e.u[e.u.length-1]);var i=s.indexOf(":");i>-1?t.basicauth={user:s.substr(0,i),pass:s.substr(i+1)}:t.basicAuth={user:s,pass:""};(e.k||e.insecure)&&(t.insecure=!0);t.method||(t.method="GET");return t}(u);return 0!=c.headers.length||"GET"!=c.method||c.data.ascii||c.data.files||c.basicauth||c.insecure?function(e){for(var i={},u=0;u1&&void 0!==arguments[1]?arguments[1]:"",n=s(t);if(null==t)return"nil";if("boolean"==n)return t.toString();if("number"==n)return t.toString();if("string"==n)return'"'+t.toString()+'"';if(Array.isArray(t)){var o="[\n";return t.forEach(function(t){o+=r+" ",o+=e(t,r+" "),o+=",\n"}),o=o.slice(0,-2),o+="\n"+r+"]"}if("object"==n){var i="{\n";for(var a in t)i+=r+" ",i+=e(a),i+=" => ",i+=e(t[a],r+" "),i+=",\n";return i=i.slice(0,-2),i+="\n"+r+"}"}throw"Invalid JSON object"}(g)+")\n"}else if(a.test(e.data.ascii)){var b=o.a.parse(e.data.ascii);for(var f in h+="request.set_form_data(\n",b){var y=b[f];h+=' "'.concat(l(f),'" => "').concat(l(y),'",\n')}h+=")\n"}else h+='request.body = "'+l(e.data.ascii)+'"\n';if(e.data.files&&e.data.files.length>0){e.data.ascii||(h+='request.body = ""\n');for(u=0;uencodeURIComponent(e).replace(/[!'()*]/g,e=>`%${e.charCodeAt(0).toString(16).toUpperCase()}`)},function(e,t,r){"use strict";var n=new RegExp("%[a-f0-9]{2}","gi"),o=new RegExp("(%[a-f0-9]{2})+","gi");function s(e,t){try{return decodeURIComponent(e.join(""))}catch(e){}if(1===e.length)return e;t=t||1;var r=e.slice(0,t),n=e.slice(t);return Array.prototype.concat.call([],s(r),s(n))}function i(e){try{return decodeURIComponent(e)}catch(o){for(var t=e.match(n),r=1;r{if("string"!=typeof e||"string"!=typeof t)throw new TypeError("Expected the arguments to be of type `string`");if(""===t)return[e];const r=e.indexOf(t);return-1===r?[e]:[e.slice(0,r),e.slice(r+t.length)]}},,,function(e,t,r){r(9)},function(e,t,r){(function(t){e.exports=t.curlToRuby=r(2)}).call(this,r(10))},function(e,t){var r;r=function(){return this}();try{r=r||new Function("return this")()}catch(e){"object"==typeof window&&(r=window)}e.exports=r}]); -------------------------------------------------------------------------------- /resources/js/default.js: -------------------------------------------------------------------------------- 1 | !function(e){var n={};function t(r){if(n[r])return n[r].exports;var a=n[r]={i:r,l:!1,exports:{}};return e[r].call(a.exports,a,a.exports,t),a.l=!0,a.exports}t.m=e,t.c=n,t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:r})},t.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},t.t=function(e,n){if(1&n&&(e=t(e)),8&n)return e;if(4&n&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(t.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var a in e)t.d(r,a,function(n){return e[n]}.bind(null,a));return r},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},t.p="",t(t.s=11)}([function(e,n,t){"use strict";const r=t(3),a=t(4),s=t(5);function i(e,n){return n.encode?n.strict?r(e):encodeURIComponent(e):e}function o(e,n){return n.decode?a(e):e}function c(e){const n=e.indexOf("#");return-1!==n&&(e=e.slice(0,n)),e}function u(e){const n=(e=c(e)).indexOf("?");return-1===n?"":e.slice(n+1)}function l(e,n){const t=function(e){let n;switch(e.arrayFormat){case"index":return(e,t,r)=>{n=/\[(\d*)\]$/.exec(e),e=e.replace(/\[\d*\]$/,""),n?(void 0===r[e]&&(r[e]={}),r[e][n[1]]=t):r[e]=t};case"bracket":return(e,t,r)=>{n=/(\[\])$/.exec(e),e=e.replace(/\[\]$/,""),n?void 0!==r[e]?r[e]=[].concat(r[e],t):r[e]=[t]:r[e]=t};case"comma":return(e,n,t)=>{const r="string"==typeof n&&n.split("").indexOf(",")>-1?n.split(","):n;t[e]=r};default:return(e,n,t)=>{void 0!==t[e]?t[e]=[].concat(t[e],n):t[e]=n}}}(n=Object.assign({decode:!0,sort:!0,arrayFormat:"none",parseNumbers:!1,parseBooleans:!1},n)),r=Object.create(null);if("string"!=typeof e)return r;if(!(e=e.trim().replace(/^[?#&]/,"")))return r;for(const a of e.split("&")){let[e,i]=s(a.replace(/\+/g," "),"=");i=void 0===i?null:o(i,n),n.parseNumbers&&!Number.isNaN(Number(i))&&"string"==typeof i&&""!==i.trim()?i=Number(i):!n.parseBooleans||null===i||"true"!==i.toLowerCase()&&"false"!==i.toLowerCase()||(i="true"===i.toLowerCase()),t(o(e,n),i,r)}return!1===n.sort?r:(!0===n.sort?Object.keys(r).sort():Object.keys(r).sort(n.sort)).reduce((e,n)=>{const t=r[n];return Boolean(t)&&"object"==typeof t&&!Array.isArray(t)?e[n]=function e(n){return Array.isArray(n)?n.sort():"object"==typeof n?e(Object.keys(n)).sort((e,n)=>Number(e)-Number(n)).map(e=>n[e]):n}(t):e[n]=t,e},Object.create(null))}n.extract=u,n.parse=l,n.stringify=(e,n)=>{if(!e)return"";const t=function(e){switch(e.arrayFormat){case"index":return n=>(t,r)=>{const a=t.length;return void 0===r?t:null===r?[...t,[i(n,e),"[",a,"]"].join("")]:[...t,[i(n,e),"[",i(a,e),"]=",i(r,e)].join("")]};case"bracket":return n=>(t,r)=>void 0===r?t:null===r?[...t,[i(n,e),"[]"].join("")]:[...t,[i(n,e),"[]=",i(r,e)].join("")];case"comma":return n=>(t,r,a)=>null==r||0===r.length?t:0===a?[[i(n,e),"=",i(r,e)].join("")]:[[t,i(r,e)].join(",")];default:return n=>(t,r)=>void 0===r?t:null===r?[...t,i(n,e)]:[...t,[i(n,e),"=",i(r,e)].join("")]}}(n=Object.assign({encode:!0,strict:!0,arrayFormat:"none"},n)),r=Object.keys(e);return!1!==n.sort&&r.sort(n.sort),r.map(r=>{const a=e[r];return void 0===a?"":null===a?i(r,n):Array.isArray(a)?a.reduce(t(r),[]).join("&"):i(r,n)+"="+i(a,n)}).filter(e=>e.length>0).join("&")},n.parseUrl=(e,n)=>({url:c(e).split("?")[0]||"",query:l(u(e),n)})},function(e,n){n.quote=function(e){return e.map(function(e){return e&&"object"==typeof e?e.op.replace(/(.)/g,"\\$1"):/["\s]/.test(e)&&!/'/.test(e)?"'"+e.replace(/(['\\])/g,"\\$1")+"'":/["'\s]/.test(e)?'"'+e.replace(/(["\\$`!])/g,"\\$1")+'"':e=(e=String(e).replace(/([A-z]:)?([#!"$&'()*,:;<=>?@\[\\\]^`{|}])/g,"$1\\$2")).replace(/\\\\/g,"\\")}).join(" ")};for(var t="(?:"+["\\|\\|","\\&\\&",";;","\\|\\&","\\<\\(",">>",">\\&","[&;()|<>]"].join("|")+")",r="(\\\\['\"|&;()<> \\t]|[^\\s'\"|&;()<> \\t])+",a='"((\\\\"|[^"])*?)"',s="'((\\\\'|[^'])*?)'",i="",o=0;o<4;o++)i+=(Math.pow(16,8)*Math.random()).toString(16);n.parse=function(e,n,o){var c=function(e,n,o){var c=new RegExp(["("+t+")","("+r+"|"+a+"|"+s+")*"].join("|"),"g"),u=e.match(c).filter(Boolean),l=!1;if(!u)return[];n||(n={});o||(o={});return u.map(function(e,r){if(!l){if(RegExp("^"+t+"$").test(e))return{op:e};for(var a=o.escape||"\\",s=!1,c=!1,d="",f=!1,g=0,p=e.length;g0?n.url=e.url[0]:e._.length>1&&(n.url=e._[1]);n.url=function(e){return e&&!new RegExp("^https?://","i").test(e)?"http://"+e:e}(n.url),e.H&&(n.headers=n.headers.concat(e.H));e.header&&(n.headers=n.headers.concat(e.header));(e.I||e.head)&&(n.method="HEAD");e.request&&e.request.length>0?n.method=e.request[e.request.length-1].toUpperCase():e.X&&e.X.length>0&&(n.method=e.X[e.X.length-1].toUpperCase());var t=[],r=[],a=function(e){n.method||(n.method="POST");for(var a=0;a0&&"@"==e[a][0]?r.push(e[a].substr(1)):t.push(e[a])};e.d&&a(e.d);e.data&&a(e.data);e["data-binary"]&&a(e["data-binary"]);t.length>0&&(n.data.ascii=t.join("&"));r.length>0&&(n.data.files=r);var s="";e.user&&e.user.length>0?s=e.user[e.user.length-1]:e.u&&e.u.length>0&&(s=e.u[e.u.length-1]);var i=s.indexOf(":");i>-1?n.basicauth={user:s.substr(0,i),pass:s.substr(i+1)}:n.basicAuth={user:s,pass:""};(e.k||e.insecure)&&(n.insecure=!0);n.method||(n.method="GET");return n}(c);return 0!=u.headers.length||"GET"!=u.method||u.data.ascii||u.data.files||u.basicauth||u.insecure?function(e){for(var i={},c=0;c1&&void 0!==arguments[1]?arguments[1]:"",r=s(n);if(null==n)return"nil";if("boolean"==r)return n.toString();if("number"==r)return n.toString();if("string"==r)return'"'+n.toString()+'"';if(Array.isArray(n)){var a="[\n";return n.forEach(function(n){a+=t+" ",a+=e(n,t+" "),a+=",\n"}),a=a.slice(0,-2),a+="\n"+t+"]"}if("object"==r){var i="{\n";for(var o in n)i+=t+" ",i+=e(o),i+=" => ",i+=e(n[o],t+" "),i+=",\n";return i=i.slice(0,-2),i+="\n"+t+"}"}throw"Invalid JSON object"}(b)+")\n"}else if(o.test(e.data.ascii)){var h=a.a.parse(e.data.ascii);for(var d in p+="request.set_form_data(\n",h){var m=h[d];p+=' "'.concat(l(d),'" => "').concat(l(m),'",\n')}p+=")\n"}else p+='request.body = "'+l(e.data.ascii)+'"\n';if(e.data.files&&e.data.files.length>0){e.data.ascii||(p+='request.body = ""\n');for(c=0;cencodeURIComponent(e).replace(/[!'()*]/g,e=>`%${e.charCodeAt(0).toString(16).toUpperCase()}`)},function(e,n,t){"use strict";var r=new RegExp("%[a-f0-9]{2}","gi"),a=new RegExp("(%[a-f0-9]{2})+","gi");function s(e,n){try{return decodeURIComponent(e.join(""))}catch(e){}if(1===e.length)return e;n=n||1;var t=e.slice(0,n),r=e.slice(n);return Array.prototype.concat.call([],s(t),s(r))}function i(e){try{return decodeURIComponent(e)}catch(a){for(var n=e.match(r),t=1;t{if("string"!=typeof e||"string"!=typeof n)throw new TypeError("Expected the arguments to be of type `string`");if(""===n)return[e];const t=e.indexOf(n);return-1===t?[e]:[e.slice(0,t),e.slice(t+n.length)]}},function(e,n,t){var r,a,s;a=function(e){var n,t=[],r=Object.keys,a={},s={},i=/^(no-?highlight|plain|text)$/i,o=/\blang(?:uage)?-([\w-]+)\b/i,c=/((^(<[^>]+>|\t|)+|(?:\n)))/gm,u="",l={classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:void 0};function d(e){return e.replace(/&/g,"&").replace(//g,">")}function f(e){return e.nodeName.toLowerCase()}function g(e,n){var t=e&&e.exec(n);return t&&0===t.index}function p(e){return i.test(e)}function b(e){var n,t={},r=Array.prototype.slice.call(arguments,1);for(n in e)t[n]=e[n];return r.forEach(function(e){for(n in e)t[n]=e[n]}),t}function h(e){var n=[];return function e(t,r){for(var a=t.firstChild;a;a=a.nextSibling)3===a.nodeType?r+=a.nodeValue.length:1===a.nodeType&&(n.push({event:"start",offset:r,node:a}),r=e(a,r),f(a).match(/br|hr|img|input/)||n.push({event:"stop",offset:r,node:a}));return r}(e,0),n}function m(e){if(n&&!e.langApiRestored){for(var t in e.langApiRestored=!0,n)e[t]&&(e[n[t]]=e[t]);(e.contains||[]).concat(e.variants||[]).forEach(m)}}function v(e){function n(e){return e&&e.source||e}function t(t,r){return new RegExp(n(t),"m"+(e.case_insensitive?"i":"")+(r?"g":""))}!function a(s,i){if(!s.compiled){if(s.compiled=!0,s.keywords=s.keywords||s.beginKeywords,s.keywords){var o={},c=function(n,t){e.case_insensitive&&(t=t.toLowerCase()),t.split(" ").forEach(function(e){var t=e.split("|");o[t[0]]=[n,t[1]?Number(t[1]):1]})};"string"==typeof s.keywords?c("keyword",s.keywords):r(s.keywords).forEach(function(e){c(e,s.keywords[e])}),s.keywords=o}s.lexemesRe=t(s.lexemes||/\w+/,!0),i&&(s.beginKeywords&&(s.begin="\\b("+s.beginKeywords.split(" ").join("|")+")\\b"),s.begin||(s.begin=/\B|\b/),s.beginRe=t(s.begin),s.endSameAsBegin&&(s.end=s.begin),s.end||s.endsWithParent||(s.end=/\B|\b/),s.end&&(s.endRe=t(s.end)),s.terminator_end=n(s.end)||"",s.endsWithParent&&i.terminator_end&&(s.terminator_end+=(s.end?"|":"")+i.terminator_end)),s.illegal&&(s.illegalRe=t(s.illegal)),null==s.relevance&&(s.relevance=1),s.contains||(s.contains=[]),s.contains=Array.prototype.concat.apply([],s.contains.map(function(e){return function(e){return e.variants&&!e.cached_variants&&(e.cached_variants=e.variants.map(function(n){return b(e,{variants:null},n)})),e.cached_variants||e.endsWithParent&&[b(e)]||[e]}("self"===e?s:e)})),s.contains.forEach(function(e){a(e,s)}),s.starts&&a(s.starts,i);var u=s.contains.map(function(e){return e.beginKeywords?"\\.?(?:"+e.begin+")\\.?":e.begin}).concat([s.terminator_end,s.illegal]).map(n).filter(Boolean);s.terminators=u.length?t(function(e,t){for(var r=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./,a=0,s="",i=0;i0&&(s+=t);c.length>0;){var u=r.exec(c);if(null==u){s+=c;break}s+=c.substring(0,u.index),c=c.substring(u.index+u[0].length),"\\"==u[0][0]&&u[1]?s+="\\"+String(Number(u[1])+o):(s+=u[0],"("==u[0]&&a++)}}return s}(u,"|"),!0):{exec:function(){return null}}}}(e)}function E(e,n,t,r){function s(e){return new RegExp(e.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&"),"m")}function i(e,n){var t=b.case_insensitive?n[0].toLowerCase():n[0];return e.keywords.hasOwnProperty(t)&&e.keywords[t]}function o(e,n,t,r){var a='')+n+(t?"":u):n}function c(){w+=null!=m.subLanguage?function(){var e="string"==typeof m.subLanguage;if(e&&!a[m.subLanguage])return d(N);var n=e?E(m.subLanguage,N,!0,_[m.subLanguage]):y(N,m.subLanguage.length?m.subLanguage:void 0);return m.relevance>0&&(x+=n.relevance),e&&(_[m.subLanguage]=n.top),o(n.language,n.value,!1,!0)}():function(){var e,n,t,r;if(!m.keywords)return d(N);for(r="",n=0,m.lexemesRe.lastIndex=0,t=m.lexemesRe.exec(N);t;)r+=d(N.substring(n,t.index)),(e=i(m,t))?(x+=e[1],r+=o(e[0],d(t[0]))):r+=d(t[0]),n=m.lexemesRe.lastIndex,t=m.lexemesRe.exec(N);return r+d(N.substr(n))}(),N=""}function f(e){w+=e.className?o(e.className,"",!0):"",m=Object.create(e,{parent:{value:m}})}function p(e,n){if(N+=e,null==n)return c(),0;var r=function(e,n){var t,r;for(t=0,r=n.contains.length;t")+'"');return N+=n,n.length||1}var b=R(e);if(!b)throw new Error('Unknown language: "'+e+'"');v(b);var h,m=r||b,_={},w="";for(h=m;h!==b;h=h.parent)h.className&&(w=o(h.className,"",!0)+w);var N="",x=0;try{for(var O,S,A=0;m.terminators.lastIndex=A,O=m.terminators.exec(n);)S=p(n.substring(A,O.index),O[0]),A=O.index+S;for(p(n.substr(A)),h=m;h.parent;h=h.parent)h.className&&(w+=u);return{relevance:x,value:w,language:e,top:m}}catch(e){if(e.message&&-1!==e.message.indexOf("Illegal"))return{relevance:0,value:d(n)};throw e}}function y(e,n){n=n||l.languages||r(a);var t={relevance:0,value:d(e)},s=t;return n.filter(R).filter(x).forEach(function(n){var r=E(n,e,!1);r.language=n,r.relevance>s.relevance&&(s=r),r.relevance>t.relevance&&(s=t,t=r)}),s.language&&(t.second_best=s),t}function _(e){return l.tabReplace||l.useBR?e.replace(c,function(e,n){return l.useBR&&"\n"===e?"
":l.tabReplace?n.replace(/\t/g,l.tabReplace):""}):e}function w(e){var n,r,a,i,c,u=function(e){var n,t,r,a,s=e.className+" ";if(s+=e.parentNode?e.parentNode.className:"",t=o.exec(s))return R(t[1])?t[1]:"no-highlight";for(n=0,r=(s=s.split(/\s+/)).length;n/g,"\n"):n=e,c=n.textContent,a=u?E(u,c,!0):y(c),(r=h(n)).length&&((i=document.createElementNS("http://www.w3.org/1999/xhtml","div")).innerHTML=a.value,a.value=function(e,n,r){var a=0,s="",i=[];function o(){return e.length&&n.length?e[0].offset!==n[0].offset?e[0].offset"}function u(e){s+=""}function l(e){("start"===e.event?c:u)(e.node)}for(;e.length||n.length;){var g=o();if(s+=d(r.substring(a,g[0].offset)),a=g[0].offset,g===e){i.reverse().forEach(u);do{l(g.splice(0,1)[0]),g=o()}while(g===e&&g.length&&g[0].offset===a);i.reverse().forEach(c)}else"start"===g[0].event?i.push(g[0].node):i.pop(),l(g.splice(0,1)[0])}return s+d(r.substr(a))}(r,h(i),c)),a.value=_(a.value),e.innerHTML=a.value,e.className=function(e,n,t){var r=n?s[n]:t,a=[e.trim()];return e.match(/\bhljs\b/)||a.push("hljs"),-1===e.indexOf(r)&&a.push(r),a.join(" ").trim()}(e.className,u,a.language),e.result={language:a.language,re:a.relevance},a.second_best&&(e.second_best={language:a.second_best.language,re:a.second_best.relevance}))}function N(){if(!N.called){N.called=!0;var e=document.querySelectorAll("pre code");t.forEach.call(e,w)}}function R(e){return e=(e||"").toLowerCase(),a[e]||a[s[e]]}function x(e){var n=R(e);return n&&!n.disableAutodetect}return e.highlight=E,e.highlightAuto=y,e.fixMarkup=_,e.highlightBlock=w,e.configure=function(e){l=b(l,e)},e.initHighlighting=N,e.initHighlightingOnLoad=function(){addEventListener("DOMContentLoaded",N,!1),addEventListener("load",N,!1)},e.registerLanguage=function(n,t){var r=a[n]=t(e);m(r),r.aliases&&r.aliases.forEach(function(e){s[e]=n})},e.listLanguages=function(){return r(a)},e.getLanguage=R,e.autoDetection=x,e.inherit=b,e.IDENT_RE="[a-zA-Z]\\w*",e.UNDERSCORE_IDENT_RE="[a-zA-Z_]\\w*",e.NUMBER_RE="\\b\\d+(\\.\\d+)?",e.C_NUMBER_RE="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",e.BINARY_NUMBER_RE="\\b(0b[01]+)",e.RE_STARTERS_RE="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",e.BACKSLASH_ESCAPE={begin:"\\\\[\\s\\S]",relevance:0},e.APOS_STRING_MODE={className:"string",begin:"'",end:"'",illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},e.QUOTE_STRING_MODE={className:"string",begin:'"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},e.PHRASAL_WORDS_MODE={begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},e.COMMENT=function(n,t,r){var a=e.inherit({className:"comment",begin:n,end:t,contains:[]},r||{});return a.contains.push(e.PHRASAL_WORDS_MODE),a.contains.push({className:"doctag",begin:"(?:TODO|FIXME|NOTE|BUG|XXX):",relevance:0}),a},e.C_LINE_COMMENT_MODE=e.COMMENT("//","$"),e.C_BLOCK_COMMENT_MODE=e.COMMENT("/\\*","\\*/"),e.HASH_COMMENT_MODE=e.COMMENT("#","$"),e.NUMBER_MODE={className:"number",begin:e.NUMBER_RE,relevance:0},e.C_NUMBER_MODE={className:"number",begin:e.C_NUMBER_RE,relevance:0},e.BINARY_NUMBER_MODE={className:"number",begin:e.BINARY_NUMBER_RE,relevance:0},e.CSS_NUMBER_MODE={className:"number",begin:e.NUMBER_RE+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",relevance:0},e.REGEXP_MODE={className:"regexp",begin:/\//,end:/\/[gimuy]*/,illegal:/\n/,contains:[e.BACKSLASH_ESCAPE,{begin:/\[/,end:/\]/,relevance:0,contains:[e.BACKSLASH_ESCAPE]}]},e.TITLE_MODE={className:"title",begin:e.IDENT_RE,relevance:0},e.UNDERSCORE_TITLE_MODE={className:"title",begin:e.UNDERSCORE_IDENT_RE,relevance:0},e.METHOD_GUARD={begin:"\\.\\s*"+e.UNDERSCORE_IDENT_RE,relevance:0},e},s="object"==typeof window&&window||"object"==typeof self&&self,n.nodeType?s&&(s.hljs=a({}),void 0===(r=function(){return s.hljs}.apply(n,[]))||(e.exports=r)):a(n)},function(e,n){e.exports=function(e){var n="[a-zA-Z_]\\w*[!?=]?|[-+~]\\@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?",t={keyword:"and then defined module in return redo if BEGIN retry end for self when next until do begin unless END rescue else break undef not super class case require yield alias while ensure elsif or include attr_reader attr_writer attr_accessor",literal:"true false nil"},r={className:"doctag",begin:"@[A-Za-z]+"},a={begin:"#<",end:">"},s=[e.COMMENT("#","$",{contains:[r]}),e.COMMENT("^\\=begin","^\\=end",{contains:[r],relevance:10}),e.COMMENT("^__END__","\\n$")],i={className:"subst",begin:"#\\{",end:"}",keywords:t},o={className:"string",contains:[e.BACKSLASH_ESCAPE,i],variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/`/,end:/`/},{begin:"%[qQwWx]?\\(",end:"\\)"},{begin:"%[qQwWx]?\\[",end:"\\]"},{begin:"%[qQwWx]?{",end:"}"},{begin:"%[qQwWx]?<",end:">"},{begin:"%[qQwWx]?/",end:"/"},{begin:"%[qQwWx]?%",end:"%"},{begin:"%[qQwWx]?-",end:"-"},{begin:"%[qQwWx]?\\|",end:"\\|"},{begin:/\B\?(\\\d{1,3}|\\x[A-Fa-f0-9]{1,2}|\\u[A-Fa-f0-9]{4}|\\?\S)\b/},{begin:/<<(-?)\w+$/,end:/^\s*\w+$/}]},c={className:"params",begin:"\\(",end:"\\)",endsParent:!0,keywords:t},u=[o,a,{className:"class",beginKeywords:"class module",end:"$|;",illegal:/=/,contains:[e.inherit(e.TITLE_MODE,{begin:"[A-Za-z_]\\w*(::\\w+)*(\\?|\\!)?"}),{begin:"<\\s*",contains:[{begin:"("+e.IDENT_RE+"::)?"+e.IDENT_RE}]}].concat(s)},{className:"function",beginKeywords:"def",end:"$|;",contains:[e.inherit(e.TITLE_MODE,{begin:n}),c].concat(s)},{begin:e.IDENT_RE+"::"},{className:"symbol",begin:e.UNDERSCORE_IDENT_RE+"(\\!|\\?)?:",relevance:0},{className:"symbol",begin:":(?!\\s)",contains:[o,{begin:n}],relevance:0},{className:"number",begin:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",relevance:0},{begin:"(\\$\\W)|((\\$|\\@\\@?)(\\w+))"},{className:"params",begin:/\|/,end:/\|/,keywords:t},{begin:"("+e.RE_STARTERS_RE+"|unless)\\s*",keywords:"unless",contains:[a,{className:"regexp",contains:[e.BACKSLASH_ESCAPE,i],illegal:/\n/,variants:[{begin:"/",end:"/[a-z]*"},{begin:"%r{",end:"}[a-z]*"},{begin:"%r\\(",end:"\\)[a-z]*"},{begin:"%r!",end:"![a-z]*"},{begin:"%r\\[",end:"\\][a-z]*"}]}].concat(s),relevance:0}].concat(s);i.contains=u,c.contains=u;var l=[{begin:/^\s*=>/,starts:{end:"$",contains:u}},{className:"meta",begin:"^([>?]>|[\\w#]+\\(\\w+\\):\\d+:\\d+>|(\\w+-)?\\d+\\.\\d+\\.\\d(p\\d+)?[^>]+>)",starts:{end:"$",contains:u}}];return{aliases:["rb","gemspec","podspec","thor","irb"],keywords:t,illegal:/\/\*/,contains:s.concat(l).concat(u)}}},,,,function(e,n,t){"use strict";t.r(n);var r=t(2),a=t(6),s=t.n(a),i=t(7),o=t.n(i);s.a.registerLanguage("ruby",o.a);var c=s.a;function u(){var e='Ruby code will appear here';function n(){var n=document.getElementById("input").value;document.getElementById("output").innerHTML=function(n){if(!n)return e;try{var t=Object(r.default)(n);if(t)return c.highlight("ruby",t).value}catch(e){return console.log(e),''+e+""}}(n)}["focus","blur","keyup"].forEach(function(e){document.getElementById("input").addEventListener(e,n)}),n(),document.getElementById("output").addEventListener("click",function(){if(document.selection){var e=document.body.createTextRange();e.moveToElementText(this),e.select()}else if(window.getSelection){var n=document.createRange();n.selectNode(this),window.getSelection().addRange(n)}}),window.useExample=function(e){var t=document.getElementById(e).innerHTML.trim();document.getElementById("input").value=t,n()}}"loading"!=document.readyState?u():document.addEventListener("DOMContentLoaded",u)}]); -------------------------------------------------------------------------------- /src/curl-to-ruby.js: -------------------------------------------------------------------------------- 1 | require("expose-loader?curlToRuby!./curlToRuby"); 2 | -------------------------------------------------------------------------------- /src/curlToRuby.js: -------------------------------------------------------------------------------- 1 | /* 2 | curl-to-ruby 3 | 4 | A simple utility to convert curl commands into ruby code. 5 | 6 | Based on curl-to-go by Matt Holt 7 | https://github.com/mholt/curl-to-go 8 | */ 9 | 10 | import queryString from 'query-string'; 11 | import jsonToRuby from './jsonToRuby'; 12 | import parseCommand from "./parseCommand"; 13 | 14 | export default function curlToRuby(curl) { 15 | var prelude = "require 'net/http'\nrequire 'uri'\n"; 16 | var coda = "\n" + 17 | "# response.code\n" + 18 | "# response.body\n"; 19 | 20 | // List of curl flags that are boolean typed; this helps with parsing 21 | // a command like `curl -abc value` to know whether 'value' belongs to '-c' 22 | // or is just a positional argument instead. 23 | var boolOptions = ['#', 'progress-bar', '-', 'next', '0', 'http1.0', 'http1.1', 'http2', 24 | 'no-npn', 'no-alpn', '1', 'tlsv1', '2', 'sslv2', '3', 'sslv3', '4', 'ipv4', '6', 'ipv6', 25 | 'a', 'append', 'anyauth', 'B', 'use-ascii', 'basic', 'compressed', 'create-dirs', 26 | 'crlf', 'digest', 'disable-eprt', 'disable-epsv', 'environment', 'cert-status', 27 | 'false-start', 'f', 'fail', 'ftp-create-dirs', 'ftp-pasv', 'ftp-skip-pasv-ip', 28 | 'ftp-pret', 'ftp-ssl-ccc', 'ftp-ssl-control', 'g', 'globoff', 'G', 'get', 29 | 'ignore-content-length', 'i', 'include', 'I', 'head', 'j', 'junk-session-cookies', 30 | 'J', 'remote-header-name', 'k', 'insecure', 'l', 'list-only', 'L', 'location', 31 | 'location-trusted', 'metalink', 'n', 'netrc', 'N', 'no-buffer', 'netrc-file', 32 | 'netrc-optional', 'negotiate', 'no-keepalive', 'no-sessionid', 'ntlm', 'O', 33 | 'remote-name', 'oauth2-bearer', 'p', 'proxy-tunnel', 'path-as-is', 'post301', 'post302', 34 | 'post303', 'proxy-anyauth', 'proxy-basic', 'proxy-digest', 'proxy-negotiate', 35 | 'proxy-ntlm', 'q', 'raw', 'remote-name-all', 's', 'silent', 'sasl-ir', 'S', 'show-error', 36 | 'ssl', 'ssl-reqd', 'ssl-allow-beast', 'ssl-no-revoke', 'socks5-gssapi-nec', 'tcp-nodelay', 37 | 'tlsv1.0', 'tlsv1.1', 'tlsv1.2', 'tr-encoding', 'trace-time', 'v', 'verbose', 'xattr', 38 | 'h', 'help', 'M', 'manual', 'V', 'version']; 39 | 40 | var httpMethods = { 41 | 'COPY': 'Copy', 42 | 'DELETE': 'Delete', 43 | 'GET': 'Get', 44 | 'HEAD': 'Head', 45 | 'LOCK': 'Lock', 46 | 'MKCOL': 'Mkcol', 47 | 'MoVE': 'Move', 48 | 'OPTIONS': 'Options', 49 | 'PATCH': 'Patch', 50 | 'POST': 'Post', 51 | 'PROPFIND': 'Propfind', 52 | 'PROPPATCH': 'Proppatch', 53 | 'PUT': 'Put', 54 | 'TRACE': 'Trace', 55 | 'UNLOCK': 'Unlock' 56 | }; 57 | 58 | let formUrlEncodedRegex = /^([^\s]+=[^\s]+)(&[^\s]+=[^\s]+)*$/; 59 | 60 | if (!curl.trim()) 61 | return; 62 | var cmd = parseCommand(curl, { boolFlags: boolOptions }); 63 | 64 | if (cmd._[0] != "curl") 65 | throw "Not a curl command"; 66 | 67 | var req = extractRelevantPieces(cmd); 68 | 69 | if (isSimple(req)) { 70 | return renderSimple(req); 71 | } else { 72 | return renderComplex(req); 73 | } 74 | 75 | 76 | // renderSimple renders a simple HTTP request using net/http convenience methods 77 | function renderSimple(req) { 78 | var ruby = ""; 79 | 80 | ruby += 'uri = URI.parse("' + rubyEsc(req.url) + '")\n'; 81 | ruby += 'response = Net::HTTP.get_response(uri)\n'; 82 | 83 | return prelude + "\n" + ruby + coda; 84 | } 85 | 86 | // renderComplex renders Go code that requires making a http.Request. 87 | function renderComplex(req) { 88 | // First, figure out the headers 89 | var headers = {}; 90 | for (var i = 0; i < req.headers.length; i++) { 91 | var split = req.headers[i].indexOf(":"); 92 | if (split == -1) continue; 93 | var name = req.headers[i].substr(0, split).trim(); 94 | var value = req.headers[i].substr(split+1).trim(); 95 | headers[toTitleCase(name)] = value; 96 | } 97 | 98 | delete headers["Accept-Encoding"]; 99 | 100 | var ruby = ""; 101 | 102 | ruby += 'uri = URI.parse("' + rubyEsc(req.url) + '")\n'; 103 | 104 | if (httpMethods[req.method]) { 105 | ruby += 'request = Net::HTTP::'+httpMethods[req.method]+'.new(uri)\n'; 106 | } else { 107 | ruby += 'request = Net::HTTPGenericRequest.new("' + rubyEsc(req.method) + '", false, false, uri)\n'; 108 | } 109 | 110 | // set basic auth 111 | if (req.basicauth) { 112 | ruby += 'request.basic_auth("'+rubyEsc(req.basicauth.user)+'", "'+rubyEsc(req.basicauth.pass)+'")\n'; 113 | } 114 | 115 | if (headers["Content-Type"]) { 116 | ruby += 'request.content_type = "' + rubyEsc(headers["Content-Type"]) + '"\n'; 117 | delete(headers["Content-Type"]); 118 | } 119 | 120 | // set headers 121 | for (var name in headers) { 122 | ruby += 'request["'+rubyEsc(name)+'"] = "'+rubyEsc(headers[name])+'"\n'; 123 | } 124 | 125 | function isJson(json) { 126 | try { 127 | JSON.parse(json); 128 | return true; 129 | } catch (e) { 130 | return false; 131 | } 132 | } 133 | 134 | if (req.data.ascii) { 135 | if (isJson(req.data.ascii)) { 136 | let json = JSON.parse(req.data.ascii); 137 | prelude += "require 'json'\n"; 138 | ruby += "request.body = JSON.dump(" + jsonToRuby(json) + ")\n"; 139 | } else if (formUrlEncodedRegex.test(req.data.ascii)) { 140 | let formData = queryString.parse(req.data.ascii); 141 | ruby += "request.set_form_data(\n"; 142 | for(var name in formData) { 143 | let value = formData[name]; 144 | ruby += ` "${rubyEsc(name)}" => "${rubyEsc(value)}",\n` 145 | } 146 | ruby += ")\n"; 147 | } else { 148 | ruby += 'request.body = "' + rubyEsc(req.data.ascii) + '"\n'; 149 | } 150 | } 151 | 152 | if (req.data.files && req.data.files.length > 0) { 153 | if (!req.data.ascii) { 154 | ruby += 'request.body = ""\n'; 155 | } 156 | 157 | for (var i = 0; i < req.data.files.length; i++) { 158 | ruby += 'request.body << File.read("'+rubyEsc(req.data.files[i])+'").delete("\\r\\n")\n'; 159 | } 160 | } 161 | 162 | ruby += '\n' 163 | ruby += 'req_options = {\n' 164 | ruby += ' use_ssl: uri.scheme == "https",\n' 165 | if (req.insecure) { 166 | prelude += "require 'openssl'\n" 167 | ruby += ' verify_mode: OpenSSL::SSL::VERIFY_NONE,\n' 168 | } 169 | ruby += '}\n' 170 | 171 | ruby += '\n' 172 | ruby += 'response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|\n' 173 | ruby += ' http.request(request)\n' 174 | ruby += 'end\n' 175 | 176 | return prelude + "\n" + ruby + coda; 177 | } 178 | 179 | // extractRelevantPieces returns an object with relevant pieces 180 | // extracted from cmd, the parsed command. This accounts for 181 | // multiple flags that do the same thing and return structured 182 | // data that makes it easy to spit out Go code. 183 | function extractRelevantPieces(cmd) { 184 | var relevant = { 185 | url: "", 186 | method: "", 187 | headers: [], 188 | data: {} 189 | }; 190 | 191 | // prefer --url over unnamed parameter, if it exists; keep first one only 192 | if (cmd.url && cmd.url.length > 0) 193 | relevant.url = cmd.url[0]; 194 | else if (cmd._.length > 1) 195 | relevant.url = cmd._[1]; // position 1 because index 0 is the curl command itself 196 | 197 | relevant.url = fixUrl(relevant.url); 198 | 199 | // gather the headers together 200 | if (cmd.H) 201 | relevant.headers = relevant.headers.concat(cmd.H); 202 | if (cmd.header) 203 | relevant.headers = relevant.headers.concat(cmd.header); 204 | 205 | // set method to HEAD? 206 | if (cmd.I || cmd.head) 207 | relevant.method = "HEAD"; 208 | 209 | // between -X and --request, prefer the long form I guess 210 | if (cmd.request && cmd.request.length > 0) 211 | relevant.method = cmd.request[cmd.request.length-1].toUpperCase(); 212 | else if (cmd.X && cmd.X.length > 0) 213 | relevant.method = cmd.X[cmd.X.length-1].toUpperCase(); // if multiple, use last (according to curl docs) 214 | 215 | // join multiple request body data, if any 216 | var dataAscii = []; 217 | var dataFiles = []; 218 | var loadData = function(d) { 219 | if (!relevant.method) 220 | relevant.method = "POST"; 221 | for (var i = 0; i < d.length; i++) 222 | { 223 | if (d[i].length > 0 && d[i][0] == "@") 224 | dataFiles.push(d[i].substr(1)); 225 | else 226 | dataAscii.push(d[i]); 227 | } 228 | }; 229 | if (cmd.d) 230 | loadData(cmd.d); 231 | if (cmd.data) 232 | loadData(cmd.data); 233 | if (cmd['data-binary']) 234 | loadData(cmd['data-binary']); 235 | if (dataAscii.length > 0) 236 | relevant.data.ascii = dataAscii.join("&"); 237 | if (dataFiles.length > 0) 238 | relevant.data.files = dataFiles; 239 | 240 | // between -u and --user, choose the long form... 241 | var basicAuthString = ""; 242 | if (cmd.user && cmd.user.length > 0) 243 | basicAuthString = cmd.user[cmd.user.length-1]; 244 | else if (cmd.u && cmd.u.length > 0) 245 | basicAuthString = cmd.u[cmd.u.length-1]; 246 | var basicAuthSplit = basicAuthString.indexOf(":"); 247 | if (basicAuthSplit > -1) { 248 | relevant.basicauth = { 249 | user: basicAuthString.substr(0, basicAuthSplit), 250 | pass: basicAuthString.substr(basicAuthSplit+1) 251 | }; 252 | } else { 253 | relevant.basicAuth = { user: basicAuthString, pass: "" }; 254 | } 255 | 256 | if (cmd.k || cmd.insecure) { 257 | relevant.insecure = true; 258 | } 259 | 260 | // default to GET if nothing else specified 261 | if (!relevant.method) 262 | relevant.method = "GET"; 263 | 264 | return relevant; 265 | } 266 | 267 | function fixUrl(url) { 268 | if(url && !(new RegExp("^https?://", "i")).test(url)) { 269 | return "http://" + url; 270 | } else { 271 | return url; 272 | } 273 | } 274 | 275 | function toTitleCase(str) { 276 | return str.replace(/\w*/g, function(txt) { 277 | return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); 278 | }); 279 | } 280 | 281 | function rubyEsc(s) { 282 | return s.replace(/\\/g, '\\\\').replace(/"/g, '\\"'); 283 | } 284 | 285 | function isSimple() { 286 | return req.headers.length == 0 && 287 | req.method == "GET" && 288 | !req.data.ascii && 289 | !req.data.files && 290 | !req.basicauth && 291 | !req.insecure; 292 | } 293 | } 294 | -------------------------------------------------------------------------------- /src/default.js: -------------------------------------------------------------------------------- 1 | import curlToRuby from "./curlToRuby"; 2 | import hljs from "./highlight.pack.js"; 3 | 4 | function init() { 5 | const emptyOutputMsg = "Ruby code will appear here"; 6 | const formattedEmptyOutputMsg = ''+emptyOutputMsg+''; 7 | 8 | function getOutputHTML(input) { 9 | if (!input) { 10 | return formattedEmptyOutputMsg; 11 | } 12 | 13 | try { 14 | const output = curlToRuby(input); 15 | if (output) { 16 | const coloredOutput = hljs.highlight("ruby", output); 17 | return coloredOutput.value; 18 | } 19 | } catch (e) { 20 | console.log(e); 21 | return ''+e+''; 22 | } 23 | } 24 | 25 | function updateOutput() { 26 | const input = document.getElementById('input').value; 27 | const output = document.getElementById('output'); 28 | 29 | output.innerHTML = getOutputHTML(input); 30 | } 31 | 32 | // Update placeholder text 33 | ['focus', 'blur', 'keyup'].forEach((ev) => { 34 | document.getElementById('input').addEventListener(ev, updateOutput); 35 | }); 36 | updateOutput(); 37 | 38 | // Highlights the output for the user 39 | document.getElementById('output').addEventListener('click', function() { 40 | if (document.selection) { 41 | const range = document.body.createTextRange(); 42 | range.moveToElementText(this); 43 | range.select(); 44 | } else if (window.getSelection) { 45 | const range = document.createRange(); 46 | range.selectNode(this); 47 | window.getSelection().addRange(range); 48 | } 49 | }); 50 | 51 | window.useExample = function(name) { 52 | const example = document.getElementById(name).innerHTML.trim(); 53 | const input = document.getElementById('input'); 54 | input.value = example; 55 | updateOutput(); 56 | } 57 | } 58 | 59 | if (document.readyState != 'loading'){ 60 | init(); 61 | } else { 62 | document.addEventListener('DOMContentLoaded', init); 63 | } 64 | -------------------------------------------------------------------------------- /src/highlight.pack.js: -------------------------------------------------------------------------------- 1 | import hljs from "highlight.js/lib/highlight"; 2 | import ruby from "highlight.js/lib/languages/ruby"; 3 | 4 | hljs.registerLanguage('ruby', ruby); 5 | 6 | export default hljs; 7 | -------------------------------------------------------------------------------- /src/jsonToRuby.js: -------------------------------------------------------------------------------- 1 | export default function jsonToRuby(json, indent = "") { 2 | let type = typeof(json); 3 | if (json == null) { 4 | return "nil"; 5 | } else if (type == "boolean") { 6 | return json.toString(); 7 | } else if (type == "number") { 8 | return json.toString(); 9 | } else if (type == "string") { 10 | return '"' + json.toString() + '"'; 11 | } else if (Array.isArray(json)) { 12 | let ret = "[\n"; 13 | json.forEach((element) => { 14 | ret += indent + " "; 15 | ret += jsonToRuby(element, indent + " "); 16 | ret += ",\n"; 17 | }); 18 | ret = ret.slice(0, -2); 19 | ret += "\n" + indent + "]"; 20 | return ret; 21 | } else if (type == "object") { 22 | let ret = "{\n"; 23 | for (var key in json) { 24 | ret += indent + " "; 25 | ret += jsonToRuby(key); 26 | ret += " => "; 27 | ret += jsonToRuby(json[key], indent + " "); 28 | ret += ",\n"; 29 | } 30 | ret = ret.slice(0, -2); 31 | ret += "\n" + indent + "}"; 32 | return ret; 33 | } else { 34 | throw "Invalid JSON object"; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/parseCommand.js: -------------------------------------------------------------------------------- 1 | import {parse} from "shell-quote"; 2 | 3 | export default function parseCommand(input, options) { 4 | if (typeof options === 'undefined') { 5 | options = {}; 6 | } 7 | 8 | // trim \ at and of line 9 | input = input.replace(/\\\n/g, ''); 10 | input = input.trim(); 11 | 12 | let argv = parse(input); 13 | 14 | let argObj = {_: []}; 15 | 16 | function setFlag(name, value) { 17 | argObj[name] || (argObj[name] = []); 18 | argObj[name].push(value); 19 | } 20 | 21 | while(argv.length) { 22 | let flag = argv.shift(); 23 | 24 | /* Assume globs are typos/missing qutotes/shell-quote sillyness w */ 25 | if (flag.op == 'glob') { 26 | flag = flag.pattern; 27 | } 28 | 29 | if (flag[0] == '-'){ 30 | flag = flag.substring(1, flag.length); 31 | if (flag[0] == '-') { /* long argument */ 32 | flag = flag.substring(1, flag.length); 33 | if (boolFlag(flag)) { 34 | argObj[flag] = true; 35 | } else { 36 | if (flag.indexOf('=') > 0) { 37 | let flagName = flag.substring(0, flag.indexOf('=')); 38 | setFlag(flagName, flag.substring(flag.indexOf('=')+1, flag.length)); 39 | } else { 40 | setFlag(flag, argv.shift()); 41 | } 42 | } 43 | } else { 44 | if (boolFlag(flag)) { 45 | argObj[flag] = true; 46 | } else if(flag.length > 1) { 47 | setFlag(flag[0], flag.substring(1, flag.length)); 48 | } else { 49 | setFlag(flag[0], argv.shift()); 50 | } 51 | } 52 | if (boolFlag(flag)) { 53 | argObj[flag] = true; 54 | } 55 | } else { 56 | setFlag('_', flag); 57 | } 58 | } 59 | 60 | return argObj; 61 | 62 | // boolFlag returns whether a flag is known to be boolean type 63 | function boolFlag(flag) { 64 | if (Array.isArray(options.boolFlags)) { 65 | for (var i = 0; i < options.boolFlags.length; i++) { 66 | if (options.boolFlags[i] == flag) 67 | return true; 68 | } 69 | } 70 | return false; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /test.rb: -------------------------------------------------------------------------------- 1 | require 'minitest' 2 | require 'minitest/autorun' 3 | require 'execjs' 4 | require 'webrick' 5 | 6 | class TestCurlToGo < Minitest::Test 7 | JS_CONTEXT = ExecJS.compile(File.read("resources/js/curl-to-ruby.js")) 8 | 9 | def test_simple_get 10 | assert_curl_eq "/" 11 | assert_curl_eq "/foo.txt" 12 | end 13 | 14 | def test_post 15 | assert_curl_eq "/", '-X POST -d "foo"' 16 | assert_curl_eq "/", '-X POST -d "foobar"' 17 | end 18 | 19 | def test_headers 20 | assert_curl_eq "/", '-XPOST -H MyCrazyHeader:foo -d "foo"' 21 | end 22 | 23 | def test_basic_auth 24 | assert_curl_eq "/", "-u banana:coconuts" 25 | end 26 | 27 | def test_complex_post 28 | assert_curl_eq "/", <<-CURL 29 | -X POST \ 30 | -u API_KEY: \ 31 | -d 'shipment[to_address][id]=adr_HrBKVA85' \ 32 | -d 'shipment[from_address][id]=adr_VtuTOj7o' \ 33 | -d 'shipment[parcel][id]=prcl_WDv2VzHp' \ 34 | -d 'shipment[is_return]=true' \ 35 | -d 'shipment[customs_info][id]=cstinfo_bl5sE20Y' 36 | CURL 37 | end 38 | 39 | def test_simple_data 40 | assert_curl_eq "/", "-d foo=bar" 41 | end 42 | 43 | def test_data_file_reference 44 | assert_curl_eq "/", "-d @README.md" 45 | end 46 | 47 | def test_query_string 48 | assert_curl_eq "/curl-to-ruby/?foo=bar", "-d @README.md" 49 | end 50 | 51 | private 52 | 53 | def assert_curl_eq(path, curl_args="") 54 | curl_req = normalize_request capture_http { |url| 55 | system "curl -s -o /dev/null #{url}#{path} #{curl_args}" 56 | } 57 | 58 | ruby_req = normalize_request capture_http { |url| 59 | ruby = curl_to_ruby("curl #{url}#{path} #{curl_args}") 60 | eval(ruby) 61 | } 62 | 63 | assert_equal curl_req.verb, ruby_req.verb 64 | assert_equal curl_req.path, ruby_req.path 65 | if curl_req.body.nil? 66 | assert_nil ruby_req.body 67 | else 68 | assert_equal curl_req.body, ruby_req.body 69 | end 70 | assert_equal curl_req.headers, ruby_req.headers 71 | end 72 | 73 | Request = Struct.new(:verb, :path, :headers, :body) 74 | 75 | def curl_to_ruby(cmd) 76 | JS_CONTEXT.call("curlToRuby.default", cmd) 77 | end 78 | 79 | def normalize_request(original) 80 | headers = original.headers.dup 81 | headers.delete("accept-encoding") 82 | headers.delete("user-agent") 83 | headers.delete("host") 84 | headers.delete("expect") 85 | 86 | if original.body 87 | body = original.body 88 | if headers['content-type'] == ['application/x-www-form-urlencoded'] 89 | # May encode slightly differently 90 | begin 91 | body = URI.decode_www_form(body).sort 92 | headers.delete("content-length") 93 | rescue ArgumentError 94 | end 95 | end 96 | end 97 | 98 | Request.new( 99 | original.verb, 100 | original.path, 101 | headers, 102 | body 103 | ) 104 | end 105 | 106 | def capture_http 107 | server = WEBrick::HTTPServer.new( 108 | Port: 0, 109 | Logger: WEBrick::Log.new("/dev/null"), 110 | AccessLog: [] 111 | ) 112 | port = server[:Port] 113 | request = nil 114 | 115 | thread = Thread.new do 116 | server.mount_proc('/') do |req, res| 117 | request = Request.new( 118 | req.request_method, 119 | req.path, 120 | req.header, 121 | req.body 122 | ) 123 | res.body = "hello, world" 124 | server.shutdown 125 | end 126 | 127 | server.start 128 | 129 | request 130 | end 131 | 132 | url = "http://127.0.0.1:#{port}" 133 | yield url, port 134 | 135 | thread.join 136 | thread.value 137 | end 138 | end 139 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var path = require('path'); 4 | var webpack = require('webpack'); 5 | 6 | module.exports = { 7 | entry: { 8 | default: './src/default.js', 9 | "curl-to-ruby": './src/curl-to-ruby.js' 10 | }, 11 | 12 | output: { 13 | path: path.join(__dirname, "resources/js/"), 14 | filename: "[name].js" 15 | }, 16 | 17 | module: { 18 | rules: [ 19 | { 20 | test: /\.js$/, 21 | exclude: /node_modules/, 22 | use: { 23 | loader: 'babel-loader', 24 | options: { 25 | presets: ['@babel/preset-env'] 26 | } 27 | } 28 | } 29 | ] 30 | } 31 | } 32 | --------------------------------------------------------------------------------