@uniweb/build 0.6.15 → 0.6.17
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 +3 -3
- package/src/i18n/index.js +11 -6
- package/src/i18n/sync.js +37 -1
- package/src/site/content-collector.js +32 -14
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uniweb/build",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.17",
|
|
4
4
|
"description": "Build tooling for the Uniweb Component Web Platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -51,8 +51,8 @@
|
|
|
51
51
|
},
|
|
52
52
|
"optionalDependencies": {
|
|
53
53
|
"@uniweb/schemas": "0.2.1",
|
|
54
|
-
"@uniweb/
|
|
55
|
-
"@uniweb/
|
|
54
|
+
"@uniweb/content-reader": "1.1.2",
|
|
55
|
+
"@uniweb/runtime": "0.5.21"
|
|
56
56
|
},
|
|
57
57
|
"peerDependencies": {
|
|
58
58
|
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0",
|
package/src/i18n/index.js
CHANGED
|
@@ -176,7 +176,8 @@ export async function extractManifest(siteRoot, options = {}) {
|
|
|
176
176
|
const {
|
|
177
177
|
localesDir = DEFAULTS.localesDir,
|
|
178
178
|
siteContentPath = join(siteRoot, 'dist', 'site-content.json'),
|
|
179
|
-
verbose = false
|
|
179
|
+
verbose = false,
|
|
180
|
+
dryRun = false
|
|
180
181
|
} = options
|
|
181
182
|
|
|
182
183
|
// Load site content
|
|
@@ -203,8 +204,10 @@ export async function extractManifest(siteRoot, options = {}) {
|
|
|
203
204
|
// Generate sync report
|
|
204
205
|
const report = syncManifests(previousManifest, manifest)
|
|
205
206
|
|
|
206
|
-
// Write new manifest
|
|
207
|
-
|
|
207
|
+
// Write new manifest (skip in dry-run mode)
|
|
208
|
+
if (!dryRun) {
|
|
209
|
+
await writeFile(manifestPath, JSON.stringify(manifest, null, 2))
|
|
210
|
+
}
|
|
208
211
|
|
|
209
212
|
if (verbose) {
|
|
210
213
|
console.log(formatSyncReport(report))
|
|
@@ -222,7 +225,7 @@ export async function extractManifest(siteRoot, options = {}) {
|
|
|
222
225
|
* @returns {Promise<Object>} { manifest, report }
|
|
223
226
|
*/
|
|
224
227
|
export async function extractCollectionManifest(siteRoot, options = {}) {
|
|
225
|
-
const { localesDir = DEFAULTS.localesDir } = options
|
|
228
|
+
const { localesDir = DEFAULTS.localesDir, dryRun = false } = options
|
|
226
229
|
|
|
227
230
|
// Extract translatable content from collections
|
|
228
231
|
const manifest = await extractCollectionContent(siteRoot)
|
|
@@ -249,8 +252,10 @@ export async function extractCollectionManifest(siteRoot, options = {}) {
|
|
|
249
252
|
// Generate sync report
|
|
250
253
|
const report = syncManifests(previousManifest, manifest)
|
|
251
254
|
|
|
252
|
-
// Write new manifest
|
|
253
|
-
|
|
255
|
+
// Write new manifest (skip in dry-run mode)
|
|
256
|
+
if (!dryRun) {
|
|
257
|
+
await writeFile(manifestPath, JSON.stringify(manifest, null, 2))
|
|
258
|
+
}
|
|
254
259
|
|
|
255
260
|
return { manifest, report }
|
|
256
261
|
}
|
package/src/i18n/sync.js
CHANGED
|
@@ -143,6 +143,15 @@ export function formatSyncReport(report) {
|
|
|
143
143
|
|
|
144
144
|
if (report.moved.length > 0) {
|
|
145
145
|
lines.push(` ↻ ${report.moved.length} strings moved (contexts updated)`)
|
|
146
|
+
for (const item of report.moved.slice(0, 5)) {
|
|
147
|
+
const preview = truncate(item.source, 40)
|
|
148
|
+
const oldCtx = formatContext(item.previousContexts?.[0])
|
|
149
|
+
const newCtx = formatContext(item.currentContexts?.[0])
|
|
150
|
+
lines.push(` - "${preview}" ${oldCtx} → ${newCtx}`)
|
|
151
|
+
}
|
|
152
|
+
if (report.moved.length > 5) {
|
|
153
|
+
lines.push(` ... and ${report.moved.length - 5} more`)
|
|
154
|
+
}
|
|
146
155
|
}
|
|
147
156
|
|
|
148
157
|
if (report.changed.length > 0) {
|
|
@@ -150,7 +159,7 @@ export function formatSyncReport(report) {
|
|
|
150
159
|
for (const item of report.changed.slice(0, 5)) {
|
|
151
160
|
const prevPreview = truncate(item.previousSource, 30)
|
|
152
161
|
const currPreview = truncate(item.source, 30)
|
|
153
|
-
lines.push(` -
|
|
162
|
+
lines.push(` - "${prevPreview}" → "${currPreview}"`)
|
|
154
163
|
}
|
|
155
164
|
if (report.changed.length > 5) {
|
|
156
165
|
lines.push(` ... and ${report.changed.length - 5} more`)
|
|
@@ -159,15 +168,42 @@ export function formatSyncReport(report) {
|
|
|
159
168
|
|
|
160
169
|
if (report.added.length > 0) {
|
|
161
170
|
lines.push(` + ${report.added.length} new strings`)
|
|
171
|
+
for (const item of report.added.slice(0, 5)) {
|
|
172
|
+
const preview = truncate(item.source, 40)
|
|
173
|
+
const ctx = formatContext(item.contexts?.[0])
|
|
174
|
+
lines.push(` - "${preview}" ${ctx}`)
|
|
175
|
+
}
|
|
176
|
+
if (report.added.length > 5) {
|
|
177
|
+
lines.push(` ... and ${report.added.length - 5} more`)
|
|
178
|
+
}
|
|
162
179
|
}
|
|
163
180
|
|
|
164
181
|
if (report.removed.length > 0) {
|
|
165
182
|
lines.push(` - ${report.removed.length} strings removed`)
|
|
183
|
+
for (const item of report.removed.slice(0, 5)) {
|
|
184
|
+
const preview = truncate(item.source, 40)
|
|
185
|
+
const ctx = formatContext(item.contexts?.[0])
|
|
186
|
+
lines.push(` - "${preview}" ${ctx}`)
|
|
187
|
+
}
|
|
188
|
+
if (report.removed.length > 5) {
|
|
189
|
+
lines.push(` ... and ${report.removed.length - 5} more`)
|
|
190
|
+
}
|
|
166
191
|
}
|
|
167
192
|
|
|
168
193
|
return lines.join('\n')
|
|
169
194
|
}
|
|
170
195
|
|
|
196
|
+
/**
|
|
197
|
+
* Format a context object for display
|
|
198
|
+
*/
|
|
199
|
+
function formatContext(context) {
|
|
200
|
+
if (!context) return ''
|
|
201
|
+
const location = context.page || context.collection || ''
|
|
202
|
+
const section = context.section || context.item || ''
|
|
203
|
+
if (!location && !section) return ''
|
|
204
|
+
return `(${location}:${section})`
|
|
205
|
+
}
|
|
206
|
+
|
|
171
207
|
/**
|
|
172
208
|
* Truncate string for display
|
|
173
209
|
*/
|
|
@@ -179,19 +179,36 @@ async function readYamlFile(filePath) {
|
|
|
179
179
|
}
|
|
180
180
|
|
|
181
181
|
/**
|
|
182
|
-
* Check if a file is a markdown file
|
|
182
|
+
* Check if a file is a markdown file that should be processed.
|
|
183
|
+
* Excludes:
|
|
184
|
+
* - Files not ending in .md
|
|
185
|
+
* - Files starting with _ (drafts/private)
|
|
186
|
+
* - README.md (repo documentation, not site content)
|
|
183
187
|
*/
|
|
184
188
|
function isMarkdownFile(filename) {
|
|
185
|
-
|
|
189
|
+
if (!filename.endsWith('.md')) return false
|
|
190
|
+
if (filename.startsWith('_')) return false
|
|
191
|
+
if (filename.toLowerCase() === 'readme.md') return false
|
|
192
|
+
return true
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Check if a folder should be ignored.
|
|
197
|
+
* Excludes folders starting with _ (drafts/private).
|
|
198
|
+
*/
|
|
199
|
+
function isIgnoredFolder(name) {
|
|
200
|
+
return name.startsWith('_')
|
|
186
201
|
}
|
|
187
202
|
|
|
188
203
|
/**
|
|
189
204
|
* Read folder configuration, determining content mode from config file presence.
|
|
190
205
|
*
|
|
191
|
-
* - folder.yml present →
|
|
192
|
-
* - page.yml present →
|
|
206
|
+
* - folder.yml present → folder mode (md files are child pages)
|
|
207
|
+
* - page.yml present → page mode (md files are sections of this page)
|
|
193
208
|
* - Neither → inherit mode from parent
|
|
194
209
|
*
|
|
210
|
+
* Internal mode values: 'pages' (folder mode), 'sections' (page mode)
|
|
211
|
+
*
|
|
195
212
|
* @param {string} dirPath - Directory path
|
|
196
213
|
* @param {string} inheritedMode - Mode inherited from parent ('sections' or 'pages')
|
|
197
214
|
* @returns {Promise<{config: Object, mode: string, source: string}>}
|
|
@@ -205,7 +222,7 @@ async function readFolderConfig(dirPath, inheritedMode) {
|
|
|
205
222
|
if (Object.keys(pageYml).length > 0) {
|
|
206
223
|
return { config: pageYml, mode: 'sections', source: 'page.yml' }
|
|
207
224
|
}
|
|
208
|
-
// Check for empty folder.yml (presence signals
|
|
225
|
+
// Check for empty folder.yml (presence signals folder mode even if empty)
|
|
209
226
|
if (existsSync(join(dirPath, 'folder.yml'))) {
|
|
210
227
|
return { config: {}, mode: 'pages', source: 'folder.yml' }
|
|
211
228
|
}
|
|
@@ -274,7 +291,7 @@ function applyNonStrictOrder(items, orderArray) {
|
|
|
274
291
|
}
|
|
275
292
|
|
|
276
293
|
/**
|
|
277
|
-
* Process a markdown file as a standalone page (
|
|
294
|
+
* Process a markdown file as a standalone page (folder mode).
|
|
278
295
|
* Creates a page with a single section from the markdown content.
|
|
279
296
|
*
|
|
280
297
|
* @param {string} filePath - Path to markdown file
|
|
@@ -763,6 +780,7 @@ async function collectPagesRecursive(dirPath, parentRoute, siteRoot, orderConfig
|
|
|
763
780
|
// First pass: discover all page folders and read their config
|
|
764
781
|
const pageFolders = []
|
|
765
782
|
for (const entry of entries) {
|
|
783
|
+
if (isIgnoredFolder(entry)) continue // Skip _prefixed folders
|
|
766
784
|
const entryPath = join(dirPath, entry)
|
|
767
785
|
const stats = await stat(entryPath)
|
|
768
786
|
if (!stats.isDirectory()) continue
|
|
@@ -802,7 +820,7 @@ async function collectPagesRecursive(dirPath, parentRoute, siteRoot, orderConfig
|
|
|
802
820
|
const folderNames = orderedFolders.map(f => f.name)
|
|
803
821
|
const detectedVersions = detectVersions(folderNames)
|
|
804
822
|
|
|
805
|
-
// If versioned section, handle version folders specially (always
|
|
823
|
+
// If versioned section, handle version folders specially (always page mode)
|
|
806
824
|
if (detectedVersions && !versionContext) {
|
|
807
825
|
const parentConfig = await readYamlFile(join(dirPath, 'page.yml'))
|
|
808
826
|
const versionMeta = buildVersionMetadata(detectedVersions, parentConfig)
|
|
@@ -864,7 +882,7 @@ async function collectPagesRecursive(dirPath, parentRoute, siteRoot, orderConfig
|
|
|
864
882
|
// Apply non-strict order to md-file-pages
|
|
865
883
|
const orderedMdPages = applyNonStrictOrder(mdPageItems, orderConfig?.order)
|
|
866
884
|
|
|
867
|
-
// In
|
|
885
|
+
// In folder mode, only promote an index if explicitly set via index: in folder.yml
|
|
868
886
|
// The container page itself owns the parent route — don't auto-promote children
|
|
869
887
|
const indexName = orderConfig?.index || null
|
|
870
888
|
|
|
@@ -890,7 +908,7 @@ async function collectPagesRecursive(dirPath, parentRoute, siteRoot, orderConfig
|
|
|
890
908
|
const isIndex = entry === indexName
|
|
891
909
|
|
|
892
910
|
if (dirMode === 'sections') {
|
|
893
|
-
// Subdirectory overrides to
|
|
911
|
+
// Subdirectory overrides to page mode — process normally
|
|
894
912
|
const result = await processPage(entryPath, entry, siteRoot, {
|
|
895
913
|
isIndex, parentRoute, parentFetch, versionContext
|
|
896
914
|
})
|
|
@@ -901,7 +919,7 @@ async function collectPagesRecursive(dirPath, parentRoute, siteRoot, orderConfig
|
|
|
901
919
|
iconCollection = mergeIconCollections(iconCollection, pageIcons)
|
|
902
920
|
pages.push(page)
|
|
903
921
|
|
|
904
|
-
// Recurse into subdirectories (
|
|
922
|
+
// Recurse into subdirectories (page mode)
|
|
905
923
|
const childParentRoute = isIndex ? parentRoute : page.route
|
|
906
924
|
const childFetch = page.fetch || parentFetch
|
|
907
925
|
const subResult = await collectPagesRecursive(entryPath, childParentRoute, siteRoot, childOrderConfig, childFetch, versionContext, 'sections')
|
|
@@ -913,7 +931,7 @@ async function collectPagesRecursive(dirPath, parentRoute, siteRoot, orderConfig
|
|
|
913
931
|
}
|
|
914
932
|
}
|
|
915
933
|
} else {
|
|
916
|
-
// Container directory in
|
|
934
|
+
// Container directory in folder mode — create minimal page, recurse
|
|
917
935
|
const containerRoute = isIndex
|
|
918
936
|
? parentRoute
|
|
919
937
|
: parentRoute === '/' ? `/${entry}` : `${parentRoute}/${entry}`
|
|
@@ -955,7 +973,7 @@ async function collectPagesRecursive(dirPath, parentRoute, siteRoot, orderConfig
|
|
|
955
973
|
|
|
956
974
|
pages.push(containerPage)
|
|
957
975
|
|
|
958
|
-
// Recurse in
|
|
976
|
+
// Recurse in folder mode
|
|
959
977
|
const subResult = await collectPagesRecursive(entryPath, containerRoute, siteRoot, childOrderConfig, parentFetch, versionContext, 'pages')
|
|
960
978
|
pages.push(...subResult.pages)
|
|
961
979
|
assetCollection = mergeAssetCollections(assetCollection, subResult.assetCollection)
|
|
@@ -984,8 +1002,8 @@ async function collectPagesRecursive(dirPath, parentRoute, siteRoot, orderConfig
|
|
|
984
1002
|
const isIndex = entry === indexPageName
|
|
985
1003
|
|
|
986
1004
|
if (dirMode === 'pages') {
|
|
987
|
-
// Child directory switches to
|
|
988
|
-
// create container page with empty sections, recurse in
|
|
1005
|
+
// Child directory switches to folder mode (has folder.yml) —
|
|
1006
|
+
// create container page with empty sections, recurse in folder mode
|
|
989
1007
|
const containerRoute = isIndex
|
|
990
1008
|
? parentRoute
|
|
991
1009
|
: parentRoute === '/' ? `/${entry}` : `${parentRoute}/${entry}`
|