Skip to content
Open
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
141 changes: 69 additions & 72 deletions packages/host/app/lib/externals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,8 @@ import * as emberHelper from '@ember/helper';
import * as emberModifier from '@ember/modifier';
import * as emberObject from '@ember/object';
import * as emberObjectInternals from '@ember/object/internals';
import * as emberService from '@ember/service';

import * as emberTemplate from '@ember/template';
import * as emberTemplateFactory from '@ember/template-factory';
import * as emberTestHelpers from '@ember/test-helpers';
import * as glimmerComponent from '@glimmer/component';
import * as glimmerTracking from '@glimmer/tracking';

Expand Down Expand Up @@ -58,17 +55,7 @@ import type { VirtualNetwork } from '@cardstack/runtime-common';
import { shimHostCommands } from '../commands';

export function shimExternals(virtualNetwork: VirtualNetwork) {
// Always shim qunit on the virtual network. In non-test environments (code
// mode, card rendering), this no-op stub prevents realm cards that co-locate
// test imports from failing to load — test callbacks are never invoked.
// In live-test runs, loadRealmTests() overrides this at the realm loader
// level via loader.shimModule('qunit', QUnit), so the real QUnit instance
// is used there and this network-level shim is never reached.
const windowQUnit = (globalThis as any).QUnit;
virtualNetwork.shimModule(
'qunit',
windowQUnit || { module: () => {}, test: () => {}, config: {} },
);
shimModulesForLiveTests(virtualNetwork);

virtualNetwork.shimModule('@cardstack/runtime-common', runtime);
virtualNetwork.shimModule(
Expand All @@ -79,26 +66,13 @@ export function shimExternals(virtualNetwork: VirtualNetwork) {
virtualNetwork.shimModule('@cardstack/boxel-ui/icons', boxelUiIcons);
virtualNetwork.shimModule('@cardstack/boxel-ui/modifiers', boxelUiModifiers);
virtualNetwork.shimModule('@glimmer/component', glimmerComponent);
virtualNetwork.shimModule('@glimmer/tracking', glimmerTracking);
virtualNetwork.shimModule('@ember/component', emberComponent);
virtualNetwork.shimModule(
'@ember/component/template-only',
emberComponentTemplateOnly,
);
virtualNetwork.shimModule('@ember/destroyable', emberDestroyable);
virtualNetwork.shimModule('@ember/helper', emberHelper);
virtualNetwork.shimModule('@ember/modifier', emberModifier);
virtualNetwork.shimModule('@ember/object', emberObject);
virtualNetwork.shimModule('@ember/object/internals', emberObjectInternals);
virtualNetwork.shimModule('@ember/service', emberService);
virtualNetwork.shimModule('@ember/template', emberTemplate);
virtualNetwork.shimModule('@ember/template-factory', emberTemplateFactory);
virtualNetwork.shimModule('@cardstack/view-transitions', viewTransitions);
virtualNetwork.shimModule('awesome-phonenumber', awesomePhoneNumber);
virtualNetwork.shimModule('date-fns', dateFns);

virtualNetwork.shimModule('ember-animated', emberAnimated);
virtualNetwork.shimModule('ember-animated/easings/cosine', eaEasingsCosine);
virtualNetwork.shimModule('ember-animated/easings/linear', eaEasingsLinear);
virtualNetwork.shimModule(
'ember-animated/motions/adjust-color',
eaMotionsAdjustColor,
Expand All @@ -124,6 +98,8 @@ export function shimExternals(virtualNetwork: VirtualNetwork) {
virtualNetwork.shimModule('ember-animated/motions/opacity', eaMotionsOpacity);
virtualNetwork.shimModule('ember-animated/motions/resize', eaMotionsResize);
virtualNetwork.shimModule('ember-animated/motions/scale', eaMotionsScale);
virtualNetwork.shimModule('ember-animated/easings/cosine', eaEasingsCosine);
virtualNetwork.shimModule('ember-animated/easings/linear', eaEasingsLinear);
virtualNetwork.shimModule(
'ember-animated/transitions/fade',
eaTransitionsFade,
Expand All @@ -132,17 +108,29 @@ export function shimExternals(virtualNetwork: VirtualNetwork) {
'ember-animated/transitions/move-over',
eaTransitionsMoveOver,
);

virtualNetwork.shimModule('@cardstack/view-transitions', viewTransitions);

virtualNetwork.shimModule('ember-css-url', cssUrl);
virtualNetwork.shimModule('@ember/template-factory', emberTemplateFactory);
virtualNetwork.shimModule('@ember/template', emberTemplate);
virtualNetwork.shimModule('@glimmer/tracking', glimmerTracking);
virtualNetwork.shimModule('@ember/object', emberObject);
virtualNetwork.shimModule('@ember/object/internals', emberObjectInternals);
virtualNetwork.shimModule('@ember/helper', emberHelper);
virtualNetwork.shimModule('@ember/modifier', emberModifier);
Comment on lines +118 to +121

Choose a reason for hiding this comment

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

P1 Badge Restore @ember/service virtual-network shim

shimExternals() no longer registers @ember/service, so any module that is loaded through this VirtualNetwork and imports @ember/service will now fail module resolution at import time. This is a behavior regression from the previous implementation, where the Ember service module was always shimmed, and it can break realm code paths that rely on service decorators/injection when evaluated via loader.import(...).

Useful? React with 👍 / 👎.

Comment on lines +114 to +121
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

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

shimExternals() no longer shims @ember/service. Because the VirtualNetwork resolves bare specifiers to https://packages/... and there is no import-map entry for @ember/service, any realm module that imports @ember/service will now fail to load. If realms are expected to be able to use Ember services, please re-add the @ember/service import + virtualNetwork.shimModule('@ember/service', …) (or document/guarantee that realm code must not import it).

Copilot uses AI. Check for mistakes.
virtualNetwork.shimModule(
'ember-modify-based-class-resource',
emberModifyClassBasedResource,
);
virtualNetwork.shimModule('ember-resources', emberResources);
virtualNetwork.shimModule('ember-concurrency', emberConcurrency);
virtualNetwork.shimModule(
'ember-concurrency/-private/async-arrow-runtime',
emberConcurrencyAsyncArrowRuntime,
);
virtualNetwork.shimModule('ember-css-url', cssUrl);

virtualNetwork.shimModule('ember-modifier', emberModifier2);
virtualNetwork.shimModule(
'ember-modify-based-class-resource',
emberModifyClassBasedResource,
);
virtualNetwork.shimModule(
'ember-provide-consume-context',
emberProvideConsumeContext,
Expand All @@ -155,64 +143,73 @@ export function shimExternals(virtualNetwork: VirtualNetwork) {
'ember-provide-consume-context/components/context-provider',
emberProvideConsumeContextContextProvider,
);
virtualNetwork.shimModule('ember-resources', emberResources);
virtualNetwork.shimModule('ember-source/types', { default: class {} });
virtualNetwork.shimModule('ember-source/types/preview', {
default: class {},
});
virtualNetwork.shimModule('flat', flat);
virtualNetwork.shimModule('lodash', lodash);
virtualNetwork.shimModule('matrix-js-sdk', matrixJsSDK);
virtualNetwork.shimModule('rsvp', rsvp);
virtualNetwork.shimModule('super-fast-md5', superFastMD5);
virtualNetwork.shimModule('tracked-built-ins', tracked);
virtualNetwork.shimModule('date-fns', dateFns);
virtualNetwork.shimModule('@ember/destroyable', emberDestroyable);
virtualNetwork.shimModule('rsvp', rsvp);
virtualNetwork.shimAsyncModule({
id: 'ethers',
resolve: () => import('ethers'),
});
virtualNetwork.shimModule('ember-source/types', { default: class {} });
virtualNetwork.shimModule('ember-source/types/preview', {
default: class {},
});
virtualNetwork.shimModule('super-fast-md5', superFastMD5);
virtualNetwork.shimModule('matrix-js-sdk', matrixJsSDK);
virtualNetwork.shimAsyncModule({
id: 'uuid',
resolve: () => import('uuid'),
});

shimModulesForLiveTests(virtualNetwork);

// Some realm modules use host-only types or helpers. Provide a safe shim so
// imports resolve even when the host module isn't present in the build.
virtualNetwork.shimModule('@cardstack/host/services/store', {
default: class {},
});

virtualNetwork.shimModule('awesome-phonenumber', awesomePhoneNumber);
shimHostCommands(virtualNetwork);
Comment on lines +166 to 167

Choose a reason for hiding this comment

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

P2 Badge Reintroduce host store compatibility shim

The fallback shim for @cardstack/host/services/store was removed, which means realm modules evaluated outside the full host build can no longer safely resolve that host-only specifier. In environments like code-mode/card rendering (where this shim layer is meant to prevent host-only imports from crashing), those imports now become hard resolution failures instead of the previous safe no-op class.

Useful? React with 👍 / 👎.

}

// Shims test-only module IDs into the virtual network as throwing stubs so
// realm cards that colocate test imports can load in any environment without
// executing test code. live-test.js overrides these at the loader level with
// the real implementations when a test runner is present.
export function shimModulesForLiveTests(virtualNetwork: VirtualNetwork) {
const windowQUnit = (globalThis as any).QUnit;

const testOnlyStub = (moduleId: string) =>
new Proxy(
{},
{
get: () => {
throw new Error(
`${moduleId} is only available in a test environment.`,
);
},
function isLiveTestMode(): boolean {
if (typeof window === 'undefined' || !window.location?.href) {
return false;
}
return new URL(window.location.href).searchParams.has('liveTest');
}

function testOnlyStub(moduleId: string) {
return new Proxy(
{},
{
get: () => {
throw new Error(`${moduleId} is only available in live-test mode.`);
},
);
},
);
}

function shimModulesForLiveTests(virtualNetwork: VirtualNetwork) {
let inLiveTestMode = isLiveTestMode();
let windowQUnit = (globalThis as any).QUnit;

// Use real @ember/test-helpers only when QUnit is running; stub otherwise.
virtualNetwork.shimModule(
'@ember/test-helpers',
windowQUnit ? emberTestHelpers : testOnlyStub('@ember/test-helpers'),
'qunit',
inLiveTestMode && windowQUnit
? windowQUnit
: { module: () => {}, test: () => {}, config: {} },
);

// Always stub host test helpers here — live-test.js shimModule() on the
// realm loader overrides these with real implementations at test time.
if (inLiveTestMode) {
// Keep test helpers out of the normal app startup path.
virtualNetwork.shimAsyncModule({
id: '@ember/test-helpers',
resolve: () => import('@ember/test-helpers'),
});
Comment on lines +201 to +204
Copy link
Contributor

Choose a reason for hiding this comment

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

this is async, but this function is not. are you gonna have a race condition where you are expecting these modules to be available and they are not?

} else {
virtualNetwork.shimModule(
'@ember/test-helpers',
testOnlyStub('@ember/test-helpers'),
);
}

// The live-test loader shims these with real implementations.
virtualNetwork.shimModule(
'@cardstack/host/tests/helpers',
testOnlyStub('@cardstack/host/tests/helpers'),
Expand Down
Loading