diff --git a/apps/typesync/client/src/Components/FormComponents/Input.tsx b/apps/typesync/client/src/Components/FormComponents/Input.tsx index ff4c6aa8..40cbb2d7 100644 --- a/apps/typesync/client/src/Components/FormComponents/Input.tsx +++ b/apps/typesync/client/src/Components/FormComponents/Input.tsx @@ -33,7 +33,10 @@ export function FormComponentTextField({ id, label, hint, ...rest }: Readonly field.handleChange(e.target.value)} + onChange={(e) => { + field.handleChange(e.target.value); + rest.onChange?.(e); + }} data-state={hasErrors ? 'invalid' : undefined} aria-invalid={hasErrors ? 'true' : undefined} aria-describedby={hasErrors ? `${id}-invalid` : hint != null ? `${id}-hint` : undefined} diff --git a/apps/typesync/client/src/routes/apps/create.tsx b/apps/typesync/client/src/routes/apps/create.tsx index 4fc75f3e..aca311db 100644 --- a/apps/typesync/client/src/routes/apps/create.tsx +++ b/apps/typesync/client/src/routes/apps/create.tsx @@ -366,23 +366,7 @@ function CreateAppPage() {

Create New App

- + {(field) => ( { + createAppForm.setFieldValue( + 'directory', + `./${pipe(e.target.value, EffectString.toLowerCase, EffectString.replaceAll(/\s/g, '-'))}`, + ); + }} /> )} @@ -808,7 +798,7 @@ function CreateAppPage() {
- + Cancel diff --git a/apps/typesync/src/Generator.ts b/apps/typesync/src/Generator.ts index 17b9e557..eaee75f9 100644 --- a/apps/typesync/src/Generator.ts +++ b/apps/typesync/src/Generator.ts @@ -117,6 +117,7 @@ export class SchemaGenerator extends Effect.Service()('/typesyn ), Effect.andThen(() => updatePackageJson(app, directory)), Effect.andThen(() => fs.writeFileString(path.join(directory, 'src', 'schema.ts'), buildSchemaFile(app))), + Effect.andThen(() => fs.writeFileString(path.join(directory, 'src', 'mapping.ts'), buildMappingFile(app))), Effect.andThen(() => cleanup), ); @@ -290,9 +291,55 @@ ${fieldStrings.join(',\n')} function buildSchemaFile(schema: Domain.InsertAppSchema) { const importStatement = `import { Entity, Type } from '@graphprotocol/hypergraph';`; + const typeDefinitions = schema.types .map(typeDefinitionToString) .filter((def) => def != null) .join('\n\n'); return [importStatement, typeDefinitions].join('\n\n'); } + +export function buildMappingFile(schema: Domain.InsertAppSchema) { + const importStatement1 = `import { Id } from '@graphprotocol/grc-20';`; + const importStatement2 = `import type { Mapping } from '@graphprotocol/hypergraph';`; + + const typeMappings: string[] = []; + + for (const type of schema.types) { + const properties: string[] = []; + const relations: string[] = []; + + // Process properties and relations + for (const property of type.properties) { + if (Domain.isDataTypeRelation(property.dataType)) { + // This is a relation + relations.push(` ${Utils.toCamelCase(property.name)}: Id.Id('${property.knowledgeGraphId}')`); + } else { + // This is a regular property + properties.push(` ${Utils.toCamelCase(property.name)}: Id.Id('${property.knowledgeGraphId}')`); + } + } + + const typeMapping = ` ${type.name}: { + typeIds: [Id.Id('${type.knowledgeGraphId}')], + properties: { +${properties.join(',\n')}, + },${ + relations.length > 0 + ? ` + relations: { +${relations.join(',\n')}, + },` + : '' + } + }`; + + typeMappings.push(typeMapping); + } + + const mappingString = `export const mapping: Mapping = { +${typeMappings.join(',\n')}, +};`; + + return [importStatement1, importStatement2, '', mappingString].join('\n'); +} diff --git a/apps/typesync/test/Generator.test.ts b/apps/typesync/test/Generator.test.ts new file mode 100644 index 00000000..d2bd1295 --- /dev/null +++ b/apps/typesync/test/Generator.test.ts @@ -0,0 +1,79 @@ +import { describe, expect, it } from '@effect/vitest'; + +// @ts-ignore - fix the ts setup +import { buildMappingFile } from '../src/Generator.js'; + +describe('buildMappingFile', () => { + it('should build a valid mapping file', () => { + const expectedMapping = `import { Id } from '@graphprotocol/grc-20'; +import type { Mapping } from '@graphprotocol/hypergraph'; + +export const mapping: Mapping = { + Space: { + typeIds: [Id.Id('362c1dbd-dc64-44bb-a3c4-652f38a642d7')], + properties: { + name: Id.Id('a126ca53-0c8e-48d5-b888-82c734c38935'), + description: Id.Id('9b1f76ff-9711-404c-861e-59dc3fa7d037'), + }, + }, + Activity: { + typeIds: [Id.Id('8275c359-4662-40fb-9aec-27177b520cd2')], + properties: { + name: Id.Id('a126ca53-0c8e-48d5-b888-82c734c38935'), + description: Id.Id('9b1f76ff-9711-404c-861e-59dc3fa7d037'), + }, + relations: { + relatedSpaces: Id.Id('5b722cd3-61d6-494e-8887-1310566437ba'), + }, + }, +};`; + + const mapping = buildMappingFile({ + name: 'test', + description: 'test', + directory: 'test', + template: 'vite_react', + types: [ + { + name: 'Space', + knowledgeGraphId: '362c1dbd-dc64-44bb-a3c4-652f38a642d7', + properties: [ + { + name: 'Name', + knowledgeGraphId: 'a126ca53-0c8e-48d5-b888-82c734c38935', + dataType: 'Text', + }, + { + name: 'Description', + knowledgeGraphId: '9b1f76ff-9711-404c-861e-59dc3fa7d037', + dataType: 'Text', + }, + ], + }, + { + name: 'Activity', + knowledgeGraphId: '8275c359-4662-40fb-9aec-27177b520cd2', + properties: [ + { + name: 'Name', + knowledgeGraphId: 'a126ca53-0c8e-48d5-b888-82c734c38935', + dataType: 'Text', + }, + { + name: 'Description', + knowledgeGraphId: '9b1f76ff-9711-404c-861e-59dc3fa7d037', + dataType: 'Text', + }, + { + name: 'Related spaces', + knowledgeGraphId: '5b722cd3-61d6-494e-8887-1310566437ba', + dataType: 'Relation(Related spaces)', + relationType: 'Related spaces', + }, + ], + }, + ], + }); + expect(mapping).toBe(expectedMapping); + }); +});