@valbuild/next 0.33.0 → 0.35.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 +296 -274
- package/client/dist/valbuild-next-client.cjs.dev.js +11 -58
- package/client/dist/valbuild-next-client.cjs.prod.js +11 -58
- package/client/dist/valbuild-next-client.esm.js +10 -57
- package/dist/ValNextProvider-3676dacc.esm.js +25 -0
- package/dist/ValNextProvider-ae031b12.cjs.dev.js +29 -0
- package/dist/ValNextProvider-fe84f6a9.cjs.js +7 -0
- package/dist/ValNextProvider-fe84f6a9.cjs.prod.js +29 -0
- package/dist/declarations/src/ValProvider.d.ts +4 -2
- package/dist/declarations/src/client/index.d.ts +1 -1
- package/dist/declarations/src/client/initValClient.d.ts +8 -0
- package/dist/declarations/src/initVal.d.ts +3 -1
- package/dist/declarations/src/rsc/index.d.ts +2 -0
- package/dist/declarations/src/rsc/initValRsc.d.ts +19 -0
- package/dist/declarations/src/server/index.d.ts +1 -1
- package/dist/declarations/src/server/initValServer.d.ts +11 -0
- package/dist/slicedToArray-1471796d.cjs.dev.js +58 -0
- package/dist/slicedToArray-4190fac1.cjs.prod.js +58 -0
- package/dist/slicedToArray-62cd636a.esm.js +56 -0
- package/dist/valbuild-next.cjs.dev.js +63 -4
- package/dist/valbuild-next.cjs.prod.js +63 -4
- package/dist/valbuild-next.esm.js +63 -5
- package/package.json +15 -6
- package/rsc/dist/valbuild-next-rsc.cjs.d.ts +2 -0
- package/rsc/dist/valbuild-next-rsc.cjs.d.ts.map +1 -0
- package/rsc/dist/valbuild-next-rsc.cjs.dev.js +159 -0
- package/rsc/dist/valbuild-next-rsc.cjs.js +7 -0
- package/rsc/dist/valbuild-next-rsc.cjs.prod.js +159 -0
- package/rsc/dist/valbuild-next-rsc.esm.js +155 -0
- package/rsc/package.json +4 -0
- package/server/dist/valbuild-next-server.cjs.dev.js +78 -133
- package/server/dist/valbuild-next-server.cjs.prod.js +78 -133
- package/server/dist/valbuild-next-server.esm.js +78 -115
- package/dist/declarations/src/client/useVal.d.ts +0 -3
- package/dist/declarations/src/server/fetchVal.d.ts +0 -3
package/README.md
CHANGED
|
@@ -1,9 +1,40 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<h1 align="center">Val</h1>
|
|
3
2
|
<p align="center">
|
|
4
|
-
|
|
3
|
+
<a href="https://app.val.build">
|
|
4
|
+
<svg width="100" height="100" viewBox="0 0 944 944" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
5
|
+
<circle cx="472" cy="472" r="472" fill="#1D1C28"/>
|
|
6
|
+
<g filter="url(#filter0_d_14_601)">
|
|
7
|
+
<path d="M181 348C181 345.791 182.791 344 185 344H320C322.209 344 324 345.791 324 348V602C324 604.209 322.209 606 320 606H185C182.791 606 181 604.209 181 602V348Z" fill="#38CD80"/>
|
|
8
|
+
</g>
|
|
9
|
+
<g filter="url(#filter1_i_14_601)">
|
|
10
|
+
<circle cx="252" cy="550" r="24" fill="#1E1F2A"/>
|
|
11
|
+
</g>
|
|
12
|
+
<path d="M659.085 550.374H658.585H654.427C652.095 550.374 650.434 549.729 649.347 548.522C648.25 547.306 647.658 545.431 647.658 542.807V439.857C647.658 437.924 646.091 436.357 644.158 436.357H629.16C627.227 436.357 625.66 437.924 625.66 439.857V455.828C625.66 456.658 624.986 457.332 624.155 457.332C623.593 457.332 623.072 457.015 622.798 456.508C618.502 448.559 612.704 442.661 605.399 438.838C597.962 434.671 589.622 432.592 580.394 432.592C571.897 432.592 563.846 434.128 556.247 437.2C548.643 440.274 541.944 444.796 536.154 450.761L536.153 450.761C530.537 456.552 526.106 463.693 522.854 472.174C519.598 480.668 517.975 490.411 517.975 501.395V505.697C517.975 516.86 519.597 526.693 522.854 535.187C526.105 543.667 530.535 550.895 536.148 556.864L536.153 556.869L536.159 556.875C541.95 562.659 548.647 567.088 556.246 570.161L556.256 570.165C563.856 573.057 572.083 574.5 580.932 574.5C589.456 574.5 597.527 572.325 605.137 567.982C612.625 563.807 618.519 557.469 622.822 548.992C623.085 548.475 623.609 548.147 624.176 548.147H624.546C625.161 548.147 625.66 548.646 625.66 549.261C625.66 555.468 627.583 560.617 631.452 564.665L631.451 564.665L631.46 564.673C635.511 568.72 640.668 570.735 646.889 570.735H658.585H659.085H661.157H661.657H760C761.933 570.735 763.5 569.168 763.5 567.235V553.874C763.5 551.941 761.933 550.374 760 550.374H733.542C732.161 550.374 731.042 549.255 731.042 547.874V385C731.042 383.067 729.475 381.5 727.542 381.5H680.701C678.768 381.5 677.201 383.067 677.201 385V398.361C677.201 400.294 678.768 401.861 680.701 401.861H706.543C707.924 401.861 709.043 402.981 709.043 404.361V547.874C709.043 549.255 707.924 550.374 706.543 550.374H661.657H661.157H659.085ZM600.117 550.146L600.111 550.149C594.977 552.448 589.304 553.601 583.086 553.601C570.468 553.601 560.194 549.435 552.222 541.12C544.436 532.633 540.512 520.847 540.512 505.697V501.395C540.512 494.274 541.581 487.79 543.712 481.936L543.714 481.931C545.849 475.89 548.778 470.842 552.495 466.775C556.398 462.521 560.92 459.246 566.061 456.944C571.195 454.645 576.867 453.492 583.086 453.492C589.117 453.492 594.696 454.731 599.829 457.207L599.838 457.211L599.848 457.215C605.166 459.517 609.681 462.79 613.4 467.035L613.4 467.035L613.408 467.044C617.306 471.292 620.324 476.431 622.458 482.469L622.459 482.474C624.59 488.328 625.66 494.812 625.66 501.933V505.16C625.66 512.46 624.59 519.125 622.458 525.159C620.324 531.022 617.393 536.075 613.669 540.326C609.95 544.571 605.435 547.844 600.117 550.146ZM464.902 570.735C466.39 570.735 467.716 569.794 468.206 568.389L512.685 441.011C513.479 438.736 511.79 436.357 509.38 436.357H491.006C489.496 436.357 488.157 437.325 487.683 438.758L447.951 558.864C447.716 559.575 447.051 560.055 446.303 560.055C445.554 560.055 444.89 559.575 444.655 558.864L404.923 438.758C404.449 437.325 403.109 436.357 401.6 436.357H383.225C380.815 436.357 379.126 438.736 379.921 441.011L424.399 568.389C424.89 569.794 426.215 570.735 427.704 570.735H464.902Z" fill="white" stroke="white"/>
|
|
13
|
+
<defs>
|
|
14
|
+
<filter id="filter0_d_14_601" x="127.464" y="290.464" width="250.072" height="369.072" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
|
15
|
+
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
|
16
|
+
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
|
17
|
+
<feOffset/>
|
|
18
|
+
<feGaussianBlur stdDeviation="26.768"/>
|
|
19
|
+
<feComposite in2="hardAlpha" operator="out"/>
|
|
20
|
+
<feColorMatrix type="matrix" values="0 0 0 0 0.219608 0 0 0 0 0.803922 0 0 0 0 0.501961 0 0 0 0.3 0"/>
|
|
21
|
+
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_14_601"/>
|
|
22
|
+
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_14_601" result="shape"/>
|
|
23
|
+
</filter>
|
|
24
|
+
<filter id="filter1_i_14_601" x="228" y="526" width="48" height="48" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
|
25
|
+
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
|
26
|
+
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
|
27
|
+
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
|
28
|
+
<feOffset/>
|
|
29
|
+
<feGaussianBlur stdDeviation="6"/>
|
|
30
|
+
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
|
|
31
|
+
<feColorMatrix type="matrix" values="0 0 0 0 0.219608 0 0 0 0 0.803922 0 0 0 0 0.501961 0 0 0 0.3 0"/>
|
|
32
|
+
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_14_601"/>
|
|
33
|
+
</filter>
|
|
34
|
+
</defs>
|
|
35
|
+
</svg>
|
|
36
|
+
</a>
|
|
5
37
|
<br/>
|
|
6
|
-
hard-coded content - super-charged
|
|
7
38
|
</p>
|
|
8
39
|
</p>
|
|
9
40
|
|
|
@@ -11,57 +42,117 @@
|
|
|
11
42
|
|
|
12
43
|
Val is PRE-ALPHA - MOST features are broken and in state of flux.
|
|
13
44
|
|
|
14
|
-
This is
|
|
45
|
+
This is release is only for **INTERNAL** **TESTING** PURPOSES.
|
|
15
46
|
|
|
16
47
|
## Table of contents
|
|
17
48
|
|
|
18
|
-
- [Table of contents](#table-of-contents)
|
|
19
49
|
- [Introduction](#introduction)
|
|
20
50
|
- [Installation](#installation)
|
|
21
51
|
- [Getting started](#getting-started)
|
|
22
|
-
- [
|
|
23
|
-
- [String](#string)
|
|
24
|
-
- [Number](#number)
|
|
25
|
-
- [Boolean](#boolean)
|
|
26
|
-
- [Optional](#optional)
|
|
27
|
-
- [Array](#array)
|
|
28
|
-
- [
|
|
29
|
-
- [
|
|
30
|
-
- [
|
|
31
|
-
- [
|
|
32
|
-
- [
|
|
33
|
-
- [One-of references](#one-of-references)
|
|
34
|
-
- [Remote](#remote)
|
|
35
|
-
- [Selector](#selector)
|
|
52
|
+
- [Schema types](#schema-types):
|
|
53
|
+
- [String](#string)
|
|
54
|
+
- [Number](#number)
|
|
55
|
+
- [Boolean](#boolean)
|
|
56
|
+
- [Optional](#optional)
|
|
57
|
+
- [Array](#array)
|
|
58
|
+
- [Record](#record)
|
|
59
|
+
- [Object](#object)
|
|
60
|
+
- [Rich text](#richtext)
|
|
61
|
+
- [Image](#image)
|
|
62
|
+
- [keyOf](#keyof)
|
|
36
63
|
|
|
37
64
|
## Introduction
|
|
38
65
|
|
|
39
|
-
Val
|
|
66
|
+
Val is a CMS where **content** is **code** stored in your git repo.
|
|
40
67
|
|
|
41
|
-
|
|
68
|
+
As a CMS, Val is useful because:
|
|
42
69
|
|
|
43
|
-
-
|
|
70
|
+
- editors can **change content** without having to ask developers to do it for them (and nobody wants that)
|
|
71
|
+
- a **well-documented** way to **structure content**
|
|
72
|
+
- **image** support is built-in
|
|
73
|
+
- **richtext** support is built-in
|
|
74
|
+
- built-in **visual editing** which lets editors click-then-edit content (and therefore **code**!) directly in your app
|
|
44
75
|
|
|
45
|
-
-
|
|
76
|
+

|
|
46
77
|
|
|
47
|
-
|
|
78
|
+
<details>
|
|
79
|
+
<summary>Definition: editor</summary>
|
|
80
|
+
An editor in this context, is a non-technical person that edits content in your application (technical writer, proof-reader, legal, ...).
|
|
81
|
+
</details>
|
|
48
82
|
|
|
49
|
-
|
|
83
|
+
<br />
|
|
50
84
|
|
|
51
|
-
|
|
85
|
+
But, with the benefits of **hard-coded** content:
|
|
52
86
|
|
|
53
|
-
-
|
|
87
|
+
- works seamlessly **locally** or with git **branches**
|
|
88
|
+
- content is **type-checked** so you can spend less time on figuring out why something isn't working
|
|
89
|
+

|
|
90
|
+
- content can be refactored (change names, etc) just as if it was hard-coded (because it is)
|
|
91
|
+

|
|
92
|
+
- works as normal with your **favorite IDE** without any plugins: search for content, references to usages, ...
|
|
93
|
+

|
|
94
|
+
- **no** need for **code-gen** and extra build steps
|
|
95
|
+
- **fast** since the content is literally hosted with the application
|
|
96
|
+
- content is **always there** and can never fail (since it is not loaded from somewhere)
|
|
97
|
+
- no need to manage different **environments** containing different versions of content
|
|
98
|
+
- **resolve conflicts** like you normally resolve conflicts: **in git**
|
|
99
|
+
|
|
100
|
+
Compared to other CMSs, Val has the following advantages:
|
|
101
|
+
|
|
102
|
+
- **easy** to setup and to _grok_: Val is designed to have a minimum of boilerplate and there's **0** query languages to learn. If you know your way around JSON that's enough (if you don't you might want to learn it)
|
|
103
|
+
- **no signup** required to use it locally
|
|
104
|
+
- **no fees** for content that is in your code: your content is your code, and your code is... yours
|
|
105
|
+
- **minimal** API surface: Val is designed to not "infect" your code base
|
|
106
|
+
- **easy to remove**: since your content is already in your code and Val is designed to have a minimal surface, it's easy to remove if you want to switch
|
|
107
|
+
|
|
108
|
+
<details>
|
|
109
|
+
<summary>Upcoming feature: <strong>i18n</strong></summary>
|
|
110
|
+
Val will soon have support for i18n. Follow this repository to get notified when this is the case.
|
|
111
|
+
</details>
|
|
112
|
+
|
|
113
|
+
<details>
|
|
114
|
+
<summary>Upcoming feature: <strong>remote content</strong></summary>
|
|
115
|
+
Having hard-coded content is great for landing pages, product pages and other pages where the amount of content is manageable.
|
|
116
|
+
|
|
117
|
+
However, checking in the 10 000th blog entry in git might feel wrong (though we would say it is ok).
|
|
118
|
+
|
|
119
|
+
Therefore, Val will add `remote content` support which enables you to seamlessly move content to the cloud and back again as desired.
|
|
120
|
+
You code will still be the one truth, but the actual content will be hosted on [val.build](https://val.build).
|
|
121
|
+
|
|
122
|
+
`.remote()` support will also make it possible to have remote images to avoid having to put them in your repository.
|
|
123
|
+
|
|
124
|
+
There will also be specific support for remote i18n, which will make it possible to split which languages are defined in code, and which are fetched from remote.
|
|
125
|
+
|
|
126
|
+
More details on `.remote()` will follow later.
|
|
127
|
+
|
|
128
|
+
</details>
|
|
129
|
+
|
|
130
|
+
## When to NOT use Val
|
|
131
|
+
|
|
132
|
+
Val is designed to work well on a single web-app, and currently only supports Next 13.4+ (more meta-frameworks will supported) and GitHub (more Git providers will follow).
|
|
133
|
+
|
|
134
|
+
Unless your application fits these requirements, you should have a look elsewhere (at least for now).
|
|
135
|
+
|
|
136
|
+
In addition, if you have a "content model", i.e. content schemas, that rarely change and you plan on using them in a lot of different applications (web, mobile, etc), Val will most likely not be a great fit.
|
|
137
|
+
|
|
138
|
+
If that is the case, we recommend having a look at [sanity](https://sanity.io) instead (we have no affiliation, but if we didn't have Val we would use Sanity).
|
|
139
|
+
|
|
140
|
+
**NOTE**: Our experience is that however nice it sounds, it is hard to "nail" the content model down. Usually content is derived from what you want to present, not vice-versa. In addition, you should think carefully whether you _really_ want to present the exact same content on all these different surfaces.
|
|
141
|
+
|
|
142
|
+
## Examples
|
|
143
|
+
|
|
144
|
+
Check out this README or the [examples](./examples) directory for examples.
|
|
54
145
|
|
|
55
146
|
## Installation
|
|
56
147
|
|
|
57
|
-
- Make sure you have TypeScript
|
|
58
|
-
- Install the
|
|
148
|
+
- Make sure you have TypeScript 5+, Next 13.4+ (other meta frameworks will come), React 18.20.+ (other frontend frameworks will come)
|
|
149
|
+
- Install the package:
|
|
59
150
|
|
|
60
151
|
```sh
|
|
61
|
-
npm install @valbuild/next
|
|
152
|
+
npm install @valbuild/next@latest
|
|
62
153
|
```
|
|
63
154
|
|
|
64
|
-
- Create
|
|
155
|
+
- Create a `val.config.ts` file. **NOTE**: this file should be in the same directory as `tsconfig.json`:
|
|
65
156
|
|
|
66
157
|
```ts
|
|
67
158
|
// ./val.config.ts
|
|
@@ -73,127 +164,170 @@ const { s, val } = initVal();
|
|
|
73
164
|
export { s, val };
|
|
74
165
|
```
|
|
75
166
|
|
|
76
|
-
-
|
|
167
|
+
- Create the endpoints file:
|
|
77
168
|
|
|
78
169
|
```ts
|
|
79
170
|
// ./src/pages/api/val/[...val].ts
|
|
80
171
|
|
|
81
|
-
import {
|
|
172
|
+
import { createRequestListener } from "@valbuild/server";
|
|
82
173
|
import { NextApiHandler } from "next";
|
|
83
174
|
|
|
84
|
-
const handler: NextApiHandler =
|
|
175
|
+
const handler: NextApiHandler = createRequestListener("/api/val", {
|
|
176
|
+
valConfigPath: "./val.config",
|
|
177
|
+
});
|
|
85
178
|
|
|
86
179
|
export default handler;
|
|
87
180
|
|
|
88
181
|
export const config = {
|
|
89
182
|
api: {
|
|
183
|
+
responseLimit: false,
|
|
90
184
|
bodyParser: false,
|
|
91
185
|
externalResolver: true,
|
|
92
186
|
},
|
|
93
187
|
};
|
|
94
188
|
```
|
|
95
189
|
|
|
96
|
-
-
|
|
190
|
+
- Use the Val provider in the root layout file:
|
|
97
191
|
|
|
98
192
|
```tsx
|
|
99
|
-
// ./
|
|
193
|
+
// ./app/layout.tsx
|
|
100
194
|
|
|
101
|
-
import { ValProvider } from "@valbuild/
|
|
102
|
-
import type { AppProps } from "next/app";
|
|
195
|
+
import { ValProvider } from "@valbuild/next";
|
|
103
196
|
|
|
104
|
-
function
|
|
197
|
+
export default function RootLayout({
|
|
198
|
+
children,
|
|
199
|
+
}: {
|
|
200
|
+
children: React.ReactNode;
|
|
201
|
+
}) {
|
|
105
202
|
return (
|
|
106
|
-
<
|
|
107
|
-
<
|
|
108
|
-
|
|
203
|
+
<html lang="en">
|
|
204
|
+
<body className={inter.className}>
|
|
205
|
+
<ValProvider> {children}</ValProvider>
|
|
206
|
+
</body>
|
|
207
|
+
</html>
|
|
109
208
|
);
|
|
110
209
|
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### [OPTIONAL]: Setup eslint
|
|
213
|
+
|
|
214
|
+
Install the eslint package:
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
npm install @valbuild/eslint-plugin@latest
|
|
218
|
+
```
|
|
111
219
|
|
|
112
|
-
|
|
220
|
+
Add the following to your `.eslintrc.json`:
|
|
221
|
+
|
|
222
|
+
```json
|
|
223
|
+
"plugins": ["@valbuild"],
|
|
224
|
+
"rules": {
|
|
225
|
+
"@valbuild/no-illegal-module-ids": "error"
|
|
226
|
+
}
|
|
113
227
|
```
|
|
114
228
|
|
|
229
|
+
### Add editor support
|
|
230
|
+
|
|
231
|
+
To make it possible to do non-local edits, head over to [val.build](https://val.build) and import your repository.
|
|
232
|
+
|
|
233
|
+
**NOTE**: your content is yours. No subscription (or similar) is required to host content from your repository.
|
|
234
|
+
|
|
235
|
+
If you do not need to edit content online (i.e. not locally), you do not need to sign up.
|
|
236
|
+
|
|
237
|
+
**WHY**: to update your code, we need to create a commit. This requires a server. We opted to create a service that does this easily, instead of having a self-hosted alternative, since time spent is money used. Also, the company behind [val.build](https://val.build) is the company that funds the development of this software.
|
|
238
|
+
|
|
115
239
|
## Getting started
|
|
116
240
|
|
|
117
241
|
### Create your first Val content file
|
|
118
242
|
|
|
119
|
-
Content
|
|
243
|
+
Content in Val is always defined in `.val.ts` files.
|
|
244
|
+
|
|
245
|
+
**NOTE**: Val also works with `.js` files.
|
|
246
|
+
|
|
247
|
+
They must export a default `val.content` where the first argument equals the path of the file relative to the `val.config.ts` file.
|
|
248
|
+
|
|
249
|
+
**NOTE**: `val.ts` files are _evaluated_ by Val, therefore they have a specific set of requirements:
|
|
250
|
+
|
|
251
|
+
- They must have a default export that is `val.content`, they must have a `export const schema` with the Schema; and
|
|
252
|
+
- they CANNOT import anything other than `val.config` and `@valbuild/core`
|
|
120
253
|
|
|
121
|
-
|
|
254
|
+
### Example of a `.val.ts` file
|
|
122
255
|
|
|
123
256
|
```ts
|
|
124
|
-
// ./src/content
|
|
257
|
+
// ./src/app/content.val.ts
|
|
125
258
|
|
|
126
259
|
import { s, val } from "../../../val.config";
|
|
127
260
|
|
|
261
|
+
export const schema = s.object({
|
|
262
|
+
title: s.string().optional(), // <- NOTE: optional()
|
|
263
|
+
sections: s.array(
|
|
264
|
+
s.object({
|
|
265
|
+
title: s.string(),
|
|
266
|
+
text: s.richtext({
|
|
267
|
+
bold: true, // <- Enables bold in richtext
|
|
268
|
+
}),
|
|
269
|
+
})
|
|
270
|
+
),
|
|
271
|
+
});
|
|
272
|
+
|
|
128
273
|
export default val.content(
|
|
129
|
-
"/src/content
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
274
|
+
"/src/app/content", // <- NOTE: this must be the same path as the file
|
|
275
|
+
schema,
|
|
276
|
+
{
|
|
277
|
+
title: "My Page",
|
|
278
|
+
sections: [
|
|
279
|
+
{
|
|
280
|
+
title: "Section 1",
|
|
281
|
+
text: val.richtext`
|
|
282
|
+
RichText is **awesome**`,
|
|
283
|
+
},
|
|
284
|
+
],
|
|
285
|
+
}
|
|
141
286
|
);
|
|
142
287
|
```
|
|
143
288
|
|
|
144
289
|
### Use your content
|
|
145
290
|
|
|
146
|
-
|
|
147
|
-
// ./src/pages/example/index.tsx
|
|
291
|
+
In client components you can access your content with the `useVal` hook:
|
|
148
292
|
|
|
293
|
+
**NOTE**: Support for React Server Components and server side rendering will come soon.
|
|
294
|
+
|
|
295
|
+
```tsx
|
|
296
|
+
// ./src/app/page.tsx
|
|
297
|
+
"use client";
|
|
149
298
|
import { NextPage } from "next";
|
|
150
|
-
import { useVal } from "@valbuild/
|
|
151
|
-
import
|
|
299
|
+
import { useVal } from "@valbuild/next/client";
|
|
300
|
+
import contentVal from "./content.val";
|
|
152
301
|
|
|
153
|
-
const
|
|
154
|
-
const
|
|
302
|
+
const Page: NextPage = () => {
|
|
303
|
+
const { title, sections } = useVal(contentVal);
|
|
155
304
|
return (
|
|
156
305
|
<main>
|
|
157
|
-
|
|
306
|
+
{title && (
|
|
307
|
+
<section>
|
|
308
|
+
<h1>{title}</h1>
|
|
309
|
+
</section>
|
|
310
|
+
)}
|
|
311
|
+
{sections.map((section) => (
|
|
158
312
|
<section>
|
|
159
|
-
<
|
|
160
|
-
<
|
|
313
|
+
<h2>{section.title}</h2>
|
|
314
|
+
<ValRichText
|
|
315
|
+
theme={{
|
|
316
|
+
bold: "font-bold",
|
|
317
|
+
}}
|
|
318
|
+
>
|
|
319
|
+
{section.text}
|
|
320
|
+
</ValRichText>
|
|
161
321
|
</section>
|
|
162
|
-
|
|
322
|
+
))}
|
|
163
323
|
</main>
|
|
164
324
|
);
|
|
165
325
|
};
|
|
166
326
|
|
|
167
|
-
export default
|
|
327
|
+
export default Page;
|
|
168
328
|
```
|
|
169
329
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
`.val.{ts|js}` files **MUST** have a default export which is a ValModule. A ValModule is a special type of [Selector](#selectors). Selectors makes it possible to select a subset of content from a ValModule or a another Selector.
|
|
173
|
-
|
|
174
|
-
Selectors can be turned to Val types using `useVal` or `fetchVal`.
|
|
175
|
-
|
|
176
|
-
### .val files
|
|
177
|
-
|
|
178
|
-
`.val.{ts|js}` files are the files in which you store your content.
|
|
179
|
-
|
|
180
|
-
They are evaluated when the content is run, therefore they have a specific set of requirements. They must have a default export that is `val.content`, they must have a `export const schema` with the Schema and they CANNOT import anything other than `val.config` and `@valbuild/core`.
|
|
181
|
-
|
|
182
|
-
Example:
|
|
183
|
-
|
|
184
|
-
```ts
|
|
185
|
-
import { s, val } from "../val.config";
|
|
186
|
-
|
|
187
|
-
export const schema = t.string();
|
|
188
|
-
|
|
189
|
-
export default val.content(
|
|
190
|
-
"/file/path/relative/to/val/config",
|
|
191
|
-
schema,
|
|
192
|
-
"My string content"
|
|
193
|
-
);
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
NOTE: IN THE FUTURE, they will be validated by the eslint-plugin.
|
|
330
|
+
# Schema types
|
|
197
331
|
|
|
198
332
|
## String
|
|
199
333
|
|
|
@@ -203,16 +337,6 @@ import { s } from "./val.config";
|
|
|
203
337
|
s.string(); // <- Schema<string>
|
|
204
338
|
```
|
|
205
339
|
|
|
206
|
-
### String selectors
|
|
207
|
-
|
|
208
|
-
See [Selectors](#selector) for more info.
|
|
209
|
-
|
|
210
|
-
### String `.eq`
|
|
211
|
-
|
|
212
|
-
```ts
|
|
213
|
-
useVal(stringVal.eq("")); // <- Val<boolean>
|
|
214
|
-
```
|
|
215
|
-
|
|
216
340
|
## Number
|
|
217
341
|
|
|
218
342
|
```ts
|
|
@@ -221,16 +345,6 @@ import { s } from "./val.config";
|
|
|
221
345
|
s.number(); // <- Schema<number>
|
|
222
346
|
```
|
|
223
347
|
|
|
224
|
-
### Number selectors
|
|
225
|
-
|
|
226
|
-
See [Selectors](#selector) for more info.
|
|
227
|
-
|
|
228
|
-
### Number `.eq`
|
|
229
|
-
|
|
230
|
-
```ts
|
|
231
|
-
useVal(numberVal.eq(2)); // <- Val<boolean>
|
|
232
|
-
```
|
|
233
|
-
|
|
234
348
|
## Boolean
|
|
235
349
|
|
|
236
350
|
```ts
|
|
@@ -239,16 +353,6 @@ import { s } from "./val.config";
|
|
|
239
353
|
s.boolean(); // <- Schema<boolean>
|
|
240
354
|
```
|
|
241
355
|
|
|
242
|
-
### Boolean selectors
|
|
243
|
-
|
|
244
|
-
See [Selectors](#selector) for more info.
|
|
245
|
-
|
|
246
|
-
### Boolean `.eq`
|
|
247
|
-
|
|
248
|
-
```ts
|
|
249
|
-
useVal(booleanVal.eq(true)); // <- Val<boolean>
|
|
250
|
-
```
|
|
251
|
-
|
|
252
356
|
## Optional
|
|
253
357
|
|
|
254
358
|
All schema types can be optional. An optional schema creates a union of the type and `null`.
|
|
@@ -259,34 +363,22 @@ import { s } from "./val.config";
|
|
|
259
363
|
s.string().optional(); // <- Schema<string | null>
|
|
260
364
|
```
|
|
261
365
|
|
|
262
|
-
### Selectors
|
|
263
|
-
|
|
264
|
-
### Accessing the underlying type: `.andThen`
|
|
265
|
-
|
|
266
|
-
To use and optional val, you can use the [.andThen](#andthen) selector.
|
|
267
|
-
|
|
268
366
|
## Array
|
|
269
367
|
|
|
270
368
|
```ts
|
|
271
|
-
s.array(t.string());
|
|
369
|
+
s.array(t.string()); // <- Schema<string[]>
|
|
272
370
|
```
|
|
273
371
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
### `.filter`
|
|
277
|
-
|
|
278
|
-
TODO: text
|
|
372
|
+
## Record
|
|
279
373
|
|
|
280
|
-
|
|
281
|
-
useVal(myArray.filter((item) => item.title.eq("Title 1")));
|
|
282
|
-
```
|
|
374
|
+
The type of `s.record` is `Record`.
|
|
283
375
|
|
|
284
|
-
|
|
376
|
+
It is similar to an array, in that editors can add and remove items in it, however it has a unique key which can be used as, for example, the slug or as a part of an route.
|
|
285
377
|
|
|
286
|
-
|
|
378
|
+
**NOTE**: records can also be used with `keyOf`.
|
|
287
379
|
|
|
288
380
|
```ts
|
|
289
|
-
|
|
381
|
+
s.record(t.number()); // <- Schema<Record<string, number>>
|
|
290
382
|
```
|
|
291
383
|
|
|
292
384
|
## Object
|
|
@@ -297,24 +389,23 @@ s.object({
|
|
|
297
389
|
});
|
|
298
390
|
```
|
|
299
391
|
|
|
300
|
-
|
|
392
|
+
## RichText
|
|
301
393
|
|
|
302
|
-
|
|
394
|
+
<details>
|
|
395
|
+
<summary>RichText in Val represented both in code and to the editors as <strong>semantic html5</strong>.</summary>
|
|
303
396
|
|
|
304
|
-
|
|
397
|
+
This means that content will be accessible and according to spec out of the box. The flip-side is that Val will not support RichText that includes elements that is not part of the html 5 standard.
|
|
305
398
|
|
|
306
|
-
|
|
307
|
-
useVal({ foo: myObjectVal.hello });
|
|
308
|
-
```
|
|
399
|
+
This opinionated approach was chosen since rendering anything, makes it hard for developers to maintain and hard for editors to understand.
|
|
309
400
|
|
|
310
|
-
|
|
401
|
+
</details>
|
|
311
402
|
|
|
312
403
|
### RichText Schema
|
|
313
404
|
|
|
314
405
|
```ts
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
406
|
+
s.richtext({
|
|
407
|
+
// options
|
|
408
|
+
});
|
|
318
409
|
```
|
|
319
410
|
|
|
320
411
|
### Initializing RichText content
|
|
@@ -324,54 +415,60 @@ To initialize some text content using a RichText schema, you can use follow the
|
|
|
324
415
|
```ts
|
|
325
416
|
import { s, val } from "./val.config";
|
|
326
417
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
type: "text",
|
|
339
|
-
version: 1,
|
|
340
|
-
mode: "normal",
|
|
341
|
-
format: 0,
|
|
342
|
-
detail: 0,
|
|
343
|
-
style: "",
|
|
344
|
-
text: "TODO: update me",
|
|
345
|
-
},
|
|
346
|
-
],
|
|
347
|
-
},
|
|
348
|
-
],
|
|
349
|
-
direction: "ltr",
|
|
350
|
-
format: "",
|
|
351
|
-
indent: 0,
|
|
352
|
-
type: "root",
|
|
353
|
-
version: 1,
|
|
418
|
+
export const schema = s.richtext({
|
|
419
|
+
// styling:
|
|
420
|
+
bold: true, // enables bold
|
|
421
|
+
//italic: true, // enables italic text
|
|
422
|
+
//lineThrough: true, // enables line/strike-through
|
|
423
|
+
// tags:
|
|
424
|
+
//headings: ["h1", "h2", "h3", "h4", "h5", "h6"], // sets which headings are available
|
|
425
|
+
//a: true, // enables links
|
|
426
|
+
//img: true, // enables images
|
|
427
|
+
//ul: true, // enables unordered lists
|
|
428
|
+
//ol: true, // enables ordered lists
|
|
354
429
|
});
|
|
430
|
+
|
|
431
|
+
export default val.content(
|
|
432
|
+
"/src/app/content",
|
|
433
|
+
schema,
|
|
434
|
+
val.richtext`
|
|
435
|
+
NOTE: this is markdown.
|
|
436
|
+
|
|
437
|
+
**Bold** text.
|
|
438
|
+
`
|
|
439
|
+
);
|
|
355
440
|
```
|
|
356
441
|
|
|
357
|
-
###
|
|
442
|
+
### Rendering RichText
|
|
358
443
|
|
|
359
444
|
You can use the `ValRichText` component to render content.
|
|
360
445
|
|
|
361
446
|
```tsx
|
|
362
447
|
"use client";
|
|
363
|
-
import {
|
|
364
|
-
import
|
|
448
|
+
import { ValRichText } from "@valbuild/next";
|
|
449
|
+
import contentVal from "./content.val";
|
|
450
|
+
import { useVal } from "@valbuild/next/client";
|
|
365
451
|
|
|
366
452
|
export default function Page() {
|
|
367
|
-
const
|
|
368
|
-
return
|
|
453
|
+
const content = useVal(contentVal);
|
|
454
|
+
return (
|
|
455
|
+
<main>
|
|
456
|
+
<ValRichText
|
|
457
|
+
theme={{
|
|
458
|
+
bold: "font-bold", // <- maps bold to a class. NOTE: tailwind classes are supported
|
|
459
|
+
//
|
|
460
|
+
}}
|
|
461
|
+
>
|
|
462
|
+
{content}
|
|
463
|
+
</ValRichText>
|
|
464
|
+
</main>
|
|
465
|
+
);
|
|
369
466
|
}
|
|
370
467
|
```
|
|
371
468
|
|
|
372
469
|
## Image
|
|
373
470
|
|
|
374
|
-
### Schema
|
|
471
|
+
### Image Schema
|
|
375
472
|
|
|
376
473
|
```ts
|
|
377
474
|
s.image();
|
|
@@ -389,7 +486,7 @@ export const schema = s.image();
|
|
|
389
486
|
export default val.content("/image", schema, val.file("/public/myfile.jpg"));
|
|
390
487
|
```
|
|
391
488
|
|
|
392
|
-
NOTE
|
|
489
|
+
**NOTE**: This will not validate, since images requires `width`, `height` and a `sha256` checksum. You can fix this validation in the UI by opening the image and clicking the Fix button.
|
|
393
490
|
|
|
394
491
|
### Using images in components
|
|
395
492
|
|
|
@@ -404,100 +501,25 @@ const image = useVal(imageVal);
|
|
|
404
501
|
return <img src={image.url} />;
|
|
405
502
|
```
|
|
406
503
|
|
|
407
|
-
##
|
|
408
|
-
|
|
409
|
-
**NOTE**: WORKS ONLY ON THE TYPE LEVEL
|
|
410
|
-
|
|
411
|
-
To enable i18n, you must update your Val with the locales you want to enforce.
|
|
412
|
-
|
|
413
|
-
Example:
|
|
414
|
-
|
|
415
|
-
```ts
|
|
416
|
-
// ./val.config.ts
|
|
417
|
-
|
|
418
|
-
import { initVal } from "@valbuild/core";
|
|
419
|
-
|
|
420
|
-
const { s, val } = initVal({
|
|
421
|
-
locales: {
|
|
422
|
-
required: ["en_US", "fr_FR"],
|
|
423
|
-
fallback: "en_US",
|
|
424
|
-
},
|
|
425
|
-
});
|
|
426
|
-
|
|
427
|
-
export { s, val };
|
|
428
|
-
```
|
|
429
|
-
|
|
430
|
-
## Union
|
|
504
|
+
## KeyOf
|
|
431
505
|
|
|
432
|
-
|
|
506
|
+
If you need to reference content in another `.val` file you can use the `keyOf` schema.
|
|
433
507
|
|
|
434
|
-
|
|
508
|
+
### KeyOf Schema
|
|
435
509
|
|
|
436
510
|
```ts
|
|
437
|
-
|
|
438
|
-
"type",
|
|
439
|
-
s.object({ type: s.literal("type1"), bar: s.string() }),
|
|
440
|
-
s.object({ type: s.literal("type2"), foo: s.number() })
|
|
441
|
-
);
|
|
442
|
-
```
|
|
443
|
-
|
|
444
|
-
### Selecting unions
|
|
445
|
-
|
|
446
|
-
### `.fold`
|
|
447
|
-
|
|
448
|
-
TODO: description
|
|
511
|
+
import otherVal from "./other.val"; // NOTE: this must be an array or a record
|
|
449
512
|
|
|
450
|
-
|
|
451
|
-
useVal(myUnionVal.fold("type")({
|
|
452
|
-
|
|
453
|
-
})
|
|
513
|
+
s.keyOf(otherVal);
|
|
454
514
|
```
|
|
455
515
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
**NOTE**: Currently not possible to change from UI
|
|
459
|
-
|
|
460
|
-
```ts
|
|
461
|
-
|
|
462
|
-
```
|
|
463
|
-
|
|
464
|
-
# Remote
|
|
465
|
-
|
|
466
|
-
**NOTE**: WORKS ONLY ON THE TYPE LEVEL
|
|
516
|
+
### Initializing keyOf
|
|
467
517
|
|
|
468
|
-
|
|
518
|
+
### Using keyOf to reference content
|
|
469
519
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
```ts
|
|
475
|
-
export const schema = s.object({ stuff: s.string() });
|
|
476
|
-
|
|
477
|
-
export default val.content("/remote/example", schema, val.remote("REFERENCE"));
|
|
478
|
-
```
|
|
479
|
-
|
|
480
|
-
## Selector
|
|
481
|
-
|
|
482
|
-
To select parts of a your content you should use Selectors.
|
|
483
|
-
If you make the content `.remote`
|
|
484
|
-
|
|
485
|
-
### `.andThen`
|
|
486
|
-
|
|
487
|
-
All selectors can use `andThen` method which is similar to the `&&` operator. You can use this to only do operations on optionals that are defined. NOTE: only TRUTHY arguments are passed in to `andThen` (i.e. the empty string, `''` is NOT truthy).
|
|
488
|
-
|
|
489
|
-
Given the example schema:
|
|
490
|
-
|
|
491
|
-
```ts
|
|
492
|
-
// ./maybeArray.val.ts
|
|
493
|
-
//...
|
|
494
|
-
|
|
495
|
-
export const schema = t.array(t.string()).optional();
|
|
496
|
-
|
|
497
|
-
//...
|
|
498
|
-
```
|
|
520
|
+
```tsx
|
|
521
|
+
const article = useVal(articleVal); // s.object({ author: s.keyOf(otherVal) })
|
|
522
|
+
const authors = useVal(otherVal); // s.array(s.object({ name: s.string() }))
|
|
499
523
|
|
|
500
|
-
|
|
501
|
-
import maybeArrayVal from "./maybeArray.val";
|
|
502
|
-
useVal(maybeArrayVal.andThen((array) => array.filter((v) => v.eq("foo"))));
|
|
524
|
+
const nameOfAuthor = authors[articleVal.author].name;
|
|
503
525
|
```
|