mirror of
https://github.com/HeyPuter/puter.git
synced 2026-10-02 01:51:55 +00:00
- npm start --server=puter.com (or -- --server=...) skips the local backend and serves the bundled GUI locally, pointed at the remote server's API. Bare domains resolve to https://api.<domain>; full origins are used verbatim. gui_origin points at the remote origin so /whoarewe, login, anti-csrf, socket.io, and builtin apps hit the real backend (CORS-open). - --extensions=<dir>[;<dir>...] bundles out-of-tree GUI extension directories (sugar for PUTER_GUI_EXTENSION_PATHS). Their imports resolve as if the files lived in src/gui/src/extensions, with the extension's own files taking precedence, and bare imports fall back to the repo-root node_modules. - Fix the bit-rotted dev-server: Express 5 wildcard routes, pass gui() params (previously called with none), inject the service_script shim, load bundle.min.js + bundle.min.css in prod mode, serve /sdk, and properly await the webpack build (it previously resolved immediately).
117 lines
4.4 KiB
JavaScript
117 lines
4.4 KiB
JavaScript
/*
|
|
* Copyright (C) 2024-present Puter Technologies Inc.
|
|
*
|
|
* This file is part of Puter.
|
|
*
|
|
* Puter is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as published
|
|
* by the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
const path = require('path');
|
|
const fs = require('fs');
|
|
const webpack = require('webpack');
|
|
const EmitPlugin = require('./EmitPlugin.cjs');
|
|
|
|
module.exports = async (options = {}) => {
|
|
const extension_directories = [];
|
|
|
|
extension_directories.push(path.join(__dirname, '../src/extensions'));
|
|
|
|
// Out-of-tree extension directories (e.g. proprietary extensions kept in
|
|
// a separate repository).
|
|
const externalDirs = [];
|
|
if ( process.env.PUTER_GUI_EXTENSION_PATHS ) {
|
|
const paths = process.env.PUTER_GUI_EXTENSION_PATHS.split(';');
|
|
externalDirs.push(...paths.filter(Boolean).map(p => path.resolve(p)));
|
|
extension_directories.push(...externalDirs);
|
|
}
|
|
|
|
// Imports in out-of-tree extensions resolve as if the extension lived in
|
|
// src/extensions — so `../UI/UIAlert.js` reaches GUI internals the same
|
|
// way it does in-tree — while files that exist next to the extension
|
|
// (its own libs) still win.
|
|
const srcDir = path.join(__dirname, '../src');
|
|
const resolvesTo = (p) => fs.existsSync(p) || fs.existsSync(`${p}.js`);
|
|
const remapExternalImport = (resource) => {
|
|
const issuerDir = resource.context;
|
|
const root = externalDirs.find(dir =>
|
|
issuerDir === dir || issuerDir.startsWith(dir + path.sep));
|
|
if ( ! root ) return;
|
|
if ( resolvesTo(path.resolve(issuerDir, resource.request)) ) return;
|
|
const virtualDir = path.join(srcDir, 'extensions', path.relative(root, issuerDir));
|
|
const target = path.resolve(virtualDir, resource.request);
|
|
if ( resolvesTo(target) ) resource.request = target;
|
|
};
|
|
|
|
const entries = [];
|
|
|
|
for ( const extensionsDir of extension_directories ) {
|
|
if ( ! fs.existsSync(extensionsDir) ) continue;
|
|
// Read and process extension entries from the extensions directory
|
|
const readdir_entries = fs.readdirSync(extensionsDir, { withFileTypes: true });
|
|
for ( const entry of readdir_entries ) {
|
|
// Case 1: Direct JavaScript files in extensions directory
|
|
if ( entry.isFile() && entry.name.endsWith('.js') ) {
|
|
const entry_path = path.join(extensionsDir, entry.name);
|
|
entries.push(entry_path);
|
|
continue;
|
|
}
|
|
// Case 2: Extension directories with index.js files
|
|
if ( entry.isDirectory() ) {
|
|
const indexPath = path.join(extensionsDir, entry.name, 'index.js');
|
|
// Check if directory contains an index.js file
|
|
if ( fs.existsSync(indexPath) ) {
|
|
entries.push(indexPath);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const config = {};
|
|
config.entry = [
|
|
'./src/init_sync.js',
|
|
'./src/init_async.js',
|
|
'./src/initgui.js',
|
|
'./src/helpers.js',
|
|
'./src/IPC.js',
|
|
'./src/globals.js',
|
|
'./src/i18n/i18n.js',
|
|
'./src/keyboard.js',
|
|
'./src/index.js',
|
|
...entries,
|
|
];
|
|
config.output = {
|
|
path: path.resolve(__dirname, '../dist'),
|
|
filename: 'bundle.min.js',
|
|
};
|
|
config.resolve = {
|
|
modules: [
|
|
'node_modules',
|
|
// Hoisted workspace deps: bare imports in out-of-tree extensions
|
|
// can't reach this repo's node_modules by walking up from their
|
|
// own location.
|
|
path.join(__dirname, '../../../node_modules'),
|
|
],
|
|
};
|
|
config.plugins = [
|
|
...(externalDirs.length
|
|
? [new webpack.NormalModuleReplacementPlugin(/^\.\.?\//, remapExternalImport)]
|
|
: []),
|
|
await EmitPlugin({
|
|
options,
|
|
dir: path.join(__dirname, '../src/icons'),
|
|
}),
|
|
];
|
|
return config;
|
|
}; |