uniweb 0.48.6 → 0.50.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/package.json +5 -5
- package/partials/agents.md +12 -10
- package/src/commands/publish.js +6 -6
- package/src/framework-index.json +9 -9
- package/templates/site/site.yml.hbs +3 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "uniweb",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.50.0",
|
|
4
4
|
"description": "Create structured Vite + React sites with content/code separation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -41,13 +41,13 @@
|
|
|
41
41
|
"js-yaml": "^4.1.0",
|
|
42
42
|
"prompts": "^2.4.2",
|
|
43
43
|
"tar": "^7.0.0",
|
|
44
|
-
"@uniweb/core": "^0.
|
|
45
|
-
"@uniweb/kit": "^0.18.
|
|
44
|
+
"@uniweb/core": "^0.25.1",
|
|
45
|
+
"@uniweb/kit": "^0.18.3",
|
|
46
46
|
"@uniweb/semantic-parser": "^1.4.0",
|
|
47
|
-
"@uniweb/runtime": "^0.
|
|
47
|
+
"@uniweb/runtime": "^0.20.1"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
50
|
-
"@uniweb/build": "^0.
|
|
50
|
+
"@uniweb/build": "^0.46.0",
|
|
51
51
|
"@uniweb/content-reader": "^1.2.4",
|
|
52
52
|
"@uniweb/semantic-parser": "^1.4.0"
|
|
53
53
|
},
|
package/partials/agents.md
CHANGED
|
@@ -857,7 +857,7 @@ A bare string is a path under `entities/`, naming one file or matching many.
|
|
|
857
857
|
- article/2025-*.md
|
|
858
858
|
```
|
|
859
859
|
|
|
860
|
-
A query
|
|
860
|
+
A query reads one branch with `scope: archive` — the folder and everything inside it (the older `where: { path: { under: … } }` is refused by the build). An entity belongs to one folder; if you want a computed subset, that is a query, not a second placement.
|
|
861
861
|
|
|
862
862
|
**`queries.yml` — how content is reached.** A bare map of name → query. A query names a schema and the published records of that schema are its rows:
|
|
863
863
|
|
|
@@ -874,12 +874,12 @@ team:
|
|
|
874
874
|
|
|
875
875
|
You can keep the same declarations under `queries:` in `site.yml` instead, if you would rather have one file.
|
|
876
876
|
|
|
877
|
-
**Show a query on a page** with `
|
|
877
|
+
**Show a query on a page** with `query:` in `page.yml` or a section's frontmatter (the whole result), or `fetch:` for anything more — a `limit`, a `where`. A list — `query: [team, articles]` — declares several, each arriving under its own `content.data` key. `query:` takes names only; `data:`, its old name, is now an error.
|
|
878
878
|
|
|
879
879
|
```yaml
|
|
880
880
|
# pages/blog/page.yml | # a section on the homepage
|
|
881
881
|
title: Blog | ---
|
|
882
|
-
|
|
882
|
+
query: recent | type: ArticleTeaser
|
|
883
883
|
| fetch: { query: recent, limit: 3 }
|
|
884
884
|
| ---
|
|
885
885
|
```
|
|
@@ -888,7 +888,7 @@ data: recent | type: ArticleTeaser
|
|
|
888
888
|
|
|
889
889
|
```
|
|
890
890
|
pages/blog/
|
|
891
|
-
├── page.yml # title: Blog /
|
|
891
|
+
├── page.yml # title: Blog / query: recent
|
|
892
892
|
├── list.md
|
|
893
893
|
└── [slug]/
|
|
894
894
|
├── page.yml
|
|
@@ -897,9 +897,11 @@ pages/blog/
|
|
|
897
897
|
|
|
898
898
|
`entities/article/design-tips.md` becomes `/blog/design-tips`. The section inside `[slug]/` needs no special markdown — the matched record is delivered to it. Generated pages are excluded from navigation menus.
|
|
899
899
|
|
|
900
|
+
**Which query the URL narrows — the page's route query:** the `[slug]` page's own `query:`, else its parent page's (the usual shape, above), else `site.yml`'s; if none declares one, the query its sections all declare. The first query of that level wins. Every section the route query reaches gets the one record; a section declaring a *different* query of its own gets that query as declared. The folder name says what the URL segment matches: `[slug]` the record's handle (`$name`, which compiled records carry — equal to their `slug`), `[uuid]` its `$uuid`, any other `[name]` the record's own field of that name. A folder inside `[slug]/` (`[slug]/cv/` → `/blog/:slug/cv`) is a parametric page too, reading the record when `[slug]/page.yml` declares the query. `[dir]` and `[path]` are refused as folder names, and so is any folder inside `[...path]/`.
|
|
901
|
+
|
|
900
902
|
> **The record arrives as a single-element array under the query key** — `content.data.recent[0]`, not `content.data.article`. The runtime never coerces it to an object and never synthesizes a singular key. See *Data* in Part 4.
|
|
901
903
|
|
|
902
|
-
**Records with URLs of their own shape — `[...path]/`.** A folder named exactly `[...path]` (one fixed spelling) captures the rest of the URL: `/blog/my-post` and `/blog/rust/2025/my-post` both reach it. The capture yields three standard variables — `:path` (the whole capture), `:dir` (everything before the last segment), `:slug` (the last segment, the record's handle) — and the record is still delivered by
|
|
904
|
+
**Records with URLs of their own shape — `[...path]/`.** A folder named exactly `[...path]` (one fixed spelling) captures the rest of the URL: `/blog/my-post` and `/blog/rust/2025/my-post` both reach it. The capture yields three standard variables — `:path` (the whole capture), `:dir` (everything before the last segment), `:slug` (the last segment, the record's handle) — and the record is still delivered by its handle, so the section reads `content.data.recent[0]` as before. The same three variables exist under every parametric page: under `[slug]`, `:slug` and `:path` are the segment and `:dir` is empty. A record's URL is its folder placement plus its slug (`- folder: rust/2025` in `records.yml` → `/blog/rust/2025/my-post`). A query may bind a part — `scope: :dir` exposes the folder branch, `where: { tag: :dir }` keeps it private — and an unbound or empty variable drops its clause, so one saved query serves the list page and the parametric page, on a static site and a hosted one alike. Without `scope: :dir` the directory is decoration: the record is found by its handle wherever it sits. Reference: `reference/dynamic-routes.md`.
|
|
903
905
|
|
|
904
906
|
**Two options for bigger sets:**
|
|
905
907
|
|
|
@@ -975,7 +977,7 @@ function MyComponent({ content, params, block }) {
|
|
|
975
977
|
}
|
|
976
978
|
```
|
|
977
979
|
|
|
978
|
-
Frontmatter becomes `params`, minus the keys the framework consumes outright: `type`, `preset`, `input`, `props`, `
|
|
980
|
+
Frontmatter becomes `params`, minus the keys the framework consumes outright: `type`, `preset`, `input`, `props`, `query`, `fetch`, `id` (a `data:` key is refused — it was `query:`'s old name). `props:` is the one that isn't dropped but merged *into* params.
|
|
979
981
|
|
|
980
982
|
**Framework fields you'd expect to be stripped are not.** `background`, `theme`, `source`, `where`, and `vars` are acted on by the runtime *and* passed through — so `params.theme` is readable when a component needs logic beyond CSS tokens (a light vs. dark logo, say). Components ignore the keys they don't use, the same way they ignore unused `content.data` keys.
|
|
981
983
|
|
|
@@ -1606,7 +1608,7 @@ export default function Grid({ block, params }) {
|
|
|
1606
1608
|
|
|
1607
1609
|
Each child is a regular section with its own type, params, and content — and you're in the middle: wrap each child, filter by type, reorder, add container classes. The author decides *what* goes in the grid; your component decides *how* it renders. Tomorrow the author can swap a child for a different section type with no code change, and your components stay reusable wherever child sections are accepted.
|
|
1608
1610
|
|
|
1609
|
-
**Data and child blocks:** page-level `
|
|
1611
|
+
**Data and child blocks:** page-level `query:` (or `fetch:`) is available to all blocks including children, and each child resolves data independently through the page → site hierarchy. If a child needs data no ancestor declares, give it its own in its frontmatter (`query: articles`, or `fetch:`). Its `meta.js` `data:` declares the shape it reads, never where the data comes from — it fetches nothing.
|
|
1610
1612
|
|
|
1611
1613
|
**SSG:** insets, `<ChildBlocks>`, and `<Visual>` all render correctly during prerender. Inset components using React hooks internally trigger prerender warnings — expected and harmless; the page renders correctly client-side.
|
|
1612
1614
|
|
|
@@ -1771,9 +1773,9 @@ Content-less containers appear as group nodes (`hasContent: false`) — use `nav
|
|
|
1771
1773
|
|
|
1772
1774
|
### Data
|
|
1773
1775
|
|
|
1774
|
-
A component on a page with a `
|
|
1776
|
+
A component on a page with a `query:` or `fetch:` declaration automatically receives that data in `content.data.{key}` — no opt-in in `meta.js`.
|
|
1775
1777
|
|
|
1776
|
-
**Bound collections always arrive as arrays.** On a list page, `content.data.articles` is the full collection. On a
|
|
1778
|
+
**Bound collections always arrive as arrays.** On a list page, `content.data.articles` is the full collection. On a parametric page (`[slug]/`), the matched record is delivered under the *same* key as a single-element array — the detail section reads `content.data.articles[0]`. When nothing matches, the key is `[]`. The runtime never coerces to a single object and never synthesizes a singular key.
|
|
1777
1779
|
|
|
1778
1780
|
```jsx
|
|
1779
1781
|
function Article({ content, block }) {
|
|
@@ -1829,7 +1831,7 @@ A backend with its own base URL, headers, wire or query language is a **transpor
|
|
|
1829
1831
|
# site.yml
|
|
1830
1832
|
fetcher:
|
|
1831
1833
|
transports:
|
|
1832
|
-
articles: acme # a foundation-registered transport handles `
|
|
1834
|
+
articles: acme # a foundation-registered transport handles `query: articles`
|
|
1833
1835
|
events: default # explicitly route back to the default fetcher
|
|
1834
1836
|
acme: # binding config that transport reads
|
|
1835
1837
|
apiKey: pk_public_123
|
package/src/commands/publish.js
CHANGED
|
@@ -772,12 +772,12 @@ export async function publish(args = []) {
|
|
|
772
772
|
// released version on the wire is required when site.yml uses an unversioned
|
|
773
773
|
// local ref; injectInfo overrides info.foundation. A registry/URL ref → fnd.ref
|
|
774
774
|
// is null → the site.yml ref is forwarded verbatim (already pinned).
|
|
775
|
-
// ⛔ DO NOT STAMP `info
|
|
776
|
-
//
|
|
777
|
-
//
|
|
778
|
-
//
|
|
779
|
-
//
|
|
780
|
-
//
|
|
775
|
+
// ⛔ DO NOT STAMP A FILE MAP INTO `info` HERE. `injectInfo` WINS the merge
|
|
776
|
+
// (`sync-package.js`: `{...siteDoc.info, ...injectInfo}`), so any name stamped
|
|
777
|
+
// here silently replaces what the author's document carries under it, and the
|
|
778
|
+
// store validator accepts it — nothing errors at any layer. This was written
|
|
779
|
+
// about `info.data`, which carried the site's `fetch:` block until 2026-09-09;
|
|
780
|
+
// that moved to `settings.fetch`, and the warning holds for any name.
|
|
781
781
|
//
|
|
782
782
|
// A file map needs a name nothing else claims (`static_data` / `data_files`
|
|
783
783
|
// were proposed) AND a consumer that reads it — neither settled. See
|
package/src/framework-index.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"generatedAt": "2026-09-
|
|
3
|
+
"generatedAt": "2026-09-11T23:04:50.071Z",
|
|
4
4
|
"packages": {
|
|
5
5
|
"@uniweb/api": {
|
|
6
|
-
"version": "0.3.
|
|
6
|
+
"version": "0.3.2",
|
|
7
7
|
"path": "framework/api",
|
|
8
8
|
"deps": [
|
|
9
9
|
"@uniweb/core"
|
|
10
10
|
]
|
|
11
11
|
},
|
|
12
12
|
"@uniweb/build": {
|
|
13
|
-
"version": "0.
|
|
13
|
+
"version": "0.46.0",
|
|
14
14
|
"path": "framework/build",
|
|
15
15
|
"deps": [
|
|
16
16
|
"@uniweb/content-reader",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"deps": []
|
|
35
35
|
},
|
|
36
36
|
"@uniweb/core": {
|
|
37
|
-
"version": "0.
|
|
37
|
+
"version": "0.25.1",
|
|
38
38
|
"path": "framework/core",
|
|
39
39
|
"deps": [
|
|
40
40
|
"@uniweb/semantic-parser",
|
|
@@ -47,14 +47,14 @@
|
|
|
47
47
|
"deps": []
|
|
48
48
|
},
|
|
49
49
|
"@uniweb/icons": {
|
|
50
|
-
"version": "0.4.
|
|
50
|
+
"version": "0.4.16",
|
|
51
51
|
"path": "framework/icons",
|
|
52
52
|
"deps": [
|
|
53
53
|
"@uniweb/core"
|
|
54
54
|
]
|
|
55
55
|
},
|
|
56
56
|
"@uniweb/kit": {
|
|
57
|
-
"version": "0.18.
|
|
57
|
+
"version": "0.18.3",
|
|
58
58
|
"path": "framework/kit",
|
|
59
59
|
"deps": [
|
|
60
60
|
"@uniweb/core",
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
"deps": []
|
|
75
75
|
},
|
|
76
76
|
"@uniweb/projections": {
|
|
77
|
-
"version": "0.6.
|
|
77
|
+
"version": "0.6.1",
|
|
78
78
|
"path": "framework/projections",
|
|
79
79
|
"deps": [
|
|
80
80
|
"@uniweb/content-writer",
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
]
|
|
83
83
|
},
|
|
84
84
|
"@uniweb/runtime": {
|
|
85
|
-
"version": "0.
|
|
85
|
+
"version": "0.20.1",
|
|
86
86
|
"path": "framework/runtime",
|
|
87
87
|
"deps": [
|
|
88
88
|
"@uniweb/core",
|
|
@@ -120,7 +120,7 @@
|
|
|
120
120
|
"deps": []
|
|
121
121
|
},
|
|
122
122
|
"@uniweb/unipress": {
|
|
123
|
-
"version": "0.9.
|
|
123
|
+
"version": "0.9.13",
|
|
124
124
|
"path": "framework/unipress",
|
|
125
125
|
"deps": [
|
|
126
126
|
"@uniweb/build",
|
|
@@ -49,8 +49,8 @@ index: home
|
|
|
49
49
|
# sort: date desc
|
|
50
50
|
# limit: 10
|
|
51
51
|
#
|
|
52
|
-
# Pages and sections then name a query: `
|
|
53
|
-
# `fetch: { query: recent }
|
|
52
|
+
# Pages and sections then name a query: `query: recent`, or
|
|
53
|
+
# `fetch: { query: recent, limit: 3 }` for anything more.
|
|
54
54
|
#
|
|
55
55
|
# You can also keep queries here instead of in queries.yml:
|
|
56
56
|
#
|
|
@@ -61,7 +61,7 @@ index: home
|
|
|
61
61
|
#
|
|
62
62
|
# fetch:
|
|
63
63
|
# - url: https://api.example.com/team # Remote JSON
|
|
64
|
-
#
|
|
64
|
+
# as: team # Access as content.data.team
|
|
65
65
|
# - path: /data/config.json # Local file from public/
|
|
66
66
|
|
|
67
67
|
# ─── Extensions ────────────────────────────────────────────────────────────────
|