uniweb 0.7.2 → 0.7.4

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/README.md CHANGED
@@ -53,7 +53,7 @@ pnpm create uniweb my-site --template dynamic
53
53
  pnpm create uniweb my-site --template extensions
54
54
 
55
55
  # Default starter (foundation + site + sample content)
56
- pnpm create uniweb my-site
56
+ pnpm create uniweb my-site --template starter
57
57
 
58
58
  # Blank workspace (grow with `add`)
59
59
  pnpm create uniweb my-site --template blank
@@ -260,8 +260,8 @@ The workspace grows organically. `add` handles placement, wires dependencies, up
260
260
  ```bash
261
261
  pnpm create uniweb acme --template blank
262
262
  cd acme
263
- uniweb add foundation
264
- uniweb add site
263
+ npx uniweb add foundation
264
+ npx uniweb add site
265
265
  pnpm install && pnpm dev
266
266
  ```
267
267
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uniweb",
3
- "version": "0.7.2",
3
+ "version": "0.7.4",
4
4
  "description": "Create structured Vite + React sites with content/code separation",
5
5
  "type": "module",
6
6
  "bin": {
@@ -14,7 +14,7 @@ import { readFile, writeFile } from 'node:fs/promises'
14
14
  import { join, relative } from 'node:path'
15
15
  import prompts from 'prompts'
16
16
  import yaml from 'js-yaml'
17
- import { scaffoldFoundation, scaffoldSite, applyContent } from '../utils/scaffold.js'
17
+ import { scaffoldFoundation, scaffoldSite, applyContent, mergeTemplateDependencies } from '../utils/scaffold.js'
18
18
  import {
19
19
  readWorkspaceConfig,
20
20
  addWorkspaceGlob,
@@ -469,6 +469,14 @@ async function applyFromTemplate(templateId, packageType, targetDir, projectName
469
469
  onProgress: (msg) => info(` ${msg}`),
470
470
  })
471
471
 
472
+ // Merge template dependencies
473
+ if (metadata.dependencies) {
474
+ const deps = metadata.dependencies[packageType] || metadata.dependencies[match?.name]
475
+ if (deps) {
476
+ await mergeTemplateDependencies(join(targetDir, 'package.json'), deps)
477
+ }
478
+ }
479
+
472
480
  // If site content applied, inform about expected section types
473
481
  if (packageType === 'site' && metadata.components) {
474
482
  log('')
package/src/index.js CHANGED
@@ -27,7 +27,7 @@ import {
27
27
  parseTemplateId,
28
28
  } from './templates/index.js'
29
29
  import { validateTemplate } from './templates/validator.js'
30
- import { scaffoldWorkspace, scaffoldFoundation, scaffoldSite, applyContent, applyStarter } from './utils/scaffold.js'
30
+ import { scaffoldWorkspace, scaffoldFoundation, scaffoldSite, applyContent, applyStarter, mergeTemplateDependencies } from './utils/scaffold.js'
31
31
 
32
32
  // Colors for terminal output
33
33
  const colors = {
@@ -40,6 +40,19 @@ const colors = {
40
40
  red: '\x1b[31m',
41
41
  }
42
42
 
43
+ // Template choices for interactive prompt
44
+ const TEMPLATE_CHOICES = [
45
+ { title: 'Starter', value: 'starter', description: 'Foundation + site + sample content' },
46
+ { title: 'Blank', value: 'blank', description: 'Empty workspace — grow with uniweb add' },
47
+ { title: 'Marketing', value: 'marketing', description: 'Landing page, features, pricing, testimonials' },
48
+ { title: 'Docs', value: 'docs', description: 'Documentation with sidebar and search' },
49
+ { title: 'Academic', value: 'academic', description: 'Research site with publications and team' },
50
+ { title: 'Dynamic', value: 'dynamic', description: 'Live API data fetching with loading states' },
51
+ { title: 'International', value: 'international', description: 'Multilingual site with i18n and blog' },
52
+ { title: 'Store', value: 'store', description: 'E-commerce with product grid' },
53
+ { title: 'Extensions', value: 'extensions', description: 'Multi-foundation with visual effects extension' },
54
+ ]
55
+
43
56
  function log(message) {
44
57
  console.log(message)
45
58
  }
@@ -202,6 +215,14 @@ async function createFromContentTemplate(projectDir, projectName, metadata, temp
202
215
  onProgress?.(`Applying ${metadata.name} content to ${pkg.name}...`)
203
216
  await applyContent(contentDir.dir, fullPath, { projectName }, { onProgress, onWarning })
204
217
  }
218
+
219
+ // Merge template dependencies into package.json
220
+ if (metadata.dependencies) {
221
+ const deps = metadata.dependencies[pkg.name] || metadata.dependencies[pkg.type]
222
+ if (deps) {
223
+ await mergeTemplateDependencies(join(fullPath, 'package.json'), deps)
224
+ }
225
+ }
205
226
  }
206
227
 
207
228
  success(`Created project: ${projectName}`)
@@ -375,6 +396,22 @@ async function main() {
375
396
  process.exit(1)
376
397
  }
377
398
 
399
+ // Prompt for template if not specified via --template
400
+ if (!templateType) {
401
+ const templateResponse = await prompts({
402
+ type: 'select',
403
+ name: 'template',
404
+ message: 'Template:',
405
+ choices: TEMPLATE_CHOICES,
406
+ }, {
407
+ onCancel: () => {
408
+ log('\nScaffolding cancelled.')
409
+ process.exit(0)
410
+ },
411
+ })
412
+ templateType = templateResponse.template
413
+ }
414
+
378
415
  const effectiveName = displayName || projectName
379
416
 
380
417
  // Create project directory
@@ -396,8 +433,8 @@ async function main() {
396
433
  onProgress: progressCb,
397
434
  onWarning: warningCb,
398
435
  })
399
- } else if (!templateType) {
400
- // Default flow (package templates + starter)
436
+ } else if (templateType === 'starter') {
437
+ // Starter: foundation + site + sample content
401
438
  log('\nCreating project...')
402
439
  await createFromPackageTemplates(projectDir, effectiveName, {
403
440
  onProgress: progressCb,
@@ -431,7 +468,7 @@ async function main() {
431
468
  log(`${colors.yellow}Troubleshooting:${colors.reset}`)
432
469
  log(` • Check your network connection`)
433
470
  log(` • Official templates require GitHub access (may be blocked by corporate networks)`)
434
- log(` • Try the default template instead: ${colors.cyan}uniweb create ${projectName}${colors.reset}`)
471
+ log(` • Try the starter template instead: ${colors.cyan}uniweb create ${projectName} --template starter${colors.reset}`)
435
472
  process.exit(1)
436
473
  }
437
474
  }
@@ -460,8 +497,8 @@ async function main() {
460
497
  if (templateType === 'blank') {
461
498
  log(`Next steps:\n`)
462
499
  log(` ${colors.cyan}cd ${projectName}${colors.reset}`)
463
- log(` ${colors.cyan}uniweb add foundation${colors.reset}`)
464
- log(` ${colors.cyan}uniweb add site${colors.reset}`)
500
+ log(` ${colors.cyan}npx uniweb add foundation${colors.reset}`)
501
+ log(` ${colors.cyan}npx uniweb add site${colors.reset}`)
465
502
  log(` ${colors.cyan}pnpm install${colors.reset}`)
466
503
  log(` ${colors.cyan}pnpm dev${colors.reset}`)
467
504
  } else {
@@ -489,7 +526,7 @@ ${colors.bright}Commands:${colors.reset}
489
526
  i18n <cmd> Internationalization (extract, sync, status)
490
527
 
491
528
  ${colors.bright}Create Options:${colors.reset}
492
- --template <type> Project template (default: creates foundation + site + starter)
529
+ --template <type> Project template (prompts if not specified)
493
530
  --name <name> Project display name
494
531
  --no-git Skip git repository initialization
495
532
 
@@ -525,6 +562,7 @@ ${colors.bright}i18n Commands:${colors.reset}
525
562
  status Show translation coverage per locale
526
563
 
527
564
  ${colors.bright}Template Types:${colors.reset}
565
+ starter Foundation + site + sample content (default)
528
566
  blank Empty workspace (grow with 'add')
529
567
  marketing Official marketing template
530
568
  ./path/to/template Local directory
@@ -533,7 +571,8 @@ ${colors.bright}Template Types:${colors.reset}
533
571
  https://github.com/user/repo GitHub URL
534
572
 
535
573
  ${colors.bright}Examples:${colors.reset}
536
- npx uniweb create my-project # Default (foundation + site + starter)
574
+ npx uniweb create my-project # Interactive (prompts for template)
575
+ npx uniweb create my-project --template starter # Foundation + site + starter content
537
576
  npx uniweb create my-project --template blank # Blank workspace
538
577
  npx uniweb create my-project --template marketing # Official template
539
578
  npx uniweb create my-project --template ./my-template # Local template
@@ -3,7 +3,7 @@
3
3
  */
4
4
 
5
5
  // Built-in templates (programmatic, not file-based)
6
- export const BUILTIN_TEMPLATES = ['blank']
6
+ export const BUILTIN_TEMPLATES = ['blank', 'starter']
7
7
 
8
8
  // Official templates from @uniweb/templates package (downloaded from GitHub releases)
9
9
  export const OFFICIAL_TEMPLATES = ['marketing', 'academic', 'docs', 'international', 'dynamic', 'store', 'extensions']
@@ -173,3 +173,24 @@ export async function applyStarter(projectDir, context, options = {}) {
173
173
  const siteTargetDir = join(projectDir, siteDir)
174
174
  await applyContent(siteContentDir, siteTargetDir, context, options)
175
175
  }
176
+
177
+ /**
178
+ * Merge additional dependencies from a content template into a scaffolded package.json
179
+ *
180
+ * Reads the package.json at the given path, adds any deps not already present
181
+ * (in either dependencies or devDependencies), and writes it back.
182
+ *
183
+ * @param {string} packageJsonPath - Absolute path to package.json
184
+ * @param {Object} deps - Dependencies to merge (name → version)
185
+ */
186
+ export async function mergeTemplateDependencies(packageJsonPath, deps) {
187
+ if (!deps || Object.keys(deps).length === 0) return
188
+ const pkg = JSON.parse(await fs.readFile(packageJsonPath, 'utf-8'))
189
+ if (!pkg.dependencies) pkg.dependencies = {}
190
+ for (const [name, version] of Object.entries(deps)) {
191
+ if (!pkg.dependencies[name] && !pkg.devDependencies?.[name]) {
192
+ pkg.dependencies[name] = version
193
+ }
194
+ }
195
+ await fs.writeFile(packageJsonPath, JSON.stringify(pkg, null, 2) + '\n')
196
+ }