@bettercms-ai/convert 0.23.0 → 0.24.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/cli.js +86 -7
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +18 -2
- package/dist/index.js +95 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @bettercms-ai/convert
|
|
2
2
|
|
|
3
|
+
## 0.24.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 15e446a: Helper v13: a repository image path (`@/images/blog/post-1.avif`, `~/…`, `../…`, `/src/…`) is never rendered as a value; the template's own imported picture shows instead. Converted sites get it on their next BetterCMS build through the helper upgrade.
|
|
8
|
+
|
|
9
|
+
## 0.24.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- 86c0c9f: Helper v12: on a server BetterCMS hosts, each content snapshot resolves at request time to its published counterpart in the file the platform rewrites on every publish (`BCMS_CONTENT_FILE`), so a server-rendered page shows a publish in seconds without a rebuild. Builds, dev servers, browsers and edge runtimes are unchanged. New export `helperUpgrades()` lists the current helper per path with the digests of every older one it may replace.
|
|
14
|
+
|
|
3
15
|
## 0.23.0
|
|
4
16
|
|
|
5
17
|
### Minor Changes
|
package/dist/cli.js
CHANGED
|
@@ -4190,7 +4190,7 @@ async function bindImages(ctx, targets) {
|
|
|
4190
4190
|
|
|
4191
4191
|
// src/helper.ts
|
|
4192
4192
|
import { createHash as createHash2 } from "crypto";
|
|
4193
|
-
var HELPER_VERSION =
|
|
4193
|
+
var HELPER_VERSION = 13;
|
|
4194
4194
|
var HEADER = "// @bettercms-ai/convert helper v";
|
|
4195
4195
|
var HELPER_PATH = Object.fromEntries(
|
|
4196
4196
|
Object.keys(DIALECT_RULES).map((d) => [d, DIALECT_RULES[d].helper])
|
|
@@ -4236,9 +4236,16 @@ function readPath(root: unknown, path: string): unknown {
|
|
|
4236
4236
|
return cursor;
|
|
4237
4237
|
}
|
|
4238
4238
|
|
|
4239
|
+
/**
|
|
4240
|
+
* A repository image path \u2014 \`@/images/x.avif\`, \`~/assets/x.png\`, \`../assets/x.jpg\`, \`/src/x.png\`.
|
|
4241
|
+
* The bundler resolves one at build and nothing else can, so as a value it is never rendered: the
|
|
4242
|
+
* template's own fallback (the picture the build already imported) shows instead of a broken image.
|
|
4243
|
+
*/
|
|
4244
|
+
const REPO_IMAGE_PATH = /^(?:@[\\w-]*|~|\\.{1,2}|\\/?src)\\/[^\\s?#"'<>]*\\.(?:avif|png|jpe?g|webp|gif|svg|bmp|ico|tiff?|jxl|heic)(?:[?#]\\S*)?$/i;
|
|
4245
|
+
|
|
4239
4246
|
/** A stored value as a template renders it: richtext is its html, an image is its url. */
|
|
4240
4247
|
function render(value: unknown): string | undefined {
|
|
4241
|
-
if (typeof value === "string") return value;
|
|
4248
|
+
if (typeof value === "string") return REPO_IMAGE_PATH.test(value.trim()) ? undefined : value;
|
|
4242
4249
|
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
4243
4250
|
if (isRecord(value)) {
|
|
4244
4251
|
if (typeof value.html === "string") return value.html;
|
|
@@ -4247,8 +4254,69 @@ function render(value: unknown): string | undefined {
|
|
|
4247
4254
|
return undefined;
|
|
4248
4255
|
}
|
|
4249
4256
|
|
|
4257
|
+
/**
|
|
4258
|
+
* The PUBLISHED copy, on a server BetterCMS hosts \u2014 read at request time, not at build time.
|
|
4259
|
+
*
|
|
4260
|
+
* BetterCMS keeps one file current for a running server app (\`BCMS_CONTENT_FILE\`, the same
|
|
4261
|
+
* bcms-content.json the build was given) and rewrites it on every publish, so a server-rendered
|
|
4262
|
+
* page shows a publish within seconds instead of after a rebuild. Each snapshot this module reads
|
|
4263
|
+
* resolves to its live counterpart: the shared layout by identity, a page by its \`slug\`.
|
|
4264
|
+
*
|
|
4265
|
+
* Nowhere else does anything change: with no variable (a build, \`astro dev\`, a browser, an edge
|
|
4266
|
+
* runtime) every snapshot is the one the bundle imported, exactly as before. A file that cannot be
|
|
4267
|
+
* read keeps the last good copy; it is checked at most once a second.
|
|
4268
|
+
*/
|
|
4269
|
+
type BcmsLiveContent = { stamp: number; bySlug: Map<string, unknown>; layout: unknown };
|
|
4270
|
+
let bcmsLive: BcmsLiveContent | null = null;
|
|
4271
|
+
let bcmsLiveCheckedAt = 0;
|
|
4272
|
+
|
|
4273
|
+
function liveContent(): BcmsLiveContent | null {
|
|
4274
|
+
const proc = (globalThis as {
|
|
4275
|
+
process?: { env?: Record<string, string | undefined>; getBuiltinModule?: (id: string) => unknown };
|
|
4276
|
+
}).process;
|
|
4277
|
+
const file = proc?.env?.BCMS_CONTENT_FILE;
|
|
4278
|
+
if (!file || typeof proc?.getBuiltinModule !== "function") return null;
|
|
4279
|
+
const now = Date.now();
|
|
4280
|
+
if (now - bcmsLiveCheckedAt < 1000) return bcmsLive;
|
|
4281
|
+
bcmsLiveCheckedAt = now;
|
|
4282
|
+
try {
|
|
4283
|
+
const fs = proc.getBuiltinModule("node:fs") as {
|
|
4284
|
+
statSync(path: string): { mtimeMs: number };
|
|
4285
|
+
readFileSync(path: string, encoding: "utf8"): string;
|
|
4286
|
+
};
|
|
4287
|
+
const stamp = fs.statSync(file).mtimeMs;
|
|
4288
|
+
if (bcmsLive && bcmsLive.stamp === stamp) return bcmsLive;
|
|
4289
|
+
const snapshot: unknown = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
4290
|
+
const bySlug = new Map<string, unknown>();
|
|
4291
|
+
const collections = isRecord(snapshot) && isRecord(snapshot.collections) ? snapshot.collections : {};
|
|
4292
|
+
for (const rows of Object.values(collections)) {
|
|
4293
|
+
if (!Array.isArray(rows)) continue;
|
|
4294
|
+
for (const row of rows) {
|
|
4295
|
+
if (isRecord(row) && typeof row.slug === "string" && !bySlug.has(row.slug)) bySlug.set(row.slug, row);
|
|
4296
|
+
}
|
|
4297
|
+
}
|
|
4298
|
+
const pages = isRecord(snapshot) && Array.isArray(snapshot.pages) ? snapshot.pages : [];
|
|
4299
|
+
const withLayout = pages.find((page: unknown) => isRecord(page) && isRecord(page.layout));
|
|
4300
|
+
const layout = isRecord(withLayout) ? withLayout.layout : isRecord(snapshot) ? snapshot.layout : undefined;
|
|
4301
|
+
bcmsLive = { stamp, bySlug, layout };
|
|
4302
|
+
} catch {
|
|
4303
|
+
// Unreadable, or caught mid-write: keep serving the last good copy.
|
|
4304
|
+
}
|
|
4305
|
+
return bcmsLive;
|
|
4306
|
+
}
|
|
4307
|
+
|
|
4308
|
+
/** The published counterpart of a snapshot this module was handed, or the snapshot itself. */
|
|
4309
|
+
function live(page: unknown): unknown {
|
|
4310
|
+
const content = liveContent();
|
|
4311
|
+
if (!content) return page;
|
|
4312
|
+
if (page === layoutSnapshot) return isRecord(content.layout) ? content.layout : page;
|
|
4313
|
+
if (isRecord(page) && typeof page.slug === "string") return content.bySlug.get(page.slug) ?? page;
|
|
4314
|
+
return page;
|
|
4315
|
+
}
|
|
4316
|
+
|
|
4250
4317
|
/** One field of one page. \`slug\` selects the entry on a dynamic route. */
|
|
4251
4318
|
export function bcms(page: unknown, path: string, fallback: string, slug?: string): string {
|
|
4319
|
+
page = live(page);
|
|
4252
4320
|
const entries = isRecord(page) ? page.entries : undefined;
|
|
4253
4321
|
const root = slug !== undefined && isRecord(entries) ? entries[slug] : page;
|
|
4254
4322
|
return render(readPath(root, path)) ?? fallback;
|
|
@@ -4256,7 +4324,7 @@ export function bcms(page: unknown, path: string, fallback: string, slug?: strin
|
|
|
4256
4324
|
|
|
4257
4325
|
/** One field of the shared layout \u2014 the header and footer every route renders. */
|
|
4258
4326
|
export function bcmsLayout(path: string, fallback: string): string {
|
|
4259
|
-
return render(readPath(layoutSnapshot, path)) ?? fallback;
|
|
4327
|
+
return render(readPath(live(layoutSnapshot), path)) ?? fallback;
|
|
4260
4328
|
}
|
|
4261
4329
|
|
|
4262
4330
|
/**
|
|
@@ -4264,6 +4332,7 @@ export function bcmsLayout(path: string, fallback: string): string {
|
|
|
4264
4332
|
* an empty snapshot still renders the same number of cards it always did.
|
|
4265
4333
|
*/
|
|
4266
4334
|
export function bcmsRows<T>(page: unknown, path: string, fallback: T[], slug?: string): T[] {
|
|
4335
|
+
page = live(page);
|
|
4267
4336
|
const entries = isRecord(page) ? page.entries : undefined;
|
|
4268
4337
|
const root = slug !== undefined && isRecord(entries) ? entries[slug] : page;
|
|
4269
4338
|
const value = readPath(root, path);
|
|
@@ -4309,6 +4378,7 @@ export function bcmsRowsAs<T>(
|
|
|
4309
4378
|
* this out loud rather than leaving an editor to discover it.
|
|
4310
4379
|
*/
|
|
4311
4380
|
export function bcmsList(page: unknown, paths: string[], fallback: string[], slug?: string): string[] {
|
|
4381
|
+
page = live(page);
|
|
4312
4382
|
const entries = isRecord(page) ? page.entries : undefined;
|
|
4313
4383
|
const root = slug !== undefined && isRecord(entries) ? entries[slug] : page;
|
|
4314
4384
|
return paths.map((path, index) => render(readPath(root, path)) ?? fallback[index] ?? "");
|
|
@@ -4333,6 +4403,7 @@ export function bcmsTuples(page: unknown, paths: string[][], fallback: string[][
|
|
|
4333
4403
|
* no such rule and takes the CMS url directly.
|
|
4334
4404
|
*/
|
|
4335
4405
|
export function bcmsImage<T>(page: unknown, path: string, fallback: T, slug?: string): T | string {
|
|
4406
|
+
page = live(page);
|
|
4336
4407
|
if (fallback !== null && typeof fallback === "object") return fallback;
|
|
4337
4408
|
const entries = isRecord(page) ? page.entries : undefined;
|
|
4338
4409
|
const root = slug !== undefined && isRecord(entries) ? entries[slug] : page;
|
|
@@ -4361,6 +4432,7 @@ export function bcmsSection(
|
|
|
4361
4432
|
overrides?: unknown,
|
|
4362
4433
|
bindPaths?: Record<string, string>,
|
|
4363
4434
|
): Record<string, unknown> {
|
|
4435
|
+
page = live(page);
|
|
4364
4436
|
const group = bind ? readPath(page, bind) : undefined;
|
|
4365
4437
|
const base: Record<string, unknown> = isRecord(group) ? { ...group } : {};
|
|
4366
4438
|
// A locale copy on its twin's component spells its fields its own way: each prop's copy is
|
|
@@ -4432,6 +4504,7 @@ export function bcmsSectionPaths(
|
|
|
4432
4504
|
map: BcmsPathMap,
|
|
4433
4505
|
bindPaths?: Record<string, string>,
|
|
4434
4506
|
): Record<string, unknown> {
|
|
4507
|
+
page = live(page);
|
|
4435
4508
|
const out: Record<string, unknown> = isRecord(page) ? { ...page } : {};
|
|
4436
4509
|
const own = isRecord(overrides) ? overrides : {};
|
|
4437
4510
|
const separator = prefix.slice(-1);
|
|
@@ -4665,7 +4738,9 @@ var HISTORICAL = {
|
|
|
4665
4738
|
7: "f22514ef48c50230985e605bea6cf9bf41756384a7dc80603e347baba9b65d93",
|
|
4666
4739
|
8: "484e72852d2b496e016a1f2a2b1a77bb993f8a163b290781522d6114b6519bb9",
|
|
4667
4740
|
9: "d6441db2ff4578c3c5d4b7d461f3374b89247ef6ba9e0d535791e840d803e4f3",
|
|
4668
|
-
10: "ffcb59862972b15a67c5951e9f3b94ce5b8b50345e6aac2bfa5789cfc3136910"
|
|
4741
|
+
10: "ffcb59862972b15a67c5951e9f3b94ce5b8b50345e6aac2bfa5789cfc3136910",
|
|
4742
|
+
11: "ceb2a932d4c2761a5045ff35032e0b45130a0b5034547ff37579c72c9ce3594e",
|
|
4743
|
+
12: "10f71724484c84af12aa3eb90db44bf6acbf71fcf8ddff5b6361c1190ba16528"
|
|
4669
4744
|
},
|
|
4670
4745
|
"src/lib/bcms-content.ts": {
|
|
4671
4746
|
1: "ae72ae14d706943d481c880d70da5789b4816ba1ba319a10fbc63ded4d0b42f2",
|
|
@@ -4677,7 +4752,9 @@ var HISTORICAL = {
|
|
|
4677
4752
|
7: "ed92fb88de953d9951abbe6a1edae1d048f4050eaf6fb607ac08167f94d8bcc7",
|
|
4678
4753
|
8: "1a6f669d76d42effe77f082436e9d21c0efa6fc499e919e3d116bc3a27a7f3d5",
|
|
4679
4754
|
9: "eee9fb1635ae7d6f8f33676a1f390600ca24a62ee992775252273c73a011c494",
|
|
4680
|
-
10: "35bb7c0e30f5c41fe68e52259ec4764de6de2734064fa0e1521f3338eef0a09e"
|
|
4755
|
+
10: "35bb7c0e30f5c41fe68e52259ec4764de6de2734064fa0e1521f3338eef0a09e",
|
|
4756
|
+
11: "818784f2088150d64d91b42eac944dc9503f104d32a0e7a18c753d2982a5adc0",
|
|
4757
|
+
12: "79dafc525a541feae55ada5dbe2ea966cac82acd741a4b41d61fe9e0cdfaa208"
|
|
4681
4758
|
},
|
|
4682
4759
|
"composables/bcms-content.ts": {
|
|
4683
4760
|
1: "c272412532efed7db64b6cf313b2bed4291f9951430412289c8a588b7f565407",
|
|
@@ -4689,7 +4766,9 @@ var HISTORICAL = {
|
|
|
4689
4766
|
7: "f22514ef48c50230985e605bea6cf9bf41756384a7dc80603e347baba9b65d93",
|
|
4690
4767
|
8: "484e72852d2b496e016a1f2a2b1a77bb993f8a163b290781522d6114b6519bb9",
|
|
4691
4768
|
9: "d6441db2ff4578c3c5d4b7d461f3374b89247ef6ba9e0d535791e840d803e4f3",
|
|
4692
|
-
10: "ffcb59862972b15a67c5951e9f3b94ce5b8b50345e6aac2bfa5789cfc3136910"
|
|
4769
|
+
10: "ffcb59862972b15a67c5951e9f3b94ce5b8b50345e6aac2bfa5789cfc3136910",
|
|
4770
|
+
11: "ceb2a932d4c2761a5045ff35032e0b45130a0b5034547ff37579c72c9ce3594e",
|
|
4771
|
+
12: "10f71724484c84af12aa3eb90db44bf6acbf71fcf8ddff5b6361c1190ba16528"
|
|
4693
4772
|
}
|
|
4694
4773
|
};
|
|
4695
4774
|
var sha256 = (value) => createHash2("sha256").update(value, "utf8").digest("hex");
|
|
@@ -10705,7 +10784,7 @@ function styleEdits(before, after) {
|
|
|
10705
10784
|
// package.json
|
|
10706
10785
|
var package_default = {
|
|
10707
10786
|
name: "@bettercms-ai/convert",
|
|
10708
|
-
version: "0.
|
|
10787
|
+
version: "0.24.1",
|
|
10709
10788
|
type: "module",
|
|
10710
10789
|
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.",
|
|
10711
10790
|
bin: {
|