@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.d.ts
CHANGED
|
@@ -175,6 +175,29 @@ interface PendingPath {
|
|
|
175
175
|
reason: PendingReason;
|
|
176
176
|
message?: string;
|
|
177
177
|
}
|
|
178
|
+
/**
|
|
179
|
+
* One path that was already DECLARED before this run — and how honestly.
|
|
180
|
+
*
|
|
181
|
+
* 🔴 `readsFromCms: false` IS THE INTERESTING ROW. An element carrying a `data-bcms-field` (or a
|
|
182
|
+
* `data-bcms-props` / `data-bcms-layout-field`) that gets its copy some other way — a hand-written
|
|
183
|
+
* declaration on markup that still renders a literal, or reads it from the project's own data
|
|
184
|
+
* module — is DECLARED: the publisher writes into it and the editor can click it. It is simply not
|
|
185
|
+
* reading through this package's helper. Reporting it `NO_ORIGINAL` said the opposite of the truth
|
|
186
|
+
* — a path nobody had declared — and sent an agent to convert an element that was already done.
|
|
187
|
+
*
|
|
188
|
+
* The row is informative; `paths.alreadyDeclared` is the number the arithmetic uses, and both
|
|
189
|
+
* shapes count towards it.
|
|
190
|
+
*/
|
|
191
|
+
interface DeclaredPath {
|
|
192
|
+
route: string;
|
|
193
|
+
scope: "page" | "layout";
|
|
194
|
+
path: string;
|
|
195
|
+
kind: string;
|
|
196
|
+
/** The file the declaration was found on. */
|
|
197
|
+
file?: string;
|
|
198
|
+
/** Does the declaring element read this value through the committed helper? */
|
|
199
|
+
readsFromCms: boolean;
|
|
200
|
+
}
|
|
178
201
|
/** One file the conversion touched, and the tier that produced it. A file is exactly one tier. */
|
|
179
202
|
interface ReceiptFile {
|
|
180
203
|
path: string;
|
|
@@ -187,6 +210,13 @@ interface ConversionReceipt {
|
|
|
187
210
|
declared: number;
|
|
188
211
|
rewritten: number;
|
|
189
212
|
alreadyDeclared: number;
|
|
213
|
+
/**
|
|
214
|
+
* One row per already-declared path, saying whether it reads from the CMS. @see DeclaredPath
|
|
215
|
+
*
|
|
216
|
+
* OPTIONAL, because a receipt written by an older version of this package does not carry it and
|
|
217
|
+
* a reader must not require it. The COUNT above is the contract; this is the detail.
|
|
218
|
+
*/
|
|
219
|
+
declarations?: DeclaredPath[];
|
|
190
220
|
pending: PendingPath[];
|
|
191
221
|
};
|
|
192
222
|
occurrences: {
|
|
@@ -205,6 +235,8 @@ interface ConversionReceipt {
|
|
|
205
235
|
* a reviewer can see what to look for, and so the scan has somewhere to disagree with.
|
|
206
236
|
*/
|
|
207
237
|
dynamicBindings: DynamicBinding[];
|
|
238
|
+
/** Facts about the repository that shaped this run. OPTIONAL: an older receipt carries none. */
|
|
239
|
+
notes?: ReceiptNote[];
|
|
208
240
|
}
|
|
209
241
|
/**
|
|
210
242
|
* One binding whose `data-bcms-field` is an expression, and where its value is written literally.
|
|
@@ -226,6 +258,19 @@ interface DynamicBinding {
|
|
|
226
258
|
literal: string;
|
|
227
259
|
}[];
|
|
228
260
|
}
|
|
261
|
+
/**
|
|
262
|
+
* Something about the REPOSITORY that changed what this run could see, named rather than swallowed.
|
|
263
|
+
*
|
|
264
|
+
* Not a path and not a refusal: a note is about a file the conversion READ, and it explains why a
|
|
265
|
+
* later reason may name the wrong cause. `TSCONFIG_UNREADABLE` is the first — a project config this
|
|
266
|
+
* package could not parse declares no `paths` aliases as far as it is concerned, so every aliased
|
|
267
|
+
* import resolves to nothing and the component behind it reads as one the page does not import.
|
|
268
|
+
*/
|
|
269
|
+
interface ReceiptNote {
|
|
270
|
+
code: "TSCONFIG_UNREADABLE";
|
|
271
|
+
file: string;
|
|
272
|
+
message: string;
|
|
273
|
+
}
|
|
229
274
|
/** Raised when the receipt does not add up. Never caught inside this package. */
|
|
230
275
|
declare class ReceiptInvariantError extends Error {
|
|
231
276
|
readonly code = "RECEIPT_INVARIANT";
|
|
@@ -653,7 +698,7 @@ declare function attrsFor(path: string, kind: string, props?: AttrBinding[], sco
|
|
|
653
698
|
declare function propsAttribute(props: AttrBinding[]): string;
|
|
654
699
|
|
|
655
700
|
/** Bumped whenever the emitted source changes. `isKnownHelper` upgrades anything older. */
|
|
656
|
-
declare const HELPER_VERSION =
|
|
701
|
+
declare const HELPER_VERSION = 4;
|
|
657
702
|
/** Where each dialect's helper lives. Null = this dialect reads nothing at build time. */
|
|
658
703
|
declare const HELPER_PATH: Record<Dialect, string | null>;
|
|
659
704
|
/** The directory the snapshots live in, at the repository root. */
|
|
@@ -712,6 +757,14 @@ declare function isSourceCandidate(path: string): boolean;
|
|
|
712
757
|
* specifiers only, which is a smaller conversion and a true one.
|
|
713
758
|
*/
|
|
714
759
|
declare function aliasesFrom(sources: SourceFile[]): Map<string, string[]>;
|
|
760
|
+
/**
|
|
761
|
+
* The project configs this package could not read, by path — a receipt NOTE, never a silent skip.
|
|
762
|
+
*
|
|
763
|
+
* A config that fails even the tolerant reader is one whose `paths` aliases nobody can resolve, so
|
|
764
|
+
* an aliased import reads as "not imported" and the refusal that follows names the wrong cause.
|
|
765
|
+
* The note says which file to look at. @see ConversionReceipt.notes
|
|
766
|
+
*/
|
|
767
|
+
declare function unreadableConfigs(sources: SourceFile[]): string[];
|
|
715
768
|
/** The repository path a specifier names, or null when it is not among the files we were given. */
|
|
716
769
|
declare function resolveSpecifier(fromFile: string, specifier: string, files: Set<string>, aliases: Map<string, string[]>): string | null;
|
|
717
770
|
/** What one local name was imported from, and WHICH export of it. */
|
|
@@ -926,6 +979,15 @@ interface ComponentizePlanSection {
|
|
|
926
979
|
componentId?: string;
|
|
927
980
|
proposedSlug?: string;
|
|
928
981
|
};
|
|
982
|
+
/**
|
|
983
|
+
* WHERE THIS PLACEMENT'S COPY LIVES — Phase B's one new field.
|
|
984
|
+
*
|
|
985
|
+
* `"instance"` says the server moved the group's values onto the instance's `overrides`, so the
|
|
986
|
+
* placement carries no `bind` and its elements declare the BLOCK lane. ABSENT MEANS `"bind"`:
|
|
987
|
+
* a plan written by a Phase A server says nothing here, and a codemod that required the field
|
|
988
|
+
* would refuse every plan older than itself. @see readPlan
|
|
989
|
+
*/
|
|
990
|
+
copy?: string;
|
|
929
991
|
/** The SERVER already decided it cannot place this one. Not this codemod's to extract. */
|
|
930
992
|
pending?: {
|
|
931
993
|
reason?: string;
|
|
@@ -963,15 +1025,29 @@ interface ComponentizePlan {
|
|
|
963
1025
|
* `COMPONENT_CONFLICT` a file already sits at the component's path and this package did
|
|
964
1026
|
* not write it.
|
|
965
1027
|
* `REGISTRY_CONFLICT` the same, for the registry or the sections library.
|
|
1028
|
+
* `REGISTRY_UNREADABLE` the registry IS ours, and its tables cannot be read back. It is
|
|
1029
|
+
* rewritten whole every run, so rebuilding it from what this run
|
|
1030
|
+
* happened to see would drop the components it already names.
|
|
966
1031
|
* `ROUTE_FILE_AMBIGUOUS` two files serve one route; the repository's own router has to
|
|
967
1032
|
* pick, and a codemod that picks differently edits a dead page.
|
|
968
1033
|
* `DIALECT_UNSUPPORTED` the page is svelte / vue / html; nothing is written for it.
|
|
969
1034
|
* `HELPER_CONFLICT` `src/bcms-content.ts` is a module this package did not write.
|
|
970
1035
|
* `SNAPSHOT_INVALID` `bcms-content/<page>.json` is not JSON this can add `blocks` to.
|
|
1036
|
+
* `COPY_MODE_MISMATCH` `--copy instance` was asked of a plan whose placements are all
|
|
1037
|
+
* still bound to page field groups. @see ComponentizeOptions
|
|
1038
|
+
* `PROP_MAP_INCONSISTENT` the plan's `fields[].path` is not `<one prefix> + fields[].key`,
|
|
1039
|
+
* so a page path cannot be turned into a prop key losslessly.
|
|
1040
|
+
* `DUPLICATE_FIELD_BINDING` one element in the section carries more than one
|
|
1041
|
+
* `data-bcms-field`, and there is no single address to rewrite.
|
|
1042
|
+
* `INLINE_NEEDS_INSTANCE` the page cannot be looped, and the in-place call site the codemod
|
|
1043
|
+
* would write carries no `blockId`/`overrides` — so a placement
|
|
1044
|
+
* whose copy has MOVED could neither render nor address it.
|
|
1045
|
+
* `SECTION_UNMIGRATABLE` an OLDER generated component at this section's path cannot be
|
|
1046
|
+
* upgraded to the current format. @see migrateSections
|
|
971
1047
|
* `NOT_IN_SOURCE` no file we were given renders the group, or the route names no
|
|
972
1048
|
* page file this package can find.
|
|
973
1049
|
*/
|
|
974
|
-
type SectionPendingReason = "SECTION_ROOT_AMBIGUOUS" | "SECTION_NOT_CONTIGUOUS" | "SECTION_FREE_IDENTIFIERS" | "COMPONENT_CONFLICT" | "REGISTRY_CONFLICT" | "ROUTE_FILE_AMBIGUOUS" | "DIALECT_UNSUPPORTED" | "HELPER_CONFLICT" | "SNAPSHOT_INVALID" | "NOT_IN_SOURCE";
|
|
1050
|
+
type SectionPendingReason = "SECTION_ROOT_AMBIGUOUS" | "SECTION_NOT_CONTIGUOUS" | "SECTION_FREE_IDENTIFIERS" | "COMPONENT_CONFLICT" | "REGISTRY_CONFLICT" | "REGISTRY_UNREADABLE" | "ROUTE_FILE_AMBIGUOUS" | "DIALECT_UNSUPPORTED" | "HELPER_CONFLICT" | "SNAPSHOT_INVALID" | "COPY_MODE_MISMATCH" | "PROP_MAP_INCONSISTENT" | "DUPLICATE_FIELD_BINDING" | "INLINE_NEEDS_INSTANCE" | "SECTION_UNMIGRATABLE" | "NOT_IN_SOURCE";
|
|
975
1051
|
interface PendingSection {
|
|
976
1052
|
route: string;
|
|
977
1053
|
groupKey: string;
|
|
@@ -998,11 +1074,31 @@ interface SectionsReceipt {
|
|
|
998
1074
|
* account for every section of the plan if "done earlier" is a bucket of its own.
|
|
999
1075
|
*/
|
|
1000
1076
|
alreadyExtracted: number;
|
|
1077
|
+
/**
|
|
1078
|
+
* The repository ALREADY has the component: the page imports it, and the registry now names it.
|
|
1079
|
+
*
|
|
1080
|
+
* 🔴 ITS OWN BUCKET, AND ORTHOGONAL TO PLACEMENT. Nothing was written for these — the file was
|
|
1081
|
+
* somebody's before this run and stays theirs — so counting them `extracted` would claim
|
|
1082
|
+
* authorship of bytes this package never produced, and counting them `pending` would say the CMS
|
|
1083
|
+
* has no component for a section it plainly does. Whether the page's call sites were then
|
|
1084
|
+
* replaced by the loop is a separate question, answered by `extracted` for the sections this run
|
|
1085
|
+
* did cut.
|
|
1086
|
+
*/
|
|
1087
|
+
registered: number;
|
|
1001
1088
|
pending: PendingSection[];
|
|
1002
1089
|
}
|
|
1003
1090
|
interface ComponentizeReceipt {
|
|
1004
1091
|
/** The plan this run was made against, carried so a receipt can be matched to one. */
|
|
1005
1092
|
planDigest: string;
|
|
1093
|
+
/**
|
|
1094
|
+
* Which copy model this run was asked for — the same word the server's receipt carries.
|
|
1095
|
+
*
|
|
1096
|
+
* It does NOT change the bytes of an extracted component: the lane an element declares is
|
|
1097
|
+
* decided at render time from the placement's own props, so one file serves both. What it
|
|
1098
|
+
* changes is what the run will accept — `"instance"` against a plan that moved no copy is
|
|
1099
|
+
* `COPY_MODE_MISMATCH` — and what the registry's shipped fallback binds. @see bcmsFields
|
|
1100
|
+
*/
|
|
1101
|
+
copy: "bind" | "instance";
|
|
1006
1102
|
sections: SectionsReceipt;
|
|
1007
1103
|
/** The component slugs this run wrote a file for, sorted. */
|
|
1008
1104
|
components: string[];
|
|
@@ -1013,6 +1109,15 @@ interface ComponentizeReceipt {
|
|
|
1013
1109
|
* that does not place them has done exactly what it was told.
|
|
1014
1110
|
*/
|
|
1015
1111
|
planPending: number;
|
|
1112
|
+
/**
|
|
1113
|
+
* Facts about the repository that shaped this run — the same list the conversion receipt carries.
|
|
1114
|
+
*
|
|
1115
|
+
* A tsconfig this package cannot read declares no `paths` aliases as far as it is concerned, and
|
|
1116
|
+
* THIS lane resolves a page's `<Hero />` through exactly those aliases: the section then reads as
|
|
1117
|
+
* one the page does not import, and `SECTION_ROOT_AMBIGUOUS` names the wrong cause. The note is
|
|
1118
|
+
* what points at the real one.
|
|
1119
|
+
*/
|
|
1120
|
+
notes?: ReceiptNote[];
|
|
1016
1121
|
}
|
|
1017
1122
|
/** Raised when the receipt does not account for every section. Never caught inside this package. */
|
|
1018
1123
|
declare class SectionInvariantError extends Error {
|
|
@@ -1030,9 +1135,27 @@ declare function assertSections(total: number, sections: SectionsReceipt): Secti
|
|
|
1030
1135
|
interface ComponentizeOptions {
|
|
1031
1136
|
/** Replace a helper module this package did not write, instead of refusing. The CLI's flag. */
|
|
1032
1137
|
overwriteHelper?: boolean;
|
|
1138
|
+
/**
|
|
1139
|
+
* WHICH COPY MODEL THE OPERATOR MEANT — `"bind"` (Phase A, the default) or `"instance"`.
|
|
1140
|
+
*
|
|
1141
|
+
* 🔴 AN ASSERTION, NOT A SWITCH. The extracted files are identical either way, so this flag
|
|
1142
|
+
* exists to catch the order-of-operations mistake that would otherwise be silent: running
|
|
1143
|
+
* `--copy instance` in the repository BEFORE the server has moved the copy leaves a tree that
|
|
1144
|
+
* looks converted and renders every section out of page fields the dashboard is about to hide.
|
|
1145
|
+
* A plan whose placements are all still bound says so, and the run refuses by name.
|
|
1146
|
+
*/
|
|
1147
|
+
copy?: "bind" | "instance";
|
|
1033
1148
|
}
|
|
1034
|
-
/**
|
|
1035
|
-
|
|
1149
|
+
/**
|
|
1150
|
+
* The marker that says a file is OURS — and WHICH FORMAT it is in.
|
|
1151
|
+
*
|
|
1152
|
+
* 🔴 THE VERSION IS LOAD-BEARING, NOT DECORATION. Idempotence is decided on this marker, so a v1
|
|
1153
|
+
* component (0.3: page-path bindings, no render-time lane) read as "already ours, nothing to do"
|
|
1154
|
+
* was skipped forever — every repository converted before Phase B stayed on the page lane, and
|
|
1155
|
+
* `--copy instance` there reported five sections done and moved no address at all. A format change
|
|
1156
|
+
* bumps the version, and anything older is MIGRATED before it is called done. @see migrateSections
|
|
1157
|
+
*/
|
|
1158
|
+
declare const SECTION_MARKER = "// @bettercms-ai/convert section v2";
|
|
1036
1159
|
/** The same, for the two modules that hold the registry rather than a section. */
|
|
1037
1160
|
declare const REGISTRY_MARKER = "// @bettercms-ai/convert sections v1";
|
|
1038
1161
|
/** Where the generated components live. Fixed, so a second run knows its own output on sight. */
|
|
@@ -1126,4 +1249,4 @@ declare function convertSources(briefIn: Brief, sources: SourceFile[], options?:
|
|
|
1126
1249
|
receipt: ConversionReceipt;
|
|
1127
1250
|
}>;
|
|
1128
1251
|
|
|
1129
|
-
export { type AstNode, type AttrBinding, type Brief, type BriefPage, type BriefPath, COMPONENT_DIR, CONTENT_DIR, 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 Dialect, type DynamicBinding, type FileDeclaration, type FindSitesResult, HELPER_PATH, HELPER_VERSION, type ImportedFrom, type LlmFallback, type LocatedPath, type Node, PARSE_FILE, type ParsedFile, type ParserError, type PathLocator, type PendingPath, type PendingReason, type PendingSection, type PlanFile, REGISTRY_MARKER, type ReceiptFile, ReceiptInvariantError, type Rewrite, SECTIONS_LIB, SECTION_MARKER, SKIP_DIRS, SKIP_FILES, SOURCE_EXTENSIONS, STUB_CONTENT, SectionInvariantError, type SectionPendingReason, type SectionsReceipt, type Site, type SiteWhere, type SourceFile, type Splice, type TargetIdentity, type UnlocatedPath, type ValidationCode, ValidationError, aliasesFrom, assertReceipt, assertSections, attrsFor, briefDigest, carryFor, componentizeSources, convertSources, convertedHere, coverageOf, declaringElements, dialectOf, exportedFunction, findSites, findSitesTolerant, flat, helperSource, importsIn, isDynamicRoute, isKnownHelper, isSourceCandidate, locate, overlapping, pageFilesFor, parseFile, propsAttribute, readBrief, readComponentizePlan, readDeclarations, readExpr, readPlan, relativeImport, resolveSpecifier, rewriteFile, routeOfFile, stripTags, walkAst };
|
|
1252
|
+
export { type AstNode, type AttrBinding, type Brief, type BriefPage, type BriefPath, COMPONENT_DIR, CONTENT_DIR, 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 DeclaredPath, type Dialect, type DynamicBinding, type FileDeclaration, type FindSitesResult, HELPER_PATH, HELPER_VERSION, type ImportedFrom, type LlmFallback, type LocatedPath, type Node, PARSE_FILE, type ParsedFile, type ParserError, type PathLocator, 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, STUB_CONTENT, SectionInvariantError, type SectionPendingReason, type SectionsReceipt, type Site, type SiteWhere, type SourceFile, type Splice, type TargetIdentity, type UnlocatedPath, type ValidationCode, ValidationError, aliasesFrom, assertReceipt, assertSections, attrsFor, briefDigest, carryFor, componentizeSources, convertSources, convertedHere, coverageOf, declaringElements, dialectOf, exportedFunction, findSites, findSitesTolerant, flat, helperSource, importsIn, isDynamicRoute, isKnownHelper, isSourceCandidate, locate, overlapping, pageFilesFor, parseFile, propsAttribute, readBrief, readComponentizePlan, readDeclarations, readExpr, readPlan, relativeImport, resolveSpecifier, rewriteFile, routeOfFile, stripTags, unreadableConfigs, walkAst };
|