@bettercms-ai/convert 0.4.0 → 0.6.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 +38 -4
- package/README.md +12 -0
- package/dist/cli.js +330 -50
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +45 -2
- package/dist/index.js +333 -50
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import
|
|
2
|
+
import MagicString5 from "magic-string";
|
|
3
3
|
|
|
4
4
|
// src/brief.ts
|
|
5
5
|
import { createHash } from "crypto";
|
|
@@ -21,6 +21,21 @@ var BY_EXTENSION = {
|
|
|
21
21
|
".svelte": "svelte",
|
|
22
22
|
".vue": "vue"
|
|
23
23
|
};
|
|
24
|
+
var TOLERANT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
25
|
+
".md",
|
|
26
|
+
".mdx",
|
|
27
|
+
".njk",
|
|
28
|
+
".hbs",
|
|
29
|
+
".ejs",
|
|
30
|
+
".liquid",
|
|
31
|
+
".erb",
|
|
32
|
+
".twig"
|
|
33
|
+
]);
|
|
34
|
+
function isTolerantDialect(path) {
|
|
35
|
+
const lower = path.toLowerCase();
|
|
36
|
+
const at = lower.lastIndexOf(".");
|
|
37
|
+
return at >= 0 && TOLERANT_EXTENSIONS.has(lower.slice(at));
|
|
38
|
+
}
|
|
24
39
|
function dialectOf(path, _content) {
|
|
25
40
|
const lower = path.toLowerCase();
|
|
26
41
|
const at = lower.lastIndexOf(".");
|
|
@@ -4261,6 +4276,7 @@ function importLines(imports) {
|
|
|
4261
4276
|
}
|
|
4262
4277
|
|
|
4263
4278
|
// src/receipt.ts
|
|
4279
|
+
var CANVAS_REASONS = /* @__PURE__ */ new Set(["CANVAS_BRIDGE_MANUAL", "SSR_DRAFT_ROUTE"]);
|
|
4264
4280
|
var ReceiptInvariantError = class extends Error {
|
|
4265
4281
|
code = "RECEIPT_INVARIANT";
|
|
4266
4282
|
constructor(message) {
|
|
@@ -4270,10 +4286,11 @@ var ReceiptInvariantError = class extends Error {
|
|
|
4270
4286
|
};
|
|
4271
4287
|
function assertReceipt(receipt) {
|
|
4272
4288
|
const { declared, rewritten, alreadyDeclared, pending } = receipt.paths;
|
|
4273
|
-
const
|
|
4289
|
+
const paths = pending.filter((p) => !CANVAS_REASONS.has(p.reason));
|
|
4290
|
+
const sum = rewritten + alreadyDeclared + paths.length;
|
|
4274
4291
|
if (sum !== declared) {
|
|
4275
4292
|
throw new ReceiptInvariantError(
|
|
4276
|
-
`The receipt does not account for every path: ${declared} declared but ${rewritten} rewritten + ${alreadyDeclared} already declared + ${
|
|
4293
|
+
`The receipt does not account for every path: ${declared} declared but ${rewritten} rewritten + ${alreadyDeclared} already declared + ${paths.length} pending = ${sum}.`
|
|
4277
4294
|
);
|
|
4278
4295
|
}
|
|
4279
4296
|
if (receipt.occurrences.rewritten > receipt.occurrences.located) {
|
|
@@ -4785,6 +4802,218 @@ var findSites = async (dialect, content, literals) => {
|
|
|
4785
4802
|
return parsed.error ? { sites: [], error: parsed.error } : { sites: collectSites(parsed.roots, literals), importAt: parsed.importAt };
|
|
4786
4803
|
};
|
|
4787
4804
|
|
|
4805
|
+
// src/canvas.ts
|
|
4806
|
+
import MagicString3 from "magic-string";
|
|
4807
|
+
var SSR_DRAFT_RECIPE = "Astro renders this site at build time, so there is no server to answer the editor's draft request. Either mount <BcmsLiveBlocks client:load> from @bettercms-ai/astro on the pages whose body is a block tree, or switch to `output: 'server'` with @astrojs/node (mode: 'standalone') \u2014 the bettercms() integration injects the draft routes for you \u2014 render pages from loadPage + loadComponents, mount <BcmsLive>, set BCMS_DRAFT_SECRET (BetterCMS hosting sets it for you), redeploy, and switch the project to 'Run as a server app'.";
|
|
4808
|
+
var isNode = (value) => !!value && typeof value === "object" && typeof value.type === "string";
|
|
4809
|
+
var FUNCTIONS3 = /* @__PURE__ */ new Set([
|
|
4810
|
+
"FunctionDeclaration",
|
|
4811
|
+
"FunctionExpression",
|
|
4812
|
+
"ArrowFunctionExpression",
|
|
4813
|
+
"ObjectMethod",
|
|
4814
|
+
"ClassMethod"
|
|
4815
|
+
]);
|
|
4816
|
+
function patternNames2(node, out) {
|
|
4817
|
+
if (!isNode(node)) return;
|
|
4818
|
+
switch (node.type) {
|
|
4819
|
+
case "Identifier":
|
|
4820
|
+
out.add(String(node.name));
|
|
4821
|
+
return;
|
|
4822
|
+
case "ObjectPattern":
|
|
4823
|
+
for (const p of node.properties ?? [])
|
|
4824
|
+
patternNames2(p.type === "RestElement" ? p.argument : p.value, out);
|
|
4825
|
+
return;
|
|
4826
|
+
case "ArrayPattern":
|
|
4827
|
+
for (const el of node.elements ?? []) patternNames2(el, out);
|
|
4828
|
+
return;
|
|
4829
|
+
case "AssignmentPattern":
|
|
4830
|
+
patternNames2(node.left, out);
|
|
4831
|
+
return;
|
|
4832
|
+
case "RestElement":
|
|
4833
|
+
patternNames2(node.argument, out);
|
|
4834
|
+
return;
|
|
4835
|
+
default:
|
|
4836
|
+
return;
|
|
4837
|
+
}
|
|
4838
|
+
}
|
|
4839
|
+
function statementNames2(statements, out) {
|
|
4840
|
+
for (const statement of statements ?? []) {
|
|
4841
|
+
if (!isNode(statement)) continue;
|
|
4842
|
+
const node = statement.type === "ExportNamedDeclaration" || statement.type === "ExportDefaultDeclaration" ? statement.declaration : statement;
|
|
4843
|
+
if (!isNode(node)) continue;
|
|
4844
|
+
if (node.type === "VariableDeclaration")
|
|
4845
|
+
for (const d of node.declarations ?? []) patternNames2(d.id, out);
|
|
4846
|
+
else if (node.type === "FunctionDeclaration" || node.type === "ClassDeclaration") patternNames2(node.id, out);
|
|
4847
|
+
else if (node.type === "ImportDeclaration")
|
|
4848
|
+
for (const specifier of node.specifiers ?? []) patternNames2(specifier.local, out);
|
|
4849
|
+
}
|
|
4850
|
+
}
|
|
4851
|
+
function functionBindings(fn) {
|
|
4852
|
+
const out = /* @__PURE__ */ new Set();
|
|
4853
|
+
for (const param of fn.params ?? []) patternNames2(param, out);
|
|
4854
|
+
const body = fn.body;
|
|
4855
|
+
if (body?.type === "BlockStatement") statementNames2(body.body, out);
|
|
4856
|
+
return out;
|
|
4857
|
+
}
|
|
4858
|
+
function stringLiterals(node, out) {
|
|
4859
|
+
if (!isNode(node)) {
|
|
4860
|
+
if (Array.isArray(node)) for (const child of node) stringLiterals(child, out);
|
|
4861
|
+
return;
|
|
4862
|
+
}
|
|
4863
|
+
if (FUNCTIONS3.has(node.type)) return;
|
|
4864
|
+
if (node.type === "StringLiteral" && typeof node.value === "string") out.add(node.value);
|
|
4865
|
+
for (const key of Object.keys(node)) {
|
|
4866
|
+
if (key === "loc" || key === "leadingComments" || key === "trailingComments") continue;
|
|
4867
|
+
stringLiterals(node[key], out);
|
|
4868
|
+
}
|
|
4869
|
+
}
|
|
4870
|
+
function usages(ast, local) {
|
|
4871
|
+
const found = [];
|
|
4872
|
+
const moduleScope2 = /* @__PURE__ */ new Set();
|
|
4873
|
+
statementNames2(ast?.program?.body, moduleScope2);
|
|
4874
|
+
const walk = (node, scope, literals) => {
|
|
4875
|
+
if (!isNode(node)) {
|
|
4876
|
+
if (Array.isArray(node)) for (const child of node) walk(child, scope, literals);
|
|
4877
|
+
return;
|
|
4878
|
+
}
|
|
4879
|
+
let nextScope = scope;
|
|
4880
|
+
let nextLiterals = literals;
|
|
4881
|
+
if (FUNCTIONS3.has(node.type)) {
|
|
4882
|
+
nextScope = /* @__PURE__ */ new Set([...scope, ...functionBindings(node)]);
|
|
4883
|
+
nextLiterals = /* @__PURE__ */ new Set();
|
|
4884
|
+
stringLiterals(node.body, nextLiterals);
|
|
4885
|
+
}
|
|
4886
|
+
if (node.type === "JSXElement") {
|
|
4887
|
+
const name = node.openingElement?.name;
|
|
4888
|
+
if (name?.type === "JSXIdentifier" && name.name === local)
|
|
4889
|
+
found.push({ element: node, scope: nextScope, literals: nextLiterals });
|
|
4890
|
+
}
|
|
4891
|
+
for (const key of Object.keys(node)) {
|
|
4892
|
+
if (key === "loc" || key === "leadingComments" || key === "trailingComments") continue;
|
|
4893
|
+
walk(node[key], nextScope, nextLiterals);
|
|
4894
|
+
}
|
|
4895
|
+
};
|
|
4896
|
+
walk(ast?.program ?? ast, moduleScope2, /* @__PURE__ */ new Set());
|
|
4897
|
+
return found;
|
|
4898
|
+
}
|
|
4899
|
+
function bridgedAlready(ast) {
|
|
4900
|
+
const body = ast?.program?.body ?? [];
|
|
4901
|
+
return body.some(
|
|
4902
|
+
(statement) => statement.type === "ImportDeclaration" && String(statement.source?.value ?? "") === "@bettercms-ai/next/draft-bridge" && (statement.specifiers ?? []).some(
|
|
4903
|
+
(specifier) => specifier.imported?.name === "BcmsDraftBridge"
|
|
4904
|
+
)
|
|
4905
|
+
);
|
|
4906
|
+
}
|
|
4907
|
+
function blocksImport(ast) {
|
|
4908
|
+
const body = ast?.program?.body ?? [];
|
|
4909
|
+
for (const statement of body) {
|
|
4910
|
+
if (statement.type !== "ImportDeclaration") continue;
|
|
4911
|
+
const source = String(statement.source?.value ?? "");
|
|
4912
|
+
if (source !== "@bettercms-ai/next" && source !== "@bettercms-ai/next/blocks") continue;
|
|
4913
|
+
for (const specifier of statement.specifiers ?? []) {
|
|
4914
|
+
const imported = specifier.imported;
|
|
4915
|
+
if (specifier.type !== "ImportSpecifier" || imported?.name !== "BcmsBlocks") continue;
|
|
4916
|
+
return { declaration: statement, specifier, local: String(specifier.local.name) };
|
|
4917
|
+
}
|
|
4918
|
+
}
|
|
4919
|
+
return null;
|
|
4920
|
+
}
|
|
4921
|
+
function slugExpression(usage, routes) {
|
|
4922
|
+
if (usage.scope.has("slug")) return "{slug}";
|
|
4923
|
+
if (usage.scope.has("page")) return "{page.slug}";
|
|
4924
|
+
const matched = [...new Set([...usage.literals].map((l) => routes.get(l)).filter((s) => !!s))];
|
|
4925
|
+
return matched.length === 1 ? JSON.stringify(matched[0]) : null;
|
|
4926
|
+
}
|
|
4927
|
+
var lineOf = (content, offset) => content.slice(0, offset).split("\n").length;
|
|
4928
|
+
var pend = (reason, hint, file, line) => ({
|
|
4929
|
+
route: "",
|
|
4930
|
+
scope: "page",
|
|
4931
|
+
path: reason === "SSR_DRAFT_ROUTE" ? "canvas.lane" : "canvas.bridge",
|
|
4932
|
+
kind: "canvas",
|
|
4933
|
+
...file ? { file } : {},
|
|
4934
|
+
...line ? { line } : {},
|
|
4935
|
+
reason,
|
|
4936
|
+
hint
|
|
4937
|
+
});
|
|
4938
|
+
var ASTRO_LANE_PENDING = pend("SSR_DRAFT_ROUTE", SSR_DRAFT_RECIPE);
|
|
4939
|
+
function canvasBridge(sources, routes) {
|
|
4940
|
+
if (sources.some((s) => s.path.toLowerCase().endsWith(".astro")))
|
|
4941
|
+
return { lane: "pending", files: [], edits: /* @__PURE__ */ new Map(), pending: [ASTRO_LANE_PENDING] };
|
|
4942
|
+
const files = [];
|
|
4943
|
+
const edits = /* @__PURE__ */ new Map();
|
|
4944
|
+
const pending = [];
|
|
4945
|
+
for (const source of sources) {
|
|
4946
|
+
if (!/\.[jt]sx$/i.test(source.path) || !source.ast) continue;
|
|
4947
|
+
if (bridgedAlready(source.ast)) {
|
|
4948
|
+
files.push(source.path);
|
|
4949
|
+
continue;
|
|
4950
|
+
}
|
|
4951
|
+
const imported = blocksImport(source.ast);
|
|
4952
|
+
if (!imported) continue;
|
|
4953
|
+
const found = usages(source.ast, imported.local);
|
|
4954
|
+
const slugs = found.map((usage) => slugExpression(usage, routes));
|
|
4955
|
+
const unresolved = found.findIndex((_, i) => slugs[i] === null);
|
|
4956
|
+
if (found.length === 0 || unresolved !== -1) {
|
|
4957
|
+
const at = found[unresolved === -1 ? 0 : unresolved]?.element.start;
|
|
4958
|
+
pending.push(
|
|
4959
|
+
pend(
|
|
4960
|
+
"CANVAS_BRIDGE_MANUAL",
|
|
4961
|
+
"Wrap this page's <BcmsBlocks> in <BcmsDraftBridge slug={\u2026}> from @bettercms-ai/next/draft-bridge. No slug expression was derivable in the component's own scope, so the codemod will not guess one: a bridge on the wrong slug never updates in the editor.",
|
|
4962
|
+
source.path,
|
|
4963
|
+
at === void 0 ? void 0 : lineOf(source.content, at)
|
|
4964
|
+
)
|
|
4965
|
+
);
|
|
4966
|
+
continue;
|
|
4967
|
+
}
|
|
4968
|
+
const out = new MagicString3(source.content);
|
|
4969
|
+
rewriteImport(out, imported);
|
|
4970
|
+
for (const [i, usage] of found.entries()) rewriteElement(out, usage, imported.local, slugs[i]);
|
|
4971
|
+
edits.set(source.path, out.toString());
|
|
4972
|
+
files.push(source.path);
|
|
4973
|
+
}
|
|
4974
|
+
return { lane: files.length > 0 ? "bridge" : "pending", files, edits, pending };
|
|
4975
|
+
}
|
|
4976
|
+
function rewriteImport(out, imported) {
|
|
4977
|
+
const { declaration: declaration2, specifier, local } = imported;
|
|
4978
|
+
const line = `import { BcmsDraftBridge${local === "BcmsBlocks" ? "" : ` as ${local}`} } from "@bettercms-ai/next/draft-bridge";`;
|
|
4979
|
+
const specifiers = declaration2.specifiers ?? [];
|
|
4980
|
+
const named = specifiers.filter((s) => s.type === "ImportSpecifier");
|
|
4981
|
+
if (named.length === 1) {
|
|
4982
|
+
if (specifiers.length === 1) {
|
|
4983
|
+
out.overwrite(declaration2.start, declaration2.end, line);
|
|
4984
|
+
return;
|
|
4985
|
+
}
|
|
4986
|
+
const other = specifiers.find((s) => s.type !== "ImportSpecifier");
|
|
4987
|
+
const close = out.original.indexOf("}", named[0].end);
|
|
4988
|
+
out.remove(other.end, close + 1);
|
|
4989
|
+
out.appendRight(declaration2.end, `
|
|
4990
|
+
${line}`);
|
|
4991
|
+
return;
|
|
4992
|
+
}
|
|
4993
|
+
const index = named.indexOf(specifier);
|
|
4994
|
+
const previous = named[index - 1];
|
|
4995
|
+
const from = previous ? previous.end : specifier.start;
|
|
4996
|
+
const to = previous ? specifier.end : named[index + 1].start;
|
|
4997
|
+
out.remove(from, to);
|
|
4998
|
+
out.appendRight(declaration2.end, `
|
|
4999
|
+
${line}`);
|
|
5000
|
+
}
|
|
5001
|
+
function rewriteElement(out, usage, local, slug) {
|
|
5002
|
+
const opening = usage.element.openingElement;
|
|
5003
|
+
const name = opening.name;
|
|
5004
|
+
if (local === "BcmsBlocks") {
|
|
5005
|
+
out.overwrite(name.start, name.end, "BcmsDraftBridge");
|
|
5006
|
+
const closing = usage.element.closingElement;
|
|
5007
|
+
if (closing) {
|
|
5008
|
+
const closingName = closing.name;
|
|
5009
|
+
out.overwrite(closingName.start, closingName.end, "BcmsDraftBridge");
|
|
5010
|
+
}
|
|
5011
|
+
}
|
|
5012
|
+
const attributes = opening.attributes ?? [];
|
|
5013
|
+
const hasSlug = attributes.some((a) => a.name?.name === "slug");
|
|
5014
|
+
if (!hasSlug) out.appendLeft(name.end, ` slug=${slug}`);
|
|
5015
|
+
}
|
|
5016
|
+
|
|
4788
5017
|
// src/tolerant.ts
|
|
4789
5018
|
var escape = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4790
5019
|
var flexible = (literal) => new RegExp(escape(literal).split(" ").map(escape).join("\\s+"), "gu");
|
|
@@ -5214,10 +5443,10 @@ function scanExpression(code) {
|
|
|
5214
5443
|
for (const key of ["typeName", "left", "right", "id"]) nameOf2(node[key], skip);
|
|
5215
5444
|
return;
|
|
5216
5445
|
case "VariableDeclarator":
|
|
5217
|
-
|
|
5446
|
+
patternNames3(node.id, bound);
|
|
5218
5447
|
return;
|
|
5219
5448
|
case "CatchClause":
|
|
5220
|
-
|
|
5449
|
+
patternNames3(node.param, bound);
|
|
5221
5450
|
return;
|
|
5222
5451
|
default:
|
|
5223
5452
|
return;
|
|
@@ -5225,8 +5454,8 @@ function scanExpression(code) {
|
|
|
5225
5454
|
});
|
|
5226
5455
|
walkAst(ast, (node) => {
|
|
5227
5456
|
if (node.type === "ArrowFunctionExpression" || node.type === "FunctionExpression" || node.type === "FunctionDeclaration" || node.type === "ObjectMethod" || node.type === "ClassMethod") {
|
|
5228
|
-
for (const param of node.params ?? [])
|
|
5229
|
-
if (node.id)
|
|
5457
|
+
for (const param of node.params ?? []) patternNames3(param, bound);
|
|
5458
|
+
if (node.id) patternNames3(node.id, bound);
|
|
5230
5459
|
}
|
|
5231
5460
|
});
|
|
5232
5461
|
const free = /* @__PURE__ */ new Set();
|
|
@@ -5241,7 +5470,7 @@ function nameOf2(node, out) {
|
|
|
5241
5470
|
nameOf2(node.name, out);
|
|
5242
5471
|
}
|
|
5243
5472
|
}
|
|
5244
|
-
function
|
|
5473
|
+
function patternNames3(node, out) {
|
|
5245
5474
|
if (!node || typeof node !== "object") return;
|
|
5246
5475
|
const typed = node;
|
|
5247
5476
|
switch (typed.type) {
|
|
@@ -5250,17 +5479,17 @@ function patternNames2(node, out) {
|
|
|
5250
5479
|
return;
|
|
5251
5480
|
case "ObjectPattern":
|
|
5252
5481
|
for (const property of typed.properties ?? []) {
|
|
5253
|
-
|
|
5482
|
+
patternNames3(property.type === "RestElement" ? property.argument : property.value, out);
|
|
5254
5483
|
}
|
|
5255
5484
|
return;
|
|
5256
5485
|
case "ArrayPattern":
|
|
5257
|
-
for (const element of typed.elements ?? [])
|
|
5486
|
+
for (const element of typed.elements ?? []) patternNames3(element, out);
|
|
5258
5487
|
return;
|
|
5259
5488
|
case "AssignmentPattern":
|
|
5260
|
-
|
|
5489
|
+
patternNames3(typed.left, out);
|
|
5261
5490
|
return;
|
|
5262
5491
|
case "RestElement":
|
|
5263
|
-
|
|
5492
|
+
patternNames3(typed.argument, out);
|
|
5264
5493
|
return;
|
|
5265
5494
|
default:
|
|
5266
5495
|
return;
|
|
@@ -5282,9 +5511,9 @@ function moduleScope(parsed, content) {
|
|
|
5282
5511
|
const text = source.slice(start, end);
|
|
5283
5512
|
const names = /* @__PURE__ */ new Set();
|
|
5284
5513
|
if (kind === "VariableDeclaration") {
|
|
5285
|
-
for (const declarator of statement.declarations ?? [])
|
|
5514
|
+
for (const declarator of statement.declarations ?? []) patternNames3(declarator.id, names);
|
|
5286
5515
|
} else {
|
|
5287
|
-
|
|
5516
|
+
patternNames3(statement.id, names);
|
|
5288
5517
|
}
|
|
5289
5518
|
if (names.size === 0) continue;
|
|
5290
5519
|
const scanned = scanExpression(text.replace(/^(?:export\s+)?/, ""));
|
|
@@ -5295,7 +5524,7 @@ function moduleScope(parsed, content) {
|
|
|
5295
5524
|
}
|
|
5296
5525
|
|
|
5297
5526
|
// src/componentize.ts
|
|
5298
|
-
import
|
|
5527
|
+
import MagicString4 from "magic-string";
|
|
5299
5528
|
var SectionInvariantError = class extends Error {
|
|
5300
5529
|
code = "SECTION_INVARIANT";
|
|
5301
5530
|
constructor(message) {
|
|
@@ -5335,7 +5564,7 @@ async function componentizeSources(briefIn, planIn, sources, options) {
|
|
|
5335
5564
|
let registered = 0;
|
|
5336
5565
|
let planPending = 0;
|
|
5337
5566
|
let total = 0;
|
|
5338
|
-
const
|
|
5567
|
+
const pend2 = (route, groupKey, reason, message) => void pending.push({ route, groupKey, reason, ...message ? { message } : {} });
|
|
5339
5568
|
const brief = readBrief(briefIn);
|
|
5340
5569
|
const plan2 = readPlan(planIn);
|
|
5341
5570
|
const copy = options?.copy ?? "bind";
|
|
@@ -5355,7 +5584,7 @@ async function componentizeSources(briefIn, planIn, sources, options) {
|
|
|
5355
5584
|
continue;
|
|
5356
5585
|
}
|
|
5357
5586
|
total++;
|
|
5358
|
-
|
|
5587
|
+
pend2(page.route, section.groupKey, reason, message);
|
|
5359
5588
|
}
|
|
5360
5589
|
}
|
|
5361
5590
|
return {
|
|
@@ -5474,7 +5703,7 @@ async function componentizeSources(briefIn, planIn, sources, options) {
|
|
|
5474
5703
|
for (const section of sections2) {
|
|
5475
5704
|
const prefix = propPrefix(section);
|
|
5476
5705
|
if (prefix === null) {
|
|
5477
|
-
|
|
5706
|
+
pend2(
|
|
5478
5707
|
page.route,
|
|
5479
5708
|
section.groupKey,
|
|
5480
5709
|
"PROP_MAP_INCONSISTENT",
|
|
@@ -5491,13 +5720,13 @@ async function componentizeSources(briefIn, planIn, sources, options) {
|
|
|
5491
5720
|
const pageSlug = briefPage?.slug ?? slugify(page.route === "/" ? "home" : page.route);
|
|
5492
5721
|
if (registryBlocked) {
|
|
5493
5722
|
for (const section of usable) {
|
|
5494
|
-
|
|
5723
|
+
pend2(page.route, section.groupKey, "REGISTRY_CONFLICT", `${registryConflicts.join(", ")} is not a file this package wrote.`);
|
|
5495
5724
|
}
|
|
5496
5725
|
continue;
|
|
5497
5726
|
}
|
|
5498
5727
|
if (registryUnread) {
|
|
5499
5728
|
for (const section of usable) {
|
|
5500
|
-
|
|
5729
|
+
pend2(
|
|
5501
5730
|
page.route,
|
|
5502
5731
|
section.groupKey,
|
|
5503
5732
|
"REGISTRY_UNREADABLE",
|
|
@@ -5509,21 +5738,21 @@ async function componentizeSources(briefIn, planIn, sources, options) {
|
|
|
5509
5738
|
const snapshotPath = `${CONTENT_DIR}/${pageSlug}.json`;
|
|
5510
5739
|
if (!snapshotUsable(contents.get(snapshotPath))) {
|
|
5511
5740
|
for (const section of usable) {
|
|
5512
|
-
|
|
5741
|
+
pend2(page.route, section.groupKey, "SNAPSHOT_INVALID", `${snapshotPath} is not a JSON object.`);
|
|
5513
5742
|
}
|
|
5514
5743
|
continue;
|
|
5515
5744
|
}
|
|
5516
5745
|
const pageFiles = pageFilesFor(page.route, contents.keys());
|
|
5517
5746
|
if (pageFiles.length > 1) {
|
|
5518
5747
|
for (const section of usable) {
|
|
5519
|
-
|
|
5748
|
+
pend2(page.route, section.groupKey, "ROUTE_FILE_AMBIGUOUS", `${pageFiles.join(" and ")} both serve ${page.route}.`);
|
|
5520
5749
|
}
|
|
5521
5750
|
continue;
|
|
5522
5751
|
}
|
|
5523
5752
|
const file = pageFiles[0];
|
|
5524
5753
|
if (file === void 0) {
|
|
5525
5754
|
for (const section of usable) {
|
|
5526
|
-
|
|
5755
|
+
pend2(page.route, section.groupKey, "NOT_IN_SOURCE", `no page file found for ${page.route}.`);
|
|
5527
5756
|
}
|
|
5528
5757
|
continue;
|
|
5529
5758
|
}
|
|
@@ -5536,7 +5765,7 @@ async function componentizeSources(briefIn, planIn, sources, options) {
|
|
|
5536
5765
|
if (why === void 0) continue;
|
|
5537
5766
|
done.delete(section.groupKey);
|
|
5538
5767
|
stuck++;
|
|
5539
|
-
|
|
5768
|
+
pend2(page.route, section.groupKey, "SECTION_UNMIGRATABLE", why);
|
|
5540
5769
|
}
|
|
5541
5770
|
alreadyExtracted += done.size;
|
|
5542
5771
|
if (done.size + stuck === usable.length) {
|
|
@@ -5550,7 +5779,7 @@ async function componentizeSources(briefIn, planIn, sources, options) {
|
|
|
5550
5779
|
treeOf
|
|
5551
5780
|
);
|
|
5552
5781
|
for (const [groupKey, reason] of found.misses) {
|
|
5553
|
-
|
|
5782
|
+
pend2(page.route, groupKey, reason.code, reason.message);
|
|
5554
5783
|
}
|
|
5555
5784
|
const live = found.placed;
|
|
5556
5785
|
if (live.length === 0) continue;
|
|
@@ -5558,14 +5787,14 @@ async function componentizeSources(briefIn, planIn, sources, options) {
|
|
|
5558
5787
|
const dialect = dialectOf(file, content);
|
|
5559
5788
|
if (!dialect || !EXTRACTABLE.includes(dialect)) {
|
|
5560
5789
|
for (const placed of live) {
|
|
5561
|
-
|
|
5790
|
+
pend2(page.route, placed.section.groupKey, "DIALECT_UNSUPPORTED", `${file} is ${dialect ?? "not a template"}.`);
|
|
5562
5791
|
}
|
|
5563
5792
|
continue;
|
|
5564
5793
|
}
|
|
5565
5794
|
const tree = await treeOf(file);
|
|
5566
5795
|
if (!tree) {
|
|
5567
5796
|
for (const placed of live) {
|
|
5568
|
-
|
|
5797
|
+
pend2(page.route, placed.section.groupKey, "NOT_IN_SOURCE", `${file} did not parse.`);
|
|
5569
5798
|
}
|
|
5570
5799
|
continue;
|
|
5571
5800
|
}
|
|
@@ -5590,7 +5819,7 @@ async function componentizeSources(briefIn, planIn, sources, options) {
|
|
|
5590
5819
|
const alsoRendered = [...placed.byFile.keys()].filter((held2) => held2 !== file && importedFiles.has(held2));
|
|
5591
5820
|
if (elements && elements.length > 0) {
|
|
5592
5821
|
if (alsoRendered.length > 0) {
|
|
5593
|
-
|
|
5822
|
+
pend2(
|
|
5594
5823
|
page.route,
|
|
5595
5824
|
placed.section.groupKey,
|
|
5596
5825
|
"SECTION_ROOT_AMBIGUOUS",
|
|
@@ -5602,7 +5831,7 @@ async function componentizeSources(briefIn, planIn, sources, options) {
|
|
|
5602
5831
|
continue;
|
|
5603
5832
|
}
|
|
5604
5833
|
if (alsoRendered.length > 1) {
|
|
5605
|
-
|
|
5834
|
+
pend2(
|
|
5606
5835
|
page.route,
|
|
5607
5836
|
placed.section.groupKey,
|
|
5608
5837
|
"SECTION_ROOT_AMBIGUOUS",
|
|
@@ -5616,7 +5845,7 @@ async function componentizeSources(briefIn, planIn, sources, options) {
|
|
|
5616
5845
|
importedSections.push({ section: placed.section, path: only, call: held.local });
|
|
5617
5846
|
continue;
|
|
5618
5847
|
}
|
|
5619
|
-
|
|
5848
|
+
pend2(
|
|
5620
5849
|
page.route,
|
|
5621
5850
|
placed.section.groupKey,
|
|
5622
5851
|
"SECTION_ROOT_AMBIGUOUS",
|
|
@@ -5630,7 +5859,7 @@ async function componentizeSources(briefIn, planIn, sources, options) {
|
|
|
5630
5859
|
const component = resolveComponent(plan2, held.section);
|
|
5631
5860
|
const already = extractable.get(component.slug);
|
|
5632
5861
|
if (already !== void 0 && already.path !== held.path) {
|
|
5633
|
-
|
|
5862
|
+
pend2(
|
|
5634
5863
|
page.route,
|
|
5635
5864
|
held.section.groupKey,
|
|
5636
5865
|
"COMPONENT_CONFLICT",
|
|
@@ -5641,7 +5870,7 @@ async function componentizeSources(briefIn, planIn, sources, options) {
|
|
|
5641
5870
|
const binding = bindingOf(held.path);
|
|
5642
5871
|
const member = binding.kind === "namespace" ? namespaceMember(tree, binding.local) : void 0;
|
|
5643
5872
|
if (binding.kind === "namespace" && member === void 0) {
|
|
5644
|
-
|
|
5873
|
+
pend2(
|
|
5645
5874
|
page.route,
|
|
5646
5875
|
held.section.groupKey,
|
|
5647
5876
|
"SECTION_ROOT_AMBIGUOUS",
|
|
@@ -5667,7 +5896,7 @@ async function componentizeSources(briefIn, planIn, sources, options) {
|
|
|
5667
5896
|
for (const placed of placedHere) {
|
|
5668
5897
|
const root = lowestCommonAncestor(placed.elements);
|
|
5669
5898
|
if (!root) {
|
|
5670
|
-
|
|
5899
|
+
pend2(page.route, placed.section.groupKey, "SECTION_ROOT_AMBIGUOUS", "its fields sit under no single element.");
|
|
5671
5900
|
continue;
|
|
5672
5901
|
}
|
|
5673
5902
|
const component = resolveComponent(plan2, placed.section);
|
|
@@ -5689,7 +5918,7 @@ async function componentizeSources(briefIn, planIn, sources, options) {
|
|
|
5689
5918
|
}
|
|
5690
5919
|
}
|
|
5691
5920
|
for (const resolved of nested) {
|
|
5692
|
-
|
|
5921
|
+
pend2(page.route, resolved.section.groupKey, "SECTION_ROOT_AMBIGUOUS", "its root contains another section's.");
|
|
5693
5922
|
}
|
|
5694
5923
|
const candidates = roots.filter((r) => !nested.has(r)).sort((a, b) => a.root.start - b.root.start);
|
|
5695
5924
|
const placeable = [];
|
|
@@ -5697,7 +5926,7 @@ async function componentizeSources(briefIn, planIn, sources, options) {
|
|
|
5697
5926
|
const path = `${COMPONENT_DIR}/${resolved.name}.${dialect === "astro" ? "astro" : "tsx"}`;
|
|
5698
5927
|
const existing = contents.get(path);
|
|
5699
5928
|
if (existing !== void 0 && !isGenerated(existing) && !extractable.has(resolved.slug)) {
|
|
5700
|
-
|
|
5929
|
+
pend2(page.route, resolved.section.groupKey, "COMPONENT_CONFLICT", `${path} is not a file this package wrote.`);
|
|
5701
5930
|
continue;
|
|
5702
5931
|
}
|
|
5703
5932
|
if (extractable.has(resolved.slug)) {
|
|
@@ -5706,7 +5935,7 @@ async function componentizeSources(briefIn, planIn, sources, options) {
|
|
|
5706
5935
|
}
|
|
5707
5936
|
const built = componentFile(dialect, path, file, content, parsed, resolved, resolved.pathPrefix);
|
|
5708
5937
|
if (built.unconvertible.length > 0) {
|
|
5709
|
-
|
|
5938
|
+
pend2(
|
|
5710
5939
|
page.route,
|
|
5711
5940
|
resolved.section.groupKey,
|
|
5712
5941
|
"PROP_MAP_INCONSISTENT",
|
|
@@ -5715,7 +5944,7 @@ async function componentizeSources(briefIn, planIn, sources, options) {
|
|
|
5715
5944
|
continue;
|
|
5716
5945
|
}
|
|
5717
5946
|
if (built.duplicates.length > 0) {
|
|
5718
|
-
|
|
5947
|
+
pend2(
|
|
5719
5948
|
page.route,
|
|
5720
5949
|
resolved.section.groupKey,
|
|
5721
5950
|
"DUPLICATE_FIELD_BINDING",
|
|
@@ -5724,7 +5953,7 @@ async function componentizeSources(briefIn, planIn, sources, options) {
|
|
|
5724
5953
|
continue;
|
|
5725
5954
|
}
|
|
5726
5955
|
if (built.free.length > 0) {
|
|
5727
|
-
|
|
5956
|
+
pend2(
|
|
5728
5957
|
page.route,
|
|
5729
5958
|
resolved.section.groupKey,
|
|
5730
5959
|
"SECTION_FREE_IDENTIFIERS",
|
|
@@ -5750,7 +5979,7 @@ async function componentizeSources(briefIn, planIn, sources, options) {
|
|
|
5750
5979
|
const layout = runOf(known, parsed.roots, content);
|
|
5751
5980
|
if (layout === null) {
|
|
5752
5981
|
for (const resolved of placeable) {
|
|
5753
|
-
|
|
5982
|
+
pend2(page.route, resolved.section.groupKey, "SECTION_NOT_CONTIGUOUS", "its roots share no container.");
|
|
5754
5983
|
}
|
|
5755
5984
|
continue;
|
|
5756
5985
|
}
|
|
@@ -5773,7 +6002,7 @@ async function componentizeSources(briefIn, planIn, sources, options) {
|
|
|
5773
6002
|
const inline = placeable.filter((resolved) => !instanceOwned(resolved.section));
|
|
5774
6003
|
for (const resolved of placeable) {
|
|
5775
6004
|
if (inline.includes(resolved)) continue;
|
|
5776
|
-
|
|
6005
|
+
pend2(
|
|
5777
6006
|
page.route,
|
|
5778
6007
|
resolved.section.groupKey,
|
|
5779
6008
|
"INLINE_NEEDS_INSTANCE",
|
|
@@ -6221,7 +6450,7 @@ export default function ${resolved.name}({ blockId, bind, overrides, page }: Bcm
|
|
|
6221
6450
|
` };
|
|
6222
6451
|
}
|
|
6223
6452
|
function sectionMarkup(content, root, fieldName) {
|
|
6224
|
-
const slice = new
|
|
6453
|
+
const slice = new MagicString4(content.slice(root.start, root.end));
|
|
6225
6454
|
const at = (offset) => offset - root.start;
|
|
6226
6455
|
const stamps = root.attrs.filter((attr) => attr.name === "data-bcms-block");
|
|
6227
6456
|
if (stamps.length === 0) slice.appendLeft(at(root.attrInsertAt), " data-bcms-block={blockId}");
|
|
@@ -6420,7 +6649,7 @@ async function migrateSections(plan2, contents, written, fileSet, aliases) {
|
|
|
6420
6649
|
);
|
|
6421
6650
|
continue;
|
|
6422
6651
|
}
|
|
6423
|
-
const source = new
|
|
6652
|
+
const source = new MagicString4(content);
|
|
6424
6653
|
const { bound, duplicates } = wrapFieldAttrs(source, content, elements, (at) => at, "bcmsField");
|
|
6425
6654
|
if (duplicates.length > 0) {
|
|
6426
6655
|
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.`);
|
|
@@ -6697,7 +6926,7 @@ export default function Sections({ page }: { page: string }) {
|
|
|
6697
6926
|
function splicePageRun(file, content, dialect, parsed, placeable, pageSlug) {
|
|
6698
6927
|
const names = allocateNames(topLevelBindings(dialect, parsed, content), [{ key: "Sections", name: "Sections" }]);
|
|
6699
6928
|
const local = names.get("Sections");
|
|
6700
|
-
const source = new
|
|
6929
|
+
const source = new MagicString4(content);
|
|
6701
6930
|
const first = placeable[0].root;
|
|
6702
6931
|
const last = placeable[placeable.length - 1].root;
|
|
6703
6932
|
source.overwrite(first.start, last.end, `<${local} page="${pageSlug}" />`);
|
|
@@ -6717,7 +6946,7 @@ function splicePageInline(file, content, dialect, parsed, placeable, pageSlug, w
|
|
|
6717
6946
|
{ key: "pageSnapshot", name: "pageSnapshot" }
|
|
6718
6947
|
]);
|
|
6719
6948
|
const snapshot = names.get("pageSnapshot");
|
|
6720
|
-
const source = new
|
|
6949
|
+
const source = new MagicString4(content);
|
|
6721
6950
|
for (const resolved of placeable) {
|
|
6722
6951
|
const component = written.get(resolved.slug);
|
|
6723
6952
|
const tag = component.member ? `${names.get(resolved.slug)}.${component.member}` : names.get(resolved.slug);
|
|
@@ -6798,6 +7027,10 @@ function readComponentizePlan(value) {
|
|
|
6798
7027
|
}
|
|
6799
7028
|
|
|
6800
7029
|
// src/index.ts
|
|
7030
|
+
var UNPARSEABLE_DIALECT = {
|
|
7031
|
+
roots: [],
|
|
7032
|
+
error: { code: "DIALECT_UNSUPPORTED", message: "no parser for this extension" }
|
|
7033
|
+
};
|
|
6801
7034
|
var ConvertError = class extends Error {
|
|
6802
7035
|
constructor(code, message) {
|
|
6803
7036
|
super(message);
|
|
@@ -6879,7 +7112,7 @@ async function convertSources(briefIn, sources, options) {
|
|
|
6879
7112
|
}
|
|
6880
7113
|
const { located, unlocated } = locate(brief, sources, { stripTags: true });
|
|
6881
7114
|
const pending = [];
|
|
6882
|
-
const
|
|
7115
|
+
const pend2 = (target, reason, file, message) => {
|
|
6883
7116
|
pending.push({
|
|
6884
7117
|
route: target.route,
|
|
6885
7118
|
scope: target.scope,
|
|
@@ -6980,7 +7213,7 @@ async function convertSources(briefIn, sources, options) {
|
|
|
6980
7213
|
declaredRow(target, false, byHand);
|
|
6981
7214
|
continue;
|
|
6982
7215
|
}
|
|
6983
|
-
|
|
7216
|
+
pend2(target, miss.reason === "no-original" ? "NO_ORIGINAL" : "NOT_IN_SOURCE");
|
|
6984
7217
|
}
|
|
6985
7218
|
const byFile = /* @__PURE__ */ new Map();
|
|
6986
7219
|
for (const [key, hit] of locatedByKey) {
|
|
@@ -7037,12 +7270,12 @@ async function convertSources(briefIn, sources, options) {
|
|
|
7037
7270
|
const worst = outcomes.find((o) => o !== "rewritten" && o !== "declared");
|
|
7038
7271
|
if (worst) {
|
|
7039
7272
|
const file = hit.files[outcomes.indexOf(worst)];
|
|
7040
|
-
|
|
7273
|
+
pend2(target, worst, file);
|
|
7041
7274
|
} else if (outcomes.every((o) => o === "declared")) {
|
|
7042
7275
|
const witness = await witnessed(target);
|
|
7043
7276
|
const byHand = witness === null ? declaredByHand(target, hit.files) : null;
|
|
7044
7277
|
if (witness === null && byHand === null) {
|
|
7045
|
-
|
|
7278
|
+
pend2(
|
|
7046
7279
|
target,
|
|
7047
7280
|
"AMBIGUOUS_LITERAL",
|
|
7048
7281
|
hit.files[0],
|
|
@@ -7055,7 +7288,7 @@ async function convertSources(briefIn, sources, options) {
|
|
|
7055
7288
|
} else rewritten++;
|
|
7056
7289
|
}
|
|
7057
7290
|
for (const [file, splices] of [...componentEdits].sort(([a], [b]) => a.localeCompare(b))) {
|
|
7058
|
-
const source = new
|
|
7291
|
+
const source = new MagicString5(contents.get(file));
|
|
7059
7292
|
const seen = /* @__PURE__ */ new Set();
|
|
7060
7293
|
for (const splice of splices) {
|
|
7061
7294
|
const id = `${splice.start}:${splice.end}:${splice.text}`;
|
|
@@ -7076,6 +7309,28 @@ async function convertSources(briefIn, sources, options) {
|
|
|
7076
7309
|
}
|
|
7077
7310
|
const { helpers, helperUpgraded } = emitHelpers(dialects, contents, outFiles, options?.overwriteHelper);
|
|
7078
7311
|
emitStubs2(dialects, snapshotSlugs, contents, outFiles);
|
|
7312
|
+
const latest = new Map(contents);
|
|
7313
|
+
for (const file of outFiles) if (file.operation === "modify") latest.set(file.path, file.content);
|
|
7314
|
+
const canvasSources = [];
|
|
7315
|
+
for (const source of sources) {
|
|
7316
|
+
const content = latest.get(source.path) ?? source.content;
|
|
7317
|
+
if (!/\.[jt]sx$/i.test(source.path)) {
|
|
7318
|
+
canvasSources.push({ path: source.path, content });
|
|
7319
|
+
continue;
|
|
7320
|
+
}
|
|
7321
|
+
const parsed = content === source.content ? await parseOf(source.path) : await parseFile4("jsx", content);
|
|
7322
|
+
canvasSources.push({ path: source.path, content, ...parsed && !parsed.error ? { ast: parsed.ast } : {} });
|
|
7323
|
+
}
|
|
7324
|
+
const canvas = canvasBridge(
|
|
7325
|
+
canvasSources,
|
|
7326
|
+
new Map(brief.pages.map((page) => [page.route, page.slug]))
|
|
7327
|
+
);
|
|
7328
|
+
for (const [path, content] of canvas.edits) {
|
|
7329
|
+
const existing = outFiles.find((f) => f.path === path);
|
|
7330
|
+
if (existing) existing.content = content;
|
|
7331
|
+
else outFiles.push({ path, operation: "modify", content });
|
|
7332
|
+
}
|
|
7333
|
+
pending.push(...canvas.pending);
|
|
7079
7334
|
const receipt = assertReceipt({
|
|
7080
7335
|
briefDigest: digest,
|
|
7081
7336
|
paths: {
|
|
@@ -7092,6 +7347,7 @@ async function convertSources(briefIn, sources, options) {
|
|
|
7092
7347
|
helpers,
|
|
7093
7348
|
helperUpgraded,
|
|
7094
7349
|
dynamicBindings,
|
|
7350
|
+
canvas: { lane: canvas.lane, ...canvas.files.length > 0 ? { files: canvas.files } : {} },
|
|
7095
7351
|
notes: unreadableConfigs(sources).map(
|
|
7096
7352
|
(file) => ({
|
|
7097
7353
|
code: "TSCONFIG_UNREADABLE",
|
|
@@ -7124,6 +7380,30 @@ async function convertFile(file, content, targets, parsed, declared, llmFallback
|
|
|
7124
7380
|
};
|
|
7125
7381
|
if (pendingTargets.length === 0) return empty;
|
|
7126
7382
|
if (!dialect) {
|
|
7383
|
+
if (isTolerantDialect(file)) {
|
|
7384
|
+
const tolerantLiterals = [...new Set(pendingTargets.map((t) => t.literal))];
|
|
7385
|
+
const tolerantSites = findSitesTolerant(content, tolerantLiterals);
|
|
7386
|
+
if (tolerantSites.length > 0) {
|
|
7387
|
+
return plan(
|
|
7388
|
+
file,
|
|
7389
|
+
content,
|
|
7390
|
+
// Never read: `plan` forces `writing = "html"` at tier 2, and every other use of this
|
|
7391
|
+
// argument is behind `!parsed.error` or `tier !== 2`. Passing the writing dialect keeps
|
|
7392
|
+
// the parameter honest rather than widening its type for one caller.
|
|
7393
|
+
"html",
|
|
7394
|
+
pendingTargets,
|
|
7395
|
+
tolerantSites,
|
|
7396
|
+
void 0,
|
|
7397
|
+
// no parse, no import point: tier 2 declares, it does not read
|
|
7398
|
+
outcomes,
|
|
7399
|
+
2,
|
|
7400
|
+
UNPARSEABLE_DIALECT,
|
|
7401
|
+
drill,
|
|
7402
|
+
[]
|
|
7403
|
+
// no parse, so nothing in this file is already declared
|
|
7404
|
+
);
|
|
7405
|
+
}
|
|
7406
|
+
}
|
|
7127
7407
|
for (const target of pendingTargets) outcomes.set(target.key, "DIALECT_UNSUPPORTED");
|
|
7128
7408
|
return { ...empty, dialect: null, outcomes };
|
|
7129
7409
|
}
|
|
@@ -7182,7 +7462,7 @@ async function convertFile(file, content, targets, parsed, declared, llmFallback
|
|
|
7182
7462
|
}
|
|
7183
7463
|
async function plan(file, content, dialect, targets, sites, importAt, outcomes, tier, parsed, drill, declarations) {
|
|
7184
7464
|
const writing = tier === 2 ? "html" : dialect;
|
|
7185
|
-
const missing = tier
|
|
7465
|
+
const missing = tier !== 2 ? "NOT_IN_SOURCE" : parsed.error?.code === "DIALECT_UNSUPPORTED" ? "DIALECT_UNSUPPORTED" : "PARSE_ERROR";
|
|
7186
7466
|
const taken = parsed.error ? /* @__PURE__ */ new Set() : topLevelBindings(dialect, parsed, content);
|
|
7187
7467
|
const slugs = [...new Set(targets.map((t) => t.slug))].sort();
|
|
7188
7468
|
const preset = /* @__PURE__ */ new Map();
|
|
@@ -7604,6 +7884,7 @@ function emitStubs2(dialects, slugs, contents, out) {
|
|
|
7604
7884
|
}
|
|
7605
7885
|
}
|
|
7606
7886
|
export {
|
|
7887
|
+
ASTRO_LANE_PENDING,
|
|
7607
7888
|
COMPONENT_DIR,
|
|
7608
7889
|
CONTENT_DIR,
|
|
7609
7890
|
ConvertError,
|
|
@@ -7618,6 +7899,7 @@ export {
|
|
|
7618
7899
|
SKIP_DIRS,
|
|
7619
7900
|
SKIP_FILES,
|
|
7620
7901
|
SOURCE_EXTENSIONS,
|
|
7902
|
+
SSR_DRAFT_RECIPE,
|
|
7621
7903
|
STUB_CONTENT,
|
|
7622
7904
|
SectionInvariantError,
|
|
7623
7905
|
ValidationError,
|
|
@@ -7626,6 +7908,7 @@ export {
|
|
|
7626
7908
|
assertSections,
|
|
7627
7909
|
attrsFor,
|
|
7628
7910
|
briefDigest,
|
|
7911
|
+
canvasBridge,
|
|
7629
7912
|
carryFor,
|
|
7630
7913
|
componentizeSources,
|
|
7631
7914
|
convertSources,
|