├── .circleci
└── config.yml
├── .gitignore
├── .node-version
├── .prettierrc
├── LICENSE
├── README.md
├── logo.png
├── package-lock.json
├── package.json
├── src
├── __tests__
│ ├── content-type.test.ts
│ ├── default.test.ts
│ ├── force-body.test.ts
│ └── option-container.test.ts
├── adapter
│ └── AxiosRequestConfigAdapter.ts
├── enum
│ ├── CURL_OPTIONS.ts
│ ├── HTTP_HEADER.ts
│ ├── HTTP_HEADER_CONTENT_TYPE.ts
│ └── HTTP_METHOD.ts
├── index.ts
├── interface
│ ├── IR2CurlOptions.ts
│ └── IRequestAdaptor.ts
└── lib
│ ├── BodyHelper.ts
│ ├── CommonUtils.ts
│ ├── CurlBuilder.ts
│ ├── HeaderHelper.ts
│ ├── OptionContainer.ts
│ └── isEmpty.ts
├── tsconfig.json
└── tslint.json
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | jobs:
3 | build:
4 | working_directory: ~/r2curl
5 | docker:
6 | - image: circleci/node:10.15.3
7 | steps:
8 | - checkout
9 | - run:
10 | name: npminstall
11 | command: npm install
12 | - run:
13 | name: tslint check
14 | command: npx tslint -c ./tslint.json "src/**/*.ts"
15 | - run:
16 | name: jest
17 | command: npm test
18 | - run:
19 | name: typescript compile & artifact
20 | command: npm pack
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # Bower dependency directory (https://bower.io/)
27 | bower_components
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (https://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules/
37 | jspm_packages/
38 |
39 | # TypeScript v1 declaration files
40 | typings/
41 |
42 | # Optional npm cache directory
43 | .npm
44 |
45 | # Optional eslint cache
46 | .eslintcache
47 |
48 | # Optional REPL history
49 | .node_repl_history
50 |
51 | # Output of 'npm pack'
52 | *.tgz
53 |
54 | # Yarn Integrity file
55 | .yarn-integrity
56 |
57 | # dotenv environment variables file
58 | .env
59 |
60 | # next.js build output
61 | .next
62 |
63 | dist
64 | .npmrc
65 |
--------------------------------------------------------------------------------
/.node-version:
--------------------------------------------------------------------------------
1 | 12.18.3
2 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "trailingComma": "all",
4 | "printWidth": 120,
5 | "parser": "typescript"
6 | }
7 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 유용우 / CX
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, 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,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # r2curl
2 |
3 | [](https://badge.fury.io/js/r2curl) [](https://npmcharts.com/compare/r2curl?minimal=true) [](https://github.com/uyu423/r2curl) [](https://opensource.org/licenses/mit-license.php) [](https://circleci.com/gh/uyu423/r2curl) [](https://www.codacy.com/app/uyu423/r2curl?utm_source=github.com&utm_medium=referral&utm_content=uyu423/r2curl&utm_campaign=Badge_Grade) [](https://codeclimate.com/github/uyu423/r2curl) [](https://codeclimate.com/github/uyu423/r2curl/test_coverage)
4 |
5 |
6 |
7 |
8 |
9 | ## Background
10 |
11 | - [r2curl](https://github.com/uyu423/r2curl) was inspired by [@delirius325/axios-curlirize](https://github.com/delirius325/axios-curlirize).
12 | - axios-curlirize is very convenient. but works as a middleware for axios, and I think this part is black box logic
13 | - it contains potentially asynchronous concurrency issues and difficult-to-manage elements.
14 | - So I created a new 'Request to cURL' package that is completely independent of the dependencies of axios.
15 |
16 | ## Feature
17 |
18 | - Generates cURL commands completely independently from the outside of the request wrapper package.
19 | - Provides additional options involved in generating the cURL command.
20 | - It will be updated soon to be available in packages like [node-fetch](https://www.npmjs.com/package/node-fetch) or [request](https://www.npmjs.com/package/request).
21 |
22 | ## Roadmap
23 |
24 | - [x] [axios](https://www.npmjs.com/package/axios)
25 | - [x] AxiosRequestConfig
26 | - [x] AxiosResposne
27 | - [ ] [node-fetch](https://www.npmjs.com/package/node-fetch)
28 | - [ ] [request](https://www.npmjs.com/package/request)
29 | - [ ] ...
30 |
31 | ## Install
32 |
33 | ```bash
34 | npm install r2curl --save
35 | ```
36 |
37 | ## Usage
38 |
39 | ### axios
40 |
41 | #### `AxiosResponse`
42 |
43 | ```typescript
44 | // if js, const r2curl = require('r2curl');
45 | import r2curl from 'r2curl';
46 |
47 | const response = await axios.get('https://google.com');
48 | const curl = r2curl(response);
49 |
50 | console.log(curl);
51 | // stdout "curl -X GET 'https://google.com' -H 'Accept:application/json, text/plain, */*' -H 'User-Agent:axios/0.18.0'"
52 | ```
53 |
54 | #### `AxiosRequestConfig`
55 |
56 | ```typescript
57 | // if js, const r2curl = require('r2curl');
58 | import r2curl from 'r2curl';
59 |
60 | // config as AxiosRequestConfig
61 | const config = {
62 | url: 'https://google.com',
63 | method: 'POST',
64 | data: {
65 | caller: 'curl tester',
66 | },
67 | headers: {
68 | 'Content-Type': 'application/json',
69 | },
70 | };
71 |
72 | const curl = r2curl(reqeustConfig);
73 | console.log(curl);
74 | // stdout `curl -X POST 'https://google.com' -H 'Content-Type:application/json' --data '{"caller":"curl tester"}'`
75 |
76 | const response = await axios.request(config);
77 | ```
78 |
79 | ### node-fetch
80 |
81 | - update soon (target 0.2.0)
82 | - see [github project board](https://github.com/uyu423/r2curl/projects/1)
83 |
84 | ### request
85 |
86 | - update soon (target 0.2.0)
87 | - see [github project board](https://github.com/uyu423/r2curl/projects/1)
88 |
89 | ## More `r2curl` Options
90 |
91 | ### `option.quote`
92 |
93 | - Determines the type of quota around the body and uri.
94 | - default is `single`
95 |
96 | ```typescript
97 | import r2curl from 'r2curl';
98 |
99 | // option as IR2CurlOptions.ts
100 | const option = {
101 | /** Determines the type of quota around the body and uri. */
102 | quote: 'double',
103 | };
104 |
105 | const curl = r2curl(requestConfig, option);
106 | console.log(curl);
107 | ```
108 |
109 | ### `option.defaultContentType`
110 |
111 | - Determines the default Content-Type header value for `POST` and `PUT` requests.
112 | - default is `application/json; charset=utf-8`
113 | - Type is `(enum) HTTP_HEADER_CONTENT_TYPE` | `string` | `false`;
114 | - If you give `(boolean) false` to `defaultContentType`, you can disable `Content-Type` Header.
115 |
116 | ```typescript
117 | import r2curl, { HTTP_HEADER_CONTENT_TYPE } from 'r2curl';
118 |
119 | // const optionUsingEnum = {
120 | // defaultContentType: HTTP_HEADER_CONTENT_TYPE.TEXT,
121 | // };
122 | const option = {
123 | defaultContentType: 'application/json5',
124 | }
125 | const request: AxiosRequestConfig = { url: 'https://google.com', method: 'POST' };
126 |
127 | const curl = r2curl(config, option);
128 | console.log(curl);
129 | // output: curl -X POST 'https://google.com' -H 'Content-Type:application/json5
130 | ```
131 |
132 | ### `option.forceBody`
133 |
134 | - Accept Body all HTTP Method.
135 | - By default, Body is not allowed in `GET` and `DELETE` methods.
136 | - However, some services such as ElasticSearch should be able to use the Body as a `GET` method. At this point, use this option to activate the Body.
137 |
138 | ```typescript
139 | import r2curl from 'r2curl';
140 |
141 | const config: AxiosRequestConfig = {
142 | url: 'https://google.com',
143 | method: 'GET',
144 | data: {
145 | caller: 'https://github.com/uyu423/r2curl',
146 | sorry: true,
147 | },
148 | };
149 |
150 | const option = {
151 | forceBody: true,
152 | }
153 |
154 | const curl = r2curl(config, option);
155 | // output: 'curl -X GET \'https://google.com\' --data \'{"caller":"https://github.com/uyu423/r2curl","sorry":true}\''
156 | ```
--------------------------------------------------------------------------------
/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uyu423/r2curl/7b8f53b6b6b14131eee2731b69fdbc52a351a54a/logo.png
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "r2curl",
3 | "version": "0.2.4",
4 | "description": "Node.js Request Wrapper (axios, fetch, ..) to cURL Command String",
5 | "main": "dist/index.js",
6 | "types": "dist/index.d.ts",
7 | "files": [
8 | "dist"
9 | ],
10 | "scripts": {
11 | "test": "npx jest --coverage",
12 | "test:log": "npx cross-env DEBUG=r2curl:* npx jest",
13 | "test:watch": "npx cross-env DEBUG=r2curl:tc:* npx jest --watch",
14 | "lint": "npx tslint -c ./tslint.json \"./src/**/*.ts\"",
15 | "clean": "npm run clean:dist && npm run clean:artifact",
16 | "clean:artifact": "rm -rf ./*.tgz && true",
17 | "clean:dist": "rm -rf dist && true",
18 | "build": "npm run lint && npm run clean:dist && tsc",
19 | "prepack": "npm run clean && tsc"
20 | },
21 | "repository": {
22 | "type": "git",
23 | "url": "git+https://github.com/uyu423/r2curl.git"
24 | },
25 | "keywords": [
26 | "curl",
27 | "axios",
28 | "debug"
29 | ],
30 | "author": "Yowu Yu ",
31 | "license": "MIT",
32 | "bugs": {
33 | "url": "https://github.com/uyu423/r2curl/issues"
34 | },
35 | "homepage": "https://github.com/uyu423/r2curl#readme",
36 | "dependencies": {
37 | "debug": "^4.1.1"
38 | },
39 | "devDependencies": {
40 | "@types/debug": "^4.1.5",
41 | "@types/jest": "^24.9.1",
42 | "@types/node": "^12.12.31",
43 | "@types/shelljs": "^0.8.7",
44 | "axios": "^0.19.2",
45 | "cross-env": "^5.2.1",
46 | "jest": "^24.9.0",
47 | "prettier": "^1.19.1",
48 | "shelljs": "^0.8.3",
49 | "ts-jest": "^24.3.0",
50 | "ts-loader": "^5.4.5",
51 | "ts-node": "^8.8.1",
52 | "tslint": "^5.20.1",
53 | "tslint-config-airbnb": "^5.11.2",
54 | "tslint-config-prettier": "^1.18.0",
55 | "typescript": "^3.8.3"
56 | },
57 | "jest": {
58 | "testEnvironment": "node",
59 | "moduleFileExtensions": [
60 | "ts",
61 | "tsx",
62 | "js"
63 | ],
64 | "transform": {
65 | "^.+\\.(ts|tsx)$": "ts-jest"
66 | },
67 | "testMatch": [
68 | "**/__tests__/*.(ts|tsx)"
69 | ]
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/__tests__/content-type.test.ts:
--------------------------------------------------------------------------------
1 | import { AxiosRequestConfig } from 'axios';
2 | import debug from 'debug';
3 | import { HTTP_HEADER } from '../enum/HTTP_HEADER';
4 | import { HTTP_HEADER_CONTENT_TYPE } from '../enum/HTTP_HEADER_CONTENT_TYPE';
5 | import { HTTP_METHOD } from '../enum/HTTP_METHOD';
6 | // tslint:disable-next-line: import-name
7 | import r2curl from '../index';
8 |
9 | // tslint:disable-next-line:no-implicit-dependencies
10 |
11 | // tslint:disable-next-line:import-name
12 |
13 | const log = debug('r2curl:tc:content-type');
14 |
15 | describe('content-type r2curl option', () => {
16 | test('default content-type option is json utf8', () => {
17 | const config: AxiosRequestConfig = {
18 | url: 'https://google.com',
19 | method: 'POST',
20 | data: {
21 | caller: 'https://github.com/uyu423/r2curl',
22 | sorry: true,
23 | },
24 | };
25 |
26 | const curl = r2curl(config);
27 |
28 | log(curl);
29 |
30 | expect(curl.includes(`Content-Type:${HTTP_HEADER_CONTENT_TYPE.JSON_UTF8}`)).toBeTruthy();
31 | });
32 |
33 | test('"PATCH" Method has content-type option is json utf8', () => {
34 | const config: AxiosRequestConfig = {
35 | url: 'https://google.com',
36 | method: 'PATCH',
37 | data: {
38 | caller: 'https://github.com/uyu423/r2curl',
39 | sorry: true,
40 | },
41 | };
42 |
43 | const curl = r2curl(config);
44 |
45 | log(curl);
46 |
47 | expect(curl.includes(`Content-Type:${HTTP_HEADER_CONTENT_TYPE.JSON_UTF8}`)).toBeTruthy();
48 | });
49 |
50 | test('formal string default content-type', () => {
51 | const config: AxiosRequestConfig = {
52 | url: 'https://google.com',
53 | method: HTTP_METHOD.POST,
54 | };
55 |
56 | const defaultContentType = 'application/json5';
57 | const curl = r2curl(config, { defaultContentType });
58 |
59 | log('json5 content-type: ', curl);
60 |
61 | expect(curl.includes(`Content-Type:${defaultContentType}`)).toBeTruthy();
62 | });
63 |
64 | test('defaultContentType value is "false" => not include Content-Type header', () => {
65 | const config: AxiosRequestConfig = {
66 | url: 'https://google.com',
67 | method: HTTP_METHOD.PUT,
68 | };
69 |
70 | const defaultContentType = false;
71 | const curl = r2curl(config, { defaultContentType });
72 |
73 | expect(curl.includes(`Content-Type:`)).toBeFalsy();
74 | });
75 |
76 | test('GET method not appear Content-Type Header', () => {
77 | const config: AxiosRequestConfig = {
78 | url: 'https://google.com',
79 | method: HTTP_METHOD.GET,
80 | };
81 |
82 | const curl = r2curl(config);
83 |
84 | expect(curl.includes(`Content-Type:`)).toBeFalsy();
85 | });
86 |
87 | test('DELETE method not appear Content-Type Header', () => {
88 | const config: AxiosRequestConfig = {
89 | url: 'https://google.com',
90 | method: HTTP_METHOD.DELETE,
91 | };
92 |
93 | const curl = r2curl(config);
94 |
95 | expect(curl.includes(`Content-Type:`)).toBeFalsy();
96 | });
97 |
98 | test('if use "forceBody" option, enable default Content-Type', () => {
99 | const config: AxiosRequestConfig = {
100 | url: 'https://google.com',
101 | method: HTTP_METHOD.GET,
102 | data: {
103 | caller: 'https://github.com/uyu423/r2curl',
104 | sorry: true,
105 | },
106 | };
107 |
108 | const curl = r2curl(config, {
109 | forceBody: true,
110 | });
111 |
112 | expect(curl.includes(`Content-Type:`)).toBeTruthy();
113 | });
114 | });
115 |
116 | describe('difference content-type', () => {
117 | test('difference body string as form-urlencoded content-type', done => {
118 | const config: AxiosRequestConfig = {
119 | url: 'https://google.com',
120 | method: HTTP_METHOD.POST,
121 | data: {
122 | caller: 'https://github.com/uyu423/r2curl',
123 | sorry: true,
124 | },
125 | headers: {
126 | [HTTP_HEADER.CONTENT_TYPE]: HTTP_HEADER_CONTENT_TYPE.FORM_URLENCODED,
127 | },
128 | };
129 |
130 | const curl = r2curl(config);
131 |
132 | log(curl);
133 |
134 | expect(curl).toBe(
135 | // tslint:disable-next-line:max-line-length
136 | "curl -X POST 'https://google.com' -H 'Content-Type:application/x-www-form-urlencoded' --data 'caller=https%3A%2F%2Fgithub.com%2Fuyu423%2Fr2curl&sorry=true'",
137 | );
138 |
139 | done();
140 | });
141 | });
142 |
--------------------------------------------------------------------------------
/src/__tests__/default.test.ts:
--------------------------------------------------------------------------------
1 | import axios, { AxiosRequestConfig } from 'axios';
2 | import debug from 'debug';
3 | import * as shelljs from 'shelljs';
4 | // tslint:disable-next-line:import-name
5 | import r2curl from '../index';
6 |
7 | const log = debug('r2curl:tc:default');
8 |
9 | describe('default', () => {
10 | test('AxiosResponse params will success', async done => {
11 | const { devDependencies } = require('../../package.json');
12 | const axiosVersion = (devDependencies.axios as string).replace('^', '');
13 |
14 | const request = await axios.get('https://google.com');
15 | const curl = r2curl(request);
16 |
17 | log(curl);
18 |
19 | const exec = shelljs.exec(`${curl} --silent > /dev/null`);
20 |
21 | expect(exec.code).toBeLessThan(1);
22 | expect(curl).toBe(
23 | // tslint:disable-next-line:max-line-length
24 | `curl -X GET 'https://google.com' -H 'Accept:application/json, text/plain, */*' -H 'User-Agent:axios/${axiosVersion}'`,
25 | );
26 |
27 | done();
28 | });
29 | test('AxiosRequestConfig params will success', async done => {
30 | const config: AxiosRequestConfig = {
31 | url: 'https://google.com',
32 | method: 'POST',
33 | data: {
34 | caller: 'https://github.com/uyu423/r2curl',
35 | sorry: true,
36 | },
37 | headers: {
38 | 'content-Type': 'application/json',
39 | },
40 | };
41 |
42 | const curl = r2curl(config);
43 | const exec = shelljs.exec(`${curl} --silent > /dev/null`);
44 |
45 | log(curl);
46 |
47 | expect(exec.code).toBeLessThan(1);
48 | expect(curl).toBe(
49 | // tslint:disable-next-line: max-line-length
50 | 'curl -X POST \'https://google.com\' -H \'content-Type:application/json\' --data \'{"caller":"https://github.com/uyu423/r2curl","sorry":true}\'',
51 | );
52 | done();
53 | });
54 | });
55 |
--------------------------------------------------------------------------------
/src/__tests__/force-body.test.ts:
--------------------------------------------------------------------------------
1 | // tslint:disable max-line-length
2 | import axios, { AxiosRequestConfig } from 'axios';
3 | import debug from 'debug';
4 | import * as shelljs from 'shelljs';
5 | // tslint:disable-next-line:import-name
6 | import r2curl from '../index';
7 |
8 | const log = debug('r2curl:tc:force-body-option');
9 |
10 | describe('force-body option', () => {
11 | test('if options is false, GET not allow body', done => {
12 | const config: AxiosRequestConfig = {
13 | url: 'https://google.com',
14 | method: 'GET',
15 | data: {
16 | caller: 'https://github.com/uyu423/r2curl',
17 | sorry: true,
18 | },
19 | };
20 |
21 | const curl = r2curl(config, { forceBody: false });
22 | const exec = shelljs.exec(`${curl} --silent > /dev/null`);
23 |
24 | log(curl);
25 |
26 | expect(exec.code).toBeLessThan(1);
27 | expect(curl).toBe('curl -X GET \'https://google.com\'');
28 | done();
29 | });
30 |
31 | test('if options is false, DELETE not allow body', done => {
32 | const config: AxiosRequestConfig = {
33 | url: 'https://google.com',
34 | method: 'DELETE',
35 | data: {
36 | caller: 'https://github.com/uyu423/r2curl',
37 | sorry: true,
38 | },
39 | };
40 |
41 | const curl = r2curl(config, { forceBody: false });
42 | const exec = shelljs.exec(`${curl} --silent > /dev/null`);
43 |
44 | log(curl);
45 |
46 | expect(exec.code).toBeLessThan(1);
47 | expect(curl).toBe('curl -X DELETE \'https://google.com\'');
48 | done();
49 | });
50 |
51 | test('if options is true, GET allow body', done => {
52 | const config: AxiosRequestConfig = {
53 | url: 'https://google.com',
54 | method: 'GET',
55 | data: {
56 | caller: 'https://github.com/uyu423/r2curl',
57 | sorry: true,
58 | },
59 | };
60 |
61 | const curl = r2curl(config, { forceBody: true });
62 |
63 | log(curl);
64 |
65 | // somtime occur http protocol error.
66 | // const exec = shelljs.exec(`${curl} --silent > /dev/null`);
67 | // expect(exec.code).toBeLessThan(1);
68 |
69 | expect(curl).toBe(
70 | 'curl -X GET \'https://google.com\' -H \'Content-Type:application/json; charset=utf-8\' --data \'{"caller":"https://github.com/uyu423/r2curl","sorry":true}\'',
71 | );
72 | done();
73 | });
74 |
75 | test('if options is true, DELETE allow body', done => {
76 | const config: AxiosRequestConfig = {
77 | url: 'https://google.com',
78 | method: 'DELETE',
79 | data: {
80 | caller: 'https://github.com/uyu423/r2curl',
81 | sorry: true,
82 | },
83 | };
84 |
85 | const curl = r2curl(config, { forceBody: true });
86 | const exec = shelljs.exec(`${curl} --silent > /dev/null`);
87 |
88 | log(curl);
89 |
90 | expect(exec.code).toBeLessThan(1);
91 | expect(curl).toBe(
92 | 'curl -X DELETE \'https://google.com\' -H \'Content-Type:application/json; charset=utf-8\' --data \'{"caller":"https://github.com/uyu423/r2curl","sorry":true}\'',
93 | );
94 | done();
95 | });
96 | });
97 |
--------------------------------------------------------------------------------
/src/__tests__/option-container.test.ts:
--------------------------------------------------------------------------------
1 | import axios, { AxiosRequestConfig } from 'axios';
2 | import debug from 'debug';
3 | import * as shelljs from 'shelljs';
4 | import { CURL_OPTIONS } from '../enum/CURL_OPTIONS';
5 | // tslint:disable-next-line:import-name
6 | import r2curl from '../index';
7 | import { defaultR2CurlOptions } from '../interface/IR2CurlOptions';
8 | import CommonUtils from '../lib/CommonUtils';
9 | import { OptionContainer } from '../lib/OptionContainer';
10 |
11 | const log = debug('r2curl:tc:option-container');
12 |
13 | describe('Option Container Class Test Case', () => {
14 | beforeAll(() => {
15 | CommonUtils.bootstrap(defaultR2CurlOptions);
16 | });
17 |
18 | test('only add one command', done => {
19 | const oc = new OptionContainer();
20 | oc.add(CURL_OPTIONS.COMPRESSED);
21 | expect(oc.toString()).toBe(CURL_OPTIONS.COMPRESSED);
22 | done();
23 | });
24 |
25 | test('only add one commmad, one value', done => {
26 | const oc = new OptionContainer();
27 | oc.add(CURL_OPTIONS.COMPRESSED, 'Trash Value');
28 | expect(oc.toString()).toBe(`${CURL_OPTIONS.COMPRESSED} 'Trash Value'`);
29 | done();
30 | });
31 | });
32 |
33 | describe('Option Container Integration Test Case', () => {
34 | test('include header "Accept-Encoding: gzip"', done => {
35 | const config: AxiosRequestConfig = {
36 | url: 'https://google.com',
37 | method: 'POST',
38 | data: {
39 | caller: 'https://github.com/uyu423/r2curl',
40 | sorry: true,
41 | },
42 | headers: {
43 | 'content-Type': 'application/json',
44 | 'Accept-Encoding': 'gzip',
45 | },
46 | };
47 |
48 | // no execute
49 | const curl = r2curl(config);
50 | const exec = shelljs.exec(`${curl} --silent > /dev/null`);
51 |
52 | log(curl);
53 |
54 | expect(exec.code).toBeLessThan(1);
55 | expect(curl).toBe(
56 | // tslint:disable-next-line: max-line-length
57 | 'curl -X POST \'https://google.com\' -H \'content-Type:application/json\' -H \'Accept-Encoding:gzip\' --data \'{"caller":"https://github.com/uyu423/r2curl","sorry":true}\' --compressed',
58 | );
59 | done();
60 | });
61 | });
62 |
--------------------------------------------------------------------------------
/src/adapter/AxiosRequestConfigAdapter.ts:
--------------------------------------------------------------------------------
1 | import { AxiosRequestConfig } from 'axios';
2 | import { HTTP_METHOD } from '../enum/HTTP_METHOD';
3 | import IRequestAdaptor from '../interface/IRequestAdaptor';
4 | import { isEmpty } from '../lib/isEmpty';
5 |
6 | export class AxiosRequestConfigAdapter implements IRequestAdaptor {
7 | constructor(private readonly _prop: AxiosRequestConfig) {}
8 |
9 | get method(): HTTP_METHOD {
10 | if (isEmpty(this._prop.method)) {
11 | return HTTP_METHOD.GET;
12 | }
13 | const method = this._prop.method.toUpperCase();
14 | switch (method) {
15 | case 'GET': {
16 | return HTTP_METHOD.GET;
17 | }
18 | case 'POST': {
19 | return HTTP_METHOD.POST;
20 | }
21 | case 'PUT': {
22 | return HTTP_METHOD.PUT;
23 | }
24 | case 'PATCH': {
25 | return HTTP_METHOD.PATCH;
26 | }
27 | case 'DELETE': {
28 | return HTTP_METHOD.DELETE;
29 | }
30 | case 'HEAD': {
31 | return HTTP_METHOD.HEAD;
32 | }
33 | case 'OPTIONS': {
34 | return HTTP_METHOD.OPTIONS;
35 | }
36 | default: {
37 | return HTTP_METHOD.GET;
38 | }
39 | }
40 | }
41 |
42 | get headers() {
43 | if (isEmpty(this._prop.headers)) {
44 | return {};
45 | }
46 | return this._prop.headers;
47 | }
48 |
49 | get body() {
50 | return this._prop.data;
51 | }
52 |
53 | get url() {
54 | return `${this._prop.baseURL || ''}${this._prop.url || ''}`;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/enum/CURL_OPTIONS.ts:
--------------------------------------------------------------------------------
1 | export enum CURL_OPTIONS {
2 | COMPRESSED = '--compressed',
3 | }
4 |
--------------------------------------------------------------------------------
/src/enum/HTTP_HEADER.ts:
--------------------------------------------------------------------------------
1 | export enum HTTP_HEADER {
2 | CONTENT_TYPE = 'Content-Type',
3 | ACCEPT_ENCODING = 'Accept-Encoding',
4 | }
5 |
6 | export enum HTTP_HEADER_LOWERCASE {
7 | CONTENT_TYPE = 'content-type',
8 | ACCEPT_ENCODING = 'accept-encoding',
9 | }
10 |
--------------------------------------------------------------------------------
/src/enum/HTTP_HEADER_CONTENT_TYPE.ts:
--------------------------------------------------------------------------------
1 | export enum HTTP_HEADER_CONTENT_TYPE {
2 | TEXT = 'text/plain',
3 | JSON = 'application/json',
4 | JSON_UTF8 = 'application/json; charset=utf-8',
5 | XML = 'application/xml',
6 | HTML = 'text/html',
7 | FORM_URLENCODED = 'application/x-www-form-urlencoded',
8 | FORM_DATA = 'multipart/form-data',
9 | }
10 |
--------------------------------------------------------------------------------
/src/enum/HTTP_METHOD.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Standard Http Methods
3 | * @see https://tools.ietf.org/html/rfc7231#section-4.3
4 | */
5 | export enum HTTP_METHOD {
6 | GET = 'GET',
7 | POST = 'POST',
8 | HEAD = 'HEAD',
9 | PUT = 'PUT',
10 | DELETE = 'DELETE',
11 | OPTIONS = 'OPTIONS',
12 | CONNECT = 'CONNECT',
13 | TRACE = 'TRACE',
14 | // purge is assumed to be non-standard, but it is used in many requests.
15 | PURGE = 'PURGE',
16 | // patch is defined in https://tools.ietf.org/html/rfc5789
17 | PATCH = 'PATCH',
18 | }
19 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import { AxiosRequestConfig, AxiosResponse } from 'axios';
2 | import debug from 'debug';
3 | import { AxiosRequestConfigAdapter } from './adapter/AxiosRequestConfigAdapter';
4 | import { defaultR2CurlOptions, IR2CurlOptions } from './interface/IR2CurlOptions';
5 | import IRequestAdaptor from './interface/IRequestAdaptor';
6 | import CommonUtils from './lib/CommonUtils';
7 | import { CurlBuilder } from './lib/CurlBuilder';
8 |
9 | const log = debug('r2curl:index');
10 |
11 | export default function r2curl(
12 | request: AxiosRequestConfig | AxiosResponse,
13 | option: Partial = {},
14 | ): string {
15 | const mergedOption: IR2CurlOptions = { ...defaultR2CurlOptions, ...option };
16 |
17 | CommonUtils.bootstrap(mergedOption);
18 |
19 | // judge request wrapper object type
20 | const adapter: IRequestAdaptor = (() => {
21 | if (((_request: any): _request is AxiosResponse => 'config' in _request)(request)) {
22 | // judge request is AxiosResponse
23 | return new AxiosRequestConfigAdapter(request.config);
24 | }
25 | // judge request is AxiosRequestConfig
26 | return new AxiosRequestConfigAdapter(request);
27 | })();
28 |
29 | const curl = new CurlBuilder(adapter, mergedOption).toString();
30 | log('cURL Command: ', curl);
31 |
32 | return curl;
33 | }
34 |
--------------------------------------------------------------------------------
/src/interface/IR2CurlOptions.ts:
--------------------------------------------------------------------------------
1 | import { HTTP_HEADER_CONTENT_TYPE } from '../enum/HTTP_HEADER_CONTENT_TYPE';
2 |
3 | export interface IR2CurlOptions {
4 | /**
5 | * Determines the type of quota around the body and uri.
6 | * @default 'single'
7 | */
8 | quote: 'single' | 'double';
9 | /**
10 | * Determines the default Content-Type header value for POST, PATCH and PUT requests.
11 | * @default HTTP_HEADER_CONTENT_TYPE.JSON_UTF8 (appplication/json; charset=utf-8)
12 | */
13 | defaultContentType: HTTP_HEADER_CONTENT_TYPE | string | false;
14 | /**
15 | * Accept Body all HTTP Method.(Including GET and DELETE methods)
16 | * @default false
17 | */
18 | forceBody: boolean;
19 | }
20 |
21 | export const defaultR2CurlOptions: IR2CurlOptions = {
22 | quote: 'single',
23 | defaultContentType: HTTP_HEADER_CONTENT_TYPE.JSON_UTF8,
24 | forceBody: false,
25 | };
26 |
--------------------------------------------------------------------------------
/src/interface/IRequestAdaptor.ts:
--------------------------------------------------------------------------------
1 | import * as shelljs from 'shelljs';
2 | import { HTTP_METHOD } from '../enum/HTTP_METHOD';
3 |
4 | export default interface IRequestAdaptor {
5 | method: HTTP_METHOD;
6 | headers: { [key in string]: string };
7 | body: { [key in string]: any } | Array<{ [key in string]: any }>;
8 | url: string;
9 | }
10 |
--------------------------------------------------------------------------------
/src/lib/BodyHelper.ts:
--------------------------------------------------------------------------------
1 | import { HTTP_HEADER_LOWERCASE } from '../enum/HTTP_HEADER';
2 | import { HTTP_HEADER_CONTENT_TYPE } from '../enum/HTTP_HEADER_CONTENT_TYPE';
3 | import { isEmpty, isNotEmpty } from './isEmpty';
4 |
5 | export class BodyHelper {
6 | private contentType: string | null;
7 | private body: string | null = null;
8 |
9 | constructor(
10 | private readonly _headers: { [key in string]: string },
11 | private readonly _rawBody: { [key in string]: any } | Array<{ [key in string]: any }>,
12 | ) {
13 | this.contentType = this.getContentType();
14 | this.body = this.parseBody();
15 | }
16 |
17 | public toString(): string {
18 | if (isEmpty(this.body)) {
19 | return '';
20 | }
21 | return this.body;
22 | }
23 |
24 | private getContentType(): string | null {
25 | if (isEmpty(this._headers)) {
26 | return null;
27 | }
28 | const lowerHeaderArray = Object.entries(this._headers);
29 | const [contentTypePair] = lowerHeaderArray.filter(
30 | header => header[0].toLowerCase() === HTTP_HEADER_LOWERCASE.CONTENT_TYPE,
31 | );
32 |
33 | if (isEmpty(contentTypePair)) {
34 | return null;
35 | }
36 | return contentTypePair[1];
37 | }
38 |
39 | private parseBody(): string | null {
40 | if (isEmpty(this._rawBody)) {
41 | return null;
42 | }
43 | if (
44 | isNotEmpty(this.contentType) &&
45 | this.contentType.includes(HTTP_HEADER_CONTENT_TYPE.FORM_URLENCODED) &&
46 | isNotEmpty(this._rawBody) &&
47 | typeof this._rawBody === 'object'
48 | ) {
49 | return this.getFormBody();
50 | }
51 | return this.getTextBody();
52 | }
53 |
54 | private getFormBody(): string {
55 | return Object.entries(this._rawBody)
56 | .map(([key, value]) => {
57 | return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
58 | })
59 | .join('&');
60 | }
61 | private getTextBody(): string | null {
62 | return typeof this._rawBody === 'object' || Array.isArray(this._rawBody) ? JSON.stringify(this._rawBody) : null;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/lib/CommonUtils.ts:
--------------------------------------------------------------------------------
1 | import { IR2CurlOptions } from '../interface/IR2CurlOptions';
2 | import { isEmpty } from './isEmpty';
3 |
4 | export default class CommonUtils {
5 | public static bootstrap(options: IR2CurlOptions) {
6 | this.quote = options.quote === 'single' ? '\'' : '"';
7 | }
8 |
9 | public static wrapQuote(content: string) {
10 | if (isEmpty(this.quote)) {
11 | throw new Error('CommonUtils not Bootstraped');
12 | }
13 | return `${this.quote}${content}${this.quote}`;
14 | }
15 |
16 | private static quote: '\'' | '"';
17 | }
18 |
--------------------------------------------------------------------------------
/src/lib/CurlBuilder.ts:
--------------------------------------------------------------------------------
1 | import debug from 'debug';
2 | import { HTTP_METHOD } from '../enum/HTTP_METHOD';
3 | import { IR2CurlOptions } from '../interface/IR2CurlOptions';
4 | import IRequestAdaptor from '../interface/IRequestAdaptor';
5 | import { BodyHelper } from './BodyHelper';
6 | import CommonUtils from './CommonUtils';
7 | import { HeaderHelper } from './HeaderHelper';
8 | import { isEmpty, isNotEmpty } from './isEmpty';
9 | import { OptionContainer } from './OptionContainer';
10 |
11 | const log = debug('r2curl:CurlBuilder');
12 |
13 | export class CurlBuilder {
14 | private optionContainer: OptionContainer;
15 |
16 | constructor(private readonly _adap: IRequestAdaptor, private readonly _option: IR2CurlOptions) {
17 | this.optionContainer = new OptionContainer();
18 | }
19 |
20 | get method(): string {
21 | if (isEmpty(this._adap.method)) {
22 | return '';
23 | }
24 | return `-X ${this._adap.method}`;
25 | }
26 |
27 | get headers(): string {
28 | const helper = new HeaderHelper(this._adap.headers, this._adap.method, this.optionContainer, this._option);
29 | const headers = helper.toObject();
30 | if (isEmpty(headers)) {
31 | return '';
32 | }
33 | return Object.entries(headers)
34 | .map(header => `-H ${CommonUtils.wrapQuote(`${header[0]}:${header[1]}`)}`)
35 | .join(' ');
36 | }
37 |
38 | get body(): string {
39 | log(`method: ${this._adap.method}`, `forceBody: ${this._option.forceBody}`, this._adap.body);
40 |
41 | if (!this._option.forceBody && [HTTP_METHOD.GET, HTTP_METHOD.DELETE].includes(this._adap.method)) {
42 | return '';
43 | }
44 |
45 | const helper = new BodyHelper(this._adap.headers, this._adap.body);
46 | const body = helper.toString();
47 |
48 | if (isEmpty(body)) {
49 | return '';
50 | }
51 | return `--data ${CommonUtils.wrapQuote(helper.toString())}`;
52 | }
53 |
54 | get url(): string {
55 | return CommonUtils.wrapQuote(this._adap.url);
56 | }
57 |
58 | public toString() {
59 | const existData = [this.method, this.url, this.headers, this.body].filter(data => !isEmpty(data));
60 | const curlOptions = this.optionContainer.toString();
61 | return `curl ${[existData.join(' '), curlOptions].filter(data => isNotEmpty(data)).join(' ')}`.trim();
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/lib/HeaderHelper.ts:
--------------------------------------------------------------------------------
1 | import debug from 'debug';
2 | import { CURL_OPTIONS } from '../enum/CURL_OPTIONS';
3 | import { HTTP_HEADER, HTTP_HEADER_LOWERCASE } from '../enum/HTTP_HEADER';
4 | import { HTTP_METHOD } from '../enum/HTTP_METHOD';
5 | import { IR2CurlOptions } from '../interface/IR2CurlOptions';
6 | import { isEmpty, isNotEmpty } from './isEmpty';
7 | import { OptionContainer } from './OptionContainer';
8 |
9 | const log = debug('r2curl:HeaderHelper');
10 |
11 | type HttpHeaderType = { [key in string]: string };
12 |
13 | export class HeaderHelper {
14 | private headers: { [key in string]: string } | null;
15 | // only contain lowercase key
16 | private keys: string[];
17 | // contain lowercase key and value pair
18 | private pairs: HttpHeaderType;
19 |
20 | private defaultContentType: string | null;
21 |
22 | constructor(
23 | private readonly _rawHeaders: HttpHeaderType,
24 | private readonly _method: string,
25 | private readonly _curlOptionContainer: OptionContainer,
26 | private readonly _option: IR2CurlOptions,
27 | ) {
28 | this.defaultContentType = _option.defaultContentType === false ? null : _option.defaultContentType;
29 |
30 | this.keys = [];
31 | this.pairs = {};
32 |
33 | Object.keys(_rawHeaders).forEach(key => {
34 | const lower = key.toLowerCase();
35 |
36 | this.pairs[lower] = _rawHeaders[key];
37 | this.keys.push(lower);
38 | });
39 |
40 | this.headers = this.parseHeader();
41 |
42 | log('keys', this.keys);
43 | }
44 |
45 | public toObject() {
46 | if (isEmpty(this.headers)) {
47 | return {};
48 | }
49 | return this.headers;
50 | }
51 |
52 | private parseHeader() {
53 | if (isEmpty(this._rawHeaders) && isEmpty(this.defaultContentType)) {
54 | return null;
55 | }
56 |
57 | this.judgeAcceptEncoding();
58 |
59 | return {
60 | ...this.parseContentHeader(),
61 | ...this._rawHeaders,
62 | };
63 | }
64 |
65 | private parseContentHeader(): HttpHeaderType {
66 | log('_rawHeaders', this._rawHeaders);
67 | log('defaultContentType', this.defaultContentType);
68 |
69 | const rawHeaderContentType = this.keys.find(key => key === HTTP_HEADER_LOWERCASE.CONTENT_TYPE);
70 | const isNeedContentType =
71 | [
72 | HTTP_METHOD.POST as string,
73 | HTTP_METHOD.PUT as string,
74 | HTTP_METHOD.PATCH as string,
75 | ].includes(this._method) || this._option.forceBody;
76 |
77 | log(
78 | 'isNeedContentType',
79 | [
80 | HTTP_METHOD.POST as string,
81 | HTTP_METHOD.PUT as string,
82 | HTTP_METHOD.PATCH as string,
83 | ].includes(this._method),
84 | this._option.forceBody,
85 | 'OR CALC',
86 | isNeedContentType,
87 | );
88 |
89 | const headers: HttpHeaderType = {};
90 |
91 | if (isNeedContentType && isEmpty(rawHeaderContentType) && isNotEmpty(this.defaultContentType)) {
92 | headers[HTTP_HEADER.CONTENT_TYPE] = this.defaultContentType;
93 | }
94 |
95 | return headers;
96 | }
97 |
98 | private judgeAcceptEncoding(): void {
99 | const rawHeaderAcceptEncoding = this.keys.find(key => key === HTTP_HEADER_LOWERCASE.ACCEPT_ENCODING);
100 |
101 | log('rawHeaderAcceptEncoding:', rawHeaderAcceptEncoding);
102 | log(
103 | 'this.paris[rawHeaderAcceptEncoding]:',
104 | isNotEmpty(rawHeaderAcceptEncoding) ? this.pairs[rawHeaderAcceptEncoding] : null,
105 | );
106 |
107 | if (isNotEmpty(rawHeaderAcceptEncoding) && this.pairs[rawHeaderAcceptEncoding] === 'gzip') {
108 | this._curlOptionContainer.add(CURL_OPTIONS.COMPRESSED);
109 | }
110 |
111 | return;
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/src/lib/OptionContainer.ts:
--------------------------------------------------------------------------------
1 | import debug from 'debug';
2 | import { CURL_OPTIONS } from '../enum/CURL_OPTIONS';
3 | import CommonUtils from './CommonUtils';
4 | import { isNotEmpty } from './isEmpty';
5 |
6 | const log = debug('r2curl:OptionContainer');
7 |
8 | /** curl 옵션을 구성한다. command - value 쌍이다. */
9 | interface ICurlOption {
10 | command: string;
11 | value: string | null;
12 | }
13 |
14 | // tslint:disable-next-line: class-name
15 | export class OptionContainer {
16 | private options: ICurlOption[];
17 |
18 | constructor() {
19 | this.options = [];
20 | }
21 |
22 | // The following methods are used from time to time when needed.
23 | public add(command: CURL_OPTIONS, value?: string) {
24 | this.options.push({
25 | command,
26 | value: isNotEmpty(value) ? value : null,
27 | });
28 | }
29 |
30 | public toString(): string {
31 | log(this.options);
32 |
33 | return this.options
34 | .map(option => {
35 | const value = isNotEmpty(option.value) ? CommonUtils.wrapQuote(option.value) : null;
36 |
37 | return `${option.command}${isNotEmpty(value) ? ' [[value]]'.replace('[[value]]', value) : ''}`;
38 | })
39 | .join(' ');
40 | }
41 |
42 | // for Debug & testCase
43 | // tslint:disable-next-line: function-name
44 | public ___reset() {
45 | this.options = [];
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/lib/isEmpty.ts:
--------------------------------------------------------------------------------
1 | export function isEmpty(value: T | null | undefined): value is null | undefined {
2 | if (
3 | value === undefined ||
4 | value === null ||
5 | ((typeof value === 'number' && isNaN(value)) ||
6 | (typeof value === 'string' && value === '') ||
7 | (Array.isArray(value) && value.length < 1) ||
8 | (typeof value === 'object' && Object.keys(value).length < 1))
9 | ) {
10 | return true;
11 | }
12 | return false;
13 | }
14 |
15 | export function isNotEmpty(value: T | null | undefined): value is T {
16 | return !isEmpty(value);
17 | }
18 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "include": ["src"],
3 | "exclude": ["node_modules", "**/__tests__/*"],
4 | "compilerOptions": {
5 | /* Basic Options */
6 | "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
7 | "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
8 | // "lib": [], /* Specify library files to be included in the compilation. */
9 | // "allowJs": true, /* Allow javascript files to be compiled. */
10 | // "checkJs": true, /* Report errors in .js files. */
11 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
12 | "declaration": true, /* Generates corresponding '.d.ts' file. */
13 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
14 | // "sourceMap": true, /* Generates corresponding '.map' file. */
15 | // "outFile": "./", /* Concatenate and emit output to single file. */
16 | "outDir": "./dist", /* Redirect output structure to the directory. */
17 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
18 | // "composite": true, /* Enable project compilation */
19 | // "incremental": true, /* Enable incremental compilation */
20 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
21 | // "removeComments": true, /* Do not emit comments to output. */
22 | // "noEmit": true, /* Do not emit outputs. */
23 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */
24 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
25 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
26 |
27 | /* Strict Type-Checking Options */
28 | "strict": true, /* Enable all strict type-checking options. */
29 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
30 | // "strictNullChecks": true, /* Enable strict null checks. */
31 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */
32 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
33 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
34 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
35 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
36 |
37 | /* Additional Checks */
38 | // "noUnusedLocals": true, /* Report errors on unused locals. */
39 | // "noUnusedParameters": true, /* Report errors on unused parameters. */
40 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
41 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
42 |
43 | /* Module Resolution Options */
44 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
45 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
46 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
47 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
48 | // "typeRoots": [], /* List of folders to include type definitions from. */
49 | // "types": [], /* Type declaration files to be included in compilation. */
50 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
51 | "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
52 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
53 |
54 | /* Source Map Options */
55 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
56 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
57 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
58 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
59 |
60 | /* Experimental Options */
61 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
62 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 |
2 | {
3 | "defaultSeverity": "error",
4 | "extends": [
5 | "tslint:latest",
6 | "tslint-config-airbnb",
7 | "tslint-config-prettier"
8 | ],
9 | "jsRules": {},
10 | "rules": {
11 | "max-line-length": [true, 130],
12 | "variable-name": [true, "allow-leading-underscore"],
13 | "prefer-array-literal": false,
14 | "no-implicit-dependencies": [true, ["shelljs", "axios"]],
15 | "object-literal-sort-keys": false
16 | },
17 | "rulesDirectory": []
18 | }
--------------------------------------------------------------------------------