Skip to content

Commit

Permalink
fix: api detection when bundled
Browse files Browse the repository at this point in the history
  • Loading branch information
skarab42 committed Jul 13, 2022
1 parent 14e266c commit 0b5af22
Show file tree
Hide file tree
Showing 8 changed files with 226 additions and 72 deletions.
24 changes: 24 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,30 @@
"main": "./build/index.js",
"module": "./build/index.mjs",
"types": "./build/index.d.ts",
"exports": {
".": {
"require": "./build/index.js",
"import": "./build/index.mjs"
},
"./tsd": {
"require": "./build/api/tsd/index.js",
"import": "./build/api/tsd/index.mjs"
},
"./tssert": {
"require": "./build/api/tssert/index.js",
"import": "./build/api/tssert/index.mjs"
}
},
"typesVersions": {
"*": {
"tsd": [
"./build/api/tsd/index.d.ts"
],
"tssert": [
"./build/api/tssert/index.d.ts"
]
}
},
"files": [
"build"
],
Expand Down
101 changes: 80 additions & 21 deletions src/api/tsd/index.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,83 @@
/* eslint-disable @typescript-eslint/explicit-function-return-type */
/* eslint-disable @typescript-eslint/no-empty-function */
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-unused-vars */
import { API_PROPERTY_KEY } from '../../common/internal';

// TODO: add docblock !!!
export function expectType<ExpectedType>(_value: ExpectedType) {}
export function expectNotType<ExpectedType>(_value: any) {}
export function expectAssignable<ExpectedType>(_value: ExpectedType) {}
export function expectNotAssignable<ExpectedType>(_value: any) {}
export function expectError<ExpectedType = any>(_value: ExpectedType) {}
export function expectDeprecated(_expression: any) {}
export function expectNotDeprecated(_expression: any) {}
export function printType(_expression: any) {}

expectType[API_PROPERTY_KEY] = 'tsd:expectType' as const;
expectNotType[API_PROPERTY_KEY] = 'tsd:expectNotType' as const;
expectAssignable[API_PROPERTY_KEY] = 'tsd:expectAssignable' as const;
expectNotAssignable[API_PROPERTY_KEY] = 'tsd:expectNotAssignable' as const;
expectError[API_PROPERTY_KEY] = 'tsd:expectError' as const;
expectDeprecated[API_PROPERTY_KEY] = 'tsd:expectDeprecated' as const;
expectNotDeprecated[API_PROPERTY_KEY] = 'tsd:expectNotDeprecated' as const;
printType[API_PROPERTY_KEY] = 'tsd:printType' as const;

/**
* Test that ExpectedType is equal to value type.
*
* @apiName tsd
* @functionName expectType
*/
export function expectType<ExpectedType>(_value: ExpectedType): undefined {
return;
}

/**
* Test that ExpectedType is not equal to value type.
*
* @apiName tsd
* @functionName expectNotType
*/
export function expectNotType<ExpectedType>(_value: ExpectedType): undefined {
return;
}

/**
* Test that ExpectedType is assignable to value type.
*
* @apiName tsd
* @functionName expectAssignable
*/
export function expectAssignable<ExpectedType>(_value: ExpectedType): undefined {
return;
}

/**
* Test that ExpectedType is not assignable to value type.
*
* @apiName tsd
* @functionName expectNotAssignable
*/
export function expectNotAssignable<ExpectedType>(_value: ExpectedType): undefined {
return;
}

/**
* Test that ExpectedType throw an type error.
*
* @apiName tsd
* @functionName expectError
*/
export function expectError<ExpectedType>(_value: ExpectedType): undefined {
return;
}

/**
* Test that ExpectedType is marked as deprecated.
*
* @apiName tsd
* @functionName expectDeprecated
*/
export function expectDeprecated(_expression: any): undefined {
return;
}

/**
* Test that ExpectedType is not marked as deprecated.
*
* @apiName tsd
* @functionName expectNotDeprecated
*/
export function expectNotDeprecated(_expression: any): undefined {
return;
}

