@jsenv/core 27.0.0-alpha.51 → 27.0.0-alpha.52

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.51",
3
+ "version": "27.0.0-alpha.52",
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"
@@ -783,7 +783,7 @@ const applyUrlVersioning = async ({
783
783
  }) => {
784
784
  const versioningTask = createTaskLog(logger, "inject version in urls")
785
785
  try {
786
- const urlsSorted = sortUrlGraphByDependencies(finalGraph)
786
+ const urlsSorted = sortByDependencies(finalGraph.urlInfos)
787
787
  urlsSorted.forEach((url) => {
788
788
  if (url.startsWith("data:")) {
789
789
  return
@@ -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
+ }
@@ -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
  ),
@@ -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
- }