feat: migrate from rollup to electron-vite (#1364)

* feat: electron-vite PoC

* fix: fix preload path

* remove rollup deps and config

* fix: debug mode

* fix: build mode, asset path

* fix: remove unused dependencies

* feat: use `executeJavaScriptInIsolatedWorld` instead of `executeJavaScript`

* feat: enable `minify`

* fix(actions): update task name

* fix: fix dev mode check

* fix: remove unused variable
This commit is contained in:
JellyBrick
2023-11-07 19:49:28 +09:00
committed by GitHub
parent c5d0314db6
commit 2da29fcfa7
31 changed files with 870 additions and 578 deletions

1
src/index.html Normal file
View File

@ -0,0 +1 @@
<script type="module" src="./renderer.ts"></script>

View File

@ -8,6 +8,7 @@ import is from 'electron-is';
import unhandled from 'electron-unhandled';
import { autoUpdater } from 'electron-updater';
import electronDebug from 'electron-debug';
import { parse } from 'node-html-parser';
import config from './config';
import { refreshMenu, setApplicationMenu } from './menu';
@ -198,7 +199,7 @@ async function createMainWindow() {
show: false,
webPreferences: {
contextIsolation: true,
preload: path.join(__dirname, 'preload.js'),
preload: path.join(__dirname, '..', 'preload', 'preload.js'),
...(isTesting()
? undefined
: {
@ -334,12 +335,34 @@ async function createMainWindow() {
removeContentSecurityPolicy();
win.webContents.on('dom-ready', () => {
const rendererScriptPath = path.join(__dirname, 'renderer.js');
win.webContents.executeJavaScriptInIsolatedWorld(0, [{
code: fs.readFileSync(rendererScriptPath, 'utf-8') + ';0',
url: url.pathToFileURL(rendererScriptPath).toString(),
}], true);
win.webContents.on('dom-ready', async () => {
// Inject index.html file as string using insertAdjacentHTML
// In dev mode, get string from process.env.VITE_DEV_SERVER_URL, else use fs.readFileSync
if (is.dev() && process.env.ELECTRON_RENDERER_URL) {
// HACK: to make vite work with electron renderer (supports hot reload)
await win.webContents.executeJavaScript(`
console.log('Loading vite from dev server');
const viteScript = document.createElement('script');
viteScript.type = 'module';
viteScript.src = '${process.env.ELECTRON_RENDERER_URL}/@vite/client';
const rendererScript = document.createElement('script');
rendererScript.type = 'module';
rendererScript.src = '${process.env.ELECTRON_RENDERER_URL}/renderer.ts';
document.body.appendChild(viteScript);
document.body.appendChild(rendererScript);
0
`);
} else {
const rendererPath = path.join(__dirname, '..', 'renderer');
const indexHTML = parse(fs.readFileSync(path.join(rendererPath, 'index.html'), 'utf-8'));
const scriptSrc = indexHTML.querySelector('script')!;
const scriptPath = path.join(rendererPath, scriptSrc.getAttribute('src')!);
const scriptString = fs.readFileSync(scriptPath, 'utf-8');
await win.webContents.executeJavaScriptInIsolatedWorld(0, [{
code: scriptString + ';0',
url: url.pathToFileURL(scriptPath).toString(),
}], true);
}
});
win.webContents.loadURL(urlToLoad);

View File

@ -1,3 +1 @@
const inject: () => void;
export default inject;
export const inject: () => void;

View File

@ -7,7 +7,7 @@
Parts of this code is derived from set-constant.js:
https://github.com/gorhill/uBlock/blob/5de0ce975753b7565759ac40983d31978d1f84ca/assets/resources/scriptlets.js#L704
*/
module.exports = () => {
export const inject = () => {
{
const pruner = function (o) {
delete o.playerAds;

View File

@ -1,5 +1,5 @@
import config, { shouldUseBlocklists } from './config';
import inject from './inject';
import { inject } from './inject';
import injectCliqzPreload from './inject-cliqz-preload';
import { blockers } from './blocker-types';

View File

@ -1,6 +1,6 @@
import configProvider from './config-renderer';
import CaptionsSettingsButtonHTML from './templates/captions-settings-template.html';
import CaptionsSettingsButtonHTML from './templates/captions-settings-template.html?raw';
import { ElementFromHtml } from '../utils-renderer';
import { YoutubePlayer } from '../../types/youtube-player';

View File

@ -208,8 +208,10 @@ export default (
// if lastSent is more than 5 seconds ago, send the new time
if (currentTime - lastSent > 5000) {
lastSent = currentTime;
lastSongInfo.elapsedSeconds = t;
updateActivity(lastSongInfo);
if (lastSongInfo) {
lastSongInfo.elapsedSeconds = t;
updateActivity(lastSongInfo);
}
}
});
});

View File

@ -1,4 +1,4 @@
import downloadHTML from './templates/download.html';
import downloadHTML from './templates/download.html?raw';
import defaultConfig from '../../config/defaults';
import { getSongMenu } from '../../providers/dom-elements';

View File

@ -1,12 +1,13 @@
import { createPanel } from './menu/panel';
import logo from './assets/menu.svg';
import close from './assets/close.svg';
import minimize from './assets/minimize.svg';
import maximize from './assets/maximize.svg';
import unmaximize from './assets/unmaximize.svg';
import logoRaw from './assets/menu.svg?inline';
import closeRaw from './assets/close.svg?inline';
import minimizeRaw from './assets/minimize.svg?inline';
import maximizeRaw from './assets/maximize.svg?inline';
import unmaximizeRaw from './assets/unmaximize.svg?inline';
import type { Menu } from 'electron';
import * as electron from 'electron';
function $<E extends Element = Element>(selector: string) {
return document.querySelector<E>(selector);
@ -23,6 +24,26 @@ export default async () => {
let maximizeButton: HTMLButtonElement;
if (isMacOS) titleBar.style.setProperty('--offset-left', '70px');
const logo = document.createElement('img');
const close = document.createElement('img');
const minimize = document.createElement('img');
const maximize = document.createElement('img');
const unmaximize = document.createElement('img');
if (window.ELECTRON_RENDERER_URL) {
logo.src = window.ELECTRON_RENDERER_URL + '/' + logoRaw;
close.src = window.ELECTRON_RENDERER_URL + '/' + closeRaw;
minimize.src = window.ELECTRON_RENDERER_URL + '/' + minimizeRaw;
maximize.src = window.ELECTRON_RENDERER_URL + '/' + maximizeRaw;
unmaximize.src = window.ELECTRON_RENDERER_URL + '/' + unmaximizeRaw;
} else {
logo.src = logoRaw;
close.src = closeRaw;
minimize.src = minimizeRaw;
maximize.src = maximizeRaw;
unmaximize.src = unmaximizeRaw;
}
logo.classList.add('title-bar-icon');
const logoClick = () => {
hideMenu = !hideMenu;

View File

@ -1,5 +1,5 @@
import forwardHTML from './templates/forward.html';
import backHTML from './templates/back.html';
import forwardHTML from './templates/forward.html?raw';
import backHTML from './templates/back.html?raw';
import { ElementFromHtml } from '../utils-renderer';

View File

@ -7,9 +7,10 @@ import config from './config';
import { cache } from '../../providers/decorators';
import { SongInfo } from '../../providers/song-info';
import { getAssetsDirectoryLocation } from '../utils';
const defaultIcon = path.join(getAssetsDirectoryLocation(), 'youtube-music.png');
import youtubeMusicIcon from '../../../assets/youtube-music.png?asset';
const userData = app.getPath('userData');
const temporaryIcon = path.join(userData, 'tempIcon.png');
const temporaryBanner = path.join(userData, 'tempBanner.png');
@ -45,7 +46,7 @@ const nativeImageToLogo = cache((nativeImage: NativeImage) => {
export const notificationImage = (songInfo: SongInfo) => {
if (!songInfo.image) {
return defaultIcon;
return youtubeMusicIcon;
}
if (!config.get('interactive')) {
@ -68,8 +69,9 @@ export const saveImage = cache((img: NativeImage, savePath: string) => {
try {
fs.writeFileSync(savePath, img.toPNG());
} catch (error: unknown) {
console.log(`Error writing song icon to disk:\n${String(error)}`);
return defaultIcon;
console.error('Error writing song icon to disk:');
console.trace(error);
return youtubeMusicIcon;
}
return savePath;

View File

@ -1,7 +1,7 @@
import { toKeyEvent } from 'keyboardevent-from-electron-accelerator';
import keyEventAreEqual from 'keyboardevents-areequal';
import pipHTML from './templates/picture-in-picture.html';
import pipHTML from './templates/picture-in-picture.html?raw';
import { getSongMenu } from '../../providers/dom-elements';

View File

@ -1,4 +1,4 @@
import sliderHTML from './templates/slider.html';
import sliderHTML from './templates/slider.html?raw';
import { getSongMenu } from '../../providers/dom-elements';
import { ElementFromHtml } from '../utils-renderer';

View File

@ -1,4 +1,4 @@
import qualitySettingsTemplate from './templates/qualitySettingsTemplate.html';
import qualitySettingsTemplate from './templates/qualitySettingsTemplate.html?raw';
import { ElementFromHtml } from '../utils-renderer';
import { YoutubePlayer } from '../../types/youtube-player';

View File

@ -4,10 +4,9 @@ import path from 'node:path';
import { app } from 'electron';
import is from 'electron-is';
import { ValueOf } from '../utils/type-utils';
import defaultConfig from '../config/defaults';
export const getAssetsDirectoryLocation = () => path.resolve(__dirname, 'assets');
export const getAssetsDirectoryLocation = () => path.resolve(__dirname, '..', 'assets');
export const getMediaIconLocation = () =>
app.isPackaged

View File

@ -1,4 +1,4 @@
import buttonTemplate from './templates/button_template.html';
import buttonTemplate from './templates/button_template.html?raw';
import { ElementFromHtml } from '../utils-renderer';

View File

@ -48,3 +48,4 @@ contextBridge.exposeInMainWorld('ipcRenderer', {
sendToHost: (channel: string, ...args: unknown[]) => ipcRenderer.sendToHost(channel, ...args),
});
contextBridge.exposeInMainWorld('reload', () => ipcRenderer.send('reload'));
contextBridge.exposeInMainWorld('ELECTRON_RENDERER_URL', process.env.ELECTRON_RENDERER_URL);

View File

@ -1,12 +1,8 @@
import path from 'node:path';
import { getAssetsDirectoryLocation } from '../plugins/utils';
const iconPath = path.join(getAssetsDirectoryLocation(), 'youtube-music-tray.png');
import youtubeMusicTrayIcon from '../../assets/youtube-music-tray.png?asset';
const promptOptions = {
customStylesheet: 'dark',
icon: iconPath,
icon: youtubeMusicTrayIcon,
};
export default () => promptOptions;

View File

@ -145,7 +145,8 @@ function onApiLoaded() {
try {
await handler?.(options as never);
} catch (error) {
console.error(`Error in plugin "${pluginName}": ${String(error)}`);
console.error(`Error in plugin "${pluginName}"`);
console.trace(error);
}
}
});

1
src/reset.d.ts vendored
View File

@ -21,6 +21,7 @@ declare global {
ipcRenderer: typeof electronIpcRenderer;
mainConfig: typeof config;
electronIs: typeof is;
ELECTRON_RENDERER_URL: string | undefined;
/**
* YouTube Music internal variable (Last interaction time)
*/

View File

@ -1,12 +1,10 @@
import path from 'node:path';
import { Menu, nativeImage, Tray } from 'electron';
import { restart } from './providers/app-controls';
import config from './config';
import getSongControls from './providers/song-controls';
import { getAssetsDirectoryLocation } from './plugins/utils';
import youtubeMusicTrayIcon from '../assets/youtube-music-tray.png?asset';
import type { MenuTemplate } from './menu';
@ -41,9 +39,8 @@ export const setUpTray = (app: Electron.App, win: Electron.BrowserWindow) => {
}
const { playPause, next, previous } = getSongControls(win);
const iconPath = path.join(getAssetsDirectoryLocation(), 'youtube-music-tray.png');
const trayIcon = nativeImage.createFromPath(iconPath).resize({
const trayIcon = nativeImage.createFromPath(youtubeMusicTrayIcon).resize({
width: 16,
height: 16,
});

View File

@ -1,12 +1,19 @@
/// <reference types="electron-vite/node" />
declare module '*.html' {
const html: string;
export default html;
}
declare module '*.svg' {
const element: SVGAElement;
declare module '*.html?raw' {
const html: string;
export default element;
export default html;
}
declare module '*.svg?inline' {
const base64: string;
export default base64;
}
declare module '*.png' {
const element: HTMLImageElement;
@ -15,7 +22,6 @@ declare module '*.png' {
}
declare module '*.jpg' {
const element: HTMLImageElement;
export default element;
}
declare module '*.css' {
@ -23,15 +29,3 @@ declare module '*.css' {
export default css;
}
declare module 'rollup-plugin-string' {
import type { Plugin } from 'rollup';
interface PluginOptions {
include?: string[] | string;
exclude?: string[] | string;
minifier?: unknown;
}
export function string(options?: PluginOptions): Plugin;
}