@bettercms-ai/convert 0.24.1 → 0.25.1
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 +18 -0
- package/dist/cli.js +148 -51
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +33 -1
- package/dist/index.js +122 -43
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -589,6 +589,8 @@ interface AttrNode {
|
|
|
589
589
|
* that has an expression says where it starts, rather than leaving each reader to scan for it.
|
|
590
590
|
*/
|
|
591
591
|
expressionRange: Range | null;
|
|
592
|
+
/** The digits of `price={49}` — a number is copy a reader sees, so it is matched like a string. */
|
|
593
|
+
number?: string;
|
|
592
594
|
}
|
|
593
595
|
type Node = {
|
|
594
596
|
type: "element";
|
|
@@ -1853,6 +1855,36 @@ declare function readBrief(value: unknown): Brief;
|
|
|
1853
1855
|
*/
|
|
1854
1856
|
declare function readPlan(value: unknown): ComponentizePlan;
|
|
1855
1857
|
|
|
1858
|
+
/**
|
|
1859
|
+
* `build:bcms` — the build BetterCMS runs instead of `build`, when a repository declares one.
|
|
1860
|
+
*
|
|
1861
|
+
* The builder runs `build:bcms` in place of `build` (as Vercel runs `vercel-build`), and never edits
|
|
1862
|
+
* a project's build script itself. A converted repository's `build` usually chains the checks its
|
|
1863
|
+
* own CI wants in front of the build — `astro check && astro build` — and every BetterCMS deploy
|
|
1864
|
+
* paid for them: the type check alone was 8–10 s of a warm ScrewFast build. This writes the same
|
|
1865
|
+
* build without those checks, once, beside the script it came from. `build` is left exactly as it
|
|
1866
|
+
* was, so the project's own CI still checks everything.
|
|
1867
|
+
*
|
|
1868
|
+
* Only a script it can read with certainty: steps joined by `&&` and nothing else. A `;`, `||`,
|
|
1869
|
+
* pipe, background `&` or substitution means the steps do not simply run in order, and that script
|
|
1870
|
+
* is left alone. `prebuild` and `postbuild` are folded in (minus their own checks), because a run
|
|
1871
|
+
* of `build:bcms` triggers `prebuild:bcms`, not `prebuild` — dropping them would lose a step the
|
|
1872
|
+
* build needs (ScrewFast splits its content snapshot in `prebuild`).
|
|
1873
|
+
*/
|
|
1874
|
+
interface BcmsBuild {
|
|
1875
|
+
/** The new package.json, formatted as the original was. */
|
|
1876
|
+
content: string;
|
|
1877
|
+
/** The `build:bcms` it adds. */
|
|
1878
|
+
script: string;
|
|
1879
|
+
/** The steps of `build` (and its pre/post hooks) left out of it. */
|
|
1880
|
+
dropped: string[];
|
|
1881
|
+
}
|
|
1882
|
+
/**
|
|
1883
|
+
* The package.json with a `build:bcms` added, or null when there is nothing to add: one already
|
|
1884
|
+
* exists, no `build` chains a check, or a script is not one it can read with certainty.
|
|
1885
|
+
*/
|
|
1886
|
+
declare function bcmsBuildScript(packageJson: string): BcmsBuild | null;
|
|
1887
|
+
|
|
1856
1888
|
/** The plan's own file shape, restated rather than imported to keep `index.ts` acyclic. */
|
|
1857
1889
|
interface UpgradeFile {
|
|
1858
1890
|
path: string;
|
|
@@ -1965,4 +1997,4 @@ declare function convertSources(briefIn: Brief, sources: SourceFile[], options?:
|
|
|
1965
1997
|
receipt: ConversionReceipt;
|
|
1966
1998
|
}>;
|
|
1967
1999
|
|
|
1968
|
-
export { ASTRO_LANE_PENDING, type AstNode, type AttrBinding, type Brief, type BriefForm, type BriefPage, type BriefPath, COMPONENT_DIR, CONTENT_DIR, type CanvasResult, type CanvasSource, type Carried, type ComponentizeOptions, type ComponentizePlan, type ComponentizePlanComponent, type ComponentizePlanField, type ComponentizePlanPage, type ComponentizePlanSection, type ComponentizeReceipt, type ConversionReceipt, ConvertError, type ConvertErrorCode, type ConvertOptions, DIALECT_RULES, type DataMember, type DataResult, type DeclaredPath, type Dialect, type DynamicBinding, FORM_SCRIPT, type FileDeclaration, type FindSitesResult, type FixAction, FormInvariantError, type FormPendingReason, type FormsReceipt, HELPER_PATH, HELPER_VERSION, type HelperUpgrade, type ImageResult, type ImageTarget, type ImportedFrom, type LlmFallback, type LocatedPath, type Node, PARSE_FILE, type ParsedFile, type ParserError, type PathLocator, type PendingFix, type PendingForm, type PendingPath, type PendingReason, type PendingSection, type PlanFile, REGISTRY_MARKER, type ReceiptFile, ReceiptInvariantError, type ReceiptNote, type Rewrite, SECTIONS_LIB, SECTION_MARKER, SKIP_DIRS, SKIP_FILES, SOURCE_EXTENSIONS, SSR_DRAFT_RECIPE, STUB_CONTENT, SectionInvariantError, type SectionPendingReason, type SectionUpgradeReceipt, type SectionsReceipt, type Site, type SiteWhere, type SkippedRoot, type SourceFile, type Splice, type TargetIdentity, type UnlocatedPath, UpgradeError, type UpgradeOptions, type UpgradeRootRef, type UpgradeSkipReason, type UpgradedRoot, type ValidationCode, ValidationError, type Xray, type XrayBucket, aliasesFrom, assertForms, assertReceipt, assertSections, assetCandidates, assetImport, attrsFor, bindDataLiterals, bindImages, briefDigest, canvasBridge, carryFor, componentizeSources, convertSources, convertedHere, coverageOf, declaringElements, dialectOf, exportedFunction, extendedConfigs, findLiteral, findSites, findSitesTolerant, fixFor, flat, helperSource, helperUpgrades, importsIn, isDecorativeGlyph, isDynamicRoute, isKnownHelper, isSourceCandidate, locate, measureUnaddressable, overlapping, pageFilesFor, parseFile, propsAttribute, readBrief, readComponentizePlan, readDeclarations, readExpr, readPlan, relativeImport, resolveSpecifier, rewriteFile, routeCounts, routeOfFile, stripTags, unreadableConfigs, upgradeSections, walkAst, wireForms };
|
|
2000
|
+
export { ASTRO_LANE_PENDING, type AstNode, type AttrBinding, type BcmsBuild, type Brief, type BriefForm, type BriefPage, type BriefPath, COMPONENT_DIR, CONTENT_DIR, type CanvasResult, type CanvasSource, type Carried, type ComponentizeOptions, type ComponentizePlan, type ComponentizePlanComponent, type ComponentizePlanField, type ComponentizePlanPage, type ComponentizePlanSection, type ComponentizeReceipt, type ConversionReceipt, ConvertError, type ConvertErrorCode, type ConvertOptions, DIALECT_RULES, type DataMember, type DataResult, type DeclaredPath, type Dialect, type DynamicBinding, FORM_SCRIPT, type FileDeclaration, type FindSitesResult, type FixAction, FormInvariantError, type FormPendingReason, type FormsReceipt, HELPER_PATH, HELPER_VERSION, type HelperUpgrade, type ImageResult, type ImageTarget, type ImportedFrom, type LlmFallback, type LocatedPath, type Node, PARSE_FILE, type ParsedFile, type ParserError, type PathLocator, type PendingFix, type PendingForm, type PendingPath, type PendingReason, type PendingSection, type PlanFile, REGISTRY_MARKER, type ReceiptFile, ReceiptInvariantError, type ReceiptNote, type Rewrite, SECTIONS_LIB, SECTION_MARKER, SKIP_DIRS, SKIP_FILES, SOURCE_EXTENSIONS, SSR_DRAFT_RECIPE, STUB_CONTENT, SectionInvariantError, type SectionPendingReason, type SectionUpgradeReceipt, type SectionsReceipt, type Site, type SiteWhere, type SkippedRoot, type SourceFile, type Splice, type TargetIdentity, type UnlocatedPath, UpgradeError, type UpgradeOptions, type UpgradeRootRef, type UpgradeSkipReason, type UpgradedRoot, type ValidationCode, ValidationError, type Xray, type XrayBucket, aliasesFrom, assertForms, assertReceipt, assertSections, assetCandidates, assetImport, attrsFor, bcmsBuildScript, bindDataLiterals, bindImages, briefDigest, canvasBridge, carryFor, componentizeSources, convertSources, convertedHere, coverageOf, declaringElements, dialectOf, exportedFunction, extendedConfigs, findLiteral, findSites, findSitesTolerant, fixFor, flat, helperSource, helperUpgrades, importsIn, isDecorativeGlyph, isDynamicRoute, isKnownHelper, isSourceCandidate, locate, measureUnaddressable, overlapping, pageFilesFor, parseFile, propsAttribute, readBrief, readComponentizePlan, readDeclarations, readExpr, readPlan, relativeImport, resolveSpecifier, rewriteFile, routeCounts, routeOfFile, stripTags, unreadableConfigs, upgradeSections, walkAst, wireForms };
|
package/dist/index.js
CHANGED
|
@@ -2711,8 +2711,9 @@ function collectSites(roots, literals) {
|
|
|
2711
2711
|
}
|
|
2712
2712
|
const bindingsAttr = node.attrs.find((a) => a.name === "bcmsBindings")?.range;
|
|
2713
2713
|
for (const attr of node.attrs) {
|
|
2714
|
-
|
|
2715
|
-
|
|
2714
|
+
const digits = attr.expression && attr.expressionRange ? attr.number : void 0;
|
|
2715
|
+
if (attr.expression && digits === void 0 || !digits && !attr.valueRange || BCMS_ATTRS.includes(attr.name)) continue;
|
|
2716
|
+
const value = norm(digits ?? attr.value);
|
|
2716
2717
|
for (const literal of wanted) {
|
|
2717
2718
|
if (value !== literal) continue;
|
|
2718
2719
|
matched.add(literal);
|
|
@@ -2720,7 +2721,7 @@ function collectSites(roots, literals) {
|
|
|
2720
2721
|
file: "",
|
|
2721
2722
|
tag: node.tag,
|
|
2722
2723
|
attrInsertAt: node.attrInsertAt,
|
|
2723
|
-
range: attr.valueRange,
|
|
2724
|
+
range: digits === void 0 ? attr.valueRange : attr.expressionRange,
|
|
2724
2725
|
inner: node.inner,
|
|
2725
2726
|
// 🔴 A COMPONENT'S ATTRIBUTE IS A PROP, and a prop is a different conversion: the value
|
|
2726
2727
|
// is rendered in another file, by an element only that file knows. @see props.ts
|
|
@@ -2828,6 +2829,8 @@ function attrsOf2(open, content) {
|
|
|
2828
2829
|
const value = attr.value;
|
|
2829
2830
|
const range = { start: attr.start ?? 0, end: attr.end ?? 0 };
|
|
2830
2831
|
const literal = value?.type === "StringLiteral";
|
|
2832
|
+
const inside = value?.type === "JSXExpressionContainer" ? value.expression : null;
|
|
2833
|
+
const numeric = inside?.type === "NumericLiteral" ? String(inside.extra.raw) : "";
|
|
2831
2834
|
return [
|
|
2832
2835
|
{
|
|
2833
2836
|
name: nameOf(attr.name),
|
|
@@ -2836,7 +2839,8 @@ function attrsOf2(open, content) {
|
|
|
2836
2839
|
valueRange: literal ? { start: (value.start ?? 0) + 1, end: (value.end ?? 0) - 1 } : null,
|
|
2837
2840
|
expression: !literal,
|
|
2838
2841
|
range,
|
|
2839
|
-
expressionRange: value?.type === "JSXExpressionContainer" ? { start: value.start ?? 0, end: value.end ?? 0 } : null
|
|
2842
|
+
expressionRange: value?.type === "JSXExpressionContainer" ? { start: value.start ?? 0, end: value.end ?? 0 } : null,
|
|
2843
|
+
...numeric ? { number: numeric } : {}
|
|
2840
2844
|
}
|
|
2841
2845
|
];
|
|
2842
2846
|
});
|
|
@@ -3312,14 +3316,21 @@ function exportedNames(ast) {
|
|
|
3312
3316
|
});
|
|
3313
3317
|
return out;
|
|
3314
3318
|
}
|
|
3319
|
+
function printed(node) {
|
|
3320
|
+
if (node?.type === "StringLiteral") return [String(node.value)];
|
|
3321
|
+
if (node?.type === "NumericLiteral") return [String(node.value), String(node.extra?.raw ?? "")];
|
|
3322
|
+
const quasis = node?.type === "TemplateLiteral" ? node.quasis ?? [] : [];
|
|
3323
|
+
const expressions = node?.expressions ?? [];
|
|
3324
|
+
return quasis.length === 1 && expressions.length === 0 ? [String(quasis[0].value.cooked ?? "")] : [];
|
|
3325
|
+
}
|
|
3315
3326
|
function propertyFor(row, literal) {
|
|
3316
3327
|
if (row?.type !== "ObjectExpression") return null;
|
|
3317
3328
|
for (const property of row.properties ?? []) {
|
|
3318
3329
|
if (property.type !== "ObjectProperty" && property.type !== "Property") continue;
|
|
3319
3330
|
const key = property.key;
|
|
3320
3331
|
const value = property.value;
|
|
3321
|
-
if (
|
|
3322
|
-
if (
|
|
3332
|
+
if (key?.type !== "Identifier") continue;
|
|
3333
|
+
if (printed(value).some((text) => norm(text) === literal)) return String(key.name);
|
|
3323
3334
|
}
|
|
3324
3335
|
return null;
|
|
3325
3336
|
}
|
|
@@ -3393,7 +3404,7 @@ function stringLiterals(ast) {
|
|
|
3393
3404
|
if (node.type !== "VariableDeclarator") return;
|
|
3394
3405
|
const id = node.id;
|
|
3395
3406
|
const init = node.init;
|
|
3396
|
-
if (id?.type !== "Identifier" || init
|
|
3407
|
+
if (id?.type !== "Identifier" || !init || printed(init).length === 0) return;
|
|
3397
3408
|
out.set(String(id.name), init);
|
|
3398
3409
|
});
|
|
3399
3410
|
return out;
|
|
@@ -3424,7 +3435,7 @@ function scalarUses(content, roots, id) {
|
|
|
3424
3435
|
}
|
|
3425
3436
|
function bindScalar(ctx, member, literals, offset) {
|
|
3426
3437
|
const rule = DIALECT_RULES[ctx.dialect];
|
|
3427
|
-
const named = [...literals].filter(([, init2]) =>
|
|
3438
|
+
const named = [...literals].filter(([, init2]) => printed(init2).some((text) => norm(text) === member.literal));
|
|
3428
3439
|
if (named.length !== 1) return { splices: [], refusal: null };
|
|
3429
3440
|
const [id, init] = named[0];
|
|
3430
3441
|
const uses = scalarUses(ctx.content, ctx.parsed.roots, id);
|
|
@@ -3639,16 +3650,17 @@ function literalTable(init) {
|
|
|
3639
3650
|
const first = elements[0];
|
|
3640
3651
|
if (!first || elements.length !== (init.elements ?? []).length) return null;
|
|
3641
3652
|
if (elements.some((element) => element.type !== first.type)) return null;
|
|
3642
|
-
if (first.
|
|
3643
|
-
|
|
3653
|
+
if (printed(first).length > 0) {
|
|
3654
|
+
if (elements.some((element) => printed(element).length === 0)) return null;
|
|
3655
|
+
return { tuple: false, rows: elements.map((element) => [norm(printed(element)[0])]) };
|
|
3644
3656
|
}
|
|
3645
3657
|
if (first.type !== "ArrayExpression") return null;
|
|
3646
3658
|
const rows = [];
|
|
3647
3659
|
for (const element of elements) {
|
|
3648
3660
|
const inner = (element.elements ?? []).filter((x) => x !== null);
|
|
3649
|
-
if (inner.length === 0 || inner.some((x) => x.
|
|
3661
|
+
if (inner.length === 0 || inner.some((x) => printed(x).length === 0)) return null;
|
|
3650
3662
|
if (inner.length !== (element.elements ?? []).length) return null;
|
|
3651
|
-
rows.push(inner.map((x) => norm(
|
|
3663
|
+
rows.push(inner.map((x) => norm(printed(x)[0])));
|
|
3652
3664
|
}
|
|
3653
3665
|
if (new Set(rows.map((row) => row.length)).size !== 1) return null;
|
|
3654
3666
|
return { tuple: true, rows };
|
|
@@ -5465,7 +5477,8 @@ function overlapping(rewrites) {
|
|
|
5465
5477
|
for (const a of rewrites) {
|
|
5466
5478
|
for (const b of rewrites) {
|
|
5467
5479
|
if (a.raw !== void 0 || b.raw !== void 0) continue;
|
|
5468
|
-
if (a
|
|
5480
|
+
if (a === b || a.key === b.key) continue;
|
|
5481
|
+
if (collide(a, b)) {
|
|
5469
5482
|
out.add(a);
|
|
5470
5483
|
out.add(b);
|
|
5471
5484
|
}
|
|
@@ -5473,6 +5486,16 @@ function overlapping(rewrites) {
|
|
|
5473
5486
|
}
|
|
5474
5487
|
return [...out];
|
|
5475
5488
|
}
|
|
5489
|
+
var writes = ({ site }) => [site.range, site.attrRange, site.where === "text" ? site.inner : null].filter((r) => r != null);
|
|
5490
|
+
function collide(a, b) {
|
|
5491
|
+
const inside = (at, spans) => spans.some((r) => r.start < at && at < r.end);
|
|
5492
|
+
const [wa, wb] = [writes(a), writes(b)];
|
|
5493
|
+
if (wa.some((x) => wb.some((y) => overlaps(x, y)))) return true;
|
|
5494
|
+
if (inside(a.site.attrInsertAt, wb) || inside(b.site.attrInsertAt, wa)) return true;
|
|
5495
|
+
const [fa, fb] = [field(a), field(b)];
|
|
5496
|
+
return a.site.attrInsertAt === b.site.attrInsertAt && !!fa && !!fb && fa !== fb;
|
|
5497
|
+
}
|
|
5498
|
+
var field = (r) => r.site.where === "prop" || r.site.where === "text" && !ownsSubtree(r.kind, r.site, r.inline) && r.site.mixed && r.site.textRange ? void 0 : declaration(r).plain[0];
|
|
5476
5499
|
function declaration(rewrite) {
|
|
5477
5500
|
const { site, path, kind, scope } = rewrite;
|
|
5478
5501
|
if (site.where === "attr" && kind !== "image") {
|
|
@@ -5487,8 +5510,8 @@ function rewriteFile(content, dialect, rewrites, imports, extra = []) {
|
|
|
5487
5510
|
const bindings = /* @__PURE__ */ new Map();
|
|
5488
5511
|
const addDeclaration = (at, add) => {
|
|
5489
5512
|
const held = attributes.get(at) ?? { plain: [], props: [] };
|
|
5490
|
-
held.plain.push(...add.plain);
|
|
5491
|
-
held.props.push(...add.props);
|
|
5513
|
+
held.plain.push(...add.plain.filter((text) => !held.plain.includes(text)));
|
|
5514
|
+
held.props.push(...add.props.filter((p) => !held.props.some((q) => JSON.stringify(q) === JSON.stringify(p))));
|
|
5492
5515
|
attributes.set(at, held);
|
|
5493
5516
|
};
|
|
5494
5517
|
for (const rewrite of rewrites) {
|
|
@@ -5919,7 +5942,8 @@ function attrsOf4(node, content) {
|
|
|
5919
5942
|
valueRange: quoted && rawStart >= 0 && attr.value !== "" ? { start: rawStart + (unquoted ? 0 : 1), end: rawStart + raw.length - (unquoted ? 0 : 1) } : null,
|
|
5920
5943
|
expression: !quoted,
|
|
5921
5944
|
range,
|
|
5922
|
-
expressionRange: !quoted && open >= 0 && close > open ? { start: open, end: close + 1 } : null
|
|
5945
|
+
expressionRange: !quoted && open >= 0 && close > open ? { start: open, end: close + 1 } : null,
|
|
5946
|
+
...attr.kind === "expression" && /^\d+(\.\d+)?$/.test(attr.value.trim()) ? { number: attr.value.trim() } : {}
|
|
5923
5947
|
}
|
|
5924
5948
|
];
|
|
5925
5949
|
});
|
|
@@ -6038,6 +6062,9 @@ function attrsOf5(node) {
|
|
|
6038
6062
|
return [{ name: `\0${name}`, value: "", valueRange: null, expression: true, expressionRange: null, range }];
|
|
6039
6063
|
}
|
|
6040
6064
|
const literal = literalValue(attr);
|
|
6065
|
+
const tag = attr.value;
|
|
6066
|
+
const raw = tag?.type === "ExpressionTag" ? tag.expression : void 0;
|
|
6067
|
+
const digits = typeof raw?.value === "number" ? String(raw.raw ?? "") : "";
|
|
6041
6068
|
return [
|
|
6042
6069
|
{
|
|
6043
6070
|
name,
|
|
@@ -6045,7 +6072,8 @@ function attrsOf5(node) {
|
|
|
6045
6072
|
valueRange: literal && literal.value !== "" ? literal.range : null,
|
|
6046
6073
|
expression: literal === null,
|
|
6047
6074
|
// Not read: svelte sections are DIALECT_UNSUPPORTED. @see componentize.ts
|
|
6048
|
-
expressionRange: null,
|
|
6075
|
+
expressionRange: digits ? { start: tag.start ?? 0, end: tag.end ?? 0 } : null,
|
|
6076
|
+
...digits ? { number: digits } : {},
|
|
6049
6077
|
range
|
|
6050
6078
|
}
|
|
6051
6079
|
];
|
|
@@ -6150,13 +6178,15 @@ var spanOf = (loc) => ({
|
|
|
6150
6178
|
function attrsOf6(node) {
|
|
6151
6179
|
return (node.props ?? []).flatMap((prop) => {
|
|
6152
6180
|
if (prop.type === DIRECTIVE && prop.name === "bind" && prop.arg?.content) {
|
|
6181
|
+
const digits = /^\d+(\.\d+)?$/.test(String(prop.exp?.content ?? "").trim()) ? String(prop.exp.content).trim() : "";
|
|
6153
6182
|
return [
|
|
6154
6183
|
{
|
|
6155
6184
|
name: prop.arg.content,
|
|
6156
6185
|
value: "",
|
|
6157
6186
|
valueRange: null,
|
|
6158
6187
|
expression: true,
|
|
6159
|
-
expressionRange: null,
|
|
6188
|
+
expressionRange: digits ? spanOf(prop.exp?.loc) : null,
|
|
6189
|
+
...digits ? { number: digits } : {},
|
|
6160
6190
|
range: spanOf(prop.loc)
|
|
6161
6191
|
}
|
|
6162
6192
|
];
|
|
@@ -6644,8 +6674,8 @@ function readPlan(value) {
|
|
|
6644
6674
|
check.optionalString(section.sectionType, `${at2}.sectionType`);
|
|
6645
6675
|
check.optionalString(section.copy, `${at2}.copy`);
|
|
6646
6676
|
if (section.fields !== void 0) {
|
|
6647
|
-
for (const [f,
|
|
6648
|
-
const shape = check.record(
|
|
6677
|
+
for (const [f, field2] of check.array(section.fields, `${at2}.fields`).entries()) {
|
|
6678
|
+
const shape = check.record(field2, `${at2}.fields[${f}]`);
|
|
6649
6679
|
check.optionalString(shape.key, `${at2}.fields[${f}].key`);
|
|
6650
6680
|
check.optionalString(shape.path, `${at2}.fields[${f}].path`);
|
|
6651
6681
|
}
|
|
@@ -6964,15 +6994,15 @@ function wire(source, candidate, form) {
|
|
|
6964
6994
|
source.remove(start, attr.range.end);
|
|
6965
6995
|
}
|
|
6966
6996
|
}
|
|
6967
|
-
for (const
|
|
6968
|
-
const control = candidate.controls.get(
|
|
6997
|
+
for (const field2 of form.fields) {
|
|
6998
|
+
const control = candidate.controls.get(field2.key);
|
|
6969
6999
|
if (candidate.foreign.has(control)) continue;
|
|
6970
7000
|
const host = candidate.wrapper.get(control) ?? control;
|
|
6971
|
-
if (!attrOf(host, "data-bcms-form-field")) source.appendLeft(host.attrInsertAt, ` data-bcms-form-field="${escapeAttr(
|
|
7001
|
+
if (!attrOf(host, "data-bcms-form-field")) source.appendLeft(host.attrInsertAt, ` data-bcms-form-field="${escapeAttr(field2.key)}"`);
|
|
6972
7002
|
const name = attrOf(control, "name");
|
|
6973
|
-
if (!name) source.appendLeft(control.attrInsertAt, ` name="${escapeAttr(
|
|
6974
|
-
else if (!name.expression && name.value !==
|
|
6975
|
-
source.overwrite(name.valueRange.start, name.valueRange.end, escapeAttr(
|
|
7003
|
+
if (!name) source.appendLeft(control.attrInsertAt, ` name="${escapeAttr(field2.key)}"`);
|
|
7004
|
+
else if (!name.expression && name.value !== field2.key && name.valueRange) {
|
|
7005
|
+
source.overwrite(name.valueRange.start, name.valueRange.end, escapeAttr(field2.key));
|
|
6976
7006
|
}
|
|
6977
7007
|
}
|
|
6978
7008
|
if (el.inner && !hasMessage(el)) {
|
|
@@ -7016,7 +7046,7 @@ async function wireForms(briefIn, sources, options_ = {}) {
|
|
|
7016
7046
|
candidates.push(candidateOf(source.path, dialect, el, imported, landmark, source.content, parsed.importAt));
|
|
7017
7047
|
}
|
|
7018
7048
|
}
|
|
7019
|
-
const holds = (c, f) => f.fields.filter((
|
|
7049
|
+
const holds = (c, f) => f.fields.filter((field2) => c.controls.has(field2.key)).length;
|
|
7020
7050
|
const byId = (c, f) => !attrOf(c.el, "data-bcms-form")?.expression && attrOf(c.el, "data-bcms-form")?.value === f.id;
|
|
7021
7051
|
const programAction = (c) => !!attrOf(c.el, "action")?.expression && calledName(exprOf(c, "action"), "bcmsFormAction") === null;
|
|
7022
7052
|
const wiredTo = (c, f) => BY_NAME.includes(c.dialect) ? calledName(exprOf(c, "data-bcms-form"), "bcmsFormId") === f.name || byId(c, f) && programAction(c) : byId(c, f);
|
|
@@ -7138,9 +7168,9 @@ async function wireForms(briefIn, sources, options_ = {}) {
|
|
|
7138
7168
|
pend2(form, "ACTION_IS_EXPRESSION", candidate.file, "its action is an expression, so this run will not replace it.");
|
|
7139
7169
|
continue;
|
|
7140
7170
|
}
|
|
7141
|
-
const misnamed = form.fields.find((
|
|
7142
|
-
const attr = attrOf(candidate.controls.get(
|
|
7143
|
-
return !!attr && attr.value !==
|
|
7171
|
+
const misnamed = form.fields.find((field2) => {
|
|
7172
|
+
const attr = attrOf(candidate.controls.get(field2.key), "name");
|
|
7173
|
+
return !!attr && attr.value !== field2.key && (attr.expression || !attr.valueRange);
|
|
7144
7174
|
});
|
|
7145
7175
|
if (misnamed) {
|
|
7146
7176
|
pend2(form, "FIELD_UNMATCHED", candidate.file, `the control for ${misnamed.key} posts under an expression, so its name cannot be set to the CMS key.`);
|
|
@@ -7220,7 +7250,7 @@ ${indent}${scriptFor(last.candidate.dialect)}`);
|
|
|
7220
7250
|
);
|
|
7221
7251
|
}
|
|
7222
7252
|
for (const { form, candidate } of matched) {
|
|
7223
|
-
const keys = form.fields.filter((
|
|
7253
|
+
const keys = form.fields.filter((field2) => candidate.foreign.has(candidate.controls.get(field2.key))).map((field2) => field2.key);
|
|
7224
7254
|
if (keys.length > 0) {
|
|
7225
7255
|
notes.push(
|
|
7226
7256
|
`form ${form.name}: ${keys.join(", ")} matched a control inside an imported component, so no data-bcms-form-field marker was written for ${keys.length === 1 ? "it" : "them"}.`
|
|
@@ -7690,6 +7720,54 @@ function moduleScope(parsed, content) {
|
|
|
7690
7720
|
return out;
|
|
7691
7721
|
}
|
|
7692
7722
|
|
|
7723
|
+
// src/build-script.ts
|
|
7724
|
+
var CHECK = /^(?:npx\s+|pnpm\s+(?:exec\s+)?|yarn\s+|bunx\s+)?(?:astro\s+check|svelte-check|vue-tsc|tsc(?:\s|$)|eslint|next\s+lint|biome\s+(?:check|lint|ci)|prettier\s+(?:--check|-c)\b|stylelint|oxlint)/;
|
|
7725
|
+
var RUN_CHECK = /^(?:npm\s+run|pnpm(?:\s+run)?|yarn(?:\s+run)?|bun\s+run)\s+(?:lint|typecheck|type-check|check|check:types|types|format:check|test)(?::[\w-]+)?\s*$/;
|
|
7726
|
+
var UNSAFE = /;|\|\||\||(?:^|[^&])&(?:[^&]|$)|\$\(|`|\n/;
|
|
7727
|
+
var isCheck = (step) => CHECK.test(step) || RUN_CHECK.test(step);
|
|
7728
|
+
function steps(script) {
|
|
7729
|
+
if (UNSAFE.test(script)) return null;
|
|
7730
|
+
const out = script.split("&&").map((s) => s.trim());
|
|
7731
|
+
return out.some((s) => s === "") ? null : out;
|
|
7732
|
+
}
|
|
7733
|
+
function bcmsBuildScript(packageJson) {
|
|
7734
|
+
let pkg;
|
|
7735
|
+
try {
|
|
7736
|
+
pkg = JSON.parse(packageJson);
|
|
7737
|
+
} catch {
|
|
7738
|
+
return null;
|
|
7739
|
+
}
|
|
7740
|
+
const scripts = pkg?.scripts;
|
|
7741
|
+
if (!scripts || typeof scripts !== "object" || "build:bcms" in scripts) return null;
|
|
7742
|
+
const build = scripts.build;
|
|
7743
|
+
if (typeof build !== "string") return null;
|
|
7744
|
+
const parts = [];
|
|
7745
|
+
for (const name of ["prebuild", "build", "postbuild"]) {
|
|
7746
|
+
const value = scripts[name];
|
|
7747
|
+
if (value === void 0) continue;
|
|
7748
|
+
if (typeof value !== "string") return null;
|
|
7749
|
+
const s = steps(value);
|
|
7750
|
+
if (!s) return null;
|
|
7751
|
+
parts.push(s);
|
|
7752
|
+
}
|
|
7753
|
+
const all = parts.flat();
|
|
7754
|
+
const dropped = all.filter(isCheck);
|
|
7755
|
+
const kept = all.filter((s) => !isCheck(s));
|
|
7756
|
+
if (dropped.length === 0 || kept.length === 0) return null;
|
|
7757
|
+
const script = kept.join(" && ");
|
|
7758
|
+
const next = {};
|
|
7759
|
+
for (const [key, value] of Object.entries(scripts)) {
|
|
7760
|
+
next[key] = value;
|
|
7761
|
+
if (key === "build") next["build:bcms"] = script;
|
|
7762
|
+
}
|
|
7763
|
+
const indent = /^[ \t]+(?=")/m.exec(packageJson)?.[0] ?? " ";
|
|
7764
|
+
const eol = packageJson.includes("\r\n") ? "\r\n" : "\n";
|
|
7765
|
+
let content = JSON.stringify({ ...pkg, scripts: next }, null, indent);
|
|
7766
|
+
if (eol !== "\n") content = content.replace(/\n/g, eol);
|
|
7767
|
+
if (/\r?\n$/.test(packageJson)) content += eol;
|
|
7768
|
+
return { content, script, dropped };
|
|
7769
|
+
}
|
|
7770
|
+
|
|
7693
7771
|
// src/componentize.ts
|
|
7694
7772
|
import MagicString5 from "magic-string";
|
|
7695
7773
|
var SectionInvariantError = class extends Error {
|
|
@@ -8439,7 +8517,7 @@ var inSection = (section, path) => {
|
|
|
8439
8517
|
const target = rowless(path);
|
|
8440
8518
|
return fieldPaths(section).some((fieldPath) => rowless(fieldPath) === target);
|
|
8441
8519
|
};
|
|
8442
|
-
var fieldPaths = (section) => (section.fields ?? []).map((
|
|
8520
|
+
var fieldPaths = (section) => (section.fields ?? []).map((field2) => field2.path ?? `${section.groupKey}.${field2.key ?? ""}`);
|
|
8443
8521
|
function elementIndex(roots) {
|
|
8444
8522
|
const out = /* @__PURE__ */ new Map();
|
|
8445
8523
|
const visit = (node, chain) => {
|
|
@@ -8597,8 +8675,8 @@ function componentFile(dialect, path, fromFile, content, parsed, resolved, pathP
|
|
|
8597
8675
|
const carriedDeclarations = carry.declarations.length > 0 ? `${carry.declarations.join("\n")}
|
|
8598
8676
|
` : "";
|
|
8599
8677
|
const copy = !snapshot ? "" : flat3 ? `const ${snapshot} = bcmsSectionPaths(page, bind, overrides, ${JSON.stringify(pathPrefix)}, ${JSON.stringify(pathMap)}, bindPaths);` : `const ${snapshot} = { ${key}: bcmsSection(page, bind, overrides, bindPaths) };`;
|
|
8600
|
-
const
|
|
8601
|
-
const declared = [copy,
|
|
8678
|
+
const field2 = bound > 0 ? `const ${fieldName} = ${fieldsName}({ blockId, bind, bindPaths, overrides }, ${JSON.stringify(pathPrefix)}${flat3 ? `, ${JSON.stringify(pathMap)}` : ""});` : "";
|
|
8679
|
+
const declared = [copy, field2].filter(Boolean).join("\n");
|
|
8602
8680
|
const styles = dialect === "astro" ? carriedStyles(content) : "";
|
|
8603
8681
|
if (dialect === "astro") {
|
|
8604
8682
|
return { free: [], duplicates: [], unconvertible: [], content: `---
|
|
@@ -8911,8 +8989,8 @@ function propPrefix(section) {
|
|
|
8911
8989
|
}
|
|
8912
8990
|
function exactPrefix(section) {
|
|
8913
8991
|
const prefixes = /* @__PURE__ */ new Set();
|
|
8914
|
-
for (const
|
|
8915
|
-
const { path, key } =
|
|
8992
|
+
for (const field2 of section.fields ?? []) {
|
|
8993
|
+
const { path, key } = field2;
|
|
8916
8994
|
if (typeof path !== "string" || typeof key !== "string") continue;
|
|
8917
8995
|
if (!path.endsWith(key)) return null;
|
|
8918
8996
|
prefixes.add(path.slice(0, path.length - key.length));
|
|
@@ -8924,11 +9002,11 @@ var foldKey = (remainder) => remainder.replace(/\[(\d+)\]/g, "_$1").replace(/[^a
|
|
|
8924
9002
|
function flatPrefix(section) {
|
|
8925
9003
|
const prefixes = /* @__PURE__ */ new Set();
|
|
8926
9004
|
let informative = false;
|
|
8927
|
-
for (const
|
|
8928
|
-
const { path, key } =
|
|
9005
|
+
for (const field2 of section.fields ?? []) {
|
|
9006
|
+
const { path, key } = field2;
|
|
8929
9007
|
if (typeof path !== "string" || typeof key !== "string") continue;
|
|
8930
9008
|
informative = true;
|
|
8931
|
-
const prefix = ["-", "_"].map((sep) => `${section.groupKey}${sep}`).find((candidate) => path.startsWith(candidate) && (typeof
|
|
9009
|
+
const prefix = ["-", "_"].map((sep) => `${section.groupKey}${sep}`).find((candidate) => path.startsWith(candidate) && (typeof field2.overrideKey === "string" || foldKey(path.slice(candidate.length)) === key));
|
|
8932
9010
|
if (!prefix) return null;
|
|
8933
9011
|
prefixes.add(prefix);
|
|
8934
9012
|
}
|
|
@@ -8937,15 +9015,15 @@ function flatPrefix(section) {
|
|
|
8937
9015
|
function flatPathMap(section, prefix) {
|
|
8938
9016
|
const scalars = {};
|
|
8939
9017
|
const tables = {};
|
|
8940
|
-
for (const
|
|
8941
|
-
const { path, key } =
|
|
9018
|
+
for (const field2 of section.fields ?? []) {
|
|
9019
|
+
const { path, key } = field2;
|
|
8942
9020
|
if (typeof path !== "string" || typeof key !== "string" || !path.startsWith(prefix)) continue;
|
|
8943
9021
|
const row = /^([^[]+)\[\d+\]\.(.+)$/.exec(path);
|
|
8944
9022
|
if (!row) {
|
|
8945
|
-
scalars[path] =
|
|
9023
|
+
scalars[path] = field2.overrideKey ?? key;
|
|
8946
9024
|
continue;
|
|
8947
9025
|
}
|
|
8948
|
-
const address = typeof
|
|
9026
|
+
const address = typeof field2.overrideKey === "string" ? /^([^[]+)\[\d+\]\.(.+)$/.exec(field2.overrideKey) : null;
|
|
8949
9027
|
if (!address) return null;
|
|
8950
9028
|
const table = tables[row[1]] ??= { key: address[1], columns: {} };
|
|
8951
9029
|
if (table.key !== address[1]) return null;
|
|
@@ -10788,6 +10866,7 @@ export {
|
|
|
10788
10866
|
assetCandidates,
|
|
10789
10867
|
assetImport,
|
|
10790
10868
|
attrsFor,
|
|
10869
|
+
bcmsBuildScript,
|
|
10791
10870
|
bindDataLiterals,
|
|
10792
10871
|
bindImages,
|
|
10793
10872
|
briefDigest,
|