[feat] install adapters on demand#7462
Conversation
🦋 Changeset detectedLatest commit: 40ac2af The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
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 |
packages/adapter-auto/index.js
Outdated
| ); | ||
| try { | ||
| execSync( | ||
| `echo "Installing ${candidate.module}" && npm install ${candidate.module} --no-save --no-package-lock`, |
There was a problem hiding this comment.
TIL --no-package-lock — if that allows this to work across package managers, that'd be pretty sweet.
Should we add a message along the lines of 'you should add this to your package.json for faster build times in future?'
There was a problem hiding this comment.
That'd only help people who look at the build logs, right? I wonder how many people that is.
Also, is there a reason for the echo to be part of the exec as well, instead of just a regular console.log?
If we get rid of that, can we do something with execFileSync like what we're doing here so we can completely avoid the shell's processing and escaping?
There was a problem hiding this comment.
What's the advantage to that? The current approach forwards the output to the main shell, so it could help in case something goes wrong.
There was a problem hiding this comment.
That'd only help people who look at the build logs, right? I wonder how many people that is.
a small number, but larger than zero
What's the advantage to that? The current approach forwards the output to the main shell
We'd still pipe stderr and stdout. The advantage is that there's no caveats around candidate.module being some weird string that we need to deal with — npm package names should be fine, but for an example of the category of bugs it prevents, see https://github.com/sveltejs/kit/pull/6129/files.
Also execFileSync should be slightly faster as it doesn't involve a shell
There was a problem hiding this comment.
tried switching to it, can't switch because windows can't execute npm without the .cmd ending in this case
There was a problem hiding this comment.
Can we have process.platform === 'win32' ? 'npm.cmd' : 'npm' as the first argument of execFileSync? Does that work?
packages/adapter-auto/index.js
Outdated
| @@ -10,31 +13,39 @@ for (const candidate of adapters) { | |||
|
|
|||
| try { | |||
| module = await import(candidate.module); | |||
There was a problem hiding this comment.
am suddenly wondering if we should actually be importing the module from the app directory rather than the adapter-auto directory. in most cases it should Just Work, but you can imagine a situation where adapter-auto is installed in the workspace root while adapter-foo is installed inside the app
There was a problem hiding this comment.
Doesn't candidate.module just contain the name of the package, and the resolution algorithm should start at the adapter-auto directory and look for node_modules there, and if it's there, look for that module there? At least I (try to) use this fact by installing the package into the adapter-auto directory by running npm install inside its directory.
There was a problem hiding this comment.
If the adapter is a dependency of the app (which it would be, if you'd already installed it) then surely resolution should start from there? (Until import.meta.resolve is stable, this would need import-meta-resolve)
There was a problem hiding this comment.
I feel like this overcomplicates things for no good reason. In what world are you using adapter-auto, but have installed a more specific adapter in another place? You either have them in the same place or switched to the one you actually want to use a long time ago.
There was a problem hiding this comment.
It's not so hard to imagine a situation where shared dependencies are installed in the workspace root but someone installs a one-off dependency in a package — this very repo used to work that way, until we decided to move all dependencies into packages. I also wonder if 'dependencies of x can import all other dependencies of x' is guaranteed across all package managers now and in the future. It's a side-effect of the resolution algorithm plus the way package managers populate node_modules, but it's the sort of thing that feels changeable, the same way pnpm prevents the npm 3+ behaviour of allowing x to directly import all indirect dependencies
|
The approach here seems to work when it comes to installing, but for some reason it
will continue to tinker around. Update: Can't reproduce the first point anymore, so ... it works? Things that are not ideal with this solution:
Things we could add:
|
|
goddammit, why is this so unreliable to test? Other options:
|
|
I don't love solving problems by adding dependencies, but we could experiment with https://github.com/antfu/ni |
|
I'm all for finding a better way to do this, but I think that can happen incrementally, and for right now, pretty much anything would be better than utterly flabbergasting dependency tree that I see that Some other options might be to |
|
I thought about the 'install in some other directory option' but I think with npm you'd end up with a flat directory containing the adapter and all its dependencies, and then you wouldn't be able to move them into the real |
|
The solution right now already install things in a separate node_modules folder, namely that inside of |
@Rich-Harris how did you test this PR? Did you use |
|
I'm completely lost as to why running an install command from |
I'm just looking at the normal build logs. I didn't think there'd be any need to do anything funky since we're not making any changes to the package we're trying to lazily install |
|
I still don't know why using |
packages/adapter-auto/index.js
Outdated
| execSync( | ||
| `${process.platform === 'win32' ? 'set' : ''} NODE_ENV=ignore_me && npm install ${ | ||
| candidate.module | ||
| } --no-save --omit=dev --no-package-lock`, | ||
| { | ||
| stdio: 'inherit', | ||
| cwd: dirname(fileURLToPath(import.meta.url)) | ||
| } | ||
| ); |
There was a problem hiding this comment.
We should be able to manually pass env vars with the env option https://nodejs.org/dist/latest-v19.x/docs/api/child_process.html#child_processexecsynccommand-options
If we do that, we can also avoid involving the shell at all, and we can use execFileSync here instead.
There was a problem hiding this comment.
execFileSync needs .cmd endings for the npm/pnpm/yarn command on windows last time I tested this. So we either have to prepend that on windows or keep using execSync
There was a problem hiding this comment.
Yeah I saw your comment in that thread after I commented here. I responded over on that thread. Even if we can't make execFileSync work, it would be nice to avoid the manual setting of the env var using shell commands.
I'm actually not even sure that FOO=bar && do-something would call do-something with FOO set to bar. I don't think environment variables automatically inherit that way in shell scripts without an export.
|
Still don't understand why running |
|
I think the various |
|
I think so, too, but this wouldn't help you in case of a monorepo, where you run |
… adapter-auto-on-demand

Closes #5123
Marking as draft because I still need to test this on one of the providers, with npm/pnpm/yarn
Please don't delete this checklist! Before submitting the PR, please make sure you do the following:
Tests
pnpm testand lint the project withpnpm lintandpnpm checkChangesets
pnpm changesetand following the prompts. All changesets should bepatchuntil SvelteKit 1.0