@zohofinance/commerce 2.1.0 → 2.2.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/VALIDATIONS.md +122 -0
- package/package.json +2 -2
- package/public/dev-error.liquid +49 -0
- package/ts-dist/commands/build-theme.d.ts.map +1 -1
- package/ts-dist/commands/build-theme.js +38 -11
- package/ts-dist/commands/serve-theme.d.ts.map +1 -1
- package/ts-dist/commands/serve-theme.js +213 -49
- package/ts-dist/index.js +0 -0
- package/ts-dist/mocks/global-objects.d.ts.map +1 -1
- package/ts-dist/mocks/global-objects.js +1 -0
- package/ts-dist/mocks/init-theme/i18n/en.schema.json +2 -1
- package/ts-dist/mocks/init-theme/sections/footer.liquid +3 -1
- package/ts-dist/mocks/init-theme/sections/header-topbar.liquid +3 -1
- package/ts-dist/mocks/init-theme/sections/product-collection.liquid +3 -1
- package/ts-dist/mocks/init-theme/sections/promo-banner.liquid +5 -1
- package/ts-dist/mocks/reviews.d.ts +6 -0
- package/ts-dist/mocks/reviews.d.ts.map +1 -0
- package/ts-dist/mocks/reviews.js +52 -0
- package/ts-dist/routes/storefront-routes.js +1 -1
- package/ts-dist/utils/api-handler.d.ts +6 -1
- package/ts-dist/utils/api-handler.d.ts.map +1 -1
- package/ts-dist/utils/api-handler.js +91 -35
- package/ts-dist/utils/error-report.d.ts +1 -1
- package/ts-dist/utils/error-report.d.ts.map +1 -1
- package/ts-dist/utils/error-report.js +58 -18
- package/ts-dist/utils/load-theme-globals.d.ts +1 -1
- package/ts-dist/utils/load-theme-globals.d.ts.map +1 -1
- package/ts-dist/utils/load-theme-globals.js +42 -2
- package/ts-dist/utils/resolve-schema-settings.d.ts.map +1 -1
- package/ts-dist/utils/resolve-schema-settings.js +0 -1
- package/ts-dist/utils/resolve-schema-translations.d.ts +1 -0
- package/ts-dist/utils/resolve-schema-translations.d.ts.map +1 -1
- package/ts-dist/utils/resolve-schema-translations.js +3 -0
- package/ts-dist/utils/schema-validation.d.ts +34 -0
- package/ts-dist/utils/schema-validation.d.ts.map +1 -0
- package/ts-dist/utils/schema-validation.js +544 -0
- package/ts-dist/utils/serve-theme-utils.d.ts +1 -1
- package/ts-dist/utils/serve-theme-utils.d.ts.map +1 -1
- package/ts-dist/utils/serve-theme-utils.js +1 -1
- package/ts-dist/utils/template-reference-validation.d.ts +11 -0
- package/ts-dist/utils/template-reference-validation.d.ts.map +1 -0
- package/ts-dist/utils/template-reference-validation.js +466 -0
- package/ts-dist/utils/theme-block-schema.d.ts +5 -0
- package/ts-dist/utils/theme-block-schema.d.ts.map +1 -0
- package/ts-dist/utils/theme-block-schema.js +35 -0
- package/ts-dist/utils/theme-validation.d.ts +8 -1
- package/ts-dist/utils/theme-validation.d.ts.map +1 -1
- package/ts-dist/utils/theme-validation.js +84 -143
package/VALIDATIONS.md
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Theme validation
|
|
2
|
+
|
|
3
|
+
`theme dev` and `theme build` run the same checks on the theme in the current directory. Each finding is an **error** or a **warning**.
|
|
4
|
+
|
|
5
|
+
## How findings are reported
|
|
6
|
+
|
|
7
|
+
Findings are printed in groups: missing translation keys, invalid JSON, section and block schemas, page and section-group JSON, Liquid references, theme settings, oversized assets, and missing required files. Each line names the file and the problem. Errors are marked ❌. Warnings are marked ⚠️.
|
|
8
|
+
|
|
9
|
+
| | `theme dev` | `theme build` |
|
|
10
|
+
| --- | --- | --- |
|
|
11
|
+
| Error | Preview is replaced with a validation page. The dev server stays up and rechecks when files change. | The errors are printed, the command throws, and no zip is written. |
|
|
12
|
+
| Warning | Printed. Preview continues. | Printed. The zip is still written. |
|
|
13
|
+
|
|
14
|
+
A section file that is missing `available_in` or a named preset is the one check whose severity depends on the command. It is a warning during `theme dev` and an error during `theme build`.
|
|
15
|
+
|
|
16
|
+
Everything else below is an error unless it is marked as a warning.
|
|
17
|
+
|
|
18
|
+
## Required files
|
|
19
|
+
|
|
20
|
+
These files must exist:
|
|
21
|
+
|
|
22
|
+
- `layouts/theme.liquid`
|
|
23
|
+
- `i18n/en.json`
|
|
24
|
+
- `i18n/en.schema.json`
|
|
25
|
+
- `settings/schema.json`
|
|
26
|
+
- `settings/theme.json`
|
|
27
|
+
|
|
28
|
+
## JSON files
|
|
29
|
+
|
|
30
|
+
Every `.json` file in the theme must parse.
|
|
31
|
+
|
|
32
|
+
## Translation keys
|
|
33
|
+
|
|
34
|
+
Every `"t:..."` key in `.liquid`, `.json`, `.js`, `.ts`, `.css`, and `.html` files must exist in `i18n/en.schema.json`.
|
|
35
|
+
|
|
36
|
+
## Assets
|
|
37
|
+
|
|
38
|
+
Each file under `assets/` must be 1 MB or smaller.
|
|
39
|
+
|
|
40
|
+
## Section and block schemas
|
|
41
|
+
|
|
42
|
+
Checked on the `{% schema %}` block in `sections/*.liquid` and `blocks/*.liquid`.
|
|
43
|
+
|
|
44
|
+
- The schema must be valid JSON.
|
|
45
|
+
- A block file with no schema is a **warning**.
|
|
46
|
+
- A section file with no schema is treated as missing both `available_in` and a named preset.
|
|
47
|
+
- `name` is required and must be a non-empty string. `tag` and `class`, when set, must be strings.
|
|
48
|
+
- `max_blocks`, when set, must be a positive integer.
|
|
49
|
+
|
|
50
|
+
### `available_in` and `presets` on sections
|
|
51
|
+
|
|
52
|
+
Every `sections/*.liquid` file must have:
|
|
53
|
+
|
|
54
|
+
- `available_in.templates` with at least one page
|
|
55
|
+
- `presets` as an array, with every preset object carrying a non-empty `name`
|
|
56
|
+
|
|
57
|
+
Preset objects may also include `settings`, `blocks`, and other defaults. A missing array, an empty array, or a preset without a name fails this check. `theme dev` logs it and still previews. `theme build` logs it, throws, and does not create the zip.
|
|
58
|
+
|
|
59
|
+
Allowed page values are `index`, `cart`, `product`, `page`, `blog`, `blogs`, `collection`, `category`, `checkout`, `orders`, `search`, `404`, `header-group`, and `footer-group`. A value outside that list is a **warning** in both commands. A malformed `available_in` (not an object, `templates` not an array, or a non-string entry) is also a **warning**.
|
|
60
|
+
|
|
61
|
+
Block files do not require `available_in` or `presets`.
|
|
62
|
+
|
|
63
|
+
### Settings
|
|
64
|
+
|
|
65
|
+
`settings` must be an array of objects.
|
|
66
|
+
|
|
67
|
+
Accepted types: `checkbox`, `radio`, `select`, `collection`, `blog`, `form`, `category`, `product`, `menu`, `color`, `color_scheme`, `range`, `image_picker`, `media_picker`, `url`, `text`, `image`, `richtext`, `inline_richtext`, `header`. Any other type, including `textarea`, `preview_layout`, and `date`, is rejected.
|
|
68
|
+
|
|
69
|
+
- `header` needs `content` and does not use `id`.
|
|
70
|
+
- Every other type needs an `id`. A repeated `id` in the same settings array is a **warning**.
|
|
71
|
+
- `meta`, when set, must be `desktop` or `mobile`.
|
|
72
|
+
- `select` and `radio` need a non-empty `options` array. Each option needs `value` and `label`. `default` must be `{ display_name, value }`. A `default.value` that is not one of the option values is a **warning**.
|
|
73
|
+
- `range` needs numeric `min` and `max` with `min < max`. `step`, when set, must be a positive number. A numeric `default` must fall inside min and max. `unit`, when set, must be a string.
|
|
74
|
+
- `url` and `menu` need `default` `{ display_name, handle }`.
|
|
75
|
+
- `collection`, `category`, `product`, and `blog` need `default.handle`.
|
|
76
|
+
- `image_picker` needs `default.url`. `enable_customisation`, when set, must be a boolean.
|
|
77
|
+
- `media_picker` needs `default.url`. `is_video` and `enable_customisation`, when set, must be booleans.
|
|
78
|
+
- A `checkbox` `default` that is not a boolean is a **warning**.
|
|
79
|
+
|
|
80
|
+
### Blocks declared in the schema
|
|
81
|
+
|
|
82
|
+
- A local block needs `type`. `limit`, when set, must be a positive integer.
|
|
83
|
+
- Nested `blocks` stop at depth 5.
|
|
84
|
+
- `@theme`, `@widget`, `@app`, `zc_theme__…`, and `zc_app__…` are opt-ins. Their settings live in their own files and are not checked inline.
|
|
85
|
+
- Each declared `zc_theme__<name>` must have `blocks/<name>.liquid`.
|
|
86
|
+
|
|
87
|
+
### Preset contents
|
|
88
|
+
|
|
89
|
+
When `presets` is present it must be an array of objects. `settings`, when set, must be an object. `blocks`, when set, must be an object keyed by block name, together with `block_order`. Storing `blocks` as an array is a **warning**: those blocks are ignored when the preset is applied. Each preset block type must be allowed by the container schema. Every `block_order` entry must be a key of `blocks`. Nesting stops at depth 5.
|
|
90
|
+
|
|
91
|
+
## Theme settings
|
|
92
|
+
|
|
93
|
+
- `settings/schema.json` must be an array of groups. Each group needs `name` and a `settings` array, checked with the setting rules above.
|
|
94
|
+
- In `settings/theme.json`, `selected` is a preset name or `{ preset_name }`, and that name must be a key of `presets`.
|
|
95
|
+
|
|
96
|
+
## Pages and section groups
|
|
97
|
+
|
|
98
|
+
Checked on `pages/*.json` and on `sections/*.json` (header and footer groups).
|
|
99
|
+
|
|
100
|
+
- `layout`, when set, must have `layouts/<layout>.liquid`.
|
|
101
|
+
- `order` must be an array and `sections` must be an object.
|
|
102
|
+
- Each `order` entry must name a section that exists in `sections` and has `type`. Listing the same section twice is a **warning**.
|
|
103
|
+
- `sections/<type>.liquid` must exist. Types starting with `custom_section_` are stored outside the theme and are not looked up as theme files.
|
|
104
|
+
- A section present in `sections` but missing from `order` is a **warning**. It is never rendered.
|
|
105
|
+
- `blocks` and `block_order` must be set together: `blocks` is an object and `block_order` is an array.
|
|
106
|
+
- Every `block_order` entry must exist in `blocks` and must have `type`.
|
|
107
|
+
- A block type must be allowed by that container's schema `blocks`: an exact type, any `zc_theme__…` when `@theme` is declared, or any `zc_app__…` when `@widget` or `@app` is declared. Nested blocks are checked the same way, up to depth 5.
|
|
108
|
+
- `zc_theme__<name>` must have `blocks/<name>.liquid`.
|
|
109
|
+
- `block_order` longer than `max_blocks`, or more instances of a type than its `limit`, is rejected. That section does not render.
|
|
110
|
+
- A block present in `blocks` but missing from `block_order` is a **warning**. It is never rendered.
|
|
111
|
+
|
|
112
|
+
## Liquid
|
|
113
|
+
|
|
114
|
+
Scanned in `layouts/`, `sections/`, `blocks/`, and `snippets/`. `{% schema %}`, `{% comment %}`, and `{% raw %}` blocks are skipped.
|
|
115
|
+
|
|
116
|
+
These tags are rejected: `paginate`, `form`, `stylesheet`, `include`, `layout`, `highlight`, and any tag outside the set below.
|
|
117
|
+
|
|
118
|
+
Accepted tags, including their end tags: `if`, `unless`, `case`, `for`, `tablerow`, `liquid`, `assign`, `capture`, `increment`, `decrement`, `echo`, `comment`, `raw`, `break`, `continue`, `cycle`, `render`, `section`, `sections`, `render_blocks`, `render_block`, `schema`, `style`, `javascript`.
|
|
119
|
+
|
|
120
|
+
- `{% render 'name' %}` must have `snippets/name.liquid`.
|
|
121
|
+
- `{% section 'name' %}` must have `sections/name.liquid`.
|
|
122
|
+
- `{% sections 'name' %}` must have `sections/name.json`.
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zohofinance/commerce",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"main": "ts-dist/index.js",
|
|
5
5
|
"bin": {
|
|
6
6
|
"zcommerce": "ts-dist/index.js"
|
|
7
7
|
},
|
|
8
8
|
"scripts": {
|
|
9
9
|
"prepare": "yarn run build",
|
|
10
|
-
"build": "tsc && node scripts/copy-init-templates.mjs",
|
|
10
|
+
"build": "tsc && node scripts/copy-init-templates.mjs && chmod +x ts-dist/index.js",
|
|
11
11
|
"dev": "yarn run build && node ts-dist/index.js",
|
|
12
12
|
"lint:js:fix": "eslint . --fix",
|
|
13
13
|
"lint": "yarn eslint src/",
|
package/public/dev-error.liquid
CHANGED
|
@@ -267,6 +267,55 @@
|
|
|
267
267
|
color: #e0e0e0;
|
|
268
268
|
}
|
|
269
269
|
|
|
270
|
+
.error-context h3 .issue-count {
|
|
271
|
+
display: inline-block;
|
|
272
|
+
margin-left: 6px;
|
|
273
|
+
padding: 0 8px;
|
|
274
|
+
border-radius: 10px;
|
|
275
|
+
background: #5a2a2a;
|
|
276
|
+
color: #ffb4b4;
|
|
277
|
+
font-size: 0.75rem;
|
|
278
|
+
font-weight: 600;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
.issue-group {
|
|
282
|
+
margin: 0 0 14px;
|
|
283
|
+
padding: 10px 12px;
|
|
284
|
+
border-left: 3px solid #e5534b;
|
|
285
|
+
background: #333;
|
|
286
|
+
border-radius: 0 6px 6px 0;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
.issue-message {
|
|
290
|
+
color: #f5f5f5;
|
|
291
|
+
font-weight: 500;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
.issue-files-count {
|
|
295
|
+
margin-left: 8px;
|
|
296
|
+
color: #9a9a9a;
|
|
297
|
+
font-size: 0.8rem;
|
|
298
|
+
font-weight: 400;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
.error-context ul.issue-files {
|
|
302
|
+
margin: 8px 0 0;
|
|
303
|
+
padding-left: 1.2rem;
|
|
304
|
+
list-style: disc;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
.error-context ul.issue-files li {
|
|
308
|
+
margin-bottom: 3px;
|
|
309
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
310
|
+
font-size: 0.85rem;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
.issue-where {
|
|
314
|
+
margin-left: 8px;
|
|
315
|
+
color: #9a9a9a;
|
|
316
|
+
font-size: 0.78rem;
|
|
317
|
+
}
|
|
318
|
+
|
|
270
319
|
.error-context pre {
|
|
271
320
|
background: transparent;
|
|
272
321
|
padding: 0;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-theme.d.ts","sourceRoot":"","sources":["../../src/commands/build-theme.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"build-theme.d.ts","sourceRoot":"","sources":["../../src/commands/build-theme.ts"],"names":[],"mappings":"AAuCA,UAAU,iBAAiB;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,UAAU,oBAAoB;IAC5B,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;CACtB;AAoXD,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAclE;AAoED,wBAAsB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAiCzE;AASD,iBAAe,UAAU,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE,iBAAiB;;;;2BAUa,oBAAoB;;GAsQ/F;AAED,eAAe,UAAU,CAAC"}
|
|
@@ -6,13 +6,14 @@ import zlib from 'zlib';
|
|
|
6
6
|
import mime from 'mime-types';
|
|
7
7
|
import CleanCSS from 'clean-css';
|
|
8
8
|
import ignore from 'ignore';
|
|
9
|
-
import {
|
|
9
|
+
import { spawn } from 'child_process';
|
|
10
10
|
import { createRequire } from 'module';
|
|
11
11
|
import lighthouse from 'lighthouse';
|
|
12
12
|
import puppeteer from 'puppeteer';
|
|
13
13
|
import { minify as terserMinify } from 'terser';
|
|
14
|
+
import AdmZip from 'adm-zip';
|
|
14
15
|
import { readJSONFileContent } from '../utils/file-operations.js';
|
|
15
|
-
import { assertThemeValid, validateTheme } from '../utils/theme-validation.js';
|
|
16
|
+
import { assertThemeValid, validateTheme, validationErrors, validationWarnings, formatValidationIssuesForConsole } from '../utils/theme-validation.js';
|
|
16
17
|
import { transformFileSync } from '@babel/core';
|
|
17
18
|
const FOLDERS_TO_COPY = [
|
|
18
19
|
'assets',
|
|
@@ -506,7 +507,16 @@ async function buildTheme({ env, skipAudit }) {
|
|
|
506
507
|
}
|
|
507
508
|
try {
|
|
508
509
|
spinner.text = 'Validating theme...';
|
|
509
|
-
|
|
510
|
+
const validationResult = await validateTheme(process.cwd(), { mode: 'build' });
|
|
511
|
+
const warnings = validationWarnings(validationResult.issues);
|
|
512
|
+
const errors = validationErrors(validationResult.issues);
|
|
513
|
+
if (errors.length > 0) {
|
|
514
|
+
console.error(formatValidationIssuesForConsole(errors));
|
|
515
|
+
}
|
|
516
|
+
if (warnings.length > 0) {
|
|
517
|
+
console.error(formatValidationIssuesForConsole(warnings));
|
|
518
|
+
}
|
|
519
|
+
assertThemeValid(validationResult);
|
|
510
520
|
spinner.text = 'Copying theme files...';
|
|
511
521
|
if (fs.existsSync(zipFolderPath))
|
|
512
522
|
fs.removeSync(zipFolderPath);
|
|
@@ -556,14 +566,31 @@ async function buildTheme({ env, skipAudit }) {
|
|
|
556
566
|
}, null, 2), 'utf-8');
|
|
557
567
|
spinner.text = 'Creating theme ZIP...';
|
|
558
568
|
await new Promise((resolve, reject) => {
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
569
|
+
try {
|
|
570
|
+
const zip = new AdmZip();
|
|
571
|
+
const EXCLUDED_PATTERNS = ['.DS_Store', '__MACOSX', '.apk'];
|
|
572
|
+
const shouldExclude = (filePath) => EXCLUDED_PATTERNS.some((p) => filePath.includes(p));
|
|
573
|
+
const addDirToZip = (dir, zipPrefix) => {
|
|
574
|
+
for (const entry of fs.readdirSync(dir)) {
|
|
575
|
+
const fullPath = path.join(dir, entry);
|
|
576
|
+
const zipPath = zipPrefix ? path.join(zipPrefix, entry) : entry;
|
|
577
|
+
if (shouldExclude(fullPath))
|
|
578
|
+
continue;
|
|
579
|
+
if (fs.statSync(fullPath).isDirectory()) {
|
|
580
|
+
addDirToZip(fullPath, zipPath);
|
|
581
|
+
}
|
|
582
|
+
else {
|
|
583
|
+
zip.addFile(zipPath.split(path.sep).join('/'), fs.readFileSync(fullPath));
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
};
|
|
587
|
+
addDirToZip(distFolderPath, '');
|
|
588
|
+
zip.writeZip(zipFolderPath);
|
|
589
|
+
resolve();
|
|
590
|
+
}
|
|
591
|
+
catch (error) {
|
|
592
|
+
reject(new Error(`ZIP creation failed: ${error instanceof Error ? error.message : String(error)}`));
|
|
593
|
+
}
|
|
567
594
|
});
|
|
568
595
|
fs.removeSync(distFolderPath);
|
|
569
596
|
spinner.succeed(chalk.green('Theme packaged successfully.'));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serve-theme.d.ts","sourceRoot":"","sources":["../../src/commands/serve-theme.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"serve-theme.d.ts","sourceRoot":"","sources":["../../src/commands/serve-theme.ts"],"names":[],"mappings":"AA8DA,iBAAe,UAAU,CACvB,UAAU,CAAC,EAAE,MAAM,EACnB,SAAS,CAAC,EAAE,OAAO,EACnB,aAAa,CAAC,EAAE,OAAO,EACvB,OAAO,UAAO,EACd,QAAQ,CAAC,EAAE,MAAM,iBA61DlB;AAED,eAAe,UAAU,CAAC"}
|