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

adapter-auto: defer adapter installation until required #7739

Merged
merged 2 commits into from
Nov 21, 2022
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
5 changes: 5 additions & 0 deletions .changeset/twenty-forks-beg.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/adapter-auto': patch
---

defer adapter installation until actually needed
131 changes: 67 additions & 64 deletions packages/adapter-auto/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@ import { adapters } from './adapters.js';
import { dirname, join } from 'path';
import { existsSync } from 'fs';

/** @type {import('./index').default} */
let fn;

/** @type {Record<string, (name: string) => string>} */
const commands = {
npm: (name) => `npm install -D ${name}`,
Expand Down Expand Up @@ -46,72 +43,78 @@ async function import_from_cwd(name) {
return import(url);
}

for (const candidate of adapters) {
if (candidate.test()) {
/** @type {{ default: () => import('@sveltejs/kit').Adapter }} */
let module;

try {
module = await import_from_cwd(candidate.module);
} catch (error) {
if (
error.code === 'ERR_MODULE_NOT_FOUND' &&
error.message.startsWith(`Cannot find package '${candidate.module}'`)
) {
const package_manager = detect_package_manager();
const command = commands[package_manager](candidate.module);

try {
console.log(`Installing ${candidate.module}...`);

execSync(command, {
stdio: 'inherit',
env: {
...process.env,
NODE_ENV: undefined
}
});

module = await import_from_cwd(candidate.module);

console.log(`Successfully installed ${candidate.module}.`);
console.warn(
`\nIf you plan on staying on this deployment platform, consider replacing @sveltejs/adapter-auto with ${candidate.module}. This will give you faster and more robust installs, and more control over deployment configuration.\n`
);
} catch (e) {
throw new Error(
`Could not install ${candidate.module}. Please install it yourself by adding it to your package.json's devDependencies and try building your project again.`
);
}
} else {
throw error;
/** @typedef {import('@sveltejs/kit').Adapter} Adapter */

/**
* @returns {Promise<Adapter | undefined>} The corresponding adapter for the current environment if found otherwise undefined
*/
async function get_adapter() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd make it clearer from the name that this is installing the adapter as get sounds side-effect free

Suggested change
async function get_adapter() {
async function install_adapter() {

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In some cases it won't install anything though. We could make it get_or_install_adapter or whatever but i think it's clear enough from context

const match = adapters.find((candidate) => candidate.test());

if (!match) return;

/** @type {{ default: () => Adapter }} */
let module;

try {
module = await import_from_cwd(match.module);
} catch (error) {
if (
error.code === 'ERR_MODULE_NOT_FOUND' &&
error.message.startsWith(`Cannot find package '${match.module}'`)
) {
const package_manager = detect_package_manager();
const command = commands[package_manager](match.module);

try {
console.log(`Installing ${match.module}...`);

execSync(command, {
stdio: 'inherit',
env: {
...process.env,
NODE_ENV: undefined
}
});

module = await import_from_cwd(match.module);

console.log(`Successfully installed ${match.module}.`);
console.warn(
`\nIf you plan on staying on this deployment platform, consider replacing @sveltejs/adapter-auto with ${match.module}. This will give you faster and more robust installs, and more control over deployment configuration.\n`
);
} catch (e) {
throw new Error(
`Could not install ${match.module}. Please install it yourself by adding it to your package.json's devDependencies and try building your project again.`,
{ cause: e }
);
}
} else {
throw error;
}

fn = () => {
const adapter = module.default();
return {
...adapter,
adapt: (builder) => {
builder.log.info(`Detected environment: ${candidate.name}. Using ${candidate.module}`);
return adapter.adapt(builder);
}
};
};

break;
}
}

if (!fn) {
fn = () => ({
name: '@sveltejs/adapter-auto',
const adapter = module.default();

return {
...adapter,
adapt: (builder) => {
builder.log.warn(
'Could not detect a supported production environment. See https://kit.svelte.dev/docs/adapters to learn how to configure your app to run on the platform of your choosing'
);
builder.log.info(`Detected environment: ${match.name}. Using ${match.module}`);
return adapter.adapt(builder);
}
});
};
}

export default fn;
/** @type {() => Adapter} */
export default () => ({
name: '@sveltejs/adapter-auto',
adapt: async (builder) => {
const adapter = await get_adapter();

if (adapter) return adapter.adapt(builder);

builder.log.warn(
'Could not detect a supported production environment. See https://kit.svelte.dev/docs/adapters to learn how to configure your app to run on the platform of your choosing'
);
}
});