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

fix(adapter-node): Enforce dir to be an actual directory. #10306

Closed
wants to merge 3 commits into from

Conversation

KitsuneDev
Copy link

This PR addresses a small issue that tends to happen when trying to utilise the handler generated by adapter-node in monorepos that require a bundler like Webpack. For some reason, the current implementation tends to set dir to a JS file instead of a directory, like /packages/stellar-desktop/dist/b1229d39b38c826bd9a3.js, resulting in public asset lookups in paths that /packages/stellar-desktop/dist/b1229d39b38c826bd9a3.js/client (which are clearly invalid).

This PR contains a single one-line change that, if verified that dir is a js file, passes its value through path.dirname to get its actual path. This fixes the code when passed through bundlers, while retaining full functionality with no changes required for either the standalone server or standard usage of the handler. All tests have passed, although I believe including an extra one (as described by checklist item 3) is out of scope for this repo.

Please don't delete this checklist! Before submitting the PR, please make sure you do the following:

  • It's really useful if your PR references an issue where it is discussed ahead of time. In many cases, features are absent for a reason. For large changes, please create an RFC: https://github.com/sveltejs/rfcs
    Not quite an issue, but there is this StackOverflow Question that recommends an external static asset server configuration as a solution (which goes against SvelteKit's Docs)
  • This message body should clearly illustrate what problems it solves.
  • Ideally, include a test that fails without this PR but passes with it.

Tests

  • Run the tests with pnpm test and lint the project with pnpm lint and pnpm check

Changesets

  • If your PR makes a change that should be noted in one or more packages' changelogs, generate a changeset by running pnpm changeset and following the prompts. Changesets that add features should be minor and those that fix bugs should be patch. Please prefix changeset messages with feat:, fix:, or chore:.

@changeset-bot
Copy link

changeset-bot bot commented Jul 3, 2023

🦋 Changeset detected

Latest commit: a74475a

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@sveltejs/adapter-node Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@KitsuneDev KitsuneDev changed the title adapter-node: Enforce dir to be an actual directory. fix(adapter-node): Enforce dir to be an actual directory. Jul 3, 2023
@Conduitry
Copy link
Member

I'm unclear on what this is changing, exactly. import.meta.SERVER_DIR was something new added in #10041. We're handling that ourselves in a call to Rollup, where we're replacing it with URL relative to import.meta.url according to how deep the chunkId path is.

resolveImportMeta(property, { chunkId, moduleId }) {
if (property === 'SERVER_DIR' && moduleId === resolve('./src/handler.js')) {
const segments = chunkId.split('/').length - 1;
return `new URL("${'../'.repeat(segments) || '.'}", import.meta.url)`;

When does this end up pointing to the wrong path? How is webpack involved? Do you have a minimal reproduction of the situation this is trying to address?

I don't want to just blindly make the change in this PR, even the stat version. Is there a specific case where import.meta.SERVER_DIR ends up getting replaced with the wrong path, and can we adjust how that gets replaced there, within the call to Rollup, rather than elsewhere in the code that expects it to be a path to the correct server directory?

@KitsuneDev
Copy link
Author

I mentioned Webpack because I believe it's resolution of import.meta.url, just as the one done by other bundlers, is probably the cause of this issue. The issue first raised when I was trying to embed a SvelteKit node-adapter server inside a NodeJS app that had it's code transpiled by Webpack/Babel, which is the reason they got mentioned in my initial comment.

I will get to work on pushing a public version of the repo where I first encountered this issue, and look into the possibility of moving the solution to the aforementioned rollup call. Thank you for taking the time to review this PR.

@Conduitry
Copy link
Member

The issue first raised when I was trying to embed a SvelteKit node-adapter server inside a NodeJS app that had it's code transpiled by Webpack/Babel

This is interesting, and helpful, thank you. It does make me think that my proposed place to handle this might not be a good place to handle it. The generated application, basically, expects to be able to trust import.meta.url - and it sounds like the problem is that, when the adapter output is itself getting bundled again, then it can't, because it might get replaced with some other value by the bundler.

I'm not sure what to do about this situation. If the bundler can't be configured to provide the correct import.meta.url values to the application(s) it's bundling, then I don't think it's the application's responsibility to try to account for (certain types of) incorrect import.meta.url values.

Is there a reason the whole application needs to be bundled again rather than just having its entrypoint imported as-is by the consuming application?

This, to me, is sounding more like a documentation issue, where we stress the importance of the generated applications (for all adapters, but it probably matters most for the Node one) being able to trust the import.meta.url values they get.

@KitsuneDev
Copy link
Author

Is there a reason the whole application needs to be bundled again rather than just having its entrypoint imported as-is by the consuming application?

Unfortunately, there is: Runtimes provided by certain local cloud companies, or even Electron, do not support features used by the handler, such as Top-Level Awaits or even ES Modules, therefore needing the re-transpilation process.

This, to me, is sounding more like a documentation issue, where we stress the importance of the generated applications (for all adapters, but it probably matters most for the Node one) being able to trust the import.meta.url values they get.

That is completely fair, and I am pretty saddened by the fact many bundlers do not implement this correctly. However, from my testing (which included deploying to multiple environments), the addition of this one extra check (which is ran during the handler's import, adding little to no overhead) seems to enable the handler to be used in all of those environments, including Electron, and our micro services solution (which runs on multiple local cloud providers, many of which ship outdated node versions).

I'm not sure what to do about this situation. If the bundler can't be configured to provide the correct import.meta.url values to the application(s) it's bundling, then I don't think it's the application's responsibility to try to account for (certain types of) incorrect import.meta.url values.

You are 100% correct - it shouldn't be the application's job to try to fix the issues caused by the bundler, at least in theory. However, in this specific case, this one line fix does not only fix issues caused by bundlers, but also fixes a compatibility with different Node versions, eliminating problems such as the one exemplified by the StackOverflow link provided in my initial comment.

I completely agree with you: We are in 2023, these kinds of things shouldn't be necessary, but unfortunately they still are. This change should not introduce any negative effects, and widen the compatibility of adapter-node by a large margin.

@benmccann
Copy link
Member

Runtimes provided by certain local cloud companies, or even Electron, do not support features used by the handler, such as Top-Level Awaits or even ES Modules

But why would you use adapter-node in that situation? There are adapters for most of the major clouds and an adapter for electron as well: https://github.com/ptkdev/sveltekit-electron-adapter

@KitsuneDev
Copy link
Author

KitsuneDev commented Jul 3, 2023

The existing adapters are great, however, there are (and probably there are always going to be) projects that require greater flexibility, which is exactly what adapter-node provides. My specific use case for the handler is integrating a SvelteKit app into a custom software stack (due to the unfortunate complexities brought by enterprise apps - if it was up to me, I wouldn't be writing software into a custom server). I am quite sure I am not the only one who found the need to integrate a SvelteKit server into custom software (as can be demonstrated by the SO question in my initial comment).

This PR does not add the custom handler feature - it already exists in SvelteKit (as documented here) - all it does is merely fix a bug caused by some bundlers, thus allowing for wider compatibility for an existing feature.

It is also important to remember that while all major cloud companies have adapters, the need to work with smaller, local cloud providers (which provide their own deployment system) may also arise - sometimes, due to regulations/data governance issues; sometimes, simply because of pricing/business issues. adapter-node is a great tool and allows developers to accommodate those cases.

@KitsuneDev
Copy link
Author

Seems to have been fixed by #10314 - will test again to confirm.

@benmccann
Copy link
Member

Seems to have been fixed by #10314

Ok, I'm going to go ahead and close this then

@benmccann benmccann closed this Jul 5, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants