@jsenv/core 40.12.7 → 40.12.9

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": "40.12.7",
3
+ "version": "40.12.9",
4
4
  "description": "Tool to develop, test and build js projects",
5
5
  "repository": {
6
6
  "type": "git",
@@ -75,14 +75,14 @@
75
75
  },
76
76
  "dependencies": {
77
77
  "@financial-times/polyfill-useragent-normaliser": "1.10.2",
78
- "@jsenv/ast": "6.7.20",
79
- "@jsenv/js-module-fallback": "1.4.28",
80
- "@jsenv/plugin-bundling": "2.10.7",
78
+ "@jsenv/ast": "6.7.21",
79
+ "@jsenv/js-module-fallback": "1.4.29",
80
+ "@jsenv/plugin-bundling": "2.10.9",
81
81
  "@jsenv/plugin-minification": "1.7.3",
82
- "@jsenv/plugin-supervisor": "1.7.14",
83
- "@jsenv/plugin-transpilation": "1.5.69",
84
- "@jsenv/server": "16.3.5",
85
- "@jsenv/sourcemap": "1.3.16",
82
+ "@jsenv/plugin-supervisor": "1.7.15",
83
+ "@jsenv/plugin-transpilation": "1.5.70",
84
+ "@jsenv/server": "16.3.6",
85
+ "@jsenv/sourcemap": "1.3.17",
86
86
  "react-table": "7.8.0"
87
87
  },
88
88
  "devDependencies": {
@@ -121,7 +121,9 @@
121
121
  "@playwright/browser-webkit": "1.58.1",
122
122
  "babel-plugin-transform-async-to-promises": "0.8.18",
123
123
  "eslint": "9.39.2",
124
- "globals": "17.3.0",
124
+ "eslint-plugin-import-x": "4.16.2",
125
+ "eslint-plugin-regexp": "3.1.0",
126
+ "globals": "17.4.0",
125
127
  "open": "11.0.0",
126
128
  "playwright": "1.58.1",
127
129
  "preact": "11.0.0-beta.0",
@@ -129,11 +131,11 @@
129
131
  "prettier-plugin-css-order": "git+https://github.com/dmail-fork/prettier-plugin-css-order.git",
130
132
  "prettier-plugin-embed": "0.5.1",
131
133
  "prettier-plugin-organize-imports": "4.3.0",
132
- "prettier-plugin-packagejson": "3.0.0",
134
+ "prettier-plugin-packagejson": "3.0.2",
133
135
  "prettier-plugin-sql": "0.19.2",
134
- "strip-ansi": "7.1.2"
136
+ "strip-ansi": "7.2.0"
135
137
  },
136
- "packageManager": "npm@11.3.0",
138
+ "packageManager": "npm@11.6.2",
137
139
  "engines": {
138
140
  "node": ">=20.8.0"
139
141
  },
