@jsenv/core 27.0.0-alpha.13 → 27.0.0-alpha.14

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 (27) hide show
  1. package/main.js +1 -1
  2. package/package.json +8 -10
  3. package/src/build/build.js +29 -17
  4. package/src/build/start_build_server.js +193 -0
  5. package/src/dev/start_dev_server.js +9 -20
  6. package/src/execute/execute.js +9 -2
  7. package/src/omega/kitchen.js +7 -3
  8. package/src/{dev/plugins/autoreload → plugins/autoreload/dev_sse}/client/autoreload_preference.js +0 -0
  9. package/src/{dev/plugins/autoreload → plugins/autoreload/dev_sse}/client/event_source_client.js +2 -2
  10. package/src/{dev/plugins/autoreload → plugins/autoreload/dev_sse}/client/reload.js +0 -0
  11. package/src/{dev/plugins/autoreload → plugins/autoreload/dev_sse}/client/url_helpers.js +0 -0
  12. package/src/plugins/autoreload/dev_sse/jsenv_plugin_dev_sse_client.js +41 -0
  13. package/src/{dev/plugins/autoreload/jsenv_plugin_autoreload.js → plugins/autoreload/dev_sse/jsenv_plugin_dev_sse_server.js} +23 -174
  14. package/src/plugins/autoreload/jsenv_plugin_autoreload.js +25 -0
  15. package/src/plugins/autoreload/jsenv_plugin_hmr.js +35 -0
  16. package/src/{dev/plugins/autoreload → plugins/import_meta_hot}/babel_plugin_metadata_import_meta_hot.js +3 -4
  17. package/src/{dev/plugins/autoreload → plugins/import_meta_hot}/client/import_meta_hot.js +0 -0
  18. package/src/{dev/plugins/autoreload → plugins/import_meta_hot}/html_hot_dependencies.js +0 -0
  19. package/src/plugins/import_meta_hot/jsenv_plugin_import_meta_hot.js +98 -0
  20. package/src/plugins/minification/jsenv_plugin_minification.js +2 -1
  21. package/src/plugins/plugins.js +23 -0
  22. package/src/plugins/transpilation/as_js_classic/jsenv_plugin_as_js_classic.js +13 -0
  23. package/src/test/execute_plan.js +6 -1
  24. package/src/test/execute_test_plan.js +4 -0
  25. package/src/dev/plugins/autoreload/client/event_source_connection.js +0 -195
  26. package/src/dev/plugins/autoreload/sse_service.js +0 -169
  27. package/src/preview/preview.js +0 -3
package/main.js CHANGED
@@ -16,8 +16,8 @@ export {
16
16
  } from "./src/execute/runtimes/browsers/webkit.js"
17
17
  export { nodeProcess } from "./src/execute/runtimes/node/node_process.js"
18
18
  // build
19
- export { preview } from "./src/preview/preview.js"
20
19
  export { build } from "./src/build/build.js"
20
+ export { startBuildServer } from "./src/build/start_build_server.js"
21
21
 
22
22
  // advanced
23
23
  export { execute } from "./src/execute/execute.js"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/core",
3
- "version": "27.0.0-alpha.13",
3
+ "version": "27.0.0-alpha.14",
4
4
  "description": "Tool to develop, test and build js projects",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -11,8 +11,7 @@
11
11
  "node": ">=16.13.0"
12
12
  },
13
13
  "publishConfig": {
14
- "access": "public",
15
- "registry": "https://registry.npmjs.org"
14
+ "access": "public"
16
15
  },
17
16
  "type": "module",
18
17
  "imports": {},
@@ -65,7 +64,7 @@
65
64
  "@jsenv/log": "1.5.1",
66
65
  "@jsenv/logger": "4.0.1",
67
66
  "@jsenv/node-esm-resolution": "0.0.2",
68
- "@jsenv/server": "12.6.0",
67
+ "@jsenv/server": "12.6.1",
69
68
  "@jsenv/uneval": "1.6.0",
70
69
  "@jsenv/utils": "1.1.0",
71
70
  "construct-style-sheets-polyfill": "3.1.0",
