@solidjs/vite-plugin 3.0.0-next.32 → 3.0.0-next.34

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/README.md CHANGED
@@ -654,6 +654,19 @@ production handler alike), and edits to the module hot-invalidate the
654
654
  handler in dev. Config calls merge per key, so it composes with the
655
655
  plugin's own runtime configuration.
656
656
 
657
+ Server-function requests are same-origin protected by `@solidjs/web`. To
658
+ allow another trusted origin, configure it in the same server-only module:
659
+
660
+ ```ts
661
+ import { configureServerFunctionsServer } from '@solidjs/web/server-functions/server';
662
+
663
+ configureServerFunctionsServer({
664
+ csrf: { origin: ['https://app.example.com'] },
665
+ });
666
+ ```
667
+
668
+ Set `csrf: false` only when another trusted layer protects the endpoint.
669
+
657
670
  Meta-frameworks that need to control plugin ordering and dispatch requests
658
671
  through their own server should use the standalone `serverFunctions()`
659
672
  export instead, which never installs the dev middleware. See
@@ -680,8 +693,8 @@ your entry files instead. See `examples/start-ssr` for a complete page.
680
693
  - Default: `"native"`
681
694
 
682
695
  Choose the JSX compiler backend. The default `"native"` compiles JSX through
683
- the native compiler from `@dom-expressions/compiler`. `"babel"` runs
684
- `babel-preset-solid` instead and only switches the JSX transform — every
696
+ the native compiler from `@solidjs/compiler`. `"babel"` runs
697
+ `@solidjs/babel-plugin` instead and only switches the JSX transform — every
685
698
  other pass (the `lazy()` module-URL transform and the solid-refresh HMR
686
699
  transform) is native in both modes.
687
700
 
@@ -689,7 +702,7 @@ transform) is native in both modes.
689
702
  you expect, set `compiler: 'babel'` and file an issue — the behavioral diff
690
703
  between the two modes is the bug report. Platforms without a prebuilt native
691
704
  binary (for example StackBlitz WebContainers) automatically fall back to the
692
- `@dom-expressions/compiler-wasm32-wasi` build, so no configuration is needed
705
+ `@solidjs/compiler-wasm32-wasi` build, so no configuration is needed
693
706
  there.
694
707
 
