Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions examples/custom-server-nextjs/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export const metadata = {
title: 'Next.js',
description: 'Generated by Next.js',
}

export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import * as React from 'react';
import { ToolpadApp } from '@toolpad/studio/next';

export default function ToolpadPage() {
return <ToolpadApp base="/my-next-app/my-toolpad" dir="./toolpad" />;
}
4 changes: 2 additions & 2 deletions examples/custom-server-nextjs/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ const dev = process.env.NODE_ENV === 'development';
const app = express();

// Initialize the Toolpad Studio handler. Make sure to pass the base path
const { handler } = await createHandler({ dev, base: '/my-toolpad-studio-app' });
app.use('/my-toolpad-studio-app', handler);
// const { handler } = await createHandler({ dev, base: '/my-toolpad-studio-app' });
// app.use('/my-toolpad-studio-app', handler);

// Initialize the Next.js app, basePath is set in next.config.mjs
const nextApp = next({ dev, dir: currentDirectory });
Expand Down
1 change: 1 addition & 0 deletions examples/custom-server-nextjs/next-env.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
/// <reference types="next/navigation-types/compat/navigation" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
9 changes: 9 additions & 0 deletions examples/custom-server-nextjs/next.config.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
/** @type {import('next').NextConfig} */
export default {
webpack: (config) => {
config.resolve.fallback = { '@blitz/internal': false };
config.externals.push({
fsevents: 'commonjs fsevents',
chokidar: 'commonjs chokidar',
});
config.resolve.alias['@mui/icons-material'] = ['@mui/icons-material/esm'];
return config;
},
basePath: '/my-next-app',
};
2 changes: 2 additions & 0 deletions examples/custom-server-nextjs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
},
"dependencies": {
"@toolpad/studio": "0.3.1",
"chokidar": "3.5.3",
"esbuild": "0.20.2",
"express": "4.19.2",
"next": "14.2.4",
"react": "18.3.1",
Expand Down
10 changes: 10 additions & 0 deletions examples/custom-server-nextjs/pages/api/toolpad/[[...path]].ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import project from '../../../toolpad-server';

export const config = {
api: {
externalResolver: true,
bodyParser: false,
},
};

export default project.createApiHandler({ base: '/api/toolpad' });
17 changes: 17 additions & 0 deletions examples/custom-server-nextjs/pages/toolpad-pages/[[...path]].tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import * as React from 'react';
import ToolpadAppClient from '@toolpad/studio/next-client';

export async function getServerSideProps() {
const { default: project } = await import('../../toolpad-server');
return project.getServerSideProps();
}

export default function Toolpad(props) {
return (
<ToolpadAppClient
{...props}
apiUrl="/my-next-app/api/toolpad"
basename="/my-next-app/toolpad-pages"
/>
);
}
3 changes: 3 additions & 0 deletions examples/custom-server-nextjs/toolpad-server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { initProject } from '@toolpad/studio/next';

export default await initProject();
3 changes: 3 additions & 0 deletions examples/custom-server-nextjs/toolpad/application.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
apiVersion: v1
kind: application
spec: {}
14 changes: 13 additions & 1 deletion examples/custom-server-nextjs/toolpad/pages/page/page.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# yaml-language-server: $schema=https://raw.githubusercontent.com/mui/mui-toolpad/v0.1.40/docs/schemas/v1/definitions.json#properties/Page
# yaml-language-server: $schema=https://raw.githubusercontent.com/mui/mui-toolpad/v0.1.46/docs/schemas/v1/definitions.json#properties/Page

apiVersion: v1
kind: page
Expand All @@ -16,3 +16,15 @@ spec:
mode: link
value: To the Next.js app
href: /my-next-app
- component: Text
name: text1
props:
mode: null
value:
$$jsExpression: query.data
queries:
- name: query
mode: query
query:
function: functions2.ts#default
kind: local
7 changes: 7 additions & 0 deletions examples/custom-server-nextjs/toolpad/resources/functions2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* Toolpad handlers file.
*/

export default async function handler(message: string) {
return `Hello ${message}`;
}
14 changes: 10 additions & 4 deletions examples/custom-server-nextjs/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,18 @@
"noEmit": true,
"incremental": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"module": "ESNext",
"moduleResolution": "Node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve"
"jsx": "preserve",
"plugins": [
{
"name": "next"
}
],
"strictNullChecks": true
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}
13 changes: 13 additions & 0 deletions packages/toolpad-studio/src/exports/next-client.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
'use client';

import * as React from 'react';
import { AppHostProvider } from '@toolpad/studio-runtime';
import ToolpadApp, { ToolpadAppProps } from '../runtime/ToolpadApp';

