Kaynağa Gözat

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
version-14
Faris Ansari 4 yıl önce
ebeveyn
işleme
adc236e35d
12 değiştirilmiş dosya ile 319 ekleme ve 26 silme
  1. +71
    -5
      esbuild/index.js
  2. +111
    -0
      frappe/public/js/frappe/build_events/BuildError.vue
  3. +52
    -0
      frappe/public/js/frappe/build_events/BuildSuccess.vue
  4. +48
    -0
      frappe/public/js/frappe/build_events/build_events.bundle.js
  5. +3
    -5
      frappe/public/js/frappe/desk.js
  6. +2
    -1
      frappe/public/js/libs.bundle.js
  7. +7
    -10
      frappe/utils/__init__.py
  8. +1
    -0
      frappe/www/app.html
  9. +2
    -2
      node_utils.js
  10. +4
    -3
      package.json
  11. +5
    -0
      socketio.js
  12. +13
    -0
      yarn.lock

+ 71
- 5
esbuild/index.js Dosyayı Görüntüle

@@ -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
- 0
frappe/public/js/frappe/build_events/BuildError.vue Dosyayı Görüntüle

@@ -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,
` &gt; <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
- 0
frappe/public/js/frappe/build_events/BuildSuccess.vue Dosyayı Görüntüle

@@ -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
- 0
frappe/public/js/frappe/build_events/build_events.bundle.js Dosyayı Görüntüle

@@ -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);
}

+ 3
- 5
frappe/public/js/frappe/desk.js Dosyayı Görüntüle

@@ -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
- 1
frappe/public/js/libs.bundle.js Dosyayı Görüntüle

@@ -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

+ 7
- 10
frappe/utils/__init__.py Dosyayı Görüntüle

@@ -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)

frappe.local.assets_json = assets_json
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)

return frappe.local.assets_json
return frappe.parse_json(frappe.local.assets_json)


def get_bench_relative_path(file_path):


+ 1
- 0
frappe/www/app.html Dosyayı Görüntüle

@@ -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 }}";


+ 2
- 2
node_utils.js Dosyayı Görüntüle

@@ -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);
}



+ 4
- 3
package.json Dosyayı Görüntüle

@@ -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",


+ 5
- 0
socketio.js Dosyayı Görüntüle

@@ -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
- 0
yarn.lock Dosyayı Görüntüle

@@ -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"


Yükleniyor…
İptal
Kaydet