@jsenv/core 27.0.0-alpha.50 → 27.0.0-alpha.53

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.50",
3
+ "version": "27.0.0-alpha.53",
4
4
  "description": "Tool to develop, test and build js projects",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -68,7 +68,7 @@
68
68
  "@jsenv/node-esm-resolution": "0.0.6",
69
69
  "@jsenv/server": "12.6.2",
70
70
  "@jsenv/uneval": "1.6.0",
71
- "@jsenv/utils": "1.7.1",
71
+ "@jsenv/utils": "1.7.2",
72
72
  "construct-style-sheets-polyfill": "3.1.0",
73
73
  "cssnano": "5.1.7",
74
74
  "cssnano-preset-default": "5.2.7",
@@ -29,6 +29,7 @@ import {
29
29
  parseHtmlString,
30
30
  stringifyHtmlAst,
31
31
  } from "@jsenv/utils/html_ast/html_ast.js"
32
+ import { sortByDependencies } from "@jsenv/utils/graph/sort_by_dependencies.js"
32
33
 
33
34
  import { jsenvPluginUrlAnalysis } from "../plugins/url_analysis/jsenv_plugin_url_analysis.js"
34
35
  import { jsenvPluginInline } from "../plugins/inline/jsenv_plugin_inline.js"
@@ -38,7 +39,6 @@ import { getCorePlugins } from "../plugins/plugins.js"
38
39
  import { createKitchen } from "../omega/kitchen.js"
39
40
  import { loadUrlGraph } from "../omega/url_graph/url_graph_load.js"
40
41
  import { createUrlGraphSummary } from "../omega/url_graph/url_graph_report.js"
41
- import { sortUrlGraphByDependencies } from "../omega/url_graph/url_graph_sort.js"
42
42
  import { isWebWorkerEntryPointReference } from "../omega/web_workers.js"
43
43
 
44
44
  import { GRAPH } from "./graph_utils.js"
@@ -618,6 +618,7 @@ ${Object.keys(rawGraph.urlInfos).join("\n")}`,
618
618
  urlGraph: finalGraph,
619
619
  kitchen: finalGraphKitchen,
620
620
  outDirectoryUrl: new URL(".jsenv/postbuild/", rootDirectoryUrl),
621
+ skipRessourceHint: true,
621
622
  startLoading: (cookEntryFile) => {
622
623
  entryUrls.forEach((entryUrl) => {
623
624
  const [, postBuildEntryUrlInfo] = cookEntryFile({
@@ -698,7 +699,7 @@ ${Object.keys(finalGraph.urlInfos).join("\n")}`,
698
699
  urlInfo.dependents.size === 0
699
700
  ) {
700
701
  cleanupActions.push(() => {
701
- delete finalGraph.urlInfos[urlInfo.url]
702
+ finalGraph.deleteUrlInfo(urlInfo.url)
702
703
  })
703
704
  }
704
705
  })
