@shop-on/theme-contract 0.5.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/dist/ai-files.d.ts +18 -0
- package/dist/ai-files.js +47 -0
- package/dist/block-allowlist.d.ts +27 -0
- package/dist/block-allowlist.js +21 -0
- package/dist/closest-scope.d.ts +51 -0
- package/dist/closest-scope.js +66 -0
- package/dist/editor-tools/apply.d.ts +85 -0
- package/dist/editor-tools/apply.js +544 -0
- package/dist/editor-tools/args.d.ts +153 -0
- package/dist/editor-tools/args.js +145 -0
- package/dist/editor-tools/coerce.d.ts +43 -0
- package/dist/editor-tools/coerce.js +200 -0
- package/dist/editor-tools/digest.d.ts +75 -0
- package/dist/editor-tools/digest.js +99 -0
- package/dist/editor-tools/index.d.ts +21 -0
- package/dist/editor-tools/index.js +24 -0
- package/dist/editor-tools/recipes.d.ts +122 -0
- package/dist/editor-tools/recipes.js +192 -0
- package/dist/fonts.d.ts +9 -0
- package/dist/fonts.js +11 -0
- package/dist/google-fonts.generated.d.ts +7 -0
- package/dist/google-fonts.generated.js +753 -0
- package/dist/index.d.ts +14 -5
- package/dist/index.js +14 -5
- package/dist/link-url.d.ts +9 -0
- package/dist/link-url.js +33 -0
- package/dist/metafield-binding.d.ts +89 -9
- package/dist/metafield-binding.js +254 -43
- package/dist/ops.d.ts +85 -0
- package/dist/ops.js +442 -0
- package/dist/richtext.d.ts +23 -0
- package/dist/richtext.js +115 -1
- package/dist/schemas.d.ts +325 -0
- package/dist/schemas.js +57 -7
- package/dist/section-scope.d.ts +17 -0
- package/dist/section-scope.js +22 -0
- package/dist/store-path.d.ts +9 -0
- package/dist/store-path.js +13 -1
- package/dist/template-limits.d.ts +34 -0
- package/dist/template-limits.js +71 -0
- package/dist/video-url.d.ts +10 -0
- package/dist/video-url.js +36 -0
- package/package.json +8 -1
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
//#region src/template-limits.ts
|
|
2
|
+
const MAX_SECTIONS_PER_TEMPLATE = 25;
|
|
3
|
+
const MAX_BLOCKS_PER_TEMPLATE = 1250;
|
|
4
|
+
const MAX_BLOCK_NESTING_DEPTH = 8;
|
|
5
|
+
/** One template or section group, as compact JSON. The largest a shipped
|
|
6
|
+
* theme has is ~60 KB; a template at the block cap is ~250–500 KB. */
|
|
7
|
+
const MAX_ENTRY_BYTES = 524288;
|
|
8
|
+
const isObject = (x) => typeof x === "object" && x !== null && !Array.isArray(x);
|
|
9
|
+
/** The blocks under `node` (a section or a block), at every depth. */
|
|
10
|
+
function countNestedBlocks(node) {
|
|
11
|
+
if (!isObject(node) || !isObject(node.blocks)) return 0;
|
|
12
|
+
let total = 0;
|
|
13
|
+
for (const child of Object.values(node.blocks)) total += 1 + countNestedBlocks(child);
|
|
14
|
+
return total;
|
|
15
|
+
}
|
|
16
|
+
/** A template's or section group's sections (its `order`, as the editor
|
|
17
|
+
* counts them) and its blocks across every section. Both are
|
|
18
|
+
* `{sections, order}` documents. */
|
|
19
|
+
function templateCounts(entry) {
|
|
20
|
+
if (!isObject(entry)) return {
|
|
21
|
+
sections: 0,
|
|
22
|
+
blocks: 0
|
|
23
|
+
};
|
|
24
|
+
const sections = Array.isArray(entry.order) ? entry.order.length : 0;
|
|
25
|
+
let blocks = 0;
|
|
26
|
+
if (isObject(entry.sections)) for (const section of Object.values(entry.sections)) blocks += countNestedBlocks(section);
|
|
27
|
+
return {
|
|
28
|
+
sections,
|
|
29
|
+
blocks
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/** Levels of blocks under `node`: 0 for none, 1 for blocks with no children. */
|
|
33
|
+
function blockLevels(node) {
|
|
34
|
+
if (!isObject(node) || !isObject(node.blocks)) return 0;
|
|
35
|
+
let deepest = 0;
|
|
36
|
+
for (const child of Object.values(node.blocks)) deepest = Math.max(deepest, blockLevels(child));
|
|
37
|
+
return Object.keys(node.blocks).length > 0 ? 1 + deepest : 0;
|
|
38
|
+
}
|
|
39
|
+
/** The deepest block level in a template or section group, as the editor
|
|
40
|
+
* counts it against `MAX_BLOCK_NESTING_DEPTH`: a block directly in a section
|
|
41
|
+
* is level 1, a section with no blocks is 0. */
|
|
42
|
+
function maxBlockDepth(entry) {
|
|
43
|
+
if (!isObject(entry) || !isObject(entry.sections)) return 0;
|
|
44
|
+
let deepest = 0;
|
|
45
|
+
for (const section of Object.values(entry.sections)) deepest = Math.max(deepest, blockLevels(section));
|
|
46
|
+
return deepest;
|
|
47
|
+
}
|
|
48
|
+
function countBlocksInBlock(block) {
|
|
49
|
+
return 1 + countNestedBlocks(block);
|
|
50
|
+
}
|
|
51
|
+
/** 1-indexed depth of the deepest descendant within `block` (the block itself
|
|
52
|
+
* counts as 1). Prices a subtree about to be inserted against the nesting cap. */
|
|
53
|
+
function maxDepthOfBlock(block) {
|
|
54
|
+
let deepest = 1;
|
|
55
|
+
if (isObject(block.blocks)) {
|
|
56
|
+
for (const child of Object.values(block.blocks)) if (isObject(child)) deepest = Math.max(deepest, 1 + maxDepthOfBlock(child));
|
|
57
|
+
}
|
|
58
|
+
return deepest;
|
|
59
|
+
}
|
|
60
|
+
function maxDepthOfBlockSchema(blockType, blockSchemas, remaining = 9) {
|
|
61
|
+
if (remaining <= 1) return 1;
|
|
62
|
+
const schema = blockSchemas.find((b) => b.type === blockType);
|
|
63
|
+
let deepest = 1;
|
|
64
|
+
for (const child of schema?.blocks ?? []) {
|
|
65
|
+
if (!child.static) continue;
|
|
66
|
+
deepest = Math.max(deepest, 1 + maxDepthOfBlockSchema(child.type, blockSchemas, remaining - 1));
|
|
67
|
+
}
|
|
68
|
+
return deepest;
|
|
69
|
+
}
|
|
70
|
+
//#endregion
|
|
71
|
+
export { MAX_BLOCKS_PER_TEMPLATE, MAX_BLOCK_NESTING_DEPTH, MAX_ENTRY_BYTES, MAX_SECTIONS_PER_TEMPLATE, countBlocksInBlock, countNestedBlocks, maxBlockDepth, maxDepthOfBlock, maxDepthOfBlockSchema, templateCounts };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
//#region src/video-url.d.ts
|
|
2
|
+
export type VideoProvider = "youtube" | "vimeo";
|
|
3
|
+
export declare function parseVideoUrl(url: string): {
|
|
4
|
+
provider: VideoProvider;
|
|
5
|
+
id: string;
|
|
6
|
+
} | null;
|
|
7
|
+
/** Whether the video field accepts the URL — a YouTube/Vimeo link within
|
|
8
|
+
* `accept`. Anything else it flags as unsupported. */
|
|
9
|
+
export declare function isAcceptedVideoUrl(url: string, accept?: readonly string[]): boolean;
|
|
10
|
+
//#endregion
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
//#region src/video-url.ts
|
|
2
|
+
function parseVideoUrl(url) {
|
|
3
|
+
try {
|
|
4
|
+
const u = new URL(url);
|
|
5
|
+
if (u.hostname.includes("youtube.com") || u.hostname.includes("youtube-nocookie.com")) {
|
|
6
|
+
const id = u.searchParams.get("v") ?? u.pathname.split("/embed/")[1] ?? u.pathname.split("/shorts/")[1];
|
|
7
|
+
return id ? {
|
|
8
|
+
provider: "youtube",
|
|
9
|
+
id
|
|
10
|
+
} : null;
|
|
11
|
+
}
|
|
12
|
+
if (u.hostname === "youtu.be") {
|
|
13
|
+
const id = u.pathname.slice(1);
|
|
14
|
+
return id ? {
|
|
15
|
+
provider: "youtube",
|
|
16
|
+
id
|
|
17
|
+
} : null;
|
|
18
|
+
}
|
|
19
|
+
if (u.hostname.includes("vimeo.com")) {
|
|
20
|
+
const id = u.pathname.match(/\/(?:video\/)?(\d+)/)?.[1];
|
|
21
|
+
return id ? {
|
|
22
|
+
provider: "vimeo",
|
|
23
|
+
id
|
|
24
|
+
} : null;
|
|
25
|
+
}
|
|
26
|
+
} catch {}
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
/** Whether the video field accepts the URL — a YouTube/Vimeo link within
|
|
30
|
+
* `accept`. Anything else it flags as unsupported. */
|
|
31
|
+
function isAcceptedVideoUrl(url, accept) {
|
|
32
|
+
const parsed = parseVideoUrl(url);
|
|
33
|
+
return !!parsed && (!accept || accept.includes(parsed.provider));
|
|
34
|
+
}
|
|
35
|
+
//#endregion
|
|
36
|
+
export { isAcceptedVideoUrl, parseVideoUrl };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shop-on/theme-contract",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"license": "SEE LICENSE IN dist/LICENSE.md",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist"
|
|
@@ -9,8 +9,13 @@
|
|
|
9
9
|
"sideEffects": false,
|
|
10
10
|
"exports": {
|
|
11
11
|
".": "./dist/index.js",
|
|
12
|
+
"./ai-files": "./dist/ai-files.js",
|
|
13
|
+
"./closest-scope": "./dist/closest-scope.js",
|
|
14
|
+
"./editor-tools": "./dist/editor-tools/index.js",
|
|
15
|
+
"./ops": "./dist/ops.js",
|
|
12
16
|
"./preset-listing": "./dist/preset-listing.js",
|
|
13
17
|
"./preset-materializer": "./dist/preset-materializer.js",
|
|
18
|
+
"./store-path": "./dist/store-path.js",
|
|
14
19
|
"./package.json": "./package.json"
|
|
15
20
|
},
|
|
16
21
|
"dependencies": {
|
|
@@ -18,11 +23,13 @@
|
|
|
18
23
|
},
|
|
19
24
|
"devDependencies": {
|
|
20
25
|
"@types/node": "26.6.5",
|
|
26
|
+
"tsx": "^4.23.15",
|
|
21
27
|
"typescript": "7.0.2",
|
|
22
28
|
"vitest": "5.0.5"
|
|
23
29
|
},
|
|
24
30
|
"scripts": {
|
|
25
31
|
"check-types": "tsc --noEmit",
|
|
32
|
+
"gen:editor-tools": "tsx scripts/gen-editor-tools.ts",
|
|
26
33
|
"test": "vitest run --maxWorkers=2"
|
|
27
34
|
}
|
|
28
35
|
}
|