mirror of
https://github.com/podman-desktop/podman-desktop
synced 2026-04-21 17:47:22 +00:00
main, preload, renderer, etc depend on api package move api first so it may avoid empty folder fixes https://github.com/podman-desktop/podman-desktop/issues/16287 Signed-off-by: Florent Benoit <fbenoit@redhat.com>
310 lines
10 KiB
JavaScript
310 lines
10 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
/**********************************************************************
|
|
* Copyright (C) 2022-2026 Red Hat, Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
***********************************************************************/
|
|
|
|
import { createServer, build, createLogger } from 'vite';
|
|
import electronPath from 'electron';
|
|
import { spawn } from 'child_process';
|
|
import { generateAsync } from 'dts-for-context-bridge';
|
|
import { dirname, resolve } from 'path';
|
|
import { fileURLToPath } from 'url';
|
|
import { readdirSync, existsSync } from 'node:fs';
|
|
import { delimiter, join } from 'node:path';
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
|
|
/** @type 'production' | 'development'' */
|
|
const mode = (process.env.MODE = process.env.MODE || 'development');
|
|
|
|
/** @type {import('vite').LogLevel} */
|
|
const LOG_LEVEL = 'info';
|
|
|
|
/** @type {import('vite').InlineConfig} */
|
|
const sharedConfig = {
|
|
mode,
|
|
build: {
|
|
watch: {},
|
|
},
|
|
logLevel: LOG_LEVEL,
|
|
};
|
|
|
|
/** Messages on stderr that match any of the contained patterns will be stripped from output */
|
|
const stderrFilterPatterns = [
|
|
// warning about devtools extension
|
|
// https://github.com/cawa-93/vite-electron-builder/issues/492
|
|
// https://github.com/MarshallOfSound/electron-devtools-installer/issues/143
|
|
/ExtensionLoadWarning/,
|
|
];
|
|
|
|
/**
|
|
* @param {{name: string; configFile: string; writeBundle: import('rollup').OutputPlugin['writeBundle'] }} param0
|
|
*/
|
|
const getWatcher = ({ name, configFile, writeBundle }) => {
|
|
return build({
|
|
...sharedConfig,
|
|
configFile,
|
|
plugins: [{ name, writeBundle }],
|
|
});
|
|
};
|
|
|
|
const EXTENSION_OPTION = '--extension-folder';
|
|
|
|
/**
|
|
* Start or restart App when source files are changed
|
|
* @param {{config: {server: import('vite').ResolvedServerOptions}}} ResolvedServerOptions
|
|
*/
|
|
const setupMainPackageWatcher = ({ config: { server, extensions } }) => {
|
|
// Create VITE_DEV_SERVER_URL environment variable to pass it to the main process.
|
|
{
|
|
const protocol = server.https ? 'https:' : 'http:';
|
|
const host = server.host || 'localhost';
|
|
const port = server.port; // Vite searches for and occupies the first free port: 3000, 3001, 3002 and so on
|
|
const path = '/';
|
|
process.env.VITE_DEV_SERVER_URL = `${protocol}//${host}:${port}${path}`;
|
|
}
|
|
|
|
const logger = createLogger(LOG_LEVEL, {
|
|
prefix: '[main]',
|
|
});
|
|
|
|
/** @type {ChildProcessWithoutNullStreams | null} */
|
|
let spawnProcess = null;
|
|
|
|
return getWatcher({
|
|
name: 'reload-app-on-main-package-change',
|
|
configFile: 'packages/main/vite.config.js',
|
|
writeBundle() {
|
|
if (spawnProcess !== null) {
|
|
spawnProcess.off('exit', process.exit);
|
|
spawnProcess.kill('SIGINT');
|
|
spawnProcess = null;
|
|
}
|
|
|
|
const extensionArgs = [];
|
|
extensions.forEach(extension => {
|
|
extensionArgs.push(EXTENSION_OPTION);
|
|
extensionArgs.push(extension);
|
|
});
|
|
spawnProcess = spawn(String(electronPath), ['--remote-debugging-port=9223', '.', ...extensionArgs], {
|
|
env: { ...process.env, ELECTRON_IS_DEV: 1 },
|
|
});
|
|
|
|
spawnProcess.stdout.on('data', d => d.toString().trim() && logger.warn(d.toString(), { timestamp: true }));
|
|
spawnProcess.stderr.on('data', d => {
|
|
const data = d.toString().trim();
|
|
if (!data) return;
|
|
const mayIgnore = stderrFilterPatterns.some(r => r.test(data));
|
|
if (mayIgnore) return;
|
|
logger.error(data, { timestamp: true });
|
|
});
|
|
|
|
// Stops the watch script when the application has been quit
|
|
spawnProcess.on('exit', process.exit);
|
|
},
|
|
});
|
|
};
|
|
|
|
const setupUiPackageWatcher = () => {
|
|
const logger = createLogger(LOG_LEVEL, {
|
|
prefix: '[ui]',
|
|
});
|
|
|
|
/** @type {ChildProcessWithoutNullStreams | null} */
|
|
let spawnProcess = null;
|
|
|
|
if (spawnProcess !== null) {
|
|
spawnProcess.off('exit', process.exit);
|
|
spawnProcess.kill('SIGINT');
|
|
spawnProcess = null;
|
|
}
|
|
|
|
const dirname = join(__dirname, '..', 'node_modules', '.bin');
|
|
const exe = 'svelte-package'.concat(process.platform === 'win32' ? '.cmd' : '');
|
|
const newPath = `${process.env.PATH}${delimiter}${dirname}`;
|
|
spawnProcess = spawn(exe, ['-w'], {
|
|
cwd: './packages/ui/',
|
|
env: { PATH: newPath, ...process.env },
|
|
shell: process.platform === 'win32',
|
|
});
|
|
|
|
spawnProcess.stdout.on('data', d => d.toString().trim() && logger.warn(d.toString(), { timestamp: true }));
|
|
spawnProcess.stderr.on('data', d => {
|
|
const data = d.toString().trim();
|
|
if (!data) return;
|
|
const mayIgnore = stderrFilterPatterns.some(r => r.test(data));
|
|
if (mayIgnore) return;
|
|
logger.error(data, { timestamp: true });
|
|
});
|
|
|
|
// Stops the watch script when the application has been quit
|
|
spawnProcess.on('exit', process.exit);
|
|
};
|
|
|
|
/**
|
|
* Start or restart App when source files are changed
|
|
* @param {{ws: import('vite').WebSocketServer}} WebSocketServer
|
|
*/
|
|
const setupCoreApiPackageWatcher = ({ ws }) =>
|
|
getWatcher({
|
|
name: 'reload-page-on-core-api-package-change',
|
|
configFile: 'packages/api/vite.config.js',
|
|
writeBundle() {
|
|
if (ws) {
|
|
ws.send({
|
|
type: 'full-reload',
|
|
});
|
|
}
|
|
},
|
|
});
|
|
|
|
/**
|
|
* Start or restart App when source files are changed
|
|
* @param {{ws: import('vite').WebSocketServer}} WebSocketServer
|
|
*/
|
|
const setupPreloadPackageWatcher = ({ ws }) =>
|
|
getWatcher({
|
|
name: 'reload-page-on-preload-package-change',
|
|
configFile: 'packages/preload/vite.config.js',
|
|
writeBundle() {
|
|
// Generating exposedInMainWorld.d.ts when preload package is changed.
|
|
generateAsync({
|
|
input: 'packages/preload/tsconfig.json',
|
|
output: 'packages/preload/exposedInMainWorld.d.ts',
|
|
});
|
|
if (ws) {
|
|
ws.send({
|
|
type: 'full-reload',
|
|
});
|
|
}
|
|
},
|
|
});
|
|
|
|
const setupPreloadDockerExtensionPackageWatcher = ({ ws }) =>
|
|
getWatcher({
|
|
name: 'reload-page-on-preload-docker-extension-package-change',
|
|
configFile: 'packages/preload-docker-extension/vite.config.js',
|
|
writeBundle() {
|
|
// Generating exposedInMainWorld.d.ts when preload package is changed.
|
|
generateAsync({
|
|
input: 'packages/preload-docker-extension/tsconfig.json',
|
|
output: 'packages/preload-docker-extension/exposedInDockerExtension.d.ts',
|
|
});
|
|
|
|
if (ws) {
|
|
ws.send({
|
|
type: 'full-reload',
|
|
});
|
|
}
|
|
},
|
|
});
|
|
|
|
const setupPreloadWebviewPackageWatcher = ({ ws }) =>
|
|
getWatcher({
|
|
name: 'reload-page-on-preload-webview-package-change',
|
|
configFile: 'packages/preload-webview/vite.config.js',
|
|
writeBundle() {
|
|
// Generating exposedInWebview.d.ts when preload package is changed.
|
|
generateAsync({
|
|
input: 'packages/preload-webview/tsconfig.json',
|
|
output: 'packages/preload-webview/exposedInWebview.d.ts',
|
|
});
|
|
|
|
if (ws) {
|
|
ws.send({
|
|
type: 'full-reload',
|
|
});
|
|
}
|
|
},
|
|
});
|
|
|
|
/**
|
|
* Start or restart App when source files are changed
|
|
* @param {{ws: import('vite').WebSocketServer}} WebSocketServer
|
|
*/
|
|
const setupExtensionApiWatcher = name => {
|
|
let spawnProcess;
|
|
const folderName = resolve(name);
|
|
|
|
console.log('dirname is', folderName);
|
|
spawnProcess = spawn('pnpm', ['watch'], { cwd: folderName, shell: process.platform === 'win32' });
|
|
|
|
spawnProcess.stdout.on('data', d => d.toString().trim() && console.warn(d.toString(), { timestamp: true }));
|
|
spawnProcess.stderr.on('data', d => {
|
|
const data = d.toString().trim();
|
|
if (!data) return;
|
|
console.error(data, { timestamp: true });
|
|
});
|
|
|
|
// Stops the watch script when the application has been quit
|
|
spawnProcess.on('exit', process.exit);
|
|
};
|
|
|
|
(async () => {
|
|
try {
|
|
const extensions = [];
|
|
for (let index = 0; index < process.argv.length; index++) {
|
|
if (process.argv[index] === EXTENSION_OPTION && index < process.argv.length - 1) {
|
|
extensions.push(resolve(process.argv[++index]));
|
|
}
|
|
}
|
|
const viteDevServer = await createServer({
|
|
...sharedConfig,
|
|
configFile: 'packages/renderer/vite.config.js',
|
|
extensions: extensions,
|
|
});
|
|
|
|
await viteDevServer.listen();
|
|
|
|
// get extensions folder
|
|
const extensionsFolder = resolve(__dirname, '../extensions/');
|
|
|
|
// Loop on all subfolders from the extensions folder.
|
|
// If package.json is present it is an extension without API.
|
|
// If package.json is missing look into packages/extension folder
|
|
// and if package.json is present it is na extension with API.
|
|
readdirSync(extensionsFolder, { withFileTypes: true })
|
|
.filter(
|
|
dirent =>
|
|
dirent.isDirectory() &&
|
|
(existsSync(join(extensionsFolder, dirent.name, 'package.json')) ||
|
|
existsSync(extensionsFolder, dirent.name, 'packages', 'extension', 'package.json')),
|
|
)
|
|
.forEach(dirent => {
|
|
const apiExtPath = join(extensionsFolder, dirent.name, 'packages', 'extension');
|
|
if (existsSync(join(apiExtPath, 'package.json'))) {
|
|
setupExtensionApiWatcher(apiExtPath);
|
|
} else if (existsSync(join(extensionsFolder, dirent.name, 'package.json'))) {
|
|
setupExtensionApiWatcher(join(extensionsFolder, dirent.name));
|
|
}
|
|
});
|
|
|
|
for (const extension of extensions) {
|
|
setupExtensionApiWatcher(extension);
|
|
}
|
|
await setupCoreApiPackageWatcher(viteDevServer);
|
|
await setupPreloadPackageWatcher(viteDevServer);
|
|
await setupPreloadDockerExtensionPackageWatcher(viteDevServer);
|
|
await setupPreloadWebviewPackageWatcher(viteDevServer);
|
|
await setupUiPackageWatcher();
|
|
await setupMainPackageWatcher(viteDevServer);
|
|
} catch (e) {
|
|
console.error(e);
|
|
process.exit(1);
|
|
}
|
|
})();
|