@jsenv/core 41.0.3 → 41.0.4

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,35 +1,27 @@
1
1
  import {
2
2
  assertAndNormalizeDirectoryUrl,
3
- bufferToEtag,
4
3
  lookupPackageDirectory,
5
4
  } from "@jsenv/filesystem";
6
- import { createLogger, createTaskLog, formatError } from "@jsenv/humanize";
5
+ import { createLogger, createTaskLog } from "@jsenv/humanize";
7
6
  import {
8
- composeTwoResponses,
9
- fetchDirectory,
10
7
  jsenvAccessControlAllowedHeaders,
11
8
  serverPluginCORS,
12
- serverPluginErrorHandler,
13
9
  startServer,
14
10
  } from "@jsenv/server";
15
- import { convertFileSystemErrorToResponseProperties } from "@jsenv/server/src/plugins/filesystem/filesystem_error_to_response.js";
16
- import { URL_META } from "@jsenv/url-meta";
17
11
  import { urlIsOrIsInsideOf, urlToRelativeUrl } from "@jsenv/urls";
18
- import { existsSync, readFileSync } from "node:fs";
12
+ import { existsSync } from "node:fs";
13
+
19
14
  import { defaultRuntimeCompat } from "../build/build_params.js";
20
15
  import { createEventEmitter } from "../helpers/event_emitter.js";
21
- import { watchSourceFiles } from "../helpers/watch_source_files.js";
22
- import { WEB_URL_CONVERTER } from "../helpers/web_url_converter.js";
23
16
  import { jsenvCoreDirectoryUrl } from "../jsenv_core_directory_url.js";
24
- import { createKitchen } from "../kitchen/kitchen.js";
25
17
  import { createPackageDirectory } from "../kitchen/package_directory.js";
26
- import {
27
- createJsenvPluginsController,
28
- createJsenvPluginStore,
29
- } from "../plugins/jsenv_plugins_controller.js";
18
+ import { createJsenvPluginStore } from "../plugins/jsenv_plugins_controller.js";
30
19
  import { getCorePlugins } from "../plugins/plugins.js";
31
20
  import { jsenvPluginServerEvents } from "../plugins/server_events/jsenv_plugin_server_events.js";
32
- import { parseUserAgentHeader } from "./user_agent.js";
21
+ import { devServerPluginChromeDevToolsJson } from "./dev_server_plugins/dev_server_plugin_chrome_devtools_json.js";
22
+ import { devServerPluginInjectServerResponseHeader } from "./dev_server_plugins/dev_server_plugin_inject_server_response_header.js";
23
+ import { devServerPluginOmegaErrorHandler } from "./dev_server_plugins/dev_server_plugin_omega_error_handler.js";
24
+ import { devServerPluginServeSourceFiles } from "./dev_server_plugins/dev_server_plugin_serve_source_files.js";
33
25
 
34
26
  const EXECUTED_BY_TEST_PLAN = process.argv.includes("--jsenv-test");
35
27
 
@@ -152,7 +144,6 @@ export const startDevServer = async ({
152
144
  );
153
145
  }
154
146
  }
155
-
156
147
  // params normalization
