@jsenv/core 27.0.0-alpha.5 → 27.0.0-alpha.8

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": "27.0.0-alpha.5",
3
+ "version": "27.0.0-alpha.8",
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": {},
@@ -107,4 +106,4 @@
107
106
  "redux": "4.1.2",
108
107
  "rollup": "2.70.1"
109
108
  }
110
- }
109
+ }
@@ -70,6 +70,8 @@ export const build = async ({
70
70
  writeOnFileSystem = true,
71
71
  buildDirectoryClean = true,
72
72
  baseUrl = "/",
73
+ assetManifest = true,
74
+ assetManifestFileRelativeUrl = "asset-manifest.json",
73
75
  }) => {
74
76
  const logger = createLogger({ logLevel })
75
77
  rootDirectoryUrl = assertAndNormalizeDirectoryUrl(rootDirectoryUrl)
@@ -84,7 +86,7 @@ export const build = async ({
84
86
  const entryPointKeys = Object.keys(entryPoints)
85
87
  if (entryPointKeys.length === 1) {
86
88
  logger.info(`
87
- build ${entryPointKeys[0]}`)
89
+ build "${entryPointKeys[0]}"`)
88
90
  } else {
89
91
  logger.info(`
90
92
  build ${entryPointKeys.length} entry points`)
@@ -119,22 +121,23 @@ build ${entryPointKeys.length} entry points`)
119
121
  scenario: "build",
120
122
  sourcemaps,
121
123
  })
122
- const loadEntryFiles = (cookEntryFile) => {
123
- Object.keys(entryPoints).forEach((key) => {
124
- cookEntryFile({
125
- trace: `"${key}" in entryPoints parameter`,
126
- type: "entry_point",
127
- specifier: key,
128
- })
129
- })
130
- }
124
+ const entryUrls = []
131
125
  try {
132
126
  await loadUrlGraph({
133
127
  urlGraph: rawGraph,
134
128
  kitchen: rawGraphKitchen,
135
129
  outDirectoryUrl: new URL(`.jsenv/build/`, rootDirectoryUrl),
136
130
  runtimeSupport,
137
- startLoading: loadEntryFiles,
131
+ startLoading: (cookEntryFile) => {
132
+ Object.keys(entryPoints).forEach((key) => {
133
+ const [, entryUrlInfo] = cookEntryFile({
134
+ trace: `"${key}" in entryPoints parameter`,
135
+ type: "entry_point",
136
+ specifier: key,
137
+ })
138
+ entryUrls.push(entryUrlInfo.url)
139
+ })
140
+ },
138
141
  })
139
142
  } catch (e) {
140
143
  prebuildTask.fail()
@@ -447,13 +450,23 @@ ${Object.keys(rawGraph.urlInfos).join("\n")}`,
447
450
  sourcemaps,
448
451
  })
449
452
  const buildTask = createTaskLog(logger, "build")
453
+ const postBuildEntryUrls = []
450
454
  try {
451
455
  await loadUrlGraph({
452
456
  urlGraph: finalGraph,
453
457
  kitchen: finalGraphKitchen,
454
458
  outDirectoryUrl: new URL(".jsenv/postbuild/", rootDirectoryUrl),
455
459
  runtimeSupport,
456
- startLoading: loadEntryFiles,
460
+ startLoading: (cookEntryFile) => {
461
+ entryUrls.forEach((entryUrl) => {
462
+ const [, postBuildEntryUrlInfo] = cookEntryFile({
463
+ trace: `entryPoint`,
464
+ type: "entry_point",
465
+ specifier: entryUrl,
466
+ })
467
+ postBuildEntryUrls.push(postBuildEntryUrlInfo.url)
468
+ })
469
+ },
457
470
  })
458
471
  } catch (e) {
459
472
  buildTask.fail()
@@ -540,12 +553,12 @@ ${Object.keys(finalGraph.urlInfos).join("\n")}`,
540
553
  {
541
554
  name: "jsenv:versioning",
542
555
  appliesDuring: { build: true },
543
- resolve: ({ parentUrl, specifier }) => {
544
- const buildUrl = buildUrls[specifier]
556
+ resolve: (reference) => {
557
+ const buildUrl = buildUrls[reference.specifier]
545
558
  if (buildUrl) {
546
559
  return buildUrl
547
560
  }
548
- const url = new URL(specifier, parentUrl).href
561
+ const url = new URL(reference.specifier, reference.parentUrl).href
549
562
  return url
550
563
  },
551
564
  formatReferencedUrl: (reference) => {
@@ -616,7 +629,15 @@ ${Object.keys(finalGraph.urlInfos).join("\n")}`,
616
629
  urlGraph: finalGraph,
617
630
  kitchen: versioningKitchen,
618
631
  runtimeSupport,
619
- startLoading: loadEntryFiles,
632
+ startLoading: (cookEntryFile) => {
633
+ postBuildEntryUrls.forEach((postBuildEntryUrl) => {
634
+ cookEntryFile({
635
+ trace: `entryPoint`,
636
+ type: "entry_point",
637
+ specifier: postBuildEntryUrl,
638
+ })
639
+ })
640
+ },
620
641
  })
621
642
  if (usedVersionMappings.length) {
622
643
  const versionMappingsNeeded = {}
@@ -689,6 +710,16 @@ ${Object.keys(finalGraph.urlInfos).join("\n")}`,
689
710
  )
690
711
  }),
691
712
  )
713
+ if (
714
+ versioning !== "none" &&
715
+ assetManifest &&
716
+ Object.keys(buildManifest).length
717
+ ) {
718
+ await writeFile(
719
+ new URL(assetManifestFileRelativeUrl, buildDirectoryUrl),
720
+ JSON.stringify(buildManifest, null, " "),
721
+ )
722
+ }
692
723
  }
