@solidjs/start 2.0.0-rc.1 → 2.0.0-rc.10

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.
Files changed (118) hide show
  1. package/CHANGELOG.md +123 -0
  2. package/dist/config/app-root-alias.d.ts +7 -0
  3. package/dist/config/app-root-alias.js +58 -0
  4. package/dist/config/constants.d.ts +2 -0
  5. package/dist/config/constants.js +2 -0
  6. package/dist/config/env.js +2 -2
  7. package/dist/config/fs-routes/index.js +3 -3
  8. package/dist/config/fs-routes/tree-shake.d.ts +27 -0
  9. package/dist/config/fs-routes/tree-shake.js +51 -1
  10. package/dist/config/index.d.ts +154 -5
  11. package/dist/config/index.js +16 -8
  12. package/dist/config/lazy.js +7 -2
  13. package/dist/config/manifest.d.ts +3 -0
  14. package/dist/config/manifest.js +26 -4
  15. package/dist/directives/remove-unused-variables.js +15 -3
  16. package/dist/directives/xxhash32.js +5 -13
  17. package/dist/fns/client.js +14 -5
  18. package/dist/fns/error-handler.d.ts +34 -0
  19. package/dist/fns/error-handler.js +10 -0
  20. package/dist/fns/handler.d.ts +1 -1
  21. package/dist/fns/handler.js +45 -10
  22. package/dist/fns/plugins.d.ts +34 -0
  23. package/dist/fns/plugins.js +33 -0
  24. package/dist/fns/serialization.d.ts +15 -0
  25. package/dist/fns/serialization.js +36 -6
  26. package/dist/server/assets/render.jsx +1 -1
  27. package/dist/server/collect-styles.d.ts +3 -1
  28. package/dist/server/collect-styles.js +8 -5
  29. package/dist/server/handler.d.ts +3 -4
  30. package/dist/server/handler.js +16 -46
  31. package/dist/server/index.d.ts +2 -1
  32. package/dist/server/manifest/dev-client-manifest.js +1 -1
  33. package/dist/server/manifest/dev-ssr-manifest.js +1 -1
  34. package/dist/server/spa/StartServer.jsx +1 -1
  35. package/dist/server/spa/handler.d.ts +2 -3
  36. package/dist/server/spa/handler.js +2 -2
  37. package/dist/server/types.d.ts +12 -1
  38. package/dist/server/web-stream.d.ts +1 -1
  39. package/dist/server/web-stream.js +5 -2
  40. package/dist/shared/ErrorBoundary.jsx +3 -3
  41. package/dist/shared/clientOnly.js +3 -1
  42. package/dist/shared/{dev-overlay → dev-toolbar/error-viewer}/CodeView.jsx +3 -3
  43. package/dist/shared/{dev-overlay/DevOverlayDialog.d.ts → dev-toolbar/error-viewer/index.d.ts} +3 -2
  44. package/dist/shared/{dev-overlay/DevOverlayDialog.jsx → dev-toolbar/error-viewer/index.jsx} +66 -77
  45. package/dist/shared/dev-toolbar/error-viewer/styles.css +224 -0
  46. package/dist/shared/dev-toolbar/functions/BlobViewer.css +4 -0
  47. package/dist/shared/dev-toolbar/functions/BlobViewer.d.ts +6 -0
  48. package/dist/shared/dev-toolbar/functions/BlobViewer.jsx +37 -0
  49. package/dist/shared/dev-toolbar/functions/FormDataViewer.d.ts +5 -0
  50. package/dist/shared/dev-toolbar/functions/FormDataViewer.jsx +23 -0
  51. package/dist/shared/dev-toolbar/functions/HeadersViewer.css +8 -0
  52. package/dist/shared/dev-toolbar/functions/HeadersViewer.d.ts +6 -0
  53. package/dist/shared/dev-toolbar/functions/HeadersViewer.jsx +15 -0
  54. package/dist/shared/dev-toolbar/functions/HexViewer.css +34 -0
  55. package/dist/shared/dev-toolbar/functions/HexViewer.d.ts +11 -0
  56. package/dist/shared/dev-toolbar/functions/HexViewer.jsx +70 -0
  57. package/dist/shared/dev-toolbar/functions/SerovalValue.css +5 -0
  58. package/dist/shared/dev-toolbar/functions/SerovalValue.d.ts +7 -0
  59. package/dist/shared/dev-toolbar/functions/SerovalValue.jsx +10 -0
  60. package/dist/shared/dev-toolbar/functions/SerovalViewer.css +70 -0
  61. package/dist/shared/dev-toolbar/functions/SerovalViewer.d.ts +6 -0
  62. package/dist/shared/dev-toolbar/functions/SerovalViewer.jsx +796 -0
  63. package/dist/shared/dev-toolbar/functions/URLSearchParamsViewer.d.ts +5 -0
  64. package/dist/shared/dev-toolbar/functions/URLSearchParamsViewer.jsx +22 -0
  65. package/dist/shared/dev-toolbar/functions/index.d.ts +13 -0
  66. package/dist/shared/dev-toolbar/functions/index.jsx +216 -0
  67. package/dist/shared/dev-toolbar/functions/styles.css +109 -0
  68. package/dist/shared/dev-toolbar/functions/tracker.d.ts +19 -0
  69. package/dist/shared/dev-toolbar/functions/tracker.js +29 -0
  70. package/dist/shared/{dev-overlay → dev-toolbar}/icons.d.ts +9 -0
  71. package/dist/shared/{dev-overlay → dev-toolbar}/icons.jsx +19 -1
  72. package/dist/shared/dev-toolbar/index.css +88 -0
  73. package/dist/shared/dev-toolbar/index.d.ts +6 -0
  74. package/dist/shared/dev-toolbar/index.jsx +179 -0
  75. package/dist/shared/ui/Badge.css +33 -0
  76. package/dist/shared/ui/Badge.d.ts +7 -0
  77. package/dist/shared/ui/Badge.jsx +7 -0
  78. package/dist/shared/ui/Button.css +32 -0
  79. package/dist/shared/ui/Button.d.ts +4 -0
  80. package/dist/shared/ui/Button.jsx +4 -0
  81. package/dist/shared/ui/Cascade.css +29 -0
  82. package/dist/shared/ui/Cascade.d.ts +4 -0
  83. package/dist/shared/ui/Cascade.jsx +4 -0
  84. package/dist/shared/ui/Dialog.css +25 -0
  85. package/dist/shared/ui/Dialog.d.ts +5 -0
  86. package/dist/shared/ui/Dialog.jsx +5 -0
  87. package/dist/shared/ui/IconButton.css +39 -0
  88. package/dist/shared/ui/IconButton.d.ts +4 -0
  89. package/dist/shared/ui/IconButton.jsx +4 -0
  90. package/dist/shared/ui/Placeholder.css +12 -0
  91. package/dist/shared/ui/Placeholder.d.ts +6 -0
  92. package/dist/shared/ui/Placeholder.jsx +4 -0
  93. package/dist/shared/ui/Section.css +18 -0
  94. package/dist/shared/ui/Section.d.ts +9 -0
  95. package/dist/shared/ui/Section.jsx +10 -0
  96. package/dist/shared/ui/Select.css +37 -0
  97. package/dist/shared/ui/Select.d.ts +4 -0
  98. package/dist/shared/ui/Select.jsx +4 -0
  99. package/dist/shared/ui/Table.css +22 -0
  100. package/dist/shared/ui/Table.d.ts +11 -0
  101. package/dist/shared/ui/Table.jsx +13 -0
  102. package/dist/shared/ui/Tabs.css +51 -0
  103. package/dist/shared/ui/Tabs.d.ts +6 -0
  104. package/dist/shared/ui/Tabs.jsx +6 -0
  105. package/dist/shared/ui/Text.css +73 -0
  106. package/dist/shared/ui/Text.d.ts +12 -0
  107. package/dist/shared/ui/Text.jsx +17 -0
  108. package/package.json +36 -29
  109. package/dist/shared/dev-overlay/index.d.ts +0 -5
  110. package/dist/shared/dev-overlay/index.jsx +0 -39
  111. package/dist/shared/dev-overlay/styles.css +0 -292
  112. /package/dist/shared/{dev-overlay → dev-toolbar/error-viewer}/CodeView.d.ts +0 -0
  113. /package/dist/shared/{dev-overlay/createStackFrame.d.ts → dev-toolbar/error-viewer/create-stack-frame.d.ts} +0 -0
  114. /package/dist/shared/{dev-overlay/createStackFrame.js → dev-toolbar/error-viewer/create-stack-frame.js} +0 -0
  115. /package/dist/shared/{dev-overlay → dev-toolbar/error-viewer}/download.d.ts +0 -0
  116. /package/dist/shared/{dev-overlay → dev-toolbar/error-viewer}/download.js +0 -0
  117. /package/dist/shared/{dev-overlay → dev-toolbar/error-viewer}/get-source-map.d.ts +0 -0
  118. /package/dist/shared/{dev-overlay → dev-toolbar/error-viewer}/get-source-map.js +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,128 @@