export default function ToolpadAppClient(props: ToolpadAppProps) {
return (
<AppHostProvider isPreview>
<ToolpadApp {...props} />
</AppHostProvider>
);
}
13 changes: 13 additions & 0 deletions packages/toolpad-studio/src/exports/next.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
'use server';

import { initProject as initProjectOrig } from '../server/localMode';

const PROJECT_REF = Symbol.for('PROJECT_REF');

export async function initProject() {
if (!(globalThis as any)[PROJECT_REF]) {
(globalThis as any)[PROJECT_REF] = initProjectOrig({ dir: './toolpad' });
}

return (globalThis as any)[PROJECT_REF];
}
3 changes: 2 additions & 1 deletion packages/toolpad-studio/src/runtime/ToolpadApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1229,7 +1229,7 @@ const PageRoot = React.forwardRef<HTMLDivElement, PageRootProps>(function PageRo
ref,
) {
const containerMaxWidth =
maxWidth === 'none' ? false : maxWidth ?? appDom.DEFAULT_CONTAINER_WIDTH;
maxWidth === 'none' ? false : (maxWidth ?? appDom.DEFAULT_CONTAINER_WIDTH);
return (
<Container ref={ref} maxWidth={containerMaxWidth}>
<Stack data-testid="page-root" direction="column" sx={{ my: 2, gap: 1 }} {...props}>
Expand Down Expand Up @@ -1731,6 +1731,7 @@ export function ToolpadAppRoutes(props: ToolpadAppProps) {

export default function ToolpadApp(props: ToolpadAppProps) {
const isSsr = useSsr();

return isSsr ? null : (
<BrowserRouter basename={props.basename}>
<Routes>
Expand Down
52 changes: 52 additions & 0 deletions packages/toolpad-studio/src/server/localMode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import {
import { z } from 'zod';
import { Awaitable } from '@toolpad/utils/types';
import * as appDom from '@toolpad/studio-runtime/appDom';
import express from 'express';
import { IncomingMessage, ServerResponse } from 'http';
import insecureHash from '../utils/insecureHash';
import {
Page,
Expand Down Expand Up @@ -61,6 +63,9 @@ import { UPGRADE_URL, VERSION_CHECK_INTERVAL } from '../constants';
import DataManager from './DataManager';
import { PAGE_COLUMN_COMPONENT_ID, PAGE_ROW_COMPONENT_ID } from '../runtime/toolpadComponents';
import packageInfo from '../packageInfo';
import createRuntimeState from '../runtime/createRuntimeState';
import { createRpcServer } from './runtimeRpcServer';
import { createRpcHandler } from './rpc';

declare global {
// eslint-disable-next-line
Expand Down Expand Up @@ -952,6 +957,39 @@ export function getRequiredEnvVars(dom: appDom.AppDom): Set<string> {
return new Set(allVars);
}

/**
* @deprecated Hack to make output compatible with Next.js getServerSideProps serialization
*/
function cleanUndefinedProperties(obj: any) {
if (Array.isArray(obj)) {
for (let i = obj.length - 1; i >= 0; i -= 1) {
const item = obj[i];
if (item === undefined) {
obj.splice(obj.indexOf(item), 1);
} else {
cleanUndefinedProperties(item);
}
}
} else if (obj && typeof obj === 'object') {
for (const key of Object.keys(obj)) {
if (obj[key] === undefined) {
delete obj[key];
} else {
cleanUndefinedProperties(obj[key]);
}
}
}
return obj;
}

export interface RequestHandler {
(req: IncomingMessage, res: ServerResponse): Promise<void>;
}

export interface CreateApiHandlerParams {
base: string;
}

const PRO_AUTH_PROVIDERS = ['azure-ad'];

interface PaidFeature {
Expand Down Expand Up @@ -1363,6 +1401,20 @@ class ToolpadProject {
}
return this.pagesManifestPromise;
}

async getServerSideProps() {
const dom = await this.loadDom();
const state = createRuntimeState({ dom });
return cleanUndefinedProperties({ props: { state } });
}

createApiHandler({ base }: CreateApiHandlerParams): RequestHandler {
const runtimeRpcServer = createRpcServer(this);
const handler = createRpcHandler(runtimeRpcServer);
const app = express();
app.use(base, handler);
return (req, res) => app(req, res);
}
}

export type { ToolpadProject };
Expand Down
1 change: 1 addition & 0 deletions packages/toolpad-studio/tsup.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export default defineConfig((options) => [
tsconfig: './tsconfig.runtime.json',
sourcemap: true,
esbuildPlugins: [cleanFolderOnFailure(path.resolve(__dirname, 'dist/runtime'))],
external: ['./next-client'],
loader: {
'.svg': 'copy',
'.png': 'copy',
Expand Down
Loading