@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.
Files changed (68) hide show
  1. package/dist/jsenv_event_source_client.js +338 -0
  2. package/dist/jsenv_event_source_client.js.map +126 -0
  3. package/dist/jsenv_exploring_index.js.map +7 -7
  4. package/dist/jsenv_redirector.js +1386 -0
  5. package/dist/jsenv_redirector.js.map +384 -0
  6. package/dist/jsenv_toolbar.js +37 -504
  7. package/dist/jsenv_toolbar.js.map +37 -112
  8. package/dist/jsenv_toolbar_injector.js +31 -126
  9. package/dist/jsenv_toolbar_injector.js.map +11 -30
  10. package/package.json +1 -1
  11. package/src/buildProject.js +2 -0
  12. package/src/dev_server.js +108 -70
  13. package/src/internal/compiling/compile-directory/getOrGenerateCompiledFile.js +1 -2
  14. package/src/internal/compiling/compileFile.js +51 -23
  15. package/src/internal/compiling/createCompiledFileService.js +22 -10
  16. package/src/internal/compiling/html_source_file_service.js +43 -9
  17. package/src/internal/compiling/jsenvCompilerForHtml.js +146 -107
  18. package/src/internal/compiling/startCompileServer.js +60 -44
  19. package/src/internal/dev_server/event_source_client/event_source_client.js +63 -0
  20. package/src/internal/dev_server/event_source_client/event_source_client_file_info.js +17 -0
  21. package/src/internal/{toolbar/eventsource/connectEventSource.js → dev_server/event_source_client/event_source_connection.js} +47 -78
  22. package/src/internal/dev_server/event_source_client/file_changes.js +82 -0
  23. package/src/internal/dev_server/event_source_client/livereload_preference.js +13 -0
  24. package/src/internal/{exploring → dev_server/exploring}/exploring.css +0 -0
  25. package/src/internal/{exploring → dev_server/exploring}/exploring.html +1 -1
  26. package/src/internal/{exploring → dev_server/exploring}/exploring.js +0 -0
  27. package/src/internal/dev_server/exploring/exploring_file_info.js +21 -0
  28. package/src/internal/{exploring → dev_server/exploring}/fetchExploringJson.js +1 -1
  29. package/src/internal/{exploring/exploring.redirector.html → dev_server/redirector/redirector.html} +1 -1
  30. package/src/internal/{exploring/exploring.redirector.js → dev_server/redirector/redirector.js} +1 -1
  31. package/src/internal/dev_server/redirector/redirector_file_info.js +24 -0
  32. package/src/internal/{toolbar → dev_server/toolbar}/animation/toolbar.animation.js +0 -0
  33. package/src/internal/{toolbar → dev_server/toolbar}/backtolist/toolbar.backtolist.js +0 -0
  34. package/src/internal/{toolbar → dev_server/toolbar}/compilation/compilation.css +0 -0
  35. package/src/internal/{toolbar → dev_server/toolbar}/compilation/toolbar.compilation.js +1 -1
  36. package/src/internal/{toolbar → dev_server/toolbar}/eventsource/eventsource.css +0 -0
  37. package/src/internal/dev_server/toolbar/eventsource/toolbar.eventsource.js +83 -0
  38. package/src/internal/{toolbar → dev_server/toolbar}/execution/execution.css +0 -0
  39. package/src/internal/{toolbar → dev_server/toolbar}/execution/toolbar.execution.js +0 -0
  40. package/src/internal/{toolbar → dev_server/toolbar}/focus/focus.css +0 -0
  41. package/src/internal/{toolbar → dev_server/toolbar}/focus/toolbar.focus.js +0 -0
  42. package/src/internal/{toolbar → dev_server/toolbar}/jsenv-logo.svg +0 -0
  43. package/src/internal/{toolbar → dev_server/toolbar}/notification/toolbar.notification.js +0 -0
  44. package/src/internal/{toolbar → dev_server/toolbar}/responsive/overflow-menu.css +0 -0
  45. package/src/internal/{toolbar → dev_server/toolbar}/responsive/toolbar.responsive.js +0 -0
  46. package/src/internal/{toolbar → dev_server/toolbar}/settings/settings.css +0 -0
  47. package/src/internal/{toolbar → dev_server/toolbar}/settings/toolbar.settings.js +0 -0
  48. package/src/internal/{toolbar → dev_server/toolbar}/theme/jsenv-theme.css +0 -0
  49. package/src/internal/{toolbar → dev_server/toolbar}/theme/light-theme.css +0 -0
  50. package/src/internal/{toolbar → dev_server/toolbar}/theme/toolbar.theme.js +0 -0
  51. package/src/internal/{toolbar → dev_server/toolbar}/toolbar.html +4 -37
  52. package/src/internal/{toolbar → dev_server/toolbar}/toolbar.injector.js +3 -92
  53. package/src/internal/{toolbar → dev_server/toolbar}/toolbar.main.css +0 -0
  54. package/src/internal/{toolbar → dev_server/toolbar}/toolbar.main.js +0 -0
  55. package/src/internal/dev_server/toolbar/toolbar_file_info.js +37 -0
  56. package/src/internal/{toolbar → dev_server/toolbar}/tooltip/tooltip.css +0 -0
  57. package/src/internal/{toolbar → dev_server/toolbar}/tooltip/tooltip.js +0 -0
  58. package/src/internal/{toolbar → dev_server/toolbar}/util/animation.js +0 -0
  59. package/src/internal/{toolbar → dev_server/toolbar}/util/dom.js +0 -0
  60. package/src/internal/{toolbar → dev_server/toolbar}/util/fetching.js +2 -2
  61. package/src/internal/{toolbar → dev_server/toolbar}/util/jsenvLogger.js +0 -0
  62. package/src/internal/{toolbar → dev_server/toolbar}/util/preferences.js +0 -0
  63. package/src/internal/{toolbar → dev_server/toolbar}/util/responsive.js +0 -0
  64. package/src/internal/{toolbar → dev_server/toolbar}/util/util.js +0 -0
  65. package/src/internal/{toolbar → dev_server/toolbar}/variant/variant.js +0 -0
  66. package/src/internal/jsenvInternalFiles.js +0 -58
  67. package/src/internal/toolbar/eventsource/connectCompileServerEventSource.js +0 -74
  68. 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
