@jsenv/core 24.0.0 → 24.2.0
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 +1386 -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/package.json +1 -1
- package/src/buildProject.js +2 -0
- package/src/dev_server.js +108 -70
- package/src/internal/compiling/compile-directory/getOrGenerateCompiledFile.js +1 -2
- package/src/internal/compiling/compileFile.js +51 -23
- package/src/internal/compiling/createCompiledFileService.js +22 -10
- 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 +60 -44
- 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} +1 -1
- 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({
|
|
@@ -239,14 +240,14 @@ export const startCompileServer = async ({
|
|
|
239
240
|
})
|
|
240
241
|
customServices = {
|
|
241
242
|
...customServices,
|
|
242
|
-
"
|
|
243
|
+
"jsenv:sse": serveSSEForLivereload,
|
|
243
244
|
}
|
|
244
245
|
} else {
|
|
245
246
|
const roomWhenLivereloadIsDisabled = createSSERoom()
|
|
246
247
|
roomWhenLivereloadIsDisabled.open()
|
|
247
248
|
customServices = {
|
|
248
249
|
...customServices,
|
|
249
|
-
"
|
|
250
|
+
"jsenv:sse": (request) => {
|
|
250
251
|
const { accept } = request.headers
|
|
251
252
|
if (!accept || !accept.includes("text/event-stream")) {
|
|
252
253
|
return null
|
|
@@ -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
|
}
|
|
@@ -389,6 +393,7 @@ export const startCompileServer = async ({
|
|
|
389
393
|
...compileServer,
|
|
390
394
|
compileServerGroupMap,
|
|
391
395
|
babelPluginMap,
|
|
396
|
+
projectFileRequestedCallback,
|
|
392
397
|
}
|
|
393
398
|
}
|
|
394
399
|
|
|
@@ -582,6 +587,9 @@ const setupServerSentEventsForLivereload = ({
|
|
|
582
587
|
const projectFileAdded = createCallbackList()
|
|
583
588
|
|
|
584
589
|
const projectFileRequestedCallback = (relativeUrl, request) => {
|
|
590
|
+
if (relativeUrl[0] === "/") {
|
|
591
|
+
relativeUrl = relativeUrl.slice(1)
|
|
592
|
+
}
|
|
585
593
|
const url = `${projectDirectoryUrl}${relativeUrl}`
|
|
586
594
|
|
|
587
595
|
if (
|
|
@@ -627,62 +635,82 @@ const setupServerSentEventsForLivereload = ({
|
|
|
627
635
|
clearTimeout(timeout)
|
|
628
636
|
})
|
|
629
637
|
|
|
630
|
-
const
|
|
631
|
-
|
|
632
|
-
|
|
638
|
+
const startTrackingRoot = (rootFile) => {
|
|
639
|
+
stopTrackingRoot(rootFile)
|
|
640
|
+
const set = new Set()
|
|
641
|
+
set.add(rootFile)
|
|
642
|
+
const depInfo = {
|
|
643
|
+
set,
|
|
644
|
+
cleanup: [],
|
|
645
|
+
}
|
|
646
|
+
trackerMap.set(rootFile, depInfo)
|
|
647
|
+
return depInfo
|
|
648
|
+
}
|
|
649
|
+
const addStopTrackingCalback = (rootFile, callback) => {
|
|
650
|
+
trackerMap.get(rootFile).cleanup.push(callback)
|
|
651
|
+
}
|
|
652
|
+
const stopTrackingRoot = (rootFile) => {
|
|
653
|
+
const depInfo = trackerMap.get(rootFile)
|
|
654
|
+
if (depInfo) {
|
|
655
|
+
depInfo.cleanup.forEach((cb) => {
|
|
656
|
+
cb()
|
|
657
|
+
})
|
|
658
|
+
trackerMap.delete(rootFile)
|
|
633
659
|
}
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
trackerMap.
|
|
637
|
-
return
|
|
660
|
+
}
|
|
661
|
+
const isDependencyOf = (file, rootFile) => {
|
|
662
|
+
const depInfo = trackerMap.get(rootFile)
|
|
663
|
+
return depInfo && depInfo.set.has(file)
|
|
664
|
+
}
|
|
665
|
+
const markAsDependencyOf = (file, rootFile) => {
|
|
666
|
+
trackerMap.get(rootFile).set.add(file)
|
|
638
667
|
}
|
|
639
668
|
|
|
640
669
|
// each time a file is requested for the first time its dependencySet is computed
|
|
641
|
-
projectFileRequested.add((
|
|
670
|
+
projectFileRequested.add((requestInfo) => {
|
|
671
|
+
const rootRelativeUrl = requestInfo.relativeUrl
|
|
642
672
|
// for now no use case of livereloading on node.js
|
|
643
673
|
// and for browsers only html file can be main files
|
|
644
674
|
// this avoid collecting dependencies of non html files that will never be used
|
|
645
|
-
if (!
|
|
675
|
+
if (!rootRelativeUrl.endsWith(".html")) {
|
|
646
676
|
return
|
|
647
677
|
}
|
|
648
678
|
|
|
679
|
+
livereloadLogger.debug(`${rootRelativeUrl} requested -> start tracking it`)
|
|
649
680
|
// when a file is requested, always rebuild its dependency in case it has changed
|
|
650
681
|
// since the last time it was requested
|
|
651
|
-
|
|
652
|
-
dependencySet.add(mainRelativeUrl)
|
|
653
|
-
trackerMap.set(mainRelativeUrl, dependencySet)
|
|
682
|
+
startTrackingRoot(rootRelativeUrl)
|
|
654
683
|
|
|
655
684
|
const removeDependencyRequestedCallback = projectFileRequested.add(
|
|
656
685
|
({ relativeUrl, request }) => {
|
|
657
|
-
if (
|
|
686
|
+
if (isDependencyOf(relativeUrl, rootRelativeUrl)) {
|
|
658
687
|
return
|
|
659
688
|
}
|
|
660
|
-
|
|
661
689
|
const dependencyReport = reportDependency(
|
|
662
690
|
relativeUrl,
|
|
663
|
-
|
|
691
|
+
rootRelativeUrl,
|
|
664
692
|
request,
|
|
665
693
|
)
|
|
666
694
|
if (dependencyReport.dependency === false) {
|
|
667
695
|
livereloadLogger.debug(
|
|
668
|
-
`${relativeUrl} not a dependency of ${
|
|
696
|
+
`${relativeUrl} not a dependency of ${rootRelativeUrl} because ${dependencyReport.reason}`,
|
|
669
697
|
)
|
|
670
698
|
return
|
|
671
699
|
}
|
|
672
|
-
|
|
673
700
|
livereloadLogger.debug(
|
|
674
|
-
`${relativeUrl} is a dependency of ${
|
|
701
|
+
`${relativeUrl} is a dependency of ${rootRelativeUrl} because ${dependencyReport.reason}`,
|
|
675
702
|
)
|
|
676
|
-
|
|
703
|
+
markAsDependencyOf(relativeUrl, rootRelativeUrl)
|
|
677
704
|
},
|
|
678
705
|
)
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
706
|
+
addStopTrackingCalback(rootRelativeUrl, removeDependencyRequestedCallback)
|
|
707
|
+
const removeRootRemovedCallback = projectFileRemoved.add((relativeUrl) => {
|
|
708
|
+
if (relativeUrl === rootRelativeUrl) {
|
|
709
|
+
stopTrackingRoot(rootRelativeUrl)
|
|
710
|
+
livereloadLogger.debug(`${rootRelativeUrl} removed -> stop tracking it`)
|
|
684
711
|
}
|
|
685
712
|
})
|
|
713
|
+
addStopTrackingCalback(rootRelativeUrl, removeRootRemovedCallback)
|
|
686
714
|
})
|
|
687
715
|
|
|
688
716
|
const trackMainAndDependencies = (
|
|
@@ -692,20 +720,17 @@ const setupServerSentEventsForLivereload = ({
|
|
|
692
720
|
livereloadLogger.debug(`track ${mainRelativeUrl} and its dependencies`)
|
|
693
721
|
|
|
694
722
|
const removeModifiedCallback = projectFileModified.add((relativeUrl) => {
|
|
695
|
-
|
|
696
|
-
if (dependencySet.has(relativeUrl)) {
|
|
723
|
+
if (isDependencyOf(relativeUrl, mainRelativeUrl)) {
|
|
697
724
|
modified(relativeUrl)
|
|
698
725
|
}
|
|
699
726
|
})
|
|
700
727
|
const removeRemovedCallback = projectFileRemoved.add((relativeUrl) => {
|
|
701
|
-
|
|
702
|
-
if (dependencySet.has(relativeUrl)) {
|
|
728
|
+
if (isDependencyOf(relativeUrl, mainRelativeUrl)) {
|
|
703
729
|
removed(relativeUrl)
|
|
704
730
|
}
|
|
705
731
|
})
|
|
706
732
|
const removeAddedCallback = projectFileAdded.add((relativeUrl) => {
|
|
707
|
-
|
|
708
|
-
if (dependencySet.has(relativeUrl)) {
|
|
733
|
+
if (isDependencyOf(relativeUrl, mainRelativeUrl)) {
|
|
709
734
|
added(relativeUrl)
|
|
710
735
|
}
|
|
711
736
|
})
|
|
@@ -744,14 +769,6 @@ const setupServerSentEventsForLivereload = ({
|
|
|
744
769
|
|
|
745
770
|
const { referer } = request.headers
|
|
746
771
|
if (referer) {
|
|
747
|
-
const { origin } = request
|
|
748
|
-
// referer is likely the dev server
|
|
749
|
-
if (referer !== origin && !urlIsInsideOf(referer, origin)) {
|
|
750
|
-
return {
|
|
751
|
-
dependency: false,
|
|
752
|
-
reason: "referer is an other origin",
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
772
|
// here we know the referer is inside compileServer
|
|
756
773
|
const refererRelativeUrl = urlToOriginalRelativeUrl(
|
|
757
774
|
referer,
|
|
@@ -764,7 +781,7 @@ const setupServerSentEventsForLivereload = ({
|
|
|
764
781
|
for (const tracker of trackerMap) {
|
|
765
782
|
if (
|
|
766
783
|
tracker[0] === mainRelativeUrl &&
|
|
767
|
-
tracker[1].has(refererRelativeUrl)
|
|
784
|
+
tracker[1].set.has(refererRelativeUrl)
|
|
768
785
|
) {
|
|
769
786
|
return {
|
|
770
787
|
dependency: true,
|
|
@@ -893,8 +910,7 @@ const createSourceFileService = ({
|
|
|
893
910
|
projectFileCacheStrategy,
|
|
894
911
|
}) => {
|
|
895
912
|
return async (request) => {
|
|
896
|
-
const
|
|
897
|
-
const relativeUrl = ressource.slice(1)
|
|
913
|
+
const relativeUrl = request.pathname.slice(1)
|
|
898
914
|
projectFileRequestedCallback(relativeUrl, request)
|
|
899
915
|
|
|
900
916
|
const responsePromise = fetchFileSystem(
|
|
@@ -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
|
+
}
|