@shaper.org/core 1.0.4 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,9 @@
1
1
  //#region src/client/iframe-post-message.d.ts
2
- type PostMessageType = "server-ready" | "all-routes" | "route-change" | "route-refresh";
2
+ type PostMessageType = "server-ready" | "all-routes" | "route-change" | "route-refresh" | "error";
3
3
  interface PostMessage {
4
4
  message: any;
5
5
  type: PostMessageType;
6
+ object: "shaper-post-message";
6
7
  }
7
8
  interface RouteInfo {
8
9
  name: string;
@@ -29,6 +30,15 @@ interface RouteRefreshMessage extends PostMessage {
29
30
  type: "route-refresh";
30
31
  message: RouteInfo;
31
32
  }
33
+ interface ErrorInfo {
34
+ errorType: string;
35
+ timestamp: number;
36
+ lineno?: number;
37
+ colno?: number;
38
+ stack: string;
39
+ has_blank_screen: boolean;
40
+ filename?: string;
41
+ }
32
42
  declare class IframePostMessageClient {
33
43
  authHost: string;
34
44
  private receiveCallbacks;
@@ -44,6 +54,8 @@ declare class IframePostMessageClient {
44
54
  sendAllRoutes(routes: RouteInfo[]): void;
45
55
  sendRouteChange(route: RouteInfo): void;
46
56
  sendRouteRefresh(route: RouteInfo): void;
57
+ sendError(errorInfo: ErrorInfo): void;
58
+ sendConsole(): void;
47
59
  }
48
60
  //#endregion
49
61
  export { AllRoutesMessage, IframePostMessageClient, PostMessage, PostMessageType, RouteChangeMessage, RouteInfo, RouteRefreshMessage, ServerReadyMessage };
@@ -33,14 +33,16 @@ var IframePostMessageClient = class {
33
33
  sendServerReady() {
34
34
  this._send({
35
35
  type: "server-ready",
36
- message: { satus: "ok" }
36
+ message: { satus: "ok" },
37
+ object: "shaper-post-message"
37
38
  });
38
39
  }
39
40
  sendAllRoutes(routes) {
40
41
  for (let route of routes) this._ensure_route_file(route);
41
42
  const message = {
42
43
  type: "all-routes",
43
- message: { routes }
44
+ message: { routes },
45
+ object: "shaper-post-message"
44
46
  };
45
47
  this._send(message);
46
48
  }
@@ -48,7 +50,8 @@ var IframePostMessageClient = class {
48
50
  this._ensure_route_file(route);
49
51
  const message = {
50
52
  type: "route-change",
51
- message: { ...route }
53
+ message: { ...route },
54
+ object: "shaper-post-message"
52
55
  };
53
56
  this._send(message);
54
57
  }
@@ -56,10 +59,20 @@ var IframePostMessageClient = class {
56
59
  this._ensure_route_file(route);
57
60
  const message = {
58
61
  type: "route-refresh",
59
- message: { ...route }
62
+ message: { ...route },
63
+ object: "shaper-post-message"
60
64
  };
61
65
  this._send(message);
62
66
  }
67
+ sendError(errorInfo) {
68
+ const message = {
69
+ type: "error",
70
+ message: errorInfo,
71
+ object: "shaper-post-message"
72
+ };
73
+ this._send(message);
74
+ }
75
+ sendConsole() {}
63
76
  };
64
77
 
65
78
  //#endregion
@@ -5,6 +5,7 @@ import { HmrContext, ViteDevServer } from "vite";
5
5
  declare const HMREventsPlugin: () => {
6
6
  name: string;
7
7
  enforce: string;
8
+ apply: string;
8
9
  resolveId(id: string): "virtual:hmr-events-plugin" | undefined;
9
10
  load(id: string): string | undefined;
10
11
  transformIndexHtml(html: string): {
@@ -4,7 +4,8 @@ import { fileURLToPath } from "url";
4
4
  const HMREventsPlugin = () => {
5
5
  return {
6
6
  name: "hmr-events-plugin",
7
- enforce: "pre",
7
+ enforce: "post",
8
+ apply: "serve",
8
9
  resolveId(id) {
9
10
  if (id === "virtual:hmr-events-plugin") return id;
10
11
  },
@@ -1,82 +1,49 @@
1
1
  import { ViteHook } from "./vite-hook";
2
- import { ViteMessage } from "./vite-message";
3
-
4
- interface LogEvent {
5
- type: "logEvent";
6
- logLevel: string;
7
- timestamp: number;
8
- message: string;
9
- }
10
-
11
- interface UnhandledrejectionEvent {
12
- type: "unhandledrejectionEvent";
13
- message: string;
14
- timestamp: number;
15
- }
16
-
17
- interface ErrorEvent {
18
- type: "errorEvent";
19
- lineno: number;
20
- colno: number;
21
- filename: string;
22
- message: string;
23
- timestamp: number;
24
- }
25
-
26
- type Event = LogEvent | UnhandledrejectionEvent | ErrorEvent;
2
+ import { IframePostMessageClient } from "../client";
3
+ import { ErrorInfo } from "src/client/iframe-post-message";
27
4
 
28
5
  class ErrorTracker {
29
- eventsQueue: Event[];
30
-
31
6
  public vHook: ViteHook;
32
- public viteMessage: ViteMessage;
7
+ private iframeClient: IframePostMessageClient;
33
8
 
34
9
  constructor() {
35
- this.eventsQueue = [];
36
-
37
- this.viteMessage = new ViteMessage();
38
-
39
10
  this.vHook = new ViteHook();
11
+ this.iframeClient = new IframePostMessageClient();
40
12
  }
41
13
 
42
14
  isBlankScreen() {
43
- const app = document.querySelector("div#app");
15
+ const app = document.querySelector("div#root");
44
16
  return app ? app.childElementCount === 0 : false;
45
17
  }
46
18
 
47
19
  start() {
48
20
  this.handleError();
49
21
  this.handleUnhandledrejection();
50
- this.consoleInterception();
51
-
52
- let startTimestamp: number;
53
- this.vHook.onBeforeUpdate((data, timestamp) => {
54
- startTimestamp = timestamp;
55
- this.viteMessage.sendBeforeUpdate(data);
56
- });
57
-
58
- this.vHook.onError((data, timestamp) => {
59
- console.debug(data);
60
- });
61
-
62
- this.vHook.onAfterUpdate((data, timestamp) => {
63
- setTimeout(() => {
64
- const endTimestamp = Date.now();
22
+ this.handleConsoleInterception();
23
+ this.handleViteError();
24
+ }
65
25
 
66
- let events = this.eventsQueue.filter(
67
- (event) =>
68
- event.timestamp >= startTimestamp &&
69
- event.timestamp <= endTimestamp,
70
- );
26
+ formatConsoleMessage(args: unknown[]): string {
27
+ let error;
28
+ const texts = [];
29
+ for (let arg of args) {
30
+ if (arg instanceof Error) {
31
+ error = arg;
32
+ } else {
33
+ texts.push(arg);
34
+ }
35
+ }
71
36
 
72
- this.viteMessage.sendAfterUpdate(events);
37
+ if (error) {
38
+ return error.stack || error.message;
39
+ }
73
40
 
74
- this.eventsQueue = [];
75
- }, 5);
76
- });
41
+ return texts.map((text) => String(text)).join(" ");
77
42
  }
78
43
 
79
- consoleInterception() {
44
+ handleConsoleInterception() {
45
+ type LogLevel = keyof typeof originalConsole;
46
+
80
47
  const originalConsole = {
81
48
  log: console.log,
82
49
  warn: console.warn,
@@ -84,83 +51,85 @@ class ErrorTracker {
84
51
  trace: console.trace,
85
52
  // debug: console.debug,
86
53
  };
87
-
88
- for (let logLevel of Object.keys(originalConsole)) {
89
- console[logLevel] = function (...args) {
90
- const logArgumentsString = args.map((arg) => {
91
- if (arg) {
92
- return arg.toString();
93
- }
94
- });
95
-
96
- this.eventsQueue.push({
97
- type: "logEvent",
98
- logLevel,
99
- timestamp: Date.now(),
100
- message: logArgumentsString.join(" "),
101
- });
54
+ for (let logLevel of Object.keys(originalConsole) as LogLevel[]) {
55
+ console[logLevel] = function (
56
+ this: ErrorTracker,
57
+ ...args: unknown[]
58
+ ) {
102
59
  originalConsole[logLevel].apply(this, args);
60
+
61
+ const message = this.formatConsoleMessage(args);
62
+
63
+ if (!message) return;
64
+
65
+ if (logLevel === "error") {
66
+ console.debug("[vite-shaper] console error", message);
67
+ this.iframeClient.sendError({
68
+ errorType: "RUNTIME_ERROR",
69
+ timestamp: Date.now(),
70
+ stack: message,
71
+ has_blank_screen: this.isBlankScreen(),
72
+ });
73
+ }
103
74
  }.bind(this);
104
75
  }
105
76
  }
106
77
 
107
- handleUnhandledrejection() {
108
- window.addEventListener("unhandledrejection", (evt) => {
109
- console.debug(evt);
110
- const UnhandledrejectionMessage = {
111
- type: "unhandledrejectionEvent",
112
- message: evt.reason.stack || evt.reason.message,
78
+ handleViteError() {
79
+ this.vHook.onError((data) => {
80
+ console.debug("[vite-shaper] vite:error", data);
81
+
82
+ let error: ErrorInfo = {
83
+ errorType: "RUNTIME_ERROR",
113
84
  timestamp: Date.now(),
85
+ stack: data.err.message,
86
+ has_blank_screen: this.isBlankScreen(),
114
87
  };
88
+ if (data.err.loc) {
89
+ error = {
90
+ ...error,
91
+ lineno: data.err.loc.line,
92
+ colno: data.err.loc.column,
93
+ filename: data.err.loc.file,
94
+ };
95
+ }
96
+ this.iframeClient.sendError(error);
97
+ });
98
+ }
115
99
 
116
- this.eventsQueue.push(UnhandledrejectionMessage);
100
+ handleUnhandledrejection() {
101
+ window.addEventListener("unhandledrejection", (evt) => {
102
+ console.debug("[vite-shaper] handleUnhandledrejection", evt);
103
+ this.iframeClient.sendError({
104
+ errorType: "RUNTIME_ERROR",
105
+ timestamp: Date.now(),
106
+ stack: evt.reason.stack || evt.reason.message,
107
+ has_blank_screen: this.isBlankScreen(),
108
+ });
117
109
  });
118
110
  }
119
111
 
120
112
  handleError() {
121
113
  window.addEventListener("error", (evt) => {
114
+ console.debug("[vite-shaper] handleError", evt);
122
115
  let message;
123
116
  if (evt.error) {
124
117
  message = evt.error.stack || evt.error.message;
125
118
  } else {
126
119
  message = evt.message;
127
120
  }
128
-
129
- console.debug(evt);
130
- const errorMessage = {
131
- type: "errorEvent",
121
+ this.iframeClient.sendError({
122
+ errorType: "RUNTIME_ERROR",
123
+ timestamp: Date.now(),
124
+ stack: message,
125
+ has_blank_screen: this.isBlankScreen(),
132
126
  lineno: evt.lineno,
133
127
  colno: evt.colno,
134
128
  filename: evt.filename,
135
- message: message,
136
- timestamp: Date.now(),
137
- };
138
-
139
- this.eventsQueue.push(errorMessage);
129
+ });
140
130
  });
141
-
142
- // This handle the following kind of runtime error
143
- // For now it's diffcult to know what to do because those errors are not sync with the HMR lifecyle
144
- // Maybe use a queue
145
- // <script setup lang="ts">
146
- // import { onMounted } from 'vue';
147
-
148
- // onMounted(() => {
149
- // // This will trigger the error event because it happens at runtime
150
- // setTimeout(() => {
151
- // undefinedFunction(); // Runtime error - WILL trigger window.error
152
- // }, 0);
153
- // });
154
- // </script>
155
131
  }
156
132
  }
157
133
 
158
134
  const tracker = new ErrorTracker();
159
135
  tracker.start();
160
-
161
- // HMR listening
162
- // const handleMessage = (event) => {
163
- // console.log(event);
164
- // };
165
-
166
- // window.addEventListener("message", handleMessage);
@@ -1,133 +1,133 @@
1
1
  import {
2
- ErrorPayload,
3
- FullReloadPayload,
4
- PrunePayload,
5
- UpdatePayload,
2
+ ErrorPayload,
3
+ FullReloadPayload,
4
+ PrunePayload,
5
+ UpdatePayload,
6
6
  } from "vite/types/hmrPayload.js";
7
7
  import { ViteHotContext } from "vite/types/hot.js";
8
8
 
9
9
  type ViteUpdateCallback =
10
- | ((data: UpdatePayload, timestamp: number) => void)
11
- | null;
10
+ | ((data: UpdatePayload, timestamp: number) => void)
11
+ | null;
12
12
 
13
13
  type VitePruneCallback =
14
- | ((data: PrunePayload, timestamp: number) => void)
15
- | null;
14
+ | ((data: PrunePayload, timestamp: number) => void)
15
+ | null;
16
16
 
17
17
  type VitFullReloadCallback =
18
- | ((data: FullReloadPayload, timestamp: number) => void)
19
- | null;
18
+ | ((data: FullReloadPayload, timestamp: number) => void)
19
+ | null;
20
20
 
21
21
  type ViteErrorCallback =
22
- | ((data: ErrorPayload, timestamp: number) => void)
23
- | null;
22
+ | ((data: ErrorPayload, timestamp: number) => void)
23
+ | null;
24
24
 
25
25
  type ViteInvalidateCallback = ((data: any, timestamp: number) => void) | null;
26
26
 
27
27
  type ViteWebSocketConnectCallback =
28
- | ((data: any, timestamp: number) => void)
29
- | null;
28
+ | ((data: any, timestamp: number) => void)
29
+ | null;
30
30
 
31
31
  type ViteWebSocketDisconnectCallback =
32
- | ((data: any, timestamp: number) => void)
33
- | null;
32
+ | ((data: any, timestamp: number) => void)
33
+ | null;
34
34
 
35
35
  class ViteHook {
36
- private hmr: ViteHotContext | undefined;
37
- constructor() {
38
- this.hmr = import.meta.hot;
39
- }
40
- hotExists(hmr: ViteHotContext | undefined): hmr is ViteHotContext {
41
- if (hmr) return true;
42
- console.error("HotContext does not exists");
43
- return false;
44
- }
45
-
46
- onBeforeUpdate(fn: ViteUpdateCallback = null) {
47
- if (!this.hotExists(this.hmr)) return;
48
-
49
- this.hmr.on("vite:beforeUpdate", (data: UpdatePayload) => {
50
- const timestamp = Date.now();
51
- console.debug("beforeUpdate");
52
- fn && fn(data, timestamp);
53
- });
54
- }
55
-
56
- onAfterUpdate(fn: ViteUpdateCallback = null) {
57
- if (!this.hotExists(this.hmr)) return;
58
-
59
- this.hmr.on("vite:afterUpdate", (data: UpdatePayload) => {
60
- const timestamp = Date.now();
61
- console.debug("afterUpdate");
62
- fn && fn(data, timestamp);
63
- });
64
- }
65
-
66
- onBeforePrune(fn: VitePruneCallback = null) {
67
- if (!this.hotExists(this.hmr)) return;
68
-
69
- this.hmr.on("vite:beforePrune", (data: PrunePayload) => {
70
- const timestamp = Date.now();
71
- console.debug("beforePrune");
72
-
73
- fn && fn(data, timestamp);
74
- });
75
- }
76
-
77
- onBeforeFullReload(fn: VitFullReloadCallback = null) {
78
- if (!this.hotExists(this.hmr)) return;
79
-
80
- this.hmr.on("vite:beforeFullReload", (data: FullReloadPayload) => {
81
- const timestamp = Date.now();
82
- console.debug("beforeFullReload");
83
-
84
- fn && fn(data, timestamp);
85
- });
86
- }
87
-
88
- onError(fn: ViteErrorCallback = null) {
89
- if (!this.hotExists(this.hmr)) return;
90
-
91
- this.hmr.on("vite:error", (data: ErrorPayload) => {
92
- const timestamp = Date.now();
93
- console.debug("error");
94
-
95
- fn && fn(data, timestamp);
96
- });
97
- }
98
-
99
- onInvalidate(fn: ViteInvalidateCallback = null) {
100
- if (!this.hotExists(this.hmr)) return;
101
-
102
- this.hmr.on("vite:invalidate", (data: any) => {
103
- const timestamp = Date.now();
104
- console.debug("invalidate");
105
-
106
- fn && fn(data, timestamp);
107
- });
108
- }
109
-
110
- onWsConnect(fn: ViteWebSocketConnectCallback = null) {
111
- if (!this.hotExists(this.hmr)) return;
112
-
113
- this.hmr.on("vite:ws:connect", (data: any) => {
114
- const timestamp = Date.now();
115
- console.debug("ws:connect");
116
-
117
- fn && fn(data, timestamp);
118
- });
119
- }
120
-
121
- onWsDisconnect(fn: ViteWebSocketDisconnectCallback = null) {
122
- if (!this.hotExists(this.hmr)) return;
123
-
124
- this.hmr.on("vite:ws:disconnect", (data: any) => {
125
- const timestamp = Date.now();
126
- console.debug(":ws:disconnect");
127
-
128
- fn && fn(data, timestamp);
129
- });
130
- }
36
+ private hmr: ViteHotContext | undefined;
37
+ constructor() {
38
+ this.hmr = import.meta.hot;
39
+ }
40
+ hotExists(hmr: ViteHotContext | undefined): hmr is ViteHotContext {
41
+ if (hmr) return true;
42
+ console.error("HotContext does not exists");
43
+ return false;
44
+ }
45
+
46
+ onBeforeUpdate(fn: ViteUpdateCallback = null) {
47
+ if (!this.hotExists(this.hmr)) return;
48
+
49
+ this.hmr.on("vite:beforeUpdate", (data: UpdatePayload) => {
50
+ const timestamp = Date.now();
51
+ console.debug("beforeUpdate");
52
+ fn && fn(data, timestamp);
53
+ });
54
+ }
55
+
56
+ onAfterUpdate(fn: ViteUpdateCallback = null) {
57
+ if (!this.hotExists(this.hmr)) return;
58
+
59
+ this.hmr.on("vite:afterUpdate", (data: UpdatePayload) => {
60
+ const timestamp = Date.now();
61
+ console.debug("afterUpdate");
62
+ fn && fn(data, timestamp);
63
+ });
64
+ }
65
+
66
+ onBeforePrune(fn: VitePruneCallback = null) {
67
+ if (!this.hotExists(this.hmr)) return;
68
+
69
+ this.hmr.on("vite:beforePrune", (data: PrunePayload) => {
70
+ const timestamp = Date.now();
71
+ console.debug("beforePrune");
72
+
73
+ fn && fn(data, timestamp);
74
+ });
75
+ }
76
+
77
+ onBeforeFullReload(fn: VitFullReloadCallback = null) {
78
+ if (!this.hotExists(this.hmr)) return;
79
+
80
+ this.hmr.on("vite:beforeFullReload", (data: FullReloadPayload) => {
81
+ const timestamp = Date.now();
82
+ console.debug("beforeFullReload");
83
+
84
+ fn && fn(data, timestamp);
85
+ });
86
+ }
87
+
88
+ onError(fn: ViteErrorCallback = null) {
89
+ if (!this.hotExists(this.hmr)) return;
90
+
91
+ this.hmr.on("vite:error", (data: ErrorPayload) => {
92
+ const timestamp = Date.now();
93
+ console.debug("error");
94
+
95
+ fn && fn(data, timestamp);
96
+ });
97
+ }
98
+
99
+ onInvalidate(fn: ViteInvalidateCallback = null) {
100
+ if (!this.hotExists(this.hmr)) return;
101
+
102
+ this.hmr.on("vite:invalidate", (data: any) => {
103
+ const timestamp = Date.now();
104
+ console.debug("invalidate");
105
+
106
+ fn && fn(data, timestamp);
107
+ });
108
+ }
109
+
110
+ onWsConnect(fn: ViteWebSocketConnectCallback = null) {
111
+ if (!this.hotExists(this.hmr)) return;
112
+
113
+ this.hmr.on("vite:ws:connect", (data: any) => {
114
+ const timestamp = Date.now();
115
+ console.debug("ws:connect");
116
+
117
+ fn && fn(data, timestamp);
118
+ });
119
+ }
120
+
121
+ onWsDisconnect(fn: ViteWebSocketDisconnectCallback = null) {
122
+ if (!this.hotExists(this.hmr)) return;
123
+
124
+ this.hmr.on("vite:ws:disconnect", (data: any) => {
125
+ const timestamp = Date.now();
126
+ console.debug(":ws:disconnect");
127
+
128
+ fn && fn(data, timestamp);
129
+ });
130
+ }
131
131
  }
132
132
 
133
133
  export { ViteHook };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shaper.org/core",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "private": false,