@bettercms-ai/convert 0.2.1 → 0.4.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 +337 -0
- package/README.md +182 -22
- package/dist/cli.js +1185 -209
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +128 -5
- package/dist/index.js +1171 -208
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -128,7 +128,7 @@ function propsAttribute(props) {
|
|
|
128
128
|
|
|
129
129
|
// src/helper.ts
|
|
130
130
|
import { createHash as createHash2 } from "crypto";
|
|
131
|
-
var HELPER_VERSION =
|
|
131
|
+
var HELPER_VERSION = 4;
|
|
132
132
|
var HEADER = "// @bettercms-ai/convert helper v";
|
|
133
133
|
var HELPER_PATH = Object.fromEntries(
|
|
134
134
|
Object.keys(DIALECT_RULES).map((d) => [d, DIALECT_RULES[d].helper])
|
|
@@ -211,32 +211,97 @@ export function bcmsRows<T>(page: unknown, path: string, fallback: T[], slug?: s
|
|
|
211
211
|
/**
|
|
212
212
|
* ONE SECTION's copy, for a component extracted by \`--componentize\`.
|
|
213
213
|
*
|
|
214
|
-
*
|
|
215
|
-
*
|
|
216
|
-
*
|
|
217
|
-
*
|
|
214
|
+
* OVERRIDES FIRST, PER KEY: \`overrides[key] ?? group[key]\`. An instance that owns its copy
|
|
215
|
+
* (Phase B) carries an override for every prop it declares, so the group underneath it is never
|
|
216
|
+
* read; a placement still bound to a page field group (Phase A) carries none, so the group is all
|
|
217
|
+
* there is. The interesting case is the one in between \u2014 a section half moved \u2014 where a key the
|
|
218
|
+
* instance does not hold has to keep rendering the page's value rather than nothing.
|
|
219
|
+
*
|
|
220
|
+
* \u{1F534} \`??\`, NOT \`||\`. An empty string is a value an editor typed, and treating it as absent put
|
|
221
|
+
* the page's old copy back into a heading somebody had deliberately cleared.
|
|
222
|
+
*
|
|
223
|
+
* With neither an override nor a group, the answer is \`{}\` and every \`bcms(...)\` below renders
|
|
224
|
+
* the fallback the template shipped with \u2014 which is why a section renders correctly against the
|
|
225
|
+
* committed \`{}\` stub.
|
|
218
226
|
*/
|
|
219
227
|
export function bcmsSection(page: unknown, bind?: string, overrides?: unknown): Record<string, unknown> {
|
|
220
|
-
// \u{1F534} AN EMPTY OBJECT IS AN ANSWER. \`overrides: {}\` is an instance an editor cleared, and
|
|
221
|
-
// "\`??\` unless it happens to be empty" quietly put the page's copy back into it.
|
|
222
|
-
if (isRecord(overrides)) return overrides;
|
|
223
228
|
const group = bind ? readPath(page, bind) : undefined;
|
|
224
|
-
|
|
229
|
+
const base: Record<string, unknown> = isRecord(group) ? { ...group } : {};
|
|
230
|
+
if (!isRecord(overrides)) return base;
|
|
231
|
+
for (const key of Object.keys(overrides)) {
|
|
232
|
+
const value = overrides[key];
|
|
233
|
+
if (value === undefined || value === null) continue;
|
|
234
|
+
/**
|
|
235
|
+
* \u{1F534} A TABLE PROP IS STORED AS \`{ rows: [...] }\` AND READ AS A LIST. That is the shape the
|
|
236
|
+
* dashboard edits a repeater in; handing it to \`bcmsRows\` unchanged is not an array, so the
|
|
237
|
+
* helper fell back to the rows the template shipped with and the editor's work never showed.
|
|
238
|
+
*/
|
|
239
|
+
base[key] = isRecord(value) && Array.isArray(value.rows) ? value.rows : value;
|
|
240
|
+
}
|
|
241
|
+
return base;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* WHICH ADDRESS one bound element declares \u2014 decided at RENDER time, from the placement.
|
|
246
|
+
*
|
|
247
|
+
* An element carries exactly ONE \`data-bcms-field\`, and which lane it names depends on where its
|
|
248
|
+
* copy lives. A placement that owns its copy declares the BLOCK lane \u2014
|
|
249
|
+
* \`<blockId>__overrides.<propKey>\` \u2014 because that is the address the editor commits an instance's
|
|
250
|
+
* own value to. A placement bound to a page field group declares the PAGE path, which is what the
|
|
251
|
+
* page form and the coverage meter already know.
|
|
252
|
+
*
|
|
253
|
+
* \u{1F534} THE BOUND ADDRESS IS RE-BASED ONTO **THIS** PLACEMENT'S GROUP. The markup was cut from ONE
|
|
254
|
+
* page and carries that page's paths (\`hero.title\`); the whole point of \`shapeHash\` is that a
|
|
255
|
+
* second page renders the same component bound to a group of its own (\`bind="landingHero"\`).
|
|
256
|
+
* Handing the embedded path through unchanged declared \`hero.title\` on a page whose field is
|
|
257
|
+
* \`landingHero.title\` \u2014 an address that page does not have, so the editor wrote into nothing and
|
|
258
|
+
* the meter counted a binding that was never there. The prefix the markup was cut with comes out,
|
|
259
|
+
* the placement's own group goes in.
|
|
260
|
+
*
|
|
261
|
+
* \u{1F534} THE SAME EXTRACTED FILE SERVES BOTH LANES. The choice is a function of the props, never of
|
|
262
|
+
* how the codemod ran: one component renders a Phase A placement on one page and a Phase B
|
|
263
|
+
* instance on the next, and a repository does not have to be re-cut when its copy moves.
|
|
264
|
+
*/
|
|
265
|
+
export function bcmsFields(
|
|
266
|
+
placement: { blockId?: string; bind?: string; overrides?: unknown },
|
|
267
|
+
pathPrefix: string,
|
|
268
|
+
): (path: string) => string {
|
|
269
|
+
const id = placement.blockId;
|
|
270
|
+
const own = typeof id === "string" && id !== "" && !placement.bind && isRecord(placement.overrides);
|
|
271
|
+
/**
|
|
272
|
+
* What replaces the prefix the markup was cut with, or null for "leave every path alone".
|
|
273
|
+
*
|
|
274
|
+
* Null covers a placement with neither a bind nor an instance \u2014 a shipped fallback, or a page
|
|
275
|
+
* nobody has componentized yet \u2014 and a bound placement in a component whose prefix is EMPTY,
|
|
276
|
+
* where the plan said the prop key IS the whole page path: there is no group segment to swap,
|
|
277
|
+
* so prepending one would invent an address rather than correct it.
|
|
278
|
+
*/
|
|
279
|
+
const lane = own ? \`\${id}__overrides.\` : placement.bind && pathPrefix ? \`\${placement.bind}.\` : null;
|
|
280
|
+
return (path: string): string => {
|
|
281
|
+
// A path outside the group this section was cut from is not one this placement re-addresses:
|
|
282
|
+
// it names a key the instance does not hold and a group this placement is not bound to.
|
|
283
|
+
// (An EMPTY prefix is a plan saying the prop key IS the page path, and matches everything.)
|
|
284
|
+
if (lane === null || !path.startsWith(pathPrefix)) return path;
|
|
285
|
+
return lane + path.slice(pathPrefix.length);
|
|
286
|
+
};
|
|
225
287
|
}
|
|
226
288
|
`;
|
|
227
289
|
}
|
|
228
290
|
var HISTORICAL = {
|
|
229
291
|
"src/bcms-content.ts": {
|
|
230
292
|
1: "c272412532efed7db64b6cf313b2bed4291f9951430412289c8a588b7f565407",
|
|
231
|
-
2: "51e2ae65ddd86bf43d14e29b3865e3f16e45f72069692d1fea57e1f986a894f0"
|
|
293
|
+
2: "51e2ae65ddd86bf43d14e29b3865e3f16e45f72069692d1fea57e1f986a894f0",
|
|
294
|
+
3: "d4305b2983adcb456ef782d35ea9d083a04465c2b68baa0d44a084dfa01a9227"
|
|
232
295
|
},
|
|
233
296
|
"src/lib/bcms-content.ts": {
|
|
234
297
|
1: "ae72ae14d706943d481c880d70da5789b4816ba1ba319a10fbc63ded4d0b42f2",
|
|
235
|
-
2: "a18731eb42f7db55dac0591c45f873758cde8e103fefcd6e5caf67936ae6a002"
|
|
298
|
+
2: "a18731eb42f7db55dac0591c45f873758cde8e103fefcd6e5caf67936ae6a002",
|
|
299
|
+
3: "0be9c73160361882a038552bc02673c14ed532287823dabaafe36179bc55ed7e"
|
|
236
300
|
},
|
|
237
301
|
"composables/bcms-content.ts": {
|
|
238
302
|
1: "c272412532efed7db64b6cf313b2bed4291f9951430412289c8a588b7f565407",
|
|
239
|
-
2: "51e2ae65ddd86bf43d14e29b3865e3f16e45f72069692d1fea57e1f986a894f0"
|
|
303
|
+
2: "51e2ae65ddd86bf43d14e29b3865e3f16e45f72069692d1fea57e1f986a894f0",
|
|
304
|
+
3: "d4305b2983adcb456ef782d35ea9d083a04465c2b68baa0d44a084dfa01a9227"
|
|
240
305
|
}
|
|
241
306
|
};
|
|
242
307
|
var sha256 = (value) => createHash2("sha256").update(value, "utf8").digest("hex");
|
|
@@ -2622,6 +2687,75 @@ function locate(brief, sources, options) {
|
|
|
2622
2687
|
// src/loops.ts
|
|
2623
2688
|
import MagicString from "magic-string";
|
|
2624
2689
|
|
|
2690
|
+
// src/declarations.ts
|
|
2691
|
+
var BINDING_ATTR = /data-bcms-(?:layout-)?field\s*=\s*["']([^"']+)["']/g;
|
|
2692
|
+
var PROPS_ATTR = /data-bcms-props\s*=\s*["']([^"']+)["']/g;
|
|
2693
|
+
var TEMPLATE_SCAN = /:?data-bcms-(?:layout-)?field\s*=\s*(?:\{\s*`([^`]+)`\s*\}|"\s*`([^`]+)`\s*")/g;
|
|
2694
|
+
var BINDINGS_SCAN = /bcmsBindings\s*=\s*\{\{[^}]*\}\}/g;
|
|
2695
|
+
var propsDeclarations = (value, at) => value.split(";").flatMap((entry) => {
|
|
2696
|
+
const [address, kind] = entry.split("|");
|
|
2697
|
+
if (!address) return [];
|
|
2698
|
+
const scope = address.startsWith("layout:") ? "layout" : "page";
|
|
2699
|
+
return [{ scope, path: stripLayoutPrefix(address), kind: kind ?? "", at, via: "props" }];
|
|
2700
|
+
});
|
|
2701
|
+
var propsPaths = (value) => propsDeclarations(value, 0).map((d) => ({ scope: d.scope, path: d.path }));
|
|
2702
|
+
var TEMPLATE_BINDING = /^:?data-bcms-(?:layout-)?field\s*=\s*(?:\{\s*`([^`]+)`\s*\}|"\s*`([^`]+)`\s*")$/;
|
|
2703
|
+
var shapeOfPath = (path) => path.replace(/\[(?:\d+|\$\{[^}]*\})\]/g, "[*]");
|
|
2704
|
+
function declarationsIn(roots, content) {
|
|
2705
|
+
const out = [];
|
|
2706
|
+
const visit = (node) => {
|
|
2707
|
+
if (node.type === "expr") {
|
|
2708
|
+
for (const child of node.children) visit(child);
|
|
2709
|
+
return;
|
|
2710
|
+
}
|
|
2711
|
+
if (node.type !== "element") return;
|
|
2712
|
+
const kind = node.attrs.find((a) => a.name === "data-bcms-kind")?.value ?? "";
|
|
2713
|
+
const at = node.attrInsertAt;
|
|
2714
|
+
for (const attr of node.attrs) {
|
|
2715
|
+
const name = attr.name.replace(/^:/, "");
|
|
2716
|
+
if (name === "data-bcms-field" || name === "data-bcms-layout-field") {
|
|
2717
|
+
const scope = name === "data-bcms-layout-field" ? "layout" : "page";
|
|
2718
|
+
const template = content.slice(attr.range.start, attr.range.end).match(TEMPLATE_BINDING);
|
|
2719
|
+
const templated = template?.[1] ?? template?.[2];
|
|
2720
|
+
if (templated) out.push({ scope, path: shapeOfPath(stripLayoutPrefix(templated)), kind, at, via: "field" });
|
|
2721
|
+
else if (attr.value) out.push({ scope, path: stripLayoutPrefix(attr.value), kind, at, via: "field" });
|
|
2722
|
+
} else if (attr.name === "data-bcms-props") {
|
|
2723
|
+
out.push(...propsDeclarations(attr.value, at));
|
|
2724
|
+
} else if (attr.name === "bcmsBindings") {
|
|
2725
|
+
for (const path of bindingsPaths(content.slice(attr.range.start, attr.range.end))) {
|
|
2726
|
+
out.push({ scope: "page", path, kind: "", at, via: "prop" });
|
|
2727
|
+
}
|
|
2728
|
+
}
|
|
2729
|
+
}
|
|
2730
|
+
for (const child of node.children) visit(child);
|
|
2731
|
+
};
|
|
2732
|
+
for (const root of roots) visit(root);
|
|
2733
|
+
return out;
|
|
2734
|
+
}
|
|
2735
|
+
var bindingsPaths = (text) => [...text.matchAll(/["']([^"']+)["']/g)].map((match) => match[1]);
|
|
2736
|
+
var scopedPath = (scope, path) => `${scope}::${path}`;
|
|
2737
|
+
var covers = (declared, target) => declared !== void 0 && (declared.has(scopedPath(target.scope, target.path)) || declared.has(scopedPath(target.scope, shapeOfPath(target.path))));
|
|
2738
|
+
var declaredInTree = (roots, content) => new Set(declarationsIn(roots, content).map((d) => scopedPath(d.scope, d.path)));
|
|
2739
|
+
var scannedDeclarations = (content) => [...declaredByScan(content)].map((entry) => {
|
|
2740
|
+
const [scope, ...rest] = entry.split("::");
|
|
2741
|
+
return { scope, path: rest.join("::") };
|
|
2742
|
+
});
|
|
2743
|
+
function declaredByScan(content) {
|
|
2744
|
+
const out = /* @__PURE__ */ new Set();
|
|
2745
|
+
const add = (scope, path) => void out.add(scopedPath(scope, path));
|
|
2746
|
+
for (const match of content.matchAll(BINDING_ATTR)) {
|
|
2747
|
+
add(match[0].includes("layout-field") ? "layout" : "page", stripLayoutPrefix(match[1]));
|
|
2748
|
+
}
|
|
2749
|
+
for (const match of content.matchAll(TEMPLATE_SCAN)) {
|
|
2750
|
+
add(match[0].includes("layout-field") ? "layout" : "page", shapeOfPath(stripLayoutPrefix(match[1] ?? match[2])));
|
|
2751
|
+
}
|
|
2752
|
+
for (const match of content.matchAll(BINDINGS_SCAN)) for (const path of bindingsPaths(match[0])) add("page", path);
|
|
2753
|
+
for (const match of content.matchAll(PROPS_ATTR))
|
|
2754
|
+
for (const entry of propsPaths(match[1])) add(entry.scope, entry.path);
|
|
2755
|
+
return out;
|
|
2756
|
+
}
|
|
2757
|
+
var stripLayoutPrefix = (value) => value.startsWith("layout:") ? value.slice("layout:".length) : value;
|
|
2758
|
+
|
|
2625
2759
|
// src/scope.ts
|
|
2626
2760
|
function patternNames(node, out) {
|
|
2627
2761
|
if (!node || typeof node !== "object") return;
|
|
@@ -2908,7 +3042,10 @@ function wordBounded(haystack, needle) {
|
|
|
2908
3042
|
}
|
|
2909
3043
|
|
|
2910
3044
|
// src/loops.ts
|
|
2911
|
-
|
|
3045
|
+
function splitNested(leaf) {
|
|
3046
|
+
const match = leaf.match(/^([^[\]]+)\[(\d+)\]\.(.+)$/);
|
|
3047
|
+
return match ? { array: match[1], index: Number(match[2]), leaf: match[3] } : null;
|
|
3048
|
+
}
|
|
2912
3049
|
var elementEnd = (node) => node.inner ? node.inner.end + node.tag.length + 3 : null;
|
|
2913
3050
|
function findRows(roots, members) {
|
|
2914
3051
|
const wanted = new Set(members.map((m) => m.index));
|
|
@@ -2980,6 +3117,69 @@ function indentOf(content, at) {
|
|
|
2980
3117
|
const prefix = content.slice(lineStart, at);
|
|
2981
3118
|
return prefix.trim() === "" ? prefix : "";
|
|
2982
3119
|
}
|
|
3120
|
+
function writeLeaf(source, shift, member, rule, read, row, literal) {
|
|
3121
|
+
const site = member.site;
|
|
3122
|
+
const expression = `${read}(${row}, ${JSON.stringify(member.leaf)}, "")`;
|
|
3123
|
+
if (member.kind === "richtext" && rule.richtext) {
|
|
3124
|
+
source.remove(site.inner.start + shift, site.inner.end + shift);
|
|
3125
|
+
source.appendLeft(site.attrInsertAt + shift, ` ${rule.richtext(expression)}`);
|
|
3126
|
+
} else {
|
|
3127
|
+
source.overwrite(site.inner.start + shift, site.inner.end + shift, rule.text(expression));
|
|
3128
|
+
}
|
|
3129
|
+
const kind = member.kind ? ` data-bcms-kind="${member.kind}"` : "";
|
|
3130
|
+
source.appendLeft(site.attrInsertAt + shift, ` ${rule.attr("data-bcms-field", `\`${literal}\``)}${kind}`);
|
|
3131
|
+
}
|
|
3132
|
+
function emitNested(rule, array, members, prefix, outer, parent, ctx) {
|
|
3133
|
+
if (!rule.loop || !rule.text) return null;
|
|
3134
|
+
const inner = [];
|
|
3135
|
+
for (const member of members) {
|
|
3136
|
+
const split = splitNested(member.leaf);
|
|
3137
|
+
if (!split || splitNested(split.leaf) !== null) return null;
|
|
3138
|
+
inner.push({ ...member, index: split.index, leaf: split.leaf });
|
|
3139
|
+
}
|
|
3140
|
+
const rows = findRows([parent.node], inner);
|
|
3141
|
+
if (!rows || rows.length === 0) return null;
|
|
3142
|
+
if (!consecutive(rows, ctx.content)) return null;
|
|
3143
|
+
if (!sameShape(rows, ctx.content)) return null;
|
|
3144
|
+
const template = rows[0];
|
|
3145
|
+
const used = /* @__PURE__ */ new Set([
|
|
3146
|
+
...ctx.content.slice(template.range.start, template.range.end).match(/[A-Za-z_$][\w$]*/g) ?? [],
|
|
3147
|
+
outer.row,
|
|
3148
|
+
outer.index
|
|
3149
|
+
]);
|
|
3150
|
+
const allocated = allocateNames(used, [
|
|
3151
|
+
{ key: "row", name: "item" },
|
|
3152
|
+
{ key: "index", name: "j" }
|
|
3153
|
+
]);
|
|
3154
|
+
const names = { row: allocated.get("row"), index: allocated.get("index") };
|
|
3155
|
+
const fallback = [];
|
|
3156
|
+
const writable = inner.every((member) => setLeaf(fallback[member.index] ??= emptyRow(), member.leaf, member.fallback));
|
|
3157
|
+
if (!writable) return null;
|
|
3158
|
+
const source = new MagicString(ctx.content.slice(template.range.start, template.range.end));
|
|
3159
|
+
const shift = -template.range.start;
|
|
3160
|
+
const declarations = [];
|
|
3161
|
+
for (const member of template.members) {
|
|
3162
|
+
const literal = `${prefix}.${array}[\${${names.index}}].${member.leaf}`;
|
|
3163
|
+
writeLeaf(source, shift, member, rule, ctx.read, names.row, literal);
|
|
3164
|
+
declarations.push({ path: shapeOfPath(literal), literal });
|
|
3165
|
+
}
|
|
3166
|
+
const rowsExpr = `${ctx.rows}(${outer.row}, ${JSON.stringify(array)}, ${JSON.stringify(fallback.filter(Boolean))})`;
|
|
3167
|
+
if (rule.loop.kind === "attr") {
|
|
3168
|
+
source.appendRight(template.attrInsertAt + shift, ` ${rule.loop.attr(rowsExpr, names.row, names.index)}`);
|
|
3169
|
+
}
|
|
3170
|
+
if (rule.loopKey) {
|
|
3171
|
+
const key = rule.loopKey(names.index);
|
|
3172
|
+
if (!template.keyAttr) source.appendRight(template.attrInsertAt + shift, ` ${key}`);
|
|
3173
|
+
else if (template.keyAttr.end > template.keyAttr.start)
|
|
3174
|
+
source.overwrite(template.keyAttr.start + shift, template.keyAttr.end + shift, key);
|
|
3175
|
+
else return null;
|
|
3176
|
+
}
|
|
3177
|
+
const indent = indentOf(ctx.content, template.range.start);
|
|
3178
|
+
const text = rule.loop.kind === "wrap" ? `${rule.loop.open(rowsExpr, names.row, names.index)}
|
|
3179
|
+
${indented(source.toString(), indent)}
|
|
3180
|
+
${indent}${rule.loop.close}` : source.toString();
|
|
3181
|
+
return { range: { start: template.range.start, end: rows[rows.length - 1].range.end }, text, declarations };
|
|
3182
|
+
}
|
|
2983
3183
|
function emitRepeater(dialect, group, arrayPath, fallbackRows, ctx) {
|
|
2984
3184
|
const rule = DIALECT_RULES[dialect];
|
|
2985
3185
|
if (!rule.loop || !rule.text || group.length === 0) return null;
|
|
@@ -2997,20 +3197,25 @@ function emitRepeater(dialect, group, arrayPath, fallbackRows, ctx) {
|
|
|
2997
3197
|
const names = { row: allocated.get("row"), index: allocated.get("index") };
|
|
2998
3198
|
const source = new MagicString(ctx.content.slice(template.range.start, template.range.end));
|
|
2999
3199
|
const shift = -template.range.start;
|
|
3000
|
-
const
|
|
3200
|
+
const declarations = [];
|
|
3201
|
+
const nested = /* @__PURE__ */ new Map();
|
|
3202
|
+
const direct = [];
|
|
3001
3203
|
for (const member of template.members) {
|
|
3002
|
-
const
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
}
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3204
|
+
const inner = splitNested(member.leaf);
|
|
3205
|
+
if (inner === null) direct.push(member);
|
|
3206
|
+
else nested.set(inner.array, [...nested.get(inner.array) ?? [], member]);
|
|
3207
|
+
}
|
|
3208
|
+
for (const [array, members] of nested) {
|
|
3209
|
+
const plan2 = emitNested(rule, array, members, `${arrayPath}[\${${names.index}}]`, names, template, ctx);
|
|
3210
|
+
if (!plan2) return null;
|
|
3211
|
+
if (direct.some((m) => m.site.range.start >= plan2.range.start && m.site.range.end <= plan2.range.end)) return null;
|
|
3212
|
+
source.overwrite(plan2.range.start + shift, plan2.range.end + shift, plan2.text);
|
|
3213
|
+
declarations.push(...plan2.declarations);
|
|
3214
|
+
}
|
|
3215
|
+
for (const member of direct) {
|
|
3216
|
+
const literal = `${arrayPath}[\${${names.index}}].${member.leaf}`;
|
|
3217
|
+
writeLeaf(source, shift, member, rule, ctx.read, names.row, literal);
|
|
3218
|
+
declarations.push({ path: shapeOfPath(literal), literal });
|
|
3014
3219
|
}
|
|
3015
3220
|
const args = [ctx.snapshot, JSON.stringify(arrayPath), JSON.stringify(fallbackRows), ...ctx.slug ? [ctx.slug] : []];
|
|
3016
3221
|
const rowsExpr = `${ctx.rows}(${args.join(", ")})`;
|
|
@@ -3031,7 +3236,8 @@ ${indent}${rule.loop.close}` : source.toString();
|
|
|
3031
3236
|
return {
|
|
3032
3237
|
range: { start: template.range.start, end: rows[rows.length - 1].range.end },
|
|
3033
3238
|
text,
|
|
3034
|
-
|
|
3239
|
+
declarations,
|
|
3240
|
+
bindings: [...new Set(declarations.map((d) => d.path))],
|
|
3035
3241
|
names
|
|
3036
3242
|
};
|
|
3037
3243
|
}
|
|
@@ -3061,9 +3267,9 @@ function setLeaf(row, leaf, value) {
|
|
|
3061
3267
|
var FORBIDDEN = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
3062
3268
|
var emptyRow = () => /* @__PURE__ */ Object.create(null);
|
|
3063
3269
|
function splitIndexed(arrayPath, path) {
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
return { index: Number(match[
|
|
3270
|
+
if (!path.startsWith(`${arrayPath}[`)) return null;
|
|
3271
|
+
const match = path.slice(arrayPath.length).match(/^\[(\d+)\]\.(.+)$/);
|
|
3272
|
+
return match ? { index: Number(match[1]), leaf: match[2] } : null;
|
|
3067
3273
|
}
|
|
3068
3274
|
|
|
3069
3275
|
// src/sites/jsx.ts
|
|
@@ -3556,22 +3762,68 @@ var firstPresent = (base, files) => [base, ...EXTENSIONS.map((ext) => `${base}${
|
|
|
3556
3762
|
function aliasesFrom(sources) {
|
|
3557
3763
|
const out = /* @__PURE__ */ new Map();
|
|
3558
3764
|
for (const source of sources) {
|
|
3559
|
-
if (!
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
}
|
|
3570
|
-
} catch {
|
|
3765
|
+
if (!isProjectConfig(source.path)) continue;
|
|
3766
|
+
const parsed = readJsonc(source.content);
|
|
3767
|
+
if (parsed === null) continue;
|
|
3768
|
+
const base = parsed.compilerOptions?.baseUrl?.replace(/^\.\/?/, "").replace(/\/$/, "") ?? "";
|
|
3769
|
+
const root = source.path.split("/").slice(0, -1).join("/");
|
|
3770
|
+
for (const [pattern, targets] of Object.entries(parsed.compilerOptions?.paths ?? {})) {
|
|
3771
|
+
out.set(
|
|
3772
|
+
pattern,
|
|
3773
|
+
targets.map((target) => [root, base, target.replace(/^\.\/?/, "")].filter(Boolean).join("/"))
|
|
3774
|
+
);
|
|
3571
3775
|
}
|
|
3572
3776
|
}
|
|
3573
3777
|
return out;
|
|
3574
3778
|
}
|
|
3779
|
+
var isProjectConfig = (path) => path.endsWith("tsconfig.json") || path.endsWith("jsconfig.json");
|
|
3780
|
+
function readJsonc(content) {
|
|
3781
|
+
let out = "";
|
|
3782
|
+
let at = 0;
|
|
3783
|
+
while (at < content.length) {
|
|
3784
|
+
const char = content[at];
|
|
3785
|
+
if (char === '"') {
|
|
3786
|
+
const start = at++;
|
|
3787
|
+
while (at < content.length && content[at] !== '"') at += content[at] === "\\" ? 2 : 1;
|
|
3788
|
+
out += content.slice(start, Math.min(at + 1, content.length));
|
|
3789
|
+
at++;
|
|
3790
|
+
continue;
|
|
3791
|
+
}
|
|
3792
|
+
if (char === "/" && content[at + 1] === "/") {
|
|
3793
|
+
while (at < content.length && content[at] !== "\n") at++;
|
|
3794
|
+
continue;
|
|
3795
|
+
}
|
|
3796
|
+
if (char === "/" && content[at + 1] === "*") {
|
|
3797
|
+
const close = content.indexOf("*/", at + 2);
|
|
3798
|
+
at = close < 0 ? content.length : close + 2;
|
|
3799
|
+
continue;
|
|
3800
|
+
}
|
|
3801
|
+
out += char;
|
|
3802
|
+
at++;
|
|
3803
|
+
}
|
|
3804
|
+
try {
|
|
3805
|
+
const parsed = JSON.parse(out.replace(/,(?=\s*[}\]])/g, ""));
|
|
3806
|
+
return isAliasShaped(parsed) ? parsed : null;
|
|
3807
|
+
} catch {
|
|
3808
|
+
return null;
|
|
3809
|
+
}
|
|
3810
|
+
}
|
|
3811
|
+
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3812
|
+
function isAliasShaped(value) {
|
|
3813
|
+
if (!isRecord(value)) return false;
|
|
3814
|
+
const options = value.compilerOptions;
|
|
3815
|
+
if (options === void 0) return true;
|
|
3816
|
+
if (!isRecord(options)) return false;
|
|
3817
|
+
if (options.baseUrl !== void 0 && typeof options.baseUrl !== "string") return false;
|
|
3818
|
+
if (options.paths === void 0) return true;
|
|
3819
|
+
if (!isRecord(options.paths)) return false;
|
|
3820
|
+
return Object.values(options.paths).every(
|
|
3821
|
+
(targets) => Array.isArray(targets) && targets.every((target) => typeof target === "string")
|
|
3822
|
+
);
|
|
3823
|
+
}
|
|
3824
|
+
function unreadableConfigs(sources) {
|
|
3825
|
+
return sources.filter((source) => isProjectConfig(source.path) && readJsonc(source.content) === null).map((source) => source.path);
|
|
3826
|
+
}
|
|
3575
3827
|
function expand(specifier, aliases) {
|
|
3576
3828
|
const out = [];
|
|
3577
3829
|
for (const [pattern, targets] of aliases) {
|
|
@@ -3740,74 +3992,6 @@ function typeMember(content, literal) {
|
|
|
3740
3992
|
return { start: at, end: at, text: `${terminated ? "" : ";"} ${text}` };
|
|
3741
3993
|
}
|
|
3742
3994
|
|
|
3743
|
-
// src/declarations.ts
|
|
3744
|
-
var BINDING_ATTR = /data-bcms-(?:layout-)?field\s*=\s*["']([^"']+)["']/g;
|
|
3745
|
-
var PROPS_ATTR = /data-bcms-props\s*=\s*["']([^"']+)["']/g;
|
|
3746
|
-
var TEMPLATE_SCAN = /:?data-bcms-(?:layout-)?field\s*=\s*(?:\{\s*`([^`]+)`\s*\}|"\s*`([^`]+)`\s*")/g;
|
|
3747
|
-
var BINDINGS_SCAN = /bcmsBindings\s*=\s*\{\{[^}]*\}\}/g;
|
|
3748
|
-
var propsDeclarations = (value, at) => value.split(";").flatMap((entry) => {
|
|
3749
|
-
const [address, kind] = entry.split("|");
|
|
3750
|
-
if (!address) return [];
|
|
3751
|
-
const scope = address.startsWith("layout:") ? "layout" : "page";
|
|
3752
|
-
return [{ scope, path: stripLayoutPrefix(address), kind: kind ?? "", at, via: "props" }];
|
|
3753
|
-
});
|
|
3754
|
-
var propsPaths = (value) => propsDeclarations(value, 0).map((d) => ({ scope: d.scope, path: d.path }));
|
|
3755
|
-
var TEMPLATE_BINDING = /^:?data-bcms-(?:layout-)?field\s*=\s*(?:\{\s*`([^`]+)`\s*\}|"\s*`([^`]+)`\s*")$/;
|
|
3756
|
-
var shapeOfPath = (path) => path.replace(/\[(?:\d+|\$\{[^}]*\})\]/g, "[*]");
|
|
3757
|
-
function declarationsIn(roots, content) {
|
|
3758
|
-
const out = [];
|
|
3759
|
-
const visit = (node) => {
|
|
3760
|
-
if (node.type === "expr") {
|
|
3761
|
-
for (const child of node.children) visit(child);
|
|
3762
|
-
return;
|
|
3763
|
-
}
|
|
3764
|
-
if (node.type !== "element") return;
|
|
3765
|
-
const kind = node.attrs.find((a) => a.name === "data-bcms-kind")?.value ?? "";
|
|
3766
|
-
const at = node.attrInsertAt;
|
|
3767
|
-
for (const attr of node.attrs) {
|
|
3768
|
-
const name = attr.name.replace(/^:/, "");
|
|
3769
|
-
if (name === "data-bcms-field" || name === "data-bcms-layout-field") {
|
|
3770
|
-
const scope = name === "data-bcms-layout-field" ? "layout" : "page";
|
|
3771
|
-
const template = content.slice(attr.range.start, attr.range.end).match(TEMPLATE_BINDING);
|
|
3772
|
-
const templated = template?.[1] ?? template?.[2];
|
|
3773
|
-
if (templated) out.push({ scope, path: shapeOfPath(stripLayoutPrefix(templated)), kind, at, via: "field" });
|
|
3774
|
-
else if (attr.value) out.push({ scope, path: stripLayoutPrefix(attr.value), kind, at, via: "field" });
|
|
3775
|
-
} else if (attr.name === "data-bcms-props") {
|
|
3776
|
-
out.push(...propsDeclarations(attr.value, at));
|
|
3777
|
-
} else if (attr.name === "bcmsBindings") {
|
|
3778
|
-
for (const path of bindingsPaths(content.slice(attr.range.start, attr.range.end))) {
|
|
3779
|
-
out.push({ scope: "page", path, kind: "", at, via: "prop" });
|
|
3780
|
-
}
|
|
3781
|
-
}
|
|
3782
|
-
}
|
|
3783
|
-
for (const child of node.children) visit(child);
|
|
3784
|
-
};
|
|
3785
|
-
for (const root of roots) visit(root);
|
|
3786
|
-
return out;
|
|
3787
|
-
}
|
|
3788
|
-
var bindingsPaths = (text) => [...text.matchAll(/["']([^"']+)["']/g)].map((match) => match[1]);
|
|
3789
|
-
var scopedPath = (scope, path) => `${scope}::${path}`;
|
|
3790
|
-
var declaredInTree = (roots, content) => new Set(declarationsIn(roots, content).map((d) => scopedPath(d.scope, d.path)));
|
|
3791
|
-
var scannedDeclarations = (content) => [...declaredByScan(content)].map((entry) => {
|
|
3792
|
-
const [scope, ...rest] = entry.split("::");
|
|
3793
|
-
return { scope, path: rest.join("::") };
|
|
3794
|
-
});
|
|
3795
|
-
function declaredByScan(content) {
|
|
3796
|
-
const out = /* @__PURE__ */ new Set();
|
|
3797
|
-
const add = (scope, path) => void out.add(scopedPath(scope, path));
|
|
3798
|
-
for (const match of content.matchAll(BINDING_ATTR)) {
|
|
3799
|
-
add(match[0].includes("layout-field") ? "layout" : "page", stripLayoutPrefix(match[1]));
|
|
3800
|
-
}
|
|
3801
|
-
for (const match of content.matchAll(TEMPLATE_SCAN)) {
|
|
3802
|
-
add(match[0].includes("layout-field") ? "layout" : "page", shapeOfPath(stripLayoutPrefix(match[1] ?? match[2])));
|
|
3803
|
-
}
|
|
3804
|
-
for (const match of content.matchAll(BINDINGS_SCAN)) for (const path of bindingsPaths(match[0])) add("page", path);
|
|
3805
|
-
for (const match of content.matchAll(PROPS_ATTR))
|
|
3806
|
-
for (const entry of propsPaths(match[1])) add(entry.scope, entry.path);
|
|
3807
|
-
return out;
|
|
3808
|
-
}
|
|
3809
|
-
var stripLayoutPrefix = (value) => value.startsWith("layout:") ? value.slice("layout:".length) : value;
|
|
3810
|
-
|
|
3811
3995
|
// src/converted.ts
|
|
3812
3996
|
var escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3813
3997
|
function splitArrayPath(path) {
|
|
@@ -4653,7 +4837,7 @@ var ValidationError = class extends Error {
|
|
|
4653
4837
|
}
|
|
4654
4838
|
code;
|
|
4655
4839
|
};
|
|
4656
|
-
var
|
|
4840
|
+
var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4657
4841
|
var Check = class {
|
|
4658
4842
|
constructor(code) {
|
|
4659
4843
|
this.code = code;
|
|
@@ -4663,7 +4847,7 @@ var Check = class {
|
|
|
4663
4847
|
throw new ValidationError(this.code, `${where} ${why}.`);
|
|
4664
4848
|
}
|
|
4665
4849
|
record(value, where) {
|
|
4666
|
-
if (!
|
|
4850
|
+
if (!isRecord2(value)) this.fail(where, "is not an object");
|
|
4667
4851
|
return value;
|
|
4668
4852
|
}
|
|
4669
4853
|
array(value, where) {
|
|
@@ -4730,6 +4914,7 @@ function readPlan(value) {
|
|
|
4730
4914
|
const section = check.record(held, at2);
|
|
4731
4915
|
check.segment(section.groupKey, `${at2}.groupKey`);
|
|
4732
4916
|
check.optionalString(section.sectionType, `${at2}.sectionType`);
|
|
4917
|
+
check.optionalString(section.copy, `${at2}.copy`);
|
|
4733
4918
|
if (section.fields !== void 0) {
|
|
4734
4919
|
for (const [f, field] of check.array(section.fields, `${at2}.fields`).entries()) {
|
|
4735
4920
|
const shape = check.record(field, `${at2}.fields[${f}]`);
|
|
@@ -5119,30 +5304,84 @@ var SectionInvariantError = class extends Error {
|
|
|
5119
5304
|
}
|
|
5120
5305
|
};
|
|
5121
5306
|
function assertSections(total, sections) {
|
|
5122
|
-
const sum = sections.extracted + sections.inlineOnly + sections.alreadyExtracted + sections.pending.length;
|
|
5307
|
+
const sum = sections.extracted + sections.inlineOnly + sections.alreadyExtracted + sections.registered + sections.pending.length;
|
|
5123
5308
|
if (sum !== total) {
|
|
5124
5309
|
throw new SectionInvariantError(
|
|
5125
|
-
`The receipt does not account for every section: ${total} planned but ${sections.extracted} extracted + ${sections.inlineOnly} inline-only + ${sections.alreadyExtracted} already extracted + ${sections.pending.length} pending = ${sum}.`
|
|
5310
|
+
`The receipt does not account for every section: ${total} planned but ${sections.extracted} extracted + ${sections.inlineOnly} inline-only + ${sections.alreadyExtracted} already extracted + ${sections.registered} registered + ${sections.pending.length} pending = ${sum}.`
|
|
5126
5311
|
);
|
|
5127
5312
|
}
|
|
5128
5313
|
return sections;
|
|
5129
5314
|
}
|
|
5130
|
-
var SECTION_MARKER = "// @bettercms-ai/convert section
|
|
5315
|
+
var SECTION_MARKER = "// @bettercms-ai/convert section v2";
|
|
5316
|
+
var SECTION_MARKERS = [SECTION_MARKER, "// @bettercms-ai/convert section v1"];
|
|
5131
5317
|
var REGISTRY_MARKER = "// @bettercms-ai/convert sections v1";
|
|
5132
5318
|
var COMPONENT_DIR = "src/components/bcms";
|
|
5133
5319
|
var SECTIONS_LIB = "src/lib/bcms-sections.ts";
|
|
5134
5320
|
var EXTRACTABLE = ["astro", "jsx"];
|
|
5321
|
+
var registryValue = (entry) => entry.member ? `${entry.name}.${entry.member}` : entry.name;
|
|
5322
|
+
function importLine(name, specifier, entry) {
|
|
5323
|
+
if (entry.kind === "namespace") return `import * as ${name} from "${specifier}";`;
|
|
5324
|
+
if (entry.kind === "named") {
|
|
5325
|
+
const exported = entry.imported ?? name;
|
|
5326
|
+
return `import { ${exported === name ? name : `${exported} as ${name}`} } from "${specifier}";`;
|
|
5327
|
+
}
|
|
5328
|
+
return `import ${name} from "${specifier}";`;
|
|
5329
|
+
}
|
|
5135
5330
|
async function componentizeSources(briefIn, planIn, sources, options) {
|
|
5136
5331
|
const pending = [];
|
|
5137
5332
|
let extracted = 0;
|
|
5138
5333
|
let inlineOnly = 0;
|
|
5139
5334
|
let alreadyExtracted = 0;
|
|
5335
|
+
let registered = 0;
|
|
5140
5336
|
let planPending = 0;
|
|
5141
5337
|
let total = 0;
|
|
5338
|
+
const pend = (route, groupKey, reason, message) => void pending.push({ route, groupKey, reason, ...message ? { message } : {} });
|
|
5142
5339
|
const brief = readBrief(briefIn);
|
|
5143
5340
|
const plan2 = readPlan(planIn);
|
|
5341
|
+
const copy = options?.copy ?? "bind";
|
|
5342
|
+
const instanceOwned = (section) => section.copy === "instance";
|
|
5343
|
+
const notes = unreadableConfigs(sources).map(
|
|
5344
|
+
(path) => ({
|
|
5345
|
+
code: "TSCONFIG_UNREADABLE",
|
|
5346
|
+
file: path,
|
|
5347
|
+
message: `${path} is not JSON this package can read, so its \`paths\` aliases resolved to nothing.`
|
|
5348
|
+
})
|
|
5349
|
+
);
|
|
5350
|
+
const refuseRun = (reason, message) => {
|
|
5351
|
+
for (const page of plan2.pages ?? []) {
|
|
5352
|
+
for (const section of page.sections ?? []) {
|
|
5353
|
+
if (section.pending) {
|
|
5354
|
+
planPending++;
|
|
5355
|
+
continue;
|
|
5356
|
+
}
|
|
5357
|
+
total++;
|
|
5358
|
+
pend(page.route, section.groupKey, reason, message);
|
|
5359
|
+
}
|
|
5360
|
+
}
|
|
5361
|
+
return {
|
|
5362
|
+
files: [],
|
|
5363
|
+
receipt: {
|
|
5364
|
+
planDigest: plan2.digest ?? "",
|
|
5365
|
+
copy,
|
|
5366
|
+
sections: assertSections(total, { extracted: 0, inlineOnly: 0, alreadyExtracted: 0, registered: 0, pending }),
|
|
5367
|
+
components: [],
|
|
5368
|
+
planPending,
|
|
5369
|
+
notes
|
|
5370
|
+
}
|
|
5371
|
+
};
|
|
5372
|
+
};
|
|
5373
|
+
const movesCopy = (plan2.pages ?? []).some(
|
|
5374
|
+
(page) => (page.sections ?? []).some((section) => !section.pending && instanceOwned(section))
|
|
5375
|
+
);
|
|
5376
|
+
if (copy === "instance" && !movesCopy) {
|
|
5377
|
+
return refuseRun(
|
|
5378
|
+
"COPY_MODE_MISMATCH",
|
|
5379
|
+
'this run was asked for --copy instance, but no placement in the plan carries copy: "instance" \u2014 run componentize_sections { copy: "instance" } on the server first, then fetch the plan again.'
|
|
5380
|
+
);
|
|
5381
|
+
}
|
|
5144
5382
|
const contents = new Map(sources.map((s) => [s.path, s.content]));
|
|
5145
|
-
const
|
|
5383
|
+
const fileSet = new Set(contents.keys());
|
|
5384
|
+
const aliases = aliasesFrom(sources);
|
|
5146
5385
|
const trees = /* @__PURE__ */ new Map();
|
|
5147
5386
|
const treeOf = async (file) => {
|
|
5148
5387
|
if (trees.has(file)) return trees.get(file);
|
|
@@ -5156,7 +5395,9 @@ async function componentizeSources(briefIn, planIn, sources, options) {
|
|
|
5156
5395
|
const helperPath = HELPER_PATH.astro;
|
|
5157
5396
|
const helperExisting = contents.get(helperPath);
|
|
5158
5397
|
const helperVersion = helperExisting === void 0 ? null : isKnownHelper(helperExisting, "astro");
|
|
5159
|
-
|
|
5398
|
+
if (helperExisting !== void 0 && helperVersion === null && options?.overwriteHelper !== true) {
|
|
5399
|
+
return refuseRun("HELPER_CONFLICT", `${helperPath} is not a module this package wrote.`);
|
|
5400
|
+
}
|
|
5160
5401
|
const outFiles = /* @__PURE__ */ new Map();
|
|
5161
5402
|
const written = /* @__PURE__ */ new Map();
|
|
5162
5403
|
const shipped = /* @__PURE__ */ new Map();
|
|
@@ -5166,7 +5407,31 @@ async function componentizeSources(briefIn, planIn, sources, options) {
|
|
|
5166
5407
|
return held;
|
|
5167
5408
|
};
|
|
5168
5409
|
const fresh = /* @__PURE__ */ new Set();
|
|
5410
|
+
const bindOf = (section) => instanceOwned(section) ? void 0 : section.groupKey;
|
|
5411
|
+
const placementOf = (slug, section) => {
|
|
5412
|
+
const bind = bindOf(section);
|
|
5413
|
+
return bind === void 0 ? { slug } : { slug, bind };
|
|
5414
|
+
};
|
|
5415
|
+
const rebind = (dialect, pageSlug, all, usable) => {
|
|
5416
|
+
if (dialect === null || !EXTRACTABLE.includes(dialect)) return;
|
|
5417
|
+
const order = shipped.get(dialect);
|
|
5418
|
+
const held = order?.get(pageSlug);
|
|
5419
|
+
if (!order || !held) return;
|
|
5420
|
+
const queues = /* @__PURE__ */ new Map();
|
|
5421
|
+
for (const section of all) {
|
|
5422
|
+
const slug = resolveComponent(plan2, section).slug;
|
|
5423
|
+
queues.set(slug, [...queues.get(slug) ?? [], section]);
|
|
5424
|
+
}
|
|
5425
|
+
order.set(
|
|
5426
|
+
pageSlug,
|
|
5427
|
+
held.map((placement) => {
|
|
5428
|
+
const section = queues.get(placement.slug)?.shift();
|
|
5429
|
+
return section && usable.has(section) ? placementOf(placement.slug, section) : placement;
|
|
5430
|
+
})
|
|
5431
|
+
);
|
|
5432
|
+
};
|
|
5169
5433
|
const registryConflicts = [];
|
|
5434
|
+
const registryUnreadable = [];
|
|
5170
5435
|
for (const dialect of EXTRACTABLE) {
|
|
5171
5436
|
const path = `${COMPONENT_DIR}/Sections.${dialect === "astro" ? "astro" : "tsx"}`;
|
|
5172
5437
|
const existing = contents.get(path);
|
|
@@ -5175,7 +5440,11 @@ async function componentizeSources(briefIn, planIn, sources, options) {
|
|
|
5175
5440
|
registryConflicts.push(path);
|
|
5176
5441
|
continue;
|
|
5177
5442
|
}
|
|
5178
|
-
const held = readRegistry(existing, dialect);
|
|
5443
|
+
const held = await readRegistry(existing, dialect, path, fileSet, aliases);
|
|
5444
|
+
if (held === null) {
|
|
5445
|
+
registryUnreadable.push(path);
|
|
5446
|
+
continue;
|
|
5447
|
+
}
|
|
5179
5448
|
componentsOf(dialect);
|
|
5180
5449
|
for (const [slug, component] of held.components) written.get(dialect).set(slug, component);
|
|
5181
5450
|
const order = shipped.get(dialect) ?? /* @__PURE__ */ new Map();
|
|
@@ -5185,6 +5454,12 @@ async function componentizeSources(briefIn, planIn, sources, options) {
|
|
|
5185
5454
|
const lib = contents.get(SECTIONS_LIB);
|
|
5186
5455
|
if (lib !== void 0 && !isGenerated(lib)) registryConflicts.push(SECTIONS_LIB);
|
|
5187
5456
|
const registryBlocked = registryConflicts.length > 0 && options?.overwriteHelper !== true;
|
|
5457
|
+
const registryUnread = registryUnreadable.length > 0;
|
|
5458
|
+
const migrated = registryBlocked || registryUnread ? { files: [], refused: /* @__PURE__ */ new Map() } : await migrateSections(plan2, contents, written, fileSet, aliases);
|
|
5459
|
+
for (const file of migrated.files) {
|
|
5460
|
+
addFile(outFiles, contents, file);
|
|
5461
|
+
contents.set(file.path, file.content);
|
|
5462
|
+
}
|
|
5188
5463
|
for (const page of plan2.pages ?? []) {
|
|
5189
5464
|
const sections2 = (page.sections ?? []).filter((section) => {
|
|
5190
5465
|
if (section.pending) {
|
|
@@ -5195,46 +5470,81 @@ async function componentizeSources(briefIn, planIn, sources, options) {
|
|
|
5195
5470
|
});
|
|
5196
5471
|
total += sections2.length;
|
|
5197
5472
|
if (sections2.length === 0) continue;
|
|
5473
|
+
const prefixes = /* @__PURE__ */ new Map();
|
|
5474
|
+
for (const section of sections2) {
|
|
5475
|
+
const prefix = propPrefix(section);
|
|
5476
|
+
if (prefix === null) {
|
|
5477
|
+
pend(
|
|
5478
|
+
page.route,
|
|
5479
|
+
section.groupKey,
|
|
5480
|
+
"PROP_MAP_INCONSISTENT",
|
|
5481
|
+
"the plan's fields do not spell every path as one common prefix plus its key, so a page path cannot be turned into a prop key."
|
|
5482
|
+
);
|
|
5483
|
+
continue;
|
|
5484
|
+
}
|
|
5485
|
+
prefixes.set(section, prefix);
|
|
5486
|
+
}
|
|
5487
|
+
const usable = sections2.filter((section) => prefixes.has(section));
|
|
5488
|
+
if (usable.length === 0) continue;
|
|
5489
|
+
const pendingBefore = pending.length;
|
|
5198
5490
|
const briefPage = brief.pages.find((p) => p.route === page.route);
|
|
5199
5491
|
const pageSlug = briefPage?.slug ?? slugify(page.route === "/" ? "home" : page.route);
|
|
5200
|
-
if (
|
|
5201
|
-
for (const section of
|
|
5202
|
-
pend(page.route, section.groupKey, "
|
|
5492
|
+
if (registryBlocked) {
|
|
5493
|
+
for (const section of usable) {
|
|
5494
|
+
pend(page.route, section.groupKey, "REGISTRY_CONFLICT", `${registryConflicts.join(", ")} is not a file this package wrote.`);
|
|
5203
5495
|
}
|
|
5204
5496
|
continue;
|
|
5205
5497
|
}
|
|
5206
|
-
if (
|
|
5207
|
-
for (const section of
|
|
5208
|
-
pend(
|
|
5498
|
+
if (registryUnread) {
|
|
5499
|
+
for (const section of usable) {
|
|
5500
|
+
pend(
|
|
5501
|
+
page.route,
|
|
5502
|
+
section.groupKey,
|
|
5503
|
+
"REGISTRY_UNREADABLE",
|
|
5504
|
+
`${registryUnreadable.join(", ")} carries our marker but its REGISTRY/SHIPPED tables cannot be read back \u2014 rebuilding it would drop the components it names.`
|
|
5505
|
+
);
|
|
5209
5506
|
}
|
|
5210
5507
|
continue;
|
|
5211
5508
|
}
|
|
5212
5509
|
const snapshotPath = `${CONTENT_DIR}/${pageSlug}.json`;
|
|
5213
5510
|
if (!snapshotUsable(contents.get(snapshotPath))) {
|
|
5214
|
-
for (const section of
|
|
5511
|
+
for (const section of usable) {
|
|
5215
5512
|
pend(page.route, section.groupKey, "SNAPSHOT_INVALID", `${snapshotPath} is not a JSON object.`);
|
|
5216
5513
|
}
|
|
5217
5514
|
continue;
|
|
5218
5515
|
}
|
|
5219
5516
|
const pageFiles = pageFilesFor(page.route, contents.keys());
|
|
5220
5517
|
if (pageFiles.length > 1) {
|
|
5221
|
-
for (const section of
|
|
5518
|
+
for (const section of usable) {
|
|
5222
5519
|
pend(page.route, section.groupKey, "ROUTE_FILE_AMBIGUOUS", `${pageFiles.join(" and ")} both serve ${page.route}.`);
|
|
5223
5520
|
}
|
|
5224
5521
|
continue;
|
|
5225
5522
|
}
|
|
5226
5523
|
const file = pageFiles[0];
|
|
5227
5524
|
if (file === void 0) {
|
|
5228
|
-
for (const section of
|
|
5525
|
+
for (const section of usable) {
|
|
5229
5526
|
pend(page.route, section.groupKey, "NOT_IN_SOURCE", `no page file found for ${page.route}.`);
|
|
5230
5527
|
}
|
|
5231
5528
|
continue;
|
|
5232
5529
|
}
|
|
5233
|
-
const
|
|
5530
|
+
const pageDialect = dialectOf(file, contents.get(file) ?? "");
|
|
5531
|
+
const done = new Set(alreadyOn(contents.get(file) ?? "", pageSlug, usable));
|
|
5532
|
+
let stuck = 0;
|
|
5533
|
+
for (const section of usable) {
|
|
5534
|
+
if (!done.has(section.groupKey)) continue;
|
|
5535
|
+
const why = pageDialect ? migrated.refused.get(refusalKey(pageDialect, resolveComponent(plan2, section).slug)) : void 0;
|
|
5536
|
+
if (why === void 0) continue;
|
|
5537
|
+
done.delete(section.groupKey);
|
|
5538
|
+
stuck++;
|
|
5539
|
+
pend(page.route, section.groupKey, "SECTION_UNMIGRATABLE", why);
|
|
5540
|
+
}
|
|
5234
5541
|
alreadyExtracted += done.size;
|
|
5235
|
-
if (done.size ===
|
|
5542
|
+
if (done.size + stuck === usable.length) {
|
|
5543
|
+
rebind(pageDialect, pageSlug, page.sections ?? [], new Set(usable));
|
|
5544
|
+
continue;
|
|
5545
|
+
}
|
|
5236
5546
|
const found = await locateSections(
|
|
5237
|
-
|
|
5547
|
+
usable.filter((section) => !done.has(section.groupKey)),
|
|
5238
5548
|
briefPage?.paths ?? [],
|
|
5239
5549
|
sources,
|
|
5240
5550
|
treeOf
|
|
@@ -5244,37 +5554,115 @@ async function componentizeSources(briefIn, planIn, sources, options) {
|
|
|
5244
5554
|
}
|
|
5245
5555
|
const live = found.placed;
|
|
5246
5556
|
if (live.length === 0) continue;
|
|
5557
|
+
const content = contents.get(file);
|
|
5558
|
+
const dialect = dialectOf(file, content);
|
|
5559
|
+
if (!dialect || !EXTRACTABLE.includes(dialect)) {
|
|
5560
|
+
for (const placed of live) {
|
|
5561
|
+
pend(page.route, placed.section.groupKey, "DIALECT_UNSUPPORTED", `${file} is ${dialect ?? "not a template"}.`);
|
|
5562
|
+
}
|
|
5563
|
+
continue;
|
|
5564
|
+
}
|
|
5565
|
+
const tree = await treeOf(file);
|
|
5566
|
+
if (!tree) {
|
|
5567
|
+
for (const placed of live) {
|
|
5568
|
+
pend(page.route, placed.section.groupKey, "NOT_IN_SOURCE", `${file} did not parse.`);
|
|
5569
|
+
}
|
|
5570
|
+
continue;
|
|
5571
|
+
}
|
|
5572
|
+
const parsed = tree.parsed;
|
|
5573
|
+
const importedFiles = /* @__PURE__ */ new Map();
|
|
5574
|
+
for (const [local, from] of importsIn(parsed, dialect)) {
|
|
5575
|
+
const resolved = resolveSpecifier(file, from.specifier, fileSet, aliases);
|
|
5576
|
+
if (resolved === null) continue;
|
|
5577
|
+
importedFiles.set(resolved, [
|
|
5578
|
+
...importedFiles.get(resolved) ?? [],
|
|
5579
|
+
{ local, kind: from.kind, imported: from.imported }
|
|
5580
|
+
]);
|
|
5581
|
+
}
|
|
5582
|
+
const bindingOf = (held) => {
|
|
5583
|
+
const bindings = importedFiles.get(held) ?? [];
|
|
5584
|
+
return bindings.find((binding) => calledIn(tree, binding)) ?? bindings[0];
|
|
5585
|
+
};
|
|
5247
5586
|
const placedHere = [];
|
|
5587
|
+
const importedSections = [];
|
|
5248
5588
|
for (const placed of live) {
|
|
5249
5589
|
const elements = placed.byFile.get(file);
|
|
5590
|
+
const alsoRendered = [...placed.byFile.keys()].filter((held2) => held2 !== file && importedFiles.has(held2));
|
|
5250
5591
|
if (elements && elements.length > 0) {
|
|
5592
|
+
if (alsoRendered.length > 0) {
|
|
5593
|
+
pend(
|
|
5594
|
+
page.route,
|
|
5595
|
+
placed.section.groupKey,
|
|
5596
|
+
"SECTION_ROOT_AMBIGUOUS",
|
|
5597
|
+
`rendered inline by ${file} AND by ${alsoRendered.join(", ")}, which it imports.`
|
|
5598
|
+
);
|
|
5599
|
+
continue;
|
|
5600
|
+
}
|
|
5251
5601
|
placedHere.push({ section: placed.section, elements });
|
|
5252
5602
|
continue;
|
|
5253
5603
|
}
|
|
5604
|
+
if (alsoRendered.length > 1) {
|
|
5605
|
+
pend(
|
|
5606
|
+
page.route,
|
|
5607
|
+
placed.section.groupKey,
|
|
5608
|
+
"SECTION_ROOT_AMBIGUOUS",
|
|
5609
|
+
`rendered by ${alsoRendered.join(" and ")}, both imported by ${file}.`
|
|
5610
|
+
);
|
|
5611
|
+
continue;
|
|
5612
|
+
}
|
|
5613
|
+
const only = alsoRendered[0] ?? [...placed.byFile.keys()][0];
|
|
5614
|
+
const held = bindingOf(only);
|
|
5615
|
+
if (held !== void 0 && dialectOf(only, contents.get(only) ?? "") === dialect) {
|
|
5616
|
+
importedSections.push({ section: placed.section, path: only, call: held.local });
|
|
5617
|
+
continue;
|
|
5618
|
+
}
|
|
5254
5619
|
pend(
|
|
5255
5620
|
page.route,
|
|
5256
5621
|
placed.section.groupKey,
|
|
5257
5622
|
"SECTION_ROOT_AMBIGUOUS",
|
|
5258
|
-
`rendered by ${[...placed.byFile.keys()].join(", ")}, not ${file}.`
|
|
5623
|
+
held === void 0 ? `rendered by ${[...placed.byFile.keys()].join(", ")}, which ${file} does not import.` : `rendered by ${only}, which is ${dialectOf(only, contents.get(only) ?? "") ?? "not a template"}, not ${dialect} like ${file}.`
|
|
5259
5624
|
);
|
|
5260
5625
|
}
|
|
5261
|
-
if (placedHere.length === 0) continue;
|
|
5262
|
-
const
|
|
5263
|
-
const
|
|
5264
|
-
|
|
5265
|
-
|
|
5266
|
-
|
|
5626
|
+
if (placedHere.length === 0 && importedSections.length === 0) continue;
|
|
5627
|
+
const extractable = componentsOf(dialect);
|
|
5628
|
+
const registeredHere = [];
|
|
5629
|
+
for (const held of importedSections) {
|
|
5630
|
+
const component = resolveComponent(plan2, held.section);
|
|
5631
|
+
const already = extractable.get(component.slug);
|
|
5632
|
+
if (already !== void 0 && already.path !== held.path) {
|
|
5633
|
+
pend(
|
|
5634
|
+
page.route,
|
|
5635
|
+
held.section.groupKey,
|
|
5636
|
+
"COMPONENT_CONFLICT",
|
|
5637
|
+
`the slug "${component.slug}" already names ${already.path}, not ${held.path}.`
|
|
5638
|
+
);
|
|
5639
|
+
continue;
|
|
5267
5640
|
}
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
|
|
5273
|
-
|
|
5641
|
+
const binding = bindingOf(held.path);
|
|
5642
|
+
const member = binding.kind === "namespace" ? namespaceMember(tree, binding.local) : void 0;
|
|
5643
|
+
if (binding.kind === "namespace" && member === void 0) {
|
|
5644
|
+
pend(
|
|
5645
|
+
page.route,
|
|
5646
|
+
held.section.groupKey,
|
|
5647
|
+
"SECTION_ROOT_AMBIGUOUS",
|
|
5648
|
+
`${held.path} is imported as a namespace and ${file} never calls one of its components, so nothing names the export that renders it.`
|
|
5649
|
+
);
|
|
5650
|
+
continue;
|
|
5274
5651
|
}
|
|
5275
|
-
|
|
5652
|
+
extractable.set(component.slug, {
|
|
5653
|
+
name: component.name,
|
|
5654
|
+
path: held.path,
|
|
5655
|
+
kind: binding.kind,
|
|
5656
|
+
imported: binding.imported,
|
|
5657
|
+
...member ? { member } : {}
|
|
5658
|
+
});
|
|
5659
|
+
registered++;
|
|
5660
|
+
registeredHere.push({
|
|
5661
|
+
slug: component.slug,
|
|
5662
|
+
section: held.section,
|
|
5663
|
+
call: callSiteOf(tree, member ? `${binding.local}.${member}` : held.call)
|
|
5664
|
+
});
|
|
5276
5665
|
}
|
|
5277
|
-
const parsed = tree.parsed;
|
|
5278
5666
|
const roots = [];
|
|
5279
5667
|
for (const placed of placedHere) {
|
|
5280
5668
|
const root = lowestCommonAncestor(placed.elements);
|
|
@@ -5283,7 +5671,13 @@ async function componentizeSources(briefIn, planIn, sources, options) {
|
|
|
5283
5671
|
continue;
|
|
5284
5672
|
}
|
|
5285
5673
|
const component = resolveComponent(plan2, placed.section);
|
|
5286
|
-
roots.push({
|
|
5674
|
+
roots.push({
|
|
5675
|
+
section: placed.section,
|
|
5676
|
+
route: page.route,
|
|
5677
|
+
root,
|
|
5678
|
+
pathPrefix: prefixes.get(placed.section),
|
|
5679
|
+
...component
|
|
5680
|
+
});
|
|
5287
5681
|
}
|
|
5288
5682
|
const nested = /* @__PURE__ */ new Set();
|
|
5289
5683
|
for (const a of roots) {
|
|
@@ -5298,8 +5692,6 @@ async function componentizeSources(briefIn, planIn, sources, options) {
|
|
|
5298
5692
|
pend(page.route, resolved.section.groupKey, "SECTION_ROOT_AMBIGUOUS", "its root contains another section's.");
|
|
5299
5693
|
}
|
|
5300
5694
|
const candidates = roots.filter((r) => !nested.has(r)).sort((a, b) => a.root.start - b.root.start);
|
|
5301
|
-
if (candidates.length === 0) continue;
|
|
5302
|
-
const extractable = componentsOf(dialect);
|
|
5303
5695
|
const placeable = [];
|
|
5304
5696
|
for (const resolved of candidates) {
|
|
5305
5697
|
const path = `${COMPONENT_DIR}/${resolved.name}.${dialect === "astro" ? "astro" : "tsx"}`;
|
|
@@ -5312,7 +5704,25 @@ async function componentizeSources(briefIn, planIn, sources, options) {
|
|
|
5312
5704
|
placeable.push(resolved);
|
|
5313
5705
|
continue;
|
|
5314
5706
|
}
|
|
5315
|
-
const built = componentFile(dialect, path, file, content, parsed, resolved);
|
|
5707
|
+
const built = componentFile(dialect, path, file, content, parsed, resolved, resolved.pathPrefix);
|
|
5708
|
+
if (built.unconvertible.length > 0) {
|
|
5709
|
+
pend(
|
|
5710
|
+
page.route,
|
|
5711
|
+
resolved.section.groupKey,
|
|
5712
|
+
"PROP_MAP_INCONSISTENT",
|
|
5713
|
+
`its fields map to the prefix "${resolved.pathPrefix}", which ${[...new Set(built.unconvertible)].slice(0, 3).join(", ")} cannot be converted through \u2014 so those elements would stay on the page lane whatever the placement says.`
|
|
5714
|
+
);
|
|
5715
|
+
continue;
|
|
5716
|
+
}
|
|
5717
|
+
if (built.duplicates.length > 0) {
|
|
5718
|
+
pend(
|
|
5719
|
+
page.route,
|
|
5720
|
+
resolved.section.groupKey,
|
|
5721
|
+
"DUPLICATE_FIELD_BINDING",
|
|
5722
|
+
`${built.duplicates.join(", ")} carries more than one data-bcms-field, so there is no single address to rewrite.`
|
|
5723
|
+
);
|
|
5724
|
+
continue;
|
|
5725
|
+
}
|
|
5316
5726
|
if (built.free.length > 0) {
|
|
5317
5727
|
pend(
|
|
5318
5728
|
page.route,
|
|
@@ -5322,7 +5732,7 @@ async function componentizeSources(briefIn, planIn, sources, options) {
|
|
|
5322
5732
|
);
|
|
5323
5733
|
continue;
|
|
5324
5734
|
}
|
|
5325
|
-
extractable.set(resolved.slug, { name: resolved.name, path });
|
|
5735
|
+
extractable.set(resolved.slug, { name: resolved.name, path, kind: "default", imported: null });
|
|
5326
5736
|
fresh.add(resolved.slug);
|
|
5327
5737
|
addFile(outFiles, contents, {
|
|
5328
5738
|
path,
|
|
@@ -5331,24 +5741,54 @@ async function componentizeSources(briefIn, planIn, sources, options) {
|
|
|
5331
5741
|
});
|
|
5332
5742
|
placeable.push(resolved);
|
|
5333
5743
|
}
|
|
5334
|
-
if (placeable.length === 0) continue;
|
|
5335
|
-
const
|
|
5744
|
+
if (placeable.length === 0 && registeredHere.length === 0) continue;
|
|
5745
|
+
const spans = [
|
|
5746
|
+
...placeable.map((r) => ({ slug: r.slug, bind: bindOf(r.section), root: r.root })),
|
|
5747
|
+
...registeredHere.map((r) => ({ slug: r.slug, bind: bindOf(r.section), root: r.call }))
|
|
5748
|
+
].sort((a, b) => (a.root?.start ?? Number.MAX_SAFE_INTEGER) - (b.root?.start ?? Number.MAX_SAFE_INTEGER));
|
|
5749
|
+
const known = spans.filter((s) => s.root !== null);
|
|
5750
|
+
const layout = runOf(known, parsed.roots, content);
|
|
5336
5751
|
if (layout === null) {
|
|
5337
5752
|
for (const resolved of placeable) {
|
|
5338
5753
|
pend(page.route, resolved.section.groupKey, "SECTION_NOT_CONTIGUOUS", "its roots share no container.");
|
|
5339
5754
|
}
|
|
5340
5755
|
continue;
|
|
5341
5756
|
}
|
|
5757
|
+
const wholePage = registeredHere.length === 0 || known.length === spans.length && pending.length === pendingBefore;
|
|
5758
|
+
const useRun = layout.contiguous && wholePage;
|
|
5342
5759
|
const order = shipped.get(dialect) ?? /* @__PURE__ */ new Map();
|
|
5343
5760
|
shipped.set(dialect, order);
|
|
5344
5761
|
order.set(
|
|
5345
5762
|
pageSlug,
|
|
5346
|
-
|
|
5763
|
+
spans.map((s) => s.bind === void 0 ? { slug: s.slug } : { slug: s.slug, bind: s.bind })
|
|
5347
5764
|
);
|
|
5348
|
-
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
|
|
5765
|
+
if (useRun) {
|
|
5766
|
+
addFile(outFiles, contents, {
|
|
5767
|
+
path: file,
|
|
5768
|
+
operation: "modify",
|
|
5769
|
+
content: splicePageRun(file, content, dialect, parsed, known, pageSlug)
|
|
5770
|
+
});
|
|
5771
|
+
extracted += placeable.length;
|
|
5772
|
+
} else {
|
|
5773
|
+
const inline = placeable.filter((resolved) => !instanceOwned(resolved.section));
|
|
5774
|
+
for (const resolved of placeable) {
|
|
5775
|
+
if (inline.includes(resolved)) continue;
|
|
5776
|
+
pend(
|
|
5777
|
+
page.route,
|
|
5778
|
+
resolved.section.groupKey,
|
|
5779
|
+
"INLINE_NEEDS_INSTANCE",
|
|
5780
|
+
`${file} cannot be replaced by one <Sections page="${pageSlug}" />, and an in-place call site carries no blockId or overrides \u2014 so this placement's own copy could not be rendered or addressed.`
|
|
5781
|
+
);
|
|
5782
|
+
}
|
|
5783
|
+
if (inline.length > 0) {
|
|
5784
|
+
addFile(outFiles, contents, {
|
|
5785
|
+
path: file,
|
|
5786
|
+
operation: "modify",
|
|
5787
|
+
content: splicePageInline(file, content, dialect, parsed, inline, pageSlug, extractable)
|
|
5788
|
+
});
|
|
5789
|
+
}
|
|
5790
|
+
inlineOnly += inline.length;
|
|
5791
|
+
}
|
|
5352
5792
|
}
|
|
5353
5793
|
const componentSlugs = [...written.values()].flatMap((held) => [...held.keys()]);
|
|
5354
5794
|
if (componentSlugs.length > 0) {
|
|
@@ -5370,15 +5810,17 @@ async function componentizeSources(briefIn, planIn, sources, options) {
|
|
|
5370
5810
|
emitHelper(outFiles, contents, helperVersion, options?.overwriteHelper);
|
|
5371
5811
|
emitStubs(outFiles, contents, pageSlugs);
|
|
5372
5812
|
}
|
|
5373
|
-
const sections = assertSections(total, { extracted, inlineOnly, alreadyExtracted, pending });
|
|
5813
|
+
const sections = assertSections(total, { extracted, inlineOnly, alreadyExtracted, registered, pending });
|
|
5374
5814
|
return {
|
|
5375
5815
|
files: [...outFiles.values()].sort((a, b) => a.path.localeCompare(b.path)),
|
|
5376
5816
|
receipt: {
|
|
5377
5817
|
planDigest: plan2.digest ?? "",
|
|
5818
|
+
copy,
|
|
5378
5819
|
sections,
|
|
5379
5820
|
// What THIS run wrote a file for. A component an earlier run left is reused, not re-reported.
|
|
5380
5821
|
components: [...fresh].sort(),
|
|
5381
|
-
planPending
|
|
5822
|
+
planPending,
|
|
5823
|
+
notes
|
|
5382
5824
|
}
|
|
5383
5825
|
};
|
|
5384
5826
|
}
|
|
@@ -5391,29 +5833,152 @@ function alreadyOn(content, pageSlug, sections) {
|
|
|
5391
5833
|
}
|
|
5392
5834
|
return sections.map((section) => section.groupKey).filter((groupKey) => new RegExp(`bind=\\s*["']${groupKey.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}["']`).test(content));
|
|
5393
5835
|
}
|
|
5394
|
-
function readRegistry(content, dialect) {
|
|
5836
|
+
async function readRegistry(content, dialect, path, files, aliases) {
|
|
5395
5837
|
const components = /* @__PURE__ */ new Map();
|
|
5396
5838
|
const shipped = /* @__PURE__ */ new Map();
|
|
5397
|
-
const
|
|
5398
|
-
|
|
5399
|
-
|
|
5400
|
-
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
|
|
5408
|
-
const table = content
|
|
5409
|
-
|
|
5839
|
+
const parsed = await parseFile4(dialect, content);
|
|
5840
|
+
if (parsed.error) return null;
|
|
5841
|
+
const imports = /* @__PURE__ */ new Map();
|
|
5842
|
+
for (const [local, from] of importsIn(parsed, dialect)) {
|
|
5843
|
+
if (!from.specifier.startsWith(".")) continue;
|
|
5844
|
+
const resolved = resolveSpecifier(path, from.specifier, files, aliases);
|
|
5845
|
+
if (resolved !== null) {
|
|
5846
|
+
imports.set(local, { name: local, path: preferDialect(resolved, dialect, files), kind: from.kind, imported: from.imported });
|
|
5847
|
+
}
|
|
5848
|
+
}
|
|
5849
|
+
const registry = blockOf(content, "REGISTRY");
|
|
5850
|
+
const table = blockOf(content, "SHIPPED");
|
|
5851
|
+
if (registry === null || table === null) return null;
|
|
5852
|
+
const registryRows = propertiesOf(registry);
|
|
5853
|
+
const shippedRows = propertiesOf(table);
|
|
5854
|
+
if (registryRows === null || shippedRows === null) return null;
|
|
5855
|
+
if (registryRows.length === 0) return null;
|
|
5856
|
+
for (const row of registryRows) {
|
|
5857
|
+
const named = row.value.match(/^([A-Za-z_$][\w$]*)(?:\.([A-Za-z_$][\w$]*))?$/);
|
|
5858
|
+
const entry = named ? imports.get(named[1]) : void 0;
|
|
5859
|
+
if (!named || !entry) return null;
|
|
5860
|
+
components.set(row.key, named[2] === void 0 ? entry : { ...entry, member: named[2] });
|
|
5861
|
+
}
|
|
5862
|
+
for (const row of shippedRows) {
|
|
5410
5863
|
try {
|
|
5411
|
-
|
|
5864
|
+
const placements = JSON.parse(row.value);
|
|
5865
|
+
if (!Array.isArray(placements) || placements.length === 0 || !placements.every(isPlacement)) return null;
|
|
5866
|
+
if (placements.some((placement) => !components.has(placement.slug))) return null;
|
|
5867
|
+
shipped.set(row.key, placements);
|
|
5412
5868
|
} catch {
|
|
5869
|
+
return null;
|
|
5413
5870
|
}
|
|
5414
5871
|
}
|
|
5415
5872
|
return { components, shipped };
|
|
5416
5873
|
}
|
|
5874
|
+
function preferDialect(resolved, dialect, files) {
|
|
5875
|
+
const wanted = dialect === "astro" ? [".astro"] : [".tsx", ".jsx"];
|
|
5876
|
+
if (wanted.some((extension) => resolved.endsWith(extension))) return resolved;
|
|
5877
|
+
const base = resolved.replace(/\.[^./]+$/, "");
|
|
5878
|
+
return wanted.map((extension) => `${base}${extension}`).find((candidate) => files.has(candidate)) ?? resolved;
|
|
5879
|
+
}
|
|
5880
|
+
function isPlacement(value) {
|
|
5881
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
5882
|
+
const held = value;
|
|
5883
|
+
if (typeof held.slug !== "string") return false;
|
|
5884
|
+
return ["bind", "blockId"].every((key) => held[key] === void 0 || typeof held[key] === "string");
|
|
5885
|
+
}
|
|
5886
|
+
function blockOf(content, name) {
|
|
5887
|
+
const declaration2 = content.indexOf(`const ${name}`);
|
|
5888
|
+
if (declaration2 < 0) return null;
|
|
5889
|
+
const open = content.indexOf("{", declaration2);
|
|
5890
|
+
if (open < 0) return null;
|
|
5891
|
+
let depth = 0;
|
|
5892
|
+
let at = open;
|
|
5893
|
+
while (at < content.length) {
|
|
5894
|
+
const skip = skipTrivia(content, at);
|
|
5895
|
+
if (skip >= 0) {
|
|
5896
|
+
at = skip;
|
|
5897
|
+
continue;
|
|
5898
|
+
}
|
|
5899
|
+
if (content[at] === "{") depth++;
|
|
5900
|
+
else if (content[at] === "}" && --depth === 0) return content.slice(open + 1, at);
|
|
5901
|
+
at++;
|
|
5902
|
+
}
|
|
5903
|
+
return null;
|
|
5904
|
+
}
|
|
5905
|
+
function skipTrivia(source, at) {
|
|
5906
|
+
const char = source[at];
|
|
5907
|
+
if (char === '"' || char === "'" || char === "`") {
|
|
5908
|
+
let cursor = at + 1;
|
|
5909
|
+
while (cursor < source.length && source[cursor] !== char) cursor += source[cursor] === "\\" ? 2 : 1;
|
|
5910
|
+
return Math.min(cursor + 1, source.length);
|
|
5911
|
+
}
|
|
5912
|
+
if (char === "/" && source[at + 1] === "/") {
|
|
5913
|
+
const line = source.indexOf("\n", at);
|
|
5914
|
+
return line < 0 ? source.length : line;
|
|
5915
|
+
}
|
|
5916
|
+
if (char === "/" && source[at + 1] === "*") {
|
|
5917
|
+
const close = source.indexOf("*/", at + 2);
|
|
5918
|
+
return close < 0 ? source.length : close + 2;
|
|
5919
|
+
}
|
|
5920
|
+
return -1;
|
|
5921
|
+
}
|
|
5922
|
+
function withoutComments(text) {
|
|
5923
|
+
let out = "";
|
|
5924
|
+
let at = 0;
|
|
5925
|
+
while (at < text.length) {
|
|
5926
|
+
const skip = skipTrivia(text, at);
|
|
5927
|
+
if (skip < 0) {
|
|
5928
|
+
out += text[at];
|
|
5929
|
+
at++;
|
|
5930
|
+
continue;
|
|
5931
|
+
}
|
|
5932
|
+
out += text[at] === '"' || text[at] === "'" || text[at] === "`" ? text.slice(at, skip) : "";
|
|
5933
|
+
at = skip;
|
|
5934
|
+
}
|
|
5935
|
+
return out;
|
|
5936
|
+
}
|
|
5937
|
+
function propertiesOf(block) {
|
|
5938
|
+
const out = [];
|
|
5939
|
+
const push = (text) => {
|
|
5940
|
+
const held = text.trim();
|
|
5941
|
+
if (held === "") return true;
|
|
5942
|
+
let depth2 = 0;
|
|
5943
|
+
for (let at = 0; at < held.length; ) {
|
|
5944
|
+
const skip = skipTrivia(held, at);
|
|
5945
|
+
if (skip >= 0) {
|
|
5946
|
+
at = skip;
|
|
5947
|
+
continue;
|
|
5948
|
+
}
|
|
5949
|
+
const char = held[at];
|
|
5950
|
+
if ("{[(".includes(char)) depth2++;
|
|
5951
|
+
else if ("}])".includes(char)) depth2--;
|
|
5952
|
+
else if (char === ":" && depth2 === 0) {
|
|
5953
|
+
const key = withoutComments(held.slice(0, at)).trim().replace(/^["'`]|["'`]$/g, "");
|
|
5954
|
+
const value = withoutComments(held.slice(at + 1)).trim();
|
|
5955
|
+
if (key === "" || value === "") return false;
|
|
5956
|
+
out.push({ key, value });
|
|
5957
|
+
return true;
|
|
5958
|
+
}
|
|
5959
|
+
at++;
|
|
5960
|
+
}
|
|
5961
|
+
return false;
|
|
5962
|
+
};
|
|
5963
|
+
let depth = 0;
|
|
5964
|
+
let start = 0;
|
|
5965
|
+
for (let at = 0; at < block.length; ) {
|
|
5966
|
+
const skip = skipTrivia(block, at);
|
|
5967
|
+
if (skip >= 0) {
|
|
5968
|
+
at = skip;
|
|
5969
|
+
continue;
|
|
5970
|
+
}
|
|
5971
|
+
const char = block[at];
|
|
5972
|
+
if ("{[(".includes(char)) depth++;
|
|
5973
|
+
else if ("}])".includes(char)) depth--;
|
|
5974
|
+
else if (char === "," && depth === 0) {
|
|
5975
|
+
if (!push(block.slice(start, at))) return null;
|
|
5976
|
+
start = at + 1;
|
|
5977
|
+
}
|
|
5978
|
+
at++;
|
|
5979
|
+
}
|
|
5980
|
+
return push(block.slice(start)) ? out : null;
|
|
5981
|
+
}
|
|
5417
5982
|
function snapshotUsable(existing) {
|
|
5418
5983
|
if (existing === void 0) return true;
|
|
5419
5984
|
try {
|
|
@@ -5498,7 +6063,22 @@ function lowestCommonAncestor(elements) {
|
|
|
5498
6063
|
}
|
|
5499
6064
|
return common[common.length - 1] ?? null;
|
|
5500
6065
|
}
|
|
5501
|
-
|
|
6066
|
+
var calledIn = (tree, binding) => [...tree.elements.values()].some(
|
|
6067
|
+
(placed) => binding.kind === "namespace" ? placed.node.tag.startsWith(`${binding.local}.`) : placed.node.tag === binding.local
|
|
6068
|
+
);
|
|
6069
|
+
function namespaceMember(tree, local) {
|
|
6070
|
+
const members = new Set(
|
|
6071
|
+
[...tree.elements.values()].map((placed) => placed.node.tag).filter((tag) => tag.startsWith(`${local}.`)).map((tag) => tag.slice(local.length + 1))
|
|
6072
|
+
);
|
|
6073
|
+
return members.size === 1 ? [...members][0] : void 0;
|
|
6074
|
+
}
|
|
6075
|
+
function callSiteOf(tree, local) {
|
|
6076
|
+
const calls = [...tree.elements.values()].map((placed) => placed.node).filter((node) => node.tag === local);
|
|
6077
|
+
const only = calls.length === 1 ? calls[0] : null;
|
|
6078
|
+
return only !== null && only.attrs.length === 0 ? only : null;
|
|
6079
|
+
}
|
|
6080
|
+
function runOf(placeable, roots, content) {
|
|
6081
|
+
if (placeable.length === 0) return null;
|
|
5502
6082
|
const parents = placeable.map((r) => parentOf(r.root, roots));
|
|
5503
6083
|
if (parents.some((p) => p === void 0)) return null;
|
|
5504
6084
|
const [first, ...rest] = parents;
|
|
@@ -5508,9 +6088,14 @@ function runOf(placeable, roots) {
|
|
|
5508
6088
|
);
|
|
5509
6089
|
const indices = placeable.map((r) => siblings.indexOf(r.root)).sort((a, b) => a - b);
|
|
5510
6090
|
if (indices.some((at) => at < 0)) return null;
|
|
5511
|
-
const
|
|
5512
|
-
|
|
6091
|
+
const consecutive2 = indices.every((at, i) => i === 0 || at === indices[i - 1] + 1);
|
|
6092
|
+
const ordered = [...placeable].sort((a, b) => a.root.start - b.root.start);
|
|
6093
|
+
const clean = ordered.every(
|
|
6094
|
+
(span, at) => at === 0 || isTrivia(content.slice(ordered[at - 1].root.end, span.root.start))
|
|
6095
|
+
);
|
|
6096
|
+
return { contiguous: consecutive2 && clean };
|
|
5513
6097
|
}
|
|
6098
|
+
var isTrivia = (between) => between.replace(/<!--[\s\S]*?-->/g, "").replace(/\{\s*\/\*[\s\S]*?\*\/\s*\}/g, "").trim() === "";
|
|
5514
6099
|
function parentOf(target, roots) {
|
|
5515
6100
|
if (roots.some((node) => node === target)) return null;
|
|
5516
6101
|
const search = (nodes, parent) => {
|
|
@@ -5544,14 +6129,35 @@ function pascal(value) {
|
|
|
5544
6129
|
return /^[A-Za-z]/.test(name) ? name : `Section${name}`;
|
|
5545
6130
|
}
|
|
5546
6131
|
var slugify = (value) => value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "section";
|
|
5547
|
-
function componentFile(dialect, path, fromFile, content, parsed, resolved) {
|
|
5548
|
-
const
|
|
6132
|
+
function componentFile(dialect, path, fromFile, content, parsed, resolved, pathPrefix) {
|
|
6133
|
+
const addresses = fieldAddresses(content, elementsUnder(resolved.root));
|
|
6134
|
+
const unconvertible = [
|
|
6135
|
+
...addresses.heads.filter((address) => !address.startsWith(pathPrefix)),
|
|
6136
|
+
...addresses.opaque
|
|
6137
|
+
];
|
|
6138
|
+
if (unconvertible.length > 0) return { content: "", free: [], duplicates: [], unconvertible };
|
|
6139
|
+
const cut = content.slice(resolved.root.start, resolved.root.end);
|
|
6140
|
+
const reserved = /* @__PURE__ */ new Set([...topLevelBindings(dialect, parsed, content), ...referencedNames2(cut)]);
|
|
6141
|
+
const introduced = allocateNames(reserved, [
|
|
6142
|
+
{ key: "bcmsFields", name: "bcmsFields" },
|
|
6143
|
+
{ key: "bcmsField", name: "bcmsField" }
|
|
6144
|
+
]);
|
|
6145
|
+
const fieldsName = introduced.get("bcmsFields");
|
|
6146
|
+
const fieldName = introduced.get("bcmsField");
|
|
6147
|
+
const { markup, bound, duplicates } = sectionMarkup(content, resolved.root, fieldName);
|
|
6148
|
+
if (duplicates.length > 0) return { content: "", free: [], duplicates, unconvertible: [] };
|
|
5549
6149
|
const groupKey = resolved.section.groupKey;
|
|
5550
6150
|
const locals = helperLocals(parsed, dialect, fromFile);
|
|
5551
6151
|
const helperNames = ["bcms", "bcmsRows", "bcmsLayout"].flatMap(
|
|
5552
6152
|
(name) => locals[name].filter((local) => mentions(markup, local)).map((local) => ({ name, local }))
|
|
5553
6153
|
);
|
|
5554
|
-
const helpers = [
|
|
6154
|
+
const helpers = [
|
|
6155
|
+
.../* @__PURE__ */ new Set([
|
|
6156
|
+
...helperNames.map((h) => alias(h.name, h.local)),
|
|
6157
|
+
"bcmsSection",
|
|
6158
|
+
...bound > 0 ? [alias("bcmsFields", fieldsName)] : []
|
|
6159
|
+
])
|
|
6160
|
+
].sort();
|
|
5555
6161
|
const helperSpecifier = relativeImport(path, HELPER_PATH[dialect]).replace(/\.tsx?$/, "");
|
|
5556
6162
|
const snapshot = snapshotOf(parsed, dialect, fromFile, markup);
|
|
5557
6163
|
const carry = carryFor({
|
|
@@ -5566,20 +6172,23 @@ function componentFile(dialect, path, fromFile, content, parsed, resolved) {
|
|
|
5566
6172
|
"bind",
|
|
5567
6173
|
"overrides",
|
|
5568
6174
|
"page",
|
|
6175
|
+
fieldName,
|
|
5569
6176
|
...helperNames.map((h) => h.local),
|
|
5570
6177
|
...snapshot ? [snapshot] : []
|
|
5571
6178
|
],
|
|
5572
6179
|
// The helper and the snapshots are what the component declares for itself.
|
|
5573
6180
|
skipSpecifier: (repoPath, specifier) => specifier.includes(`${CONTENT_DIR}/`) || repoPath.startsWith(HELPER_PATH[dialect].replace(/\.tsx?$/, ""))
|
|
5574
6181
|
});
|
|
5575
|
-
if (carry.free.length > 0) return { content: "", free: carry.free };
|
|
6182
|
+
if (carry.free.length > 0) return { content: "", free: carry.free, duplicates: [], unconvertible: [] };
|
|
5576
6183
|
const carried = [...carry.imports];
|
|
5577
6184
|
const key = /^[A-Za-z_$][\w$]*$/.test(groupKey) ? groupKey : JSON.stringify(groupKey);
|
|
5578
6185
|
const carriedDeclarations = carry.declarations.length > 0 ? `${carry.declarations.join("\n")}
|
|
5579
6186
|
` : "";
|
|
5580
|
-
const copy = snapshot ? `const ${snapshot} = { ${key}: bcmsSection(page, bind
|
|
6187
|
+
const copy = snapshot ? `const ${snapshot} = { ${key}: bcmsSection(page, bind, overrides) };` : "";
|
|
6188
|
+
const field = bound > 0 ? `const ${fieldName} = ${fieldsName}({ blockId, bind, overrides }, ${JSON.stringify(pathPrefix)});` : "";
|
|
6189
|
+
const declared = [copy, field].filter(Boolean).join("\n");
|
|
5581
6190
|
if (dialect === "astro") {
|
|
5582
|
-
return { free: [], content: `---
|
|
6191
|
+
return { free: [], duplicates: [], unconvertible: [], content: `---
|
|
5583
6192
|
${SECTION_MARKER}
|
|
5584
6193
|
${[...carried, `import { ${helpers.join(", ")} } from "${helperSpecifier}";`].join("\n")}
|
|
5585
6194
|
|
|
@@ -5594,27 +6203,297 @@ interface Props {
|
|
|
5594
6203
|
page?: unknown;
|
|
5595
6204
|
}
|
|
5596
6205
|
const { blockId, bind, overrides, page } = Astro.props;
|
|
5597
|
-
${carriedDeclarations}${
|
|
6206
|
+
${carriedDeclarations}${declared}
|
|
5598
6207
|
---
|
|
5599
6208
|
${markup}
|
|
5600
6209
|
` };
|
|
5601
6210
|
}
|
|
5602
|
-
return { free: [], content: `${SECTION_MARKER}
|
|
6211
|
+
return { free: [], duplicates: [], unconvertible: [], content: `${SECTION_MARKER}
|
|
5603
6212
|
${[...carried, `import { ${helpers.join(", ")} } from "${helperSpecifier}";`, `import type { BcmsSectionProps } from "${relativeImport(path, SECTIONS_LIB).replace(/\.tsx?$/, "")}";`].join("\n")}
|
|
5604
6213
|
${carriedDeclarations === "" ? "" : `
|
|
5605
6214
|
${carriedDeclarations}`}
|
|
5606
6215
|
export default function ${resolved.name}({ blockId, bind, overrides, page }: BcmsSectionProps) {
|
|
5607
|
-
${
|
|
6216
|
+
${declared.split("\n").join("\n ")}
|
|
5608
6217
|
return (
|
|
5609
6218
|
${markup}
|
|
5610
6219
|
);
|
|
5611
6220
|
}
|
|
5612
6221
|
` };
|
|
5613
6222
|
}
|
|
5614
|
-
function
|
|
6223
|
+
function sectionMarkup(content, root, fieldName) {
|
|
5615
6224
|
const slice = new MagicString3(content.slice(root.start, root.end));
|
|
5616
|
-
|
|
5617
|
-
|
|
6225
|
+
const at = (offset) => offset - root.start;
|
|
6226
|
+
const stamps = root.attrs.filter((attr) => attr.name === "data-bcms-block");
|
|
6227
|
+
if (stamps.length === 0) slice.appendLeft(at(root.attrInsertAt), " data-bcms-block={blockId}");
|
|
6228
|
+
else {
|
|
6229
|
+
slice.overwrite(at(stamps[0].range.start), at(stamps[0].range.end), "data-bcms-block={blockId}");
|
|
6230
|
+
for (const extra of stamps.slice(1)) {
|
|
6231
|
+
let from = extra.range.start;
|
|
6232
|
+
while (from > root.start && /\s/.test(content[from - 1])) from--;
|
|
6233
|
+
slice.remove(at(from), at(extra.range.end));
|
|
6234
|
+
}
|
|
6235
|
+
}
|
|
6236
|
+
const wrapped = wrapFieldAttrs(slice, content, elementsUnder(root), at, fieldName);
|
|
6237
|
+
return { markup: slice.toString(), ...wrapped };
|
|
6238
|
+
}
|
|
6239
|
+
function wrapFieldAttrs(slice, content, elements, at, fieldName) {
|
|
6240
|
+
let bound = 0;
|
|
6241
|
+
const duplicates = [];
|
|
6242
|
+
for (const element of elements) {
|
|
6243
|
+
const fields = element.attrs.filter((attr) => attr.name === "data-bcms-field");
|
|
6244
|
+
if (fields.length > 1) {
|
|
6245
|
+
duplicates.push(`<${element.tag}>`);
|
|
6246
|
+
continue;
|
|
6247
|
+
}
|
|
6248
|
+
for (const attr of fields) {
|
|
6249
|
+
bound++;
|
|
6250
|
+
if (attr.expressionRange) {
|
|
6251
|
+
slice.appendRight(at(attr.expressionRange.start) + 1, `${fieldName}(`);
|
|
6252
|
+
slice.appendLeft(at(attr.expressionRange.end) - 1, ")");
|
|
6253
|
+
continue;
|
|
6254
|
+
}
|
|
6255
|
+
const value = attr.valueRange ? content.slice(attr.valueRange.start, attr.valueRange.end) : "";
|
|
6256
|
+
slice.overwrite(
|
|
6257
|
+
at(attr.range.start),
|
|
6258
|
+
at(attr.range.end),
|
|
6259
|
+
`data-bcms-field={${fieldName}(${JSON.stringify(value)})}`
|
|
6260
|
+
);
|
|
6261
|
+
}
|
|
6262
|
+
}
|
|
6263
|
+
return { bound, duplicates };
|
|
6264
|
+
}
|
|
6265
|
+
function provableAddress(expression) {
|
|
6266
|
+
const first = expression[0];
|
|
6267
|
+
if (first === '"' || first === "'") {
|
|
6268
|
+
for (let at = 1; at < expression.length; at++) {
|
|
6269
|
+
if (expression[at] === "\\") {
|
|
6270
|
+
at++;
|
|
6271
|
+
continue;
|
|
6272
|
+
}
|
|
6273
|
+
if (expression[at] === first) return at === expression.length - 1 ? expression.slice(1, at) : null;
|
|
6274
|
+
}
|
|
6275
|
+
return null;
|
|
6276
|
+
}
|
|
6277
|
+
if (first !== "`" || expression.length < 2 || !expression.endsWith("`")) return null;
|
|
6278
|
+
const body = expression.slice(1, -1);
|
|
6279
|
+
if (body.includes("`") || body.includes("\\")) return null;
|
|
6280
|
+
let head = null;
|
|
6281
|
+
for (let at = 0; at < body.length; at++) {
|
|
6282
|
+
if (body[at] !== "$" || body[at + 1] !== "{") continue;
|
|
6283
|
+
const close = body.indexOf("}", at + 2);
|
|
6284
|
+
if (close < 0) return null;
|
|
6285
|
+
if (!/^[A-Za-z_$][\w$]*$/.test(body.slice(at + 2, close).trim())) return null;
|
|
6286
|
+
if (body[at - 1] !== "[" || body[close + 1] !== "]") return null;
|
|
6287
|
+
head ??= body.slice(0, at);
|
|
6288
|
+
at = close;
|
|
6289
|
+
}
|
|
6290
|
+
return head ?? body;
|
|
6291
|
+
}
|
|
6292
|
+
function fieldAddresses(content, elements) {
|
|
6293
|
+
const heads = [];
|
|
6294
|
+
const opaque = [];
|
|
6295
|
+
for (const element of elements) {
|
|
6296
|
+
for (const attr of element.attrs) {
|
|
6297
|
+
if (attr.name !== "data-bcms-field") continue;
|
|
6298
|
+
if (!attr.expression && attr.valueRange) {
|
|
6299
|
+
heads.push(content.slice(attr.valueRange.start, attr.valueRange.end));
|
|
6300
|
+
continue;
|
|
6301
|
+
}
|
|
6302
|
+
const inner = attr.expressionRange ? content.slice(attr.expressionRange.start + 1, attr.expressionRange.end - 1).trim() : "";
|
|
6303
|
+
const head = provableAddress(inner);
|
|
6304
|
+
if (head === null) opaque.push(`data-bcms-field={${inner}}`);
|
|
6305
|
+
else heads.push(head);
|
|
6306
|
+
}
|
|
6307
|
+
}
|
|
6308
|
+
return { heads, opaque };
|
|
6309
|
+
}
|
|
6310
|
+
function elementsUnder(...roots) {
|
|
6311
|
+
const out = [];
|
|
6312
|
+
const walk = (node) => {
|
|
6313
|
+
if (node.type === "element") {
|
|
6314
|
+
out.push(node);
|
|
6315
|
+
for (const child of node.children) walk(child);
|
|
6316
|
+
return;
|
|
6317
|
+
}
|
|
6318
|
+
if (node.type === "expr") for (const child of node.children) walk(child);
|
|
6319
|
+
};
|
|
6320
|
+
for (const root of roots) walk(root);
|
|
6321
|
+
return out;
|
|
6322
|
+
}
|
|
6323
|
+
var V1_ANCHOR = {
|
|
6324
|
+
astro: "const { blockId, bind, overrides, page } = Astro.props;",
|
|
6325
|
+
jsx: "}: BcmsSectionProps) {"
|
|
6326
|
+
};
|
|
6327
|
+
var refusalKey = (dialect, slug) => `${dialect}\0${slug}`;
|
|
6328
|
+
function generatedBindings(parsed) {
|
|
6329
|
+
const program = parsed.ast?.program;
|
|
6330
|
+
const body = program?.body ?? [];
|
|
6331
|
+
const scopes = [body];
|
|
6332
|
+
for (const statement of body) {
|
|
6333
|
+
const declaration2 = statement.type === "ExportDefaultDeclaration" ? statement.declaration : void 0;
|
|
6334
|
+
const inner = declaration2?.type === "FunctionDeclaration" ? declaration2.body?.body : void 0;
|
|
6335
|
+
if (inner) scopes.push(inner);
|
|
6336
|
+
}
|
|
6337
|
+
const out = /* @__PURE__ */ new Map();
|
|
6338
|
+
for (const scope of scopes) {
|
|
6339
|
+
for (const statement of scope) {
|
|
6340
|
+
if (statement.type !== "VariableDeclaration") continue;
|
|
6341
|
+
for (const declarator of statement.declarations ?? []) {
|
|
6342
|
+
const id = declarator.id;
|
|
6343
|
+
const name = id?.type === "Identifier" ? id.name : void 0;
|
|
6344
|
+
if (name !== void 0 && !out.has(name)) out.set(name, declarator.init);
|
|
6345
|
+
}
|
|
6346
|
+
}
|
|
6347
|
+
}
|
|
6348
|
+
return out;
|
|
6349
|
+
}
|
|
6350
|
+
function stripBindFallback(parsed, source, content, locals) {
|
|
6351
|
+
if (!parsed.ast?.program) return "unreadable";
|
|
6352
|
+
const offset = parsed.script?.offset ?? 0;
|
|
6353
|
+
let rewritten = 0;
|
|
6354
|
+
let unproven = 0;
|
|
6355
|
+
walkAst(parsed.ast, (node) => {
|
|
6356
|
+
if (node.type !== "CallExpression") return;
|
|
6357
|
+
const argument = (node.arguments ?? [])[1];
|
|
6358
|
+
if (argument?.type !== "LogicalExpression" || argument.operator !== "??") return;
|
|
6359
|
+
const callee = node.callee;
|
|
6360
|
+
if (callee?.type !== "Identifier" || !locals.has(callee.name)) {
|
|
6361
|
+
unproven++;
|
|
6362
|
+
return;
|
|
6363
|
+
}
|
|
6364
|
+
const left = argument.left;
|
|
6365
|
+
source.overwrite(
|
|
6366
|
+
offset + argument.start,
|
|
6367
|
+
offset + argument.end,
|
|
6368
|
+
content.slice(offset + left.start, offset + left.end)
|
|
6369
|
+
);
|
|
6370
|
+
rewritten++;
|
|
6371
|
+
});
|
|
6372
|
+
return rewritten === 0 && unproven > 0 ? "unproven" : "ok";
|
|
6373
|
+
}
|
|
6374
|
+
var isCallOf = (init, callee) => {
|
|
6375
|
+
const fn = init?.type === "CallExpression" ? init.callee : void 0;
|
|
6376
|
+
return fn?.type === "Identifier" && fn.name === callee;
|
|
6377
|
+
};
|
|
6378
|
+
async function migrateSections(plan2, contents, written, fileSet, aliases) {
|
|
6379
|
+
const files = [];
|
|
6380
|
+
const refused = /* @__PURE__ */ new Map();
|
|
6381
|
+
const prefixesFor = (slug) => {
|
|
6382
|
+
const out = /* @__PURE__ */ new Set();
|
|
6383
|
+
for (const page of plan2.pages ?? []) {
|
|
6384
|
+
for (const section of page.sections ?? []) {
|
|
6385
|
+
if (resolveComponent(plan2, section).slug !== slug) continue;
|
|
6386
|
+
const prefix = propPrefix(section);
|
|
6387
|
+
if (prefix !== null) out.add(prefix);
|
|
6388
|
+
}
|
|
6389
|
+
}
|
|
6390
|
+
return [...out];
|
|
6391
|
+
};
|
|
6392
|
+
for (const [dialect, components] of written) {
|
|
6393
|
+
for (const [slug, entry] of components) {
|
|
6394
|
+
const content = contents.get(entry.path);
|
|
6395
|
+
if (content === void 0 || content.includes(SECTION_MARKER)) continue;
|
|
6396
|
+
const older = SECTION_MARKERS.find((marker) => content.includes(marker));
|
|
6397
|
+
if (older === void 0) continue;
|
|
6398
|
+
const candidates = prefixesFor(slug);
|
|
6399
|
+
if (candidates.length === 0) continue;
|
|
6400
|
+
const parsed = await parseFile4(dialect, content);
|
|
6401
|
+
if (parsed.error) {
|
|
6402
|
+
refused.set(refusalKey(dialect, slug), `${entry.path} carries our marker but does not parse (${parsed.error.message}).`);
|
|
6403
|
+
continue;
|
|
6404
|
+
}
|
|
6405
|
+
const elements = elementsUnder(...parsed.roots);
|
|
6406
|
+
const imports = [...importsIn(parsed, dialect)];
|
|
6407
|
+
const helperModule = HELPER_PATH[dialect];
|
|
6408
|
+
const helperLocalsFor = (exported) => new Set(
|
|
6409
|
+
imports.filter(
|
|
6410
|
+
([, from]) => from.imported === exported && resolveSpecifier(entry.path, from.specifier, fileSet, aliases) === helperModule
|
|
6411
|
+
).map(([local]) => local)
|
|
6412
|
+
);
|
|
6413
|
+
const { heads: evidence, opaque } = fieldAddresses(content, elements);
|
|
6414
|
+
const pathPrefix = evidence.length + opaque.length === 0 ? "" : opaque.length > 0 ? null : onlyOne(candidates.filter((prefix) => evidence.every((head) => head.startsWith(prefix)))) ?? null;
|
|
6415
|
+
if (pathPrefix === null) {
|
|
6416
|
+
const matching = candidates.filter((prefix) => evidence.every((head) => head.startsWith(prefix)));
|
|
6417
|
+
refused.set(
|
|
6418
|
+
refusalKey(dialect, slug),
|
|
6419
|
+
opaque.length > 0 ? `${entry.path} carries ${[...new Set(opaque)].slice(0, 3).join(", ")}, whose address this cannot read \u2014 so there is no prefix it can be shown to convert through.` : matching.length === 0 ? `${entry.path} declares ${evidence.length > 0 ? `addresses (${[...new Set(evidence)].slice(0, 3).join(", ")})` : "addresses"} that match none of this slug's placements (${candidates.join(", ") || "none with a usable prop map"}), so the prefix its markup was cut with cannot be recovered.` : `${entry.path} matches more than one of this slug's placements (${matching.join(", ")}), so the prefix its markup was cut with is ambiguous.`
|
|
6420
|
+
);
|
|
6421
|
+
continue;
|
|
6422
|
+
}
|
|
6423
|
+
const source = new MagicString3(content);
|
|
6424
|
+
const { bound, duplicates } = wrapFieldAttrs(source, content, elements, (at) => at, "bcmsField");
|
|
6425
|
+
if (duplicates.length > 0) {
|
|
6426
|
+
refused.set(refusalKey(dialect, slug), `${entry.path} has ${duplicates.join(", ")} carrying more than one data-bcms-field, so there is no single address to rewrite.`);
|
|
6427
|
+
continue;
|
|
6428
|
+
}
|
|
6429
|
+
const markerEnd = content.indexOf(older) + older.length;
|
|
6430
|
+
source.overwrite(markerEnd - older.length, markerEnd, SECTION_MARKER);
|
|
6431
|
+
const stripped = stripBindFallback(parsed, source, content, helperLocalsFor("bcmsSection"));
|
|
6432
|
+
if (stripped !== "ok") {
|
|
6433
|
+
refused.set(
|
|
6434
|
+
refusalKey(dialect, slug),
|
|
6435
|
+
stripped === "unreadable" ? `${entry.path} carries our marker but its module could not be read as a program, so the Phase A \`bind ?? "\u2026"\` fallback cannot be shown to be gone.` : `${entry.path} carries a \`?? \` fallback in a call this cannot identify as ${helperModule}'s bcmsSection, so the Phase A page-group default cannot be shown to be gone.`
|
|
6436
|
+
);
|
|
6437
|
+
continue;
|
|
6438
|
+
}
|
|
6439
|
+
if (bound > 0) {
|
|
6440
|
+
const locals = generatedBindings(parsed);
|
|
6441
|
+
const names = /* @__PURE__ */ new Set([...topLevelBindings(dialect, parsed, content), ...locals.keys()]);
|
|
6442
|
+
const fieldsLocal = [...helperLocalsFor("bcmsFields")][0];
|
|
6443
|
+
if (fieldsLocal === void 0 && names.has("bcmsFields")) {
|
|
6444
|
+
refused.set(
|
|
6445
|
+
refusalKey(dialect, slug),
|
|
6446
|
+
`${entry.path} already binds "bcmsFields" to something other than ${helperModule}'s export.`
|
|
6447
|
+
);
|
|
6448
|
+
continue;
|
|
6449
|
+
}
|
|
6450
|
+
const declared = names.has("bcmsField");
|
|
6451
|
+
if (declared && (fieldsLocal === void 0 || !isCallOf(locals.get("bcmsField"), fieldsLocal))) {
|
|
6452
|
+
refused.set(
|
|
6453
|
+
refusalKey(dialect, slug),
|
|
6454
|
+
`${entry.path} already binds "bcmsField" to something other than a call of ${helperModule}'s bcmsFields.`
|
|
6455
|
+
);
|
|
6456
|
+
continue;
|
|
6457
|
+
}
|
|
6458
|
+
if (!declared) {
|
|
6459
|
+
const anchor = V1_ANCHOR[dialect];
|
|
6460
|
+
const at = anchor ? content.indexOf(anchor) : -1;
|
|
6461
|
+
if (anchor === void 0 || at < 0) {
|
|
6462
|
+
refused.set(refusalKey(dialect, slug), `${entry.path} carries our marker but no longer declares its placement, so there is nowhere the chooser can be put.`);
|
|
6463
|
+
continue;
|
|
6464
|
+
}
|
|
6465
|
+
source.appendLeft(
|
|
6466
|
+
at + anchor.length,
|
|
6467
|
+
`
|
|
6468
|
+
${dialect === "astro" ? "" : " "}const bcmsField = ${fieldsLocal ?? "bcmsFields"}({ blockId, bind, overrides }, ${JSON.stringify(pathPrefix)});`
|
|
6469
|
+
);
|
|
6470
|
+
}
|
|
6471
|
+
if (fieldsLocal === void 0) {
|
|
6472
|
+
const helper = imports.find(([, from]) => from.imported === "bcmsSection")?.[1].specifier ?? relativeImport(entry.path, HELPER_PATH[dialect]).replace(/\.tsx?$/, "");
|
|
6473
|
+
source.appendLeft(markerEnd, `
|
|
6474
|
+
import { bcmsFields } from "${helper}";`);
|
|
6475
|
+
}
|
|
6476
|
+
}
|
|
6477
|
+
files.push({ path: entry.path, operation: "modify", content: source.toString() });
|
|
6478
|
+
}
|
|
6479
|
+
}
|
|
6480
|
+
return { files, refused };
|
|
6481
|
+
}
|
|
6482
|
+
function propPrefix(section) {
|
|
6483
|
+
const prefixes = /* @__PURE__ */ new Set();
|
|
6484
|
+
for (const field of section.fields ?? []) {
|
|
6485
|
+
const { path, key } = field;
|
|
6486
|
+
if (typeof path !== "string" || typeof key !== "string") continue;
|
|
6487
|
+
if (!path.endsWith(key)) return null;
|
|
6488
|
+
prefixes.add(path.slice(0, path.length - key.length));
|
|
6489
|
+
}
|
|
6490
|
+
if (prefixes.size > 1) return null;
|
|
6491
|
+
return prefixes.size === 1 ? [...prefixes][0] : `${section.groupKey}.`;
|
|
6492
|
+
}
|
|
6493
|
+
function referencedNames2(markup) {
|
|
6494
|
+
const out = /* @__PURE__ */ new Set();
|
|
6495
|
+
for (const [name] of markup.matchAll(/[A-Za-z_$][\w$]*/g)) out.add(name);
|
|
6496
|
+
return out;
|
|
5618
6497
|
}
|
|
5619
6498
|
var alias = (name, local) => name === local ? name : `${name} as ${local}`;
|
|
5620
6499
|
var mentions = (markup, name) => new RegExp(`(?<![\\w$])${name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![\\w$])`).test(markup);
|
|
@@ -5730,9 +6609,9 @@ function registryFile(dialect, path, written, shipped) {
|
|
|
5730
6609
|
const entries = [...written].sort(([a], [b]) => a.localeCompare(b));
|
|
5731
6610
|
const imports = entries.map(([, component]) => {
|
|
5732
6611
|
const specifier = relativeImport(path, component.path);
|
|
5733
|
-
return
|
|
6612
|
+
return importLine(component.name, dialect === "astro" ? specifier : specifier.replace(/\.tsx?$/, ""), component);
|
|
5734
6613
|
}).join("\n");
|
|
5735
|
-
const registry = entries.map(([slug, component]) => ` ${JSON.stringify(slug)}: ${component
|
|
6614
|
+
const registry = entries.map(([slug, component]) => ` ${JSON.stringify(slug)}: ${registryValue(component)},`).join("\n");
|
|
5736
6615
|
const shippedTable = [...shipped].sort(([a], [b]) => a.localeCompare(b)).map(([page, list]) => ` ${JSON.stringify(page)}: ${JSON.stringify(list)},`).join("\n");
|
|
5737
6616
|
const lib = relativeImport(path, SECTIONS_LIB).replace(/\.tsx?$/, "");
|
|
5738
6617
|
const registryType = dialect === "astro" ? "Record<string, unknown>" : "Record<string, (props: BcmsSectionProps) => JSX.Element | null>";
|
|
@@ -5840,17 +6719,24 @@ function splicePageInline(file, content, dialect, parsed, placeable, pageSlug, w
|
|
|
5840
6719
|
const snapshot = names.get("pageSnapshot");
|
|
5841
6720
|
const source = new MagicString3(content);
|
|
5842
6721
|
for (const resolved of placeable) {
|
|
6722
|
+
const component = written.get(resolved.slug);
|
|
6723
|
+
const tag = component.member ? `${names.get(resolved.slug)}.${component.member}` : names.get(resolved.slug);
|
|
5843
6724
|
source.overwrite(
|
|
5844
6725
|
resolved.root.start,
|
|
5845
6726
|
resolved.root.end,
|
|
5846
|
-
`<${
|
|
6727
|
+
`<${tag} bind=${JSON.stringify(resolved.section.groupKey)} page={${snapshot}(${JSON.stringify(pageSlug)})} />`
|
|
5847
6728
|
);
|
|
5848
6729
|
}
|
|
5849
6730
|
const lib = relativeImport(file, SECTIONS_LIB).replace(/\.tsx?$/, "");
|
|
5850
6731
|
const imports = [
|
|
5851
6732
|
...wanted.map(({ key }) => {
|
|
5852
|
-
const
|
|
5853
|
-
|
|
6733
|
+
const component = written.get(key);
|
|
6734
|
+
const specifier = relativeImport(file, component.path);
|
|
6735
|
+
return importLine(
|
|
6736
|
+
names.get(key),
|
|
6737
|
+
dialect === "astro" ? specifier : specifier.replace(/\.tsx?$/, ""),
|
|
6738
|
+
component
|
|
6739
|
+
);
|
|
5854
6740
|
}),
|
|
5855
6741
|
`import { pageSnapshot${snapshot === "pageSnapshot" ? "" : ` as ${snapshot}`} } from "${lib}";`
|
|
5856
6742
|
];
|
|
@@ -5866,7 +6752,7 @@ function addImports(source, parsed, lines) {
|
|
|
5866
6752
|
else source.appendLeft(at.at, `
|
|
5867
6753
|
${lines.join("\n")}`);
|
|
5868
6754
|
}
|
|
5869
|
-
var isGenerated = (content) => content.includes(
|
|
6755
|
+
var isGenerated = (content) => SECTION_MARKERS.some((marker) => content.includes(marker)) || content.includes(REGISTRY_MARKER);
|
|
5870
6756
|
function emitHelper(out, contents, version, overwrite) {
|
|
5871
6757
|
const path = HELPER_PATH.astro;
|
|
5872
6758
|
const source = helperSource("astro");
|
|
@@ -6023,28 +6909,78 @@ async function convertSources(briefIn, sources, options) {
|
|
|
6023
6909
|
);
|
|
6024
6910
|
}
|
|
6025
6911
|
let declaredOnly = 0;
|
|
6912
|
+
const declaredRows = [];
|
|
6913
|
+
const briefRoutes = new Set(brief.pages.map((page) => page.route));
|
|
6914
|
+
const declaredRow = (target, readsFromCms, file) => {
|
|
6915
|
+
declaredRows.push({
|
|
6916
|
+
route: target.route,
|
|
6917
|
+
scope: target.scope,
|
|
6918
|
+
path: target.path,
|
|
6919
|
+
kind: target.kind,
|
|
6920
|
+
...file ? { file } : {},
|
|
6921
|
+
readsFromCms
|
|
6922
|
+
});
|
|
6923
|
+
};
|
|
6026
6924
|
const witnessed = async (target) => {
|
|
6027
6925
|
for (const source of sources) {
|
|
6028
6926
|
const parsed = await parseOf(source.path);
|
|
6029
6927
|
const dialect = dialectOf(source.path, source.content);
|
|
6030
6928
|
if (!parsed || !dialect) continue;
|
|
6031
|
-
if (convertedHere(source.content, parsed, dialect, target, source.path))
|
|
6929
|
+
if (convertedHere(source.content, parsed, dialect, target, source.path) && attributable(target, source.path)) {
|
|
6930
|
+
return source.path;
|
|
6931
|
+
}
|
|
6032
6932
|
}
|
|
6033
|
-
return
|
|
6933
|
+
return null;
|
|
6034
6934
|
};
|
|
6035
6935
|
const locatedByKey = /* @__PURE__ */ new Map();
|
|
6036
6936
|
for (const hit of located) {
|
|
6037
6937
|
const key = keyOf(hit.route, hit.scope ?? "page", hit.path);
|
|
6038
6938
|
if (!locatedByKey.has(key)) locatedByKey.set(key, hit);
|
|
6039
6939
|
}
|
|
6940
|
+
const sharingPath = /* @__PURE__ */ new Map();
|
|
6941
|
+
for (const [key, hit] of locatedByKey) {
|
|
6942
|
+
const held = targets.get(key);
|
|
6943
|
+
if (!held) continue;
|
|
6944
|
+
const id = scopedPath(held.scope, held.path);
|
|
6945
|
+
sharingPath.set(id, [...sharingPath.get(id) ?? [], { key, files: hit.files }]);
|
|
6946
|
+
}
|
|
6947
|
+
const claimedByAnother = (target, file) => (sharingPath.get(scopedPath(target.scope, target.path)) ?? []).some(
|
|
6948
|
+
(other) => other.key !== target.key && other.files.includes(file)
|
|
6949
|
+
);
|
|
6950
|
+
const attributable = (target, file, claimed) => {
|
|
6951
|
+
if (target.scope === "layout") return true;
|
|
6952
|
+
const serves = routeOfFile(file);
|
|
6953
|
+
if (serves !== null && serves !== target.route && briefRoutes.has(serves)) return false;
|
|
6954
|
+
if (serves === target.route && pageFilesFor(target.route, contents.keys()).length !== 1) return false;
|
|
6955
|
+
return claimed !== true;
|
|
6956
|
+
};
|
|
6957
|
+
const declaredByHand = (target, within) => {
|
|
6958
|
+
const candidates = within ?? (target.scope === "layout" ? sources.map((s) => s.path) : pageFilesFor(target.route, contents.keys()));
|
|
6959
|
+
for (const file of candidates) {
|
|
6960
|
+
if (!covers(declarations.get(file), target)) continue;
|
|
6961
|
+
if (attributable(target, file, within !== void 0 && claimedByAnother(target, file))) return file;
|
|
6962
|
+
}
|
|
6963
|
+
return null;
|
|
6964
|
+
};
|
|
6040
6965
|
const missed = /* @__PURE__ */ new Set();
|
|
6041
6966
|
for (const miss of unlocated) {
|
|
6042
6967
|
const key = keyOf(miss.route, miss.scope ?? "page", miss.path);
|
|
6043
6968
|
const target = targets.get(key);
|
|
6044
6969
|
if (!target || locatedByKey.has(key) || missed.has(key)) continue;
|
|
6045
6970
|
missed.add(key);
|
|
6046
|
-
|
|
6047
|
-
|
|
6971
|
+
const witness = await witnessed(target);
|
|
6972
|
+
if (witness !== null) {
|
|
6973
|
+
declaredOnly++;
|
|
6974
|
+
declaredRow(target, true, witness);
|
|
6975
|
+
continue;
|
|
6976
|
+
}
|
|
6977
|
+
const byHand = declaredByHand(target);
|
|
6978
|
+
if (byHand !== null) {
|
|
6979
|
+
declaredOnly++;
|
|
6980
|
+
declaredRow(target, false, byHand);
|
|
6981
|
+
continue;
|
|
6982
|
+
}
|
|
6983
|
+
pend(target, miss.reason === "no-original" ? "NO_ORIGINAL" : "NOT_IN_SOURCE");
|
|
6048
6984
|
}
|
|
6049
6985
|
const byFile = /* @__PURE__ */ new Map();
|
|
6050
6986
|
for (const [key, hit] of locatedByKey) {
|
|
@@ -6102,8 +7038,21 @@ async function convertSources(briefIn, sources, options) {
|
|
|
6102
7038
|
if (worst) {
|
|
6103
7039
|
const file = hit.files[outcomes.indexOf(worst)];
|
|
6104
7040
|
pend(target, worst, file);
|
|
6105
|
-
} else if (outcomes.every((o) => o === "declared"))
|
|
6106
|
-
|
|
7041
|
+
} else if (outcomes.every((o) => o === "declared")) {
|
|
7042
|
+
const witness = await witnessed(target);
|
|
7043
|
+
const byHand = witness === null ? declaredByHand(target, hit.files) : null;
|
|
7044
|
+
if (witness === null && byHand === null) {
|
|
7045
|
+
pend(
|
|
7046
|
+
target,
|
|
7047
|
+
"AMBIGUOUS_LITERAL",
|
|
7048
|
+
hit.files[0],
|
|
7049
|
+
`${hit.files.join(", ")} already declares ${target.path}, and nothing there says it is ${target.route}'s.`
|
|
7050
|
+
);
|
|
7051
|
+
continue;
|
|
7052
|
+
}
|
|
7053
|
+
alreadyDeclared++;
|
|
7054
|
+
declaredRow(target, witness !== null, witness ?? byHand);
|
|
7055
|
+
} else rewritten++;
|
|
6107
7056
|
}
|
|
6108
7057
|
for (const [file, splices] of [...componentEdits].sort(([a], [b]) => a.localeCompare(b))) {
|
|
6109
7058
|
const source = new MagicString4(contents.get(file));
|
|
@@ -6129,12 +7078,27 @@ async function convertSources(briefIn, sources, options) {
|
|
|
6129
7078
|
emitStubs2(dialects, snapshotSlugs, contents, outFiles);
|
|
6130
7079
|
const receipt = assertReceipt({
|
|
6131
7080
|
briefDigest: digest,
|
|
6132
|
-
paths: {
|
|
7081
|
+
paths: {
|
|
7082
|
+
declared: targets.size,
|
|
7083
|
+
rewritten,
|
|
7084
|
+
alreadyDeclared,
|
|
7085
|
+
declarations: declaredRows.sort(
|
|
7086
|
+
(a, b) => a.route.localeCompare(b.route) || a.scope.localeCompare(b.scope) || a.path.localeCompare(b.path)
|
|
7087
|
+
),
|
|
7088
|
+
pending
|
|
7089
|
+
},
|
|
6133
7090
|
occurrences: { located: occurrencesLocated, rewritten: occurrencesRewritten },
|
|
6134
7091
|
files: receiptFiles,
|
|
6135
7092
|
helpers,
|
|
6136
7093
|
helperUpgraded,
|
|
6137
|
-
dynamicBindings
|
|
7094
|
+
dynamicBindings,
|
|
7095
|
+
notes: unreadableConfigs(sources).map(
|
|
7096
|
+
(file) => ({
|
|
7097
|
+
code: "TSCONFIG_UNREADABLE",
|
|
7098
|
+
file,
|
|
7099
|
+
message: `${file} is not JSON this package can read, so its \`paths\` aliases resolved to nothing.`
|
|
7100
|
+
})
|
|
7101
|
+
)
|
|
6138
7102
|
});
|
|
6139
7103
|
return { files: outFiles.sort((a, b) => a.path.localeCompare(b.path)), receipt };
|
|
6140
7104
|
}
|
|
@@ -6402,14 +7366,12 @@ async function plan(file, content, dialect, targets, sites, importAt, outcomes,
|
|
|
6402
7366
|
looped.add(member.key);
|
|
6403
7367
|
outcomes.set(member.key, "rewritten");
|
|
6404
7368
|
}
|
|
6405
|
-
for (const
|
|
7369
|
+
for (const declaration2 of plan2.declarations) {
|
|
6406
7370
|
dynamic.push({
|
|
6407
7371
|
file,
|
|
6408
7372
|
prop: null,
|
|
6409
|
-
path:
|
|
6410
|
-
|
|
6411
|
-
// described a binding the file does not contain, which is what the proposer looks for.
|
|
6412
|
-
callSites: [{ file, literal: `${arrayPath}[\${${plan2.names.index}}].${leaf}` }]
|
|
7373
|
+
path: declaration2.path,
|
|
7374
|
+
callSites: [{ file, literal: declaration2.literal }]
|
|
6413
7375
|
});
|
|
6414
7376
|
}
|
|
6415
7377
|
rewrites.push({
|
|
@@ -6695,6 +7657,7 @@ export {
|
|
|
6695
7657
|
rewriteFile,
|
|
6696
7658
|
routeOfFile,
|
|
6697
7659
|
stripTags,
|
|
7660
|
+
unreadableConfigs,
|
|
6698
7661
|
walkAst
|
|
6699
7662
|
};
|
|
6700
7663
|
//# sourceMappingURL=index.js.map
|