- jsenvBrowserSystemFileInfo,
7
- jsenvToolbarHtmlFileInfo,
8
- jsenvToolbarInjectorFileInfo,
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 jsenvToolbarInjectorBuildRelativeUrlForProject = urlToRelativeUrl(
59
- jsenvToolbarInjectorFileInfo.jsenvBuildUrl,
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
- ...(url !== jsenvToolbarHtmlFileInfo.url && jsenvScriptInjection
81
+ ...(urlNoSearch !== toolbarHtmlFileInfo.sourceUrl && jsenvScriptInjection
73
82
  ? [
74
83
  {
75
84
  src: `/${jsenvBrowserBuildUrlRelativeToProject}`,
76
85
  },
77
86
  ]
78
87
  : []),
79
- ...(url !== jsenvToolbarHtmlFileInfo.url && jsenvToolbarInjection
88
+ ...(urlNoSearch !== toolbarHtmlFileInfo.sourceUrl &&
89
+ jsenvEventSourceClientInjection
80
90
  ? [
81
91
  {
82
- src: `/${jsenvToolbarInjectorBuildRelativeUrlForProject}`,
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
- const htmlDependencies = collectHtmlDependenciesFromAst(htmlAst)
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
- hasImportmap = true
102
-
103
- if (srcAttribute) {
104
- if (moduleOutFormat === "systemjs") {
105
- typeAttribute.value = "jsenv-importmap"
106
- return // no need to inline
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" && srcAttribute) {
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: [url],
311
- sourcesContent: [code],
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
- "service:sse": serveSSEForLivereload,
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
- "service:sse": (request) => {
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 getDependencySet = (mainRelativeUrl) => {
631
- if (trackerMap.has(mainRelativeUrl)) {
632
- return trackerMap.get(mainRelativeUrl)
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
- const dependencySet = new Set()
635
- dependencySet.add(mainRelativeUrl)
636
- trackerMap.set(mainRelativeUrl, dependencySet)
637
- return dependencySet
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(({ relativeUrl: mainRelativeUrl }) => {
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 (!mainRelativeUrl.endsWith(".html")) {
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
- const dependencySet = new Set()
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 (dependencySet.has(relativeUrl)) {
686
+ if (isDependencyOf(relativeUrl, rootRelativeUrl)) {
658
687
  return
659
688
  }
660
-
661
689
  const dependencyReport = reportDependency(
662
690
  relativeUrl,
663
- mainRelativeUrl,
691
+ rootRelativeUrl,
664
692
  request,
665
693
  )
666
694
  if (dependencyReport.dependency === false) {
667
695
  livereloadLogger.debug(
668
- `${relativeUrl} not a dependency of ${mainRelativeUrl} because ${dependencyReport.reason}`,
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 ${mainRelativeUrl} because ${dependencyReport.reason}`,
701
+ `${relativeUrl} is a dependency of ${rootRelativeUrl} because ${dependencyReport.reason}`,
675
702
  )
676
- dependencySet.add(relativeUrl)
703
+ markAsDependencyOf(relativeUrl, rootRelativeUrl)
677
704
  },
678
705
  )
679
- const removeMainRemovedCallback = projectFileRemoved.add((relativeUrl) => {
680
- if (relativeUrl === mainRelativeUrl) {
681
- removeDependencyRequestedCallback()
682
- removeMainRemovedCallback()
683
- trackerMap.delete(mainRelativeUrl)
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
- const dependencySet = getDependencySet(mainRelativeUrl)
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
- const dependencySet = getDependencySet(mainRelativeUrl)
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
- const dependencySet = getDependencySet(mainRelativeUrl)
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 { ressource } = request
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
+ }