@uniweb/build 1.8.3 → 1.8.5
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 -6
- package/src/i18n/merge.js +22 -28
- package/src/i18n/sync.js +14 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uniweb/build",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.5",
|
|
4
4
|
"description": "Build tooling for the Uniweb Component Web Platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -56,15 +56,15 @@
|
|
|
56
56
|
"js-yaml": "^4.1.0",
|
|
57
57
|
"sharp": "^0.35.3",
|
|
58
58
|
"yaml": "^2.5.0",
|
|
59
|
-
"@uniweb/content-reader": "~1.5.
|
|
60
|
-
"@uniweb/projections": "~1.1.7",
|
|
59
|
+
"@uniweb/content-reader": "~1.5.6",
|
|
61
60
|
"@uniweb/content-writer": "~1.3.0",
|
|
62
61
|
"@uniweb/schemas": "~1.6.5",
|
|
63
62
|
"@uniweb/semantic-parser": "~1.13.0",
|
|
63
|
+
"@uniweb/projections": "~1.1.7",
|
|
64
64
|
"@uniweb/theming": "~1.2.1"
|
|
65
65
|
},
|
|
66
66
|
"optionalDependencies": {
|
|
67
|
-
"@uniweb/runtime": "~1.1.
|
|
67
|
+
"@uniweb/runtime": "~1.1.19"
|
|
68
68
|
},
|
|
69
69
|
"peerDependencies": {
|
|
70
70
|
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0",
|
|
@@ -73,8 +73,8 @@
|
|
|
73
73
|
"@tailwindcss/vite": "^4.0.0",
|
|
74
74
|
"@vitejs/plugin-react": "^4.0.0 || ^5.0.0",
|
|
75
75
|
"vite-plugin-svgr": "^4.0.0",
|
|
76
|
-
"@uniweb/core": "~1.4.
|
|
77
|
-
"@uniweb/runtime": "~1.1.
|
|
76
|
+
"@uniweb/core": "~1.4.4",
|
|
77
|
+
"@uniweb/runtime": "~1.1.19"
|
|
78
78
|
},
|
|
79
79
|
"peerDependenciesMeta": {
|
|
80
80
|
"vite": {
|
package/src/i18n/merge.js
CHANGED
|
@@ -17,19 +17,16 @@ import { loadFreeformTranslation } from './freeform.js'
|
|
|
17
17
|
import { elementText, blockElements, translationContext, SITE_META_CONTEXT } from './extract.js'
|
|
18
18
|
import { visitDataBlockStrings } from './data-strings.js'
|
|
19
19
|
|
|
20
|
-
// Inline
|
|
20
|
+
// Inline markdown → ProseMirror inline nodes, for resolving a whole-element
|
|
21
21
|
// translation VALUE (which carries marks/links/icons as inline markdown). Same
|
|
22
22
|
// lazy-import-with-fallback pattern as freeform.js / collection-processor.js, so
|
|
23
23
|
// the synchronous merge path has the converter ready at call time.
|
|
24
|
-
let
|
|
24
|
+
let inlineMarkdownToProseMirror
|
|
25
25
|
try {
|
|
26
26
|
const contentReader = await import('@uniweb/content-reader')
|
|
27
|
-
|
|
27
|
+
inlineMarkdownToProseMirror = contentReader.inlineMarkdownToProseMirror
|
|
28
28
|
} catch {
|
|
29
|
-
|
|
30
|
-
type: 'doc',
|
|
31
|
-
content: [{ type: 'paragraph', content: [{ type: 'text', text: markdown.trim() }] }]
|
|
32
|
-
})
|
|
29
|
+
inlineMarkdownToProseMirror = (markdown) => [{ type: 'text', text: markdown.trim() }]
|
|
33
30
|
}
|
|
34
31
|
|
|
35
32
|
/**
|
|
@@ -183,9 +180,6 @@ async function mergeTranslationsAsync(siteContent, translations, options) {
|
|
|
183
180
|
return translated
|
|
184
181
|
}
|
|
185
182
|
|
|
186
|
-
// The blocks that hold words, which `toPlainText` reads as one run each.
|
|
187
|
-
const TEXT_BLOCKS = new Set(['paragraph', 'heading', 'codeBlock'])
|
|
188
|
-
|
|
189
183
|
/**
|
|
190
184
|
* ⭐ A TRANSLATION VALUE IS INLINE MARKDOWN, AND A PLAIN-TEXT SLOT RENDERS ITS TEXT.
|
|
191
185
|
*
|
|
@@ -221,17 +215,19 @@ export function toPlainText(value) {
|
|
|
221
215
|
return value
|
|
222
216
|
}
|
|
223
217
|
try {
|
|
224
|
-
|
|
225
|
-
//
|
|
226
|
-
//
|
|
218
|
+
// Each paragraph's words, read as inline markdown and never as blocks, so `1. *Introduction*`
|
|
219
|
+
// keeps its number; paragraphs are apart. ⛔ Until 2026-10-16 every level joined with nothing,
|
|
220
|
+
// so a value of two paragraphs read as one word at the seam ("…17hFermé…"). ⛔ Until 2026-10-24
|
|
221
|
+
// the value was read as a document, and one that began like a block lost its marker: an ordered
|
|
222
|
+
// list's number, a bullet, a `#`.
|
|
227
223
|
const inline = (nodes) =>
|
|
228
224
|
(nodes || []).map((n) => (n.type === 'text' ? n.text || '' : n.type === 'hardBreak' ? ' ' : inline(n.content))).join('')
|
|
229
|
-
const
|
|
230
|
-
(
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
225
|
+
const text = value
|
|
226
|
+
.split(/\n[ \t]*\n/)
|
|
227
|
+
.map((paragraph) => inline(inlineMarkdownToProseMirror(paragraph.trim())))
|
|
228
|
+
.filter((t) => t.trim())
|
|
229
|
+
.join(' ')
|
|
230
|
+
.trim()
|
|
235
231
|
return text || value
|
|
236
232
|
} catch {
|
|
237
233
|
return value
|
|
@@ -639,17 +635,15 @@ export function resolveDocForLocale(sourceDoc, table, context = { page: '', sect
|
|
|
639
635
|
return elements || data ? doc : null
|
|
640
636
|
}
|
|
641
637
|
|
|
642
|
-
// Parse an inline-markdown translation value into a ProseMirror inline fragment
|
|
643
|
-
//
|
|
644
|
-
//
|
|
645
|
-
//
|
|
638
|
+
// Parse an inline-markdown translation value into a ProseMirror inline fragment: one element's
|
|
639
|
+
// words, read as inline markdown and never as blocks (`inlineMarkdownToProseMirror`).
|
|
640
|
+
// ⛔ Until 2026-10-24 it was read as a document and each block's content taken, so a value that
|
|
641
|
+
// began like a block — `1. Informations que nous collectons`, the French of a numbered heading —
|
|
642
|
+
// was an ordered list, and the heading was handed its list item. It rendered empty, and a pull
|
|
643
|
+
// refused the site, since no file can say a heading holds a list item.
|
|
646
644
|
function inlineMarkdownToFragment(value) {
|
|
647
645
|
if (typeof value !== 'string' || value.trim() === '') return null
|
|
648
|
-
const
|
|
649
|
-
const inline = []
|
|
650
|
-
for (const block of doc?.content || []) {
|
|
651
|
-
if (Array.isArray(block.content)) inline.push(...block.content)
|
|
652
|
-
}
|
|
646
|
+
const inline = inlineMarkdownToProseMirror(value.trim())
|
|
653
647
|
return inline.length ? inline : [{ type: 'text', text: value.trim() }]
|
|
654
648
|
}
|
|
655
649
|
|
package/src/i18n/sync.js
CHANGED
|
@@ -26,6 +26,8 @@ export function syncManifests(previous, current) {
|
|
|
26
26
|
|
|
27
27
|
const previousHashes = new Set(Object.keys(previousUnits))
|
|
28
28
|
const currentHashes = new Set(Object.keys(currentUnits))
|
|
29
|
+
// Previous units a new one replaced, so that no two new units claim the same one.
|
|
30
|
+
const replaced = new Set()
|
|
29
31
|
|
|
30
32
|
// Check each current unit
|
|
31
33
|
for (const hash of currentHashes) {
|
|
@@ -66,10 +68,11 @@ export function syncManifests(previous, current) {
|
|
|
66
68
|
} else {
|
|
67
69
|
// New hash - could be new content or changed content
|
|
68
70
|
// Check if any previous context now has this new hash
|
|
69
|
-
const matchingContext = findMatchingContext(currentUnit.contexts, previousUnits)
|
|
71
|
+
const matchingContext = findMatchingContext(currentUnit.contexts, previousUnits, currentHashes, replaced)
|
|
70
72
|
|
|
71
73
|
if (matchingContext) {
|
|
72
74
|
// Same context, different hash = content changed
|
|
75
|
+
replaced.add(matchingContext.hash)
|
|
73
76
|
report.changed.push({
|
|
74
77
|
hash,
|
|
75
78
|
previousHash: matchingContext.hash,
|
|
@@ -94,8 +97,7 @@ export function syncManifests(previous, current) {
|
|
|
94
97
|
if (!currentHashes.has(hash)) {
|
|
95
98
|
const prevUnit = previousUnits[hash]
|
|
96
99
|
// Only mark as removed if not detected as changed above
|
|
97
|
-
|
|
98
|
-
if (!wasChanged) {
|
|
100
|
+
if (!replaced.has(hash)) {
|
|
99
101
|
report.removed.push({
|
|
100
102
|
hash,
|
|
101
103
|
source: prevUnit.source,
|
|
@@ -128,16 +130,22 @@ function contextsEqual(contexts1, contexts2) {
|
|
|
128
130
|
}
|
|
129
131
|
|
|
130
132
|
/**
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
+
* The previous unit a new one replaced: one in a context of the new unit that is gone from the
|
|
134
|
+
* current manifest and not yet replaced. Returns its info, or null.
|
|
135
|
+
*
|
|
136
|
+
* ⛔ Until 2026-10-24 it was the first previous unit in such a context, gone or not. Removing a
|
|
137
|
+
* hero's second button then reported the hero's unchanged title as changed into the remaining
|
|
138
|
+
* button, and the button paragraph it did replace as removed (measured that day on a site's
|
|
139
|
+
* `uniweb i18n extract`).
|
|
133
140
|
*/
|
|
134
|
-
function findMatchingContext(currentContexts, previousUnits) {
|
|
141
|
+
function findMatchingContext(currentContexts, previousUnits, currentHashes, replaced) {
|
|
135
142
|
// One key shape for both lanes: a page/section pair, or a record's identity.
|
|
136
143
|
const key = (c) => (c.record ? `record:${c.record}` : `${c.page}:${c.section}`)
|
|
137
144
|
for (const context of currentContexts) {
|
|
138
145
|
const contextKey = key(context)
|
|
139
146
|
|
|
140
147
|
for (const [hash, unit] of Object.entries(previousUnits)) {
|
|
148
|
+
if (currentHashes.has(hash) || replaced.has(hash)) continue
|
|
141
149
|
const unitContexts = unit.contexts || []
|
|
142
150
|
const hasContext = unitContexts.some((c) => key(c) === contextKey)
|
|
143
151
|
if (hasContext) {
|