From d6d32e7d9d9a2b823c6043a4afcaee1a6aee6bc6 Mon Sep 17 00:00:00 2001 From: Sam Verschueren Date: Mon, 30 Sep 2019 19:27:38 +0200 Subject: [PATCH] Expose programming API - fixes #37 --- package.json | 5 ++-- readme.md | 31 +++++++++++++++++++++++ source/cli.ts | 2 +- source/index.ts | 3 +++ source/lib/index.ts | 2 +- source/test/cli.ts | 34 +++++++++++++++++++++++++ source/test/test.ts | 60 ++++++++++++++++++++++----------------------- 7 files changed, 103 insertions(+), 34 deletions(-) create mode 100644 source/test/cli.ts diff --git a/package.json b/package.json index 36601c28..612a5f99 100644 --- a/package.json +++ b/package.json @@ -16,10 +16,10 @@ "node": ">=6" }, "scripts": { - "prepublishOnly": "npm run build && chmod +x dist/cli.js", + "prepublishOnly": "npm run build", "pretest": "npm run build && cpy \"./**/**\" \"../../../dist/test/fixtures/\" --parents --cwd=source/test/fixtures", "test": "npm run lint && ava dist/test/test.js", - "build": "npm run clean && tsc", + "build": "npm run clean && tsc && chmod +x dist/cli.js", "clean": "del-cli dist", "lint": "tslint -p . --format stylish" }, @@ -41,6 +41,7 @@ ], "dependencies": { "eslint-formatter-pretty": "^1.3.0", + "execa": "^2.0.4", "globby": "^9.1.0", "meow": "^5.0.0", "path-exists": "^3.0.0", diff --git a/readme.md b/readme.md index eeaf2846..68930022 100644 --- a/readme.md +++ b/readme.md @@ -153,6 +153,37 @@ Check if the function call has argument type errors. Check if a value is of the provided type `T`. +## Programmatic API + +You can use the programmatic API to retrieve the diagnostics and do something with them. This can be useful to run the tests with AVA, Jest or any other testing framework. + +```ts +import tsd from 'tsd'; + +(async () => { + const diagnostics = await tsd(); + + console.log(diagnostics.length); + //=> 2 +})(); +``` + +### tsd([options]) + +Retrieve the type definition diagnostics of the project. + +#### options + +Type: `object` + +##### cwd + +Type: `string`
+Default: `process.cwd()` + +Current working directory of the project to retrieve the diagnostics for. + + ## License MIT © [Sam Verschueren](https://github.com/SamVerschueren) diff --git a/source/cli.ts b/source/cli.ts index 0e91af56..1ed34a51 100644 --- a/source/cli.ts +++ b/source/cli.ts @@ -6,7 +6,7 @@ import tsd from './lib'; const cli = meow(` Usage - $ tsd [path] + $ tsd [path] Examples $ tsd /path/to/project diff --git a/source/index.ts b/source/index.ts index 8b6937f6..ae6bb6b5 100644 --- a/source/index.ts +++ b/source/index.ts @@ -1 +1,4 @@ +import tsd from './lib'; + export * from './lib/assert'; +export default tsd; diff --git a/source/lib/index.ts b/source/lib/index.ts index a58b04e5..ba71256c 100644 --- a/source/lib/index.ts +++ b/source/lib/index.ts @@ -7,7 +7,7 @@ import loadConfig from './config'; import getCustomDiagnostics from './rules'; import {Context, Config} from './interfaces'; -interface Options { +export interface Options { cwd: string; } diff --git a/source/test/cli.ts b/source/test/cli.ts new file mode 100644 index 00000000..f332b29e --- /dev/null +++ b/source/test/cli.ts @@ -0,0 +1,34 @@ +import * as path from 'path'; +import test from 'ava'; +import * as execa from 'execa'; + +interface ExecaError extends Error { + readonly exitCode: number; + readonly stderr: string; +} + +test('fail if errors are found', async t => { + const {exitCode, stderr} = await t.throwsAsync(execa('../../../cli.js', { + cwd: path.join(__dirname, 'fixtures/failure') + })); + + t.is(exitCode, 1); + t.regex(stderr, /5:19[ ]{2}Argument of type number is not assignable to parameter of type string./); +}); + +test('succeed if no errors are found', async t => { + const {exitCode} = await execa('../../../cli.js', { + cwd: path.join(__dirname, 'fixtures/success') + }); + + t.is(exitCode, 0); +}); + +test('provide a path', async t => { + const file = path.join(__dirname, 'fixtures/failure'); + + const {exitCode, stderr} = await t.throwsAsync(execa('dist/cli.js', [file])); + + t.is(exitCode, 1); + t.regex(stderr, /5:19[ ]{2}Argument of type number is not assignable to parameter of type string./); +}); diff --git a/source/test/test.ts b/source/test/test.ts index 730be2dc..b69a398d 100644 --- a/source/test/test.ts +++ b/source/test/test.ts @@ -1,6 +1,6 @@ import * as path from 'path'; import test, {ExecutionContext} from 'ava'; -import m from '../lib'; +import tsd from '..'; import {Diagnostic} from '../lib/interfaces'; type Expectation = [number, number, 'error' | 'warning', string, (string | RegExp)?]; @@ -32,15 +32,15 @@ const verify = (t: ExecutionContext, diagnostics: Diagnostic[], expectations: Ex }; test('throw if no type definition was found', async t => { - await t.throwsAsync(m({cwd: path.join(__dirname, 'fixtures/no-tsd')}), 'The type definition `index.d.ts` does not exist. Create one and try again.'); + await t.throwsAsync(tsd({cwd: path.join(__dirname, 'fixtures/no-tsd')}), 'The type definition `index.d.ts` does not exist. Create one and try again.'); }); test('throw if no test is found', async t => { - await t.throwsAsync(m({cwd: path.join(__dirname, 'fixtures/no-test')}), 'The test file `index.test-d.ts` or `index.test-d.tsx` does not exist. Create one and try again.'); + await t.throwsAsync(tsd({cwd: path.join(__dirname, 'fixtures/no-test')}), 'The test file `index.test-d.ts` or `index.test-d.tsx` does not exist. Create one and try again.'); }); test('return diagnostics', async t => { - const diagnostics = await m({cwd: path.join(__dirname, 'fixtures/failure')}); + const diagnostics = await tsd({cwd: path.join(__dirname, 'fixtures/failure')}); verify(t, diagnostics, [ [5, 19, 'error', 'Argument of type \'number\' is not assignable to parameter of type \'string\'.'] @@ -48,7 +48,7 @@ test('return diagnostics', async t => { }); test('return diagnostics from imported files as well', async t => { - const diagnostics = await m({cwd: path.join(__dirname, 'fixtures/failure-nested')}); + const diagnostics = await tsd({cwd: path.join(__dirname, 'fixtures/failure-nested')}); verify(t, diagnostics, [ [5, 19, 'error', 'Argument of type \'number\' is not assignable to parameter of type \'string\'.', /child.test-d.ts$/], @@ -57,7 +57,7 @@ test('return diagnostics from imported files as well', async t => { }); test('fail if typings file is not part of `files` list', async t => { - const diagnostics = await m({cwd: path.join(__dirname, 'fixtures/no-files')}); + const diagnostics = await tsd({cwd: path.join(__dirname, 'fixtures/no-files')}); verify(t, diagnostics, [ [3, 1, 'error', 'TypeScript type definition `index.d.ts` is not part of the `files` list.', 'package.json'], @@ -65,7 +65,7 @@ test('fail if typings file is not part of `files` list', async t => { }); test('fail if `typings` property is used instead of `types`', async t => { - const diagnostics = await m({cwd: path.join(__dirname, 'fixtures/types-property/typings')}); + const diagnostics = await tsd({cwd: path.join(__dirname, 'fixtures/types-property/typings')}); verify(t, diagnostics, [ [3, 1, 'error', 'Use property `types` instead of `typings`.', 'package.json'], @@ -73,7 +73,7 @@ test('fail if `typings` property is used instead of `types`', async t => { }); test('fail if tests don\'t pass in strict mode', async t => { - const diagnostics = await m({ + const diagnostics = await tsd({ cwd: path.join(__dirname, 'fixtures/failure-strict-null-checks') }); @@ -83,7 +83,7 @@ test('fail if tests don\'t pass in strict mode', async t => { }); test('overridden config defaults to `strict` if `strict` is not explicitly overridden', async t => { - const diagnostics = await m({ + const diagnostics = await tsd({ cwd: path.join(__dirname, 'fixtures/strict-null-checks-as-default-config-value') }); @@ -93,7 +93,7 @@ test('overridden config defaults to `strict` if `strict` is not explicitly overr }); test('fail if types are used from a lib that was not explicitly specified', async t => { - const diagnostics = await m({cwd: path.join(__dirname, 'fixtures/lib-config/failure-missing-lib')}); + const diagnostics = await tsd({cwd: path.join(__dirname, 'fixtures/lib-config/failure-missing-lib')}); verify(t, diagnostics, [ [1, 22, 'error', 'Cannot find name \'Window\'.', /failure-missing-lib\/index.d.ts$/], @@ -102,31 +102,31 @@ test('fail if types are used from a lib that was not explicitly specified', asyn }); test('allow specifying a lib as a triple-slash-reference', async t => { - const diagnostics = await m({cwd: path.join(__dirname, 'fixtures/lib-config/lib-as-triple-slash-reference')}); + const diagnostics = await tsd({cwd: path.join(__dirname, 'fixtures/lib-config/lib-as-triple-slash-reference')}); verify(t, diagnostics, []); }); test('allow specifying a lib in package.json\'s `tsd` field', async t => { - const diagnostics = await m({cwd: path.join(__dirname, 'fixtures/lib-config/lib-from-package-json')}); + const diagnostics = await tsd({cwd: path.join(__dirname, 'fixtures/lib-config/lib-from-package-json')}); verify(t, diagnostics, []); }); test('allow specifying a lib in tsconfig.json', async t => { - const diagnostics = await m({cwd: path.join(__dirname, 'fixtures/lib-config/lib-from-tsconfig-json')}); + const diagnostics = await tsd({cwd: path.join(__dirname, 'fixtures/lib-config/lib-from-tsconfig-json')}); verify(t, diagnostics, []); }); test('a lib option in package.json overrdides a lib option in tsconfig.json', async t => { - const diagnostics = await m({cwd: path.join(__dirname, 'fixtures/lib-config/lib-from-package-json-overrides-tsconfig-json')}); + const diagnostics = await tsd({cwd: path.join(__dirname, 'fixtures/lib-config/lib-from-package-json-overrides-tsconfig-json')}); verify(t, diagnostics, []); }); test('pass in loose mode when strict mode is disabled in settings', async t => { - const diagnostics = await m({ + const diagnostics = await tsd({ cwd: path.join(__dirname, 'fixtures/non-strict-check-with-config') }); @@ -134,49 +134,49 @@ test('pass in loose mode when strict mode is disabled in settings', async t => { }); test('return no diagnostics', async t => { - const diagnostics = await m({cwd: path.join(__dirname, 'fixtures/success')}); + const diagnostics = await tsd({cwd: path.join(__dirname, 'fixtures/success')}); verify(t, diagnostics, []); }); test('support non-barrel main', async t => { - const diagnostics = await m({cwd: path.join(__dirname, 'fixtures/test-non-barrel-main')}); + const diagnostics = await tsd({cwd: path.join(__dirname, 'fixtures/test-non-barrel-main')}); verify(t, diagnostics, []); }); test('support non-barrel main using `types` property', async t => { - const diagnostics = await m({cwd: path.join(__dirname, 'fixtures/test-non-barrel-main-via-types')}); + const diagnostics = await tsd({cwd: path.join(__dirname, 'fixtures/test-non-barrel-main-via-types')}); verify(t, diagnostics, []); }); test('support testing in sub-directories', async t => { - const diagnostics = await m({cwd: path.join(__dirname, 'fixtures/test-in-subdir')}); + const diagnostics = await tsd({cwd: path.join(__dirname, 'fixtures/test-in-subdir')}); verify(t, diagnostics, []); }); test('support top-level await', async t => { - const diagnostics = await m({cwd: path.join(__dirname, 'fixtures/top-level-await')}); + const diagnostics = await tsd({cwd: path.join(__dirname, 'fixtures/top-level-await')}); verify(t, diagnostics, []); }); test('support default test directory', async t => { - const diagnostics = await m({cwd: path.join(__dirname, 'fixtures/test-directory/default')}); + const diagnostics = await tsd({cwd: path.join(__dirname, 'fixtures/test-directory/default')}); verify(t, diagnostics, []); }); test('support tsx in subdirectory', async t => { - const diagnostics = await m({cwd: path.join(__dirname, 'fixtures/test-directory/tsx')}); + const diagnostics = await tsd({cwd: path.join(__dirname, 'fixtures/test-directory/tsx')}); verify(t, diagnostics, []); }); test('support setting a custom test directory', async t => { - const diagnostics = await m({cwd: path.join(__dirname, 'fixtures/test-directory/custom')}); + const diagnostics = await tsd({cwd: path.join(__dirname, 'fixtures/test-directory/custom')}); verify(t, diagnostics, [ [4, 0, 'error', 'Expected an error, but found none.'] @@ -184,7 +184,7 @@ test('support setting a custom test directory', async t => { }); test('expectError for functions', async t => { - const diagnostics = await m({cwd: path.join(__dirname, 'fixtures/expect-error/functions')}); + const diagnostics = await tsd({cwd: path.join(__dirname, 'fixtures/expect-error/functions')}); verify(t, diagnostics, [ [5, 0, 'error', 'Expected an error, but found none.'] @@ -192,7 +192,7 @@ test('expectError for functions', async t => { }); test('expectError should not ignore syntactical errors', async t => { - const diagnostics = await m({cwd: path.join(__dirname, 'fixtures/expect-error/syntax')}); + const diagnostics = await tsd({cwd: path.join(__dirname, 'fixtures/expect-error/syntax')}); verify(t, diagnostics, [ [4, 29, 'error', '\')\' expected.'], @@ -203,7 +203,7 @@ test('expectError should not ignore syntactical errors', async t => { }); test('expectError for values', async t => { - const diagnostics = await m({cwd: path.join(__dirname, 'fixtures/expect-error/values')}); + const diagnostics = await tsd({cwd: path.join(__dirname, 'fixtures/expect-error/values')}); verify(t, diagnostics, [ [5, 0, 'error', 'Expected an error, but found none.'] @@ -211,7 +211,7 @@ test('expectError for values', async t => { }); test('missing import', async t => { - const diagnostics = await m({cwd: path.join(__dirname, 'fixtures/missing-import')}); + const diagnostics = await tsd({cwd: path.join(__dirname, 'fixtures/missing-import')}); verify(t, diagnostics, [ [3, 18, 'error', 'Cannot find name \'Primitive\'.'] @@ -219,13 +219,13 @@ test('missing import', async t => { }); test('tsx', async t => { - const diagnostics = await m({cwd: path.join(__dirname, 'fixtures/tsx')}); + const diagnostics = await tsd({cwd: path.join(__dirname, 'fixtures/tsx')}); verify(t, diagnostics, []); }); test('loose types', async t => { - const diagnostics = await m({cwd: path.join(__dirname, 'fixtures/strict-types/loose')}); + const diagnostics = await tsd({cwd: path.join(__dirname, 'fixtures/strict-types/loose')}); verify(t, diagnostics, [ [5, 0, 'error', 'Parameter type `string` is declared too wide for argument type `"cat"`.'], @@ -242,7 +242,7 @@ test('loose types', async t => { }); test('strict types', async t => { - const diagnostics = await m({cwd: path.join(__dirname, 'fixtures/strict-types/strict')}); + const diagnostics = await tsd({cwd: path.join(__dirname, 'fixtures/strict-types/strict')}); verify(t, diagnostics, []); });