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 +133 -159
- package/package.json +3 -3
- package/partials/{claude-md.hbs → agents-md.hbs} +1 -1
- package/partials/ai-assistance.hbs +1 -1
- package/partials/components-docs.hbs +6 -8
- package/src/commands/docs.js +74 -13
- package/src/templates/resolver.js +1 -1
- package/templates/_shared/AGENTS.md.hbs +1 -0
- package/templates/multi/AGENTS.md.hbs +1 -0
- package/templates/multi/README.md.hbs +1 -1
- package/templates/multi/foundations/default/package.json.hbs +1 -2
- package/templates/single/README.md.hbs +1 -1
- package/templates/single/foundation/package.json.hbs +1 -2
- package/templates/single/template.json +1 -1
- package/templates/template/template/{CLAUDE.md.hbs → AGENTS.md.hbs} +1 -1
- package/templates/_shared/CLAUDE.md.hbs +0 -1
- package/templates/multi/CLAUDE.md.hbs +0 -1
package/README.md
CHANGED
|
@@ -1,24 +1,42 @@
|
|
|
1
1
|
# uniweb
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
5
|
+
Create well-structured Vite + React projects with file-based routing, localization, and clean content/code separation out of the box.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## Quick Start
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
npx uniweb@latest create my-
|
|
11
|
-
cd my-
|
|
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
|
-
|
|
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/`** —
|
|
37
|
+
- **`foundation/`** — React components
|
|
20
38
|
|
|
21
|
-
Content authors work in markdown. Component authors work in React. Neither can break the other's work
|
|
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 =
|
|
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 ===
|
|
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
|
|
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
|
-
|
|
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
|
|
119
|
-
|
|
120
|
-
|
|
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
|
|
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
|
|
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
|
|
294
|
+
- **Performance**: First contentful paint is instant
|
|
257
295
|
- **Hosting**: Deploy to any static host (GitHub Pages, Netlify, S3, etc.)
|
|
258
296
|
|
|
259
|
-
|
|
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.
|
|
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
|
|
294
|
-
├── pnpm-workspace.yaml
|
|
295
|
-
├──
|
|
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
|
|
300
|
-
│ ├──
|
|
301
|
-
│ ├──
|
|
302
|
-
│
|
|
303
|
-
|
|
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
|
|
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
|
|
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
|
|
341
|
-
│
|
|
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/ #
|
|
350
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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:
|
|
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
|
-
-
|
|
524
|
-
-
|
|
525
|
-
-
|
|
526
|
-
-
|
|
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
|
|
537
|
-
|
|
538
|
-
## Evolving Your Project
|
|
529
|
+
This means no config changes when evolving from single to multi-site.
|
|
539
530
|
|
|
540
|
-
|
|
531
|
+
## Releasing a Foundation
|
|
541
532
|
|
|
542
|
-
|
|
533
|
+
Publish your foundation to npm:
|
|
543
534
|
|
|
544
535
|
```bash
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
mkdir -p foundations/documentation
|
|
536
|
+
cd foundation
|
|
537
|
+
npm publish
|
|
548
538
|
```
|
|
549
539
|
|
|
550
|
-
|
|
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
|
|
545
|
+
uniweb publish
|
|
570
546
|
```
|
|
571
547
|
|
|
572
|
-
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
41
|
-
"@uniweb/core": "0.1.
|
|
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
|
}
|
|
@@ -3,13 +3,11 @@
|
|
|
3
3
|
Generate up-to-date documentation for all foundation components:
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
-
# From
|
|
7
|
-
pnpm docs
|
|
8
|
-
```
|
|
6
|
+
# From foundation directory
|
|
7
|
+
pnpm uniweb docs
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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.
|
package/src/commands/docs.js
CHANGED
|
@@ -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 {
|
|
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
|
|
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
|
-
|
|
86
|
-
|
|
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
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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(
|
|
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'
|
|
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(
|
|
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 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
|
-
└──
|
|
25
|
+
└── AGENTS.md # AI assistant instructions
|
|
26
26
|
```
|
|
27
27
|
|
|
28
28
|
## Content Authoring
|
|
@@ -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", "
|
|
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 +0,0 @@
|
|
|
1
|
-
{{> claude-md}}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{{> claude-md}}
|