Skip to content

Commit

Permalink
fix bug and add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
taefi committed Jan 20, 2025
1 parent e626db1 commit 39026f5
Show file tree
Hide file tree
Showing 7 changed files with 563 additions and 95 deletions.
29 changes: 21 additions & 8 deletions packages/ts/generator-plugin-signals/src/SignalProcessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export default class SignalProcessor {
const initTypeId = imports.named.getIdentifier('@vaadin/hilla-frontend', 'EndpointRequestInit');
let initTypeUsageCount = 0;
const functionParams: Map<string, ts.ParameterDeclaration[]> = new Map<string, ts.ParameterDeclaration[]>();
const modelNameToIdentifierMap: Map<string, ts.Identifier> = new Map<string, ts.Identifier>();

const [file] = ts.transform<SourceFile>(this.#sourceFile, [
transform((tsNode) => {
Expand All @@ -66,7 +67,7 @@ export default class SignalProcessor {
const defaultValueType = SignalProcessor.#getDefaultValueType(genericReturnType);
if (defaultValueType) {
const { entityName, modelName, modelNameUniqueId, createEmptyValueExpression, param } =
SignalProcessor.#createDefaultValueParameter(defaultValueType);
SignalProcessor.#createDefaultValueParameter(defaultValueType, modelNameToIdentifierMap);
initialValue = ts.factory.createBinaryExpression(
ts.factory.createPropertyAccessChain(
ts.factory.createIdentifier('options'),
Expand Down Expand Up @@ -161,7 +162,10 @@ export default class SignalProcessor {
return undefined;
}

static #createDefaultValueParameter(defaultValueType: ts.TypeNode) {
static #createDefaultValueParameter(
defaultValueType: ts.TypeNode,
modelNameToIdentifierMap: Map<string, ts.Identifier>,
) {
const paramType = ts.factory.createTypeLiteralNode([
ts.factory.createPropertySignature(
undefined,
Expand Down Expand Up @@ -195,7 +199,10 @@ export default class SignalProcessor {
};

if (undefinedDefaultValue === undefined) {
emptyValueGeneratorResult = SignalProcessor.#createEmptyValueGeneratorExpression(defaultValueType);
emptyValueGeneratorResult = SignalProcessor.#createEmptyValueGeneratorExpression(
defaultValueType,
modelNameToIdentifierMap,
);
}
// Return the model name that is needed for the imports along with the parameter and empty value creation expression
return {
Expand All @@ -213,7 +220,10 @@ export default class SignalProcessor {
};
}

static #createEmptyValueGeneratorExpression(returnType: ts.TypeNode) {
static #createEmptyValueGeneratorExpression(
returnType: ts.TypeNode,
modelNameToIdentifierMap: Map<string, ts.Identifier>,
) {
let modelName = '';
let entityName: string | undefined;
const typeNodeObject = returnType as ts.UnionTypeNode;
Expand All @@ -227,7 +237,7 @@ export default class SignalProcessor {
case ts.SyntaxKind.BooleanKeyword:
modelName = 'BooleanModel';
break;
case ts.SyntaxKind.ArrayType: // check whether this is supported at all!
case ts.SyntaxKind.ArrayType:
modelName = 'ArrayModel';
break;
default:
Expand All @@ -239,7 +249,10 @@ export default class SignalProcessor {
}
}
}
const modelNameUniqueId = createFullyUniqueIdentifier(modelName);
const modelNameUniqueId = modelNameToIdentifierMap.get(modelName) ?? createFullyUniqueIdentifier(modelName);
if (!modelNameToIdentifierMap.has(modelName)) {
modelNameToIdentifierMap.set(modelName, modelNameUniqueId);
}
const callExpression = ts.factory.createCallExpression(
ts.factory.createPropertyAccessExpression(modelNameUniqueId, 'createEmptyValue'),
undefined,
Expand Down Expand Up @@ -281,11 +294,11 @@ export default class SignalProcessor {
const entityImport = imports.default
.iter()
.map(([path]) => path)
.find((path) => path.startsWith('./') && path.endsWith(`${entityName}.js`));
.find((path) => path.startsWith('./') && path.endsWith(`/${entityName}.js`));
if (entityImport === undefined) {
throw new Error(`Import for Entity '${entityName}' not found`);
}
const entityModelImportPath = entityImport.replace(entityName, modelName);
const entityModelImportPath = entityImport.replace(`/${entityName}.js`, `/${modelName}.js`);
const importedModel = imports.default.paths().find((path) => path === entityModelImportPath);
if (importedModel === undefined) {
imports.default.add(entityModelImportPath, modelName, false, modelNameUniqueId);
Expand Down
14 changes: 14 additions & 0 deletions packages/ts/generator-plugin-signals/test/SignalsEndpoints.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,5 +86,19 @@ describe('SignalsPlugin', () => {
import.meta.url,
);
});

it('correctly generates service with automatic default values for value signals of primitive types', async () => {
const generator = new Generator([BackbonePlugin, SignalsPlugin], {
logger: new LoggerFactory({ name: 'signals-plugin-test', verbose: true }),
});
const input = await readFile(new URL('./hilla-openapi-default-value.json', import.meta.url), 'utf8');
const files = await generator.process(input);

const generatedValueSignalService = files.find((f) => f.name === 'PrimitiveTypeValueSignalService.ts')!;
await expect(await generatedValueSignalService.text()).toMatchSnapshot(
`PrimitiveTypeValueSignalService.snap.ts`,
import.meta.url,
);
});
});
});

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { ArrayModel as ArrayModel_1, BooleanModel as BooleanModel_1, NumberModel as NumberModel_1, StringModel as StringModel_1 } from "@vaadin/hilla-lit-form";
import { ValueSignal as ValueSignal_1 } from "@vaadin/hilla-react-signals";
import client_1 from "./connect-client.default.js";
function anotherStringValueSignal_1(options?: {
defaultValue: string;
}): ValueSignal_1<string> {
return new ValueSignal_1(options?.defaultValue ?? StringModel_1.createEmptyValue(), { client: client_1, endpoint: "PrimitiveTypeValueSignalService", method: "anotherStringValueSignal" });
}
function booleanValueSignal_1(options?: {
defaultValue: boolean;
}): ValueSignal_1<boolean> {
return new ValueSignal_1(options?.defaultValue ?? BooleanModel_1.createEmptyValue(), { client: client_1, endpoint: "PrimitiveTypeValueSignalService", method: "booleanValueSignal" });
}
function booleanValueSignalNullable_1(options?: {
defaultValue: boolean | undefined;
}): ValueSignal_1<boolean | undefined> {
return new ValueSignal_1(options?.defaultValue ?? undefined, { client: client_1, endpoint: "PrimitiveTypeValueSignalService", method: "booleanValueSignalNullable" });
}
function doubleValueSignal_1(options?: {
defaultValue: number;
}): ValueSignal_1<number> {
return new ValueSignal_1(options?.defaultValue ?? NumberModel_1.createEmptyValue(), { client: client_1, endpoint: "PrimitiveTypeValueSignalService", method: "doubleValueSignal" });
}
function doubleValueSignalNullable_1(options?: {
defaultValue: number | undefined;
}): ValueSignal_1<number | undefined> {
return new ValueSignal_1(options?.defaultValue ?? undefined, { client: client_1, endpoint: "PrimitiveTypeValueSignalService", method: "doubleValueSignalNullable" });
}
function stringArrayValueSignal_1(options?: {
defaultValue: Array<string>;
}): ValueSignal_1<Array<string>> {
return new ValueSignal_1(options?.defaultValue ?? ArrayModel_1.createEmptyValue(), { client: client_1, endpoint: "PrimitiveTypeValueSignalService", method: "stringArrayValueSignal" });
}
function stringArrayValueSignalNullable_1(options?: {
defaultValue: Array<string | undefined>;
}): ValueSignal_1<Array<string | undefined>> {
return new ValueSignal_1(options?.defaultValue ?? ArrayModel_1.createEmptyValue(), { client: client_1, endpoint: "PrimitiveTypeValueSignalService", method: "stringArrayValueSignalNullable" });
}
function stringValueSignal_1(options?: {
defaultValue: string;
}): ValueSignal_1<string> {
return new ValueSignal_1(options?.defaultValue ?? StringModel_1.createEmptyValue(), { client: client_1, endpoint: "PrimitiveTypeValueSignalService", method: "stringValueSignal" });
}
function stringValueSignalNullable_1(options?: {
defaultValue: string | undefined;
}): ValueSignal_1<string | undefined> {
return new ValueSignal_1(options?.defaultValue ?? undefined, { client: client_1, endpoint: "PrimitiveTypeValueSignalService", method: "stringValueSignalNullable" });
}
export { anotherStringValueSignal_1 as anotherStringValueSignal, booleanValueSignal_1 as booleanValueSignal, booleanValueSignalNullable_1 as booleanValueSignalNullable, doubleValueSignal_1 as doubleValueSignal, doubleValueSignalNullable_1 as doubleValueSignalNullable, stringArrayValueSignal_1 as stringArrayValueSignal, stringArrayValueSignalNullable_1 as stringArrayValueSignalNullable, stringValueSignal_1 as stringValueSignal, stringValueSignalNullable_1 as stringValueSignalNullable };
Original file line number Diff line number Diff line change
@@ -1,29 +1,36 @@
import { EndpointRequestInit as EndpointRequestInit_1 } from "@vaadin/hilla-frontend";
import { ArrayModel as ArrayModel_1 } from "@vaadin/hilla-lit-form";
import { ListSignal as ListSignal_1, ValueSignal as ValueSignal_1 } from "@vaadin/hilla-react-signals";
import type Person_1 from "./com/github/taefi/data/Person.js";
import PersonModel_1 from "./com/github/taefi/data/PersonModel.js";
import client_1 from "./connect-client.default.js";
async function getPerson_1(init?: EndpointRequestInit_1): Promise<Person_1 | undefined> { return client_1.call("PersonService", "getPerson", {}, init); }
function personArraySignal_1(options?: {
defaultValue: Array<Person_1>;
}): ValueSignal_1<Array<Person_1>> {
return new ValueSignal_1(options?.defaultValue ?? ArrayModel_1.createEmptyValue(), { client: client_1, endpoint: "PersonService", method: "personArraySignal" });
}
function personListSignal_1(): ListSignal_1<Person_1> {
return new ListSignal_1({ client: client_1, endpoint: "PersonService", method: "personListSignal" });
}
function personSignal_1({ defaultValue: defaultValue_1 }: {
function personSignalNotNull_1(options?: {
defaultValue: Person_1;
}): ValueSignal_1<Person_1> {
return new ValueSignal_1(options?.defaultValue ?? PersonModel_1.createEmptyValue(), { client: client_1, endpoint: "PersonService", method: "personSignalNotNull" });
}
function personSignalNullable_1(isAdult: boolean, options?: {
defaultValue: Person_1 | undefined;
}): ValueSignal_1<Person_1 | undefined> {
return new ValueSignal_1(defaultValue_1, { client: client_1, endpoint: "PersonService", method: "personSignal" });
return new ValueSignal_1(options?.defaultValue ?? undefined, { client: client_1, endpoint: "PersonService", method: "personSignalNullable", params: { isAdult } });
}
function personSignalWithParams_1(dummyBoolean: boolean, dummyString: string | undefined, { defaultValue: defaultValue_2 }: {
function personSignalWithParams_1(dummyBoolean: boolean, dummyString: string | undefined, options?: {
defaultValue: Person_1 | undefined;
}): ValueSignal_1<Person_1 | undefined> {
return new ValueSignal_1(defaultValue_2, { client: client_1, endpoint: "PersonService", method: "personSignalWithParams", params: { dummyBoolean, dummyString } });
}
function personSignalNonNull_1({ defaultValue: defaultValue_3 }: {
defaultValue: Person_1;
}): ValueSignal_1<Person_1> {
return new ValueSignal_1(defaultValue_3, { client: client_1, endpoint: "PersonService", method: "personSignalNonNull" });
return new ValueSignal_1(options?.defaultValue ?? undefined, { client: client_1, endpoint: "PersonService", method: "personSignalWithParams", params: { dummyBoolean, dummyString } });
}
function personSignalNonNullWithParams_1(dummyBoolean: boolean, dummyString: string | undefined, { defaultValue: defaultValue_4 }: {
function personSignalNonNullWithParams_1(dummyBoolean: boolean, dummyString: string | undefined, options?: {
defaultValue: Person_1;
}): ValueSignal_1<Person_1> {
return new ValueSignal_1(defaultValue_4, { client: client_1, endpoint: "PersonService", method: "personSignalNonNullWithParams", params: { dummyBoolean, dummyString } });
return new ValueSignal_1(options?.defaultValue ?? PersonModel_1.createEmptyValue(), { client: client_1, endpoint: "PersonService", method: "personSignalNonNullWithParams", params: { dummyBoolean, dummyString } });
}
export { getPerson_1 as getPerson, personListSignal_1 as personListSignal, personSignal_1 as personSignal, personSignalNonNull_1 as personSignalNonNull, personSignalNonNullWithParams_1 as personSignalNonNullWithParams, personSignalWithParams_1 as personSignalWithParams };
export { getPerson_1 as getPerson, personArraySignal_1 as personArraySignal, personListSignal_1 as personListSignal, personSignalNonNullWithParams_1 as personSignalNonNullWithParams, personSignalNotNull_1 as personSignalNotNull, personSignalNullable_1 as personSignalNullable, personSignalWithParams_1 as personSignalWithParams };
Loading

0 comments on commit 39026f5

Please sign in to comment.