@solidjs/start 0.5.0 → 0.5.1

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.
@@ -24,7 +24,6 @@ export class SolidStartClientFileRouter extends BaseFileSystemRouter {
24
24
 
25
25
  if (src.endsWith(".md") || src.endsWith(".mdx")) {
26
26
  return {
27
- type: "page",
28
27
  $component: {
29
28
  src: src,
30
29
  pick: ["$css"]
@@ -40,7 +39,6 @@ export class SolidStartClientFileRouter extends BaseFileSystemRouter {
40
39
  const hasRouteConfig = exports.find(e => e.n === "route");
41
40
  if (hasDefault) {
42
41
  return {
43
- type: "page",
44
42
  $component: {
45
43
  src: src,
46
44
  pick: ["default", "$css"]
@@ -58,6 +56,20 @@ export class SolidStartClientFileRouter extends BaseFileSystemRouter {
58
56
  }
59
57
  }
60
58
 
59
+ const HTTP_METHODS = ["GET", "POST", "PUT", "DELETE", "PATCH"];
60
+ function createHTTPHandlers(src, exports) {
61
+ const handlers = {};
62
+ for (const exp of exports) {
63
+ if (HTTP_METHODS.includes(exp.n)) {
64
+ handlers[`$${exp.n}`] = {
65
+ src: src,
66
+ pick: [exp.n]
67
+ };
68
+ }
69
+ }
70
+ return handlers;
71
+ }
72
+
61
73
  export class SolidStartServerFileRouter extends BaseFileSystemRouter {
62
74
  toPath(src) {
63
75
  const routePath = cleanPath(src, this.config)
@@ -79,10 +91,8 @@ export class SolidStartServerFileRouter extends BaseFileSystemRouter {
79
91
 
80
92
  toRoute(src) {
81
93
  let path = this.toPath(src);
82
-
83
94
  if (src.endsWith(".md") || src.endsWith(".mdx")) {
84
95
  return {
85
- type: "page",
86
96
  $component: {
87
97
  src: src,
88
98
  pick: ["$css"]
@@ -95,62 +105,22 @@ export class SolidStartServerFileRouter extends BaseFileSystemRouter {
95
105
 
96
106
  const [_, exports] = analyzeModule(src);
97
107
  const hasRouteConfig = exports.find(e => e.n === "route");
98
- if (exports.find(exp => exp.n === "default")) {
108
+ const hasDefault = exports.find(e => e.n === "default");
109
+ const hasAPIRoutes = exports.find(exp => HTTP_METHODS.includes(exp.n));
110
+ if (hasDefault || hasAPIRoutes) {
99
111
  return {
100
- type: "page",
101
- $component: {
102
- src: src,
103
- pick: ["default", "$css"]
104
- },
112
+ $component: hasDefault
113
+ ? {
114
+ src: src,
115
+ pick: ["default", "$css"]
116
+ }
117
+ : undefined,
105
118
  $$route: hasRouteConfig
106
119
  ? {
107
120
  src: src,
108
121
  pick: ["route"]
109
122
  }
110
123
  : undefined,
111
- path,
112
- filePath: src
113
- };
114
- }
115
- }
116
- }
117
-
118
- const HTTP_METHODS = ["GET", "POST", "PUT", "DELETE", "PATCH"];
119
- function createHTTPHandlers(src, exports) {
120
- const handlers = {};
121
- for (const exp of exports) {
122
- if (HTTP_METHODS.includes(exp.n)) {
123
- handlers[`$${exp.n}`] = {
124
- src: src,
125
- pick: [exp.n]
126
- };
127
- }
128
- }
129
- return handlers;
130
- }
131
-
132
- export class SolidStartAPIFileRouter extends BaseFileSystemRouter {
133
- toPath(src) {
134
- const routePath = cleanPath(src, this.config)
135
- // remove the initial slash
136
- .slice(1)
137
- .replace(/index$/, "")
138
- .replace(/\[([^\/]+)\]/g, (_, m) => {
139
- if (m.length > 2 && m.startsWith("...")) {
140
- return `**${m.length > 3 ? ":" + m.slice(3) : ""}`;
141
- }
142
- return `:${m}`;
143
- });
144
-
145
- return routePath?.length > 0 ? `/${routePath}` : "/";
146
- }
147
-
148
- toRoute(src) {
149
- let path = this.toPath(src);
150
- const [_, exports] = analyzeModule(src);
151
- if (exports.find(exp => HTTP_METHODS.includes(exp.n))) {
152
- return {
153
- type: "api",
154
124
  ...createHTTPHandlers(src, exports),
155
125
  path,
156
126
  filePath: src
package/config/index.js CHANGED
@@ -8,7 +8,10 @@ import { createApp, resolve } from "vinxi";
8
8
  import { normalize } from "vinxi/lib/path";
9
9
  import { config } from "vinxi/plugins/config";
10
10
  import solid from "vite-plugin-solid";
11
- import { SolidStartAPIFileRouter, SolidStartClientFileRouter, SolidStartServerFileRouter } from "./fs-router.js";
11
+ import {
12
+ SolidStartClientFileRouter,
13
+ SolidStartServerFileRouter
14
+ } from "./fs-router.js";
12
15
  import { serverComponents } from "./server-components.js";
13
16
 
14
17
  const DEFAULT_EXTENSIONS = ["js", "jsx", "ts", "tsx"];
@@ -37,18 +40,6 @@ function solidStartServerFsRouter(config) {
37
40
  );
38
41
  }
39
42
 
40
- function solidStartAPIFileRouter(config) {
41
- return (router, app) =>
42
- new SolidStartAPIFileRouter(
43
- {
44
- dir: resolve.absolute(config.dir, router.root),
45
- extensions: config.extensions ?? DEFAULT_EXTENSIONS
46
- },
47
- router,
48
- app
49
- );
50
- }
51
-
52
43
  export function defineConfig(baseConfig = {}) {
53
44
  let { plugins = [], start = {}, ...userConfig } = baseConfig;
54
45
  const extensions = [...DEFAULT_EXTENSIONS, ...(start.extensions || [])];
@@ -86,15 +77,6 @@ export function defineConfig(baseConfig = {}) {
86
77
  dir: "./public",
87
78
  base: "/"
88
79
  },
89
- {
90
- name: "api",
91
- type: "http",
92
- target: "server",
93
- base: "/",
94
- handler: normalize(fileURLToPath(new URL("./api-handler.ts", import.meta.url))),
95
- routes: solidStartAPIFileRouter({ dir: `${start.appRoot}/routes` }),
96
- middleware: start.middleware
97
- },
98
80
  {
99
81
  name: "ssr",
100
82
  type: "http",
@@ -1,5 +1,4 @@
1
1
  import { getRequestEvent } from "solid-js/web";
2
- //@ts-ignore
3
2
  import { provideRequestEvent } from "solid-js/web/storage";
4
3
  import { cloneEvent } from "../server/fetchEvent";
5
4
 
@@ -13,10 +13,9 @@ import {
13
13
  URLSearchParamsPlugin
14
14
  } from "seroval-plugins/web";
15
15
  import { sharedConfig } from "solid-js";
16
- /* @ts-ignore */
17
16
  import { provideRequestEvent } from "solid-js/web/storage";
17
+ import { eventHandler, setHeader } from "vinxi/http";
18
18
  import invariant from "vinxi/lib/invariant";
19
- import { eventHandler, setHeader } from "vinxi/server";
20
19
  import { getFetchEvent, mergeResponseHeaders } from "../server/fetchEvent";
21
20
 
22
21
  function createChunk(data) {
@@ -1,8 +1,8 @@
1
1
  import {
2
- H3Event,
3
- defineMiddleware,
4
- sendWebResponse
5
- } from "vinxi/server";
2
+ H3Event,
3
+ defineMiddleware,
4
+ sendWebResponse
5
+ } from "vinxi/http";
6
6
  import { getFetchEvent } from "../server/fetchEvent";
7
7
  import type { FetchEvent } from "../server/types";
8
8
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solidjs/start",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "type": "module",
5
5
  "author": "Ryan Carniato",
6
6
  "publishConfig": {
@@ -42,7 +42,7 @@
42
42
  }
43
43
  },
44
44
  "devDependencies": {
45
- "solid-js": "^1.8.12",
45
+ "solid-js": "^1.8.13",
46
46
  "vinxi": "^0.2.1"
47
47
  },
48
48
  "dependencies": {
@@ -12,7 +12,7 @@ import {
12
12
  setResponseHeader,
13
13
  setResponseStatus,
14
14
  toWebRequest
15
- } from "vinxi/server";
15
+ } from "vinxi/http";
16
16
  import type { FetchEvent, ResponseStub } from "./types";
17
17
 
18
18
  const fetchEventSymbol = Symbol("fetchEvent");
@@ -40,7 +40,6 @@ export function getFetchEvent(h3Event: H3Event): FetchEvent {
40
40
  if (!h3Event[fetchEventSymbol]) {
41
41
  const fetchEvent = createFetchEvent(h3Event);
42
42
  h3Event[fetchEventSymbol] = fetchEvent;
43
- // @ts-ignore
44
43
  }
45
44
 
46
45
  return h3Event[fetchEventSymbol];
package/server/handler.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import { sharedConfig } from "solid-js";
2
2
  import { renderToStream, renderToString } from "solid-js/web";
3
- /* @ts-ignore */
4
3
  import { provideRequestEvent } from "solid-js/web/storage";
5
4
  import {
6
5
  EventHandlerObject,
@@ -10,10 +9,11 @@ import {
10
9
  sendRedirect,
11
10
  setHeader,
12
11
  setResponseStatus
13
- } from "vinxi/server";
12
+ } from "vinxi/http";
13
+ import { matchAPIRoute } from "../shared/routes";
14
14
  import { getFetchEvent } from "./fetchEvent";
15
15
  import { createPageEvent } from "./pageEvent";
16
- import { FetchEvent, PageEvent } from "./types";
16
+ import type { APIEvent, FetchEvent, PageEvent } from "./types";
17
17
 
18
18
  export function createBaseHandler(
19
19
  fn: (context: PageEvent) => unknown,
@@ -36,6 +36,23 @@ export function createBaseHandler(
36
36
  const mode = import.meta.env.START_SSR;
37
37
 
38
38
  return provideRequestEvent(event, async () => {
39
+ // api
40
+ const match = matchAPIRoute(new URL(event.request.url).pathname, event.request.method);
41
+ if (match) {
42
+ const mod = await match.handler.import();
43
+ const fn = mod[event.request.method];
44
+ (event as APIEvent).params = match.params;
45
+ // @ts-ignore
46
+ sharedConfig.context = { event };
47
+ const res = await fn(event);
48
+ if (res !== undefined) return res;
49
+ if (event.request.method !== "GET") {
50
+ throw new Error(
51
+ `API handler for ${event.request.method} "${event.request.url}" did not return a response.`
52
+ );
53
+ }
54
+ }
55
+
39
56
  // render
40
57
  const context = await createPageEvent(event);
41
58
  if (mode === "sync") {
@@ -65,7 +82,7 @@ export function createBaseHandler(
65
82
  } else cloned.onCompleteShell = handleShellCompleteRedirect(context, e);
66
83
  const stream = renderToStream(() => {
67
84
  (sharedConfig.context as any).event = context;
68
- return fn(context)
85
+ return fn(context);
69
86
  }, cloned);
70
87
  if (context.response && context.response.headers.get("Location")) {
71
88
  return sendRedirect(event, context.response.headers.get("Location"));
@@ -86,7 +103,7 @@ function handleShellCompleteRedirect(context: PageEvent, e: H3Event<EventHandler
86
103
  setResponseStatus(e, 302);
87
104
  setHeader(e, "Location", context.response.headers.get("Location"));
88
105
  }
89
- }
106
+ };
90
107
  }
91
108
 
92
109
  function handleStreamCompleteRedirect(context: PageEvent) {
@@ -101,4 +118,4 @@ export function createHandler(
101
118
  options: Parameters<typeof createBaseHandler>[1] = {}
102
119
  ) {
103
120
  return createBaseHandler(fn, { ...options, createPageEvent });
104
- }
121
+ }
@@ -1,7 +1,7 @@
1
1
  import {
2
- getCookie,
3
- setCookie
4
- } from "vinxi/server";
2
+ getCookie,
3
+ setCookie
4
+ } from "vinxi/http";
5
5
  import { createRoutes } from "../shared/FileRoutes";
6
6
  import { FetchEvent, PageEvent } from "./types";
7
7
 
package/server/types.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { JSX } from "solid-js";
2
- import { HTTPEventSymbol, type EventHandlerRequest, type H3Event } from "vinxi/server";
2
+ import { HTTPEventSymbol, type EventHandlerRequest, type H3Event } from "vinxi/http";
3
3
 
4
4
  // export const FETCH_EVENT = "$FETCH";
5
5
 
package/shared/routes.ts CHANGED
@@ -1,23 +1,25 @@
1
+ import { createRouter } from "radix3";
1
2
  import fileRoutes from "vinxi/routes";
2
3
 
3
4
  interface Route {
4
5
  path: string;
5
6
  id: string;
6
- type: "page";
7
7
  children?: Route[];
8
+ $component?: any;
8
9
  }
9
10
 
10
11
  declare module "vinxi/routes" {
11
12
  export interface Register {
12
13
  route: {
13
14
  path: string;
14
- type: "api" | "page";
15
15
  children?: Route[];
16
16
  };
17
17
  }
18
18
  }
19
19
 
20
- export const pageRoutes = defineRoutes((fileRoutes as unknown as Route[]).filter(o => o.type === "page"));
20
+ export const pageRoutes = defineRoutes(
21
+ (fileRoutes as unknown as Route[]).filter(o => o.$component)
22
+ );
21
23
 
22
24
  function defineRoutes(fileRoutes: Route[]) {
23
25
  function processRoute(routes: Route[], route: Route, id: string, full: string) {
@@ -46,3 +48,35 @@ function defineRoutes(fileRoutes: Route[]) {
46
48
  }, []);
47
49
  }
48
50
 
51
+ export function matchAPIRoute(path: string, method: string) {
52
+ const match = router.lookup(path);
53
+ if (match) {
54
+ const handler = match.route[`$${method}`];
55
+ if (handler === undefined) return;
56
+ return {
57
+ handler,
58
+ params: match.params
59
+ };
60
+ }
61
+ }
62
+
63
+ function containsHTTP(route: Route) {
64
+ return route["$GET"] || route["$POST"] || route["$PUT"] || route["$PATCH"] || route["$DELETE"];
65
+ }
66
+
67
+ const router = createRouter({
68
+ routes: (fileRoutes as unknown as Route[]).reduce((memo, route) => {
69
+ if (!containsHTTP(route)) return memo;
70
+ let path = route.path.replace(/\(.*\)\/?/g, "").replace(/\*([^/]*)/g, (_, m) => `**:${m}`);
71
+ if (/:[^/]*\?/g.test(path)) {
72
+ throw new Error(`Optional parameters are not supported in API routes: ${path}`);
73
+ }
74
+ if (memo[path]) {
75
+ throw new Error(
76
+ `Duplicate API routes for "${path}" found at "${memo[path].route.path}" and "${route.path}"`
77
+ );
78
+ }
79
+ memo[path] = { route };
80
+ return memo;
81
+ }, {} as Record<string, { route: Route }>)
82
+ });
@@ -1,64 +0,0 @@
1
- /// <reference types="vinxi/types/server" />
2
- import { createRouter } from "radix3";
3
- import { sharedConfig } from "solid-js";
4
- import fileRoutes from "vinxi/routes";
5
-
6
- // @ts-ignore
7
- import { provideRequestEvent } from "solid-js/web/storage";
8
- import { eventHandler } from "vinxi/server";
9
- import { getFetchEvent } from "../server/fetchEvent";
10
- import { APIEvent } from "../server/types";
11
-
12
- export default eventHandler(h3Event => {
13
- const event = getFetchEvent(h3Event);
14
- const match = matchAPIRoute(new URL(event.request.url).pathname, event.request.method);
15
- if (match) {
16
- return provideRequestEvent(event, async () => {
17
- const mod = await match.handler.import();
18
- const fn = mod[event.request.method];
19
- (event as APIEvent).params = match.params;
20
- // @ts-ignore
21
- sharedConfig.context = { event };
22
- const res = await fn(event);
23
- if (res === undefined && event.request.method !== "GET") {
24
- throw new Error(
25
- `API handler for ${event.request.method} "${event.request.url}" did not return a response.`
26
- );
27
- }
28
- return res;
29
- });
30
- }
31
- });
32
-
33
- interface Route {
34
- path: string;
35
- id: string;
36
- type: "api" | "page";
37
- children?: Route[];
38
- }
39
-
40
- const router = createRouter({
41
- routes: (fileRoutes as unknown as Route[]).reduce((memo, route) => {
42
- if (route.type !== "api") return memo;
43
- const path = route.path.replace(/\(.*\)\/?/g, "");
44
- if (memo[path]) {
45
- throw new Error(
46
- `Duplicate API routes for "${path}" found at "${memo[path].route.path}" and "${route.path}"`
47
- );
48
- }
49
- memo[path] = { route };
50
- return memo;
51
- }, {} as Record<string, { route: Route }>)
52
- });
53
-
54
- function matchAPIRoute(path: string, method: string) {
55
- const match = router.lookup(path);
56
- if (match) {
57
- const handler = match.route[`$${method}`];
58
- if (handler === "skip" || handler === undefined) return;
59
- return {
60
- handler,
61
- params: match.params
62
- };
63
- }
64
- }