@bettercms-ai/convert 0.25.1 → 0.26.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 +6 -0
- package/dist/cli.js +52 -18
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +51 -17
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @bettercms-ai/convert
|
|
2
2
|
|
|
3
|
+
## 0.26.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 69dfcfa: A server app hosted on BetterCMS shows a publish on its very next request: the converted-site helper (v14) and `readPublishedContent` ask the content file's time on every read instead of once a second. Helper v14 also renders an image an editor replaced on a page the app renders per request: `bcmsImage` returns the local copy BetterCMS puts beside the app, in the shape of an import, where it used to keep the build's picture. A site built with an older helper gets v14 on its next BetterCMS build, with no commit.
|
|
8
|
+
|
|
3
9
|
## 0.25.1
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
package/dist/cli.js
CHANGED
|
@@ -4203,7 +4203,7 @@ async function bindImages(ctx, targets) {
|
|
|
4203
4203
|
|
|
4204
4204
|
// src/helper.ts
|
|
4205
4205
|
import { createHash as createHash2 } from "crypto";
|
|
4206
|
-
var HELPER_VERSION =
|
|
4206
|
+
var HELPER_VERSION = 14;
|
|
4207
4207
|
var HEADER = "// @bettercms-ai/convert helper v";
|
|
4208
4208
|
var HELPER_PATH = Object.fromEntries(
|
|
4209
4209
|
Object.keys(DIALECT_RULES).map((d) => [d, DIALECT_RULES[d].helper])
|
|
@@ -4277,11 +4277,11 @@ function render(value: unknown): string | undefined {
|
|
|
4277
4277
|
*
|
|
4278
4278
|
* Nowhere else does anything change: with no variable (a build, \`astro dev\`, a browser, an edge
|
|
4279
4279
|
* runtime) every snapshot is the one the bundle imported, exactly as before. A file that cannot be
|
|
4280
|
-
* read keeps the last good copy
|
|
4280
|
+
* read keeps the last good copy. Its modification time is asked on EVERY read (a stat, microseconds;
|
|
4281
|
+
* the file is parsed again only when it changed), so a publish shows on the very next request.
|
|
4281
4282
|
*/
|
|
4282
|
-
type BcmsLiveContent = { stamp: number; bySlug: Map<string, unknown>; layout: unknown };
|
|
4283
|
+
type BcmsLiveContent = { helper: string; stamp: number; bySlug: Map<string, unknown>; entries: Record<string, unknown>; layout: unknown };
|
|
4283
4284
|
let bcmsLive: BcmsLiveContent | null = null;
|
|
4284
|
-
let bcmsLiveCheckedAt = 0;
|
|
4285
4285
|
|
|
4286
4286
|
function liveContent(): BcmsLiveContent | null {
|
|
4287
4287
|
const proc = (globalThis as {
|
|
@@ -4289,9 +4289,6 @@ function liveContent(): BcmsLiveContent | null {
|
|
|
4289
4289
|
}).process;
|
|
4290
4290
|
const file = proc?.env?.BCMS_CONTENT_FILE;
|
|
4291
4291
|
if (!file || typeof proc?.getBuiltinModule !== "function") return null;
|
|
4292
|
-
const now = Date.now();
|
|
4293
|
-
if (now - bcmsLiveCheckedAt < 1000) return bcmsLive;
|
|
4294
|
-
bcmsLiveCheckedAt = now;
|
|
4295
4292
|
try {
|
|
4296
4293
|
const fs = proc.getBuiltinModule("node:fs") as {
|
|
4297
4294
|
statSync(path: string): { mtimeMs: number };
|
|
@@ -4305,13 +4302,30 @@ function liveContent(): BcmsLiveContent | null {
|
|
|
4305
4302
|
for (const rows of Object.values(collections)) {
|
|
4306
4303
|
if (!Array.isArray(rows)) continue;
|
|
4307
4304
|
for (const row of rows) {
|
|
4308
|
-
|
|
4305
|
+
// A published row carries its values under \`data\` (the delivery API's shape); the snapshot a
|
|
4306
|
+
// template reads is the values themselves. Read flat, as before, where a row has none.
|
|
4307
|
+
if (isRecord(row) && typeof row.slug === "string" && !bySlug.has(row.slug)) {
|
|
4308
|
+
bySlug.set(row.slug, isRecord(row.data) ? { ...row.data, slug: row.slug } : row);
|
|
4309
|
+
}
|
|
4309
4310
|
}
|
|
4310
4311
|
}
|
|
4312
|
+
// A dynamic route reads \`page.entries[slug]\`: every published row, by its slug.
|
|
4313
|
+
const entries: Record<string, unknown> = Object.fromEntries(bySlug);
|
|
4314
|
+
for (const row of bySlug.values()) if (isRecord(row) && !("entries" in row)) row.entries = entries;
|
|
4311
4315
|
const pages = isRecord(snapshot) && Array.isArray(snapshot.pages) ? snapshot.pages : [];
|
|
4312
4316
|
const withLayout = pages.find((page: unknown) => isRecord(page) && isRecord(page.layout));
|
|
4313
|
-
|
|
4314
|
-
|
|
4317
|
+
// The site's Layout first; a page's own (its overrides resolved in) only where there is no other.
|
|
4318
|
+
const delivered = isRecord(snapshot) && isRecord(snapshot.layout) ? snapshot.layout : isRecord(withLayout) ? withLayout.layout : undefined;
|
|
4319
|
+
// A template addresses a Layout value as \`<section id>:<field id>\`; the published Layout holds
|
|
4320
|
+
// each Section's values under \`sections.<slug>.fields\`. Both spellings are answered.
|
|
4321
|
+
const layout: Record<string, unknown> | undefined = isRecord(delivered) ? { ...delivered } : undefined;
|
|
4322
|
+
for (const section of layout && isRecord(delivered) && isRecord(delivered.sections) ? Object.values(delivered.sections) : []) {
|
|
4323
|
+
if (!isRecord(section) || typeof section.id !== "string" || !isRecord(section.fields)) continue;
|
|
4324
|
+
for (const [field, value] of Object.entries(section.fields)) layout![section.id + ":" + field] = value;
|
|
4325
|
+
}
|
|
4326
|
+
// \`helper\` names this reader in the built bundle: the platform reads it to know the app shows a
|
|
4327
|
+
// publish on its next request, and an image BetterCMS copied beside it (@see bcmsImage).
|
|
4328
|
+
bcmsLive = { helper: "bcms-helper@${HELPER_VERSION}", stamp, bySlug, entries, layout };
|
|
4315
4329
|
} catch {
|
|
4316
4330
|
// Unreadable, or caught mid-write: keep serving the last good copy.
|
|
4317
4331
|
}
|
|
@@ -4323,7 +4337,10 @@ function live(page: unknown): unknown {
|
|
|
4323
4337
|
const content = liveContent();
|
|
4324
4338
|
if (!content) return page;
|
|
4325
4339
|
if (page === layoutSnapshot) return isRecord(content.layout) ? content.layout : page;
|
|
4326
|
-
if (isRecord(page) && typeof page.slug === "string")
|
|
4340
|
+
if (isRecord(page) && typeof page.slug === "string") {
|
|
4341
|
+
// A collection's page has no row of its own: its entries are the published ones all the same.
|
|
4342
|
+
return content.bySlug.get(page.slug) ?? ("entries" in page ? page : { ...page, entries: content.entries });
|
|
4343
|
+
}
|
|
4327
4344
|
return page;
|
|
4328
4345
|
}
|
|
4329
4346
|
|
|
@@ -4337,7 +4354,10 @@ export function bcms(page: unknown, path: string, fallback: string, slug?: strin
|
|
|
4337
4354
|
|
|
4338
4355
|
/** One field of the shared layout \u2014 the header and footer every route renders. */
|
|
4339
4356
|
export function bcmsLayout(path: string, fallback: string): string {
|
|
4340
|
-
|
|
4357
|
+
const layout = live(layoutSnapshot);
|
|
4358
|
+
// The whole address first: a field id may carry a dot of its own (\`derived.nav-cta\`).
|
|
4359
|
+
const whole = isRecord(layout) ? layout[path] : undefined;
|
|
4360
|
+
return render(whole !== undefined ? whole : readPath(layout, path)) ?? fallback;
|
|
4341
4361
|
}
|
|
4342
4362
|
|
|
4343
4363
|
/**
|
|
@@ -4414,13 +4434,24 @@ export function bcmsTuples(page: unknown, paths: string[][], fallback: string[][
|
|
|
4414
4434
|
* release write (\`writeImage\`) puts the editor's image on the \`data-bcms-field\` element, which
|
|
4415
4435
|
* is where every published value lands. A plain string fallback (\`<img src="/hero.png">\`) has
|
|
4416
4436
|
* no such rule and takes the CMS url directly.
|
|
4437
|
+
*
|
|
4438
|
+
* \u{1F534} ON A HOSTED SERVER THE EDITOR'S IMAGE IS A LOCAL FILE. A page the app renders per request has no
|
|
4439
|
+
* release write: what this returns is the page. BetterCMS copies a replaced image beside the app and
|
|
4440
|
+
* names the copy on the published value (\`local\`, with its size), and it is returned in the shape
|
|
4441
|
+
* of an import (\`{ src, width, height, format }\`), so \`<Image>\` optimises it like the site's own.
|
|
4442
|
+
* Absent (a build, or the site's own picture) the import stands.
|
|
4417
4443
|
*/
|
|
4418
4444
|
export function bcmsImage<T>(page: unknown, path: string, fallback: T, slug?: string): T | string {
|
|
4419
4445
|
page = live(page);
|
|
4420
|
-
if (fallback !== null && typeof fallback === "object") return fallback;
|
|
4421
4446
|
const entries = isRecord(page) ? page.entries : undefined;
|
|
4422
4447
|
const root = slug !== undefined && isRecord(entries) ? entries[slug] : page;
|
|
4423
|
-
|
|
4448
|
+
const value = readPath(root, path);
|
|
4449
|
+
if (fallback !== null && typeof fallback === "object") {
|
|
4450
|
+
if (!isRecord(value) || typeof value.local !== "string" || typeof value.width !== "number" || typeof value.height !== "number") return fallback;
|
|
4451
|
+
const format = /\\.([a-z0-9]+)$/i.exec(value.local)?.[1]?.toLowerCase() ?? "";
|
|
4452
|
+
return { src: value.local, width: value.width, height: value.height, format: format === "jpg" ? "jpeg" : format } as unknown as T;
|
|
4453
|
+
}
|
|
4454
|
+
return render(value) ?? fallback;
|
|
4424
4455
|
}
|
|
4425
4456
|
|
|
4426
4457
|
/**
|
|
@@ -4753,7 +4784,8 @@ var HISTORICAL = {
|
|
|
4753
4784
|
9: "d6441db2ff4578c3c5d4b7d461f3374b89247ef6ba9e0d535791e840d803e4f3",
|
|
4754
4785
|
10: "ffcb59862972b15a67c5951e9f3b94ce5b8b50345e6aac2bfa5789cfc3136910",
|
|
4755
4786
|
11: "ceb2a932d4c2761a5045ff35032e0b45130a0b5034547ff37579c72c9ce3594e",
|
|
4756
|
-
12: "10f71724484c84af12aa3eb90db44bf6acbf71fcf8ddff5b6361c1190ba16528"
|
|
4787
|
+
12: "10f71724484c84af12aa3eb90db44bf6acbf71fcf8ddff5b6361c1190ba16528",
|
|
4788
|
+
13: "cd8bdd74dbc944f2331503bdf8e995cb7f74f0d5b307d08aee4690e1695ae798"
|
|
4757
4789
|
},
|
|
4758
4790
|
"src/lib/bcms-content.ts": {
|
|
4759
4791
|
1: "ae72ae14d706943d481c880d70da5789b4816ba1ba319a10fbc63ded4d0b42f2",
|
|
@@ -4767,7 +4799,8 @@ var HISTORICAL = {
|
|
|
4767
4799
|
9: "eee9fb1635ae7d6f8f33676a1f390600ca24a62ee992775252273c73a011c494",
|
|
4768
4800
|
10: "35bb7c0e30f5c41fe68e52259ec4764de6de2734064fa0e1521f3338eef0a09e",
|
|
4769
4801
|
11: "818784f2088150d64d91b42eac944dc9503f104d32a0e7a18c753d2982a5adc0",
|
|
4770
|
-
12: "79dafc525a541feae55ada5dbe2ea966cac82acd741a4b41d61fe9e0cdfaa208"
|
|
4802
|
+
12: "79dafc525a541feae55ada5dbe2ea966cac82acd741a4b41d61fe9e0cdfaa208",
|
|
4803
|
+
13: "06ee70c9f0bb25e03f3930d2250da8dae172ade457f27ebba20382b6482ad3f4"
|
|
4771
4804
|
},
|
|
4772
4805
|
"composables/bcms-content.ts": {
|
|
4773
4806
|
1: "c272412532efed7db64b6cf313b2bed4291f9951430412289c8a588b7f565407",
|
|
@@ -4781,7 +4814,8 @@ var HISTORICAL = {
|
|
|
4781
4814
|
9: "d6441db2ff4578c3c5d4b7d461f3374b89247ef6ba9e0d535791e840d803e4f3",
|
|
4782
4815
|
10: "ffcb59862972b15a67c5951e9f3b94ce5b8b50345e6aac2bfa5789cfc3136910",
|
|
4783
4816
|
11: "ceb2a932d4c2761a5045ff35032e0b45130a0b5034547ff37579c72c9ce3594e",
|
|
4784
|
-
12: "10f71724484c84af12aa3eb90db44bf6acbf71fcf8ddff5b6361c1190ba16528"
|
|
4817
|
+
12: "10f71724484c84af12aa3eb90db44bf6acbf71fcf8ddff5b6361c1190ba16528",
|
|
4818
|
+
13: "cd8bdd74dbc944f2331503bdf8e995cb7f74f0d5b307d08aee4690e1695ae798"
|
|
4785
4819
|
}
|
|
4786
4820
|
};
|
|
4787
4821
|
var sha256 = (value) => createHash2("sha256").update(value, "utf8").digest("hex");
|
|
@@ -10863,7 +10897,7 @@ function styleEdits(before, after) {
|
|
|
10863
10897
|
// package.json
|
|
10864
10898
|
var package_default = {
|
|
10865
10899
|
name: "@bettercms-ai/convert",
|
|
10866
|
-
version: "0.
|
|
10900
|
+
version: "0.26.0",
|
|
10867
10901
|
type: "module",
|
|
10868
10902
|
description: "Deterministic codemod that turns an imported site's text-matched bindings into declared ones \u2014 parser offsets in, one magic-string pass out, plus a receipt of everything it could not do.",
|
|
10869
10903
|
bin: {
|