693
724
  logger.info(createUrlGraphSummary(finalGraph, { title: "build files" }))
694
725
  return {
@@ -6,6 +6,12 @@ export const createBuilUrlsGenerator = ({ buildDirectoryUrl }) => {
6
6
  const cache = {}
7
7
  const generate = memoizeByUrl((url, urlInfo, parentUrlInfo) => {
8
8
  const directoryPath = determineDirectoryPath(urlInfo, parentUrlInfo)
9
+ let names = cache[directoryPath]
10
+ if (!names) {
11
+ names = []
12
+ cache[directoryPath] = names
13
+ }
14
+
9
15
  let name = urlToFilename(url)
10
16
  const { searchParams } = new URL(url)
11
17
  if (
@@ -15,12 +21,7 @@ export const createBuilUrlsGenerator = ({ buildDirectoryUrl }) => {
15
21
  ) {
16
22
  name = `${name}.js`
17
23
  }
18
- let names = cache[directoryPath]
19
- if (!names) {
20
- names = []
21
- cache[directoryPath] = names
22
- }
23
-
24
+ const [basename, extension] = splitFileExtension(name)
24
25
  let nameCandidate = name
25
26
  let integer = 1
26
27
  // eslint-disable-next-line no-constant-condition
@@ -30,7 +31,7 @@ export const createBuilUrlsGenerator = ({ buildDirectoryUrl }) => {
30
31
  break
31
32
  }
32
33
  integer++
33
- nameCandidate = `${name}${integer}`
34
+ nameCandidate = `${basename}${integer}${extension}`
34
35
  }
35
36
  return `${buildDirectoryUrl}${directoryPath}${nameCandidate}`
36
37
  })
@@ -40,6 +41,14 @@ export const createBuilUrlsGenerator = ({ buildDirectoryUrl }) => {
40
41
  }
41
42
  }
42
43
 
44
+ const splitFileExtension = (filename) => {
45
+ const dotLastIndex = filename.lastIndexOf(".")
46
+ if (dotLastIndex === -1) {
47
+ return [filename, ""]
48
+ }
49
+ return [filename.slice(0, dotLastIndex), filename.slice(dotLastIndex)]
50
+ }
51
+
43
52
  const determineDirectoryPath = (urlInfo, parentUrlInfo) => {
44
53
  if (urlInfo.isInline) {
45
54
  const parentDirectoryPath = determineDirectoryPath(parentUrlInfo)
@@ -131,15 +131,6 @@ const rollupPluginJsenv = ({
131
131
  const rollupFileInfo = rollupResult[fileName]
132
132
  // there is 3 types of file: "placeholder", "asset", "chunk"
133
133
  if (rollupFileInfo.type === "chunk") {
134
- const { facadeModuleId } = rollupFileInfo
135
- let url
136
- if (facadeModuleId) {
137
- url = fileUrlConverter.asFileUrl(facadeModuleId)
138
- } else {
139
- const { sources } = rollupFileInfo.map
140
- const sourcePath = sources[sources.length - 1]
141
- url = fileUrlConverter.asFileUrl(sourcePath)
142
- }
143
134
  const jsModuleBundleUrlInfo = {
144
135
  // buildRelativeUrl: rollupFileInfo.fileName,
145
136
  data: {
@@ -149,6 +140,12 @@ const rollupPluginJsenv = ({
149
140
  content: rollupFileInfo.code,
150
141
  sourcemap: rollupFileInfo.map,
151
142
  }
143
+ let url
144
+ if (rollupFileInfo.facadeModuleId) {
145
+ url = fileUrlConverter.asFileUrl(rollupFileInfo.facadeModuleId)
146
+ } else {
147
+ url = new URL(rollupFileInfo.fileName, rootDirectoryUrl).href
148
+ }
152
149
  jsModuleBundleUrlInfos[url] = jsModuleBundleUrlInfo
153
150
  }
154
151
  })
@@ -171,13 +168,18 @@ const rollupPluginJsenv = ({
171
168
  },
172
169
  chunkFileNames: (chunkInfo) => {
173
170
  // preserves relative path parts:
174
- // the goal is to mantain the original relative path (relative to the root directory)
171
+ // the goal is to maintain the original relative path (relative to the root directory)
175
172
  // so that later in the build process, when resolving these urls, we are able to
176
173
  // re-resolve the specifier againt the original parent url and find the original url
177
- const { facadeModuleId } = chunkInfo
178
- const fileUrl = fileUrlConverter.asFileUrl(facadeModuleId)
179
- const relativePath = urlToRelativeUrl(fileUrl, rootDirectoryUrl)
180
- return relativePath
174
+ if (chunkInfo.facadeModuleId) {
175
+ const fileUrl = fileUrlConverter.asFileUrl(chunkInfo.facadeModuleId)
176
+ const relativePath = urlToRelativeUrl(fileUrl, rootDirectoryUrl)
177
+ return relativePath
178
+ }
179
+ // chunk generated dynamically by rollup to share code.
180
+ // we prefix with "__rollup__/" to avoid potential conflict of filename
181
+ // between this one and a file with the same name existing in the root directory
182
+ return `__rollup__/${chunkInfo.name}.js`
181
183
  },
182
184
  // https://rollupjs.org/guide/en/#outputpaths
183
185
  // paths: (id) => {
@@ -10,10 +10,7 @@ export const getBaseBabelPluginStructure = ({
10
10
  isJsModule,
11
11
  }) => {
12
12
  const isBabelPluginNeeded = (babelPluginName) => {
13
- return !isSupportedOnRuntime(
14
- babelPluginName,
15
- babelPluginCompatMap[babelPluginName],
16
- )
13
+ return !isSupportedOnRuntime(babelPluginCompatMap[babelPluginName])
17
14
  }
18
15
 
19
16
  const babelPluginStructure = {}
@@ -1,5 +1,6 @@
1
1
  import { applyBabelPlugins } from "@jsenv/utils/js_ast/apply_babel_plugins.js"
2
2
 
3
+ import { RUNTIME_SUPPORT } from "@jsenv/core/src/omega/runtime_support/runtime_support.js"
3
4
  import { getBaseBabelPluginStructure } from "./helpers/babel_plugin_structure.js"
4
5
  import { babelPluginBabelHelpersAsJsenvImports } from "./helpers/babel_plugin_babel_helpers_as_jsenv_imports.js"
5
6
  import { babelPluginNewStylesheetAsJsenvImport } from "./new_stylesheet/babel_plugin_new_stylesheet_as_jsenv_import.js"
@@ -12,16 +13,29 @@ export const jsenvPluginBabel = ({
12
13
  } = {}) => {
13
14
  const transformWithBabel = async (urlInfo, context) => {
14
15
  const isJsModule = urlInfo.type === "js_module"
15
- const isWorker =
16
- urlInfo.subtype === "worker" || urlInfo.subtype === "service_worker"
17
- const { isSupportedOnRuntime, referenceUtils } = context
16
+ const isWorker = urlInfo.subtype === "worker"
17
+ const isServiceWorker = urlInfo.subtype === "service_worker"
18
+ const isWorkerContext = isWorker || isServiceWorker
19
+ let { runtimeSupport } = context
20
+ if (isServiceWorker) {
21
+ // when code is executed by a service worker we can assume
22
+ // the execution context supports more than the default one
23
+ // for instance arrow function are supported
24
+ runtimeSupport = RUNTIME_SUPPORT.add(runtimeSupport, "service_worker")
25
+ }
26
+ if (isWorker) {
27
+ runtimeSupport = RUNTIME_SUPPORT.add(runtimeSupport, "worker")
28
+ }
29
+ const { referenceUtils } = context
30
+ const isSupportedOnRuntime = (feature) =>
31
+ RUNTIME_SUPPORT.isSupported(runtimeSupport, feature)
18
32
  const babelPluginStructure = getBaseBabelPluginStructure({
19
33
  url: urlInfo.url,
20
34
  isSupportedOnRuntime,
21
35
  topLevelAwait,
22
36
  usesTopLevelAwait: urlInfo.data.usesTopLevelAwait,
23
37
  isJsModule,
24
- isWorker,
38
+ isWorkerContext,
25
39
  })
26
40
  if (getCustomBabelPlugins) {
27
41
  Object.assign(babelPluginStructure, getCustomBabelPlugins(context))
@@ -52,10 +52,7 @@ export const jsenvPluginNewInlineContent = ({ allowEscapeForVersioning }) => {
52
52
  columnEnd: inlineContentCall.columnEnd,
53
53
  })
54
54
  let { quote } = inlineContentCall
55
- if (
56
- quote === "`" &&
57
- !isSupportedOnRuntime("transform-template-literals")
58
- ) {
55
+ if (quote === "`" && !isSupportedOnRuntime("template_literals")) {
59
56
  // if quote is "`" and template literals are not supported
60
57
  // we'll use a regular string (single or double quote)
61
58
  // when rendering the string
@@ -10,8 +10,7 @@ import {
10
10
  import { stringifyUrlSite } from "@jsenv/utils/urls/url_trace.js"
11
11
 
12
12
  import { createUrlInfoTransformer } from "./url_graph/url_info_transformations.js"
13
- import { featuresCompatMap } from "./runtime_support/features_compatibility.js"
14
- import { isFeatureSupportedOnRuntimes } from "./runtime_support/runtime_support.js"
13
+ import { RUNTIME_SUPPORT } from "./runtime_support/runtime_support.js"
15
14
  import { fileUrlConverter } from "./file_url_converter.js"
16
15
  import { parseUrlMentions } from "./url_mentions/parse_url_mentions.js"
17
16
  import {
@@ -185,14 +184,6 @@ export const createKitchen = ({
185
184
  },
186
185
  })
187
186
 
188
- const isSupported = ({
189
- runtimeSupport,
190
- featureName,
191
- featureCompat = featuresCompatMap[featureName],
192
- }) => {
193
- return isFeatureSupportedOnRuntimes(runtimeSupport, featureCompat)
194
- }
195
-
196
187
  const load = async ({ reference, urlInfo, context }) => {
197
188
  try {
198
189
  const loadReturnValue = urlInfo.isInline
@@ -274,8 +265,8 @@ export const createKitchen = ({
274
265
  reference,
275
266
  outDirectoryUrl,
276
267
  runtimeSupport,
277
- isSupportedOnRuntime: (featureName, featureCompat) => {
278
- return isSupported({ runtimeSupport, featureName, featureCompat })
268
+ isSupportedOnRuntime: (feature) => {
269
+ return RUNTIME_SUPPORT.isSupported(runtimeSupport, feature)
279
270
  },
280
271
  cook: (params) => {
281
272
  return cookDuringCook({
@@ -585,7 +576,6 @@ export const createKitchen = ({
585
576
  urlInfoTransformer,
586
577
  rootDirectoryUrl,
587
578
  jsenvDirectoryUrl,
588
- isSupported,
589
579
  createReference,
590
580
  resolveReference,
591
581
  cook,
@@ -1,4 +1,4 @@
1
- export const featuresCompatMap = {
1
+ export const featureCompats = {
2
2
  script_type_module: {
3
3
  edge: "16",
4
4
  firefox: "60",
@@ -52,6 +52,7 @@ export const featuresCompatMap = {
52
52
  chrome: "93",
53
53
  edge: "93",
54
54
  },
55
+ import_type_text: {},
55
56
  // https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet#browser_compatibility
56
57
  new_stylesheet: {
57
58
  chrome: "73",
@@ -59,6 +60,33 @@ export const featuresCompatMap = {
59
60
  opera: "53",
60
61
  android: "73",
61
62
  },
63
+ // https://caniuse.com/?search=worker
64
+ worker: {
65
+ ie: "10",
66
+ edge: "12",
67
+ firefox: "3.5",
68
+ chrome: "4",
69
+ opera: "11.5",
70
+ safari: "4",
71
+ ios: "5",
72
+ android: "4.4",
73
+ },
74
+ service_worker: {
75
+ edge: "17",
76
+ firefox: "44",
77
+ chrome: "40",
78
+ safari: "11.1",
79
+ opera: "27",
80
+ ios: "11.3",
81
+ android: "12.12",
82
+ },
83
+ service_worker_type_module: {
84
+ chrome: "80",
85
+ edge: "80",
86
+ opera: "67",
87
+ android: "80",
88
+ },
89
+ // https://developer.mozilla.org/en-US/docs/Web/API/Worker/Worker#browser_compatibility
62
90
  worker_type_module: {
63
91
  chrome: "80",
64
92
  edge: "80",
@@ -88,4 +116,15 @@ export const featuresCompatMap = {
88
116
  samsung: "8",
89
117
  electron: "3",
90
118
  },
119
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#browser_compatibility
120
+ template_literals: {
121
+ chrome: "41",
122
+ edge: "12",
123
+ firefox: "34",
124
+ opera: "28",
125
+ safari: "9",
126
+ ios: "9",
127
+ android: "4",
128
+ node: "4",
129
+ },
91
130
  }
@@ -1,20 +1,52 @@
1
1
  import { findHighestVersion } from "@jsenv/utils/semantic_versioning/highest_version.js"
2
+ import { featureCompats } from "./features_compatibility.js"
2
3
 
3
- export const isFeatureSupportedOnRuntimes = (
4
- runtimeSupport,
5
- featureCompat = {},
6
- ) => {
7
- const runtimeNames = Object.keys(runtimeSupport)
8
- return runtimeNames.every((runtimeName) => {
9
- const runtimeVersion = runtimeSupport[runtimeName]
10
- const runtimeVersionCompatible = featureCompat[runtimeName] || "Infinity"
11
- const highestVersion = findHighestVersion(
12
- runtimeVersion,
13
- runtimeVersionCompatible,
14
- )
15
- if (highestVersion !== runtimeVersion) {
16
- return false
4
+ export const RUNTIME_SUPPORT = {
5
+ featureCompats,
6
+
7
+ add: (originalRuntimeSupport, feature) => {
8
+ const featureCompat = getFeatureCompat(feature)
9
+ const runtimeSupport = {
10
+ ...originalRuntimeSupport,
11
+ }
12
+ Object.keys(featureCompat).forEach((runtimeName) => {
13
+ const firstVersion = originalRuntimeSupport[runtimeName]
14
+ const secondVersion = featureCompat[runtimeName]
15
+ runtimeSupport[runtimeName] = firstVersion
16
+ ? findHighestVersion(firstVersion, secondVersion)
17
+ : secondVersion
18
+ })
19
+ return runtimeSupport
20
+ },
21
+
22
+ isSupported: (runtimeSupport, feature) => {
23
+ const featureCompat = getFeatureCompat(feature)
24
+ const runtimeNames = Object.keys(runtimeSupport)
25
+ return runtimeNames.every((runtimeName) => {
26
+ const runtimeVersion = runtimeSupport[runtimeName]
27
+ const runtimeVersionCompatible = featureCompat[runtimeName] || "Infinity"
28
+ const highestVersion = findHighestVersion(
29
+ runtimeVersion,
30
+ runtimeVersionCompatible,
31
+ )
32
+ if (highestVersion !== runtimeVersion) {
33
+ return false
34
+ }
35
+ return true
36
+ })
37
+ },
38
+ }
39
+
40
+ const getFeatureCompat = (feature) => {
41
+ if (typeof feature === "string") {
42
+ const compat = featureCompats[feature]
43
+ if (!compat) {
44
+ throw new Error(`"${feature}" feature is unknown`)
17
45
  }
18
- return true
19
- })
46
+ return compat
47
+ }
48
+ if (typeof feature !== "object") {
49
+ throw new TypeError(`feature must be a string or an object, got ${feature}`)
50
+ }
51
+ return feature
20
52
  }
@@ -52,6 +52,7 @@ export const loadUrlGraph = async ({
52
52
  reference: entryReference,
53
53
  urlInfo: entryUrlInfo,
54
54
  })
55
+ return [entryReference, entryUrlInfo]
55
56
  },
56
57
  )
57
58