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 +3 -4
- package/lib/config/index.d.ts +1 -1
- package/lib/config/index.js +1 -1
- package/lib/config/type.d.ts +2 -1
- package/lib/core/components/index.d.ts +1 -1
- package/lib/core/components/index.js +11 -3
- package/lib/core/interfaces.d.ts +23 -6
- package/lib/core/interfaces.js +36 -12
- package/lib/core/types.d.ts +3 -3
- package/lib/decorators/route.d.ts +2 -1
- package/lib/decorators/route.js +8 -12
- package/lib/decorators/router.d.ts +2 -1
- package/lib/decorators/router.js +23 -12
- package/lib/decorators/types.d.ts +5 -0
- package/lib/entries/entry-server.js +5 -3
- package/lib/routing/components/index.d.ts +6 -0
- package/lib/routing/components/index.js +13 -0
- package/lib/routing/index.d.ts +4 -1
- package/lib/routing/index.js +3 -1
- package/lib/routing/types.d.ts +12 -0
- package/lib/routing/utils/index.d.ts +6 -8
- package/lib/routing/utils/index.js +81 -76
- package/lib/server/functions/vercel/vercel.json +4 -0
- package/lib/server/utils/log.d.ts +2 -1
- package/lib/server/utils/log.js +7 -1
- package/package.json +5 -1
- package/server.js +11 -3
- package/tsconfig.json +6 -1
- package/types/style.d.ts +4 -0
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("
|
|
49
|
-
.description("
|
|
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("
|
|
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;
|
package/lib/config/index.d.ts
CHANGED
package/lib/config/index.js
CHANGED
|
@@ -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 ||
|
|
28
|
+
open: server?.development?.open || false,
|
|
29
29
|
},
|
|
30
30
|
production: {
|
|
31
31
|
hosting: server?.production?.hosting || "custom",
|
package/lib/config/type.d.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
package/lib/core/interfaces.d.ts
CHANGED
|
@@ -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
|
}
|
package/lib/core/interfaces.js
CHANGED
|
@@ -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
|
}
|
package/lib/core/types.d.ts
CHANGED
|
@@ -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
|
-
|
|
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): (
|
|
8
|
+
export declare function Route(props: RouteDecoratorProps): (Component: new () => PageComponent) => void;
|
package/lib/decorators/route.js
CHANGED
|
@@ -5,20 +5,16 @@
|
|
|
5
5
|
* @returns
|
|
6
6
|
*/
|
|
7
7
|
export function Route(props) {
|
|
8
|
-
return function (
|
|
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
|
-
//
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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(
|
|
8
|
+
export declare function Router(option: RouterDecoratorProps): (Component: new () => RouterComponent) => void;
|
package/lib/decorators/router.js
CHANGED
|
@@ -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(
|
|
9
|
-
|
|
9
|
+
export function Router(option) {
|
|
10
|
+
const { imports, layout, pages, loaderComponent, notFoundComponent } = option;
|
|
11
|
+
return (Component) => {
|
|
10
12
|
// Handle errors
|
|
11
|
-
if (!
|
|
13
|
+
if (!option.pages)
|
|
12
14
|
throw new Error("You must provide a list of pages in the router decorator");
|
|
13
|
-
//
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
Object.seal(
|
|
21
|
-
Object.seal(
|
|
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
|
|
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
|
+
};
|
package/lib/routing/index.d.ts
CHANGED
|
@@ -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,
|
|
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 };
|
package/lib/routing/index.js
CHANGED
|
@@ -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,
|
|
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 };
|
package/lib/routing/types.d.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
+
};
|
|
@@ -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>;
|
package/lib/server/utils/log.js
CHANGED
|
@@ -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.
|
|
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({
|
|
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"
|
|
32
|
+
"include": ["src", "server.js", "types"],
|
|
28
33
|
|
|
29
34
|
"references": [{ "path": "./tsconfig.node.json" }]
|
|
30
35
|
}
|
package/types/style.d.ts
ADDED