Skip to content

Commit

Permalink
feat: improve types strictness with web-buddy v1.14
Browse files Browse the repository at this point in the history
  • Loading branch information
tsofist committed Nov 6, 2024
1 parent 6befc58 commit 6bdd660
Show file tree
Hide file tree
Showing 18 changed files with 62 additions and 35 deletions.
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"tslib": "^2.8.1"
},
"devDependencies": {
"@tsofist/web-buddy": "^1.13.1",
"@tsofist/web-buddy": "^1.14.0",
"@types/jest": "^29.5.14",
"@types/node": "^20.17.6",
"@types/supertest": "^6.0.2",
Expand Down Expand Up @@ -77,7 +77,10 @@
"prettier": "./node_modules/@tsofist/web-buddy/.prettierrc.js",
"eslintConfig": {
"root": true,
"extends": "./node_modules/@tsofist/web-buddy/.eslintrc.yaml"
"extends": [
"./node_modules/@tsofist/web-buddy/.eslintrc.yaml",
"./node_modules/@tsofist/web-buddy/eslint/stricter.extends.yaml"
]
},
"eslintIgnore": [
"lib",
Expand Down
4 changes: 3 additions & 1 deletion src/fake-generator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { SchemaObject } from 'ajv';
import { generateFakeData } from './fake-generator';
import { createSchemaForgeValidator, SchemaForgeValidator } from './validator';

/* eslint-disable @typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-argument */

describe('generateFakeData', () => {
const testSchema1: SchemaObject = {
$schema: 'http://json-schema.org/draft-07/schema#',
Expand Down Expand Up @@ -134,7 +136,7 @@ describe('generateFakeData', () => {

let validator: SchemaForgeValidator;

beforeEach(async () => {
beforeEach(() => {
validator = createSchemaForgeValidator(
{ schemas: [testSchema1, testSchema2, testSchema3] },
true,
Expand Down
4 changes: 3 additions & 1 deletion src/fake-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ function proxyFakerDateModule<T extends object>(obj: T): T {

if (typeof originalValue === 'function') {
return function (this: any, ...args: any[]) {
const result = originalValue.apply(this, args);
const result: unknown = originalValue.apply(this, args);
return result instanceof Date ? result.toISOString() : result;
};
}
Expand All @@ -135,6 +135,7 @@ function fixJSONSchemaFakerQuirks(schema: SchemaObject): SchemaObject {

for (const key in rest) {
if (hasOwnProperty.call(rest, key)) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
rest[key] = fixJSONSchemaFakerQuirks(rest[key]);
}
}
Expand All @@ -146,6 +147,7 @@ function fixJSONSchemaFakerQuirks(schema: SchemaObject): SchemaObject {
const result: SchemaObject = {};
for (const key in schema) {
if (hasOwnProperty.call(schema, key)) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
result[key] = fixJSONSchemaFakerQuirks(schema[key]);
}
}
Expand Down
25 changes: 14 additions & 11 deletions src/generator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ import { createSchemaForgeValidator, SchemaForgeValidator } from './validator';

const KEEP_ARTEFACTS = false;

/* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */

describe('generator for a7', () => {
const outputSchemaFile = './a7.generated.schema.tmp.json';
const outputSchemaMetadataFile = './a7.generated.definitions.tmp.json';
Expand Down Expand Up @@ -45,7 +48,7 @@ describe('generator for a7', () => {
}
});

it('generated schema should be valid', async () => {
it('generated schema should be valid', () => {
expect(forgeSchemaResult).toBeTruthy();
const schema = validator.getSchema('test#/definitions/SomeAPI_doSomeWithUser_Args') as any;
expect(schema).toBeTruthy();
Expand Down Expand Up @@ -91,7 +94,7 @@ describe('generator for a6', () => {
}
});

it('generated schema should be valid', async () => {
it('generated schema should be valid', () => {
expect(forgeSchemaResult).toBeTruthy();
const defs = forgeSchemaResult?.schema?.definitions;
expect(defs).toBeTruthy();
Expand Down Expand Up @@ -162,7 +165,7 @@ describe('generator for a5', () => {
}
});

it('generated schema should be valid', async () => {
it('generated schema should be valid', () => {
expect(forgeSchemaResult).toBeTruthy();
const defs = forgeSchemaResult?.schema?.definitions;
expect(defs).toBeTruthy();
Expand Down Expand Up @@ -275,7 +278,7 @@ describe('generator for a4', () => {
}
});

it('generated schema should be valid', async () => {
it('generated schema should be valid', () => {
expect(forgeSchemaResult).toBeTruthy();
const defs = forgeSchemaResult?.schema?.definitions;
expect(defs).toBeTruthy();
Expand Down Expand Up @@ -306,7 +309,7 @@ describe('generator for a3', () => {
}
});

it('generated schema should be valid', async () => {
it('generated schema should be valid', () => {
expect(forgeSchemaResult).toBeTruthy();
});

Expand Down Expand Up @@ -360,7 +363,7 @@ describe('generator for a2', () => {
}
});

it('generated schema should be valid', async () => {
it('generated schema should be valid', () => {
expect(forgeSchemaResult).toBeTruthy();
});
});
Expand Down Expand Up @@ -397,13 +400,13 @@ describe('generator for a1', () => {
}
});

it('generated schema should be valid', async () => {
it('generated schema should be valid', () => {
expect(forgeSchemaResult).toBeTruthy();
expect(forgeSchemaResult!.schema.$id).toStrictEqual(schemaId);
expect(forgeSchemaResult!.generatedTemporaryFiles.length).toStrictEqual(2);
expect(forgeSchemaResult!.refs.length).toStrictEqual(10);
});
it('getSchema', async () => {
it('getSchema', () => {
expect(validator.getValidator('test#/definitions/Int')!.schema).toStrictEqual({
type: 'integer',
});
Expand All @@ -418,11 +421,11 @@ describe('generator for a1', () => {
type: 'number',
});
});
it('hasSchema', async () => {
it('hasSchema', () => {
expect(validator.hasValidator('test#/definitions/Int')).toStrictEqual(true);
expect(validator.hasValidator('test#/definitions/!Int')).toStrictEqual(false);
});
it('checkBySchema', async () => {
it('checkBySchema', () => {
expect(() =>
validator.checkBySchema(
'test#/definitions/ExportedInterfaceB_InterfaceDeclaration',
Expand Down Expand Up @@ -471,7 +474,7 @@ describe('generator for a1', () => {
}
}
});
it('listDefinitions', async () => {
it('listDefinitions', () => {
const defs: SchemaDefinitionInfo[] = [
{
ref: 'test#/definitions/ExportedInterfaceB_InterfaceDeclaration',
Expand Down
3 changes: 2 additions & 1 deletion src/generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { readFileSync } from 'fs';
import { readFile, writeFile } from 'fs/promises';
import { createHash } from 'node:crypto';
import { unlink } from 'node:fs/promises';
import { URec } from '@tsofist/stem';
import { asArray } from '@tsofist/stem/lib/as-array';
import { raise } from '@tsofist/stem/lib/error';
import { noop } from '@tsofist/stem/lib/noop';
Expand Down Expand Up @@ -111,7 +112,7 @@ export async function forgeSchema(options: SchemaForgeOptions): Promise<SchemaFo
serviceNames: {},
};

const defs = new Set(Object.keys(schema.definitions));
const defs = new Set(Object.keys((schema.definitions || {}) as URec));
for (const name of definitions) {
const ref: SchemaForgeDefinitionRef = `${options.schemaId || ''}#/definitions/${name}`;
map.names[name] = ref;
Expand Down
7 changes: 5 additions & 2 deletions src/generator/schema-generator.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { raise } from '@tsofist/stem/lib/error';
import '../util/patch.extended-annotations-reader';
import { URec } from '@tsofist/stem';
import { raise } from '@tsofist/stem/lib/error';
import Ajv, { SchemaObject } from 'ajv';
import {
AnnotatedType,
Expand Down Expand Up @@ -87,7 +88,9 @@ export async function generateSchemaByDraftTypes(options: Options): Promise<Sche
const schema = generator.createSchema(definitionName);
Object.assign(result.definitions, schema.definitions);
}
result.definitions = Object.fromEntries(Object.entries(result.definitions || {}).sort());
result.definitions = Object.fromEntries(
Object.entries((result.definitions || {}) as URec).sort(),
);

if (options.sortObjectProperties) sortProperties(result.definitions);

Expand Down
3 changes: 2 additions & 1 deletion src/generator/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export const SG_CONFIG_DEFAULTS = {
encodeRefs: false,
markdownDescription: false,
discriminatorType: undefined,
functions: 'hide',
} satisfies Config;

export interface SchemaForgeBaseOptions {
Expand All @@ -38,7 +39,7 @@ export interface SchemaForgeBaseOptions {
* Filter for definitions
* Important: dependencies will not be filtered
*/
definitionsFilter?(name: string): boolean;
definitionsFilter?: (name: string) => boolean;
/**
* Create shared $ref definitions for all types
*
Expand Down
2 changes: 1 addition & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ export type SchemaDefinitionInfo = SDIType | SDIAPIInterface | SDIMethodArgument
export interface SchemaForgeValidationResult {
valid: boolean;
errors: Nullable<SchemaForgeValidationReport>;
errorsText(options?: ErrorsTextOptions): string;
errorsText: (options?: ErrorsTextOptions) => string;
}

export type SchemaForgeValidationFunction<T = unknown> = ValidateFunction<T>;
Expand Down
2 changes: 1 addition & 1 deletion src/util/format.error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ export function formatSchemaForgeError(error: Error, dir = '') {
getNewLine: () => '\n',
});
}
return error.message || error + '';
return error.message || String(error);
}
5 changes: 3 additions & 2 deletions src/util/patch.extended-annotations-reader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,16 @@ import { hasJSDocTag } from './tsc';
{
// Support for @inheritDoc tag to enforce inheritance of annotations

// eslint-disable-next-line @typescript-eslint/unbound-method
const getAnnotations = ExtendedAnnotationsReader.prototype.getAnnotations;

ExtendedAnnotationsReader.prototype.getAnnotations = function getAnnotationsWithInheritance(
node: Node,
): Annotations | undefined {
if (!hasJSDocTag(node, 'inheritDoc')) return getAnnotations.call(this, node);

const checker =
((this as any).typeChecker as TypeChecker) || raise('TypeChecker is not available');
// @ts-expect-error access to private property
const checker = (this.typeChecker as TypeChecker) || raise('TypeChecker is not available');
const result: Annotations = {};

if (
Expand Down
6 changes: 3 additions & 3 deletions src/util/sort-properties.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { compareStringsAsc } from '@tsofist/stem/lib/string/compare';
import { SchemaObject } from 'ajv';

export function sortProperties<T extends SchemaObject>(schema: T): T {
const stack: unknown[] = [];
const stack: T[] = [];

const isTarget = (
target: unknown,
Expand All @@ -20,7 +20,7 @@ export function sortProperties<T extends SchemaObject>(schema: T): T {
);
};

const process = (item: unknown) => {
const process = (item: T | undefined) => {
if (!item) return;

if (Array.isArray(item)) {
Expand All @@ -33,7 +33,7 @@ export function sortProperties<T extends SchemaObject>(schema: T): T {
if (item.required?.length) item.required.sort(compareStringsAsc);
}
if (typeof item === 'object') {
stack.push(...Object.values(item));
stack.push(...(Object.values(item) as T[]));
}
}
};
Expand Down
6 changes: 4 additions & 2 deletions src/util/tsc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,20 @@ import {
getTextOfJSDocComment,
Identifier,
isInterfaceDeclaration,
isJSDocUnknownTag,
isTypeReferenceNode,
JSDocTag,
MethodSignature,
NamedDeclaration,
Node,
PropertySignature,
resolveModuleName,
SignatureDeclarationBase,
SyntaxKind,
} from 'typescript';

export function readMemberTypeName(
member: MethodSignature | PropertySignature,
member: MethodSignature | PropertySignature | SignatureDeclarationBase,
): string | undefined {
const type = member.type;

Expand All @@ -44,7 +46,7 @@ export function readJSDocDescription(

{
const isTag = (tag: JSDocTag): tag is JSDocTag => {
if (tag.kind === SyntaxKind.JSDocTag && tag.tagName.escapedText === 'description') {
if (isJSDocUnknownTag(tag) && tag.tagName.escapedText === 'description') {
value = getTextOfJSDocComment(tag.comment);
}
if (
Expand Down
7 changes: 4 additions & 3 deletions src/validator.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Nullable } from '@tsofist/stem';
import { Nullable, URec } from '@tsofist/stem';
import { raiseEx } from '@tsofist/stem/lib/error';
import { entries } from '@tsofist/stem/lib/object/entries';
import Ajv, { AnySchema, ErrorsTextOptions, Options, Schema, SchemaObject } from 'ajv';
Expand Down Expand Up @@ -131,9 +131,10 @@ export function createSchemaForgeValidator(engineOptions?: Options, useAdditiona
env &&
typeof env.schema === 'object' &&
!schemaId.startsWith('http') &&
env.schema.definitions
env.schema.definitions &&
typeof env.schema.definitions === 'object'
) {
for (const name of Object.keys(env.schema.definitions)) {
for (const name of Object.keys(env.schema.definitions as URec)) {
callback(name, schemaId);
}
}
Expand Down
2 changes: 2 additions & 0 deletions test-sources/a1/service.api.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { PositiveInt } from '@tsofist/stem/lib/number/types';

/* eslint-disable @typescript-eslint/method-signature-style */

/**
* Description for Service
*/
Expand Down
2 changes: 2 additions & 0 deletions test-sources/a2/service-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { NonEmptyString } from '@tsofist/stem';
import { UUID } from '@tsofist/stem/lib/crypto/uuid/types';
import { User, UserID } from './types';

/* eslint-disable @typescript-eslint/method-signature-style */

/**
* By default, if you declare this interface as public,
* it will still get into the schema even if it is not exported from the target module
Expand Down
2 changes: 2 additions & 0 deletions test-sources/a3/service-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { NonEmptyString } from '@tsofist/stem';
import { Int, PositiveInt } from '@tsofist/stem/lib/number/types';
import { StringPhoneNumber } from '@tsofist/stem/lib/phone-number/types';

/* eslint-disable @typescript-eslint/method-signature-style */

type List = readonly StringPhoneNumber[];

/**
Expand Down
2 changes: 2 additions & 0 deletions test-sources/a4/service-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { NonEmptyString } from '@tsofist/stem';
export { FormatMode } from 'ajv-formats/dist/formats';
export { Some2 } from '../a3/service-api';

/* eslint-disable @typescript-eslint/method-signature-style */

/**
* @public
*/
Expand Down

0 comments on commit 6bdd660

Please sign in to comment.