uniweb 0.2.41 → 0.2.42

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
@@ -18,6 +18,9 @@ The `marketing` template includes real components (Hero, Features, Pricing, Test
18
18
  **Other templates:**
19
19
 
20
20
  ```bash
21
+ # Multilingual business site (English, Spanish, French)
22
+ npx uniweb@latest create my-site --template international
23
+
21
24
  # Academic site (researcher portfolios, lab pages)
22
25
  npx uniweb@latest create my-site --template academic
23
26
 
@@ -371,6 +374,31 @@ uniweb create my-site --template docs
371
374
 
372
375
  Perfect for technical documentation, guides, and API references.
373
376
 
377
+ ### International
378
+
379
+ ```bash
380
+ uniweb create my-site --template international
381
+ ```
382
+
383
+ **Includes:** Hero, Features, Team, CTA, Header (with language switcher), Footer (with language links)
384
+
385
+ **Languages:** English (default), Spanish, French
386
+
387
+ A multilingual business site demonstrating Uniweb's i18n capabilities. Includes pre-configured translation files and a complete localization workflow:
388
+
389
+ ```bash
390
+ # Extract translatable strings
391
+ uniweb i18n extract
392
+
393
+ # Check translation coverage
394
+ uniweb i18n status
395
+
396
+ # Build generates locale-specific output (dist/es/, dist/fr/)
397
+ uniweb build
398
+ ```
399
+
400
+ Perfect for international businesses and learning the i18n workflow.
401
+
374
402
  ## External Templates
375
403
 
376
404
  Use templates from npm or GitHub:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uniweb",
3
- "version": "0.2.41",
3
+ "version": "0.2.42",
4
4
  "description": "Create structured Vite + React sites with content/code separation",
5
5
  "type": "module",
6
6
  "bin": {
@@ -37,7 +37,7 @@
37
37
  "js-yaml": "^4.1.0",
38
38
  "prompts": "^2.4.2",
39
39
  "tar": "^7.0.0",
40
- "@uniweb/build": "0.1.22",
40
+ "@uniweb/build": "0.1.23",
41
41
  "@uniweb/core": "0.1.10",
42
42
  "@uniweb/kit": "0.1.5",
43
43
  "@uniweb/runtime": "0.2.11"
@@ -1,4 +1,4 @@
1
- # CLAUDE.md
1
+ # AGENTS.md
2
2
 
3
3
  This file provides guidance for AI assistants working with this Uniweb project.
4
4
 
@@ -1,3 +1,3 @@
1
1
  ## AI Assistance
2
2
 
3
- See [CLAUDE.md](./CLAUDE.md) for detailed instructions that AI assistants can use.
3
+ See [AGENTS.md](./AGENTS.md) for detailed instructions that AI assistants can use.
@@ -3,13 +3,11 @@
3
3
  Generate up-to-date documentation for all foundation components:
4
4
 
5
5
  ```bash
6
- # From within the foundation directory
7
- pnpm docs
8
- ```
6
+ # From foundation directory
7
+ pnpm uniweb docs
9
8
 
10
- This creates `COMPONENTS.md` with full details on each component's:
11
- - Content elements (title, paragraphs, links, images, items)
12
- - Parameters (theme, layout, columns, etc.)
13
- - Presets (pre-configured parameter combinations)
9
+ # Or from site directory (auto-detects linked foundation)
10
+ cd site && pnpm uniweb docs
11
+ ```
14
12
 
15
- The documentation is generated from component `meta.js` files, so it's always current.
13
+ This creates `COMPONENTS.md` with details on each component's parameters, presets, and content elements. The documentation is generated from component `meta.js` files, so it's always current.
@@ -7,10 +7,14 @@
7
7
  * uniweb docs # Generate docs for current directory
8
8
  * uniweb docs --output README.md # Custom output filename
9
9
  * uniweb docs --from-source # Build schema from source (no build required)
10
+ *
11
+ * When run from a site directory, automatically finds and documents the
12
+ * linked foundation, placing COMPONENTS.md in the site folder.
10
13
  */
11
14
 
12
15
  import { existsSync } from 'node:fs'
13
- import { resolve, join } from 'node:path'
16
+ import { readFile } from 'node:fs/promises'
17
+ import { resolve, join, dirname } from 'node:path'
14
18
  import { generateDocs } from '@uniweb/build'
15
19
 
16
20
  // Colors for terminal output
@@ -72,7 +76,7 @@ function showHelp() {
72
76
  ${colors.bright}uniweb docs${colors.reset} - Generate component documentation
73
77
 
74
78
  ${colors.dim}Usage:${colors.reset}
75
- uniweb docs Generate COMPONENTS.md from schema.json
79
+ uniweb docs Generate COMPONENTS.md
76
80
  uniweb docs --output DOCS.md Custom output filename
77
81
  uniweb docs --from-source Build schema from source (no build required)
78
82
 
@@ -82,8 +86,9 @@ ${colors.dim}Options:${colors.reset}
82
86
  -h, --help Show this help message
83
87
 
84
88
  ${colors.dim}Notes:${colors.reset}
85
- By default, docs are generated from dist/schema.json (requires build).
86
- Use --from-source to generate without building first.
89
+ Run from a foundation directory to generate docs there.
90
+ Run from a site directory to auto-detect the linked foundation
91
+ and generate docs in the site folder for convenience.
87
92
  `)
88
93
  }
89
94
 
@@ -96,6 +101,47 @@ function isFoundation(dir) {
96
101
  return existsSync(componentsDir)
97
102
  }
98
103
 
104
+ /**
105
+ * Detect if current directory is a site
106
+ */
107
+ function isSite(dir) {
108
+ return existsSync(join(dir, 'site.yml')) || existsSync(join(dir, 'site.yaml'))
109
+ }
110
+
111
+ /**
112
+ * Resolve foundation path from a site directory
113
+ * Reads package.json to find foundation dependency with file: protocol
114
+ */
115
+ async function resolveFoundationFromSite(siteDir) {
116
+ const pkgPath = join(siteDir, 'package.json')
117
+ if (!existsSync(pkgPath)) {
118
+ return null
119
+ }
120
+
121
+ try {
122
+ const pkg = JSON.parse(await readFile(pkgPath, 'utf-8'))
123
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies }
124
+
125
+ // Look for foundation dependency with file: protocol
126
+ // Common names: "foundation", or the foundation name from site.yml
127
+ for (const [name, version] of Object.entries(deps)) {
128
+ if (typeof version === 'string' && version.startsWith('file:')) {
129
+ const relativePath = version.slice(5) // Remove 'file:' prefix
130
+ const absolutePath = resolve(siteDir, relativePath)
131
+
132
+ // Check if this is actually a foundation
133
+ if (isFoundation(absolutePath)) {
134
+ return { name, path: absolutePath }
135
+ }
136
+ }
137
+ }
138
+ } catch {
139
+ // Ignore parse errors
140
+ }
141
+
142
+ return null
143
+ }
144
+
99
145
  /**
100
146
  * Main docs command
101
147
  */
@@ -108,19 +154,34 @@ export async function docs(args) {
108
154
  }
109
155
 
110
156
  const projectDir = resolve(process.cwd())
157
+ let foundationDir = projectDir
158
+ let outputDir = projectDir
159
+
160
+ // Check if we're in a site directory
161
+ if (isSite(projectDir)) {
162
+ const foundation = await resolveFoundationFromSite(projectDir)
163
+ if (!foundation) {
164
+ error('Could not find a linked foundation in this site.')
165
+ log(`${colors.dim}Make sure package.json has a foundation dependency like:${colors.reset}`)
166
+ log(`${colors.dim} "foundation": "file:../foundation"${colors.reset}`)
167
+ process.exit(1)
168
+ }
111
169
 
112
- // Verify this is a foundation
113
- if (!isFoundation(projectDir)) {
114
- error('This directory does not appear to be a foundation.')
115
- log(`${colors.dim}Foundations have a src/components/ directory with component folders.${colors.reset}`)
170
+ foundationDir = foundation.path
171
+ outputDir = projectDir
172
+ info(`Found foundation: ${foundation.name} (${foundation.path})`)
173
+ } else if (!isFoundation(projectDir)) {
174
+ error('This directory does not appear to be a foundation or site.')
175
+ log(`${colors.dim}Foundations have a src/components/ directory.${colors.reset}`)
176
+ log(`${colors.dim}Sites have a site.yml file.${colors.reset}`)
116
177
  process.exit(1)
117
178
  }
118
179
 
119
180
  // Check if schema.json exists (if not using --from-source)
120
- const schemaPath = join(projectDir, 'dist', 'schema.json')
181
+ const schemaPath = join(foundationDir, 'dist', 'schema.json')
121
182
  if (!options.fromSource && !existsSync(schemaPath)) {
122
- log(`${colors.yellow}⚠${colors.reset} No dist/schema.json found.`)
123
- log(`${colors.dim}Run 'uniweb build' first, or use '--from-source' to read meta.js files directly.${colors.reset}`)
183
+ log(`${colors.yellow}⚠${colors.reset} No dist/schema.json found in foundation.`)
184
+ log(`${colors.dim}Run 'uniweb build' in the foundation first, or use '--from-source'.${colors.reset}`)
124
185
  log('')
125
186
  info('Falling back to --from-source mode')
126
187
  options.fromSource = true
@@ -129,8 +190,8 @@ export async function docs(args) {
129
190
  try {
130
191
  info('Generating documentation...')
131
192
 
132
- const result = await generateDocs(projectDir, {
133
- output: options.output,
193
+ const result = await generateDocs(foundationDir, {
194
+ output: join(outputDir, options.output),
134
195
  fromSource: options.fromSource,
135
196
  })
136
197
 
@@ -6,7 +6,7 @@
6
6
  export const BUILTIN_TEMPLATES = ['single', 'multi', 'template']
7
7
 
8
8
  // Official templates from @uniweb/templates package (downloaded from GitHub releases)
9
- export const OFFICIAL_TEMPLATES = ['marketing', 'academic', 'docs']
9
+ export const OFFICIAL_TEMPLATES = ['marketing', 'academic', 'docs', 'international']
10
10
 
11
11
  /**
12
12
  * Parse a template identifier and determine its source type
@@ -0,0 +1 @@
1
+ {{> agents-md}}
@@ -0,0 +1 @@
1
+ {{> agents-md}}
@@ -22,7 +22,7 @@ A multi-site workspace built with [Uniweb](https://github.com/uniweb/cli) — a
22
22
  │ ├── main.js
23
23
  │ └── vite.config.js
24
24
 
25
- ├── CLAUDE.md # AI assistant instructions
25
+ ├── AGENTS.md # AI assistant instructions
26
26
  └── README.md # This file
27
27
  ```
28
28
 
@@ -14,8 +14,7 @@
14
14
  "dev": "vite",
15
15
  "build": "uniweb build",
16
16
  "build:vite": "vite build",
17
- "preview": "vite preview",
18
- "docs": "uniweb docs"
17
+ "preview": "vite preview"
19
18
  },
20
19
  "peerDependencies": {
21
20
  "react": "^18.0.0 || ^19.0.0",
@@ -22,7 +22,7 @@ A website built with [Uniweb](https://github.com/uniweb/cli) — a component web
22
22
  │ ├── site.yml # Site configuration
23
23
  │ └── vite.config.js # defineSiteConfig()
24
24
 
25
- └── CLAUDE.md # AI assistant instructions
25
+ └── AGENTS.md # AI assistant instructions
26
26
  ```
27
27
 
28
28
  ## Content Authoring
@@ -14,8 +14,7 @@
14
14
  "dev": "vite",
15
15
  "build": "uniweb build",
16
16
  "build:vite": "vite build",
17
- "preview": "vite preview",
18
- "docs": "uniweb docs"
17
+ "preview": "vite preview"
19
18
  },
20
19
  "peerDependencies": {
21
20
  "react": "^18.0.0 || ^19.0.0",
@@ -3,7 +3,7 @@
3
3
  "description": "A minimal Uniweb project with one foundation and one site. The canonical starting point for any Uniweb project.",
4
4
  "base": "_shared",
5
5
  "structure": {
6
- "root": ["package.json.hbs", "pnpm-workspace.yaml", ".gitignore", "CLAUDE.md.hbs"],
6
+ "root": ["package.json.hbs", "pnpm-workspace.yaml", ".gitignore", "AGENTS.md.hbs"],
7
7
  "foundation": ["package.json.hbs", "vite.config.js", ".gitignore", "src/"],
8
8
  "site": ["package.json.hbs", "vite.config.js", "index.html.hbs", "site.yml.hbs", "src/", "pages/"]
9
9
  }
@@ -1,4 +1,4 @@
1
- # CLAUDE.md - AI Assistant Instructions
1
+ # AGENTS.md - AI Assistant Instructions
2
2
 
3
3
  This file provides guidance for AI assistants working with this Uniweb template project.
4
4
 
@@ -1 +0,0 @@
1
- {{> claude-md}}
@@ -1 +0,0 @@
1
- {{> claude-md}}