15 | )
16 | }
--------------------------------------------------------------------------------
/src/hydrate.tsx:
--------------------------------------------------------------------------------
1 | import { hydrateRoot } from 'react-dom/client';
2 | const { default: App} = await import(globalThis.__CLIENT_COMPONENT_SRC__)
3 |
4 | hydrateRoot(document, );
--------------------------------------------------------------------------------
/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { Elysia, t } from "elysia";
2 | import { staticPlugin } from '@elysiajs/static';
3 | import { swagger } from '@elysiajs/swagger';
4 | import { Counter } from "./components/Counter";
5 | import { renderToReadableStream } from 'react-dom/server';
6 | import { basename } from "path";
7 | import App from "./pages";
8 | import html from "@elysiajs/html";
9 |
10 | const {outputs: [hydratratejsAsset, pageAsset]} = await Bun.build({
11 | entrypoints: ['src/hydrate.tsx', 'src/pages/index.tsx'],
12 | outdir: 'public',
13 | target: 'browser',
14 | splitting: true,
15 | minify: true,
16 | publicPath: '/',
17 | }); // You can read automatically from outputs
18 |
19 |
20 | const TODOS = [
21 | { id: 1, title: 'Buy milk' },
22 | { id: 2, title: 'Buy eggs' },
23 | { id: 3, title: 'Buy bread' }
24 | ]
25 |
26 | const app = new Elysia()
27 | .use(html())
28 | .use(swagger())
29 | .use(staticPlugin({
30 | assets: 'public',
31 | prefix: ''
32 | }))
33 | .get("/", () => "Hello Elysia", {
34 | response: t.String()
35 | })
36 | .get('/todos', () => TODOS, {
37 | detail: {
38 | summary: 'Return all TODOs for the user',
39 | tags: ['todos']
40 | },
41 | response: t.Array(t.Object({
42 | id: t.Number(),
43 | title: t.String()
44 | }))
45 | })
46 | .get('/todos/:id', ({ params: { id } }) => {
47 | return TODOS.find(todo => todo.id === +id)
48 | }, {
49 | params: t.Object({
50 | id: t.Numeric() // Cast the type to Number, you can also use tranform object
51 | }),
52 | })
53 |
54 | // This will work only if you use server-side apis only if none you should return html with react and do hydration or something =)
55 | .get('/app', () => )
56 | .get(basename(hydratratejsAsset.path), () => Bun.file(hydratratejsAsset.path))
57 | .get('/page', async () => {
58 | const reactStream = await renderToReadableStream(, {
59 | bootstrapScriptContent: `globalThis.__CLIENT_COMPONENT_SRC__ = "/pages/${basename(pageAsset.path)}"`,
60 | bootstrapModules: [basename(hydratratejsAsset.path)],
61 | onError: console.error
62 | });
63 | return new Response(reactStream, {
64 | headers: { // Not necessary but be explicit in some cases it's fine
65 | 'Content-Type': 'text/html'
66 | }
67 | })
68 | })
69 | .listen(Bun.env.PORT || 0, ({ hostname, port}) => {
70 | console.log('Elysia started at http://%s:%s', hostname, port);
71 | });
72 |
73 |
--------------------------------------------------------------------------------
/src/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import { Counter } from "../components/Counter";
2 |
3 | export default function IndexPage() {
4 | return (
5 |
6 |
7 |
8 |
9 | {/* */}
10 | My app
11 |
12 |
13 |
This is a SSR component with a client side component inside
14 |
15 |
16 |
17 | );
18 | }
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
4 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
5 | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
6 | /* Type Checking */
7 | "jsx": "react-jsx",
8 | "module": "ES2022", /* Specify what module code is generated. */
9 | // "rootDir": "./", /* Specify the root folder within your source files. */
10 | "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
11 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
12 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
13 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
14 | // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
15 | "skipLibCheck": true, /* Skip type checking all .d.ts files. */
16 | "strict": true, /* Enable all strict type-checking options. */
17 | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
18 | // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
19 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
20 | // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
21 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
22 | // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
23 | // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
24 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
25 | // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
26 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
27 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
28 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
29 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
30 | // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
31 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
32 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
33 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
34 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
35 | /* Completeness */
36 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
37 | /* Visit https://aka.ms/tsconfig to read more about this file */
38 | /* Projects */
39 | // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
40 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
41 | // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
42 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
43 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
44 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
45 | /* Language and Environment */
46 | "target": "ES2021", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
47 | // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
48 | // "jsx": "preserve", /* Specify what JSX code is generated. */
49 | // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
50 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
51 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
52 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
53 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
54 | // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
55 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
56 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
57 | // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
58 | /* Modules */
59 | "types": [
60 | "bun-types"
61 | ] /* Specify type package names to be included without being referenced in a source file. */
62 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
63 | // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
64 | // "resolveJsonModule": true, /* Enable importing .json files. */
65 | // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */
66 | /* JavaScript Support */
67 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
68 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
69 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
70 | /* Emit */
71 | // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
72 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */
73 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
74 | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */
75 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
76 | // "outDir": "./", /* Specify an output folder for all emitted files. */
77 | // "removeComments": true, /* Disable emitting comments. */
78 | // "noEmit": true, /* Disable emitting files from a compilation. */
79 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
80 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
81 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
82 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
83 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
84 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
85 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
86 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
87 | // "newLine": "crlf", /* Set the newline character for emitting files. */
88 | // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
89 | // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
90 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
91 | // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
92 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */
93 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
94 | /* Interop Constraints */
95 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
96 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
97 | }
98 | }
--------------------------------------------------------------------------------