Switch to esbuild

This commit is contained in:
JannisX11 2025-03-02 21:16:55 +01:00
parent 5e30b97912
commit 56d6cbfa52
21 changed files with 250 additions and 1474 deletions

View File

@ -1,23 +0,0 @@
import fs from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import { createRequire } from 'node:module'
import { spawn } from 'node:child_process'
const pkg = createRequire(import.meta.url)('../package.json')
const __dirname = path.dirname(fileURLToPath(import.meta.url))
// write .debug.env
const envContent = Object.entries(pkg.debug.env).map(([key, val]) => `${key}=${val}`)
fs.writeFileSync(path.join(__dirname, '.debug.env'), envContent.join('\n'))
// bootstrap
spawn(
// TODO: terminate `npm run dev` when Debug exits.
process.platform === 'win32' ? 'npm.cmd' : 'npm',
['run', 'dev'],
{
stdio: 'inherit',
env: Object.assign(process.env, { VSCODE_DEBUG: 'true' }),
},
)

53
.vscode/launch.json vendored
View File

@ -1,53 +0,0 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"compounds": [
{
"name": "Debug App",
"preLaunchTask": "Before Debug",
"configurations": [
"Debug Main Process",
"Debug Renderer Process"
],
"presentation": {
"hidden": false,
"group": "",
"order": 1
},
"stopAll": true
}
],
"configurations": [
{
"name": "Debug Main Process",
"type": "node",
"request": "launch",
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron",
"windows": {
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd"
},
"runtimeArgs": [
"--remote-debugging-port=9229",
"."
],
"envFile": "${workspaceFolder}/.vscode/.debug.env",
"console": "integratedTerminal"
},
{
"name": "Debug Renderer Process",
"port": 9229,
"request": "attach",
"type": "chrome",
"timeout": 60000,
"skipFiles": [
"<node_internals>/**",
"${workspaceRoot}/node_modules/**",
"${workspaceRoot}/dist-electron/**",
// Skip files in host(VITE_DEV_SERVER_URL)
"http://127.0.0.1:3344/**"
]
},
]
}

24
.vscode/tasks.json vendored
View File

@ -10,30 +10,6 @@
"kind": "build",
"isDefault": true
}
},
{
"label": "Before Debug",
"type": "shell",
"command": "node .vscode/.debug.script.mjs",
"isBackground": true,
"problemMatcher": {
"owner": "typescript",
"fileLocation": "relative",
"pattern": {
// TODO: correct "regexp"
"regexp": "^([a-zA-Z]\\:\/?([\\w\\-]\/?)+\\.\\w+):(\\d+):(\\d+): (ERROR|WARNING)\\: (.*)$",
"file": 1,
"line": 3,
"column": 4,
"code": 5,
"message": 6
},
"background": {
"activeOnStart": true,
"beginsPattern": "^.*VITE v.* ready in \\d* ms.*$",
"endsPattern": "^.*\\[startup\\] Electron App.*$"
}
}
}
]
}

56
build.js Normal file
View File

@ -0,0 +1,56 @@
import * as esbuild from 'esbuild'
import { createRequire } from "module";
import commandLineArgs from 'command-line-args'
import path from 'path';
const pkg = createRequire(import.meta.url)("./package.json");
const options = commandLineArgs([
{name: 'target', type: String},
{name: 'watch', type: Boolean}
])
function conditionalImportPlugin(config) {
return {
name: 'conditional-import-plugin',
setup(build) {
build.onResolve({ filter: /desktop.js$/ }, args => {
return { path: path.join(args.resolveDir, config.file) };
});
}
};
};
let isApp = options.target == 'electron';
const config = {
entryPoints: ['./js/main.js'],
define: {
isApp: isApp.toString(),
appVersion: `"${pkg.version}"`,
},
platform: 'node',
target: 'es2020',
format: 'esm',
bundle: true,
outfile: './dist/bundle.js',
external: [
'electron',
],
plugins: [
conditionalImportPlugin({
file: isApp ? 'desktop.js' : 'web.js'
})
],
sourcemap: true,
}
if (options.watch) {
async function watch() {
let ctx = await esbuild.context(config);
await ctx.watch({});
console.log('Watching files')
}
watch();
} else {
await esbuild.build(config);
}

