mirror of
https://github.com/HeyPuter/puter
synced 2024-11-14 14:03:42 +00:00
dev: bundle gui in dev
This commit is contained in:
parent
0d01d1f800
commit
8ad435a0ea
8
package-lock.json
generated
8
package-lock.json
generated
@ -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/*"
|
||||
|
@ -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',
|
||||
|
@ -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() {
|
||||
|
81
src/gui/doc/webpack_attempts.md
Normal file
81
src/gui/doc/webpack_attempts.md
Normal 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'
|
||||
// };
|
||||
// });
|
||||
```
|
@ -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/*"
|
@ -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');
|
||||
|
@ -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
|
||||
|
8
src/gui/webpack.config.cjs
Normal file
8
src/gui/webpack.config.cjs
Normal file
@ -0,0 +1,8 @@
|
||||
const BaseConfig = require('./webpack/BaseConfig.cjs');
|
||||
|
||||
module.exports = {
|
||||
...BaseConfig(),
|
||||
optimization: {
|
||||
minimize: false
|
||||
},
|
||||
};
|
27
src/gui/webpack/BaseConfig.cjs
Normal file
27
src/gui/webpack/BaseConfig.cjs
Normal 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;
|
||||
};
|
78
src/gui/webpack/EmitPlugin.cjs
Normal file
78
src/gui/webpack/EmitPlugin.cjs
Normal 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();
|
||||
});
|
||||
};
|
15
src/gui/webpack/libPaths.cjs
Normal file
15
src/gui/webpack/libPaths.cjs
Normal 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"
|
||||
];
|
Loading…
Reference in New Issue
Block a user