@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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,342 @@
|
|
|
1
1
|
# @bettercms-ai/convert
|
|
2
2
|
|
|
3
|
+
## 0.4.0
|
|
4
|
+
|
|
5
|
+
The workspace lockfile records this version: a lock still naming 0.3.0 makes a frozen install
|
|
6
|
+
reject the checkout, which is what broke the migrator image on #616.
|
|
7
|
+
|
|
8
|
+
### Minor Changes
|
|
9
|
+
|
|
10
|
+
- Componentize Phase B: an extracted section declares the lane its copy actually lives in.
|
|
11
|
+
|
|
12
|
+
An element carries exactly ONE `data-bcms-field`, and which address it names is now decided
|
|
13
|
+
when the component RENDERS rather than when the codemod ran. A placement bound to a page
|
|
14
|
+
field group declares the page path, as before. A placement that owns its copy — `overrides`
|
|
15
|
+
and no `bind` — declares the block lane, `<blockId>__overrides.<propKey>`, which is where
|
|
16
|
+
the editor commits an instance's own value.
|
|
17
|
+
|
|
18
|
+
🔴 The extracted files are BYTE-IDENTICAL in both copy models. That is the point: moving a
|
|
19
|
+
page's copy onto its instances is a data change on the server, not a second cut of the
|
|
20
|
+
repository, and one component renders a Phase A placement on one page and a Phase B
|
|
21
|
+
instance on the next. Each of P2's bindings is wrapped in the placement's own chooser,
|
|
22
|
+
`bcmsFields({ blockId, bind, overrides }, "<prefix>")`, so the page path is still verbatim
|
|
23
|
+
in the file. The prefix that turns a page path into a prop key comes from the plan's
|
|
24
|
+
`fields[].key`/`path` map, not from the group key.
|
|
25
|
+
|
|
26
|
+
The root's `data-bcms-block` is now REPLACED when the repository already wrote one, instead
|
|
27
|
+
of a second being appended beside it — two instances claiming one element.
|
|
28
|
+
|
|
29
|
+
- `--copy bind|instance` (default `bind`), and the `COPY_MODE_MISMATCH` refusal.
|
|
30
|
+
|
|
31
|
+
An assertion rather than a switch, since the output does not depend on it. It catches the
|
|
32
|
+
one order-of-operations mistake that would otherwise be silent: `--copy instance` run in the
|
|
33
|
+
repository before the server has moved the copy leaves a tree that looks converted and
|
|
34
|
+
renders every section out of page fields the dashboard is about to hide. A plan whose
|
|
35
|
+
placements all still carry `bind` refuses every section by name, with nothing written. The
|
|
36
|
+
receipt carries `copy`, the same word the server's receipt uses.
|
|
37
|
+
|
|
38
|
+
The registry's shipped fallback now omits `bind` for an instance-owned placement, for the
|
|
39
|
+
same reason: binding it would make a fresh clone render a section out of hidden fields.
|
|
40
|
+
|
|
41
|
+
- The extracted-component format is VERSIONED (`section v2`), and older ones are upgraded.
|
|
42
|
+
|
|
43
|
+
🔴 Without this Phase B never reaches a repository that already ran 0.3. Completion is
|
|
44
|
+
decided on the marker and a v1 component carries one, so every already-componentized page
|
|
45
|
+
was reported done, its components kept their page-path bindings, and `--copy instance`
|
|
46
|
+
there placed five sections while moving no address at all. Each v1 component is now brought
|
|
47
|
+
to the current format in place — every `data-bcms-field` wrapped in the chooser, the chooser
|
|
48
|
+
declared and imported, the marker restamped — once, before any page is called done. A
|
|
49
|
+
component the plan says nothing about is left alone; one that cannot be upgraded is
|
|
50
|
+
`SECTION_UNMIGRATABLE` rather than a silent skip.
|
|
51
|
+
|
|
52
|
+
- An already-componentized page's shipped placements are restated from the current plan.
|
|
53
|
+
|
|
54
|
+
A bind run followed by an instance run kept `bind` on every placement in the registry's
|
|
55
|
+
fallback table, so a fresh clone rendered sections out of page fields the dashboard had
|
|
56
|
+
already hidden — and the reverse transition never put `bind` back. The `bind` is now
|
|
57
|
+
restated on every run, in the order the registry already holds; nothing is extracted, and
|
|
58
|
+
the registry is rewritten only when those bytes actually change. Repeated placements of one
|
|
59
|
+
component — two groups sharing a `shapeHash`, which is what `shapeHash` is FOR — are matched
|
|
60
|
+
POSITIONALLY, registry order against plan order per slug: keying them by slug collapsed them,
|
|
61
|
+
so a page with one group still bound and another moved wrote both from the second.
|
|
62
|
+
|
|
63
|
+
- The v1 → v2 upgrade reuses names the file already has, or refuses.
|
|
64
|
+
|
|
65
|
+
These files have been sitting in somebody's repository. Blindly adding
|
|
66
|
+
`import { bcmsFields }` and `const bcmsField = …` to a module that already declares either
|
|
67
|
+
produced a duplicate binding — a file that does not compile — reported as a successful
|
|
68
|
+
upgrade. An import of the helper's own `bcmsFields` is reused under whatever local name it
|
|
69
|
+
has; a `bcmsField` the file demonstrably builds from our helper is left alone and only its
|
|
70
|
+
bindings are wrapped; anything else is `SECTION_UNMIGRATABLE`.
|
|
71
|
+
|
|
72
|
+
🔴 A NAME IS NOT A PROVENANCE. Reusing anything *called* `bcmsFields` trusted an import from
|
|
73
|
+
any module at all — `import { bcmsFields } from "./their-utils"` is a different function with
|
|
74
|
+
the same name — and any coexisting `bcmsField` vouched for itself. The specifier must RESOLVE
|
|
75
|
+
to the helper module this package writes (through the project's own aliases), and `bcmsField`
|
|
76
|
+
must be declared as `const bcmsField = <that exact local>(…)`, read structurally off the
|
|
77
|
+
module's own top-level AST.
|
|
78
|
+
|
|
79
|
+
- Which group an upgraded component was cut from is read off the FILE, not off the plan.
|
|
80
|
+
|
|
81
|
+
One component serves every page whose group has its shape, and its markup carries the paths of
|
|
82
|
+
the one page it was cut FROM. Taking the plan's first placement for that slug gave a component
|
|
83
|
+
reading `hero.*` the prefix `landingHero.` as soon as that placement came first: every wrapped
|
|
84
|
+
address matched nothing, the block lane never fired, and the file was stamped current for good.
|
|
85
|
+
The candidate prefixes are matched against the addresses the file actually declares, and a slug
|
|
86
|
+
whose placements cannot be told apart that way is `SECTION_UNMIGRATABLE`. Server-pending
|
|
87
|
+
placements count as prefix EVIDENCE — which group the bytes were cut from is a fact about the
|
|
88
|
+
past, and a refused placement may be the only thing that explains them — while still being
|
|
89
|
+
excluded from placement and copy-mode decisions.
|
|
90
|
+
|
|
91
|
+
- Fresh extraction allocates collision-free names for the chooser and its import.
|
|
92
|
+
|
|
93
|
+
The markup being cut is the PAGE's, and a page is free to have declared `bcmsField` itself.
|
|
94
|
+
Declaring ours unconditionally — and listing the name as `provided`, so `carryFor` neither
|
|
95
|
+
carried the page's declaration nor reported it free — silently rebound every use of theirs
|
|
96
|
+
inside the extracted section to our chooser. Both names now go through the same allocator as
|
|
97
|
+
every other identifier this codemod introduces, reserved against the module's bindings AND
|
|
98
|
+
against every identifier-shaped token in the bytes being lifted: in a TSX page that declaration
|
|
99
|
+
lives inside the component function, where a module-level check cannot see it, so the capture
|
|
100
|
+
happened anyway. ALL of the names, not the two base ones — reserving only `bcmsField` sent the
|
|
101
|
+
allocator to `bcmsField_1`, which a section referencing both also uses, and the capture simply
|
|
102
|
+
moved one suffix along.
|
|
103
|
+
|
|
104
|
+
- The prop map is checked before this repository is consulted at all, AND against the
|
|
105
|
+
addresses it has to convert.
|
|
106
|
+
|
|
107
|
+
Checking it where the markup is cut let an ALREADY-componentized page bypass it: a later plan
|
|
108
|
+
spelling `{ key: "title", path: "hero.heading" }` was reported complete while the component it
|
|
109
|
+
names went on addressing `overrides.heading`, a key the instance does not hold. The map is a
|
|
110
|
+
property of the plan, so it is now answered from the plan, before completion is even asked.
|
|
111
|
+
|
|
112
|
+
🔴 AND A MAP CAN BE INTERNALLY CONSISTENT AND STILL WRONG ABOUT THE SECTION.
|
|
113
|
+
`{ groupKey: "hero", key: "title", path: "other.title" }` derives one clean prefix, `other.`,
|
|
114
|
+
which matches nothing the markup declares — so every element quietly stayed on the page lane
|
|
115
|
+
while the run reported the section done. The derived prefix is now verified against every
|
|
116
|
+
address the section actually declares — and a field address this cannot read statically
|
|
117
|
+
(`{computedPath}`, `{prefix + ".title"}`) counts as one it cannot convert, because contributing
|
|
118
|
+
nothing to the check is how such an element stayed on the page lane at render time. A
|
|
119
|
+
repeater's template literal is readable and is not affected.
|
|
120
|
+
|
|
121
|
+
- Migration refusals are about ONE component, and a `.tsx` chooser is found where it lives.
|
|
122
|
+
|
|
123
|
+
Keyed by slug alone, an unmigratable `Hero.astro` marked the perfectly good `Hero.tsx`
|
|
124
|
+
unmigratable in a mixed repository — a page refused for a fact about a file its route does not
|
|
125
|
+
render. And a `.tsx` component declares its chooser INSIDE the function, which is where this
|
|
126
|
+
package puts it: a program-level binding check saw nothing there and inserted a second
|
|
127
|
+
`const bcmsField` into the same block. Both scopes are read; the refusal is keyed by dialect
|
|
128
|
+
and slug.
|
|
129
|
+
|
|
130
|
+
Reading a registry back also resolves its extensionless imports to a file its OWN dialect can
|
|
131
|
+
render. Two dialects share a component name whenever they share a slug, and a general resolver
|
|
132
|
+
— whose extension order is this package's, not the project's — bound the `.tsx` registry's
|
|
133
|
+
`hero` to the astro file, after which everything was about the wrong component.
|
|
134
|
+
|
|
135
|
+
- `readRegistry` validates each `SHIPPED` row instead of trusting the array.
|
|
136
|
+
|
|
137
|
+
`[null]` is valid JSON, and the first thing to read it back dereferenced `placement.slug` and
|
|
138
|
+
threw a `TypeError` out of a pure function that names nothing. A row must be an object with a
|
|
139
|
+
string `slug` and, when present, string `bind`/`blockId`; anything else is `REGISTRY_UNREADABLE`,
|
|
140
|
+
which is what every other unreadable row in that file already was — as is a `REGISTRY` naming
|
|
141
|
+
no components (this module is only emitted when there is at least one, so a table with no rows
|
|
142
|
+
is one whose rows were lost) and a `SHIPPED` slug the `REGISTRY` does not name. An EMPTY
|
|
143
|
+
`SHIPPED` is still read: a run whose only page was `SECTION_NOT_CONTIGUOUS` writes the
|
|
144
|
+
components and no order at all, so refusing that would strand the repository. A single page
|
|
145
|
+
whose placement list is EMPTY is refused, though — `every` is vacuously true of it, and the
|
|
146
|
+
fallback exists precisely so a build with no CMS blocks renders the bands the repository ships.
|
|
147
|
+
|
|
148
|
+
- Two more edges: a root carrying SEVERAL `data-bcms-block` attributes keeps exactly one (only
|
|
149
|
+
the first was being rewritten, so the extracted element still came out with two), and a v1
|
|
150
|
+
component with no `data-bcms-field` at all is upgraded on its marker alone rather than refused
|
|
151
|
+
for an ambiguity it cannot have — every candidate prefix matched it vacuously.
|
|
152
|
+
|
|
153
|
+
### Patch Changes
|
|
154
|
+
|
|
155
|
+
- No synthesized group fallback: an extracted component reads `bcmsSection(page, bind, overrides)`.
|
|
156
|
+
|
|
157
|
+
🔴 `bind ?? "<groupKey>"` belonged to Phase A, where every placement was bound and the default
|
|
158
|
+
could only restate what the placement already said. Under Phase B a placement that OWNS its
|
|
159
|
+
copy carries no bind on purpose — and the fallback put the page's field group straight back
|
|
160
|
+
underneath it, so a key the instance had not been given rendered the page's stale copy out of
|
|
161
|
+
fields the dashboard has already hidden. A placement that names no group has none, and every
|
|
162
|
+
`bcms(...)` then renders what the template shipped with. The v1 → v2 upgrade rewrites the old
|
|
163
|
+
form away rather than stamping it as current — through the module's own AST, by offset and
|
|
164
|
+
through the file's OWN local name for the export, because an exact-text rewrite missed a call a
|
|
165
|
+
formatter had reflowed and a name-matched one missed an aliased
|
|
166
|
+
`import { bcmsSection as sectionCopy }`; the marker then said the question had been settled. A
|
|
167
|
+
module with no readable program is refused, and so is one where nothing was rewritten while a
|
|
168
|
+
`??` fallback sits in a call this cannot identify as the helper's.
|
|
169
|
+
|
|
170
|
+
- Only provably safe field expressions count as an address.
|
|
171
|
+
|
|
172
|
+
Starting with a quote is not a proof: `{"hero.title".replace("hero", "footer")}` names
|
|
173
|
+
`footer.title` at render time, and reading its first characters as the address accepted a
|
|
174
|
+
section whose element then quietly stayed on the page lane — the failure the check exists to
|
|
175
|
+
catch, reached through the check itself. Two shapes are provable: a string literal and NOTHING
|
|
176
|
+
else, and a template literal whose every interpolation is an INDEX (`[${i}]`, what a repeater
|
|
177
|
+
writes, which cannot change the prefix). Everything else — `{computedPath}`, `{"a" + x}`,
|
|
178
|
+
`` {`${prefix}.title`} `` — is opaque, and opaque is `PROP_MAP_INCONSISTENT`.
|
|
179
|
+
|
|
180
|
+
- `COPY_MODE_MISMATCH` ignores sections the SERVER already refused.
|
|
181
|
+
|
|
182
|
+
A `copy: "instance"` section carrying `pending` is not a placement this run would make, but it
|
|
183
|
+
counted as evidence that the plan had moved its copy — so `--copy instance` passed the gate on
|
|
184
|
+
a plan whose only instance-owned section was server-pending, and the run wrote the BIND
|
|
185
|
+
conversion for the sections it could act on. Which is the exact tree the flag exists to stop.
|
|
186
|
+
|
|
187
|
+
- A bound address is RE-BASED onto its own placement's group.
|
|
188
|
+
|
|
189
|
+
The markup is cut from ONE page and carries that page's paths, and the whole point of
|
|
190
|
+
`shapeHash` is that a second page renders the same component bound to a group of its own.
|
|
191
|
+
Handing the embedded path through unchanged declared `hero.title` on a page whose field is
|
|
192
|
+
`landingHero.title` — an address that page does not have, so the editor wrote into nothing
|
|
193
|
+
and the meter counted a binding that was never there.
|
|
194
|
+
|
|
195
|
+
- `INLINE_NEEDS_INSTANCE`: a page that cannot be looped cannot own its copy.
|
|
196
|
+
|
|
197
|
+
The in-place call site — `<Hero bind="hero" page={pageSnapshot("home")} />` — is written by
|
|
198
|
+
the codemod from the plan and carries no `blockId` and no `overrides`; it never consults the
|
|
199
|
+
CMS's blocks at all, which is exactly what makes the inline form componentisation *without*
|
|
200
|
+
the page builder. Emitting it for a placement whose copy has MOVED rendered the page's field
|
|
201
|
+
group and declared the page lane for an address that no longer holds anything. The component
|
|
202
|
+
is still written; the page is left as its author wrote it.
|
|
203
|
+
|
|
204
|
+
- `HELPER_CONFLICT` and `COPY_MODE_MISMATCH` now return an EMPTY file list.
|
|
205
|
+
|
|
206
|
+
On an already-componentized repository the registry read-back seeds the component table, so
|
|
207
|
+
the emit fired even though every section was pending: the helper was upgraded, the sections
|
|
208
|
+
library rebuilt and the registry rewritten for a run the operator was told did nothing. With
|
|
209
|
+
a hand-edited helper it was worse — the v1 → v2 upgrade rewrote components to
|
|
210
|
+
`import { bcmsFields }` from a module that does not export it. A refusal that modifies the
|
|
211
|
+
tree is not a refusal, so both return before reading or emitting any generated artefact.
|
|
212
|
+
|
|
213
|
+
- Two new refusals where the codemod used to guess.
|
|
214
|
+
|
|
215
|
+
`PROP_MAP_INCONSISTENT`: a plan whose `fields[].path` is not one common prefix plus its
|
|
216
|
+
`key` — `{ path: "hero.heading", key: "title" }` — fell back to `<groupKey>.`, so the block
|
|
217
|
+
address became `overrides.heading` for a prop the instance holds as `title`, and every edit
|
|
218
|
+
went to a key nothing reads. `DUPLICATE_FIELD_BINDING`: an element already carrying two
|
|
219
|
+
`data-bcms-field` had both rewritten, leaving the extracted element still violating the
|
|
220
|
+
one-attribute invariant.
|
|
221
|
+
|
|
222
|
+
- Helper v4: `bcmsSection` merges OVERRIDES-FIRST, per key.
|
|
223
|
+
|
|
224
|
+
`overrides[key] ?? group[key]` rather than "overrides, or else the group". The case that
|
|
225
|
+
matters is the one in between — a section whose copy is half moved — where a key the
|
|
226
|
+
instance does not hold has to keep rendering the page's value rather than nothing. `??`,
|
|
227
|
+
never `||`: an empty string is a value an editor typed. A table prop stored in the shape
|
|
228
|
+
the dashboard edits (`{ rows: [...] }`) is unwrapped to its list, so `bcmsRows` sees an
|
|
229
|
+
array instead of falling back to the rows the template shipped with.
|
|
230
|
+
|
|
231
|
+
New export `bcmsFields`, which is what an extracted section's bindings read through. An
|
|
232
|
+
existing v1–v3 helper is upgraded in place as usual; one this package did not write is
|
|
233
|
+
still `HELPER_CONFLICT`.
|
|
234
|
+
|
|
235
|
+
## 0.3.0
|
|
236
|
+
|
|
237
|
+
### Minor Changes
|
|
238
|
+
|
|
239
|
+
- Componentize: a section the repository has ALREADY componentised is REGISTERED, not refused.
|
|
240
|
+
|
|
241
|
+
A page whose `index.astro` imports `Hero.astro` and renders `<Hero />` has made the cut
|
|
242
|
+
this lane exists to make. There is nothing in the page to extract, and refusing left the
|
|
243
|
+
CMS naming a section no component in the repo rendered. The existing file is now
|
|
244
|
+
registered under the section's slug — the registry imports it where it lives — and the
|
|
245
|
+
page keeps its call site. It has to be ONE file, one the page IMPORTS, and one the
|
|
246
|
+
registry's own dialect can render; anything else is `SECTION_ROOT_AMBIGUOUS` with the
|
|
247
|
+
reason named.
|
|
248
|
+
|
|
249
|
+
New receipt bucket, in the arithmetic:
|
|
250
|
+
`plan sections === extracted + inlineOnly + alreadyExtracted + registered + pending.length`.
|
|
251
|
+
`registered` is orthogonal to placement — nothing was written for those sections, so
|
|
252
|
+
counting them `extracted` would claim authorship of bytes this package never produced.
|
|
253
|
+
|
|
254
|
+
A page with a registered section is ALL OR NOTHING: `<Sections>` renders every placement
|
|
255
|
+
the page has, so the run is replaced by one `<Sections page="…" />` only when every
|
|
256
|
+
section of the page resolved and every call site is a bare tag in that run. Otherwise the
|
|
257
|
+
call sites stay exactly where their author put them — replacing a subset would render a
|
|
258
|
+
registered section twice, once in place and once out of the registry. The registry
|
|
259
|
+
read-back also resolves ANY relative import now, not only `./Name`: without that a second
|
|
260
|
+
run dropped the registered entry and the page rendered blank.
|
|
261
|
+
|
|
262
|
+
- Repeaters: a row that holds a list of its own becomes a loop inside the loop.
|
|
263
|
+
|
|
264
|
+
`cards[i].items[j].label` is emitted as a nested `.map` in astro and jsx, reading its rows
|
|
265
|
+
off the ROW — `bcmsRows(card, "items", [ …this card's own items… ])` — so a bullet added in
|
|
266
|
+
the dashboard renders. The committed helper needed nothing: `bcmsRows` already passes a
|
|
267
|
+
nested array through untouched and `bcmsSection` hands a section its group whole.
|
|
268
|
+
|
|
269
|
+
The bindings are `` data-bcms-field={`cards[${i}].items[${j}].label`} `` and the shape is
|
|
270
|
+
`cards[*].items[*].label`; the loop now REPORTS the bindings it wrote, both loop variables
|
|
271
|
+
included, instead of the caller re-deriving a literal the file does not contain. ONE level
|
|
272
|
+
of nesting — what the plan mints and what the component prop validator accepts — so a leaf
|
|
273
|
+
indexed twice stays `REPEATER_FIXED_LENGTH`, and so does the html dialect, which has no
|
|
274
|
+
loop form at all.
|
|
275
|
+
|
|
276
|
+
- A hand-written declaration counts as `alreadyDeclared`, never `NO_ORIGINAL`.
|
|
277
|
+
|
|
278
|
+
An element that names the target (`data-bcms-field`, `data-bcms-layout-field`, an address
|
|
279
|
+
in `data-bcms-props`) but gets its copy some other way is DECLARED: the publisher injects
|
|
280
|
+
into it and the editor makes it click-to-edit. It is simply not reading through the
|
|
281
|
+
committed helper. The receipt gains one row per already-declared path —
|
|
282
|
+
`paths.declarations[] { route, scope, path, kind, file, readsFromCms }` — and the
|
|
283
|
+
arithmetic is unchanged. The evidence has to be on the file the ROUTER says serves that
|
|
284
|
+
route: without a read there is nothing in a declaration that says which route it is for,
|
|
285
|
+
and two routes legitimately carry `hero.title`.
|
|
286
|
+
|
|
287
|
+
README: the "declared by hand" paragraph, and the address grammar in one place —
|
|
288
|
+
`data-bcms-field` / `data-bcms-layout-field="layout:<section>:<field>"`, attribute values
|
|
289
|
+
through `data-bcms-props="<address>|<kind>|<attr>"`, and `data-bcms-block` on a section root.
|
|
290
|
+
|
|
291
|
+
- Review hardening for all three of the above.
|
|
292
|
+
|
|
293
|
+
**Componentize.** A section the page renders BOTH inline and through a component it imports is
|
|
294
|
+
`SECTION_ROOT_AMBIGUOUS`: cutting the inline copy left `<Sections>` rendering the band out of
|
|
295
|
+
the registry while the untouched `<Hero />` beside it rendered it again. So is a section two
|
|
296
|
+
imported components both render, and a namespace the page imports but never calls. A registered
|
|
297
|
+
component is imported in the shape its module actually exports — default, `import { Hero }`, or
|
|
298
|
+
`import * as Bands` plus the member the call site names — because a default import of a named
|
|
299
|
+
export binds `undefined`. Contiguity now requires whitespace or comments between the roots, not
|
|
300
|
+
merely consecutive element siblings: `<Hero />Read this first<Features />` had no element in the
|
|
301
|
+
way and the run splice deleted the sentence. The registry read-back goes through the dialect's
|
|
302
|
+
parser instead of a line-shaped regex, so a formatter cannot make it forget the components it
|
|
303
|
+
names — and when the tables genuinely cannot be reconstructed the run refuses with the new
|
|
304
|
+
`REGISTRY_UNREADABLE` rather than rebuilding a registry without them.
|
|
305
|
+
|
|
306
|
+
**Repeaters.** A nested list the repository ships ONE row of still becomes a loop; the plan
|
|
307
|
+
already says `items` is an array, and reading one bullet as "not a list" is how the second one an
|
|
308
|
+
editor adds never renders.
|
|
309
|
+
|
|
310
|
+
**Declarations.** The route rule now covers LOCATED paths too — a hand-written declaration on
|
|
311
|
+
another route's page file, or on an element two routes both claim, is not evidence for either —
|
|
312
|
+
and every declarations row names the file it was found on. `aliasesFrom` reads JSONC (block
|
|
313
|
+
comments, trailing commas), because a tsconfig this package could not parse silently declared no
|
|
314
|
+
aliases and every aliased import then read as "not imported"; one it still cannot read is a
|
|
315
|
+
`TSCONFIG_UNREADABLE` note on the receipt.
|
|
316
|
+
|
|
317
|
+
- A second review round, over the same three.
|
|
318
|
+
|
|
319
|
+
**The registry read-back.** An import must resolve to a file that is actually there —
|
|
320
|
+
composing the path from our own naming convention meant a component somebody DELETED still
|
|
321
|
+
"resolved", and the run re-emitted a registry importing it. Both tables are now parsed
|
|
322
|
+
property by property, with strings and comments skipped by the brace matcher, so a one-line
|
|
323
|
+
table, a wrapped one and a comment containing a `}` are all read whole; anything that cannot
|
|
324
|
+
be reconstructed in full is `REGISTRY_UNREADABLE` rather than a quiet partial rebuild.
|
|
325
|
+
|
|
326
|
+
**Bindings.** `import Default, { Hero } from "./bands"` binds one module twice; the binding
|
|
327
|
+
the page actually CALLS is the one registered, instead of whichever came first.
|
|
328
|
+
|
|
329
|
+
**Declarations.** The route gate now also applies to a declaration that reads from the CMS —
|
|
330
|
+
a home page rendering a teaser of `/about`'s headline reads `/about`'s snapshot legitimately,
|
|
331
|
+
and counting it left `/about`'s own page hardcoded and unreported. "Another route's page" means
|
|
332
|
+
a route THE BRIEF names: `routeOfFile` reads the repository's convention and the two disagree
|
|
333
|
+
about a dynamic route (`/[slug]` vs `/post/[slug]`), and refusing on a name the brief never
|
|
334
|
+
issued rejected every dynamic route's own page.
|
|
335
|
+
|
|
336
|
+
**Configs.** A tsconfig whose `baseUrl`/`paths` are valid JSON of the wrong type threw a
|
|
337
|
+
`TypeError` out of a pure function; the shape is checked and a bad one is
|
|
338
|
+
`TSCONFIG_UNREADABLE`, which the componentize receipt now carries too.
|
|
339
|
+
|
|
3
340
|
## 0.2.0
|
|
4
341
|
|
|
5
342
|
### Minor Changes
|