157
148
  {
158
149
  if (clientAutoreload === true) {
@@ -174,511 +165,94 @@ export const startDevServer = async ({
174
165
  serverStopAbortController.abort();
175
166
  });
176
167
  const serverStopAbortSignal = serverStopAbortController.signal;
177
- const kitchenCache = new Map();
178
168
 
179
- const finalServerPlugins = [];
180
- // x-server-inspect service
181
- {
182
- finalServerPlugins.push({
183
- name: "jsenv:server_header",
184
- routes: [
185
- {
186
- endpoint: "GET /.internal/server.json",
187
- description: "Get information about jsenv dev server",
188
- availableMediaTypes: ["application/json"],
189
- declarationSource: import.meta.url,
190
- fetch: () =>
191
- Response.json({
192
- server: "jsenv_dev_server/1",
193
- sourceDirectoryUrl,
194
- }),
195
- },
196
- ],
197
- injectResponseProperties: () => {
198
- return {
199
- headers: {
200
- server: "jsenv_dev_server/1",
201
- },
202
- };
203
- },
204
- });
205
- }
206
- // cors service
207
- {
208
- finalServerPlugins.push(
209
- serverPluginCORS({
210
- accessControlAllowRequestOrigin: true,
211
- accessControlAllowRequestMethod: true,
212
- accessControlAllowRequestHeaders: true,
213
- accessControlAllowedRequestHeaders: [
214
- ...jsenvAccessControlAllowedHeaders,
215
- "x-jsenv-execution-id",
216
- ],
217
- accessControlAllowCredentials: true,
218
- timingAllowOrigin: true,
219
- }),
220
- );
221
- }
222
- // custom server plugins
223
- {
224
- finalServerPlugins.push(...serverPlugins);
225
- }
226
- // file_service
227
- {
228
- const clientFileChangeEventEmitter = createEventEmitter();
229
- const clientFileDereferencedEventEmitter = createEventEmitter();
230
- clientAutoreload = {
231
- enabled: true,
232
- clientServerEventsConfig: {},
233
- clientFileChangeEventEmitter,
234
- clientFileDereferencedEventEmitter,
235
- ...clientAutoreload,
236
- };
237
- const stopWatchingSourceFiles = watchSourceFiles(
238
- sourceDirectoryUrl,
239
- (fileInfo) => {
240
- clientFileChangeEventEmitter.emit(fileInfo);
241
- },
242
- {
243
- sourceFilesConfig,
244
- keepProcessAlive: false,
245
- cooldownBetweenFileEvents: clientAutoreload.cooldownBetweenFileEvents,
246
- },
247
- );
248
- serverStopCallbackSet.add(stopWatchingSourceFiles);
169
+ const kitchenCache = new Map();
170
+ const packageDirectory = createPackageDirectory({ sourceDirectoryUrl });
171
+ const clientFileChangeEventEmitter = createEventEmitter();
172
+ const clientFileDereferencedEventEmitter = createEventEmitter();
173
+ clientAutoreload = {
174
+ enabled: true,
175
+ clientServerEventsConfig: {},
176
+ clientFileChangeEventEmitter,
177
+ clientFileDereferencedEventEmitter,
178
+ ...clientAutoreload,
179
+ };
249
180
 
250
- const packageDirectory = createPackageDirectory({
251
- sourceDirectoryUrl,
252
- });
181
+ const devServerJsenvPluginStore = await createJsenvPluginStore([
182
+ jsenvPluginServerEvents({ clientAutoreload }),
183
+ ...plugins,
184
+ ...getCorePlugins({
185
+ packageDirectory,
186
+ rootDirectoryUrl: sourceDirectoryUrl,
187
+ mainFilePath: sourceMainFilePath,
188
+ runtimeCompat,
189
+ sourceFilesConfig,
253
190
 
254
- const devServerJsenvPluginStore = await createJsenvPluginStore([
255
- jsenvPluginServerEvents({ clientAutoreload }),
256
- ...plugins,
257
- ...getCorePlugins({
258
- packageDirectory,
259
- rootDirectoryUrl: sourceDirectoryUrl,
260
- mainFilePath: sourceMainFilePath,
261
- runtimeCompat,
262
- sourceFilesConfig,
191
+ referenceAnalysis,
192
+ nodeEsmResolution,
193
+ packageConditions,
194
+ packageConditionsConfig,
195
+ magicExtensions,
196
+ magicDirectoryIndex,
197
+ directoryListing,
198
+ supervisor,
199
+ injections,
200
+ transpilation,
201
+ spa,
202
+ packageBundle,
263
203
 
264
- referenceAnalysis,
265
- nodeEsmResolution,
266
- packageConditions,
267
- packageConditionsConfig,
268
- magicExtensions,
269
- magicDirectoryIndex,
270
- directoryListing,
271
- supervisor,
272
- injections,
273
- transpilation,
274
- spa,
275
- packageBundle,
204
+ clientAutoreload,
205
+ clientAutoreloadOnServerRestart,
206
+ cacheControl,
207
+ ribbon,
208
+ dropToOpen,
209
+ }),
210
+ ]);
276
211
 
277
- clientAutoreload,
278
- clientAutoreloadOnServerRestart,
279
- cacheControl,
280
- ribbon,
281
- dropToOpen,
282
- }),
283
- ]);
284
- const getOrCreateKitchen = async (request) => {
285
- const { runtimeName, runtimeVersion } = parseUserAgentHeader(
286
- request.headers["user-agent"] || "",
287
- );
288
- const runtimeId = `${runtimeName}@${runtimeVersion}`;
289
- const existing = kitchenCache.get(runtimeId);
290
- if (existing) {
291
- return existing;
292
- }
293
- const watchAssociations = URL_META.resolveAssociations(
294
- { watch: stopWatchingSourceFiles.watchPatterns },
295
- sourceDirectoryUrl,
296
- );
297
- let kitchen;
298
- clientFileChangeEventEmitter.on(({ url, event }) => {
299
- const urlInfo = kitchen.graph.getUrlInfo(url);
300
- if (urlInfo) {
301
- if (event === "removed") {
302
- urlInfo.onRemoved();
303
- } else {
304
- urlInfo.onModified();
305
- }
306
- }
307
- });
308
- const clientRuntimeCompat = { [runtimeName]: runtimeVersion };
309
-
310
- kitchen = createKitchen({
311
- name: runtimeId,
312
- signal: serverStopAbortSignal,
313
- logLevel,
314
- rootDirectoryUrl: sourceDirectoryUrl,
315
- mainFilePath: sourceMainFilePath,
316
- ignore,
317
- dev: true,
318
- runtimeCompat,
319
- clientRuntimeCompat,
320
- supervisor,
321
- sourcemaps,
322
- sourcemapsSourcesContent,
323
- outDirectoryUrl: outDirectoryUrl
324
- ? new URL(`${runtimeName}@${runtimeVersion}/`, outDirectoryUrl)
325
- : undefined,
326
- packageDirectory,
327
- });
328
- kitchen.graph.urlInfoCreatedEventEmitter.on((urlInfoCreated) => {
329
- const { watch } = URL_META.applyAssociations({
330
- url: urlInfoCreated.url,
331
- associations: watchAssociations,
332
- });
333
- urlInfoCreated.isWatched = watch;
334
- // when an url depends on many others, we check all these (like package.json)
335
- urlInfoCreated.isValid = () => {
336
- const seenSet = new Set();
337
- const checkValidity = (urlInfo) => {
338
- if (seenSet.has(urlInfo)) {
339
- return true;
340
- }
341
- seenSet.add(urlInfo);
342
- if (!urlInfo.url.startsWith("file:")) {
343
- return false;
344
- }
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;
352
- }
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
- );
376
- return false;
377
- }
378
- }
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
- }
399
- }
400
- return true;
401
- };
402
- const valid = checkValidity(urlInfoCreated);
403
- return valid;
404
- };
405
- });
406
- kitchen.graph.urlInfoDereferencedEventEmitter.on(
407
- (urlInfoDereferenced, lastReferenceFromOther) => {
408
- clientFileDereferencedEventEmitter.emit(
409
- urlInfoDereferenced,
410
- lastReferenceFromOther,
411
- );
412
- },
413
- );
414
- const devServerJsenvPluginController = await createJsenvPluginsController(
415
- devServerJsenvPluginStore,
416
- kitchen,
417
- );
418
- kitchen.setJsenvPluginsController(devServerJsenvPluginController);
212
+ const finalServerPlugins = [];
213
+ finalServerPlugins.push(
214
+ // "header" service
215
+ devServerPluginInjectServerResponseHeader({ sourceDirectoryUrl }),
216
+ // cors service
217
+ serverPluginCORS({
218
+ accessControlAllowRequestOrigin: true,
219
+ accessControlAllowRequestMethod: true,
220
+ accessControlAllowRequestHeaders: true,
221
+ accessControlAllowedRequestHeaders: [
222
+ ...jsenvAccessControlAllowedHeaders,
223
+ "x-jsenv-execution-id",
224
+ ],
225
+ accessControlAllowCredentials: true,
226
+ timingAllowOrigin: true,
227
+ }),
228
+ // chrome devtools
229
+ devServerPluginChromeDevToolsJson({ sourceDirectoryUrl }),
230
+ ...serverPlugins,
231
+ devServerPluginServeSourceFiles({
232
+ packageDirectory,
233
+ sourceDirectoryUrl,
234
+ sourceMainFilePath,
235
+ ignore,
236
+ sourceFilesConfig,
237
+ clientAutoreload,
419
238
 
420
- serverStopCallbackSet.add(() => {
421
- devServerJsenvPluginController.callHooks("destroy", kitchen.context);
422
- });
423
- kitchenCache.set(runtimeId, kitchen);
424
- onKitchenCreated(kitchen);
425
- return kitchen;
426
- };
239
+ logLevel,
240
+ runtimeCompat,
241
+ onKitchenCreated,
427
242
 
428
- finalServerPlugins.push({
429
- name: "jsenv:dev_server_routes",
430
- augmentRouteFetchSecondArg: async (request) => {
431
- const kitchen = await getOrCreateKitchen(request);
432
- return { kitchen };
433
- },
434
- routes: [
435
- ...devServerJsenvPluginStore.allServerRoutes,
436
- {
437
- endpoint: "GET *",
438
- description: "Serve project files.",
439
- declarationSource: import.meta.url,
440
- fetch: async (request, { kitchen }) => {
441
- const { rootDirectoryUrl, mainFilePath } = kitchen.context;
442
- let requestResource = request.resource;
443
- let requestedUrl;
444
- if (requestResource.startsWith("/@fs/")) {
445
- const fsRootRelativeUrl = requestResource.slice("/@fs/".length);
446
- requestedUrl = `file:///${fsRootRelativeUrl}`;
447
- } else {
448
- const requestedUrlObject = new URL(
449
- requestResource === "/"
450
- ? mainFilePath
451
- : requestResource.slice(1),
452
- rootDirectoryUrl,
453
- );
454
- requestedUrlObject.searchParams.delete("hot");
455
- requestedUrl = requestedUrlObject.href;
456
- }
457
- const { referer } = request.headers;
458
- const parentUrl = referer
459
- ? WEB_URL_CONVERTER.asFileUrl(referer, {
460
- origin: request.origin,
461
- rootDirectoryUrl: sourceDirectoryUrl,
462
- })
463
- : sourceDirectoryUrl;
464
- let reference = kitchen.graph.inferReference(
465
- request.resource,
466
- parentUrl,
467
- );
468
- if (reference) {
469
- reference.urlInfo.context.request = request;
470
- reference.urlInfo.context.requestedUrl = requestedUrl;
471
- } else {
472
- const rootUrlInfo = kitchen.graph.rootUrlInfo;
473
- rootUrlInfo.context.request = request;
474
- rootUrlInfo.context.requestedUrl = requestedUrl;
475
- reference = rootUrlInfo.dependencies.createResolveAndFinalize({
476
- trace: { message: parentUrl },
477
- type: "http_request",
478
- specifier: request.resource,
479
- });
480
- reference.urlInfo.context.requestedUrl = requestedUrl;
481
- rootUrlInfo.context.request = null;
482
- rootUrlInfo.context.requestedUrl = null;
483
- }
484
- const urlInfo = reference.urlInfo;
485
- const ifNoneMatch = request.headers["if-none-match"];
486
- const urlInfoTargetedByCache =
487
- urlInfo.findParentIfInline() || urlInfo;
243
+ supervisor,
244
+ sourcemaps,
245
+ sourcemapsSourcesContent,
246
+ outDirectoryUrl,
488
247
 
489
- try {
490
- if (!urlInfo.error && ifNoneMatch) {
491
- const [clientOriginalContentEtag, clientContentEtag] =
492
- ifNoneMatch.split("_");
493
- if (
494
- urlInfoTargetedByCache.originalContentEtag ===
495
- clientOriginalContentEtag &&
496
- urlInfoTargetedByCache.contentEtag === clientContentEtag &&
497
- urlInfoTargetedByCache.isValid()
498
- ) {
499
- const headers = {
500
- "cache-control": `private,max-age=0,must-revalidate`,
501
- };
502
- Object.keys(urlInfo.headers).forEach((key) => {
503
- if (key !== "content-length") {
504
- headers[key] = urlInfo.headers[key];
505
- }
506
- });
507
- return {
508
- status: 304,
509
- headers,
510
- };
511
- }
512
- }
513
- await urlInfo.cook({ request, reference });
514
- let { response } = urlInfo;
515
- if (response) {
516
- return response;
517
- }
518
- response = {
519
- url: reference.url,
520
- status: 200,
521
- headers: {
522
- // when we send eTag to the client the next request to the server
523
- // will send etag in request headers.
524
- // If they match jsenv bypass cooking and returns 304
525
- // This must not happen when a plugin uses "no-store" or "no-cache" as it means
526
- // plugin logic wants to happens for every request to this url
527
- ...(cacheIsDisabledInResponseHeader(urlInfoTargetedByCache)
528
- ? {
529
- "cache-control": "no-store", // for inline file we force no-store when parent is no-store
530
- }
531
- : {
532
- "cache-control": `private,max-age=0,must-revalidate`,
533
- // it's safe to use "_" separator because etag is encoded with base64 (see https://stackoverflow.com/a/13195197)
534
- "eTag": `${urlInfoTargetedByCache.originalContentEtag}_${urlInfoTargetedByCache.contentEtag}`,
535
- }),
536
- ...urlInfo.headers,
537
- "content-type": urlInfo.contentType,
538
- "content-length": urlInfo.contentLength,
539
- },
540
- body: urlInfo.content,
541
- timing: urlInfo.timing, // TODO: use something else
542
- };
543
- const augmentResponseInfo = {
544
- ...kitchen.context,
545
- reference,
546
- urlInfo,
547
- };
548
- kitchen.jsenvPluginsController.callHooks(
549
- "augmentResponse",
550
- augmentResponseInfo,
551
- (returnValue) => {
552
- response = composeTwoResponses(response, returnValue);
553
- },
554
- );
555
- return response;
556
- } catch (error) {
557
- const originalError = error ? error.cause || error : error;
558
- if (originalError.asResponse) {
559
- return originalError.asResponse();
560
- }
561
- const code = originalError.code;
562
- if (code === "PARSE_ERROR") {
563
- // when possible let browser re-throw the syntax error
564
- // it's not possible to do that when url info content is not available
565
- // (happens for js_module_fallback for instance)
566
- if (urlInfo.content !== undefined) {
567
- kitchen.context.logger
568
- .error(`Error while handling ${request.url}:
569
- ${originalError.reasonCode || originalError.code}
570
- ${error.trace?.message}`);
571
- return {
572
- url: reference.url,
573
- status: 200,
574
- // reason becomes the http response statusText, it must not contain invalid chars
575
- // https://github.com/nodejs/node/blob/0c27ca4bc9782d658afeaebcec85ec7b28f1cc35/lib/_http_common.js#L221
576
- statusText: error.reason,
577
- statusMessage: originalError.message,
578
- headers: {
579
- "content-type": urlInfo.contentType,
580
- "content-length": urlInfo.contentLength,
581
- "cache-control": "no-store",
582
- },
583
- body: urlInfo.content,
584
- };
585
- }
586
- return {
587
- url: reference.url,
588
- status: 500,
589
- statusText: error.reason,
590
- statusMessage: originalError.message,
591
- headers: {
592
- "cache-control": "no-store",
593
- },
594
- body: urlInfo.content,
595
- };
596
- }
597
- if (code === "DIRECTORY_REFERENCE_NOT_ALLOWED") {
598
- return fetchDirectory(reference.url, {
599
- headers: {
600
- accept: "text/html",
601
- },
602
- canReadDirectory: true,
603
- rootDirectoryUrl: sourceDirectoryUrl,
604
- });
605
- }
606
- if (code === "NOT_ALLOWED") {
607
- return {
608
- url: reference.url,
609
- status: 403,
610
- statusText: originalError.reason,
611
- };
612
- }
613
- if (code === "NOT_FOUND") {
614
- return {
615
- url: reference.url,
616
- status: 404,
617
- statusText: originalError.reason,
618
- statusMessage: originalError.message,
619
- };
620
- }
621
- return {
622
- url: reference.url,
623
- status: 500,
624
- statusText: error.reason,
625
- statusMessage: formatError(error),
626
- headers: {
627
- "cache-control": "no-store",
628
- },
629
- };
630
- }
631
- },
632
- },
633
- ],
634
- });
635
- finalServerPlugins.push(...devServerJsenvPluginStore.allServerPlugins);
636
- }
637
- // jsenv error handler service
638
- {
639
- finalServerPlugins.push({
640
- name: "jsenv:omega_error_handler",
641
- handleError: (error) => {
642
- const getResponseForError = () => {
643
- if (error && error.asResponse) {
644
- return error.asResponse();
645
- }
646
- if (error && error.statusText === "Unexpected directory operation") {
647
- return {
648
- status: 403,
649
- };
650
- }
651
- return convertFileSystemErrorToResponseProperties(error);
652
- };
653
- const response = getResponseForError();
654
- if (!response) {
655
- return null;
656
- }
657
- const body = JSON.stringify({
658
- status: response.status,
659
- statusText: response.statusText,
660
- headers: response.headers,
661
- body: response.body,
662
- });
663
- return {
664
- status: response.status,
665
- headers: {
666
- "content-type": "application/json",
667
- "content-length": Buffer.byteLength(body),
668
- },
669
- body,
670
- };
671
- },
672
- });
673
- }
674
- // default error handler
675
- {
676
- finalServerPlugins.push(
677
- serverPluginErrorHandler({
678
- sendErrorDetails: true,
679
- }),
680
- );
681
- }
248
+ serverStopAbortSignal,
249
+ serverStopCallbackSet,
250
+ devServerJsenvPluginStore,
251
+ kitchenCache,
252
+ }),
253
+ // jsenv error handler service
254
+ devServerPluginOmegaErrorHandler(),
255
+ );
682
256
 