695
708
  ```ts
@@ -710,13 +723,13 @@ Pass any additional [babel transform options](https://babeljs.io/docs/en/options
710
723
 
711
724
  #### options.solid
712
725
 
713
- - Type: [@dom-expressions/compiler](https://github.com/ryansolid/dom-expressions/tree/main/packages/compiler#options) / [@dom-expressions/babel-plugin-jsx](https://github.com/ryansolid/dom-expressions/tree/main/packages/babel-plugin-jsx#plugin-options)
726
+ - Type: [@solidjs/compiler](https://github.com/solidjs/solid/tree/main/packages/compiler) / [@solidjs/babel-plugin](https://github.com/solidjs/solid/tree/main/packages/babel-plugin)
714
727
  - Default: {}
715
728
 
716
- Pass additional DOM Expressions JSX compiler options. They will be merged with
717
- Solid's defaults (`moduleName: "@solidjs/web"`, Solid built-ins, custom-element
718
- context, and conditional wrapping) and applied to whichever compiler backend is
719
- selected.
729
+ Pass additional Solid JSX compiler options. Both backends carry the Solid
730
+ defaults (`moduleName: "@solidjs/web"`, the control-flow built-ins,
731
+ custom-element context, and conditional wrapping) internally; anything set
732
+ here is merged over them and applied to whichever backend is selected.
720
733
 
721
734
  #### options.typescript
722
735
 
@@ -765,7 +778,7 @@ plugin's errors are prefixed `[@solidjs/vite-plugin]`.
765
778
  ## Note on HMR
766
779
 
767
780
  Starting from version `1.1.0`, this plugin handles automatic HMR. The refresh
768
- transform is compiled natively by `@dom-expressions/compiler` and drives the
781
+ transform is compiled natively by `@solidjs/compiler` and drives the
769
782
  dev-only `solid-js/refresh` runtime entry that ships with Solid (the
770
783
  standalone [solid-refresh](https://github.com/solidjs/solid-refresh) package
771
784
  is no longer used).
@@ -4,7 +4,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var babel = require('@babel/core');
6
6
  var remapping = require('@ampproject/remapping');
7
- var solid = require('babel-preset-solid');
7
+ var solid = require('@solidjs/babel-plugin');
8
8
  var fs = require('fs');
9
9
  var mergeAnything = require('merge-anything');
10
10
  var module$1 = require('module');
@@ -501,6 +501,27 @@ function createDevAssetResolver(server, filter = defaultStyleFilter) {
501
501
  };
502
502
  }
503
503
 
504
+ /**
505
+ * Cross-instance-safe stand-in for vite's `isRunnableDevEnvironment`.
506
+ *
507
+ * Vite's helper is an `instanceof RunnableDevEnvironment` check against the
508
+ * class of whichever `vite` module the CALLER imported. When this plugin is
509
+ * consumed through a workspace/`link:` install, its own `vite` import can
510
+ * resolve to a different physical copy than the one running the dev server —
511
+ * and then the `instanceof` is false for every environment, silently standing
512
+ * the SSR/dev middlewares down. The `runner` accessor is the type's defining
513
+ * member (`RunnableDevEnvironment` is exactly "a DevEnvironment with a
514
+ * runner"), so presence-check it instead of trusting class identity.
515
+ */
516
+ function isRunnableEnvironment(environment) {
517
+ return !!environment && typeof environment === 'object' && 'runner' in environment;
518
+ }
519
+ function getEnvironmentConsumer(environment, options) {
520
+ const consumer = environment?.config?.consumer;
521
+ if (consumer === 'client' || consumer === 'server') return consumer;
522
+ return options?.ssr ? 'server' : 'client';
523
+ }
524
+
504
525
  const VIRTUAL_ID = '\0@solidjs/vite-plugin:boundary-modules';
505
526
 
506
527
  /**
@@ -535,7 +556,7 @@ function boundaryModules() {
535
556
  // scan all the same. Real dev/build module graphs resolve without
536
557
  // the flag and stay fully guarded.
537
558
  const scan = !!options?.scan;
538
- const server = this.environment.config.consumer === 'server';
559
+ const server = getEnvironmentConsumer(this.environment, options) === 'server';
539
560
  if (id === 'server-only') {
540
561
  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).`);
