Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support running multiple handlers side-by-side in a custom server #2882

Merged
merged 6 commits into from
Nov 9, 2023
Merged
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
11 changes: 9 additions & 2 deletions packages/toolpad-app/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ export interface RunOptions {
port?: number;
dev?: boolean;
base: string;
create: boolean;
}

async function runCommand(
cmd: 'dev' | 'start',
{ dir, dev: toolpadDevMode, ...args }: Omit<RunOptions, 'cmd'>,
{ dir, dev: toolpadDevMode, create: createIfNotExists, ...args }: Omit<RunOptions, 'cmd'>,
) {
const projectDir = path.resolve(process.cwd(), dir);

Expand All @@ -24,6 +25,7 @@ async function runCommand(
dir: projectDir,
dev: cmd !== 'start',
toolpadDevMode,
createIfNotExists,
});

process.once('SIGINT', () => {
Expand Down Expand Up @@ -85,13 +87,18 @@ export default async function cli(argv: string[]) {
dir: {
type: 'string',
describe: 'Directory of the Toolpad application',
default: '.',
default: './toolpad',
},
base: {
type: 'string',
describe: 'Public base path of the Toolpad application',
default: '/prod',
},
create: {
type: 'boolean',
describe: "Create the application directory if it doesn't exist",
default: false,
},
} as const;

const sharedRunOptions = {
Expand Down
18 changes: 9 additions & 9 deletions packages/toolpad-app/src/server/EnvManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ interface IToolpadProject {
invalidateQueries(): void;
}

/**
* Get file path for the env file.
*/
function getEnvFilePath() {
return path.resolve(process.cwd(), '.env');
}

/**
* Handles loading env files and watches for updates.
*/
Expand Down Expand Up @@ -54,7 +61,7 @@ export default class EnvManager {
}

private loadEnvFile() {
const envFilePath = this.getEnvFilePath();
const envFilePath = getEnvFilePath();
this.resetEnv();
const { parsed = {} } = dotenv.config({ path: envFilePath, override: true });
this.values = parsed;
Expand All @@ -67,19 +74,12 @@ export default class EnvManager {
);
}

/**
* Get file path for the env file.
*/
getEnvFilePath() {
return path.resolve(this.project.getRoot(), '.env');
}

private initWatcher() {
if (!this.project.options.dev) {
return;
}

this.watcher = chokidar.watch([this.getEnvFilePath()], {
this.watcher = chokidar.watch([getEnvFilePath()], {
usePolling: true,
ignoreInitial: true,
});
Expand Down
5 changes: 2 additions & 3 deletions packages/toolpad-app/src/server/FunctionsManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@ interface IToolpadProject {
options: ToolpadProjectOptions;
events: Emitter<ProjectEvents>;
getRoot(): string;
getToolpadFolder(): string;
getOutputFolder(): string;
envManager: EnvManager;
invalidateQueries(): void;
Expand All @@ -127,7 +126,7 @@ export default class FunctionsManager {
}

private getResourcesFolder(): string {
return path.join(this.project.getToolpadFolder(), './resources');
return path.join(this.project.getRoot(), './resources');
}

private getFunctionsFile(): string {
Expand All @@ -144,7 +143,7 @@ export default class FunctionsManager {
}

private async migrateLegacy() {
const legacyQueriesFile = path.resolve(this.project.getToolpadFolder(), 'queries.ts');
const legacyQueriesFile = path.resolve(this.project.getRoot(), 'queries.ts');
if (await fileExists(legacyQueriesFile)) {
const functionsFile = this.getFunctionsFile();
await fs.mkdir(path.dirname(functionsFile), { recursive: true });
Expand Down
12 changes: 9 additions & 3 deletions packages/toolpad-app/src/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ export interface ToolpadHandlerConfig {

export async function createHandler({
dev = false,
dir = '.',
dir = './toolpad',
base = '/prod',
externalUrl = 'http://localhost:3000',
}: ToolpadHandlerConfig): Promise<AppHandler> {
Expand Down Expand Up @@ -425,6 +425,7 @@ export interface RunAppOptions {
dir?: string;
base?: string;
toolpadDevMode?: boolean;
createIfNotExists?: boolean;
}

export async function runApp({
Expand All @@ -433,11 +434,16 @@ export async function runApp({
base = '/prod',
port = 3000,
toolpadDevMode = false,
createIfNotExists,
}: RunAppOptions) {
const projectDir = resolveProjectDir(dir);

if (!(await folderExists(projectDir))) {
console.error(`${chalk.red('error')} - No project found at ${chalk.cyan(`"${projectDir}"`)}`);
if (!(await folderExists(projectDir)) && !createIfNotExists) {
console.error(
`${chalk.red('error')} - No project found at ${chalk.cyan(
`"${projectDir}"`,
)}. Use the --create option to initialize a new Toolpad project in this folder.`,
);
process.exit(1);
}

Expand Down
74 changes: 20 additions & 54 deletions packages/toolpad-app/src/server/localMode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import {
readMaybeDir,
updateYamlFile,
fileExists,
folderExists,
readJsonFile,
} from '@mui/toolpad-utils/fs';
import { z } from 'zod';
Expand Down Expand Up @@ -68,22 +67,16 @@ invariant(
'localMode should be used only in the main thread. Use message passing to get data from the main thread.',
);

function getToolpadFolder(root: string): string {
return path.join(root, './toolpad');
}

function getThemeFile(root: string): string {
return path.join(getToolpadFolder(root), './theme.yml');
return path.join(root, './theme.yml');
}

function getComponentsFolder(root: string): string {
const toolpadFolder = getToolpadFolder(root);
return path.join(toolpadFolder, './components');
return path.join(root, './components');
}

function getPagesFolder(root: string): string {
const toolpadFolder = getToolpadFolder(root);
return path.join(toolpadFolder, './pages');
return path.join(root, './pages');
}

function getPageFolder(root: string, name: string): string {
Expand All @@ -103,21 +96,13 @@ function getComponentFilePath(componentsFolder: string, componentName: string):
}

function getOutputFolder(root: string) {
return path.join(getToolpadFolder(root), '.generated');
return path.join(root, '.generated');
}

function getAppOutputFolder(root: string) {
return path.join(getOutputFolder(root), 'app');
}

async function legacyConfigFileExists(root: string): Promise<boolean> {
const [yamlFileExists, ymlFileExists] = await Promise.all([
fileExists(path.join(root, './toolpad.yaml')),
fileExists(path.join(root, './toolpad.yml')),
]);
return yamlFileExists || ymlFileExists;
}

type ComponentsContent = Record<string, { code: string }>;

export interface ComponentEntry {
Expand Down Expand Up @@ -276,8 +261,7 @@ type BuildInfo = z.infer<typeof buildInfoSchema>;
const DEFAULT_GENERATED_GITIGNORE_FILE_CONTENT = '.generated\n';

async function initGitignore(root: string) {
const projectFolder = getToolpadFolder(root);
const generatedGitignorePath = path.resolve(projectFolder, '.gitignore');
const generatedGitignorePath = path.resolve(root, '.gitignore');
if (!(await fileExists(generatedGitignorePath))) {
// eslint-disable-next-line no-console
console.log(`${chalk.blue('info')} - Initializing .gitignore file`);
Expand Down Expand Up @@ -908,24 +892,11 @@ export async function loadDomFromDisk(root: string): Promise<appDom.AppDom> {
return projectFolderToAppDom(projectFolder);
}

async function migrateLegacyProject(root: string) {
const isLegacyProject = await legacyConfigFileExists(root);

if (isLegacyProject) {
console.error(
`${chalk.red(
'error',
)} - This project was created with a deprecated version of Toolpad, please use @mui/toolpad@0.1.17 to migrate this project`,
);
process.exit(1);
}
}

function getDomFilePatterns(root: string) {
return [
path.resolve(root, './toolpad/pages/*/page.yml'),
path.resolve(root, './toolpad/components'),
path.resolve(root, './toolpad/components/*.*'),
path.resolve(root, './pages/*/page.yml'),
path.resolve(root, './components'),
path.resolve(root, './components/*.*'),
];
}
/**
Expand Down Expand Up @@ -1067,10 +1038,6 @@ class ToolpadProject {
return this.root;
}

getToolpadFolder() {
return getToolpadFolder(this.getRoot());
}

getOutputFolder() {
return getOutputFolder(this.getRoot());
}
Expand Down Expand Up @@ -1260,7 +1227,7 @@ export type { ToolpadProject };

declare global {
// eslint-disable-next-line
var __toolpadProject: ToolpadProject | undefined;
var __toolpadProjects: Set<string> | undefined;
}

export function resolveProjectDir(dir: string) {
Expand All @@ -1273,14 +1240,19 @@ export interface InitProjectOptions extends Partial<ToolpadProjectOptions> {
}

export async function initProject({ dir: dirInput, ...config }: InitProjectOptions) {
// eslint-disable-next-line no-underscore-dangle
invariant(!global.__toolpadProject, 'A project is already running');
const dir = resolveProjectDir(dirInput);

const dir = await resolveProjectDir(dirInput);
invariant(
// eslint-disable-next-line no-underscore-dangle
!global.__toolpadProjects?.has(dir),
`A project is already running for "${dir}"`,
);
// eslint-disable-next-line no-underscore-dangle
global.__toolpadProjects ??= new Set();
// eslint-disable-next-line no-underscore-dangle
global.__toolpadProjects.add(dir);

if (!(await folderExists(dir))) {
throw new Error(`No Toolpad project found at ${chalk.cyan(`"${dir}"`)}`);
}
await initToolpadFolder(dir);

const resolvedConfig: ToolpadProjectOptions = {
dev: false,
Expand All @@ -1289,13 +1261,7 @@ export async function initProject({ dir: dirInput, ...config }: InitProjectOptio
...config,
};

await migrateLegacyProject(dir);

await initToolpadFolder(dir);

const project = new ToolpadProject(dir, resolvedConfig);
// eslint-disable-next-line no-underscore-dangle
globalThis.__toolpadProject = project;

return project;
}
2 changes: 1 addition & 1 deletion packages/toolpad-app/src/server/toolpadAppBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ function toolpadVitePlugin({ root, base, getComponents }: ToolpadVitePluginParam
return resolvedComponentsId;
}
if (importer === resolvedRuntimeEntryPointId || importer === resolvedComponentsId) {
const newId = path.resolve(root, 'toolpad', id);
const newId = path.resolve(root, id);
return this.resolve(newId, importer);
}
return null;
Expand Down
19 changes: 19 additions & 0 deletions test/integration/backend-basic/fixture/server.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { unstable_createHandler as createHandler } from '@mui/toolpad';
import express from 'express';
import * as path from 'path';

const app = express();

const base = process.env.BASE;
const dir = path.resolve(process.cwd(), './toolpad');

const handler = await createHandler({
dev: process.env.NODE_ENV === 'development',
dir,
base,
});
app.use(base, handler.handler);

app.listen(process.env.PORT, () => {
console.log(`Custom server listening at http://localhost:${process.env.PORT}`);
});
1 change: 1 addition & 0 deletions test/integration/editor/deleteLast.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { ToolpadEditor } from '../../models/ToolpadEditor';
test.use({
localAppConfig: {
cmd: 'dev',
create: true,
},
});

Expand Down
1 change: 1 addition & 0 deletions test/integration/editor/new.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { ToolpadEditor } from '../../models/ToolpadEditor';
test.use({
localAppConfig: {
cmd: 'dev',
create: true,
},
});

Expand Down
Loading
Loading