diff --git a/MIGRATION.md b/MIGRATION.md new file mode 100644 index 0000000..da8f57f --- /dev/null +++ b/MIGRATION.md @@ -0,0 +1,56 @@ +## Migrating from v2 to v3 +This document outlines the necessary changes to separate nested modular blocks into distinct interfaces. This update will affect how modular blocks are structured and used throughout the codebase. + +## Before +```typescript +export interface Test { + /** Version */ + _version?: 2; + /** Title */ + title: string; + /** Modular Blocks */ + modular_blocks?: { + test: { + /** Multi Line Textbox */ + multi_line?: string; + /** Rich Text Editor */ + rich_text_editor?: string; + /** Modular Blocks1 */ + modular_blocks1?: { + test1: { + /** Multi Line Textbox */ + multi_line?: string; + }; + }[]; + }; + }[]; +} +``` + + +## After +```typescript +export interface Test { + /** Version */ + _version: 2; + /** Title */ + title: string; + /** Modular Blocks */ + modular_blocks?: ModularBlocks[]; +} + + +export interface ModularBlocks { + /** Multi Line Textbox */ + multi_line?: string; + /** Rich Text Editor */ + rich_text_editor?: string; + /** Modular Blocks1 */ + modular_blocks1?: ModularBlocks1[]; +} + +export interface ModularBlocks1 { + /** Multi Line Textbox */ + multi_line?: string; +} +``` \ No newline at end of file diff --git a/README.md b/README.md index cb0e6a2..fbaad89 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,9 @@ This plugin generates TypeScript typings from Content Types. Interfaces and fiel $ csdx plugins:install contentstack-cli-tsgen ``` +## Migration +Refer to the [Migration Guide](https://github.com/contentstack/contentstack-cli-tsgen/blob/master/MIGRATION.md) version 3 if you are migrating from version 2 or older. + ## How to use this plugin `$ csdx tsgen` @@ -92,38 +95,7 @@ interface BuiltinExample { /** Single Choice */ single_choice: "Choice 1" | "Choice 2" | "Choice 3"; /** Modular Blocks */ - modular_blocks?: ( - | { - block_1: { - /** Number */ - number?: number; - /** Single line textbox */ - single_line?: string; - }; - block_2: undefined; - seo_gf: undefined; - } - | { - block_2: { - /** Boolean */ - boolean?: boolean; - /** Date */ - date?: string; - }; - block_1: undefined; - seo_gf: undefined; - } - | { - seo_gf: { - /** Keywords */ - keywords?: string; - /** Description */ - description?: string; - }; - block_1: undefined; - block_2: undefined; - } - )[]; + modular_blocks?:ModularBlocks[]; /** Number */ number?: number; /** Link */ @@ -135,4 +107,25 @@ interface BuiltinExample { /** Date */ date?: string; } + +interface ModularBlocks { + block_1: { + /** Number */ + number?: number; + /** Single line textbox */ + single_line?: string; + }; + block_2: { + /** Boolean */ + boolean?: boolean; + /** Date */ + date?: string; + }; + seo_gf: { + /** Keywords */ + keywords?: string; + /** Description */ + description?: string; + }; +} ``` diff --git a/package-lock.json b/package-lock.json index 581533e..3e57e2d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "contentstack-cli-tsgen", - "version": "2.3.4", + "version": "3.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "contentstack-cli-tsgen", - "version": "2.3.4", + "version": "3.0.0", "license": "MIT", "dependencies": { "@contentstack/cli-command": "^1.2.17", diff --git a/package.json b/package.json index 75b0223..e9b6840 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "contentstack-cli-tsgen", "description": "Generate TypeScript typings from a Stack.", - "version": "2.3.4", + "version": "3.0.0", "author": "Michael Davis", "bugs": "https://github.com/Contentstack-Solutions/contentstack-cli-tsgen/issues", "dependencies": { diff --git a/src/lib/tsgen/factory.ts b/src/lib/tsgen/factory.ts index 8b10343..d09076f 100644 --- a/src/lib/tsgen/factory.ts +++ b/src/lib/tsgen/factory.ts @@ -31,6 +31,10 @@ type GlobalFieldCache = { [prop: string]: { definition: string }; }; +type ModularBlockCache = { + [prop: string]: string; +}; + enum TypeFlags { BuiltinJS = 1 << 0, BuiltinCS = 1 << 1, @@ -65,6 +69,8 @@ export default function (userOptions: TSGenOptions) { const visitedGlobalFields = new Set() const visitedContentTypes = new Set() const cachedGlobalFields: GlobalFieldCache = {} + const cachedModularBlocks: ModularBlockCache = {} + const modularBlockInterfaces = new Set() const typeMap: TypeMap = { text: {func: type_text, track: true, flag: TypeFlags.BuiltinJS}, @@ -215,6 +221,8 @@ export default function (userOptions: TSGenOptions) { if (field.multiple) { fieldType += "[]"; } + }else if (field.data_type === 'blocks') { + fieldType = type_modular_blocks(field); } return [ field.uid + op_required(field.mandatory) + ':', @@ -235,7 +243,8 @@ export default function (userOptions: TSGenOptions) { function visit_content_type( contentType: ContentstackTypes.ContentType | ContentstackTypes.GlobalField ) { - return [ + modularBlockInterfaces.clear(); + const contentTypeInterface = [ options.docgen.interface(contentType.description), define_interface(contentType, options.systemFields), '{', @@ -246,25 +255,36 @@ export default function (userOptions: TSGenOptions) { ] .filter(v => v) .join('\n') - } - function visit_modular_block( - field: ContentstackTypes.Field, - block: ContentstackTypes.Block - ) { - return ( - '{' + - [block.uid + ':', block.reference_to ? name_type(block.reference_to as string) + ';' : '{' + visit_fields(block.schema || []) + '};'].join(' ') + - visit_block_names(field, block) + - '}' - ) + return [...modularBlockInterfaces, contentTypeInterface].join('\n\n'); } - function type_modular_blocks(field: ContentstackTypes.Field) { - return op_paren( - field.blocks.map(block => visit_modular_block(field, block)).join(' | ') - ) + function type_modular_blocks(field: ContentstackTypes.Field): string { + const blockInterfaceName = name_type(field.uid); + if(!cachedModularBlocks[blockInterfaceName]){ + const blockInterfaces = field.blocks.map((block) => { + const fieldType = block.reference_to && cachedGlobalFields[name_type(block.reference_to)] + ? name_type(block.reference_to) + : visit_fields(block.schema || []); + + const schema = block.reference_to ? `${fieldType};` : `{\n ${fieldType} }`; + return `${block.uid}: ${schema}`; + }); + + const modularInterface = [ + `export interface ${blockInterfaceName} {`, + blockInterfaces.join('\n'), + '}', + ].join('\n'); + + // Store or track the generated block interface for later use + modularBlockInterfaces.add(modularInterface); + cachedModularBlocks[blockInterfaceName] = blockInterfaceName; + } + + return field.multiple ? `${blockInterfaceName}[]` : blockInterfaceName; } + function type_group(field: ContentstackTypes.Field) { return ['{', visit_fields(field.schema), '}'].filter(v => v).join('\n') diff --git a/tests/tsgen/modular.blocks.test.ts b/tests/tsgen/modular.blocks.test.ts index f8a7ad1..455f7b5 100644 --- a/tests/tsgen/modular.blocks.test.ts +++ b/tests/tsgen/modular.blocks.test.ts @@ -18,20 +18,26 @@ describe("modular blocks", () => { test("definition", () => { expect(result.definition).toMatchInlineSnapshot(` - "export interface ModularBlocks + "export interface ModularBlocks { + string_block: { + single_line?: string ; + multi_line?: string ; + markdown?: string ; + rich_text_editor?: string ; } + string_block_with_options: { + single_line_textbox_required: string ; + single_line_textbox_multiple?: string[] ; } + boolean_block: { + boolean?: boolean ; } + } + + export interface ModularBlocks { /** Version */ _version?: 2 ; title: string ; url: string ; - modular_blocks?: ({string_block: {single_line?: string ; - multi_line?: string ; - markdown?: string ; - rich_text_editor?: string ;};string_block_with_options: undefined; - boolean_block: undefined;} | {string_block_with_options: {single_line_textbox_required: string ; - single_line_textbox_multiple?: string[] ;};string_block: undefined; - boolean_block: undefined;} | {boolean_block: {boolean?: boolean ;};string_block: undefined; - string_block_with_options: undefined;})[] ; + modular_blocks?: ModularBlocks[] ; }" `); });