@@ -783,7 +784,7 @@ const applyUrlVersioning = async ({
783
784
  }) => {
784
785
  const versioningTask = createTaskLog(logger, "inject version in urls")
785
786
  try {
786
- const urlsSorted = sortUrlGraphByDependencies(finalGraph)
787
+ const urlsSorted = sortByDependencies(finalGraph.urlInfos)
787
788
  urlsSorted.forEach((url) => {
788
789
  if (url.startsWith("data:")) {
789
790
  return
@@ -969,6 +970,7 @@ const applyUrlVersioning = async ({
969
970
  await loadUrlGraph({
970
971
  urlGraph: finalGraph,
971
972
  kitchen: versioningKitchen,
973
+ skipRessourceHint: true,
972
974
  startLoading: (cookEntryFile) => {
973
975
  postBuildEntryUrls.forEach((postBuildEntryUrl) => {
974
976
  cookEntryFile({
@@ -56,8 +56,8 @@ export const createBuilUrlsGenerator = ({ buildDirectoryUrl }) => {
56
56
  // To keep in mind: if you have "user.jsx" and "user.js" AND both file are not bundled
57
57
  // you end up with "dist/js/user.js" and "dist/js/user2.js"
58
58
  const extensionMappings = {
59
- ".ts": ".js",
60
59
  ".jsx": ".js",
60
+ ".ts": ".js",
61
61
  ".tsx": ".js",
62
62
  }
63
63
 
@@ -262,6 +262,9 @@ export const createRuntimeFromPlaywright = ({
262
262
  cb({ reason: "page closed" })
263
263
  })
264
264
  cleanupCallbackList.add(closePage)
265
+ cleanupCallbackList.add(() => {
266
+ browser.removeListener("disconnected", disconnectedCallback)
267
+ })
265
268
  const notifyPrevious = stopAfterAllSignal.notify
266
269
  stopAfterAllSignal.notify = async () => {
267
270
  await notifyPrevious()
@@ -164,6 +164,12 @@ export const createKitchen = ({
164
164
  if (returnValue === reference.url) {
165
165
  return
166
166
  }
167
+ const normalizedReturnValue = returnValue.startsWith("data:")
168
+ ? returnValue
169
+ : returnValue.replace(/[=](?=&|$)/g, "")
170
+ if (normalizedReturnValue === reference.url) {
171
+ return
172
+ }
167
173
  const previousReference = { ...reference }
168
174
  reference.url = returnValue
169
175
  mutateReference(previousReference, reference)
@@ -6,6 +6,7 @@ export const loadUrlGraph = async ({
6
6
  startLoading,
7
7
  outDirectoryUrl,
8
8
  clientRuntimeCompat,
9
+ skipRessourceHint = false,
9
10
  }) => {
10
11
  if (outDirectoryUrl) {
11
12
  await ensureEmptyDirectory(outDirectoryUrl)
@@ -33,6 +34,9 @@ export const loadUrlGraph = async ({
33
34
  })
34
35
  const { references } = urlInfo
35
36
  references.forEach((reference) => {
37
+ if (skipRessourceHint && reference.isRessourceHint) {
38
+ return
39
+ }
36
40
  // we use reference.generatedUrl to mimic what a browser would do:
37
41
  // do a fetch to the specifier as found in the file
38
42
  const referencedUrlInfo = urlGraph.reuseOrCreateUrlInfo(
@@ -1,21 +1,140 @@
1
- import { bundleWithParcel } from "@jsenv/utils/css_ast/parcel_css.js"
1
+ /*
2
+ * Each @import found in css is replaced by the file content
3
+ * - There is no need to worry about urls (such as background-image: url())
4
+ * because they are absolute (file://*) and will be made relative again by jsenv build
5
+ * - The sourcemap are not generated but ideally they should be
6
+ * It can be quite challenging, see "bundle_sourcemap.js"
7
+ */
2
8
 
9
+ import { applyPostCss } from "@jsenv/utils/css_ast/apply_post_css.js"
10
+ import { postCssPluginUrlVisitor } from "@jsenv/utils/css_ast/postcss_plugin_url_visitor.js"
11
+ import { createMagicSource } from "@jsenv/utils/sourcemap/magic_source.js"
12
+ import { sortByDependencies } from "@jsenv/utils/graph/sort_by_dependencies.js"
13
+
14
+ // Do not use until https://github.com/parcel-bundler/parcel-css/issues/181
3
15
  export const bundleCss = async ({ cssUrlInfos, context }) => {
4
16
  const bundledCssUrlInfos = {}
17
+ const cssBundleInfos = await performCssBundling({
18
+ cssEntryUrlInfos: cssUrlInfos,
19
+ context,
20
+ })
5
21
  cssUrlInfos.forEach((cssUrlInfo) => {
6
- const { code, map } = bundleWithParcel(cssUrlInfo, context)
7
- const content = String(code)
8
- const sourcemap = map
9
- // here we need to replace css urls to ensure
10
- // all urls targets the correct stuff
11
22
  bundledCssUrlInfos[cssUrlInfo.url] = {
12
23
  data: {
13
24
  generatedBy: "parcel",
14
25
  },
15
26
  contentType: "text/css",
16
- content,
17
- sourcemap,
27
+ content: cssBundleInfos[cssUrlInfo.url].bundleContent,
18
28
  }
19
29
  })
20
30
  return bundledCssUrlInfos
21
31
  }
32
+
33
+ const performCssBundling = async ({ cssEntryUrlInfos, context }) => {
34
+ const cssBundleInfos = await loadCssUrls({
35
+ cssEntryUrlInfos,
36
+ context,
37
+ })
38
+ const cssUrlsSorted = sortByDependencies(cssBundleInfos)
39
+ cssUrlsSorted.forEach((cssUrl) => {
40
+ const cssBundleInfo = cssBundleInfos[cssUrl]
41
+ const magicSource = createMagicSource(cssBundleInfo.content)
42
+ cssBundleInfo.cssUrls.forEach((cssUrl) => {
43
+ if (cssUrl.type === "@import") {
44
+ magicSource.replace({
45
+ start: cssUrl.atRuleStart,
46
+ end: cssUrl.atRuleEnd,
47
+ replacement: cssBundleInfos[cssUrl.url].bundleContent,
48
+ })
49
+ }
50
+ })
51
+ const { content } = magicSource.toContentAndSourcemap()
52
+ cssBundleInfo.bundleContent = content.trim()
53
+ })
54
+ return cssBundleInfos
55
+ }
56
+
57
+ const parseCssUrls = async ({ css, url }) => {
58
+ const cssUrls = []
59
+ await applyPostCss({
60
+ sourcemaps: false,
61
+ plugins: [
62
+ postCssPluginUrlVisitor({
63
+ urlVisitor: ({
64
+ type,
65
+ specifier,
66
+ specifierStart,
67
+ specifierEnd,
68
+ atRuleStart,
69
+ atRuleEnd,
70
+ }) => {
71
+ cssUrls.push({
72
+ type,
73
+ url: new URL(specifier, url).href,
74
+ specifierStart,
75
+ specifierEnd,
76
+ atRuleStart,
77
+ atRuleEnd,
78
+ })
79
+ },
80
+ }),
81
+ ],
82
+ url,
83
+ content: css,
84
+ })
85
+
86
+ return cssUrls
87
+ }
88
+
89
+ const loadCssUrls = async ({ cssEntryUrlInfos, context }) => {
90
+ const cssBundleInfos = {}
91
+ const promises = []
92
+ const promiseMap = new Map()
93
+
94
+ const load = (cssUrlInfo) => {
95
+ const promiseFromData = promiseMap.get(cssUrlInfo.url)
96
+ if (promiseFromData) return promiseFromData
97
+ const promise = _load(cssUrlInfo)
98
+ promises.push(promise)
99
+ promiseMap.set(cssUrlInfo.url, promise)
100
+ return promise
101
+ }
102
+
103
+ const _load = async (cssUrlInfo) => {
104
+ const cssUrls = await parseCssUrls({
105
+ css: cssUrlInfo.content,
106
+ url: cssUrlInfo.url,
107
+ })
108
+ const cssBundleInfo = {
109
+ content: cssUrlInfo.content,
110
+ cssUrls,
111
+ dependencies: [],
112
+ }
113
+ cssBundleInfos[cssUrlInfo.url] = cssBundleInfo
114
+ cssUrls.forEach((cssUrl) => {
115
+ if (cssUrl.type === "@import") {
116
+ cssBundleInfo.dependencies.push(cssUrl.url)
117
+ const importedCssUrlInfo = context.urlGraph.getUrlInfo(cssUrl.url)
118
+ load(importedCssUrlInfo)
119
+ }
120
+ })
121
+ }
122
+
123
+ cssEntryUrlInfos.forEach((cssEntryUrlInfo) => {
124
+ load(cssEntryUrlInfo)
125
+ })
126
+
127
+ const waitAll = async () => {
128
+ if (promises.length === 0) {
129
+ return
130
+ }
131
+ const promisesToWait = promises.slice()
132
+ promises.length = 0
133
+ await Promise.all(promisesToWait)
134
+ await waitAll()
135
+ }
136
+ await waitAll()
137
+ promiseMap.clear()
138
+
139
+ return cssBundleInfos
140
+ }
@@ -147,9 +147,6 @@ export const jsenvPluginHtmlSupervisor = ({
147
147
  return
148
148
  }
149
149
  })
150
- if (scriptsToSupervise.length === 0) {
151
- return null
152
- }
153
150
  const [htmlSupervisorInstallerFileReference] = referenceUtils.inject({
154
151
  type: "js_import_export",
155
152
  expectedType: "js_module",
@@ -274,8 +274,9 @@ const getOriginalName = (path, name) => {
274
274
  return getOriginalName(path, importedName)
275
275
  }
276
276
  if (binding.path.type === "VariableDeclarator") {
277
- if (binding.path.node.init.type === "Identifier") {
278
- const previousName = binding.path.node.init.name
277
+ const { init } = binding.path.node
278
+ if (init && init.type === "Identifier") {
279
+ const previousName = init.name
279
280
  return getOriginalName(path, previousName)
280
281
  }
281
282
  }
@@ -119,7 +119,15 @@ const asJsClassic = ({ systemJsInjection, systemJsClientFileUrl }) => {
119
119
 
120
120
  const generateJsClassicFilename = (url) => {
121
121
  const filename = urlToFilename(url)
122
- const [basename, extension] = splitFileExtension(filename)
122
+ let [basename, extension] = splitFileExtension(filename)
123
+ const { searchParams } = new URL(url)
124
+ if (
125
+ searchParams.has("as_json_module") ||
126
+ searchParams.has("as_css_module") ||
127
+ searchParams.has("as_text_module")
128
+ ) {
129
+ extension = ".js"
130
+ }
123
131
  return `${basename}.es5${extension}`
124
132
  }
125
133
 
@@ -27,10 +27,10 @@ export const jsenvPluginImportAssertions = () => {
27
27
  end: reference.assertNode.end,
28
28
  })
29
29
  }
30
-
31
- return injectQueryParams(reference.url, {
30
+ const newUrl = injectQueryParams(reference.url, {
32
31
  [searchParam]: "",
33
32
  })
33
+ return newUrl
34
34
  }
35
35
 
36
36
  const importAssertions = {
@@ -13,19 +13,26 @@ export const parseAndTransformCssUrls = async (urlInfo, context) => {
13
13
  sourcemaps: false,
14
14
  plugins: [
15
15
  postCssPluginUrlVisitor({
16
- urlVisitor: ({ type, specifier, line, column, start, end }) => {
16
+ urlVisitor: ({
17
+ type,
18
+ specifier,
19
+ specifierStart,
20
+ specifierEnd,
21
+ specifierLine,
22
+ specifierColumn,
23
+ }) => {
17
24
  const [reference] = context.referenceUtils.found({
18
25
  type: `css_${type}`,
19
26
  specifier,
20
- specifierStart: start,
21
- specifierEnd: end,
22
- specifierLine: line,
23
- specifierColumn: column,
27
+ specifierStart,
28
+ specifierEnd,
29
+ specifierLine,
30
+ specifierColumn,
24
31
  })
25
32
  actions.push(async () => {
26
33
  magicSource.replace({
27
- start,
28
- end,
34
+ start: specifierStart,
35
+ end: specifierEnd,
29
36
  replacement: await context.referenceUtils.readGeneratedSpecifier(
30
37
  reference,
31
38
  ),
@@ -60,7 +60,7 @@ export const formatExecutionResult = (
60
60
  file: fileRelativeUrl,
61
61
  runtime: `${runtimeName}/${runtimeVersion}`,
62
62
  duration: msAsDuration(duration),
63
- ...(error ? { error: error.stack } : {}),
63
+ ...(error ? { error: error.stack || error.message || error } : {}),
64
64
  },
65
65
  consoleOutput,
66
66
  })
@@ -1,29 +0,0 @@
1
- export const sortUrlGraphByDependencies = (urlGraph) => {
2
- const { urlInfos } = urlGraph
3
-
4
- const visited = []
5
- const sorted = []
6
- const circular = []
7
- const visit = (url) => {
8
- const isSorted = sorted.includes(url)
9
- if (isSorted) {
10
- return
11
- }
12
- const isVisited = visited.includes(url)
13
- if (isVisited) {
14
- circular.push(url)
15
- sorted.push(url)
16
- } else {
17
- visited.push(url)
18
- urlInfos[url].dependencies.forEach((dependencyUrl) => {
19
- visit(dependencyUrl, url)
20
- })
21
- sorted.push(url)
22
- }
23
- }
24
- Object.keys(urlInfos).forEach((url) => {
25
- visit(url)
26
- })
27
- sorted.circular = circular
28
- return sorted
29
- }