@blocofy/cli 0.1.0 → 0.1.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/README.md CHANGED
@@ -1,48 +1,65 @@
1
1
  # @blocofy/cli
2
2
 
3
- Blocofy tema geliştirme CLI — Shopify CLI modeli: local tema + **canlı veri** ile anında
4
- önizleme, sonra yayına alma. CLI **build almaz**; asset'leri kendi araçlarınla
5
- (npm/Vite/Tailwind) üretirsin, platform Liquid + statik asset olarak sunar.
3
+ Develop your [Blocofy](https://blocofy.com) theme locally against **live data**, see an
4
+ instant preview, and publish. The CLI does **not** build assets — generate them with your
5
+ own tools (npm/Vite/Tailwind); the platform serves plain Liquid + static assets.
6
6
 
7
7
  ```bash
8
- npx @blocofy/cli login # site URL + dev token (admin → Ayarlar → Tema CLI token'ları)
9
- cd path/to/tema
10
- npx @blocofy/cli theme dev # http://localhost:3030 — local tema + canlı veri, livereload
8
+ npx @blocofy/cli login # site URL + dev token (admin panel: Settings → Theme CLI tokens)
9
+ cd path/to/theme
10
+ npx @blocofy/cli theme dev # http://localhost:3030 — local theme + live data, livereload
11
11
  ```
12
12
 
13
- ## Komutlar
13
+ ## Commands
14
14
 
15
15
  ```bash
16
16
  blocofy login [--url <url>] [--token <bcf_…>]
17
- # Platform URL + dev token kaydet (~/.blocofy/credentials.json, 0600).
18
- blocofy theme dev [dir] # Local dev sunucusu: düzenle → tarayıcı otomatik yenilenir.
19
- # --port <n> (varsayılan 3030)
20
- blocofy theme pull [dir] # Canlı temayı diske indir.
21
- blocofy theme push [dir] # Local temayı canlı siteye yaz (create/update; silme yok).
17
+ # Save your platform URL + dev token (~/.blocofy/credentials.json, 0600).
18
+ blocofy theme dev [dir] # Local dev server: edit a file and the browser reloads automatically.
19
+ # --port <n> (default 3030)
20
+ blocofy theme pull [dir] # Download the live theme to disk.
21
+ blocofy theme push [dir] # Write the local theme to the live site (create/update; no delete).
22
22
  blocofy --version
23
23
  blocofy --help
24
24
  ```
25
25
 
26
- ## Nasıl çalışır
26
+ ## How it works
27
27
 
28
- `theme dev` kendi http sunucusunu açar; her sayfa isteğinde local tema dosyalarını okuyup
29
- platformun dev-render endpoint'ine (`/api/dev/render`) gönderir. Platform site'ın **canlı
30
- verisiyle** render edip HTML döner — yani CLI render motorunu içermez, üretimle birebir aynı
31
- çıktı görürsün. `fs.watch` ile dosya değişince tarayıcı SSE üzerinden yenilenir.
28
+ `theme dev` starts a local HTTP server. For each page request it reads your local theme
29
+ files and sends them to the platform's dev-render endpoint (`/api/dev/render`). The platform
30
+ renders them with the site's **live data** and returns HTML — so the CLI ships no rendering
31
+ engine and you see exactly the production output. A file watcher reloads the browser over
32
+ Server-Sent Events when you save.
32
33
 
33
- Kimlik bilgisi: `~/.blocofy/credentials.json` ya da `BLOCOFY_URL` + `BLOCOFY_TOKEN` ortam
34
- değişkenleri (CI/agent için).
34
+ Credentials come from `~/.blocofy/credentials.json` (written by `blocofy login`) or the
35
+ `BLOCOFY_URL` + `BLOCOFY_TOKEN` environment variables (for CI/automation).
35
36
 
36
- ## Geliştirme
37
+ ## Theme structure
37
38
 
38
- Zero-dependency (yalnız Node yerleşikleri). Test:
39
+ A theme is a directory of files grouped by top-level folder:
40
+
41
+ | Folder | Contents |
42
+ | --- | --- |
43
+ | `layout/` | Page shell (`theme.liquid`) |
44
+ | `section/` | Page sections (`Hero.liquid`, `FeaturedCards.liquid`, …) |
45
+ | `block/` | Repeatable pieces used inside sections |
46
+ | `partial/` | Shared snippets (header/footer, …) |
47
+ | `asset/` | CSS and static files (`theme.css`) |
48
+ | `pages/<slug>.json` | Page content (`/` → `pages/index.json`) |
49
+ | `config/settings.json` | Theme settings + color schemes |
50
+
51
+ Liquid templates use the `.liquid` extension; files under `asset/` are served as-is.
52
+
53
+ ## Development
54
+
55
+ Zero runtime dependencies (Node built-ins only). Run the tests with:
39
56
 
40
57
  ```bash
41
58
  node --test
42
59
  ```
43
60
 
44
- Node ≥ 18 gerekir.
61
+ Requires Node ≥ 18.
45
62
 
46
- ## Lisans
63
+ ## License
47
64
 
48
65
  [MIT](./LICENSE)
package/bin/blocofy.mjs CHANGED
@@ -1,30 +1,25 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * Blocofy tema CLI (#119). Shopify CLI modeli: local tema + canlı veri instant
4
- * preview, sonra yayına alma.
3
+ * Blocofy theme CLI. Develop your theme locally against live data with instant
4
+ * preview, then publish.
5
5
  *
6
- * İki mod:
7
- * - Standalone (Faz 3d): `blocofy login` ile platform URL + dev token kaydet →
8
- * `blocofy theme dev` kendi http sunucusunu açar, her isteği platformun
9
- * `/api/dev/render`'ına proxy'ler (local tema + canlı veri), fs.watch ile
10
- * livereload. Monorepo GEREKMEZ → external müşteri `npx @blocofy/cli` ile.
11
- * - Monorepo (Faz 2 v1): login yoksa, monorepo içindeyse renderer'ı THEME_DEV_DIR
12
- * ile spawn eder (fallback).
6
+ * `blocofy login` stores your platform URL + dev token, then `blocofy theme dev`
7
+ * starts a local server that proxies each request to the platform's
8
+ * `/api/dev/render` endpoint (local theme files + live data) with file-watch
9
+ * livereload. No monorepo required.
13
10
  */
14
- import { spawn } from "node:child_process";
15
11
  import { existsSync } from "node:fs";
16
- import { dirname, resolve } from "node:path";
12
+ import { resolve } from "node:path";
17
13
  import { createInterface } from "node:readline/promises";
18
- import { fileURLToPath } from "node:url";
19
14
 
20
15
  import { credentialsPath, loadCredentials, saveCredentials } from "../lib/credentials.mjs";
21
16
  import { startDevServer } from "../lib/dev-server.mjs";
22
17
  import { pullTheme, pushTheme } from "../lib/theme-sync.mjs";
23
18
 
24
- const VERSION = "0.1.0";
19
+ const VERSION = "0.1.1";
25
20
  const args = process.argv.slice(2);
26
21
 
27
- /** `--key value` ve `--flag` (boolean) ayrıştırıcı. */
22
+ /** Parse `--key value` and `--flag` (boolean) arguments. */
28
23
  function parseFlags(rest) {
29
24
  const flags = {};
30
25
  for (let i = 0; i < rest.length; i++) {
@@ -42,36 +37,23 @@ function parseFlags(rest) {
42
37
  return flags;
43
38
  }
44
39
 
45
- function findMonorepoRoot(start) {
46
- let dir = start;
47
- for (let i = 0; i < 8; i++) {
48
- if (existsSync(resolve(dir, "pnpm-workspace.yaml"))) return dir;
49
- const parent = resolve(dir, "..");
50
- if (parent === dir) break;
51
- dir = parent;
52
- }
53
- return null;
54
- }
55
-
56
40
  function printHelp() {
57
- console.log(`blocofy — Blocofy tema geliştirme CLI (v${VERSION})
41
+ console.log(`blocofy — Blocofy theme development CLI (v${VERSION})
58
42
 
59
- Kullanım:
43
+ Usage:
60
44
  blocofy login [--url <url>] [--token <bcf_…>]
61
- Platform URL + dev token kaydet (~/.blocofy/credentials.json).
62
- Token: admin → Ayarlar → Tema CLI token'ları.
63
- blocofy theme dev [dir] Local tema dev sunucusu — local tema + canlı veri instant
64
- preview. Giriş yapıldıysa standalone (monorepo gerekmez);
65
- yoksa monorepo içinde renderer'ı sarar. dir varsayılan: cwd.
66
- --port <n> (varsayılan 3030), --standalone, --monorepo
67
- blocofy theme pull [dir] Canlı temayı diske indir (giriş gerekli). dir varsayılan: cwd.
68
- blocofy theme push [dir] Local temayı canlı siteye yaz (create/update; v1 silme yok).
45
+ Save your platform URL + dev token (~/.blocofy/credentials.json).
46
+ Get a token from your admin panel: Settings → Theme CLI tokens.
47
+ blocofy theme dev [dir] Local dev server — local theme + live data with instant preview.
48
+ Edit a file and the browser reloads automatically. dir defaults to cwd.
49
+ --port <n> (default 3030)
50
+ blocofy theme pull [dir] Download the live theme to disk. dir defaults to cwd.
51
+ blocofy theme push [dir] Write the local theme to the live site (create/update; no delete).
69
52
  blocofy --version
70
53
  blocofy --help
71
54
 
72
- Yakında (#119): blocofy init, blocofy check.
73
- Not: CLI build ALMAZ — asset'leri kendi araçlarınla (npm/Vite/Tailwind) üretirsin;
74
- platform Liquid + statik asset olarak kalır.`);
55
+ The CLI does not build assets — generate them with your own tools (npm/Vite/Tailwind);
56
+ the platform serves plain Liquid + static assets.`);
75
57
  }
76
58
 
77
59
  async function login(rest) {
@@ -82,7 +64,7 @@ async function login(rest) {
82
64
  if (!url || !token) {
83
65
  const rl = createInterface({ input: process.stdin, output: process.stdout });
84
66
  try {
85
- if (!url) url = (await rl.question("Platform/site URL (örn. https://magaza.myblocofy.com): ")).trim();
67
+ if (!url) url = (await rl.question("Platform/site URL (e.g. https://store.myblocofy.com): ")).trim();
86
68
  if (!token) token = (await rl.question("Dev token (bcf_…): ")).trim();
87
69
  } finally {
88
70
  rl.close();
@@ -91,24 +73,24 @@ async function login(rest) {
91
73
 
92
74
  url = url.replace(/\/+$/, "");
93
75
  if (!/^https?:\/\//.test(url)) {
94
- console.error("Geçersiz URL — http(s):// ile başlamalı.");
76
+ console.error("Invalid URL — must start with http(s)://");
95
77
  process.exit(1);
96
78
  }
97
79
  if (!token.startsWith("bcf_")) {
98
- console.error("Geçersiz token — bcf_ ile başlamalı (admin → Ayarlar → Tema CLI token'ları).");
80
+ console.error("Invalid token — must start with bcf_ (admin panel: Settings → Theme CLI tokens).");
99
81
  process.exit(1);
100
82
  }
101
83
 
102
84
  saveCredentials({ url, token });
103
- console.log(`Giriş kaydedildi: ${credentialsPath()}`);
104
- console.log(`Artık tema dizininde: blocofy theme dev`);
85
+ console.log(`Saved credentials: ${credentialsPath()}`);
86
+ console.log(`Now run, in your theme directory: blocofy theme dev`);
105
87
  }
106
88
 
107
- /** Giriş bilgisi (URL+token) çöz ya da hata ver. pull/push standalone gerektirir. */
89
+ /** Resolve credentials (URL + token) or exit with guidance. */
108
90
  function requireCreds() {
109
91
  const creds = loadCredentials();
110
92
  if (!creds || !creds.url || !creds.token) {
111
- console.error("Giriş gerekli: `blocofy login` (ya da BLOCOFY_URL + BLOCOFY_TOKEN).");
93
+ console.error("Login required: run `blocofy login` (or set BLOCOFY_URL + BLOCOFY_TOKEN).");
112
94
  process.exit(1);
113
95
  }
114
96
  return creds;
@@ -119,22 +101,22 @@ async function themePull(rest) {
119
101
  const dir = resolve(positional[0] ?? process.cwd());
120
102
  const creds = requireCreds();
121
103
  const { count } = await pullTheme({ dir, url: creds.url, token: creds.token });
122
- console.log(`${count} tema dosyası indirildi → ${dir}`);
104
+ console.log(`Downloaded ${count} theme files → ${dir}`);
123
105
  }
124
106
 
125
107
  async function themePush(rest) {
126
108
  const positional = rest.filter((a) => !a.startsWith("--"));
127
109
  const dir = resolve(positional[0] ?? process.cwd());
128
110
  if (!existsSync(dir)) {
129
- console.error(`Tema dizini yok: ${dir}`);
111
+ console.error(`Theme directory not found: ${dir}`);
130
112
  process.exit(1);
131
113
  }
132
114
  const creds = requireCreds();
133
115
  const result = await pushTheme({ dir, url: creds.url, token: creds.token });
134
116
  const extra = result.skippedDeletes
135
- ? `, ${result.skippedDeletes} uzak dosya yerelde yok (v1: silinmedi)`
117
+ ? `, ${result.skippedDeletes} remote file(s) absent locally (not deleted)`
136
118
  : "";
137
- console.log(`Push: ${result.created} yeni, ${result.updated} güncellendi${extra}.`);
119
+ console.log(`Push: ${result.created} created, ${result.updated} updated${extra}.`);
138
120
  }
139
121
 
140
122
  function themeDev(rest) {
@@ -143,83 +125,45 @@ function themeDev(rest) {
143
125
  const themeDir = resolve(positional[0] ?? process.cwd());
144
126
 
145
127
  if (!existsSync(themeDir)) {
146
- console.error(`Tema dizini yok: ${themeDir}`);
128
+ console.error(`Theme directory not found: ${themeDir}`);
147
129
  process.exit(1);
148
130
  }
149
131
 
150
- const creds = flags.monorepo ? null : loadCredentials();
151
- const canStandalone = Boolean(creds && creds.url && creds.token);
152
-
153
- // Standalone: giriş yapıldıysa (ya da --standalone zorlanırsa).
154
- if (canStandalone || flags.standalone) {
155
- if (!canStandalone) {
156
- console.error("Standalone için giriş gerekli: `blocofy login` (ya da BLOCOFY_URL + BLOCOFY_TOKEN).");
157
- process.exit(1);
158
- }
159
- const port = Number(flags.port) || 3030;
160
- console.log(`blocofy theme dev (standalone)`);
161
- console.log(` tema dizini : ${themeDir}`);
162
- console.log(` platform : ${creds.url}`);
163
- console.log(` token : ${creds.token.slice(0, 8)}… (${creds.source})`);
164
- console.log(` önizleme : http://localhost:${port}`);
165
- console.log(` → tema dosyasını düzenle, kaydet = tarayıcı otomatik yenilenir (livereload)`);
166
-
167
- if (flags.dry) {
168
- console.log("(--dry: sunucu başlatılmadı)");
169
- return;
170
- }
171
-
172
- const handle = startDevServer({
173
- dir: themeDir,
174
- url: creds.url,
175
- token: creds.token,
176
- port,
177
- onError: (err) => {
178
- if (err && err.code === "EADDRINUSE") {
179
- console.error(`Port ${port} kullanımda. Farklı port: blocofy theme dev --port <n>`);
180
- } else {
181
- console.error(`Sunucu hatası: ${err?.message ?? err}`);
182
- }
183
- process.exit(1);
184
- },
185
- });
186
- const shutdown = () => {
187
- handle.close();
188
- process.exit(0);
189
- };
190
- process.on("SIGINT", shutdown);
191
- process.on("SIGTERM", shutdown);
192
- return;
193
- }
194
-
195
- // Monorepo fallback (Faz 2 v1): renderer'ı THEME_DEV_DIR ile spawn et.
196
- const root = findMonorepoRoot(dirname(fileURLToPath(import.meta.url)));
197
- if (!root) {
198
- console.error(
199
- "Ne giriş yapıldı ne de monorepo bulundu.\n" +
200
- "External kullanım için: `blocofy login` (admin'den dev token al).\n" +
201
- "Monorepo içinde çalışıyorsan pnpm-workspace.yaml görünür bir dizinden çalıştır.",
202
- );
203
- process.exit(1);
204
- }
132
+ const creds = requireCreds();
133
+ const port = Number(flags.port) || 3030;
205
134
 
206
- console.log(`blocofy theme dev (monorepo)`);
207
- console.log(` tema dizini : ${themeDir}`);
208
- console.log(` renderer : ${root} (THEME_DEV_DIR ile)`);
209
- console.log(` önizleme : http://<slug>.localhost:3003 (örn. test-cafe.localhost)`);
210
- console.log(` → tema dosyasını düzenle, tarayıcıyı yenile = instant preview (gerçek içerikle)`);
135
+ console.log(`blocofy theme dev`);
136
+ console.log(` theme dir : ${themeDir}`);
137
+ console.log(` platform : ${creds.url}`);
138
+ console.log(` token : ${creds.token.slice(0, 8)}… (${creds.source})`);
139
+ console.log(` preview : http://localhost:${port}`);
140
+ console.log(` → edit a theme file and save = the browser reloads automatically (livereload)`);
211
141
 
212
142
  if (flags.dry) {
213
- console.log("(--dry: renderer başlatılmadı)");
143
+ console.log("(--dry: server not started)");
214
144
  return;
215
145
  }
216
146
 
217
- const child = spawn("pnpm", ["--filter", "renderer", "dev"], {
218
- cwd: root,
219
- env: { ...process.env, THEME_DEV_DIR: themeDir },
220
- stdio: "inherit",
147
+ const handle = startDevServer({
148
+ dir: themeDir,
149
+ url: creds.url,
150
+ token: creds.token,
151
+ port,
152
+ onError: (err) => {
153
+ if (err && err.code === "EADDRINUSE") {
154
+ console.error(`Port ${port} is in use. Try a different port: blocofy theme dev --port <n>`);
155
+ } else {
156
+ console.error(`Server error: ${err?.message ?? err}`);
157
+ }
158
+ process.exit(1);
159
+ },
221
160
  });
222
- child.on("exit", (code) => process.exit(code ?? 0));
161
+ const shutdown = () => {
162
+ handle.close();
163
+ process.exit(0);
164
+ };
165
+ process.on("SIGINT", shutdown);
166
+ process.on("SIGTERM", shutdown);
223
167
  }
224
168
 
225
169
  const [first, ...rest] = args;
@@ -246,7 +190,7 @@ if (first === "--version" || first === "-v") {
246
190
  process.exit(1);
247
191
  });
248
192
  } else {
249
- console.error(`Bilinmeyen komut: ${args.join(" ")}\n`);
193
+ console.error(`Unknown command: ${args.join(" ")}\n`);
250
194
  printHelp();
251
195
  process.exit(1);
252
196
  }
@@ -3,11 +3,11 @@ import { homedir } from "node:os";
3
3
  import { join } from "node:path";
4
4
 
5
5
  /**
6
- * Blocofy CLI kimlik bilgileri (#119 Faz 3d). `blocofy login` platform URL'i +
7
- * dev token'ı `~/.blocofy/credentials.json`'a (0600) yazar; `blocofy theme dev`
8
- * standalone modu bunu okur. Env (BLOCOFY_URL/BLOCOFY_TOKEN) dosyayı geçersiz
9
- * kılar (CI/agent). Token plaintext diskte tutulur (Shopify/gh CLI deseni) →
10
- * dosya izni 0600.
6
+ * Blocofy CLI credentials. `blocofy login` writes the platform URL + dev token to
7
+ * `~/.blocofy/credentials.json` (0600); `blocofy theme dev` reads it. Environment
8
+ * variables (BLOCOFY_URL/BLOCOFY_TOKEN) take precedence over the file (CI/agents).
9
+ * The token is stored in plaintext on disk (the usual dev-CLI pattern), so the file
10
+ * is locked down to 0600.
11
11
  */
12
12
 
13
13
  const DIR = join(homedir(), ".blocofy");
@@ -17,16 +17,16 @@ export function credentialsPath() {
17
17
  return FILE;
18
18
  }
19
19
 
20
- /** URL + token'ı 0600 izniyle yaz. */
20
+ /** Write URL + token with 0600 permissions. */
21
21
  export function saveCredentials({ url, token }) {
22
22
  mkdirSync(DIR, { recursive: true, mode: 0o700 });
23
23
  writeFileSync(FILE, JSON.stringify({ url, token }, null, 2) + "\n", { mode: 0o600 });
24
- chmodSync(FILE, 0o600); // dosya zaten varsa create-mode uygulanmaz → zorla
24
+ chmodSync(FILE, 0o600); // create-mode is ignored if the file already exists → enforce it
25
25
  }
26
26
 
27
27
  /**
28
- * Kimlik bilgisi çöz: önce env (tam çift), sonra dosya. `{ url, token, source }`
29
- * ya da null. URL sondaki `/`'lardan arındırılır.
28
+ * Resolve credentials: environment first (both vars), then the file. Returns
29
+ * `{ url, token, source }` or null. Trailing slashes are stripped from the URL.
30
30
  */
31
31
  export function loadCredentials() {
32
32
  const envUrl = process.env.BLOCOFY_URL;
@@ -40,7 +40,7 @@ export function loadCredentials() {
40
40
  return { url: stripSlash(data.url), token: data.token, source: "file" };
41
41
  }
42
42
  } catch {
43
- // dosya yok/bozuk → null
43
+ // missing/corrupt file → null
44
44
  }
45
45
  return null;
46
46
  }
@@ -10,13 +10,13 @@ import {
10
10
  } from "./local-theme.mjs";
11
11
 
12
12
  /**
13
- * Standalone dev sunucusu (#119 Faz 3d). Her sayfa isteğinde local tema dosyalarını
14
- * okuyup platformun `/api/dev/render`'ına yollar, dönen HTML'e livereload script'i
15
- * enjekte eder. `fs.watch` ile dosya değişince SSE üzerinden tarayıcıyı yeniler.
13
+ * Standalone dev server. For each page request it reads the local theme files and
14
+ * posts them to the platform's `/api/dev/render` endpoint, then injects a livereload
15
+ * script into the returned HTML. `fs.watch` reloads the browser over SSE on change.
16
16
  *
17
- * Asset'ler genelde `{% render 'asset/x.css' %}` ile HTML'e GÖMÜLÜ gelir (ayrı
18
- * istek yok); doğrudan `/asset/*` referansları için diskten statik servis edilir.
19
- * Görsel/JS uzak (Directus/CDN) → local servise gerek yok.
17
+ * Assets are usually inlined into the HTML via `{% render 'asset/x.css' %}` (no
18
+ * separate request); direct `/asset/*` references are served from disk. Images/JS
19
+ * are remote (Directus/CDN), so no local serving is needed for them.
20
20
  */
21
21
  export function startDevServer({ dir, url, token, port, onError }) {
22
22
  const base = url.replace(/\/+$/, "");
@@ -35,7 +35,7 @@ export function startDevServer({ dir, url, token, port, onError }) {
35
35
  try {
36
36
  message = JSON.parse(text).error ?? text;
37
37
  } catch {
38
- /* ham metin */
38
+ /* raw text */
39
39
  }
40
40
  return { status: res.status, html: errorHtml(res.status, message) };
41
41
  }
@@ -50,7 +50,7 @@ export function startDevServer({ dir, url, token, port, onError }) {
50
50
  try {
51
51
  const pathname = decodeURIComponent(new URL(req.url, "http://localhost").pathname);
52
52
 
53
- // Livereload SSE kanalı.
53
+ // Livereload SSE channel.
54
54
  if (pathname === LIVERELOAD_PATH) {
55
55
  res.writeHead(200, {
56
56
  "content-type": "text/event-stream",
@@ -63,7 +63,7 @@ export function startDevServer({ dir, url, token, port, onError }) {
63
63
  return;
64
64
  }
65
65
 
66
- // Doğrudan /asset/* referansı → diskten servis et (varsa).
66
+ // Direct /asset/* reference → serve from disk (if present).
67
67
  const assetFile = safeAssetFile(dir, pathname);
68
68
  if (assetFile) {
69
69
  res.writeHead(200, { "content-type": contentTypeFor(assetFile), "cache-control": "no-store" });
@@ -71,7 +71,7 @@ export function startDevServer({ dir, url, token, port, onError }) {
71
71
  return;
72
72
  }
73
73
 
74
- // Sayfa → platforma render ettir.
74
+ // Page → render on the platform.
75
75
  const { status, html } = await renderPath(pathname);
76
76
  res.writeHead(status, { "content-type": "text/html; charset=utf-8", "cache-control": "no-store" });
77
77
  res.end(injectLivereload(html));
@@ -95,7 +95,7 @@ export function startDevServer({ dir, url, token, port, onError }) {
95
95
  try {
96
96
  watcher?.close();
97
97
  } catch {
98
- /* yoksay */
98
+ /* ignore */
99
99
  }
100
100
  for (const client of clients) client.end();
101
101
  server.close();
@@ -104,8 +104,9 @@ export function startDevServer({ dir, url, token, port, onError }) {
104
104
  }
105
105
 
106
106
  /**
107
- * `dir`'i izle; recursive desteklenmiyorsa (eski Node/Linux) recursive'siz dener.
108
- * Debounce'lu `onChange` çağırır. İzlenemezse null (livereload pasif).
107
+ * Watch `dir`; if recursive watching is unsupported (older Node/Linux), fall back to
108
+ * a non-recursive watch. Calls `onChange` debounced. Returns null if unwatchable
109
+ * (livereload disabled).
109
110
  */
110
111
  function startWatch(dir, onChange) {
111
112
  let timer = null;
@@ -124,7 +125,7 @@ function startWatch(dir, onChange) {
124
125
  }
125
126
  }
126
127
 
127
- /** `/asset/*` yolunu disk dosyasına eşle (path-traversal korumalı); yoksa null. */
128
+ /** Map a `/asset/*` path to a disk file (path-traversal safe); null if missing. */
128
129
  function safeAssetFile(dir, pathname) {
129
130
  if (!pathname.startsWith("/asset/")) return null;
130
131
  const root = normalize(dir);
@@ -133,7 +134,7 @@ function safeAssetFile(dir, pathname) {
133
134
  try {
134
135
  if (existsSync(full) && statSync(full).isFile()) return full;
135
136
  } catch {
136
- /* yoksay */
137
+ /* ignore */
137
138
  }
138
139
  return null;
139
140
  }
@@ -144,12 +145,12 @@ function escapeHtml(value) {
144
145
 
145
146
  function errorHtml(status, message) {
146
147
  return (
147
- `<!doctype html><html lang="tr"><head><meta charset="utf-8">` +
148
+ `<!doctype html><html lang="en"><head><meta charset="utf-8">` +
148
149
  `<title>blocofy dev — ${status}</title></head>` +
149
150
  `<body style="font-family:system-ui,sans-serif;padding:2rem;max-width:48rem;margin:0 auto">` +
150
- `<h1 style="color:#b91c1c">Render hatası (${status})</h1>` +
151
+ `<h1 style="color:#b91c1c">Render error (${status})</h1>` +
151
152
  `<pre style="white-space:pre-wrap;background:#f4f4f5;padding:1rem;border-radius:.5rem;color:#333">${escapeHtml(message)}</pre>` +
152
- `<p style="color:#666">Tema dosyasını düzelt — kaydedince bu sayfa otomatik yenilenir.</p>` +
153
+ `<p style="color:#666">Fix the theme file — this page reloads automatically when you save.</p>` +
153
154
  `</body></html>`
154
155
  );
155
156
  }
@@ -2,25 +2,24 @@ import { readdirSync, readFileSync, statSync } from "node:fs";
2
2
  import { extname, join, relative, sep } from "node:path";
3
3
 
4
4
  /**
5
- * Local tema dosyalarını okuma + livereload — `@blocofy/cms`'in zero-dep aynası
6
- * (standalone CLI cms'i bundle edemez). `readLocalTemplates`, cms'teki
7
- * `loadLocalTemplates` (templates.ts) ile BİREBİR aynı eşlemeyi yapar:
8
- * üst-dizini THEME_DIRS olan dosyalar, key = göreli yol, `.liquid` strip'li.
5
+ * Reading local theme files + livereload helpers. `readLocalTemplates` mirrors the
6
+ * platform's own theme loader exactly: files whose top-level directory is one of
7
+ * THEME_DIRS, keyed by relative path with the `.liquid` extension stripped.
9
8
  */
10
9
 
11
10
  const THEME_DIRS = new Set(["layout", "section", "block", "partial", "asset", "template"]);
12
11
  const LIQUID_KINDS = new Set(["layout", "section", "block", "partial", "template"]);
13
12
 
14
13
  /**
15
- * Stripped key → disk yolu (cms `repoPathFor`'un aynası): liquid kind'lara `.liquid`
16
- * ekler, asset'i ham bırakır. `pull` diske yazarken kullanır (readLocalTemplates tersi).
14
+ * Stripped key → on-disk path: re-adds `.liquid` for Liquid kinds, leaves assets
15
+ * raw. Used by `pull` when writing to disk (the inverse of readLocalTemplates).
17
16
  */
18
17
  export function localPathFor(key) {
19
18
  const top = key.split("/")[0] ?? "";
20
19
  return LIQUID_KINDS.has(top) && !key.endsWith(".liquid") ? `${key}.liquid` : key;
21
20
  }
22
21
 
23
- /** Tema dizinini `{path: content}` haritasına çevir (dev-render'a gönderilir). */
22
+ /** Turn a theme directory into a `{ path: content }` map (sent to dev-render). */
24
23
  export function readLocalTemplates(dir) {
25
24
  const out = {};
26
25
  const walk = (current) => {
@@ -48,7 +47,7 @@ const LIVERELOAD_SNIPPET = `<script>
48
47
 
49
48
  export { LIVERELOAD_PATH };
50
49
 
51
- /** Render edilen HTML'e livereload script'ini ekle (</body>'den önce). */
50
+ /** Inject the livereload script into rendered HTML (before </body>). */
52
51
  export function injectLivereload(html) {
53
52
  if (typeof html !== "string") return html;
54
53
  return html.includes("</body>")
@@ -76,7 +75,7 @@ const CONTENT_TYPES = {
76
75
  ".map": "application/json",
77
76
  };
78
77
 
79
- /** Uzantıdan content-type (statik asset servisi için). */
78
+ /** Content-type from a file extension (for static asset serving). */
80
79
  export function contentTypeFor(p) {
81
80
  return CONTENT_TYPES[extname(p).toLowerCase()] ?? "application/octet-stream";
82
81
  }
@@ -4,9 +4,9 @@ import { dirname, join } from "node:path";
4
4
  import { localPathFor, readLocalTemplates } from "./local-theme.mjs";
5
5
 
6
6
  /**
7
- * `blocofy theme pull/push` (#119 Faz 3e). Platformun `/api/dev/theme` endpoint'iyle
8
- * konuşur (token Bearer). pull = GET → diske yaz (.liquid re-map); push = readLocalTemplates
9
- * → POST (create/update; v1 silme yok).
7
+ * `blocofy theme pull/push`. Talks to the platform's `/api/dev/theme` endpoint
8
+ * (Bearer token). pull = GET → write to disk (re-adding `.liquid`); push =
9
+ * readLocalTemplates → POST (create/update; no delete).
10
10
  */
11
11
 
12
12
  async function errorText(res) {
@@ -18,7 +18,7 @@ async function errorText(res) {
18
18
  }
19
19
  }
20
20
 
21
- /** Canlı temayı diske indir. `{path: content}` (stripped) → `.liquid`'li disk dosyaları. */
21
+ /** Download the live theme to disk. `{ path: content }` (stripped) → `.liquid` files. */
22
22
  export async function pullTheme({ dir, url, token }) {
23
23
  const base = url.replace(/\/+$/, "");
24
24
  const res = await fetch(`${base}/api/dev/theme`, { headers: { authorization: `Bearer ${token}` } });
@@ -34,7 +34,7 @@ export async function pullTheme({ dir, url, token }) {
34
34
  return { count };
35
35
  }
36
36
 
37
- /** Local temayı canlı siteye yaz (create/update; v1 silme yok). */
37
+ /** Write the local theme to the live site (create/update; no delete). */
38
38
  export async function pushTheme({ dir, url, token }) {
39
39
  const base = url.replace(/\/+$/, "");
40
40
  const files = readLocalTemplates(dir);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@blocofy/cli",
3
- "version": "0.1.0",
4
- "description": "Blocofy tema geliştirme CLI — local dev, pull, push (Shopify CLI modeli).",
3
+ "version": "0.1.1",
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",
7
7
  "author": "Blocofy",
@@ -18,7 +18,7 @@
18
18
  "theme",
19
19
  "cli",
20
20
  "liquid",
21
- "shopify-cli"
21
+ "theme-development"
22
22
  ],
23
23
  "bin": {
24
24
  "blocofy": "./bin/blocofy.mjs"