@@ -89,15 +88,14 @@
89
88
  "devDependencies": {
90
89
  "@babel/eslint-parser": "7.17.0",
91
90
  "@babel/plugin-syntax-import-assertions": "7.16.7",
92
- "@jsenv/assert": "2.5.3",
91
+ "@jsenv/assert": "2.5.4",
93
92
  "@jsenv/eslint-config": "16.0.9",
94
- "@jsenv/file-size-impact": "12.1.10",
95
- "@jsenv/github-release-package": "1.3.5",
93
+ "@jsenv/file-size-impact": "12.1.11",
94
+ "@jsenv/github-release-package": "1.3.6",
96
95
  "@jsenv/https-local": "1.0.8",
97
96
  "@jsenv/importmap-node-module": "5.1.3",
98
- "@jsenv/package-publish": "1.7.2",
99
97
  "@jsenv/package-workspace": "0.0.5",
100
- "@jsenv/performance-impact": "2.2.9",
98
+ "@jsenv/performance-impact": "2.2.10",
101
99
  "@jsenv/pwa": "4.0.1",
102
100
  "eslint": "8.13.0",
103
101
  "eslint-plugin-html": "6.2.0",
@@ -112,4 +110,4 @@
112
110
  "redux": "4.1.2",
113
111
  "rollup": "2.70.1"
114
112
  }
115
- }
113
+ }
@@ -66,7 +66,6 @@ export const build = async ({
66
66
  transpilation = {},
67
67
  bundling = true,
68
68
  minification = true,
69
-
70
69
  versioning = true,
71
70
  versioningMethod = "search_param", // "filename", "search_param"
72
71
  lineBreakNormalization = process.platform === "win32",
@@ -582,13 +581,27 @@ ${Object.keys(finalGraph.urlInfos).join("\n")}`,
582
581
  if (!urlInfo.data.isEntryPoint && urlInfo.dependents.size === 0) {
583
582
  return
584
583
  }
584
+
585
+ const urlContent =
586
+ urlInfo.type === "html"
587
+ ? stringifyHtmlAst(
588
+ parseHtmlString(urlInfo.content, {
589
+ storeOriginalPositions: false,
590
+ }),
591
+ { removeOriginalPositionAttributes: true },
592
+ )
593
+ : urlInfo.content
585
594
  const versionGenerator = createVersionGenerator()
586
595
  versionGenerator.augmentWithContent({
587
- content: urlInfo.content,
596
+ content: urlContent,
588
597
  contentType: urlInfo.contentType,
589
598
  lineBreakNormalization,
590
599
  })
591
600
  urlInfo.dependencies.forEach((dependencyUrl) => {
601
+ // this dependency is inline (data:) or remote (http://, https://)
602
+ if (!dependencyUrl.startsWith("file:")) {
603
+ return
604
+ }
592
605
  const dependencyUrlInfo = finalGraph.getUrlInfo(dependencyUrl)
593
606
  if (
594
607
  // this content is part of the file, no need to take into account twice
@@ -615,6 +628,7 @@ ${Object.keys(finalGraph.urlInfos).join("\n")}`,
615
628
  }
616
629
  })
617
630
  urlInfo.data.version = versionGenerator.generate()
631
+
618
632
  urlInfo.data.versionedUrl = injectVersionIntoBuildUrl({
619
633
  buildUrl: urlInfo.url,
620
634
  version: urlInfo.data.version,
@@ -717,18 +731,6 @@ ${Object.keys(finalGraph.urlInfos).join("\n")}`,
717
731
  }
718
732
  return versionedUrlInfo
719
733
  },
720
- transformUrlContent: {
721
- html: (urlInfo) => {
722
- const htmlAst = parseHtmlString(urlInfo.content, {
723
- storeOriginalPositions: false,
724
- })
725
- return {
726
- content: stringifyHtmlAst(htmlAst, {
727
- removeOriginalPositionAttributes: true,
728
- }),
729
- }
730
- },
731
- },
732
734
  },
733
735
  ],
734
736
  })
@@ -761,8 +763,6 @@ ${Object.keys(finalGraph.urlInfos).join("\n")}`,
761
763
  throw e
762
764
  }
763
765
  versioningTask.done()
764
- } else {
765
- // TODO: remove html attributes such as original-src-position
766
766
  }
767
767
 
