Skip to content
This repository was archived by the owner on Jan 19, 2026. It is now read-only.

Commit 4fba898

Browse files
committed
refactor(types): correctly handle camelCase
1 parent 6f4a48b commit 4fba898

3 files changed

Lines changed: 42 additions & 9 deletions

File tree

src/commands/types/generate/actions.test.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { generateStoryblokTypes, generateTypes, getComponentType } from './actions';
1+
import { generateStoryblokTypes, generateTypes, getComponentType, getStoryType } from './actions';
22
import type { SpaceComponent, SpaceData } from '../../../commands/components/constants';
33
import type { GenerateTypesOptions } from './constants';
44
import { join, resolve } from 'node:path';
@@ -298,6 +298,33 @@ describe('getComponentType', () => {
298298
});
299299
});
300300

301+
describe('getStoryType', () => {
302+
it('should convert property names to the correct format', () => {
303+
// Test cases for different property name formats
304+
expect(getStoryType('my_property')).toBe('ISbStoryData<MyProperty>');
305+
expect(getStoryType('my-property')).toBe('ISbStoryData<MyProperty>');
306+
expect(getStoryType('myProperty')).toBe('ISbStoryData<MyProperty>');
307+
expect(getStoryType('MyProperty')).toBe('ISbStoryData<MyProperty>');
308+
expect(getStoryType('my property')).toBe('ISbStoryData<MyProperty>');
309+
expect(getStoryType('my_property_name')).toBe('ISbStoryData<MyPropertyName>');
310+
});
311+
312+
it('should handle special characters and numbers', () => {
313+
expect(getStoryType('my_property_123')).toBe('ISbStoryData<MyProperty123>');
314+
expect(getStoryType('my-property!')).toBe('ISbStoryData<MyProperty>');
315+
expect(getStoryType('my_property@name')).toBe('ISbStoryData<MyPropertyName>');
316+
});
317+
318+
it('should handle empty or single character properties', () => {
319+
expect(getStoryType('')).toBe('ISbStoryData<>');
320+
expect(getStoryType('a')).toBe('ISbStoryData<A>');
321+
});
322+
323+
it('should handle prefix', () => {
324+
expect(getStoryType('my_property', 'Custom')).toBe('ISbStoryData<CustomMyProperty>');
325+
});
326+
});
327+
301328
describe('component property type annotations', () => {
302329
it('should handle text property type', async () => {
303330
// Create a component with text property type

src/commands/types/generate/actions.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const DEFAULT_TYPEDEFS_HEADER = [
2121
'// DO NOT MODIFY THIS FILE BY HAND.',
2222
];
2323

24-
const getPropertyTypeAnnotation = (property: ComponentPropertySchema) => {
24+
const getPropertyTypeAnnotation = (property: ComponentPropertySchema, prefix?: string) => {
2525
// If a property type is one of the ones provided by Storyblok, return that type
2626
// Casting as string[] to avoid TS error on using Array.includes on different narrowed types
2727
if (Array.from(storyblokSchemas.keys()).includes(property.type as StoryblokPropertyType)) {
@@ -42,13 +42,13 @@ const getPropertyTypeAnnotation = (property: ComponentPropertySchema) => {
4242
if (property.filter_content_type) {
4343
if (typeof property.filter_content_type === 'string') {
4444
return {
45-
tsType: `(${getStoryType(property.filter_content_type)} | string )${property.type === 'options' ? '[]' : ''}`,
45+
tsType: `(${getStoryType(property.filter_content_type, prefix)} | string )${property.type === 'options' ? '[]' : ''}`,
4646
};
4747
}
4848

4949
return {
5050
tsType: `(${property.filter_content_type
51-
.map(type2 => getStoryType(type2))
51+
.map(type2 => getStoryType(type2, prefix))
5252
// In this case property.type can be `option` or `options`. In case of `options` the type should be an array
5353
.join(' | ')} | string )${property.type === 'options' ? '[]' : ''}`,
5454
};
@@ -115,8 +115,8 @@ const getPropertyTypeAnnotation = (property: ComponentPropertySchema) => {
115115
}
116116
};
117117

118-
export function getStoryType(property: string) {
119-
return `${STORY_TYPE}<${capitalize(property)}>`;
118+
export function getStoryType(property: string, prefix?: string) {
119+
return `${STORY_TYPE}<${prefix ?? ''}${capitalize(toCamelCase(property))}>`;
120120
}
121121

122122
/**
@@ -167,7 +167,7 @@ const getComponentPropertiesTypeAnnotations = async (
167167

168168
const propertyType = value.type;
169169
const propertyTypeAnnotation: JSONSchema = {
170-
[key]: getPropertyTypeAnnotation(value as ComponentPropertySchema),
170+
[key]: getPropertyTypeAnnotation(value as ComponentPropertySchema, options.typePrefix),
171171
};
172172

173173
if (propertyType === 'custom' && customFieldsParser) {

src/utils/format.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,15 @@ export const toPascalCase = (str: string) => {
44

55
export const toCamelCase = (str: string) => {
66
return str
7-
.replace(/(?:^|_)(\w)/g, (_, char) => char.toUpperCase())
7+
// First replace snake_case
8+
.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase())
89
.replace(/_/g, '')
9-
.replace(/^[A-Z]/, char => char.toLowerCase());
10+
// Then replace kebab-case
11+
.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase())
12+
// Capitalize letters after special characters
13+
.replace(/[^a-z0-9]([a-z])/gi, (_, letter) => letter.toUpperCase())
14+
// Remove special characters
15+
.replace(/[^a-z0-9]/gi, '');
1016
};
1117

1218
export const toSnakeCase = (str: string) => {

0 commit comments

Comments
 (0)