@jsenv/core 24.0.2 → 24.2.2
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.
- package/dist/jsenv_event_source_client.js +338 -0
- package/dist/jsenv_event_source_client.js.map +126 -0
- package/dist/jsenv_exploring_index.js.map +7 -7
- package/dist/jsenv_redirector.js +1388 -0
- package/dist/jsenv_redirector.js.map +384 -0
- package/dist/jsenv_toolbar.js +37 -504
- package/dist/jsenv_toolbar.js.map +37 -112
- package/dist/jsenv_toolbar_injector.js +31 -126
- package/dist/jsenv_toolbar_injector.js.map +11 -30
- package/{LICENSE → license} +0 -0
- package/package.json +2 -3
- package/src/buildProject.js +2 -0
- package/src/dev_server.js +108 -57
- package/src/internal/compiling/compile-directory/getOrGenerateCompiledFile.js +1 -2
- package/src/internal/compiling/compileFile.js +51 -23
- package/src/internal/compiling/createCompiledFileService.js +8 -0
- package/src/internal/compiling/html_source_file_service.js +43 -9
- package/src/internal/compiling/jsenvCompilerForHtml.js +146 -107
- package/src/internal/compiling/startCompileServer.js +10 -1
- package/src/internal/dev_server/event_source_client/event_source_client.js +63 -0
- package/src/internal/dev_server/event_source_client/event_source_client_file_info.js +17 -0
- package/src/internal/{toolbar/eventsource/connectEventSource.js → dev_server/event_source_client/event_source_connection.js} +47 -78
- package/src/internal/dev_server/event_source_client/file_changes.js +82 -0
- package/src/internal/dev_server/event_source_client/livereload_preference.js +13 -0
- package/src/internal/{exploring → dev_server/exploring}/exploring.css +0 -0
- package/src/internal/{exploring → dev_server/exploring}/exploring.html +1 -1
- package/src/internal/{exploring → dev_server/exploring}/exploring.js +0 -0
- package/src/internal/dev_server/exploring/exploring_file_info.js +21 -0
- package/src/internal/{exploring → dev_server/exploring}/fetchExploringJson.js +1 -1
- package/src/internal/{exploring/exploring.redirector.html → dev_server/redirector/redirector.html} +1 -1
- package/src/internal/{exploring/exploring.redirector.js → dev_server/redirector/redirector.js} +4 -2
- package/src/internal/dev_server/redirector/redirector_file_info.js +24 -0
- package/src/internal/{toolbar → dev_server/toolbar}/animation/toolbar.animation.js +0 -0
- package/src/internal/{toolbar → dev_server/toolbar}/backtolist/toolbar.backtolist.js +0 -0
- package/src/internal/{toolbar → dev_server/toolbar}/compilation/compilation.css +0 -0
- package/src/internal/{toolbar → dev_server/toolbar}/compilation/toolbar.compilation.js +1 -1
- package/src/internal/{toolbar → dev_server/toolbar}/eventsource/eventsource.css +0 -0
- package/src/internal/dev_server/toolbar/eventsource/toolbar.eventsource.js +83 -0
- package/src/internal/{toolbar → dev_server/toolbar}/execution/execution.css +0 -0
- package/src/internal/{toolbar → dev_server/toolbar}/execution/toolbar.execution.js +0 -0
- package/src/internal/{toolbar → dev_server/toolbar}/focus/focus.css +0 -0
- package/src/internal/{toolbar → dev_server/toolbar}/focus/toolbar.focus.js +0 -0
- package/src/internal/{toolbar → dev_server/toolbar}/jsenv-logo.svg +0 -0
- package/src/internal/{toolbar → dev_server/toolbar}/notification/toolbar.notification.js +0 -0
- package/src/internal/{toolbar → dev_server/toolbar}/responsive/overflow-menu.css +0 -0
- package/src/internal/{toolbar → dev_server/toolbar}/responsive/toolbar.responsive.js +0 -0
- package/src/internal/{toolbar → dev_server/toolbar}/settings/settings.css +0 -0
- package/src/internal/{toolbar → dev_server/toolbar}/settings/toolbar.settings.js +0 -0
- package/src/internal/{toolbar → dev_server/toolbar}/theme/jsenv-theme.css +0 -0
- package/src/internal/{toolbar → dev_server/toolbar}/theme/light-theme.css +0 -0
- package/src/internal/{toolbar → dev_server/toolbar}/theme/toolbar.theme.js +0 -0
- package/src/internal/{toolbar → dev_server/toolbar}/toolbar.html +4 -37
- package/src/internal/{toolbar → dev_server/toolbar}/toolbar.injector.js +3 -92
- package/src/internal/{toolbar → dev_server/toolbar}/toolbar.main.css +0 -0
- package/src/internal/{toolbar → dev_server/toolbar}/toolbar.main.js +0 -0
- package/src/internal/dev_server/toolbar/toolbar_file_info.js +37 -0
- package/src/internal/{toolbar → dev_server/toolbar}/tooltip/tooltip.css +0 -0
- package/src/internal/{toolbar → dev_server/toolbar}/tooltip/tooltip.js +0 -0
- package/src/internal/{toolbar → dev_server/toolbar}/util/animation.js +0 -0
- package/src/internal/{toolbar → dev_server/toolbar}/util/dom.js +0 -0
- package/src/internal/{toolbar → dev_server/toolbar}/util/fetching.js +2 -2
- package/src/internal/{toolbar → dev_server/toolbar}/util/jsenvLogger.js +0 -0
- package/src/internal/{toolbar → dev_server/toolbar}/util/preferences.js +0 -0
- package/src/internal/{toolbar → dev_server/toolbar}/util/responsive.js +0 -0
- package/src/internal/{toolbar → dev_server/toolbar}/util/util.js +0 -0
- package/src/internal/{toolbar → dev_server/toolbar}/variant/variant.js +0 -0
- package/src/internal/jsenvInternalFiles.js +0 -58
- package/src/internal/toolbar/eventsource/connectCompileServerEventSource.js +0 -74
- package/src/internal/toolbar/eventsource/toolbar.eventsource.js +0 -239
|
@@ -2,11 +2,12 @@ import { resolveUrl, urlToRelativeUrl } from "@jsenv/filesystem"
|
|
|
2
2
|
import { moveImportMap, composeTwoImportMaps } from "@jsenv/importmap"
|
|
3
3
|
import { createDetailedMessage } from "@jsenv/logger"
|
|
4
4
|
|
|
5
|
+
import { jsenvBrowserSystemFileInfo } from "@jsenv/core/src/internal/jsenvInternalFiles.js"
|
|
6
|
+
import { eventSourceClientFileInfo } from "@jsenv/core/src/internal/dev_server/event_source_client/event_source_client_file_info.js"
|
|
5
7
|
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
} from "@jsenv/core/src/internal/jsenvInternalFiles.js"
|
|
8
|
+
toolbarInjectorFileInfo,
|
|
9
|
+
toolbarHtmlFileInfo,
|
|
10
|
+
} from "@jsenv/core/src/internal/dev_server/toolbar/toolbar_file_info.js"
|
|
10
11
|
import { fetchUrl } from "@jsenv/core/src/internal/fetchUrl.js"
|
|
11
12
|
import { getDefaultImportMap } from "@jsenv/core/src/internal/import-resolution/importmap-default.js"
|
|
12
13
|
import {
|
|
@@ -49,14 +50,20 @@ export const compileHtml = async ({
|
|
|
49
50
|
sourcemapMethod,
|
|
50
51
|
|
|
51
52
|
jsenvScriptInjection = true,
|
|
53
|
+
jsenvEventSourceClientInjection,
|
|
52
54
|
jsenvToolbarInjection,
|
|
55
|
+
onHtmlImportmapInfo,
|
|
53
56
|
}) => {
|
|
54
57
|
const jsenvBrowserBuildUrlRelativeToProject = urlToRelativeUrl(
|
|
55
58
|
jsenvBrowserSystemFileInfo.jsenvBuildUrl,
|
|
56
59
|
projectDirectoryUrl,
|
|
57
60
|
)
|
|
58
|
-
const
|
|
59
|
-
|
|
61
|
+
const eventSourceClientBuildRelativeUrlForProject = urlToRelativeUrl(
|
|
62
|
+
eventSourceClientFileInfo.buildUrl,
|
|
63
|
+
projectDirectoryUrl,
|
|
64
|
+
)
|
|
65
|
+
const toolbarInjectorBuildRelativeUrlForProject = urlToRelativeUrl(
|
|
66
|
+
toolbarInjectorFileInfo.buildUrl,
|
|
60
67
|
projectDirectoryUrl,
|
|
61
68
|
)
|
|
62
69
|
|
|
@@ -67,74 +74,158 @@ export const compileHtml = async ({
|
|
|
67
74
|
await mutateRessourceHints(htmlAst)
|
|
68
75
|
}
|
|
69
76
|
|
|
77
|
+
const urlNoSearch = urlWithoutSearch(url)
|
|
78
|
+
|
|
70
79
|
manipulateHtmlAst(htmlAst, {
|
|
71
80
|
scriptInjections: [
|
|
72
|
-
...(
|
|
81
|
+
...(urlNoSearch !== toolbarHtmlFileInfo.sourceUrl && jsenvScriptInjection
|
|
73
82
|
? [
|
|
74
83
|
{
|
|
75
84
|
src: `/${jsenvBrowserBuildUrlRelativeToProject}`,
|
|
76
85
|
},
|
|
77
86
|
]
|
|
78
87
|
: []),
|
|
79
|
-
...(
|
|
88
|
+
...(urlNoSearch !== toolbarHtmlFileInfo.sourceUrl &&
|
|
89
|
+
jsenvEventSourceClientInjection
|
|
80
90
|
? [
|
|
81
91
|
{
|
|
82
|
-
src: `/${
|
|
92
|
+
src: `/${eventSourceClientBuildRelativeUrlForProject}`,
|
|
93
|
+
},
|
|
94
|
+
]
|
|
95
|
+
: []),
|
|
96
|
+
...(urlNoSearch !== toolbarHtmlFileInfo.sourceUrl && jsenvToolbarInjection
|
|
97
|
+
? [
|
|
98
|
+
{
|
|
99
|
+
src: `/${toolbarInjectorBuildRelativeUrlForProject}`,
|
|
100
|
+
defer: "",
|
|
101
|
+
async: "",
|
|
83
102
|
},
|
|
84
103
|
]
|
|
85
104
|
: []),
|
|
86
105
|
],
|
|
87
106
|
})
|
|
88
107
|
|
|
108
|
+
let sources = []
|
|
109
|
+
let sourcesContent = []
|
|
89
110
|
const { scripts } = parseHtmlAstRessources(htmlAst)
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
let hasImportmap = false
|
|
93
|
-
const inlineScriptsContentMap = {}
|
|
94
|
-
const importmapsToInline = []
|
|
111
|
+
let importmapInfo = null
|
|
95
112
|
scripts.forEach((script) => {
|
|
96
113
|
const typeAttribute = getHtmlNodeAttributeByName(script, "type")
|
|
97
|
-
const srcAttribute = getHtmlNodeAttributeByName(script, "src")
|
|
98
|
-
|
|
99
|
-
// importmap
|
|
100
114
|
if (typeAttribute && typeAttribute.value === "importmap") {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
115
|
+
if (importmapInfo) {
|
|
116
|
+
console.error("HTML file must contain max 1 importmap")
|
|
117
|
+
} else {
|
|
118
|
+
const srcAttribute = getHtmlNodeAttributeByName(script, "src")
|
|
119
|
+
const src = srcAttribute ? srcAttribute.value : ""
|
|
120
|
+
if (src) {
|
|
121
|
+
importmapInfo = {
|
|
122
|
+
script,
|
|
123
|
+
url: resolveUrl(src, url),
|
|
124
|
+
loadAsText: async () => {
|
|
125
|
+
const importMapResponse = await fetchUrl(importmapInfo.url)
|
|
126
|
+
if (importMapResponse.status !== 200) {
|
|
127
|
+
logger.warn(
|
|
128
|
+
createDetailedMessage(
|
|
129
|
+
importMapResponse.status === 404
|
|
130
|
+
? `importmap script file cannot be found.`
|
|
131
|
+
: `importmap script file unexpected response status (${importMapResponse.status}).`,
|
|
132
|
+
{
|
|
133
|
+
"importmap url": importmapInfo.url,
|
|
134
|
+
"html url": url,
|
|
135
|
+
},
|
|
136
|
+
),
|
|
137
|
+
)
|
|
138
|
+
return "{}"
|
|
139
|
+
}
|
|
140
|
+
const importmapAsText = await importMapResponse.text()
|
|
141
|
+
sources.push(importmapInfo.url)
|
|
142
|
+
sourcesContent.push(importmapAsText)
|
|
143
|
+
|
|
144
|
+
const importMapMoved = moveImportMap(
|
|
145
|
+
JSON.parse(importmapAsText),
|
|
146
|
+
importmapInfo.url,
|
|
147
|
+
url,
|
|
148
|
+
)
|
|
149
|
+
const compiledImportmapAsText = JSON.stringify(
|
|
150
|
+
importMapMoved,
|
|
151
|
+
null,
|
|
152
|
+
" ",
|
|
153
|
+
)
|
|
154
|
+
return compiledImportmapAsText
|
|
155
|
+
},
|
|
156
|
+
}
|
|
157
|
+
} else {
|
|
158
|
+
importmapInfo = {
|
|
159
|
+
script,
|
|
160
|
+
url: compiledUrl,
|
|
161
|
+
loadAsText: () => getHtmlNodeTextNode(script).value,
|
|
162
|
+
}
|
|
107
163
|
}
|
|
108
|
-
|
|
109
|
-
// we force inline because browsers supporting importmap supports only when they are inline
|
|
110
|
-
importmapsToInline.push({
|
|
111
|
-
script,
|
|
112
|
-
src: srcAttribute.value,
|
|
113
|
-
})
|
|
114
|
-
return
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
const defaultImportMap = getDefaultImportMap({
|
|
118
|
-
importMapFileUrl: compiledUrl,
|
|
119
|
-
projectDirectoryUrl,
|
|
120
|
-
compileDirectoryRelativeUrl: `${outDirectoryRelativeUrl}${compileId}/`,
|
|
121
|
-
})
|
|
122
|
-
const inlineImportMap = JSON.parse(getHtmlNodeTextNode(script).value)
|
|
123
|
-
const mappings = composeTwoImportMaps(defaultImportMap, inlineImportMap)
|
|
124
|
-
if (moduleOutFormat === "systemjs") {
|
|
125
|
-
typeAttribute.value = "jsenv-importmap"
|
|
126
164
|
}
|
|
127
|
-
setHtmlNodeText(script, JSON.stringify(mappings, null, " "))
|
|
128
|
-
return
|
|
129
165
|
}
|
|
166
|
+
})
|
|
167
|
+
if (importmapInfo) {
|
|
168
|
+
const htmlImportMap = JSON.parse(await importmapInfo.loadAsText())
|
|
169
|
+
const importMapFromJsenv = getDefaultImportMap({
|
|
170
|
+
importMapFileUrl: compiledUrl,
|
|
171
|
+
projectDirectoryUrl,
|
|
172
|
+
compileDirectoryRelativeUrl: `${outDirectoryRelativeUrl}${compileId}/`,
|
|
173
|
+
})
|
|
174
|
+
const mappings = composeTwoImportMaps(importMapFromJsenv, htmlImportMap)
|
|
175
|
+
const importmapAsText = JSON.stringify(mappings, null, " ")
|
|
176
|
+
replaceHtmlNode(
|
|
177
|
+
importmapInfo.script,
|
|
178
|
+
`<script type="${
|
|
179
|
+
moduleOutFormat === "systemjs" ? "jsenv-importmap" : "importmap"
|
|
180
|
+
}">${importmapAsText}</script>`,
|
|
181
|
+
{
|
|
182
|
+
attributesToIgnore: ["src"],
|
|
183
|
+
},
|
|
184
|
+
)
|
|
185
|
+
importmapInfo.inlinedFrom = importmapInfo.url
|
|
186
|
+
importmapInfo.url = compiledUrl
|
|
187
|
+
importmapInfo.text = importmapAsText
|
|
188
|
+
} else {
|
|
189
|
+
// inject a default importmap
|
|
190
|
+
const defaultImportMap = getDefaultImportMap({
|
|
191
|
+
importMapFileUrl: compiledUrl,
|
|
192
|
+
projectDirectoryUrl,
|
|
193
|
+
compileDirectoryRelativeUrl: `${outDirectoryRelativeUrl}${compileId}/`,
|
|
194
|
+
})
|
|
195
|
+
const importmapAsText = JSON.stringify(defaultImportMap, null, " ")
|
|
196
|
+
manipulateHtmlAst(htmlAst, {
|
|
197
|
+
scriptInjections: [
|
|
198
|
+
{
|
|
199
|
+
type:
|
|
200
|
+
moduleOutFormat === "systemjs" ? "jsenv-importmap" : "importmap",
|
|
201
|
+
// in case there is no importmap, force the presence
|
|
202
|
+
// so that '@jsenv/core/' are still remapped
|
|
203
|
+
text: importmapAsText,
|
|
204
|
+
},
|
|
205
|
+
],
|
|
206
|
+
})
|
|
207
|
+
importmapInfo = {
|
|
208
|
+
url: compiledUrl,
|
|
209
|
+
text: importmapAsText,
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
onHtmlImportmapInfo({
|
|
213
|
+
htmlUrl: url,
|
|
214
|
+
importmapInfo,
|
|
215
|
+
})
|
|
130
216
|
|
|
217
|
+
const htmlDependencies = collectHtmlDependenciesFromAst(htmlAst)
|
|
218
|
+
const inlineScriptsContentMap = {}
|
|
219
|
+
scripts.forEach((script) => {
|
|
220
|
+
const typeAttribute = getHtmlNodeAttributeByName(script, "type")
|
|
221
|
+
const srcAttribute = getHtmlNodeAttributeByName(script, "src")
|
|
222
|
+
const src = srcAttribute ? srcAttribute.value : ""
|
|
131
223
|
// remote module script
|
|
132
|
-
if (typeAttribute && typeAttribute.value === "module" &&
|
|
224
|
+
if (typeAttribute && typeAttribute.value === "module" && src) {
|
|
133
225
|
if (moduleOutFormat === "systemjs") {
|
|
134
226
|
removeHtmlNodeAttribute(script, typeAttribute)
|
|
135
227
|
}
|
|
136
228
|
removeHtmlNodeAttribute(script, srcAttribute)
|
|
137
|
-
const src = srcAttribute.value
|
|
138
229
|
const jsenvMethod =
|
|
139
230
|
moduleOutFormat === "systemjs"
|
|
140
231
|
? "executeFileUsingSystemJs"
|
|
@@ -174,66 +265,6 @@ export const compileHtml = async ({
|
|
|
174
265
|
return
|
|
175
266
|
}
|
|
176
267
|
})
|
|
177
|
-
|
|
178
|
-
if (hasImportmap === false) {
|
|
179
|
-
const defaultImportMap = getDefaultImportMap({
|
|
180
|
-
importMapFileUrl: compiledUrl,
|
|
181
|
-
projectDirectoryUrl,
|
|
182
|
-
compileDirectoryRelativeUrl: `${outDirectoryRelativeUrl}${compileId}/`,
|
|
183
|
-
})
|
|
184
|
-
manipulateHtmlAst(htmlAst, {
|
|
185
|
-
scriptInjections: [
|
|
186
|
-
{
|
|
187
|
-
type:
|
|
188
|
-
moduleOutFormat === "systemjs" ? "jsenv-importmap" : "importmap",
|
|
189
|
-
// in case there is no importmap, force the presence
|
|
190
|
-
// so that '@jsenv/core/' are still remapped
|
|
191
|
-
text: JSON.stringify(defaultImportMap, null, " "),
|
|
192
|
-
},
|
|
193
|
-
],
|
|
194
|
-
})
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
await Promise.all(
|
|
198
|
-
importmapsToInline.map(async ({ script, src }) => {
|
|
199
|
-
const importMapUrl = resolveUrl(src, url)
|
|
200
|
-
const importMapResponse = await fetchUrl(importMapUrl)
|
|
201
|
-
if (importMapResponse.status !== 200) {
|
|
202
|
-
logger.warn(
|
|
203
|
-
createDetailedMessage(
|
|
204
|
-
importMapResponse.status === 404
|
|
205
|
-
? `Cannot inline importmap script because file cannot be found.`
|
|
206
|
-
: `Cannot inline importmap script due to unexpected response status (${importMapResponse.status}).`,
|
|
207
|
-
{
|
|
208
|
-
"importmap script src": src,
|
|
209
|
-
"importmap url": importMapUrl,
|
|
210
|
-
"html url": url,
|
|
211
|
-
},
|
|
212
|
-
),
|
|
213
|
-
)
|
|
214
|
-
return
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
const importMapContent = await importMapResponse.json()
|
|
218
|
-
const importMapInlined = moveImportMap(
|
|
219
|
-
importMapContent,
|
|
220
|
-
importMapUrl,
|
|
221
|
-
url,
|
|
222
|
-
)
|
|
223
|
-
replaceHtmlNode(
|
|
224
|
-
script,
|
|
225
|
-
`<script type="importmap">${JSON.stringify(
|
|
226
|
-
importMapInlined,
|
|
227
|
-
null,
|
|
228
|
-
" ",
|
|
229
|
-
)}</script>`,
|
|
230
|
-
{
|
|
231
|
-
attributesToIgnore: ["src"],
|
|
232
|
-
},
|
|
233
|
-
)
|
|
234
|
-
}),
|
|
235
|
-
)
|
|
236
|
-
|
|
237
268
|
const htmlAfterTransformation = stringifyHtmlAst(htmlAst)
|
|
238
269
|
|
|
239
270
|
let assets = []
|
|
@@ -303,12 +334,14 @@ export const compileHtml = async ({
|
|
|
303
334
|
}
|
|
304
335
|
}),
|
|
305
336
|
)
|
|
337
|
+
sources.push(url)
|
|
338
|
+
sourcesContent.push(code)
|
|
306
339
|
|
|
307
340
|
return {
|
|
308
341
|
contentType: "text/html",
|
|
309
342
|
compiledSource: htmlAfterTransformation,
|
|
310
|
-
sources
|
|
311
|
-
sourcesContent
|
|
343
|
+
sources,
|
|
344
|
+
sourcesContent,
|
|
312
345
|
assets,
|
|
313
346
|
assetsContent,
|
|
314
347
|
dependencies: htmlDependencies.map(({ specifier }) => {
|
|
@@ -372,3 +405,9 @@ const mutateRessourceHints = async (htmlAst) => {
|
|
|
372
405
|
)
|
|
373
406
|
mutations.forEach((mutation) => mutation())
|
|
374
407
|
}
|
|
408
|
+
|
|
409
|
+
const urlWithoutSearch = (url) => {
|
|
410
|
+
const urlObject = new URL(url)
|
|
411
|
+
urlObject.search = ""
|
|
412
|
+
return urlObject.href
|
|
413
|
+
}
|
|
@@ -104,8 +104,9 @@ export const startCompileServer = async ({
|
|
|
104
104
|
plugins,
|
|
105
105
|
livereloadSSE = false,
|
|
106
106
|
transformHtmlSourceFiles = true,
|
|
107
|
-
jsenvToolbarInjection = false,
|
|
108
107
|
jsenvScriptInjection = true,
|
|
108
|
+
jsenvEventSourceClientInjection = false,
|
|
109
|
+
jsenvToolbarInjection = false,
|
|
109
110
|
inlineImportMapIntoHTML = true,
|
|
110
111
|
}) => {
|
|
111
112
|
assertArguments({
|
|
@@ -312,6 +313,7 @@ export const startCompileServer = async ({
|
|
|
312
313
|
customCompilers,
|
|
313
314
|
moduleOutFormat,
|
|
314
315
|
importMetaFormat,
|
|
316
|
+
jsenvEventSourceClientInjection,
|
|
315
317
|
jsenvToolbarInjection,
|
|
316
318
|
|
|
317
319
|
projectFileRequestedCallback,
|
|
@@ -327,8 +329,10 @@ export const startCompileServer = async ({
|
|
|
327
329
|
createTransformHtmlSourceFileService({
|
|
328
330
|
logger,
|
|
329
331
|
projectDirectoryUrl,
|
|
332
|
+
projectFileRequestedCallback,
|
|
330
333
|
inlineImportMapIntoHTML,
|
|
331
334
|
jsenvScriptInjection,
|
|
335
|
+
jsenvEventSourceClientInjection,
|
|
332
336
|
jsenvToolbarInjection,
|
|
333
337
|
}),
|
|
334
338
|
}
|
|
@@ -583,6 +587,9 @@ const setupServerSentEventsForLivereload = ({
|
|
|
583
587
|
const projectFileAdded = createCallbackList()
|
|
584
588
|
|
|
585
589
|
const projectFileRequestedCallback = (relativeUrl, request) => {
|
|
590
|
+
if (relativeUrl[0] === "/") {
|
|
591
|
+
relativeUrl = relativeUrl.slice(1)
|
|
592
|
+
}
|
|
586
593
|
const url = `${projectDirectoryUrl}${relativeUrl}`
|
|
587
594
|
|
|
588
595
|
if (
|
|
@@ -669,6 +676,7 @@ const setupServerSentEventsForLivereload = ({
|
|
|
669
676
|
return
|
|
670
677
|
}
|
|
671
678
|
|
|
679
|
+
livereloadLogger.debug(`${rootRelativeUrl} requested -> start tracking it`)
|
|
672
680
|
// when a file is requested, always rebuild its dependency in case it has changed
|
|
673
681
|
// since the last time it was requested
|
|
674
682
|
startTrackingRoot(rootRelativeUrl)
|
|
@@ -699,6 +707,7 @@ const setupServerSentEventsForLivereload = ({
|
|
|
699
707
|
const removeRootRemovedCallback = projectFileRemoved.add((relativeUrl) => {
|
|
700
708
|
if (relativeUrl === rootRelativeUrl) {
|
|
701
709
|
stopTrackingRoot(rootRelativeUrl)
|
|
710
|
+
livereloadLogger.debug(`${rootRelativeUrl} removed -> stop tracking it`)
|
|
702
711
|
}
|
|
703
712
|
})
|
|
704
713
|
addStopTrackingCalback(rootRelativeUrl, removeRootRemovedCallback)
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/* eslint-env browser */
|
|
2
|
+
|
|
3
|
+
import { createEventSourceConnection } from "./event_source_connection.js"
|
|
4
|
+
import {
|
|
5
|
+
getFileChanges,
|
|
6
|
+
addFileChange,
|
|
7
|
+
setFileChangeCallback,
|
|
8
|
+
reloadIfNeeded,
|
|
9
|
+
} from "./file_changes.js"
|
|
10
|
+
import {
|
|
11
|
+
isLivereloadEnabled,
|
|
12
|
+
setLivereloadPreference,
|
|
13
|
+
} from "./livereload_preference.js"
|
|
14
|
+
|
|
15
|
+
const eventsourceConnection = createEventSourceConnection(
|
|
16
|
+
document.location.href,
|
|
17
|
+
{
|
|
18
|
+
"file-added": ({ data }) => {
|
|
19
|
+
addFileChange({
|
|
20
|
+
file: data,
|
|
21
|
+
eventType: "added",
|
|
22
|
+
})
|
|
23
|
+
},
|
|
24
|
+
"file-modified": ({ data }) => {
|
|
25
|
+
addFileChange({
|
|
26
|
+
file: data,
|
|
27
|
+
eventType: "modified",
|
|
28
|
+
})
|
|
29
|
+
},
|
|
30
|
+
"file-removed": ({ data }) => {
|
|
31
|
+
addFileChange({
|
|
32
|
+
file: data,
|
|
33
|
+
eventType: "removed",
|
|
34
|
+
})
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
retryMaxAttempt: Infinity,
|
|
39
|
+
retryAllocatedMs: 20 * 1000,
|
|
40
|
+
},
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
const {
|
|
44
|
+
connect,
|
|
45
|
+
disconnect,
|
|
46
|
+
setConnectionStatusChangeCallback,
|
|
47
|
+
getConnectionStatus,
|
|
48
|
+
} = eventsourceConnection
|
|
49
|
+
|
|
50
|
+
connect()
|
|
51
|
+
|
|
52
|
+
window.__jsenv_event_source_client__ = {
|
|
53
|
+
connect,
|
|
54
|
+
disconnect,
|
|
55
|
+
getConnectionStatus,
|
|
56
|
+
setConnectionStatusChangeCallback,
|
|
57
|
+
getFileChanges,
|
|
58
|
+
addFileChange,
|
|
59
|
+
setFileChangeCallback,
|
|
60
|
+
reloadIfNeeded,
|
|
61
|
+
isLivereloadEnabled,
|
|
62
|
+
setLivereloadPreference,
|
|
63
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsenvCoreDirectoryUrl } from "@jsenv/core/src/internal/jsenvCoreDirectoryUrl.js"
|
|
2
|
+
|
|
3
|
+
const sourceRelativeUrl =
|
|
4
|
+
"./src/internal/dev_server/event_source_client/event_source_client.js"
|
|
5
|
+
const buildRelativeUrl = "./jsenv_event_source_client.js"
|
|
6
|
+
const sourceUrl = new URL(sourceRelativeUrl, jsenvCoreDirectoryUrl).href
|
|
7
|
+
const buildUrl = new URL(
|
|
8
|
+
"./dist/jsenv_event_source_client.js",
|
|
9
|
+
jsenvCoreDirectoryUrl,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
export const eventSourceClientFileInfo = {
|
|
13
|
+
sourceRelativeUrl,
|
|
14
|
+
buildRelativeUrl,
|
|
15
|
+
sourceUrl,
|
|
16
|
+
buildUrl,
|
|
17
|
+
}
|
|
@@ -1,15 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
/* eslint-env browser */
|
|
2
|
+
|
|
3
|
+
export const createEventSourceConnection = (
|
|
2
4
|
eventSourceUrl,
|
|
3
5
|
events = {},
|
|
4
|
-
{
|
|
5
|
-
connecting = () => {},
|
|
6
|
-
connected = () => {},
|
|
7
|
-
cancelled = () => {},
|
|
8
|
-
failed = () => {},
|
|
9
|
-
retryMaxAttempt = Infinity,
|
|
10
|
-
retryAllocatedMs = Infinity,
|
|
11
|
-
lastEventId,
|
|
12
|
-
} = {},
|
|
6
|
+
{ retryMaxAttempt = Infinity, retryAllocatedMs = Infinity, lastEventId } = {},
|
|
13
7
|
) => {
|
|
14
8
|
const { EventSource } = window
|
|
15
9
|
if (typeof EventSource !== "function") {
|
|
@@ -18,82 +12,40 @@ export const connectEventSource = (
|
|
|
18
12
|
|
|
19
13
|
const eventSourceOrigin = new URL(eventSourceUrl).origin
|
|
20
14
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
let
|
|
15
|
+
let connectionStatus = "default"
|
|
16
|
+
let connectionStatusChangeCallback = () => {}
|
|
17
|
+
let disconnect = () => {}
|
|
24
18
|
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
? addLastEventIdIntoUrlSearchParams(eventSourceUrl, lastEventId)
|
|
29
|
-
: eventSourceUrl,
|
|
30
|
-
)
|
|
19
|
+
const goToStatus = (newStatus) => {
|
|
20
|
+
connectionStatus = newStatus
|
|
21
|
+
connectionStatusChangeCallback()
|
|
31
22
|
}
|
|
32
23
|
|
|
33
24
|
const attemptConnection = (url) => {
|
|
34
25
|
const eventSource = new EventSource(url, {
|
|
35
26
|
withCredentials: true,
|
|
36
27
|
})
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
28
|
+
disconnect = () => {
|
|
29
|
+
if (
|
|
30
|
+
connectionStatus !== "connecting" &&
|
|
31
|
+
connectionStatus !== "connected"
|
|
32
|
+
) {
|
|
33
|
+
console.warn(
|
|
34
|
+
`disconnect() ignored because connection is ${connectionStatus}`,
|
|
35
|
+
)
|
|
42
36
|
return
|
|
43
37
|
}
|
|
44
|
-
connectionStatus = "aborted"
|
|
45
38
|
eventSource.onerror = undefined
|
|
46
39
|
eventSource.close()
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
cancelCurrentConnection = abort
|
|
50
|
-
connecting({ cancel: abort })
|
|
51
|
-
|
|
52
|
-
eventSource.onopen = () => {
|
|
53
|
-
connectionStatus = "connected"
|
|
54
|
-
const disconnect = () => {
|
|
55
|
-
if (connectionStatus !== "connected") {
|
|
56
|
-
console.warn(
|
|
57
|
-
`disconnect ignored because connection is ${connectionStatus}`,
|
|
58
|
-
)
|
|
59
|
-
return
|
|
60
|
-
}
|
|
61
|
-
connectionStatus = "disconnected"
|
|
62
|
-
eventSource.onerror = undefined
|
|
63
|
-
eventSource.close()
|
|
64
|
-
cancelled({ connect: reconnect })
|
|
65
|
-
}
|
|
66
|
-
cancelCurrentConnection = disconnect
|
|
67
|
-
connected({ cancel: disconnect })
|
|
40
|
+
goToStatus("disconnected")
|
|
68
41
|
}
|
|
69
|
-
|
|
70
42
|
let retryCount = 0
|
|
71
43
|
let firstRetryMs = Date.now()
|
|
72
|
-
|
|
73
44
|
eventSource.onerror = (errorEvent) => {
|
|
74
|
-
const considerFailed = () => {
|
|
75
|
-
connectionStatus = "disconnected"
|
|
76
|
-
failed({
|
|
77
|
-
cancel: () => {
|
|
78
|
-
if (connectionStatus !== "failed") {
|
|
79
|
-
console.warn(
|
|
80
|
-
`disable ignored because connection is ${connectionStatus}`,
|
|
81
|
-
)
|
|
82
|
-
return
|
|
83
|
-
}
|
|
84
|
-
connectionStatus = "disabled"
|
|
85
|
-
cancelled({ connect: reconnect })
|
|
86
|
-
},
|
|
87
|
-
connect: reconnect,
|
|
88
|
-
})
|
|
89
|
-
}
|
|
90
|
-
|
|
91
45
|
if (errorEvent.target.readyState === EventSource.CONNECTING) {
|
|
92
46
|
if (retryCount > retryMaxAttempt) {
|
|
93
47
|
console.info(`could not connect after ${retryMaxAttempt} attempt`)
|
|
94
|
-
|
|
95
|
-
eventSource.close()
|
|
96
|
-
considerFailed()
|
|
48
|
+
disconnect()
|
|
97
49
|
return
|
|
98
50
|
}
|
|
99
51
|
|
|
@@ -105,24 +57,24 @@ export const connectEventSource = (
|
|
|
105
57
|
console.info(
|
|
106
58
|
`could not connect in less than ${retryAllocatedMs} ms`,
|
|
107
59
|
)
|
|
108
|
-
|
|
109
|
-
eventSource.close()
|
|
110
|
-
considerFailed()
|
|
60
|
+
disconnect()
|
|
111
61
|
return
|
|
112
62
|
}
|
|
113
63
|
}
|
|
114
64
|
|
|
115
|
-
connectionStatus = "connecting"
|
|
116
65
|
retryCount++
|
|
117
|
-
connecting
|
|
66
|
+
goToStatus("connecting")
|
|
118
67
|
return
|
|
119
68
|
}
|
|
120
69
|
|
|
121
70
|
if (errorEvent.target.readyState === EventSource.CLOSED) {
|
|
122
|
-
|
|
71
|
+
disconnect()
|
|
123
72
|
return
|
|
124
73
|
}
|
|
125
74
|
}
|
|
75
|
+
eventSource.onopen = () => {
|
|
76
|
+
goToStatus("connected")
|
|
77
|
+
}
|
|
126
78
|
Object.keys(events).forEach((eventName) => {
|
|
127
79
|
eventSource.addEventListener(eventName, (e) => {
|
|
128
80
|
if (e.origin === eventSourceOrigin) {
|
|
@@ -140,21 +92,38 @@ export const connectEventSource = (
|
|
|
140
92
|
}
|
|
141
93
|
})
|
|
142
94
|
}
|
|
95
|
+
goToStatus("connecting")
|
|
143
96
|
}
|
|
144
97
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
98
|
+
let connect = () => {
|
|
99
|
+
attemptConnection(eventSourceUrl)
|
|
100
|
+
connect = () => {
|
|
101
|
+
attemptConnection(
|
|
102
|
+
lastEventId
|
|
103
|
+
? addLastEventIdIntoUrlSearchParams(eventSourceUrl, lastEventId)
|
|
104
|
+
: eventSourceUrl,
|
|
105
|
+
)
|
|
106
|
+
}
|
|
148
107
|
}
|
|
149
108
|
|
|
150
109
|
const removePageUnloadListener = listenPageUnload(() => {
|
|
151
110
|
disconnect()
|
|
152
111
|
})
|
|
153
112
|
|
|
154
|
-
|
|
113
|
+
const destroy = () => {
|
|
155
114
|
removePageUnloadListener()
|
|
156
115
|
disconnect()
|
|
157
116
|
}
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
getConnectionStatus: () => connectionStatus,
|
|
120
|
+
setConnectionStatusCallback: (callback) => {
|
|
121
|
+
connectionStatusChangeCallback = callback
|
|
122
|
+
},
|
|
123
|
+
connect,
|
|
124
|
+
disconnect,
|
|
125
|
+
destroy,
|
|
126
|
+
}
|
|
158
127
|
}
|
|
159
128
|
|
|
160
129
|
const addLastEventIdIntoUrlSearchParams = (url, lastEventId) => {
|