Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 21 additions & 16 deletions packages/typegen/proofkit-typegen.config.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -2,56 +2,61 @@
"config": [
{
"type": "fmodata",
"envNames": {
"auth": {
"apiKey": "unused"
}
},
"path": "schema",
"clearOldFiles": false,
"alwaysOverrideFieldNames": true,
"tables": [
{
"tableName": "isolated_contacts",
"reduceMetadata": false,
"reduceMetadata": false
},
{
"tableName": "fmdapi_test",
"fields": [
{
"fieldName": "CreationTimestamp",
"exclude": true,
"exclude": true
},
{
"fieldName": "CreatedBy",
"exclude": true,
"exclude": true
},
{
"fieldName": "ModificationTimestamp",
"exclude": true,
"exclude": true
},
{
"fieldName": "ModifiedBy",
"exclude": true,
"exclude": true
},
{
"fieldName": "anything",
"exclude": true,
"exclude": true
},
{
"fieldName": "fieldWithValues",
"exclude": true,
"exclude": true
},
{
"fieldName": "myContainer",
"exclude": true,
"exclude": true
},
{
"fieldName": "repeatingContainer",
"exclude": true,
"exclude": true
},
{
"fieldName": "booleanField",
"exclude": true,
},
],
},
],
},
],
"exclude": true
}
]
}
]
}
]
}
10 changes: 3 additions & 7 deletions packages/typegen/src/buildLayoutClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,19 +64,15 @@ export function buildLayoutClient(
addTypeGuardStatements(sourceFile, {
envVarName: envNames.server ?? defaultEnvNames.server,
});
if (typeof envNames.auth === "object" && "apiKey" in envNames.auth) {
if (typeof envNames.auth === "object") {
addTypeGuardStatements(sourceFile, {
envVarName: envNames.auth.apiKey ?? defaultEnvNames.apiKey,
});
} else if (
typeof envNames.auth === "object" &&
"username" in envNames.auth
) {
addTypeGuardStatements(sourceFile, {
envVarName: envNames.auth?.username ?? defaultEnvNames.username,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Generated code requires all auth env vars unnecessarily

The type guard generation now adds throw statements for apiKey, username, AND password environment variables whenever envNames.auth is an object. Previously, it only added guards for the specific auth method being used. Now, if a user configures only API key authentication, the generated client will throw errors for missing FM_USERNAME and FM_PASSWORD env vars that aren't actually needed, making the generated code unusable.

Fix in Cursor Fix in Web

envVarName: envNames.auth.username ?? defaultEnvNames.username,
});
addTypeGuardStatements(sourceFile, {
envVarName: envNames.auth?.password ?? defaultEnvNames.password,
envVarName: envNames.auth.password ?? defaultEnvNames.password,
});
}
}
Expand Down
84 changes: 79 additions & 5 deletions packages/typegen/src/fmodata/generateODataTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,43 @@ function mapTypeOverrideToFieldBuilder(
}
}

/**
* Applies import aliases to a field builder expression
* e.g., "textField()" with alias "textField" -> "tf" becomes "tf()"
*/
function applyAliasToFieldBuilder(
fieldBuilder: string,
importAliases: Map<string, string> | undefined,
): string {
if (!importAliases || importAliases.size === 0) {
return fieldBuilder;
}

// Map of field builder function names to their import names
const fieldBuilderMap = new Map([
["textField", "textField"],
["numberField", "numberField"],
["dateField", "dateField"],
["timestampField", "timestampField"],
["containerField", "containerField"],
]);

// Try to find and replace each field builder with its alias
let result = fieldBuilder;
for (const [baseName, importName] of fieldBuilderMap) {
const alias = importAliases.get(baseName);
if (alias) {
// Replace "baseName(" with "alias("
result = result.replace(
new RegExp(`\\b${baseName}\\(`, "g"),
`${alias}(`,
);
}
}

return result;
}

