@jsenv/core 23.0.4 → 23.0.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": "23.0.4",
3
+ "version": "23.0.8",
4
4
  "description": "Tool to develop, test and build js projects",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -42,9 +42,7 @@
42
42
  "test-build": "node ./script/test/test_build.js",
43
43
  "test-launch-browser": "node ./script/test/test-launch-browser.js",
44
44
  "test-launch-node": "node ./script/test/test-launch-node.js",
45
- "measure-performances": "node --expose-gc ./script/performance/generate_performance_report.js --local",
46
- "measure-dev-server-performances": "node --expose-gc ./script/performance/measure_dev_server/measure_dev_server.js --local",
47
- "measure-test-plan-performances": "node --expose-gc ./script/performance/measure_test_plan/measure_test_plan.js --local",
45
+ "measure-performances": "node --expose-gc ./script/performance/generate_performance_report.js --log --once",
48
46
  "prettier-format": "node ./script/prettier/prettier_format.mjs",
49
47
  "prettier-format-stage": "npm run prettier-format -- --staged",
50
48
  "prettier-check": "npm run prettier-format -- --dry-run",
@@ -66,12 +64,13 @@
66
64
  "@babel/plugin-transform-modules-systemjs": "7.15.4",
67
65
  "@c88/v8-coverage": "0.1.1",
68
66
  "@jsenv/cancellation": "3.0.0",
69
- "@jsenv/filesystem": "2.2.0",
67
+ "@jsenv/filesystem": "2.3.1",
70
68
  "@jsenv/importmap": "1.1.0",
71
69
  "@jsenv/logger": "4.0.1",
72
70
  "@jsenv/node-signals": "2.0.1",
73
71
  "@jsenv/server": "7.2.0",
74
72
  "@jsenv/uneval": "1.6.0",
73
+ "@jsenv/workers": "1.1.0",
75
74
  "@rollup/plugin-commonjs": "21.0.0",
76
75
  "@rollup/plugin-json": "4.1.0",
77
76
  "@rollup/plugin-node-resolve": "13.0.5",
@@ -79,8 +78,8 @@
79
78
  "acorn-import-assertions": "1.8.0",
80
79
  "ansi-to-html": "0.7.2",
81
80
  "bytes": "3.1.0",
82
- "construct-style-sheets-polyfill": "3.0.4",
83
81
  "cjs-module-lexer": "1.2.2",
82
+ "construct-style-sheets-polyfill": "3.0.4",
84
83
  "cssnano": "5.0.8",
85
84
  "cssnano-preset-default": "5.1.4",
86
85
  "cuid": "2.1.8",
@@ -122,7 +121,7 @@
122
121
  "@babel/plugin-transform-react-jsx": "7.14.9",
123
122
  "@babel/plugin-transform-typescript": "7.15.8",
124
123
  "@babel/preset-env": "7.15.8",
125
- "@jsenv/assert": "2.3.1",
124
+ "@jsenv/assert": "2.3.2",
126
125
  "@jsenv/babel-preset": "./packages/jsenv-babel-preset",
127
126
  "@jsenv/codecov-upload": "3.5.0",
128
127
  "@jsenv/eslint-config": "16.0.8",
@@ -130,7 +129,7 @@
130
129
  "@jsenv/importmap-eslint-resolver": "5.1.2",
131
130
  "@jsenv/importmap-node-module": "2.4.1",
132
131
  "@jsenv/package-publish": "1.6.2",
133
- "@jsenv/performance-impact": "1.7.0",
132
+ "@jsenv/performance-impact": "2.1.2",
134
133
  "@jsenv/prettier-check-project": "5.6.1",
135
134
  "@jsenv/pwa": "4.0.0",
136
135
  "babel-plugin-transform-async-to-promises": "0.8.15",
@@ -15,6 +15,7 @@ import {
15
15
  normalizeStructuredMetaMap,
16
16
  urlToMeta,
17
17
  } from "@jsenv/filesystem"
18
+ import { createWorkersForJavaScriptModules } from "@jsenv/workers"
18
19
 
19
20
  import { createUrlConverter } from "@jsenv/core/src/internal/url_conversion.js"
