@varlabs/create-solidstep 0.1.2 → 0.1.3

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.
@@ -0,0 +1,49 @@
1
+ import { defineMiddleware } from 'vinxi/http';
2
+ import { csp } from '../utils/csp';
3
+ import { cors } from '../utils/cors';
4
+ import { csrf } from '../utils/csrf';
5
+
6
+ const trustedOrigins = ['https://example.com', 'https://another-example.com'];
7
+
8
+ const corsMiddleware = cors(trustedOrigins);
9
+ const csrfMiddleware = csrf(trustedOrigins);
10
+
11
+ const middleware = defineMiddleware({
12
+ onRequest: async (event) => {
13
+ event.node.res.setHeader('Content-Security-Policy', csp);
14
+ event.node.res.setHeader('Vary', 'Origin, Access-Control-Request-Method');
15
+
16
+ const origin = event.node.req.headers.origin || '';
17
+ const requestUrl = new URL(event.node.req.url, `http://${event.node.req.headers.host || 'localhost'}`);
18
+
19
+ const csrfResult = csrfMiddleware(
20
+ event.node.req.method,
21
+ requestUrl,
22
+ origin,
23
+ event.node.req.headers.referer
24
+ );
25
+
26
+ if (!csrfResult.success) {
27
+ event.node.res.statusCode = 403; // Forbidden
28
+ event.node.res.end(csrfResult.message);
29
+ return;
30
+ }
31
+
32
+ if (origin) {
33
+ const corsHeaders = corsMiddleware(origin, event.node.req.method === 'OPTIONS');
34
+ for (const [key, value] of Object.entries(corsHeaders)) {
35
+ event.node.res.setHeader(key, value);
36
+ }
37
+ if (
38
+ event.node.req.method === 'OPTIONS'
39
+ && event.node.req.headers['access-control-request-method']
40
+ ) {
41
+ event.node.res.statusCode = 204; // No Content for preflight requests
42
+ event.node.res.end();
43
+ return;
44
+ }
45
+ }
46
+ },
47
+ });
48
+
49
+ export default middleware;
@@ -1,7 +1,7 @@
1
1
  import { createApp } from 'vinxi';
2
2
  import solid from 'vite-plugin-solid';
3
3
  import { serverFunctions } from '@vinxi/server-functions/plugin';
4
- import { Router } from './utils/router';
4
+ import { ServerRouter. ClientRouter } from './utils/router';
5
5
  import path from 'path';
6
6
  import { dirname } from 'node:path';
7
7
  import { fileURLToPath } from 'node:url';
@@ -10,6 +10,9 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
10
10
 
