diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index c33ae00..95c1896 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -153,6 +153,11 @@ jobs: with: persist-credentials: false + - name: Set up node@24 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version: 24 + - name: Set up bun@latest uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 diff --git a/.husky/pre-commit b/.husky/pre-commit index fb1ccc6..af6ba63 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -52,7 +52,7 @@ echo # Check lint printSubHeader "Check lint on staged files..." -bun biome lint --fix --staged --no-errors-on-unmatched +bunx biome lint --fix --staged --no-errors-on-unmatched printPassed "Lint" # Reindex staged files diff --git a/__tests__/__example/example.test.ts b/__tests__/__example/example.test.ts index ffcd33a..5dad846 100644 --- a/__tests__/__example/example.test.ts +++ b/__tests__/__example/example.test.ts @@ -1,7 +1,7 @@ import { join } from 'node:path'; import { TypesTesting } from 'src'; import exampleModule from './example-module'; -import * as ExampleTypes from './example-types'; +import type * as ExampleTypes from './example-types'; if (process.versions.bun) { let bunTest = (await import('bun:test')).default; @@ -38,16 +38,12 @@ describe('example-module', () => { test(`exampleModule.getName type is function without parameter and return 'example-module'`, () => { // using runtime argument expectType(exampleModule.getName).toBe<() => 'example-module'>(); - expectType(exampleModule.getName).toBe<{ - (): 'example-module'; - }>(); + expectType(exampleModule.getName).toBe<() => 'example-module'>(); expectType(exampleModule.getName()).toBe<'example-module'>(); // using type argument expectType().toBe<() => 'example-module'>(); - expectType().toBe<{ - (): 'example-module'; - }>(); + expectType().toBe<() => 'example-module'>(); expectType< ReturnType >().toBe<'example-module'>(); diff --git a/__tests__/__example/jest.config.ts b/__tests__/__example/jest.config.ts index 0b8682d..0612cae 100644 --- a/__tests__/__example/jest.config.ts +++ b/__tests__/__example/jest.config.ts @@ -1,4 +1,4 @@ -import { type JestConfigWithTsJest, createJsWithTsEsmPreset } from 'ts-jest'; +import { createJsWithTsEsmPreset, type JestConfigWithTsJest } from 'ts-jest'; const presetConfig = createJsWithTsEsmPreset({ tsconfig: '__tests__/__example/tsconfig.jest.json' diff --git a/__tests__/__example/tsconfig.jest.json b/__tests__/__example/tsconfig.jest.json index e6ad3ed..0403a7c 100644 --- a/__tests__/__example/tsconfig.jest.json +++ b/__tests__/__example/tsconfig.jest.json @@ -1,11 +1,13 @@ { "extends": "../../tsconfig.json", "compilerOptions": { + "ignoreDeprecations": "6.0", "module": "ESNext", "target": "ESNext", "isolatedModules": true, "esModuleInterop": true, - "outDir": "." + "outDir": ".", + "rootDir": "../../" }, "files": ["example.test.ts"], "include": ["../../src"] diff --git a/__tests__/__example/vitest.config.ts b/__tests__/__example/vitest.config.ts new file mode 100644 index 0000000..1392945 --- /dev/null +++ b/__tests__/__example/vitest.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + resolve: { + tsconfigPaths: true + } +}); diff --git a/__tests__/__integrations/jest.config.ts b/__tests__/__integrations/jest.config.ts index 5b09cdc..f4ae738 100644 --- a/__tests__/__integrations/jest.config.ts +++ b/__tests__/__integrations/jest.config.ts @@ -1,4 +1,4 @@ -import { type JestConfigWithTsJest, createJsWithTsEsmPreset } from 'ts-jest'; +import { createJsWithTsEsmPreset, type JestConfigWithTsJest } from 'ts-jest'; const presetConfig = createJsWithTsEsmPreset({ tsconfig: '__tests__/__integrations/tsconfig.jest.json' diff --git a/__tests__/__integrations/jest.test.ts b/__tests__/__integrations/jest.test.ts index b1d0001..3d78c4f 100644 --- a/__tests__/__integrations/jest.test.ts +++ b/__tests__/__integrations/jest.test.ts @@ -24,10 +24,7 @@ if (process.versions.bun) { meta: Record; }[]; }[]; - } = - await Bun.$`node --experimental-vm-modules node_modules/jest/bin/jest.js jest.test.ts -c=__tests__/__integrations/jest.config.ts --json` - .nothrow() - .json(); + } = await Bun.$`bun test:integrations:jest --json`.nothrow().json(); describe('jest', () => { test('successfully run in jest', () => { diff --git a/__tests__/__integrations/test-case.ts b/__tests__/__integrations/test-case.ts index d0a6188..39b67b9 100644 --- a/__tests__/__integrations/test-case.ts +++ b/__tests__/__integrations/test-case.ts @@ -25,16 +25,13 @@ const prepareThrowMessage = (fnName: string) => { }; }; -export const testCase = ( +export const testCase = ( describe: (label: string, fn: () => void) => void, test: | import('vitest').TestAPI - | import('bun:test').Test + | import('bun:test').Test | import('@jest/types').Global.ItConcurrent, - expect: - | import('vitest').ExpectStatic - | import('bun:test').Expect - | import('@jest/expect').JestExpect + expect: T ) => { describe('toBeAny', () => { const throwMessage = prepareThrowMessage('toBeAny'); diff --git a/__tests__/__integrations/tsconfig.jest.json b/__tests__/__integrations/tsconfig.jest.json index 519db28..6317a5f 100644 --- a/__tests__/__integrations/tsconfig.jest.json +++ b/__tests__/__integrations/tsconfig.jest.json @@ -1,11 +1,13 @@ { "extends": "../../tsconfig.json", "compilerOptions": { + "ignoreDeprecations": "6.0", "module": "ESNext", "target": "ESNext", "isolatedModules": true, "esModuleInterop": true, - "outDir": "." + "outDir": ".", + "rootDir": "../../" }, "files": ["jest.test.ts", "test-case.ts"], "include": ["../../src"] diff --git a/__tests__/__integrations/vitest.config.ts b/__tests__/__integrations/vitest.config.ts new file mode 100644 index 0000000..1392945 --- /dev/null +++ b/__tests__/__integrations/vitest.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + resolve: { + tsconfigPaths: true + } +}); diff --git a/__tests__/__integrations/vitest.test.ts b/__tests__/__integrations/vitest.test.ts index 9070164..4ad7877 100644 --- a/__tests__/__integrations/vitest.test.ts +++ b/__tests__/__integrations/vitest.test.ts @@ -1,6 +1,23 @@ import { afterEach, describe, expect, test, vi } from 'vitest'; import { testCase } from './test-case'; +vi.mock('ansis', async (importOriginal) => { + if (process.versions.bun) { + return {}; + } + + const originalAnsis = await importOriginal(); + return { + ...originalAnsis, + default: { + ...originalAnsis.default, + green: vi.fn((str: string) => str), + red: vi.fn((str: string) => str), + reset: vi.fn((str: string) => str) + } + }; +}); + if (process.versions.bun) { let bunTest = (await import('bun:test')).default; @@ -24,7 +41,7 @@ if (process.versions.bun) { meta: Record; }[]; }[]; - } = await Bun.$`bunx vitest run vitest.test.ts --reporter=json` + } = await Bun.$`bun test:integrations:vitest --reporter json` .nothrow() .json(); @@ -46,24 +63,11 @@ if (process.versions.bun) { } }); } else { - vi.mock('ansis', async (importOriginal) => { - const originalAnsis = await importOriginal(); - return { - ...originalAnsis, - default: { - ...originalAnsis.default, - green: vi.fn((str: string) => str), - red: vi.fn((str: string) => str), - reset: vi.fn((str: string) => str) - } - }; - }); - describe('typesTesting', () => { afterEach(() => { vi.clearAllMocks(); }); - testCase(describe, test, expect); + testCase(describe, test, expect as any); }); } diff --git a/__tests__/lib/compiler/create-error-value.spec.ts b/__tests__/lib/compiler/create-error-value.spec.ts index 5f47ad9..6c2097a 100644 --- a/__tests__/lib/compiler/create-error-value.spec.ts +++ b/__tests__/lib/compiler/create-error-value.spec.ts @@ -11,7 +11,7 @@ import { createErrorValue } from 'lib/compiler/create-error-value'; import ts from 'typescript'; describe('lib > compiler > create-error-value', () => { - let resolvePath: JestMock.Spied; + let resolvePath: jest.Spied; let sourceFile: ts.SourceFile; let caller: ts.Node; diff --git a/__tests__/lib/factories/assertion.spec.ts b/__tests__/lib/factories/assertion.spec.ts index 5d35e76..a494e04 100644 --- a/__tests__/lib/factories/assertion.spec.ts +++ b/__tests__/lib/factories/assertion.spec.ts @@ -1,7 +1,7 @@ import { describe, expect, it, jest } from 'bun:test'; import { - assertionWithTypeArgument, - assertionWithoutTypeArgument + assertionWithoutTypeArgument, + assertionWithTypeArgument } from 'lib/factories/assertion'; describe('lib > factories > assertion', () => { diff --git a/__tests__/src/blueprints/TypesTesting.spec.ts b/__tests__/src/blueprints/TypesTesting.spec.ts index 433b382..88f96a3 100644 --- a/__tests__/src/blueprints/TypesTesting.spec.ts +++ b/__tests__/src/blueprints/TypesTesting.spec.ts @@ -9,18 +9,26 @@ import { } from 'bun:test'; import type { Compiler } from 'lib'; import * as Lib from 'lib'; -import { TypesTesting } from 'src/blueprints/TypesTesting'; import * as __Internal from 'src/blueprints/__internal/TypesTestingError'; +import { TypesTesting } from 'src/blueprints/TypesTesting'; const { TypesTestingError: OriginalTypesTestingError } = __Internal; const { Compiler: OriginalCompiler, Assertion: OriginalAssertion } = Lib; describe('src > blueprints > TypesTesting', () => { + let error: __Internal.TypesTestingError; let compileMock: jest.Mock; let assertionMock: typeof OriginalAssertion; let TypesTestingErrorMock: jest.SpiedClass; beforeEach(() => { + error = { + name: '', + message: '', + stack: '', + errors: new Map() + } as unknown as __Internal.TypesTestingError; + compileMock = jest.fn(() => ({ files: ['test.ts'], options: {}, @@ -31,12 +39,17 @@ describe('src > blueprints > TypesTesting', () => { toBe: jest.fn() } as unknown as typeof assertionMock; - TypesTestingErrorMock = jest.fn().mockImplementation(() => ({ - name: '', - message: '', - stack: '', - errors: new Map() - })); + TypesTestingErrorMock = jest + .fn() + .mockImplementation( + ( + _: Map, + onErrorFound: __Internal.OnErrorFound + ) => { + onErrorFound(''); + return error; + } + ); mock.module('lib', () => ({ Compiler: { @@ -131,29 +144,15 @@ describe('src > blueprints > TypesTesting', () => { ); }); - it('should throw TypesTestingError if afterErrorFound is called', () => { + it('should throw if error.name exists', () => { const test = new TypesTesting({}); test.prepare(); - TypesTestingErrorMock.mockImplementationOnce((_, afterErrorFound) => { - const _this = {} as __Internal.TypesTestingError; - afterErrorFound('', _this); - return _this; - }); + const { toBe } = test.expectType(); + error.name = 'TypesTestingError'; expect(() => test.expectType<'error on expect call'>()).toThrow(); - - TypesTestingErrorMock.mockImplementationOnce( - jest.fn() - ).mockImplementationOnce((_, afterErrorFound) => { - const _this = {} as __Internal.TypesTestingError; - afterErrorFound('', _this); - return _this; - }); - - expect(() => - test.expectType<''>().toBe<'error on assertionCall'>() - ).toThrow(); + expect(() => toBe<'error on assertionCall'>()).toThrow(); }); it('should return assertion methods when no error', () => { diff --git a/__tests__/src/blueprints/__internal/TestingTypesError.spec.ts b/__tests__/src/blueprints/__internal/TypesTestingError.spec.ts similarity index 85% rename from __tests__/src/blueprints/__internal/TestingTypesError.spec.ts rename to __tests__/src/blueprints/__internal/TypesTestingError.spec.ts index 4c71a4b..2175c32 100644 --- a/__tests__/src/blueprints/__internal/TestingTypesError.spec.ts +++ b/__tests__/src/blueprints/__internal/TypesTestingError.spec.ts @@ -11,24 +11,24 @@ import { import * as Ansis from 'ansis'; import type { Compiler } from 'lib'; import { - type AfterErrorFound, + type OnErrorFound, TypesTestingError } from 'src/blueprints/__internal/TypesTestingError'; describe('TypesTestingError', () => { - let afterErrorFound: AfterErrorFound; + let onErrorFound: OnErrorFound; let errorMessage: string; let errors: Compiler.CompileResult['errors']; beforeEach(() => { - afterErrorFound = jest.fn(); + onErrorFound = jest.fn(); errorMessage = 'message'; errors = new Map(); }); describe('instantiation', () => { it('should be instance of Error', () => { - const error = new TypesTestingError(errors, afterErrorFound); + const error = new TypesTestingError(errors, onErrorFound); expect(error).toBeInstanceOf(Error); }); @@ -36,11 +36,11 @@ describe('TypesTestingError', () => { let error: TypesTestingError; beforeEach(() => { - error = new TypesTestingError(errors, afterErrorFound); + error = new TypesTestingError(errors, onErrorFound); }); - it('should not call afterErrorFound fn', () => { - expect(afterErrorFound).toHaveBeenCalledTimes(0); + it('should not call onErrorFound fn', () => { + expect(onErrorFound).toHaveBeenCalledTimes(0); }); it('should return empty name', () => { @@ -89,7 +89,7 @@ describe('TypesTestingError', () => { } as Compiler.CompileResultError; }); - error = new TypesTestingError(errors, afterErrorFound); + error = new TypesTestingError(errors, onErrorFound); }); afterEach(() => { @@ -100,8 +100,8 @@ describe('TypesTestingError', () => { }); }); - it('should call afterErrorFound fn', () => { - expect(afterErrorFound).toHaveBeenCalledTimes(1); + it('should call onErrorFound fn', () => { + expect(onErrorFound).toHaveBeenCalledTimes(1); }); it('should return TypesTestingError name', () => { diff --git a/biome.jsonc b/biome.jsonc index 49d406a..551a5c3 100644 --- a/biome.jsonc +++ b/biome.jsonc @@ -3,9 +3,6 @@ "files": { "ignoreUnknown": true }, - "organizeImports": { - "enabled": true - }, "formatter": { "enabled": true, "attributePosition": "multiline", @@ -18,7 +15,11 @@ }, "linter": { "enabled": true, - "ignore": ["__example", "__integrations"], + "includes": [ + "**", + "!!__tests__/__example/**", + "!!__tests__/__integrations/**" + ], "rules": { "recommended": true } diff --git a/bun.lockb b/bun.lockb index e4bd8d0..7b1e79d 100644 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/lib/compiler/compile.ts b/lib/compiler/compile.ts index 28cc88d..a95a42d 100644 --- a/lib/compiler/compile.ts +++ b/lib/compiler/compile.ts @@ -14,7 +14,7 @@ declare module 'typescript' { } /** @internal */ -export const compile = (options: CompileOptions) => { +export function compile(options: CompileOptions) { const program = createProgram(options); const config = program.__internal; const checker = getTypeChecker(program); @@ -96,7 +96,7 @@ export const compile = (options: CompileOptions) => { } return result; -}; +} /** @internal */ export type CompileResultError = { diff --git a/lib/compiler/create-error-key.ts b/lib/compiler/create-error-key.ts index f21b74e..09674e5 100644 --- a/lib/compiler/create-error-key.ts +++ b/lib/compiler/create-error-key.ts @@ -1,8 +1,4 @@ /** @internal */ -export const createErrorKey = ( - filePath: string, - line: number, - column: number -) => { +export function createErrorKey(filePath: string, line: number, column: number) { return `${filePath}:${line}:${column}`; -}; +} diff --git a/lib/compiler/create-error-value.ts b/lib/compiler/create-error-value.ts index c63aa8a..8984db8 100644 --- a/lib/compiler/create-error-value.ts +++ b/lib/compiler/create-error-value.ts @@ -2,7 +2,7 @@ import ts from 'typescript'; import type { CompileResultError } from './compile'; /** @internal */ -export const createErrorValue = ( +export function createErrorValue( sourceFile: ts.SourceFile, caller: ts.Node | ts.CallExpression | ts.MemberName, expectCallExpressionText?: string, @@ -11,7 +11,7 @@ export const createErrorValue = ( expectedType?: string, isNegated?: boolean, need2TypeArguments?: boolean -): CompileResultError => { +): CompileResultError { const position = sourceFile.getLineAndCharacterOfPosition(caller.getStart()); const filePath = ts.sys.resolvePath(sourceFile.fileName); const line = position.line + 1; @@ -28,4 +28,4 @@ export const createErrorValue = ( isNegated, need2TypeArguments }; -}; +} diff --git a/lib/compiler/create-program.ts b/lib/compiler/create-program.ts index 433f897..2237a4f 100644 --- a/lib/compiler/create-program.ts +++ b/lib/compiler/create-program.ts @@ -12,7 +12,7 @@ const throwIfConfigError = (diagnostics?: readonly ts.Diagnostic[]) => { }; /** @internal */ -export const createProgram = (options: CompileOptions) => { +export function createProgram(options: CompileOptions) { const { basePath, tsConfig, compilerOptions, files, projectReferences } = options; @@ -95,7 +95,7 @@ export const createProgram = (options: CompileOptions) => { 'define basePath and tsConfig to implement configuration from tsconfig file\n' + 'or define compilerOptions and files to implement configuration without tsconfig file.' ); -}; +} /** @internal */ export type Program = ts.Program & { diff --git a/lib/compiler/get-type-checker.ts b/lib/compiler/get-type-checker.ts index f481405..4d55bb8 100644 --- a/lib/compiler/get-type-checker.ts +++ b/lib/compiler/get-type-checker.ts @@ -3,7 +3,7 @@ import { AssertionState } from 'src/definitions/__internal/assertions'; import ts from 'typescript'; /** @internal */ -export const getTypeChecker = (program: ts.Program) => { +export function getTypeChecker(program: ts.Program) { const __cache = new WeakMap(); const checker = program.getTypeChecker() as TypeChecker; @@ -168,7 +168,7 @@ export const getTypeChecker = (program: ts.Program) => { }; return checker; -}; +} /** @internal */ export type TypeChecker = ts.TypeChecker & { diff --git a/lib/compiler/traverse-source-file.ts b/lib/compiler/traverse-source-file.ts index df628f4..1ed92fc 100644 --- a/lib/compiler/traverse-source-file.ts +++ b/lib/compiler/traverse-source-file.ts @@ -1,14 +1,14 @@ import ts from 'typescript'; /** @internal */ -export const traverseSourceFile = ( +export function traverseSourceFile( sourceFile: ts.SourceFile, callback: (node: ts.Node) => void -) => { +) { const traverseNode = (node: ts.Node) => { callback(node); ts.forEachChild(node, traverseNode); }; traverseNode(sourceFile); -}; +} diff --git a/lib/compiler/validate-assertion.ts b/lib/compiler/validate-assertion.ts index 35482a7..2daa7dc 100644 --- a/lib/compiler/validate-assertion.ts +++ b/lib/compiler/validate-assertion.ts @@ -10,10 +10,10 @@ export type ValidateAssertionParams = { }; /** @internal */ -export const validateAssertion = ( +export function validateAssertion( assertionFn: AssertionFn, params: ValidateAssertionParams -) => { +) { const result = assertionFn( params.checker, params.receivedType, @@ -21,4 +21,4 @@ export const validateAssertion = ( ); return params.isNegated ? !result : result; -}; +} diff --git a/lib/factories/assertion.ts b/lib/factories/assertion.ts index 2224454..6d6d711 100644 --- a/lib/factories/assertion.ts +++ b/lib/factories/assertion.ts @@ -1,27 +1,23 @@ import type ts from 'typescript'; /** @internal */ -export const assertionWithTypeArgument = < +export function assertionWithTypeArgument< Fn extends ( checker: ts.TypeChecker, received: ts.Type, expected: ts.Type ) => boolean ->( - fn: Fn -) => { +>(fn: Fn) { const _fn = fn as Fn & { needTypeArgument: true }; _fn.needTypeArgument = true; return _fn; -}; +} /** @internal */ -export const assertionWithoutTypeArgument = < +export function assertionWithoutTypeArgument< Fn extends (checker: ts.TypeChecker, received: ts.Type) => boolean ->( - fn: Fn -) => { +>(fn: Fn) { const _fn = fn as Fn & { needTypeArgument: false }; _fn.needTypeArgument = false; return _fn; -}; +} diff --git a/lib/index.ts b/lib/index.ts index 91a8514..c640987 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -1,3 +1,4 @@ +/** biome-ignore-all assist/source/organizeImports: order matter for these exports*/ export * as Factory from './factories'; -export * as Assertion from './assertions'; export * as Compiler from './compiler'; +export * as Assertion from './assertions'; diff --git a/package.json b/package.json index 92ac9d4..2c2cea4 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,9 @@ "bugs": { "url": "https://github.com/codeismyid/types-testing/issues" }, - "files": ["dist"], + "files": [ + "dist" + ], "publishConfig": { "access": "public", "provenance": true @@ -36,7 +38,8 @@ "toBeNever", "toBeUnknown", "toExtends", - "toAssignable" + "toAssignable", + "toEqual" ], "scripts": { "prepare": "bunx husky || true", @@ -53,45 +56,43 @@ "fix": "bun fix:format && bun fix:lint", "fix:format": "bunx biome check --linter-enabled=false --fix", "fix:lint": "bunx biome lint --fix", - "jest:run-example": "cross-env FAIL_EXAMPLE_TEST=true node --experimental-vm-modules node_modules/jest/bin/jest.js example.test.ts -c=__tests__/__example/jest.config.ts", - "jest:run-integrations": "node --experimental-vm-modules node_modules/jest/bin/jest.js jest.test.ts -c=__tests__/__integrations/jest.config.ts", "reinstall": "bun clean:deps && bun install", "release": "bun ./scripts/release.ts", "test:example:bun": "cross-env FAIL_EXAMPLE_TEST=true bun test example.test.ts", "test:example:jest": "cross-env FAIL_EXAMPLE_TEST=true node --experimental-vm-modules node_modules/jest/bin/jest.js example.test.ts -c=__tests__/__example/jest.config.ts", - "test:example:vitest": "cross-env FAIL_EXAMPLE_TEST=true bunx vitest run example.test.ts", + "test:example:vitest": "cross-env FAIL_EXAMPLE_TEST=true bunx vitest run example.test.ts --config __tests__/__example/vitest.config.ts", "test:integrations:bun": "bun test bun.test.ts", "test:integrations:jest": "node --experimental-vm-modules node_modules/jest/bin/jest.js jest.test.ts -c=__tests__/__integrations/jest.config.ts", - "test:integrations:vitest": "bunx vitest run vitest.test.ts" + "test:integrations:vitest": "bunx vitest run vitest.test.ts --config __tests__/__integrations/vitest.config.ts" }, "dependencies": { - "ansis": "3.17.0" + "ansis": "4.2.0" }, "devDependencies": { - "@biomejs/biome": "1.9.4", - "@commitlint/cli": "19.8.0", - "@commitlint/types": "19.8.0", - "@jest/globals": "29.7.0", - "@types/bun": "1.2.10", - "@types/jest": "29.5.14", - "commitlint-format": "1.0.2", - "conventional-changelog-conventionalcommits": "8.0.0", - "cross-env": "7.0.3", - "esbuild": "0.25.4", + "@biomejs/biome": "2.4.13", + "@commitlint/cli": "20.5.2", + "@commitlint/types": "20.5.0", + "@jest/globals": "30.3.0", + "@types/bun": "1.3.13", + "@types/jest": "30.0.0", + "commitlint-format": "1.1.0", + "conventional-changelog-conventionalcommits": "9.3.1", + "cross-env": "10.1.0", + "esbuild": "0.28.0", "husky": "9.1.7", - "jest": "29.7.0", - "publint": "0.3.12", - "semantic-release": "24.2.3", - "ts-jest": "29.3.2", + "jest": "30.3.0", + "publint": "0.3.18", + "semantic-release": "25.0.3", + "ts-jest": "29.4.9", "ts-node": "10.9.2", - "tsc-alias": "1.8.15", + "tsc-alias": "1.8.16", "tsc-output-format": "1.1.1", "type-coverage": "2.29.7", - "typescript": "5.8.3", - "vitest": "3.1.2" + "typescript": "6.0.3", + "vitest": "4.1.5" }, "peerDependencies": { - "typescript": "5.x" + "typescript": "^5 || ^6" }, "type": "module", "module": "./dist/src/index.js", @@ -120,5 +121,8 @@ "ignoreAsAssertion": true, "ignoreEmptyType": true }, - "trustedDependencies": ["@biomejs/biome", "esbuild"] + "trustedDependencies": [ + "@biomejs/biome", + "esbuild" + ] } diff --git a/scripts/release.ts b/scripts/release.ts index 695ba35..546c245 100644 --- a/scripts/release.ts +++ b/scripts/release.ts @@ -169,53 +169,59 @@ const releaseOptions: SemanticRelease.Options = (() => { return { branches: RELEASE_BRANCHES, repositoryUrl: packageJson.repository.url, + // biome-ignore lint/suspicious/noTemplateCurlyInString: intended tagFormat: 'v${version}', plugins }; })(); -const generateGhaSummary = (nextRelease: SemanticRelease.NextRelease) => { - const content = `## 🚀 Release Report +const generateSummary = (result: SemanticRelease.Result) => { + let content = '## 🚀 Release Report'; + + if (result) { + const { nextRelease } = result; + const repoUrl = (releaseOptions.repositoryUrl as string) + .replace(/^git\+/, '') + .replace(/.git$/, ''); + + content += ` - Type: ${nextRelease.type} - Version: ${nextRelease.version} - Tag: ${nextRelease.gitTag} -See this release at this [link](${releaseOptions.repositoryUrl}/releases/tag/${nextRelease.gitTag}). +See this release at this [link](${repoUrl}/releases/tag/${nextRelease.gitTag}). ## 📝 Generated Notes ${nextRelease.notes}`; + } else { + content += ` +- Type: N/A +- Version: N/A +- Tag: N/A + +No release published.`; + } return content; }; const runRelease = async () => { try { - const releasing = SemanticRelease.default; - const result = await releasing(releaseOptions); + const release = SemanticRelease.default; + const result = await release(releaseOptions); console.info('--------------------------------------------------\n'); - if (!result) { - console.info('No release published.'); - return; - } + const summary = generateSummary(result); - const { nextRelease } = result; + console.info(summary); if (isGHA) { - const summary = generateGhaSummary(nextRelease); - - console.info('Generating github step summary...'); await Bun.$`printf "%s" "${summary}" >> $GITHUB_STEP_SUMMARY`; - console.info('> $GITHUB_STEP_SUMMARY'); - console.info(); - console.info('--------------------------------------------------\n'); } - console.info(`${ansis.bold('Release Report')}\n`); - console.info(`Type: ${nextRelease.type}`); - console.info(`Version: ${nextRelease.version}`); - console.info(`Tag: ${nextRelease.gitTag}`); + console.info(); + console.info('--------------------------------------------------\n'); } catch (err) { if (err instanceof Error) { console.error(`${err.name}:`, `${ansis.white(err.message)}`); diff --git a/src/blueprints/TypesTesting.ts b/src/blueprints/TypesTesting.ts index 7f3ed2f..42fe95a 100644 --- a/src/blueprints/TypesTesting.ts +++ b/src/blueprints/TypesTesting.ts @@ -33,19 +33,20 @@ export class TypesTesting { this.#assertions = new Proxy(Assertion, { get: () => { - const assertionFn = () => { + return () => { if (!this.#compileResult) { return; } const errors = this.#compileResult.errors; - new TypesTestingError(errors, (key, TypesTestingError) => { - errors.delete(key); - throw TypesTestingError; + const err = new TypesTestingError(errors, (errorKey) => { + errors.delete(errorKey); }); - }; - return assertionFn; + if (err.name) { + throw err; + } + }; } }) as unknown as Assertions; } @@ -112,6 +113,7 @@ export class TypesTesting { */ expectType( // @ts-expect-error: intended (only the value type will be used) + // biome-ignore lint/correctness/noUnusedFunctionParameters: intended (only the value type will be used) received?: Received ) { const notPrepared = !this.isPrepared; @@ -127,11 +129,14 @@ export class TypesTesting { } const errors = (this.#compileResult as Compiler.CompileResult).errors; - new TypesTestingError(errors, (key, TypesTestingError) => { + const err = new TypesTestingError(errors, (key) => { errors.delete(key); - throw TypesTestingError; }); + if (err.name) { + throw err; + } + return { ...this.#assertions, not: { diff --git a/src/blueprints/__internal/TypesTestingError.ts b/src/blueprints/__internal/TypesTestingError.ts index 3435b60..4d2d534 100644 --- a/src/blueprints/__internal/TypesTestingError.ts +++ b/src/blueprints/__internal/TypesTestingError.ts @@ -11,15 +11,15 @@ export class TypesTestingError extends Error { constructor( errors: Compiler.CompileResult['errors'], - afterErrorFound: AfterErrorFound + onErrorFound: OnErrorFound ) { super(); - this.#findError(errors, afterErrorFound); + this.#findError(errors, onErrorFound); } #findError = ( errors: Compiler.CompileResult['errors'], - afterErrorFound: AfterErrorFound + onErrorFound: OnErrorFound ) => { const trace = {} as { originalLine: number; @@ -40,7 +40,7 @@ export class TypesTestingError extends Error { this.name = 'TypesTestingError'; this.#prepareMessage(errorFound); this.#prepareStack(errorFound); - afterErrorFound(key, this); + onErrorFound(key); return; } } @@ -114,10 +114,11 @@ export class TypesTestingError extends Error { const prev = stacks[index - 1]; if (prev) { - return ( - stack.getFileName() === errorFound.filePath || - stack.getScriptNameOrSourceURL() === errorFound.filePath - ); + const path = stack.getFileName() ?? stack.getScriptNameOrSourceURL(); + + if (path) { + return ts.sys.resolvePath(path) === errorFound.filePath; + } } return false; @@ -138,7 +139,4 @@ export class TypesTestingError extends Error { } /** @internal */ -export type AfterErrorFound = ( - errorKey: string, - TypesTestingError: TypesTestingError -) => void; +export type OnErrorFound = (errorKey: string) => void; diff --git a/src/definitions/__internal/assertions.ts b/src/definitions/__internal/assertions.ts index 0e5c11f..b172a44 100644 --- a/src/definitions/__internal/assertions.ts +++ b/src/definitions/__internal/assertions.ts @@ -55,18 +55,15 @@ type Asserts = { }; }; -type AssertionFn< - AssertionName, - Negated, - Received = NotProvided -> = AssertionName extends Exclude, 'not'> - ? ( - AssertionName extends AssertionNeedTypeArgument - ? ( - expected?: Expected - ) => Asserts - : () => Asserts - ) extends infer Fn extends CallableFunction - ? Fn - : never - : never; +type AssertionFn = + AssertionName extends Exclude, 'not'> + ? ( + AssertionName extends AssertionNeedTypeArgument + ? ( + expected?: Expected + ) => Asserts + : () => Asserts + ) extends infer Fn extends CallableFunction + ? Fn + : never + : never; diff --git a/src/index.ts b/src/index.ts index aaa621a..916c0bb 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,4 @@ -export type * from './definitions'; export * from './blueprints'; +export type * from './definitions'; export * from './instances'; export { default } from './instances'; diff --git a/tsconfig.json b/tsconfig.json index 40aa55c..3865edd 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -35,9 +35,14 @@ "lib": ["./lib"], "lib/*": ["./lib/*"], "tsconfig.json": ["./tsconfig.json"], - "package.json": ["./package.json"] + "package.json": ["./package.json"], + "dist": ["./dist"], + "dist/*": ["./dist/*"] }, + // TypeScript V6 + "types": ["bun", "jest"], + // Others "noEmitOnError": true, "noErrorTruncation": true,