@jsenv/core 28.6.0 → 29.0.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/core",
3
- "version": "28.6.0",
3
+ "version": "29.0.0",
4
4
  "description": "Tool to develop, test and build js projects",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -123,8 +123,8 @@ export const build = async ({
123
123
  sourcemaps = false,
124
124
  sourcemapsSourcesContent,
125
125
  urlAnalysis = {},
126
- nodeEsmResolution = true,
127
- fileSystemMagicResolution,
126
+ urlResolution,
127
+ fileSystemMagicRedirection,
128
128
  directoryReferenceAllowed,
129
129
  transpilation = {},
130
130
  bundling = true,
@@ -221,8 +221,8 @@ build ${entryPointKeys.length} entry points`)
221
221
  runtimeCompat,
222
222
 
223
223
  urlAnalysis,
224
- nodeEsmResolution,
225
- fileSystemMagicResolution,
224
+ urlResolution,
225
+ fileSystemMagicRedirection,
226
226
  directoryReferenceAllowed,
227
227
  transpilation: {
228
228
  ...transpilation,
@@ -326,10 +326,7 @@ ${ANSI.color(buildUrl, ANSI.MAGENTA)}
326
326
  if (rawUrlInfo.content === reference.content) {
327
327
  return true
328
328
  }
329
- if (rawUrlInfo.originalContent === reference.content) {
330
- return true
331
- }
332
- return false
329
+ return rawUrlInfo.originalContent === reference.content
333
330
  })
334
331
  const parentUrlInfo = finalGraph.getUrlInfo(reference.parentUrl)
335
332
  if (!rawUrlInfo) {
@@ -635,7 +632,6 @@ ${ANSI.color(buildUrl, ANSI.MAGENTA)}
635
632
  const bundler = bundlers[rawUrlInfo.type]
636
633
  if (bundler) {
637
634
  bundler.urlInfos.push(rawUrlInfo)
638
- return
639
635
  }
640
636
  }
641
637
  GRAPH.forEach(rawGraph, (rawUrlInfo) => {
@@ -1510,18 +1506,12 @@ const isUsed = (urlInfo) => {
1510
1506
  if (urlInfo.injected) {
1511
1507
  return true
1512
1508
  }
1513
- if (urlInfo.dependents.size > 0) {
1514
- return true
1515
- }
1516
- return false
1509
+ return urlInfo.dependents.size > 0
1517
1510
  }
1518
1511
 
1519
1512
  const canUseVersionedUrl = (urlInfo) => {
1520
1513
  if (urlInfo.isEntryPoint) {
1521
1514
  return false
1522
1515
  }
1523
- if (urlInfo.type === "webmanifest") {
1524
- return false
1525
- }
1526
- return true
1516
+ return urlInfo.type !== "webmanifest"
1527
1517
  }
@@ -50,9 +50,9 @@ export const startDevServer = async ({
50
50
  runtimeCompat = defaultRuntimeCompat,
51
51
  plugins = [],
52
52
  urlAnalysis = {},
53
+ urlResolution,
53
54
  supervisor = true,
54
- nodeEsmResolution,
55
- fileSystemMagicResolution,
55
+ fileSystemMagicRedirection,
56
56
  transpilation,
57
57
  explorer = true, // see jsenv_plugin_explorer.js
58
58
  // toolbar = false,
@@ -152,9 +152,9 @@ export const startDevServer = async ({
152
152
 
153
153
  plugins,
154
154
  urlAnalysis,
155
+ urlResolution,
156
+ fileSystemMagicRedirection,
155
157
  supervisor,
156
- nodeEsmResolution,
157
- fileSystemMagicResolution,
158
158
  transpilation,
159
159
  clientFiles,
160
160
  clientMainFileUrl,
@@ -134,6 +134,7 @@ export const createKitchen = ({
134
134
  isResourceHint,
135
135
  isImplicit,
136
136
  hasVersioningEffect,
137
+ version: null,
137
138
  injected,
138
139
  timing: {},
139
140
  // for inline resources the reference contains the content
@@ -936,53 +937,3 @@ const determineFileUrlForOutDirectory = ({ urlInfo, context }) => {
936
937
  preferAbsolute: true,
937
938
  })
938
939
  }
939
-
940
- // import { getOriginalPosition } from "@jsenv/core/src/utils/sourcemap/original_position.js"
941
- // const getUrlSite = async (
942
- // urlInfo,
943
- // { line, column, originalLine, originalColumn },
944
- // ) => {
945
- // if (typeof originalLine === "number") {
946
- // return {
947
- // url: urlInfo.url,
948
- // line: originalLine,
949
- // column: originalColumn,
950
- // }
951
- // }
952
- // if (urlInfo.content === urlInfo.originalContent) {
953
- // return {
954
- // url: urlInfo.url,
955
- // line,
956
- // column,
957
- // }
958
- // }
959
- // // at this point things were transformed: line and column are generated
960
- // // no sourcemap -> cannot map back to original file
961
- // const { sourcemap } = urlInfo
962
- // if (!sourcemap) {
963
- // return {
964
- // url: urlInfo.generatedUrl,
965
- // content: urlInfo.content,
966
- // line,
967
- // column,
968
- // }
969
- // }
970
- // const originalPosition = await getOriginalPosition({
971
- // sourcemap,
972
- // line,
973
- // column,
974
- // })
975
- // // cannot map back to original file
976
- // if (!originalPosition || originalPosition.line === null) {
977
- // return {
978
- // url: urlInfo.generatedUrl,
979
- // line,
980
- // column,
981
- // }
982
- // }
983
- // return {
984
- // url: urlInfo.url,
985
- // line: originalPosition.line,
986
- // column: originalPosition.column,
987
- // }
988
- // }
@@ -30,9 +30,9 @@ export const startOmegaServer = async ({
30
30
 
31
31
  plugins,
32
32
  urlAnalysis,
33
+ urlResolution,
34
+ fileSystemMagicRedirection,
33
35
  supervisor,
34
- nodeEsmResolution,
35
- fileSystemMagicResolution,
36
36
  transpilation,
37
37
  clientAutoreload,
38
38
  clientFiles,
@@ -93,9 +93,9 @@ export const startOmegaServer = async ({
93
93
 
94
94
  plugins,
95
95
  urlAnalysis,
96
+ urlResolution,
97
+ fileSystemMagicRedirection,
96
98
  supervisor,
97
- nodeEsmResolution,
98
- fileSystemMagicResolution,
99
99
  transpilation,
100
100
  clientAutoreload,
101
101
  clientFiles,
@@ -26,9 +26,9 @@ export const createFileService = ({
26
26
 
27
27
  plugins,
28
28
  urlAnalysis,
29
+ urlResolution,
30
+ fileSystemMagicRedirection,
29
31
  supervisor,
30
- nodeEsmResolution,
31
- fileSystemMagicResolution,
32
32
  transpilation,
33
33
  clientAutoreload,
34
34
  clientFiles,
@@ -128,9 +128,9 @@ export const createFileService = ({
128
128
  runtimeCompat,
129
129
 
130
130
  urlAnalysis,
131
+ urlResolution,
132
+ fileSystemMagicRedirection,
131
133
  supervisor,
132
- nodeEsmResolution,
133
- fileSystemMagicResolution,
134
134
  transpilation,
135
135
 
136
136
  clientMainFileUrl,
@@ -2,17 +2,7 @@ export const jsenvPluginCacheControl = () => {
2
2
  return {
3
3
  name: "jsenv:cache_control",
4
4
  appliesDuring: "dev",
5
- augmentResponse: ({ reference }, context) => {
6
- if (context.scenarios.test) {
7
- // During dev, all files are put into browser cache for 1 hour because:
8
- // 1: Browser cache is a temporary directory created by playwright
9
- // 2: We assume source files won't be modified while tests are running
10
- return {
11
- headers: {
12
- "cache-control": `private,max-age=3600,immutable`,
13
- },
14
- }
15
- }
5
+ augmentResponse: ({ reference }) => {
16
6
  if (
17
7
  reference.searchParams.has("v") &&
18
8
  !reference.searchParams.has("hmr")
@@ -365,7 +365,7 @@
365
365
  </section>
366
366
  </article>
367
367
  </div>
368
- <script>
368
+ <script no-supervisor>
369
369
  // eslint-disable-next-line no-undef
370
370
  const { rootDirectoryUrl, groups, files } = SERVER_PARAMS
371
371
 
@@ -479,7 +479,7 @@
479
479
 
480
480
  document.querySelector("main").appendChild(fileListElement)
481
481
  </script>
482
- <script>
482
+ <script no-supervisor>
483
483
  // make menu scrollable
484
484
  const getMenuWrapperSize = () => {
485
485
  return document.querySelector(".menu-wrapper").getBoundingClientRect()
@@ -17,6 +17,7 @@ export const jsenvPluginExplorer = ({
17
17
  "./tests/**/*.test.html": true,
18
18
  },
19
19
  },
20
+ clientMainFileUrl,
20
21
  }) => {
21
22
  const faviconClientFileUrl = new URL("./client/jsenv.png", import.meta.url)
22
23
 
@@ -25,53 +26,60 @@ export const jsenvPluginExplorer = ({
25
26
  appliesDuring: "dev",
26
27
  transformUrlContent: {
27
28
  html: async (urlInfo, context) => {
28
- if (urlInfo.url !== explorerHtmlFileUrl) {
29
+ if (urlInfo.url !== clientMainFileUrl) {
29
30
  return null
30
31
  }
31
- const associationsForExplorable = {}
32
- Object.keys(groups).forEach((groupName) => {
33
- const groupConfig = groups[groupName]
34
- associationsForExplorable[groupName] = {
35
- "**/.jsenv/": false, // avoid visting .jsenv directory in jsenv itself
36
- ...groupConfig,
37
- }
38
- })
39
- const matchingFileResultArray = await collectFiles({
40
- directoryUrl: context.rootDirectoryUrl,
41
- associations: associationsForExplorable,
42
- predicate: (meta) =>
43
- Object.keys(meta).some((group) => Boolean(meta[group])),
44
- })
45
- const files = matchingFileResultArray.map(({ relativeUrl, meta }) => ({
46
- relativeUrl,
47
- meta,
48
- }))
49
32
  let html = urlInfo.content
50
- html = html.replace(
51
- "ignore:FAVICON_HREF",
52
- DATA_URL.stringify({
53
- contentType: CONTENT_TYPE.fromUrlExtension(faviconClientFileUrl),
54
- base64Flag: true,
55
- data: readFileSync(new URL(faviconClientFileUrl)).toString(
56
- "base64",
33
+ if (html.includes("ignore:FAVICON_HREF")) {
34
+ html = html.replace(
35
+ "ignore:FAVICON_HREF",
36
+ DATA_URL.stringify({
37
+ contentType: CONTENT_TYPE.fromUrlExtension(faviconClientFileUrl),
38
+ base64Flag: true,
39
+ data: readFileSync(new URL(faviconClientFileUrl)).toString(
40
+ "base64",
41
+ ),
42
+ }),
43
+ )
44
+ }
45
+ if (html.includes("SERVER_PARAMS")) {
46
+ const associationsForExplorable = {}
47
+ Object.keys(groups).forEach((groupName) => {
48
+ const groupConfig = groups[groupName]
49
+ associationsForExplorable[groupName] = {
50
+ "**/.jsenv/": false, // avoid visting .jsenv directory in jsenv itself
51
+ ...groupConfig,
52
+ }
53
+ })
54
+ const matchingFileResultArray = await collectFiles({
55
+ directoryUrl: context.rootDirectoryUrl,
56
+ associations: associationsForExplorable,
57
+ predicate: (meta) =>
58
+ Object.keys(meta).some((group) => Boolean(meta[group])),
59
+ })
60
+ const files = matchingFileResultArray.map(
61
+ ({ relativeUrl, meta }) => ({
62
+ relativeUrl,
63
+ meta,
64
+ }),
65
+ )
66
+
67
+ html = html.replace(
68
+ "SERVER_PARAMS",
69
+ JSON.stringify(
70
+ {
71
+ rootDirectoryUrl: context.rootDirectoryUrl,
72
+ groups,
73
+ files,
74
+ },
75
+ null,
76
+ " ",
57
77
  ),
58
- }),
59
- )
60
- html = html.replace(
61
- "SERVER_PARAMS",
62
- JSON.stringify(
63
- {
64
- rootDirectoryUrl: context.rootDirectoryUrl,
65
- groups,
66
- files,
67
- },
68
- null,
69
- " ",
70
- ),
71
- )
72
- Object.assign(urlInfo.headers, {
73
- "cache-control": "no-store",
74
- })
78
+ )
79
+ Object.assign(urlInfo.headers, {
80
+ "cache-control": "no-store",
81
+ })
82
+ }
75
83
  return html
76
84
  },
77
85
  },
@@ -1,8 +1,6 @@
1
1
  import { jsenvPluginUrlAnalysis } from "../plugins/url_analysis/jsenv_plugin_url_analysis.js"
2
- import { jsenvPluginLeadingSlash } from "./leading_slash/jsenv_plugin_leading_slash.js"
3
2
  import { jsenvPluginImportmap } from "./importmap/jsenv_plugin_importmap.js"
4
3
  import { jsenvPluginUrlResolution } from "./url_resolution/jsenv_plugin_url_resolution.js"
5
- import { jsenvPluginNodeEsmResolution } from "./node_esm_resolution/jsenv_plugin_node_esm_resolution.js"
6
4
  import { jsenvPluginUrlVersion } from "./url_version/jsenv_plugin_url_version.js"
7
5
  import { jsenvPluginFileUrls } from "./file_urls/jsenv_plugin_file_urls.js"
8
6
  import { jsenvPluginHttpUrls } from "./http_urls/jsenv_plugin_http_urls.js"
@@ -30,10 +28,10 @@ export const getCorePlugins = ({
30
28
  runtimeCompat,
31
29
 
32
30
  urlAnalysis = {},
33
- supervisor,
34
- nodeEsmResolution = true,
35
- fileSystemMagicResolution,
31
+ urlResolution = {},
32
+ fileSystemMagicRedirection,
36
33
  directoryReferenceAllowed,
34
+ supervisor,
37
35
  transpilation = true,
38
36
  minification = false,
39
37
  bundling = false,
@@ -50,19 +48,19 @@ export const getCorePlugins = ({
50
48
  if (supervisor === true) {
51
49
  supervisor = {}
52
50
  }
53
- if (nodeEsmResolution === true) {
54
- nodeEsmResolution = {}
55
- }
56
- if (fileSystemMagicResolution === true) {
57
- fileSystemMagicResolution = {}
51
+ if (fileSystemMagicRedirection === true) {
52
+ fileSystemMagicRedirection = {}
58
53
  }
59
54
  if (clientAutoreload === true) {
60
55
  clientAutoreload = {}
61
56
  }
62
- clientMainFileUrl =
63
- clientMainFileUrl || explorer
64
- ? explorerHtmlFileUrl
65
- : new URL("./index.html", rootDirectoryUrl)
57
+ if (clientMainFileUrl === undefined) {
58
+ clientMainFileUrl = explorer
59
+ ? String(explorerHtmlFileUrl)
60
+ : String(new URL("./index.html", rootDirectoryUrl))
61
+ } else {
62
+ clientMainFileUrl = String(clientMainFileUrl)
63
+ }
66
64
 
67
65
  return [
68
66
  jsenvPluginUrlAnalysis({ rootDirectoryUrl, ...urlAnalysis }),
@@ -74,13 +72,14 @@ export const getCorePlugins = ({
74
72
  jsenvPluginInline(), // before "file urls" to resolve and load inline urls
75
73
  jsenvPluginFileUrls({
76
74
  directoryReferenceAllowed,
77
- ...fileSystemMagicResolution,
75
+ ...fileSystemMagicRedirection,
78
76
  }),
79
77
  jsenvPluginHttpUrls(),
80
- jsenvPluginLeadingSlash(),
81
- // before url resolution to handle "js_import_export" resolution
82
- jsenvPluginNodeEsmResolution(nodeEsmResolution),
83
- jsenvPluginUrlResolution({ clientMainFileUrl }),
78
+ jsenvPluginUrlResolution({
79
+ runtimeCompat,
80
+ urlResolution,
81
+ clientMainFileUrl,
82
+ }),
84
83
  jsenvPluginUrlVersion(),
85
84
  jsenvPluginCommonJsGlobals(),
86
85
  jsenvPluginImportMetaScenarios(),
@@ -100,6 +99,13 @@ export const getCorePlugins = ({
100
99
  ]
101
100
  : []),
102
101
  jsenvPluginCacheControl(),
103
- ...(explorer ? [jsenvPluginExplorer(explorer)] : []),
102
+ ...(explorer
103
+ ? [
104
+ jsenvPluginExplorer({
105
+ ...explorer,
106
+ clientMainFileUrl,
107
+ }),
108
+ ]
109
+ : []),
104
110
  ]
105
111
  }
@@ -1,39 +1,115 @@
1
- export const jsenvPluginUrlResolution = ({ clientMainFileUrl }) => {
2
- const urlResolver = (reference) => {
1
+ /*
2
+ * This plugin is responsible to resolve urls except for a few cases:
3
+ * - A custom plugin implements a resolveUrl hook returning something
4
+ * - The reference.type is "filesystem" -> it is handled by jsenv_plugin_file_urls.js
5
+ *
6
+ * By default node esm resolution applies inside js modules
7
+ * and the rest uses the web standard url resolution (new URL):
8
+ * - "http_request"
9
+ * - "entry_point"
10
+ * - "js_import_export"
11
+ * - "link_href"
12
+ * - "script_src"
13
+ * - "a_href"
14
+ * - "iframe_src
15
+ * - "img_src"
16
+ * - "img_srcset"
17
+ * - "source_src"
18
+ * - "source_srcset"
19
+ * - "image_href"
20
+ * - "use_href"
21
+ * - "css_@import"
22
+ * - "css_url"
23
+ * - "sourcemap_comment"
24
+ * - "js_url_specifier"
25
+ * - "js_inline_content"
26
+ * - "webmanifest_icon_src"
27
+ * - "package_json"
28
+ */
29
+
30
+ import { createNodeEsmResolver } from "./node_esm_resolver.js"
31
+
32
+ export const jsenvPluginUrlResolution = ({
33
+ runtimeCompat,
34
+ clientMainFileUrl,
35
+ urlResolution,
36
+ }) => {
37
+ const resolveUrlUsingWebResolution = (reference) => {
3
38
  return new URL(
4
39
  reference.specifier,
5
40
  reference.baseUrl || reference.parentUrl,
6
41
  ).href
7
42
  }
43
+
44
+ const resolvers = {}
45
+ Object.keys(urlResolution).forEach((referenceType) => {
46
+ const resolver = urlResolution[referenceType]
47
+ if (typeof resolver !== "object") {
48
+ throw new Error(
49
+ `Unexpected urlResolution configuration:
50
+ "${referenceType}" resolution value must be an object, got ${resolver}`,
51
+ )
52
+ }
53
+ let { web, node_esm, ...rest } = resolver
54
+ const unexpectedKey = Object.keys(rest)[0]
55
+ if (unexpectedKey) {
56
+ throw new Error(
57
+ `Unexpected urlResolution configuration:
58
+ "${referenceType}" resolution key must be "web" or "node_esm", found "${
59
+ Object.keys(rest)[0]
60
+ }"`,
61
+ )
62
+ }
63
+ if (node_esm === undefined) {
64
+ node_esm = referenceType === "js_import_export"
65
+ }
66
+ if (web === undefined) {
67
+ web = true
68
+ }
69
+ if (node_esm) {
70
+ if (node_esm === true) node_esm = {}
71
+ const { packageConditions } = node_esm
72
+ resolvers[referenceType] = createNodeEsmResolver({
73
+ runtimeCompat,
74
+ packageConditions,
75
+ })
76
+ } else if (web) {
77
+ resolvers[referenceType] = resolveUrlUsingWebResolution
78
+ }
79
+ })
80
+
81
+ if (!resolvers["js_import_export"]) {
82
+ resolvers.js_import_export = createNodeEsmResolver({ runtimeCompat })
83
+ }
84
+ if (!resolvers["*"]) {
85
+ resolvers["*"] = resolveUrlUsingWebResolution
86
+ }
87
+
8
88
  return {
9
89
  name: "jsenv:url_resolution",
10
90
  appliesDuring: "*",
11
- resolveUrl: {
12
- "http_request": (reference) => {
13
- if (reference.specifier === "/") {
14
- return String(clientMainFileUrl)
91
+ resolveUrl: (reference, context) => {
92
+ if (reference.specifier === "/") {
93
+ return String(clientMainFileUrl)
94
+ }
95
+ if (reference.specifier[0] === "/") {
96
+ return new URL(reference.specifier.slice(1), context.rootDirectoryUrl)
97
+ .href
98
+ }
99
+ const resolver = resolvers[reference.type] || resolvers["*"]
100
+ return resolver(reference, context)
101
+ },
102
+ // when specifier is prefixed by "file:///@ignore/"
103
+ // we return an empty js module (used by node esm)
104
+ fetchUrlContent: (urlInfo) => {
105
+ if (urlInfo.url.startsWith("file:///@ignore/")) {
106
+ return {
107
+ content: "export default {}",
108
+ contentType: "text/javascript",
109
+ type: "js_module",
15
110
  }
16
- return urlResolver(reference)
17
- },
18
- "entry_point": urlResolver, // during build
19
- "link_href": urlResolver,
20
- "script_src": urlResolver,
21
- "a_href": urlResolver,
22
- "iframe_src": urlResolver,
23
- "img_src": urlResolver,
24
- "img_srcset": urlResolver,
25
- "source_src": urlResolver,
26
- "source_srcset": urlResolver,
27
- "image_href": urlResolver,
28
- "use_href": urlResolver,
29
- "css_@import": urlResolver,
30
- "css_url": urlResolver,
31
- "sourcemap_comment": urlResolver,
32
- "js_import_export": urlResolver,
33
- "js_url_specifier": urlResolver,
34
- "js_inline_content": urlResolver,
35
- "webmanifest_icon_src": urlResolver,
36
- "package_json": urlResolver,
111
+ }
112
+ return null
37
113
  },
38
114
  }
39
115
  }