20
21
  import { createUrlFetcher } from "@jsenv/core/src/internal/building/url_fetcher.js"
@@ -42,7 +43,6 @@ import {
42
43
  import { computeBuildRelativeUrl } from "./url-versioning.js"
43
44
  import { visitImportReferences } from "./import_references.js"
44
45
 
45
- import { minifyJs } from "./js/minifyJs.js"
46
46
  import { createImportResolverForNode } from "../import-resolution/import-resolver-node.js"
47
47
  import { createImportResolverForImportmap } from "../import-resolution/import-resolver-importmap.js"
48
48
  import { getDefaultImportMap } from "../import-resolution/importmap-default.js"
@@ -218,7 +218,63 @@ export const createJsenvRollupPlugin = async ({
218
218
  return rollupSetAssetSource(rollupReferenceId, assetSource)
219
219
  }
220
220
 
221
- const jsenvRollupPlugin = {
221
+ let onBundleEnd = () => {}
222
+ let minifyJs
223
+ let minifyHtml
224
+
225
+ const jsenvRollupPlugin = {}
226
+
227
+ if (minify) {
228
+ const { methodHooks, workers } = createWorkersForJavaScriptModules({
229
+ minifyJs: `${new URL("./js/minifyJs.js", import.meta.url)}#minifyJs`,
230
+ minifyHtml: `${new URL(
231
+ "./html/minifyHtml.js",
232
+ import.meta.url,
233
+ )}#minifyHtml`,
234
+ })
235
+
236
+ // inspired from https://github.com/vitejs/vite/blob/06d86e4a2e90ca916a43d450bca1e6c28bc4bfe2/packages/vite/src/node/plugins/terser.ts#L23
237
+ minifyJs = async ({ url, code, map, ...rest }) => {
238
+ const result = await methodHooks.minifyJs({
239
+ url,
240
+ code,
241
+ map,
242
+ ...minifyJsOptions,
243
+ ...rest,
244
+ })
245
+ return {
246
+ code: result.code,
247
+ map: result.map,
248
+ }
249
+ }
250
+
251
+ minifyHtml = async (html) => {
252
+ return methodHooks.minifyHtml(html, minifyHtmlOptions)
253
+ }
254
+
255
+ jsenvRollupPlugin.renderChunk = async (code, chunk) => {
256
+ let map = chunk.map
257
+ const result = await minifyJs({
258
+ url: asOriginalUrl(chunk.facadeModuleId),
259
+ code,
260
+ map,
261
+ ...(format === "global" ? { toplevel: false } : { toplevel: true }),
262
+ })
263
+
264
+ code = result.code
265
+ map = result.map
266
+ return {
267
+ code,
268
+ map,
269
+ }
270
+ }
271
+
272
+ onBundleEnd = () => {
273
+ workers.destroy()
274
+ }
275
+ }
276
+
277
+ Object.assign(jsenvRollupPlugin, {
222
278
  name: "jsenv",
223
279
 
224
280
  async buildStart() {
@@ -414,9 +470,9 @@ export const createJsenvRollupPlugin = async ({
414
470
  useImportMapToMaximizeCacheReuse,
415
471
  createImportMapForFilesUsedInJs,
416
472
  minify,
417
- minifyHtmlOptions,
473
+ minifyJs,
474
+ minifyHtml,
418
475
  minifyCssOptions,
419
- minifyJsOptions,
420
476
  })
421
477
  },
422
478
  },
@@ -1001,28 +1057,6 @@ export const createJsenvRollupPlugin = async ({
1001
1057
  return outputOptions
1002
1058
  },
1003
1059
 
1004
- renderChunk: async (code, chunk) => {
1005
- let map = chunk.map
1006
-
1007
- if (!minify) {
1008
- return null
1009
- }
1010
-
1011
- const result = await minifyJs({
1012
- url: asOriginalUrl(chunk.facadeModuleId),
1013
- code,
1014
- map,
1015
- ...(format === "global" ? { toplevel: false } : { toplevel: true }),
1016
- ...minifyJsOptions,
1017
- })
1018
- code = result.code
1019
- map = result.map
1020
- return {
1021
- code,
1022
- map,
1023
- }
1024
- },
1025
-
1026
1060
  async generateBundle(outputOptions, rollupResult) {
1027
1061
  const jsChunks = {}
1028
1062
  // rollupResult can be mutated by late asset emission
@@ -1119,6 +1153,7 @@ export const createJsenvRollupPlugin = async ({
1119
1153
  ressourceBuilder.rollupBuildEnd({ jsModuleBuild, buildManifest })
1120
1154
  // wait html files to be emitted
1121
1155
  await ressourceBuilder.getAllEntryPointsEmittedPromise()
1156
+ onBundleEnd()
1122
1157
 
1123
1158
  const assetBuild = {}
1124
1159
  Object.keys(rollupResult).forEach((rollupFileId) => {
@@ -1260,7 +1295,7 @@ export const createJsenvRollupPlugin = async ({
1260
1295
  }),
1261
1296
  )
1262
1297
  },
1263
- }
1298
+ })
1264
1299
 
1265
1300
  const fetchImportMapFromUrl = async (importMapUrl, importer) => {
1266
1301
  const importMapResponse = await urlFetcher.fetchUrl(importMapUrl, {
@@ -51,14 +51,13 @@ import {
51
51
  import { collectNodesMutations } from "../parsing.utils.js"
52
52
 
53
53
  import { collectSvgMutations } from "../svg/parseSvgRessource.js"
54
- import { minifyHtml } from "./minifyHtml.js"
55
54
 
56
55
  export const parseHtmlRessource = async (
57
56
  htmlRessource,
58
57
  notifiers,
59
58
  {
60
59
  minify,
61
- minifyHtmlOptions,
60
+ minifyHtml,
62
61
  htmlStringToHtmlAst = (htmlString) => parseHtmlString(htmlString),
63
62
  htmlAstToHtmlString = (htmlAst) => stringifyHtmlAst(htmlAst),
64
63
  ressourceHintNeverUsedCallback = () => {},
@@ -153,7 +152,7 @@ export const parseHtmlRessource = async (
153
152
 
154
153
  const htmlAfterTransformation = htmlAstToHtmlString(htmlAst)
155
154
  const html = minify
156
- ? minifyHtml(htmlAfterTransformation, minifyHtmlOptions)
155
+ ? await minifyHtml(htmlAfterTransformation)
157
156
  : htmlAfterTransformation
158
157
  htmlRessource.buildEnd(html)
159
158
  }
@@ -5,12 +5,11 @@ import {
5
5
  setJavaScriptSourceMappingUrl,
6
6
  } from "@jsenv/core/src/internal/sourceMappingURLUtils.js"
7
7
  import { bundleWorker } from "@jsenv/core/src/internal/building/bundleWorker.js"
8
- import { minifyJs } from "./minifyJs.js"
9
8
 
10
9
  export const parseJsRessource = async (
11
10
  jsRessource,
12
11
  { notifyReferenceFound },
13
- { asProjectUrl, asOriginalUrl, minify, minifyJsOptions },
12
+ { asProjectUrl, asOriginalUrl, minify, minifyJs },
14
13
  ) => {
15
14
  const jsUrl = jsRessource.url
16
15
  const jsString = String(jsRessource.bufferBeforeBuild)
@@ -72,7 +71,6 @@ export const parseJsRessource = async (
72
71
  code: jsString,
73
72
  map,
74
73
  toplevel: false,
75
- ...minifyJsOptions,
76
74
  })
77
75
  code = result.code
78
76
  map = result.map
@@ -36,9 +36,9 @@ export const parseRessource = (
36
36
  useImportMapToMaximizeCacheReuse,
37
37
  createImportMapForFilesUsedInJs,
38
38
  minify,
39
- minifyHtmlOptions,
39
+ minifyJs,
40
+ minifyHtml,
40
41
  minifyCssOptions,
41
- minifyJsOptions,
42
42
  },
43
43
  ) => {
44
44
  const { contentType } = ressource
@@ -49,7 +49,7 @@ export const parseRessource = (
49
49
  if (contentType === "text/html") {
50
50
  return parseHtmlRessource(ressource, notifiers, {
51
51
  minify,
52
- minifyHtmlOptions,
52
+ minifyHtml,
53
53
  htmlStringToHtmlAst: async (htmlString) => {
54
54
  const htmlAst = parseHtmlString(htmlString)
55
55
 
@@ -157,14 +157,14 @@ export const parseRessource = (
157
157
  asOriginalUrl,
158
158
  asOriginalServerUrl,
159
159
  minify,
160
- minifyJsOptions,
160
+ minifyJs,
161
161
  })
162
162
  }
163
163
 
164
164
  if (contentType === "image/svg+xml") {
165
165
  return parseSvgRessource(ressource, notifiers, {
166
166
  minify,
167
- minifyHtmlOptions,
167
+ minifyHtml,
168
168
  })
169
169
  }
170
170
 
@@ -19,7 +19,7 @@ export const parseSvgRessource = async (
19
19
  const htmlRessources = parseHtmlAstRessources(svgAst)
20
20
  const mutations = collectSvgMutations(htmlRessources, notifiers, svgRessource)
21
21
 
22
- return ({ getUrlRelativeToImporter }) => {
22
+ return async ({ getUrlRelativeToImporter }) => {
23
23
  mutations.forEach((mutationCallback) => {
24
24
  mutationCallback({ getUrlRelativeToImporter })
25
25
  })
@@ -27,7 +27,7 @@ export const parseSvgRessource = async (
27
27
  // could also benefit of minification https://github.com/svg/svgo
28
28
  if (minify) {
29
29
  svgRessource.buildEnd(
30
- minifyHtml(svgAfterTransformation, minifyHtmlOptions),
30
+ await minifyHtml(svgAfterTransformation, minifyHtmlOptions),
31
31
  )
32
32
  return
33
33
  }
@@ -1,3 +1,9 @@
1
+ /*
2
+ * TODO:
3
+ * - code should also inject helper when code uses new keyword on "CSSStyleSheet"
4
+ * - code should also inject helper when code uses "document.adoptedStylesheets"
5
+ */
6
+
1
7
  import { require } from "../require.js"
2
8
 
3
9
  export const babelPluginNewStylesheetAsJsenvImport = (
@@ -18,8 +24,6 @@ export const babelPluginNewStylesheetAsJsenvImport = (
18
24
  addSideEffect(path.scope.getProgramParent().path, newStylesheetImportPath)
19
25
  }
20
26
 
21
- // TODO: we should detect usage of new keyword on "CSSStyleSheet"
22
- // and inject too in that case
23
27
  return {
24
28
  name: "constructable-stylesheet-as-jsenv-import",
25
29
  visitor: {
@@ -2,9 +2,7 @@ import { urlToFileSystemPath } from "@jsenv/filesystem"
2
2
  import { createDetailedMessage } from "@jsenv/logger"
3
3
  import { timeStart, timeFunction } from "@jsenv/server"
4
4
  import { readFileContent } from "./fs-optimized-for-cache.js"
5
- import { readMeta } from "./readMeta.js"
6
- import { validateMeta } from "./validateMeta.js"
7
- import { updateMeta } from "./updateMeta.js"
5
+ import { validateCache } from "./validateCache.js"
8
6
  import { getMetaJsonFileUrl } from "./compile-asset.js"
9
7
  import { createLockRegistry } from "./createLockRegistry.js"
10
8
 
@@ -16,13 +14,13 @@ export const getOrGenerateCompiledFile = async ({
16
14
  projectDirectoryUrl,
17
15
  originalFileUrl,
18
16
  compiledFileUrl = originalFileUrl,
19
- writeOnFilesystem,
20
17
  useFilesystemAsCache,
21
- cacheHitTracking = false,
22
18
  compileCacheSourcesValidation,
23
19
  compileCacheAssetsValidation,
24
20
  fileContentFallback,
21
+ clientNeedsEtagHeader,
25
22
  ifEtagMatch,
23
+ clientNeedsLastModifiedHeader,
26
24
  ifModifiedSinceDate,
27
25
  compile,
28
26
  }) => {
@@ -71,7 +69,9 @@ export const getOrGenerateCompiledFile = async ({
71
69
  compiledFileUrl,
72
70
  compile,
73
71
  fileContentFallback,
72
+ clientNeedsEtagHeader,
74
73
  ifEtagMatch,
74
+ clientNeedsLastModifiedHeader,
75
75
  ifModifiedSinceDate,
76
76
  useFilesystemAsCache,
77
77
  compileCacheSourcesValidation,
@@ -79,22 +79,6 @@ export const getOrGenerateCompiledFile = async ({
79
79
  logger,
80
80
  })
81
81
 
82
- let cacheWriteTiming = {}
83
- if (writeOnFilesystem) {
84
- const result = await timeFunction("cache write", () =>
85
- updateMeta({
86
- logger,
87
- meta,
88
- compileResult,
89
- compileResultStatus,
90
- compiledFileUrl,
91
- // originalFileUrl,
92
- cacheHitTracking,
93
- }),
94
- )
95
- cacheWriteTiming = result[0]
96
- }
97
-
98
82
  return {
99
83
  meta,
100
84
  compileResult,
@@ -102,7 +86,6 @@ export const getOrGenerateCompiledFile = async ({
102
86
  timing: {
103
87
  ...lockTiming,
104
88
  ...timing,
105
- ...cacheWriteTiming,
106
89
  },
107
90
  }
108
91
  },
@@ -118,55 +101,49 @@ const computeCompileReport = async ({
118
101
  compiledFileUrl,
119
102
  compile,
120
103
  fileContentFallback,
104
+ clientNeedsEtagHeader,
121
105
  ifEtagMatch,
106
+ clientNeedsLastModifiedHeader,
122
107
  ifModifiedSinceDate,
123
108
  useFilesystemAsCache,
124
109
  compileCacheSourcesValidation,
125
110
  compileCacheAssetsValidation,
126
111
  logger,
127
112
  }) => {
128
- const [cacheReadTiming, meta] = await timeFunction("cache read", async () => {
129
- if (useFilesystemAsCache) {
130
- return readMeta({
113
+ const [readCacheTiming, cacheValidity] = await timeFunction(
114
+ "read cache",
115
+ () => {
116
+ if (!useFilesystemAsCache) {
117
+ return {
118
+ isValid: false,
119
+ code: "META_FILE_NOT_FOUND",
120
+ meta: {
121
+ isValid: false,
122
+ code: "META_FILE_NOT_FOUND",
123
+ },
124
+ }
125
+ }
126
+ return validateCache({
131
127
  logger,
128
+ useFilesystemAsCache,
132
129
  compiledFileUrl,
130
+ clientNeedsEtagHeader,
131
+ ifEtagMatch,
132
+ clientNeedsLastModifiedHeader,
133
+ ifModifiedSinceDate,
134
+ compileCacheSourcesValidation,
135
+ compileCacheAssetsValidation,
133
136
  })
134
- }
135
- return null
136
- })
137
-
138
- if (!meta) {
139
- const [compileTiming, compileResult] = await timeFunction("compile", () =>
140
- callCompile({
141
- logger,
142
- originalFileUrl,
143
- fileContentFallback,
144
- compile,
145
- }),
146
- )
137
+ },
138
+ )
147
139
 
148
- return {
149
- meta: null,
150
- compileResult,
151
- compileResultStatus: "created",
152
- timing: {
153
- ...cacheReadTiming,
154
- ...compileTiming,
155
- },
140
+ if (!cacheValidity.isValid) {
141
+ if (cacheValidity.code === "SOURCES_EMPTY") {
142
+ logger.warn(`WARNING: meta.sources is empty for ${compiledFileUrl}`)
156
143
  }
157
- }
158
144
 
159
- const metaValidation = await validateMeta({
160
- logger,
161
- meta,
162
- compiledFileUrl,
163
- ifEtagMatch,
164
- ifModifiedSinceDate,
165
- compileCacheSourcesValidation,
166
- compileCacheAssetsValidation,
167
- })
145
+ const metaIsValid = cacheValidity.meta.isValid
168
146
 
169
- if (!metaValidation.valid) {
170
147
  const [compileTiming, compileResult] = await timeFunction("compile", () =>
171
148
  callCompile({
172
149
  logger,
@@ -175,34 +152,35 @@ const computeCompileReport = async ({
175
152
  compile,
176
153
  }),
177
154
  )
155
+
178
156
  return {
179
- meta,
157
+ meta: metaIsValid ? cacheValidity.meta.data : null,
180
158
  compileResult,
181
- compileResultStatus: "updated",
159
+ compileResultStatus: metaIsValid ? "updated" : "created",
182
160
  timing: {
183
- ...cacheReadTiming,
184
- ...metaValidation.timing,
161
+ ...readCacheTiming,
185
162
  ...compileTiming,
186
163
  },
187
164
  }
188
165
  }
189
166
 
167
+ const meta = cacheValidity.meta.data
190
168
  const { contentType, sources, assets } = meta
191
- const { compiledSource, sourcesContent, assetsContent } = metaValidation.data
192
169
  return {
193
170
  meta,
194
171
  compileResult: {
172
+ compiledSource: String(
173
+ cacheValidity.compiledFile.data.compiledSourceBuffer,
174
+ ),
175
+ compiledEtag: cacheValidity.compiledFile.data.compiledEtag,
176
+ compiledMtime: cacheValidity.compiledFile.data.compiledMtime,
195
177
  contentType,
196
- compiledSource,
197
178
  sources,
198
- sourcesContent,
199
179
  assets,
200
- assetsContent,
201
180
  },
202
181
  compileResultStatus: "cached",
203
182
  timing: {
204
- ...cacheReadTiming,
205
- ...metaValidation.timing,
183
+ ...readCacheTiming,
206
184
  },
207
185
  }
208
186
  }
@@ -3,6 +3,7 @@ import {
3
3
  urlToFileSystemPath,
4
4
  bufferToEtag,
5
5
  } from "@jsenv/filesystem"
6
+ import { utimesSync } from "node:fs"
6
7
 
7
8
  import { writeFileContent, testFilePresence } from "./fs-optimized-for-cache.js"
8
9
  import { getMetaJsonFileUrl } from "./compile-asset.js"
@@ -11,13 +12,11 @@ export const updateMeta = async ({
11
12
  logger,
12
13
  meta,
13
14
  compiledFileUrl,
14
- cacheHitTracking,
15
15
  compileResult,
16
16
  compileResultStatus,
17
17
  }) => {
18
18
  const isNew = compileResultStatus === "created"
19
19
  const isUpdated = compileResultStatus === "updated"
20
- const isCached = compileResultStatus === "cached"
21
20
  const {
22
21
  compiledSource,
23
22
  contentType,
@@ -65,6 +64,15 @@ ${sourcesToRemove.join(`\n`)}`)
65
64
  promises.push(
66
65
  writeFileContent(compiledFileUrl, compiledSource, {
67
66
  fileLikelyNotFound: isNew,
67
+ }).then(() => {
68
+ const mtime = compileResult.compiledMtime
69
+ // when compileResult.compiledMtime do not exists it means
70
+ // the client is not interested in it so
71
+ // -> moment we write the file is not important
72
+ // -> There is no need to update mtime
73
+ if (mtime) {
74
+ utimesSync(urlToFileSystemPath(compiledFileUrl), new Date(mtime), new Date(mtime))
75
+ }
68
76
  }),
69
77
  )
70
78
  }
@@ -85,7 +93,7 @@ ${sourcesToRemove.join(`\n`)}`)
85
93
 
86
94
  const metaJsonFileUrl = getMetaJsonFileUrl(compiledFileUrl)
87
95
 
88
- if (isNew || isUpdated || (isCached && cacheHitTracking)) {
96
+ if (isNew || isUpdated) {
89
97
  let latestMeta
90
98
 
91
99
  const sourceAndAssetProps = {
@@ -120,14 +128,6 @@ ${sourcesToRemove.join(`\n`)}`)
120
128
  }
121
129
  }
122
130
 
123
- if (cacheHitTracking) {
124
- latestMeta = {
125
- ...latestMeta,
126
- matchCount: 1,
127
- lastMatchMs: Number(Date.now()),
128
- }
129
- }
130
-
131
131
  logger.debug(
132
132
  `write compiled file meta at ${urlToFileSystemPath(metaJsonFileUrl)}`,
133
133
  )