1
1
  # @solidjs/start
2
2
 
3
+ ## 2.0.0-rc.10
4
+
5
+ ### Minor Changes
6
+
7
+ - 3f2b7a7: The file filter logic used for CSS crawling in development can now be configured with the vite plugin option `css.filter` analog to `serverFunctions.filter`:
8
+
9
+ ```ts
10
+ solidStart({
11
+ css: {
12
+ filter: {
13
+ // Exclude all node_modules except "my-dependency" with a flat node_modules layout
14
+ exclude: "node_modules/!(my-dependency)/**/*",
15
+ },
16
+ },
17
+ });
18
+ ```
19
+
20
+ ### Patch Changes
21
+
22
+ - 6581877: Fixed shared chunk css not being server rendered in production (Vite 8 regression).
23
+ - 37d4488: Migrate the built-in Vite configuration from the deprecated `rollupOptions` alias to `rolldownOptions`.
24
+ - 3f2b7a7: Fixed css from files with url sensitive characters such as `+` not being server-rendered.
25
+
26
+ ## 2.0.0-rc.9
27
+
28
+ ### Patch Changes
29
+
30
+ - 8eef552: Remove type-only import remnants from client server-function transforms so they do not retain server-only dependency chains.
31
+
32
+ ## 2.0.0-rc.8
33
+
34
+ ### Patch Changes
35
+
36
+ - 3573985: Await asynchronous `serverFunctions.onError` handlers before serializing server function errors.
37
+ Preserve the original error if the handler throws or rejects.
38
+
39
+ ## 2.0.0-rc.7
40
+
41
+ ### Patch Changes
42
+
43
+ - 87d73e1: chore: bump seroval to v1.6.0
44
+
45
+ ## 2.0.0-rc.6
46
+
47
+ ### Minor Changes
48
+
49
+ - bac24b2: Add a `serverFunctions.onError` option naming a module that observes and replaces what a server function threw, before it is serialized into the response
50
+
51
+ ### Patch Changes
52
+
53
+ - 27c2877: Remove leftover debug `console.log` calls from the server functions inspector, which logged on every server function request in dev.
54
+ - f15724b: Declare `@solidjs/router` as an optional peer dependency constrained to `>=0.16.0 <2.0.0-0`. Router v2 is expected to target Solid v2, so installing it alongside `@solidjs/start` v2 now surfaces a peer warning instead of silently producing an incompatible pairing. The peer is marked optional, so apps that do not use the router are unaffected.
55
+ - 5c8612f: Apply cookies set on a returned or thrown response during single flight mutations. `redirect(to, { headers: { "Set-Cookie": ... } })` previously only reached the browser: the single flight re-render of the redirect target still ran with the old request cookies, so queries reading that cookie saw stale values. Those cookies are now merged into the request the re-render sees, matching what a browser round trip would have sent.
56
+ - 83122ed: Reject server function calls when the response is a 5xx without an X-Error header, instead of resolving with the parsed error body
57
+
58
+ ## 2.0.0-rc.5
59
+
60
+ ### Minor Changes
61
+
62
+ - 4c803e5: Add `serialization.plugins` to configure custom Seroval plugins for server functions.
63
+
64
+ Values Seroval has no built-in support for (Mongo's `ObjectId`, Prisma's `Decimal`, `Temporal`, and other custom classes) previously threw when returned from or passed to a server function. Point the new option at a module whose default export is an array of plugins:
65
+
66
+ ```ts
67
+ // vite.config.ts
68
+ solidStart({
69
+ serialization: {
70
+ plugins: "src/seroval-plugins.ts",
71
+ },
72
+ });
73
+ ```
74
+
75
+ ```ts
76
+ // src/seroval-plugins.ts
77
+ import { createPlugin } from "@solidjs/start/serialization";
78
+ ```
79
+
80
+ The module is bundled into both the client and the server so both ends of a server function agree on the format, so it must not import server-only code. SolidStart's built-in plugins keep precedence. Only server-function and action payloads are affected; the SSR hydration payload is serialized by `solid-js/web`.
81
+
82
+ Also adds a `@solidjs/start/serialization` entrypoint re-exporting Seroval's `createPlugin`, `OpaqueReference`, and plugin types, so plugin authors stay on the same Seroval version SolidStart serializes with.
83
+
84
+ ### Patch Changes
85
+
86
+ - e117d91: Route module ids now end in the source extension, so ecosystem plugins apply inside `src/routes`.
87
+
88
+ Route files are imported through an id carrying the picked exports in the query (`routes/api.ts?pick=GET`), which left the id ending in the export name. Plugins whose filter is anchored on the file extension (`/\.[cm]?[jt]sx?$/`, the default for `unplugin-auto-import`, `unplugin-macros` and others) silently skipped every route file. The id now ends with a `lang.<ext>` marker, the same convention Vue SFCs use for `?vue&type=script&lang.ts`. Chunk filenames are unchanged.
89
+
90
+ - d8f1ea8: Apply the configured `nonce` to the two script tags that were still missing it, so a strict `script-src` CSP no longer needs `unsafe-inline`:
91
+ - The client-side redirect that streaming mode emits after the shell has already flushed (`<script>window.location=...</script>`) now carries the nonce.
92
+ - The SPA entry script tag now carries the nonce, matching the SSR entry script.
93
+
94
+ - 27fca88: Fix actions returning `json()` or `reload()` leaving no-JS form submissions stranded on the `/_server` endpoint. These responses carry a value rather than a destination, so the redirect issued for progressive-enhancement submissions had no `Location` header. It now falls back to the submitting page, and the response value is unwrapped into the flash cookie so `useSubmission().result` matches the JS path.
95
+ - 75debc3: Scope the built-in `~` alias to the app package, so files in other workspace packages can map `~` to their own root through an importer-aware plugin such as `vite-tsconfig-paths`. In stylesheets and asset URLs (CSS `@import`, `url()`, `new URL(..., import.meta.url)`) `~` still always means the app root, since Vite resolves those without running plugins.
96
+
97
+ ## 2.0.0-rc.4
98
+
99
+ ### Patch Changes
100
+
101
+ - b6dfaac: Updated dependencies, including `cookie-es` to 3, `shiki` to 4, `srvx` to 0.12.4, `oxc-parser` to 0.141 and `seroval` to 1.5.6. `parseSetCookie` now returns `undefined` for cookies with a forbidden name or an empty name and value, and those cookies are no longer forwarded to nested server function requests.
102
+ - 02cd41e: Stop the dev toolbar from reporting benign `ResizeObserver loop` notifications as application errors. Browsers dispatch these as window `error` events carrying no error object, so the toolbar was capturing the raw `ErrorEvent` and force-opening the error panel over the app on every resize.
103
+ - d3c2af2: Don't send server error stack traces to the client in production builds. When a server function throws, the error is serialized and rethrown on the client, and seroval included `Error.prototype.stack` by default, leaking server file paths and internal function names. Stacks are still serialized in development.
104
+
105
+ ## 2.0.0-rc.3
106
+
107
+ ### Patch Changes
108
+
109
+ - d9018d6: fix(types): add missing properties to `SolidStartOptions` and expose them via Vite plugin configuration
110
+ - e26cef0: Fix TS2883/TS2742 when emitting declarations for `entry-server.tsx`. `createHandler` now returns `StartHandler`, a type owned by `@solidjs/start`, instead of h3's `H3`, so the inferred type of `export default createHandler(...)` no longer has to be named through a nested `node_modules/@solidjs/start/node_modules/h3` path.
111
+ - 7c8dbe6: keep TypeScript namespace members in route files during production builds
112
+ - b3c7aaf: Update `h3` to `2.0.1-rc.26`.
113
+ - b3c7aaf: Fixed event response status and headers set during server-side rendering with deferred async resources, not being applied on the outgoing response.
114
+
115
+ ## 2.0.0-rc.2
116
+
117
+ ### Minor Changes
118
+
119
+ - eeff49a: add new dev toolbar
120
+
121
+ ### Patch Changes
122
+
123
+ - bd8cb01: Update srvx to version 0.12.0.
124
+ - 4e98ac0: Restore the optional `routerLoad` third argument to `createHandler`, which primes custom routers (e.g. TanStack Router) on the server before SSR rendering. It was accidentally dropped in the v2 rewrite.
125
+
3
126
  ## 2.0.0-rc.1
