feat: Notify build events to browser
- Update assets_json directly from node - Show error overlay or success message - Open file in editor from error overlay
This commit is contained in:
parent
86c0f7f9d6
commit
adc236e35d
12 changed files with 319 additions and 26 deletions
|
|
@ -20,7 +20,9 @@ let {
|
|||
log,
|
||||
log_warn,
|
||||
log_error,
|
||||
bench_path
|
||||
} = require("./utils");
|
||||
let { get_redis_subscriber } = require("../node_utils");
|
||||
|
||||
let argv = yargs
|
||||
.usage("Usage: node esbuild [options]")
|
||||
|
|
@ -173,11 +175,19 @@ function build_files({ files, outdir }) {
|
|||
watch: WATCH_MODE
|
||||
? {
|
||||
onRebuild(error, result) {
|
||||
if (error) console.error("watch build failed:", error);
|
||||
else {
|
||||
if (error) {
|
||||
log_error(
|
||||
"There was an error during rebuilding changes."
|
||||
);
|
||||
log();
|
||||
log(chalk.dim(error.stack));
|
||||
notify_redis({ error });
|
||||
} else {
|
||||
console.log(
|
||||
`${new Date().toLocaleTimeString()}: Compiled changes...`
|
||||
);
|
||||
write_meta_file(result.metafile);
|
||||
notify_redis({ success: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -268,8 +278,64 @@ function write_meta_file(metafile) {
|
|||
}
|
||||
}
|
||||
|
||||
return fs.promises.writeFile(
|
||||
path.resolve(assets_path, "frappe", "dist", "assets.json"),
|
||||
JSON.stringify(out, null, 4)
|
||||
let assets_json = JSON.stringify(out, null, 4);
|
||||
return fs.promises
|
||||
.writeFile(
|
||||
path.resolve(assets_path, "frappe", "dist", "assets.json"),
|
||||
assets_json
|
||||
)
|
||||
.then(() => {
|
||||
let client = get_redis_subscriber("redis_cache");
|
||||
// update assets_json cache in redis, so that it can be read directly by python
|
||||
return client.set("assets_json", assets_json);
|
||||
});
|
||||
}
|
||||
|
||||
async function notify_redis({ error, success }) {
|
||||
let subscriber = get_redis_subscriber("redis_socketio");
|
||||
// notify redis which in turns tells socketio to publish this to browser
|
||||
|
||||
let payload = null;
|
||||
|
||||
if (error) {
|
||||
let formatted = await esbuild.formatMessages(error.errors, {
|
||||
kind: "error",
|
||||
terminalWidth: 100
|
||||
});
|
||||
let stack = error.stack.replace(new RegExp(bench_path, "g"), "");
|
||||
payload = {
|
||||
error,
|
||||
formatted,
|
||||
stack
|
||||
};
|
||||
}
|
||||
if (success) {
|
||||
payload = {
|
||||
success: true
|
||||
};
|
||||
}
|
||||
|
||||
subscriber.publish(
|
||||
"events",
|
||||
JSON.stringify({
|
||||
event: "build_event",
|
||||
message: payload
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function open_in_editor() {
|
||||
let subscriber = get_redis_subscriber("redis_socketio");
|
||||
subscriber.on("message", (event, file) => {
|
||||
if (event === "open_in_editor") {
|
||||
file = JSON.parse(file);
|
||||
let file_path = path.resolve(file.file);
|
||||
console.log("Opening file in editor:", file_path);
|
||||
let launch = require("launch-editor");
|
||||
launch(`${file_path}:${file.line}:${file.column}`);
|
||||
}
|
||||
});
|
||||
subscriber.subscribe("open_in_editor");
|
||||
}
|
||||
|
||||
open_in_editor();
|
||||
|
|
|
|||
111
frappe/public/js/frappe/build_events/BuildError.vue
Normal file
111
frappe/public/js/frappe/build_events/BuildError.vue
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
<template>
|
||||
<div class="build-error-overlay" @click.self="data = null" v-show="data">
|
||||
<div class="window" v-if="data">
|
||||
<div v-for="(error, i) in data.formatted" :key="i">
|
||||
<!-- prettier-ignore -->
|
||||
<pre class="frame"><component :is="error_component(error, i)" /></pre>
|
||||
</div>
|
||||
<pre class="stack">{{ data.stack }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: "BuildError",
|
||||
data() {
|
||||
return {
|
||||
data: null
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
show(data) {
|
||||
this.data = data;
|
||||
},
|
||||
hide() {
|
||||
this.data = null;
|
||||
},
|
||||
open_in_editor(location) {
|
||||
frappe.socketio.socket.emit("open_in_editor", location);
|
||||
},
|
||||
error_component(error, i) {
|
||||
let location = this.data.error.errors[i].location;
|
||||
let location_string = `${location.file}:${location.line}:${
|
||||
location.column
|
||||
}`;
|
||||
let template = error.replace(
|
||||
" > " + location_string,
|
||||
` > <a class="file-link" @click="open">${location_string}</a>`
|
||||
);
|
||||
|
||||
return {
|
||||
template: `<div>${template}</div>`,
|
||||
methods: {
|
||||
open() {
|
||||
frappe.socketio.socket.emit("open_in_editor", location);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
.build-error-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 9999;
|
||||
margin: 0;
|
||||
background: rgba(0, 0, 0, 0.66);
|
||||
--monospace: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier,
|
||||
monospace;
|
||||
--dim: var(--gray-400);
|
||||
}
|
||||
.window {
|
||||
font-family: var(--monospace);
|
||||
line-height: 1.5;
|
||||
width: 800px;
|
||||
color: #d8d8d8;
|
||||
margin: 30px auto;
|
||||
padding: 25px 40px;
|
||||
position: relative;
|
||||
background: #181818;
|
||||
border-radius: 6px 6px 8px 8px;
|
||||
box-shadow: 0 19px 38px rgba(0, 0, 0, 0.3), 0 15px 12px rgba(0, 0, 0, 0.22);
|
||||
overflow: hidden;
|
||||
border-top: 8px solid var(--red);
|
||||
}
|
||||
|
||||
pre {
|
||||
font-family: var(--monospace);
|
||||
font-size: 13px;
|
||||
margin-top: 0;
|
||||
margin-bottom: 1em;
|
||||
overflow-x: auto;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
code {
|
||||
font-size: 13px;
|
||||
font-family: var(--monospace);
|
||||
color: var(--yellow);
|
||||
}
|
||||
|
||||
.message {
|
||||
line-height: 1.3;
|
||||
font-weight: 600;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
.frame {
|
||||
color: var(--yellow);
|
||||
}
|
||||
.stack {
|
||||
font-size: 13px;
|
||||
color: var(--dim);
|
||||
}
|
||||
.file-link {
|
||||
text-decoration: underline !important;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
52
frappe/public/js/frappe/build_events/BuildSuccess.vue
Normal file
52
frappe/public/js/frappe/build_events/BuildSuccess.vue
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
<template>
|
||||
<div
|
||||
v-if="is_shown"
|
||||
class="flex justify-between build-success-message align-center"
|
||||
>
|
||||
<div class="mr-4">Compiled successfully</div>
|
||||
<a class="text-white underline" href="/" @click.prevent="reload">
|
||||
Refresh
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: "BuildSuccess",
|
||||
data() {
|
||||
return {
|
||||
is_shown: false
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
show() {
|
||||
this.is_shown = true;
|
||||
if (this.timeout) {
|
||||
clearTimeout(this.timeout);
|
||||
}
|
||||
this.timeout = setTimeout(() => {
|
||||
this.hide();
|
||||
}, 10000);
|
||||
},
|
||||
hide() {
|
||||
this.is_shown = false;
|
||||
},
|
||||
reload() {
|
||||
window.location.reload();
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
.build-success-message {
|
||||
position: fixed;
|
||||
z-index: 9999;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
border-radius: var(--border-radius);
|
||||
padding: 0.5rem 1rem;
|
||||
color: white;
|
||||
font-weight: 500;
|
||||
margin: 1rem;
|
||||
}
|
||||
</style>
|
||||
48
frappe/public/js/frappe/build_events/build_events.bundle.js
Normal file
48
frappe/public/js/frappe/build_events/build_events.bundle.js
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
import BuildError from "./BuildError.vue";
|
||||
import BuildSuccess from "./BuildSuccess.vue";
|
||||
|
||||
let $container = $("#build-events-overlay");
|
||||
let success = null;
|
||||
let error = null;
|
||||
|
||||
frappe.realtime.on("build_event", data => {
|
||||
if (data.success) {
|
||||
show_build_success(data);
|
||||
} else if (data.error) {
|
||||
show_build_error(data);
|
||||
}
|
||||
});
|
||||
|
||||
function show_build_success() {
|
||||
if (error) {
|
||||
error.hide();
|
||||
}
|
||||
if (!success) {
|
||||
let target = $('<div class="build-success-container">')
|
||||
.appendTo($container)
|
||||
.get(0);
|
||||
let vm = new Vue({
|
||||
el: target,
|
||||
render: h => h(BuildSuccess)
|
||||
});
|
||||
success = vm.$children[0];
|
||||
}
|
||||
success.show();
|
||||
}
|
||||
|
||||
function show_build_error(data) {
|
||||
if (success) {
|
||||
success.hide();
|
||||
}
|
||||
if (!error) {
|
||||
let target = $('<div class="build-error-container">')
|
||||
.appendTo($container)
|
||||
.get(0);
|
||||
let vm = new Vue({
|
||||
el: target,
|
||||
render: h => h(BuildError)
|
||||
});
|
||||
error = vm.$children[0];
|
||||
}
|
||||
error.show(data);
|
||||
}
|
||||
|
|
@ -117,7 +117,7 @@ frappe.Application = class Application {
|
|||
this.setup_user_group_listeners();
|
||||
|
||||
// listen to build errors
|
||||
this.setup_build_error_listener();
|
||||
this.setup_build_events();
|
||||
|
||||
if (frappe.sys_defaults.email_user_password) {
|
||||
var email_list = frappe.sys_defaults.email_user_password.split(',');
|
||||
|
|
@ -585,11 +585,9 @@ frappe.Application = class Application {
|
|||
}
|
||||
}
|
||||
|
||||
setup_build_error_listener() {
|
||||
setup_build_events() {
|
||||
if (frappe.boot.developer_mode) {
|
||||
frappe.realtime.on('build_error', (data) => {
|
||||
console.log(data);
|
||||
});
|
||||
frappe.require("build_events.bundle.js");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import "./jquery-bootstrap";
|
|||
import Vue from "vue/dist/vue.esm.js";
|
||||
import moment from "moment/min/moment-with-locales.js";
|
||||
import momentTimezone from "moment-timezone/builds/moment-timezone-with-data.js";
|
||||
import "socket.io-client/dist/socket.io.slim.js";
|
||||
import io from "socket.io-client/dist/socket.io.slim.js";
|
||||
import Sortable from "./lib/Sortable.min.js";
|
||||
// TODO: esbuild
|
||||
// Don't think jquery.hotkeys is being used anywhere. Will remove this after being sure.
|
||||
|
|
@ -12,3 +12,4 @@ import Sortable from "./lib/Sortable.min.js";
|
|||
window.moment = momentTimezone;
|
||||
window.Vue = Vue;
|
||||
window.Sortable = Sortable;
|
||||
window.io = io
|
||||
|
|
|
|||
|
|
@ -762,18 +762,15 @@ def get_build_version():
|
|||
|
||||
def get_assets_json():
|
||||
if not hasattr(frappe.local, "assets_json"):
|
||||
|
||||
assets_json = frappe.cache().get_value("assets_json", shared=True)
|
||||
cache = frappe.cache()
|
||||
# using .get instead of .get_value to avoid pickle.loads
|
||||
assets_json = cache.get("assets_json")
|
||||
if not assets_json:
|
||||
import json
|
||||
assets_json = json.loads(
|
||||
frappe.read_file("assets/frappe/dist/assets.json")
|
||||
)
|
||||
frappe.cache().set_value("assets_json", assets_json, shared=True)
|
||||
assets_json = frappe.read_file("assets/frappe/dist/assets.json")
|
||||
cache.set_value("assets_json", assets_json, shared=True)
|
||||
frappe.local.assets_json = frappe.safe_decode(assets_json)
|
||||
|
||||
frappe.local.assets_json = assets_json
|
||||
|
||||
return frappe.local.assets_json
|
||||
return frappe.parse_json(frappe.local.assets_json)
|
||||
|
||||
|
||||
def get_bench_relative_path(file_path):
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@
|
|||
<div id="body"></div>
|
||||
<footer></footer>
|
||||
</div>
|
||||
<div id="build-events-overlay"></div>
|
||||
|
||||
<script type="text/javascript">
|
||||
window._version_number = "{{ build_version }}";
|
||||
|
|
|
|||
|
|
@ -38,9 +38,9 @@ function get_conf() {
|
|||
return conf;
|
||||
}
|
||||
|
||||
function get_redis_subscriber() {
|
||||
function get_redis_subscriber(kind="redis_socketio") {
|
||||
const conf = get_conf();
|
||||
const host = conf.redis_socketio || conf.redis_async_broker_port;
|
||||
const host = conf[kind] || conf.redis_async_broker_port;
|
||||
return redis.createClient(host);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"name": "frappe-framework",
|
||||
"scripts": {
|
||||
"build": "node rollup/build.js",
|
||||
"production": "FRAPPE_ENV=production node rollup/build.js",
|
||||
"watch": "node rollup/watch.js",
|
||||
"build": "node esbuild",
|
||||
"production": "node esbuild --production",
|
||||
"watch": "node esbuild --watch",
|
||||
"snyk-protect": "snyk protect"
|
||||
},
|
||||
"repository": {
|
||||
|
|
@ -64,6 +64,7 @@
|
|||
"fast-glob": "^3.2.5",
|
||||
"graphlib": "^2.1.8",
|
||||
"http-proxy": "^1.18.1",
|
||||
"launch-editor": "^2.2.1",
|
||||
"less": "^3.11.1",
|
||||
"md5": "^2.3.0",
|
||||
"rollup": "^1.2.2",
|
||||
|
|
|
|||
|
|
@ -199,6 +199,11 @@ io.on('connection', function (socket) {
|
|||
'type'
|
||||
);
|
||||
});
|
||||
|
||||
socket.on('open_in_editor', (data) => {
|
||||
let s = get_redis_subscriber('redis_socketio');
|
||||
s.publish('open_in_editor', JSON.stringify(data));
|
||||
});
|
||||
});
|
||||
|
||||
subscriber.on("message", function (_channel, message) {
|
||||
|
|
|
|||
13
yarn.lock
13
yarn.lock
|
|
@ -4520,6 +4520,14 @@ latest-version@^5.0.0:
|
|||
dependencies:
|
||||
package-json "^6.3.0"
|
||||
|
||||
launch-editor@^2.2.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/launch-editor/-/launch-editor-2.2.1.tgz#871b5a3ee39d6680fcc26d37930b6eeda89db0ca"
|
||||
integrity sha512-On+V7K2uZK6wK7x691ycSUbLD/FyKKelArkbaAMSSJU8JmqmhwN2+mnJDNINuJWSrh2L0kDk+ZQtbC/gOWUwLw==
|
||||
dependencies:
|
||||
chalk "^2.3.0"
|
||||
shell-quote "^1.6.1"
|
||||
|
||||
lazy-cache@^1.0.3:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e"
|
||||
|
|
@ -7711,6 +7719,11 @@ shebang-regex@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
|
||||
integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=
|
||||
|
||||
shell-quote@^1.6.1:
|
||||
version "1.7.2"
|
||||
resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.2.tgz#67a7d02c76c9da24f99d20808fcaded0e0e04be2"
|
||||
integrity sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==
|
||||
|
||||
should-equal@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/should-equal/-/should-equal-2.0.0.tgz#6072cf83047360867e68e98b09d71143d04ee0c3"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue