@solidjs/vite-plugin 3.0.0-next.33 → 3.0.0-next.35

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,6 +1,6 @@
1
1
  import * as babel from '@babel/core';
2
2
  import remapping from '@ampproject/remapping';
3
- import solid from 'babel-preset-solid';
3
+ import solid from '@solidjs/babel-plugin';
4
4
  import { existsSync, readFileSync, mkdirSync, writeFileSync, rmSync } from 'fs';
5
5
  import { mergeAndConcat } from 'merge-anything';
6
6
  import { createRequire } from 'module';
@@ -477,6 +477,27 @@ function createDevAssetResolver(server, filter = defaultStyleFilter) {
477
477
  };
478
478
  }
479
479
 
480
+ /**
481
+ * Cross-instance-safe stand-in for vite's `isRunnableDevEnvironment`.
482
+ *
483
+ * Vite's helper is an `instanceof RunnableDevEnvironment` check against the
484
+ * class of whichever `vite` module the CALLER imported. When this plugin is
485
+ * consumed through a workspace/`link:` install, its own `vite` import can
486
+ * resolve to a different physical copy than the one running the dev server —
487
+ * and then the `instanceof` is false for every environment, silently standing
488
+ * the SSR/dev middlewares down. The `runner` accessor is the type's defining
489
+ * member (`RunnableDevEnvironment` is exactly "a DevEnvironment with a
490
+ * runner"), so presence-check it instead of trusting class identity.
491
+ */
492
+ function isRunnableEnvironment(environment) {
493
+ return !!environment && typeof environment === 'object' && 'runner' in environment;
494
+ }
495
+ function getEnvironmentConsumer(environment, options) {
496
+ const consumer = environment?.config?.consumer;
497
+ if (consumer === 'client' || consumer === 'server') return consumer;
498
+ return options?.ssr ? 'server' : 'client';
499
+ }
500
+
480
501
  const VIRTUAL_ID = '\0@solidjs/vite-plugin:boundary-modules';
481
502
 
482
503
  /**
@@ -511,7 +532,7 @@ function boundaryModules() {
511
532
  // scan all the same. Real dev/build module graphs resolve without
512
533
  // the flag and stay fully guarded.
513
534
  const scan = !!options?.scan;
514
- const server = this.environment.config.consumer === 'server';
535
+ const server = getEnvironmentConsumer(this.environment, options) === 'server';
515
536
  if (id === 'server-only') {
516
537
  if (!server && !scan) this.error(`[@solidjs/vite-plugin] Attempt to import 'server-only' in a client module: ${importer}. ` + `Code that uses this module must run only on the server — make sure it is only ` + `imported by server code (e.g. a server entry, a "use server" module, or code ` + `reached exclusively from them).`);
517
538
  } else if (id === 'client-only') {
@@ -528,31 +549,216 @@ function boundaryModules() {
528
549
  }
529
550
 
530
551
  /**
531
- * Cross-instance-safe stand-in for vite's `isRunnableDevEnvironment`.
552
+ * Agent diagnostics surface (`diagnostics: true`, dev serve only).
532
553
  *
533
- * Vite's helper is an `instanceof RunnableDevEnvironment` check against the
534
- * class of whichever `vite` module the CALLER imported. When this plugin is
535
- * consumed through a workspace/`link:` install, its own `vite` import can
536
- * resolve to a different physical copy than the one running the dev server —
537
- * and then the `instanceof` is false for every environment, silently standing
538
- * the SSR/dev middlewares down. The `runner` accessor is the type's defining
539
- * member (`RunnableDevEnvironment` is exactly "a DevEnvironment with a
540
- * runner"), so presence-check it instead of trusting class identity.
554
+ * Three pieces:
555
+ * - an injected client module (virtual, imported by index.html or the
556
+ * start-mode client entry) that installs the in-page bridge from the
557
+ * app's own `@solidjs/diagnostics` and answers requests over Vite's
558
+ * WebSocket custom events;
559
+ * - a collector that forwards requests to the page and correlates
560
+ * responses by id;
561
+ * - an HTTP endpoint (`/__solid/diagnostics`) fronting that round-trip so
562
+ * any out-of-process consumer (agent, MCP tool, curl) can drive capture
563
+ * sessions without holding a WebSocket.
564
+ *
565
+ * `@solidjs/diagnostics` is deliberately a type-only dependency of this
566
+ * plugin: the runtime bridge always comes from the app's own installed
567
+ * copy, so plugin releases and diagnostics releases stay uncoupled. The
568
+ * wire constants are re-declared here with types imported from the
569
+ * package, so drift fails the plugin's own compile.
541
570
  */
542
- function isRunnableEnvironment(environment) {
543
- return !!environment && typeof environment === 'object' && 'runner' in environment;
571
+ const DIAGNOSTICS_ENDPOINT = '/__solid/diagnostics';
572
+ const REQUEST_EVENT = 'solid:diagnostics:request';
573
+ const RESPONSE_EVENT = 'solid:diagnostics:response';
574
+ const DIAGNOSTICS_PACKAGE = '@solidjs/diagnostics';
575
+ const DIAGNOSTICS_CLIENT_ID = 'virtual:solid-diagnostics/client';
576
+ const METHODS = ['begin', 'end', 'active', 'whyDidRun', 'costs'];
577
+
578
+ /** How long the endpoint waits for a page to answer before failing the call. */
579
+ const RESPONSE_TIMEOUT_MS = 10_000;
580
+ function diagnosticsClientModuleCode() {
581
+ // Runtime imports resolve to the APP's diagnostics package (see the
582
+ // resolveId assist below) — the page speaks its own package's protocol.
583
+ return [`import { installDiagnosticsBridge } from '${DIAGNOSTICS_PACKAGE}/browser';`, `import {`, ` DIAGNOSTICS_REQUEST_EVENT,`, ` DIAGNOSTICS_RESPONSE_EVENT,`, `} from '${DIAGNOSTICS_PACKAGE}/protocol';`, ``, `const bridge = installDiagnosticsBridge();`, ``, `async function dispatch(request) {`, ` switch (request.method) {`, ` case 'begin': bridge.begin(request.params); return true;`, ` case 'end': return bridge.end();`, ` case 'active': return bridge.active();`, ` case 'whyDidRun': return bridge.whyDidRun(request.params.name);`, ` case 'costs': return bridge.costs();`, ` default: throw new Error('Unknown diagnostics method: ' + request.method);`, ` }`, `}`, ``, `if (import.meta.hot) {`, ` import.meta.hot.on(DIAGNOSTICS_REQUEST_EVENT, async (request) => {`, ` let response;`, ` try {`, ` response = { id: request.id, result: await dispatch(request) };`, ` } catch (error) {`, ` response = {`, ` id: request.id,`, ` error: error instanceof Error ? error.message : String(error),`, ` };`, ` }`, ` import.meta.hot.send(DIAGNOSTICS_RESPONSE_EVENT, response);`, ` });`, `}`].join('\n');
544
584
  }
545
- function getEnvironmentConsumer(environment, options) {
546
- const consumer = environment?.config?.consumer;
547
- if (consumer === 'client' || consumer === 'server') return consumer;
548
- return options?.ssr ? 'server' : 'client';
585
+ function sendJson(res, status, body) {
586
+ res.statusCode = status;
587
+ res.setHeader('Content-Type', 'application/json');
588
+ res.end(JSON.stringify(body));
589
+ }
590
+ function readJsonBody(req) {
591
+ return new Promise((resolve, reject) => {
592
+ const chunks = [];
593
+ req.on('data', chunk => chunks.push(chunk));
594
+ req.on('end', () => {
595
+ const text = Buffer.concat(chunks).toString('utf8');
596
+ if (!text) return resolve({});
597
+ try {
598
+ resolve(JSON.parse(text));
599
+ } catch {
600
+ reject(new Error('Request body is not valid JSON'));
601
+ }
602
+ });
603
+ req.on('error', reject);
604
+ });
605
+ }
606
+ function solidDiagnostics() {
607
+ let root = process.cwd();
608
+ let base = '/';
609
+ return {
610
+ name: 'solid:diagnostics',
611
+ // Dev-serve only: the channels this fronts exist in dev builds only.
612
+ apply(_config, env) {
613
+ return env.command === 'serve' && !env.isPreview;
614
+ },
615
+ configResolved(config) {
616
+ root = config.root;
617
+ base = config.base;
618
+ },
619
+ async resolveId(source, importer) {
620
+ if (source === DIAGNOSTICS_CLIENT_ID) {
621
+ return {
622
+ id: DIAGNOSTICS_CLIENT_ID,
623
+ moduleSideEffects: true
624
+ };
625
+ }
626
+ // The virtual module has no directory to resolve bare imports from;
627
+ // resolve the app's diagnostics package from the project root.
628
+ if (importer === DIAGNOSTICS_CLIENT_ID && source.startsWith(DIAGNOSTICS_PACKAGE)) {
629
+ const resolved = await this.resolve(source, path.resolve(root, 'index.html'), {
630
+ skipSelf: true
631
+ });
632
+ if (!resolved || resolved.id.startsWith('__vite-optional-peer-dep:')) {
633
+ this.error(`[@solidjs/vite-plugin] the diagnostics option requires ${DIAGNOSTICS_PACKAGE} ` + 'installed in the app (it provides the in-page bridge). Install it as a ' + 'development dependency or remove `diagnostics: true`.');
634
+ }
635
+ return resolved;
636
+ }
637
+ return null;
638
+ },
639
+ load(id) {
640
+ if (id === DIAGNOSTICS_CLIENT_ID) return diagnosticsClientModuleCode();
641
+ return null;
642
+ },
643
+ // Plain (index.html) apps get the client module injected here;
644
+ // start-mode apps import it from the generated client entry instead.
645
+ transformIndexHtml() {
646
+ return [{
647
+ tag: 'script',
648
+ attrs: {
649
+ type: 'module',
650
+ src: joinBase(base, '/@id/' + DIAGNOSTICS_CLIENT_ID)
651
+ },
652
+ injectTo: 'head'
653
+ }];
654
+ },
655
+ configureServer(server) {
656
+ // Announce the surface in the startup block. This is a discovery
657
+ // channel: agents watching dev-server output learn the endpoint and
658
+ // the skill documents without any project-level pointer (AGENTS.md).
659
+ const originalPrintUrls = server.printUrls.bind(server);
660
+ server.printUrls = () => {
661
+ originalPrintUrls();
662
+ const local = server.resolvedUrls?.local[0];
663
+ const endpoint = local ? new URL(DIAGNOSTICS_ENDPOINT, local).href : DIAGNOSTICS_ENDPOINT;
664
+ server.config.logger.info(` ➜ Solid diagnostics: ${endpoint} ` + `(GET status; POST {"method":"begin"|"end"|"whyDidRun"|"costs"})\n` + ` ➜ Agent skills: node_modules/${DIAGNOSTICS_PACKAGE}/skills/agent-loops/SKILL.md, ` + `node_modules/solid-js/skills/reactivity-diagnostics/SKILL.md`);
665
+ };
666
+ const pending = new Map();
667
+ let nextId = 1;
668
+ server.ws.on(RESPONSE_EVENT, data => {
669
+ const entry = pending.get(data?.id);
670
+ if (!entry) return;
671
+ pending.delete(data.id);
672
+ clearTimeout(entry.timer);
673
+ entry.resolve(data);
674
+ });
675
+ server.middlewares.use(DIAGNOSTICS_ENDPOINT, async (req, res) => {
676
+ // The middleware mounts on the exact path; anything deeper is 404.
677
+ if (req.url && req.url !== '/' && req.url !== '') {
678
+ sendJson(res, 404, {
679
+ error: `Unknown diagnostics path ${req.url}`
680
+ });
681
+ return;
682
+ }
683
+ if (req.method === 'GET') {
684
+ sendJson(res, 200, {
685
+ ok: true,
686
+ methods: METHODS,
687
+ clients: server.ws.clients.size
688
+ });
689
+ return;
690
+ }
691
+ if (req.method !== 'POST') {
692
+ sendJson(res, 405, {
693
+ error: 'Use GET for status or POST { method, params }'
694
+ });
695
+ return;
696
+ }
697
+ let body;
698
+ try {
699
+ body = await readJsonBody(req);
700
+ } catch (error) {
701
+ sendJson(res, 400, {
702
+ error: error.message
703
+ });
704
+ return;
705
+ }
706
+ if (!body.method || !METHODS.includes(body.method)) {
707
+ sendJson(res, 400, {
708
+ error: `Unknown method ${JSON.stringify(body.method)}; expected one of: ${METHODS.join(', ')}`
709
+ });
710
+ return;
711
+ }
712
+ if (server.ws.clients.size === 0) {
713
+ sendJson(res, 503, {
714
+ error: 'No connected page. Open the app in a browser (dev server) so the ' + 'diagnostics bridge can answer.'
715
+ });
716
+ return;
717
+ }
718
+ const id = nextId++;
719
+ // Broadcast; with several open tabs the first responder wins. Good
720
+ // enough for the agent loop (one page under test); revisit with
721
+ // client targeting if multi-page capture ever matters.
722
+ const response = await new Promise(resolve => {
723
+ const timer = setTimeout(() => {
724
+ pending.delete(id);
725
+ resolve({
726
+ timeout: `No page answered within ${RESPONSE_TIMEOUT_MS}ms. The connected page ` + 'may predate `diagnostics: true` — reload it.'
727
+ });
728
+ }, RESPONSE_TIMEOUT_MS);
729
+ pending.set(id, {
730
+ resolve,
731
+ timer
732
+ });
733
+ server.ws.send(REQUEST_EVENT, {
734
+ id,
735
+ method: body.method,
736
+ params: body.params
737
+ });
738
+ });
739
+ if ('timeout' in response) {
740
+ sendJson(res, 504, {
741
+ error: response.timeout
742
+ });
743
+ } else if (response.error !== undefined) {
744
+ sendJson(res, 400, {
745
+ error: response.error
746
+ });
747
+ } else {
748
+ sendJson(res, 200, {
749
+ result: response.result
750
+ });
751
+ }
752
+ });
753
+ }
754
+ };
549
755
  }
550
756
 
551
757
  // The `"use server"` directive compiler. This wraps the native
552
- // `transformDirectives` pass from @dom-expressions/compiler (Rust/Oxc); the
758
+ // `transformDirectives` pass from @solidjs/compiler (Rust/Oxc); the
553
759
  // original Babel implementation (hoisted from solid-start) lived in this
554
760
  // directory through vite-plugin-solid@c052963e and remains the frozen
555
- // reference for the native pass's fixture suite in dom-expressions.
761
+ // reference for the native pass's fixture suite.
556
762
 
557
763
  let compilerPromise;
558
764
 
@@ -561,11 +767,11 @@ let compilerPromise;
561
767
  // compiler's opt-in loader in index.ts).
562
768
  async function loadCompiler() {
563
769
  try {
564
- return await (compilerPromise ??= import('@dom-expressions/compiler'));
770
+ return await (compilerPromise ??= import('@solidjs/compiler'));
565
771
  } catch (error) {
566
772
  compilerPromise = undefined;
567
773
  const reason = error instanceof Error ? `\n\nCause: ${error.message}` : '';
568
- throw new Error('@solidjs/vite-plugin: failed to load @dom-expressions/compiler (the "use server" ' + 'transform). Your platform should get a prebuilt native binary or the ' + '@dom-expressions/compiler-wasm32-wasi fallback — check that optional ' + 'dependencies were installed.' + reason);
774
+ throw new Error('@solidjs/vite-plugin: failed to load @solidjs/compiler (the "use server" ' + 'transform). Your platform should get a prebuilt native binary or the ' + '@solidjs/compiler-wasm32-wasi fallback — check that optional ' + 'dependencies were installed.' + reason);
569
775
  }
570
776
  }
571
777
 
@@ -1013,24 +1219,47 @@ function serverFunctions(options = {}, internal = {}) {
1013
1219
  if (internal.externalDevServer || !isRunnableEnvironment(ssrEnvironment)) {
1014
1220
  return;
1015
1221
  }
1222
+ // A call's address is `<endpoint>/<id>` (solidjs/solid#3076) — the
1223
+ // mount plus exactly one path segment. Bare-mount requests still
1224
+ // reach the runtime handler (it answers 404), so misdirected posts
1225
+ // fail through the endpoint rather than falling through to SSR.
1226
+ const underMount = (pathname, mount) => pathname === mount || pathname.startsWith(mount + '/');
1016
1227
  server.middlewares.use((req, res, next) => {
1017
1228
  const url = new URL(req.url || '/', 'http://localhost');
1018
1229
  // Match with and without `base` — middleware-mode hosts may mount
1019
1230
  // vite.middlewares below the base themselves.
1020
- if (url.pathname !== resolvedEndpoint && url.pathname !== endpoint) {
1231
+ if (!underMount(url.pathname, resolvedEndpoint) && !underMount(url.pathname, endpoint)) {
1021
1232
  return next();
1022
1233
  }
1234
+ const basePrefixed = underMount(url.pathname, resolvedEndpoint);
1023
1235
  // When the stripped form matched, restore the base for dispatch:
1024
1236
  // the generated handler compares the request pathname against the
1025
1237
  // base-prefixed endpoint, and production handlers only ever see
1026
1238
  // base-prefixed URLs.
1027
- const dispatchUrl = url.pathname === resolvedEndpoint ? undefined : joinBase(base, req.url || '/');
1239
+ const dispatchUrl = basePrefixed ? undefined : joinBase(base, req.url || '/');
1028
1240
  (async () => {
1029
1241
  // Make sure the referenced module has been evaluated in the SSR
1030
1242
  // environment so its registration exists — functions only client
1031
1243
  // code references are never loaded by the SSR render itself.
1032
- const headerId = req.headers['x-server-function-id'];
1033
- const functionId = (typeof headerId === 'string' ? headerId.split('#')[0] : undefined) || url.searchParams.get('id');
1244
+ // The id lives in the path segment after the mount.
1245
+ const mount = basePrefixed ? resolvedEndpoint : endpoint;
1246
+ const segment = url.pathname.slice(mount.length + 1);
1247
+ let functionId = null;
1248
+ if (segment && !segment.includes('/')) {
1249
+ try {
1250
+ functionId = decodeURIComponent(segment);
1251
+ } catch {
1252
+ // not an address; the runtime handler answers the 404
1253
+ }
1254
+ }
1255
+ if (!functionId) {
1256
+ // TRANSITIONAL (remove before 3.0 stable): the retired header
1257
+ // and `?id=` addressing, kept only for the RC window where this
1258
+ // plugin meets a @solidjs/web older than the path-addressing
1259
+ // change (solidjs/solid#3076).
1260
+ const headerId = req.headers['x-server-function-id'];
1261
+ functionId = (typeof headerId === 'string' ? headerId.split('#')[0] : undefined) || url.searchParams.get('id');
1262
+ }
1034
1263
  if (functionId) {
1035
1264
  const entry = moduleForFunctionId(functionId);
1036
1265
  if (entry) await ssrEnvironment.runner.import(moduleDevUrl(entry));
@@ -1377,6 +1606,7 @@ function startServe(options, internal = {}) {
1377
1606
  const serverComponents = !!internal.serverComponents;
1378
1607
  const errorBoundary = options.errorBoundary !== false;
1379
1608
  const styleFilter = internal.styleFilter;
1609
+ const diagnostics = !!internal.diagnostics;
1380
1610
  let devtoolsEnabled = false;
1381
1611
  let devtoolsResolutions = {};
1382
1612
  let devtoolsIds = {};
@@ -1541,15 +1771,18 @@ function startServe(options, internal = {}) {
1541
1771
  const {
1542
1772
  app
1543
1773
  } = requireEntries();
1774
+ // Dev-only: the diagnostics bridge fronts dev-mode channels, so builds
1775
+ // never see this import (mirrors the plugin's own serve-only `apply`).
1776
+ const diagnosticsImport = diagnostics && !isBuild ? [`import ${JSON.stringify(DIAGNOSTICS_CLIENT_ID)};`] : [];
1544
1777
  if (clientMode) {
1545
1778
  // render(), not hydrate(): the shell's body is empty, the app mounts
1546
1779
  // fresh. Client code compiles non-hydratable in client mode, so the
1547
1780
  // app cannot claim server DOM anyway. The entry script is injected
1548
1781
  // without `async` (plain module = deferred), so document.body is
1549
1782
  // complete when this runs.
1550
- return [`import { render } from '@solidjs/web';`, ...errorBoundaryImport(), ...(toolbar ? [`import { DevToolbar } from ${JSON.stringify(DEVTOOLS_PACKAGE)};`] : []), `import App from ${JSON.stringify(app)};`, ``, `render(() => ${isBuild && errorBoundary ? '<DefaultErrorBoundary><App /></DefaultErrorBoundary>' : toolbar ? '<DevToolbar><App /></DevToolbar>' : '<App />'}, document.body);`].join('\n');
1783
+ return [...diagnosticsImport, `import { render } from '@solidjs/web';`, ...errorBoundaryImport(), ...(toolbar ? [`import { DevToolbar } from ${JSON.stringify(DEVTOOLS_PACKAGE)};`] : []), `import App from ${JSON.stringify(app)};`, ``, `render(() => ${isBuild && errorBoundary ? '<DefaultErrorBoundary><App /></DefaultErrorBoundary>' : toolbar ? '<DevToolbar><App /></DevToolbar>' : '<App />'}, document.body);`].join('\n');
1551
1784
  }
1552
- return [`import { hydrate } from '@solidjs/web';`, ...(toolbar ? [`import { DevToolbar } from ${JSON.stringify(DEVTOOLS_PACKAGE)};`] : []), ...(serverComponents ? [`import { installServerComponents } from '@solidjs/web/frames';`] : []), ...errorBoundaryImport(), `import Document from ${JSON.stringify(documentSpec())};`, `import App from ${JSON.stringify(app)};`, ``, ...(serverComponents ? [
1785
+ return [...diagnosticsImport, `import { hydrate } from '@solidjs/web';`, ...(toolbar ? [`import { DevToolbar } from ${JSON.stringify(DEVTOOLS_PACKAGE)};`] : []), ...(serverComponents ? [`import { installServerComponents } from '@solidjs/web/frames';`] : []), ...errorBoundaryImport(), `import Document from ${JSON.stringify(documentSpec())};`, `import App from ${JSON.stringify(app)};`, ``, ...(serverComponents ? [
1553
1786
  // Installs the t=0 document-adoption registry and the transport
1554
1787
  // policy (component responses morph their boundary instead of
1555
1788
  // decoding as data). Must run before hydrate().
@@ -1664,7 +1897,11 @@ function startServe(options, internal = {}) {
1664
1897
  // through untouched, so the edge applies it unconditionally.
1665
1898
  lines.push(``, `async function dispatchRequest(request, event, options) {`);
1666
1899
  if (composeServerFunctions) {
1667
- lines.push(` if (new URL(request.url).pathname === endpoint) {`,
1900
+ lines.push(
1901
+ // A call's address is `<endpoint>/<id>` (solidjs/solid#3076); the
1902
+ // bare mount still routes so a misaddressed request 404s through the
1903
+ // runtime handler instead of rendering a page at it.
1904
+ ` const requestPath = new URL(request.url).pathname;`, ` if (requestPath === endpoint || requestPath.startsWith(endpoint + '/')) {`,
1668
1905
  // The call shares the middleware chain's event (locals decoration,
1669
1906
  // the response stub); an explicit host-provided createEvent wins.
1670
1907
  // No fold here: the runtime's server-function handler runs the
@@ -1961,7 +2198,7 @@ function startServe(options, internal = {}) {
1961
2198
  return null;
1962
2199
  },
1963
2200
  async transform(code, id, opts) {
1964
- if (isBuild || !devtoolsEnabled) return null;
2201
+ if (isBuild || !devtoolsEnabled && !diagnostics) return null;
1965
2202
  const current = requireEntries();
1966
2203
  if (current.generated || getEnvironmentConsumer(this.environment, opts) !== 'client') {
1967
2204
  return null;
@@ -1971,12 +2208,17 @@ function startServe(options, internal = {}) {
1971
2208
  if (normalizePath(id.split('?')[0]) !== normalizePath(path.resolve(root, current.entryClient))) {
1972
2209
  return null;
1973
2210
  }
1974
- const toolbar = await resolveDevtools((source, importer) => this.resolve(source, importer, {
1975
- skipSelf: true
1976
- }), id, 'client');
1977
- if (!toolbar) return null;
2211
+ const injected = [];
2212
+ if (diagnostics) injected.push(`import ${JSON.stringify(DIAGNOSTICS_CLIENT_ID)};`);
2213
+ if (devtoolsEnabled) {
2214
+ const toolbar = await resolveDevtools((source, importer) => this.resolve(source, importer, {
2215
+ skipSelf: true
2216
+ }), id, 'client');
2217
+ if (toolbar) injected.push(`import ${JSON.stringify(DEVTOOLS_MOUNT_ID)};`);
2218
+ }
2219
+ if (injected.length === 0) return null;
1978
2220
  return {
1979
- code: `import ${JSON.stringify(DEVTOOLS_MOUNT_ID)};\n${code}`,
2221
+ code: `${injected.join('\n')}\n${code}`,
1980
2222
  map: null
1981
2223
  };
1982
2224
  },
@@ -2710,7 +2952,7 @@ const require$1 = createRequire(import.meta.url);
2710
2952
  * second string-literal argument of the form
2711
2953
  * `"__SOLID_LAZY_MODULE__:" + spec`, which `resolveLazyModuleUrls` swaps for
2712
2954
  * the project-relative resolved module path. The prefix and shape are FROZEN
2713
- * — the emitting side lives in @dom-expressions/compiler and must match.
2955
+ * — the emitting side lives in @solidjs/compiler and must match.
2714
2956
  */
2715
2957
  const LAZY_PLACEHOLDER_PREFIX = '__SOLID_LAZY_MODULE__:';
2716
2958
 
@@ -2828,18 +3070,17 @@ async function fetchAssets(key) {
2828
3070
  }
2829
3071
  export default (registry && registry[${JSON.stringify(root)}]) ||
2830
3072
  (bridgeUrl ? createBridgeResolver() : { resolve: jsOnly, resolveSync: jsOnly });`;
2831
- const SOLID_BUILT_INS = ['For', 'Show', 'Switch', 'Match', 'Loading', 'Reveal', 'Portal', 'Repeat', 'Dynamic', 'Errored'];
2832
3073
 
2833
3074
  /** Possible options for the extensions property */
2834
3075
 
2835
3076
  let nativeCompilerPromise;
2836
3077
  async function loadNativeCompiler() {
2837
3078
  try {
2838
- return await (nativeCompilerPromise ??= import('@dom-expressions/compiler'));
3079
+ return await (nativeCompilerPromise ??= import('@solidjs/compiler'));
2839
3080
  } catch (error) {
2840
3081
  nativeCompilerPromise = undefined;
2841
3082
  const reason = error instanceof Error ? `\n\nCause: ${error.message}` : '';
2842
- throw new Error('@solidjs/vite-plugin: failed to load @dom-expressions/compiler, which is required ' + 'in every mode (it drives the lazy, refresh, and server-function transforms; ' + 'compiler: "babel" only switches the JSX transform). Your platform should get ' + 'a prebuilt native binary or the @dom-expressions/compiler-wasm32-wasi fallback ' + '— check that optional dependencies were installed.' + reason);
3083
+ throw new Error('@solidjs/vite-plugin: failed to load @solidjs/compiler, which is required ' + 'in every mode (it drives the lazy, refresh, and server-function transforms; ' + 'compiler: "babel" only switches the JSX transform). Your platform should get ' + 'a prebuilt native binary or the @solidjs/compiler-wasm32-wasi fallback ' + '— check that optional dependencies were installed.' + reason);
2843
3084
  }
2844
3085
  }
2845
3086
 
@@ -2916,11 +3157,12 @@ function getSolidOptions(options, isSsr, dev, isTestMode = false) {
2916
3157
  // by construction (the dom generate ignores the flag), and apps without
2917
3158
  // the flag compile byte-for-byte as before.
2918
3159
  const serverComponents = typeof options.serverFunctions === 'object' && !!options.serverFunctions.components;
3160
+
3161
+ // Solid-specific defaults (moduleName "@solidjs/web", the control-flow
3162
+ // builtIns, contextToCustomElements, wrapConditionals) are baked into both
3163
+ // backends — @solidjs/compiler and @solidjs/babel-plugin — so only the
3164
+ // posture this plugin actually decides is passed.
2919
3165
  return {
2920
- moduleName: '@solidjs/web',
2921
- builtIns: SOLID_BUILT_INS,
2922
- contextToCustomElements: true,
2923
- wrapConditionals: true,
2924
3166
  ...solidOptions,
2925
3167
  ...(serverComponents && solidOptions.generate === 'ssr' ? {
2926
3168
  serverComponents: true
@@ -3514,10 +3756,13 @@ function solidPlugin(options = {}) {
3514
3756
  }
3515
3757
 
3516
3758
  // Babel JSX backend: one babel.transformAsync hosting the user's
3517
- // options plus babel-preset-solid.
3759
+ // options plus @solidjs/babel-plugin. Appended to `plugins` (was the
3760
+ // sole preset pre-rename): user plugins still run before it, user
3761
+ // presets still run after — babel runs plugins before presets and
3762
+ // presets in reverse order, so the pass order is unchanged.
3518
3763
  const babelOptions = mergeAndConcat(babelUserOptions, {
3519
3764
  ...babelBaseOptions,
3520
- presets: [[solid, solidOptions]]
3765
+ plugins: [[solid, solidOptions]]
3521
3766
  });
3522
3767
  const result = await babel.transformAsync(code, babelOptions);
3523
3768
  if (!result) {
@@ -3559,10 +3804,17 @@ function solidPlugin(options = {}) {
3559
3804
  serverFunctions: !!options.serverFunctions,
3560
3805
  serverComponents,
3561
3806
  ssr: !!options.ssr,
3562
- styleFilter: filterDevStyles
3807
+ styleFilter: filterDevStyles,
3808
+ diagnostics: !!options.diagnostics
3563
3809
  }));
3564
3810
  }
3565
3811
 
3812
+ // Agent diagnostics endpoint + injected bridge (dev serve only — the
3813
+ // plugin no-ops itself for builds and preview via `apply`).
3814
+ if (options.diagnostics) {
3815
+ plugins.push(solidDiagnostics());
3816
+ }
3817
+
3566
3818
  // Builder-mode (environments API) client-before-server build ordering.
3567
3819
  // Server builds read the client manifest — `virtual:solid-manifest` bakes
3568
3820
  // dist/client/.vite/manifest.json in, and the persisted server-function