Skip to content

chore: remove import-meta-resolve #13629

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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
6 changes: 6 additions & 0 deletions .changeset/famous-points-switch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@sveltejs/adapter-auto': patch
'@sveltejs/kit': patch
---

chore: remove `import-meta-resolve` dependency
125 changes: 75 additions & 50 deletions packages/adapter-auto/index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { execSync } from 'node:child_process';
import { pathToFileURL } from 'node:url';
import { resolve } from 'import-meta-resolve';
import { adapters } from './adapters.js';
import { dirname, join } from 'node:path';
import { existsSync } from 'node:fs';
import path from 'node:path';
import fs from 'node:fs';
import process from 'node:process';

/** @type {Record<string, (name: string, version: string) => string>} */
Copy link
Member

Choose a reason for hiding this comment

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

This logic exists in package-manager-detector (used by svelte CLI, changesets, and publint). It might be good to use here too as it supports Deno, which I notice we're not supporting here

Copy link
Member Author

Choose a reason for hiding this comment

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

maybe we could just follow this PR up with one that adds Deno support? Or is there a reason to strongly prefer adding the dependency?

Copy link
Member

Choose a reason for hiding this comment

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

yeah, we probably could. I think the main reason to possibly prefer the dependency is that getting some of the commands right for Deno has been maddeningly difficult. even now there's an issue open that the install command is not working correctly: antfu-collective/package-manager-detector#51

Expand All @@ -17,12 +15,15 @@ const commands = {
function detect_lockfile() {
let dir = process.cwd();

/** @param {string} file */
const exists = (file) => fs.existsSync(path.join(dir, file));

do {
if (existsSync(join(dir, 'pnpm-lock.yaml'))) return 'pnpm';
if (existsSync(join(dir, 'yarn.lock'))) return 'yarn';
if (existsSync(join(dir, 'package-lock.json'))) return 'npm';
if (existsSync(join(dir, 'bun.lockb')) || existsSync(join(dir, 'bun.lock'))) return 'bun';
} while (dir !== (dir = dirname(dir)));
if (exists('pnpm-lock.yaml')) return 'pnpm';
if (exists('yarn.lock')) return 'yarn';
if (exists('package-lock.json')) return 'npm';
if (exists('bun.lockb') || exists('bun.lock')) return 'bun';
} while (dir !== (dir = path.dirname(dir)));

return 'npm';
}
Expand All @@ -38,12 +39,40 @@ function detect_package_manager() {
}
}

/** @param {string} name */
function import_from_cwd(name) {
const cwd = pathToFileURL(process.cwd()).href;
const url = resolve(name, cwd + '/x.js');
/**
* Resolves a peer dependency relative to the current CWD. Duplicated with `packages/kit`
* @param {string} dependency
*/
function resolve_peer(dependency) {
let [name, ...parts] = dependency.split('/');
if (name[0] === '@') name += `/${parts.shift()}`;

let dir = process.cwd();

while (!fs.existsSync(`${dir}/node_modules/${name}/package.json`)) {
if (dir === (dir = path.dirname(dir))) {
throw new Error(
`Could not resolve peer dependency "${name}" relative to your project — please install it and try again.`
);
}
}

const pkg_dir = `${dir}/node_modules/${name}`;
const pkg = JSON.parse(fs.readFileSync(`${pkg_dir}/package.json`, 'utf-8'));

const subpackage = ['.', ...parts].join('/');

let exported = pkg.exports[subpackage];

while (typeof exported !== 'string') {
if (!exported) {
throw new Error(`Could not find valid "${subpackage}" export in ${name}/package.json`);
}

exported = exported['import'] ?? exported['default'];
}

return import(url);
return path.resolve(pkg_dir, exported);
}

/** @typedef {import('@sveltejs/kit').Adapter} Adapter */
Expand All @@ -56,47 +85,43 @@ async function get_adapter() {

if (!match) return;

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

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, match.version);

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;
resolved = resolve_peer(match.module);
} catch {
const package_manager = detect_package_manager();
const command = commands[package_manager](match.module, match.version);

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

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

resolved = resolve_peer(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 }
);
}
}

/** @type {{ default: () => Adapter }} */
const module = await import(resolved);

const adapter = module.default();

