@blocofy/cli 0.1.8 → 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 +52 -2
- package/lib/content-sync.mjs +58 -0
- package/lib/messages.mjs +28 -0
- package/package.json +1 -1
package/bin/blocofy.mjs
CHANGED
|
@@ -12,15 +12,16 @@ 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";
|
|
18
|
-
import { githubNote } from "../lib/messages.mjs";
|
|
19
|
+
import { githubNote, statusLine, syncScopeNote } from "../lib/messages.mjs";
|
|
19
20
|
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.
|
|
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("--"));
|
|
@@ -215,12 +245,22 @@ async function themeDev(rest) {
|
|
|
215
245
|
console.log(` (l) Local ${hyperlink(localUrl)}`);
|
|
216
246
|
if (previewUrl) console.log(` (p) Preview ${hyperlink(previewUrl)}`);
|
|
217
247
|
if (editorUrl) console.log(` (e) Editor ${hyperlink(editorUrl)}`);
|
|
248
|
+
|
|
249
|
+
// Kalıcı durum satırı (#119 CLI bulgu #3): yerel hangi taslağa gidiyor + canlı tema.
|
|
250
|
+
const status = statusLine(session);
|
|
251
|
+
if (status) console.log(`\n ${status}`);
|
|
252
|
+
|
|
218
253
|
const keys = ["l local", previewUrl && "p preview", editorUrl && "e editor", "q quit"]
|
|
219
254
|
.filter(Boolean)
|
|
220
255
|
.join(" ");
|
|
221
256
|
console.log(`\n Press: ${keys}`);
|
|
222
257
|
console.log(` Edit a theme file and save — every open view reloads automatically.\n`);
|
|
223
258
|
|
|
259
|
+
// Senkron kapsamı (#119 CLI bulgu #2): hangi dizinler taşınıyor / taşınmıyor —
|
|
260
|
+
// "config/pages senkronlanıyor sandım" karışıklığını açıkça önler.
|
|
261
|
+
for (const line of syncScopeNote()) console.log(` ${line}`);
|
|
262
|
+
console.log("");
|
|
263
|
+
|
|
224
264
|
// Düzlem uyarısı: CLI yalnız tema KODUNU taşır; editörde yapılan içerik/ayar
|
|
225
265
|
// bulutta yaşar (githubNote → lib/messages.mjs, oturum durumuna göre uyarlanır).
|
|
226
266
|
const note = githubNote(session);
|
|
@@ -334,6 +374,16 @@ if (first === "--version" || first === "-v") {
|
|
|
334
374
|
console.error(error?.message ?? error);
|
|
335
375
|
process.exit(1);
|
|
336
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
|
+
});
|
|
337
387
|
} else {
|
|
338
388
|
console.error(`Unknown command: ${args.join(" ")}\n`);
|
|
339
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/lib/messages.mjs
CHANGED
|
@@ -3,6 +3,34 @@
|
|
|
3
3
|
* CLI yalnız tema KODUNU taşır; editörde yapılan içerik/ayar bulutta yaşar.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { THEME_DIRS } from "./local-theme.mjs";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Senkron kapsamı (#119 CLI bulgu #2) — geliştiriciye AÇIKÇA hangi dizinlerin
|
|
10
|
+
* platforma taşındığını (tema kodu) ve hangilerinin taşınMADIĞINI (editör/bulut
|
|
11
|
+
* içeriği) söyler; "config/pages senkronlanıyor sandım" kafa karışıklığını önler.
|
|
12
|
+
* 2 satırlık dizi döner (saf → test edilebilir).
|
|
13
|
+
*/
|
|
14
|
+
export function syncScopeNote() {
|
|
15
|
+
const synced = [...THEME_DIRS].sort().map((d) => `${d}/`).join(" ");
|
|
16
|
+
return [
|
|
17
|
+
`Synced (theme code): ${synced}`,
|
|
18
|
+
`Not synced (cloud): config/ pages/ — edit these in the admin editor`,
|
|
19
|
+
];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Kalıcı durum satırı (#119 CLI bulgu #3) — yerel dosyaların hangi taslak temaya
|
|
24
|
+
* gittiğini ve canlı temanın hangi instance olduğunu gösterir. Oturum yoksa null
|
|
25
|
+
* (local-only mod). `liveThemeId` yoksa "(none)".
|
|
26
|
+
*/
|
|
27
|
+
export function statusLine(session) {
|
|
28
|
+
if (!session) return null;
|
|
29
|
+
const draft = session.draftInstanceId != null ? `#${session.draftInstanceId}` : "—";
|
|
30
|
+
const live = session.liveThemeId != null ? `#${session.liveThemeId}` : "(none)";
|
|
31
|
+
return `Local files → Draft theme ${draft} · Live theme → ${live}`;
|
|
32
|
+
}
|
|
33
|
+
|
|
6
34
|
/**
|
|
7
35
|
* Dev oturumuna göre "düzlem" uyarısı döndürür (string), oturum yoksa null.
|
|
8
36
|
* - GitHub bağlı → repo@branch + `git pull` ipucu.
|