/**
* Print the type in a diagnostic.
*
* @apiName tsd
* @functionName printType
*/
export function printType(_expression: any): undefined {
return;
}
126 changes: 98 additions & 28 deletions src/api/tssert/index.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1,106 @@
/* eslint-disable @typescript-eslint/explicit-function-return-type */
/* eslint-disable @typescript-eslint/no-empty-function */
/* eslint-disable @typescript-eslint/no-unused-vars */
import { API_PROPERTY_KEY } from '../../common/internal';

// TODO: add docblock !!!
function assignableTo<TargetType>(_target?: unknown) {}
function notAssignableTo<TargetType>(_target?: unknown) {}

function identicalTo<TargetType>(_target?: unknown) {}
function notIdenticalTo<TargetType>(_target?: unknown) {}

function subtypeOf<TargetType>(_target?: unknown) {}
function notSubtypeOf<TargetType>(_target?: unknown) {}

function equalTo<TargetType>(_target?: unknown) {}
/**
* Test that TargetType is assignable to SourceType.
*
* @apiName tssert
* @functionName assignableTo
*/
function assignableTo<TargetType>(_target?: TargetType): undefined {
return;
}

function toBeDeprecated<TargetType>(_target?: unknown) {}
function notToBeDeprecated<TargetType>(_target?: unknown) {}
/**
* Test that TargetType is not assignable to SourceType.
*
* @apiName tssert
* @functionName notAssignableTo
*/
function notAssignableTo<TargetType>(_target?: TargetType): undefined {
return;
}

function toThrowError<TargetType = undefined>(_target: unknown = undefined) {}
/**
* Test that TargetType is identical to SourceType.
*
* @apiName tssert
* @functionName identicalTo
*/
function identicalTo<TargetType>(_target?: TargetType): undefined {
return;
}

expectType[API_PROPERTY_KEY] = 'tssert:expectType' as const;
/**
* Test that TargetType is not identical to SourceType.
*
* @apiName tssert
* @functionName notIdenticalTo
*/
function notIdenticalTo<TargetType>(_target?: TargetType): undefined {
return;
}

assignableTo[API_PROPERTY_KEY] = 'tssert:assignableTo' as const;
notAssignableTo[API_PROPERTY_KEY] = 'tssert:notAssignableTo' as const;
/**
* Test that TargetType is subtype of SourceType.
*
* @apiName tssert
* @functionName subtypeOf
*/
function subtypeOf<TargetType>(_target?: TargetType): undefined {
return;
}

identicalTo[API_PROPERTY_KEY] = 'tssert:identicalTo' as const;
notIdenticalTo[API_PROPERTY_KEY] = 'tssert:notIdenticalTo' as const;
/**
* Test that TargetType is not subtype of to SourceType.
*
* @apiName tssert
* @functionName notSubtypeOf
*/
function notSubtypeOf<TargetType>(_target?: TargetType): undefined {
return;
}

subtypeOf[API_PROPERTY_KEY] = 'tssert:subtypeOf' as const;
notSubtypeOf[API_PROPERTY_KEY] = 'tssert:notSubtypeOf' as const;
/**
* Test that TargetType is equal to SourceType.
*
* @apiName tssert
* @functionName equalTo
*/
function equalTo<TargetType>(_target?: TargetType): undefined {
return;
}

equalTo[API_PROPERTY_KEY] = 'tssert:equalTo' as const;
/**
* Test that SourceType is marked as deprecated.
*
* @apiName tssert
* @functionName toBeDeprecated
*/
function toBeDeprecated(): undefined {
return;
}

toBeDeprecated[API_PROPERTY_KEY] = 'tssert:toBeDeprecated' as const;
notToBeDeprecated[API_PROPERTY_KEY] = 'tssert:notToBeDeprecated' as const;
/**
* Test that SourceType is not marked as deprecated.
*
* @apiName tssert
* @functionName notToBeDeprecated
*/
function notToBeDeprecated(): undefined {
return;
}

toThrowError[API_PROPERTY_KEY] = 'tssert:toThrowError' as const;
/**
* Test that TargetType throw an type error.
*
* @apiName tssert
* @functionName toThrowError
*/
function toThrowError<TargetType>(_target?: TargetType): undefined {
return;
}

const api = {
assignableTo,
Expand All @@ -54,7 +118,13 @@ const api = {
},
} as const;

