Skip to content
Draft
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
11 changes: 11 additions & 0 deletions packages/base/card-serialization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
getSerializer,
humanReadable,
identifyCard,
isRegisteredPrefix,
isSingleCardDocument,
isSingleFileMetaDocument,
loadCardDef,
Expand Down Expand Up @@ -221,6 +222,11 @@ export function serializeCard(
...opts,
...{
maybeRelativeURL(possibleURL: string) {
// Registered prefix refs (e.g. @cardstack/catalog/foo) are already
// in their canonical portable form — return as-is
if (isRegisteredPrefix(possibleURL)) {
return possibleURL;
}
let url = maybeURL(possibleURL, modelRelativeTo);
if (!url) {
throw new Error(
Expand Down Expand Up @@ -316,6 +322,11 @@ export function serializeFileDef(
...opts,
...{
maybeRelativeURL(possibleURL: string) {
// Registered prefix refs (e.g. @cardstack/catalog/foo) are already
// in their canonical portable form — return as-is
if (isRegisteredPrefix(possibleURL)) {
return possibleURL;
}
let url = maybeURL(possibleURL, modelRelativeTo);
if (!url) {
throw new Error(
Expand Down
21 changes: 21 additions & 0 deletions packages/runtime-common/card-reference-resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ export function registerCardReferencePrefix(
prefixMappings.set(prefix, targetURL);
}

export function isRegisteredPrefix(reference: string): boolean {
for (let [prefix] of prefixMappings) {
if (reference.startsWith(prefix)) {
return true;
}
}
return false;
}

function isUrlLikeReference(ref: string): boolean {
return (
ref.startsWith('.') ||
Expand All @@ -32,3 +41,15 @@ export function resolveCardReference(
}
return new URL(reference, relativeTo).href;
}

// Reverse of resolveCardReference: converts a resolved URL back to
// its registered prefix form if one matches.
// e.g. "http://localhost:4201/catalog/foo" → "@cardstack/catalog/foo"
export function unresolveCardReference(resolvedURL: string): string {
for (let [prefix, target] of prefixMappings) {
if (resolvedURL.startsWith(target)) {
return prefix + resolvedURL.slice(target.length);
}
}
return resolvedURL;
}
12 changes: 10 additions & 2 deletions packages/runtime-common/definition-lookup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ import {
hasExecutableExtension,
trimExecutableExtension,
} from './index';
import { isRegisteredPrefix } from './card-reference-resolver';
import type { VirtualNetwork } from './virtual-network';
import { unresolveCardReference } from './card-reference-resolver';

const MODULES_TABLE = 'modules';
const PREFERRED_EXECUTABLE_EXTENSIONS = ['.gts', '.ts', '.gjs', '.js'];
Expand All @@ -40,7 +42,7 @@ function canonicalURL(url: string, relativeTo?: string): string {
let parsed = new URL(url, relativeTo);
parsed.search = '';
parsed.hash = '';
return parsed.href;
return unresolveCardReference(parsed.href);
} catch (_e) {
let stripped = url.split('#')[0] ?? url;
return stripped.split('?')[0] ?? stripped;
Expand All @@ -54,7 +56,8 @@ function normalizeExecutableURL(url: string): string {
try {
return trimExecutableExtension(new URL(url)).href;
} catch (_e) {
return url;
// Handle non-URL identifiers like @cardstack/catalog/foo.gts
return url.replace(/\.(gts|ts|js|gjs)$/, '');
}
}

Expand Down Expand Up @@ -769,6 +772,11 @@ export class CachingDefinitionLookup implements DefinitionLookup {

private normalizeDependencyForLookup(dep: string, relativeTo: URL): string {
let canonical = canonicalURL(dep, relativeTo.href);
// For registered prefix deps (e.g. @cardstack/catalog/foo.gts),
// trim executable extensions without URL parsing
if (isRegisteredPrefix(canonical)) {
return canonical.replace(/\.(gts|ts|js|gjs)$/, '');
}
try {
let url = new URL(canonical);
if (hasExecutableExtension(url.href)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { trimExecutableExtension } from '../index';
import { isRegisteredPrefix } from '../card-reference-resolver';
import { canonicalURL } from './dependency-url';

export function isExtensionlessPath(url: URL): boolean {
Expand Down Expand Up @@ -53,6 +54,11 @@ export function normalizeDependencyForLookup(
relativeTo: URL,
): string {
let canonical = canonicalURL(dep, relativeTo.href);
// For registered prefix deps (e.g. @cardstack/catalog/foo.gts),
// trim executable extensions without URL parsing
if (isRegisteredPrefix(canonical)) {
return canonical.replace(/\.(gts|ts|js|gjs)$/, '');
}
try {
return trimExecutableExtension(new URL(canonical)).href;
} catch (_err) {
Expand Down
15 changes: 13 additions & 2 deletions packages/runtime-common/index-runner/dependency-url.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
import { resolveCardReference } from '../card-reference-resolver';
import {
resolveCardReference,
unresolveCardReference,
isRegisteredPrefix,
} from '../card-reference-resolver';

export function canonicalURL(url: string, relativeTo?: string): string {
try {
// If the URL is already a registered prefix (e.g. @cardstack/catalog/foo),
// keep it in that form — it's already canonical.
if (isRegisteredPrefix(url)) {
let stripped = url.split('#')[0] ?? url;
return stripped.split('?')[0] ?? stripped;
}
let resolved = resolveCardReference(url, relativeTo);
let parsed = new URL(resolved);
parsed.search = '';
parsed.hash = '';
return parsed.href;
// Convert resolved URLs back to prefix form if possible
return unresolveCardReference(parsed.href);
} catch (_e) {
let stripped = url.split('#')[0] ?? url;
return stripped.split('?')[0] ?? stripped;
Expand Down
13 changes: 9 additions & 4 deletions packages/runtime-common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,10 @@ export { v4 as uuidv4 } from '@lukeed/uuid'; // isomorphic UUID's using Math.ran
import type { LocalPath } from './paths';
import type { CardTypeFilter, Query, DataQuery, EveryFilter } from './query';
import { Loader } from './loader';
import { resolveCardReference } from './card-reference-resolver';
import {
resolveCardReference,
unresolveCardReference,
} from './card-reference-resolver';
export * from './paths';
export * from './cached-fetch';
export * from './definition-lookup';
Expand Down Expand Up @@ -664,9 +667,11 @@ export function internalKeyFor(
relativeTo: URL | undefined,
): string {
if (!('type' in ref)) {
let module = trimExecutableExtension(
new URL(resolveCardReference(ref.module, relativeTo)),
).href;
let resolved = resolveCardReference(ref.module, relativeTo);
let module = trimExecutableExtension(new URL(resolved)).href;
// Use the prefix form (e.g. @cardstack/catalog/foo) as the canonical
// internal key when a registered prefix mapping matches
module = unresolveCardReference(module);
return `${module}/${ref.name}`;
}
switch (ref.type) {
Expand Down
5 changes: 4 additions & 1 deletion packages/runtime-common/loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
trackRuntimeModuleDependency,
type RuntimeDependencyTrackingContext,
} from './dependency-tracker';
import { unresolveCardReference } from './card-reference-resolver';

type FetchingModule = {
state: 'fetching';
Expand Down Expand Up @@ -562,7 +563,9 @@ export class Loader {
module: any,
moduleIdentifier: string,
) {
let moduleId = trimExecutableExtension(new URL(moduleIdentifier)).href;
let moduleId = unresolveCardReference(
trimExecutableExtension(new URL(moduleIdentifier)).href,
);
for (let propName of Object.keys(module)) {
let exportedEntity = module[propName];
if (
Expand Down
6 changes: 6 additions & 0 deletions packages/runtime-common/realm-index-query-engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
maxLinkDepth,
maybeURL,
resolveCardReference,
isRegisteredPrefix,
IndexQueryEngine,
codeRefWithAbsoluteURL,
logger,
Expand Down Expand Up @@ -1038,6 +1039,11 @@ function relativizeResource(
setURL(maybeRelativeURL(urlObj, primaryURL, realmURL));
});
visitModuleDeps(resource, (moduleURL, setModuleURL) => {
// Registered prefix references (e.g. @cardstack/catalog/foo) are already
// in their canonical portable form — don't resolve or relativize them.
if (isRegisteredPrefix(moduleURL)) {
return;
}
let absoluteModuleURL = new URL(
resolveCardReference(moduleURL, resource.id ?? primaryURL),
);
Expand Down
Loading