diff --git a/.gitignore b/.gitignore index 41847c7fd604..7b81c70ca57a 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ pids *.pid *.seed *.pid.lock +package-lock.json # Directory for instrumented libs generated by jscoverage/JSCover lib-cov diff --git a/.prettierignore b/.prettierignore index 49b3a61e6d8f..d96a940857f3 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,8 +1,7 @@ -**/tests/fixtures/**/* +**/fixtures/**/* **/dist **/coverage **/shared-fixtures -**/tests/integration/fixtures/**/* **/lib/configs/recommended.json **/.vscode -**/.nyc_output \ No newline at end of file +**/.nyc_output diff --git a/packages/benchmark/benchmark.js b/packages/benchmark/benchmark.js new file mode 100644 index 000000000000..f74616be4db4 --- /dev/null +++ b/packages/benchmark/benchmark.js @@ -0,0 +1,95 @@ +const Benchmark = require('benchmark'); + +function createBenchmark(name, directory, files, useServices) { + return new Promise((resolve, reject) => { + const suite = new Benchmark.Suite(name, { + async: true + }); + suite + .add('tslint', function() { + const result = require('./tslint-runner').runTSLint( + directory, + files, + useServices + ); + if (typeof result !== 'string') { + throw new Error('something went wrong'); + } + }) + .add('eslint', function() { + const result = require('./eslint-runner').runESLint( + directory, + files, + useServices + ); + if (typeof result !== 'string') { + throw new Error('something went wrong'); + } + }) + // add listeners + .on('cycle', function(event) { + console.log(String(event.target)); + }) + .on('error', function(e) { + reject(e); + }) + .on('complete', function() { + console.log( + `Fastest is ${this.filter('fastest') + .map(i => i.name) + .join(', ')}` + ); + resolve(); + }) + .run(); + }); +} + +async function runAllBenchmarks(scenarios) { + for (const scenario of scenarios) { + console.log(scenario.name); + await createBenchmark( + scenario.name, + scenario.directory, + scenario.files, + scenario.useServices + ); + } +} + +runAllBenchmarks([ + { + name: 'Single File: restrict-plus-operands', + directory: 'fixtures/restrict-plus-operands/', + useServices: true, + files: ['fixtures/restrict-plus-operands/test1.ts'] + }, + { + name: 'Multi File: restrict-plus-operands', + directory: 'fixtures/restrict-plus-operands/', + useServices: true, + files: [ + 'fixtures/restrict-plus-operands/test1.ts', + 'fixtures/restrict-plus-operands/test2.ts', + 'fixtures/restrict-plus-operands/test3.ts' + ] + }, + { + name: 'Single File: no-empty-interface', + directory: 'fixtures/no-empty-interface/', + useServices: false, + files: ['fixtures/no-empty-interface/test1.ts'] + }, + { + name: 'Multi File: no-empty-interface', + directory: 'fixtures/no-empty-interface/', + useServices: false, + files: [ + 'fixtures/no-empty-interface/test1.ts', + 'fixtures/no-empty-interface/test2.ts', + 'fixtures/no-empty-interface/test3.ts' + ] + } +]).catch(e => { + console.log(e); +}); diff --git a/packages/benchmark/e2e.js b/packages/benchmark/e2e.js new file mode 100644 index 000000000000..c80e5741f48c --- /dev/null +++ b/packages/benchmark/e2e.js @@ -0,0 +1,66 @@ +const Benchmark = require('benchmark'); +const child_process = require('child_process'); +const path = require('path'); + +function normalizeCommand(command) { + return path.normalize(command); +} + +function createBenchmark(name, directory, files, useServices) { + return new Promise((resolve, reject) => { + const suite = new Benchmark.Suite(name, { + async: true + }); + suite + .add('tslint', function() { + let hasError = false; + try { + child_process.execSync( + normalizeCommand('./node_modules/.bin/tslint') + + ' -p fixtures/restrict-plus-operands/tsconfig.json "fixtures/restrict-plus-operands/*.ts"' + ); + } catch (e) { + // console.error(e.output ? e.output.toString() : e); + hasError = true; + } + if (!hasError) { + throw new Error('something went wrong'); + } + }) + .add('eslint', function() { + let hasError = false; + try { + child_process.execSync( + normalizeCommand('./node_modules/.bin/eslint') + + ' --ext .ts "fixtures/restrict-plus-operands/*.ts"' + ); + } catch (e) { + // console.error(e.output ? e.output.toString() : e); + hasError = true; + } + if (!hasError) { + throw new Error('something went wrong'); + } + }) + // add listeners + .on('cycle', function(event) { + console.log(String(event.target)); + }) + .on('error', function(e) { + reject(e); + }) + .on('complete', function() { + console.log( + `Fastest is ${this.filter('fastest') + .map(i => i.name) + .join(', ')}` + ); + resolve(); + }) + .run(); + }); +} + +createBenchmark().catch(e => { + console.log(e); +}); diff --git a/packages/benchmark/eslint-runner.js b/packages/benchmark/eslint-runner.js new file mode 100644 index 000000000000..397d6b56a271 --- /dev/null +++ b/packages/benchmark/eslint-runner.js @@ -0,0 +1,56 @@ +const eslint = require('eslint'); +const path = require('path'); +const fs = require('fs'); + +// exports.runESLint = function(directory, files, useServices) { +// const linter = new eslint.CLIEngine({ +// files: files, +// configFile: `${directory}.eslintrc.js`, +// extensions: ['.js', '.ts'] +// }); +// const results = []; +// for (const file of files) { +// results.push( +// linter.executeOnText( +// fs.readFileSync(path.join(__dirname, file), 'utf8'), +// file, +// true +// ) +// ); +// } +// +// if (results[0].errorCount === 0) { +// throw new Error('something went wrong'); +// } +// if ( +// results[0].results[0].messages[0].message !== +// 'An empty interface is equivalent to `{}`.' +// ) { +// throw new Error('something went wrong'); +// } +// }; + +exports.runESLint = function(directory, files, useServices) { + const linter = new eslint.Linter(); + linter.defineRule( + '@typescript-eslint/no-empty-interface', + require('@typescript-eslint/eslint-plugin/lib/rules/no-empty-interface') + ); + linter.defineRule( + '@typescript-eslint/restrict-plus-operands', + require('@typescript-eslint/eslint-plugin/lib/rules/restrict-plus-operands') + ); + let result; + for (const file of files) { + result = linter.verify( + fs.readFileSync(path.join(__dirname, file), 'utf8'), + require(path.join(__dirname, `./${directory}.eslintrc.js`)), + file + ); + } + if (result.length === 0) { + throw new Error('something went wrong'); + } + + return result[0].message; +}; diff --git a/packages/benchmark/fixtures/complex/test.ts b/packages/benchmark/fixtures/complex/test.ts new file mode 100644 index 000000000000..6dd1d10852a1 --- /dev/null +++ b/packages/benchmark/fixtures/complex/test.ts @@ -0,0 +1,402 @@ +class Red extends Color { + public shade() { + var getHue = () => { + return this.hue(); + }; + return getHue() + ' red'; + } +} + +class Color { + public shade() { + return 'some shade'; + } + public hue() { + return 'some hue'; + } +} + +class Blue extends Color { + public shade() { + var getHue = () => { + return this.hue(); + }; + return getHue() + ' blue'; + } +} + +var r = new Red(); +var b = new Blue(); + +r.shade(); +r.hue(); +b.shade(); +b.hue(); + +// @declaration: true +// @filename: foo.ts +const foo = { + bar: 'hello', + bat: 'world', + bam: { bork: { bar: 'a', baz: 'b' } } +}; +const arr: [0, 1, 2, ['a', 'b', 'c', [{ def: 'def' }, { sec: 'sec' }]]] = [ + 0, + 1, + 2, + ['a', 'b', 'c', [{ def: 'def' }, { sec: 'sec' }]] +]; + +const { + bar: baz, + bat, + bam: { + bork: { bar: ibar, baz: ibaz } + } +} = foo; +{ baz, ibaz }; + +const [, one, , [, bee, , [, { sec }]]] = arr; +{ one, bee, sec }; + +const getFoo = () => ({ + foo: 'foo' +}); + +const { foo: foo2 } = getFoo(); +class TestFile { + name: string; + foo(message: string): () => string { + return (...x: string[]) => + /// Test summary + /// + /// + + message + this.name; + } +} +var simpleExample = class { + static getTags() {} + tags() {} +}; +var circularReference = class C { + static getTags(c: C): C { + return c; + } + tags(c: C): C { + return c; + } +}; + +// repro from #15066 +class FooItem { + foo(): void {} + name?: string; +} + +type Constructor = new (...args: any[]) => T; +function WithTags>(Base: T) { + return class extends Base { + static getTags(): void {} + tags(): void {} + }; +} + +class Test extends WithTags(FooItem) {} + +const test = new Test(); + +Test.getTags(); +test.tags(); +interface Foo { + a: string; + b: number; +}; + +interface Bar { + b: string; +} + +interface Other { + totallyUnrelatedProperty: number; +} + +let x = { a: '', b: '' }; + +declare function f(x: Foo | Other): any; + +f(x); +f({ a: '', b: '' }) + +declare function g(x: Bar | Other): any; + +g(x); +g({ a: '', b: '' }) + +declare function h(x: Foo | Bar | Other): any; + +h(x); +h({ a: '', b: '' }) + +interface CatDog { cat: any, dog: any } +interface ManBearPig { man: any, bear: any, pig: any } +interface Platypus { platypus: any } + +type ExoticAnimal = + | CatDog + | ManBearPig + | Platypus; + +declare function addToZoo(animal: ExoticAnimal): void; + +addToZoo({ dog: "Barky McBarkface" }); +addToZoo({ man: "Manny", bear: "Coffee" }); + +const manBeer = { man: "Manny", beer: "Coffee" }; +addToZoo({ man: "Manny", beer: "Coffee" }); +addToZoo(manBeer); +interface I { +} + +enum E { + Red, Green, Blue +} + +function f() { + var a: any; + var n=3; + var s=""; + var b=false; + var i:I; + var e:E; + + n&&a; + n&&s; + n&&b; + n&&i; + n&&n; + n&&e; + + s&&a; + s&&n; + s&&b; + s&&i; + s&&s; + s&&e; + + a&&n; + a&&s; + a&&b; + a&&i; + a&&a; + a&&e; + + i&&n; + i&&s; + i&&b; + i&&a; + i&&i; + i&&e; + + e&&n; + e&&s; + e&&b; + e&&a; + e&&i; + e&&e; + + n||a; + n||s; + n||b; + n||i; + n||n; + n||e; + + s||a; + s||n; + s||b; + s||i; + s||s; + s||e; + + a||n; + a||s; + a||b; + a||i; + a||a; + a||e; + + i||n; + i||s; + i||b; + i||a; + i||i; + i||e; + + e||n; + e||s; + e||b; + e||a; + e||i; + e||e; + + n==a; + n==s; + n==b; + n==i; + n==n; + n==e; + + s==a; + s==n; + s==b; + s==i; + s==s; + s==e; + + a==n; + a==s; + a==b; + a==i; + a==a; + a==e; + + i==n; + i==s; + i==b; + i==a; + i==i; + i==e; + + e==n; + e==s; + e==b; + e==a; + e==i; + e==e; + + +i; + +s; + +n; + +a; + +b; + + -i; + -s; + -n; + -a; + -b; + + !i; + !s; + !n; + !a; + !b; + + + n+a; + n+s; + n+b; + n+i; + n+n; + n+e; + + s+a; + s+n; + s+b; + s+i; + s+s; + s+e; + + a+n; + a+s; + a+b; + a+i; + a+a; + a+e; + + i+n; + i+s; + i+b; + i+a; + i+i; + i+e; + + e+n; + e+s; + e+b; + e+a; + e+i; + e+e; + + n^a; + n^s; + n^b; + n^i; + n^n; + n^e; + + s^a; + s^n; + s^b; + s^i; + s^s; + s^e; + + a^n; + a^s; + a^b; + a^i; + a^a; + a^e; + + i^n; + i^s; + i^b; + i^a; + i^i; + i^e; + + e^n; + e^s; + e^b; + e^a; + e^i; + e^e; + + n-a; + n-s; + n-b; + n-i; + n-n; + n-e; + + s-a; + s-n; + s-b; + s-i; + s-s; + s-e; + + a-n; + a-s; + a-b; + a-i; + a-a; + a-e; + + i-n; + i-s; + i-b; + i-a; + i-i; + i-e; + + e-n; + e-s; + e-b; + e-a; + e-i; + e-e; + +} diff --git a/packages/benchmark/fixtures/no-empty-interface/.eslintrc.js b/packages/benchmark/fixtures/no-empty-interface/.eslintrc.js new file mode 100644 index 000000000000..4eb332bf900d --- /dev/null +++ b/packages/benchmark/fixtures/no-empty-interface/.eslintrc.js @@ -0,0 +1,14 @@ +module.exports = { + root: true, + plugins: ['@typescript-eslint'], + rules: { + '@typescript-eslint/no-empty-interface': 'error' + }, + parser: '@typescript-eslint/parser', + parserOptions: { + sourceType: 'module', + ecmaFeatures: { + jsx: false + } + } +}; diff --git a/packages/benchmark/fixtures/no-empty-interface/test1.ts b/packages/benchmark/fixtures/no-empty-interface/test1.ts new file mode 100644 index 000000000000..fa0ebf3c5638 --- /dev/null +++ b/packages/benchmark/fixtures/no-empty-interface/test1.ts @@ -0,0 +1 @@ +interface Foo {} diff --git a/packages/benchmark/fixtures/no-empty-interface/test2.ts b/packages/benchmark/fixtures/no-empty-interface/test2.ts new file mode 100644 index 000000000000..fa0ebf3c5638 --- /dev/null +++ b/packages/benchmark/fixtures/no-empty-interface/test2.ts @@ -0,0 +1 @@ +interface Foo {} diff --git a/packages/benchmark/fixtures/no-empty-interface/test3.ts b/packages/benchmark/fixtures/no-empty-interface/test3.ts new file mode 100644 index 000000000000..fa0ebf3c5638 --- /dev/null +++ b/packages/benchmark/fixtures/no-empty-interface/test3.ts @@ -0,0 +1 @@ +interface Foo {} diff --git a/packages/benchmark/fixtures/no-empty-interface/tslint.json b/packages/benchmark/fixtures/no-empty-interface/tslint.json new file mode 100644 index 000000000000..366f38b35e96 --- /dev/null +++ b/packages/benchmark/fixtures/no-empty-interface/tslint.json @@ -0,0 +1,5 @@ +{ + "rules": { + "no-empty-interface": true + } +} diff --git a/packages/benchmark/fixtures/restrict-plus-operands/.eslintrc.js b/packages/benchmark/fixtures/restrict-plus-operands/.eslintrc.js new file mode 100644 index 000000000000..29e8d14c7e9d --- /dev/null +++ b/packages/benchmark/fixtures/restrict-plus-operands/.eslintrc.js @@ -0,0 +1,16 @@ +module.exports = { + root: true, + plugins: ['@typescript-eslint'], + rules: { + '@typescript-eslint/restrict-plus-operands': 'error' + }, + parser: '@typescript-eslint/parser', + parserOptions: { + tsconfigRootDir: __dirname, + project: './tsconfig.json', + sourceType: 'module', + ecmaFeatures: { + jsx: false + } + } +}; diff --git a/packages/benchmark/fixtures/restrict-plus-operands/test1.ts b/packages/benchmark/fixtures/restrict-plus-operands/test1.ts new file mode 100644 index 000000000000..aa88a44282b9 --- /dev/null +++ b/packages/benchmark/fixtures/restrict-plus-operands/test1.ts @@ -0,0 +1 @@ +const test = 'a' + 1 diff --git a/packages/benchmark/fixtures/restrict-plus-operands/test2.ts b/packages/benchmark/fixtures/restrict-plus-operands/test2.ts new file mode 100644 index 000000000000..aa88a44282b9 --- /dev/null +++ b/packages/benchmark/fixtures/restrict-plus-operands/test2.ts @@ -0,0 +1 @@ +const test = 'a' + 1 diff --git a/packages/benchmark/fixtures/restrict-plus-operands/test3.ts b/packages/benchmark/fixtures/restrict-plus-operands/test3.ts new file mode 100644 index 000000000000..aa88a44282b9 --- /dev/null +++ b/packages/benchmark/fixtures/restrict-plus-operands/test3.ts @@ -0,0 +1 @@ +const test = 'a' + 1 diff --git a/packages/benchmark/fixtures/restrict-plus-operands/tsconfig.json b/packages/benchmark/fixtures/restrict-plus-operands/tsconfig.json new file mode 100644 index 000000000000..3792da469e0e --- /dev/null +++ b/packages/benchmark/fixtures/restrict-plus-operands/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "declaration": true + }, + "include": [ + "./*" + ] +} diff --git a/packages/benchmark/fixtures/restrict-plus-operands/tslint.json b/packages/benchmark/fixtures/restrict-plus-operands/tslint.json new file mode 100644 index 000000000000..8389b1de47a8 --- /dev/null +++ b/packages/benchmark/fixtures/restrict-plus-operands/tslint.json @@ -0,0 +1,5 @@ +{ + "rules": { + "restrict-plus-operands": true + } +} diff --git a/packages/benchmark/package.json b/packages/benchmark/package.json new file mode 100644 index 000000000000..58f4d26fcdc5 --- /dev/null +++ b/packages/benchmark/package.json @@ -0,0 +1,22 @@ +{ + "name": "@typescript-eslint/benchmark", + "version": "1.1.0", + "private": true, + "scripts": { + "benchmark:rules": "node --allow-natives-syntax benchmark.js", + "benchmark:parsers": "node --allow-natives-syntax parsers.js", + "benchmark:e2e": "node --allow-natives-syntax e2e.js", + "eslint": "eslint --ext .ts fixtures/restrict-plus-operands", + "tslint": "tslint -p fixtures/restrict-plus-operands/tsconfig.json \"fixtures/restrict-plus-operands/*.ts\"" + }, + "devDependencies": { + "@types/benchmark": "^1.0.31", + "@types/eslint": "^4.16.5", + "@typescript-eslint/eslint-plugin": "1.1.1", + "@typescript-eslint/parser": "1.1.1", + "@typescript-eslint/typescript-estree": "1.1.1", + "benchmark": "^2.1.4", + "eslint": "^5.12.1", + "tslint": "^5.12.1" + } +} diff --git a/packages/benchmark/parsers.js b/packages/benchmark/parsers.js new file mode 100644 index 000000000000..9565c262531f --- /dev/null +++ b/packages/benchmark/parsers.js @@ -0,0 +1,90 @@ +const Benchmark = require('benchmark'); +const fs = require('fs'); +const path = require('path'); +const tsEstree = require('@typescript-eslint/typescript-estree'); +const tsParser = require('@typescript-eslint/parser'); + +function runTSESTree(directory, files) { + for (const file of files) { + const result = tsEstree.parse( + fs.readFileSync(path.join(__dirname, file), 'utf8'), + { + comment: true, + tokens: true + } + ); + if (result.type !== 'Program') { + throw new Error('something went wrong'); + } + } +} + +function runTSParser(directory, files) { + for (const file of files) { + const result = tsParser.parse( + fs.readFileSync(path.join(__dirname, file), 'utf8'), + { + comment: true, + tokens: true + } + ); + if (result.type !== 'Program') { + throw new Error('something went wrong'); + } + } +} + +function createBenchmark(name, directory, files) { + return new Promise((resolve, reject) => { + const suite = new Benchmark.Suite(name); + suite + .add('ts-estree', function() { + runTSESTree(directory, files); + }) + .add('ts-parser', function tsParser() { + runTSParser(directory, files); + }) + // add listeners + .on('cycle', function(event) { + console.log(String(event.target)); + }) + .on('error', function(e) { + reject(e); + }) + .on('complete', function() { + console.log( + `Fastest is ${this.filter('fastest') + .map(i => i.name) + .join(', ')}` + ); + resolve(); + }) + .run({ + async: true, + minSamples: 100000, + initCount: 100000 + }); + }); +} + +async function runAllBenchmarks(scenarios) { + for (const scenario of scenarios) { + console.log(scenario.name); + await createBenchmark(scenario.name, scenario.directory, scenario.files); + } +} + +runAllBenchmarks([ + { + name: 'Complex File', + directory: 'fixtures/complex/', + files: ['fixtures/complex/test.ts'] + }, + { + name: 'Simple File', + directory: 'fixtures/restrict-plus-operands/', + files: ['fixtures/restrict-plus-operands/test1.ts'] + } +]).catch(e => { + console.log(e); +}); diff --git a/packages/benchmark/tsconfig.json b/packages/benchmark/tsconfig.json new file mode 100644 index 000000000000..38e6103e9c6e --- /dev/null +++ b/packages/benchmark/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "declaration": true + }, + "include": ["src"] +} diff --git a/packages/benchmark/tslint-runner.js b/packages/benchmark/tslint-runner.js new file mode 100644 index 000000000000..aff8f3c5fe0d --- /dev/null +++ b/packages/benchmark/tslint-runner.js @@ -0,0 +1,31 @@ +const tslint = require('tslint'); +const path = require('path'); +const fs = require('fs'); + +exports.runTSLint = function(directory, files, useServices) { + const program = useServices + ? tslint.Linter.createProgram(`${directory}tsconfig.json`) + : undefined; + const linter = new tslint.Linter( + { + fix: false, + formatter: 'json' + }, + program + ); + const tslintConfig = tslint.Configuration.loadConfigurationFromPath( + `./${directory}tslint.json` + ); + for (const file of files) { + linter.lint( + file, + fs.readFileSync(path.join(__dirname, file), 'utf8'), + tslintConfig + ); + } + const result = linter.getResult(); + if (result.errorCount === 0) { + throw new Error('something went wrong'); + } + return result.failures[0].failure; +}; diff --git a/yarn.lock b/yarn.lock index b6a6d614dfc0..c91501604006 100644 --- a/yarn.lock +++ b/yarn.lock @@ -803,6 +803,11 @@ resolved "https://registry.yarnpkg.com/@types/babel-code-frame/-/babel-code-frame-6.20.1.tgz#e79a40ea81435034df7b46b5e32e8ed638aea4dd" integrity sha1-55pA6oFDUDTfe0a14y6O1jiupN0= +"@types/benchmark@^1.0.31": + version "1.0.31" + resolved "https://registry.yarnpkg.com/@types/benchmark/-/benchmark-1.0.31.tgz#2dd3514e93396f362ba5551a7c9ff0da405c1d38" + integrity sha512-F6fVNOkGEkSdo/19yWYOwVKGvzbTeWkR/XQYBKtGBQ9oGRjBN9f/L4aJI4sDcVPJO58Y1CJZN8va9V2BhrZapA== + "@types/eslint-visitor-keys@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" @@ -1361,6 +1366,14 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" +benchmark@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/benchmark/-/benchmark-2.1.4.tgz#09f3de31c916425d498cc2ee565a0ebf3c2a5629" + integrity sha1-CfPeMckWQl1JjMLuVloOvzwqVik= + dependencies: + lodash "^4.17.4" + platform "^1.3.3" + bin-links@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/bin-links/-/bin-links-1.1.2.tgz#fb74bd54bae6b7befc6c6221f25322ac830d9757" @@ -5743,6 +5756,11 @@ pkg-dir@^3.0.0: dependencies: find-up "^3.0.0" +platform@^1.3.3: + version "1.3.5" + resolved "https://registry.yarnpkg.com/platform/-/platform-1.3.5.tgz#fb6958c696e07e2918d2eeda0f0bc9448d733444" + integrity sha512-TuvHS8AOIZNAlE77WUDiR4rySV/VMptyMfcfeoMgs4P8apaZM3JrnbzBiixKUv+XR6i+BXrQh8WAnjaSPFO65Q== + please-upgrade-node@^3.0.2, please-upgrade-node@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/please-upgrade-node/-/please-upgrade-node-3.1.1.tgz#ed320051dfcc5024fae696712c8288993595e8ac" @@ -7000,7 +7018,7 @@ tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ== -tslint@^5.11.0: +tslint@^5.11.0, tslint@^5.12.1: version "5.12.1" resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.12.1.tgz#8cec9d454cf8a1de9b0a26d7bdbad6de362e52c1" integrity sha512-sfodBHOucFg6egff8d1BvuofoOQ/nOeYNfbp7LDlKBcLNrL3lmS5zoiDGyOMdT7YsEXAwWpTdAHwOGOc8eRZAw== pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy