uniweb 0.2.40 → 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
@@ -1,24 +1,42 @@
1
1
  # uniweb
2
2
 
3
- Create a well-structured Vite + React site with content/code separation, file-based routing, localization, and structured content—out of the box. The architecture scales to dynamic content management and collaborative visual editing when you need it.
3
+ A component content architecture for React. Build sites where content authors and component developers can't break each other's work—and scale from local files to visual editing without rewrites.
4
4
 
5
- ## Quick Start
5
+ Create well-structured Vite + React projects with file-based routing, localization, and clean content/code separation out of the box.
6
6
 
7
- Create a new Uniweb project:
7
+ ## Quick Start
8
8
 
9
9
  ```bash
10
- npx uniweb@latest create my-project
11
- cd my-project
10
+ npx uniweb@latest create my-site --template marketing
11
+ cd my-site
12
12
  pnpm install
13
13
  pnpm dev
14
14
  ```
15
15
 
16
- You get a workspace with two packages:
16
+ The `marketing` template includes real components (Hero, Features, Pricing, Testimonials, FAQ, and more) with sample content—a working site you can explore and modify.
17
+
18
+ **Other templates:**
19
+
20
+ ```bash
21
+ # Multilingual business site (English, Spanish, French)
22
+ npx uniweb@latest create my-site --template international
23
+
24
+ # Academic site (researcher portfolios, lab pages)
25
+ npx uniweb@latest create my-site --template academic
26
+
27
+ # Documentation site
28
+ npx uniweb@latest create my-site --template docs
29
+
30
+ # Minimal starter (build from scratch)
31
+ npx uniweb@latest create my-site
32
+ ```
33
+
34
+ Every project is a workspace with two packages:
17
35
 
18
36
  - **`site/`** — Content, pages, entry point
19
- - **`foundation/`** — Your React components
37
+ - **`foundation/`** — React components
20
38
 
21
- Content authors work in markdown. Component authors work in React. Neither can break the other's work, and component updates flow to every site using them.
39
+ Content authors work in markdown. Component authors work in React. Neither can break the other's work.
22
40
 
23
41
  ## What You Get
24
42
 
@@ -30,8 +48,6 @@ my-project/
30
48
  │ │ ├── page.yml # Page metadata
31
49
  │ │ └── 1-hero.md # Section content
32
50
  │ ├── locales/ # i18n (hash-based translations)
33
- │ │ ├── manifest.json # Auto-extracted strings
34
- │ │ └── es.json # Spanish translations
35
51
  │ ├── main.js # Entry point (~6 lines)
36
52
  │ ├── vite.config.js # 3-line config
37
53
  │ └── public/ # Static assets
@@ -84,13 +100,13 @@ Components receive validated, localized data. Natural content stays in markdown;
84
100
 
85
101
  ```jsx
86
102
  export function Hero({ content, params }) {
87
- const { title } = content.main.header;
88
- const { paragraphs, links } = content.main.body;
89
- const { theme = "light" } = params;
103
+ const { title } = content.main.header
104
+ const { paragraphs, links } = content.main.body
105
+ const { theme = 'light' } = params
90
106
 
91
107
  return (
92
108
  <section
93
- className={`py-20 text-center ${theme === "dark" ? "bg-gray-900 text-white" : ""}`}
109
+ className={`py-20 text-center ${theme === 'dark' ? 'bg-gray-900 text-white' : ''}`}
94
110
  >
95
111
  <h1 className="text-4xl font-bold">{title}</h1>
96
112
  <p className="text-xl text-gray-600">{paragraphs[0]}</p>
@@ -103,23 +119,45 @@ export function Hero({ content, params }) {
103
119
  </a>
104
120
  )}
105
121
  </section>
106
- );
122
+ )
107
123
  }
108
124
  ```
109
125
 
110
- Standard React. Standard Tailwind. The `{ content, params }` interface is only for _exposed_ components—the ones content creators select in markdown frontmatter. Internal components (the majority of your codebase) use regular React props.
126
+ Standard React. Standard Tailwind. The `{ content, params }` interface is only for _exposed_ components—the ones content creators select in markdown frontmatter. Internal components use regular React props.
111
127
 
112
- No framework to learn. Foundations are purpose-built component systems designed for a specific domain (marketing, documentation, learning, etc.). Sites are Vite apps that load content from markdown files.
128
+ ## Foundations Are Portable
129
+
130
+ The `foundation/` folder ships with your project as a convenience, but a foundation is a self-contained artifact with no dependency on any specific site. Sites reference foundations by configuration, not by folder proximity.
131
+
132
+ **Three ways to use a foundation:**
133
+
134
+ | Mode | How it works | Best for |
135
+ | ---------------- | ---------------------------------- | -------------------------------------------------- |
136
+ | **Local folder** | Foundation lives in your workspace | Developing site and components together |
137
+ | **npm package** | `pnpm add @acme/foundation` | Distributing via standard package tooling |
138
+ | **Runtime link** | Foundation loads from a URL | Independent release cycles, platform-managed sites |
139
+
140
+ You can delete the `foundation/` folder entirely and point your site at a published foundation. Or develop a foundation locally, then publish it for other sites to consume. The site doesn't care where its components come from.
141
+
142
+ **This enables two development patterns:**
143
+
144
+ _Site-first_ — You're building a website. The foundation is your component library, co-developed with the site. This is the common case.
145
+
146
+ _Foundation-first_ — You're building a component system. The site is a test harness with sample content. The real sites live elsewhere—other repositories, other teams, or managed on [uniweb.app](https://uniweb.app). The `multi` template supports this workflow with multiple test sites exercising a shared foundation.
113
147
 
114
148
  ## The Bigger Picture
115
149
 
116
150
  The structure you start with scales without rewrites:
117
151
 
118
- 1. **Single project** — One site, one component library. Most projects stay here.
119
- 2. **Multi-site** — One foundation powers multiple sites. Release it once, updates propagate automatically.
120
- 3. **Full platform** — [uniweb.app](https://uniweb.app) adds visual editing, live content management, and team collaboration. Your foundation plugs in and its components become native to the editor.
152
+ 1. **Single project** — One site, one foundation. Develop and deploy together. Most projects stay here.
153
+
154
+ 2. **Published foundation** — Release your foundation as an npm package or to [uniweb.app](https://uniweb.app). Other sites can use it without copying code.
155
+
156
+ 3. **Multiple sites** — Several sites share one foundation. Update components once, every site benefits.
157
+
158
+ 4. **Platform-managed sites** — Sites built on [uniweb.app](https://uniweb.app) with visual editing tools can use your foundation. You develop components locally; content teams work in the browser.
121
159
 
122
- Start with local markdown files deployed anywhere. Connect to [uniweb.app](https://uniweb.app) when you're ready for dynamic content and team collaboration.
160
+ Start with local files deployed anywhere. The same foundation works across all these scenarios.
123
161
 
124
162
  ---
125
163
 
@@ -250,123 +288,58 @@ build:
250
288
  prerender: true
251
289
  ```
252
290
 
253
- Or use the `--prerender` flag to force it on (or `--no-prerender` to skip it). This is useful for:
291
+ Or use the `--prerender` flag. This gives you:
254
292
 
255
293
  - **SEO**: Search engines see fully rendered content immediately
256
- - **Performance**: First contentful paint is instant (no JavaScript required)
294
+ - **Performance**: First contentful paint is instant
257
295
  - **Hosting**: Deploy to any static host (GitHub Pages, Netlify, S3, etc.)
258
296
 
259
- **How it works:**
260
-
261
- 1. The site build runs first, generating the JavaScript bundle and `site-content.json`
262
- 2. The prerenderer loads the foundation and site content in Node.js
263
- 3. Each page is rendered to HTML using React's `renderToString()`
264
- 4. The HTML is injected into the shell with the site content embedded
265
-
266
- **Hydration:**
267
-
268
- The pre-rendered HTML includes a `<script id="__SITE_CONTENT__">` tag with the full site data. When the page loads in the browser:
269
-
270
- 1. The static HTML displays immediately (no flash of loading state)
271
- 2. React hydrates the existing DOM instead of replacing it
272
- 3. The site becomes fully interactive with client-side routing
273
-
274
- **Usage:**
275
-
276
- ```bash
277
- # From site directory (with build.prerender: true in site.yml)
278
- cd site
279
- pnpm build
280
-
281
- # Or force pre-rendering via CLI flag
282
- cd site && uniweb build --prerender
283
- ```
297
+ The pre-rendered HTML includes embedded site content. When the page loads, React hydrates the existing DOM—no flash of loading state, then full client-side interactivity.
284
298
 
285
299
  ## Built-in Templates
286
300
 
287
301
  ### Single (Default)
288
302
 
289
- A minimal workspace with a site and foundation as sibling packages. This is the recommended starting point.
303
+ A minimal workspace with a site and foundation as sibling packages. The recommended starting point.
290
304
 
291
305
  ```
292
306
  my-project/
293
- ├── package.json # Workspace root (npm + pnpm compatible)
294
- ├── pnpm-workspace.yaml # Pre-configured for evolution (see below)
295
- ├── CLAUDE.md # AI assistant instructions
296
-
297
- ├── site/ # Site package (content + entry)
307
+ ├── package.json # Workspace root
308
+ ├── pnpm-workspace.yaml
309
+ ├── site/
298
310
  │ ├── package.json
299
- │ ├── vite.config.js # 3-line config
300
- │ ├── index.html
301
- │ ├── site.yml # Site configuration (foundation, title, i18n)
302
- ├── main.js # Entry point (~6 lines)
303
- │ ├── pages/ # Content pages (file-based routing)
304
- │ │ └── home/
305
- │ │ ├── page.yml
306
- │ │ └── 1-hero.md
307
- │ └── public/ # Static assets
308
-
309
- └── foundation/ # Foundation package (components)
311
+ │ ├── vite.config.js
312
+ │ ├── site.yml
313
+ │ ├── main.js
314
+ └── pages/
315
+ └── foundation/
310
316
  ├── package.json
311
- ├── vite.config.js # 3-line config
312
- └── src/
313
- ├── styles.css # Tailwind CSS v4
314
- ├── meta.js # Foundation metadata
315
- ├── exports.js # (optional) Custom Layout, props
316
- └── components/
317
- └── Section/
318
- ├── index.jsx
319
- └── meta.js
317
+ ├── vite.config.js
318
+ └── src/components/
320
319
  ```
321
320
 
322
- **Key characteristics:**
323
-
324
- - **Convention-compliant** — Each package has its own `src/`
325
- - **Clear dep boundaries** — Component libs → `foundation/package.json`, runtime → `site/package.json`
326
- - **Zero extraction** — `foundation/` is already a complete, publishable package
327
- - **Scales naturally** — Rename to `sites/marketing/` and `foundations/marketing/` when needed
328
-
329
321
  ### Multi
330
322
 
331
- A monorepo for multi-site or multi-foundation development.
323
+ A monorepo for foundation development or multi-site projects.
332
324
 
333
325
  ```
334
326
  my-workspace/
335
- ├── package.json # Workspace root (npm + pnpm compatible)
336
- ├── pnpm-workspace.yaml # Same config as single template
337
- ├── CLAUDE.md # AI assistant instructions
338
-
339
327
  ├── sites/
340
- │ ├── marketing/ # Main marketing site
341
- │ ├── package.json
342
- │ │ ├── vite.config.js # 3-line config
343
- │ │ ├── site.yml
344
- │ │ ├── main.js # Entry point (~6 lines)
345
- │ │ └── pages/
346
- │ └── docs/ # Documentation site
347
-
328
+ │ ├── marketing/ # Main site or test site
329
+ └── docs/ # Additional site
348
330
  └── foundations/
349
- ├── marketing/ # Marketing foundation
350
- │ ├── package.json
351
- │ ├── vite.config.js # 3-line config
352
- │ └── src/components/
353
- └── documentation/ # Documentation foundation
331
+ ├── marketing/ # Primary foundation
332
+ └── documentation/ # Additional foundation
354
333
  ```
355
334
 
356
- Use this when you need:
357
-
358
- - Multiple sites sharing foundations
359
- - Multiple foundations for different purposes
360
- - A testing site for foundation development
335
+ Use this when you need multiple sites sharing foundations, multiple foundations for different purposes, or test sites for foundation development.
361
336
 
362
337
  ## Official Templates
363
338
 
364
- Feature-rich templates that demonstrate what's possible with Uniweb. These include real components, sample content, and production-ready structure.
339
+ Feature-rich templates with real components and sample content.
365
340
 
366
341
  ### Marketing
367
342
 
368
- A complete marketing site with landing page components:
369
-
370
343
  ```bash
371
344
  uniweb create my-site --template marketing
372
345
  ```
@@ -383,8 +356,6 @@ uniweb create my-site --template marketing --variant tailwind3
383
356
 
384
357
  ### Academic
385
358
 
386
- A professional academic site for researchers, labs, and departments:
387
-
388
359
  ```bash
389
360
  uniweb create my-site --template academic
390
361
  ```
@@ -395,8 +366,6 @@ Perfect for researcher portfolios, lab websites, and academic department sites.
395
366
 
396
367
  ### Docs
397
368
 
398
- A documentation site with navigation levels:
399
-
400
369
  ```bash
401
370
  uniweb create my-site --template docs
402
371
  ```
@@ -405,9 +374,34 @@ uniweb create my-site --template docs
405
374
 
406
375
  Perfect for technical documentation, guides, and API references.
407
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
+
408
402
  ## External Templates
409
403
 
410
- You can use templates from npm or GitHub:
404
+ Use templates from npm or GitHub:
411
405
 
412
406
  ```bash
413
407
  # npm package
@@ -420,8 +414,6 @@ uniweb create my-site --template github:user/repo
420
414
  uniweb create my-site --template github:user/repo#v1.0.0
421
415
  ```
422
416
 
423
- External templates must follow the same structure as official templates. See [`@uniweb/templates`](https://github.com/uniweb/templates) for the template format specification.
424
-
425
417
  ## Dependency Management
426
418
 
427
419
  Each package manages its own dependencies:
@@ -452,14 +444,14 @@ cd foundation && pnpm add embla-carousel
452
444
  The `defineSiteConfig()` function handles all Vite configuration for sites:
453
445
 
454
446
  ```javascript
455
- import { defineSiteConfig } from "@uniweb/build/site";
447
+ import { defineSiteConfig } from '@uniweb/build/site'
456
448
 
457
449
  export default defineSiteConfig({
458
450
  // All options are optional
459
451
  tailwind: true, // Enable Tailwind CSS v4 (default: true)
460
452
  plugins: [], // Additional Vite plugins
461
453
  // ...any other Vite config options
462
- });
454
+ })
463
455
  ```
464
456
 
465
457
  ### Foundation Configuration
@@ -467,11 +459,11 @@ export default defineSiteConfig({
467
459
  The `defineFoundationConfig()` function handles all Vite configuration for foundations:
468
460
 
469
461
  ```javascript
470
- import { defineFoundationConfig } from "@uniweb/build";
462
+ import { defineFoundationConfig } from '@uniweb/build'
471
463
 
472
464
  export default defineFoundationConfig({
473
465
  // All options are optional - entry is auto-generated
474
- fileName: "foundation", // Output file name
466
+ fileName: 'foundation', // Output file name
475
467
  externals: [], // Additional packages to externalize
476
468
  includeDefaultExternals: true, // Include react, @uniweb/core, etc.
477
469
  tailwind: true, // Enable Tailwind CSS v4 Vite plugin
@@ -479,7 +471,7 @@ export default defineFoundationConfig({
479
471
  plugins: [], // Additional Vite plugins
480
472
  build: {}, // Additional Vite build options
481
473
  // ...any other Vite config options
482
- });
474
+ })
483
475
  ```
484
476
 
485
477
  For Tailwind CSS v3 projects, set `tailwind: false` and use PostCSS:
@@ -487,7 +479,7 @@ For Tailwind CSS v3 projects, set `tailwind: false` and use PostCSS:
487
479
  ```javascript
488
480
  export default defineFoundationConfig({
489
481
  tailwind: false, // Uses PostCSS instead of Vite plugin
490
- });
482
+ })
491
483
  ```
492
484
 
493
485
  ## Foundation Build Process
@@ -520,62 +512,40 @@ Both templates use the same unified workspace configuration:
520
512
  ```yaml
521
513
  # pnpm-workspace.yaml
522
514
  packages:
523
- - "site"
524
- - "foundation"
525
- - "sites/*"
526
- - "foundations/*"
515
+ - 'site'
516
+ - 'foundation'
517
+ - 'sites/*'
518
+ - 'foundations/*'
527
519
  ```
528
520
 
521
+ Also set in `package.json` for npm compatibility.
522
+
529
523
  ```json
530
- // package.json (workspaces field for npm compatibility)
531
524
  {
532
525
  "workspaces": ["site", "foundation", "sites/*", "foundations/*"]
533
526
  }
534
527
  ```
535
528
 
536
- This means **no config changes when evolving your project**.
537
-
538
- ## Evolving Your Project
529
+ This means no config changes when evolving from single to multi-site.
539
530
 
540
- The workspace is pre-configured for growth. Choose your path:
531
+ ## Releasing a Foundation
541
532
 
542
- **Add alongside existing structure:**
533
+ Publish your foundation to npm:
543
534
 
544
535
  ```bash
545
- # Keep site/ and foundation/, add more in sites/ and foundations/
546
- mkdir -p sites/docs
547
- mkdir -p foundations/documentation
536
+ cd foundation
537
+ npm publish
548
538
  ```
549
539
 
550
- **Or migrate to multi-site structure:**
551
-
552
- ```bash
553
- # Move and rename by purpose
554
- mv site sites/marketing
555
- mv foundation foundations/marketing
556
-
557
- # Update package names and workspace dependencies in package.json files
558
- ```
559
-
560
- Both patterns work simultaneously—evolve gradually as needed.
561
-
562
- ## Releasing a Foundation
563
-
564
- Publish your foundation to [uniweb.app](https://uniweb.app) to make it available for your sites:
540
+ Or to [uniweb.app](https://uniweb.app) for use with platform-managed sites:
565
541
 
566
542
  ```bash
567
543
  uniweb login # First time only
568
544
  uniweb build
569
- uniweb publish # Publishes the foundation in the current directory
545
+ uniweb publish
570
546
  ```
571
547
 
572
- Each release creates a new version. Sites link to foundations at runtime and control their own update strategy—automatic, minor-only, patch-only, or pinned to a specific version. You own your foundations and license them to sites—yours or your clients'.
573
-
574
- You can also publish to npm:
575
-
576
- ```bash
577
- npm publish
578
- ```
548
+ Sites control their own update strategy—automatic, minor-only, patch-only, or pinned to a specific version.
579
549
 
580
550
  ## FAQ
581
551
 
@@ -585,19 +555,23 @@ MDX blends markdown and JSX—content authors write code. Uniweb keeps them sepa
585
555
 
586
556
  **How is this different from Astro?**
587
557
 
588
- Astro is a static site generator. Uniweb is a content architecture that works with any deployment (static, SSR, or platform-managed). The Foundation model means components are reusable across sites and ready for visual editing.
558
+ Astro is a static site generator. Uniweb is a component content architecture that works with any deployment (static, SSR, or platform-managed). The foundation model means components are portable across sites and ready for integration with visual editors.
589
559
 
590
560
  **Do I need uniweb.app?**
591
561
 
592
562
  No. Local markdown files work great for developer-managed sites. The platform adds dynamic content, visual editing, and team collaboration when you need it.
593
563
 
564
+ **Can I use an existing component library?**
565
+
566
+ Yes. Foundations are standard React. Import any library into your foundation components. The `{ content, params }` interface only applies to exposed components—internal components use regular props.
567
+
594
568
  **Is this SEO-friendly?**
595
569
 
596
- Yes. Content is pre-embedded in the initial HTML—no fetch waterfalls, no layout shifts, instant interaction. Meta tags are generated per page for proper social sharing previews. SSG and SSR are also supported.
570
+ Yes. Content is pre-embedded in the initial HTML—no fetch waterfalls, no layout shifts. Meta tags are generated per page. SSG is supported by default.
597
571
 
598
572
  **What about dynamic routes?**
599
573
 
600
- Pages can define data sources that auto-generate subroutes. A `/blog` page can have an index (listing) and a `[slug]` template that renders each post. No manual folders for every entry.
574
+ Pages can define data sources that auto-generate subroutes. A `/blog` page can have an index and a `[slug]` template that renders each post.
601
575
 
602
576
  ## Related Packages
603
577
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uniweb",
3
- "version": "0.2.40",
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,8 +37,8 @@
37
37
  "js-yaml": "^4.1.0",
38
38
  "prompts": "^2.4.2",
39
39
  "tar": "^7.0.0",
40
- "@uniweb/build": "0.1.21",
41
- "@uniweb/core": "0.1.9",
40
+ "@uniweb/build": "0.1.23",
41
+ "@uniweb/core": "0.1.10",
42
42
  "@uniweb/kit": "0.1.5",
43
43
  "@uniweb/runtime": "0.2.11"
44
44
  }
@@ -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}}