Skip to content

Commit

Permalink
Merge pull request #21 from YanagiEiichi/main
Browse files Browse the repository at this point in the history
Release 1.1.1
  • Loading branch information
YanagiEiichi authored Jul 10, 2024
2 parents a5c409a + c2b853b commit a343aac
Show file tree
Hide file tree
Showing 43 changed files with 472 additions and 210 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"@huolala-tech/custom-error": "^1.0.0",
"@huolala-tech/hooks": "^1.0.0",
"@huolala-tech/nad-builder": "^1.0.4",
"@huolala-tech/request": "^1.1.3",
"@huolala-tech/request": "^1.1.4",
"@rollup/plugin-babel": "^6.0.4",
"@rollup/plugin-json": "^6.1.0",
"@rollup/plugin-node-resolve": "^15.2.3",
Expand Down
4 changes: 0 additions & 4 deletions packages/builder/jest.config.js

This file was deleted.

5 changes: 5 additions & 0 deletions packages/builder/jest.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default {
preset: 'ts-jest',
testRegex: '\\.test\\.ts$',
setupFilesAfterEnv: ['./src/tests/jest.setup.ts'],
};
2 changes: 1 addition & 1 deletion packages/builder/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@huolala-tech/nad-builder",
"version": "1.1.0",
"version": "1.1.1",
"description": "Convert the Java AST to client-side code",
"main": "./dist/cjs/index.js",
"module": "./dist/esm/index.js",
Expand Down
7 changes: 5 additions & 2 deletions packages/builder/src/codegen/CodeGenForTs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export class CodeGenForTs extends CodeGen {
.map((p) => {
// If a parameter is optional and has any required parameters in the right,
// then change its type to `T | null` and make it requreid.
if (hasRequired && p.required === '?') return `${p.name}: ${t2s(p.type)} | null`;
if (hasRequired && p.required === '?') return `${p.name}: ${t2s(p.type)} | undefined`;
// If current parameter is required, update `hasRequired` flag to `true`.
hasRequired = hasRequired || p.required === '';
// Otherwise, the current parameter is requreid, or there are no required parameters to its right,
Expand All @@ -103,6 +103,7 @@ export class CodeGenForTs extends CodeGen {
for (const p of a.parameters) {
if (p.description) this.write(`@param ${p.name} ${p.description}`);
}
if (a.deprecated) this.write('@deprecated');
});
this.write(`async ${a.uniqName}(${pars.join(', ')}) {`);
this.writeBlock(() => {
Expand Down Expand Up @@ -138,6 +139,7 @@ export class CodeGenForTs extends CodeGen {
this.writeComment(() => {
this.write(m.description || m.moduleName);
this.write(`@iface ${m.name}`);
if (m.deprecated) this.write(`@deprecated`);
});
this.write(`export const ${m.moduleName} = {`);
this.writeBlock(() => {
Expand Down Expand Up @@ -203,9 +205,10 @@ export class CodeGenForTs extends CodeGen {
this.write(`export interface ${defStr} {`);
this.writeBlock(() => {
for (const m of c.members) {
if (m.description) {
if (m.description || m.deprecated) {
this.writeComment(() => {
this.write(m.description);
if (m.deprecated) this.write('@deprecated');
});
}
this.write(`${m.name}${m.optional}: ${t2s(m.type)};`);
Expand Down
2 changes: 2 additions & 0 deletions packages/builder/src/helpers/javaHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,14 @@ export const isJavaPrimitiveTypes = [
export const isJavaStringTypes = [
'java.lang.String',
'java.lang.StringBuffer',

'java.time.LocalDateTime',
'java.time.LocalDate',
'java.time.OffsetDateTime',
'java.time.OffsetTime',
'java.time.ZonedDateTime',
'java.time.LocalTime',

'java.lang.Class',
'java.net.URL',
'java.net.URI',
Expand Down
8 changes: 4 additions & 4 deletions packages/builder/src/helpers/tsHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,18 @@ export const t2s = (type: Type): string => {

switch (name) {
case 'java.math.BigDecimal':
builder.commonDefs.BigDecimal = 'string | number';
builder.commonDefs.BigDecimal = '`${number}` | number';
return 'BigDecimal';
case 'java.math.BigInteger':
builder.commonDefs.BigInteger = 'string | number';
builder.commonDefs.BigInteger = '`${number}` | number';
return 'BigInteger';
case 'org.springframework.web.multipart.MultipartFile':
builder.commonDefs.MultipartFile = 'Blob | File | string';
return 'MultipartFile';
default:
}
if (isJavaLong(name)) {
builder.commonDefs.Long = 'string | number';
builder.commonDefs.Long = '`${number}` | number';
return 'Long';
}
if (isJavaNumber(name)) return 'number';
Expand All @@ -67,7 +67,7 @@ export const t2s = (type: Type): string => {
} else {
keyType = 'PropertyKey';
}
return `Record<${keyType}, ${t2s(second)}>`;
return `Record<${keyType}, ${t2s(second)} | undefined>`;
}
if (isJavaList(name)) {
return `${t2s(parameters[0])}[]`;
Expand Down
24 changes: 19 additions & 5 deletions packages/builder/src/models/Member.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { u2a, u2o, u2s } from 'u2x';
import { Annotations } from './annotations';
import type { Class } from './Class';
import { Type } from './Type';
import { Dubious, notEmpty, toLowerCamel } from '../utils';
import { Dubious, notEmpty, toSnake } from '../utils';
import { NadMember } from '../types/nad';

export class Member {
Expand All @@ -13,14 +13,26 @@ export class Member {
public readonly description;
public readonly visible: boolean;
public readonly optional: '' | '?';
public readonly deprecated;
constructor(
raw: Dubious<NadMember>,
public readonly owner: Class,
) {
const { name, type, annotations } = raw;
this.annotations = Annotations.create(u2a(annotations).filter(notEmpty).map(u2o).flat());
this.name = owner.options.fixPropertyName(u2s(this.annotations.json.alias || name) || '');
this.type = Type.create(u2s(type), owner);
this.annotations = Annotations.create(u2a(raw.annotations).filter(notEmpty).map(u2o).flat());

const { fixPropertyName } = owner.options;
if (this.annotations.json.alias) {
this.name = fixPropertyName(this.annotations.json.alias);
} else {
const rawName = u2s(raw.name) ?? '';
if (owner.annotations.json.needToSnake) {
this.name = fixPropertyName(toSnake(rawName));
} else {
this.name = fixPropertyName(rawName);
}
}

this.type = Type.create(u2s(raw.type), owner);
const amp = this.annotations.swagger.getApiModelProperty();
this.description = amp?.value;

Expand All @@ -42,5 +54,7 @@ export class Member {
// It is optinoal by default unless set to @NotNull or @ApiModelProperty(required = true) or JavaPrimitive types.
this.optional =
amp?.required === true || this.annotations.hasNonNull() || isJavaPrimitive(this.type.name) ? '' : '?';

this.deprecated = this.annotations.hasDeprecated();
}
}
2 changes: 2 additions & 0 deletions packages/builder/src/models/Module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export class Module extends Annotated<ModuleRaw> {
public readonly moduleName;
public readonly routes;
public readonly description;
public readonly deprecated;
constructor(raw: ModuleRaw, builder: Root, list: RouteRaw[]) {
super(raw);
this.builder = builder;
Expand Down Expand Up @@ -51,5 +52,6 @@ export class Module extends Annotated<ModuleRaw> {
this.routes = sList.map((o) => new Route(o, this));

this.description = this.annotations.swagger.getApi()?.value || '';
this.deprecated = this.annotations.hasDeprecated();
}
}
16 changes: 9 additions & 7 deletions packages/builder/src/models/Parameter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,15 @@ export class Parameter extends Annotated<Dubious<NadParameter>> {
// 2. This parameter has any "NotNull" annotations (contains @NotNull, @NonNull, and @Nonnull).
// 3. The type of this parameter is a java primitive type.
this.required =
ap?.required ||
rp?.required ||
pv?.required ||
rb?.required ||
rh?.required ||
this.annotations.hasNonNull() ||
isJavaPrimitive(this.type.name)
(ap?.required ||
rp?.required ||
pv?.required ||
rb?.required ||
rh?.required ||
this.annotations.hasNonNull() ||
isJavaPrimitive(this.type.name)) &&
// In fact, a parameter can be optional if a default value is provided.
rp?.defaultValue === undefined
? ('' as const)
: ('?' as const);

Expand Down
2 changes: 2 additions & 0 deletions packages/builder/src/models/Route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export class Route extends Annotated<RouteRaw> {
public readonly customFlags;
public readonly requiredHeaders: [string, string][];
public readonly requiredParams: [string, string][];
public readonly deprecated;

constructor(raw: RouteRaw | undefined, module: Module) {
super(raw);
Expand Down Expand Up @@ -55,6 +56,7 @@ export class Route extends Annotated<RouteRaw> {

this.parameters = u2a(this.raw.parameters, (i) => Parameter.create(i, this)).filter(notEmpty);
this.description = this.annotations.swagger.getApiOperation()?.description || '';
this.deprecated = this.annotations.hasDeprecated();

this.requiredHeaders = [];
this.requiredParams = [];
Expand Down
12 changes: 12 additions & 0 deletions packages/builder/src/models/annotations/JsonAnnotations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ export interface JSONField {
serialize: boolean;
}

export interface JsonNaming {
value: string;
}

export class JsonAnnotations {
private annotations;
constructor(annotations: Annotations) {
Expand All @@ -29,6 +33,10 @@ export class JsonAnnotations {
return this.getJsonIgnore()?.value === true || this.getJSONField()?.serialize === false;
}

public get needToSnake() {
return this.getJsonNaming()?.value === 'com.fasterxml.jackson.databind.PropertyNamingStrategy$SnakeCaseStrategy';
}

private getJsonProperty() {
return this.annotations.find<JsonProperty>('com.fasterxml.jackson.annotation.JsonProperty');
}
Expand All @@ -40,4 +48,8 @@ export class JsonAnnotations {
private getJSONField() {
return this.annotations.find<JSONField>('com.alibaba.fastjson.annotation.JSONField');
}

private getJsonNaming() {
return this.annotations.find<JsonNaming>('com.fasterxml.jackson.databind.annotation.JsonNaming');
}
}
8 changes: 7 additions & 1 deletion packages/builder/src/models/annotations/WebAnnocations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,13 @@ export abstract class ValueAliasName extends AnnotationBase<string> {
* @see https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestParam.html
*/
export class RequestParam extends ValueAliasName {
public static iface = 'org.springframework.web.bind.annotation.RequestParam';
public static readonly iface = 'org.springframework.web.bind.annotation.RequestParam';
public static readonly DEFAULT_VALUE = '\n\t\t\n\t\t\n\uE000\uE001\uE002\n\t\t\t\t\n';
get defaultValue() {
const value = u2s(this.raw.defaultValue);
if (value === RequestParam.DEFAULT_VALUE) return undefined;
return value;
}
get required() {
return u2b(this.raw.required) ?? true;
}
Expand Down
5 changes: 5 additions & 0 deletions packages/builder/src/models/annotations/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ export class Annotations {
return false;
}

hasDeprecated() {
if (this.find<NotNull>('java.lang.Deprecated')) return true;
return false;
}

find<T = Record<string, unknown>>(name: string, endsWith = false): T | null {
if (!endsWith) return (this.map.get(name) as T) || null;
for (const [key, value] of this.map) {
Expand Down
2 changes: 1 addition & 1 deletion packages/builder/src/tests/codegen/all-types.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const typeVector = describe.each([
['java.util.List', 'unknown[]', 'NSArray<NSObject*>*'],
['java.util.List<java.lang.Long>', 'Long[]', 'NSArray<NSNumber*>*'],
['java.util.List<java.lang.Void>', 'void[]', 'NSArray<void*>*'],
['java.util.Map<java.lang.Long, java.lang.Long>', 'Record<Long, Long>', 'NSDictionary*'],
['java.util.Map<java.lang.Long, java.lang.Long>', 'Record<Long, Long | undefined>', 'NSDictionary*'],
['groovy.lang.Tuple2<java.lang.String, java.lang.Long>', '[ string, Long ]', 'NSArray<NSObject*>*'],
['java.util.List<java.lang.ThreadLocal<java.lang.Long>>', 'Optional<Long>[]', 'NSArray<NSNumber*>*' ],
['java.util.List<java.util.Optional<java.lang.Long>>', 'Optional<Long>[]', 'NSArray<NSNumber*>*' ],
Expand Down
11 changes: 5 additions & 6 deletions packages/builder/src/tests/codegen/oc-enum.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { NadEnum, NadEnumConstant, NadRoute } from '../../types/nad';
import { Builder } from '../../Builder';
import { mg } from '../test-tools/mg';
import { DeepPartial } from '../../utils';

const config = { base: 'test', target: 'oc' } as const;
Expand All @@ -21,12 +20,12 @@ const buildEnum = (...constants: Partial<NadEnumConstant>[]) => {
return new Builder({
...config,
defs: { routes: [foo], enums: [MyType] },
}).code.replace(/\s+/g, ' ');
}).code;
};

test('number enum', () => {
const code = buildEnum({ name: 'WATER', value: 1 }, { name: 'FIRE', value: 2 });
expect(code).toContain(mg`
expect(code).toMatchCode(`
typedef NSNumber MyType;
const MyType *MyType_WATER = @1;
const MyType *MyType_FIRE = @2;
Expand All @@ -35,7 +34,7 @@ test('number enum', () => {

test('string enum', () => {
const code = buildEnum({ name: 'WATER', value: 'water' }, { name: 'FIRE', value: 'fire' });
expect(code).toContain(mg`
expect(code).toMatchCode(`
typedef NSString MyType;
const MyType *MyType_WATER = @"water";
const MyType *MyType_FIRE = @"fire";
Expand All @@ -44,7 +43,7 @@ test('string enum', () => {

test('mixed enum', () => {
const code = buildEnum({ name: 'WATER', value: 'water' }, { name: 'FIRE', value: 1 });
expect(code).toContain(mg`
expect(code).toMatchCode(`
typedef NSObject MyType;
const MyType *MyType_WATER = @"WATER";
const MyType *MyType_FIRE = @"FIRE";
Expand All @@ -53,7 +52,7 @@ test('mixed enum', () => {

test('enum string includes spetial characters', () => {
const code = buildEnum({ name: 'WATER', value: 'wa\nter' }, { name: 'FIRE', value: 'fi\\re' });
expect(code).toContain(mg`
expect(code).toMatchCode(`
typedef NSString MyType;
const MyType *MyType_WATER = @"wa\\x0ater";
const MyType *MyType_FIRE = @"fi\\x5cre";
Expand Down
15 changes: 8 additions & 7 deletions packages/builder/src/tests/codegen/oc-swagger.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { Builder } from '../../Builder';
import { mg } from '../test-tools/mg';
import { swaggerTestDefs } from '../defs/swaggerTestDefs';

const code = new Builder({ target: 'oc', base: '', defs: swaggerTestDefs }).code.replace(/\s+/g, ' ');
const code = new Builder({ target: 'oc', base: '', defs: swaggerTestDefs }).code;

test('module', () => {
expect(code).toContain(mg`
expect(code).toMatchCode(`
/**
* My Module
* @JavaClass test.Demo
Expand All @@ -15,7 +14,7 @@ test('module', () => {
});

test('route', () => {
expect(code).toContain(mg`
expect(code).toMatchCode(`
/**
* My Route
* @param a My A
Expand All @@ -27,20 +26,22 @@ test('route', () => {
});

test('class', () => {
expect(code).toContain(mg`
expect(code).toMatchCode(`
/**
* My Model
* @JavaClass test.FooModel
*/
@interface FooModel : NSObject
/** * My Field */
/**
* My Field
*/
@property (nonatomic, assign) FooEnum *type;
@end
`);
});

test('enum', () => {
expect(code).toContain(mg`
expect(code).toMatchCode(`
/**
* My Enum
* @JavaClass test.FooEnum
Expand Down
5 changes: 2 additions & 3 deletions packages/builder/src/tests/codegen/oc-tree.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { NadClass, NadRoute } from '../../types/nad';
import { Builder } from '../../Builder';
import { mg } from '../test-tools/mg';
import { DeepPartial } from '../../utils';

const config = { base: 'test', target: 'oc' } as const;
Expand All @@ -23,9 +22,9 @@ test('Tree', () => {
const code = new Builder({
...config,
defs: { routes: [getTree], classes: [Node] },
}).code.replace(/\s+/g, ' ');
}).code;

expect(code).toContain(mg`
expect(code).toMatchCode(`
@interface Node : NSObject
@property (nonatomic, assign) Node *parent;
@property (nonatomic, assign) NSArray<Node*> *children;
Expand Down
Loading

0 comments on commit a343aac

Please sign in to comment.