@gradial/aci 0.1.20-preview.3 → 0.1.21
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 +48 -108
- package/bin/{aci → aci-darwin-arm64} +0 -0
- package/bin/aci.js +99 -6
- package/dist/assets/index.d.ts +3 -3
- package/dist/assets/index.js +3 -3
- package/dist/astro/index.d.ts +12 -3
- package/dist/astro/index.js +20 -5
- package/dist/content/assets.d.ts +17 -0
- package/dist/content/assets.js +65 -0
- package/dist/content/index.d.ts +1 -0
- package/dist/content/index.js +1 -0
- package/dist/define-component.d.ts +1 -2
- package/dist/define-component.js +73 -5
- package/dist/dev/content-watch.d.ts +37 -0
- package/dist/dev/content-watch.js +154 -0
- package/dist/dev/index.d.ts +24 -43
- package/dist/dev/index.js +4 -169
- package/dist/index.d.ts +14 -6
- package/dist/index.js +8 -9
- package/dist/next/asset-route.js +2 -2
- package/dist/next/config.d.ts +2 -2
- package/dist/next/config.js +5 -5
- package/dist/next/content-watch.js +6 -1
- package/dist/next/index.d.ts +2 -2
- package/dist/next/index.js +2 -2
- package/dist/next/middleware.d.ts +1 -0
- package/dist/next/middleware.js +49 -8
- package/dist/next/page.d.ts +1 -1
- package/dist/next/page.js +14 -12
- package/dist/next/render.d.ts +3 -3
- package/dist/next/render.js +11 -9
- package/dist/next/server.d.ts +12 -12
- package/dist/next/server.js +1 -1
- package/dist/preview/core.d.ts +5 -0
- package/dist/preview/core.js +1 -1
- package/dist/react/Media.d.ts +24 -0
- package/dist/react/Media.js +30 -0
- package/dist/react/Picture.d.ts +12 -0
- package/dist/react/Picture.js +11 -0
- package/dist/react/VideoPlayer.d.ts +13 -0
- package/dist/react/{GradialVideoPlayer.js → VideoPlayer.js} +6 -5
- package/dist/react/index.d.ts +333 -3
- package/dist/react/index.js +89 -3
- package/dist/registry.d.ts +2 -2
- package/dist/registry.js +3 -3
- package/dist/types/block-ref.d.ts +26 -0
- package/dist/types/block-ref.js +28 -0
- package/dist/types/component.d.ts +36 -32
- package/dist/types/config.d.ts +1 -1
- package/dist/types/image.d.ts +33 -20
- package/dist/types/image.js +42 -50
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +1 -0
- package/dist/types/link.d.ts +38 -0
- package/dist/types/link.js +58 -0
- package/dist/types/media.d.ts +16 -35
- package/dist/types/media.js +15 -84
- package/dist/types/renderer.d.ts +1 -1
- package/dist/types/rich-text.d.ts +40 -0
- package/dist/types/rich-text.js +39 -0
- package/dist/types/video.d.ts +7 -13
- package/dist/types/video.js +3 -3
- package/package.json +29 -76
- package/src/cli/compile-registry.mjs +120 -63
- package/src/cli/validate-content.mjs +38 -75
- package/src/types/block-ref.ts +74 -0
- package/src/types/image.ts +79 -60
- package/templates/astro/template/.aci.yaml +28 -0
- package/templates/astro/template/.storybook/main.ts +79 -0
- package/templates/astro/template/.storybook/manager.ts +6 -0
- package/templates/astro/template/.storybook/preview.ts +33 -0
- package/templates/astro/template/astro.config.mjs +37 -0
- package/templates/astro/template/package.json.tmpl +41 -0
- package/templates/astro/template/src/cms/contracts/layouts/index.ts +18 -0
- package/templates/astro/template/src/cms/registry.ts +28 -0
- package/templates/astro/template/src/components/chrome/Footer.astro +107 -0
- package/templates/astro/template/src/components/chrome/Header.astro +94 -0
- package/templates/astro/template/src/components/elements/AtomicBlock.astro +15 -0
- package/templates/astro/template/src/components/elements/AtomicBlocks.astro +16 -0
- package/templates/astro/template/src/components/elements/Button.astro +51 -0
- package/templates/astro/template/src/components/elements/ButtonBlock.astro +25 -0
- package/templates/astro/template/src/components/elements/Container.astro +11 -0
- package/templates/astro/template/src/components/elements/Icon.astro +31 -0
- package/templates/astro/template/src/components/elements/Logo.astro +29 -0
- package/templates/astro/template/src/components/elements/RemoteImage.astro +41 -0
- package/templates/astro/template/src/components/sections/ContainerSection.astro +32 -0
- package/templates/astro/template/src/components/sections/FeatureCard.astro +23 -0
- package/templates/astro/template/src/components/sections/FeatureGrid.astro +35 -0
- package/templates/astro/template/src/components/sections/HomeHero.astro +51 -0
- package/templates/astro/template/src/components/sections/StatsBar.astro +33 -0
- package/templates/astro/template/src/design-system/styles.css +399 -0
- package/templates/astro/template/src/design-system/tokens/colors.css +60 -0
- package/templates/astro/template/src/design-system/tokens/components.css +69 -0
- package/templates/astro/template/src/design-system/tokens/index.css +6 -0
- package/templates/astro/template/src/design-system/tokens/radii.css +5 -0
- package/templates/astro/template/src/design-system/tokens/shadows.css +5 -0
- package/templates/astro/template/src/design-system/tokens/spacing.css +13 -0
- package/templates/astro/template/src/design-system/tokens/typography.css +24 -0
- package/templates/astro/template/src/env.d.ts +1 -0
- package/templates/astro/template/src/fixtures/atomicBlocks.ts +33 -0
- package/templates/astro/template/src/fixtures/composition.ts +31 -0
- package/templates/astro/template/src/fixtures/index.ts +17 -0
- package/templates/astro/template/src/fixtures/sections.ts +39 -0
- package/templates/astro/template/src/layouts/SiteLayout.astro +30 -0
- package/templates/astro/template/src/pages/[...slug].astro +61 -0
- package/templates/astro/template/src/render/RenderPage.astro +66 -0
- package/templates/astro/template/src/stories/CompositionStoryFrame.astro +15 -0
- package/templates/astro/template/src/stories/HomeHero.stories.ts +24 -0
- package/templates/astro/template/src/stories/SectionStoryFrame.astro +16 -0
- package/templates/astro/template/src/stories/StoryDocFrame.astro +31 -0
- package/templates/astro/template/tsconfig.json +11 -0
- package/templates/common/.agents/AGENTS.md +37 -0
- package/templates/common/.agents/skills/aci/SKILL.md +162 -0
- package/templates/common/.agents/skills/authoring/SKILL.md +590 -0
- package/templates/common/.agents/skills/authoring/references/block-registry.md +151 -0
- package/templates/common/.agents/skills/authoring/references/contracts.md +153 -0
- package/templates/common/.agents/skills/authoring/references/design-system.md +171 -0
- package/templates/common/.agents/skills/authoring/references/image-assets.md +73 -0
- package/templates/common/.agents/skills/authoring/references/repo-structure.md +157 -0
- package/templates/common/.agents/skills/migration/SKILL.md +160 -0
- package/templates/common/.agents/skills/migration/references/analyzing-source.md +215 -0
- package/templates/common/.agents/skills/migration/references/component-migration.md +200 -0
- package/templates/common/.agents/skills/migration/references/selector-map.md +31 -0
- package/templates/common/.content/.gradial-dam/blobs/sha256/7f/bf/7fbfc7fe230ff12688c496f660119e41d9725c4504d82e310c15665c73c09292 +0 -0
- package/templates/common/.content/.gradial-dam/blobs/sha256/a0/f5/a0f506689de9b15a7055f70103a3f02cd3ba105771ae2de6754df2ede1210d3a +0 -0
- package/templates/common/.content/.gradial-dam/blobs/sha256/dd/b7/ddb7e9642be08317bcd4b2bd26b58ba8d0d34dd7239e03e684a4f3634fa3845b +0 -0
- package/templates/common/.content/.gradial-dam/blobs/sha256/fb/32/fb3260e8286ea8231ea1c77b9c0be92d6edd9026565731eb14cfdcfd6b1b75b3 +0 -0
- package/templates/common/.content/config/site.json +192 -0
- package/templates/common/.content/dam/folders/seed/ast_site_about_hero/asset.json +9 -0
- package/templates/common/.content/dam/folders/seed/ast_site_about_hero/editorial.json +14 -0
- package/templates/common/.content/dam/folders/seed/ast_site_about_hero/governance.json +23 -0
- package/templates/common/.content/dam/folders/seed/ast_site_about_hero/versions/v0001.json +37 -0
- package/templates/common/.content/dam/folders/seed/ast_site_home_hero/asset.json +9 -0
- package/templates/common/.content/dam/folders/seed/ast_site_home_hero/editorial.json +14 -0
- package/templates/common/.content/dam/folders/seed/ast_site_home_hero/governance.json +23 -0
- package/templates/common/.content/dam/folders/seed/ast_site_home_hero/versions/v0001.json +37 -0
- package/templates/common/.content/fragments/navbar/_index.json +25 -0
- package/templates/common/.content/fragments/site-footer/_index.json +62 -0
- package/templates/common/.content/pages/about/_index.json +51 -0
- package/templates/common/.content/pages/home/_index.json +112 -0
- package/templates/common/.content/schemas/site.schema.json +123 -0
- package/templates/common/README.md.tmpl +45 -0
- package/templates/common/contracts/components/button.contract.ts +19 -0
- package/templates/common/contracts/components/container.contract.ts +19 -0
- package/templates/common/contracts/components/featureCard.contract.ts +15 -0
- package/templates/common/contracts/components/featureGrid.contract.ts +16 -0
- package/templates/common/contracts/components/homeHero.contract.ts +36 -0
- package/templates/common/contracts/components/index.ts +21 -0
- package/templates/common/contracts/components/siteFooter.contract.ts +45 -0
- package/templates/common/contracts/components/siteNavigation.contract.ts +30 -0
- package/templates/common/contracts/components/statsBar.contract.ts +19 -0
- package/templates/common/design-system/styles.css +346 -0
- package/templates/common/design-system/tokens/colors.css +110 -0
- package/templates/common/design-system/tokens/components.css +86 -0
- package/templates/common/design-system/tokens/index.css +6 -0
- package/templates/common/design-system/tokens/radii.css +5 -0
- package/templates/common/design-system/tokens/shadows.css +9 -0
- package/templates/common/design-system/tokens/spacing.css +26 -0
- package/templates/common/design-system/tokens/typography.css +32 -0
- package/templates/common/eslint-rules/no-raw-tailwind-values.js +127 -0
- package/templates/common/public/assets/Dwire-model.png +0 -0
- package/templates/common/public/assets/bare-metal-logo.png +0 -0
- package/templates/common/public/assets/meridian-logo.svg +8 -0
- package/templates/common/public/assets/seattle.avif +0 -0
- package/templates/nextjs/template/.aci.yaml +26 -0
- package/templates/nextjs/template/.storybook/main.ts +13 -0
- package/templates/nextjs/template/.storybook/preview.ts +34 -0
- package/templates/nextjs/template/eslint.config.mjs +36 -0
- package/templates/nextjs/template/next-env.d.ts +6 -0
- package/templates/nextjs/template/next.config.ts +8 -0
- package/templates/nextjs/template/package.json.tmpl +46 -0
- package/templates/nextjs/template/postcss.config.mjs +7 -0
- package/templates/nextjs/template/src/app/[[...slug]]/page.tsx +8 -0
- package/templates/nextjs/template/src/app/gradial/assets/[releaseId]/[...path]/route.ts +31 -0
- package/templates/nextjs/template/src/app/layout.tsx +29 -0
- package/templates/nextjs/template/src/cms/contracts/components/button.contract.ts +19 -0
- package/templates/nextjs/template/src/cms/contracts/components/container.contract.ts +20 -0
- package/templates/nextjs/template/src/cms/contracts/components/homeHero.contract.ts +36 -0
- package/templates/nextjs/template/src/cms/contracts/components/index.ts +15 -0
- package/templates/nextjs/template/src/cms/contracts/components/siteFooter.contract.ts +45 -0
- package/templates/nextjs/template/src/cms/contracts/components/siteNavigation.contract.ts +30 -0
- package/templates/nextjs/template/src/cms/contracts/layouts/index.ts +16 -0
- package/templates/nextjs/template/src/cms/registry.ts +28 -0
- package/templates/nextjs/template/src/components/chrome/Footer.tsx +86 -0
- package/templates/nextjs/template/src/components/chrome/Header.tsx +74 -0
- package/templates/nextjs/template/src/components/elements/AtomicBlock.tsx +13 -0
- package/templates/nextjs/template/src/components/elements/Button.tsx +40 -0
- package/templates/nextjs/template/src/components/elements/ButtonBlock.tsx +18 -0
- package/templates/nextjs/template/src/components/elements/Container.tsx +14 -0
- package/templates/nextjs/template/src/components/elements/Icon.tsx +29 -0
- package/templates/nextjs/template/src/components/elements/Logo.tsx +38 -0
- package/templates/nextjs/template/src/components/sections/ContainerSection.tsx +28 -0
- package/templates/nextjs/template/src/components/sections/FeatureCard.tsx +25 -0
- package/templates/nextjs/template/src/components/sections/FeatureGrid.tsx +36 -0
- package/templates/nextjs/template/src/components/sections/HomeHero.tsx +62 -0
- package/templates/nextjs/template/src/components/sections/StatsBar.tsx +30 -0
- package/templates/nextjs/template/src/middleware.ts +4 -0
- package/templates/nextjs/template/src/stories/Button.stories.tsx +38 -0
- package/templates/nextjs/template/src/stories/Header.stories.tsx +30 -0
- package/templates/nextjs/template/src/stories/HomeHero.stories.tsx +33 -0
- package/templates/nextjs/template/tsconfig.json +23 -0
- package/templates/nextjs/template/vercel.json +6 -0
- package/dist/content/contract.d.ts +0 -83
- package/dist/content/contract.js +0 -104
- package/dist/content/tailwind-validator.d.ts +0 -6
- package/dist/content/tailwind-validator.js +0 -31
- package/dist/content/validation.d.ts +0 -108
- package/dist/content/validation.js +0 -183
- package/dist/react/GradialMedia.d.ts +0 -24
- package/dist/react/GradialMedia.js +0 -31
- package/dist/react/GradialPicture.d.ts +0 -14
- package/dist/react/GradialPicture.js +0 -30
- package/dist/react/GradialVideoPlayer.d.ts +0 -13
- package/dist/sveltekit/index.d.ts +0 -25
- package/dist/sveltekit/index.js +0 -66
- package/dist/sveltekit/preview.d.ts +0 -5
- package/dist/sveltekit/preview.js +0 -33
- package/src/types/component.ts +0 -49
- package/src/types/config.ts +0 -37
- package/src/types/data.ts +0 -47
- package/src/types/index.ts +0 -10
- package/src/types/layout.ts +0 -29
- package/src/types/media.ts +0 -125
- package/src/types/page.ts +0 -47
- package/src/types/render-mode.ts +0 -10
- package/src/types/renderer.ts +0 -83
- package/src/types/video.ts +0 -66
package/package.json
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gradial/aci",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.21",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
7
7
|
"files": [
|
|
8
8
|
"dist",
|
|
9
9
|
"bin/aci.js",
|
|
10
|
-
"bin/aci",
|
|
10
|
+
"bin/aci-*",
|
|
11
11
|
"src/cli/*.mjs",
|
|
12
|
-
"src/types
|
|
12
|
+
"src/types/image.ts",
|
|
13
|
+
"src/types/block-ref.ts",
|
|
14
|
+
"templates/common",
|
|
15
|
+
"templates/nextjs",
|
|
16
|
+
"templates/astro",
|
|
13
17
|
"README.md"
|
|
14
18
|
],
|
|
15
19
|
"bin": {
|
|
@@ -23,29 +27,13 @@
|
|
|
23
27
|
"types": "./dist/index.d.ts",
|
|
24
28
|
"import": "./dist/index.js"
|
|
25
29
|
},
|
|
26
|
-
"./compiler": {
|
|
27
|
-
"types": "./dist/compiler/index.d.ts",
|
|
28
|
-
"import": "./dist/compiler/index.js"
|
|
29
|
-
},
|
|
30
30
|
"./content": {
|
|
31
31
|
"types": "./dist/content/index.d.ts",
|
|
32
32
|
"import": "./dist/content/index.js"
|
|
33
33
|
},
|
|
34
|
-
"./
|
|
35
|
-
"types": "./dist/
|
|
36
|
-
"import": "./dist/
|
|
37
|
-
},
|
|
38
|
-
"./content/cache": {
|
|
39
|
-
"types": "./dist/content/cache.d.ts",
|
|
40
|
-
"import": "./dist/content/cache.js"
|
|
41
|
-
},
|
|
42
|
-
"./content/validation": {
|
|
43
|
-
"types": "./dist/content/validation.d.ts",
|
|
44
|
-
"import": "./dist/content/validation.js"
|
|
45
|
-
},
|
|
46
|
-
"./content/tailwind-validator": {
|
|
47
|
-
"types": "./dist/content/tailwind-validator.d.ts",
|
|
48
|
-
"import": "./dist/content/tailwind-validator.js"
|
|
34
|
+
"./compiler": {
|
|
35
|
+
"types": "./dist/compiler/index.d.ts",
|
|
36
|
+
"import": "./dist/compiler/index.js"
|
|
49
37
|
},
|
|
50
38
|
"./providers/file": {
|
|
51
39
|
"types": "./dist/providers/file.d.ts",
|
|
@@ -59,51 +47,19 @@
|
|
|
59
47
|
"types": "./dist/dev/index.d.ts",
|
|
60
48
|
"import": "./dist/dev/index.js"
|
|
61
49
|
},
|
|
62
|
-
"./assets": {
|
|
63
|
-
"types": "./dist/assets/index.d.ts",
|
|
64
|
-
"import": "./dist/assets/index.js"
|
|
65
|
-
},
|
|
66
|
-
"./runtime": {
|
|
67
|
-
"types": "./dist/runtime/page.d.ts",
|
|
68
|
-
"import": "./dist/runtime/page.js"
|
|
69
|
-
},
|
|
70
50
|
"./react": {
|
|
71
51
|
"types": "./dist/react/index.d.ts",
|
|
72
52
|
"import": "./dist/react/index.js"
|
|
73
53
|
},
|
|
74
|
-
"./astro": {
|
|
75
|
-
"types": "./dist/astro/index.d.ts",
|
|
76
|
-
"import": "./dist/astro/index.js"
|
|
77
|
-
},
|
|
78
|
-
"./preview": {
|
|
79
|
-
"types": "./dist/preview/core.d.ts",
|
|
80
|
-
"import": "./dist/preview/core.js",
|
|
81
|
-
"default": "./dist/preview/core.js"
|
|
82
|
-
},
|
|
83
54
|
"./next": {
|
|
84
55
|
"types": "./dist/next/index.d.ts",
|
|
85
56
|
"import": "./dist/next/index.js",
|
|
86
57
|
"default": "./dist/next/index.js"
|
|
87
58
|
},
|
|
88
|
-
"./next/
|
|
89
|
-
"types": "./dist/next/
|
|
90
|
-
"import": "./dist/next/
|
|
91
|
-
"default": "./dist/next/
|
|
92
|
-
},
|
|
93
|
-
"./next/root-layout": {
|
|
94
|
-
"types": "./dist/next/root-layout.d.ts",
|
|
95
|
-
"import": "./dist/next/root-layout.js",
|
|
96
|
-
"default": "./dist/next/root-layout.js"
|
|
97
|
-
},
|
|
98
|
-
"./next/render": {
|
|
99
|
-
"types": "./dist/next/render.d.ts",
|
|
100
|
-
"import": "./dist/next/render.js",
|
|
101
|
-
"default": "./dist/next/render.js"
|
|
102
|
-
},
|
|
103
|
-
"./next/server": {
|
|
104
|
-
"types": "./dist/next/server.d.ts",
|
|
105
|
-
"import": "./dist/next/server.js",
|
|
106
|
-
"default": "./dist/next/server.js"
|
|
59
|
+
"./next/config": {
|
|
60
|
+
"types": "./dist/next/config.d.ts",
|
|
61
|
+
"import": "./dist/next/config.js",
|
|
62
|
+
"default": "./dist/next/config.js"
|
|
107
63
|
},
|
|
108
64
|
"./next/middleware": {
|
|
109
65
|
"types": "./dist/next/middleware.d.ts",
|
|
@@ -114,28 +70,22 @@
|
|
|
114
70
|
"types": "./dist/next/dev-refresh.d.ts",
|
|
115
71
|
"import": "./dist/next/dev-refresh.js"
|
|
116
72
|
},
|
|
117
|
-
"./
|
|
118
|
-
"types": "./dist/
|
|
119
|
-
"import": "./dist/
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
"types": "./dist/sveltekit/index.d.ts",
|
|
123
|
-
"import": "./dist/sveltekit/index.js"
|
|
124
|
-
},
|
|
125
|
-
"./testing": {
|
|
126
|
-
"types": "./dist/testing/index.d.ts",
|
|
127
|
-
"import": "./dist/testing/index.js"
|
|
128
|
-
},
|
|
129
|
-
"./cli/compile-registry": "./src/cli/compile-registry.mjs",
|
|
130
|
-
"./cli/validate-content": "./src/cli/validate-content.mjs",
|
|
131
|
-
"./cli/verify-renderer": "./src/cli/verify-renderer.mjs"
|
|
73
|
+
"./astro": {
|
|
74
|
+
"types": "./dist/astro/index.d.ts",
|
|
75
|
+
"import": "./dist/astro/index.js",
|
|
76
|
+
"default": "./dist/astro/index.js"
|
|
77
|
+
}
|
|
132
78
|
},
|
|
133
79
|
"scripts": {
|
|
134
|
-
"build": "tsc -p tsconfig.json",
|
|
80
|
+
"build": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\" && tsc -p tsconfig.json",
|
|
135
81
|
"build:cli": "cd ../.. && ./scripts/build-cli.sh",
|
|
136
|
-
"
|
|
82
|
+
"generate-starters": "cd ../.. && tsx scripts/generate-starters.ts",
|
|
83
|
+
"compile-registry": "tsx src/cli/compile-registry.mjs",
|
|
84
|
+
"prepack": "npm run generate-starters && npm run build && npm run build:cli"
|
|
137
85
|
},
|
|
138
86
|
"dependencies": {
|
|
87
|
+
"markdown-it": "^14.1.0",
|
|
88
|
+
"sanitize-html": "^2.13.1",
|
|
139
89
|
"ws": "^8.18.0"
|
|
140
90
|
},
|
|
141
91
|
"peerDependencies": {
|
|
@@ -162,13 +112,16 @@
|
|
|
162
112
|
"devDependencies": {
|
|
163
113
|
"@types/node": "^24.10.1",
|
|
164
114
|
"@types/react": "^19.0.0",
|
|
115
|
+
"@types/markdown-it": "^14.1.2",
|
|
116
|
+
"@types/sanitize-html": "^2.16.0",
|
|
165
117
|
"@types/ws": "^8.18.0",
|
|
166
118
|
"@vercel/edge-config": "^1.4.3",
|
|
167
119
|
"next": "^15.5.6",
|
|
168
120
|
"react": "^19.0.0",
|
|
121
|
+
"react-dom": "^19.0.0",
|
|
169
122
|
"tsx": "^4.20.0",
|
|
170
123
|
"typescript": "^5.9.3",
|
|
171
124
|
"ws": "^8.18.0",
|
|
172
125
|
"zod": "^4.0.0"
|
|
173
126
|
}
|
|
174
|
-
}
|
|
127
|
+
}
|
|
@@ -3,7 +3,8 @@ import { mkdir, writeFile } from 'node:fs/promises';
|
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { pathToFileURL } from 'node:url';
|
|
5
5
|
import { z } from 'zod';
|
|
6
|
-
import {
|
|
6
|
+
import { ImageSchema } from '../types/image.ts';
|
|
7
|
+
import { getBlockRefMeta } from '../types/block-ref.ts';
|
|
7
8
|
|
|
8
9
|
const DATA_TYPE_PATTERN = /^[A-Za-z][A-Za-z0-9_-]*$/;
|
|
9
10
|
|
|
@@ -47,30 +48,20 @@ async function compileRegistry(options) {
|
|
|
47
48
|
const schemaFiles = [];
|
|
48
49
|
for (const [index, component] of components.entries()) {
|
|
49
50
|
validateComponent(component, index, seenComponents);
|
|
50
|
-
const
|
|
51
|
-
const
|
|
51
|
+
const componentId = component.id;
|
|
52
|
+
const schema = compileSchema(component.props, componentId);
|
|
53
|
+
const schemaFile = `schemas/${componentId}.schema.json`;
|
|
52
54
|
const schemaPath = path.join(outDir, schemaFile);
|
|
53
55
|
await writeFile(schemaPath, JSON.stringify(schema, null, 2) + '\n');
|
|
54
56
|
schemaFiles.push(schemaPath);
|
|
55
|
-
const varyDimensions = component.varyDimensions ?? component.vary;
|
|
56
57
|
const imageSlots = normalizeImageSlots(component.imageSlots);
|
|
57
|
-
const
|
|
58
|
+
const slots = extractBlockSlots(component.props, componentId);
|
|
58
59
|
componentEntries.push({
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
static: Boolean(renderModes.canStatic ?? renderModes.static),
|
|
63
|
-
ssr: Boolean(renderModes.canSSR ?? renderModes.ssr),
|
|
64
|
-
clientIsland: Boolean(renderModes.canClientIsland ?? renderModes.clientIsland),
|
|
65
|
-
} : { static: true, ssr: true, clientIsland: false },
|
|
66
|
-
renderModes: renderModes ? {
|
|
67
|
-
canStatic: Boolean(renderModes.canStatic ?? renderModes.static),
|
|
68
|
-
canSSR: Boolean(renderModes.canSSR ?? renderModes.ssr),
|
|
69
|
-
canClientIsland: Boolean(renderModes.canClientIsland ?? renderModes.clientIsland),
|
|
70
|
-
} : { canStatic: true, canSSR: true, canClientIsland: false },
|
|
71
|
-
...(component.defaultIslandMode ? { defaultIslandMode: component.defaultIslandMode } : {}),
|
|
72
|
-
...(varyDimensions?.length ? { vary: varyDimensions, varyDimensions } : {}),
|
|
60
|
+
id: componentId,
|
|
61
|
+
name: componentId,
|
|
62
|
+
props: schemaFile,
|
|
73
63
|
...(Object.keys(imageSlots).length ? { imageSlots } : {}),
|
|
64
|
+
...(Object.keys(slots).length ? { slots } : {}),
|
|
74
65
|
});
|
|
75
66
|
}
|
|
76
67
|
|
|
@@ -95,16 +86,14 @@ async function compileRegistry(options) {
|
|
|
95
86
|
});
|
|
96
87
|
}
|
|
97
88
|
|
|
98
|
-
|
|
99
|
-
// validate that referenced component names exist in the registry.
|
|
100
|
-
const knownNames = new Set(componentEntries.map((c) => c.name));
|
|
89
|
+
const knownNames = new Set(componentEntries.map((c) => c.id));
|
|
101
90
|
for (const entry of componentEntries) {
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
91
|
+
for (const [slotName, slot] of Object.entries(entry.slots ?? {})) {
|
|
92
|
+
for (const childName of slot.allowed) {
|
|
93
|
+
if (!knownNames.has(childName)) {
|
|
94
|
+
throw new Error(`${entry.id}.slots.${slotName}.allowed references unknown component "${childName}"`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
108
97
|
}
|
|
109
98
|
}
|
|
110
99
|
|
|
@@ -148,29 +137,84 @@ function validateDataModel(dataModel, index, seen) {
|
|
|
148
137
|
|
|
149
138
|
function validateComponent(component, index, seen) {
|
|
150
139
|
if (!component || typeof component !== 'object') throw new Error(`components[${index}] must be an object`);
|
|
151
|
-
if (!component.
|
|
152
|
-
if (seen.has(component.
|
|
153
|
-
seen.add(component.
|
|
154
|
-
if (!component.
|
|
155
|
-
validateZodSubset(component.
|
|
140
|
+
if (!component.id) throw new Error(`components[${index}].id is required`);
|
|
141
|
+
if (seen.has(component.id)) throw new Error(`duplicate component id ${component.id}`);
|
|
142
|
+
seen.add(component.id);
|
|
143
|
+
if (!component.props) throw new Error(`components[${index}].props is required`);
|
|
144
|
+
validateZodSubset(component.props, `components[${index}].props`);
|
|
156
145
|
validateImageSlotSchemaMatch(component, index);
|
|
157
146
|
}
|
|
158
147
|
|
|
159
148
|
function validateImageSlotSchemaMatch(component, index) {
|
|
160
149
|
const imageSlots = normalizeImageSlots(component.imageSlots);
|
|
161
|
-
const imageFields =
|
|
150
|
+
const imageFields = collectImageFields(component.props);
|
|
162
151
|
for (const field of imageFields) {
|
|
163
152
|
if (!Object.prototype.hasOwnProperty.call(imageSlots, field)) {
|
|
164
|
-
throw new Error(`components[${index}].imageSlots.${field} is required because
|
|
153
|
+
throw new Error(`components[${index}].imageSlots.${field} is required because props field ${field} uses an image schema`);
|
|
165
154
|
}
|
|
166
155
|
}
|
|
167
156
|
for (const field of Object.keys(imageSlots)) {
|
|
168
157
|
if (!imageFields.has(field)) {
|
|
169
|
-
throw new Error(`components[${index}].
|
|
158
|
+
throw new Error(`components[${index}].props.${field} must use an image schema because imageSlots.${field} is declared`);
|
|
170
159
|
}
|
|
171
160
|
}
|
|
172
161
|
}
|
|
173
162
|
|
|
163
|
+
function extractBlockSlots(schema, componentId) {
|
|
164
|
+
const shape = getSchemaShape(schema);
|
|
165
|
+
if (!shape) return {};
|
|
166
|
+
const out = {};
|
|
167
|
+
for (const [name, fieldSchema] of Object.entries(shape)) {
|
|
168
|
+
const unwrapped = unwrapOptionalSchema(fieldSchema);
|
|
169
|
+
const meta = getBlockRefMeta(unwrapped) ?? getBlockRefMeta(unwrapArraySchema(unwrapped));
|
|
170
|
+
if (meta) {
|
|
171
|
+
out[name] = {
|
|
172
|
+
allowed: [...meta.allowed],
|
|
173
|
+
multiple: meta.multiple,
|
|
174
|
+
required: false,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return out;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function unwrapOptionalSchema(schema) {
|
|
182
|
+
let current = schema;
|
|
183
|
+
const seen = new Set();
|
|
184
|
+
while (current && typeof current === 'object' && !seen.has(current)) {
|
|
185
|
+
seen.add(current);
|
|
186
|
+
const def = current._def;
|
|
187
|
+
if (!def || typeof def !== 'object') break;
|
|
188
|
+
const type = def.type;
|
|
189
|
+
const typeName = def.typeName;
|
|
190
|
+
if (type === 'optional' || type === 'nullable' || type === 'default' ||
|
|
191
|
+
typeName === 'ZodOptional' || typeName === 'ZodNullable' || typeName === 'ZodDefault') {
|
|
192
|
+
const inner = def.innerType ?? def.schema;
|
|
193
|
+
if (inner) { current = inner; continue; }
|
|
194
|
+
}
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
return current;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function getSchemaShape(schema) {
|
|
201
|
+
if (!schema || typeof schema !== 'object') return null;
|
|
202
|
+
const def = schema._def;
|
|
203
|
+
if (!def) return null;
|
|
204
|
+
const shape = typeof def.shape === 'function' ? def.shape() : def.shape;
|
|
205
|
+
if (shape && typeof shape === 'object') return shape;
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function unwrapArraySchema(schema) {
|
|
210
|
+
if (!schema || typeof schema !== 'object') return null;
|
|
211
|
+
const def = schema._def;
|
|
212
|
+
if (!def) return null;
|
|
213
|
+
if (def.type === 'array' && def.element) return def.element;
|
|
214
|
+
if (def.typeName === 'ZodArray' && def.type) return def.type;
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
|
|
174
218
|
function normalizeImageSlots(imageSlots) {
|
|
175
219
|
if (imageSlots == null) return {};
|
|
176
220
|
if (typeof imageSlots !== 'object' || Array.isArray(imageSlots)) {
|
|
@@ -228,34 +272,34 @@ function normalizeSlotOutput(slotKey, output, outputIndex) {
|
|
|
228
272
|
};
|
|
229
273
|
}
|
|
230
274
|
|
|
231
|
-
function
|
|
275
|
+
function collectImageFields(schema) {
|
|
232
276
|
const out = new Set();
|
|
233
277
|
|
|
234
|
-
|
|
278
|
+
collectImageFieldsFromSchema(schema, '', out);
|
|
235
279
|
|
|
236
280
|
return out;
|
|
237
281
|
}
|
|
238
282
|
|
|
239
|
-
function
|
|
283
|
+
function collectImageFieldsFromSchema(schema, prefix, out) {
|
|
240
284
|
if (!schema) {
|
|
241
285
|
return;
|
|
242
286
|
}
|
|
243
287
|
|
|
244
|
-
if (prefix &&
|
|
288
|
+
if (prefix && isImageSchema(schema)) {
|
|
245
289
|
out.add(prefix);
|
|
246
290
|
return;
|
|
247
291
|
}
|
|
248
292
|
|
|
249
293
|
const itemSchema = arrayItemSchema(schema);
|
|
250
294
|
if (itemSchema) {
|
|
251
|
-
|
|
295
|
+
collectImageFieldsFromSchema(itemSchema, prefix, out);
|
|
252
296
|
return;
|
|
253
297
|
}
|
|
254
298
|
|
|
255
299
|
const unionOptions = unionOptionSchemas(schema);
|
|
256
300
|
if (unionOptions) {
|
|
257
301
|
for (const option of unionOptions) {
|
|
258
|
-
|
|
302
|
+
collectImageFieldsFromSchema(option, prefix, out);
|
|
259
303
|
}
|
|
260
304
|
return;
|
|
261
305
|
}
|
|
@@ -268,7 +312,7 @@ function collectGradialImageFieldsFromSchema(schema, prefix, out) {
|
|
|
268
312
|
|
|
269
313
|
for (const [field, fieldSchema] of Object.entries(shape)) {
|
|
270
314
|
const nextPrefix = prefix ? `${prefix}.${field}` : field;
|
|
271
|
-
|
|
315
|
+
collectImageFieldsFromSchema(fieldSchema, nextPrefix, out);
|
|
272
316
|
}
|
|
273
317
|
}
|
|
274
318
|
|
|
@@ -299,21 +343,27 @@ function arrayItemSchema(schema) {
|
|
|
299
343
|
return null;
|
|
300
344
|
}
|
|
301
345
|
|
|
302
|
-
function
|
|
346
|
+
function isImageSchema(schema) {
|
|
303
347
|
const unwrapped = unwrapSchema(schema);
|
|
304
|
-
if (unwrapped ===
|
|
348
|
+
if (unwrapped === ImageSchema) return true;
|
|
305
349
|
try {
|
|
306
350
|
const compiled = z.toJSONSchema(unwrapped, {
|
|
307
351
|
target: 'draft-2020-12',
|
|
308
352
|
unrepresentable: 'throw',
|
|
309
353
|
});
|
|
310
|
-
const
|
|
311
|
-
const
|
|
312
|
-
|| (Array.isArray(sources?.anyOf) && sources.anyOf.some((s) => s?.type === 'array'));
|
|
354
|
+
const properties = compiled?.properties ?? {};
|
|
355
|
+
const required = new Set(compiled?.required ?? []);
|
|
313
356
|
return compiled?.type === 'object'
|
|
314
|
-
&&
|
|
315
|
-
&&
|
|
316
|
-
&&
|
|
357
|
+
&& properties.$type?.const === 'gradial.image'
|
|
358
|
+
&& properties.assetId?.type === 'string'
|
|
359
|
+
&& properties.versionId?.type === 'string'
|
|
360
|
+
&& properties.alt?.type === 'string'
|
|
361
|
+
&& properties.sources?.type === 'array'
|
|
362
|
+
&& required.has('$type')
|
|
363
|
+
&& required.has('assetId')
|
|
364
|
+
&& required.has('versionId')
|
|
365
|
+
&& required.has('alt')
|
|
366
|
+
&& required.has('sources');
|
|
317
367
|
} catch {
|
|
318
368
|
return false;
|
|
319
369
|
}
|
|
@@ -324,7 +374,7 @@ function unwrapSchema(schema) {
|
|
|
324
374
|
const seen = new Set();
|
|
325
375
|
while (current && !seen.has(current)) {
|
|
326
376
|
seen.add(current);
|
|
327
|
-
if (current ===
|
|
377
|
+
if (current === ImageSchema) return current;
|
|
328
378
|
const def = current._def;
|
|
329
379
|
const next = def?.innerType ?? def?.schema;
|
|
330
380
|
if (!next) break;
|
|
@@ -381,6 +431,13 @@ async function generatePageSchema(schemaDir, componentEntries, outDir) {
|
|
|
381
431
|
id: { type: 'string', minLength: 1 },
|
|
382
432
|
component: { type: 'string' },
|
|
383
433
|
props: { type: 'object' },
|
|
434
|
+
slots: {
|
|
435
|
+
type: 'object',
|
|
436
|
+
additionalProperties: {
|
|
437
|
+
type: 'array',
|
|
438
|
+
items: { $ref: '#/$defs/baseBlock' },
|
|
439
|
+
},
|
|
440
|
+
},
|
|
384
441
|
},
|
|
385
442
|
},
|
|
386
443
|
damAssetRef: {
|
|
@@ -397,11 +454,11 @@ async function generatePageSchema(schemaDir, componentEntries, outDir) {
|
|
|
397
454
|
};
|
|
398
455
|
const oneOf = [];
|
|
399
456
|
for (const entry of componentEntries) {
|
|
400
|
-
const schemaPath = path.join(outDir, entry.
|
|
457
|
+
const schemaPath = path.join(outDir, entry.props);
|
|
401
458
|
const raw = await readFile(schemaPath, 'utf-8');
|
|
402
459
|
const componentSchema = JSON.parse(raw);
|
|
403
460
|
const { $schema: _s, title: _t, ...propsSchema } = componentSchema;
|
|
404
|
-
|
|
461
|
+
replaceImagesWithDamAssetRef(propsSchema, defs.damAssetRef);
|
|
405
462
|
const defName = `${entry.name}Block`;
|
|
406
463
|
defs[defName] = {
|
|
407
464
|
allOf: [
|
|
@@ -453,37 +510,37 @@ async function generatePageSchema(schemaDir, componentEntries, outDir) {
|
|
|
453
510
|
return pageSchemaPath;
|
|
454
511
|
}
|
|
455
512
|
|
|
456
|
-
function
|
|
513
|
+
function replaceImagesWithDamAssetRef(schema, damAssetRef) {
|
|
457
514
|
if (!schema || typeof schema !== 'object') return schema;
|
|
458
515
|
if (schema.$ref) return;
|
|
459
516
|
if (Array.isArray(schema.anyOf)) {
|
|
460
|
-
for (const s of schema.anyOf)
|
|
517
|
+
for (const s of schema.anyOf) replaceImagesWithDamAssetRef(s, damAssetRef);
|
|
461
518
|
}
|
|
462
519
|
if (Array.isArray(schema.allOf)) {
|
|
463
|
-
for (const s of schema.allOf)
|
|
520
|
+
for (const s of schema.allOf) replaceImagesWithDamAssetRef(s, damAssetRef);
|
|
464
521
|
}
|
|
465
522
|
if (Array.isArray(schema.oneOf)) {
|
|
466
|
-
for (const s of schema.oneOf)
|
|
523
|
+
for (const s of schema.oneOf) replaceImagesWithDamAssetRef(s, damAssetRef);
|
|
467
524
|
}
|
|
468
525
|
if (schema.properties) {
|
|
469
526
|
for (const [key, value] of Object.entries(schema.properties)) {
|
|
470
|
-
if (
|
|
527
|
+
if (isJsonImage(value)) {
|
|
471
528
|
schema.properties[key] = { $ref: '#/$defs/damAssetRef' };
|
|
472
529
|
} else {
|
|
473
|
-
|
|
530
|
+
replaceImagesWithDamAssetRef(value, damAssetRef);
|
|
474
531
|
}
|
|
475
532
|
}
|
|
476
533
|
}
|
|
477
534
|
if (schema.items) {
|
|
478
|
-
if (
|
|
535
|
+
if (isJsonImage(schema.items)) {
|
|
479
536
|
schema.items = { $ref: '#/$defs/damAssetRef' };
|
|
480
537
|
} else {
|
|
481
|
-
|
|
538
|
+
replaceImagesWithDamAssetRef(schema.items, damAssetRef);
|
|
482
539
|
}
|
|
483
540
|
}
|
|
484
541
|
}
|
|
485
542
|
|
|
486
|
-
function
|
|
543
|
+
function isJsonImage(node) {
|
|
487
544
|
return node?.type === 'object'
|
|
488
545
|
&& node?.properties?.$type?.const === 'gradial.image';
|
|
489
546
|
}
|
|
@@ -53,20 +53,17 @@ async function validateContent(options) {
|
|
|
53
53
|
const registry = JSON.parse(registryRaw);
|
|
54
54
|
|
|
55
55
|
// Build schema map: component name → JSON Schema object
|
|
56
|
-
// Build nested blocks map: component name → array of { path, allow? }
|
|
57
56
|
const schemaMap = new Map();
|
|
58
57
|
const knownComponents = new Set();
|
|
59
|
-
const
|
|
58
|
+
const componentMap = new Map();
|
|
60
59
|
for (const component of registry.components) {
|
|
61
60
|
knownComponents.add(component.name);
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
componentMap.set(component.name, component);
|
|
62
|
+
if (component.props) {
|
|
63
|
+
const schemaPath = path.join(registryDir, component.props);
|
|
64
64
|
const schemaRaw = await readFile(schemaPath, 'utf-8');
|
|
65
65
|
schemaMap.set(component.name, JSON.parse(schemaRaw));
|
|
66
66
|
}
|
|
67
|
-
if (Array.isArray(component.nestedBlocks) && component.nestedBlocks.length > 0) {
|
|
68
|
-
nestedBlocksMap.set(component.name, component.nestedBlocks);
|
|
69
|
-
}
|
|
70
67
|
}
|
|
71
68
|
|
|
72
69
|
// Find all compiled route files
|
|
@@ -134,10 +131,7 @@ async function validateContent(options) {
|
|
|
134
131
|
});
|
|
135
132
|
}
|
|
136
133
|
|
|
137
|
-
|
|
138
|
-
const nestedIssues = validateNestedBlocks(
|
|
139
|
-
block.component, props, knownComponents, schemaMap, nestedBlocksMap,
|
|
140
|
-
);
|
|
134
|
+
const nestedIssues = validateNestedSlots(block, componentMap, schemaMap);
|
|
141
135
|
for (const issue of nestedIssues) {
|
|
142
136
|
collector.push({
|
|
143
137
|
page: relativePath,
|
|
@@ -360,83 +354,52 @@ function resolveRef(ref, rootSchema) {
|
|
|
360
354
|
// ---------------------------------------------------------------------------
|
|
361
355
|
|
|
362
356
|
// ---------------------------------------------------------------------------
|
|
363
|
-
//
|
|
357
|
+
// Block ref validation — recursively validates nested blocks inside props.
|
|
364
358
|
// ---------------------------------------------------------------------------
|
|
365
359
|
|
|
366
|
-
function
|
|
360
|
+
function validateNestedSlots(parentBlock, componentMap, schemaMap, path = 'props') {
|
|
367
361
|
const issues = [];
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
362
|
+
const parentComponent = componentMap.get(parentBlock.component);
|
|
363
|
+
if (!parentComponent) return issues;
|
|
364
|
+
|
|
365
|
+
const blockSlots = parentComponent.slots ?? {};
|
|
366
|
+
for (const [slotName, slot] of Object.entries(blockSlots)) {
|
|
367
|
+
const blocks = parentBlock.props?.[slotName];
|
|
368
|
+
if (!Array.isArray(blocks)) {
|
|
369
|
+
if (slot.required) {
|
|
370
|
+
issues.push({ path: `${path}.${slotName}`, message: `${parentBlock.component}: required block ref "${slotName}" has no children` });
|
|
371
|
+
}
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
if (!slot.multiple && blocks.length > 1) {
|
|
375
|
+
issues.push({ path: `${path}.${slotName}`, message: `${parentBlock.component}: block ref "${slotName}" allows at most one child` });
|
|
376
|
+
}
|
|
371
377
|
for (const [index, nestedBlock] of blocks.entries()) {
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
const nestedPath = `${blockPath}[${index}]`;
|
|
377
|
-
|
|
378
|
-
// Check nested component exists in registry
|
|
379
|
-
if (!knownComponents.has(childComponent)) {
|
|
380
|
-
issues.push({
|
|
381
|
-
path: `${nestedPath}.component`,
|
|
382
|
-
message: `${parentComponent} → ${nestedPath}: unknown nested component "${childComponent}"`,
|
|
383
|
-
});
|
|
378
|
+
const nestedPath = `${path}.${slotName}[${index}]`;
|
|
379
|
+
if (!nestedBlock || typeof nestedBlock !== 'object' || typeof nestedBlock.component !== 'string') {
|
|
380
|
+
issues.push({ path: nestedPath, message: `${parentBlock.component}: block ref child must be a block` });
|
|
384
381
|
continue;
|
|
385
382
|
}
|
|
386
383
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
const childProps = nestedBlock.props ?? {};
|
|
392
|
-
const childIssues = validateJsonSchema(childProps, childSchema, `${nestedPath}.props`);
|
|
393
|
-
for (const issue of childIssues) {
|
|
394
|
-
issues.push({
|
|
395
|
-
nestedComponent: childComponent,
|
|
396
|
-
path: issue.path,
|
|
397
|
-
message: `${parentComponent} → ${childComponent}: ${issue.message}`,
|
|
398
|
-
});
|
|
384
|
+
if (!componentMap.has(nestedBlock.component)) {
|
|
385
|
+
issues.push({ path: `${nestedPath}.component`, message: `${parentBlock.component}: unknown nested component "${nestedBlock.component}"` });
|
|
386
|
+
continue;
|
|
399
387
|
}
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
const deeperIssues = validateNestedBlocks(
|
|
403
|
-
childComponent, childProps, knownComponents, schemaMap, nestedBlocksMap,
|
|
404
|
-
);
|
|
405
|
-
issues.push(...deeperIssues);
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
return issues;
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
/**
|
|
412
|
-
* Walks a props object looking for arrays of block-shaped objects
|
|
413
|
-
* (objects with `component` + `props` fields).
|
|
414
|
-
*/
|
|
415
|
-
function findNestedBlockArrays(obj, prefix = 'props') {
|
|
416
|
-
const results = [];
|
|
417
|
-
if (!obj || typeof obj !== 'object') return results;
|
|
418
|
-
|
|
419
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
420
|
-
const currentPath = `${prefix}.${key}`;
|
|
421
|
-
if (Array.isArray(value)) {
|
|
422
|
-
// Check if array contains block-shaped objects
|
|
423
|
-
const hasBlocks = value.some(
|
|
424
|
-
(item) => item && typeof item === 'object' && typeof item.component === 'string' && 'props' in item,
|
|
425
|
-
);
|
|
426
|
-
if (hasBlocks) {
|
|
427
|
-
results.push({ path: currentPath, blocks: value });
|
|
388
|
+
if (!slot.allowed.includes(nestedBlock.component)) {
|
|
389
|
+
issues.push({ path: `${nestedPath}.component`, message: `${parentBlock.component}: component "${nestedBlock.component}" is not allowed in block ref "${slotName}"` });
|
|
428
390
|
}
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
391
|
+
|
|
392
|
+
const childSchema = schemaMap.get(nestedBlock.component);
|
|
393
|
+
const childProps = nestedBlock.props ?? {};
|
|
394
|
+
if (childSchema) {
|
|
395
|
+
for (const issue of validateJsonSchema(childProps, childSchema, `${nestedPath}.props`)) {
|
|
396
|
+
issues.push({ nestedComponent: nestedBlock.component, path: issue.path, message: `${parentBlock.component} → ${nestedBlock.component}: ${issue.message}` });
|
|
433
397
|
}
|
|
434
398
|
}
|
|
435
|
-
|
|
436
|
-
results.push(...findNestedBlockArrays(value, currentPath));
|
|
399
|
+
issues.push(...validateNestedSlots(nestedBlock, componentMap, schemaMap, nestedPath));
|
|
437
400
|
}
|
|
438
401
|
}
|
|
439
|
-
return
|
|
402
|
+
return issues;
|
|
440
403
|
}
|
|
441
404
|
|
|
442
405
|
function collectBlocks(pageData) {
|