@jsenv/core 27.3.4 → 27.5.1

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.
Files changed (88) hide show
  1. package/dist/js/autoreload.js +359 -0
  2. package/dist/js/execute_using_dynamic_import.js +1 -1
  3. package/dist/js/html_supervisor_installer.js +524 -147
  4. package/dist/js/html_supervisor_setup.js +3 -4
  5. package/dist/js/new_stylesheet.js +26 -58
  6. package/dist/js/server_events_client.js +307 -0
  7. package/dist/main.js +7709 -7324
  8. package/package.json +15 -15
  9. package/{README.md → readme.md} +18 -7
  10. package/src/build/build.js +16 -18
  11. package/src/build/start_build_server.js +24 -28
  12. package/src/dev/start_dev_server.js +34 -96
  13. package/src/execute/execute.js +17 -35
  14. package/src/omega/errors.js +43 -9
  15. package/src/omega/kitchen.js +42 -25
  16. package/src/omega/omega_server.js +96 -74
  17. package/src/omega/server/file_service.js +256 -28
  18. package/src/omega/url_graph.js +33 -20
  19. package/src/plugins/autoreload/client/autoreload.js +201 -0
  20. package/src/plugins/autoreload/{dev_sse/client → client}/autoreload_preference.js +0 -0
  21. package/src/plugins/autoreload/{dev_sse/client → client}/reload.js +29 -10
  22. package/src/plugins/autoreload/{dev_sse/client → client}/url_helpers.js +0 -0
  23. package/src/plugins/autoreload/jsenv_plugin_autoreload.js +4 -8
  24. package/src/plugins/autoreload/{dev_sse/jsenv_plugin_dev_sse_client.js → jsenv_plugin_autoreload_client.js} +8 -8
  25. package/src/plugins/autoreload/jsenv_plugin_autoreload_server.js +196 -0
  26. package/src/{dev/plugins → plugins}/explorer/client/explorer.html +0 -0
  27. package/src/{dev/plugins → plugins}/explorer/client/jsenv.png +0 -0
  28. package/src/{dev/plugins → plugins}/explorer/jsenv_plugin_explorer.js +1 -3
  29. package/src/plugins/html_supervisor/client/error_overlay.js +401 -0
  30. package/src/plugins/html_supervisor/client/html_supervisor_installer.js +138 -23
  31. package/src/plugins/html_supervisor/client/html_supervisor_setup.js +3 -4
  32. package/src/plugins/html_supervisor/jsenv_plugin_html_supervisor.js +55 -23
  33. package/src/plugins/inline/jsenv_plugin_html_inline_content.js +97 -117
  34. package/src/plugins/node_esm_resolution/jsenv_plugin_node_esm_resolution.js +66 -58
  35. package/src/plugins/plugin_controller.js +102 -67
  36. package/src/plugins/plugins.js +10 -10
  37. package/src/{helpers/event_source/event_source.js → plugins/server_events/client/event_source_connection.js} +125 -33
  38. package/src/plugins/server_events/client/server_events_client.js +17 -0
  39. package/src/plugins/server_events/jsenv_plugin_server_events_client_injection.js +48 -0
  40. package/src/plugins/server_events/server_events_dispatcher.js +69 -0
  41. package/src/{dev/plugins → plugins}/toolbar/client/animation/toolbar_animation.js +0 -0
  42. package/src/{dev/plugins → plugins}/toolbar/client/eventsource/eventsource.css +0 -0
  43. package/src/{dev/plugins → plugins}/toolbar/client/eventsource/toolbar_eventsource.js +0 -0
  44. package/src/{dev/plugins → plugins}/toolbar/client/execution/execution.css +0 -0
  45. package/src/{dev/plugins → plugins}/toolbar/client/execution/toolbar_execution.js +0 -0
  46. package/src/{dev/plugins → plugins}/toolbar/client/focus/focus.css +0 -0
  47. package/src/{dev/plugins → plugins}/toolbar/client/focus/toolbar_focus.js +0 -0
  48. package/src/{dev/plugins → plugins}/toolbar/client/jsenv_logo.svg +0 -0
  49. package/src/{dev/plugins → plugins}/toolbar/client/notification/toolbar_notification.js +0 -0
  50. package/src/{dev/plugins → plugins}/toolbar/client/responsive/overflow_menu.css +0 -0
  51. package/src/{dev/plugins → plugins}/toolbar/client/responsive/toolbar_responsive.js +0 -0
  52. package/src/{dev/plugins → plugins}/toolbar/client/settings/settings.css +0 -0
  53. package/src/{dev/plugins → plugins}/toolbar/client/settings/toolbar_settings.js +0 -0
  54. package/src/{dev/plugins → plugins}/toolbar/client/theme/jsenv_theme.css +0 -0
  55. package/src/{dev/plugins → plugins}/toolbar/client/theme/light_theme.css +0 -0
  56. package/src/{dev/plugins → plugins}/toolbar/client/theme/toolbar_theme.js +0 -0
  57. package/src/{dev/plugins → plugins}/toolbar/client/toolbar.html +0 -0
  58. package/src/{dev/plugins → plugins}/toolbar/client/toolbar_injector.js +0 -0
  59. package/src/{dev/plugins → plugins}/toolbar/client/toolbar_main.css +0 -0
  60. package/src/{dev/plugins → plugins}/toolbar/client/toolbar_main.js +0 -0
  61. package/src/{dev/plugins → plugins}/toolbar/client/tooltip/tooltip.css +0 -0
  62. package/src/{dev/plugins → plugins}/toolbar/client/tooltip/tooltip.js +0 -0
  63. package/src/{dev/plugins → plugins}/toolbar/client/util/animation.js +0 -0
  64. package/src/{dev/plugins → plugins}/toolbar/client/util/dom.js +0 -0
  65. package/src/{dev/plugins → plugins}/toolbar/client/util/fetch_using_xhr.js +0 -0
  66. package/src/{dev/plugins → plugins}/toolbar/client/util/fetching.js +0 -0
  67. package/src/{dev/plugins → plugins}/toolbar/client/util/iframe_to_parent_href.js +0 -0
  68. package/src/{dev/plugins → plugins}/toolbar/client/util/jsenv_logger.js +0 -0
  69. package/src/{dev/plugins → plugins}/toolbar/client/util/preferences.js +0 -0
  70. package/src/{dev/plugins → plugins}/toolbar/client/util/responsive.js +0 -0
  71. package/src/{dev/plugins → plugins}/toolbar/client/util/util.js +0 -0
  72. package/src/{dev/plugins → plugins}/toolbar/client/variant/variant.js +0 -0
  73. package/src/{dev/plugins → plugins}/toolbar/jsenv_plugin_toolbar.js +0 -0
  74. package/src/plugins/transpilation/as_js_classic/jsenv_plugin_as_js_classic_html.js +4 -3
  75. package/src/plugins/transpilation/babel/new_stylesheet/client/new_stylesheet.js +25 -55
  76. package/src/plugins/transpilation/import_assertions/jsenv_plugin_import_assertions.js +44 -24
  77. package/src/plugins/transpilation/jsenv_plugin_transpilation.js +6 -1
  78. package/src/plugins/url_analysis/html/html_urls.js +8 -8
  79. package/src/plugins/url_analysis/jsenv_plugin_url_analysis.js +3 -1
  80. package/src/test/execute_plan.js +36 -54
  81. package/src/test/execute_test_plan.js +2 -2
  82. package/src/test/logs_file_execution.js +60 -27
  83. package/src/test/logs_file_execution.test.mjs +41 -0
  84. package/dist/js/event_source_client.js +0 -528
  85. package/src/helpers/event_source/sse_service.js +0 -53
  86. package/src/plugins/autoreload/dev_sse/client/event_source_client.js +0 -193
  87. package/src/plugins/autoreload/dev_sse/jsenv_plugin_dev_sse_server.js +0 -203
  88. package/src/plugins/html_supervisor/client/error_in_document.js +0 -198