View File

@ -1,23 +0,0 @@
/// <reference types="vite-plugin-electron/electron-env" />
declare namespace NodeJS {
interface ProcessEnv {
VSCODE_DEBUG?: 'true'
/**
* The built directory structure
*
* ```tree
* dist-electron
* main
* index.js > Electron-Main
* preload
* index.mjs > Preload-Scripts
* dist
* index.html > Electron-Renderer
* ```
*/
APP_ROOT: string
/** /dist/ or /public/ */
VITE_PUBLIC: string
}
}

View File

@ -30,20 +30,6 @@ let load_project_data;
}
})()
process.env.APP_ROOT = path.join(__dirname, '../..')
export const MAIN_DIST = path.join(process.env.APP_ROOT, 'dist-electron')
export const RENDERER_DIST = path.join(process.env.APP_ROOT, 'dist-vite')
export const VITE_DEV_SERVER_URL = process.env.VITE_DEV_SERVER_URL
process.env.VITE_PUBLIC = VITE_DEV_SERVER_URL ? path.join(process.env.APP_ROOT, 'public') : RENDERER_DIST
const preload = path.join(__dirname, '../preload/index.mjs')
const indexHtml = path.join(RENDERER_DIST, 'index.html')
const LaunchSettings = {
path: path.join(app.getPath('userData'), 'launch_settings.json'),
settings: {},
@ -103,6 +89,7 @@ function createWindow(second_instance, options = {}) {
remote.enable(win.webContents)
var index_path = path.join(__dirname, './../index.html')
if (process.platform === 'darwin') {
let template = [
@ -177,40 +164,11 @@ function createWindow(second_instance, options = {}) {
if (options.maximize !== false) win.maximize()
win.show()
if (VITE_DEV_SERVER_URL) { // #298
win.loadURL(VITE_DEV_SERVER_URL)
// Open devTool if the app is not packaged
win.webContents.openDevTools()
} else {
win.loadFile(indexHtml)
}
// Test actively push message to the Electron-Renderer
win.webContents.on('did-finish-load', () => {
win?.webContents.send('main-process-message', new Date().toLocaleString())
})
// Make all links open with the browser, not with the application
win.webContents.setWindowOpenHandler(({ url }) => {
if (url.startsWith('https:')) shell.openExternal(url)
return { action: 'deny' }
})
/*win.webContents.session.webRequest.onBeforeSendHeaders(
(details, callback) => {
callback({ requestHeaders: { Origin: '*', ...details.requestHeaders } });
},
);
win.webContents.session.webRequest.onHeadersReceived((details, callback) => {
callback({
responseHeaders: {
'Access-Control-Allow-Origin': ['*'],
...details.responseHeaders,
},
});
});*/
win.loadURL(url.format({
pathname: index_path,
protocol: 'file:',
slashes: true
}))
win.webContents.openDevTools()
win.on('closed', () => {
win = null;

View File

@ -1,118 +0,0 @@
import { ipcRenderer, contextBridge } from 'electron'
// --------- Expose some API to the Renderer process ---------
contextBridge.exposeInMainWorld('ipcRenderer', {
on(...args: Parameters<typeof ipcRenderer.on>) {
const [channel, listener] = args
return ipcRenderer.on(channel, (event, ...args) => listener(event, ...args))
},
off(...args: Parameters<typeof ipcRenderer.off>) {
const [channel, ...omit] = args
return ipcRenderer.off(channel, ...omit)
},
send(...args: Parameters<typeof ipcRenderer.send>) {
const [channel, ...omit] = args
return ipcRenderer.send(channel, ...omit)
},
invoke(...args: Parameters<typeof ipcRenderer.invoke>) {
const [channel, ...omit] = args
return ipcRenderer.invoke(channel, ...omit)
},
// You can expose other APTs you need here.
// ...
})
// --------- Preload scripts loading ---------
function domReady(condition: DocumentReadyState[] = ['complete', 'interactive']) {
return new Promise((resolve) => {
if (condition.includes(document.readyState)) {
resolve(true)
} else {
document.addEventListener('readystatechange', () => {
if (condition.includes(document.readyState)) {
resolve(true)
}
})
}
})
}
const safeDOM = {
append(parent: HTMLElement, child: HTMLElement) {
if (!Array.from(parent.children).find(e => e === child)) {
return parent.appendChild(child)
}
},
remove(parent: HTMLElement, child: HTMLElement) {
if (Array.from(parent.children).find(e => e === child)) {
return parent.removeChild(child)
}
},
}
/**
* https://tobiasahlin.com/spinkit
* https://connoratherton.com/loaders
* https://projects.lukehaas.me/css-loaders
* https://matejkustec.github.io/SpinThatShit
*/
function useLoading() {
const className = `loaders-css__square-spin`
const styleContent = `
@keyframes square-spin {
25% { transform: perspective(100px) rotateX(180deg) rotateY(0); }
50% { transform: perspective(100px) rotateX(180deg) rotateY(180deg); }
75% { transform: perspective(100px) rotateX(0) rotateY(180deg); }
100% { transform: perspective(100px) rotateX(0) rotateY(0); }
}
.${className} > div {
animation-fill-mode: both;
width: 50px;
height: 50px;
background: #fff;
animation: square-spin 3s 0s cubic-bezier(0.09, 0.57, 0.49, 0.9) infinite;
}
.app-loading-wrap {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background: #282c34;
z-index: 9;
}
`
const oStyle = document.createElement('style')
const oDiv = document.createElement('div')
oStyle.id = 'app-loading-style'
oStyle.innerHTML = styleContent
oDiv.className = 'app-loading-wrap'
oDiv.innerHTML = `<div class="${className}"><div></div></div>`
return {
appendLoading() {
safeDOM.append(document.head, oStyle)
safeDOM.append(document.body, oDiv)
},
removeLoading() {
safeDOM.remove(document.head, oStyle)
safeDOM.remove(document.body, oDiv)
},
}
}
// ----------------------------------------------------------------------
const { appendLoading, removeLoading } = useLoading()
domReady().then(appendLoading)
window.onmessage = (ev) => {
ev.data.payload === 'removeLoading' && removeLoading()
}
setTimeout(removeLoading, 4999)

View File

@ -62,7 +62,7 @@
error_element.innerHTML = `Incompatible browser version. Please update your web browser.`
}
</script>
<script type="module" src="/js/main.js"></script>
<script type="module" src="dist/bundle.js"></script>
<script>if (window.module) module = window.module;</script>
<div style="display: none;"></div>

View File

@ -971,7 +971,6 @@ BARS.defineActions(() => {
}
})
})
console.log('a')
Object.assign(window, {
GeneralAnimator,

View File

@ -1,4 +1,3 @@
export const LastVersion = localStorage.getItem('last_version') || localStorage.getItem('welcomed_version') || appVersion;
export const Blockbench = {
@ -25,7 +24,7 @@ export const Blockbench = {
Blockbench.setProgress(0)
Blockbench.addFlag('allow_closing')
Blockbench.addFlag('allow_reload')
currentwindow.reload()
location.reload()
} else {
location.reload()
}
@ -411,5 +410,6 @@ export const StateMemory = {
Object.assign(window, {
LastVersion,
Blockbench,
StateMemory
StateMemory,
isApp
});

View File

@ -1,8 +1,6 @@
import { Blockbench } from "./api";
import { ipcRenderer } from "./desktop.electron";
import { loadInstalledPlugins } from "./plugin_loader";
import { animate } from "./preview/preview";
import { initializeWebApp, loadInfoFromURL } from "./web.browser";
Interface.page_wrapper = document.getElementById('page_wrapper');
Interface.work_screen = document.getElementById('work_screen');

View File

@ -755,7 +755,12 @@ ipcRenderer.on('update-available', (event, arg) => {
Object.assign(window, {
electron,
clipboard, shell, nativeImage, ipcRenderer, dialog, webUtils,
clipboard,
shell,
nativeImage,
ipcRenderer,
dialog,
webUtils,
app,
fs,
NodeBuffer,

View File

@ -35,8 +35,8 @@ import "./interface/themes"
import "./interface/shared_actions"
import "./interface/keyboard"
import "./misc"
import "./modes"
import "./api"
import "./modes"
import "./file_system"
import "./interface/vue_components"
import "./interface/panels"
@ -51,8 +51,9 @@ import "./interface/about"
import "./interface/action_control"
import "./copy_paste"
import "./undo"
import './desktop.electron'
import './web.browser'
import './desktop.js';
import "./edit_sessions"
import "./validator"
import "./outliner/outliner"

View File

@ -1667,5 +1667,6 @@ BARS.defineActions(function() {
Object.assign(window, {
CubeFace,
Cube
Cube,
adjustFromAndToForInflateAndStretch
});

View File

@ -111,7 +111,7 @@ export function trimFloatNumber(val, max_digits = 4) {
if (val == '') return val;
var string = val.toFixed(max_digits)
string = string.replace(/0+$/g, '').replace(/\.$/g, '')
if (string == -0) return 0;
if (string === -0) return 0;
return string;
}
export function separateThousands(number) {

View File

@ -333,11 +333,11 @@ Date.prototype.dayOfYear = function() {
//Object
export function omitKeys(obj, keys, dual_level) {
var dup = {};
for (key in obj) {
for (let key in obj) {
if (keys.indexOf(key) == -1) {
if (dual_level === true && typeof obj[key] === 'object') {
dup[key] = {}
for (key2 in obj[key]) {
for (let key2 in obj[key]) {
if (keys.indexOf(key2) == -1) {
dup[key][key2] = obj[key][key2];
}

View File

@ -158,3 +158,8 @@ window.onbeforeunload = function() {
if (Project.EditSession) Project.EditSession.quit()
}
}
Object.assign(window, {
initializeWebApp,
loadInfoFromURL
})

1137
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -14,11 +14,6 @@
},
"main": "electron/main.js",
"type": "module",
"debug": {
"env": {
"VITE_DEV_SERVER_URL": "http://127.0.0.1:3344/"
}
},
"build": {
"afterSign": "scripts/notarize.js",
"appId": "blockbench",
@ -120,27 +115,25 @@
]
},
"scripts": {
"dev": "vite --config vite.electron.config.js",
"dev-web": "vite --config vite.web.config.js",
"build-web": "vite build --config vite.web.config.js",
"build-electron": "vite build --config vite.web.electron.js",
"preview": "vite preview --config vite.web.config.js",
"dev": "concurrently --raw \"electron .\" \"node ./build.js --target=electron --watch\"",
"dev-web": "node ./build.js --target=web --serve",
"build-web": "node ./build.js --target=web",
"build-electron": "node ./build.js --target=electron",
"build-beta": "npm run build-electron && electron-builder --windows portable",
"publish-windows": "npm run build-electron && electron-builder -w --publish=onTagOrDraft && node ./scripts/rename_portable.js && electron-builder --windows portable --publish=onTagOrDraft",
"prepublish": "npm run build-web",
"prepublish-beta": "node ./scripts/enable_beta.js && npm run build-web",
"pwa": "node ./scripts/generate_pwa.js",
"prepublish": "npm run build-web && npm run pwa",
"prepublish-beta": "node ./scripts/enable_beta.js && npm run build-web && npm run pwa",
"webapp": "git checkout gh-pages && git pull && git merge master && git push && git checkout master"
},
"devDependencies": {
"@electron/notarize": "^2.5.0",
"command-line-args": "^6.0.1",
"concurrently": "^9.1.2",
"electron": "^33.3.1",
"electron-builder": "^24.13.3",
"esbuild": "0.25.0",
"typescript": "^5.8.2",
"vite": "^6.2.0",
"vite-plugin-conditional-import": "0.1.7",
"vite-plugin-electron": "^0.29.0",
"vite-plugin-electron-renderer": "^0.14.6",
"vite-plugin-pwa": "^0.21.1",
"workbox-build": "^6.5.3"
},
"dependencies": {

View File

@ -1,83 +0,0 @@
import fs from 'node:fs'
import { defineConfig } from 'vite'
import electron from 'vite-plugin-electron/simple'
import conditionalImportPlugin from "vite-plugin-conditional-import";
import pkg from './package.json'
// import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig(({ command, mode }) => {
fs.rmSync('dist-electron', { recursive: true, force: true })
const isServe = command === 'serve'
const isBuild = command === 'build'
const sourcemap = isServe || !!process.env.VSCODE_DEBUG
return {
build: {
outDir: './dist-vite'
},
define: {
isApp: true,
appVersion: '"'+pkg.version+'"'
},
plugins: [
// vue(),
conditionalImportPlugin({
currentEnv: "electron",
envs: ["electron", "web"],
}),
electron({
main: {
// Shortcut of `build.lib.entry`
entry: 'electron/main.js',
onstart({ startup }) {
if (process.env.VSCODE_DEBUG) {
console.log(/* For `.vscode/.debug.script.mjs` */'[startup] Electron App')
} else {
startup()
}
},
vite: {
build: {
sourcemap,
minify: isBuild,
outDir: 'dist-electron/main',
rollupOptions: {
// Some third-party Node.js libraries may not be built correctly by Vite, especially `C/C++` addons,
// we can use `external` to exclude them to ensure they work correctly.
// Others need to put them in `dependencies` to ensure they are collected into `app.asar` after the app is built.
// Of course, this is not absolute, just this way is relatively simple. :)
external: Object.keys('dependencies' in pkg ? pkg.dependencies : {}),
},
},
},
},
preload: {
// Shortcut of `build.rollupOptions.input`.
// Preload scripts may contain Web assets, so use the `build.rollupOptions.input` instead `build.lib.entry`.
input: 'electron/preload/index.ts',
vite: {
build: {
sourcemap: sourcemap ? 'inline' : undefined, // #332
minify: isBuild,
outDir: 'dist-electron/preload',
rollupOptions: {
external: Object.keys('dependencies' in pkg ? pkg.dependencies : {}),
},
},
},
},
// Ployfill the Electron and Node.js API for Renderer process.
// If you want use Node.js in Renderer process, the `nodeIntegration` needs to be enabled in the Main process.
// See 👉 https://github.com/electron-vite/vite-plugin-electron-renderer
renderer: {},
}),
],
server: {
cors: false,
},
clearScreen: false,
}
})

View File

@ -1,87 +0,0 @@
import { defineConfig } from 'vite'
import { VitePWA } from 'vite-plugin-pwa'
import conditionalImportPlugin from "vite-plugin-conditional-import";
import pkg from './package.json'
// import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig(({ command, mode }) => {
return {
build: {
outDir: './dist-vite'
},
define: {
isApp: false,
appVersion: '"'+pkg.version+'"'
},
plugins: [
// vue(),
conditionalImportPlugin({
currentEnv: "browser",
envs: ["electron", "browser"],
}),
VitePWA({
registerType: 'prompt',
manifest: {
"short_name": "Blockbench",
"name": "Blockbench",
"icons": [
{
"src": "favicon.png",
"type": "image/png",
"sizes": "128x128"
},
{
"src": "icon.png",
"type": "image/png",
"sizes": "1024x1024"
},
{
"src": "icon_maskable.png",
"type": "image/png",
"sizes": "256x256",
"purpose": "maskable"
}
],
"screenshots": [
{
"src": "content/front_page_app.png",
"sizes": "1920x1040",
"type": "image/png",
"label": "Blockbench Interface"
}
],
"start_url": "./index.html",
"background_color": "#21252b",
"theme_color": "#3e90ff",
"display": "standalone",
"display_override": ["window-controls-overlay"],
"orientation": "any"
},
manifestFilename: './manifest.webmanifest',
workbox: {
cacheId: 'blockbench',
globDirectory: './',
globPatterns: [
'./index.html',
'./favicon.png',
'./icon_maskable.png',
'./js/**/*',
'./bundle.js',
'./lib/**/*',
'./css/**/*',
'./assets/**/*',
'./font/*',
],
swDest: './service_worker.js',
maximumFileSizeToCacheInBytes: 4_096_000,
sourcemap: false
}
}),
],
clearScreen: false,
}
})