@@ -24,7 +24,6 @@ import {
24
24
  compareFileUrls,
25
25
  createLookupPackageDirectory,
26
26
  ensureEmptyDirectory,
27
- lookupPackageDirectory,
28
27
  readPackageAtOrNull,
29
28
  updateJsonFileSync,
30
29
  writeFileSync,
@@ -62,6 +61,7 @@ import { memoryUsage as processMemoryUsage } from "node:process";
62
61
  import { watchSourceFiles } from "../helpers/watch_source_files.js";
63
62
  import { jsenvCoreDirectoryUrl } from "../jsenv_core_directory_url.js";
64
63
  import { createKitchen } from "../kitchen/kitchen.js";
64
+ import { createPackageDirectory } from "../kitchen/package_directory.js";
65
65
  import { GRAPH_VISITOR } from "../kitchen/url_graph/url_graph_visitor.js";
66
66
  import { jsenvPluginDirectoryReferenceEffect } from "../plugins/directory_reference_effect/jsenv_plugin_directory_reference_effect.js";
67
67
  import { jsenvPluginInlining } from "../plugins/inlining/jsenv_plugin_inlining.js";
@@ -562,20 +562,19 @@ export const build = async ({
562
562
  return compareFileUrls(a.sourceUrl, b.sourceUrl);
563
563
  });
564
564
 
565
- const lookupPackageDirectoryUrl = createLookupPackageDirectory();
565
+ const lookupPackageDirectory = createLookupPackageDirectory();
566
+ const packageDirectory = createPackageDirectory({
567
+ sourceDirectoryUrl,
568
+ lookupPackageDirectory,
569
+ });
566
570
  const packageDirectoryCache = new Map();
567
- const readPackageDirectory = (url) => {
571
+ packageDirectory.read = (url) => {
568
572
  const fromCache = packageDirectoryCache.get(url);
569
573
  if (fromCache !== undefined) {
570
574
  return fromCache;
571
575
  }
572
576
  return readPackageAtOrNull(url);
573
577
  };
574
- const packageDirectory = {
575
- url: lookupPackageDirectory(sourceDirectoryUrl),
576
- find: lookupPackageDirectoryUrl,
577
- read: readPackageDirectory,
578
- };
579
578
 
580
579
  if (outDirectoryUrl === undefined) {
581
580
  if (
@@ -772,7 +771,7 @@ export const build = async ({
772
771
  sideEffects: sideEffectRelativeUrlArray,
773
772
  });
774
773
  };
775
- const sideEffects = readPackageDirectory(
774
+ const sideEffects = packageDirectory.read(
776
775
  packageDirectory.url,
777
776
  )?.sideEffects;
778
777
  if (sideEffects === false) {
@@ -2,7 +2,6 @@ import {
2
2
  assertAndNormalizeDirectoryUrl,
3
3
  bufferToEtag,
4
4
  lookupPackageDirectory,
5
- readPackageAtOrNull,
6
5
  } from "@jsenv/filesystem";
7
6
  import { createLogger, createTaskLog, formatError } from "@jsenv/humanize";
8
7
  import {
@@ -23,6 +22,7 @@ import { watchSourceFiles } from "../helpers/watch_source_files.js";
23
22
  import { WEB_URL_CONVERTER } from "../helpers/web_url_converter.js";
24
23
  import { jsenvCoreDirectoryUrl } from "../jsenv_core_directory_url.js";
25
24
  import { createKitchen } from "../kitchen/kitchen.js";
25
+ import { createPackageDirectory } from "../kitchen/package_directory.js";
26
26
  import {
27
27
  createPluginController,
28
28
  createPluginStore,
@@ -97,9 +97,11 @@ export const startDevServer = async ({
97
97
  transpilation,
98
98
  cacheControl = true,
99
99
  ribbon = true,
100
+ dropToOpen = true,
100
101
  // toolbar = false,
101
102
  onKitchenCreated = () => {},
102
103
  spa,
104
+ packageBundle,
103
105
 
104
106
  sourcemaps = "inline",
105
107
  sourcemapsSourcesContent,
@@ -245,11 +247,9 @@ export const startDevServer = async ({
245
247
  );
246
248
  serverStopCallbackSet.add(stopWatchingSourceFiles);
247
249
 
248
- const packageDirectory = {
249
- url: lookupPackageDirectory(sourceDirectoryUrl),
250
- find: lookupPackageDirectory,
251
- read: readPackageAtOrNull,
252
- };
250
+ const packageDirectory = createPackageDirectory({
251
+ sourceDirectoryUrl,
252
+ });
253
253
 
254
254
  const devServerPluginStore = await createPluginStore([
255
255
  jsenvPluginServerEvents({ clientAutoreload }),
@@ -272,11 +272,13 @@ export const startDevServer = async ({
272
272
  injections,
273
273
  transpilation,
274
274
  spa,
275
+ packageBundle,
275
276
 
276
277
  clientAutoreload,
277
278
  clientAutoreloadOnServerRestart,
278
279
  cacheControl,
279
280
  ribbon,
281
+ dropToOpen,
280
282
  }),
281
283
  ]);
282
284
  const getOrCreateKitchen = async (request) => {
@@ -331,66 +333,74 @@ export const startDevServer = async ({
331
333
  urlInfoCreated.isWatched = watch;
332
334
  // when an url depends on many others, we check all these (like package.json)
333
335
  urlInfoCreated.isValid = () => {
334
- if (!urlInfoCreated.url.startsWith("file:")) {
335
- return false;
336
- }
337
- if (urlInfoCreated.content === undefined) {
338
- // urlInfo content is undefined when:
339
- // - url info content never fetched
340
- // - it is considered as modified because undelying file is watched and got saved
341
- // - it is considered as modified because underlying file content
342
- // was compared using etag and it has changed
343
- return false;
344
- }
345
- if (!watch) {
346
- // file is not watched, check the filesystem
347
- let fileContentAsBuffer;
348
- try {
349
- fileContentAsBuffer = readFileSync(new URL(urlInfoCreated.url));
350
- } catch (e) {
351
- if (e.code === "ENOENT") {
352
- urlInfoCreated.onModified();
353
- return false;
354
- }
355
- return false;
336
+ const seenSet = new Set();
337
+ const checkValidity = (urlInfo) => {
338
+ if (seenSet.has(urlInfo)) {
339
+ return true;
356
340
  }
357
- const fileContentEtag = bufferToEtag(fileContentAsBuffer);
358
- if (fileContentEtag !== urlInfoCreated.originalContentEtag) {
359
- urlInfoCreated.onModified();
360
- // restore content to be able to compare it again later
361
- urlInfoCreated.kitchen.urlInfoTransformer.setContent(
362
- urlInfoCreated,
363
- String(fileContentAsBuffer),
364
- {
365
- contentEtag: fileContentEtag,
366
- },
367
- );
341
+ seenSet.add(urlInfo);
342
+ if (!urlInfo.url.startsWith("file:")) {
368
343
  return false;
369
344
  }
370
- }
371
- for (const implicitUrl of urlInfoCreated.implicitUrlSet) {
372
- const implicitUrlInfo =
373
- urlInfoCreated.graph.getUrlInfo(implicitUrl);
374
- if (!implicitUrlInfo) {
375
- continue;
345
+ if (urlInfo.content === undefined) {
346
+ // urlInfo content is undefined when:
347
+ // - url info content never fetched
348
+ // - it is considered as modified because undelying file is watched and got saved
349
+ // - it is considered as modified because underlying file content
350
+ // was compared using etag and it has changed
351
+ return false;
376
352
  }
377
- if (implicitUrlInfo.content === undefined) {
378
- // happens when we explicitely load an url with a search param
379
- // - it creates an implicit url info to the url without params
380
- // - we never explicitely request the url without search param so it has no content
381
- // in that case the underlying urlInfo cannot be invalidate by the implicit
382
- // we use modifiedTimestamp to detect if the url was loaded once
383
- // or is just here to be used later
384
- if (implicitUrlInfo.modifiedTimestamp) {
353
+ if (!urlInfo.isWatched) {
354
+ // file is not watched, check the filesystem
355
+ let fileContentAsBuffer;
356
+ try {
357
+ fileContentAsBuffer = readFileSync(new URL(urlInfo.url));
358
+ } catch (e) {
359
+ if (e.code === "ENOENT") {
360
+ urlInfo.onModified();
361
+ return false;
362
+ }
363
+ return false;
364
+ }
365
+ const fileContentEtag = bufferToEtag(fileContentAsBuffer);
366
+ if (fileContentEtag !== urlInfo.originalContentEtag) {
367
+ urlInfo.onModified();
368
+ // restore content to be able to compare it again later
369
+ urlInfo.kitchen.urlInfoTransformer.setContent(
370
+ urlInfo,
371
+ String(fileContentAsBuffer),
372
+ {
373
+ contentEtag: fileContentEtag,
374
+ },
375
+ );
385
376
  return false;
386
377
  }
387
- continue;
388
378
  }
389
- if (!implicitUrlInfo.isValid()) {
390
- return false;
379
+ for (const implicitUrl of urlInfo.implicitUrlSet) {
380
+ const implicitUrlInfo = urlInfo.graph.getUrlInfo(implicitUrl);
381
+ if (!implicitUrlInfo) {
382
+ continue;
383
+ }
384
+ if (implicitUrlInfo.content === undefined) {
385
+ // happens when we explicitely load an url with a search param
386
+ // - it creates an implicit url info to the url without params
387
+ // - we never explicitely request the url without search param so it has no content
388
+ // in that case the underlying urlInfo cannot be invalidate by the implicit
389
+ // we use modifiedTimestamp to detect if the url was loaded once
390
+ // or is just here to be used later
391
+ if (implicitUrlInfo.modifiedTimestamp) {
392
+ return false;
393
+ }
394
+ continue;
395
+ }
396
+ if (!checkValidity(implicitUrlInfo)) {
397
+ return false;
398
+ }
391
399
  }
392
- }
393
- return true;
400
+ return true;
401
+ };
402
+ const valid = checkValidity(urlInfoCreated);
403
+ return valid;
394
404
  };
395
405
  });
396
406
  kitchen.graph.urlInfoDereferencedEventEmitter.on(
@@ -338,6 +338,14 @@ const detailsFromPluginController = (pluginController) => {
338
338
 
339
339
  const detailsFromValueThrown = (valueThrownByPlugin) => {
340
340
  if (valueThrownByPlugin && valueThrownByPlugin instanceof Error) {
341
+ // if (
342
+ // valueThrownByPlugin.message.includes("Maximum call stack size exceeded")
343
+ // ) {
344
+ // return {
345
+ // "error message": valueThrownByPlugin.message,
346
+ // "error stack": valueThrownByPlugin.stack,
347
+ // };
348
+ // }
341
349
  if (
342
350
  valueThrownByPlugin.code === "PARSE_ERROR" ||
343
351
  valueThrownByPlugin.code === "MODULE_NOT_FOUND" ||
@@ -0,0 +1,22 @@
1
+ import {
2
+ lookupPackageDirectory as lookupPackageDirectoryDefault,
3
+ readPackageAtOrNull,
4
+ } from "@jsenv/filesystem";
5
+
6
+ export const createPackageDirectory = ({
7
+ sourceDirectoryUrl,
8
+ lookupPackageDirectory = lookupPackageDirectoryDefault,
9
+ }) => {
10
+ const packageDirectory = {
11
+ url: lookupPackageDirectory(sourceDirectoryUrl),
12
+ find: (url) => {
13
+ const urlString = typeof url === "string" ? url : url?.href;
14
+ if (!urlString.startsWith("file:")) {
15
+ return null;
16
+ }
17
+ return lookupPackageDirectory(url);
18
+ },
19
+ read: readPackageAtOrNull,
20
+ };
21
+ return packageDirectory;
22
+ };
@@ -324,26 +324,13 @@ const createUrlInfo = (url, context) => {
324
324
  }
325
325
  return false;
326
326
  };
327
- urlInfo.getWithoutSearchParam = (searchParam, { expectedType } = {}) => {
328
- // The search param can be
329
- // 1. injected by a plugin during "redirectReference"
330
- // - import assertions
331
- // - js module fallback to systemjs
332
- // 2. already inside source files
333
- // - turn js module into js classic for convenience ?as_js_classic
334
- // - turn js classic to js module for to make it importable
335
- if (!urlInfo.searchParams.has(searchParam)) {
336
- return null;
337
- }
327
+ const getNextUrlInfo = (newProps) => {
338
328
  const reference = urlInfo.firstReference;
339
- const newSpecifier = injectQueryParamsIntoSpecifier(reference.specifier, {
340
- [searchParam]: undefined,
341
- });
342
- const referenceWithoutSearchParam = reference.addImplicit({
329
+ const nextReference = reference.addImplicit({
343
330
  type: reference.type,
344
331
  subtype: reference.subtype,
345
332
  expectedContentType: reference.expectedContentType,
346
- expectedType: expectedType || reference.expectedType,
333
+ expectedType: reference.expectedType,
347
334
  expectedSubtype: reference.expectedSubtype,
348
335
  integrity: reference.integrity,
349
336
  crossorigin: reference.crossorigin,
@@ -367,7 +354,6 @@ const createUrlInfo = (url, context) => {
367
354
  astInfo: reference.astInfo,
368
355
  mutation: reference.mutation,
369
356
  data: { ...reference.data },
370
- specifier: newSpecifier,
371
357
  isWeak: true,
372
358
  isInline: reference.isInline,
373
359
  original: reference.original || reference,
@@ -377,9 +363,37 @@ const createUrlInfo = (url, context) => {
377
363
  // generatedUrl: null,
378
364
  // generatedSpecifier: null,
379
365
  // filename: null,
366
+ ...newProps,
367
+ });
368
+ reference.next = nextReference;
369
+ return nextReference.urlInfo;
370
+ };
371
+
372
+ urlInfo.redirect = (props) => {
373
+ return getNextUrlInfo(props);
374
+ };
375
+ urlInfo.getWithoutSearchParam = (searchParam, props) => {
376
+ // The search param can be
377
+ // 1. injected by a plugin during "redirectReference"
378
+ // - import assertions
379
+ // - js module fallback to systemjs
380
+ // 2. already inside source files
381
+ // - turn js module into js classic for convenience ?as_js_classic
382
+ // - turn js classic to js module for to make it importable
383
+ if (!urlInfo.searchParams.has(searchParam)) {
384
+ return null;
385
+ }
386
+ const reference = urlInfo.firstReference;
387
+ const specifierWithoutSearchParam = injectQueryParamsIntoSpecifier(
388
+ reference.specifier,
389
+ {
390
+ [searchParam]: undefined,
391
+ },
392
+ );
393
+ return urlInfo.redirect({
394
+ specifier: specifierWithoutSearchParam,
395
+ ...props,
380
396
  });
381
- reference.next = referenceWithoutSearchParam;
382
- return referenceWithoutSearchParam.urlInfo;
383
397
  };
384
398
  urlInfo.onRemoved = () => {
385
399
  urlInfo.kitchen.urlInfoTransformer.resetContent(urlInfo);
@@ -28,6 +28,7 @@ import { jsenvPluginCleanHTML } from "./clean_html/jsenv_plugin_clean_html.js";
28
28
  import { jsenvPluginChromeDevtoolsJson } from "./chrome_devtools_json/jsenv_plugin_chrome_devtools_json.js";
29
29
  import { jsenvPluginAutoreloadOnServerRestart } from "./autoreload_on_server_restart/jsenv_plugin_autoreload_on_server_restart.js";
30
30
  import { jsenvPluginPackageSideEffects } from "./package_side_effects/jsenv_plugin_package_side_effects.js";
31
+ import { jsenvPluginWorkspaceBundle } from "./workspace_bundle/jsenv_plugin_workspace_bundle.js";
31
32
 
32
33
  export const getCorePlugins = ({
33
34
  rootDirectoryUrl,
@@ -50,12 +51,14 @@ export const getCorePlugins = ({
50
51
  inlining = true,
51
52
  http = false,
52
53
  spa,
54
+ packageBundle,
53
55
 
54
56
  clientAutoreload,
55
57
  clientAutoreloadOnServerRestart,
56
58
  cacheControl,
57
59
  scenarioPlaceholders = true,
58
60
  ribbon = true,
61
+ dropToOpen = true,
59
62
  packageSideEffects = false,
60
63
  } = {}) => {
61
64
  if (cacheControl === true) {
@@ -78,6 +81,9 @@ export const getCorePlugins = ({
78
81
  }
79
82
 
80
83
  return [
84
+ ...(packageBundle
85
+ ? [jsenvPluginWorkspaceBundle({ packageDirectory })]
86
+ : []),
81
87
  jsenvPluginReferenceAnalysis(referenceAnalysis),
82
88
  jsenvPluginInjections(injections),
83
89
  jsenvPluginTranspilation(transpilation),
@@ -116,11 +122,12 @@ export const getCorePlugins = ({
116
122
  },
117
123
  ...(nodeEsmResolution
118
124
  ? [
119
- jsenvPluginNodeEsmResolution(
120
- nodeEsmResolution,
125
+ jsenvPluginNodeEsmResolution({
126
+ packageDirectory,
127
+ resolutionConfig: nodeEsmResolution,
121
128
  packageConditions,
122
129
  packageConditionsConfig,
123
- ),
130
+ }),
124
131
  ]
125
132
  : []),
126
133
  jsenvPluginWebResolution(),
@@ -147,7 +154,7 @@ export const getCorePlugins = ({
147
154
  : []),
148
155
  ...(cacheControl ? [jsenvPluginCacheControl(cacheControl)] : []),
149
156
  ...(ribbon ? [jsenvPluginRibbon({ rootDirectoryUrl, ...ribbon })] : []),
150
- jsenvPluginDropToOpen(),
157
+ ...(dropToOpen ? [jsenvPluginDropToOpen()] : []),
151
158
  jsenvPluginCleanHTML(),
152
159
  jsenvPluginChromeDevtoolsJson(),
153
160
  ...(packageSideEffects
@@ -1,10 +1,11 @@
1
1
  import { createNodeEsmResolver } from "./node_esm_resolver.js";
2
2
 
3
- export const jsenvPluginNodeEsmResolution = (
3
+ export const jsenvPluginNodeEsmResolution = ({
4
+ packageDirectory,
4
5
  resolutionConfig = {},
5
6
  packageConditions,
6
7
  packageConditionsConfig = {},
7
- ) => {
8
+ }) => {
8
9
  let nodeEsmResolverDefault;
9
10
  const resolverMap = new Map();
10
11
  let anyTypeResolver;
@@ -22,6 +23,7 @@ export const jsenvPluginNodeEsmResolution = (
22
23
  );
23
24
  }
24
25
  return createNodeEsmResolver({
26
+ packageDirectory,
25
27
  runtimeCompat: kitchenContext.runtimeCompat,
26
28
  rootDirectoryUrl: kitchenContext.rootDirectoryUrl,
27
29
  packageConditions,
@@ -38,9 +40,10 @@ export const jsenvPluginNodeEsmResolution = (
38
40
  appliesDuring: "*",
39
41
  init: (kitchenContext) => {
40
42
  nodeEsmResolverDefault = createNodeEsmResolver({
43
+ packageDirectory,
41
44
  runtimeCompat: kitchenContext.runtimeCompat,
42
45
  rootDirectoryUrl: kitchenContext.rootDirectoryUrl,
43
- preservesSymlink: true,
46
+ // preservesSymlink: true,
44
47
  packageConditions,
45
48
  packageConditionsConfig: {
46
49
  ...kitchenContext.packageConditionsConfig,