@blocofy/cli 0.1.9 → 0.1.10

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/bin/blocofy.mjs CHANGED
@@ -12,6 +12,7 @@ import { existsSync } from "node:fs";
12
12
  import { resolve } from "node:path";
13
13
  import { createInterface } from "node:readline/promises";
14
14
 
15
+ import { pushContent } from "../lib/content-sync.mjs";
15
16
  import { credentialsPath, loadCredentials, saveCredentials } from "../lib/credentials.mjs";
16
17
  import { startDevServer } from "../lib/dev-server.mjs";
17
18
  import { readLocalTemplates } from "../lib/local-theme.mjs";
@@ -20,7 +21,7 @@ import { fetchDevSession, pullTheme, pushTheme } from "../lib/theme-sync.mjs";
20
21
  import { hyperlink, openUrl } from "../lib/term.mjs";
21
22
  import { isValidToken, isValidUrl, normalizeUrl } from "../lib/validate.mjs";
22
23
 
23
- const VERSION = "0.1.9";
24
+ const VERSION = "0.1.10";
24
25
  const args = process.argv.slice(2);
25
26
 
26
27
  /** Parse `--key value` and `--flag` (boolean) arguments. */
@@ -66,6 +67,13 @@ Usage
66
67
  Write the local theme to the site (create/update; no delete).
67
68
  --draft write to a draft theme — preview & publish it from the admin panel
68
69
 
70
+ blocofy pages push [dir]
71
+ Push local pages/*.json to the site. Updates EXISTING pages only —
72
+ never creates or deletes a page. Unchanged pages are skipped.
73
+
74
+ blocofy settings push [dir]
75
+ Push local config/settings.json (theme tokens/settings + color schemes).
76
+
69
77
  blocofy --version
70
78
  blocofy --help
71
79
 
@@ -180,6 +188,28 @@ async function themePush(rest) {
180
188
  }
181
189
  }
182
190
 
191
+ async function contentPush(scope, rest) {
192
+ const positional = rest.filter((a) => !a.startsWith("--"));
193
+ const dir = resolve(positional[0] ?? process.cwd());
194
+ if (!existsSync(dir)) {
195
+ console.error(`Directory not found: ${dir}`);
196
+ process.exit(1);
197
+ }
198
+ const creds = requireCreds();
199
+ const result = await pushContent({ dir, url: creds.url, token: creds.token, scope });
200
+ if (scope === "settings") {
201
+ console.log(
202
+ `Settings push: ${result.settingsUpdated ? "theme settings updated" : "theme settings unchanged"}, ` +
203
+ `${result.schemesUpserted} color scheme(s) upserted (${result.fileCount} file).`,
204
+ );
205
+ } else {
206
+ console.log(
207
+ `Pages push: ${result.pagesUpdated} updated, ${result.pagesSkipped} skipped ` +
208
+ `(only existing pages are updated — none created or deleted).`,
209
+ );
210
+ }
211
+ }
212
+
183
213
  async function themeDev(rest) {
184
214
  const flags = parseFlags(rest);
185
215
  const positional = rest.filter((a) => !a.startsWith("--"));
@@ -344,6 +374,16 @@ if (first === "--version" || first === "-v") {
344
374
  console.error(error?.message ?? error);
345
375
  process.exit(1);
346
376
  });
377
+ } else if (first === "pages" && rest[0] === "push") {
378
+ contentPush("pages", rest.slice(1)).catch((error) => {
379
+ console.error(error?.message ?? error);
380
+ process.exit(1);
381
+ });
382
+ } else if (first === "settings" && rest[0] === "push") {
383
+ contentPush("settings", rest.slice(1)).catch((error) => {
384
+ console.error(error?.message ?? error);
385
+ process.exit(1);
386
+ });
347
387
  } else {
348
388
  console.error(`Unknown command: ${args.join(" ")}\n`);
349
389
  printHelp();
@@ -0,0 +1,58 @@
1
+ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
2
+ import { join, relative, sep } from "node:path";
3
+
4
+ /**
5
+ * `blocofy pages push` / `settings push` (#119). CLI'nın yerel içerik dosyalarını
6
+ * (`pages/<slug>.json` + `config/settings.json`) platformun `/api/dev/content`
7
+ * endpoint'ine gönderir. Tema KODU `theme push`'a gider; bu yalnız içerik+ayar.
8
+ * Güvenlik platformda: yalnız mevcut sayfayı günceller, yeni oluşturmaz, silmez.
9
+ */
10
+
11
+ async function errorText(res) {
12
+ const text = await res.text();
13
+ try {
14
+ return JSON.parse(text).error ?? text;
15
+ } catch {
16
+ return text;
17
+ }
18
+ }
19
+
20
+ /** scope "pages" → pages dizinindeki tüm .json (recursive); "settings" → config/settings.json. `{path: content}`. */
21
+ export function readContentFiles(dir, scope) {
22
+ const out = {};
23
+ if (scope === "settings") {
24
+ const p = join(dir, "config", "settings.json");
25
+ if (existsSync(p)) out["config/settings.json"] = readFileSync(p, "utf8");
26
+ return out;
27
+ }
28
+ // pages
29
+ const pagesDir = join(dir, "pages");
30
+ if (!existsSync(pagesDir)) return out;
31
+ const walk = (current) => {
32
+ for (const name of readdirSync(current)) {
33
+ const full = join(current, name);
34
+ if (statSync(full).isDirectory()) {
35
+ walk(full);
36
+ continue;
37
+ }
38
+ if (!name.endsWith(".json")) continue;
39
+ const key = "pages/" + relative(pagesDir, full).split(sep).join("/");
40
+ out[key] = readFileSync(full, "utf8");
41
+ }
42
+ };
43
+ walk(pagesDir);
44
+ return out;
45
+ }
46
+
47
+ /** Yerel içeriği push'la. Yanıt: platform sonucu + gönderilen dosya sayısı. */
48
+ export async function pushContent({ dir, url, token, scope }) {
49
+ const files = readContentFiles(dir, scope);
50
+ const base = url.replace(/\/+$/, "");
51
+ const res = await fetch(`${base}/api/dev/content`, {
52
+ method: "POST",
53
+ headers: { "content-type": "application/json", authorization: `Bearer ${token}` },
54
+ body: JSON.stringify({ files }),
55
+ });
56
+ if (!res.ok) throw new Error(await errorText(res));
57
+ return { ...(await res.json()), fileCount: Object.keys(files).length };
58
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blocofy/cli",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "Develop your Blocofy theme locally against live data with instant preview — login, theme dev, pull, push.",
5
5
  "type": "module",
6
6
  "license": "MIT",