// TODO: add docblock !!!
export function expectType<SourceType>(_source?: unknown) {
/**
* SourceType holder to test with.
*
* @apiName tssert
* @functionName expectType
*/

export function expectType<SourceType>(_source?: SourceType): typeof api {
return api;
}
1 change: 0 additions & 1 deletion src/common/internal.ts

This file was deleted.

12 changes: 8 additions & 4 deletions src/plugin/assert/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,22 +73,26 @@ export function expressionToString(typeChecker: ts.TypeChecker, node: ts.Node):
return node.getText();
}

export function hasDeprecatedTag(argument: ts.Node, typeChecker: ts.TypeChecker): boolean {
export function getTag(tagName: string, argument: ts.Node, typeChecker: ts.TypeChecker): ts.JSDocTagInfo | undefined {
const signatureOrSymbol = ts.isCallLikeExpression(argument)
? typeChecker.getResolvedSignature(argument)
: typeChecker.getSymbolAtLocation(argument);

if (!signatureOrSymbol) {
return false;
return;
}

const tags = signatureOrSymbol.getJsDocTags();

if (!tags.length) {
return false;
return;
}

return !!tags.find((tag) => tag.name === 'deprecated');
return tags.find((tag) => tag.name === tagName);
}

export function hasDeprecatedTag(argument: ts.Node, typeChecker: ts.TypeChecker): boolean {
return !!getTag('deprecated', argument, typeChecker);
}

export function isArgumentInDiagnostic(argument: ts.Node, diagnostic: ts.Diagnostic): boolean {
Expand Down
26 changes: 12 additions & 14 deletions src/plugin/transform.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import ts from 'unleashed-typescript';
import { assert } from './assert';
import ts from 'unleashed-typescript';
import { getTag } from './assert/util';
import MagicString from 'magic-string';
import type { TransformResult } from 'vite';
import { reportDiagnostics } from './diagnostics';
import type { Compiler } from '../typescript/types';
import { API_PROPERTY_KEY } from '../common/internal';
import { createCompiler } from '../typescript/compiler';
import { createError, ErrorCode } from '../common/error';
import type { APIName, Assertion, TransformSettings } from './types';
Expand Down Expand Up @@ -34,24 +34,22 @@ export function transform({ code, fileName, report, typescript }: TransformSetti

export function getAssertion(node: ts.Node, typeChecker: ts.TypeChecker): Assertion | undefined {
if (!ts.isCallExpression(node)) {
return undefined;
return;
}

const expression = node.expression;
const expressionType = typeChecker.getTypeAtLocation(expression);
const assertionProperty = expressionType.getProperty(API_PROPERTY_KEY);
const apiName = getTag('apiName', node, typeChecker);

if (assertionProperty) {
const assertionPropertyType = typeChecker.getTypeOfSymbolAtLocation(assertionProperty, expression);
const assertionPropertyValue = typeChecker.typeToString(assertionPropertyType).slice(1, -1);
const [apiName, functionName] = assertionPropertyValue.split(':');
if (!apiName || !apiName.text?.[0]) {
return;
}

if (apiName && functionName) {
return { apiName: apiName as APIName, functionName, node };
}
const functionName = getTag('functionName', node, typeChecker);

if (!functionName || !functionName.text?.[0]) {
return;
}

return undefined;
return { apiName: apiName.text[0].text as APIName, functionName: functionName.text[0].text, node };
}

function getAssertions(sourceFile: ts.SourceFile, typeChecker: ts.TypeChecker): Assertion[] {
Expand Down
2 changes: 1 addition & 1 deletion test/tssert.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ test('test-4', () => {
expectType(42 === 'life').toThrowError('Prout');
});

test.only('test-4', () => {
test('test-4', () => {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
expectType(42 === 'life').toThrowError(42);
});
Expand Down
6 changes: 3 additions & 3 deletions tsup.config.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { defineConfig } from 'tsup';

export default defineConfig({
entry: ['src/index.ts'],
entry: ['src/index.ts', 'src/api/tsd/index.ts', 'src/api/tssert/index.ts'],
format: ['cjs', 'esm'],
outDir: 'build',
platform: 'node',
splitting: true,
splitting: false,
sourcemap: true,
minify: true,
minify: false,
clean: true,
dts: true,
});

0 comments on commit 0b5af22

Please sign in to comment.