11
11
  export default createApp({
12
12
  server: {
13
+ experimental: {
14
+ asyncContext: true,
15
+ },
13
16
  },
14
17
  routers: [
15
18
  {
@@ -19,18 +22,14 @@ export default createApp({
19
22
  base: '/',
20
23
  },
21
24
  {
22
- name: 'ssr',
23
- type: 'http',
24
- base: '/',
25
- handler: './server.ts',
26
- target: 'server',
27
- plugins: () => [solid({ ssr: true })],
28
- link: {
29
- client: 'client',
30
- },
31
- middleware: './app/middleware.ts',
25
+ name: 'client',
26
+ type: 'client',
27
+ target: 'browser',
28
+ handler: './client.ts',
29
+ plugins: () => [serverFunctions.client(), solid({ ssr: true })],
30
+ base: '/_build',
32
31
  routes: (router, app) => {
33
- return new Router(
32
+ return new ServerRouter(
34
33
  {
35
34
  dir: path.join(__dirname, 'app'),
36
35
  extensions: ['jsx', 'js', 'tsx', 'ts'],
@@ -41,14 +40,21 @@ export default createApp({
41
40
  }
42
41
  },
43
42
  {
44
- name: 'client',
45
- type: 'client',
46
- target: 'browser',
47
- handler: './client.ts',
48
- plugins: () => [serverFunctions.client(), solid({ ssr: true })],
49
- base: '/_build',
43
+ name: 'ssr',
44
+ type: 'http',
45
+ base: '/',
46
+ handler: './server.ts',
47
+ target: 'server',
48
+ plugins: () => [
49
+ serverFunctions.server(),
50
+ solid({ ssr: true })
51
+ ],
52
+ // link: {
53
+ // client: 'client',
54
+ // },
55
+ middleware: './app/middleware.ts',
50
56
  routes: (router, app) => {
51
- return new Router(
57
+ return new ClientRouter(
52
58
  {
53
59
  dir: path.join(__dirname, 'app'),
54
60
  extensions: ['jsx', 'js', 'tsx', 'ts'],
@@ -58,6 +64,6 @@ export default createApp({
58
64
  );
59
65
  }
60
66
  },
61
- serverFunctions.router(),
67
+ // serverFunctions.router(),
62
68
  ],
63
69
  });
@@ -8,31 +8,6 @@ const importModule = async (routeModule: any) => {
8
8
  if ((import.meta as any).env.DEV) {
9
9
  return await manifest.inputs[routeModule.src].import();
10
10
  }
11
- const assets = await manifest.inputs?.[routeModule.src].assets();
12
- if (typeof window !== 'undefined' && assets && assets.length > 0) {
13
- const styles = assets.filter(
14
- (asset) => asset.tag === 'style' || asset.attrs.rel === 'stylesheet',
15
- );
16
- for (const asset of styles) {
17
- const attributeString = Object.entries(asset.attrs)
18
- .map(([key, value]) => `${key}="${value}"`)
19
- .join(' ');
20
- if (asset.tag === 'style') {
21
- document.head.insertAdjacentHTML(
22
- 'beforeend',
23
- `<style ${attributeString}>${asset.children}</style>`,
24
- );
25
- } else {
26
- const link = document.createElement('link');
27
- link.rel = 'stylesheet';
28
- link.href = asset.attrs.href;
29
- Object.entries(asset.attrs).forEach(([key, value]) => {
30
- link.setAttribute(key, value);
31
- });
32
- document.head.appendChild(link);
33
- }
34
- }
35
- }
36
11
  return await routeModule.import();
37
12
  };
38
13
 
@@ -40,6 +15,7 @@ export const main = async (
40
15
  modulePath: string,
41
16
  routeParams: Record<string, string> = {},
42
17
  searchParams: Record<string, string> = {},
18
+ loaderDataManifest: Record<string, any> = {}
43
19
  ) => {
44
20
  // find the route that matches the path
45
21
  const pageModule = fileRoutes.find((route) => route.path === modulePath);
@@ -47,15 +23,18 @@ export const main = async (
47
23
  console.error(`No route found for path: ${modulePath}`);
48
24
  return;
49
25
  }
26
+ const pageLoaderData = loaderDataManifest[modulePath];
50
27
 
51
28
  const segments = modulePath.split('/').slice(2);
52
29
  if (segments.at(0)) {
53
30
  segments.unshift('');
54
31
  }
55
- let layouts: any[] = [];
56
- let groups: Record<string, any> = {};
32
+ const layouts: any[] = [];
33
+ const layoutLoaderData: any[] = [];
34
+ const groups: Record<string, any> = {};
57
35
  for (let i = 0; i < segments.length; i++) {
58
36
  const path = '/' + segments.slice(1, segments.length - i).join('/');
37
+ const loaderData = loaderDataManifest[`/layout${path}`];
59
38
  const layoutModule = fileRoutes.find((route) => {
60
39
  const routePath = '/' + route.path.split('/').slice(2).join('/');
61
40
  return routePath === path && (route as any).type === 'layout';
@@ -63,6 +42,9 @@ export const main = async (
63
42
  if (layoutModule) {
64
43
  layouts.unshift(layoutModule);
65
44
  }
45
+ if (loaderData) {
46
+ layoutLoaderData.unshift(loaderData);
47
+ }
66
48
  }
67
49
  const groupModules = fileRoutes.filter((route) => {
68
50
  const parentPath = (route as any).parent || '';
@@ -77,23 +59,32 @@ export const main = async (
77
59
  const compose = layouts.reduceRight(
78
60
  (children, layout, index) => async () => {
79
61
  const { default: layoutModule } = await importModule(layout.$component);
80
- let slots: Record<string, any> = {};
62
+ const loaderData = layoutLoaderData[index] || {};
63
+ const slots: Record<string, any> = {};
64
+ const slotPromises: Promise<any>[] = [children()];
81
65
  if (index === layouts.length - 1) {
82
66
  // last layout, we can render slots
83
67
  for (const [groupName, group] of Object.entries(groups)) {
84
- const { default: groupPage } = await importModule(group.$component);
85
- slots[groupName] = () => groupPage({
86
- routeParams,
87
- searchParams,
88
- });
68
+ slotPromises.push(
69
+ (async () => {
70
+ const { default: groupPage } = await importModule(group.$component);
71
+ const groupLoaderData = loaderDataManifest[group.path] || {};
72
+ slots[groupName] = () => groupPage({
73
+ routeParams,
74
+ searchParams,
75
+ loaderData: groupLoaderData,
76
+ });
77
+ })()
78
+ );
89
79
  }
90
80
  }
91
- const childrenRendered = await children();
81
+ const [childrenRendered] = await Promise.all(slotPromises);
92
82
  return () => layoutModule({
93
83
  children: childrenRendered,
94
84
  routeParams,
95
85
  searchParams,
96
86
  slots: slots,
87
+ loaderData,
97
88
  });
98
89
  },
99
90
  async () => {
@@ -101,6 +92,7 @@ export const main = async (
101
92
  return () => page({
102
93
  routeParams,
103
94
  searchParams,
95
+ loaderData: pageLoaderData || {},
104
96
  });
105
97
  }
106
98
  );