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

Make svelte-kit logic Framework Agnostic #9583

Closed
MrWaip opened this issue Apr 1, 2023 · 5 comments
Closed

Make svelte-kit logic Framework Agnostic #9583

MrWaip opened this issue Apr 1, 2023 · 5 comments

Comments

@MrWaip
Copy link

MrWaip commented Apr 1, 2023

Describe the problem

Hello!

We use svelte-kit in our work. To solve our problems, we extend the default server, which is used under the hood of svelte-kit with its middleware and custom logic.

It is very inconvenient that the extended server is only available in the final build, and is not available in dev mode

Ideally, the dev server should not differ from the production server

In an attempt to solve this problem, I tried to write my adapter. I tried to transplant svelte-kit with polka.js on fastify.
I did it in two approaches. It turned out that this does not work very well, since fastify does the same job as svelte-kit (parsing cookies, parsing the request body, etc.)

I also tried to make friends with svelte-kit-vite-plugin and vite-node-plugin to have one entry point to the entire application. But this is impossible, because all the logic of middleware is hidden in closures and has no expansion points.

Describe the proposed solution

I propose to develop some kind of function that is available for export, which would accept a Request (web) and return a Response (web). And with the help of vite-node-plugin, it would be possible to have one entry point to the entire application and the same server, both in dev and in production.

Alternatives considered

No response

Importance

would make my life easier

Additional Information

No response

@dummdidumm
Copy link
Member

Which adapter are you using? What's the deployment platform you're running prod on? What's a concrete use case for something like this?

SvelteKit's logic is already platform agnostic - anything that can run JavaScript can run it. The handle hook sounds like what you're looking for, since it's request in, response out. I don't fully understand what you want on top of that.

@MrWaip
Copy link
Author

MrWaip commented Apr 1, 2023

Handle hook is good, but it's not enough for all cases. For example, we needed to be able to proxy requests to another service on the same http server. Or hang it with Prometheus to collect metrics. Using a handle hook for this is expensive, because there is logic under the hood that is unnecessary for us.

In addition, we don't really like middleware because of their ability to lead to bugs and lack of flexibility.

@dummdidumm
Copy link
Member

Then I don't understand even less what you mean by "make svelte-kit logic framework agnostic" - what you want sounds like the opposite, namely having dedicated hooks for the node environment. It sounds like you want to customize the node server, which is sketched out here.

As for the parity of dev and prod environment, that's tracked in #3535 - therefore closing.

@dummdidumm dummdidumm closed this as not planned Won't fix, can't repro, duplicate, stale Apr 4, 2023
@MrWaip
Copy link
Author

MrWaip commented Nov 5, 2023

Hello again)

I think my thought was not very clear

It seems to me that production and development builds should behave the same way.

For example, you need svelte-kit with adapter-node to use https, you need to replace build/index.js on its implementation. But this will only work for production. If you need the same behavior, but for development (npm run dev), you need to extend this to vite.config.js . And it turns out duplication.

I would like to be able to make server customization work for both production and development builds.

@MrWaip
Copy link
Author

MrWaip commented Nov 5, 2023

And I achieved the desired behavior by writing my vite plugins. But this is not a system solution, I would like to have some kind of API to achieve the same

function isDependency(module: ModuleNode, managerId: string): boolean {
	for (const importer of module.importers.values()) {
		if (importer.id === managerId) {
			return true;
		}

		return isDependency(importer, managerId);
	}

	return false;
}

async function loadBootstrap(opts: {
	vite: ViteDevServer;
	svelteKit: Middleware;
	bootstrapId: string;
	config: ResolvedConfig;
}) {
	const { config, bootstrapId: managerId, svelteKit, vite } = opts;

	const { bootstrap } = (await vite.ssrLoadModule(managerId)) as { bootstrap: Bootstrap };

	const entry = await bootstrap({
		https: config?.server?.https as SecureServerOptions | never,
		svelteKitMiddleware: (req, res, next) => {
			// eslint-disable-next-line @typescript-eslint/no-explicit-any
			req.originalUrl = (req as any).backupUrl.originalUrl;
			// eslint-disable-next-line @typescript-eslint/no-explicit-any
			req.url = (req as any).backupUrl.url;

			return svelteKit(req, res, next);
		},
		baseUrl: config?.base.replace(/\/$/, ''),
	});

	return entry;
}

export const fastifyAdapter = ({ config, serverEntryPath, vite }: StrategyOptions) => {
	return async () => {
		const { handle: svelteKit } = vite.middlewares.stack.splice(-1, 1)[0]!;
		const [serverUrl, bootstrapId] = await vite.moduleGraph.resolveUrl(serverEntryPath, true);

		let manager = await loadBootstrap({
			config,
			bootstrapId,
			svelteKit: svelteKit as Middleware,
			vite,
		});

		const systemServer = createServer(config.server.https as SecureServerOptions, (req, res) => {
			manager.useSystemServer(req, res);
		});

		systemServer.listen(84, '0.0.0.0');

		vite.watcher.on('all', async (_, file) => {
			const changedModule = vite.moduleGraph.getModuleById(file);

			if (changedModule && isDependency(changedModule, bootstrapId)) {
				const old = manager;

				manager = await loadBootstrap({
					config,
					bootstrapId,
					svelteKit: svelteKit as Middleware,
					vite,
				});

				await old.close();

				vite.config.logger.info(`server reloaded ${serverUrl}`, {
					timestamp: true,
				});
			}
		});

		vite.httpServer?.on('close', () => systemServer.close());

		vite.middlewares.use(async (req, res) => {
			const originalUrl = req.originalUrl;

			Object.defineProperty(req, 'httpVersion', { value: '1.1' });
			Object.defineProperty(req, 'httpVersionMajor', { value: 1 });
			Object.defineProperty(req, 'httpVersionMinor', { value: 1 });
			Object.defineProperty(req, 'backupUrl', { value: { originalUrl, url: req.url } });

			req.url = originalUrl;

			manager.useApplicationServer(req, res);
		});
	};
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants