From 4fbdc97ec7116d817d03f4912ee38b44dea95cb3 Mon Sep 17 00:00:00 2001 From: EK Nayeem Date: Tue, 18 Aug 2026 20:51:33 +0600 Subject: [PATCH 1/2] fix: semicolor after label field --- .../generators/generate-twenty-view-fields.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/internal/generators/generate-twenty-view-fields.ts b/src/internal/generators/generate-twenty-view-fields.ts index 7b6358c..fe56de5 100644 --- a/src/internal/generators/generate-twenty-view-fields.ts +++ b/src/internal/generators/generate-twenty-view-fields.ts @@ -8,6 +8,8 @@ import { fieldUidVarNames } from "../utils/fields.js"; import { FieldType } from "twenty-sdk/define"; import { toImportStatement } from "../utils/to-import-statement.js"; +const fieldSeperator = ",\n"; + export function generateTwentyViewFields( objectFilePath: string, viewFilePath: string, @@ -22,7 +24,7 @@ export function generateTwentyViewFields( ...fieldUidVarNames(fields) ); - let viewFields: Array = []; + let viewFields: string = ""; let labelField: string = ""; let islabelFieldExists = false; let position = 0; @@ -37,7 +39,8 @@ export function generateTwentyViewFields( } position += 1; - viewFields.push(getFieldString(position, fieldUidVarName)); + viewFields += getFieldString(position, fieldUidVarName); + viewFields += fieldSeperator; }); if (!islabelFieldExists) { @@ -50,9 +53,13 @@ export function generateTwentyViewFields( ); } + const labelFieldSeperator = islabelFieldExists + ? fieldSeperator + : "\n"; + return { fieldMetadataUidsImportStatement, - viewFields: labelField + "\n" + [viewFields].join(",\n"), + viewFields: labelField + labelFieldSeperator + viewFields, }; } From 27195443ee1af583ae16df1af2b42f09eb5cf251 Mon Sep 17 00:00:00 2001 From: EK Nayeem Date: Wed, 19 Aug 2026 00:14:12 +0600 Subject: [PATCH 2/2] fix: unique position for multiple generated views and nav menu items fixes: #1 --- src/internal/create-cli.ts | 20 ++++++++-- .../generate-twenty-nav-menu-item.ts | 6 ++- .../generators/generate-twenty-view.ts | 5 ++- src/internal/utils/string-to-int.ts | 18 +++++++++ src/internal/utils/temp-store.ts | 38 +++++++++++++++++++ 5 files changed, 81 insertions(+), 6 deletions(-) create mode 100644 src/internal/utils/string-to-int.ts create mode 100644 src/internal/utils/temp-store.ts diff --git a/src/internal/create-cli.ts b/src/internal/create-cli.ts index e4c5ce9..8876718 100644 --- a/src/internal/create-cli.ts +++ b/src/internal/create-cli.ts @@ -2,6 +2,7 @@ import { Command } from "commander"; import { styleText } from "node:util"; import packageJson from "../../package.json" with { type: "json" }; import { logErrorAndExit } from "./utils/log-error-and-exit.js"; +import { stringToInt } from "./utils/string-to-int.js"; export type Entity = "constant" | "object" | "view" | "navItem"; @@ -15,11 +16,13 @@ const VALID_ENTITIES = new Set([ export type CliOptions = { input: string; output: string; + entities: Array; constantsDir: string; objectsDir: string; viewsDir: string; navMenuItemsDir: string; - entities: Array; + viewsBasePosition: number; + navMenuItemsBasePosition: number; print: boolean; dryRun: boolean; clipboard: boolean; @@ -32,6 +35,9 @@ const DEFAULT_OBJECTS_DIR = "objects"; const DEFAULT_VIEWS_DIR = "views"; const DEFAULT_NAV_MENU_ITEMS_DIR = "navigation-menu-items"; +const DEFAULT_VIEWS_BASE_POSITION = "0"; +const DEFAULT_NAV_MENU_ITEMS_BASE_POSITION = "0"; + function validateEntities(entities: Array) { if (entities.length === 0) return; @@ -41,7 +47,7 @@ function validateEntities(entities: Array) { if (invalidEntities.length > 0) { logErrorAndExit( - `Invalid entity name(s): ${invalidEntities.join(", ")}.\nAllowed values: ${Array.from(VALID_ENTITIES).join(", ")}.` + `Invalid entity name(s): ${invalidEntities.join(", ")}. Allowed values: ${Array.from(VALID_ENTITIES).join(", ")}.` ); } } @@ -75,11 +81,13 @@ export function createCLI(argv = process.argv) { .name("Generate twenty fields from types(Object/Interface)") .option("-i, --input ", "*.ts/*.d.ts file") .option("-o, --output ", "output root directory", DEFAULT_ROOT_DIR) + .option("-e, --entities [entities...]", `can specify single or multiple entities among: ${styleText("yellow", "(constant | object | view | navItem)")}`) .option("--constants-dir ", "output constants directory", DEFAULT_CONSTANTS_DIR) .option("--objects-dir ", "output object directory", DEFAULT_OBJECTS_DIR) .option("--views-dir ", "output views directory", DEFAULT_VIEWS_DIR) .option("--nav-menu-items-dir ", "output navigation menu items directory", DEFAULT_NAV_MENU_ITEMS_DIR) - .option("-e, --entities [entities...]", `can specify single or multiple entities among: ${styleText("yellow", "(constant | object | view | navItem)")}`) + .option("--views-base-position ", "offset for views starting index", DEFAULT_VIEWS_BASE_POSITION) + .option("--nav-menu-items-base-position ", "offset for navigation menu items starting index", DEFAULT_NAV_MENU_ITEMS_BASE_POSITION) .option("-p, --print", "print to console", false) .option("-d, --dry-run", "print outputs to console, do not write on disk", false) .option("-c, --clipboard", "copy object to clipboard", false) @@ -87,7 +95,11 @@ export function createCLI(argv = process.argv) { .helpOption() .action((parsedOpts: CliOptions) => { validateEntities(normalizeEntities(parsedOpts.entities)); - opts = parsedOpts; + + const viewsBasePosition = stringToInt(parsedOpts.viewsBasePosition); + const navMenuItemsBasePosition = stringToInt(parsedOpts.navMenuItemsBasePosition); + + opts = { ...parsedOpts, viewsBasePosition, navMenuItemsBasePosition}; }) .parse(argv); diff --git a/src/internal/generators/generate-twenty-nav-menu-item.ts b/src/internal/generators/generate-twenty-nav-menu-item.ts index f6178f9..be3fba1 100644 --- a/src/internal/generators/generate-twenty-nav-menu-item.ts +++ b/src/internal/generators/generate-twenty-nav-menu-item.ts @@ -4,6 +4,7 @@ import { NavigationMenuItemType } from "twenty-sdk/define"; import { toUidVarName } from "../utils/to-uid-var-name.js"; import { toImportStatement } from "../utils/to-import-statement.js"; import { toKebabCase } from "../utils/case-transformation.js"; +import { tempStore } from "../utils/temp-store.js"; export function generateTwentyNavMenuItem( navItemName: string, @@ -25,6 +26,9 @@ export function generateTwentyNavMenuItem( objectUidVarName ); + const navMenuItemsPositionStore = + tempStore().navMenuItemsPositionStore; + const output = dedent`import { defineNavigationMenuItem, NavigationMenuItemType } from "twenty-sdk/define"; ${constantImportStatement} @@ -32,7 +36,7 @@ export function generateTwentyNavMenuItem( universalIdentifier: ${navMenuItemUidVarName}, name: "${toKebabCase(navItemName)}", icon: "IconList", - position: 0, + position: ${navMenuItemsPositionStore.getPositionAndIncrement()}, type: NavigationMenuItemType.${ NavigationMenuItemType.OBJECT }, diff --git a/src/internal/generators/generate-twenty-view.ts b/src/internal/generators/generate-twenty-view.ts index acd85e4..8ea0c8c 100644 --- a/src/internal/generators/generate-twenty-view.ts +++ b/src/internal/generators/generate-twenty-view.ts @@ -4,6 +4,7 @@ import { generateTwentyViewFields } from "./generate-twenty-view-fields.js"; import type { IRField } from "../types.js"; import { toUidVarName } from "../utils/to-uid-var-name.js"; import { toImportStatement } from "../utils/to-import-statement.js"; +import { tempStore } from "../utils/temp-store.js"; export function generateTwentyView( viewName: string, @@ -31,6 +32,8 @@ export function generateTwentyView( fields ); + const viewPositionStore = tempStore().viewsPositionStore; + const output = dedent`import { defineView, ViewKey } from "twenty-sdk/define"; ${viewUidImportStatement} ${fieldMetadataUidsImportStatement} @@ -41,7 +44,7 @@ export function generateTwentyView( objectUniversalIdentifier: ${objectUidVarName}, icon: "IconList", key: ViewKey.INDEX, - position: 0, + position: ${viewPositionStore.getPositionAndIncrement()}, fields: [ ${viewFields} ], diff --git a/src/internal/utils/string-to-int.ts b/src/internal/utils/string-to-int.ts new file mode 100644 index 0000000..e0326bb --- /dev/null +++ b/src/internal/utils/string-to-int.ts @@ -0,0 +1,18 @@ +import { styleText } from "node:util"; + +export const stringToInt = (str: any): number => { + let num = parseInt(str as string, 10); + + if (isNaN(num)) { + console.error( + styleText( + "red", + `Cannot convert "${str}" to an integer. Falling back to 0.` + ) + ); + + num = 0; + } + + return num; +}; diff --git a/src/internal/utils/temp-store.ts b/src/internal/utils/temp-store.ts new file mode 100644 index 0000000..29928cc --- /dev/null +++ b/src/internal/utils/temp-store.ts @@ -0,0 +1,38 @@ +import { createCLI } from "../create-cli.js"; + +const { viewsBasePosition, navMenuItemsBasePosition } = + createCLI(); + +type positionStore = { + position: number; + getPositionAndIncrement: () => number; +}; + +function getValueAndIncrement(store: positionStore): number { + const currentPosition = store.position; + store.position += 1; + return currentPosition; +} + +export const viewsPositionStore: positionStore = { + position: viewsBasePosition, + + getPositionAndIncrement: function () { + return getValueAndIncrement(this); + }, +}; + +export const navMenuItemsPositionStore: positionStore = { + position: navMenuItemsBasePosition, + + getPositionAndIncrement: function () { + return getValueAndIncrement(this); + }, +}; + +export function tempStore() { + return { + viewsPositionStore, + navMenuItemsPositionStore, + }; +}