Skip to content

Commit d659edb

Browse files
committed
add support for transforms, add TS type generation from CF JSON schema
1 parent 8478d99 commit d659edb

File tree

18 files changed

+1466
-30
lines changed

18 files changed

+1466
-30
lines changed

.knip.json

Lines changed: 0 additions & 5 deletions
This file was deleted.

@generated/cloudform/AwsLambdaFunction.ts

Lines changed: 485 additions & 0 deletions
Large diffs are not rendered by default.

_test-stacks/monorepo-ts/packages/prisma/prisma.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { PrismaPg } from '@prisma/adapter-pg';
2-
import { PrismaClient } from './generated/client';
2+
import { PrismaClient } from './generated/prisma/client';
33

44
const adapter = new PrismaPg({
55
ssl: { rejectUnauthorized: false },

_test-stacks/simple-lambda/stacktape.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,15 @@ export default defineConfig(() => {
1111
enabled: true
1212
}
1313
},
14-
overrides: {}
14+
transforms: {
15+
lambda: (props) => {
16+
return {
17+
...props,
18+
MemorySize: (props.MemorySize ?? 128) * 2,
19+
Description: 'This is a test lambda',
20+
}
21+
}
22+
}
1523
});
1624

1725
return {

scripts/build-npm-main-export.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
generateStacktapeConfigType
1616
} from './code-generation/generate-augmented-props';
1717
import { generatePropertiesInterfaces } from './code-generation/generate-cf-properties';
18-
import { generateOverrideTypes } from './code-generation/generate-overrides';
18+
import { generateOverrideTypes, generateTransformsTypes } from './code-generation/generate-overrides';
1919
import { generateResourceClassDeclarations } from './code-generation/generate-resource-classes';
2020
import {
2121
generateTypePropertiesClassDeclarations,
@@ -281,6 +281,7 @@ export async function generateTypeDeclarations(): Promise<void> {
281281
logInfo('Extracting Properties interfaces from CloudFormation files...');
282282
const propertiesInterfaces = generatePropertiesInterfaces(CHILD_RESOURCES);
283283
const overridesTypes = generateOverrideTypes(CHILD_RESOURCES);
284+
const transformsTypes = generateTransformsTypes(CHILD_RESOURCES);
284285

285286
// Compile source files
286287
logInfo('Compiling TypeScript source files...');
@@ -354,6 +355,12 @@ ${augmentedPropsTypes}
354355
355356
${overridesTypes}
356357
358+
// ==========================================
359+
// CLOUDFORMATION TRANSFORMS
360+
// ==========================================
361+
362+
${transformsTypes}
363+
357364
// ==========================================
358365
// RESOURCE CLASS DECLARATIONS
359366
// ==========================================

scripts/code-generation/generate-augmented-props.ts

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,16 @@ Allows fine-grained control over the generated infrastructure.`,
6767
tags: []
6868
};
6969

70+
/**
71+
* Default JSDoc for transforms property
72+
*/
73+
const DEFAULT_TRANSFORMS_JSDOC = {
74+
description: `Transform functions for underlying CloudFormation resources.
75+
Each function receives the current properties and returns modified properties.
76+
Unlike overrides, transforms allow dynamic modification based on existing values.`,
77+
tags: []
78+
};
79+
7080
/**
7181
* Generates a property declaration with JSDoc
7282
*/
@@ -123,6 +133,18 @@ function getOverridesPropertyInfo(overridesTypeName: string): PropertyInfo {
123133
};
124134
}
125135

136+
/**
137+
* Gets transforms property info with JSDoc
138+
*/
139+
function getTransformsPropertyInfo(transformsTypeName: string): PropertyInfo {
140+
return {
141+
name: 'transforms',
142+
type: transformsTypeName,
143+
optional: true,
144+
jsdoc: DEFAULT_TRANSFORMS_JSDOC
145+
};
146+
}
147+
126148
/**
127149
* Generates a ConnectTo type alias for a resource
128150
*/
@@ -143,7 +165,7 @@ function generateAugmentedPropsType(
143165
originalPropsType: string,
144166
resourceType: string,
145167
connectToType: string,
146-
includeOverrides: boolean
168+
includeOverridesAndTransforms: boolean
147169
): string {
148170
const lines: string[] = [];
149171

@@ -158,10 +180,13 @@ function generateAugmentedPropsType(
158180
const environmentProperty = getEnvironmentPropertyInfo(originalPropsType);
159181
lines.push(generatePropertyWithJSDoc(environmentProperty));
160182

161-
// Add overrides if needed
162-
if (includeOverrides) {
183+
// Add overrides and transforms if needed
184+
if (includeOverridesAndTransforms) {
163185
const overridesProperty = getOverridesPropertyInfo(`${resourceType}Overrides`);
164186
lines.push(generatePropertyWithJSDoc(overridesProperty));
187+
188+
const transformsProperty = getTransformsPropertyInfo(`${resourceType}Transforms`);
189+
lines.push(generatePropertyWithJSDoc(transformsProperty));
165190
}
166191

167192
lines.push('};');
@@ -171,15 +196,19 @@ function generateAugmentedPropsType(
171196

172197
/**
173198
* Generates a WithOverrides type for resources without augmented props
199+
* Also includes transforms
174200
*/
175-
function generateWithOverridesType(propsType: string, className: string): string {
201+
function generateWithOverridesAndTransformsType(propsType: string, className: string): string {
176202
const lines: string[] = [];
177203

178204
lines.push(`export type ${propsType}WithOverrides = ${propsType} & {`);
179205

180206
const overridesProperty = getOverridesPropertyInfo(`${className}Overrides`);
181207
lines.push(generatePropertyWithJSDoc(overridesProperty));
182208

209+
const transformsProperty = getTransformsPropertyInfo(`${className}Transforms`);
210+
lines.push(generatePropertyWithJSDoc(transformsProperty));
211+
183212
lines.push('};');
184213

185214
return lines.join('\n');
@@ -219,7 +248,7 @@ export function generateAugmentedPropsTypes(): string {
219248
}
220249

221250
result.push('');
222-
result.push('// Augmented props types with connectTo, environment, and overrides');
251+
result.push('// Augmented props types with connectTo, environment, overrides, and transforms');
223252
result.push('');
224253

225254
// Generate augmented props for resources
@@ -229,7 +258,7 @@ export function generateAugmentedPropsTypes(): string {
229258
`Sdk${resource.propsType}`,
230259
resource.className,
231260
`${resource.className}ConnectTo`,
232-
true // includeOverrides
261+
true // includeOverridesAndTransforms
233262
);
234263
result.push(augmentedType);
235264
result.push('');
@@ -242,7 +271,7 @@ export function generateAugmentedPropsTypes(): string {
242271
`Sdk${scriptPropsType}`,
243272
'Script',
244273
'ScriptConnectTo',
245-
false // Scripts don't have overrides
274+
false // Scripts don't have overrides or transforms
246275
);
247276
result.push(augmentedType);
248277
result.push('');
@@ -257,7 +286,7 @@ export function generateAugmentedPropsTypes(): string {
257286
const resourcesWithOverrides = getResourcesWithOverrides();
258287
for (const resource of resourcesWithOverrides) {
259288
if (!augmentedPropsNames.includes(resource.propsType)) {
260-
const withOverridesType = generateWithOverridesType(resource.propsType, resource.className);
289+
const withOverridesType = generateWithOverridesAndTransformsType(resource.propsType, resource.className);
261290
result.push(withOverridesType);
262291
result.push('');
263292
}

scripts/code-generation/generate-overrides.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,35 @@ function generateOverrideProperties(childResources: ChildResourceMetadata[]): st
3232
return properties;
3333
}
3434

35+
/**
36+
* Generates transform property declarations for a resource's child resources
37+
* Transforms are functions that receive props and return modified props
38+
*/
39+
function generateTransformProperties(childResources: ChildResourceMetadata[]): string[] {
40+
const properties: string[] = [];
41+
42+
for (const childResource of childResources) {
43+
// Skip unresolvable resources
44+
if (childResource.unresolvable) {
45+
continue;
46+
}
47+
48+
const mapping = cfTypeToInterface(childResource.resourceType);
49+
if (!mapping) {
50+
continue;
51+
}
52+
53+
const propertyName = getPropertyNameFromLogicalName(childResource.logicalName);
54+
if (!propertyName) {
55+
continue;
56+
}
57+
58+
properties.push(` ${propertyName}?: (props: Partial<${mapping.interface}>) => Partial<${mapping.interface}>;`);
59+
}
60+
61+
return properties;
62+
}
63+
3564
/**
3665
* Generates a single override type declaration
3766
*/
@@ -52,6 +81,26 @@ function generateOverrideType(typeName: string, properties: string[]): string {
5281
return lines.join('\n');
5382
}
5483

84+
/**
85+
* Generates a single transforms type declaration
86+
*/
87+
function generateTransformsType(typeName: string, properties: string[]): string {
88+
if (properties.length === 0) {
89+
return '';
90+
}
91+
92+
const lines = [
93+
`export type ${typeName} = {`,
94+
...properties,
95+
' // Allow any additional transform functions',
96+
' [key: string]: ((props: any) => any) | undefined;',
97+
'};',
98+
''
99+
];
100+
101+
return lines.join('\n');
102+
}
103+
55104
/**
56105
* Generate override types for all child resources
57106
*/
@@ -78,3 +127,29 @@ export function generateOverrideTypes(CHILD_RESOURCES: ChildResourcesMap): strin
78127

79128
return results.join('\n');
80129
}
130+
131+
/**
132+
* Generate transforms types for all child resources
133+
*/
134+
export function generateTransformsTypes(CHILD_RESOURCES: ChildResourcesMap): string {
135+
const results: string[] = [];
136+
const resourcesWithOverrides = getResourcesWithOverrides();
137+
138+
for (const resource of resourcesWithOverrides) {
139+
const childResourcesArray = CHILD_RESOURCES[resource.resourceType];
140+
141+
if (!childResourcesArray || childResourcesArray.length === 0) {
142+
continue;
143+
}
144+
145+
const transformsTypeName = `${resource.className}Transforms`;
146+
const properties = generateTransformProperties(childResourcesArray);
147+
const transformsType = generateTransformsType(transformsTypeName, properties);
148+
149+
if (transformsType) {
150+
results.push(transformsType);
151+
}
152+
}
153+
154+
return results.join('\n');
155+
}

scripts/code-generation/generate-type-properties.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,49 @@
1+
import type { JSDocComment } from './types';
12
import { TYPE_PROPERTIES } from '../../src/api/npm/ts/resource-metadata';
3+
import { getTypePropertiesDescription } from './jsdoc-extractor';
4+
5+
/**
6+
* Formats a JSDoc comment for a constructor (with indentation)
7+
*/
8+
function formatConstructorJSDoc(description: JSDocComment | undefined, className: string): string {
9+
const lines: string[] = [' /**'];
10+
11+
if (description?.description) {
12+
// Split description into lines and add proper indentation
13+
const descriptionLines = description.description.split('\n');
14+
for (const line of descriptionLines) {
15+
lines.push(` * ${line}`);
16+
}
17+
} else {
18+
lines.push(` * Create a ${className}`);
19+
}
20+
21+
// Add tags if any
22+
if (description?.tags) {
23+
for (const tag of description.tags) {
24+
if (tag.value) {
25+
lines.push(` * @${tag.tag} ${tag.value}`);
26+
} else {
27+
lines.push(` * @${tag.tag}`);
28+
}
29+
}
30+
}
31+
32+
lines.push(' */');
33+
return lines.join('\n');
34+
}
235

336
/**
437
* Generate type/properties class declarations
538
*/
639
export function generateTypePropertiesClassDeclarations(): string {
740
return TYPE_PROPERTIES.map(({ className, typeValue, propsType }) => {
41+
const description = getTypePropertiesDescription(className);
42+
const constructorJsDoc = formatConstructorJSDoc(description, className);
43+
844
return `
945
export declare class ${className} extends BaseTypeProperties {
46+
${constructorJsDoc}
1047
constructor(properties: ${propsType});
1148
readonly type: '${typeValue}';
1249
}`;

0 commit comments

Comments
 (0)