@bettercms-ai/convert 0.24.0 → 0.25.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 CHANGED
@@ -1,5 +1,21 @@
1
1
  # @bettercms-ai/convert
2
2
 
3
+ ## 0.25.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 832e08a: A converted repository whose `build` runs checks before the build (`astro check && astro build`, `tsc --noEmit && vite build`, `npm run lint && next build`) gets a `build:bcms` script beside it with the same build minus those checks, `prebuild`/`postbuild` folded in. BetterCMS builds with `build:bcms` when a project declares one, so a deploy no longer pays for the project's CI checks; `build` is left unchanged. Every writing run adds it once (a repository that already has one is never touched), and only for a plain `&&` chain. Exported as `bcmsBuildScript`.
8
+
9
+ ### Patch Changes
10
+
11
+ - c6fd790: The codemod never walks into a directory holding its own `.git` (a git worktree, submodule or vendored clone), so a tool's worktree copy of the repo is no longer converted alongside the real source.
12
+
13
+ ## 0.24.1
14
+
15
+ ### Patch Changes
16
+
17
+ - 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.
18
+
3
19
  ## 0.24.0
4
20
 
5
21
  ### Minor Changes
package/dist/cli.js CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  // src/cli.ts
4
4
  import { execFileSync } from "child_process";
5
+ import { existsSync } from "fs";
5
6
  import { readdir, readFile, writeFile, mkdir, stat } from "fs/promises";
6
7
  import { dirname, join as join2, relative, resolve, sep } from "path";
7
8
 
