uvako-cli 0.4.0 → 0.6.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/README.md +37 -1
- package/bin/uvako-cli.js +12 -7
- package/package.json +9 -3
- package/src/api.js +7 -4
- package/src/app.js +412 -0
- package/src/check.js +78 -4
- package/src/config.js +44 -2
- package/src/files.js +2 -1
- package/src/init.js +97 -6
- package/src/locales/en.js +165 -12
- package/src/main.js +76 -20
- package/src/partner.js +135 -0
- package/src/remote.js +80 -7
- package/src/rules.generated.js +452 -0
- package/src/sections.js +333 -0
- package/src/svg.js +152 -0
- package/templates/app/README.md +65 -0
- package/templates/app/blocks/yildiz-puani/block.css +5 -0
- package/templates/app/blocks/yildiz-puani/block.html +8 -0
- package/templates/app/blocks/yildiz-puani/block.json +11 -0
- package/templates/app/server.js +357 -0
package/src/check.js
CHANGED
|
@@ -2,6 +2,9 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { walk } from "./files.js";
|
|
4
4
|
import { t } from "./i18n.js";
|
|
5
|
+
import { checkSVG } from "./svg.js";
|
|
6
|
+
import { rules } from "./rules.generated.js";
|
|
7
|
+
import { checkPageTemplate, checkSectionSchema, checkSources, platformSchemas, sectionFile, templateTexts } from "./sections.js";
|
|
5
8
|
|
|
6
9
|
// Tema denetimi. Mağazaya yüklenen paketi platform aynı kurallarla yeniden denetler; buradaki
|
|
7
10
|
// denetim geliştiriciye hatayı yüklemeden önce göstermek içindir, güvenlik kararı sunucudadır.
|
|
@@ -11,7 +14,8 @@ import { t } from "./i18n.js";
|
|
|
11
14
|
// üstüne ayar, bölüm sırası, SCSS ve görsel getirir.
|
|
12
15
|
// depo — platform deposundaki katalog teması (themes/<anahtar>).
|
|
13
16
|
|
|
14
|
-
|
|
17
|
+
// Sınırlar sunucuyla ortaktır (backend/internal/theme/rules/rules.json).
|
|
18
|
+
export const limits = { fileBytes: rules.limits.fileBytes, totalBytes: rules.limits.totalBytes, files: rules.limits.files, styleBytes: rules.limits.styleBytes, archiveBytes: rules.limits.archiveBytes, localeBytes: rules.limits.localeBytes, localeEntries: rules.limits.localeEntries };
|
|
15
19
|
|
|
16
20
|
// Temanın kendi metinlerinin çevirisi: locales/<dil>.json, {"temadaki metin": "çevirisi"}.
|
|
17
21
|
// Vitrin ve panel bu sözlükle temanın varsayılan metinlerini ziyaretçinin diline çevirir.
|
|
@@ -135,6 +139,10 @@ function scanText(rel, text, report) {
|
|
|
135
139
|
for (const { pattern, text: what } of scriptPatterns) {
|
|
136
140
|
if (pattern.test(text)) report.errors.push(t("{0}: {1} içeriyor. Tema içinde betik çalıştırılamaz.", rel, what));
|
|
137
141
|
}
|
|
142
|
+
scanSecrets(rel, text, report);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function scanSecrets(rel, text, report) {
|
|
138
146
|
for (const pattern of secretPatterns) {
|
|
139
147
|
if (pattern.test(text)) report.errors.push(t("{0}: gizli anahtar ya da parola gibi görünen bir değer içeriyor.", rel));
|
|
140
148
|
}
|
|
@@ -260,6 +268,42 @@ function checkImages(data, assets, report) {
|
|
|
260
268
|
}
|
|
261
269
|
}
|
|
262
270
|
|
|
271
|
+
// ── bölüm motoru ("engine": "sections") ───────────────────────────────────────────────────────
|
|
272
|
+
|
|
273
|
+
function checkSectionsEngine({ dir, pages, sectionSchemas, sources, index, legacy, locales, report }) {
|
|
274
|
+
for (const file of walk(path.join(dir, "styles")).files) {
|
|
275
|
+
const source = readFileSync(file.full, "utf8");
|
|
276
|
+
if (/fonts\.googleapis\.com|fonts\.gstatic\.com|fonts\.bunny\.net/.test(source)) report.errors.push(t("styles/{0}: yazı tipleri assets/ içinde pakete gömülmeli.", file.rel));
|
|
277
|
+
}
|
|
278
|
+
checkSources(sources, report);
|
|
279
|
+
const schemas = new Map(platformSchemas);
|
|
280
|
+
for (const [handle, schema] of Object.entries(sectionSchemas)) {
|
|
281
|
+
const rel = `sections/${handle}.schema.json`;
|
|
282
|
+
if (!/^[a-z][a-z0-9_-]{0,63}$/.test(handle)) {
|
|
283
|
+
report.errors.push(t("{0}: geçersiz bölüm adı.", rel));
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
checkSectionSchema(rel, schema, report);
|
|
287
|
+
const kind = platformSchemas.has(handle) ? handle : `pkg:${handle}`;
|
|
288
|
+
schemas.set(kind, schema);
|
|
289
|
+
if (kind.startsWith("pkg:") && !(`sections/${handle}.html.tmpl` in sources)) report.errors.push(t("sections/{0}.schema.json: bölümün şablonu (sections/{0}.html.tmpl) yok.", handle));
|
|
290
|
+
if (schema?.fallback && !platformSchemas.has(schema.fallback) && legacy && !legacy.has(schema.fallback)) report.errors.push(t("sections/{0}.schema.json: \"fallback\" platformda ya da temel temada olan bir bölüm türü olmalı.", handle));
|
|
291
|
+
}
|
|
292
|
+
for (const rel of Object.keys(sources)) {
|
|
293
|
+
const handle = rel.startsWith("sections/") ? rel.slice("sections/".length, -".html.tmpl".length) : "";
|
|
294
|
+
if (handle && !platformSchemas.has(handle) && !schemas.has(`pkg:${handle}`)) schemas.set(`pkg:${handle}`, { name: handle, settings: [] });
|
|
295
|
+
}
|
|
296
|
+
const all = { ...pages, ...(index ? { index } : {}) };
|
|
297
|
+
for (const [name, tpl] of Object.entries(all)) checkPageTemplate(`templates/${name}.json`, tpl, { schemas, legacy }, report);
|
|
298
|
+
// Şablonlardaki {{ t "…" }} metinleri: çevirisi olmayan metin yazıldığı gibi gösterilir.
|
|
299
|
+
const texts = templateTexts(sources);
|
|
300
|
+
for (const [locale, dict] of Object.entries(locales)) {
|
|
301
|
+
if (!dict) continue;
|
|
302
|
+
const missing = [...texts].filter((text) => !(text in dict));
|
|
303
|
+
if (missing.length) report.warnings.push(t("locales/{0}.json: şablonlardaki {1} metnin çevirisi yok (ör. \"{2}\").", locale, missing.length, missing[0].slice(0, 60)));
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
263
307
|
// ── paket ──────────────────────────────────────────────────────────────────────────────────────
|
|
264
308
|
|
|
265
309
|
function checkPackage(dir, meta, report) {
|
|
@@ -276,6 +320,10 @@ function checkPackage(dir, meta, report) {
|
|
|
276
320
|
let data = null;
|
|
277
321
|
let index = null;
|
|
278
322
|
const locales = {};
|
|
323
|
+
// "engine": "sections" dosyaları: sayfa şablonları, bölüm şemaları ve şablon kaynakları.
|
|
324
|
+
const pages = {};
|
|
325
|
+
const sectionSchemas = {};
|
|
326
|
+
const sources = {};
|
|
279
327
|
for (const file of files) {
|
|
280
328
|
const rel = file.rel;
|
|
281
329
|
if (localOnly(rel)) continue;
|
|
@@ -314,9 +362,30 @@ function checkPackage(dir, meta, report) {
|
|
|
314
362
|
}
|
|
315
363
|
if (file.size > limits.fileBytes) report.errors.push(t("{0}: {1} MB; tek dosya sınırı {2} MB.", rel, (file.size / 1024 / 1024).toFixed(1), limits.fileBytes / 1024 / 1024));
|
|
316
364
|
else if (file.size > 1024 * 1024 && /\.(png|jpe?g|gif)$/i.test(ext)) report.warnings.push(t("{0}: {1} MB. WebP ya da sıkıştırılmış JPEG sayfayı hızlandırır.", rel, (file.size / 1024 / 1024).toFixed(1)));
|
|
317
|
-
if (ext === ".svg" && file.size <= limits.fileBytes)
|
|
365
|
+
if (ext === ".svg" && file.size <= limits.fileBytes) {
|
|
366
|
+
const body = readFileSync(file.full);
|
|
367
|
+
const problem = checkSVG(body);
|
|
368
|
+
if (problem) report.errors.push(`${rel}: ${problem}`);
|
|
369
|
+
scanSecrets(rel, body.toString("utf8"), report);
|
|
370
|
+
}
|
|
371
|
+
} else if (sectionFile(rel)) {
|
|
372
|
+
const kind = sectionFile(rel);
|
|
373
|
+
if (kind === "source") {
|
|
374
|
+
sources[rel] = text();
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
377
|
+
if (file.size > rules.limits.jsonBytes) {
|
|
378
|
+
report.errors.push(t("{0}: 1 MB sınırını aşıyor.", rel));
|
|
379
|
+
continue;
|
|
380
|
+
}
|
|
381
|
+
const body = text();
|
|
382
|
+
scanText(rel, body, report);
|
|
383
|
+
let parsed = null;
|
|
384
|
+
try { parsed = JSON.parse(body.replace(/^\uFEFF/, "")); } catch (error) { report.errors.push(t("{0} geçerli JSON değil: {1}", rel, error.message)); continue; }
|
|
385
|
+
if (kind === "template") pages[parts[1].slice(0, -5)] = parsed;
|
|
386
|
+
else sectionSchemas[parts[1].slice(0, -".schema.json".length)] = parsed;
|
|
318
387
|
} else {
|
|
319
|
-
report.errors.push(t("{0}: bu dosya pakette bulunamaz. İzin verilenler:
|
|
388
|
+
report.errors.push(t("{0}: bu dosya pakette bulunamaz. İzin verilenler: {1}.", rel, rules.paths.join(", ")));
|
|
320
389
|
}
|
|
321
390
|
}
|
|
322
391
|
if (totals.style > limits.styleBytes) report.errors.push(t("Stil dosyaları toplam {0} KB sınırını aşıyor.", limits.styleBytes / 1024));
|
|
@@ -332,7 +401,12 @@ function checkPackage(dir, meta, report) {
|
|
|
332
401
|
index = readJson(path.join(dir, "templates", "index.json"), report, "templates/index.json", false);
|
|
333
402
|
// Bölüm türleri temel temadan gelir; `theme init` bunları reference/sections.json dosyasına yazar.
|
|
334
403
|
const reference = existsSync(path.join(dir, "reference", "sections.json")) ? readJson(path.join(dir, "reference", "sections.json"), { errors: [] }, "") : null;
|
|
335
|
-
|
|
404
|
+
const legacy = reference?.sections ? new Set(Object.keys(reference.sections)) : null;
|
|
405
|
+
const engine = String(meta?.engine ?? "").trim();
|
|
406
|
+
if (meta && !rules.engines.includes(engine)) report.errors.push(t("theme.json: bilinmeyen \"engine\" değeri."));
|
|
407
|
+
if (engine !== "sections" && (Object.keys(pages).length || Object.keys(sectionSchemas).length || Object.keys(sources).length)) report.errors.push(t("Yeni sayfa ve bölüm dosyaları için theme.json içinde \"engine\":\"sections\" gerekli."));
|
|
408
|
+
if (engine === "sections") checkSectionsEngine({ dir, pages, sectionSchemas, sources, index, legacy, locales, report });
|
|
409
|
+
else checkTemplate(index, legacy, report, t("{0} temasında", base));
|
|
336
410
|
checkImages(data, path.join(dir, "assets"), report);
|
|
337
411
|
checkProductTabs(data, report);
|
|
338
412
|
checkTranslations(locales, meta, data, report);
|
package/src/config.js
CHANGED
|
@@ -8,6 +8,13 @@ import { t } from "./i18n.js";
|
|
|
8
8
|
|
|
9
9
|
export const defaultStoreApex = "uvoo.store";
|
|
10
10
|
export const siteOrigin = () => process.env.UVAKO_SITE_URL || "https://uvako.com";
|
|
11
|
+
/** Partner paneli: `uvako login` (cihaz girişi) ve `uvako app` komutları buraya bağlanır. */
|
|
12
|
+
export const partnerOrigin = () => {
|
|
13
|
+
const raw = (process.env.UVAKO_PARTNER_URL || "https://partner.uvako.com").replace(/\/+$/, "");
|
|
14
|
+
const url = new URL(raw);
|
|
15
|
+
if (url.protocol !== "https:" && !local(url.hostname)) throw new Error(t("Partner paneline yalnızca https ile bağlanılır."));
|
|
16
|
+
return url.origin;
|
|
17
|
+
};
|
|
11
18
|
|
|
12
19
|
export function configDir() {
|
|
13
20
|
if (process.env.UVAKO_CONFIG_DIR) return process.env.UVAKO_CONFIG_DIR;
|
|
@@ -20,9 +27,13 @@ const file = () => path.join(configDir(), "credentials.json");
|
|
|
20
27
|
function read() {
|
|
21
28
|
try {
|
|
22
29
|
const data = JSON.parse(readFileSync(file(), "utf8"));
|
|
23
|
-
return {
|
|
30
|
+
return {
|
|
31
|
+
current: typeof data.current === "string" ? data.current : "",
|
|
32
|
+
stores: data.stores && typeof data.stores === "object" ? data.stores : {},
|
|
33
|
+
partner: data.partner && typeof data.partner === "object" ? data.partner : null,
|
|
34
|
+
};
|
|
24
35
|
} catch {
|
|
25
|
-
return { current: "", stores: {} };
|
|
36
|
+
return { current: "", stores: {}, partner: null };
|
|
26
37
|
}
|
|
27
38
|
}
|
|
28
39
|
|
|
@@ -99,3 +110,34 @@ export function sessions() {
|
|
|
99
110
|
}
|
|
100
111
|
|
|
101
112
|
export const credentialsPath = () => (existsSync(file()) ? file() : "");
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Partner oturumu (uvako app komutları): önce UVAKO_PARTNER_TOKEN (CI), sonra `uvako login` ile
|
|
116
|
+
* kaydedilen giriş. Anahtar yalnızca girişin yapıldığı partner adresine gönderilir.
|
|
117
|
+
*/
|
|
118
|
+
export function partnerSession() {
|
|
119
|
+
const origin = partnerOrigin();
|
|
120
|
+
if (process.env.UVAKO_PARTNER_TOKEN) return { origin, token: process.env.UVAKO_PARTNER_TOKEN.trim(), name: "" };
|
|
121
|
+
const entry = read().partner;
|
|
122
|
+
if (!entry?.token || entry.origin !== origin) throw new Error(t("Partner girişi yapılmamış. Önce: uvako login"));
|
|
123
|
+
return { origin, token: entry.token, name: entry.name ?? "", email: entry.email ?? "" };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function savePartner(origin, token, name, email) {
|
|
127
|
+
const data = read();
|
|
128
|
+
data.partner = { origin, token, name, email, savedAt: new Date().toISOString() };
|
|
129
|
+
write(data);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function dropPartner() {
|
|
133
|
+
const data = read();
|
|
134
|
+
const had = Boolean(data.partner);
|
|
135
|
+
data.partner = null;
|
|
136
|
+
write(data);
|
|
137
|
+
return had;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export function partnerSaved() {
|
|
141
|
+
const entry = read().partner;
|
|
142
|
+
return entry?.token ? { origin: entry.origin, name: entry.name ?? "", email: entry.email ?? "" } : null;
|
|
143
|
+
}
|
package/src/files.js
CHANGED
|
@@ -2,7 +2,8 @@ import { lstatSync, readdirSync } from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
|
|
4
4
|
// Pakete hiç girmeyen adlar (sürüm kontrolü, bağımlılıklar, işletim sistemi dosyaları).
|
|
5
|
-
|
|
5
|
+
// .uvako: CLI'ın yerel durumu (son push/pull revizyonu); pakete girmez.
|
|
6
|
+
const skipped = new Set([".git", "node_modules", ".DS_Store", "Thumbs.db", ".idea", ".vscode", ".uvako"]);
|
|
6
7
|
|
|
7
8
|
/** Klasördeki dosyaları (alt klasörlerle) "a/b.c" biçiminde, sıralı döndürür. Sembolik bağlar ayrıca bildirilir. */
|
|
8
9
|
export function walk(root) {
|
package/src/init.js
CHANGED
|
@@ -19,7 +19,8 @@ function logo(name) {
|
|
|
19
19
|
* `starter` temel temanın ayarlarıdır (platformdan okunur: şema, varsayılanlar, bölüm türleri, ana
|
|
20
20
|
* sayfa şablonu). Verilmezse iskelet şablonsuz kurulur; tema temel temanın bölüm sırasını kullanır.
|
|
21
21
|
*/
|
|
22
|
-
export function initTheme(key, { name, author, cwd, base = "linea", starter = null }) {
|
|
22
|
+
export function initTheme(key, { name, author, cwd, base = "linea", starter = null, engine = "" }) {
|
|
23
|
+
if (engine && engine !== "sections") throw new Error(t("Bilinmeyen tema motoru: {0} (kullanılabilir: sections)", engine));
|
|
23
24
|
if (!keyPattern.test(key) || key.length > 40) throw new Error(t("Tema anahtarı küçük harf, rakam ve tireden oluşmalı (ör. \"atolye\"): {0}", key));
|
|
24
25
|
if (!keyPattern.test(base)) throw new Error(t("Temel tema adı geçersiz: {0}", base));
|
|
25
26
|
const dir = path.join(cwd, key);
|
|
@@ -32,12 +33,13 @@ export function initTheme(key, { name, author, cwd, base = "linea", starter = nu
|
|
|
32
33
|
};
|
|
33
34
|
|
|
34
35
|
const description = t("{0} teması.", title);
|
|
35
|
-
write("theme.json", json({ key, name: title, version: "0.1.0", base, author: String(author || ""), description, screenshot: "", industries: [], features: [], price: 0 }));
|
|
36
|
+
write("theme.json", json({ key, name: title, version: "0.1.0", base, ...(engine ? { engine } : {}), author: String(author || ""), description, screenshot: "", industries: [], features: [], price: 0 }));
|
|
36
37
|
|
|
37
38
|
// Yalnızca temel temadan farklı olan değerler yazılır; gerisi temel temadan gelir.
|
|
38
39
|
write("config/settings_data.json", json({ logo: "uploads/logo.svg" }));
|
|
39
40
|
|
|
40
|
-
if (
|
|
41
|
+
if (engine === "sections") writeSections(write, getLocale() === "en");
|
|
42
|
+
else if (starter?.template) write("templates/index.json", json(starter.template));
|
|
41
43
|
if (starter) {
|
|
42
44
|
// Başvuru dosyaları: pakete girmez. Hangi ayarların ve bölüm türlerinin olduğunu gösterir.
|
|
43
45
|
write("reference/settings_schema.json", json(starter.schema ?? {}));
|
|
@@ -49,10 +51,20 @@ export function initTheme(key, { name, author, cwd, base = "linea", starter = nu
|
|
|
49
51
|
|
|
50
52
|
// Temanın kendi metinlerinin çevirisi: anahtar temadaki metin, değer o dildeki karşılığıdır.
|
|
51
53
|
// İskelet tek bir örnekle gelir; settings_data.json'a yazdığınız başlık ve buton metinlerini ekleyin.
|
|
52
|
-
|
|
53
|
-
|
|
54
|
+
const extra = engine === "sections" ? sectionTexts(en) : {};
|
|
55
|
+
if (en) write("locales/tr.json", json({ [description]: `${title} teması.`, ...extra }));
|
|
56
|
+
else write("locales/en.json", json({ [description]: `${title} theme.`, ...extra }));
|
|
54
57
|
|
|
55
|
-
write("styles/_tokens.scss", en ? `// Overrides the base theme's variables
|
|
58
|
+
if (engine === "sections") write("styles/_tokens.scss", en ? `// Overrides the base theme's variables. Section themes embed their fonts in assets/fonts
|
|
59
|
+
// (KVKK: no font services are called); declare them with @font-face in styles/theme.scss.
|
|
60
|
+
// $font-family: 'Inter', sans-serif;
|
|
61
|
+
// $color-text: #111111;
|
|
62
|
+
` : `// Temel temanın değişkenlerinin üstüne yazılır. Bölüm temaları yazı tiplerini assets/fonts içine
|
|
63
|
+
// gömer (KVKK: yazı tipi servisi çağrılmaz); styles/theme.scss içinde @font-face ile tanımlayın.
|
|
64
|
+
// $font-family: 'Inter', sans-serif;
|
|
65
|
+
// $color-text: #111111;
|
|
66
|
+
`);
|
|
67
|
+
else write("styles/_tokens.scss", en ? `// Overrides the base theme's variables: the values you set here spread across the whole theme.
|
|
56
68
|
// This file may contain only variable assignments and font @imports.
|
|
57
69
|
//
|
|
58
70
|
// @import url('https://fonts.googleapis.com/css2?family=Manrope:wght@400;500;600;700&display=swap');
|
|
@@ -150,3 +162,82 @@ metinleri listeler.
|
|
|
150
162
|
${commands}`);
|
|
151
163
|
return dir;
|
|
152
164
|
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* "engine": "sections" iskeleti: ana sayfa ve ürün sayfası şablonları, iki örnek paket bölümü ve
|
|
168
|
+
* bir ürün kartı snippet'i. Şablonlar sunucuda sandbox içinde çizilir; betik içeremez. Etkileşim
|
|
169
|
+
* (sepete ekle, yan sepet) platformun data-uvako-* öznitelikleriyle gelir.
|
|
170
|
+
*/
|
|
171
|
+
function writeSections(write, en) {
|
|
172
|
+
const json = (value) => `${JSON.stringify(value, null, 2)}\n`;
|
|
173
|
+
write("templates/index.json", json({
|
|
174
|
+
order: ["hero", "rack"],
|
|
175
|
+
sections: {
|
|
176
|
+
hero: { type: "pkg:hero", settings: { title: en ? "Pages that fill up" : "Sayfası dolacak defterler", text: en ? "Pick by paper, binding and size." : "Kâğıdını, cildini ve boyutunu görerek seç.", link: "/search" } },
|
|
177
|
+
rack: { type: "plate-rack", settings: { title: en ? "Best sellers" : "Bu hafta çok satanlar", source: "popular", limit: 8 } },
|
|
178
|
+
},
|
|
179
|
+
}));
|
|
180
|
+
write("templates/product.json", json({
|
|
181
|
+
order: ["gallery", "info", "usp", "details", "related"],
|
|
182
|
+
sections: {
|
|
183
|
+
gallery: { type: "product-gallery", settings: { layout: "slider", show_thumbnails: true } },
|
|
184
|
+
info: { type: "product-info", settings: { show_sku: true, show_stock: true, show_unit_price: true } },
|
|
185
|
+
usp: { type: "pkg:usp", settings: {} },
|
|
186
|
+
details: { type: "product-details", settings: { show_specs: true } },
|
|
187
|
+
related: { type: "product-related", settings: { limit: 4 } },
|
|
188
|
+
},
|
|
189
|
+
}));
|
|
190
|
+
write("sections/hero.schema.json", json({
|
|
191
|
+
name: en ? "Hero" : "Karşılama",
|
|
192
|
+
fallback: "plate-rack",
|
|
193
|
+
settings: [
|
|
194
|
+
{ id: "title", type: "text", label: en ? "Title" : "Başlık", default: "" },
|
|
195
|
+
{ id: "text", type: "textarea", label: en ? "Text" : "Metin", default: "" },
|
|
196
|
+
{ id: "link", type: "url", label: en ? "Button link" : "Düğme bağlantısı", default: "/search" },
|
|
197
|
+
{ id: "image", type: "image", label: en ? "Image" : "Görsel", default: "" },
|
|
198
|
+
],
|
|
199
|
+
}));
|
|
200
|
+
write("sections/hero.html.tmpl", `<section class="hero">
|
|
201
|
+
<div class="hero-copy">
|
|
202
|
+
<h1>{{ .section.settings.title }}</h1>
|
|
203
|
+
{{ with .section.settings.text }}<p>{{ . }}</p>{{ end }}
|
|
204
|
+
<a class="btn btn-primary" href="{{ default "/search" .section.settings.link }}">{{ t "Ürünlere göz at" }}</a>
|
|
205
|
+
</div>
|
|
206
|
+
{{ with .section.settings.image }}<img class="hero-image" src="{{ image_url . }}" alt="">{{ end }}
|
|
207
|
+
<div class="hero-cards">
|
|
208
|
+
{{ range .shop.featured }}{{ template "snippets/product-card" . }}{{ end }}
|
|
209
|
+
</div>
|
|
210
|
+
</section>
|
|
211
|
+
`);
|
|
212
|
+
write("sections/usp.schema.json", json({
|
|
213
|
+
name: en ? "Shipping and returns" : "Kargo ve iade",
|
|
214
|
+
fallback: "product-details",
|
|
215
|
+
settings: [
|
|
216
|
+
{ id: "shipping", type: "text", label: en ? "Shipping" : "Kargo", default: en ? "Ships today on weekdays before 15:00" : "Hafta içi 15.00'e kadar aynı gün kargoda" },
|
|
217
|
+
{ id: "returns", type: "text", label: en ? "Returns" : "İade", default: en ? "14-day returns" : "14 gün iade" },
|
|
218
|
+
{ id: "show_cart_button", type: "checkbox", label: en ? "Open cart button" : "Sepeti aç düğmesi", default: true },
|
|
219
|
+
],
|
|
220
|
+
}));
|
|
221
|
+
write("sections/usp.html.tmpl", `<section class="usp">
|
|
222
|
+
<ul>
|
|
223
|
+
<li>{{ .section.settings.shipping }}</li>
|
|
224
|
+
<li>{{ .section.settings.returns }}</li>
|
|
225
|
+
</ul>
|
|
226
|
+
{{ if .section.settings.show_cart_button }}<button type="button" class="btn" data-uvako-cart-open>{{ t "Sepeti aç" }}</button>{{ end }}
|
|
227
|
+
</section>
|
|
228
|
+
`);
|
|
229
|
+
write("snippets/product-card.html.tmpl", `<article class="card">
|
|
230
|
+
<a href="{{ url_for "product" .handle }}">
|
|
231
|
+
{{ with .image }}<img src="{{ image_url . }}" alt="" loading="lazy">{{ end }}
|
|
232
|
+
<h3>{{ .title }}</h3>
|
|
233
|
+
</a>
|
|
234
|
+
<p class="price"><strong>{{ money .price }}</strong>{{ with .compare_at_price }} <del>{{ money . }}</del>{{ end }}</p>
|
|
235
|
+
<button type="button" class="btn" data-uvako-add-to-cart="{{ .handle }}" data-name="{{ .title }}" data-price="{{ .price }}" data-image="{{ .image }}" data-url="{{ url_for "product" .handle }}" data-stock="{{ .stock }}">{{ t "Sepete ekle" }}</button>
|
|
236
|
+
</article>
|
|
237
|
+
`);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/** Örnek bölümlerin {{ t "…" }} metinleri ve karşılıkları (locales/ dosyasına eklenir). */
|
|
241
|
+
export const sectionTexts = (en) => (en
|
|
242
|
+
? { "Ürünlere göz at": "Ürünlere göz at", "Sepeti aç": "Sepeti aç", "Sepete ekle": "Sepete ekle" }
|
|
243
|
+
: { "Ürünlere göz at": "Browse products", "Sepeti aç": "Open cart", "Sepete ekle": "Add to cart" });
|
package/src/locales/en.js
CHANGED
|
@@ -191,35 +191,188 @@ export const en = {
|
|
|
191
191
|
"theme.json: \"name\" gerekli (en çok 60 karakter).": "theme.json: \"name\" is required (60 characters at most).",
|
|
192
192
|
"theme.json: \"base\" katalogdaki bir tema olmalı ({0}).": "theme.json: \"base\" must be a theme in the catalog ({0}).",
|
|
193
193
|
"tema paketi gecersiz: {0}": "the theme package is invalid: {0}",
|
|
194
|
+
// uvako app ve partner girişi
|
|
195
|
+
"{0} bir uygulama klasörü değil: {1} bulunamadı. Yeni uygulama için: uvako app init <ad>": "{0} is not an app folder: {1} was not found. For a new app: uvako app init <name>",
|
|
196
|
+
"yapılandırma bir nesne olmalı.": "the configuration must be an object.",
|
|
197
|
+
"\"name\" gerekli (en çok 60 karakter).": "\"name\" is required (60 characters at most).",
|
|
198
|
+
"\"client_id\" 32 karakterlik bir kimlik olmalı (ya da boş).": "\"client_id\" must be a 32-character id (or empty).",
|
|
199
|
+
"\"application_url\" https:// ile başlayan bir adres olmalı.": "\"application_url\" must be an address starting with https://.",
|
|
200
|
+
"\"redirect_urls\" https adreslerinden oluşan bir liste olmalı.": "\"redirect_urls\" must be a list of https addresses.",
|
|
201
|
+
"\"scopes\" kapsam listesi olmalı (ör. [\"read_products\"]).": "\"scopes\" must be a list of scopes (e.g. [\"read_products\"]).",
|
|
202
|
+
"\"webhooks\" [{\"topics\": [...], \"uri\": \"/webhooks\"}] biçiminde olmalı.": "\"webhooks\" must look like [{\"topics\": [...], \"uri\": \"/webhooks\"}].",
|
|
203
|
+
"\"privacy_webhooks.{0}\" zorunlu: /webhooks/... yolu ya da https adresi (KVKK/GDPR).": "\"privacy_webhooks.{0}\" is required: a /webhooks/... path or an https address (GDPR).",
|
|
204
|
+
"\"webhooks\" içinde zorunlu app/uninstalled konusu yok.": "\"webhooks\" lacks the mandatory app/uninstalled topic.",
|
|
205
|
+
"\"dev.port\" geçerli bir port olmalı.": "\"dev.port\" must be a valid port.",
|
|
206
|
+
"Uygulama adı gerekli: uvako app init <ad>": "An app name is required: uvako app init <name>",
|
|
207
|
+
"Uygulama adı harf ya da rakam içermeli.": "The app name must contain letters or numbers.",
|
|
208
|
+
"Klasörde zaten bir uygulama var: {0}": "There is already an app in the folder: {0}",
|
|
209
|
+
"askıda": "suspended",
|
|
210
|
+
"taslak": "draft",
|
|
211
|
+
"incelemede": "in review",
|
|
212
|
+
"yayında": "live",
|
|
213
|
+
"reddedildi": "rejected",
|
|
214
|
+
"Uygulama kaydedildi: {0} (client_id {1})": "App registered: {0} (client_id {1})",
|
|
215
|
+
"Dağıtıldı: {0} sürüm {1}": "Deployed: {0} version {1}",
|
|
216
|
+
"Değişiklik yok: {0} sürüm {1}": "No changes: {0} version {1}",
|
|
217
|
+
"Durum: {0}{1}": "Status: {0}{1}",
|
|
218
|
+
" · mağazalardaki sürüm {0}": " · version in stores {0}",
|
|
219
|
+
"Bu sürüm yalnızca kendi geliştirme mağazalarınızda geçerli. Herkese açmak için: uvako app submit": "This version applies only to your own development stores. To make it public: uvako app submit",
|
|
220
|
+
"Uygulama henüz kayıtlı değil. Önce: uvako app deploy": "The app is not registered yet. First: uvako app deploy",
|
|
221
|
+
"İncelemeye gönderildi: {0} sürüm {1}. Uvako onayladığında tüm mağazalar bu sürümü kurabilir.": "Submitted for review: {0} version {1}. Once Uvako approves it, every store can install this version.",
|
|
222
|
+
"Kayıtlı uygulamanız yok. Başlamak için: uvako app init <ad>": "You have no registered apps. To start: uvako app init <name>",
|
|
223
|
+
"sürüm {0}": "version {0}",
|
|
224
|
+
"{0} kurulum": "{0} installs",
|
|
225
|
+
"ulaşılamadı ({0})": "unreachable ({0})",
|
|
226
|
+
"[{0}] {1} → {2} {3} ({4} ms)": "[{0}] {1} → {2} {3} ({4} ms)",
|
|
227
|
+
"Geçersiz port: {0}": "Invalid port: {0}",
|
|
228
|
+
"Yapılandırma dağıtıldı: sürüm {0} (yalnızca kendi mağazalarınızda)": "Configuration deployed: version {0} (only on your own stores)",
|
|
229
|
+
"Uygulama henüz kayıtlı değil. --no-update olmadan çalıştırın ya da: uvako app deploy": "The app is not registered yet. Run without --no-update or: uvako app deploy",
|
|
230
|
+
"Hangi mağazada geliştireceğinizi seçin: --store <mağaza>": "Choose the store to develop on: --store <store>",
|
|
231
|
+
"Partner hesabınızda geliştirme mağazası yok. partner.uvako.com → Mağazalar → Geliştirme mağazası aç.": "Your partner account has no development store. partner.uvako.com → Stores → Open a development store.",
|
|
232
|
+
"! Yerel uygulama {0} koduyla kapandı.": "! The local app exited with code {0}.",
|
|
233
|
+
"\nGeliştirme: {0} → {1} ({2})": "\nDevelopment: {0} → {1} ({2})",
|
|
234
|
+
"Yerel uygulama: {0}": "Local app: {0}",
|
|
235
|
+
"Kur / aç: {0}": "Install / open: {0}",
|
|
236
|
+
"Bu mağazada uygulamanın adresi yerel adres oldu ve webhook'ları buraya aktarılıyor. Durdurmak için Ctrl+C.\n": "In this store the app now points to the local address and its webhooks are relayed here. Press Ctrl+C to stop.\n",
|
|
237
|
+
"\nGeliştirme durdu. Mağaza dağıtılan adresleri kullanıyor; webhook'lar yeniden internetten gidiyor.": "\nDevelopment stopped. The store uses the deployed addresses again; webhooks go over the internet again.",
|
|
238
|
+
"[{0}] ! Partner paneline ulaşılamadı: {1}": "[{0}] ! Could not reach the partner panel: {1}",
|
|
239
|
+
"Uygulama oluşturuldu: {0}": "App created: {0}",
|
|
240
|
+
"Uvako'ya kaydedildi: client_id {0}": "Registered with Uvako: client_id {0}",
|
|
241
|
+
"Henüz kayıtlı değil (giriş yok ya da --offline). Kayıt için: uvako login, sonra uvako app deploy {0}": "Not registered yet (not signed in or --offline). To register: uvako login, then uvako app deploy {0}",
|
|
242
|
+
"Sıradaki adım: cd {0} && uvako app dev --store <geliştirme mağazası>": "Next step: cd {0} && uvako app dev --store <development store>",
|
|
243
|
+
"Partner paneline bağlanılamadı ({0}): {1}": "Could not connect to the partner panel ({0}): {1}",
|
|
244
|
+
"Bu adreste Uvako partner uç noktası yok (UVAKO_PARTNER_URL?).": "There is no Uvako partner endpoint at this address (UVAKO_PARTNER_URL?).",
|
|
245
|
+
" Yeniden giriş yapın: uvako login": " Sign in again: uvako login",
|
|
246
|
+
"Sunucu beklenmeyen bir doğrulama adresi verdi: {0}": "The server gave an unexpected verification address: {0}",
|
|
247
|
+
"Partner paneline giriş: tarayıcıda açılan sayfada kodu onaylayın.": "Partner sign-in: approve the code on the page that opens in your browser.",
|
|
248
|
+
" Adres: {0}": " Address: {0}",
|
|
249
|
+
" Kod: {0}\n": " Code: {0}\n",
|
|
250
|
+
"Giriş yapıldı: {0} ({1}) — {2}": "Signed in: {0} ({1}) — {2}",
|
|
251
|
+
"! Partner başvurunuz henüz onaylanmadı; uygulama kaydı onaydan sonra açılır.": "! Your partner application is not approved yet; app registration opens after approval.",
|
|
252
|
+
"✖ Giriş kodunun süresi doldu. `uvako login` komutunu yeniden çalıştırın.": "✖ The sign-in code expired. Run `uvako login` again.",
|
|
253
|
+
"Kayıtlı partner girişi yok.": "No saved partner sign-in.",
|
|
254
|
+
"Partner girişinden çıkıldı: {0}": "Signed out of the partner account: {0}",
|
|
255
|
+
"Partner: {0} — {1} ({2})": "Partner: {0} — {1} ({2})",
|
|
256
|
+
"Partner: {0}": "Partner: {0}",
|
|
257
|
+
"Partner paneline yalnızca https ile bağlanılır.": "The partner panel is reached over https only.",
|
|
258
|
+
"Partner girişi yapılmamış. Önce: uvako login": "Not signed in to a partner account. First: uvako login",
|
|
259
|
+
"Bilinmeyen uygulama komutu: {0}\n\n{1}": "Unknown app command: {0}\n\n{1}",
|
|
260
|
+
// Vitrin blokları (uvako app)
|
|
261
|
+
"{0}: bilinmeyen dosya {1}. Blokta yalnızca block.json, block.html ve block.css olabilir.": "{0}: unknown file {1}. A block may only contain block.json, block.html and block.css.",
|
|
262
|
+
"{0}: klasör adı küçük harf, rakam ve tire olmalı.": "{0}: the folder name must be lower-case letters, digits and hyphens.",
|
|
263
|
+
"{0}: block.html boş.": "{0}: block.html is empty.",
|
|
264
|
+
"{0}: blokta betik çalıştırılamaz (<script>, onclick=, javascript:).": "{0}: a block cannot run scripts (<script>, onclick=, javascript:).",
|
|
265
|
+
"{0}: şablonda yalnızca {{ .settings.ad }} ve if/else kullanılabilir.": "{0}: the template may only use {{ .settings.name }} and if/else.",
|
|
266
|
+
"{0}: block.css içinde url() ve @import kullanılamaz.": "{0}: block.css cannot use url() or @import.",
|
|
267
|
+
"{0}: block.json \"settings\" bir liste olmalı.": "{0}: block.json \"settings\" must be a list.",
|
|
268
|
+
// Bölüm motoru ("engine": "sections"), sunucu denetimi, push revizyonu.
|
|
269
|
+
"styles/{0}: yazı tipleri assets/ içinde pakete gömülmeli.": "styles/{0}: fonts must be embedded in the package under assets/.",
|
|
270
|
+
"{0}: geçersiz bölüm adı.": "{0}: invalid section name.",
|
|
271
|
+
"sections/{0}.schema.json: bölümün şablonu (sections/{0}.html.tmpl) yok.": "sections/{0}.schema.json: the section has no template (sections/{0}.html.tmpl).",
|
|
272
|
+
"sections/{0}.schema.json: \"fallback\" platformda ya da temel temada olan bir bölüm türü olmalı.": "sections/{0}.schema.json: \"fallback\" must be a section type of the platform or the base theme.",
|
|
273
|
+
"locales/{0}.json: şablonlardaki {1} metnin çevirisi yok (ör. \"{2}\").": "locales/{0}.json: {1} texts in the templates have no translation (e.g. \"{2}\").",
|
|
274
|
+
"{0}: bu dosya pakette bulunamaz. İzin verilenler: {1}.": "{0}: this file cannot be in a package. Allowed: {1}.",
|
|
275
|
+
"theme.json: bilinmeyen \"engine\" değeri.": "theme.json: unknown \"engine\" value.",
|
|
276
|
+
"Yeni sayfa ve bölüm dosyaları için theme.json içinde \"engine\":\"sections\" gerekli.": "Page and section files need \"engine\":\"sections\" in theme.json.",
|
|
277
|
+
"Bilinmeyen tema motoru: {0} (kullanılabilir: sections)": "Unknown theme engine: {0} (available: sections)",
|
|
278
|
+
"Sunucu denetimi için giriş yapın (uvako login --store <mağaza>) ya da --offline ekleyin.": "Log in to check on the server too (uvako login --store <store>), or add --offline.",
|
|
279
|
+
"✖ Sunucu denetimi ({0}): {1}": "✖ Server check ({0}): {1}",
|
|
280
|
+
"Sunucu denetimi ({0}): geçti.": "Server check ({0}): passed.",
|
|
281
|
+
" Yükleme sınırı aşıldı. {0} saniye sonra tekrar deneyin.": " Upload limit reached. Try again in {0} seconds.",
|
|
282
|
+
" Sunucudaki sürümü almak için: uvako theme pull <anahtar> --force. Üzerine yazmak için: uvako theme push --force": " To get the server's version: uvako theme pull <key> --force. To overwrite it: uvako theme push --force",
|
|
283
|
+
"Sunucu denetimi yapılamadı: {0}": "The server check could not run: {0}",
|
|
284
|
+
"kapanmamış şablon aksiyonu": "unterminated template action",
|
|
285
|
+
"izin verilmeyen öznitelik: {0}": "attribute not allowed: {0}",
|
|
286
|
+
"{0} içinde izin verilmeyen adres şeması": "URL scheme not allowed in {0}",
|
|
287
|
+
"srcset içinde izin verilmeyen adres şeması": "URL scheme not allowed in srcset",
|
|
288
|
+
"style özniteliğinde izin verilmeyen ifade: {0}": "expression not allowed in the style attribute: {0}",
|
|
289
|
+
"kapanmamış HTML yorumu": "unterminated HTML comment",
|
|
290
|
+
"işaretleme bildirimi kullanılamaz": "markup declarations are not allowed",
|
|
291
|
+
"şablon aksiyonu etiket adı oluşturamaz": "a template action cannot form a tag name",
|
|
292
|
+
"izin verilmeyen etiket: {0}": "element not allowed: {0}",
|
|
293
|
+
"kapanmamış etiket: {0}": "unterminated tag: {0}",
|
|
294
|
+
"şablon aksiyonu öznitelik adı oluşturamaz": "a template action cannot form an attribute name",
|
|
295
|
+
"{0} etiketinde kapanmamış öznitelik değeri": "unterminated attribute value on {0}",
|
|
296
|
+
"izin verilmeyen aksiyon: {0}": "action not allowed: {0}",
|
|
297
|
+
"bilinmeyen fonksiyon: {0}": "unknown function: {0}",
|
|
298
|
+
"{0} yalnızca paketin snippets/ şablonlarını adlandırabilir: {1}": "{0} may only name the package's snippets/ templates: {1}",
|
|
299
|
+
"snippet bulunamadı: {0}": "snippet not found: {0}",
|
|
300
|
+
"Pakette en çok 128 bölüm şablonu olabilir.": "A package may contain at most 128 section templates.",
|
|
301
|
+
"{0}: şablon adında yalnızca küçük harf, rakam, tire ve alt çizgi olabilir.": "{0}: a template name may contain only lowercase letters, digits, hyphens and underscores.",
|
|
302
|
+
"{0}: platform bölümünün adı kullanılamaz; başka bir ad verin.": "{0}: the name of a platform section cannot be used; choose another name.",
|
|
303
|
+
"{0}: bölüm ayar şeması geçersiz ({1}).": "{0}: invalid section setting schema ({1}).",
|
|
304
|
+
"nesne olmalı": "must be an object",
|
|
305
|
+
"\"name\" gerekli, en çok 100 karakter": "\"name\" is required, at most 100 characters",
|
|
306
|
+
"\"settings\" en çok {0} ayarlık bir liste olmalı": "\"settings\" must be a list of at most {0} settings",
|
|
307
|
+
"\"fallback\" bir bölüm türü olmalı": "\"fallback\" must be a section type",
|
|
308
|
+
"\"{0}\" ayar adı geçersiz ya da tekrarlı": "setting name \"{0}\" is invalid or repeated",
|
|
309
|
+
"\"{0}\" ayarının \"label\" alanı gerekli": "setting \"{0}\" needs a \"label\"",
|
|
310
|
+
"\"{0}\" ayarının türü ({1}) bilinmiyor; kullanılabilir: {2}": "setting \"{0}\" has an unknown type ({1}); available: {2}",
|
|
311
|
+
"\"{0}\" ayarının min/max değerleri geçersiz": "setting \"{0}\" has invalid min/max values",
|
|
312
|
+
"\"{0}\" ayarının step değeri geçersiz": "setting \"{0}\" has an invalid step",
|
|
313
|
+
"\"{0}\" (range) için min ve max gerekli": "\"{0}\" (range) needs min and max",
|
|
314
|
+
"\"{0}\" (select) için 1-50 seçenek gerekli": "\"{0}\" (select) needs 1-50 options",
|
|
315
|
+
"\"{0}\" ayarının seçenekleri geçersiz": "setting \"{0}\" has invalid options",
|
|
316
|
+
"\"{0}\" ayarının varsayılan değeri türüne uymuyor": "the default of setting \"{0}\" does not match its type",
|
|
317
|
+
"geçerli bir şablon değil ({\"order\": […], \"sections\": {…}}).": "not a valid template ({\"order\": […], \"sections\": {…}}).",
|
|
318
|
+
"en çok 100 bölüm olabilir.": "may contain at most 100 sections.",
|
|
319
|
+
"\"{0}\" bölüm kimliği geçersiz ya da tekrarlı.": "section id \"{0}\" is invalid or repeated.",
|
|
320
|
+
"\"order\" içindeki \"{0}\" bölümü \"sections\" altında yok.": "section \"{0}\" in \"order\" is not under \"sections\".",
|
|
321
|
+
"\"{0}\" bölüm kimliği geçersiz.": "section id \"{0}\" is invalid.",
|
|
322
|
+
"\"{0}\" bölümünün türü ({1}) ne platformda ne pakette ne de temel temada var.": "the type of section \"{0}\" ({1}) is neither a platform, package nor base theme section.",
|
|
323
|
+
"\"{0}\" bölümünün türü ({1}) pakette yok: sections/{2}.html.tmpl ekleyin.": "the type of section \"{0}\" ({1}) is not in the package: add sections/{2}.html.tmpl.",
|
|
324
|
+
"\"{0}\" bölümünün \"{1}\" ayarı betik ya da izin verilmeyen bir değer içeriyor.": "setting \"{1}\" of section \"{0}\" contains a script or a value that is not allowed.",
|
|
325
|
+
"\"{0}\" bölümünün \"{1}\" ayarı şemada yok ya da türüne uymuyor.": "setting \"{1}\" of section \"{0}\" is not in the schema or does not match its type.",
|
|
326
|
+
"Bu mağazada bölüm motoru etkin değil.": "The section engine is not enabled for this store.",
|
|
327
|
+
"Paket sunucuda sizin kopyanızdan sonra değişti. `uvako theme pull` ile güncelleyin ya da --force ile üzerine yazın.": "The package changed on the server after your copy. Update with `uvako theme pull` or overwrite with --force.",
|
|
328
|
+
"Paket yolda bozuldu (SHA-256 uyuşmuyor). Tekrar deneyin.": "The package was damaged in transit (SHA-256 mismatch). Try again.",
|
|
329
|
+
"Tema yükleme sınırı aşıldı. Belirtilen süre sonra tekrar deneyin.": "Theme upload limit reached. Try again after the given time.",
|
|
330
|
+
"Dosya boyut sınırını aşıyor.": "The file exceeds the size limit.",
|
|
331
|
+
"Bölüm şablonları derlenemedi: {0}": "Section templates could not be compiled: {0}",
|
|
194
332
|
};
|
|
195
333
|
|
|
196
|
-
export const help = (version) => `uvako-cli ${version} — Uvako theme development tool
|
|
334
|
+
export const help = (version) => `uvako-cli ${version} — Uvako theme and app development tool
|
|
197
335
|
https://uvako.com
|
|
198
336
|
|
|
199
337
|
Sign-in:
|
|
338
|
+
uvako login Signs in with your partner account (approved in the browser; app commands)
|
|
200
339
|
uvako login --store <store> Connects to the store with a theme access key (you are asked for the key)
|
|
201
|
-
uvako logout [--store <store>]
|
|
202
|
-
uvako whoami Shows the connected store
|
|
340
|
+
uvako logout [--store <store>] [--partner] Deletes the saved sign-in
|
|
341
|
+
uvako whoami Shows the connected store and the partner sign-in
|
|
342
|
+
|
|
343
|
+
App (public apps, with the partner sign-in):
|
|
344
|
+
uvako app init <name> [--dir folder] [--offline]
|
|
345
|
+
A sample app (OAuth install, signed webhooks) and uvako.app.json
|
|
346
|
+
uvako app dev [folder] --store <store> [--port 3000] [--no-serve] [--no-update]
|
|
347
|
+
Runs the app locally; the development store points to the local address
|
|
348
|
+
and its webhooks are relayed to the receiver on your computer
|
|
349
|
+
uvako app deploy [folder] [--submit] Deploys uvako.app.json as a new version (first only on your own stores)
|
|
350
|
+
uvako app submit [folder] Submits the app for Uvako review so every store can install it
|
|
351
|
+
uvako app list [--json] Lists your registered apps
|
|
203
352
|
|
|
204
353
|
Theme:
|
|
205
|
-
uvako theme init <key> [--base linea] [--name "Name"] [--author "Author"]
|
|
354
|
+
uvako theme init <key> [--base linea] [--name "Name"] [--author "Author"] [--engine sections]
|
|
206
355
|
A new theme scaffold built on a catalog theme
|
|
207
|
-
|
|
208
|
-
uvako theme
|
|
356
|
+
--engine sections: a theme that brings its own sections (with examples)
|
|
357
|
+
uvako theme check [folder] [--offline] Checks the theme (structure, settings, translations, security, section templates);
|
|
358
|
+
when you are logged in the server checks it with the same rules too
|
|
359
|
+
uvako theme dev [folder] Live development: uploads only the file you changed, the preview refreshes
|
|
209
360
|
The store returns to its previous state when you stop (--keep: leave in library)
|
|
210
|
-
uvako theme push [folder]
|
|
211
|
-
|
|
361
|
+
uvako theme push [folder] [--force] Uploads the theme to the store's theme library (with SHA-256 and revision;
|
|
362
|
+
refused if the package changed after your copy, --force overwrites)
|
|
363
|
+
uvako theme pull <key> [--dir folder] Downloads a theme you uploaded (including sections/, snippets/, templates/)
|
|
212
364
|
uvako theme list Lists the themes in the store and the packages you uploaded
|
|
213
365
|
uvako theme submit <key> Submits the theme for review for themes.uvako.com
|
|
214
366
|
uvako theme delete <key> --yes Deletes a package you uploaded
|
|
215
367
|
uvako theme package [folder] [--out x.zip] Checks the theme and builds a zip package
|
|
216
368
|
uvako version Prints the version
|
|
217
369
|
|
|
218
|
-
A theme is a package that runs no code: settings,
|
|
219
|
-
|
|
220
|
-
|
|
370
|
+
A theme is a package that runs no code: settings, page templates, SCSS and images. "engine": "sections"
|
|
371
|
+
themes may bring their own sections (sections/*.html.tmpl); they are drawn only on the server, in a
|
|
372
|
+
restricted template language. Packages containing JavaScript or server code are rejected. An uploaded
|
|
373
|
+
theme is visible only in your own store; publishing it in the theme store depends on Uvako's review.
|
|
221
374
|
|
|
222
|
-
For CI: the UVAKO_STORE and UVAKO_TOKEN environment variables take the place of signing in.
|
|
375
|
+
For CI: the UVAKO_STORE and UVAKO_TOKEN (theme) and UVAKO_PARTNER_TOKEN (app) environment variables take the place of signing in.
|
|
223
376
|
Language: --lang tr or UVAKO_LANG=tr prints the output in Turkish.
|
|
224
377
|
|
|
225
378
|
Uvako — https://uvako.com
|