Repository: jiahaog/nativefier
File: app/src/components/menu.ts (TypeScript)

Code before:
        },
        {
          label: 'Quit',
          accelerator: 'Cmd+Q',
          click: appQuit,
        },
      ],
    };
    (windowMenu.submenu as MenuItemConstructorOptions[]).push(
      {
        type: 'separator',
      },
      {
        label: 'Bring All to Front',
        role: 'front',
      },
    );
    menuTemplate = [electronMenu, editMenu, viewMenu, windowMenu, helpMenu];
  } else {
    menuTemplate = [editMenu, viewMenu, windowMenu, helpMenu];
  }

  try {
    const bookmarkConfigPath = path.join(__dirname, '..', 'bookmarks.json');
    if (fs.existsSync(bookmarkConfigPath)) {
      const bookmarksConfig = JSON.parse(
        fs.readFileSync(bookmarkConfigPath, 'utf-8'),
      );
      const bookmarksMenu: MenuItemConstructorOptions = {
        label: bookmarksConfig['menuLabel'],
        submenu: bookmarksConfig['bookmarks'].map((el) => {
          if (el['type'] === 'link') {
            let accelerator = null;
            if ('shortcut' in el) {
              accelerator = el['shortcut'];
            }
            return {
              label: el['title'],
              click: () => {
                gotoUrl(el['url']);
              },
              accelerator: accelerator,
            };
          } else if (el['type'] === 'separator') {
            return {
              type: 'separator',
            };
          }
        }),
      };
      menuTemplate.splice(menuTemplate.length - 2, 0, bookmarksMenu);

Review comment (suggestion):
To replace later `['` access with friendlier `.` access and to avoid eventual whoopsies, please type your `bookmarksConfig as a `BookmarksConfig` expressed as the union of link or separator. Something like

```ts
type BookmarksLink = {
  type: 'link';
  title: string;
  url: string;
  shortcut?: string;
};
type BookmarksSeparator = {
  type: 'separator';
};
type BookmarksConfig = BookmarksLink | BookmarksSeparator;
```

Code after:
        {
          label: 'Hide App',
          accelerator: 'Cmd+H',
          role: 'hide',
        },
        {
          label: 'Hide Others',
          accelerator: 'Cmd+Shift+H',
          role: 'hideOthers',
        },
        {
          label: 'Show All',
          role: 'unhide',
        },
        {
          type: 'separator',
        },
        {
          label: 'Quit',
          accelerator: 'Cmd+Q',
          click: appQuit,
        },
      ],
    };
    (windowMenu.submenu as MenuItemConstructorOptions[]).push(
      {
        type: 'separator',
      },
      {
        label: 'Bring All to Front',
        role: 'front',
      },
    );
    menuTemplate = [electronMenu, editMenu, viewMenu, windowMenu, helpMenu];
  } else {
    menuTemplate = [editMenu, viewMenu, windowMenu, helpMenu];
  }

  try {
    const bookmarkConfigPath = path.join(__dirname, '..', 'bookmarks.json');
    if (fs.existsSync(bookmarkConfigPath)) {
      const bookmarksMenuConfig: BookmarksMenuConfig = JSON.parse(
        fs.readFileSync(bookmarkConfigPath, 'utf-8'),
      );
      const bookmarksMenu: MenuItemConstructorOptions = {
        label: bookmarksMenuConfig.menuLabel,
        submenu: bookmarksMenuConfig.bookmarks.map((bookmark) => {
          if (bookmark.type === 'link') {
            if (!('title' in bookmark && 'url' in bookmark)) {
              throw Error(
                'All links in the bookmarks menu must have a title and url.',