@@ -1,53 +0,0 @@
1
- import { createSSERoom } from "@jsenv/server"
2
- import { createCallbackListNotifiedOnce } from "@jsenv/abort"
3
-
4
- export const createSSEService = ({ serverEventCallbackList }) => {
5
- const destroyCallbackList = createCallbackListNotifiedOnce()
6
-
7
- const cache = []
8
- const sseRoomLimit = 100
9
- const getOrCreateSSERoom = (request) => {
10
- const htmlFileRelativeUrl = request.ressource.slice(1)
11
- const cacheEntry = cache.find(
12
- (cacheEntryCandidate) =>
13
- cacheEntryCandidate.htmlFileRelativeUrl === htmlFileRelativeUrl,
14
- )
15
- if (cacheEntry) {
16
- return cacheEntry.sseRoom
17
- }
18
- const sseRoom = createSSERoom({
19
- retryDuration: 2000,
20
- historyLength: 100,
21
- welcomeEventEnabled: true,
22
- effect: () => {
23
- return serverEventCallbackList.add((event) => {
24
- sseRoom.sendEvent(event)
25
- })
26
- },
27
- })
28
- const removeSSECleanupCallback = destroyCallbackList.add(() => {
29
- removeSSECleanupCallback()
30
- sseRoom.close()
31
- })
32
- cache.push({
33
- htmlFileRelativeUrl,
34
- sseRoom,
35
- cleanup: () => {
36
- removeSSECleanupCallback()
37
- sseRoom.close()
38
- },
39
- })
40
- if (cache.length >= sseRoomLimit) {
41
- const firstCacheEntry = cache.shift()
42
- firstCacheEntry.cleanup()
43
- }
44
- return sseRoom
45
- }
46
-
47
- return {
48
- getOrCreateSSERoom,
49
- destroy: () => {
50
- destroyCallbackList.notify()
51
- },
52
- }
53
- }
@@ -1,193 +0,0 @@
1
- import { createEventSourceConnection } from "@jsenv/core/src/helpers/event_source/event_source.js"
2
- import { urlHotMetas } from "../../../import_meta_hot/client/import_meta_hot.js"
3
- import {
4
- isAutoreloadEnabled,
5
- setAutoreloadPreference,
6
- } from "./autoreload_preference.js"
7
- import { compareTwoUrlPaths } from "./url_helpers.js"
8
- import {
9
- reloadHtmlPage,
10
- reloadDOMNodesUsingUrl,
11
- reloadJsImport,
12
- } from "./reload.js"
13
-
14
- const reloadMessages = []
15
- const reloadMessagesSignal = { onchange: () => {} }
16
- let pendingCallbacks = []
17
- let running = false
18
- const addToHotQueue = async (callback) => {
19
- pendingCallbacks.push(callback)
20
- dequeue()
21
- }
22
- const dequeue = async () => {
23
- if (running) {
24
- return
25
- }
26
- const callbacks = pendingCallbacks.slice()
27
- pendingCallbacks = []
28
- running = true
29
- try {
30
- await callbacks.reduce(async (previous, callback) => {
31
- await previous
32
- await callback()
33
- }, Promise.resolve())
34
- } finally {
35
- running = false
36
- if (pendingCallbacks.length) {
37
- dequeue()
38
- }
39
- }
40
- }
41
-
42
- const applyReloadMessageEffects = async () => {
43
- const someEffectIsFullReload = reloadMessages.some(
44
- (reloadMessage) => reloadMessage.type === "full",
45
- )
46
- if (someEffectIsFullReload) {
47
- reloadHtmlPage()
48
- return
49
- }
50
-
51
- const onApplied = (reloadMessage) => {
52
- const index = reloadMessages.indexOf(reloadMessage)
53
- reloadMessages.splice(index, 1)
54
- reloadMessagesSignal.onchange()
55
- }
56
- const setReloadMessagePromise = (reloadMessage, promise) => {
57
- reloadMessage.status = "pending"
58
- promise.then(
59
- () => {
60
- onApplied(reloadMessage)
61
- },
62
- (e) => {
63
- // TODO: reuse error display from html supervisor
64
- console.error(e)
65
- console.error(
66
- `[hmr] Failed to reload after ${reloadMessage.reason}.
67
- This could be due to syntax errors or importing non-existent modules (see errors above)`,
68
- )
69
- reloadMessage.status = "failed"
70
- reloadMessagesSignal.onchange()
71
- },
72
- )
73
- }
74
- reloadMessages.forEach((reloadMessage) => {
75
- if (reloadMessage.type === "hot") {
76
- const promise = addToHotQueue(() => {
77
- return applyHotReload(reloadMessage)
78
- })
79
- setReloadMessagePromise(reloadMessage, promise)
80
- } else {
81
- setReloadMessagePromise(reloadMessage, Promise.resolve())
82
- }
83
- })
84
- reloadMessagesSignal.onchange() // reload status is "pending"
85
- }
86
-
87
- const applyHotReload = async ({ hotInstructions }) => {
88
- await hotInstructions.reduce(
89
- async (previous, { type, boundary, acceptedBy }) => {
90
- await previous
91
-
92
- const urlToFetch = new URL(boundary, `${window.location.origin}/`).href
93
- const urlHotMeta = urlHotMetas[urlToFetch]
94
- // TODO: we should return when there is no url hot meta because
95
- // it means code was not executed (code splitting with dynamic import)
96
- // if (!urlHotMeta) {return }
97
-
98
- if (type === "prune") {
99
- console.groupCollapsed(
100
- `[jsenv] prune: ${boundary} (inside ${acceptedBy})`,
101
- )
102
- } else if (acceptedBy === boundary) {
103
- console.groupCollapsed(`[jsenv] hot reloading: ${boundary}`)
104
- } else {
105
- console.groupCollapsed(
106
- `[jsenv] hot reloading: ${acceptedBy} inside ${boundary}`,
107
- )
108
- }
109
- if (urlHotMeta && urlHotMeta.disposeCallback) {
110
- console.log(`call dispose callback`)
111
- await urlHotMeta.disposeCallback()
112
- }
113
- if (type === "prune") {
114
- delete urlHotMetas[urlToFetch]
115
- console.log(`cleanup pruned url`)
116
- console.groupEnd()
117
- return null
118
- }
119
- if (type === "js_module") {
120
- console.log(`importing js module`)
121
- const namespace = await reloadJsImport(urlToFetch)
122
- if (urlHotMeta && urlHotMeta.acceptCallback) {
123
- await urlHotMeta.acceptCallback(namespace)
124
- }
125
- console.log(`js module import done`)
126
- console.groupEnd()
127
- return namespace
128
- }
129
- if (type === "html") {
130
- if (!compareTwoUrlPaths(urlToFetch, window.location.href)) {
131
- // we are not in that HTML page
132
- return null
133
- }
134
- console.log(`reloading url`)
135
- const urlToReload = new URL(acceptedBy, `${window.location.origin}/`)
136
- .href
137
- reloadDOMNodesUsingUrl(urlToReload)
138
- console.log(`url reloaded`)
139
- console.groupEnd()
140
- return null
141
- }
142
- console.warn(`unknown update type: "${type}"`)
143
- return null
144
- },
145
- Promise.resolve(),
146
- )
147
- }
148
-
149
- const addReloadMessage = (reloadMessage) => {
150
- reloadMessages.push(reloadMessage)
151
- if (isAutoreloadEnabled()) {
152
- applyReloadMessageEffects()
153
- } else {
154
- reloadMessagesSignal.onchange()
155
- }
156
- }
157
-
158
- const eventsourceConnection = createEventSourceConnection(
159
- document.location.href,
160
- {
161
- reload: ({ data }) => {
162
- const reloadMessage = JSON.parse(data)
163
- addReloadMessage(reloadMessage)
164
- },
165
- },
166
- {
167
- retryMaxAttempt: Infinity,
168
- retryAllocatedMs: 20 * 1000,
169
- },
170
- )
171
-
172
- const { status, connect, disconnect } = eventsourceConnection
173
- connect()
174
- window.__jsenv_event_source_client__ = {
175
- status,
176
- connect,
177
- disconnect,
178
- isAutoreloadEnabled,
179
- setAutoreloadPreference,
180
- urlHotMetas,
181
- reloadMessages,
182
- reloadMessagesSignal,
183
- applyReloadMessageEffects,
184
- addReloadMessage,
185
- }
186
-
187
- // const findHotMetaUrl = (originalFileRelativeUrl) => {
188
- // return Object.keys(urlHotMetas).find((compileUrl) => {
189
- // return (
190
- // parseCompiledUrl(compileUrl).fileRelativeUrl === originalFileRelativeUrl
191
- // )
192
- // })
193
- // }
@@ -1,203 +0,0 @@
1
- import { urlToRelativeUrl } from "@jsenv/urls"
2
- import { createCallbackList } from "@jsenv/abort"
3
-
4
- import { createSSEService } from "@jsenv/core/src/helpers/event_source/sse_service.js"
5
-
6
- export const jsenvPluginDevSSEServer = ({
7
- rootDirectoryUrl,
8
- urlGraph,
9
- clientFileChangeCallbackList,
10
- clientFilesPruneCallbackList,
11
- }) => {
12
- const serverEventCallbackList = createCallbackList()
13
- const sseService = createSSEService({ serverEventCallbackList })
14
-
15
- const notifyDeclined = ({ cause, reason, declinedBy }) => {
16
- serverEventCallbackList.notify({
17
- type: "reload",
18
- data: JSON.stringify({
19
- cause,
20
- type: "full",
21
- typeReason: reason,
22
- declinedBy,
23
- }),
24
- })
25
- }
26
- const notifyAccepted = ({ cause, reason, instructions }) => {
27
- serverEventCallbackList.notify({
28
- type: "reload",
29
- data: JSON.stringify({
30
- cause,
31
- type: "hot",
32
- typeReason: reason,
33
- hotInstructions: instructions,
34
- }),
35
- })
36
- }
37
- const propagateUpdate = (firstUrlInfo) => {
38
- const iterate = (urlInfo, trace) => {
39
- if (urlInfo.data.hotAcceptSelf) {
40
- return {
41
- accepted: true,
42
- reason:
43
- urlInfo === firstUrlInfo
44
- ? `file accepts hot reload`
45
- : `a dependent file accepts hot reload`,
46
- instructions: [
47
- {
48
- type: urlInfo.type,
49
- boundary: urlToRelativeUrl(urlInfo.url, rootDirectoryUrl),
50
- acceptedBy: urlToRelativeUrl(urlInfo.url, rootDirectoryUrl),
51
- },
52
- ],
53
- }
54
- }
55
- const { dependents } = urlInfo
56
- const instructions = []
57
- for (const dependentUrl of dependents) {
58
- const dependentUrlInfo = urlGraph.getUrlInfo(dependentUrl)
59
- if (dependentUrlInfo.data.hotDecline) {
60
- return {
61
- declined: true,
62
- reason: `a dependent file declines hot reload`,
63
- declinedBy: dependentUrl,
64
- }
65
- }
66
- const { hotAcceptDependencies = [] } = dependentUrlInfo.data
67
- if (hotAcceptDependencies.includes(urlInfo.url)) {
68
- instructions.push({
69
- type: dependentUrlInfo.type,
70
- boundary: urlToRelativeUrl(dependentUrl, rootDirectoryUrl),
71
- acceptedBy: urlToRelativeUrl(urlInfo.url, rootDirectoryUrl),
72
- })
73
- continue
74
- }
75
- if (trace.includes(dependentUrl)) {
76
- return {
77
- declined: true,
78
- reason: "circular dependency",
79
- declinedBy: urlToRelativeUrl(dependentUrl, rootDirectoryUrl),
80
- }
81
- }
82
- const dependentPropagationResult = iterate(dependentUrlInfo, [
83
- ...trace,
84
- dependentUrl,
85
- ])
86
- if (dependentPropagationResult.accepted) {
87
- instructions.push(...dependentPropagationResult.instructions)
88
- continue
89
- }
90
- if (
91
- // declined explicitely by an other file, it must decline the whole update
92
- dependentPropagationResult.declinedBy
93
- ) {
94
- return dependentPropagationResult
95
- }
96
- // declined by absence of boundary, we can keep searching
97
- continue
98
- }
99
- if (instructions.length === 0) {
100
- return {
101
- declined: true,
102
- reason: `there is no file accepting hot reload while propagating update`,
103
- }
104
- }
105
- return {
106
- accepted: true,
107
- reason: `${instructions.length} dependent file(s) accepts hot reload`,
108
- instructions,
109
- }
110
- }
111
- const trace = []
112
- return iterate(firstUrlInfo, trace)
113
- }
114
- clientFileChangeCallbackList.push(({ url, event }) => {
115
- const urlInfo = urlGraph.getUrlInfo(url)
116
- // file not part of dependency graph
117
- if (!urlInfo) {
118
- return
119
- }
120
- const relativeUrl = urlToRelativeUrl(url, rootDirectoryUrl)
121
- const hotUpdate = propagateUpdate(urlInfo)
122
- if (hotUpdate.declined) {
123
- notifyDeclined({
124
- cause: `${relativeUrl} ${event}`,
125
- reason: hotUpdate.reason,
126
- declinedBy: hotUpdate.declinedBy,
127
- })
128
- } else {
129
- notifyAccepted({
130
- cause: `${relativeUrl} ${event}`,
131
- reason: hotUpdate.reason,
132
- instructions: hotUpdate.instructions,
133
- })
134
- }
135
- })
136
- clientFilesPruneCallbackList.push(({ prunedUrlInfos, firstUrlInfo }) => {
137
- const mainHotUpdate = propagateUpdate(firstUrlInfo)
138
- const cause = `following files are no longer referenced: ${prunedUrlInfos.map(
139
- (prunedUrlInfo) => urlToRelativeUrl(prunedUrlInfo.url, rootDirectoryUrl),
140
- )}`
141
- // now check if we can hot update the main ressource
142
- // then if we can hot update all dependencies
143
- if (mainHotUpdate.declined) {
144
- notifyDeclined({
145
- cause,
146
- reason: mainHotUpdate.reason,
147
- declinedBy: mainHotUpdate.declinedBy,
148
- })
149
- return
150
- }
151
- // main can hot update
152
- let i = 0
153
- const instructions = []
154
- while (i < prunedUrlInfos.length) {
155
- const prunedUrlInfo = prunedUrlInfos[i++]
156
- if (prunedUrlInfo.data.hotDecline) {
157
- notifyDeclined({
158
- cause,
159
- reason: `a pruned file declines hot reload`,
160
- declinedBy: urlToRelativeUrl(prunedUrlInfo.url, rootDirectoryUrl),
161
- })
162
- return
163
- }
164
- instructions.push({
165
- type: "prune",
166
- boundary: urlToRelativeUrl(prunedUrlInfo.url, rootDirectoryUrl),
167
- acceptedBy: urlToRelativeUrl(firstUrlInfo.url, rootDirectoryUrl),
168
- })
169
- }
170
- notifyAccepted({
171
- cause,
172
- reason: mainHotUpdate.reason,
173
- instructions,
174
- })
175
- })
176
-
177
- return {
178
- name: "jsenv:sse_server",
179
- appliesDuring: { dev: true },
180
- serve: (request) => {
181
- if (request.ressource === "/__graph__") {
182
- const graphJson = JSON.stringify(urlGraph.toJSON(rootDirectoryUrl))
183
- return {
184
- status: 200,
185
- headers: {
186
- "content-type": "application/json",
187
- "content-length": Buffer.byteLength(graphJson),
188
- },
189
- body: graphJson,
190
- }
191
- }
192
- const { accept } = request.headers
193
- if (accept && accept.includes("text/event-stream")) {
194
- const room = sseService.getOrCreateSSERoom(request)
195
- return room.join(request)
196
- }
197
- return null
198
- },
199
- destroy: () => {
200
- sseService.destroy()
201
- },
202
- }
203
- }
@@ -1,198 +0,0 @@
1
- export const displayErrorInDocument = (error) => {
2
- const title = "An error occured"
3
- let theme =
4
- error && error.cause && error.cause.code === "PARSE_ERROR"
5
- ? "light"
6
- : "dark"
7
- let message = errorToHTML(error)
8
- const css = `
9
- .jsenv-console {
10
- background: rgba(0, 0, 0, 0.95);
11
- position: absolute;
12
- top: 0;
13
- left: 0;
14
- width: 100%;
15
- height: 100%;
16
- display: flex;
17
- flex-direction: column;
18
- align-items: center;
19
- z-index: 1000;
20
- box-sizing: border-box;
21
- padding: 1em;
22
- }
23
-
24
- .jsenv-console h1 {
25
- color: red;
26
- display: flex;
27
- align-items: center;
28
- }
29
-
30
- #button-close-jsenv-console {
31
- margin-left: 10px;
32
- }
33
-
34
- .jsenv-console pre {
35
- overflow: auto;
36
- max-width: 70em;
37
- /* avoid scrollbar to hide the text behind it */
38
- padding: 20px;
39
- }
40
-
41
- .jsenv-console pre[data-theme="dark"] {
42
- background: #111;
43
- border: 1px solid #333;
44
- color: #eee;
45
- }
46
-
47
- .jsenv-console pre[data-theme="light"] {
48
- background: #1E1E1E;
49
- border: 1px solid white;
50
- color: #EEEEEE;
51
- }
52
-
53
- .jsenv-console pre a {
54
- color: inherit;
55
- }
56
- `
57
- const html = `
58
- <style type="text/css">${css}></style>
59
- <div class="jsenv-console">
60
- <h1>${title} <button id="button-close-jsenv-console">X</button></h1>
61
- <pre data-theme="${theme}">${message}</pre>
62
- </div>
63
- `
64
- const removeJsenvConsole = appendHMTLInside(html, document.body)
65
-
66
- document.querySelector("#button-close-jsenv-console").onclick = () => {
67
- removeJsenvConsole()
68
- }
69
- }
70
-
71
- const escapeHtml = (string) => {
72
- return string
73
- .replace(/&/g, "&amp;")
74
- .replace(/</g, "&lt;")
75
- .replace(/>/g, "&gt;")
76
- .replace(/"/g, "&quot;")
77
- .replace(/'/g, "&#039;")
78
- }
79
-
80
- const errorToHTML = (error) => {
81
- let html
82
-
83
- if (error && error instanceof Error) {
84
- if (error.cause && error.cause.code === "PARSE_ERROR") {
85
- html = error.messageHTML || escapeHtml(error.message)
86
- }
87
- // stackTrace formatted by V8
88
- else if (Error.captureStackTrace) {
89
- html = escapeHtml(error.stack)
90
- } else {
91
- // other stack trace such as firefox do not contain error.message
92
- html = escapeHtml(`${error.message}
93
- ${error.stack}`)
94
- }
95
- } else if (typeof error === "string") {
96
- html = error
97
- } else if (error === undefined) {
98
- html = "undefined"
99
- } else {
100
- html = JSON.stringify(error)
101
- }
102
-
103
- const htmlWithCorrectLineBreaks = html.replace(/\n/g, "\n")
104
- const htmlWithLinks = stringToStringWithLink(htmlWithCorrectLineBreaks, {
105
- transform: (url) => {
106
- return { href: url, text: url }
107
- },
108
- })
109
- return htmlWithLinks
110
- }
111
-
112
- // `Error: yo
113
- // at Object.execute (http://127.0.0.1:57300/build/src/__test__/file-throw.js:9:13)
114
- // at doExec (http://127.0.0.1:3000/src/__test__/file-throw.js:452:38)
115
- // at postOrderExec (http://127.0.0.1:3000/src/__test__/file-throw.js:448:16)
116
- // at http://127.0.0.1:3000/src/__test__/file-throw.js:399:18`.replace(/(?:https?|ftp|file):\/\/(.*+)$/gm, (...args) => {
117
- // debugger
118
- // })
119
- const stringToStringWithLink = (
120
- source,
121
- {
122
- transform = (url) => {
123
- return {
124
- href: url,
125
- text: url,
126
- }
127
- },
128
- } = {},
129
- ) => {
130
- return source.replace(/(?:https?|ftp|file):\/\/\S+/gm, (match) => {
131
- let linkHTML = ""
132
-
133
- const lastChar = match[match.length - 1]
134
-
135
- // hotfix because our url regex sucks a bit
136
- const endsWithSeparationChar = lastChar === ")" || lastChar === ":"
137
- if (endsWithSeparationChar) {
138
- match = match.slice(0, -1)
139
- }
140
-
141
- const lineAndColumnPattern = /:([0-9]+):([0-9]+)$/
142
- const lineAndColumMatch = match.match(lineAndColumnPattern)
143
- if (lineAndColumMatch) {
144
- const lineAndColumnString = lineAndColumMatch[0]
145
- const lineNumber = lineAndColumMatch[1]
146
- const columnNumber = lineAndColumMatch[2]
147
- const url = match.slice(0, -lineAndColumnString.length)
148
- const { href, text } = transform(url)
149
- linkHTML = link({ href, text: `${text}:${lineNumber}:${columnNumber}` })
150
- } else {
151
- const linePattern = /:([0-9]+)$/
152
- const lineMatch = match.match(linePattern)
153
- if (lineMatch) {
154
- const lineString = lineMatch[0]
155
- const lineNumber = lineMatch[1]
156
- const url = match.slice(0, -lineString.length)
157
- const { href, text } = transform(url)
158
- linkHTML = link({
159
- href,
160
- text: `${text}:${lineNumber}`,
161
- })
162
- } else {
163
- const url = match
164
- const { href, text } = transform(url)
165
- linkHTML = link({ href, text })
166
- }
167
- }
168
-
169
- if (endsWithSeparationChar) {
170
- return `${linkHTML}${lastChar}`
171
- }
172
- return linkHTML
173
- })
174
- }
175
-
176
- const link = ({ href, text = href }) => `<a href="${href}">${text}</a>`
177
-
178
- const appendHMTLInside = (html, parentNode) => {
179
- const temoraryParent = document.createElement("div")
180
- temoraryParent.innerHTML = html
181
- return transferChildren(temoraryParent, parentNode)
182
- }
183
-
184
- const transferChildren = (fromNode, toNode) => {
185
- const childNodes = [].slice.call(fromNode.childNodes, 0)
186
- let i = 0
187
- while (i < childNodes.length) {
188
- toNode.appendChild(childNodes[i])
189
- i++
190
- }
191
- return () => {
192
- let c = 0
193
- while (c < childNodes.length) {
194
- fromNode.appendChild(childNodes[c])
195
- c++
196
- }
197
- }
198
- }