Skip to content

Commit 921a4ab

Browse files
authored
feat: Only extract/inject usercode when pull/pushing iframe recipes with CLI (#1305)
1 parent 9a3e994 commit 921a4ab

File tree

3 files changed

+75
-18
lines changed

3 files changed

+75
-18
lines changed

packages/charm/src/iframe/recipe.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ export const getIframeRecipe = (
6868
runtime: Runtime,
6969
): {
7070
recipeId: string;
71+
// `src` is either a single file string source, or the entry
72+
// file source code in a recipe.
7173
src?: string;
7274
iframe?: IFrameRecipe;
7375
} => {
@@ -76,9 +78,13 @@ export const getIframeRecipe = (
7678
console.warn("No recipeId found for charm", getEntityId(charm));
7779
return { recipeId, src: "", iframe: undefined };
7880
}
79-
const src = runtime.recipeManager.getRecipeMeta({ recipeId })?.src;
81+
const meta = runtime.recipeManager.getRecipeMeta({ recipeId });
82+
const src = meta
83+
? (meta.src ??
84+
meta.program?.files.find((file) => file.name === meta.program?.main)
85+
?.contents)
86+
: undefined;
8087
if (!src) {
81-
console.warn("No src found for charm", getEntityId(charm));
8288
return { recipeId };
8389
}
8490
try {

packages/charm/src/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,11 @@ export {
2929
modifyCharm,
3030
renameCharm,
3131
} from "./commands.ts";
32-
export { getIframeRecipe, type IFrameRecipe } from "./iframe/recipe.ts";
32+
export {
33+
buildFullRecipe,
34+
getIframeRecipe,
35+
type IFrameRecipe,
36+
} from "./iframe/recipe.ts";
3337
export { type ParsedMention, type ProcessedPrompt } from "./imagine.ts";
3438
export { formatPromptWithMentions, parseComposerDocument } from "./format.ts";
3539

packages/cli/lib/charm.ts

Lines changed: 62 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,21 @@
11
import { ANYONE, Identity, Session } from "@commontools/identity";
22
import { ensureDir } from "@std/fs";
33
import { loadIdentity } from "./identity.ts";
4-
import {
5-
Cell,
6-
getEntityId,
7-
NAME,
8-
Recipe,
9-
RecipeMeta,
10-
Runtime,
11-
} from "@commontools/runner";
4+
import { Cell, NAME, Recipe, RecipeMeta, Runtime } from "@commontools/runner";
125
import { StorageManager } from "@commontools/runner/storage/cache";
136
import {
7+
buildFullRecipe,
148
Charm,
159
charmId,
1610
CharmManager,
1711
compileRecipe,
12+
extractUserCode,
13+
getIframeRecipe,
1814
getRecipeIdFromCharm,
15+
injectUserCode,
1916
processSchema,
2017
} from "@commontools/charm";
21-
import { dirname, join } from "@std/path";
18+
import { join } from "@std/path";
2219
import { CliProgram } from "./dev.ts";
2320

2421
export interface SpaceConfig {
@@ -97,6 +94,29 @@ async function getRecipeMeta(
9794
) as RecipeMeta;
9895
}
9996

97+
async function getIframeRecipeFromFile(
98+
manager: CharmManager,
99+
mainPath: string,
100+
charmId: string,
101+
): Promise<Recipe> {
102+
const charm = await manager.get(charmId);
103+
if (!charm) {
104+
throw new Error(`Charm "${charmId}" not found.`);
105+
}
106+
const iframeRecipe = getIframeRecipe(charm, manager.runtime);
107+
if (!iframeRecipe.iframe) {
108+
throw new Error(`Expected charm "${charmId}" to be an iframe recipe.`);
109+
}
110+
iframeRecipe.iframe.src = injectUserCode(await Deno.readTextFile(mainPath));
111+
return await compileRecipe(
112+
buildFullRecipe(iframeRecipe.iframe),
113+
"recipe",
114+
manager.runtime,
115+
manager.getSpace(),
116+
undefined,
117+
);
118+
}
119+
100120
async function getRecipeFromFile(
101121
manager: CharmManager,
102122
mainPath: string,
@@ -119,6 +139,10 @@ async function getRecipeFromService(
119139
charmId: string,
120140
): Promise<Recipe> {
121141
const charm = await manager.get(charmId, false);
142+
if (!charm) {
143+
throw new Error(`Charm "${charmId}" not found`);
144+
}
145+
122146
const recipeId = getRecipeIdFromCharm(charm!);
123147
return await manager.runtime.recipeManager.loadRecipe(
124148
recipeId,
@@ -180,10 +204,15 @@ export async function setCharmRecipe(
180204
mainPath: string,
181205
): Promise<void> {
182206
const manager = await loadManager(config);
183-
const recipe = await getRecipeFromFile(
184-
manager,
185-
mainPath,
186-
);
207+
let recipe;
208+
if (mainPath.endsWith(".iframe.js")) {
209+
recipe = await getIframeRecipeFromFile(manager, mainPath, config.charm);
210+
} else {
211+
recipe = await getRecipeFromFile(
212+
manager,
213+
mainPath,
214+
);
215+
}
187216
await exec({ manager, recipe, charmId: config.charm });
188217
}
189218

@@ -193,9 +222,27 @@ export async function saveCharmRecipe(
193222
): Promise<void> {
194223
await ensureDir(outPath);
195224
const manager = await loadManager(config);
196-
const meta = await getRecipeMeta(manager, config.charm);
225+
const recipe = await getRecipeFromService(manager, config.charm);
226+
const meta = manager.runtime.recipeManager.getRecipeMeta(
227+
recipe,
228+
) as RecipeMeta;
197229

198-
if (meta.src) {
230+
const charm = await manager.get(config.charm);
231+
if (!charm) {
232+
throw new Error(`Charm ${config.charm} not found.`);
233+
}
234+
235+
const iframeRecipe = getIframeRecipe(charm, manager.runtime);
236+
if (iframeRecipe.iframe) {
237+
const userCode = extractUserCode(iframeRecipe.iframe.src);
238+
if (!userCode) {
239+
throw new Error(`No user code found in iframe recipe "${config.charm}".`);
240+
}
241+
await Deno.writeTextFile(
242+
join(outPath, "main.iframe.js"),
243+
userCode,
244+
);
245+
} else if (meta.src) {
199246
// Write the main source file
200247
await Deno.writeTextFile(join(outPath, "main.tsx"), meta.src);
201248
} else if (meta.program) {

0 commit comments

Comments
 (0)