541
562
  } else if (id === 'client-only') {
@@ -552,31 +573,206 @@ function boundaryModules() {
552
573
  }
553
574
 
554
575
  /**
555
- * Cross-instance-safe stand-in for vite's `isRunnableDevEnvironment`.
576
+ * Agent diagnostics surface (`diagnostics: true`, dev serve only).
556
577
  *
557
- * Vite's helper is an `instanceof RunnableDevEnvironment` check against the
558
- * class of whichever `vite` module the CALLER imported. When this plugin is
559
- * consumed through a workspace/`link:` install, its own `vite` import can
560
- * resolve to a different physical copy than the one running the dev server —
561
- * and then the `instanceof` is false for every environment, silently standing
562
- * the SSR/dev middlewares down. The `runner` accessor is the type's defining
563
- * member (`RunnableDevEnvironment` is exactly "a DevEnvironment with a
564
- * runner"), so presence-check it instead of trusting class identity.
578
+ * Three pieces:
579
+ * - an injected client module (virtual, imported by index.html or the
580
+ * start-mode client entry) that installs the in-page bridge from the
581
+ * app's own `@solidjs/diagnostics` and answers requests over Vite's
582
+ * WebSocket custom events;
583
+ * - a collector that forwards requests to the page and correlates
584
+ * responses by id;
585
+ * - an HTTP endpoint (`/__solid/diagnostics`) fronting that round-trip so
586
+ * any out-of-process consumer (agent, MCP tool, curl) can drive capture
587
+ * sessions without holding a WebSocket.
588
+ *
589
+ * `@solidjs/diagnostics` is deliberately a type-only dependency of this
590
+ * plugin: the runtime bridge always comes from the app's own installed
591
+ * copy, so plugin releases and diagnostics releases stay uncoupled. The
592
+ * wire constants are re-declared here with types imported from the
593
+ * package, so drift fails the plugin's own compile.
565
594
  */
566
- function isRunnableEnvironment(environment) {
567
- return !!environment && typeof environment === 'object' && 'runner' in environment;
595
+ const DIAGNOSTICS_ENDPOINT = '/__solid/diagnostics';
596
+ const REQUEST_EVENT = 'solid:diagnostics:request';
597
+ const RESPONSE_EVENT = 'solid:diagnostics:response';
598
+ const DIAGNOSTICS_PACKAGE = '@solidjs/diagnostics';
599
+ const DIAGNOSTICS_CLIENT_ID = 'virtual:solid-diagnostics/client';
600
+ const METHODS = ['begin', 'end', 'active', 'whyDidRun', 'costs'];
601
+
602
+ /** How long the endpoint waits for a page to answer before failing the call. */
603
+ const RESPONSE_TIMEOUT_MS = 10_000;
604
+ function diagnosticsClientModuleCode() {
605
+ // Runtime imports resolve to the APP's diagnostics package (see the
606
+ // resolveId assist below) — the page speaks its own package's protocol.
607
+ 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');
568
608
  }
569
- function getEnvironmentConsumer(environment, options) {
570
- const consumer = environment?.config?.consumer;
571
- if (consumer === 'client' || consumer === 'server') return consumer;
572
- return options?.ssr ? 'server' : 'client';
609
+ function sendJson(res, status, body) {
610
+ res.statusCode = status;
611
+ res.setHeader('Content-Type', 'application/json');
612
+ res.end(JSON.stringify(body));
613
+ }
614
+ function readJsonBody(req) {
615
+ return new Promise((resolve, reject) => {
616
+ const chunks = [];
617
+ req.on('data', chunk => chunks.push(chunk));
618
+ req.on('end', () => {
619
+ const text = Buffer.concat(chunks).toString('utf8');
620
+ if (!text) return resolve({});
621
+ try {
622
+ resolve(JSON.parse(text));
623
+ } catch {
624
+ reject(new Error('Request body is not valid JSON'));
625
+ }
626
+ });
627
+ req.on('error', reject);
628
+ });
629
+ }
630
+ function solidDiagnostics() {
631
+ let root = process.cwd();
632
+ let base = '/';
633
+ return {
634
+ name: 'solid:diagnostics',
635
+ // Dev-serve only: the channels this fronts exist in dev builds only.
636
+ apply(_config, env) {
637
+ return env.command === 'serve' && !env.isPreview;
638
+ },
639
+ configResolved(config) {
640
+ root = config.root;
641
+ base = config.base;
642
+ },
643
+ async resolveId(source, importer) {
644
+ if (source === DIAGNOSTICS_CLIENT_ID) {
645
+ return {
646
+ id: DIAGNOSTICS_CLIENT_ID,
647
+ moduleSideEffects: true
648
+ };
649
+ }
650
+ // The virtual module has no directory to resolve bare imports from;
651
+ // resolve the app's diagnostics package from the project root.
652
+ if (importer === DIAGNOSTICS_CLIENT_ID && source.startsWith(DIAGNOSTICS_PACKAGE)) {
653
+ const resolved = await this.resolve(source, path.resolve(root, 'index.html'), {
654
+ skipSelf: true
655
+ });
656
+ if (!resolved || resolved.id.startsWith('__vite-optional-peer-dep:')) {
657
+ 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`.');
658
+ }
659
+ return resolved;
660
+ }
661
+ return null;
662
+ },
663
+ load(id) {
664
+ if (id === DIAGNOSTICS_CLIENT_ID) return diagnosticsClientModuleCode();
665
+ return null;
666
+ },
667
+ // Plain (index.html) apps get the client module injected here;
668
+ // start-mode apps import it from the generated client entry instead.
669
+ transformIndexHtml() {
670
+ return [{
671
+ tag: 'script',
672
+ attrs: {
673
+ type: 'module',
674
+ src: joinBase(base, '/@id/' + DIAGNOSTICS_CLIENT_ID)
675
+ },
676
+ injectTo: 'head'
677
+ }];
678
+ },
679
+ configureServer(server) {
680
+ const pending = new Map();
681
+ let nextId = 1;
682
+ server.ws.on(RESPONSE_EVENT, data => {
683
+ const entry = pending.get(data?.id);
684
+ if (!entry) return;
685
+ pending.delete(data.id);
686
+ clearTimeout(entry.timer);
687
+ entry.resolve(data);
688
+ });
689
+ server.middlewares.use(DIAGNOSTICS_ENDPOINT, async (req, res) => {
690
+ // The middleware mounts on the exact path; anything deeper is 404.
691
+ if (req.url && req.url !== '/' && req.url !== '') {
692
+ sendJson(res, 404, {
693
+ error: `Unknown diagnostics path ${req.url}`
694
+ });
695
+ return;
696
+ }
697
+ if (req.method === 'GET') {
698
+ sendJson(res, 200, {
699
+ ok: true,
700
+ methods: METHODS,
701
+ clients: server.ws.clients.size
702
+ });
703
+ return;
704
+ }
705
+ if (req.method !== 'POST') {
706
+ sendJson(res, 405, {
707
+ error: 'Use GET for status or POST { method, params }'
708
+ });
709
+ return;
710
+ }
711
+ let body;
712
+ try {
713
+ body = await readJsonBody(req);
714
+ } catch (error) {
715
+ sendJson(res, 400, {
716
+ error: error.message
717
+ });
718
+ return;
719
+ }
720
+ if (!body.method || !METHODS.includes(body.method)) {
721
+ sendJson(res, 400, {
722
+ error: `Unknown method ${JSON.stringify(body.method)}; expected one of: ${METHODS.join(', ')}`
723
+ });
724
+ return;
725
+ }
726
+ if (server.ws.clients.size === 0) {
727
+ sendJson(res, 503, {
728
+ error: 'No connected page. Open the app in a browser (dev server) so the ' + 'diagnostics bridge can answer.'
729
+ });
730
+ return;
731
+ }
732
+ const id = nextId++;
733
+ // Broadcast; with several open tabs the first responder wins. Good
734
+ // enough for the agent loop (one page under test); revisit with
735
+ // client targeting if multi-page capture ever matters.
736
+ const response = await new Promise(resolve => {
737
+ const timer = setTimeout(() => {
738
+ pending.delete(id);
739
+ resolve({
740
+ timeout: `No page answered within ${RESPONSE_TIMEOUT_MS}ms. The connected page ` + 'may predate `diagnostics: true` — reload it.'
741
+ });
742
+ }, RESPONSE_TIMEOUT_MS);
743
+ pending.set(id, {
744
+ resolve,
745
+ timer
746
+ });
747
+ server.ws.send(REQUEST_EVENT, {
748
+ id,
749
+ method: body.method,
750
+ params: body.params
751
+ });
752
+ });
753
+ if ('timeout' in response) {
754
+ sendJson(res, 504, {
755
+ error: response.timeout
756
+ });
757
+ } else if (response.error !== undefined) {
758
+ sendJson(res, 400, {
759
+ error: response.error
760
+ });
761
+ } else {
762
+ sendJson(res, 200, {
763
+ result: response.result
764
+ });
765
+ }
766
+ });
767
+ }
768
+ };
573
769
  }
574
770
 
575
771
  // The `"use server"` directive compiler. This wraps the native
576
- // `transformDirectives` pass from @dom-expressions/compiler (Rust/Oxc); the
772
+ // `transformDirectives` pass from @solidjs/compiler (Rust/Oxc); the
577
773
  // original Babel implementation (hoisted from solid-start) lived in this
578
774
  // directory through vite-plugin-solid@c052963e and remains the frozen
579
- // reference for the native pass's fixture suite in dom-expressions.
775
+ // reference for the native pass's fixture suite.
580
776
 
581
777
  let compilerPromise;
582
778
 
@@ -585,11 +781,11 @@ let compilerPromise;
585
781
  // compiler's opt-in loader in index.ts).
586
782
  async function loadCompiler() {
587
783
  try {
588
- return await (compilerPromise ??= import('@dom-expressions/compiler'));
784
+ return await (compilerPromise ??= import('@solidjs/compiler'));
589
785
  } catch (error) {
590
786
  compilerPromise = undefined;
591
787
  const reason = error instanceof Error ? `\n\nCause: ${error.message}` : '';
592
- 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);
788
+ 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);
593
789
  }
594
790
  }
595
791
 
@@ -1401,6 +1597,7 @@ function startServe(options, internal = {}) {
1401
1597
  const serverComponents = !!internal.serverComponents;
1402
1598
  const errorBoundary = options.errorBoundary !== false;
1403
1599
  const styleFilter = internal.styleFilter;
1600
+ const diagnostics = !!internal.diagnostics;
1404
1601
  let devtoolsEnabled = false;
1405
1602
  let devtoolsResolutions = {};
1406
1603
  let devtoolsIds = {};
@@ -1565,15 +1762,18 @@ function startServe(options, internal = {}) {
1565
1762
  const {
1566
1763
  app
1567
1764
  } = requireEntries();
1765
+ // Dev-only: the diagnostics bridge fronts dev-mode channels, so builds
1766
+ // never see this import (mirrors the plugin's own serve-only `apply`).
1767
+ const diagnosticsImport = diagnostics && !isBuild ? [`import ${JSON.stringify(DIAGNOSTICS_CLIENT_ID)};`] : [];
1568
1768
  if (clientMode) {
1569
1769
  // render(), not hydrate(): the shell's body is empty, the app mounts
1570
1770
  // fresh. Client code compiles non-hydratable in client mode, so the
1571
1771
  // app cannot claim server DOM anyway. The entry script is injected
1572
1772
  // without `async` (plain module = deferred), so document.body is
1573
1773
  // complete when this runs.
1574
- 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');
1774
+ 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');
1575
1775
  }
1576
- 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 ? [
1776
+ 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 ? [
1577
1777
  // Installs the t=0 document-adoption registry and the transport
1578
1778
  // policy (component responses morph their boundary instead of
1579
1779
  // decoding as data). Must run before hydrate().
@@ -1985,7 +2185,7 @@ function startServe(options, internal = {}) {
1985
2185
  return null;
1986
2186
  },
1987
2187
  async transform(code, id, opts) {
1988
- if (isBuild || !devtoolsEnabled) return null;
2188
+ if (isBuild || !devtoolsEnabled && !diagnostics) return null;
1989
2189
  const current = requireEntries();
1990
2190
  if (current.generated || getEnvironmentConsumer(this.environment, opts) !== 'client') {
1991
2191
  return null;
@@ -1995,12 +2195,17 @@ function startServe(options, internal = {}) {
1995
2195
  if (vite.normalizePath(id.split('?')[0]) !== vite.normalizePath(path.resolve(root, current.entryClient))) {
1996
2196
  return null;
1997
2197
  }
1998
- const toolbar = await resolveDevtools((source, importer) => this.resolve(source, importer, {
1999
- skipSelf: true
2000
- }), id, 'client');
2001
- if (!toolbar) return null;
2198
+ const injected = [];
2199
+ if (diagnostics) injected.push(`import ${JSON.stringify(DIAGNOSTICS_CLIENT_ID)};`);
2200
+ if (devtoolsEnabled) {
2201
+ const toolbar = await resolveDevtools((source, importer) => this.resolve(source, importer, {
2202
+ skipSelf: true
2203
+ }), id, 'client');
2204
+ if (toolbar) injected.push(`import ${JSON.stringify(DEVTOOLS_MOUNT_ID)};`);
2205
+ }
2206
+ if (injected.length === 0) return null;
2002
2207
  return {
2003
- code: `import ${JSON.stringify(DEVTOOLS_MOUNT_ID)};\n${code}`,
2208
+ code: `${injected.join('\n')}\n${code}`,
2004
2209
  map: null
2005
2210
  };
2006
2211
  },
@@ -2734,7 +2939,7 @@ const require$1 = module$1.createRequire((typeof document === 'undefined' ? requ
2734
2939
  * second string-literal argument of the form
2735
2940
  * `"__SOLID_LAZY_MODULE__:" + spec`, which `resolveLazyModuleUrls` swaps for
2736
2941
  * the project-relative resolved module path. The prefix and shape are FROZEN
2737
- * — the emitting side lives in @dom-expressions/compiler and must match.
2942
+ * — the emitting side lives in @solidjs/compiler and must match.
2738
2943
  */
2739
2944
  const LAZY_PLACEHOLDER_PREFIX = '__SOLID_LAZY_MODULE__:';
2740
2945
 
@@ -2852,18 +3057,17 @@ async function fetchAssets(key) {
2852
3057
  }
2853
3058
  export default (registry && registry[${JSON.stringify(root)}]) ||
2854
3059
  (bridgeUrl ? createBridgeResolver() : { resolve: jsOnly, resolveSync: jsOnly });`;
2855
- const SOLID_BUILT_INS = ['For', 'Show', 'Switch', 'Match', 'Loading', 'Reveal', 'Portal', 'Repeat', 'Dynamic', 'Errored'];
2856
3060
 
2857
3061
  /** Possible options for the extensions property */
2858
3062
 
2859
3063
  let nativeCompilerPromise;
2860
3064
  async function loadNativeCompiler() {
2861
3065
  try {
2862
- return await (nativeCompilerPromise ??= import('@dom-expressions/compiler'));
3066
+ return await (nativeCompilerPromise ??= import('@solidjs/compiler'));
2863
3067
  } catch (error) {
2864
3068
  nativeCompilerPromise = undefined;
2865
3069
  const reason = error instanceof Error ? `\n\nCause: ${error.message}` : '';
2866
- 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);
3070
+ 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);
2867
3071
  }
2868
3072
  }
2869
3073
 
@@ -2933,12 +3137,23 @@ function getSolidOptions(options, isSsr, dev, isTestMode = false) {
2933
3137
  hydratable: false
2934
3138
  };
2935
3139
  }
3140
+
3141
+ // Server components (serverFunctions.components) turn on the SSR-side
3142
+ // behavior-claims transform: ref/on* positions on intrinsic elements
3143
+ // compile to guarded `_bnd` claim holes instead of dropping. SSR-only
3144
+ // by construction (the dom generate ignores the flag), and apps without
3145
+ // the flag compile byte-for-byte as before.
3146
+ const serverComponents = typeof options.serverFunctions === 'object' && !!options.serverFunctions.components;
3147
+
3148
+ // Solid-specific defaults (moduleName "@solidjs/web", the control-flow
3149
+ // builtIns, contextToCustomElements, wrapConditionals) are baked into both
3150
+ // backends — @solidjs/compiler and @solidjs/babel-plugin — so only the
3151
+ // posture this plugin actually decides is passed.
2936
3152
  return {
2937
- moduleName: '@solidjs/web',
2938
- builtIns: SOLID_BUILT_INS,
2939
- contextToCustomElements: true,
2940
- wrapConditionals: true,
2941
3153
  ...solidOptions,
3154
+ ...(serverComponents && solidOptions.generate === 'ssr' ? {
3155
+ serverComponents: true
3156
+ } : {}),
2942
3157
  dev,
2943
3158
  ...(options.solid || {})
2944
3159
  };
@@ -3528,10 +3743,13 @@ function solidPlugin(options = {}) {
3528
3743
  }
3529
3744
 
3530
3745
  // Babel JSX backend: one babel.transformAsync hosting the user's
3531
- // options plus babel-preset-solid.
3746
+ // options plus @solidjs/babel-plugin. Appended to `plugins` (was the
3747
+ // sole preset pre-rename): user plugins still run before it, user
3748
+ // presets still run after — babel runs plugins before presets and
3749
+ // presets in reverse order, so the pass order is unchanged.
3532
3750
  const babelOptions = mergeAnything.mergeAndConcat(babelUserOptions, {
3533
3751
  ...babelBaseOptions,
3534
- presets: [[solid, solidOptions]]
3752
+ plugins: [[solid, solidOptions]]
3535
3753
  });
3536
3754
  const result = await babel__namespace.transformAsync(code, babelOptions);
3537
3755
  if (!result) {
@@ -3573,10 +3791,17 @@ function solidPlugin(options = {}) {
3573
3791
  serverFunctions: !!options.serverFunctions,
3574
3792
  serverComponents,
3575
3793
  ssr: !!options.ssr,
3576
- styleFilter: filterDevStyles
3794
+ styleFilter: filterDevStyles,
3795
+ diagnostics: !!options.diagnostics
3577
3796
  }));
3578
3797
  }
3579
3798
 
3799
+ // Agent diagnostics endpoint + injected bridge (dev serve only — the
3800
+ // plugin no-ops itself for builds and preview via `apply`).
3801
+ if (options.diagnostics) {
3802
+ plugins.push(solidDiagnostics());
3803
+ }
3804
+
3580
3805
  // Builder-mode (environments API) client-before-server build ordering.
3581
3806
  // Server builds read the client manifest — `virtual:solid-manifest` bakes
3582
3807
  // dist/client/.vite/manifest.json in, and the persisted server-function