4
127
 
5
128
  ### Minor Changes
@@ -0,0 +1,7 @@
1
+ import { type Plugin } from "vite";
2
+ /**
3
+ * Provides SolidStart's `~` app-root alias without claiming module imports made
4
+ * by other workspace packages. Those packages may map `~` to their own root
5
+ * through an importer-aware resolver such as vite-tsconfig-paths.
6
+ */
7
+ export declare function appRootAlias(projectRoot: string, appRoot: string): Plugin;
@@ -0,0 +1,58 @@
1
+ import { existsSync } from "node:fs";
2
+ import { dirname, isAbsolute, join, resolve } from "node:path";
3
+ import { normalizePath } from "vite";
4
+ /**
5
+ * `~/app.css`, `~/logo.svg`, ... but not `~/lib/api.ts` or `~/components/Counter`.
6
+ *
7
+ * CSS `@import`, `url()`, preprocessor imports and `new URL(..., import.meta.url)`
8
+ * are resolved by internal Vite resolvers that only run the alias plugin, never
9
+ * user plugins, so those ids can never reach `resolveId` below. Keeping non-module
10
+ * ids on a plain alias preserves `~` in stylesheets and asset URLs, where it always
11
+ * means the app root.
12
+ */
13
+ const NON_MODULE_ID = /^~\/([^?#]*\.(?![cm]?[jt]sx?(?:[?#]|$))[^./?#]+(?:[?#].*)?)$/;
14
+ /**
15
+ * Provides SolidStart's `~` app-root alias without claiming module imports made
16
+ * by other workspace packages. Those packages may map `~` to their own root
17
+ * through an importer-aware resolver such as vite-tsconfig-paths.
18
+ */
19
+ export function appRootAlias(projectRoot, appRoot) {
20
+ const appDir = normalizePath(resolve(projectRoot, appRoot));
21
+ const packageRoots = new Map();
22
+ /** Nearest directory at or above `directory` that holds a `package.json`. */
23
+ function packageRoot(directory) {
24
+ if (!packageRoots.has(directory)) {
25
+ const parent = dirname(directory);
26
+ packageRoots.set(directory, existsSync(join(directory, "package.json"))
27
+ ? directory
28
+ : parent === directory
29
+ ? undefined
30
+ : packageRoot(parent));
31
+ }
32
+ return packageRoots.get(directory);
33
+ }
34
+ const appPackage = packageRoot(appDir);
35
+ /** True only when the importer demonstrably belongs to another package. */
36
+ function isForeignImporter(importer) {
37
+ const file = normalizePath(importer.replace(/[?#].*$/s, ""));
38
+ if (!isAbsolute(file))
39
+ return false;
40
+ const owner = packageRoot(dirname(file));
41
+ return owner !== undefined && owner !== appPackage;
42
+ }
43
+ return {
44
+ name: "solid-start:app-root-alias",
45
+ enforce: "pre",
46
+ config() {
47
+ return { resolve: { alias: [{ find: NON_MODULE_ID, replacement: `${appDir}/$1` }] } };
48
+ },
49
+ async resolveId(id, importer, options) {
50
+ if (id !== "~" && !id.startsWith("~/"))
51
+ return null;
52
+ if (importer && isForeignImporter(importer))
53
+ return null;
54
+ const target = join(appDir, id.slice(1));
55
+ return (await this.resolve(target, importer, { ...options, skipSelf: true })) ?? target;
56
+ },
57
+ };
58
+ }
@@ -5,7 +5,9 @@ export declare const VIRTUAL_MODULES: {
5
5
  readonly getClientManifest: "solid-start:get-client-manifest";
6
6
  readonly getManifest: "solid-start:get-manifest";
7
7
  readonly middleware: "solid-start:middleware";
8
+ readonly serovalPlugins: "solid-start:seroval-plugins";
8
9
  readonly serverFnManifest: "solid-start:server-fn-manifest";
10
+ readonly serverFnErrorHandler: "solid-start:server-fn-error-handler";
9
11
  readonly clientEntry: "solid-start:client-entry";
10
12
  readonly serverEntry: "solid-start:server-entry";
11
13
  readonly app: "solid-start:app";
@@ -5,7 +5,9 @@ export const VIRTUAL_MODULES = {
5
5
  getClientManifest: "solid-start:get-client-manifest",
6
6
  getManifest: "solid-start:get-manifest",
7
7
  middleware: "solid-start:middleware",
8
+ serovalPlugins: "solid-start:seroval-plugins",
8
9
  serverFnManifest: "solid-start:server-fn-manifest",
10
+ serverFnErrorHandler: "solid-start:server-fn-error-handler",
9
11
  clientEntry: "solid-start:client-entry",
10
12
  serverEntry: "solid-start:server-entry",
11
13
  app: "solid-start:app",
@@ -58,13 +58,13 @@ export function envPlugin(options) {
58
58
  }
59
59
  const vars = currentOptions.server?.load
60
60
  ? currentOptions.server.load(env)
61
- : loadEnv(env, '.', serverPrefix);
61
+ : loadEnv(env, ".", serverPrefix);
62
62
  return convertObjectToModule(vars);
63
63
  }
64
64
  if (filename === CLIENT_ENV) {
65
65
  const vars = currentOptions.client?.load
66
66
  ? currentOptions.client.load(env)
67
- : loadEnv(env, '.', clientPrefix);
67
+ : loadEnv(env, ".", clientPrefix);
68
68
  return convertObjectToModule(vars);
69
69
  }
70
70
  if (filename === SERVER_RUNTIME_LOADER) {
@@ -1,6 +1,6 @@
1
1
  import { relative } from "node:path";
2
2
  import { fileSystemWatcher } from "./fs-watcher.js";
3
- import { treeShake } from "./tree-shake.js";
3
+ import { toPickId, treeShake } from "./tree-shake.js";
4
4
  export const moduleId = "solid-start:routes";
5
5
  export function fsRoutes({ routers }) {
6
6
  globalThis.ROUTERS = routers;
@@ -24,7 +24,7 @@ export function fsRoutes({ routers }) {
24
24
  if (v === undefined)
25
25
  return undefined;
26
26
  if (k.startsWith("$$")) {
27
- const buildId = `${v.src}?${v.pick.map((p) => `pick=${p}`).join("&")}`;
27
+ const buildId = toPickId(v.src, v.pick);
28
28
  /**
29
29
  * @type {{ [key: string]: string }}
30
30
  */
@@ -40,7 +40,7 @@ export function fsRoutes({ routers }) {
40
40
  };
41
41
  }
42
42
  else if (k.startsWith("$")) {
43
- const buildId = `${v.src}?${v.pick.map((p) => `pick=${p}`).join("&")}`;
43
+ const buildId = toPickId(v.src, v.pick);
44
44
  return {
45
45
  src: relative(root, buildId),
46
46
  build: isBuild ? `_$() => import('${buildId}')$_` : undefined,
@@ -1,2 +1,29 @@
1
1
  import type { Plugin } from "vite";
2
+ /**
3
+ * Builds the module id used to import a subset of a route file's exports.
4
+ *
5
+ * The `pick` list has to live in the query so that the same file can be
6
+ * instantiated once per export subset (the client picks `default`/`$css`,
7
+ * the server picks its HTTP handlers). That query would otherwise leave the
8
+ * id ending in `?pick=GET`, which silently excludes route files from any
9
+ * plugin whose filter is anchored on the file extension -- a very common
10
+ * default, e.g. unplugin-macros' `/\.[cm]?[jt]sx?$/`.
11
+ *
12
+ * The trailing `lang.<ext>` marker is the same convention Vue SFCs use
13
+ * (`?vue&type=script&lang.ts`) and puts a real extension back at the end of
14
+ * the id, so those filters match again.
15
+ *
16
+ * @see https://github.com/solidjs/solid-start/issues/1918
17
+ */
18
+ export declare function toPickId(src: string, pick: string[]): string;
19
+ /**
20
+ * Rolldown's default chunk name sanitizer, plus a pass that drops the
21
+ * `?pick=...&lang` tail so route chunks keep the short, stable filenames they
22
+ * had before the `lang` marker was introduced (`index-<hash>.js`, not
23
+ * `index.tsx_pick_default_pick__css_lang-<hash>.js`). Client chunk filenames
24
+ * are public URLs, so this is not purely cosmetic.
25
+ *
26
+ * @see https://github.com/rollup/rollup/blob/master/src/utils/sanitizeFileName.ts
27
+ */
28
+ export declare function sanitizeChunkFileName(name: string): string;
2
29
  export declare function treeShake(): Plugin;
@@ -1,7 +1,52 @@
1
1
  // All credit for this work goes to the amazing Next.js team.
2
2
  // https://github.com/vercel/next.js/blob/canary/packages/next/build/babel/plugins/next-ssg-transform.ts
3
3
  // This is adapted to work with routeData functions. It can be run in two modes, one which preserves the routeData and the Component in the same file, and one which creates a
4
- import { basename } from "pathe";
4
+ import { basename, extname } from "pathe";
5
+ const PICKABLE_EXTENSIONS = ["js", "jsx", "ts", "tsx"];
6
+ /**
7
+ * Builds the module id used to import a subset of a route file's exports.
8
+ *
9
+ * The `pick` list has to live in the query so that the same file can be
10
+ * instantiated once per export subset (the client picks `default`/`$css`,
11
+ * the server picks its HTTP handlers). That query would otherwise leave the
12
+ * id ending in `?pick=GET`, which silently excludes route files from any
13
+ * plugin whose filter is anchored on the file extension -- a very common
14
+ * default, e.g. unplugin-macros' `/\.[cm]?[jt]sx?$/`.
15
+ *
16
+ * The trailing `lang.<ext>` marker is the same convention Vue SFCs use
17
+ * (`?vue&type=script&lang.ts`) and puts a real extension back at the end of
18
+ * the id, so those filters match again.
19
+ *
20
+ * @see https://github.com/solidjs/solid-start/issues/1918
21
+ */
22
+ export function toPickId(src, pick) {
23
+ const query = pick.map(p => `pick=${p}`).join("&");
24
+ const ext = extname(src).slice(1);
25
+ return PICKABLE_EXTENSIONS.includes(ext) ? `${src}?${query}&lang.${ext}` : `${src}?${query}`;
26
+ }
27
+ /**
28
+ * Matches the tail that {@link toPickId} appends, as it appears in a chunk
29
+ * name. Rolldown derives a chunk name from the module id by taking the
30
+ * basename minus its extension, so `index.tsx?pick=default&pick=$css&lang.tsx`
31
+ * arrives here as `index.tsx?pick=default&pick=$css&lang`.
32
+ */
33
+ const PICK_CHUNK_NAME_RE = /\.[cm]?[jt]sx?\?pick=[^?]*&lang$/;
34
+ const INVALID_CHAR_RE = /[\u0000-\u001F"#$&*+,:;<=>?[\]^`{|}\u007F]/g;
35
+ const DRIVE_LETTER_RE = /^[a-z]:/i;
36
+ /**
37
+ * Rolldown's default chunk name sanitizer, plus a pass that drops the
38
+ * `?pick=...&lang` tail so route chunks keep the short, stable filenames they
39
+ * had before the `lang` marker was introduced (`index-<hash>.js`, not
40
+ * `index.tsx_pick_default_pick__css_lang-<hash>.js`). Client chunk filenames
41
+ * are public URLs, so this is not purely cosmetic.
42
+ *
43
+ * @see https://github.com/rollup/rollup/blob/master/src/utils/sanitizeFileName.ts
44
+ */
45
+ export function sanitizeChunkFileName(name) {
46
+ const stripped = name.replace(PICK_CHUNK_NAME_RE, "");
47
+ const driveLetter = DRIVE_LETTER_RE.exec(stripped)?.[0] ?? "";
48
+ return driveLetter + stripped.slice(driveLetter.length).replace(INVALID_CHAR_RE, "_");
49
+ }
5
50
  function treeShakeTransform({ types: t }) {
6
51
  function getIdentifier(path) {
7
52
  const parentPath = path.parentPath;
@@ -133,6 +178,11 @@ function treeShakeTransform({ types: t }) {
133
178
  if (!state.opts.pick) {
134
179
  return;
135
180
  }
181
+ // Only module-level exports are route exports; `export` inside a
182
+ // TS namespace/module block is unrelated to the pick list.
183
+ if (!exportNamedPath.parentPath.isProgram()) {
184
+ return;
185
+ }
136
186
  const specifiers = exportNamedPath.get("specifiers");
137
187
  if (specifiers.length) {
138
188
  specifiers.forEach(s => {
@@ -1,25 +1,174 @@
1
- import type { PluginOption } from "vite";
1
+ import type { PluginOption, FilterPattern } from "vite";
2
2
  import { type Options as SolidOptions } from "vite-plugin-solid";
3
3
  import { type ServerFunctionsOptions } from "../directives/index.ts";
4
4
  import { type EnvPluginOptions } from "./env.ts";
5
+ /**
6
+ * Configuration options for SolidStart. (previously in `app.config.ts`)
7
+ *
8
+ * @see https://docs.solidjs.com/solid-start/v2/migrating-from-v1#move-framework-configuration-into-viteconfigts
9
+ */
5
10
  export interface SolidStartOptions {
11
+ /**
12
+ * Path to the root of the application (where `app.tsx` / `app.jsx` lives).
13
+ *
14
+ * @default "./src"
15
+ */
16
+ appRoot?: string;
17
+ /**
18
+ * Options related to the css crawling logic
19
+ */
20
+ css?: {
21
+ /**
22
+ * Filter files included during css crawling in development.
23
+ */
24
+ filter?: {
25
+ include?: FilterPattern;
26
+ exclude?: FilterPattern;
27
+ };
28
+ };
29
+ /**
30
+ * Options forwarded to `vite-plugin-solid`.
31
+ *
32
+ * @see https://github.com/solidjs/vite-plugin-solid#api
33
+ */
6
34
  solid?: Partial<SolidOptions>;
35
+ /**
36
+ * Enable or disable server-side rendering.
37
+ *
38
+ * - `true` — SSR (default)
39
+ * - `false` — client-side rendering only (SPA mode)
40
+ *
41
+ * @default true
42
+ */
7
43
  ssr?: boolean;
44
+ /**
45
+ * Show the SolidStart development overlay (error overlay, etc.) in development.
46
+ *
47
+ * @default true
48
+ */
49
+ devOverlay?: boolean;
50
+ /**
51
+ * Experimental features.
52
+ */
53
+ experimental?: {
54
+ /**
55
+ * Enable islands architecture mode.
56
+ *
57
+ * Currently fixed to `false` (not yet fully supported).
58
+ *
59
+ * @default false
60
+ */
61
+ islands?: false;
62
+ };
63
+ /**
64
+ * Directory containing file-system routes, relative to {@link appRoot}.
65
+ *
66
+ * @default "./routes"
67
+ */
8
68
  routeDir?: string;
69
+ /**
70
+ * File extensions that should be treated as routes.
71
+ *
72
+ * @default ["js", "jsx", "ts", "tsx"]
73
+ */
9
74
  extensions?: string[];
75
+ /**
76
+ * Path to an optional middleware module.
77
+ *
78
+ * The module should export a middleware created with `createMiddleware`
79
+ * from `@solidjs/start/middleware`.
80
+ *
81
+ * @example "src/middleware/index.ts"
82
+ */
10
83
  middleware?: string;
84
+ /**
85
+ * Serialization settings for server-function / action payloads
86
+ * that cross the server-client boundary.
87
+ */
11
88
  serialization?: {
12
89
  /**
13
90
  * The serialization mode to use for server functions/actions.
14
- * The "js" mode uses a custom binary format that is more efficient than JSON, but requires a custom deserializer (with `eval()`) on the client.
15
- * A strong CSP should block `eval()` executions, which would prevent the "js" mode from working.
16
- * The "json" mode uses JSON for serialization, which is less efficient but can be deserialized with `JSON.parse` on the client.
91
+ *
92
+ * - `"js"` — Uses a custom binary format (Seroval) that is more efficient
93
+ * than JSON, but requires a custom deserializer (with `eval()`) on the client.
94
+ * A strong CSP that blocks `eval()` will prevent this mode from working.
95
+ * - `"json"` — Uses JSON for serialization. Less efficient / larger payloads,
96
+ * but can be deserialized with `JSON.parse` on the client and is CSP-friendly.
17
97
  *
18
98
  * @default "json"
19
99
  */
20
100
  mode?: "js" | "json";
101
+ /**
102
+ * Path to a module whose default export is an array of custom Seroval
103
+ * plugins, used to serialize values Seroval doesn't understand natively
104
+ * (ORM id types, decimals, `Temporal`, and other custom classes).
105
+ *
106
+ * Build plugins with `createPlugin` from `seroval`. The module is bundled
107
+ * into both the client and the server so that both ends of a server
108
+ * function agree on the format, so it must not import server-only code.
109
+ *
110
+ * SolidStart's built-in plugins take precedence: Seroval uses the first
111
+ * plugin whose `test()` passes, and these are appended after the built-ins.
112
+ *
113
+ * A plugin's `deserialize` rebuilds the value under {@link mode} `"json"`.
114
+ * `serialize` is only used by `mode: "js"`, where the payload is evaluated
115
+ * on the client and may therefore reference globals only, not the plugin
116
+ * module's own imports.
117
+ *
118
+ * Only applies to server-function and action payloads. The SSR hydration
119
+ * payload is serialized by `solid-js/web` and is unaffected.
120
+ *
121
+ * @example "src/seroval-plugins.ts"
122
+ */
123
+ plugins?: string;
21
124
  };
125
+ /**
126
+ * Configures plugin behavior per build environment
127
+ */
22
128
  env?: EnvPluginOptions;
23
- serverFunctions?: Pick<ServerFunctionsOptions, "filter">;
129
+ /**
130
+ * Options for server functions: which files the `"use server"` transform
131
+ * processes (inclusion / exclusion filters), and what happens when a server
132
+ * function throws.
133
+ */
134
+ serverFunctions?: Pick<ServerFunctionsOptions, "filter"> & {
135
+ /**
136
+ * Path to a module whose default export handles whatever a server function
137
+ * throws, before it reaches the client. Use it to report failures to a
138
+ * monitoring service, or to replace an error carrying internal detail with
139
+ * one that is safe to send.
140
+ *
141
+ * Only server function calls made over the network run through it. A
142
+ * server function called during rendering runs in process and throws
143
+ * straight to its caller, and errors from API routes never reach it
144
+ * either.
145
+ *
146
+ * The handler may be asynchronous. SolidStart awaits its return value
147
+ * before serializing the response, allowing a monitoring service to flush
148
+ * first. The resolved value decides what the client sees:
149
+ *
150
+ * - `undefined` (or `null`) sends what was thrown, unchanged.
151
+ * - A `Response` is passed through unchanged, which keeps a thrown
152
+ * `redirect()` working.
153
+ * - Any other value is sent in place of what was thrown, and the client
154
+ * call rejects with it.
155
+ * - If the handler throws or rejects, what the server function threw is
156
+ * sent unchanged.
157
+ *
158
+ * Control flow reaches the export the same way errors do, so a handler
159
+ * that replaces everything it sees turns redirects into errors.
160
+ *
161
+ * Whatever is sent gets serialized to the client along with its own
162
+ * properties, so an error meant to be safe to expose must not carry
163
+ * internal detail.
164
+ *
165
+ * Type the export as `ServerFunctionErrorHandler` from
166
+ * `@solidjs/start/server`. The module is bundled into the server only, so
167
+ * it may import server-only code such as a monitoring SDK.
168
+ *
169
+ * @example "src/server-fn-error.ts"
170
+ */
171
+ onError?: string;
172
+ };
24
173
  }
25
174
  export declare function solidStart(options?: SolidStartOptions): Array<PluginOption>;
@@ -3,12 +3,14 @@ import { globSync } from "node:fs";
3
3
  import { basename, extname, isAbsolute, join } from "node:path";
4
4
  import solid from "vite-plugin-solid";
5
5
  import { serverFunctionsPlugin } from "../directives/index.js";
6
+ import { appRootAlias } from "./app-root-alias.js";
6
7
  import { boundaryModules } from "./boundary-modules.js";
7
8
  import { DEFAULT_EXTENSIONS, VIRTUAL_MODULES, VITE_ENVIRONMENTS } from "./constants.js";
8
9
  import { devServer } from "./dev-server.js";
9
10
  import { envPlugin } from "./env.js";
10
11
  import { SolidStartClientFileRouter, SolidStartServerFileRouter } from "./fs-router.js";
11
12
  import { fsRoutes } from "./fs-routes/index.js";
13
+ import { sanitizeChunkFileName, toPickId } from "./fs-routes/tree-shake.js";
12
14
  import lazy from "./lazy.js";
13
15
  import { manifest } from "./manifest.js";
14
16
  import { parseIdQuery } from "./utils.js";
@@ -71,17 +73,23 @@ export function solidStart(options) {
71
73
  for (const route of await clientRouter.getRoutes()) {
72
74
  for (const [key, value] of Object.entries(route)) {
73
75
  if (value && key.startsWith("$") && !key.startsWith("$$")) {
74
- function toRouteId(route) {
75
- return `${route.src}?${route.pick.map((p) => `pick=${p}`).join("&")}`;
76
- }
77
- clientInput.push(toRouteId(value));
76
+ clientInput.push(toPickId(value.src, value.pick));
78
77
  }
79
78
  }
80
79
  }
81
80
  }
82
81
  return {
83
82
  appType: "custom",
84
- build: { assetsDir: "_build/assets" },
83
+ build: {
84
+ assetsDir: "_build/assets",
85
+ rolldownOptions: {
86
+ output: {
87
+ // Keeps route chunks named after their file rather than after
88
+ // the `?pick=...` id that addresses them. See toPickId.
89
+ sanitizeFileName: sanitizeChunkFileName,
90
+ },
91
+ },
92
+ },
85
93
  optimizeDeps: {
86
94
  // Suppress TS errors from Vite 7 types when configuring Vite 8's Rolldown
87
95
  ...{
@@ -99,7 +107,7 @@ export function solidStart(options) {
99
107
  write: true,
100
108
  manifest: true,
101
109
  outDir: "dist/client",
102
- rollupOptions: {
110
+ rolldownOptions: {
103
111
  input: clientInput,
104
112
  treeshake: true,
105
113
  preserveEntrySignatures: "exports-only",
@@ -113,7 +121,7 @@ export function solidStart(options) {
113
121
  write: true,
114
122
  manifest: true,
115
123
  copyPublicDir: false,
116
- rollupOptions: {
124
+ rolldownOptions: {
117
125
  input: handlers.server,
118
126
  },
119
127
  outDir: "dist/server",
@@ -126,7 +134,6 @@ export function solidStart(options) {
126
134
  resolve: {
127
135
  alias: {
128
136
  "@solidjs/start/server/entry": handlers.server,
129
- "~": join(process.cwd(), start.appRoot),
130
137
  ...(!start.ssr
131
138
  ? {
132
139
  "@solidjs/start/server": "@solidjs/start/server/spa",
@@ -175,6 +182,7 @@ export function solidStart(options) {
175
182
  };
176
183
  },
177
184
  },
185
+ appRootAlias(root, start.appRoot),
178
186
  manifest(start),
179
187
  fsRoutes({
180
188
  routers: {
@@ -58,12 +58,17 @@ const lazy = () => {
58
58
  if (this.environment.name !== VITE_ENVIRONMENTS.client)
59
59
  return;
60
60
  for (const chunk of Object.values(bundle)) {
61
- if (chunk.type !== "chunk" || !chunk.isDynamicEntry || chunk.facadeModuleId)
61
+ if (chunk.type !== "chunk" || chunk.facadeModuleId)
62
+ continue;
63
+ if (!chunk.viteMetadata?.importedCss.size)
64
+ continue;
65
+ const moduleIds = chunk.moduleIds.filter(id => !id.endsWith("css"));
66
+ if (moduleIds.length <= 1)
62
67
  continue;
63
68
  // Has to follow Vites implementation:
64
69
  // https://github.com/vitejs/vite/blob/4be37a8389c67873880f826b01fe40137e1c29a7/packages/vite/src/node/plugins/manifest.ts#L179
65
70
  const chunkName = `_${basename(chunk.fileName)}`;
66
- for (const id of chunk.moduleIds) {
71
+ for (const id of moduleIds) {
67
72
  sharedChunkNames[id] = chunkName;
68
73
  }
69
74
  }
@@ -1,3 +1,6 @@
1
1
  import { type PluginOption } from "vite";
2
2
  import { type SolidStartOptions } from "./index.ts";
3
+ type StyleFilterOptions = NonNullable<SolidStartOptions["css"]>["filter"];
4
+ export declare function createStyleFilter(options?: StyleFilterOptions): (id: unknown) => boolean;
3
5
  export declare function manifest(start: SolidStartOptions): PluginOption;
6
+ export {};