768
768
  GRAPH.forEach(finalGraph, (urlInfo) => {
@@ -772,6 +772,14 @@ ${Object.keys(finalGraph.urlInfos).join("\n")}`,
772
772
  if (urlInfo.external) {
773
773
  return
774
774
  }
775
+ if (urlInfo.type === "html") {
776
+ const htmlAst = parseHtmlString(urlInfo.content, {
777
+ storeOriginalPositions: false,
778
+ })
779
+ urlInfo.content = stringifyHtmlAst(htmlAst, {
780
+ removeOriginalPositionAttributes: true,
781
+ })
782
+ }
775
783
  const version = urlInfo.data.version
776
784
  const useVersionedUrl = version && canUseVersionedUrl(urlInfo, finalGraph)
777
785
  const buildUrl = useVersionedUrl ? urlInfo.data.versionedUrl : urlInfo.url
@@ -795,7 +803,11 @@ ${Object.keys(finalGraph.urlInfos).join("\n")}`,
795
803
  // nothing uses this url anymore
796
804
  // - versioning update inline content
797
805
  // - file converted for import assertion of js_classic conversion
798
- if (!urlInfo.data.isEntryPoint && urlInfo.dependents.size === 0) {
806
+ if (
807
+ !urlInfo.data.isEntryPoint &&
808
+ urlInfo.type !== "sourcemap" &&
809
+ urlInfo.dependents.size === 0
810
+ ) {
799
811
  cleanupActions.push(() => {
800
812
  delete finalGraph.urlInfos[urlInfo.url]
801
813
  })
@@ -0,0 +1,193 @@
1
+ /*
2
+ * startBuildServer is mean to interact with the build files;
3
+ * files that will be deployed to production server(s).
4
+ * We want to be as close as possible from the production in order to:
5
+ * - run lighthouse
6
+ * - run an automated test tool such as cypress, playwright
7
+ * - see exactly how build file behaves (debug, measure perf, etc)
8
+ * For these reasons "startBuildServer" must be as close as possible from a static file server.
9
+ * It is not meant to provide a nice developper experience: this is the role "startDevServer".
10
+ *
11
+ * Conclusion:
12
+ * "startBuildServer" must be as close as possible from a static file server because
13
+ * we want to be in the user shoes and we should not alter build files.
14
+ */
15
+
16
+ import {
17
+ jsenvAccessControlAllowedHeaders,
18
+ startServer,
19
+ pluginServerTiming,
20
+ pluginRequestWaitingCheck,
21
+ pluginCORS,
22
+ fetchFileSystem,
23
+ } from "@jsenv/server"
24
+ import { Abort, raceProcessTeardownEvents } from "@jsenv/abort"
25
+ import { createLogger } from "@jsenv/logger"
26
+
27
+ import { createTaskLog } from "@jsenv/utils/logs/task_log.js"
28
+ import { watchFiles } from "@jsenv/utils/file_watcher/file_watcher.js"
29
+ import { build } from "../build/build.js"
30
+
31
+ export const startBuildServer = async ({
32
+ signal = new AbortController().signal,
33
+ handleSIGINT = true,
34
+ logLevel,
35
+ protocol,
36
+ http2,
37
+ certificate,
38
+ privateKey,
39
+ listenAnyIp,
40
+ ip,
41
+ port,
42
+
43
+ rootDirectoryUrl,
44
+ buildDirectoryUrl,
45
+ entryPoints,
46
+
47
+ plugins,
48
+ sourcemaps,
49
+ nodeEsmResolution,
50
+ fileSystemMagicResolution,
51
+ injectedGlobals,
52
+ runtimeCompat,
53
+ transpilation,
54
+ bundling,
55
+ minification,
56
+ versioning,
57
+ versioningMethod = "search_param", // "filename", "search_param"
58
+ lineBreakNormalization,
59
+ watchedFilePatterns,
60
+ cooldownBetweenFileEvents,
61
+
62
+ baseUrl,
63
+ }) => {
64
+ const operation = Abort.startOperation()
65
+ operation.addAbortSignal(signal)
66
+ if (handleSIGINT) {
67
+ operation.addAbortSource((abort) => {
68
+ return raceProcessTeardownEvents(
69
+ {
70
+ SIGINT: true,
71
+ },
72
+ abort,
73
+ )
74
+ })
75
+ }
76
+
77
+ let buildPromise
78
+ let abortController
79
+ const runBuild = () => {
80
+ abortController = new AbortController()
81
+ operation.addAbortCallback(() => {
82
+ abortController.abort()
83
+ })
84
+ buildPromise = build({
85
+ signal: abortController.signal,
86
+ logLevel: "warn",
87
+ rootDirectoryUrl,
88
+ buildDirectoryUrl,
89
+ entryPoints,
90
+
91
+ plugins,
92
+ sourcemaps,
93
+ nodeEsmResolution,
94
+ fileSystemMagicResolution,
95
+ injectedGlobals,
96
+ runtimeCompat,
97
+ transpilation,
98
+ bundling,
99
+ minification,
100
+ versioning,
101
+ versioningMethod,
102
+ lineBreakNormalization,
103
+
104
+ writeOnFileSystem: true,
105
+ buildDirectoryClean: true,
106
+ baseUrl,
107
+ assetManifest: false,
108
+ })
109
+ }
110
+
111
+ const logger = createLogger({ logLevel })
112
+ const startServerTask = createTaskLog(logger, "start build server")
113
+ const server = await startServer({
114
+ signal,
115
+ stopOnExit: false,
116
+ stopOnSIGINT: false,
117
+ stopOnInternalError: false,
118
+ keepProcessAlive: true,
119
+ logLevel,
120
+ startLog: false,
121
+
122
+ protocol,
123
+ http2,
124
+ certificate,
125
+ privateKey,
126
+ listenAnyIp,
127
+ ip,
128
+ port,
129
+ plugins: {
130
+ ...pluginCORS({
131
+ accessControlAllowRequestOrigin: true,
132
+ accessControlAllowRequestMethod: true,
133
+ accessControlAllowRequestHeaders: true,
134
+ accessControlAllowedRequestHeaders: [
135
+ ...jsenvAccessControlAllowedHeaders,
136
+ "x-jsenv-execution-id",
137
+ ],
138
+ accessControlAllowCredentials: true,
139
+ }),
140
+ ...pluginServerTiming(),
141
+ ...pluginRequestWaitingCheck({
142
+ requestWaitingMs: 60 * 1000,
143
+ }),
144
+ },
145
+ sendErrorDetails: true,
146
+ requestToResponse: async (request) => {
147
+ await buildPromise
148
+ const urlIsVersioned =
149
+ versioningMethod === "search_param"
150
+ ? new URL(request.ressource, request.origin).searchParams.has("v")
151
+ : // we could use a regex, but there can be false-positive
152
+ false
153
+
154
+ return fetchFileSystem(
155
+ new URL(request.ressource.slice(1), buildDirectoryUrl),
156
+ {
157
+ headers: request.headers,
158
+ cacheControl: urlIsVersioned
159
+ ? `private,max-age=${SECONDS_IN_30_DAYS},immutable`
160
+ : "private,max-age=0,must-revalidate",
161
+ etagEnabled: true,
162
+ compressionEnabled: !request.pathname.endsWith(".mp4"),
163
+ rootDirectoryUrl: buildDirectoryUrl,
164
+ canReadDirectory: true,
165
+ },
166
+ )
167
+ },
168
+ })
169
+ startServerTask.done()
170
+ logger.info(``)
171
+ Object.keys(server.origins).forEach((key) => {
172
+ logger.info(`- ${server.origins[key]}`)
173
+ })
174
+ logger.info(``)
175
+
176
+ const unregisterDirectoryLifecyle = watchFiles({
177
+ rootDirectoryUrl,
178
+ watchedFilePatterns,
179
+ cooldownBetweenFileEvents,
180
+ fileChangeCallback: () => {
181
+ abortController.abort()
182
+ runBuild()
183
+ },
184
+ })
185
+ operation.addAbortCallback(() => {
186
+ unregisterDirectoryLifecyle()
187
+ })
188
+ runBuild()
189
+
190
+ return server
191
+ }
192
+
193
+ const SECONDS_IN_30_DAYS = 60 * 60 * 24 * 30
@@ -7,7 +7,6 @@ import { createUrlGraph } from "@jsenv/core/src/omega/url_graph.js"
7
7
  import { createKitchen } from "@jsenv/core/src/omega/kitchen.js"
8
8
  import { startOmegaServer } from "@jsenv/core/src/omega/omega_server.js"
9
9
 
10
- import { jsenvPluginAutoreload } from "./plugins/autoreload/jsenv_plugin_autoreload.js"
11
10
  import { jsenvPluginExplorer } from "./plugins/explorer/jsenv_plugin_explorer.js"
12
11
  import { jsenvPluginToolbar } from "./plugins/toolbar/jsenv_plugin_toolbar.js"
13
12
 
@@ -29,16 +28,9 @@ export const startDevServer = async ({
29
28
  plugins = [],
30
29
  htmlSupervisor = true,
31
30
  injectedGlobals,
32
-
31
+ nodeEsmResolution,
32
+ fileSystemMagicResolution,
33
33
  autoreload = true,
34
- autoreloadPatterns = {
35
- "./**": true,
36
- "./**/.*/": false, // any folder starting with a dot is ignored (includes .git for instance)
37
- "./dist/": false,
38
- "./**/node_modules/": false,
39
- },
40
- cooldownBetweenFileEvents = 0,
41
-
42
34
  explorerGroups = {
43
35
  source: {
44
36
  "./*.html": true,
@@ -65,19 +57,16 @@ export const startDevServer = async ({
65
57
  plugins: [
66
58
  ...plugins,
67
59
  ...getCorePlugins({
60
+ rootDirectoryUrl,
61
+ urlGraph,
62
+ scenario: "dev",
63
+
68
64
  htmlSupervisor,
69
65
  injectedGlobals,
66
+ nodeEsmResolution,
67
+ fileSystemMagicResolution,
68
+ autoreload,
70
69
  }),
71
- ...(autoreload
72
- ? [
73
- jsenvPluginAutoreload({
74
- rootDirectoryUrl,
75
- urlGraph,
76
- autoreloadPatterns,
77
- cooldownBetweenFileEvents,
78
- }),
79
- ]
80
- : []),
81
70
  jsenvPluginExplorer({
82
71
  groups: explorerGroups,
83
72
  }),
@@ -26,10 +26,13 @@ export const execute = async ({
26
26
  runtime,
27
27
  runtimeParams,
28
28
 
29
- injectedGlobals,
30
- plugins = [],
31
29
  scenario = "dev",
32
30
  sourcemaps = "inline",
31
+ plugins = [],
32
+ nodeEsmResolution,
33
+ fileSystemMagicResolution,
34
+ injectedGlobals,
35
+ transpilation,
33
36
 
34
37
  port,
35
38
  protocol,
@@ -72,7 +75,11 @@ export const execute = async ({
72
75
  plugins: [
73
76
  ...plugins,
74
77
  ...getCorePlugins({
78
+ scenario,
79
+ nodeEsmResolution,
80
+ fileSystemMagicResolution,
75
81
  injectedGlobals,
82
+ transpilation,
76
83
  }),
77
84
  ],
78
85
  })
@@ -38,9 +38,13 @@ export const createKitchen = ({
38
38
  test: "inline",
39
39
  build: "none",
40
40
  }[scenario],
41
- // we don't need sources in sourcemap as long as the url in the
42
- // sourcemap uses file:/// (chrome will understand and read from filesystem)
43
- sourcemapsSources = false,
41
+ sourcemapsSources = {
42
+ // during dev/test, chrome is able to find the sourcemap sources
43
+ // as long as they use file:// protocol in the sourcemap files
44
+ dev: false,
45
+ test: false,
46
+ build: true,
47
+ }[scenario],
44
48
  runtimeCompat = defaultRuntimeCompat,
45
49
  writeOnFileSystem = true,
46
50
  }) => {
@@ -1,4 +1,5 @@
1
- import { createEventSourceConnection } from "./event_source_connection.js"
1
+ import { createEventSourceConnection } from "@jsenv/utils/event_source/event_source.js"
2
+ import { urlHotMetas } from "../../../import_meta_hot/client/import_meta_hot.js"
2
3
  import {
3
4
  isAutoreloadEnabled,
4
5
  setAutoreloadPreference,
@@ -9,7 +10,6 @@ import {
9
10
  reloadDOMNodesUsingUrl,
10
11
  reloadJsImport,
11
12
  } from "./reload.js"
12
- import { urlHotMetas } from "./import_meta_hot.js"
13
13
 
14
14
  const reloadMessages = []
15
15
  const reloadMessagesSignal = { onchange: () => {} }
@@ -0,0 +1,41 @@
1
+ import {
2
+ parseHtmlString,
3
+ stringifyHtmlAst,
4
+ injectScriptAsEarlyAsPossible,
5
+ createHtmlNode,
6
+ } from "@jsenv/utils/html_ast/html_ast.js"
7
+
8
+ export const jsenvPluginDevSSEClient = () => {
9
+ const eventSourceClientFileUrl = new URL(
10
+ "./client/event_source_client.js",
11
+ import.meta.url,
12
+ ).href
13
+
14
+ return {
15
+ name: "jsenv:dev_sse_client",
16
+ appliesDuring: { dev: true },
17
+ transformUrlContent: {
18
+ html: (htmlUrlInfo, context) => {
19
+ const htmlAst = parseHtmlString(htmlUrlInfo.content)
20
+ const [eventSourceClientReference] = context.referenceUtils.inject({
21
+ type: "script_src",
22
+ expectedType: "js_module",
23
+ specifier: eventSourceClientFileUrl,
24
+ })
25
+ injectScriptAsEarlyAsPossible(
26
+ htmlAst,
27
+ createHtmlNode({
28
+ "tagName": "script",
29
+ "type": "module",
30
+ "src": eventSourceClientReference.generatedSpecifier,
31
+ "injected-by": "jsenv:dev_sse_client",
32
+ }),
33
+ )
34
+ const htmlModified = stringifyHtmlAst(htmlAst)
35
+ return {
36
+ content: htmlModified,
37
+ }
38
+ },
39
+ },
40
+ }
41
+ }