diff --git a/packages/repl/src/lib/workers/bundler/index.ts b/packages/repl/src/lib/workers/bundler/index.ts index 8f483d0cdd..0d2b534b6e 100644 --- a/packages/repl/src/lib/workers/bundler/index.ts +++ b/packages/repl/src/lib/workers/bundler/index.ts @@ -27,7 +27,8 @@ import { parse_npm_url, resolve_local, resolve_subpath, - resolve_version + resolve_version, + type Package } from '../npm'; import type { BundleResult } from '$lib/public'; @@ -180,6 +181,14 @@ async function get_bundle( // importing from a URL if (/^[a-z]+:/.test(importee)) return importee; + /** The npm package we're importing from, if any */ + let current: null | Package; + + if (importer.startsWith(NPM)) { + const { name, version } = parse_npm_url(importer); + current = await fetch_package(name, name === 'svelte' ? svelte_version : version); + } + // importing a relative file if (importee[0] === '.') { if (importer.startsWith(VIRTUAL)) { @@ -199,18 +208,22 @@ async function get_bundle( ); } - if (importer.startsWith(NPM)) { - const { name, version } = parse_npm_url(importer); - - const pkg = await fetch_package(name, name === 'svelte' ? svelte_version : version); + if (current) { + const { name, version } = current.meta; const path = new URL(importee, importer).href.replace(`${NPM}/${name}@${version}/`, ''); - return normalize_path(pkg, path); + return normalize_path(current, path); } return new URL(importee, importer).href; } + // importing a file from the same package via pkg.imports + if (importee[0] === '#' && current) { + const subpath = resolve_subpath(current, importee); + return normalize_path(current, subpath.slice(2)); + } + // importing an external package -> `npm://$/@/` const match = /^((?:@[^/]+\/)?[^/@]+)(?:@([^/]+))?(\/.+)?$/.exec(importee); if (!match) throw new Error(`Invalid import "${importee}"`); @@ -223,11 +236,9 @@ async function get_bundle( let default_version = 'latest'; - if (importer.startsWith(NPM)) { + if (current) { // use the version specified in importer's package.json, not `latest` - const { name, version } = parse_npm_url(importer); - - const { meta } = await fetch_package(name, name === 'svelte' ? svelte_version : version); + const { meta } = current; if (meta.name === pkg_name) { default_version = meta.version; diff --git a/packages/repl/src/lib/workers/npm.ts b/packages/repl/src/lib/workers/npm.ts index 1b2d71871f..232a0f7dfd 100644 --- a/packages/repl/src/lib/workers/npm.ts +++ b/packages/repl/src/lib/workers/npm.ts @@ -2,7 +2,7 @@ import * as resolve from 'resolve.exports'; import { parseTar, type FileDescription } from 'tarparser'; import { NPM } from './constants'; -interface Package { +export interface Package { meta: any; // package.json contents contents: Record; } @@ -126,6 +126,21 @@ export function resolve_subpath(pkg: Package, subpath: string): string { return `./${pkg.meta.svelte.replace('./', '')}`; } + if (subpath[0] === '#') { + try { + const resolved = resolve.imports(pkg.meta, subpath, { + browser: true, + conditions: ['svelte', 'module', 'browser', 'development'] + }); + + return resolved?.[0] as string; + } catch { + throw new Error( + `No matched import path was found for "${subpath}" in "${pkg.meta.name}/package.json"` + ); + } + } + // modern if (pkg.meta.exports) { try {