dev: bundle gui in dev

This commit is contained in:
KernelDeimos 2024-08-29 21:10:22 -04:00 committed by Eric Dubé
parent 0d01d1f800
commit 8ad435a0ea
11 changed files with 232 additions and 51 deletions

8
package-lock.json generated
View File

@ -2545,6 +2545,10 @@
"resolved": "src/backend",
"link": true
},
"node_modules/@heyputer/gui": {
"resolved": "src/gui",
"link": true
},
"node_modules/@heyputer/kv.js": {
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/@heyputer/kv.js/-/kv.js-0.1.6.tgz",
@ -16790,8 +16794,8 @@
}
},
"src/gui": {
"version": "2.3.0",
"extraneous": true,
"name": "@heyputer/gui",
"version": "2.4.0",
"license": "AGPL-3.0-only",
"workspaces": [
"src/*"

View File

@ -48,6 +48,12 @@ class SelfHostedModule extends AdvancedBase {
command: 'npm',
args: ['run', 'start-webpack'],
},
{
name: 'gui:webpack-watch',
directory: 'src/gui',
command: 'npm',
args: ['run', 'start-webpack'],
},
{
name: 'terminal:rollup-watch',
directory: 'src/terminal',

View File

@ -255,46 +255,9 @@ class PuterHomepageService extends BaseService {
? `<script>window.gui_env = 'prod';</script>`
: ''
}
${
((!bundled && manifest?.lib_paths)
? manifest.lib_paths.map(path => `<script type="text/javascript" src="${path}"></script>\n`)
: []).join('')
}
<script>
window.icons = {};
${(() => {
if ( !(!bundled && manifest) ) return '';
const html = [];
fs_.readdirSync(path_.join(gui_path, 'src/icons')).forEach(file => {
// skip dotfiles
if(file.startsWith('.'))
return;
// load image
let buff = new Buffer.from(fs_.readFileSync(path_.join(gui_path, 'src/icons') + '/' + file));
// convert to base64
let base64data = buff.toString('base64');
// add to `window.icons`
if(file.endsWith('.png'))
html.push(`window.icons['${file}'] = "data:image/png;base64,${base64data}";\n`);
else if(file.endsWith('.svg'))
html.push(`window.icons['${file}'] = "data:image/svg+xml;base64,${base64data}";\n`);
})
return html.join('');
})()}
</script>
${
((!bundled && manifest?.js_paths)
? manifest.js_paths.map(path => writeScriptTag(path))
: []).join('')
}
<!-- Load the GUI script -->
<script ${
// !bundled ? ' type="module"' : ''
' type="module"'
} src="${(!bundled && manifest?.index) || '/dist/gui.js'}"></script>
<script src="/dist/gui.bundle.js"></script>
<!-- Initialize GUI when document is loaded -->
<script type="module">
window.addEventListener('load', function() {

View File

@ -0,0 +1,81 @@
Multiple things attempted when trying to add icons to the bundle.
None of this worked - eventually just prepended text on emit instead.
```javascript
// compilation.hooks.processAssets.tap(
// {
// name: 'AddImportPlugin',
// stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONS,
// },
// (assets) => {
// for (const assetName of Object.keys(assets)) {
// if (assetName.endsWith('.js')) {
// const source = assets[assetName].source();
// const newSource = `${icons}\n${source}`;
// compilation.updateAsset(assetName, new compiler.webpack.sources.RawSource(newSource));
// }
// }
// }
// );
// Inject into bundle
// console.log('adding this:' + icons);
// compilation.assets['icons-thing'] = {
// source: () => icons,
// size: () => icons.length,
// };
// compilation.addModule({
// identifier() {
// return 'icons-thing';
// },
// build() {
// this._source = {
// source() {
// return content;
// },
// size() {
// return content.length;
// }
// };
// }
// });
// Add the generated module to Webpack's internal modules
// compilation.hooks.optimizeModules.tap('IconsPlugin', (modules) => {
// const virtualModule = {
// identifier: () => 'icons.js',
// readableIdentifier: () => 'icons.js',
// build: () => {},
// source: () => icons,
// size: () => icons.length,
// chunks: [],
// assets: [],
// hash: () => 'icons',
// };
// modules.push(virtualModule);
// });
});
// this.hooks.entryOption.tap('IconsPlugin', (context, entry) => {
// entry.main.import.push('icons-thing');
// });
// this.hooks.make.tapAsync('InjectTextEntryPlugin', (compilation, callback) => {
// // Create a new asset (fake module) from the generated content
// const content = `console.log('${this.options.text}');`;
// callback();
// });
// this.hooks.entryOption.tap('IconsPlugin', (context, entry) => {
// });
// this.hooks.entryOption.tap('InjectTextEntryPlugin', (context, entry) => {
// // Add this as an additional entry point
// this.options.entry = {
// ...this.options.entry,
// 'generated-entry': '// FINDME\n'
// };
// });
```

View File

@ -28,7 +28,8 @@
"test": "mocha ./packages/phoenix/test ./packages/phoenix/packages/contextlink/test",
"start=gui": "nodemon --exec \"node dev-server.js\" ",
"build": "node ./build.js",
"check-translations": "node tools/check-translations.js"
"check-translations": "node tools/check-translations.js",
"start-webpack": "webpack --watch --devtool source-map"
},
"workspaces": [
"src/*"

View File

@ -61,13 +61,15 @@ window.gui = async function(options){
// DEV: Load the initgui.js file if we are in development mode
if(!window.gui_env || window.gui_env === "dev"){
await window.loadScript('/sdk/puter.dev.js');
await window.loadScript(`${options.asset_dir}/initgui.js`, {isModule: true});
await window.loadScript('/putil.js/v1');
// await window.loadScript(`${options.asset_dir}/initgui.js`, {isModule: true});
}
// PROD: load the minified bundles if we are in production mode
// note: the order of the bundles is important
// note: Build script will prepend `window.gui_env="prod"` to the top of the file
else if(window.gui_env === "prod"){
await window.loadScript('https://js.puter.com/putil/v1/');
await window.loadScript('https://js.puter.com/v2/');
// Load the minified bundles
await window.loadCSS('/dist/bundle.min.css');

View File

@ -20,10 +20,12 @@ import { encode } from 'html-entities';
import fs from 'fs';
import path from 'path';
import webpack from 'webpack';
import webpack_config from './webpack.config.cjs';
import CleanCSS from 'clean-css';
import uglifyjs from 'uglify-js';
import { lib_paths, css_paths, js_paths } from './src/static-assets.js';
import { fileURLToPath } from 'url';
import BaseConfig from './webpack/BaseConfig.cjs';
// Polyfill __dirname, which doesn't exist in modules mode
const __dirname = path.dirname(fileURLToPath(import.meta.url));
@ -128,14 +130,8 @@ async function build(options){
main_array.push(path.join(__dirname, 'src', js_paths[i]));
}
webpack({
...BaseConfig(options),
mode: 'production',
entry: {
main: main_array,
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js',
},
optimization: {
minimize: true,
},
@ -147,9 +143,9 @@ async function build(options){
if(options?.verbose)
console.log(stats.toString());
// write to ./dist/bundle.min.js
fs.writeFileSync(path.join(__dirname, 'dist', 'bundle.min.js'), icons + '\n\n\n' + js + '\n\n\n' + fs.readFileSync(path.join(__dirname, 'dist', 'main.js')));
// fs.writeFileSync(path.join(__dirname, 'dist', 'bundle.min.js'), fs.readFileSync(path.join(__dirname, 'dist', 'main.js')));
// remove ./dist/main.js
fs.unlinkSync(path.join(__dirname, 'dist', 'main.js'));
// fs.unlinkSync(path.join(__dirname, 'dist', 'main.js'));
});
// Copy index.js to dist/gui.js

View File

@ -0,0 +1,8 @@
const BaseConfig = require('./webpack/BaseConfig.cjs');
module.exports = {
...BaseConfig(),
optimization: {
minimize: false
},
};

View File

@ -0,0 +1,27 @@
const path = require('path');
const EmitPlugin = require('./EmitPlugin.cjs');
module.exports = (options) => {
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',
];
config.output = {
path: path.resolve(__dirname, '../dist'),
filename: 'gui.bundle.js',
};
config.plugins = [
EmitPlugin({
options,
dir: path.join(__dirname, '../src/icons'),
}),
];
return config;
};

View File

@ -0,0 +1,78 @@
const fs = require('fs');
const path = require('path');
const uglifyjs = require('uglify-js');
module.exports = ({ dir, options }) => function () {
const compiler = this;
compiler.hooks.emit.tapAsync('EmitPlugin', async (compilation, callback) => {
let prefix_text = '';
prefix_text += 'window.gui_env="dev";\n';
// -----------------------------------------------
// Combine all images into a single js file
// -----------------------------------------------
{
let icons = 'window.icons = [];\n';
fs.readdirSync(dir).forEach(file => {
// skip dotfiles
if (file.startsWith('.'))
return;
// load image
let buff = new Buffer.from(fs.readFileSync(dir + '/' + file));
// convert to base64
let base64data = buff.toString('base64');
// add to `window.icons`
if (file.endsWith('.png'))
icons += `window.icons['${file}'] = "data:image/png;base64,${base64data}";\n`;
else if (file.endsWith('.svg'))
icons += `window.icons['${file}'] = "data:image/svg+xml;base64,${base64data}";\n`;
});
prefix_text += icons + '\n';
}
// -----------------------------------------------
// Concat/merge the JS libraries and save them to ./dist/libs.js
// -----------------------------------------------
{
const lib_paths = require('./libPaths.cjs');
let js = '';
for(let i = 0; i < lib_paths.length; i++){
const file = path.join(__dirname, '../src/lib/', lib_paths[i]);
// js
if(file.endsWith('.js') && !file.endsWith('.min.js')){
let minified_code = await uglifyjs.minify(fs.readFileSync(file).toString(), {mangle: false});
if(minified_code && minified_code.code){
js += minified_code.code;
if(options?.verbose)
console.log('minified: ', file);
}
}else{
js += fs.readFileSync(file);
if(options?.verbose)
console.log('skipped minification: ', file);
}
js += '\n\n\n';
}
prefix_text += js;
}
// -----------------------------------------------
// Webpack understands this code better than I do
// -----------------------------------------------
Object.keys(compilation.assets).forEach((assetName) => {
if (assetName.endsWith('.js')) {
const asset = compilation.assets[assetName];
const originalSource = asset.source();
const newSource = `${prefix_text}\n${originalSource}`;
compilation.assets[assetName] = {
source: () => newSource,
size: () => newSource.length,
};
}
});
console.log('END');
callback();
});
};

View File

@ -0,0 +1,15 @@
module.exports = [
"jquery-3.6.1/jquery-3.6.1.min.js",
"viselect.min.js",
"FileSaver.min.js",
"socket.io/socket.io.min.js",
"qrcode.min.js",
"jquery-ui-1.13.2/jquery-ui.min.js",
"lodash@4.17.21.min.js",
"jquery.dragster.js",
"html-entities.js",
"timeago.min.js",
"iro.min.js",
"isMobile.min.js",
"jszip-3.10.1.min.js"
];