@bettercms-ai/convert 0.17.1 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/dist/cli.js +188 -23
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +26 -5
- package/dist/index.js +101 -22
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -951,7 +951,7 @@ declare function attrsFor(path: string, kind: string, props?: AttrBinding[], sco
|
|
|
951
951
|
declare function propsAttribute(props: AttrBinding[]): string;
|
|
952
952
|
|
|
953
953
|
/** Bumped whenever the emitted source changes. `isKnownHelper` upgrades anything older. */
|
|
954
|
-
declare const HELPER_VERSION =
|
|
954
|
+
declare const HELPER_VERSION = 8;
|
|
955
955
|
/** Where each dialect's helper lives. Null = this dialect reads nothing at build time. */
|
|
956
956
|
declare const HELPER_PATH: Record<Dialect, string | null>;
|
|
957
957
|
/** The directory the snapshots live in, at the repository root. */
|
|
@@ -1196,6 +1196,14 @@ interface ImageContext {
|
|
|
1196
1196
|
snapshotName: (slug: string) => string;
|
|
1197
1197
|
/** How this file reads its route parameter, on a dynamic route. Null off one. */
|
|
1198
1198
|
slug: string | null;
|
|
1199
|
+
/**
|
|
1200
|
+
* Follow a prop into the component that renders it. @see drillProp
|
|
1201
|
+
*
|
|
1202
|
+
* Most Astro pages import an image and hand it to a section component (`<Hero src={hero} />`),
|
|
1203
|
+
* which renders the `<Image>`. Without this, every such image was `IMAGE_ASSET_UNRESOLVED` and a
|
|
1204
|
+
* converted site had no editable images at all.
|
|
1205
|
+
*/
|
|
1206
|
+
drill?: (component: string, prop: string, kind: string) => Promise<DrillOutcome>;
|
|
1199
1207
|
}
|
|
1200
1208
|
interface ImageResult {
|
|
1201
1209
|
splices: ImageSplice[];
|
|
@@ -1205,6 +1213,18 @@ interface ImageResult {
|
|
|
1205
1213
|
key: string;
|
|
1206
1214
|
reason: "IMAGE_ASSET_UNRESOLVED";
|
|
1207
1215
|
}[];
|
|
1216
|
+
/** Edits to the components an image prop was drilled into. */
|
|
1217
|
+
componentEdits: {
|
|
1218
|
+
file: string;
|
|
1219
|
+
splices: ImageSplice[];
|
|
1220
|
+
}[];
|
|
1221
|
+
/** Each drilled image: the component file, the prop, the path, and the call-site literal. */
|
|
1222
|
+
props: {
|
|
1223
|
+
file: string;
|
|
1224
|
+
prop: string;
|
|
1225
|
+
path: string;
|
|
1226
|
+
literal: string;
|
|
1227
|
+
}[];
|
|
1208
1228
|
}
|
|
1209
1229
|
/**
|
|
1210
1230
|
* Which local binding this built url came from, or null when nothing here says.
|
|
@@ -1231,7 +1251,7 @@ declare function assetImport(parsed: ParsedFile, dialect: Dialect, original: str
|
|
|
1231
1251
|
* one, the import the bundler already optimised when there is not — so a build against the
|
|
1232
1252
|
* committed `{}` stub renders exactly the image the repository renders today.
|
|
1233
1253
|
*/
|
|
1234
|
-
declare function bindImages(ctx: ImageContext, targets: ImageTarget[]): ImageResult
|
|
1254
|
+
declare function bindImages(ctx: ImageContext, targets: ImageTarget[]): Promise<ImageResult>;
|
|
1235
1255
|
|
|
1236
1256
|
/** The recipe an Astro site needs instead — never auto-migrate a site to SSR. */
|
|
1237
1257
|
declare const SSR_DRAFT_RECIPE: string;
|
|
@@ -1293,10 +1313,11 @@ declare function isSourceCandidate(path: string): boolean;
|
|
|
1293
1313
|
* could lift — and the target is a number someone has to be able to reach. They are not copy an
|
|
1294
1314
|
* author edits, so they are not copy this measures.
|
|
1295
1315
|
*
|
|
1296
|
-
* - a decorative glyph:
|
|
1297
|
-
*
|
|
1316
|
+
* - a decorative glyph: no letter and no digit (`✓`, `→`), or an ordinal (`01`, `2.`, `3 /`)
|
|
1317
|
+
* that numbers a list rather than saying anything. Length decides nothing: `OK` and `$1,200`
|
|
1318
|
+
* are copy. The server's `extract-fields.ts` isDecorativeGlyph, verbatim;
|
|
1298
1319
|
* - the platform badge every free site renders;
|
|
1299
|
-
* - a skip link, which exists for keyboards and is never edited.
|
|
1320
|
+
* - a skip link, which exists for keyboards and is never edited. @see isSkipLink
|
|
1300
1321
|
*/
|
|
1301
1322
|
declare const isDecorativeGlyph: (text: string) => boolean;
|
|
1302
1323
|
interface XrayBucket {
|
package/dist/index.js
CHANGED
|
@@ -3185,6 +3185,7 @@ function soleChild(node) {
|
|
|
3185
3185
|
const kept = node.children.filter((c) => !(c.type === "text" && c.value.trim() === ""));
|
|
3186
3186
|
return kept.length === 1 ? kept[0] : null;
|
|
3187
3187
|
}
|
|
3188
|
+
var ASSET_TAGS = /* @__PURE__ */ new Set(["Image", "Picture"]);
|
|
3188
3189
|
function findPropTargets(content, parsed, propName, dialect, imported = null) {
|
|
3189
3190
|
const empty = {
|
|
3190
3191
|
targets: [],
|
|
@@ -3201,6 +3202,7 @@ function findPropTargets(content, parsed, propName, dialect, imported = null) {
|
|
|
3201
3202
|
const reception = dialect === "astro" ? astroReception(ast, propName, offset, content) : receptionIn(component, propName, offset, content);
|
|
3202
3203
|
if (!reception.receivable) return empty;
|
|
3203
3204
|
const names = new Set(reception.names.size > 0 ? reception.names : [propName]);
|
|
3205
|
+
const assetComponents = /from\s+["']astro:assets["']/.test(content);
|
|
3204
3206
|
const within = (node) => component === null || node.start >= (component.start ?? 0) + offset && node.start <= (component.end ?? 0) + offset;
|
|
3205
3207
|
const targets = [];
|
|
3206
3208
|
const forwards = [];
|
|
@@ -3210,6 +3212,15 @@ function findPropTargets(content, parsed, propName, dialect, imported = null) {
|
|
|
3210
3212
|
return;
|
|
3211
3213
|
}
|
|
3212
3214
|
if (node.type !== "element") return;
|
|
3215
|
+
const sources = IMAGE_SOURCES[node.tag.toLowerCase()] ?? (assetComponents && ASSET_TAGS.has(node.tag) ? ["src"] : []);
|
|
3216
|
+
const readsImage = node.attrs.some((attr) => {
|
|
3217
|
+
if (!sources.includes(attr.name) || !attr.expressionRange) return false;
|
|
3218
|
+
return readsProp(content.slice(attr.expressionRange.start, attr.expressionRange.end), propName, names);
|
|
3219
|
+
});
|
|
3220
|
+
if (readsImage) {
|
|
3221
|
+
if (within(node)) targets.push({ attrInsertAt: node.attrInsertAt, inner: null, tag: node.tag });
|
|
3222
|
+
return;
|
|
3223
|
+
}
|
|
3213
3224
|
if (isIntrinsic(node.tag)) {
|
|
3214
3225
|
const child = soleChild(node);
|
|
3215
3226
|
if (within(node) && child?.type === "expr" && readsProp(child.value, propName, names)) {
|
|
@@ -4067,6 +4078,27 @@ function baseName(url) {
|
|
|
4067
4078
|
const base = file.split(".")[0] ?? "";
|
|
4068
4079
|
return base === "" ? null : base.toLowerCase();
|
|
4069
4080
|
}
|
|
4081
|
+
function passedTo(content, parsed, binding) {
|
|
4082
|
+
const out = [];
|
|
4083
|
+
const visit = (node) => {
|
|
4084
|
+
if (node.type === "expr") {
|
|
4085
|
+
for (const child of node.children) visit(child);
|
|
4086
|
+
return;
|
|
4087
|
+
}
|
|
4088
|
+
if (node.type !== "element" || node.raw) return;
|
|
4089
|
+
const isComponent = /^[A-Z]/.test(node.tag) && !ASSET_COMPONENTS.has(node.tag);
|
|
4090
|
+
if (isComponent && !node.attrs.some((a) => a.name === "bcmsBindings")) {
|
|
4091
|
+
for (const attr of node.attrs) {
|
|
4092
|
+
if (!attr.expressionRange) continue;
|
|
4093
|
+
const inner = content.slice(attr.expressionRange.start, attr.expressionRange.end).replace(/^\{|\}$/g, "").trim();
|
|
4094
|
+
if (inner === binding) out.push({ node, prop: attr.name, attr });
|
|
4095
|
+
}
|
|
4096
|
+
}
|
|
4097
|
+
for (const child of node.children) visit(child);
|
|
4098
|
+
};
|
|
4099
|
+
for (const root of parsed.roots) visit(root);
|
|
4100
|
+
return out;
|
|
4101
|
+
}
|
|
4070
4102
|
function assetCandidates(parsed, dialect, original) {
|
|
4071
4103
|
const wanted = baseName(original);
|
|
4072
4104
|
if (wanted === null || parsed.error) return [];
|
|
@@ -4104,8 +4136,8 @@ function renderedBy(content, parsed, dialect, binding) {
|
|
|
4104
4136
|
for (const root of parsed.roots) visit(root);
|
|
4105
4137
|
return out;
|
|
4106
4138
|
}
|
|
4107
|
-
function bindImages(ctx, targets) {
|
|
4108
|
-
const out = { splices: [], slugs: [], bound: [], refused: [] };
|
|
4139
|
+
async function bindImages(ctx, targets) {
|
|
4140
|
+
const out = { splices: [], slugs: [], bound: [], refused: [], componentEdits: [], props: [] };
|
|
4109
4141
|
const rule = DIALECT_RULES[ctx.dialect];
|
|
4110
4142
|
if (!rule.attr || !rule.text || ctx.parsed.error) return out;
|
|
4111
4143
|
const claimed = /* @__PURE__ */ new Set();
|
|
@@ -4113,6 +4145,23 @@ function bindImages(ctx, targets) {
|
|
|
4113
4145
|
const binding = assetImport(ctx.parsed, ctx.dialect, target.original);
|
|
4114
4146
|
const rendered = binding === null ? [] : renderedBy(ctx.content, ctx.parsed, ctx.dialect, binding);
|
|
4115
4147
|
const free = rendered.filter((hit2) => !claimed.has(hit2.node.attrInsertAt));
|
|
4148
|
+
const args = (b) => [ctx.snapshotName(target.slug), JSON.stringify(target.path), b, ...ctx.slug ? [ctx.slug] : []];
|
|
4149
|
+
if (free.length === 0 && binding !== null && ctx.drill) {
|
|
4150
|
+
const calls = passedTo(ctx.content, ctx.parsed, binding).filter((c) => !claimed.has(c.node.attrInsertAt));
|
|
4151
|
+
const call = calls.length === 1 ? calls[0] : null;
|
|
4152
|
+
const drilled = call ? await ctx.drill(call.node.tag, call.prop, target.kind || "image") : null;
|
|
4153
|
+
if (call && drilled?.ok) {
|
|
4154
|
+
claimed.add(call.node.attrInsertAt);
|
|
4155
|
+
const literal = `${call.prop}: ${JSON.stringify(target.path)}`;
|
|
4156
|
+
out.splices.push({ start: call.attr.range.start, end: call.attr.range.end, text: rule.attr(call.prop, `${ctx.image}(${args(binding).join(", ")})`) });
|
|
4157
|
+
out.splices.push({ start: call.node.attrInsertAt, end: call.node.attrInsertAt, text: ` bcmsBindings={{ ${literal} }}` });
|
|
4158
|
+
out.componentEdits.push(...drilled.edits);
|
|
4159
|
+
out.props.push({ file: drilled.edits[0].file, prop: call.prop, path: target.path, literal });
|
|
4160
|
+
out.slugs.push(target.slug);
|
|
4161
|
+
out.bound.push(target.key);
|
|
4162
|
+
continue;
|
|
4163
|
+
}
|
|
4164
|
+
}
|
|
4116
4165
|
if (free.length !== 1) {
|
|
4117
4166
|
out.refused.push({ key: target.key, reason: "IMAGE_ASSET_UNRESOLVED" });
|
|
4118
4167
|
continue;
|
|
@@ -4120,16 +4169,10 @@ function bindImages(ctx, targets) {
|
|
|
4120
4169
|
const hit = free[0];
|
|
4121
4170
|
claimed.add(hit.node.attrInsertAt);
|
|
4122
4171
|
const attr = hit.node.attrs.find((a) => a.name === hit.attr);
|
|
4123
|
-
const args = [
|
|
4124
|
-
ctx.snapshotName(target.slug),
|
|
4125
|
-
JSON.stringify(target.path),
|
|
4126
|
-
binding,
|
|
4127
|
-
...ctx.slug ? [ctx.slug] : []
|
|
4128
|
-
];
|
|
4129
4172
|
out.splices.push({
|
|
4130
4173
|
start: attr.range.start,
|
|
4131
4174
|
end: attr.range.end,
|
|
4132
|
-
text: rule.attr(hit.attr, `${ctx.image}(${args.join(", ")})`)
|
|
4175
|
+
text: rule.attr(hit.attr, `${ctx.image}(${args(binding).join(", ")})`)
|
|
4133
4176
|
});
|
|
4134
4177
|
out.splices.push({
|
|
4135
4178
|
start: hit.node.attrInsertAt,
|
|
@@ -4144,7 +4187,7 @@ function bindImages(ctx, targets) {
|
|
|
4144
4187
|
|
|
4145
4188
|
// src/helper.ts
|
|
4146
4189
|
import { createHash as createHash2 } from "crypto";
|
|
4147
|
-
var HELPER_VERSION =
|
|
4190
|
+
var HELPER_VERSION = 8;
|
|
4148
4191
|
var HEADER = "// @bettercms-ai/convert helper v";
|
|
4149
4192
|
var HELPER_PATH = Object.fromEntries(
|
|
4150
4193
|
Object.keys(DIALECT_RULES).map((d) => [d, DIALECT_RULES[d].helper])
|
|
@@ -4276,8 +4319,18 @@ export function bcmsTuples(page: unknown, paths: string[][], fallback: string[][
|
|
|
4276
4319
|
return paths.map((row, index) => bcmsList(page, row, fallback[index] ?? [], slug));
|
|
4277
4320
|
}
|
|
4278
4321
|
|
|
4279
|
-
/**
|
|
4322
|
+
/**
|
|
4323
|
+
* An image field's url, or the asset the template already imports. @see IMAGE_ASSET_UNRESOLVED
|
|
4324
|
+
*
|
|
4325
|
+
* \u{1F534} AN IMPORTED ASSET IS KEPT AT BUILD. \`<Image src={\u2026}>\` from \`astro:assets\` refuses a remote
|
|
4326
|
+
* url string without width, height and an allowed domain, so returning the CMS url here broke the
|
|
4327
|
+
* build the moment an editor replaced the image. The build renders the optimised import; the
|
|
4328
|
+
* release write (\`writeImage\`) puts the editor's image on the \`data-bcms-field\` element, which
|
|
4329
|
+
* is where every published value lands. A plain string fallback (\`<img src="/hero.png">\`) has
|
|
4330
|
+
* no such rule and takes the CMS url directly.
|
|
4331
|
+
*/
|
|
4280
4332
|
export function bcmsImage<T>(page: unknown, path: string, fallback: T, slug?: string): T | string {
|
|
4333
|
+
if (fallback !== null && typeof fallback === "object") return fallback;
|
|
4281
4334
|
const entries = isRecord(page) ? page.entries : undefined;
|
|
4282
4335
|
const root = slug !== undefined && isRecord(entries) ? entries[slug] : page;
|
|
4283
4336
|
return render(readPath(root, path)) ?? fallback;
|
|
@@ -4443,7 +4496,8 @@ var HISTORICAL = {
|
|
|
4443
4496
|
3: "d4305b2983adcb456ef782d35ea9d083a04465c2b68baa0d44a084dfa01a9227",
|
|
4444
4497
|
4: "a5742f64dacb1b26ee6dd5e1469a54871487934c71bc8267df19e3a80e417595",
|
|
4445
4498
|
5: "0472fe3ffdcfa49e719c7b9da9cb63b6eba637cc22783bb00c3c2cadd3d800da",
|
|
4446
|
-
6: "c907e7245578d947c6541b1ab2c5c6659ea7789a1519e895b4a4aed122c6f649"
|
|
4499
|
+
6: "c907e7245578d947c6541b1ab2c5c6659ea7789a1519e895b4a4aed122c6f649",
|
|
4500
|
+
7: "f22514ef48c50230985e605bea6cf9bf41756384a7dc80603e347baba9b65d93"
|
|
4447
4501
|
},
|
|
4448
4502
|
"src/lib/bcms-content.ts": {
|
|
4449
4503
|
1: "ae72ae14d706943d481c880d70da5789b4816ba1ba319a10fbc63ded4d0b42f2",
|
|
@@ -4451,7 +4505,8 @@ var HISTORICAL = {
|
|
|
4451
4505
|
3: "0be9c73160361882a038552bc02673c14ed532287823dabaafe36179bc55ed7e",
|
|
4452
4506
|
4: "0146d027a727ecd3c9e3fc336fcf1299b59ddf75b04091c05703f3f983ec28af",
|
|
4453
4507
|
5: "c8c93655320fa65664611ba84541f3fa7bfa292cc9a2fd655373440b99049fba",
|
|
4454
|
-
6: "63f1fa29e47e70c5193b81ee24addb238ba100247124d55bc17713102d587df5"
|
|
4508
|
+
6: "63f1fa29e47e70c5193b81ee24addb238ba100247124d55bc17713102d587df5",
|
|
4509
|
+
7: "ed92fb88de953d9951abbe6a1edae1d048f4050eaf6fb607ac08167f94d8bcc7"
|
|
4455
4510
|
},
|
|
4456
4511
|
"composables/bcms-content.ts": {
|
|
4457
4512
|
1: "c272412532efed7db64b6cf313b2bed4291f9951430412289c8a588b7f565407",
|
|
@@ -4459,7 +4514,8 @@ var HISTORICAL = {
|
|
|
4459
4514
|
3: "d4305b2983adcb456ef782d35ea9d083a04465c2b68baa0d44a084dfa01a9227",
|
|
4460
4515
|
4: "a5742f64dacb1b26ee6dd5e1469a54871487934c71bc8267df19e3a80e417595",
|
|
4461
4516
|
5: "0472fe3ffdcfa49e719c7b9da9cb63b6eba637cc22783bb00c3c2cadd3d800da",
|
|
4462
|
-
6: "c907e7245578d947c6541b1ab2c5c6659ea7789a1519e895b4a4aed122c6f649"
|
|
4517
|
+
6: "c907e7245578d947c6541b1ab2c5c6659ea7789a1519e895b4a4aed122c6f649",
|
|
4518
|
+
7: "f22514ef48c50230985e605bea6cf9bf41756384a7dc80603e347baba9b65d93"
|
|
4463
4519
|
}
|
|
4464
4520
|
};
|
|
4465
4521
|
var sha256 = (value) => createHash2("sha256").update(value, "utf8").digest("hex");
|
|
@@ -6811,7 +6867,7 @@ function isSourceCandidate(path) {
|
|
|
6811
6867
|
}
|
|
6812
6868
|
|
|
6813
6869
|
// src/xray.ts
|
|
6814
|
-
var SKIP_TAGS = /* @__PURE__ */ new Set(["svg", "iframe", "head", "title"]);
|
|
6870
|
+
var SKIP_TAGS = /* @__PURE__ */ new Set(["svg", "math", "iframe", "head", "title", "object", "canvas", "select"]);
|
|
6815
6871
|
var CONTEXT_TAGS = /* @__PURE__ */ new Set([
|
|
6816
6872
|
"nav",
|
|
6817
6873
|
"header",
|
|
@@ -6829,10 +6885,16 @@ var CONTEXT_TAGS = /* @__PURE__ */ new Set([
|
|
|
6829
6885
|
"figure",
|
|
6830
6886
|
"blockquote"
|
|
6831
6887
|
]);
|
|
6832
|
-
var BOUND_ATTRS = ["data-bcms-field", "data-bcms-layout-field"
|
|
6833
|
-
var isDecorativeGlyph = (text) =>
|
|
6888
|
+
var BOUND_ATTRS = ["data-bcms-field", "data-bcms-layout-field"];
|
|
6889
|
+
var isDecorativeGlyph = (text) => {
|
|
6890
|
+
const t = flat2(text);
|
|
6891
|
+
return t === "" || !/[\p{L}\p{N}]/u.test(t) || new RegExp("^\\p{Nd}{1,2}\\s*[/.)\\-\u2013\u2014]?$", "u").test(t);
|
|
6892
|
+
};
|
|
6893
|
+
var PLATFORM_BADGE = /^made with\b/iu;
|
|
6834
6894
|
var MAX_BUCKETS = 12;
|
|
6835
6895
|
var flat2 = (value) => value.replace(/\s+/g, " ").trim();
|
|
6896
|
+
var textOf = (node) => node.type === "text" ? node.value : node.type === "element" ? node.children.map(textOf).join("") : "";
|
|
6897
|
+
var isSkipLink = (node, attrs) => node.tag === "a" && (attrs.get("href") ?? "").startsWith("#") && (/^skip\b/iu.test(flat2(textOf(node))) || /\bskip[-_]?(link|nav|to|content)\b/iu.test(attrs.get("class") ?? ""));
|
|
6836
6898
|
function measureUnaddressable(html) {
|
|
6837
6899
|
const parsed = parse2(html);
|
|
6838
6900
|
let visible = 0;
|
|
@@ -6841,7 +6903,7 @@ function measureUnaddressable(html) {
|
|
|
6841
6903
|
const visit = (node, tag, context, bound) => {
|
|
6842
6904
|
if (node.type === "text") {
|
|
6843
6905
|
const text = flat2(node.value);
|
|
6844
|
-
if (
|
|
6906
|
+
if (isDecorativeGlyph(text) || PLATFORM_BADGE.test(text)) return;
|
|
6845
6907
|
visible += text.length;
|
|
6846
6908
|
if (bound) return;
|
|
6847
6909
|
unaddressable += text.length;
|
|
@@ -6855,7 +6917,7 @@ function measureUnaddressable(html) {
|
|
|
6855
6917
|
}
|
|
6856
6918
|
if (node.type !== "element" || node.raw || SKIP_TAGS.has(node.tag)) return;
|
|
6857
6919
|
const attrs = new Map(node.attrs.map((a) => [a.name, a.value]));
|
|
6858
|
-
if (attrs.has("hidden") || attrs.get("aria-hidden") === "true" || /display\s*:\s*none
|
|
6920
|
+
if (attrs.has("hidden") || attrs.get("aria-hidden") === "true" || /display\s*:\s*none/i.test(attrs.get("style") ?? "") || isSkipLink(node, attrs)) {
|
|
6859
6921
|
return;
|
|
6860
6922
|
}
|
|
6861
6923
|
const owned = bound || BOUND_ATTRS.some((name) => attrs.has(name));
|
|
@@ -6866,7 +6928,7 @@ function measureUnaddressable(html) {
|
|
|
6866
6928
|
return {
|
|
6867
6929
|
visible,
|
|
6868
6930
|
unaddressable,
|
|
6869
|
-
buckets: [...buckets.values()].sort((a, b) => b.chars - a.chars).slice(0, MAX_BUCKETS)
|
|
6931
|
+
buckets: [...buckets.values()].sort((a, b) => b.chars - a.chars || `${a.tag} @ ${a.context}`.localeCompare(`${b.tag} @ ${b.context}`)).slice(0, MAX_BUCKETS)
|
|
6870
6932
|
};
|
|
6871
6933
|
}
|
|
6872
6934
|
|
|
@@ -9576,8 +9638,21 @@ async function plan(file, content, dialect, targets, sites, importAt, outcomes,
|
|
|
9576
9638
|
callSites: [{ file, literal: drilledProp.literal }]
|
|
9577
9639
|
});
|
|
9578
9640
|
}
|
|
9579
|
-
const images = bindImages(
|
|
9580
|
-
{
|
|
9641
|
+
const images = await bindImages(
|
|
9642
|
+
{
|
|
9643
|
+
content,
|
|
9644
|
+
parsed,
|
|
9645
|
+
dialect: writing,
|
|
9646
|
+
image: helperName("bcmsImage"),
|
|
9647
|
+
snapshotName,
|
|
9648
|
+
slug: params?.slug ?? null,
|
|
9649
|
+
// An image handed to a section component is bound in that component, by the same drill a
|
|
9650
|
+
// text prop takes. @see images.ts
|
|
9651
|
+
drill: async (component, prop, kind) => {
|
|
9652
|
+
const drilled = await drillProp(drill, file, component, prop, kind, 1);
|
|
9653
|
+
return drilled.ok ? { ok: true, edits: drilled.edits } : { ok: false, reason: drilled.reason ?? "PROP_TARGET_NOT_FOUND" };
|
|
9654
|
+
}
|
|
9655
|
+
},
|
|
9581
9656
|
targets.filter((t) => {
|
|
9582
9657
|
const outcome = outcomes.get(t.key);
|
|
9583
9658
|
return t.kind === "image" && t.scope === "page" && (outcome === void 0 || UNPLACED.has(outcome));
|
|
@@ -9585,6 +9660,10 @@ async function plan(file, content, dialect, targets, sites, importAt, outcomes,
|
|
|
9585
9660
|
);
|
|
9586
9661
|
for (const key of images.bound) outcomes.set(key, "rewritten");
|
|
9587
9662
|
for (const refusal of images.refused) outcomes.set(refusal.key, refusal.reason);
|
|
9663
|
+
componentEdits.push(...images.componentEdits);
|
|
9664
|
+
for (const drilledProp of images.props) {
|
|
9665
|
+
dynamic.push({ file: drilledProp.file, prop: drilledProp.prop, path: drilledProp.path, callSites: [{ file, literal: drilledProp.literal }] });
|
|
9666
|
+
}
|
|
9588
9667
|
const lanes = [...data.splices, ...images.splices];
|
|
9589
9668
|
if (applied.length === 0 && lanes.length === 0) {
|
|
9590
9669
|
return { tier, content: null, outcomes, bindings: [], occurrences: { located, rewritten: 0 }, dialect: writing, dynamic: [], componentEdits, snapshots: [], notes: [] };
|