@@ -4190,7 +4191,7 @@ async function bindImages(ctx, targets) {
4190
4191
 
4191
4192
  // src/helper.ts
4192
4193
  import { createHash as createHash2 } from "crypto";
4193
- var HELPER_VERSION = 12;
4194
+ var HELPER_VERSION = 13;
4194
4195
  var HEADER = "// @bettercms-ai/convert helper v";
4195
4196
  var HELPER_PATH = Object.fromEntries(
4196
4197
  Object.keys(DIALECT_RULES).map((d) => [d, DIALECT_RULES[d].helper])
@@ -4236,9 +4237,16 @@ function readPath(root: unknown, path: string): unknown {
4236
4237
  return cursor;
4237
4238
  }
4238
4239
 
4240
+ /**
4241
+ * A repository image path \u2014 \`@/images/x.avif\`, \`~/assets/x.png\`, \`../assets/x.jpg\`, \`/src/x.png\`.
4242
+ * The bundler resolves one at build and nothing else can, so as a value it is never rendered: the
4243
+ * template's own fallback (the picture the build already imported) shows instead of a broken image.
4244
+ */
4245
+ const REPO_IMAGE_PATH = /^(?:@[\\w-]*|~|\\.{1,2}|\\/?src)\\/[^\\s?#"'<>]*\\.(?:avif|png|jpe?g|webp|gif|svg|bmp|ico|tiff?|jxl|heic)(?:[?#]\\S*)?$/i;
4246
+
4239
4247
  /** A stored value as a template renders it: richtext is its html, an image is its url. */
4240
4248
  function render(value: unknown): string | undefined {
4241
- if (typeof value === "string") return value;
4249
+ if (typeof value === "string") return REPO_IMAGE_PATH.test(value.trim()) ? undefined : value;
4242
4250
  if (typeof value === "number" || typeof value === "boolean") return String(value);
4243
4251
  if (isRecord(value)) {
4244
4252
  if (typeof value.html === "string") return value.html;
@@ -4732,7 +4740,8 @@ var HISTORICAL = {
4732
4740
  8: "484e72852d2b496e016a1f2a2b1a77bb993f8a163b290781522d6114b6519bb9",
4733
4741
  9: "d6441db2ff4578c3c5d4b7d461f3374b89247ef6ba9e0d535791e840d803e4f3",
4734
4742
  10: "ffcb59862972b15a67c5951e9f3b94ce5b8b50345e6aac2bfa5789cfc3136910",
4735
- 11: "ceb2a932d4c2761a5045ff35032e0b45130a0b5034547ff37579c72c9ce3594e"
4743
+ 11: "ceb2a932d4c2761a5045ff35032e0b45130a0b5034547ff37579c72c9ce3594e",
4744
+ 12: "10f71724484c84af12aa3eb90db44bf6acbf71fcf8ddff5b6361c1190ba16528"
4736
4745
  },
4737
4746
  "src/lib/bcms-content.ts": {
4738
4747
  1: "ae72ae14d706943d481c880d70da5789b4816ba1ba319a10fbc63ded4d0b42f2",
@@ -4745,7 +4754,8 @@ var HISTORICAL = {
4745
4754
  8: "1a6f669d76d42effe77f082436e9d21c0efa6fc499e919e3d116bc3a27a7f3d5",
4746
4755
  9: "eee9fb1635ae7d6f8f33676a1f390600ca24a62ee992775252273c73a011c494",
4747
4756
  10: "35bb7c0e30f5c41fe68e52259ec4764de6de2734064fa0e1521f3338eef0a09e",
4748
- 11: "818784f2088150d64d91b42eac944dc9503f104d32a0e7a18c753d2982a5adc0"
4757
+ 11: "818784f2088150d64d91b42eac944dc9503f104d32a0e7a18c753d2982a5adc0",
4758
+ 12: "79dafc525a541feae55ada5dbe2ea966cac82acd741a4b41d61fe9e0cdfaa208"
4749
4759
  },
4750
4760
  "composables/bcms-content.ts": {
4751
4761
  1: "c272412532efed7db64b6cf313b2bed4291f9951430412289c8a588b7f565407",
@@ -4758,7 +4768,8 @@ var HISTORICAL = {
4758
4768
  8: "484e72852d2b496e016a1f2a2b1a77bb993f8a163b290781522d6114b6519bb9",
4759
4769
  9: "d6441db2ff4578c3c5d4b7d461f3374b89247ef6ba9e0d535791e840d803e4f3",
4760
4770
  10: "ffcb59862972b15a67c5951e9f3b94ce5b8b50345e6aac2bfa5789cfc3136910",
4761
- 11: "ceb2a932d4c2761a5045ff35032e0b45130a0b5034547ff37579c72c9ce3594e"
4771
+ 11: "ceb2a932d4c2761a5045ff35032e0b45130a0b5034547ff37579c72c9ce3594e",
4772
+ 12: "10f71724484c84af12aa3eb90db44bf6acbf71fcf8ddff5b6361c1190ba16528"
4762
4773
  }
4763
4774
  };
4764
4775
  var sha256 = (value) => createHash2("sha256").update(value, "utf8").digest("hex");
@@ -7654,6 +7665,54 @@ function moduleScope(parsed, content) {
7654
7665
  return out;
7655
7666
  }
7656
7667
 
7668
+ // src/build-script.ts
7669
+ var CHECK = /^(?:npx\s+|pnpm\s+(?:exec\s+)?|yarn\s+|bunx\s+)?(?:astro\s+check|svelte-check|vue-tsc|tsc(?:\s|$)|eslint|next\s+lint|biome\s+(?:check|lint|ci)|prettier\s+(?:--check|-c)\b|stylelint|oxlint)/;
7670
+ var RUN_CHECK = /^(?:npm\s+run|pnpm(?:\s+run)?|yarn(?:\s+run)?|bun\s+run)\s+(?:lint|typecheck|type-check|check|check:types|types|format:check|test)(?::[\w-]+)?\s*$/;
7671
+ var UNSAFE = /;|\|\||\||(?:^|[^&])&(?:[^&]|$)|\$\(|`|\n/;
7672
+ var isCheck = (step) => CHECK.test(step) || RUN_CHECK.test(step);
7673
+ function steps(script) {
7674
+ if (UNSAFE.test(script)) return null;
7675
+ const out = script.split("&&").map((s) => s.trim());
7676
+ return out.some((s) => s === "") ? null : out;
7677
+ }
7678
+ function bcmsBuildScript(packageJson) {
7679
+ let pkg;
7680
+ try {
7681
+ pkg = JSON.parse(packageJson);
7682
+ } catch {
7683
+ return null;
7684
+ }
7685
+ const scripts = pkg?.scripts;
7686
+ if (!scripts || typeof scripts !== "object" || "build:bcms" in scripts) return null;
7687
+ const build = scripts.build;
7688
+ if (typeof build !== "string") return null;
7689
+ const parts = [];
7690
+ for (const name of ["prebuild", "build", "postbuild"]) {
7691
+ const value = scripts[name];
7692
+ if (value === void 0) continue;
7693
+ if (typeof value !== "string") return null;
7694
+ const s = steps(value);
7695
+ if (!s) return null;
7696
+ parts.push(s);
7697
+ }
7698
+ const all = parts.flat();
7699
+ const dropped = all.filter(isCheck);
7700
+ const kept = all.filter((s) => !isCheck(s));
7701
+ if (dropped.length === 0 || kept.length === 0) return null;
7702
+ const script = kept.join(" && ");
7703
+ const next = {};
7704
+ for (const [key, value] of Object.entries(scripts)) {
7705
+ next[key] = value;
7706
+ if (key === "build") next["build:bcms"] = script;
7707
+ }
7708
+ const indent = /^[ \t]+(?=")/m.exec(packageJson)?.[0] ?? " ";
7709
+ const eol = packageJson.includes("\r\n") ? "\r\n" : "\n";
7710
+ let content = JSON.stringify({ ...pkg, scripts: next }, null, indent);
7711
+ if (eol !== "\n") content = content.replace(/\n/g, eol);
7712
+ if (/\r?\n$/.test(packageJson)) content += eol;
7713
+ return { content, script, dropped };
7714
+ }
7715
+
7657
7716
  // src/componentize.ts
7658
7717
  import MagicString5 from "magic-string";
7659
7718
  var SectionInvariantError = class extends Error {
@@ -10774,7 +10833,7 @@ function styleEdits(before, after) {
10774
10833
  // package.json
10775
10834
  var package_default = {
10776
10835
  name: "@bettercms-ai/convert",
10777
- version: "0.24.0",
10836
+ version: "0.25.0",
10778
10837
  type: "module",
10779
10838
  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.",
10780
10839
  bin: {
@@ -10951,7 +11010,7 @@ async function readTree(root, verbose) {
10951
11010
  const full = join2(dir, entry.name);
10952
11011
  const rel = relative(root, full).split(sep).join("/");
10953
11012
  if (entry.isDirectory()) {
10954
- if (isSourceCandidate(`${rel}/x.html`)) await walk(full);
11013
+ if (isSourceCandidate(`${rel}/x.html`) && !existsSync(join2(full, ".git"))) await walk(full);
10955
11014
  continue;
10956
11015
  }
10957
11016
  if (!entry.isFile() || !isSourceCandidate(rel)) continue;
@@ -11132,6 +11191,21 @@ async function verifyStyles(root, base, verbose) {
11132
11191
  );
11133
11192
  return 1;
11134
11193
  }
11194
+ async function addBcmsBuild(root, dryRun) {
11195
+ let packageJson;
11196
+ try {
11197
+ packageJson = await readFile(join2(root, "package.json"), "utf-8");
11198
+ } catch {
11199
+ return;
11200
+ }
11201
+ const added = bcmsBuildScript(packageJson);
11202
+ if (!added) return;
11203
+ if (!dryRun) await writeFile(join2(root, "package.json"), added.content, "utf-8");
11204
+ process.stderr.write(
11205
+ `package.json: ${dryRun ? "would add" : "added"} "build:bcms": "${added.script}" \u2014 BetterCMS builds with it, without ${added.dropped.join(", ")}; "build" is unchanged.
11206
+ `
11207
+ );
11208
+ }
11135
11209
  async function main() {
11136
11210
  const args = parseArgs(process.argv.slice(2));
11137
11211
  if (args.help) {
@@ -11163,6 +11237,7 @@ async function main() {
11163
11237
  await writeFile(target, file.content, "utf-8");
11164
11238
  }
11165
11239
  }
11240
+ await addBcmsBuild(root2, args.dryRun);
11166
11241
  if (args.receipt) await writeFile(resolve(args.receipt), `${JSON.stringify(receipt2, null, 2)}
11167
11242
  `, "utf-8");
11168
11243
  reportUpgrade(files2, receipt2, args.dryRun);
@@ -11190,12 +11265,14 @@ async function main() {
11190
11265
  if (args.verbose) process.stderr.write(`read ${sources.length} candidate files under ${root}
11191
11266
  `);
11192
11267
  const written = async (files2) => {
11193
- if (args.dryRun) return;
11194
- for (const file of files2) {
11195
- const target = under(root, file.path);
11196
- await mkdir(dirname(target), { recursive: true });
11197
- await writeFile(target, file.content, "utf-8");
11268
+ if (!args.dryRun) {
11269
+ for (const file of files2) {
11270
+ const target = under(root, file.path);
11271
+ await mkdir(dirname(target), { recursive: true });
11272
+ await writeFile(target, file.content, "utf-8");
11273
+ }
11198
11274
  }
11275
+ await addBcmsBuild(root, args.dryRun);
11199
11276
  };
11200
11277
  const saveReceipt = async (receipt2) => {
11201
11278
  if (!args.receipt) return;