/**
* Maps OData types to field builder functions from @proofkit/fmodata
*/
Expand Down Expand Up @@ -121,6 +158,7 @@ function generateTableOccurrence(
tableOverride?: NonNullable<FmodataConfig["tables"]>[number],
existingFields?: ParsedTableOccurrence,
alwaysOverrideFieldNames?: boolean,
importAliases?: Map<string, string>, // Map base name -> alias (e.g., "textField" -> "tf")
): GeneratedTO {
const fmtId = entityType["@TableID"];
const keyFields = entityType.$Key || [];
Expand Down Expand Up @@ -250,7 +288,7 @@ function generateTableOccurrence(
}

// Apply typeOverride if provided, otherwise use inferred type
const fieldBuilder = mapODataTypeToFieldBuilder(
let fieldBuilder = mapODataTypeToFieldBuilder(
metadata.$Type,
fieldOverride?.typeOverride as
| "text"
Expand All @@ -263,6 +301,9 @@ function generateTableOccurrence(
| undefined,
);

// Apply import aliases if present
fieldBuilder = applyAliasToFieldBuilder(fieldBuilder, importAliases);

// Track which field builders are used
if (fieldBuilder.includes("textField()")) {
usedFieldBuilders.add("textField");
Expand Down Expand Up @@ -433,6 +474,7 @@ interface ParsedTableOccurrence {
fields: Map<string, ParsedField>; // keyed by field name
fieldsByEntityId: Map<string, ParsedField>; // keyed by entity ID
existingImports: string[]; // All existing import statements as strings
importAliases: Map<string, string>; // Map base name -> alias (e.g., "textField" -> "tf")
}

/**
Expand Down Expand Up @@ -725,14 +767,26 @@ function parseExistingTableFile(
}
}

// Extract existing imports
// Extract existing imports and build alias map
const existingImports: string[] = [];
const importAliases = new Map<string, string>(); // base name -> alias
const importDeclarations = sourceFile.getImportDeclarations();
for (const importDecl of importDeclarations) {
const importText = importDecl.getFullText();
if (importText.trim()) {
existingImports.push(importText.trim());
}

// Extract aliases from named imports
const namedImports = importDecl.getNamedImports();
for (const namedImport of namedImports) {
const name = namedImport.getName(); // The original name
const aliasNode = namedImport.getAliasNode();
if (aliasNode) {
const alias = aliasNode.getText(); // The alias
importAliases.set(name, alias);
}
}
}

// Parse each field
Expand Down Expand Up @@ -785,6 +839,7 @@ function parseExistingTableFile(
fields,
fieldsByEntityId,
existingImports,
importAliases,
};
}

Expand Down Expand Up @@ -937,6 +992,7 @@ export async function generateODataTypes(
tableOverride,
undefined,
tableAlwaysOverrideFieldNames,
undefined,
);
generatedTOs.push({
...generated,
Expand Down Expand Up @@ -995,6 +1051,7 @@ export async function generateODataTypes(
generated.tableOverride,
existingFields,
tableAlwaysOverrideFieldNames,
existingFields.importAliases,
)
: generated;

Expand Down Expand Up @@ -1194,8 +1251,15 @@ export async function generateODataTypes(
});
}

// Add missing required imports (without aliases)
importSpecs.push(...missingImports);
// Add missing required imports (apply aliases if they exist)
for (const missingImport of missingImports) {
const alias = existingFields.importAliases.get(missingImport);
if (alias) {
importSpecs.push(`${missingImport} as ${alias}`);
} else {
importSpecs.push(missingImport);
}
}

// Sort imports (but preserve aliases)
importSpecs.sort();
Expand Down Expand Up @@ -1259,7 +1323,17 @@ export async function generateODataTypes(
// Add any required imports that don't exist yet
for (const [module, namedImports] of requiredImportsByModule.entries()) {
if (module && !handledModules.has(module)) {
const importsList = Array.from(namedImports).sort().join(", ");
// Apply aliases to new imports if they exist
const importSpecs: string[] = [];
for (const importName of Array.from(namedImports).sort()) {
const alias = existingFields.importAliases.get(importName);
if (alias) {
importSpecs.push(`${importName} as ${alias}`);
} else {
importSpecs.push(importName);
}
}
const importsList = importSpecs.join(", ");
if (importsList) {
finalImportLines.push(
`import { ${importsList} } from "${module}";`,
Expand Down
Loading
Loading