rasengan 1.0.0-beta.5 → 1.0.0-beta.7

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/lib/cli/index.js CHANGED
@@ -37,7 +37,6 @@ program
37
37
  .command("build")
38
38
  .description("Build the project")
39
39
  .action(() => {
40
- // const childProcess = exec("npm --prefix node_modules/rasengan run build");
41
40
  execa("npm", ["run", "build"], {
42
41
  cwd: "node_modules/rasengan",
43
42
  stdio: "inherit", // Pipe child process output to the parent process
@@ -45,12 +44,12 @@ program
45
44
  });
46
45
  // Handle the prebuild command
47
46
  program
48
- .command("prebuild")
49
- .description("Prebuild the project")
47
+ .command("prepare")
48
+ .description("Prepare the project")
50
49
  .action(() => {
51
50
  // Displaying the message
52
51
  console.log("");
53
- console.log(chalk.blue("Prebuilding your project..."));
52
+ console.log(chalk.blue("Preparing your project for production..."));
54
53
  console.log("");
55
54
  // Checking the config file in order to know about hosting strategy
56
55
  const { server } = config;
@@ -9,7 +9,7 @@ export declare const defineConfig: (loadedConfig: AppConfig) => {
9
9
  server: {
10
10
  development: {
11
11
  port: number;
12
- open: true;
12
+ open: boolean;
13
13
  };
14
14
  production: {
15
15
  hosting: import("./type.js").HostingStrategy;
@@ -25,7 +25,7 @@ export const defineConfig = (loadedConfig) => {
25
25
  const defaultServerConfig = {
26
26
  development: {
27
27
  port: server?.development?.port || undefined,
28
- open: server?.development?.open || true,
28
+ open: server?.development?.open || false,
29
29
  },
30
30
  production: {
31
31
  hosting: server?.production?.hosting || "custom",
@@ -14,11 +14,12 @@ export type AppConfig = {
14
14
  development?: {
15
15
  /**
16
16
  * Port to listen on
17
- * @default 3000
17
+ * @default 5320
18
18
  */
19
19
  port?: number;
20
20
  /**
21
21
  * Automatically open browser
22
+ * @default false
22
23
  */
23
24
  open?: boolean;
24
25
  };
@@ -3,7 +3,7 @@ import { ComponentProps, PageToRenderProps } from "../types.js";
3
3
  /**
4
4
  * App component that represent the entry point of the application
5
5
  */
6
- export declare const Component: ({ router: AppRouter }: ComponentProps) => import("react/jsx-runtime").JSX.Element;
6
+ export declare const Component: ({ router: AppRouter, children, }: ComponentProps) => string | number | true | Iterable<React.ReactNode> | import("react/jsx-runtime").JSX.Element;
7
7
  /**
8
8
  * Page component that defines title and description to a page
9
9
  */
@@ -1,13 +1,17 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import * as React from "react";
3
- import { getRouter } from "../../routing/utils/index.js";
3
+ import { generateMetadata, getRouter } from "../../routing/utils/index.js";
4
4
  import * as pkg from "react-helmet-async";
5
5
  // @ts-ignore
6
6
  const { Helmet } = pkg.default || pkg;
7
7
  /**
8
8
  * App component that represent the entry point of the application
9
9
  */
10
- export const Component = ({ router: AppRouter }) => {
10
+ export const Component = ({ router: AppRouter, children = undefined, }) => {
11
+ // Return children if they exist
12
+ if (children)
13
+ return children;
14
+ // Otherwise, get the router and return it
11
15
  const Router = getRouter(AppRouter);
12
16
  return _jsx(Router, {});
13
17
  };
@@ -19,7 +23,11 @@ export const PageToRender = ({ page, data }) => {
19
23
  const Page = page.render;
20
24
  // Get the page props
21
25
  const props = data.props || {};
22
- return (_jsxs(React.Fragment, { children: [_jsxs(Helmet, { children: [_jsx("title", { children: page.title }), _jsx("meta", { name: "description", content: page.description })] }), _jsx(Page, { ...props })] }));
26
+ // Generate meta tags
27
+ const metaTags = React.useMemo(() => {
28
+ return generateMetadata(page.metadata);
29
+ }, []);
30
+ return (_jsxs(React.Fragment, { children: [_jsxs(Helmet, { children: [metaTags.map((meta) => meta), _jsx("meta", { name: "description", content: page.description }), _jsx("title", { children: page.title })] }), _jsx(Page, { ...props })] }));
23
31
  };
24
32
  /**
25
33
  * Error fallback component that will be displayed if an error occurs
@@ -1,5 +1,6 @@
1
1
  import * as React from "react";
2
2
  import { LoaderOptions, LoaderResponse, ReactComponentProps } from "./types.js";
3
+ import { Metadata } from "../routing/types.js";
3
4
  /**
4
5
  * Layout component interface that defines the base structure of a Layout and a Page too.
5
6
  */
@@ -18,6 +19,10 @@ export declare abstract class LayoutComponent implements ILayoutComponent {
18
19
  * Page path
19
20
  */
20
21
  protected _path: string;
22
+ /**
23
+ * Metadata
24
+ */
25
+ protected _metadata: Metadata[];
21
26
  /**
22
27
  * Render method which is a React component
23
28
  * @param props - props for the component
@@ -27,10 +32,28 @@ export declare abstract class LayoutComponent implements ILayoutComponent {
27
32
  * Get page path
28
33
  */
29
34
  get path(): string;
35
+ /**
36
+ * Get metadata
37
+ */
38
+ get metadata(): Metadata[];
30
39
  /**
31
40
  * Set page path
32
41
  */
33
42
  set path(path: string);
43
+ /**
44
+ * Set metadata
45
+ */
46
+ set metadata(metadata: Metadata[]);
47
+ /**
48
+ * Add metadata
49
+ */
50
+ addMetadata(metadata: Metadata[]): void;
51
+ /**
52
+ * Loader method that will be called during a routing on the server side
53
+ * in order to get data for the page from the server
54
+ * @returns Promise<any>
55
+ */
56
+ loader(_options: LoaderOptions): Promise<LoaderResponse>;
34
57
  }
35
58
  /**
36
59
  * Default Layout
@@ -67,10 +90,4 @@ export declare abstract class PageComponent extends LayoutComponent {
67
90
  * Set page description
68
91
  */
69
92
  set description(description: string);
70
- /**
71
- * Loader method that will be called during a routing on the server side
72
- * in order to get data for the page from the server
73
- * @returns Promise<any>
74
- */
75
- loader(_options: LoaderOptions): Promise<LoaderResponse>;
76
93
  }
@@ -10,6 +10,10 @@ export class LayoutComponent {
10
10
  * Page path
11
11
  */
12
12
  _path;
13
+ /**
14
+ * Metadata
15
+ */
16
+ _metadata;
13
17
  // Getters
14
18
  /**
15
19
  * Get page path
@@ -17,6 +21,12 @@ export class LayoutComponent {
17
21
  get path() {
18
22
  return this._path;
19
23
  }
24
+ /**
25
+ * Get metadata
26
+ */
27
+ get metadata() {
28
+ return this._metadata;
29
+ }
20
30
  // Setters
21
31
  /**
22
32
  * Set page path
@@ -24,6 +34,32 @@ export class LayoutComponent {
24
34
  set path(path) {
25
35
  this._path = path;
26
36
  }
37
+ /**
38
+ * Set metadata
39
+ */
40
+ set metadata(metadata) {
41
+ this._metadata = metadata;
42
+ }
43
+ /**
44
+ * Add metadata
45
+ */
46
+ addMetadata(metadata) {
47
+ metadata.forEach(meta => {
48
+ this._metadata.unshift(meta);
49
+ });
50
+ }
51
+ /**
52
+ * Loader method that will be called during a routing on the server side
53
+ * in order to get data for the page from the server
54
+ * @returns Promise<any>
55
+ */
56
+ async loader(_options) {
57
+ return new Promise((resolve) => {
58
+ resolve({
59
+ props: {},
60
+ });
61
+ });
62
+ }
27
63
  }
28
64
  /**
29
65
  * Default Layout
@@ -75,16 +111,4 @@ export class PageComponent extends LayoutComponent {
75
111
  set description(description) {
76
112
  this._description = description;
77
113
  }
78
- /**
79
- * Loader method that will be called during a routing on the server side
80
- * in order to get data for the page from the server
81
- * @returns Promise<any>
82
- */
83
- async loader(_options) {
84
- return new Promise((resolve) => {
85
- resolve({
86
- props: {},
87
- });
88
- });
89
- }
90
114
  }
@@ -5,15 +5,15 @@ import { PageComponent } from "./interfaces.js";
5
5
  * Props for App component
6
6
  */
7
7
  export type AppProps = {
8
- Component: React.FC<{
9
- router: any;
10
- }>;
8
+ Component: React.FC<ComponentProps>;
9
+ children: React.ReactNode;
11
10
  };
12
11
  /**
13
12
  * Props for the base Component that takes the app router
14
13
  */
15
14
  export type ComponentProps = {
16
15
  router: RouterComponent;
16
+ children: React.ReactNode;
17
17
  };
18
18
  export type PageToRenderProps = {
19
19
  page: PageComponent;
@@ -1,7 +1,8 @@
1
+ import { PageComponent } from "../core/interfaces.js";
1
2
  import { RouteDecoratorProps } from "./types.js";
2
3
  /**
3
4
  * Decorator that add metadata for a page.
4
5
  * @param props Object that define the necessary elements to create a router
5
6
  * @returns
6
7
  */
7
- export declare function Route(props: RouteDecoratorProps): (constructor: Function) => void;
8
+ export declare function Route(props: RouteDecoratorProps): (Component: new () => PageComponent) => void;
@@ -5,20 +5,16 @@
5
5
  * @returns
6
6
  */
7
7
  export function Route(props) {
8
- return function (constructor) {
8
+ return function (Component) {
9
9
  // Handle errors
10
10
  if (!props.path)
11
11
  throw new Error("You must provide a path in the route decorator");
12
- // Add values to properties
13
- // Define path of the page
14
- constructor.prototype._path = props.path;
15
- // Define title of the page
16
- constructor.prototype._title =
17
- props.title ||
18
- constructor.name.charAt(0).toUpperCase() + constructor.name.slice(1);
19
- // Define description of the page
20
- constructor.prototype._description = props.description || "";
21
- Object.seal(constructor);
22
- Object.seal(constructor.prototype);
12
+ // Set properties
13
+ Component.prototype._path = props.path;
14
+ Component.prototype._title = props.title || Component.name;
15
+ Component.prototype._description = props.description || "";
16
+ // Seal the class
17
+ Object.seal(Component);
18
+ Object.seal(Component.prototype);
23
19
  };
24
20
  }
@@ -1,7 +1,8 @@
1
+ import { RouterComponent } from "../routing/interfaces.js";
1
2
  import { RouterDecoratorProps } from "./types.js";
2
3
  /**
3
4
  * Decorator that define a new router.
4
5
  * @param props Object that define the necessary elements to create a router
5
6
  * @returns
6
7
  */
7
- export declare function Router(props: RouterDecoratorProps): (constructor: Function) => void;
8
+ export declare function Router(option: RouterDecoratorProps): (Component: new () => RouterComponent) => void;
@@ -1,23 +1,34 @@
1
1
  // Router Decorators
2
2
  import { DefaultLayout } from "../core/interfaces.js";
3
+ import { NotFoundPageComponent } from "../routing/components/index.js";
3
4
  /**
4
5
  * Decorator that define a new router.
5
6
  * @param props Object that define the necessary elements to create a router
6
7
  * @returns
7
8
  */
8
- export function Router(props) {
9
- return function (constructor) {
9
+ export function Router(option) {
10
+ const { imports, layout, pages, loaderComponent, notFoundComponent } = option;
11
+ return (Component) => {
10
12
  // Handle errors
11
- if (!props.pages)
13
+ if (!option.pages)
12
14
  throw new Error("You must provide a list of pages in the router decorator");
13
- // Add values to properties
14
- // Define sub routers if provided or set and empty array
15
- constructor.prototype["_routers"] = props.imports || [];
16
- // Define layout if provided or set a default one.
17
- constructor.prototype["_layout"] = props.layout || DefaultLayout;
18
- // Define pages
19
- constructor.prototype["_pages"] = props.pages;
20
- Object.seal(constructor);
21
- Object.seal(constructor.prototype);
15
+ // Set properties
16
+ Component.prototype._routers = imports || [];
17
+ Component.prototype._layout = layout || new DefaultLayout();
18
+ Component.prototype._pages = pages;
19
+ Component.prototype._loaderComponent = loaderComponent || (() => null);
20
+ Component.prototype._notFoundComponent = notFoundComponent || NotFoundPageComponent;
21
+ // Seal the class
22
+ Object.seal(Component);
23
+ Object.seal(Component.prototype);
24
+ // // Create router
25
+ // const router = new Component();
26
+ // // Set properties
27
+ // router.routers = imports || [];
28
+ // router.layout = layout || new DefaultLayout();
29
+ // router.pages = pages;
30
+ // router.loaderComponent = loaderComponent || (() => null);
31
+ // router.notFoundComponent = notFoundComponent || NotFoundPageComponent;
32
+ // return router;
22
33
  };
23
34
  }
@@ -1,4 +1,5 @@
1
1
  /// <reference types="react" />
2
+ import { Metadata } from "../routing/types.js";
2
3
  import { LayoutComponent, PageComponent } from "../core/interfaces.js";
3
4
  import { RouterComponent } from "../routing/interfaces.js";
4
5
  /**
@@ -44,4 +45,8 @@ export type RouteLayoutDecoratorProps = {
44
45
  * base path of the layout
45
46
  */
46
47
  path: string;
48
+ /**
49
+ * Metadata
50
+ */
51
+ metadata?: Metadata[];
47
52
  };
@@ -4,16 +4,18 @@ import ReactDOMServer from "react-dom/server";
4
4
  // @ts-ignore
5
5
  import AppRouter from "./../../../../src/pages/app.router";
6
6
  // @ts-ignore
7
- import { generateStaticRoutes, } from "../routing/utils/index.js";
7
+ import App from "./../../../../src/main";
8
+ // @ts-ignore
9
+ import { generateStaticRoutes } from "../routing/utils/index.js";
8
10
  import { StaticRouterProvider, } from "react-router-dom/server.js";
9
11
  // @ts-ignore
10
12
  import config from "./../../../../rasengan.config.js";
11
- import { ErrorBoundary } from "../core/components";
13
+ import { Component, ErrorBoundary } from "../core/components";
12
14
  import * as pkg from "react-helmet-async";
13
15
  // @ts-ignore
14
16
  const { HelmetProvider } = pkg.default || pkg;
15
17
  export function render(router, context, helmetContext = {}) {
16
- const html = ReactDOMServer.renderToString(config.reactStrictMode ? (_jsx(React.StrictMode, { children: _jsx(ErrorBoundary, { children: _jsx(HelmetProvider, { context: helmetContext, children: _jsx(StaticRouterProvider, { router: router, context: context }) }) }) })) : (_jsx(ErrorBoundary, { children: _jsx(HelmetProvider, { context: helmetContext, children: _jsx(StaticRouterProvider, { router: router, context: context }) }) })));
18
+ const html = ReactDOMServer.renderToString(config.reactStrictMode ? (_jsx(React.StrictMode, { children: _jsx(ErrorBoundary, { children: _jsx(HelmetProvider, { context: helmetContext, children: _jsx(App, { Component: Component, children: _jsx(StaticRouterProvider, { router: router, context: context }) }) }) }) })) : (_jsx(ErrorBoundary, { children: _jsx(HelmetProvider, { context: helmetContext, children: _jsx(App, { Component: Component, children: _jsx(StaticRouterProvider, { router: router, context: context }) }) }) })));
17
19
  return { html };
18
20
  }
19
21
  export const staticRoutes = generateStaticRoutes(AppRouter);
@@ -30,3 +30,9 @@ export declare const NotFoundPageComponent: () => import("react/jsx-runtime").JS
30
30
  * Component that will be displayed when a page is not found
31
31
  */
32
32
  export declare const NotFoundComponentContainer: ({ content, }: NotFoundComponentContainerProps) => import("react/jsx-runtime").JSX.Element;
33
+ /**
34
+ * Custom Link Component
35
+ * @param props
36
+ * @returns React.ReactNode
37
+ */
38
+ export declare const CustomLink: (props: any) => import("react/jsx-runtime").JSX.Element;
@@ -66,3 +66,16 @@ export const NotFoundPageComponent = () => {
66
66
  export const NotFoundComponentContainer = ({ content, }) => {
67
67
  return _jsx(_Fragment, { children: content({}) });
68
68
  };
69
+ /**
70
+ * Custom Link Component
71
+ * @param props
72
+ * @returns React.ReactNode
73
+ */
74
+ export const CustomLink = (props) => {
75
+ const { to, children, ...rest } = props;
76
+ const splitted = to.split("#");
77
+ if (splitted.length > 1) {
78
+ return _jsx("a", { href: to, ...rest, children: children });
79
+ }
80
+ return _jsx(Link, { to: to, ...rest, children: children });
81
+ };
@@ -1,3 +1,6 @@
1
+ import { CustomLink } from "./components/index.js";
2
+ export type { Metadata } from "./types.js";
1
3
  export { defineRouteLayout, defineRoutePage, defineRouter, } from "./utils/index.js";
2
4
  export { RouterComponent } from "./interfaces.js";
3
- export { Outlet, Link, useLocation, useNavigate, useParams, useSearchParams, useFetcher, useMatch, useRoutes, useResolvedPath, matchRoutes, generatePath, matchPath, createRoutesFromChildren, Navigate, } from "react-router-dom";
5
+ export { Outlet, useLocation, useNavigate, useParams, useSearchParams, useFetcher, useMatch, useRoutes, useResolvedPath, matchRoutes, generatePath, matchPath, createRoutesFromChildren, Navigate, } from "react-router-dom";
6
+ export { CustomLink as Link };
@@ -1,3 +1,5 @@
1
+ import { CustomLink } from "./components/index.js";
1
2
  export { defineRouteLayout, defineRoutePage, defineRouter, } from "./utils/index.js";
2
3
  export { RouterComponent } from "./interfaces.js";
3
- export { Outlet, Link, useLocation, useNavigate, useParams, useSearchParams, useFetcher, useMatch, useRoutes, useResolvedPath, matchRoutes, generatePath, matchPath, createRoutesFromChildren, Navigate, } from "react-router-dom";
4
+ export { Outlet, useLocation, useNavigate, useParams, useSearchParams, useFetcher, useMatch, useRoutes, useResolvedPath, matchRoutes, generatePath, matchPath, createRoutesFromChildren, Navigate, } from "react-router-dom";
5
+ export { CustomLink as Link };
@@ -2,3 +2,15 @@
2
2
  export type NotFoundComponentContainerProps = {
3
3
  content: React.FC;
4
4
  };
5
+ export type MetadataLink = {
6
+ rel: string;
7
+ type?: string;
8
+ sizes?: string;
9
+ href: string;
10
+ };
11
+ export type MetadataTag = {
12
+ name?: string;
13
+ property?: string;
14
+ content: string;
15
+ };
16
+ export type Metadata = MetadataTag | MetadataLink;
@@ -1,6 +1,7 @@
1
1
  import { RouterComponent } from "../interfaces.js";
2
2
  import { RouteDecoratorProps, RouteLayoutDecoratorProps, RouterDecoratorProps } from "../../decorators/types.js";
3
3
  import { LayoutComponent, PageComponent } from "../../index.js";
4
+ import { Metadata } from "../types.js";
4
5
  /**
5
6
  * This function receives a router component and get a formated router first
6
7
  * and then return a router.
@@ -12,14 +13,6 @@ export declare const getRouter: (router: RouterComponent) => () => import("react
12
13
  * @returns
13
14
  */
14
15
  export declare const generateStaticRoutes: (router: RouterComponent, isRoot?: boolean) => any;
15
- /**
16
- * This function receives a router component and extract all metadatas of all pages
17
- * and put all of them inside a map function in order to be used to enhance ssr
18
- */
19
- export declare const extractPageMetadata: (router: RouterComponent) => Map<string, {
20
- title: string;
21
- description: string;
22
- }>;
23
16
  /**
24
17
  * This function adds metadata to a page or a layout
25
18
  * @param option
@@ -38,3 +31,8 @@ export declare const defineRouteLayout: (option: RouteLayoutDecoratorProps) => (
38
31
  * @returns
39
32
  */
40
33
  export declare const defineRouter: (option: RouterDecoratorProps) => (Component: new () => RouterComponent) => RouterComponent;
34
+ /**
35
+ * This function generates metadata useful for pages to show images when sharing on social media
36
+ * @param {Metadata[]} metadatas
37
+ */
38
+ export declare const generateMetadata: (metadatas: Metadata[]) => import("react/jsx-runtime").JSX.Element[];
@@ -1,6 +1,6 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
2
- import { RouterProvider, createBrowserRouter } from "react-router-dom";
3
- import { DefaultLayout } from "../../index.js";
2
+ import { RouterProvider, createBrowserRouter, useLoaderData, } from "react-router-dom";
3
+ import { DefaultLayout, } from "../../index.js";
4
4
  import { ClientComponent, NotFoundComponentContainer, NotFoundPageComponent, ServerComponent, } from "../components/index.js";
5
5
  import { ErrorBoundary } from "../../core/components/index.js";
6
6
  /**
@@ -24,7 +24,14 @@ const generateBrowserRoutes = (router, isRoot = true) => {
24
24
  const route = {
25
25
  path: layout.path,
26
26
  elementError: _jsx(ErrorBoundary, {}),
27
- element: _jsx(LayoutToRender, {}),
27
+ Component() {
28
+ // Default data
29
+ const defaultData = {
30
+ props: {},
31
+ };
32
+ const { props } = useLoaderData() || defaultData;
33
+ return _jsx(LayoutToRender, { ...props });
34
+ },
28
35
  children: [],
29
36
  };
30
37
  // Defining the page not found route
@@ -38,24 +45,10 @@ const generateBrowserRoutes = (router, isRoot = true) => {
38
45
  const pages = router.pages.map((page) => {
39
46
  // Get the path of the page
40
47
  const path = page.path === "/" ? layout.path : page.path;
48
+ // Add metadata to the page
49
+ page.addMetadata(layout.metadata);
41
50
  return {
42
51
  path,
43
- loader: async ({ params, request }) => {
44
- // Get the response from the loader
45
- const response = await page.loader({ params, request });
46
- // Handle redirection
47
- if (response.redirect) {
48
- const formData = new FormData();
49
- formData.append("redirect", response.redirect);
50
- return new Response(formData, {
51
- status: 302,
52
- headers: {
53
- Location: response.redirect,
54
- },
55
- });
56
- }
57
- return response;
58
- },
59
52
  element: (_jsx(ClientComponent, { page: page, loader: router.loaderComponent({}) })),
60
53
  elementError: _jsx(ErrorBoundary, {}),
61
54
  };
@@ -95,7 +88,32 @@ export const generateStaticRoutes = (router, isRoot = true) => {
95
88
  const route = {
96
89
  path: layout.path,
97
90
  elementError: _jsx(ErrorBoundary, {}),
98
- element: _jsx(LayoutToRender, {}),
91
+ Component() {
92
+ return _jsx(LayoutToRender, {});
93
+ },
94
+ loader: async ({ params, request }) => {
95
+ try {
96
+ // Get the response from the loader
97
+ const response = await layout.loader({ params, request });
98
+ // Handle redirection
99
+ if (response.redirect) {
100
+ const formData = new FormData();
101
+ formData.append("redirect", response.redirect);
102
+ return new Response(formData, {
103
+ status: 302,
104
+ headers: {
105
+ Location: response.redirect,
106
+ },
107
+ });
108
+ }
109
+ return response;
110
+ }
111
+ catch (error) {
112
+ return {
113
+ props: {},
114
+ };
115
+ }
116
+ },
99
117
  children: [],
100
118
  };
101
119
  // Defining the page not found route
@@ -109,23 +127,32 @@ export const generateStaticRoutes = (router, isRoot = true) => {
109
127
  const pages = router.pages.map((page) => {
110
128
  // Get the path of the page
111
129
  const path = page.path === "/" ? layout.path : page.path;
130
+ // Add metadata to the page
131
+ page.addMetadata(layout.metadata);
112
132
  return {
113
133
  path,
114
134
  async loader({ params, request }) {
115
- // Get the response from the loader
116
- const response = await page.loader({ params, request });
117
- // Handle redirection
118
- if (response.redirect) {
119
- const formData = new FormData();
120
- formData.append("redirect", response.redirect);
121
- return new Response(formData, {
122
- status: 302,
123
- headers: {
124
- Location: response.redirect,
125
- },
126
- });
135
+ try {
136
+ // Get the response from the loader
137
+ const response = await page.loader({ params, request });
138
+ // Handle redirection
139
+ if (response.redirect) {
140
+ const formData = new FormData();
141
+ formData.append("redirect", response.redirect);
142
+ return new Response(formData, {
143
+ status: 302,
144
+ headers: {
145
+ Location: response.redirect,
146
+ },
147
+ });
148
+ }
149
+ return response;
150
+ }
151
+ catch (error) {
152
+ return {
153
+ props: {},
154
+ };
127
155
  }
128
- return response;
129
156
  },
130
157
  Component() {
131
158
  return (_jsx(ServerComponent, { page: page, loader: router.loaderComponent({}) }));
@@ -154,54 +181,13 @@ export const generateStaticRoutes = (router, isRoot = true) => {
154
181
  // Return the formated router
155
182
  return routes;
156
183
  };
157
- /**
158
- * This function receives a router component and extract all metadatas of all pages
159
- * and put all of them inside a map function in order to be used to enhance ssr
160
- */
161
- export const extractPageMetadata = (router) => {
162
- // Initialisation of the Map of metadata
163
- const metadatas = new Map();
164
- // Get base url
165
- const baseURL = router.layout.path;
166
- // Get informations about pages of the main router
167
- router.pages.forEach((page) => {
168
- // Add the first slash if not exists from the page path
169
- const pagePath = page.path[0] === "/" ? page.path : "/" + page.path;
170
- // Remove the last slash if exists from the base url
171
- const finalBaseURL = baseURL === "/"
172
- ? baseURL
173
- : baseURL[baseURL.length - 1] === "/"
174
- ? baseURL.slice(0, -1)
175
- : baseURL;
176
- // Get the path of the page
177
- const path = pagePath === "/"
178
- ? finalBaseURL
179
- : finalBaseURL === "/"
180
- ? pagePath
181
- : finalBaseURL + "/" + pagePath;
182
- // Add metadata
183
- metadatas.set(path, {
184
- title: page.title,
185
- description: page.description,
186
- });
187
- });
188
- // Loop through the others routers recursively
189
- for (let besidesRouter of router.routers) {
190
- const data = extractPageMetadata(besidesRouter);
191
- // Copy metadata from data to metadatas map
192
- data.forEach((value, key) => {
193
- metadatas.set(key, value);
194
- });
195
- }
196
- return metadatas;
197
- };
198
184
  /**
199
185
  * This function adds metadata to a page or a layout
200
186
  * @param option
201
187
  * @returns
202
188
  */
203
189
  export const defineRoutePage = (option) => {
204
- const { path, title, description } = option;
190
+ const { path, title, description, metadata } = option;
205
191
  return (Component) => {
206
192
  if (!path)
207
193
  throw new Error("You must provide a path to the page");
@@ -211,6 +197,7 @@ export const defineRoutePage = (option) => {
211
197
  component.path = path;
212
198
  component.title = title || Component.name;
213
199
  component.description = description || "";
200
+ component.metadata = metadata || [];
214
201
  return component;
215
202
  };
216
203
  };
@@ -220,7 +207,7 @@ export const defineRoutePage = (option) => {
220
207
  * @returns
221
208
  */
222
209
  export const defineRouteLayout = (option) => {
223
- const { path } = option;
210
+ const { path, metadata } = option;
224
211
  return (Component) => {
225
212
  if (!path)
226
213
  throw new Error("You must provide a path to the layout");
@@ -228,6 +215,7 @@ export const defineRouteLayout = (option) => {
228
215
  const component = new Component();
229
216
  // Set properties
230
217
  component.path = path;
218
+ component.metadata = metadata || [];
231
219
  return component;
232
220
  };
233
221
  };
@@ -253,3 +241,20 @@ export const defineRouter = (option) => {
253
241
  return router;
254
242
  };
255
243
  };
244
+ /**
245
+ * This function generates metadata useful for pages to show images when sharing on social media
246
+ * @param {Metadata[]} metadatas
247
+ */
248
+ export const generateMetadata = (metadatas) => {
249
+ return metadatas.map((metadata) => {
250
+ const { rel, sizes, type, href } = metadata;
251
+ const { content, name, property } = metadata;
252
+ if (rel && href) {
253
+ return (_jsx("link", { rel: rel, sizes: sizes, type: type, href: href }, rel));
254
+ }
255
+ if (property) {
256
+ return (_jsx("meta", { property: property, content: content }, property));
257
+ }
258
+ return (_jsx("meta", { name: name, content: content }, name));
259
+ });
260
+ };
@@ -4,6 +4,10 @@
4
4
  "src": "/assets/(.*)",
5
5
  "dest": "client/assets/$1"
6
6
  },
7
+ {
8
+ "src": "/(.+\\.(png|ico|svg|jpg|jpeg|gif))",
9
+ "dest": "client/$1"
10
+ },
7
11
  {
8
12
  "src": "/(.*)",
9
13
  "dest": "api/index.js"
@@ -2,5 +2,6 @@
2
2
  * Log server info after the server is started
3
3
  * @param {number} port The port the server is running on
4
4
  * @param {boolean} isProduction Whether the server is running in production mode
5
+ * @param {boolean} open Whether to open the browser automatically
5
6
  */
6
- export declare function logServerInfo(port: number, isProduction: boolean): Promise<void>;
7
+ export declare function logServerInfo(port: number, isProduction: boolean, open?: boolean): Promise<void>;
@@ -3,13 +3,15 @@ import ora from "ora";
3
3
  import fs from "fs/promises";
4
4
  // @ts-ignore
5
5
  import keypress from "keypress";
6
+ import openBrowser from "open";
6
7
  import { getIP } from "./index.js";
7
8
  /**
8
9
  * Log server info after the server is started
9
10
  * @param {number} port The port the server is running on
10
11
  * @param {boolean} isProduction Whether the server is running in production mode
12
+ * @param {boolean} open Whether to open the browser automatically
11
13
  */
12
- export async function logServerInfo(port, isProduction) {
14
+ export async function logServerInfo(port, isProduction, open = false) {
13
15
  // Constants
14
16
  const arrowRight = "\u2192";
15
17
  // Spinner
@@ -43,6 +45,10 @@ export async function logServerInfo(port, isProduction) {
43
45
  process.stdout.write(`${chalk.bold.green(arrowRight)} ${chalk.gray("Press")} ${chalk.bold("c")} ${chalk.gray("to clear")}\n`);
44
46
  process.stdout.write(`${chalk.bold.green(arrowRight)} ${chalk.gray("Press")} ${chalk.bold("ctrl + c")} ${chalk.gray("to close the server")}\n`);
45
47
  console.log("\n");
48
+ // Open the browser
49
+ if (open) {
50
+ openBrowser(`http://localhost:${port}`);
51
+ }
46
52
  // Enable keypress events on the process.stdin stream
47
53
  keypress(process.stdin);
48
54
  // Listen on user keyboard input on the terminal
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "rasengan",
3
3
  "private": false,
4
- "version": "1.0.0-beta.5",
4
+ "version": "1.0.0-beta.7",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "bin": {
@@ -21,6 +21,9 @@
21
21
  "./types/image": {
22
22
  "types": "./types/image.d.ts"
23
23
  },
24
+ "./types/style": {
25
+ "types": "./types/style.d.ts"
26
+ },
24
27
  "./package.json": "./package.json"
25
28
  },
26
29
  "author": {
@@ -59,6 +62,7 @@
59
62
  "inquirer": "^9.2.12",
60
63
  "keypress": "^0.2.1",
61
64
  "node-fetch": "^3.3.2",
65
+ "open": "^10.1.0",
62
66
  "ora": "^7.0.1",
63
67
  "react-helmet-async": "^2.0.4",
64
68
  "react-router-dom": "^6.20.1",
package/server.js CHANGED
@@ -31,6 +31,7 @@ async function createServer({
31
31
  port,
32
32
  base = "/",
33
33
  enableSearchingPort = false,
34
+ open = false
34
35
  }) {
35
36
  // Get directory name
36
37
  const __dirname = dirname(fileURLToPath(import.meta.url));
@@ -60,7 +61,7 @@ async function createServer({
60
61
  server: { middlewareMode: true },
61
62
  appType: "custom",
62
63
  base,
63
- configFile: "node_modules/rasengan/vite.config.js"
64
+ configFile: "node_modules/rasengan/vite.config.js",
64
65
  });
65
66
  app.use(vite.middlewares);
66
67
  } else {
@@ -157,7 +158,7 @@ async function createServer({
157
158
  // Start http server
158
159
  const server = app.listen(port, () => {
159
160
  setTimeout(() => {
160
- logServerInfo(port, isProduction);
161
+ logServerInfo(port, isProduction, open);
161
162
  }, 100);
162
163
  });
163
164
 
@@ -200,6 +201,7 @@ async function createServer({
200
201
  port: newPort,
201
202
  base,
202
203
  enableSearchingPort: true,
204
+ open
203
205
  });
204
206
  } else {
205
207
  console.log(chalk.blue(`Trying port ${newPort}... \n\n`));
@@ -209,6 +211,7 @@ async function createServer({
209
211
  port: newPort,
210
212
  base,
211
213
  enableSearchingPort,
214
+ open
212
215
  });
213
216
  }
214
217
  }
@@ -226,5 +229,10 @@ const base = process.env.BASE || "/";
226
229
 
227
230
  // Launch server
228
231
  (async function launchServer() {
229
- await createServer({ isProduction, port, base });
232
+ await createServer({
233
+ isProduction,
234
+ port,
235
+ base,
236
+ open: config.server?.development?.open,
237
+ });
230
238
  })();
package/tsconfig.json CHANGED
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "compilerOptions": {
3
+ "baseUrl": ".",
3
4
  "target": "ES2020",
4
5
  "useDefineForClassFields": true,
5
6
  "lib": ["ES2020", "DOM", "DOM.Iterable"],
@@ -23,8 +24,12 @@
23
24
 
24
25
  /* Plugins */
25
26
  "plugins": [{ "name": "typescript-plugin-css-modules" }],
27
+
28
+ "paths": {
29
+ "@/*": ["src/*"]
30
+ }
26
31
  },
27
- "include": ["src", "server.js", "types", "types"],
32
+ "include": ["src", "server.js", "types"],
28
33
 
29
34
  "references": [{ "path": "./tsconfig.node.json" }]
30
35
  }
@@ -0,0 +1,4 @@
1
+ declare module "*.module.css" {
2
+ const classes: { [key: string]: string };
3
+ export default classes;
4
+ }