@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniweb/build",
3
- "version": "0.6.15",
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/runtime": "0.5.21",
55
- "@uniweb/content-reader": "1.1.2"
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
- await writeFile(manifestPath, JSON.stringify(manifest, null, 2))
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
- await writeFile(manifestPath, JSON.stringify(manifest, null, 2))
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(` - ${item.previousHash}: "${prevPreview}" → "${currPreview}"`)
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
- return filename.endsWith('.md') && !filename.startsWith('_')
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 → pages mode (md files are child pages)
192
- * - page.yml present → sections mode (md files are sections of this page)
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 pages mode even if empty)
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 (pages mode).
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 sections mode)
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 pages mode, only promote an index if explicitly set via index: in folder.yml
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 sections mode — process normally
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 (sections mode)
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 pages mode — create minimal page, recurse
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 pages mode
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 pages mode (has folder.yml) —
988
- // create container page with empty sections, recurse in pages mode
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}`