683
257
  const server = await startServer({
684
258
  signal,
@@ -724,10 +298,3 @@ export const startDevServer = async ({
724
298
  kitchenCache,
725
299
  };
726
300
  };
727
-
728
- const cacheIsDisabledInResponseHeader = (urlInfo) => {
729
- return (
730
- urlInfo.headers["cache-control"] === "no-store" ||
731
- urlInfo.headers["cache-control"] === "no-cache"
732
- );
733
- };
@@ -25,7 +25,6 @@ import { jsenvPluginCacheControl } from "./cache_control/jsenv_plugin_cache_cont
25
25
  import { jsenvPluginRibbon } from "./ribbon/jsenv_plugin_ribbon.js";
26
26
  import { jsenvPluginDropToOpen } from "./drop_to_open/jsenv_plugin_drop_to_open.js";
27
27
  import { jsenvPluginCleanHTML } from "./clean_html/jsenv_plugin_clean_html.js";
28
- import { jsenvPluginChromeDevtoolsJson } from "./chrome_devtools_json/jsenv_plugin_chrome_devtools_json.js";
29
28
  import { jsenvPluginAutoreloadOnServerRestart } from "./autoreload_on_server_restart/jsenv_plugin_autoreload_on_server_restart.js";
30
29
  import { jsenvPluginPackageSideEffects } from "./package_side_effects/jsenv_plugin_package_side_effects.js";
31
30
  import { jsenvPluginWorkspaceBundle } from "./workspace_bundle/jsenv_plugin_workspace_bundle.js";
@@ -156,7 +155,6 @@ export const getCorePlugins = ({
156
155
  ...(ribbon ? [jsenvPluginRibbon({ rootDirectoryUrl, ...ribbon })] : []),
157
156
  ...(dropToOpen ? [jsenvPluginDropToOpen()] : []),
158
157
  jsenvPluginCleanHTML(),
159
- jsenvPluginChromeDevtoolsJson(),
160
158
  ...(packageSideEffects
161
159
  ? [jsenvPluginPackageSideEffects({ packageDirectory })]
162
160
  : []),