@uniweb/build 0.39.0 → 0.40.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 +6 -7
- package/src/content/index.js +0 -1
- package/src/generate-entry.js +5 -11
- package/src/i18n/index.js +1 -1
- package/src/index.js +0 -1
- package/src/runtime-schema.js +1 -9
- package/src/schema.js +2 -11
- package/src/site/content-collector.js +3 -5
- package/src/site/data-fetcher.js +16 -110
- package/src/site/index.js +0 -1
- package/src/site/plugin.js +1 -1
- package/src/site/queries-config.js +10 -17
- package/src/site/query-processor.js +1 -6
- package/src/site/records-config.js +1 -1
- package/src/uwx/folder.js +1 -1
- package/src/uwx/records.js +0 -3
- package/src/uwx/site.js +1 -1
- package/src/search/extract.js +0 -2
- package/src/search/generate.js +0 -8
- package/src/search/index.js +0 -20
- package/src/search/records-index.js +0 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uniweb/build",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.40.0",
|
|
4
4
|
"description": "Build tooling for the Uniweb Component Web Platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -19,7 +19,6 @@
|
|
|
19
19
|
"./prerender": "./src/prerender.js",
|
|
20
20
|
"./hosts": "./src/hosts/index.js",
|
|
21
21
|
"./i18n": "./src/i18n/index.js",
|
|
22
|
-
"./search": "./src/search/index.js",
|
|
23
22
|
"./uwx": "./src/uwx/index.js",
|
|
24
23
|
"./import-map-plugin": "./src/import-map-plugin.js"
|
|
25
24
|
},
|
|
@@ -57,15 +56,15 @@
|
|
|
57
56
|
"js-yaml": "^4.1.0",
|
|
58
57
|
"sharp": "^0.35.3",
|
|
59
58
|
"yaml": "^2.5.0",
|
|
60
|
-
"@uniweb/
|
|
59
|
+
"@uniweb/schemas": "^0.2.13",
|
|
60
|
+
"@uniweb/projections": "^0.5.10",
|
|
61
61
|
"@uniweb/semantic-parser": "^1.4.0",
|
|
62
62
|
"@uniweb/theming": "^0.1.15",
|
|
63
63
|
"@uniweb/content-reader": "^1.2.4",
|
|
64
|
-
"@uniweb/
|
|
65
|
-
"@uniweb/schemas": "^0.2.13"
|
|
64
|
+
"@uniweb/content-writer": "^0.3.4"
|
|
66
65
|
},
|
|
67
66
|
"optionalDependencies": {
|
|
68
|
-
"@uniweb/runtime": "^0.
|
|
67
|
+
"@uniweb/runtime": "^0.17.0"
|
|
69
68
|
},
|
|
70
69
|
"peerDependencies": {
|
|
71
70
|
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0",
|
|
@@ -74,7 +73,7 @@
|
|
|
74
73
|
"@tailwindcss/vite": "^4.0.0",
|
|
75
74
|
"@vitejs/plugin-react": "^4.0.0 || ^5.0.0",
|
|
76
75
|
"vite-plugin-svgr": "^4.0.0",
|
|
77
|
-
"@uniweb/core": "^0.
|
|
76
|
+
"@uniweb/core": "^0.22.0"
|
|
78
77
|
},
|
|
79
78
|
"peerDependenciesMeta": {
|
|
80
79
|
"vite": {
|
package/src/content/index.js
CHANGED
package/src/generate-entry.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Exports:
|
|
7
7
|
* - `components` - Object map of component name -> React component
|
|
8
|
-
* - `capabilities` - Custom Layout and props from src/
|
|
8
|
+
* - `capabilities` - Custom Layout and props from src/main.js (if present)
|
|
9
9
|
* - `meta` - Per-component runtime metadata extracted from meta.js files
|
|
10
10
|
*
|
|
11
11
|
* The `meta` export contains only properties needed at runtime:
|
|
@@ -81,11 +81,9 @@ async function detectHostShareableImports(srcDir) {
|
|
|
81
81
|
/**
|
|
82
82
|
* Detect foundation config file (for props, vars, etc.)
|
|
83
83
|
*
|
|
84
|
-
* Looks for (in priority order): main.js, main.jsx
|
|
84
|
+
* Looks for (in priority order): main.js, main.jsx
|
|
85
85
|
*
|
|
86
|
-
* The
|
|
87
|
-
* module). `foundation.js` is the legacy name and remains supported so
|
|
88
|
-
* existing foundations build without a rename.
|
|
86
|
+
* The foundation's main authored module.
|
|
89
87
|
*
|
|
90
88
|
* The file should export:
|
|
91
89
|
* - props (optional) - Foundation-wide props
|
|
@@ -98,8 +96,6 @@ function detectFoundationExports(srcDir) {
|
|
|
98
96
|
const candidates = [
|
|
99
97
|
{ path: 'main.js', ext: 'js' },
|
|
100
98
|
{ path: 'main.jsx', ext: 'jsx' },
|
|
101
|
-
{ path: 'foundation.js', ext: 'js' },
|
|
102
|
-
{ path: 'foundation.jsx', ext: 'jsx' },
|
|
103
99
|
]
|
|
104
100
|
|
|
105
101
|
for (const { path, ext } of candidates) {
|
|
@@ -156,7 +152,7 @@ function generateEntrySource(components, options = {}) {
|
|
|
156
152
|
}
|
|
157
153
|
|
|
158
154
|
// Foundation capabilities import (for props, vars, etc.)
|
|
159
|
-
// Note: Layout/layouts no longer merged from
|
|
155
|
+
// Note: Layout/layouts no longer merged from main.js — layouts come from src/layouts/ discovery
|
|
160
156
|
if (foundationExports) {
|
|
161
157
|
lines.push(`import * as _foundationModule from '${foundationExports.path}'`)
|
|
162
158
|
}
|
|
@@ -409,8 +405,6 @@ export function getStructuralWatchPaths(srcDir, options = {}) {
|
|
|
409
405
|
'meta.js',
|
|
410
406
|
'main.js',
|
|
411
407
|
'main.jsx',
|
|
412
|
-
'foundation.js',
|
|
413
|
-
'foundation.jsx',
|
|
414
408
|
'styles.css',
|
|
415
409
|
'index.css'
|
|
416
410
|
]
|
|
@@ -459,7 +453,7 @@ export function shouldRegenerateForFile(file, srcDir) {
|
|
|
459
453
|
return 'meta.js changed'
|
|
460
454
|
}
|
|
461
455
|
|
|
462
|
-
// main.js / main.jsx
|
|
456
|
+
// main.js / main.jsx at root —
|
|
463
457
|
// the authored declarations file affects the capabilities import.
|
|
464
458
|
if (/^(main|foundation)\.(js|jsx)$/.test(rel)) {
|
|
465
459
|
return 'foundation config changed'
|
package/src/i18n/index.js
CHANGED
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
translateRecordData,
|
|
24
24
|
RECORDS_DIR
|
|
25
25
|
} from './records.js'
|
|
26
|
-
import { generateSearchIndex, isSearchEnabled } from '
|
|
26
|
+
import { generateSearchIndex, isSearchEnabled } from '@uniweb/projections'
|
|
27
27
|
|
|
28
28
|
// Free-form translation support
|
|
29
29
|
import {
|
package/src/index.js
CHANGED
package/src/runtime-schema.js
CHANGED
|
@@ -154,7 +154,6 @@ function isFullSchemaFormat(schema) {
|
|
|
154
154
|
* the same schema feeds both audiences.
|
|
155
155
|
*
|
|
156
156
|
* Normalizations:
|
|
157
|
-
* - `type: 'string'` → `type: 'text'` (legacy alias; warn in dev)
|
|
158
157
|
*
|
|
159
158
|
* @param {Object} schema - Rich schema as authored
|
|
160
159
|
* @returns {Object} - Normalized rich schema
|
|
@@ -170,14 +169,7 @@ function normalizeRichSchemaValue(value) {
|
|
|
170
169
|
if (!value || typeof value !== 'object') return value
|
|
171
170
|
const out = {}
|
|
172
171
|
for (const [key, v] of Object.entries(value)) {
|
|
173
|
-
if (
|
|
174
|
-
if (typeof process !== 'undefined' && process.env?.NODE_ENV !== 'production') {
|
|
175
|
-
console.warn(
|
|
176
|
-
"[uniweb] form schema field type 'string' is a legacy alias; use 'text' instead."
|
|
177
|
-
)
|
|
178
|
-
}
|
|
179
|
-
out[key] = 'text'
|
|
180
|
-
} else if (v && typeof v === 'object') {
|
|
172
|
+
if (v && typeof v === 'object') {
|
|
181
173
|
out[key] = normalizeRichSchemaValue(v)
|
|
182
174
|
} else {
|
|
183
175
|
out[key] = v
|
package/src/schema.js
CHANGED
|
@@ -21,10 +21,8 @@ import { collectSchemaRefs, buildDataSchemaMap } from './resolve-data-schema.js'
|
|
|
21
21
|
// Component meta file name
|
|
22
22
|
const META_FILE_NAME = 'meta.js'
|
|
23
23
|
|
|
24
|
-
// Foundation authored-declarations file name.
|
|
25
|
-
|
|
26
|
-
// Whichever exists at the source root is loaded.
|
|
27
|
-
const FOUNDATION_FILE_NAMES = ['main.js', 'foundation.js']
|
|
24
|
+
// Foundation authored-declarations file name.
|
|
25
|
+
const FOUNDATION_FILE_NAMES = ['main.js']
|
|
28
26
|
|
|
29
27
|
// Default paths to scan for section types (relative to srcDir).
|
|
30
28
|
// Exported so dev-server watchers can derive their watch surface from the same
|
|
@@ -394,13 +392,6 @@ function inferFontVarTypes(vars) {
|
|
|
394
392
|
return out
|
|
395
393
|
}
|
|
396
394
|
|
|
397
|
-
/**
|
|
398
|
-
* @deprecated Use loadFoundationConfig instead
|
|
399
|
-
*/
|
|
400
|
-
export async function loadFoundationMeta(srcDir) {
|
|
401
|
-
return loadFoundationConfig(srcDir)
|
|
402
|
-
}
|
|
403
|
-
|
|
404
395
|
/**
|
|
405
396
|
* Check if a filename looks like a PascalCase component (starts with uppercase)
|
|
406
397
|
*/
|
|
@@ -14,9 +14,7 @@
|
|
|
14
14
|
* - input: Input field mapping
|
|
15
15
|
* - props: Additional component props (merged with other params)
|
|
16
16
|
* - fetch: Data fetching configuration (path, url, schema, prerender, merge, transform)
|
|
17
|
-
|
|
18
|
-
* Note: `component` is supported as an alias for `type` (deprecated)
|
|
19
|
-
*
|
|
17
|
+
*
|
|
20
18
|
* Uses @uniweb/content-reader for markdown → ProseMirror conversion
|
|
21
19
|
* when available, otherwise uses a simplified parser.
|
|
22
20
|
*
|
|
@@ -923,7 +921,7 @@ async function processMarkdownFile(filePath, id, siteRoot, defaultStableId = nul
|
|
|
923
921
|
console.warn(`[content-collector] ${err.message}`)
|
|
924
922
|
}
|
|
925
923
|
|
|
926
|
-
const { type,
|
|
924
|
+
const { type, preset, input, props, fetch, data, id: frontmatterId, ...params } = frontMatter
|
|
927
925
|
|
|
928
926
|
// Convert markdown to ProseMirror
|
|
929
927
|
const proseMirrorContent = markdownToProseMirror(markdown)
|
|
@@ -961,7 +959,7 @@ async function processMarkdownFile(filePath, id, siteRoot, defaultStableId = nul
|
|
|
961
959
|
const section = {
|
|
962
960
|
id,
|
|
963
961
|
stableId,
|
|
964
|
-
type: type ||
|
|
962
|
+
type: type || null,
|
|
965
963
|
preset,
|
|
966
964
|
input,
|
|
967
965
|
params: { ...params, ...props },
|
package/src/site/data-fetcher.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* - Local JSON/YAML files
|
|
12
12
|
* - Remote URLs
|
|
13
13
|
* - Transform paths to extract nested data
|
|
14
|
-
* - Post-processing: limit, sort
|
|
14
|
+
* - Post-processing: limit, sort
|
|
15
15
|
*
|
|
16
16
|
* @module @uniweb/build/site/data-fetcher
|
|
17
17
|
*/
|
|
@@ -65,68 +65,6 @@ function getNestedValue(obj, path) {
|
|
|
65
65
|
return current
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
/**
|
|
69
|
-
* Parse a filter value from string
|
|
70
|
-
*
|
|
71
|
-
* @param {string} raw - Raw value string
|
|
72
|
-
* @returns {any} Parsed value
|
|
73
|
-
*/
|
|
74
|
-
function parseFilterValue(raw) {
|
|
75
|
-
if (raw === 'true') return true
|
|
76
|
-
if (raw === 'false') return false
|
|
77
|
-
if (raw === 'null') return null
|
|
78
|
-
if (/^\d+$/.test(raw)) return parseInt(raw, 10)
|
|
79
|
-
if (/^\d+\.\d+$/.test(raw)) return parseFloat(raw)
|
|
80
|
-
|
|
81
|
-
// Remove quotes if present
|
|
82
|
-
if ((raw.startsWith('"') && raw.endsWith('"')) ||
|
|
83
|
-
(raw.startsWith("'") && raw.endsWith("'"))) {
|
|
84
|
-
return raw.slice(1, -1)
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return raw
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Apply filter expression to array of items
|
|
92
|
-
*
|
|
93
|
-
* Supported operators: ==, !=, >, <, >=, <=, contains
|
|
94
|
-
*
|
|
95
|
-
* @param {Array} items - Items to filter
|
|
96
|
-
* @param {string} filterExpr - Filter expression (e.g., "published != false")
|
|
97
|
-
* @returns {Array} Filtered items
|
|
98
|
-
*
|
|
99
|
-
* @example
|
|
100
|
-
* applyFilter(items, 'published != false')
|
|
101
|
-
* applyFilter(items, 'tags contains featured')
|
|
102
|
-
*/
|
|
103
|
-
export function applyFilter(items, filterExpr) {
|
|
104
|
-
if (!filterExpr || !Array.isArray(items)) return items
|
|
105
|
-
|
|
106
|
-
const match = filterExpr.match(/^(\S+)\s*(==|!=|>=?|<=?|contains)\s*(.+)$/)
|
|
107
|
-
if (!match) return items
|
|
108
|
-
|
|
109
|
-
const [, field, op, rawValue] = match
|
|
110
|
-
const value = parseFilterValue(rawValue.trim())
|
|
111
|
-
|
|
112
|
-
return items.filter(item => {
|
|
113
|
-
const itemValue = getNestedValue(item, field)
|
|
114
|
-
switch (op) {
|
|
115
|
-
case '==': return itemValue === value
|
|
116
|
-
case '!=': return itemValue !== value
|
|
117
|
-
case '>': return itemValue > value
|
|
118
|
-
case '<': return itemValue < value
|
|
119
|
-
case '>=': return itemValue >= value
|
|
120
|
-
case '<=': return itemValue <= value
|
|
121
|
-
case 'contains':
|
|
122
|
-
return Array.isArray(itemValue)
|
|
123
|
-
? itemValue.includes(value)
|
|
124
|
-
: String(itemValue).includes(value)
|
|
125
|
-
default: return true
|
|
126
|
-
}
|
|
127
|
-
})
|
|
128
|
-
}
|
|
129
|
-
|
|
130
68
|
/**
|
|
131
69
|
* Apply a `sort:` to an array of items — `@uniweb/core`'s ONE evaluator, the
|
|
132
70
|
* same the runtime's fallback runs, so a query orders identically on the file
|
|
@@ -134,7 +72,7 @@ export function applyFilter(items, filterExpr) {
|
|
|
134
72
|
*
|
|
135
73
|
* ⛔ SINGLE-KEY, BY RULING [Diego, 2026-09-04]. This was its own implementation
|
|
136
74
|
* until then, and it honoured `order asc, title asc` — a multi-key sort the
|
|
137
|
-
* records
|
|
75
|
+
* records service refuses and the ruling dropped. A comma now THROWS here, at build
|
|
138
76
|
* time, which is where an authoring error on the file lane belongs.
|
|
139
77
|
*
|
|
140
78
|
* @param {Array} items - Items to sort
|
|
@@ -152,7 +90,7 @@ export function applySort(items, sortExpr) {
|
|
|
152
90
|
* The where-object is the query language (see @uniweb/core's
|
|
153
91
|
* matchWhere). Structured JSON predicate; the one evaluator walks the
|
|
154
92
|
* object against each record, here at build time and in the runtime
|
|
155
|
-
* alike. The same shape crosses to a host's
|
|
93
|
+
* alike. The same shape crosses to a host's records service unchanged.
|
|
156
94
|
*
|
|
157
95
|
* @param {Array} items - Items to filter
|
|
158
96
|
* @param {object} where - Where-object predicate
|
|
@@ -164,25 +102,20 @@ export function applyWhere(items, where) {
|
|
|
164
102
|
}
|
|
165
103
|
|
|
166
104
|
/**
|
|
167
|
-
* Apply post-processing to fetched data (where,
|
|
105
|
+
* Apply post-processing to fetched data (where, sort, limit)
|
|
168
106
|
*
|
|
169
107
|
* Order of operations:
|
|
170
|
-
* 1. where (where-object predicate
|
|
171
|
-
* 2.
|
|
172
|
-
* 3.
|
|
173
|
-
* 4. limit
|
|
174
|
-
*
|
|
175
|
-
* `where:` and `filter:` may both appear during the deprecation window
|
|
176
|
-
* but in practice authors should pick one. Using `filter:` emits a dev
|
|
177
|
-
* warning at parse time (see parseFetchConfig).
|
|
108
|
+
* 1. where (where-object predicate) — narrows the record set
|
|
109
|
+
* 2. sort
|
|
110
|
+
* 3. limit
|
|
178
111
|
*
|
|
179
112
|
* @param {any} data - Fetched data
|
|
180
|
-
* @param {object} config - Fetch config with optional where,
|
|
113
|
+
* @param {object} config - Fetch config with optional where, sort, limit
|
|
181
114
|
* @returns {any} Processed data
|
|
182
115
|
*/
|
|
183
116
|
export function applyPostProcessing(data, config) {
|
|
184
117
|
if (!data || !Array.isArray(data)) return data
|
|
185
|
-
if (!config.where && !config.
|
|
118
|
+
if (!config.where && !config.sort && !config.limit) return data
|
|
186
119
|
|
|
187
120
|
let result = data
|
|
188
121
|
|
|
@@ -191,11 +124,6 @@ export function applyPostProcessing(data, config) {
|
|
|
191
124
|
result = applyWhere(result, config.where)
|
|
192
125
|
}
|
|
193
126
|
|
|
194
|
-
// Apply legacy filter expression (deprecated)
|
|
195
|
-
if (config.filter) {
|
|
196
|
-
result = applyFilter(result, config.filter)
|
|
197
|
-
}
|
|
198
|
-
|
|
199
127
|
// Apply sort
|
|
200
128
|
if (config.sort) {
|
|
201
129
|
result = applySort(result, config.sort)
|
|
@@ -240,7 +168,7 @@ export function applyPostProcessing(data, config) {
|
|
|
240
168
|
// not understand, but we can refuse to pretend it was never there. Reported
|
|
241
169
|
// once per key name per process so a 200-record build does not print 200 lines.
|
|
242
170
|
const RECOGNIZED_FETCH_KEYS = {
|
|
243
|
-
refine: new Set(['refine', 'detail', 'limit', 'sort', 'where'
|
|
171
|
+
refine: new Set(['refine', 'detail', 'limit', 'sort', 'where']),
|
|
244
172
|
// ⛔ `schema` IS NOT ON EITHER LIST, and its absence is the point. It was the
|
|
245
173
|
// binding key until 2026-09-02 and stopped being READ on 2026-09-03 (`e4fe077`,
|
|
246
174
|
// one name no alias) — but it was left on these lists, which exempted it from
|
|
@@ -250,11 +178,11 @@ const RECOGNIZED_FETCH_KEYS = {
|
|
|
250
178
|
// "unrecognized" understates a key that used to work.
|
|
251
179
|
query: new Set([
|
|
252
180
|
'query', 'as', 'prerender', 'merge', 'transform',
|
|
253
|
-
'where', 'limit', 'sort', 'detailPage',
|
|
181
|
+
'where', 'limit', 'sort', 'detailPage',
|
|
254
182
|
]),
|
|
255
183
|
source: new Set([
|
|
256
184
|
'path', 'url', 'as', 'prerender', 'merge', 'transform', 'detail',
|
|
257
|
-
'detailPage', 'where', 'limit', 'sort',
|
|
185
|
+
'detailPage', 'where', 'limit', 'sort',
|
|
258
186
|
]),
|
|
259
187
|
}
|
|
260
188
|
|
|
@@ -377,21 +305,19 @@ export function parseFetchConfig(fetch) {
|
|
|
377
305
|
// No URL — merges with the parent fetch config at runtime; only carries
|
|
378
306
|
// override props.
|
|
379
307
|
//
|
|
380
|
-
// Note on build-vs-runtime scope: this parser passes `sort`
|
|
308
|
+
// Note on build-vs-runtime scope: this parser passes `sort`
|
|
381
309
|
// through on refine configs, but the runtime EntityStore only applies
|
|
382
|
-
// `detail`, `limit`, and `order` overrides. `sort`
|
|
310
|
+
// `detail`, `limit`, and `order` overrides. `sort` on a refine
|
|
383
311
|
// block are currently accepted by the parser but not honored at runtime.
|
|
384
312
|
// Preserved as-is in this rename commit; revisit separately if needed.
|
|
385
313
|
if (fetch.refine === true) {
|
|
386
314
|
warnUnknownFetchKeys(fetch, 'refine')
|
|
387
|
-
if (fetch.filter !== undefined) warnFilterDeprecated()
|
|
388
315
|
return {
|
|
389
316
|
refine: true,
|
|
390
317
|
...(fetch.detail !== undefined ? { detail: fetch.detail } : {}),
|
|
391
318
|
...(fetch.limit !== undefined ? { limit: fetch.limit } : {}),
|
|
392
319
|
...(fetch.sort !== undefined ? { sort: fetch.sort } : {}),
|
|
393
320
|
...(fetch.where !== undefined ? { where: fetch.where } : {}),
|
|
394
|
-
...(fetch.filter !== undefined ? { filter: fetch.filter } : {}),
|
|
395
321
|
}
|
|
396
322
|
}
|
|
397
323
|
|
|
@@ -411,14 +337,13 @@ export function parseFetchConfig(fetch) {
|
|
|
411
337
|
// Named-query reference: { query: 'articles', limit: 3 }
|
|
412
338
|
if (fetch.query) {
|
|
413
339
|
warnUnknownFetchKeys(fetch, 'query')
|
|
414
|
-
if (fetch.filter !== undefined) warnFilterDeprecated()
|
|
415
340
|
warnSchemaRetired(fetch, fetch.as || fetch.query)
|
|
416
341
|
return {
|
|
417
342
|
// ⭐ **`query` IS EMITTED, and that is what makes the two producers agree.**
|
|
418
343
|
// The sync lane has always emitted it (`uwx/site.js`) and this one did not,
|
|
419
344
|
// for the same declaration — so `resolveQuerySource` fired on a published
|
|
420
345
|
// site and never on a `--link`-deployed one. Measured 2026-09-02 against a
|
|
421
|
-
// host
|
|
346
|
+
// host offering the `records` service:
|
|
422
347
|
//
|
|
423
348
|
// --link endpoint undefined, path /data/articles.json ← the STATIC file
|
|
424
349
|
// publish endpoint /_api/q/articles ← the live lane
|
|
@@ -450,8 +375,6 @@ export function parseFetchConfig(fetch) {
|
|
|
450
375
|
// Canonical detail page for a list card's href (page:<stable_id> ref;
|
|
451
376
|
// resolved to a route template + interpolated per record at runtime).
|
|
452
377
|
detailPage: fetch.detailPage,
|
|
453
|
-
// Legacy post-processing (deprecated, see warning above)
|
|
454
|
-
filter: fetch.filter,
|
|
455
378
|
}
|
|
456
379
|
}
|
|
457
380
|
|
|
@@ -469,14 +392,11 @@ export function parseFetchConfig(fetch) {
|
|
|
469
392
|
where,
|
|
470
393
|
limit,
|
|
471
394
|
sort,
|
|
472
|
-
// Legacy post-processing (deprecated)
|
|
473
|
-
filter,
|
|
474
395
|
} = fetch
|
|
475
396
|
|
|
476
397
|
// Must have either path or url
|
|
477
398
|
if (!path && !url) return null
|
|
478
399
|
|
|
479
|
-
if (filter !== undefined) warnFilterDeprecated()
|
|
480
400
|
warnSchemaRetired(fetch, as ?? inferSchemaFromPath(path || url))
|
|
481
401
|
|
|
482
402
|
return {
|
|
@@ -496,8 +416,6 @@ export function parseFetchConfig(fetch) {
|
|
|
496
416
|
where,
|
|
497
417
|
limit,
|
|
498
418
|
sort,
|
|
499
|
-
// Legacy post-processing (deprecated)
|
|
500
|
-
filter,
|
|
501
419
|
}
|
|
502
420
|
}
|
|
503
421
|
|
|
@@ -541,18 +459,6 @@ export function _resetRetiredSchemaWarnings() {
|
|
|
541
459
|
warnedRetiredSchema.clear()
|
|
542
460
|
}
|
|
543
461
|
|
|
544
|
-
let filterDeprecationWarned = false
|
|
545
|
-
function warnFilterDeprecated() {
|
|
546
|
-
if (filterDeprecationWarned) return
|
|
547
|
-
filterDeprecationWarned = true
|
|
548
|
-
console.warn(
|
|
549
|
-
"[uniweb] 'fetch: { filter: ... }' (DSL string) is deprecated; use 'where: { ... }' " +
|
|
550
|
-
'with a where-object. Example: where: { tags: \"featured\" } instead of ' +
|
|
551
|
-
"filter: 'tags contains featured'. " +
|
|
552
|
-
'Accepted for one release; will be removed in the next minor.'
|
|
553
|
-
)
|
|
554
|
-
}
|
|
555
|
-
|
|
556
462
|
/**
|
|
557
463
|
* Keys a fetch declaration carries for THE BUILD ONLY, which no runtime reads.
|
|
558
464
|
*
|
|
@@ -716,7 +622,7 @@ export async function executeFetch(config, options = {}) {
|
|
|
716
622
|
data = getNestedValue(data, transform)
|
|
717
623
|
}
|
|
718
624
|
|
|
719
|
-
// Apply post-processing (
|
|
625
|
+
// Apply post-processing (where, sort, limit)
|
|
720
626
|
data = applyPostProcessing(data, config)
|
|
721
627
|
|
|
722
628
|
// Ensure we return an array or object, defaulting to empty array
|
package/src/site/index.js
CHANGED
package/src/site/plugin.js
CHANGED
|
@@ -209,7 +209,7 @@ async function processDevSectionFetches(sections, fetchOptions) {
|
|
|
209
209
|
}
|
|
210
210
|
}
|
|
211
211
|
}
|
|
212
|
-
import { generateSearchIndex, isSearchEnabled, getSearchIndexFilename } from '
|
|
212
|
+
import { generateSearchIndex, isSearchEnabled, getSearchIndexFilename } from '@uniweb/projections'
|
|
213
213
|
import { mergeTranslations } from '../i18n/merge.js'
|
|
214
214
|
import { mountDevApi } from '../dev/api-mount.js'
|
|
215
215
|
|
|
@@ -105,10 +105,8 @@ function normalizeQueryDecl(name, decl) {
|
|
|
105
105
|
* @param {object} [opts]
|
|
106
106
|
* @param {object} [opts.siteYml] - an already-read site.yml (avoids a re-read)
|
|
107
107
|
* @returns {Promise<{
|
|
108
|
-
* folderSync: boolean, // vestigial — see below; always true
|
|
109
108
|
* hasQueriesYml: boolean,
|
|
110
109
|
* declarations: object, // { name: decl } — merged, schema-defaulted
|
|
111
|
-
* folders: Array|null, // the folder's virtual org, or null
|
|
112
110
|
* }>}
|
|
113
111
|
*/
|
|
114
112
|
export async function resolveQueriesConfig(siteRoot, opts = {}) {
|
|
@@ -172,25 +170,20 @@ export async function resolveQueriesConfig(siteRoot, opts = {}) {
|
|
|
172
170
|
|
|
173
171
|
await deriveDeferredFromSchemas(siteRoot, siteYml, declarations)
|
|
174
172
|
|
|
175
|
-
// ⛔
|
|
173
|
+
// ⛔ `folderSync` AND `folders` WERE DELETED HERE, 2026-09-06. Both were
|
|
174
|
+
// `collections.yml` survivors kept "for one step" while `records.yml` took
|
|
175
|
+
// over, and both had become literals — `true` and `null` — that no longer
|
|
176
|
+
// decided anything. `folderSync` was `collections.yml::sync`; its one reader
|
|
177
|
+
// guarded on `!folderSync`, a branch that had been unreachable since the
|
|
178
|
+
// literal landed. `folders` was the virtual org, and by the time it was
|
|
179
|
+
// deleted it had **no reader at all** — the comment naming
|
|
180
|
+
// `uwx/sync-package.js` was itself stale.
|
|
176
181
|
//
|
|
177
|
-
//
|
|
178
|
-
//
|
|
179
|
-
// `records.yml`" — the actual round trip. Its one reader is
|
|
180
|
-
// `uwx/records.js`, and it goes when `records.yml` supplies the real
|
|
181
|
-
// control. Until then it must stay TRUE, or nothing syncs at all.
|
|
182
|
-
//
|
|
183
|
-
// `folders` was `collections.yml::folders`, the virtual org. Its one reader is
|
|
184
|
-
// `uwx/sync-package.js`, and `records.yml` replaces it. Null meanwhile is the
|
|
185
|
-
// long-standing default (`folder.js::defaultContents` — one branch per query).
|
|
186
|
-
//
|
|
187
|
-
// ⚠️ Leaving them as literals rather than ripping out their readers keeps this
|
|
188
|
-
// step revertible on its own, which is the whole reason the work is ordered.
|
|
182
|
+
// ⚠️ The lesson is the shape, not the keys: a vestige parked as a literal
|
|
183
|
+
// stops failing, so nothing brings anyone back to remove it.
|
|
189
184
|
return {
|
|
190
|
-
folderSync: true,
|
|
191
185
|
hasQueriesYml,
|
|
192
186
|
declarations,
|
|
193
|
-
folders: null,
|
|
194
187
|
}
|
|
195
188
|
}
|
|
196
189
|
|
|
@@ -55,7 +55,7 @@ import { existsSync } from 'node:fs'
|
|
|
55
55
|
import yaml from 'js-yaml'
|
|
56
56
|
import { parseBibtex } from '@citestyle/bibtex'
|
|
57
57
|
import { DATA_DIR, fillRoutePattern } from '@uniweb/core'
|
|
58
|
-
import { applyWhere,
|
|
58
|
+
import { applyWhere, applySort } from './data-fetcher.js'
|
|
59
59
|
import { resolveAssetPath, walkContentAssets, isLocalAssetPath } from './assets.js'
|
|
60
60
|
import { readEntityPool, groupPoolBySchema, ENTITIES_DIR } from './entity-pool.js'
|
|
61
61
|
import { readRecordsConfig, resolveFolder, FOLDER_MISSING } from './records-config.js'
|
|
@@ -715,11 +715,6 @@ async function collectItems(siteDir, config, entitiesDir, basePath) {
|
|
|
715
715
|
items = applyWhere(items, config.where)
|
|
716
716
|
}
|
|
717
717
|
|
|
718
|
-
// Apply the legacy filter expression (deprecated)
|
|
719
|
-
if (config.filter) {
|
|
720
|
-
items = applyFilter(items, config.filter)
|
|
721
|
-
}
|
|
722
|
-
|
|
723
718
|
// Apply sort
|
|
724
719
|
if (config.sort) {
|
|
725
720
|
items = applySort(items, config.sort)
|
|
@@ -249,7 +249,7 @@ export function resolveFolder(entries, pool) {
|
|
|
249
249
|
// ⭐ `name` is the handle (the URL segment, sibling-unique); `label` is the
|
|
250
250
|
// display text. The store renamed the pair on 2026-09-04 — `path_segment` →
|
|
251
251
|
// `name`, and the old `name` (display) → `label` — so one word means one
|
|
252
|
-
// thing from records.yml (`folder:` / `label:`) to the wire to the
|
|
252
|
+
// thing from records.yml (`folder:` / `label:`) to the wire to the service's
|
|
253
253
|
// `$name`.
|
|
254
254
|
const branch = { kind: 'branch', name: segment }
|
|
255
255
|
if (entry.label !== undefined && entry.label !== null) branch.label = String(entry.label)
|
package/src/uwx/folder.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
// or `$ref: "<id>"` while brand-new (resolved within this payload).
|
|
13
13
|
// - a BRANCH is a sub-folder: `{ kind: 'branch', name, label?, $children }`.
|
|
14
14
|
//
|
|
15
|
-
// ⭐ `name` IS THE HANDLE — the URL segment, sibling-unique, the
|
|
15
|
+
// ⭐ `name` IS THE HANDLE — the URL segment, sibling-unique, the records service's `$name` —
|
|
16
16
|
// and `label` is the display text, a localized map (`{ en: "Blog" }`). The store
|
|
17
17
|
// renamed the pair on 2026-09-04 (`path_segment` → `name`; the old display `name`
|
|
18
18
|
// → `label`); this emitter writes the new shape only and the pull reader
|
package/src/uwx/records.js
CHANGED
|
@@ -558,9 +558,6 @@ export async function buildRecordEntities(siteRoot, opts = {}) {
|
|
|
558
558
|
const recordsState = recordsCfg.state
|
|
559
559
|
|
|
560
560
|
const colConfig = opts.queriesConfig || (await resolveQueriesConfig(siteRoot))
|
|
561
|
-
if (!colConfig.folderSync) {
|
|
562
|
-
return { entities: [], index: [], warnings: [], schemaless: [], mappedCount: 0, colConfig, recordsState, folder: null }
|
|
563
|
-
}
|
|
564
561
|
const mapped = syncableQueries(colConfig.declarations)
|
|
565
562
|
if (mapped.length === 0) return { entities: [], index: [], warnings: [], schemaless: [], mappedCount: 0, colConfig, recordsState, folder: null }
|
|
566
563
|
|
package/src/uwx/site.js
CHANGED
|
@@ -246,7 +246,7 @@ function buildPageData(config, ctx) {
|
|
|
246
246
|
// ⭐ BOTH, deliberately, and they are not redundant.
|
|
247
247
|
//
|
|
248
248
|
// `query` — the author's named query, unresolved. A consumer that can ask
|
|
249
|
-
// a host where records live (`config.records`) resolves it there, which
|
|
249
|
+
// a host where records live (`config.services.records`) resolves it there, which
|
|
250
250
|
// is the only way a live lane is reachable at all: a resolved path names
|
|
251
251
|
// one place and closes the question.
|
|
252
252
|
//
|
package/src/search/extract.js
DELETED
package/src/search/generate.js
DELETED
package/src/search/index.js
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Search Index Generation Module
|
|
3
|
-
*
|
|
4
|
-
* Re-export of `@uniweb/projections/search`. The implementation moved there so
|
|
5
|
-
* one copy serves every publisher of a site — the CLI and the app both import
|
|
6
|
-
* it, and the backend stores the result rather than generating its own. This
|
|
7
|
-
* shim keeps `@uniweb/build/search` and every existing call site working.
|
|
8
|
-
*
|
|
9
|
-
* @module @uniweb/build/search
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
export {
|
|
13
|
-
extractSearchContent,
|
|
14
|
-
extractSearchContent as default,
|
|
15
|
-
generateSearchIndex,
|
|
16
|
-
isSearchEnabled,
|
|
17
|
-
getSearchConfig,
|
|
18
|
-
getSearchIndexFilename,
|
|
19
|
-
generateRecordSearchIndex
|
|
20
|
-
} from '@uniweb/projections/search'
|