diff --git a/package-lock.json b/package-lock.json index bc9e5fa..f816be8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "contentstack-cli-tsgen", - "version": "2.1.7", + "version": "2.1.8", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "contentstack-cli-tsgen", - "version": "2.1.7", + "version": "2.1.8", "license": "MIT", "dependencies": { "@contentstack/cli-command": "^1.2.9", diff --git a/package.json b/package.json index 280593a..d9892fb 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "contentstack-cli-tsgen", "description": "Generate TypeScript typings from a Stack.", - "version": "2.1.7", + "version": "2.1.8", "author": "Michael Davis", "bugs": "https://github.com/Contentstack-Solutions/contentstack-cli-tsgen/issues", "dependencies": { diff --git a/src/commands/tsgen.ts b/src/commands/tsgen.ts index 674930f..94f6aa9 100644 --- a/src/commands/tsgen.ts +++ b/src/commands/tsgen.ts @@ -51,6 +51,11 @@ export default class TypeScriptCodeGeneratorCommand extends Command { hidden: false, multiple: false, }), + + 'include-system-fields': flags.boolean({ + description: 'include system fields in generated types', + default: false, + }), }; async run() { @@ -62,6 +67,7 @@ export default class TypeScriptCodeGeneratorCommand extends Command { const includeDocumentation = flags.doc const outputPath = flags.output const branch = flags.branch + const includeSystemFields = flags['include-system-fields'] if (token.type !== 'delivery') { this.warn('Possibly using a management token. You may not be able to connect to your Stack. Please use a delivery token.') @@ -91,7 +97,7 @@ export default class TypeScriptCodeGeneratorCommand extends Command { })) } schemas = schemas.concat(client.types) - const result = await tsgenRunner(outputPath, schemas, prefix, includeDocumentation) + const result = await tsgenRunner(outputPath, schemas, prefix, includeDocumentation, includeSystemFields) this.log(`Wrote ${result.definitions} Content Types to '${result.outputPath}'.`) } else { this.log('No Content Types exist in the Stack.') diff --git a/src/lib/stack/builtins.ts b/src/lib/stack/builtins.ts index 541dc80..902aa88 100644 --- a/src/lib/stack/builtins.ts +++ b/src/lib/stack/builtins.ts @@ -1,30 +1,52 @@ // File and Link fields are additional, non-scalar, data types within a stack. -export const defaultInterfaces = (prefix = '') => [ - `export interface ${prefix}File { - uid: string; - created_at: string; - updated_at: string; - created_by: string; - updated_by: string; - content_type: string; - file_size: string; - tags: string[]; - filename: string; - url: string; - ACL: any[]; - is_dir: boolean; - parent_uid: string; - _version: number; - title: string; - publish_details: { +export const defaultInterfaces = (prefix = '', systemFields = false) => { + const defaultInterfaces = [ + `export interface ${prefix}PublishDetails { environment: string; locale: string; time: string; user: string; - }; - }`, - `export interface ${prefix}Link { - title: string; - href: string; - }`, -] + }`, + `export interface ${prefix}File { + uid: string; + created_at: string; + updated_at: string; + created_by: string; + updated_by: string; + content_type: string; + file_size: string; + tags: string[]; + filename: string; + url: string; + ACL: any[]; + is_dir: boolean; + parent_uid: string; + _version: number; + title: string; + publish_details: ${prefix}PublishDetails; + }`, + `export interface ${prefix}Link { + title: string; + href: string; + }` + ] + if (systemFields) { + defaultInterfaces.push( + `export interface ${prefix}SystemFields { + uid?: string; + created_at?: string; + updated_at?: string; + created_by?: string; + updated_by?: string; + _content_type_uid?: string; + tags?: string[]; + ACL?: any[]; + _version?: number; + _in_progress?: boolean; + locale?: string; + publish_details?: ${prefix}PublishDetails[]; + title?: string; + }`) + } + return defaultInterfaces; +} diff --git a/src/lib/stack/schema.ts b/src/lib/stack/schema.ts index e02e8a4..baa12e0 100644 --- a/src/lib/stack/schema.ts +++ b/src/lib/stack/schema.ts @@ -27,6 +27,7 @@ export type Block = { export type GlobalField = { reference_to: string; schema: Schema; + schema_type?: string; _version?: number } & FieldOptions; @@ -50,7 +51,8 @@ export type Field = GlobalField & ReferenceField & GroupField & EnumField & - BlockField; + BlockField & + { field_metadata: FieldMetaData }; export type Schema = Array; export type ContentType = { @@ -61,3 +63,7 @@ export type ContentType = { data_type?: string; schema_type?: string; } & Identifier; + +export type FieldMetaData = { + ref_multiple?: boolean; +} diff --git a/src/lib/tsgen/factory.ts b/src/lib/tsgen/factory.ts index 75929ee..f90a63d 100644 --- a/src/lib/tsgen/factory.ts +++ b/src/lib/tsgen/factory.ts @@ -8,6 +8,7 @@ export type TSGenOptions = { naming?: { prefix: string; }; + systemFields?: boolean; }; export type TSGenResult = { @@ -54,6 +55,7 @@ const defaultOptions: TSGenOptions = { naming: { prefix: '', }, + systemFields: false } export default function (userOptions: TSGenOptions) { @@ -115,9 +117,14 @@ export default function (userOptions: TSGenOptions) { } function define_interface( - contentType: ContentstackTypes.ContentType | ContentstackTypes.GlobalField + contentType: ContentstackTypes.ContentType | ContentstackTypes.GlobalField, + systemFields = false ) { - return ['export interface', name_type(contentType.data_type === 'global_field' ? (contentType.reference_to as string) : contentType.uid)].join(' ') + const interface_declaration = ['export interface', name_type(contentType.data_type === 'global_field' ? (contentType.reference_to as string) : contentType.uid)] + if (systemFields && contentType.schema_type !== "global_field") { + interface_declaration.push('extends', name_type('SystemFields')) + } + return interface_declaration.join(' ') } function op_array(type: string, field: ContentstackTypes.Field) { @@ -226,7 +233,7 @@ export default function (userOptions: TSGenOptions) { ) { return [ options.docgen.interface(contentType.description), - define_interface(contentType), + define_interface(contentType, options.systemFields), '{', ['/**', "Version", '*/'].join(' '), [`version: `,contentType._version,';'].join(' '), @@ -302,7 +309,7 @@ export default function (userOptions: TSGenOptions) { references.push(name_type(field.reference_to)) } - return ['(', references.join(' | '), ')', '[]'].join('') + return ['(', references.join(' | '), ')', field.field_metadata?.ref_multiple ? '[]' : ''].join('') } return function (contentType: ContentstackTypes.ContentType): TSGenResult|any { diff --git a/src/lib/tsgen/runner.ts b/src/lib/tsgen/runner.ts index ab4ce11..3bd902c 100644 --- a/src/lib/tsgen/runner.ts +++ b/src/lib/tsgen/runner.ts @@ -26,7 +26,7 @@ function createOutputPath(outputFile: string) { return outputPath } -export default async function tsgenRunner(outputFile: string, contentTypes: any[], prefix = '', includeDocumentation = true) { +export default async function tsgenRunner(outputFile: string, contentTypes: any[], prefix = '', includeDocumentation = true, systemFields = false) { const docgen: DocumentationGenerator = includeDocumentation ? new JSDocumentationGenerator() : new NullDocumentationGenerator() const outputPath = createOutputPath(outputFile) @@ -38,6 +38,7 @@ export default async function tsgenRunner(outputFile: string, contentTypes: any[ naming: { prefix, }, + systemFields }) for (const contentType of contentTypes) { @@ -56,7 +57,7 @@ export default async function tsgenRunner(outputFile: string, contentTypes: any[ const output = await format( [ - defaultInterfaces(prefix).join('\n\n'), + defaultInterfaces(prefix, systemFields).join('\n\n'), [...globalFields].join('\n\n'), definitions.join('\n\n'), ].join('\n\n') diff --git a/tests/tsgen/references.test.ts b/tests/tsgen/references.test.ts index 0d70a5d..acf9d7b 100644 --- a/tests/tsgen/references.test.ts +++ b/tests/tsgen/references.test.ts @@ -1,27 +1,27 @@ -const testData = require('./references.ct') +const testData = require("./references.ct"); -import NullDocumentationGenerator from '../../src/lib/tsgen/docgen/nulldoc' -import tsgenFactory from '../../src/lib/tsgen/factory' +import NullDocumentationGenerator from "../../src/lib/tsgen/docgen/nulldoc"; +import tsgenFactory from "../../src/lib/tsgen/factory"; const tsgen = tsgenFactory({ docgen: new NullDocumentationGenerator(), naming: { - prefix: 'I', + prefix: "I", }, -}) +}); -describe('references', () => { - const result = tsgen(testData.references) +describe("references", () => { + const result = tsgen(testData.references); - test('metadata', () => { - const contentTypes = [...result.metadata.dependencies.contentTypes] + test("metadata", () => { + const contentTypes = [...result.metadata.dependencies.contentTypes]; expect(contentTypes).toEqual( - expect.arrayContaining(['IReferenceChild', 'IBoolean', 'IBuiltinExample']) - ) - }) + expect.arrayContaining(["IReferenceChild", "IBoolean", "IBuiltinExample"]) + ); + }); - test('definition', () => { + test("definition", () => { expect(result.definition).toMatchInlineSnapshot(` "export interface IReferenceParent { @@ -29,9 +29,9 @@ describe('references', () => { version: 5 ; title: string; url: string; - single_reference: (IReferenceChild)[]; + single_reference: (IReferenceChild); multiple_reference?: (IReferenceChild | IBoolean | IBuiltinExample)[]; }" - `) - }) -}) + `); + }); +});