Skip to content

Commit

Permalink
Add support for adding computed fields
Browse files Browse the repository at this point in the history
  • Loading branch information
jurgenwerk committed Jan 30, 2025
1 parent debce74 commit e98af4d
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 6 deletions.
1 change: 1 addition & 0 deletions packages/base/command.gts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ export class AddFieldToCardDefinitionInput extends CardDef {
@field outgoingRelativeTo = contains(StringField); // can be undefined when you know url is not going to be relative
@field outgoingRealmURL = contains(StringField); // should be provided when the other 2 params are provided
@field addFieldAtIndex = contains(NumberField); // if provided, the field will be added at the specified index in the card's possibleFields map
@field sourceCodeForComputedField = contains(StringField); // if provided, the field will be added as a computed field
}

export {
Expand Down
3 changes: 2 additions & 1 deletion packages/host/app/commands/add-field-to-card-definition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export default class AddFieldToCardDefinitionCommand extends HostBaseCommand<
cardBeingModified: input.cardDefinitionToModify,
fieldName: input.fieldName,
fieldRef: input.fieldRef,
fieldType: input.fieldDefinitionType as FieldType,
fieldType: input.fieldType as FieldType,
fieldDefinitionType: input.fieldDefinitionType as 'field' | 'card',
incomingRelativeTo: input.incomingRelativeTo
? new URL(input.incomingRelativeTo)
Expand All @@ -50,6 +50,7 @@ export default class AddFieldToCardDefinitionCommand extends HostBaseCommand<
? new URL(input.outgoingRealmURL)
: undefined,
addFieldAtIndex: input.addFieldAtIndex,
sourceCodeForComputedField: input.sourceCodeForComputedField,
});

let writeTextFileCommand = new WriteTextFileCommand(this.commandContext);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,10 @@ module(
contents: {
'person.gts': `
import { contains, field, Component, CardDef } from "https://cardstack.com/base/card-api";
import StringCard from "https://cardstack.com/base/string";
import StringField from "https://cardstack.com/base/string";
export class Person extends CardDef {
static displayName = 'Person';
@field firstName = contains(StringCard);
@field firstName = contains(StringField);
}
`,
},
Expand Down Expand Up @@ -87,15 +87,61 @@ module(
response,
`
import { contains, field, Component, CardDef } from "https://cardstack.com/base/card-api";
import StringCard from "https://cardstack.com/base/string";
import StringField from "https://cardstack.com/base/string";
export class Person extends CardDef {
static displayName = 'Person';
@field firstName = contains(StringCard);
@field lastName = field(StringCard);
@field firstName = contains(StringField);
@field lastName = field(StringField);
}
`,
'lastName field was added to the card definition',
);
});

test('can add a computed field', async function (assert) {
let commandService = lookupService<CommandService>('command-service');
let cardService = lookupService<CardService>('card-service');
let addFieldToCardDefinitionCommand = new AddFieldToCardDefinitionCommand(
commandService.commandContext,
);

await addFieldToCardDefinitionCommand.execute({
cardDefinitionToModify: {
module: 'http://test-realm/test/person',
name: 'Person',
},
fieldName: 'rapName',
fieldDefinitionType: 'field',
fieldType: 'contains',
fieldRef: {
module: 'https://cardstack.com/base/string',
name: 'default',
},
incomingRelativeTo: undefined,
outgoingRelativeTo: undefined,
outgoingRealmURL: undefined,
sourceCodeForComputedField: '`Lil ${this.firstName}`;',
});

let response = await cardService.getSource(
new URL('person.gts', testRealmURL),
);
assert.strictEqual(
response,
`
import { contains, field, Component, CardDef } from "https://cardstack.com/base/card-api";
import StringField from "https://cardstack.com/base/string";
export class Person extends CardDef {
static displayName = 'Person';
@field firstName = contains(StringField);
@field rapName = contains(StringField, {
computeVia: function () {
return \`Lil \${this.firstName}\`;
},
});
}
`,
);
});
},
);
47 changes: 47 additions & 0 deletions packages/realm-server/tests/module-syntax-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -853,6 +853,53 @@ module(basename(__filename), function () {
);
});

