実現したいこと
electron + sveltekit でのデスクトップアプリで、メニューバーを自作(レイアウト等)したいです。
melt ui の Menubarを用いて作成しており、electron.cjs
からメニューをインポート → {#each}
でメニューを実装したいです。
発生している問題・分からないこと
electron.cjs
で、
javascript
1preload: path.join(__dirname, 'preload.cjs')
という風に実行すると、エラーが起きてメニューを読み込みたい部分での
typeScript
1import { Menu } from 'electron'; 2import { ipcRenderer } from 'electron';
などの electron
との通信を行うようなものが使えなくなる
エラーメッセージ
error
1app.js:16 ReferenceError: __dirname is not defined 2 at node_modules/electron/index.js (index.js:4:28) 3 at __require (chunk-UXIASGQL.js?v=14a85010:8:50) 4 at index.js:21:34 5handleError @ app.js:16 62index.js:4 Uncaught (in promise) ReferenceError: __dirname is not defined 7 at node_modules/electron/index.js (index.js:4:28) 8 at __require (chunk-UXIASGQL.js?v=14a85010:8:50) 9 at index.js:21:34
該当のソースコード
typescript
1// src/electron.cjs 2 3const windowStateManager = require('electron-window-state'); 4const { app, BrowserWindow, Menu, ipcMain } = require('electron'); 5const contextMenu = require('electron-context-menu'); 6const serve = require('electron-serve'); 7const path = require('path'); 8 9try { 10 require('electron-reloader')(module); 11} catch (e) { 12 console.error(e); 13} 14 15const serveURL = serve({ directory: '.' }); 16const port = process.env.PORT || 5173; 17const dev = !app.isPackaged; 18let mainWindow; 19 20function createWindow() { 21 let windowState = windowStateManager({ 22 defaultWidth: 800, 23 defaultHeight: 600, 24 }); 25 26 const mainWindow = new BrowserWindow({ 27 backgroundColor: 'whitesmoke', 28 titleBarStyle: 'hidden', 29 autoHideMenuBar: false, 30 trafficLightPosition: { 31 x: 17, 32 y: 32, 33 }, 34 minHeight: 450, 35 minWidth: 500, 36 webPreferences: { 37 enableRemoteModule: true, 38 contextIsolation: true, 39 nodeIntegration: false, 40 spellcheck: false, 41 devTools: dev, 42 preload: path.join(__dirname, 'preload.cjs'), 43 }, 44 x: windowState.x, 45 y: windowState.y, 46 width: windowState.width, 47 height: windowState.height, 48 }); 49 50 windowState.manage(mainWindow); 51 52 mainWindow.once('ready-to-show', () => { 53 mainWindow.show(); 54 mainWindow.focus(); 55 }); 56 57 mainWindow.on('close', () => { 58 windowState.saveState(mainWindow); 59 }); 60 61 return mainWindow; 62} 63 64ipcMain.on('close-window', () => { 65 BrowserWindow.getFocusedWindow().close(); 66}); 67 68ipcMain.on('minimize-window', () => { 69 BrowserWindow.getFocusedWindow().minimize(); 70}); 71 72ipcMain.on('maximize-window', () => { 73 const currentWindow = BrowserWindow.getFocusedWindow(); 74 if (currentWindow.isMaximized()) { 75 currentWindow.unmaximize(); 76 } else { 77 currentWindow.maximize(); 78 } 79}); 80 81contextMenu({ 82 showLookUpSelection: false, 83 showSearchWithGoogle: false, 84 showCopyImage: false, 85 prepend: (defaultActions, params, browserWindow) => [ 86 { 87 label: 'Make App 💻', 88 }, 89 ], 90}); 91 92function loadVite(port) { 93 mainWindow.loadURL(`http://localhost:${port}`).catch((e) => { 94 console.log('Error loading URL, retrying', e); 95 setTimeout(() => { 96 loadVite(port); 97 }, 200); 98 }); 99} 100 101function createMainWindow() { 102 mainWindow = createWindow(); 103 mainWindow.once('close', () => { 104 mainWindow = null; 105 }); 106 107 if (dev) loadVite(port); 108 else serveURL(mainWindow); 109} 110 111const template = [ 112 { 113 label: 'File', 114 submenu: [ 115 { label: 'New', click: () => console.log('New File') }, 116 { label: 'Open', click: () => console.log('Open File') }, 117 { label: 'Save', click: () => console.log('Save File') }, 118 { type: 'separator' }, 119 { label: 'Exit', role: 'quit' } 120 ] 121 }, 122 { 123 label: 'Edit', 124 submenu: [ 125 { label: 'Undo', role: 'undo' }, 126 { label: 'Redo', role: 'redo' }, 127 { type: 'separator' }, 128 { label: 'Cut', role: 'cut' }, 129 { label: 'Copy', role: 'copy' }, 130 { label: 'Paste', role: 'paste' } 131 ] 132 } 133] 134app.on('ready', function () { 135 const menu = Menu.buildFromTemplate(template) 136 Menu.setApplicationMenu(menu) 137 createMainWindow(); 138}) 139 140ipcMain.handle('get-menu', async (event) => { 141 return template; 142}) 143 144app.on('activate', () => { 145 if (!mainWindow) { 146 createMainWindow(); 147 } 148}); 149app.on('window-all-closed', () => { 150 if (process.platform !== 'darwin') app.quit(); 151}); 152 153ipcMain.on('to-main', (event, count) => { 154 return mainWindow.webContents.send('from-main', `next count is ${count + 1}`); 155}); 156
typescript
1// src/preload.cjs 2 3const { contextBridge, ipcRenderer } = require('electron'); 4 5contextBridge.exposeInMainWorld('electron', { 6 invoke: (channel, data) => ipcRenderer.invoke(channel, data), 7 send: (channel, data) => ipcRenderer.send(channel, data), 8 on: (channel, func) => { 9 ipcRenderer.on(channel, (event, ...args) => func(...args)); 10 }, 11 sendSync: (channel, data) => { 12 ipcRenderer.sendSync(channel, data); 13 }, 14});
試したこと・調べたこと
- teratailやGoogle等で検索した
- ソースコードを自分なりに変更した
- 知人に聞いた
- その他
上記の詳細・結果
「referenceerror: __dirname is not defined electron」と調べると、多くは
javascript
1// src/electron.cjs 2webPreferences: { 3 enableRemoteModule: true, 4 contextIsolation: true, 5 nodeIntegration: false, 6 spellcheck: false, 7 devTools: dev, 8 preload: path.join(__dirname, 'preload.cjs'), 9 },
の nodeIntegration
を ture
にして contextIsolation
を false
にするといいよと書いてあったのですが、そもそもこれを直しても error のままだったのと、本来 nodeIntegration
はセキュリティ的に false
がいいよと書いてあったので、どうすればいいのやら…って感じです。
補足
上記以外に必要なソースコードがあれば教えてください。
![guest](/img/icon/icnUserSample.jpg)
あなたの回答
tips
プレビュー