commit
bd010c360b
17
.eslintrc.js
Normal file
17
.eslintrc.js
Normal file
@ -0,0 +1,17 @@
|
||||
module.exports = {
|
||||
parser: '@typescript-eslint/parser', // Specifies the ESLint parser
|
||||
parserOptions: {
|
||||
ecmaVersion: 2020, // Allows for the parsing of modern ECMAScript features
|
||||
sourceType: 'module', // Allows for the use of imports
|
||||
},
|
||||
extends: [
|
||||
'plugin:@typescript-eslint/recommended', // Uses the recommended rules from the @typescript-eslint/eslint-plugin
|
||||
'prettier/@typescript-eslint', // Uses eslint-config-prettier to disable ESLint rules from @typescript-eslint/eslint-plugin that would conflict with prettier
|
||||
'plugin:prettier/recommended', // Enables eslint-plugin-prettier and eslint-config-prettier. This will display prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array.
|
||||
],
|
||||
rules: {
|
||||
indent: ['error', 2],
|
||||
// Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs
|
||||
// e.g. "@typescript-eslint/explicit-function-return-type": "off",
|
||||
},
|
||||
};
|
6
.github/workflows/nodejs.yml
vendored
6
.github/workflows/nodejs.yml
vendored
@ -17,11 +17,7 @@ jobs:
|
||||
node-version: 12
|
||||
- run: yarn install --frozen-lockfile
|
||||
- run: yarn test
|
||||
- name: TSLint checks
|
||||
uses: mooyoul/tslint-actions@v1.1.1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
project: tsconfig.json
|
||||
- run: yarn lint
|
||||
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
|
@ -1,6 +1,8 @@
|
||||
{
|
||||
"arrowParens": "avoid",
|
||||
"semi": true,
|
||||
"trailingComma": "all",
|
||||
"singleQuote": true,
|
||||
"printWidth": 110,
|
||||
"trailingComma": "all"
|
||||
"tabWidth": 2,
|
||||
"arrowParens": "avoid"
|
||||
}
|
||||
|
19
README.md
19
README.md
@ -8,10 +8,11 @@ Current offchain list:
|
||||
- https://gas-oracle.zoltu.io/
|
||||
- https://www.etherchain.org/api/gasPriceOracle
|
||||
- https://gasprice.poa.network/
|
||||
- https://www.gasnow.org/api/v3/gas/price
|
||||
|
||||
Current onchain list:
|
||||
|
||||
- [chainlink](https://etherscan.io/address/0xA417221ef64b1549575C977764E651c9FAB50141)
|
||||
- [chainlink](https://etherscan.io/address/0x169e633a2d1e6c10dd91238ba11c4a708dfef37c#readContract)
|
||||
|
||||
## Installation
|
||||
|
||||
@ -30,6 +31,7 @@ const { GasPriceOracle } = require('gas-price-oracle');
|
||||
```js
|
||||
const options = {
|
||||
defaultRpc: 'https://api.mycryptoapi.com/eth',
|
||||
timeout: 10000,
|
||||
};
|
||||
const oracle = new GasPriceOracle(options);
|
||||
// optional fallbackGasPrices
|
||||
@ -44,6 +46,9 @@ oracle.gasPrices(fallbackGasPrices).then(gasPrices => {
|
||||
});
|
||||
```
|
||||
|
||||
The `gasPrices` method also accepts `median` argument (`true`) by default. For more details see [below](#offchain-oracles-only-get-median-price).
|
||||
Under the hood it's a combination of `fetchMedianGasPriceOffChain`(`fetchGasPricesOffChain`) and `fetchGasPricesOnChain` methods.
|
||||
|
||||
### Offchain oracles only
|
||||
|
||||
```js
|
||||
@ -54,6 +59,18 @@ oracle.fetchGasPricesOffChain().then(gasPrices => {
|
||||
});
|
||||
```
|
||||
|
||||
### Offchain oracles only (get median price)
|
||||
|
||||
```js
|
||||
const oracle = new GasPriceOracle();
|
||||
|
||||
oracle.fetchMedianGasPriceOffChain().then(gasPrices => {
|
||||
console.log(gasPrices); // { instant: 50, fast: 21, standard: 10, low: 3 }
|
||||
});
|
||||
```
|
||||
|
||||
it returns the median gas price of all the oracles configured.
|
||||
|
||||
### Custom RPC URL for onchain oracles
|
||||
|
||||
```js
|
||||
|
21
package.json
21
package.json
@ -12,12 +12,12 @@
|
||||
"prepare": "npm run build",
|
||||
"prepublishOnly": "npm test && npm run lint",
|
||||
"scripts": {
|
||||
"test": "mocha --timeout 30000 -r ts-node/register tests/*.test.ts",
|
||||
"test": "mocha -r ts-node/register --timeout 30000 --exit src tests/*.test.ts",
|
||||
"build": "tsc",
|
||||
"tslint": "tslint -p tsconfig.json",
|
||||
"prettier:check": "npx prettier --check . --config .prettierrc",
|
||||
"prettier:fix": "npx prettier --write . --config .prettierrc",
|
||||
"lint": "yarn tslint && yarn prettier:check"
|
||||
"eslint": "eslint 'src/*.ts'",
|
||||
"prettier:check": "prettier --check . --config .prettierrc",
|
||||
"prettier:fix": "prettier --write . --config .prettierrc",
|
||||
"lint": "yarn eslint && yarn prettier:check"
|
||||
},
|
||||
"author": "Alexey Pertsev <alexey@peppersec.com> (https://peppersec.com)",
|
||||
"keywords": [
|
||||
@ -29,19 +29,22 @@
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@types/chai": "^4.2.11",
|
||||
"@types/chai-as-promised": "^7.1.3",
|
||||
"@types/mocha": "^7.0.2",
|
||||
"@types/mockery": "^1.4.29",
|
||||
"@types/node": "^14.0.5",
|
||||
"@typescript-eslint/eslint-plugin": "^4.4.1",
|
||||
"@typescript-eslint/parser": "^4.4.1",
|
||||
"chai": "^4.2.0",
|
||||
"chai-as-promised": "^7.1.1",
|
||||
"eslint": "^7.11.0",
|
||||
"eslint-config-prettier": "^6.13.0",
|
||||
"eslint-plugin-prettier": "^3.1.4",
|
||||
"mocha": "^7.2.0",
|
||||
"mockery": "^2.1.0",
|
||||
"prettier": "^2.1.2",
|
||||
"ts-node": "^8.10.1",
|
||||
"tslint": "^6.1.2",
|
||||
"tslint-config-prettier": "^1.18.0",
|
||||
"tslint-plugin-prettier": "^2.3.0",
|
||||
"typescript": "^3.9.3"
|
||||
"typescript": "^4.0.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.19.2",
|
||||
|
@ -8,6 +8,7 @@ const ethgasstation: OffChainOracle = {
|
||||
standardPropertyName: 'average',
|
||||
lowPropertyName: 'safeLow',
|
||||
denominator: 10,
|
||||
additionalDataProperty: null,
|
||||
};
|
||||
|
||||
const zoltu: OffChainOracle = {
|
||||
@ -18,6 +19,7 @@ const zoltu: OffChainOracle = {
|
||||
standardPropertyName: 'percentile_60',
|
||||
lowPropertyName: 'percentile_30',
|
||||
denominator: 1,
|
||||
additionalDataProperty: null,
|
||||
};
|
||||
|
||||
const etherchain: OffChainOracle = {
|
||||
@ -28,6 +30,7 @@ const etherchain: OffChainOracle = {
|
||||
standardPropertyName: 'standard',
|
||||
lowPropertyName: 'safeLow',
|
||||
denominator: 1,
|
||||
additionalDataProperty: null,
|
||||
};
|
||||
|
||||
const poa: OffChainOracle = {
|
||||
@ -38,6 +41,18 @@ const poa: OffChainOracle = {
|
||||
standardPropertyName: 'standard',
|
||||
lowPropertyName: 'slow',
|
||||
denominator: 1,
|
||||
additionalDataProperty: null,
|
||||
};
|
||||
|
||||
const gasNow: OffChainOracle = {
|
||||
name: 'gasNow',
|
||||
url: 'https://www.gasnow.org/api/v3/gas/price?utm_source=gas-price-oracle',
|
||||
instantPropertyName: 'rapid',
|
||||
fastPropertyName: 'fast',
|
||||
standardPropertyName: 'standard',
|
||||
lowPropertyName: 'slow',
|
||||
denominator: 1e9,
|
||||
additionalDataProperty: 'data',
|
||||
};
|
||||
|
||||
const chainlink: OnChainOracle = {
|
||||
@ -49,9 +64,10 @@ const chainlink: OnChainOracle = {
|
||||
|
||||
export const offChainOracles: { [key: string]: OffChainOracle } = {
|
||||
ethgasstation,
|
||||
zoltu,
|
||||
gasNow,
|
||||
poa,
|
||||
etherchain,
|
||||
zoltu,
|
||||
};
|
||||
|
||||
export const onChainOracles: { [key: string]: OnChainOracle } = {
|
||||
|
107
src/index.ts
107
src/index.ts
@ -1,22 +1,24 @@
|
||||
import axios from 'axios';
|
||||
import config from './config';
|
||||
import { GasPrice, OffChainOracle, OnChainOracle, ConstructorArgs } from './types';
|
||||
import { GasPrice, OffChainOracle, OnChainOracle, Config, GasPriceKey, Options } from './types';
|
||||
import BigNumber from 'bignumber.js';
|
||||
|
||||
export class GasPriceOracle {
|
||||
lastGasPrice: GasPrice;
|
||||
defaultRpc = 'https://api.mycryptoapi.com/eth';
|
||||
offChainOracles = { ...config.offChainOracles };
|
||||
onChainOracles = { ...config.onChainOracles };
|
||||
configuration: Config = {
|
||||
defaultRpc: 'https://api.mycryptoapi.com/eth',
|
||||
timeout: 10000,
|
||||
};
|
||||
|
||||
constructor(options: ConstructorArgs) {
|
||||
if (options && options.defaultRpc) {
|
||||
this.defaultRpc = options.defaultRpc;
|
||||
constructor(options?: Options) {
|
||||
if (options) {
|
||||
Object.assign(this.configuration, options);
|
||||
}
|
||||
}
|
||||
|
||||
async fetchGasPricesOffChain(): Promise<GasPrice> {
|
||||
for (let oracle of Object.values(this.offChainOracles)) {
|
||||
async askOracle(oracle: OffChainOracle): Promise<GasPrice> {
|
||||
const {
|
||||
name,
|
||||
url,
|
||||
@ -25,11 +27,11 @@ export class GasPriceOracle {
|
||||
standardPropertyName,
|
||||
lowPropertyName,
|
||||
denominator,
|
||||
additionalDataProperty,
|
||||
} = oracle;
|
||||
try {
|
||||
const response = await axios.get(url, { timeout: 10000 });
|
||||
const response = await axios.get(url, { timeout: this.configuration.timeout });
|
||||
if (response.status === 200) {
|
||||
const gas = response.data;
|
||||
const gas = additionalDataProperty ? response.data[additionalDataProperty] : response.data;
|
||||
if (Number(gas[fastPropertyName]) === 0) {
|
||||
throw new Error(`${name} oracle provides corrupted values`);
|
||||
}
|
||||
@ -43,18 +45,76 @@ export class GasPriceOracle {
|
||||
} else {
|
||||
throw new Error(`Fetch gasPrice from ${name} oracle failed. Trying another one...`);
|
||||
}
|
||||
}
|
||||
async fetchGasPricesOffChain(): Promise<GasPrice> {
|
||||
for (const oracle of Object.values(this.offChainOracles)) {
|
||||
try {
|
||||
return await this.askOracle(oracle);
|
||||
} catch (e) {
|
||||
console.error(e.message);
|
||||
console.info(e.message);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
throw new Error('All oracles are down. Probably a network error.');
|
||||
}
|
||||
|
||||
async fetchMedianGasPriceOffChain(): Promise<GasPrice> {
|
||||
const promises: Promise<GasPrice>[] = [];
|
||||
for (const oracle of Object.values(this.offChainOracles) as Array<OffChainOracle>) {
|
||||
promises.push(this.askOracle(oracle));
|
||||
}
|
||||
|
||||
const settledPromises = await Promise.allSettled(promises);
|
||||
const allGasPrices = settledPromises.reduce((acc: GasPrice[], result) => {
|
||||
if (result.status === 'fulfilled') {
|
||||
acc.push(result.value);
|
||||
return acc;
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
if (allGasPrices.length === 0) {
|
||||
throw new Error('All oracles are down. Probably a network error.');
|
||||
}
|
||||
return this.median(allGasPrices);
|
||||
}
|
||||
|
||||
median(gasPrices: GasPrice[]): GasPrice {
|
||||
const medianGasPrice: GasPrice = { instant: 0, fast: 0, standard: 0, low: 0 };
|
||||
|
||||
const results: { [key in GasPriceKey]: number[] } = {
|
||||
instant: [],
|
||||
fast: [],
|
||||
standard: [],
|
||||
low: [],
|
||||
};
|
||||
|
||||
for (const gasPrice of gasPrices) {
|
||||
results.instant.push(gasPrice.instant);
|
||||
results.fast.push(gasPrice.fast);
|
||||
results.standard.push(gasPrice.standard);
|
||||
results.low.push(gasPrice.low);
|
||||
}
|
||||
|
||||
for (const type of Object.keys(medianGasPrice) as Array<keyof GasPrice>) {
|
||||
const allPrices = results[type].sort((a, b) => a - b);
|
||||
if (allPrices.length === 1) {
|
||||
medianGasPrice[type] = allPrices[0];
|
||||
continue;
|
||||
} else if (allPrices.length === 0) {
|
||||
continue;
|
||||
}
|
||||
const isEven = allPrices.length % 2 === 0;
|
||||
const middle = Math.floor(allPrices.length / 2);
|
||||
medianGasPrice[type] = isEven ? (allPrices[middle - 1] + allPrices[middle]) / 2.0 : allPrices[middle];
|
||||
}
|
||||
return medianGasPrice;
|
||||
}
|
||||
|
||||
async fetchGasPricesOnChain(): Promise<number> {
|
||||
for (let oracle of Object.values(this.onChainOracles)) {
|
||||
const { name, callData, contract, denominator } = oracle;
|
||||
let { rpc } = oracle;
|
||||
rpc = rpc ? rpc : this.defaultRpc;
|
||||
for (const oracle of Object.values(this.onChainOracles)) {
|
||||
const { name, callData, contract, denominator, rpc } = oracle;
|
||||
const rpcUrl = rpc || this.configuration.defaultRpc;
|
||||
const body = {
|
||||
jsonrpc: '2.0',
|
||||
id: 1337,
|
||||
@ -62,7 +122,8 @@ export class GasPriceOracle {
|
||||
params: [{ data: callData, to: contract }, 'latest'],
|
||||
};
|
||||
try {
|
||||
const response = await axios.post(rpc, body, { timeout: 10000 });
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const response = await axios.post(rpcUrl!, body, { timeout: this.configuration.timeout });
|
||||
if (response.status === 200) {
|
||||
const { result } = response.data;
|
||||
let fastGasPrice = new BigNumber(result);
|
||||
@ -81,7 +142,7 @@ export class GasPriceOracle {
|
||||
throw new Error('All oracles are down. Probably a network error.');
|
||||
}
|
||||
|
||||
async gasPrices(fallbackGasPrices?: GasPrice): Promise<GasPrice> {
|
||||
async gasPrices(fallbackGasPrices?: GasPrice, median = true): Promise<GasPrice> {
|
||||
const defaultFastGas = 22;
|
||||
const defaultFallbackGasPrices = {
|
||||
instant: defaultFastGas * 1.3,
|
||||
@ -91,7 +152,9 @@ export class GasPriceOracle {
|
||||
};
|
||||
this.lastGasPrice = this.lastGasPrice || fallbackGasPrices || defaultFallbackGasPrices;
|
||||
try {
|
||||
this.lastGasPrice = await this.fetchGasPricesOffChain();
|
||||
this.lastGasPrice = median
|
||||
? await this.fetchMedianGasPriceOffChain()
|
||||
: await this.fetchGasPricesOffChain();
|
||||
return this.lastGasPrice;
|
||||
} catch (e) {
|
||||
console.log('Failed to fetch gas prices from offchain oracles. Trying onchain ones...');
|
||||
@ -112,19 +175,19 @@ export class GasPriceOracle {
|
||||
return this.lastGasPrice;
|
||||
}
|
||||
|
||||
addOffChainOracle(oracle: OffChainOracle) {
|
||||
addOffChainOracle(oracle: OffChainOracle): void {
|
||||
this.offChainOracles[oracle.name] = oracle;
|
||||
}
|
||||
|
||||
addOnChainOracle(oracle: OnChainOracle) {
|
||||
addOnChainOracle(oracle: OnChainOracle): void {
|
||||
this.onChainOracles[oracle.name] = oracle;
|
||||
}
|
||||
|
||||
removeOnChainOracle(name: string) {
|
||||
removeOnChainOracle(name: string): void {
|
||||
delete this.onChainOracles[name];
|
||||
}
|
||||
|
||||
removeOffChainOracle(name: string) {
|
||||
removeOffChainOracle(name: string): void {
|
||||
delete this.offChainOracles[name];
|
||||
}
|
||||
}
|
||||
|
15
src/types.ts
15
src/types.ts
@ -6,6 +6,7 @@ export type OffChainOracle = {
|
||||
standardPropertyName: string;
|
||||
lowPropertyName: string;
|
||||
denominator: number;
|
||||
additionalDataProperty: string | null;
|
||||
};
|
||||
|
||||
export type OnChainOracle = {
|
||||
@ -17,12 +18,14 @@ export type OnChainOracle = {
|
||||
};
|
||||
|
||||
export type GasPrice = {
|
||||
instant: number;
|
||||
fast: number;
|
||||
standard: number;
|
||||
low: number;
|
||||
[key in GasPriceKey]: number;
|
||||
};
|
||||
|
||||
export interface ConstructorArgs {
|
||||
export type GasPriceKey = 'instant' | 'fast' | 'standard' | 'low';
|
||||
|
||||
export type Options = {
|
||||
defaultRpc?: string;
|
||||
}
|
||||
timeout?: number;
|
||||
};
|
||||
|
||||
export type Config = Required<Options>;
|
||||
|
@ -1,15 +1,17 @@
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
import { GasPrice } from '../src/types';
|
||||
import mockery from 'mockery';
|
||||
import chai from 'chai';
|
||||
import { onChainOracles } from '../src/config';
|
||||
|
||||
const { GasPriceOracle } = require('../src/index');
|
||||
import { GasPriceOracle } from '../src/index';
|
||||
chai.use(require('chai-as-promised'));
|
||||
chai.should();
|
||||
let oracle = new GasPriceOracle();
|
||||
|
||||
before('before', function () {
|
||||
let axiosMock = {
|
||||
const axiosMock = {
|
||||
get: () => {
|
||||
throw new Error('axios GET method is mocked for tests');
|
||||
},
|
||||
@ -24,6 +26,19 @@ beforeEach('beforeEach', function () {
|
||||
oracle = new GasPriceOracle();
|
||||
});
|
||||
|
||||
describe('constructor', function () {
|
||||
it('should set default values', async function () {
|
||||
oracle.configuration.defaultRpc.should.be.equal('https://api.mycryptoapi.com/eth');
|
||||
oracle.configuration.timeout.should.be.equal(10000);
|
||||
});
|
||||
|
||||
it('should set passed values', async function () {
|
||||
const newOracle = new GasPriceOracle({ timeout: 1337 });
|
||||
newOracle.configuration.defaultRpc.should.be.equal('https://api.mycryptoapi.com/eth');
|
||||
newOracle.configuration.timeout.should.be.equal(1337);
|
||||
});
|
||||
});
|
||||
|
||||
describe('fetchGasPricesOffChain', function () {
|
||||
it('should work', async function () {
|
||||
const gas: GasPrice = await oracle.fetchGasPricesOffChain();
|
||||
@ -61,7 +76,7 @@ describe('fetchGasPricesOnChain', function () {
|
||||
it('should work with custom rpc', async function () {
|
||||
const rpc = 'https://ethereum-rpc.trustwalletapp.com';
|
||||
const oracle = new GasPriceOracle({ defaultRpc: rpc });
|
||||
oracle.defaultRpc.should.be.equal(rpc);
|
||||
oracle.configuration.defaultRpc.should.be.equal(rpc);
|
||||
const gas: number = await oracle.fetchGasPricesOnChain();
|
||||
|
||||
gas.should.be.a('number');
|
||||
@ -144,6 +159,51 @@ describe('gasPrice', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('median', function () {
|
||||
it('should work', async function () {
|
||||
const gas1 = { instant: 100, fast: 100, standard: 100, low: 100 };
|
||||
const gas2 = { instant: 90, fast: 90, standard: 90, low: 90 };
|
||||
const gas3 = { instant: 70, fast: 70, standard: 70, low: 70 };
|
||||
const gas4 = { instant: 110.1, fast: 110.1, standard: 110.1, low: 110.1 };
|
||||
let gas: GasPrice = await oracle.median([gas1, gas2, gas3]);
|
||||
gas.instant.should.be.a('number');
|
||||
gas.fast.should.be.a('number');
|
||||
gas.standard.should.be.a('number');
|
||||
gas.low.should.be.a('number');
|
||||
|
||||
gas.instant.should.be.eq(90);
|
||||
gas.fast.should.be.eq(90);
|
||||
gas.standard.should.be.eq(90);
|
||||
gas.low.should.be.eq(90);
|
||||
|
||||
gas = await oracle.median([gas1, gas2, gas3, gas4]);
|
||||
gas.instant.should.be.a('number');
|
||||
gas.fast.should.be.a('number');
|
||||
gas.standard.should.be.a('number');
|
||||
gas.low.should.be.a('number');
|
||||
|
||||
gas.instant.should.be.eq(95);
|
||||
gas.fast.should.be.eq(95);
|
||||
gas.standard.should.be.eq(95);
|
||||
gas.low.should.be.eq(95);
|
||||
});
|
||||
});
|
||||
|
||||
describe('fetchMedianGasPriceOffChain', function () {
|
||||
it('should work', async function () {
|
||||
const gas: GasPrice = await oracle.fetchMedianGasPriceOffChain();
|
||||
gas.instant.should.be.a('number');
|
||||
gas.fast.should.be.a('number');
|
||||
gas.standard.should.be.a('number');
|
||||
gas.low.should.be.a('number');
|
||||
|
||||
gas.instant.should.be.at.least(gas.fast); // greater than or equal to the given number.
|
||||
gas.fast.should.be.at.least(gas.standard);
|
||||
gas.standard.should.be.at.least(gas.low);
|
||||
gas.low.should.not.be.equal(0);
|
||||
});
|
||||
});
|
||||
|
||||
after('after', function () {
|
||||
after(function () {
|
||||
mockery.disable();
|
||||
|
@ -6,13 +6,15 @@
|
||||
"lib": [
|
||||
"es2017",
|
||||
"esnext.asynciterable",
|
||||
"es2019"
|
||||
"es2019",
|
||||
"ES2020.Promise"
|
||||
] /* Specify library files to be included in the compilation. */,
|
||||
"outDir": "./lib" /* Redirect output structure to the directory. */,
|
||||
"strict": true /* Enable all strict type-checking options. */,
|
||||
"strictPropertyInitialization": false /* Enable strict checking of property initialization in classes. */,
|
||||
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
|
||||
"declaration": true /* Generates corresponding '.d.ts' file. */,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
// "allowJs": true, /* Allow javascript files to be compiled. */
|
||||
// "checkJs": true, /* Report errors in .js files. */
|
||||
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
|
||||
@ -60,11 +62,7 @@
|
||||
/* Experimental Options */
|
||||
"experimentalDecorators": true /* Enables experimental support for ES7 decorators. */,
|
||||
"emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */,
|
||||
"plugins": [
|
||||
{
|
||||
"name": "typescript-tslint-plugin"
|
||||
}
|
||||
]
|
||||
"plugins": []
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules", "tests"]
|
||||
|
12
tslint.json
12
tslint.json
@ -1,12 +0,0 @@
|
||||
{
|
||||
"extends": "tslint-config-prettier",
|
||||
"defaultSeverity": "error",
|
||||
"rules": {
|
||||
"semicolon": [true, "always"],
|
||||
"space-before-function-paren": [
|
||||
true,
|
||||
{ "anonymous": "always", "named": "never", "asyncArrow": "always" }
|
||||
],
|
||||
"type-literal-delimiter": true
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user