Skip to content
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
7 changes: 5 additions & 2 deletions core/config/yaml/loadYaml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,18 @@ async function loadConfigYaml(options: {
} = options;

// Add local .continue blocks
// Use "content" uriType to pass pre-read content directly, avoiding
// fs.readFileSync which fails for vscode-remote:// URIs in WSL (#6242, #7810)
const localBlockPromises = BLOCK_TYPES.map(async (blockType) => {
const localBlocks = await getAllDotContinueDefinitionFiles(
ide,
{ includeGlobal: true, includeWorkspace: true, fileExtType: "yaml" },
blockType,
);
return localBlocks.map((b) => ({
uriType: "file" as const,
fileUri: b.path,
uriType: "content" as const,
content: b.content,
sourceUri: b.path,
}));
});
const localPackageIdentifiers: PackageIdentifier[] = (
Expand Down
3 changes: 3 additions & 0 deletions packages/config-yaml/src/__tests__/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ describe("E2E Scenarios", () => {

const registry: Registry = {
getContent: async function (id: PackageIdentifier): Promise<string> {
if (id.uriType === "content") {
return id.content;
}
const slug = packageIdentifierToShorthandSlug(id);
const filePath =
id.uriType === "slug"
Expand Down
39 changes: 39 additions & 0 deletions packages/config-yaml/src/interfaces/slugs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
encodeFullSlug,
encodePackageIdentifier,
encodePackageSlug,
packageIdentifierToDisplayName,
VirtualTags,
} from "./slugs.js";

Expand Down Expand Up @@ -216,4 +217,42 @@ describe("PackageIdentifier", () => {
}
}
});

it("should encode content-based package identifier with sourceUri", () => {
const testIdentifier = {
uriType: "content" as const,
content: "yaml content here",
sourceUri: "vscode-remote://wsl+Ubuntu/path/to/file.yaml",
};
const encoded = encodePackageIdentifier(testIdentifier);
expect(encoded).toBe("vscode-remote://wsl+Ubuntu/path/to/file.yaml");
});

it("should encode content-based package identifier without sourceUri", () => {
const testIdentifier = {
uriType: "content" as const,
content: "yaml content here",
};
const encoded = encodePackageIdentifier(testIdentifier);
expect(encoded).toBe("<inline content>");
});

it("should return display name for content identifier with sourceUri", () => {
const testIdentifier = {
uriType: "content" as const,
content: "yaml content here",
sourceUri: "vscode-remote://wsl+Ubuntu/path/to/file.yaml",
};
const displayName = packageIdentifierToDisplayName(testIdentifier);
expect(displayName).toBe("vscode-remote://wsl+Ubuntu/path/to/file.yaml");
});

it("should return display name for content identifier without sourceUri", () => {
const testIdentifier = {
uriType: "content" as const,
content: "yaml content here",
};
const displayName = packageIdentifierToDisplayName(testIdentifier);
expect(displayName).toBe("<inline content>");
});
});
24 changes: 22 additions & 2 deletions packages/config-yaml/src/interfaces/slugs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export interface FullSlug extends PackageSlug {
// Identifier roughly equals URI, except that we want to provide shorthand for slugs and filepaths
// Later it is possible to allow URIs as well since they can be uniquely parsed
interface BasePackageIdentifier {
uriType: "file" | "slug";
uriType: "file" | "slug" | "content";
}

interface FullSlugIdentifier extends BasePackageIdentifier {
Expand All @@ -70,14 +70,31 @@ interface FileIdentifier extends BasePackageIdentifier {
fileUri: string;
}

export type PackageIdentifier = FullSlugIdentifier | FileIdentifier;
/**
* ContentIdentifier allows passing pre-read content directly.
* This is used when content has already been read via IDE APIs (e.g., for
* vscode-remote:// URIs in WSL) and should not be re-read via fs.readFileSync.
*/
interface ContentIdentifier extends BasePackageIdentifier {
uriType: "content";
content: string;
/** Original URI for error messages and debugging */
sourceUri?: string;
}

export type PackageIdentifier =
| FullSlugIdentifier
| FileIdentifier
| ContentIdentifier;

export function packageIdentifierToDisplayName(id: PackageIdentifier): string {
switch (id.uriType) {
case "file":
return id.fileUri;
case "slug":
return id.fullSlug.packageSlug;
case "content":
return id.sourceUri ?? "<inline content>";
}
}

Expand All @@ -88,6 +105,9 @@ export function encodePackageIdentifier(identifier: PackageIdentifier): string {
case "file":
// For file paths, just return the path directly without a prefix
return identifier.fileUri;
case "content":
// For content identifiers, return the source URI if available
return identifier.sourceUri ?? "<inline content>";
default:
throw new Error(`Unknown URI type: ${(identifier as any).uriType}`);
}
Expand Down
2 changes: 2 additions & 0 deletions packages/config-yaml/src/load/clientRender.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ export function packageIdentifierToShorthandSlug(
return `${id.fullSlug.ownerSlug}/${id.fullSlug.packageSlug}`;
case "file":
return "/";
case "content":
return "/";
}
}

Expand Down
3 changes: 3 additions & 0 deletions packages/config-yaml/src/load/injectBlocks.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ class MockRegistry implements Registry {
}

async getContent(id: PackageIdentifier): Promise<string> {
if (id.uriType === "content") {
return id.content;
}
const key =
id.uriType === "file"
? id.fileUri
Expand Down
6 changes: 5 additions & 1 deletion packages/config-yaml/src/load/unroll.ts
Original file line number Diff line number Diff line change
Expand Up @@ -574,13 +574,17 @@ export async function unrollBlocks(
source:
injectBlock.uriType === "file"
? injectBlock.fileUri
: undefined,
: injectBlock.uriType === "content"
? injectBlock.sourceUri
: undefined,
error: null,
};
} catch (err) {
let msg = "";
if (injectBlock.uriType === "file") {
msg = `${(err as Error).message}.\n> ${injectBlock.fileUri}`;
} else if (injectBlock.uriType === "content") {
msg = `${(err as Error).message}.\n> ${injectBlock.sourceUri ?? "<inline content>"}`;
} else {
msg = `${(err as Error).message}.\n> ${injectBlock.fullSlug}`;
}
Expand Down
25 changes: 25 additions & 0 deletions packages/config-yaml/src/registryClient.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,31 @@ describe("RegistryClient", () => {
"Unknown package identifier type: unknown",
);
});

it("should return pre-read content directly for content uriType", async () => {
const client = new RegistryClient();

const id: PackageIdentifier = {
uriType: "content",
content: "pre-read yaml content",
sourceUri: "vscode-remote://wsl+Ubuntu/path/to/file.yaml",
};

const result = await client.getContent(id);
expect(result).toBe("pre-read yaml content");
});

it("should return content without sourceUri", async () => {
const client = new RegistryClient();

const id: PackageIdentifier = {
uriType: "content",
content: "content without source",
};

const result = await client.getContent(id);
expect(result).toBe("content without source");
});
});

describe("getContentFromFilePath", () => {
Expand Down
3 changes: 3 additions & 0 deletions packages/config-yaml/src/registryClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ export class RegistryClient implements Registry {

async getContent(id: PackageIdentifier): Promise<string> {
switch (id.uriType) {
case "content":
// Content was pre-read (e.g., via IDE APIs for vscode-remote:// URIs)
return id.content;
case "file":
return this.getContentFromFilePath(id.fileUri);
case "slug":
Expand Down
Loading