diff --git a/packages/core/src/module-resolver.ts b/packages/core/src/module-resolver.ts index 2c57e556f..aa83cbf73 100644 --- a/packages/core/src/module-resolver.ts +++ b/packages/core/src/module-resolver.ts @@ -5,24 +5,20 @@ import { packageName as getPackageName, packageName, } from '@embroider/shared-internals'; -import { dirname, resolve, posix } from 'path'; +import { dirname, resolve, posix, basename } from 'path'; import type { Package, V2Package } from '@embroider/shared-internals'; import { explicitRelative, RewrittenPackageCache } from '@embroider/shared-internals'; import makeDebug from 'debug'; import assertNever from 'assert-never'; import { externalName } from '@embroider/reverse-exports'; import { exports as resolveExports } from 'resolve.exports'; - -import { virtualPairComponent, fastbootSwitch, decodeFastbootSwitch, decodeImplicitModules } from './virtual-content'; import { Memoize } from 'typescript-memoize'; import { describeExports } from './describe-exports'; import { readFileSync } from 'fs'; import { nodeResolve } from './node-resolve'; -import { decodePublicRouteEntrypoint, encodeRouteEntrypoint } from './virtual-route-entrypoint'; import type { Options, EngineConfig } from './module-resolver-options'; import { satisfies } from 'semver'; import type { ModuleRequest, Resolution } from './module-request'; -import { virtualEntrypoint } from './virtual-entrypoint'; const debug = makeDebug('embroider:resolver'); @@ -250,14 +246,10 @@ export class Resolver { configFile: false, }); let switchFile = fastbootSwitch(candidate, resolve(pkg.root, 'package.json'), names); - if (switchFile === request.fromFile) { + if (switchFile.specifier === request.fromFile) { return logTransition('internal lookup from fastbootSwitch', request); } else { - return logTransition( - 'shadowed app fastboot', - request, - request.virtualize({ type: 'fastboot-switch', specifier: switchFile }) - ); + return logTransition('shadowed app fastboot', request, request.virtualize(switchFile)); } } else { return logTransition( @@ -336,31 +328,39 @@ export class Resolver { if (request.resolvedTo) { return request; } - let im = decodeImplicitModules(request.specifier); - if (!im) { - return request; - } - - let pkg = this.packageCache.ownerOfFile(request.fromFile); - if (!pkg?.isV2Ember()) { - throw new Error(`bug: found implicit modules import in non-ember package at ${request.fromFile}`); - } - let packageName = getPackageName(im.fromFile); - if (packageName) { - let dep = this.packageCache.resolve(packageName, pkg); - return logTransition( - `dep's implicit modules`, - request, - request.virtualize({ type: im.type, specifier: resolve(dep.root, `-embroider-${im.type}.js`) }) - ); - } else { - return logTransition( - `own implicit modules`, - request, - request.virtualize({ type: im.type, specifier: resolve(pkg.root, `-embroider-${im.type}.js`) }) - ); + for (let variant of ['', 'test-'] as const) { + let suffix = `-embroider-implicit-${variant}modules.js`; + if (!request.specifier.endsWith(suffix)) { + continue; + } + let filename = request.specifier.slice(0, -1 * suffix.length); + if (!filename.endsWith('/') && filename.endsWith('\\')) { + continue; + } + filename = filename.slice(0, -1); + let pkg = this.packageCache.ownerOfFile(request.fromFile); + if (!pkg?.isV2Ember()) { + throw new Error(`bug: found implicit modules import in non-ember package at ${request.fromFile}`); + } + let type = `implicit-${variant}modules` as const; + let packageName = getPackageName(filename); + if (packageName) { + let dep = this.packageCache.resolve(packageName, pkg); + return logTransition( + `dep's implicit modules`, + request, + request.virtualize({ type, specifier: resolve(dep.root, `-embroider-${type}.js`), fromFile: dep.root }) + ); + } else { + return logTransition( + `own implicit modules`, + request, + request.virtualize({ type, specifier: resolve(pkg.root, `-embroider-${type}.js`), fromFile: pkg.root }) + ); + } } + return request; } private handleEntrypoint(request: R): R { @@ -368,47 +368,76 @@ export class Resolver { return request; } - let virtualResponse = virtualEntrypoint(request, this.packageCache); - if (virtualResponse) { - return logTransition('entrypoint', request, request.virtualize(virtualResponse)); - } else { + const compatModulesSpecifier = '@embroider/virtual/compat-modules'; + + let isCompatModules = + request.specifier === compatModulesSpecifier || request.specifier.startsWith(compatModulesSpecifier + '/'); + + if (!isCompatModules) { return request; } + + const requestingPkg = this.packageCache.ownerOfFile(request.fromFile); + + if (!requestingPkg?.isV2Ember()) { + throw new Error(`bug: found entrypoint import in non-ember package at ${request.fromFile}`); + } + + let pkg: Package; + if (request.specifier === compatModulesSpecifier) { + pkg = requestingPkg; + } else { + let packageName = request.specifier.slice(compatModulesSpecifier.length + 1); + pkg = this.packageCache.resolve(packageName, requestingPkg); + } + + let matched = resolveExports(pkg.packageJSON, '-embroider-entrypoint.js', { + browser: true, + conditions: ['default', 'imports'], + }); + let specifier = resolve(pkg.root, matched?.[0] ?? '-embroider-entrypoint.js'); + return logTransition( + 'entrypoint', + request, + request.virtualize({ + type: 'entrypoint', + specifier, + fromDir: dirname(specifier), + }) + ); } private handleRouteEntrypoint(request: R): R { if (request.resolvedTo) { return request; } - - let routeName = decodePublicRouteEntrypoint(request.specifier); - - if (!routeName) { + const publicPrefix = '@embroider/core/route/'; + if (!request.specifier.startsWith(publicPrefix)) { return request; } - + let routeName = request.specifier.slice(publicPrefix.length); let pkg = this.packageCache.ownerOfFile(request.fromFile); if (!pkg?.isV2Ember()) { throw new Error(`bug: found entrypoint import in non-ember package at ${request.fromFile}`); } + let matched = resolveExports(pkg.packageJSON, '-embroider-route-entrypoint.js', { + browser: true, + conditions: ['default', 'imports'], + }); + let target = matched ? `${matched}:route=${routeName}` : `-embroider-route-entrypoint.js:route=${routeName}`; + let specifier = resolve(pkg.root, target); + return logTransition( 'route entrypoint', request, - request.virtualize({ type: 'route-entrypoint', specifier: encodeRouteEntrypoint(pkg, routeName) }) + request.virtualize({ type: 'route-entrypoint', specifier, route: routeName, fromDir: dirname(specifier) }) ); } private handleImplicitTestScripts(request: R): R { - //TODO move the extra forwardslash handling out into the vite plugin - const candidates = [ - '@embroider/virtual/test-support.js', - '/@embroider/virtual/test-support.js', - './@embroider/virtual/test-support.js', - ]; - - if (!candidates.includes(request.specifier)) { + if (request.specifier !== '@embroider/virtual/test-support.js') { return request; } @@ -427,14 +456,7 @@ export class Resolver { } private handleTestSupportStyles(request: R): R { - //TODO move the extra forwardslash handling out into the vite plugin - const candidates = [ - '@embroider/virtual/test-support.css', - '/@embroider/virtual/test-support.css', - './@embroider/virtual/test-support.css', - ]; - - if (!candidates.includes(request.specifier)) { + if (request.specifier !== '@embroider/virtual/test-support.css') { return request; } @@ -485,14 +507,7 @@ export class Resolver { } private handleVendorStyles(request: R): R { - //TODO move the extra forwardslash handling out into the vite plugin - const candidates = [ - '@embroider/virtual/vendor.css', - '/@embroider/virtual/vendor.css', - './@embroider/virtual/vendor.css', - ]; - - if (!candidates.includes(request.specifier)) { + if (request.specifier !== '@embroider/virtual/vendor.css') { return request; } @@ -562,12 +577,18 @@ export class Resolver { `Components with separately resolved templates were removed at Ember 6.0. Migrate to either co-located js/ts + hbs files or to gjs/gts. https://deprecations.emberjs.com/id/component-template-resolving/. Bad template was: ${hbsModule.filename}.` ); } + return logTransition( `resolveComponent found legacy HBS`, request, request.virtualize({ type: 'component-pair', - specifier: virtualPairComponent(this.options.appRoot, hbsModule.filename, jsModule?.filename), + hbsModule: hbsModule.filename, + jsModule: jsModule?.filename ?? null, + debugName: basename(hbsModule.filename).replace(/\.(js|hbs)$/, ''), + specifier: `${this.options.appRoot}/embroider-pair-component/${encodeURIComponent( + hbsModule.filename + )}/__vpc__/${encodeURIComponent(jsModule?.filename ?? '')}`, }) ); } else if (jsModule) { @@ -958,14 +979,7 @@ export class Resolver { } private handleVendor(request: R): R { - //TODO move the extra forwardslash handling out into the vite plugin - const candidates = [ - '@embroider/virtual/vendor.js', - '/@embroider/virtual/vendor.js', - './@embroider/virtual/vendor.js', - ]; - - if (!candidates.includes(request.specifier)) { + if (request.specifier !== '@embroider/virtual/vendor.js') { return request; } @@ -1305,10 +1319,7 @@ export class Resolver { ); } let { names } = describeExports(readFileSync(foundAppJS.filename, 'utf8'), { configFile: false }); - return request.virtualize({ - type: 'fastboot-switch', - specifier: fastbootSwitch(matched.matched, resolve(engine.root, 'package.json'), names), - }); + return request.virtualize(fastbootSwitch(matched.matched, resolve(engine.root, 'package.json'), names)); } } @@ -1405,3 +1416,28 @@ function engineRelativeName(pkg: Package, filename: string): string | undefined return '.' + outsideName.slice(pkg.name.length); } } + +const fastbootSwitchSuffix = '/embroider_fastboot_switch'; + +function fastbootSwitch(specifier: string, fromFile: string, names: Set) { + let filename = `${resolve(dirname(fromFile), specifier)}${fastbootSwitchSuffix}`; + let virtualSpecifier: string; + if (names.size > 0) { + virtualSpecifier = `${filename}?names=${[...names].join(',')}`; + } else { + virtualSpecifier = filename; + } + return { + type: 'fastboot-switch' as const, + specifier: virtualSpecifier, + names, + hasDefaultExport: 'x', + }; +} + +function decodeFastbootSwitch(filename: string) { + let index = filename.indexOf(fastbootSwitchSuffix); + if (index >= 0) { + return { filename: filename.slice(0, index) }; + } +} diff --git a/packages/core/src/node-resolve.ts b/packages/core/src/node-resolve.ts index f69609663..7b40efa2b 100644 --- a/packages/core/src/node-resolve.ts +++ b/packages/core/src/node-resolve.ts @@ -47,7 +47,7 @@ export class NodeRequestAdapter implements RequestAdapter string; -const pairComponentMarker = '/embroider-pair-component/'; -const pairComponentPattern = /\/embroider-pair-component\/(?[^\/]*)\/__vpc__\/(?[^\/]*)$/; - -export function virtualPairComponent(appRoot: string, hbsModule: string, jsModule: string | undefined): string { - return `${appRoot}/embroider-pair-component/${encodeURIComponent(hbsModule)}/__vpc__/${encodeURIComponent( - jsModule ?? '' - )}`; -} - -function decodeVirtualPairComponent( - filename: string -): { hbsModule: string; jsModule: string | null; debugName: string } | null { - // Performance: avoid paying regex exec cost unless needed - if (!filename.includes(pairComponentMarker)) { - return null; - } - let match = pairComponentPattern.exec(filename); - if (!match) { - return null; - } - let { hbsModule, jsModule } = match.groups! as { hbsModule: string; jsModule: string }; - return { - hbsModule: decodeURIComponent(hbsModule), - jsModule: jsModule ? decodeURIComponent(jsModule) : null, - debugName: basename(decodeURIComponent(hbsModule)).replace(/\.(js|hbs)$/, ''), - }; -} - -const fastbootSwitchSuffix = '/embroider_fastboot_switch'; -const fastbootSwitchPattern = /(?.+)\/embroider_fastboot_switch(?:\?names=(?.+))?$/; -export function fastbootSwitch(specifier: string, fromFile: string, names: Set): string { - let filename = `${resolve(dirname(fromFile), specifier)}${fastbootSwitchSuffix}`; - if (names.size > 0) { - return `${filename}?names=${[...names].join(',')}`; - } else { - return filename; - } -} - -export function decodeFastbootSwitch(filename: string) { - // Performance: avoid paying regex exec cost unless needed - if (!filename.includes(fastbootSwitchSuffix)) { - return; - } - let match = fastbootSwitchPattern.exec(filename); - if (match) { - let names = match.groups?.names?.split(',') ?? []; - return { - names: names.filter(name => name !== 'default'), - hasDefaultExport: names.includes('default'), - filename: match.groups!.original, - }; - } +export interface VirtualPairResponse { + type: 'component-pair'; + specifier: string; + hbsModule: string; + jsModule: string | null; + debugName: string; } -interface FastbootSwitchParams { - names: string[]; - hasDefaultExport: boolean; +interface FastbootSwitchResponse { + type: 'fastboot-switch'; + names: Set; } -function renderFastbootSwitchTemplate(params: FastbootSwitchParams): VirtualContentResult { +function renderFastbootSwitchTemplate(params: FastbootSwitchResponse): VirtualContentResult { return { - src: fastbootSwitchTemplate(params), + src: fastbootSwitchTemplate({ + names: [...params.names].filter(name => name !== 'default'), + hasDefaultExport: params.names.has('default'), + }), watches: [], }; } @@ -211,36 +136,14 @@ export default mod.default; {{#each names as |name|}} export const {{name}} = mod.{{name}}; {{/each}} -`) as (params: FastbootSwitchParams) => string; - -const implicitModulesPattern = /(?.*)[\\/]-embroider-implicit-(?test-)?modules\.js$/; +`) as (params: { names: string[]; hasDefaultExport: boolean }) => string; -export function decodeImplicitModules( - filename: string -): { type: 'implicit-modules' | 'implicit-test-modules'; fromFile: string } | undefined { - // Performance: avoid paying regex exec cost unless needed - if (!filename.includes('-embroider-implicit-')) { - return; - } - let m = implicitModulesPattern.exec(filename); - if (m) { - return { - type: m.groups!.test ? 'implicit-test-modules' : 'implicit-modules', - fromFile: m.groups!.filename, - }; - } +export interface ImplicitModulesResponse { + type: 'implicit-modules' | 'implicit-test-modules'; + fromFile: string; } -function renderImplicitModules( - { - type, - fromFile, - }: { - type: 'implicit-modules' | 'implicit-test-modules'; - fromFile: string; - }, - resolver: Resolver -): VirtualContentResult { +function renderImplicitModules({ type, fromFile }: ImplicitModulesResponse, resolver: Resolver): VirtualContentResult { let resolvableExtensionsPattern = extensionsPattern(resolver.options.resolvableExtensions); const pkg = resolver.packageCache.ownerOfFile(fromFile); diff --git a/packages/core/src/virtual-entrypoint.ts b/packages/core/src/virtual-entrypoint.ts index cd5298033..60b29b7d8 100644 --- a/packages/core/src/virtual-entrypoint.ts +++ b/packages/core/src/virtual-entrypoint.ts @@ -3,75 +3,17 @@ import { compile } from './js-handlebars'; import type { Resolver } from './module-resolver'; import type { CompatResolverOptions } from '../../compat/src/resolver-transform'; import { flatten, partition } from 'lodash'; -import { join, resolve } from 'path'; -import { extensionsPattern, type PackageCachePublicAPI, type Package } from '@embroider/shared-internals'; +import { join } from 'path'; +import { extensionsPattern } from '@embroider/shared-internals'; import walkSync from 'walk-sync'; import type { V2AddonPackage } from '@embroider/shared-internals/src/package'; -import { encodePublicRouteEntrypoint } from './virtual-route-entrypoint'; import escapeRegExp from 'escape-string-regexp'; import { optionsWithDefaults } from './options'; -import { type ModuleRequest } from './module-request'; -import { exports as resolveExports } from 'resolve.exports'; -import { type VirtualResponse } from './virtual-content'; - -const entrypointPattern = /(?.*)[\\/]-embroider-entrypoint.js/; - -export function virtualEntrypoint( - request: ModuleRequest, - packageCache: PackageCachePublicAPI -): VirtualResponse | undefined { - //TODO move the extra forwardslash handling out into the vite plugin - const candidates = [ - '@embroider/virtual/compat-modules', - '/@embroider/virtual/compat-modules', - './@embroider/virtual/compat-modules', - ]; - - if (!candidates.some(c => request.specifier.startsWith(c + '/') || request.specifier === c)) { - return undefined; - } - - const result = /\.?\/?@embroider\/virtual\/compat-modules(?:\/(?.*))?/.exec(request.specifier); - - if (!result) { - throw new Error('bug: entrypoint does not match pattern' + request.specifier); - } - - const { packageName } = result.groups!; - - const requestingPkg = packageCache.ownerOfFile(request.fromFile); - - if (!requestingPkg?.isV2Ember()) { - throw new Error(`bug: found entrypoint import in non-ember package at ${request.fromFile}`); - } - let pkg: Package; - if (packageName) { - pkg = packageCache.resolve(packageName, requestingPkg); - } else { - pkg = requestingPkg; - } - let matched = resolveExports(pkg.packageJSON, '-embroider-entrypoint.js', { - browser: true, - conditions: ['default', 'imports'], - }); - return { - type: 'entrypoint', - specifier: resolve(pkg.root, matched?.[0] ?? '-embroider-entrypoint.js'), - }; -} - -export function decodeEntrypoint(filename: string): { fromDir: string } | undefined { - // Performance: avoid paying regex exec cost unless needed - if (!filename.includes('-embroider-entrypoint')) { - return; - } - let m = entrypointPattern.exec(filename); - if (m) { - return { - fromDir: m.groups!.filename, - }; - } +export interface EntrypointResponse { + type: 'entrypoint'; + fromDir: string; + specifier: string; } export function staticAppPathsPattern(staticAppPaths: string[] | undefined): RegExp | undefined { @@ -84,7 +26,6 @@ export function renderEntrypoint( resolver: Resolver, { fromDir }: { fromDir: string } ): { src: string; watches: string[] } { - // this is new const owner = resolver.packageCache.ownerOfFile(fromDir); let eagerModules: string[] = []; @@ -167,10 +108,10 @@ export function renderEntrypoint( (_: string, filename: string) => { requiredAppFiles.push([filename]); }, - (routeNames: string[], _files: string[]) => { + (routeNames: string[]) => { lazyRoutes.push({ names: routeNames, - path: encodePublicRouteEntrypoint(routeNames, _files), + path: `@embroider/core/route/${encodeURIComponent(routeNames[0])}`, }); } ); diff --git a/packages/core/src/virtual-route-entrypoint.ts b/packages/core/src/virtual-route-entrypoint.ts index a73084aaf..232aad7e2 100644 --- a/packages/core/src/virtual-route-entrypoint.ts +++ b/packages/core/src/virtual-route-entrypoint.ts @@ -1,54 +1,20 @@ import type { V2AddonPackage } from '@embroider/shared-internals/src/package'; import { AppFiles } from './app-files'; import type { Resolver } from './module-resolver'; -import { resolve } from 'path'; import { compile } from './js-handlebars'; -import { extensionsPattern, type Package } from '@embroider/shared-internals'; +import { extensionsPattern } from '@embroider/shared-internals'; import { partition } from 'lodash'; import { getAppFiles, getFastbootFiles, importPaths, splitRoute, staticAppPathsPattern } from './virtual-entrypoint'; -import { exports as resolveExports } from 'resolve.exports'; -const entrypointPattern = /(?.*)[\\/]-embroider-route-entrypoint.js:route=(?.*)/; - -export function encodeRouteEntrypoint(pkg: Package, routeName: string): string { - let matched = resolveExports(pkg.packageJSON, '-embroider-route-entrypoint.js', { - browser: true, - conditions: ['default', 'imports'], - }); - let target = matched ? `${matched}:route=${routeName}` : `-embroider-route-entrypoint.js:route=${routeName}`; - return resolve(pkg.root, target); -} - -export function decodeRouteEntrypoint(filename: string): { fromDir: string; route: string } | undefined { - // Performance: avoid paying regex exec cost unless needed - if (!filename.includes('-embroider-route-entrypoint')) { - return; - } - let m = entrypointPattern.exec(filename); - if (m) { - return { - fromDir: m.groups!.filename, - route: m.groups!.route, - }; - } -} - -export function encodePublicRouteEntrypoint(routeNames: string[], _files: string[]) { - return `@embroider/core/route/${encodeURIComponent(routeNames[0])}`; -} - -export function decodePublicRouteEntrypoint(specifier: string): string | null { - const publicPrefix = '@embroider/core/route/'; - if (!specifier.startsWith(publicPrefix)) { - return null; - } - - return specifier.slice(publicPrefix.length); +export interface RouteEntrypointResponse { + type: 'route-entrypoint'; + fromDir: string; + route: string; } export function renderRouteEntrypoint( - resolver: Resolver, - { fromDir, route }: { fromDir: string; route: string } + { fromDir, route }: RouteEntrypointResponse, + resolver: Resolver ): { src: string; watches: string[] } { const owner = resolver.packageCache.ownerOfFile(fromDir); diff --git a/packages/core/src/virtual-test-support-styles.ts b/packages/core/src/virtual-test-support-styles.ts index 0afd88b16..1387ab105 100644 --- a/packages/core/src/virtual-test-support-styles.ts +++ b/packages/core/src/virtual-test-support-styles.ts @@ -7,11 +7,13 @@ import type { Engine } from './app-files'; import type { Resolver } from './module-resolver'; import type { VirtualContentResult } from './virtual-content'; -export function decodeTestSupportStyles(filename: string): boolean { - return filename.endsWith('-embroider-test-support-styles.css'); +export interface TestSupportStylesResponse { + type: 'test-support-css'; + specifier: string; } -export function renderTestSupportStyles(filename: string, resolver: Resolver): VirtualContentResult { +export function renderTestSupportStyles(response: TestSupportStylesResponse, resolver: Resolver): VirtualContentResult { + const filename = response.specifier; const owner = resolver.packageCache.ownerOfFile(filename); if (!owner) { throw new Error(`Failed to find a valid owner for ${filename}`); diff --git a/packages/core/src/virtual-test-support.ts b/packages/core/src/virtual-test-support.ts index ff8a6a912..4c40f97e8 100644 --- a/packages/core/src/virtual-test-support.ts +++ b/packages/core/src/virtual-test-support.ts @@ -6,11 +6,13 @@ import type { Engine } from './app-files'; import type { Resolver } from './module-resolver'; import type { VirtualContentResult } from './virtual-content'; -export function decodeImplicitTestScripts(filename: string): boolean { - return filename.endsWith('-embroider-test-support.js'); +export interface TestSupportResponse { + type: 'test-support-js'; + specifier: string; } -export function renderImplicitTestScripts(filename: string, resolver: Resolver): VirtualContentResult { +export function renderImplicitTestScripts(response: TestSupportResponse, resolver: Resolver): VirtualContentResult { + const filename = response.specifier; const owner = resolver.packageCache.ownerOfFile(filename); if (!owner) { throw new Error(`Failed to find a valid owner for ${filename}`); diff --git a/packages/core/src/virtual-vendor-styles.ts b/packages/core/src/virtual-vendor-styles.ts index fbd07d59b..cf054ef56 100644 --- a/packages/core/src/virtual-vendor-styles.ts +++ b/packages/core/src/virtual-vendor-styles.ts @@ -7,11 +7,17 @@ import type { Resolver } from './module-resolver'; import type { VirtualContentResult } from './virtual-content'; import type { Engine } from './app-files'; +export interface VirtualVendorStylesResponse { + type: 'vendor-css'; + specifier: string; +} + export function decodeVirtualVendorStyles(filename: string): boolean { return filename.endsWith('-embroider-vendor-styles.css'); } -export function renderVendorStyles(filename: string, resolver: Resolver): VirtualContentResult { +export function renderVendorStyles(response: VirtualVendorStylesResponse, resolver: Resolver): VirtualContentResult { + const filename = response.specifier; const owner = resolver.packageCache.ownerOfFile(filename); if (!owner) { throw new Error(`Failed to find a valid owner for ${filename}`); diff --git a/packages/core/src/virtual-vendor.ts b/packages/core/src/virtual-vendor.ts index 81f0899b7..a6d66abd5 100644 --- a/packages/core/src/virtual-vendor.ts +++ b/packages/core/src/virtual-vendor.ts @@ -6,16 +6,17 @@ import resolve from 'resolve'; import type { Resolver } from './module-resolver'; import type { VirtualContentResult } from './virtual-content'; -export function decodeVirtualVendor(filename: string): boolean { - return filename.endsWith('-embroider-vendor.js'); +export interface VirtualVendorResponse { + type: 'vendor-js'; + specifier: string; } -export function renderVendor(filename: string, resolver: Resolver): VirtualContentResult { - const owner = resolver.packageCache.ownerOfFile(filename); +export function renderVendor(response: VirtualVendorResponse, resolver: Resolver): VirtualContentResult { + const owner = resolver.packageCache.ownerOfFile(response.specifier); if (!owner) { - throw new Error(`Failed to find a valid owner for ${filename}`); + throw new Error(`Failed to find a valid owner for ${response.specifier}`); } - return { src: getVendor(owner, resolver, filename), watches: [] }; + return { src: getVendor(owner, resolver, response.specifier), watches: [] }; } function getVendor(owner: Package, resolver: Resolver, filename: string): string { diff --git a/packages/vite/src/esbuild-request.ts b/packages/vite/src/esbuild-request.ts index cfe49fc56..63c5d47e6 100644 --- a/packages/vite/src/esbuild-request.ts +++ b/packages/vite/src/esbuild-request.ts @@ -76,7 +76,7 @@ export class EsBuildRequestAdapter implements RequestAdapter { + async function onLoad({ + path, + namespace, + pluginData, + }: { + path: string; + namespace: string; + pluginData?: { virtual: VirtualResponse }; + }): Promise { let src: string; if (namespace === 'embroider-template-only-component') { src = templateOnlyComponent; } else if (namespace === 'embroider-virtual') { - src = virtualContent(path, resolverLoader.resolver).src; + // castin because response in our namespace are supposed to always have + // this pluginData. + src = virtualContent(pluginData!.virtual, resolverLoader.resolver).src; } else { src = readFileSync(path, 'utf8'); } diff --git a/packages/vite/src/request.ts b/packages/vite/src/request.ts index 7cab24b02..747944559 100644 --- a/packages/vite/src/request.ts +++ b/packages/vite/src/request.ts @@ -30,6 +30,12 @@ export class RollupRequestAdapter implements RequestAdapter { + async function ensureVirtualResolve(context: PluginContext, specifier: string): Promise { let result = await resolveId( context, specifier, @@ -68,16 +68,20 @@ export function resolver(): Plugin { throw new Error(`bug: expected to resolve ${specifier}`); } if (typeof result === 'string') { - return result; + throw new Error(`bug: expected to get a PartialResolvedId`); } - return result.id; + let meta = result.meta?.['embroider-resolver'] as ResponseMeta | undefined; + if (!meta) { + throw new Error(`bug: no response meta for ${specifier}`); + } + return meta; } async function emitVirtualFile(context: PluginContext, fileName: string): Promise { context.emitFile({ type: 'asset', fileName, - source: virtualContent(await ensureResolve(context, fileName), resolverLoader.resolver).src, + source: virtualContent((await ensureVirtualResolve(context, fileName)).virtual, resolverLoader.resolver).src, }); } @@ -137,7 +141,7 @@ export function resolver(): Plugin { load(id) { let meta = responseMetas.get(normalizePath(id)); if (meta?.virtual) { - let { src, watches } = virtualContent(cleanUrl(id), resolverLoader.resolver); + let { src, watches } = virtualContent(meta.virtual, resolverLoader.resolver); virtualDeps.set(id, watches); server?.watcher.add(watches); return src; diff --git a/packages/webpack/src/virtual-loader.ts b/packages/webpack/src/virtual-loader.ts index 67c9d4cf3..174e38c90 100644 --- a/packages/webpack/src/virtual-loader.ts +++ b/packages/webpack/src/virtual-loader.ts @@ -1,4 +1,4 @@ -import { ResolverLoader, virtualContent } from '@embroider/core'; +import { ResolverLoader, virtualContent, type VirtualResponse } from '@embroider/core'; import type { LoaderContext } from 'webpack'; let resolverLoader: ResolverLoader | undefined; @@ -20,7 +20,9 @@ export default function virtualLoader(this: LoaderContext): string | un } let { resolver } = setup(appRoot); this.resourcePath = filename; - return virtualContent(filename, resolver).src; + // @ts-expect-error unimplemented + let virtual: VirtualResponse = fixmeImplementVirtualResponse(); + return virtualContent(virtual, resolver).src; } throw new Error(`@embroider/webpack/src/virtual-loader received unexpected request: ${this.query}`); }