rasengan 1.0.0-beta.57 → 1.0.0-beta.59
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/CHANGELOG.md +4 -0
- package/lib/esm/core/config/utils/define-config.js +3 -1
- package/lib/esm/core/config/vite/defaults.js +10 -6
- package/lib/esm/core/plugins/index.js +67 -16
- package/lib/esm/entries/client/render.js +13 -1
- package/lib/esm/entries/server/index.js +13 -2
- package/lib/esm/routing/components/template.js +10 -6
- package/lib/esm/routing/utils/define-routes-group.js +1 -1
- package/lib/esm/routing/utils/generate-routes.js +63 -13
- package/lib/esm/server/build/rendering.js +18 -0
- package/lib/esm/server/dev/handlers.js +29 -0
- package/lib/esm/server/dev/server.js +21 -15
- package/lib/esm/server/node/rendering.js +1 -1
- package/lib/tsconfig.esm.tsbuildinfo +1 -1
- package/lib/tsconfig.types.tsbuildinfo +1 -1
- package/lib/types/core/config/type.d.ts +1 -0
- package/lib/types/entries/server/index.d.ts +5 -4
- package/lib/types/server/build/rendering.d.ts +7 -0
- package/lib/types/server/dev/handlers.d.ts +5 -0
- package/lib/types/server/node/rendering.d.ts +1 -1
- package/package.json +2 -2
- package/types/client.d.ts +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -21,7 +21,7 @@ export const defineConfig = async (loadedConfig) => {
|
|
|
21
21
|
else {
|
|
22
22
|
config = loadedConfig;
|
|
23
23
|
}
|
|
24
|
-
const { server, vite, redirects } = config;
|
|
24
|
+
const { ssr, server, vite, redirects } = config;
|
|
25
25
|
// Define default values for vite config coming from loadedConfig.vite
|
|
26
26
|
const defaultViteConfig = {
|
|
27
27
|
...vite,
|
|
@@ -41,6 +41,7 @@ export const defineConfig = async (loadedConfig) => {
|
|
|
41
41
|
const defaultRedirectsConfig = redirects || (() => new Promise((resolve) => resolve([])));
|
|
42
42
|
try {
|
|
43
43
|
const config = {
|
|
44
|
+
ssr: ssr ?? true,
|
|
44
45
|
server: defaultServerConfig,
|
|
45
46
|
vite: {
|
|
46
47
|
...defaultViteConfig,
|
|
@@ -60,6 +61,7 @@ export const defineConfig = async (loadedConfig) => {
|
|
|
60
61
|
}
|
|
61
62
|
catch (error) {
|
|
62
63
|
return {
|
|
64
|
+
ssr: true,
|
|
63
65
|
vite: {
|
|
64
66
|
appType: 'custom',
|
|
65
67
|
resolve: {
|
|
@@ -28,11 +28,13 @@ export const createDefaultViteConfig = (rootPath, __dirname, mode, config) => {
|
|
|
28
28
|
return 'vendor';
|
|
29
29
|
if (id.includes('src/components'))
|
|
30
30
|
return 'shared-components';
|
|
31
|
-
if (
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
if (config.ssr) {
|
|
32
|
+
if (id.includes('src/app') && id.includes('.page.')) {
|
|
33
|
+
const parts = id.split('src/app')[1]?.split('/');
|
|
34
|
+
if (parts?.length) {
|
|
35
|
+
const pageName = parts.pop()?.split('.')[0];
|
|
36
|
+
return pageName ? `page-${pageName}` : undefined;
|
|
37
|
+
}
|
|
36
38
|
}
|
|
37
39
|
}
|
|
38
40
|
return undefined;
|
|
@@ -78,7 +80,9 @@ export const createDefaultViteConfig = (rootPath, __dirname, mode, config) => {
|
|
|
78
80
|
},
|
|
79
81
|
builder: {
|
|
80
82
|
buildApp: async (builder) => {
|
|
81
|
-
|
|
83
|
+
if (config.ssr) {
|
|
84
|
+
await builder.build(builder.environments.ssr);
|
|
85
|
+
}
|
|
82
86
|
await builder.build(builder.environments.client);
|
|
83
87
|
},
|
|
84
88
|
},
|
|
@@ -1,30 +1,34 @@
|
|
|
1
|
-
import { resolve } from 'path';
|
|
1
|
+
import path, { resolve } from 'path';
|
|
2
2
|
import fs from 'fs';
|
|
3
3
|
import { loadModuleSSR } from '../config/utils/load-modules.js';
|
|
4
|
-
|
|
4
|
+
import { resolveBuildOptions } from '../../server.js';
|
|
5
|
+
import { renderIndexHTML } from '../../server/build/rendering.js';
|
|
6
|
+
function loadRasenganGlobal() {
|
|
5
7
|
return {
|
|
6
8
|
name: 'vite-plugin-rasengan-config',
|
|
7
9
|
async config(_, { command }) {
|
|
8
10
|
if (command !== 'build')
|
|
9
11
|
return;
|
|
10
|
-
const
|
|
11
|
-
if (!fs.existsSync(
|
|
12
|
-
throw new Error(`
|
|
12
|
+
const packageJsonPath = resolve(process.cwd(), 'package.json');
|
|
13
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
14
|
+
throw new Error(`Package.json file not found at: ${packageJsonPath}`);
|
|
13
15
|
}
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
const packageJsonRaw = fs.readFileSync(packageJsonPath, {
|
|
17
|
+
encoding: 'utf-8',
|
|
18
|
+
});
|
|
19
|
+
const packageJson = JSON.parse(packageJsonRaw);
|
|
20
|
+
const rasenganConfig = {
|
|
21
|
+
version: packageJson.version,
|
|
22
|
+
ssr: true,
|
|
20
23
|
};
|
|
21
24
|
// Inject the configuration as a global constant
|
|
22
25
|
return {
|
|
23
26
|
define: {
|
|
24
|
-
['
|
|
27
|
+
['Rasengan']: JSON.stringify(rasenganConfig),
|
|
25
28
|
},
|
|
26
29
|
};
|
|
27
30
|
},
|
|
31
|
+
apply: 'build',
|
|
28
32
|
};
|
|
29
33
|
}
|
|
30
34
|
function rasenganConfigPlugin() {
|
|
@@ -89,21 +93,67 @@ export const Adapters = {
|
|
|
89
93
|
DEFAULT: '',
|
|
90
94
|
};
|
|
91
95
|
export function rasengan({ adapter = { name: Adapters.DEFAULT, prepare: async () => { } }, }) {
|
|
92
|
-
let config
|
|
96
|
+
let config;
|
|
97
|
+
let viteConfig;
|
|
98
|
+
const templateFileRegex = /template\.(tsx|jsx)$/;
|
|
99
|
+
const buildOptions = resolveBuildOptions({});
|
|
93
100
|
return {
|
|
94
101
|
name: 'vite-plugin-rasengan',
|
|
102
|
+
async config() {
|
|
103
|
+
// load rasengan.config.js
|
|
104
|
+
const configPath = resolve(process.cwd(), 'rasengan.config.js');
|
|
105
|
+
if (!fs.existsSync(configPath)) {
|
|
106
|
+
throw new Error(`Configuration file not found at: ${configPath}`);
|
|
107
|
+
}
|
|
108
|
+
const rasenganConfigHandler = await (await loadModuleSSR(configPath)).default;
|
|
109
|
+
config = await rasenganConfigHandler();
|
|
110
|
+
},
|
|
111
|
+
async load(id) {
|
|
112
|
+
if (id === 'virtual:rasengan-config') {
|
|
113
|
+
return `
|
|
114
|
+
export const __RASENGAN_CONFIG__ = ${JSON.stringify(config)};
|
|
115
|
+
`;
|
|
116
|
+
}
|
|
117
|
+
},
|
|
95
118
|
configResolved(resolvedConfig) {
|
|
96
|
-
|
|
119
|
+
viteConfig = resolvedConfig;
|
|
120
|
+
},
|
|
121
|
+
async writeBundle(_) {
|
|
122
|
+
const modulePaths = ['template.jsx', 'template.tsx'].map((file) => {
|
|
123
|
+
return path.posix.join(process.cwd(), 'src', file);
|
|
124
|
+
});
|
|
125
|
+
const modulePath = modulePaths.find((modulePath) => {
|
|
126
|
+
return fs.existsSync(modulePath);
|
|
127
|
+
});
|
|
128
|
+
const module = await this.load({ id: modulePath });
|
|
129
|
+
// Generate the template.js file into the dist/assets
|
|
130
|
+
fs.writeFileSync(path.posix.join(process.cwd(), buildOptions.buildDirectory, buildOptions.clientPathDirectory, buildOptions.assetPathDirectory, 'template.js'), module.code, 'utf-8');
|
|
97
131
|
},
|
|
98
132
|
async closeBundle() {
|
|
99
133
|
// We check here if the environment is client has been built because it's the
|
|
100
134
|
// last environment to be built in the Vite build process
|
|
101
135
|
if (this.environment.name === 'client') {
|
|
136
|
+
// Check if SPA mode is enabled
|
|
137
|
+
if (!config.ssr) {
|
|
138
|
+
// Load the template.js file
|
|
139
|
+
const templatePath = path.posix.join(process.cwd(), buildOptions.buildDirectory, buildOptions.clientPathDirectory, buildOptions.assetPathDirectory, 'template.js');
|
|
140
|
+
const Template = (await import(templatePath)).default;
|
|
141
|
+
// Render the index.html file
|
|
142
|
+
await renderIndexHTML(Template, {
|
|
143
|
+
rootPath: process.cwd(),
|
|
144
|
+
config,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
// Generate a config.json file into the dist/client/assets
|
|
148
|
+
fs.writeFileSync(path.posix.join(process.cwd(), buildOptions.buildDirectory, buildOptions.clientPathDirectory, buildOptions.assetPathDirectory, 'config.json'), JSON.stringify({
|
|
149
|
+
buildOptions,
|
|
150
|
+
ssr: config.ssr,
|
|
151
|
+
}), 'utf-8');
|
|
102
152
|
// Preparing app for deployment
|
|
103
153
|
switch (adapter.name) {
|
|
104
154
|
case Adapters.VERCEL: {
|
|
105
155
|
console.log('Preparing app for deployment to Vercel');
|
|
106
|
-
await adapter.prepare();
|
|
156
|
+
// await adapter.prepare();
|
|
107
157
|
break;
|
|
108
158
|
}
|
|
109
159
|
default:
|
|
@@ -111,6 +161,7 @@ export function rasengan({ adapter = { name: Adapters.DEFAULT, prepare: async ()
|
|
|
111
161
|
}
|
|
112
162
|
}
|
|
113
163
|
},
|
|
164
|
+
apply: 'build',
|
|
114
165
|
};
|
|
115
166
|
}
|
|
116
|
-
export const plugins = [];
|
|
167
|
+
export const plugins = [loadRasenganGlobal()];
|
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { hydrateRoot } from 'react-dom/client';
|
|
2
|
+
import { hydrateRoot, createRoot } from 'react-dom/client';
|
|
3
3
|
import { StrictMode } from 'react';
|
|
4
4
|
import { RootComponent } from '../../routing/components/template.js';
|
|
5
|
+
const isSpaMode = window.__RASENGAN_SPA_MODE__;
|
|
5
6
|
export default function renderApp(App, options) {
|
|
6
7
|
const root = document.getElementById('root');
|
|
7
8
|
if (!root) {
|
|
8
9
|
throw new Error('Root element not found');
|
|
9
10
|
}
|
|
11
|
+
// If SPA mode, render the app
|
|
12
|
+
if (isSpaMode) {
|
|
13
|
+
if (options.reactStrictMode) {
|
|
14
|
+
createRoot(root).render(_jsx(StrictMode, { children: _jsx(App, { Component: RootComponent }) }));
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
createRoot(root).render(_jsx(App, { Component: RootComponent }));
|
|
18
|
+
}
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
// Handling hydration
|
|
10
22
|
if (options.reactStrictMode) {
|
|
11
23
|
hydrateRoot(root, _jsx(StrictMode, { children: _jsx(App, { Component: RootComponent }) }));
|
|
12
24
|
}
|
|
@@ -2,9 +2,10 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import { BodyComponent, HeadComponent, RootComponent, ScriptComponent, } from '../../routing/components/template.js';
|
|
4
4
|
import { isServerMode, ServerMode } from '../../server/runtime/mode.js';
|
|
5
|
-
export const TemplateLayout = ({ StaticRouterComponent, metadata, assets, App, Template, }) => {
|
|
5
|
+
export const TemplateLayout = ({ StaticRouterComponent, metadata, assets, App, Template, isSpaMode = false, }) => {
|
|
6
6
|
// inject vite refresh script to avoid "React refresh preamble was not loaded"
|
|
7
7
|
let viteScripts = _jsx(React.Fragment, {});
|
|
8
|
+
let otherScripts = _jsx(React.Fragment, {});
|
|
8
9
|
if (isServerMode(process.env.NODE_ENV) &&
|
|
9
10
|
process.env.NODE_ENV === ServerMode.Development) {
|
|
10
11
|
const refreshScript = `
|
|
@@ -16,5 +17,15 @@ export const TemplateLayout = ({ StaticRouterComponent, metadata, assets, App, T
|
|
|
16
17
|
`;
|
|
17
18
|
viteScripts = (_jsxs(React.Fragment, { children: [_jsx("script", { type: "module", src: "/@vite/client" }), _jsx("script", { type: "module", dangerouslySetInnerHTML: { __html: refreshScript } })] }));
|
|
18
19
|
}
|
|
19
|
-
|
|
20
|
+
if (isSpaMode) {
|
|
21
|
+
otherScripts = (_jsxs(React.Fragment, { children: [_jsx("script", { type: "module", dangerouslySetInnerHTML: {
|
|
22
|
+
__html: `window.__RASENGAN_SPA_MODE__ = true;`,
|
|
23
|
+
} }), !assets && (_jsx("script", { type: "module", src: "/src/index", async: true }))] }));
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
otherScripts = (_jsx(React.Fragment, { children: _jsx("script", { type: "module", dangerouslySetInnerHTML: {
|
|
27
|
+
__html: `window.__RASENGAN_SPA_MODE__ = false;`,
|
|
28
|
+
} }) }));
|
|
29
|
+
}
|
|
30
|
+
return (_jsx(Template, { Head: ({ children }) => (_jsxs(HeadComponent, { metadata: metadata, assets: assets, children: [viteScripts, otherScripts, children] })), Body: ({ children }) => (_jsx(BodyComponent, { asChild: App ? true : false, AppContent: App && _jsx(App, { Component: RootComponent, children: StaticRouterComponent }), children: children })), Script: ({ children }) => _jsx(ScriptComponent, { children: children }) }));
|
|
20
31
|
};
|
|
@@ -23,18 +23,22 @@ export const HeadComponent = ({ metadata, assets = [], children = undefined, })
|
|
|
23
23
|
// Generate meta tags
|
|
24
24
|
const metaTags = React.useMemo(() => {
|
|
25
25
|
const metadatas = [];
|
|
26
|
-
if (metadata
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
if (metadata) {
|
|
27
|
+
if (metadata.page)
|
|
28
|
+
metadatas.push(metadata.page);
|
|
29
|
+
if (metadata.layout)
|
|
30
|
+
metadatas.push(metadata.layout);
|
|
31
|
+
}
|
|
30
32
|
return generateMetadata(metadatas);
|
|
31
33
|
}, [metadata]);
|
|
32
34
|
const { title, description } = useMemo(() => {
|
|
35
|
+
if (!metadata)
|
|
36
|
+
return { title: 'Rasengan', description: '' };
|
|
33
37
|
const title = metadata.page.title;
|
|
34
38
|
const description = metadata.page.description;
|
|
35
39
|
return { title, description };
|
|
36
40
|
}, [metadata]);
|
|
37
|
-
return (_jsxs("head", { children: [metaTags, assets, _jsx("title", { children: title }), _jsx("meta", { name: "description", content: description, "data-rg": "true" }), children] }));
|
|
41
|
+
return (_jsxs("head", { children: [_jsx("meta", { name: "generator", content: "Rasengan.js" }), metaTags, assets, _jsx("title", { children: title }), _jsx("meta", { name: "description", content: description, "data-rg": "true" }), children] }));
|
|
38
42
|
};
|
|
39
43
|
/**
|
|
40
44
|
* Body component
|
|
@@ -42,7 +46,7 @@ export const HeadComponent = ({ metadata, assets = [], children = undefined, })
|
|
|
42
46
|
export const BodyComponent = ({ children = undefined, asChild = false, AppContent = undefined, }) => {
|
|
43
47
|
return (_jsxs("body", { children: [_jsx("noscript", { dangerouslySetInnerHTML: {
|
|
44
48
|
__html: `<b>Enable JavaScript to run this app.</b>`,
|
|
45
|
-
} }),
|
|
49
|
+
} }), _jsx("div", { id: "root", children: asChild && AppContent }), children] }));
|
|
46
50
|
};
|
|
47
51
|
/**
|
|
48
52
|
* Scripts component
|
|
@@ -24,7 +24,7 @@ const generateRoutesGroup = (path, children) => {
|
|
|
24
24
|
pages.push(...childrenPages);
|
|
25
25
|
}
|
|
26
26
|
else {
|
|
27
|
-
const routePath = path[0] === '/' ? path : `/${path}`;
|
|
27
|
+
const routePath = path === '/' ? '' : path[0] === '/' ? path : `/${path}`;
|
|
28
28
|
// Check if the page is a PageComponent
|
|
29
29
|
if (page['path']) {
|
|
30
30
|
const pagePath = page['path'][0] === '/' ? page['path'].slice(1) : page['path'];
|
|
@@ -20,16 +20,27 @@ export const getRouter = (routerInstance) => {
|
|
|
20
20
|
/**
|
|
21
21
|
* This function merge the metadata, giving priority to the ones comming from loader
|
|
22
22
|
*/
|
|
23
|
-
const mergeMetaData = (responseMeta, meta) => {
|
|
23
|
+
const mergeMetaData = (responseMeta, meta, isLayout = false) => {
|
|
24
24
|
let mergedMetaData = {
|
|
25
25
|
metaTags: [],
|
|
26
26
|
links: [],
|
|
27
|
+
openGraph: {
|
|
28
|
+
url: '',
|
|
29
|
+
image: '',
|
|
30
|
+
},
|
|
31
|
+
twitter: {
|
|
32
|
+
card: 'summary_large_image',
|
|
33
|
+
image: '',
|
|
34
|
+
title: '',
|
|
35
|
+
},
|
|
27
36
|
};
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
37
|
+
if (!isLayout) {
|
|
38
|
+
// merge title and description
|
|
39
|
+
mergedMetaData['title'] =
|
|
40
|
+
responseMeta.title ?? meta.title;
|
|
41
|
+
mergedMetaData['description'] =
|
|
42
|
+
responseMeta.description ?? meta.description;
|
|
43
|
+
}
|
|
33
44
|
// merge openGraph datas
|
|
34
45
|
mergedMetaData['openGraph'] = {
|
|
35
46
|
...meta.openGraph,
|
|
@@ -100,7 +111,7 @@ const mergeMetaData = (responseMeta, meta) => {
|
|
|
100
111
|
/**
|
|
101
112
|
* This function create a loader function
|
|
102
113
|
*/
|
|
103
|
-
const createLoaderFunction = ({ loader, metadata, }) => {
|
|
114
|
+
const createLoaderFunction = ({ loader, metadata, isLayout = false, }) => {
|
|
104
115
|
return async ({ params, request }) => {
|
|
105
116
|
try {
|
|
106
117
|
// Check if the loader is defined
|
|
@@ -125,14 +136,26 @@ const createLoaderFunction = ({ loader, metadata, }) => {
|
|
|
125
136
|
}
|
|
126
137
|
return {
|
|
127
138
|
props: response.props,
|
|
128
|
-
meta: mergeMetaData(response.meta, metadata),
|
|
139
|
+
meta: mergeMetaData(response.meta ?? {}, metadata, isLayout),
|
|
129
140
|
};
|
|
130
141
|
}
|
|
131
142
|
catch (error) {
|
|
132
143
|
console.error(error);
|
|
133
144
|
return {
|
|
134
145
|
props: {},
|
|
135
|
-
meta: {
|
|
146
|
+
meta: {
|
|
147
|
+
openGraph: {
|
|
148
|
+
url: '',
|
|
149
|
+
image: '',
|
|
150
|
+
},
|
|
151
|
+
twitter: {
|
|
152
|
+
card: 'summary_large_image',
|
|
153
|
+
image: '',
|
|
154
|
+
title: '',
|
|
155
|
+
},
|
|
156
|
+
metaTags: [],
|
|
157
|
+
links: [],
|
|
158
|
+
},
|
|
136
159
|
};
|
|
137
160
|
}
|
|
138
161
|
};
|
|
@@ -171,14 +194,30 @@ export const generateRoutes = (router, isRoot = true, parentLayout = undefined)
|
|
|
171
194
|
},
|
|
172
195
|
async loader({ params, request }) {
|
|
173
196
|
// Extract metadata from the layout
|
|
174
|
-
const metadata =
|
|
175
|
-
|
|
197
|
+
const metadata = {
|
|
198
|
+
openGraph: {
|
|
199
|
+
url: '',
|
|
200
|
+
image: '',
|
|
201
|
+
},
|
|
202
|
+
twitter: {
|
|
203
|
+
card: 'summary_large_image',
|
|
204
|
+
image: '',
|
|
205
|
+
title: '',
|
|
206
|
+
},
|
|
207
|
+
...Layout.metadata,
|
|
208
|
+
};
|
|
209
|
+
return createLoaderFunction({
|
|
210
|
+
loader: Layout.loader,
|
|
211
|
+
metadata,
|
|
212
|
+
isLayout: true,
|
|
213
|
+
})({
|
|
176
214
|
params,
|
|
177
215
|
request,
|
|
178
216
|
});
|
|
179
217
|
},
|
|
180
218
|
children: [],
|
|
181
219
|
nested: router.useParentLayout,
|
|
220
|
+
hydrateFallbackElement: _jsx(_Fragment, {}), // TODO: Add hydration fallback
|
|
182
221
|
};
|
|
183
222
|
// Defining the page not found route
|
|
184
223
|
if (isRoot || router.notFoundComponent) {
|
|
@@ -208,7 +247,18 @@ export const generateRoutes = (router, isRoot = true, parentLayout = undefined)
|
|
|
208
247
|
path,
|
|
209
248
|
async loader({ params, request }) {
|
|
210
249
|
// Extracting metadata from the page
|
|
211
|
-
const metadata =
|
|
250
|
+
const metadata = {
|
|
251
|
+
openGraph: {
|
|
252
|
+
url: '',
|
|
253
|
+
image: '',
|
|
254
|
+
},
|
|
255
|
+
twitter: {
|
|
256
|
+
card: 'summary_large_image',
|
|
257
|
+
image: '',
|
|
258
|
+
title: '',
|
|
259
|
+
},
|
|
260
|
+
...Page.metadata,
|
|
261
|
+
};
|
|
212
262
|
return createLoaderFunction({ loader: Page.loader, metadata })({
|
|
213
263
|
params,
|
|
214
264
|
request,
|
|
@@ -225,7 +275,7 @@ export const generateRoutes = (router, isRoot = true, parentLayout = undefined)
|
|
|
225
275
|
return (_jsx(Suspense, { fallback: _jsx(_Fragment, { children: "Loading" }), children: _jsx(RasenganPageComponent, { page: Page, data: loaderData }) }));
|
|
226
276
|
},
|
|
227
277
|
errorElement: _jsx(ErrorBoundary, {}),
|
|
228
|
-
hydrateFallbackElement: _jsx(_Fragment, {
|
|
278
|
+
hydrateFallbackElement: _jsx(_Fragment, {}), // TODO: Add hydration fallback
|
|
229
279
|
};
|
|
230
280
|
});
|
|
231
281
|
// Add pages into children of the current route
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { TemplateLayout } from "../../entries/server/index.js";
|
|
3
|
+
import { renderToString } from "../node/rendering.js";
|
|
4
|
+
import { resolveBuildOptions } from "./index.js";
|
|
5
|
+
import { ManifestManager } from "./manifest.js";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import fs from "node:fs/promises";
|
|
8
|
+
export const renderIndexHTML = async (template, options) => {
|
|
9
|
+
const { rootPath, config } = options;
|
|
10
|
+
const buildOptions = resolveBuildOptions({});
|
|
11
|
+
const manifest = new ManifestManager(path.posix.join(buildOptions.buildDirectory, buildOptions.clientPathDirectory, buildOptions.manifestPathDirectory, 'manifest.json'));
|
|
12
|
+
// Get assets tags
|
|
13
|
+
const assets = manifest.generateMetaTags(''); // TODO: Add the correct path
|
|
14
|
+
// Generate html from template
|
|
15
|
+
const html = renderToString(_jsx(TemplateLayout, { Template: template, assets: assets, isSpaMode: true }));
|
|
16
|
+
// Render the html into an index.html file
|
|
17
|
+
await fs.writeFile(path.posix.join(rootPath, buildOptions.buildDirectory, buildOptions.clientPathDirectory, 'index.html'), html, 'utf-8');
|
|
18
|
+
};
|
|
@@ -4,6 +4,8 @@ import { logRedirection as log } from '../../core/utils/log.js';
|
|
|
4
4
|
import createRasenganRequest, { createRasenganHeaders, sendRasenganResponse, } from '../node/utils.js';
|
|
5
5
|
import { join } from 'path';
|
|
6
6
|
import { isStaticRedirectFromConfig, isRedirectResponse, extractMetaFromRRContext, extractHeadersFromRRContext, } from './utils.js';
|
|
7
|
+
import { renderToString } from '../node/rendering.js';
|
|
8
|
+
import { TemplateLayout } from '../../entries/server/index.js';
|
|
7
9
|
/**
|
|
8
10
|
* Handle redirect request
|
|
9
11
|
* @param req
|
|
@@ -94,3 +96,30 @@ export async function handleDataRequest(request, handler) {
|
|
|
94
96
|
headers: { 'Content-Type': 'application/json' },
|
|
95
97
|
});
|
|
96
98
|
}
|
|
99
|
+
export async function handleSpaModeRequest(res, runner, options) {
|
|
100
|
+
try {
|
|
101
|
+
// Import Template
|
|
102
|
+
const Template = (await runner.import(`${options.rootPath}/src/template`))
|
|
103
|
+
.default;
|
|
104
|
+
// Convert TemplateLayout to string
|
|
105
|
+
const html = renderToString(_jsx(TemplateLayout, { Template: Template, isSpaMode: true }));
|
|
106
|
+
// Set status code
|
|
107
|
+
res.status(200);
|
|
108
|
+
// Set headers
|
|
109
|
+
res.setHeader('Content-Type', 'text/html');
|
|
110
|
+
// Send response
|
|
111
|
+
return res.send(html);
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
console.error(error);
|
|
115
|
+
// Set status code
|
|
116
|
+
res.status(500);
|
|
117
|
+
// Set headers
|
|
118
|
+
res.setHeader('Content-Type', 'text/html');
|
|
119
|
+
// Send response
|
|
120
|
+
return res.send(`
|
|
121
|
+
<h1>Internal Server Error</h1>
|
|
122
|
+
<p>Something went wrong</p>
|
|
123
|
+
`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -9,7 +9,7 @@ import { loggerMiddleware } from '../../core/middlewares/index.js';
|
|
|
9
9
|
import { isDataRequest, isDocumentRequest, logServerInfo } from './utils.js';
|
|
10
10
|
import { getDirname, loadModuleSSR, } from '../../core/config/utils/load-modules.js';
|
|
11
11
|
import { ServerMode } from '../runtime/mode.js';
|
|
12
|
-
import { handleDataRequest, handleDocumentRequest } from './handlers.js';
|
|
12
|
+
import { handleDataRequest, handleDocumentRequest, handleSpaModeRequest, } from './handlers.js';
|
|
13
13
|
import { createStaticHandler } from 'react-router';
|
|
14
14
|
import { generateRoutes } from '../../routing/utils/generate-routes.js';
|
|
15
15
|
/**
|
|
@@ -23,21 +23,27 @@ async function devRequestHandler(req, res, viteDevServer, options) {
|
|
|
23
23
|
try {
|
|
24
24
|
// Get the module runner through ssr environment
|
|
25
25
|
const runner = createServerModuleRunner(viteDevServer.environments.ssr);
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
26
|
+
if (options.config.ssr) {
|
|
27
|
+
// Load app-router
|
|
28
|
+
const AppRouter = await (await runner.import(join(`${options.rootPath}/src/app/app.router`))).default;
|
|
29
|
+
// Get static routes
|
|
30
|
+
const staticRoutes = generateRoutes(AppRouter);
|
|
31
|
+
// Create static handler
|
|
32
|
+
let handler = createStaticHandler(staticRoutes);
|
|
33
|
+
if (isDataRequest(req)) {
|
|
34
|
+
// Handle data request
|
|
35
|
+
return await handleDataRequest(req, handler);
|
|
36
|
+
}
|
|
37
|
+
if (isDocumentRequest(req)) {
|
|
38
|
+
return await handleDocumentRequest(req, res, runner, {
|
|
39
|
+
...options,
|
|
40
|
+
handler,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
35
43
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
handler,
|
|
40
|
-
});
|
|
44
|
+
else {
|
|
45
|
+
// handling spa mode here
|
|
46
|
+
return await handleSpaModeRequest(res, runner, options);
|
|
41
47
|
}
|
|
42
48
|
return res.status(404).send('Not found');
|
|
43
49
|
}
|
|
@@ -38,7 +38,7 @@ export const renderToStream = async (Component, res) => {
|
|
|
38
38
|
setTimeout(abort, ABORT_DELAY);
|
|
39
39
|
});
|
|
40
40
|
};
|
|
41
|
-
export const renderToString =
|
|
41
|
+
export const renderToString = (Component) => {
|
|
42
42
|
const html = ReactDOM.renderToString(Component);
|
|
43
43
|
return html;
|
|
44
44
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["../src/client.ts","../src/index.ts","../src/plugin.ts","../src/server.ts","../src/cli/index.ts","../src/core/index.ts","../src/core/types.ts","../src/core/config/index.ts","../src/core/config/type.ts","../src/core/config/utils/define-config.ts","../src/core/config/utils/load-modules.ts","../src/core/config/utils/path.ts","../src/core/config/vite/defaults.ts","../src/core/dynamic/index.tsx","../src/core/middlewares/index.ts","../src/core/middlewares/logger.ts","../src/core/plugins/index.ts","../src/core/utils/log.ts","../src/entries/client/render.tsx","../src/entries/server/entry.server.tsx","../src/entries/server/index.tsx","../src/routing/index.ts","../src/routing/interfaces.tsx","../src/routing/types.ts","../src/routing/components/index.tsx","../src/routing/components/template.tsx","../src/routing/utils/define-router.tsx","../src/routing/utils/define-routes-group.tsx","../src/routing/utils/generate-metadata.tsx","../src/routing/utils/generate-routes.tsx","../src/routing/utils/index.tsx","../src/scripts/build-command.js","../src/scripts/generate-package-json.js","../src/scripts/utils/check-os.js","../src/scripts/utils/copy.js","../src/server/build/index.ts","../src/server/build/manifest.tsx","../src/server/dev/handlers.tsx","../src/server/dev/server.ts","../src/server/dev/utils.ts","../src/server/node/index.tsx","../src/server/node/rendering.ts","../src/server/node/stream.ts","../src/server/node/utils.ts","../src/server/runtime/mode.ts","../src/server/runtime/utils.ts","../src/server/virtual/index.ts","../types/client.d.ts"],"version":"5.8.2"}
|
|
1
|
+
{"root":["../src/client.ts","../src/index.ts","../src/plugin.ts","../src/server.ts","../src/cli/index.ts","../src/core/index.ts","../src/core/types.ts","../src/core/config/index.ts","../src/core/config/type.ts","../src/core/config/utils/define-config.ts","../src/core/config/utils/load-modules.ts","../src/core/config/utils/path.ts","../src/core/config/vite/defaults.ts","../src/core/dynamic/index.tsx","../src/core/middlewares/index.ts","../src/core/middlewares/logger.ts","../src/core/plugins/index.ts","../src/core/utils/log.ts","../src/entries/client/render.tsx","../src/entries/server/entry.server.tsx","../src/entries/server/index.tsx","../src/routing/index.ts","../src/routing/interfaces.tsx","../src/routing/types.ts","../src/routing/components/index.tsx","../src/routing/components/template.tsx","../src/routing/utils/define-router.tsx","../src/routing/utils/define-routes-group.tsx","../src/routing/utils/generate-metadata.tsx","../src/routing/utils/generate-routes.tsx","../src/routing/utils/index.tsx","../src/scripts/build-command.js","../src/scripts/generate-package-json.js","../src/scripts/utils/check-os.js","../src/scripts/utils/copy.js","../src/server/build/index.ts","../src/server/build/manifest.tsx","../src/server/build/rendering.tsx","../src/server/dev/handlers.tsx","../src/server/dev/server.ts","../src/server/dev/utils.ts","../src/server/node/index.tsx","../src/server/node/rendering.ts","../src/server/node/stream.ts","../src/server/node/utils.ts","../src/server/runtime/mode.ts","../src/server/runtime/utils.ts","../src/server/virtual/index.ts","../types/client.d.ts"],"version":"5.8.2"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["../src/client.ts","../src/index.ts","../src/plugin.ts","../src/server.ts","../src/cli/index.ts","../src/core/index.ts","../src/core/types.ts","../src/core/config/index.ts","../src/core/config/type.ts","../src/core/config/utils/define-config.ts","../src/core/config/utils/load-modules.ts","../src/core/config/utils/path.ts","../src/core/config/vite/defaults.ts","../src/core/dynamic/index.tsx","../src/core/middlewares/index.ts","../src/core/middlewares/logger.ts","../src/core/plugins/index.ts","../src/core/utils/log.ts","../src/entries/client/render.tsx","../src/entries/server/entry.server.tsx","../src/entries/server/index.tsx","../src/routing/index.ts","../src/routing/interfaces.tsx","../src/routing/types.ts","../src/routing/components/index.tsx","../src/routing/components/template.tsx","../src/routing/utils/define-router.tsx","../src/routing/utils/define-routes-group.tsx","../src/routing/utils/generate-metadata.tsx","../src/routing/utils/generate-routes.tsx","../src/routing/utils/index.tsx","../src/scripts/build-command.js","../src/scripts/generate-package-json.js","../src/scripts/utils/check-os.js","../src/scripts/utils/copy.js","../src/server/build/index.ts","../src/server/build/manifest.tsx","../src/server/dev/handlers.tsx","../src/server/dev/server.ts","../src/server/dev/utils.ts","../src/server/node/index.tsx","../src/server/node/rendering.ts","../src/server/node/stream.ts","../src/server/node/utils.ts","../src/server/runtime/mode.ts","../src/server/runtime/utils.ts","../src/server/virtual/index.ts","../types/client.d.ts"],"version":"5.8.2"}
|
|
1
|
+
{"root":["../src/client.ts","../src/index.ts","../src/plugin.ts","../src/server.ts","../src/cli/index.ts","../src/core/index.ts","../src/core/types.ts","../src/core/config/index.ts","../src/core/config/type.ts","../src/core/config/utils/define-config.ts","../src/core/config/utils/load-modules.ts","../src/core/config/utils/path.ts","../src/core/config/vite/defaults.ts","../src/core/dynamic/index.tsx","../src/core/middlewares/index.ts","../src/core/middlewares/logger.ts","../src/core/plugins/index.ts","../src/core/utils/log.ts","../src/entries/client/render.tsx","../src/entries/server/entry.server.tsx","../src/entries/server/index.tsx","../src/routing/index.ts","../src/routing/interfaces.tsx","../src/routing/types.ts","../src/routing/components/index.tsx","../src/routing/components/template.tsx","../src/routing/utils/define-router.tsx","../src/routing/utils/define-routes-group.tsx","../src/routing/utils/generate-metadata.tsx","../src/routing/utils/generate-routes.tsx","../src/routing/utils/index.tsx","../src/scripts/build-command.js","../src/scripts/generate-package-json.js","../src/scripts/utils/check-os.js","../src/scripts/utils/copy.js","../src/server/build/index.ts","../src/server/build/manifest.tsx","../src/server/build/rendering.tsx","../src/server/dev/handlers.tsx","../src/server/dev/server.ts","../src/server/dev/utils.ts","../src/server/node/index.tsx","../src/server/node/rendering.ts","../src/server/node/stream.ts","../src/server/node/utils.ts","../src/server/runtime/mode.ts","../src/server/runtime/utils.ts","../src/server/virtual/index.ts","../types/client.d.ts"],"version":"5.8.2"}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import React, { FunctionComponent, JSX } from 'react';
|
|
2
2
|
import { AppProps } from '../../core/types.js';
|
|
3
3
|
import { Metadata, MetadataWithoutTitleAndDescription, TemplateProps } from '../../routing/types.js';
|
|
4
|
-
export declare const TemplateLayout: ({ StaticRouterComponent, metadata, assets, App, Template, }: {
|
|
5
|
-
StaticRouterComponent
|
|
6
|
-
metadata
|
|
4
|
+
export declare const TemplateLayout: ({ StaticRouterComponent, metadata, assets, App, Template, isSpaMode, }: {
|
|
5
|
+
StaticRouterComponent?: React.ReactNode;
|
|
6
|
+
metadata?: {
|
|
7
7
|
page: Metadata;
|
|
8
8
|
layout: MetadataWithoutTitleAndDescription;
|
|
9
9
|
};
|
|
10
10
|
assets?: JSX.Element[];
|
|
11
|
-
App
|
|
11
|
+
App?: FunctionComponent<AppProps>;
|
|
12
12
|
Template: FunctionComponent<TemplateProps>;
|
|
13
|
+
isSpaMode?: boolean;
|
|
13
14
|
}) => import("react/jsx-runtime.js").JSX.Element;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { FunctionComponent } from "react";
|
|
2
|
+
import { AppConfig } from "../../core/config/type.js";
|
|
3
|
+
import { TemplateProps } from "../../routing/types.js";
|
|
4
|
+
export declare const renderIndexHTML: (template: FunctionComponent<TemplateProps>, options: {
|
|
5
|
+
rootPath: string;
|
|
6
|
+
config: AppConfig;
|
|
7
|
+
}) => Promise<void>;
|
|
@@ -20,3 +20,8 @@ export declare function handleDocumentRequest(req: Express.Request, res: Express
|
|
|
20
20
|
handler: StaticHandler;
|
|
21
21
|
}): Promise<any>;
|
|
22
22
|
export declare function handleDataRequest(request: Express.Request, handler: StaticHandler): Promise<Response>;
|
|
23
|
+
export declare function handleSpaModeRequest(res: Express.Response, runner: ModuleRunner, options: {
|
|
24
|
+
rootPath: string;
|
|
25
|
+
__dirname: string;
|
|
26
|
+
config: AppConfig;
|
|
27
|
+
}): Promise<Express.Response<any, Record<string, any>>>;
|
|
@@ -6,4 +6,4 @@ import type * as Express from 'express';
|
|
|
6
6
|
* @returns
|
|
7
7
|
*/
|
|
8
8
|
export declare const renderToStream: (Component: React.ReactNode, res: Express.Response) => Promise<unknown>;
|
|
9
|
-
export declare const renderToString: (Component: React.ReactNode) =>
|
|
9
|
+
export declare const renderToString: (Component: React.ReactNode) => string;
|
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.59",
|
|
5
5
|
"description": "The modern React Framework",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "lib/esm/index.js",
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
"sass": "*",
|
|
80
80
|
"stylus": "*",
|
|
81
81
|
"vite": "^6.0.0",
|
|
82
|
-
"@rasenganjs/mdx": "^1.0.
|
|
82
|
+
"@rasenganjs/mdx": "^1.1.0-beta.1"
|
|
83
83
|
},
|
|
84
84
|
"peerDependenciesMeta": {
|
|
85
85
|
"@types/node": {
|