@jsenv/core 40.0.10 → 40.1.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.
@@ -1,9 +1,28 @@
1
+ import { URL_META } from "@jsenv/url-meta";
1
2
  import { urlToFilename } from "@jsenv/urls";
2
3
  import { defineNonEnumerableProperties } from "../../kitchen/errors.js";
3
4
 
4
5
  export const jsenvPluginDirectoryReferenceEffect = (
5
6
  directoryReferenceEffect = "error",
7
+ { rootDirectoryUrl },
6
8
  ) => {
9
+ let getDirectoryReferenceEffect;
10
+ if (typeof directoryReferenceEffect === "string") {
11
+ getDirectoryReferenceEffect = () => directoryReferenceEffect;
12
+ } else if (typeof directoryReferenceEffect === "function") {
13
+ getDirectoryReferenceEffect = directoryReferenceEffect;
14
+ } else if (typeof directoryReferenceEffect === "object") {
15
+ const associations = URL_META.resolveAssociations(
16
+ { effect: directoryReferenceEffect },
17
+ rootDirectoryUrl,
18
+ );
19
+ getDirectoryReferenceEffect = (reference) => {
20
+ const { url } = reference;
21
+ const meta = URL_META.applyAssociations({ url, associations });
22
+ return meta.effect || "error";
23
+ };
24
+ }
25
+
7
26
  return {
8
27
  name: "jsenv:directory_reference_effect",
9
28
  appliesDuring: "*",
@@ -41,12 +60,8 @@ export const jsenvPluginDirectoryReferenceEffect = (
41
60
  actionForDirectory = "copy";
42
61
  } else if (reference.type === "http_request") {
43
62
  actionForDirectory = "preserve";
44
- } else if (typeof directoryReferenceEffect === "string") {
45
- actionForDirectory = directoryReferenceEffect;
46
- } else if (typeof directoryReferenceEffect === "function") {
47
- actionForDirectory = directoryReferenceEffect(reference);
48
63
  } else {
49
- actionForDirectory = "error";
64
+ actionForDirectory = getDirectoryReferenceEffect(reference);
50
65
  }
51
66
  reference.actionForDirectory = actionForDirectory;
52
67
  if (actionForDirectory !== "copy") {
@@ -32,6 +32,7 @@ export const getCorePlugins = ({
32
32
 
33
33
  referenceAnalysis = {},
34
34
  nodeEsmResolution = {},
35
+ packageConditions,
35
36
  magicExtensions,
36
37
  magicDirectoryIndex,
37
38
  directoryListing = true,
@@ -102,10 +103,12 @@ export const getCorePlugins = ({
102
103
  },
103
104
  },
104
105
  ...(nodeEsmResolution
105
- ? [jsenvPluginNodeEsmResolution(nodeEsmResolution)]
106
+ ? [jsenvPluginNodeEsmResolution(nodeEsmResolution, packageConditions)]
106
107
  : []),
107
108
  jsenvPluginWebResolution(),
108
- jsenvPluginDirectoryReferenceEffect(directoryReferenceEffect),
109
+ jsenvPluginDirectoryReferenceEffect(directoryReferenceEffect, {
110
+ rootDirectoryUrl,
111
+ }),
109
112
  jsenvPluginVersionSearchParam(),
110
113
 
111
114
  // "jsenvPluginSupervisor" MUST be after "jsenvPluginInlining" as it needs inline script to be cooked
@@ -23,7 +23,7 @@
23
23
  */
24
24
 
25
25
  import {
26
- comparePathnames,
26
+ compareFileUrls,
27
27
  lookupPackageDirectory,
28
28
  readEntryStatSync,
29
29
  registerDirectoryLifecycle,
@@ -399,9 +399,8 @@ const getDirectoryContentItems = ({
399
399
  fileUrls.push(fileUrlObject);
400
400
  }
401
401
  }
402
- fileUrls.sort((a, b) => {
403
- return comparePathnames(a.pathname, b.pathname);
404
- });
402
+ fileUrls.sort(compareFileUrls);
403
+
405
404
  const items = [];
406
405
  for (const fileUrl of fileUrls) {
407
406
  const urlRelativeToCurrentDirectory = urlToRelativeUrl(
@@ -10,12 +10,9 @@ export const jsenvPluginReferenceAnalysis = ({
10
10
  inlineContent = true,
11
11
  inlineConvertedScript = false,
12
12
  fetchInlineUrls = true,
13
- directoryReferenceEffect,
14
13
  }) => {
15
14
  return [
16
- jsenvPluginDirectoryReferenceAnalysis({
17
- directoryReferenceEffect,
18
- }),
15
+ jsenvPluginDirectoryReferenceAnalysis(),
19
16
  jsenvPluginHtmlReferenceAnalysis({
20
17
  inlineContent,
21
18
  inlineConvertedScript,
@@ -1,8 +1,33 @@
1
1
  import { createNodeEsmResolver } from "./node_esm_resolver.js";
2
2
 
3
- export const jsenvPluginNodeEsmResolution = (resolutionConfig = {}) => {
3
+ export const jsenvPluginNodeEsmResolution = (
4
+ resolutionConfig = {},
5
+ packageConditions,
6
+ ) => {
4
7
  let nodeEsmResolverDefault;
5
- const resolvers = {};
8
+ const resolverMap = new Map();
9
+ let anyTypeResolver;
10
+
11
+ const resolverFromObject = (
12
+ { preservesSymlink, ...rest },
13
+ { kitchenContext, urlType },
14
+ ) => {
15
+ const unexpectedKeys = Object.keys(rest);
16
+ if (unexpectedKeys.length) {
17
+ throw new TypeError(
18
+ `${unexpectedKeys.join(
19
+ ",",
20
+ )}: there is no such configuration on "${urlType}"`,
21
+ );
22
+ }
23
+ return createNodeEsmResolver({
24
+ build: kitchenContext.build,
25
+ runtimeCompat: kitchenContext.runtimeCompat,
26
+ rootDirectoryUrl: kitchenContext.rootDirectoryUrl,
27
+ packageConditions,
28
+ preservesSymlink,
29
+ });
30
+ };
6
31
 
7
32
  return {
8
33
  name: "jsenv:node_esm_resolution",
@@ -11,47 +36,38 @@ export const jsenvPluginNodeEsmResolution = (resolutionConfig = {}) => {
11
36
  nodeEsmResolverDefault = createNodeEsmResolver({
12
37
  build: kitchenContext.build,
13
38
  runtimeCompat: kitchenContext.runtimeCompat,
39
+ rootDirectoryUrl: kitchenContext.rootDirectoryUrl,
14
40
  preservesSymlink: true,
41
+ packageConditions,
15
42
  });
16
- Object.keys(resolutionConfig).forEach((urlType) => {
43
+ for (const urlType of Object.keys(resolutionConfig)) {
44
+ let resolver;
17
45
  const config = resolutionConfig[urlType];
18
46
  if (config === true) {
19
- resolvers[urlType] = (...args) => nodeEsmResolverDefault(...args);
47
+ resolver = nodeEsmResolverDefault;
20
48
  } else if (config === false) {
21
- resolvers[urlType] = () => null;
49
+ // resolverMap.set(urlType, () => null);
50
+ continue;
22
51
  } else if (typeof config === "object") {
23
- const {
24
- runtimeCompat,
25
- packageConditions,
26
- preservesSymlink,
27
- ...rest
28
- } = config;
29
- const unexpectedKeys = Object.keys(rest);
30
- if (unexpectedKeys.length) {
31
- throw new TypeError(
32
- `${unexpectedKeys.join(
33
- ",",
34
- )}: there is no such configuration on "${urlType}"`,
35
- );
36
- }
37
- resolvers[urlType] = createNodeEsmResolver({
38
- build: kitchenContext.build,
39
- runtimeCompat,
40
- packageConditions,
41
- preservesSymlink,
42
- });
52
+ resolver = resolverFromObject(config, { kitchenContext, urlType });
43
53
  } else {
44
54
  throw new TypeError(
45
- `config must be true, false or an object, got ${config} on "${urlType}"`,
55
+ `The value "${config}" for ${urlType} in nodeEsmResolution is invalid: it must be true, false or an object.`,
46
56
  );
47
57
  }
48
- });
49
58
 
50
- if (resolvers.js_module === undefined) {
51
- resolvers.js_module = nodeEsmResolverDefault;
59
+ if (urlType === "*") {
60
+ anyTypeResolver = resolver;
61
+ } else {
62
+ resolverMap.set(urlType, resolver);
63
+ }
64
+ }
65
+
66
+ if (!resolverMap.has("js_module")) {
67
+ resolverMap.set("js_module", nodeEsmResolverDefault);
52
68
  }
53
- if (resolvers.js_classic === undefined) {
54
- resolvers.js_classic = (reference) => {
69
+ if (!resolverMap.has("js_classic")) {
70
+ resolverMap.set("js_classic", (reference) => {
55
71
  if (reference.subtype === "self_import_scripts_arg") {
56
72
  return nodeEsmResolverDefault(reference);
57
73
  }
@@ -60,7 +76,7 @@ export const jsenvPluginNodeEsmResolution = (resolutionConfig = {}) => {
60
76
  return nodeEsmResolverDefault(reference);
61
77
  }
62
78
  return null;
63
- };
79
+ });
64
80
  }
65
81
  },
66
82
  resolveReference: (reference) => {
@@ -70,8 +86,14 @@ export const jsenvPluginNodeEsmResolution = (resolutionConfig = {}) => {
70
86
  return result;
71
87
  }
72
88
  const urlType = urlTypeFromReference(reference);
73
- const resolver = resolvers[urlType];
74
- return resolver ? resolver(reference) : null;
89
+ const resolver = resolverMap.get(urlType);
90
+ if (resolver) {
91
+ return resolver(reference);
92
+ }
93
+ if (anyTypeResolver) {
94
+ return anyTypeResolver(reference);
95
+ }
96
+ return null;
75
97
  },
76
98
  // when specifier is prefixed by "file:///@ignore/"
77
99
  // we return an empty js module
@@ -13,22 +13,23 @@ import {
13
13
  defaultReadPackageJson,
14
14
  readCustomConditionsFromProcessArgs,
15
15
  } from "@jsenv/node-esm-resolution";
16
+ import { URL_META } from "@jsenv/url-meta";
16
17
  import { urlToBasename, urlToExtension } from "@jsenv/urls";
17
18
  import { readFileSync } from "node:fs";
18
19
 
19
20
  export const createNodeEsmResolver = ({
20
- build,
21
21
  runtimeCompat,
22
- packageConditions,
22
+ rootDirectoryUrl,
23
+ packageConditions = {},
23
24
  preservesSymlink,
24
25
  }) => {
25
- const nodeRuntimeEnabled = Object.keys(runtimeCompat).includes("node");
26
- // https://nodejs.org/api/esm.html#resolver-algorithm-specification
27
- packageConditions = packageConditions || [
28
- ...(build ? [] : readCustomConditionsFromProcessArgs()),
29
- nodeRuntimeEnabled ? "node" : "browser",
30
- "import",
31
- ];
26
+ const buildPackageConditions = createBuildPackageConditions(
27
+ packageConditions,
28
+ {
29
+ rootDirectoryUrl,
30
+ runtimeCompat,
31
+ },
32
+ );
32
33
 
33
34
  return (reference) => {
34
35
  if (reference.type === "package_json") {
@@ -49,10 +50,12 @@ export const createNodeEsmResolver = ({
49
50
  if (!parentUrl.startsWith("file:")) {
50
51
  return null; // let it to jsenv_web_resolution
51
52
  }
53
+ const { specifier } = reference;
54
+ const conditions = buildPackageConditions(specifier, parentUrl);
52
55
  const { url, type, isMain, packageDirectoryUrl } = applyNodeEsmResolution({
53
- conditions: packageConditions,
56
+ conditions,
54
57
  parentUrl,
55
- specifier: reference.specifier,
58
+ specifier,
56
59
  preservesSymlink,
57
60
  });
58
61
  // try to give a more meaningful filename after build
@@ -116,6 +119,107 @@ export const createNodeEsmResolver = ({
116
119
  };
117
120
  };
118
121
 
122
+ const createBuildPackageConditions = (
123
+ packageConditions,
124
+ { rootDirectoryUrl, runtimeCompat },
125
+ ) => {
126
+ const nodeRuntimeEnabled = Object.keys(runtimeCompat).includes("node");
127
+ // https://nodejs.org/api/esm.html#resolver-algorithm-specification
128
+ const processArgConditions = readCustomConditionsFromProcessArgs();
129
+ const packageConditionsDefaultResolvers = {};
130
+ for (const processArgCondition of processArgConditions) {
131
+ packageConditionsDefaultResolvers[processArgCondition] = true;
132
+ }
133
+ const packageConditionResolvers = {
134
+ ...packageConditionsDefaultResolvers,
135
+ development: (specifier, importer) => {
136
+ if (isBareSpecifier(specifier)) {
137
+ const { url } = applyNodeEsmResolution({
138
+ specifier,
139
+ parentUrl: importer,
140
+ });
141
+ return !url.includes("/node_modules/");
142
+ }
143
+ return !importer.includes("/node_modules/");
144
+ },
145
+ node: nodeRuntimeEnabled,
146
+ browser: !nodeRuntimeEnabled,
147
+ import: true,
148
+ };
149
+ for (const condition of Object.keys(packageConditions)) {
150
+ const value = packageConditions[condition];
151
+ let customResolver;
152
+ if (typeof value === "object") {
153
+ const associations = URL_META.resolveAssociations(
154
+ { applies: value },
155
+ (pattern) => {
156
+ if (isBareSpecifier(pattern)) {
157
+ try {
158
+ if (pattern.endsWith("/")) {
159
+ // avoid package path not exported
160
+ const { packageDirectoryUrl } = applyNodeEsmResolution({
161
+ specifier: pattern.slice(0, -1),
162
+ parentUrl: rootDirectoryUrl,
163
+ });
164
+ return packageDirectoryUrl;
165
+ }
166
+ const { url } = applyNodeEsmResolution({
167
+ specifier: pattern,
168
+ parentUrl: rootDirectoryUrl,
169
+ });
170
+ return url;
171
+ } catch {
172
+ return new URL(pattern, rootDirectoryUrl);
173
+ }
174
+ }
175
+ return new URL(pattern, rootDirectoryUrl);
176
+ },
177
+ );
178
+ customResolver = (specifier, importer) => {
179
+ if (isBareSpecifier(specifier)) {
180
+ const { url } = applyNodeEsmResolution({
181
+ specifier,
182
+ parentUrl: importer,
183
+ });
184
+ const { applies } = URL_META.applyAssociations({ url, associations });
185
+ return applies;
186
+ }
187
+ return URL_META.applyAssociations({ url: importer, associations })
188
+ .applies;
189
+ };
190
+ } else if (typeof value === "function") {
191
+ customResolver = value;
192
+ } else {
193
+ customResolver = () => value;
194
+ }
195
+ const existing = packageConditionResolvers[condition];
196
+ if (existing) {
197
+ packageConditionResolvers[condition] = (...args) => {
198
+ const customResult = customResolver(...args);
199
+ return customResult === undefined ? existing(...args) : customResult;
200
+ };
201
+ } else {
202
+ packageConditionResolvers[condition] = customResolver;
203
+ }
204
+ }
205
+
206
+ return (specifier, importer) => {
207
+ const conditions = [];
208
+ for (const conditionCandidate of Object.keys(packageConditionResolvers)) {
209
+ const packageConditionResolver =
210
+ packageConditionResolvers[conditionCandidate];
211
+ if (typeof packageConditionResolver === "function") {
212
+ if (packageConditionResolver(specifier, importer)) {
213
+ conditions.push(conditionCandidate);
214
+ }
215
+ } else if (packageConditionResolver) {
216
+ conditions.push(conditionCandidate);
217
+ }
218
+ }
219
+ return conditions;
220
+ };
221
+ };
222
+
119
223
  const addRelationshipWithPackageJson = ({
120
224
  reference,
121
225
  packageJsonUrl,
@@ -149,3 +253,20 @@ const addRelationshipWithPackageJson = ({
149
253
  );
150
254
  }
151
255
  };
256
+
257
+ const isBareSpecifier = (specifier) => {
258
+ if (
259
+ specifier[0] === "/" ||
260
+ specifier.startsWith("./") ||
261
+ specifier.startsWith("../")
262
+ ) {
263
+ return false;
264
+ }
265
+ try {
266
+ // eslint-disable-next-line no-new
267
+ new URL(specifier);
268
+ return false;
269
+ } catch {
270
+ return true;
271
+ }
272
+ };
@@ -1,76 +0,0 @@
1
- import { urlToRelativeUrl } from "@jsenv/urls";
2
- import { defaultRuntimeCompat, getDefaultBase } from "./build_params.js";
3
-
4
- export const jsenvPluginSubbuilds = (
5
- subBuildParamsArray,
6
- { parentBuildParams, onCustomBuildDirectory, buildStart },
7
- ) => {
8
- if (subBuildParamsArray.length === 0) {
9
- return [];
10
- }
11
- return subBuildParamsArray.map((subBuildParams, index) => {
12
- const defaultChildBuildParams = {};
13
- const childBuildParams = {
14
- ...parentBuildParams,
15
- logs: {
16
- level: "warn",
17
- disabled: true,
18
- },
19
- ...defaultChildBuildParams,
20
- ...subBuildParams,
21
- outDirectoryUrl: new URL(
22
- `./subbuild_${index}/`,
23
- parentBuildParams.outDirectoryUrl,
24
- ),
25
- };
26
- const subBuildDirectoryUrl = subBuildParams.buildDirectoryUrl;
27
- if (subBuildDirectoryUrl) {
28
- const subBuildRelativeUrl = urlToRelativeUrl(
29
- subBuildDirectoryUrl,
30
- parentBuildParams.buildDirectoryUrl,
31
- );
32
- childBuildParams.base =
33
- parentBuildParams.base === "./"
34
- ? `./`
35
- : subBuildParams.base ||
36
- getDefaultBase(
37
- childBuildParams.runtimeCompat || defaultRuntimeCompat,
38
- );
39
- onCustomBuildDirectory(subBuildRelativeUrl);
40
- }
41
- const buildPromise = buildStart(childBuildParams, index);
42
- const entryPointBuildUrlMap = new Map();
43
- const entryPointSourceUrlSet = new Set();
44
- const entryPointBuildUrlSet = new Set();
45
- const childBuildEntryPoints = childBuildParams.entryPoints;
46
- for (const key of Object.keys(childBuildEntryPoints)) {
47
- const entryPointUrl = new URL(key, childBuildParams.sourceDirectoryUrl)
48
- .href;
49
- const entryPointBuildUrl = new URL(
50
- childBuildEntryPoints[key],
51
- childBuildParams.buildDirectoryUrl,
52
- ).href;
53
- entryPointBuildUrlMap.set(entryPointUrl, entryPointBuildUrl);
54
- entryPointSourceUrlSet.add(entryPointUrl);
55
- entryPointBuildUrlSet.add(entryPointBuildUrl);
56
- }
57
-
58
- return {
59
- name: `jsenv:subbuild_${index}`,
60
- redirectReference: (reference) => {
61
- const entryPointBuildUrl = entryPointBuildUrlMap.get(reference.url);
62
- if (!entryPointBuildUrl) {
63
- return null;
64
- }
65
- return entryPointBuildUrl;
66
- },
67
- fetchUrlContent: async (urlInfo) => {
68
- if (!entryPointBuildUrlSet.has(urlInfo.url)) {
69
- return;
70
- }
71
- await buildPromise;
72
- urlInfo.typeHint = "asset"; // this ensure the rest of jsenv do not scan or modify the content of this file
73
- },
74
- };
75
- });
76
- };
@@ -1,202 +0,0 @@
1
- import { ANSI, distributePercentages, humanizeFileSize } from "@jsenv/humanize";
2
-
3
- import { GRAPH_VISITOR } from "./url_graph_visitor.js";
4
-
5
- export const createUrlGraphSummary = (
6
- urlGraph,
7
- { title = "graph summary" } = {},
8
- ) => {
9
- const graphReport = createUrlGraphReport(urlGraph);
10
- return `--- ${title} ---
11
- ${createRepartitionMessage(graphReport)}
12
- --------------------`;
13
- };
14
-
15
- const createUrlGraphReport = (urlGraph) => {
16
- const countGroups = {
17
- sourcemaps: 0,
18
- html: 0,
19
- css: 0,
20
- js: 0,
21
- json: 0,
22
- other: 0,
23
- total: 0,
24
- };
25
- const sizeGroups = {
26
- sourcemaps: 0,
27
- html: 0,
28
- css: 0,
29
- js: 0,
30
- json: 0,
31
- other: 0,
32
- total: 0,
33
- };
34
-
35
- GRAPH_VISITOR.forEachUrlInfoStronglyReferenced(
36
- urlGraph.rootUrlInfo,
37
- (urlInfo) => {
38
- // ignore:
39
- // - ignored files: we don't know their content
40
- // - inline files and data files: they are already taken into account in the file where they appear
41
- if (urlInfo.url.startsWith("ignore:")) {
42
- return;
43
- }
44
- if (urlInfo.isInline) {
45
- return;
46
- }
47
- if (urlInfo.url.startsWith("data:")) {
48
- return;
49
- }
50
-
51
- // file loaded via import assertion are already inside the graph
52
- // their js module equivalent are ignored to avoid counting it twice
53
- // in the build graph the file targeted by import assertion will likely be gone
54
- // and only the js module remain (likely bundled)
55
- if (
56
- urlInfo.searchParams.has("as_json_module") ||
57
- urlInfo.searchParams.has("as_css_module") ||
58
- urlInfo.searchParams.has("as_text_module")
59
- ) {
60
- return;
61
- }
62
-
63
- const urlContentSize = Buffer.byteLength(urlInfo.content);
64
- const category = determineCategory(urlInfo);
65
- if (category === "sourcemap") {
66
- countGroups.sourcemaps++;
67
- sizeGroups.sourcemaps += urlContentSize;
68
- return;
69
- }
70
- countGroups.total++;
71
- sizeGroups.total += urlContentSize;
72
- if (category === "html") {
73
- countGroups.html++;
74
- sizeGroups.html += urlContentSize;
75
- return;
76
- }
77
- if (category === "css") {
78
- countGroups.css++;
79
- sizeGroups.css += urlContentSize;
80
- return;
81
- }
82
- if (category === "js") {
83
- countGroups.js++;
84
- sizeGroups.js += urlContentSize;
85
- return;
86
- }
87
- if (category === "json") {
88
- countGroups.json++;
89
- sizeGroups.json += urlContentSize;
90
- return;
91
- }
92
- countGroups.other++;
93
- sizeGroups.other += urlContentSize;
94
- return;
95
- },
96
- );
97
-
98
- const sizesToDistribute = {};
99
- Object.keys(sizeGroups).forEach((groupName) => {
100
- if (groupName !== "sourcemaps" && groupName !== "total") {
101
- sizesToDistribute[groupName] = sizeGroups[groupName];
102
- }
103
- });
104
- const percentageGroups = distributePercentages(sizesToDistribute);
105
-
106
- return {
107
- // sourcemaps are special, there size are ignored
108
- // so there is no "percentage" associated
109
- sourcemaps: {
110
- count: countGroups.sourcemaps,
111
- size: sizeGroups.sourcemaps,
112
- percentage: undefined,
113
- },
114
-
115
- html: {
116
- count: countGroups.html,
117
- size: sizeGroups.html,
118
- percentage: percentageGroups.html,
119
- },
120
- css: {
121
- count: countGroups.css,
122
- size: sizeGroups.css,
123
- percentage: percentageGroups.css,
124
- },
125
- js: {
126
- count: countGroups.js,
127
- size: sizeGroups.js,
128
- percentage: percentageGroups.js,
129
- },
130
- json: {
131
- count: countGroups.json,
132
- size: sizeGroups.json,
133
- percentage: percentageGroups.json,
134
- },
135
- other: {
136
- count: countGroups.other,
137
- size: sizeGroups.other,
138
- percentage: percentageGroups.other,
139
- },
140
- total: {
141
- count: countGroups.total,
142
- size: sizeGroups.total,
143
- percentage: 100,
144
- },
145
- };
146
- };
147
-
148
- const determineCategory = (urlInfo) => {
149
- if (urlInfo.type === "sourcemap") {
150
- return "sourcemap";
151
- }
152
- if (urlInfo.type === "html") {
153
- return "html";
154
- }
155
- if (urlInfo.type === "css") {
156
- return "css";
157
- }
158
- if (urlInfo.type === "js_module" || urlInfo.type === "js_classic") {
159
- return "js";
160
- }
161
- if (urlInfo.type === "json") {
162
- return "json";
163
- }
164
- return "other";
165
- };
166
-
167
- const createRepartitionMessage = ({ html, css, js, json, other, total }) => {
168
- const addPart = (name, { count, size, percentage }) => {
169
- parts.push(
170
- `${ANSI.color(`${name}:`, ANSI.GREY)} ${count} (${humanizeFileSize(
171
- size,
172
- )} / ${percentage} %)`,
173
- );
174
- };
175
-
176
- const parts = [];
177
- // if (sourcemaps.count) {
178
- // parts.push(
179
- // `${ANSI.color(`sourcemaps:`, ANSI.GREY)} ${
180
- // sourcemaps.count
181
- // } (${humanizeFileSize(sourcemaps.size)})`,
182
- // )
183
- // }
184
- if (html.count) {
185
- addPart("html ", html);
186
- }
187
- if (css.count) {
188
- addPart("css ", css);
189
- }
190
- if (js.count) {
191
- addPart("js ", js);
192
- }
193
- if (json.count) {
194
- addPart("json ", json);
195
- }
196
- if (other.count) {
197
- addPart("other", other);
198
- }
199
- addPart("total", total);
200
- return `- ${parts.join(`
201
- - `)}`;
202
- };