test('can add a contains field with a computed value', async function (assert) {
let src = `
import { contains, field, CardDef } from "https://cardstack.com/base/card-api";
import StringField from "https://cardstack.com/base/string";
export class Person extends CardDef {
@field firstName = contains(StringField);
@field lastName = contains(StringField);
}
`;

let mod = new ModuleSyntax(src, new URL(`${testRealm}dir/person`));
mod.addField({
cardBeingModified: { module: `${testRealm}dir/person`, name: 'Person' },
fieldName: 'fullName',
fieldType: 'contains',
fieldDefinitionType: 'field',
fieldRef: {
module: 'https://cardstack.com/base/string',
name: 'default',
},
incomingRelativeTo: undefined,
outgoingRelativeTo: undefined,
outgoingRealmURL: undefined,
sourceCodeForComputedField:
"[this.firstName, this.lastName].filter(Boolean).join(' ');",
});

assert.codeEqual(
mod.code(),
`
import { contains, field, CardDef } from "https://cardstack.com/base/card-api";
import StringField from "https://cardstack.com/base/string";
export class Person extends CardDef {
@field firstName = contains(StringField);
@field lastName = contains(StringField);
@field fullName = contains(StringField, {
computeVia: function () {
return [this.firstName, this.lastName].filter(Boolean).join(' ');
},
});
}
`,
);
});

test('can handle field card declaration collisions when adding field', async function (assert) {
let src = `
import { contains, field, CardDef } from "https://cardstack.com/base/card-api";
Expand Down
15 changes: 15 additions & 0 deletions packages/runtime-common/module-syntax.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ export class ModuleSyntax {
outgoingRelativeTo,
outgoingRealmURL,
addFieldAtIndex,
sourceCodeForComputedField,
}: {
cardBeingModified: CodeRef;
fieldName: string;
Expand All @@ -127,6 +128,7 @@ export class ModuleSyntax {
outgoingRelativeTo: URL | undefined; // can be undefined when you know url is not going to be relative
outgoingRealmURL: URL | undefined; // should be provided when the other 2 params are provided
addFieldAtIndex?: number; // if provided, the field will be added at the specified index in the card's possibleFields map
sourceCodeForComputedField?: string; // if provided, the field will be added as a computed field
}) {
let card = this.getCard(cardBeingModified);
if (card.possibleFields.has(fieldName)) {
Expand All @@ -147,6 +149,7 @@ export class ModuleSyntax {
outgoingRelativeTo,
outgoingRealmURL,
moduleURL: this.url,
sourceCodeForComputedField,
});

let src = this.code();
Expand Down Expand Up @@ -372,6 +375,7 @@ function makeNewField({
outgoingRelativeTo,
outgoingRealmURL,
moduleURL,
sourceCodeForComputedField,
}: {
target: NodePath<t.Node>;
fieldRef: { name: string; module: string };
Expand All @@ -383,6 +387,7 @@ function makeNewField({
outgoingRelativeTo: URL | undefined;
outgoingRealmURL: URL | undefined;
moduleURL: URL;
sourceCodeForComputedField?: string;
}): string {
let programPath = getProgramPath(target);
//@ts-ignore ImportUtil doesn't seem to believe our Babel.types is a
Expand All @@ -395,6 +400,7 @@ function makeNewField({
`${baseRealm.url}card-api`,
'field',
);
debugger;
let fieldTypeIdentifier = importUtil.import(
target as NodePath<any>,
`${baseRealm.url}card-api`,
Expand Down Expand Up @@ -434,6 +440,15 @@ function makeNewField({
suggestedCardName(fieldRef, fieldDefinitionType),
);

if (sourceCodeForComputedField) {
debugger;
return `@${fieldDecorator.name} ${fieldName} = ${fieldTypeIdentifier.name}(${fieldCardIdentifier.name}, {
computeVia: function () {
return ${sourceCodeForComputedField}
},
});`;
}

return `@${fieldDecorator.name} ${fieldName} = ${fieldTypeIdentifier.name}(${fieldCardIdentifier.name});`;
}

Expand Down

0 comments on commit e98af4d

Please sign in to comment.