return {
Expand Down
3 changes: 0 additions & 3 deletions packages/adapter-auto/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,6 @@
"typescript": "^5.3.3",
"vitest": "^3.0.1"
},
"dependencies": {
"import-meta-resolve": "^4.1.0"
},
"peerDependencies": {
"@sveltejs/kit": "^2.0.0"
}
Expand Down
1 change: 0 additions & 1 deletion packages/kit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
"cookie": "^0.6.0",
"devalue": "^5.1.0",
"esm-env": "^1.2.2",
"import-meta-resolve": "^4.1.0",
"kleur": "^4.1.5",
"magic-string": "^0.30.5",
"mrmime": "^2.0.0",
Expand Down
4 changes: 2 additions & 2 deletions packages/kit/src/core/sync/utils.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import fs from 'node:fs';
import path from 'node:path';
import { mkdirp } from '../../utils/filesystem.js';
import { resolve_peer_dependency } from '../../utils/import.js';
import { import_peer } from '../../utils/import.js';

/** @type {{ VERSION: string }} */
const { VERSION } = await resolve_peer_dependency('svelte/compiler');
const { VERSION } = await import_peer('svelte/compiler');

/** @type {Map<string, string>} */
const previous_contents = new Map();
Expand Down
6 changes: 3 additions & 3 deletions packages/kit/src/exports/vite/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import {
sveltekit_paths,
sveltekit_server
} from './module_ids.js';
import { resolve_peer_dependency } from '../../utils/import.js';
import { import_peer } from '../../utils/import.js';
import { compact } from '../../utils/array.js';

const cwd = process.cwd();
Expand Down Expand Up @@ -155,7 +155,7 @@ export async function sveltekit() {
...svelte_config.vitePlugin
};

const { svelte } = await resolve_peer_dependency('@sveltejs/vite-plugin-svelte');
const { svelte } = await import_peer('@sveltejs/vite-plugin-svelte');

return [...svelte(vite_plugin_svelte_options), ...(await kit({ svelte_config }))];
}
Expand All @@ -181,7 +181,7 @@ let manifest_data;
* @return {Promise<import('vite').Plugin[]>}
*/
async function kit({ svelte_config }) {
const vite = await resolve_peer_dependency('vite');
const vite = await import_peer('vite');

const { kit } = svelte_config;
const out = `${kit.outDir}/output`;
Expand Down
55 changes: 41 additions & 14 deletions packages/kit/src/utils/import.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,52 @@
import * as imr from 'import-meta-resolve';
import process from 'node:process';
import { pathToFileURL } from 'node:url';
import fs from 'node:fs';
import path from 'node:path';

/**
* Resolves a peer dependency relative to the current CWD. Duplicated with `packages/adapter-auto`
* @param {string} dependency
*/
function resolve_peer(dependency) {
let [name, ...parts] = dependency.split('/');
if (name[0] === '@') name += `/${parts.shift()}`;

let dir = process.cwd();

while (!fs.existsSync(`${dir}/node_modules/${name}/package.json`)) {
if (dir === (dir = path.dirname(dir))) {
throw new Error(
`Could not resolve peer dependency "${name}" relative to your project — please install it and try again.`
);
}
}

const pkg_dir = `${dir}/node_modules/${name}`;
const pkg = JSON.parse(fs.readFileSync(`${pkg_dir}/package.json`, 'utf-8'));

const subpackage = ['.', ...parts].join('/');

let exported = pkg.exports[subpackage];

while (typeof exported !== 'string') {
if (!exported) {
throw new Error(`Could not find valid "${subpackage}" export in ${name}/package.json`);
}

exported = exported['import'] ?? exported['default'];
}

return path.resolve(pkg_dir, exported);
}

/**
* Resolve a dependency relative to the current working directory,
* rather than relative to this package (but falls back to trying that, if necessary)
* @param {string} dependency
*/
export async function resolve_peer_dependency(dependency) {
export async function import_peer(dependency) {
try {
// @ts-expect-error the types are wrong
const resolved = imr.resolve(dependency, pathToFileURL(process.cwd() + '/dummy.js'));
return await import(resolved);
return await import(resolve_peer(dependency));
} catch {
try {
// both imr.resolve and await import above can throw, which is why we can't just do import(resolved).catch(...) above
return await import(dependency);
} catch {
throw new Error(
`Could not resolve peer dependency "${dependency}" relative to your project — please install it and try again.`
);
}
return await import(dependency);
}
}
12 changes: 0 additions & 12 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading