uniweb 0.2.15 → 0.2.19
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 -35
- package/package.json +4 -3
- package/src/partials/search-docs.hbs +110 -0
- package/src/templates/processor.js +44 -1
- package/templates/_shared/CLAUDE.md.hbs +146 -0
- package/templates/_shared/package.json.hbs +23 -0
- package/templates/_shared/pnpm-workspace.yaml +5 -0
- package/templates/multi/CLAUDE.md.hbs +100 -0
- package/templates/multi/README.md.hbs +96 -0
- package/templates/multi/foundations/default/package.json.hbs +37 -0
- package/templates/multi/foundations/default/src/components/Section/index.jsx +121 -0
- package/templates/multi/foundations/default/src/components/Section/meta.js +66 -0
- package/templates/multi/foundations/default/src/entry-runtime.js +3 -0
- package/templates/multi/foundations/default/src/index.js +32 -0
- package/templates/multi/foundations/default/src/meta.js.hbs +6 -0
- package/templates/multi/foundations/default/src/styles.css +11 -0
- package/templates/multi/foundations/default/vite.config.js +3 -0
- package/templates/multi/package.json.hbs +25 -0
- package/templates/multi/pnpm-workspace.yaml +5 -0
- package/templates/multi/sites/main/index.html.hbs +13 -0
- package/templates/multi/sites/main/package.json.hbs +27 -0
- package/templates/multi/sites/main/pages/home/1-welcome.md.hbs +14 -0
- package/templates/multi/sites/main/pages/home/page.yml +2 -0
- package/templates/multi/sites/main/site.yml.hbs +9 -0
- package/templates/multi/sites/main/src/main.jsx +1 -0
- package/templates/multi/sites/main/vite.config.js +7 -0
- package/templates/multi/template.json +4 -0
- package/templates/single/README.md.hbs +101 -0
- package/templates/single/foundation/package.json.hbs +37 -0
- package/templates/single/foundation/src/components/Section/index.jsx +121 -0
- package/templates/single/foundation/src/components/Section/meta.js +66 -0
- package/templates/single/foundation/src/entry-runtime.js +3 -0
- package/templates/single/foundation/src/index.js +35 -0
- package/templates/single/foundation/src/meta.js.hbs +6 -0
- package/templates/single/foundation/src/styles.css +11 -0
- package/templates/single/foundation/vite.config.js +3 -0
- package/templates/single/site/index.html.hbs +19 -0
- package/templates/single/site/package.json.hbs +27 -0
- package/templates/single/site/pages/home/1-welcome.md.hbs +14 -0
- package/templates/single/site/pages/home/page.yml +2 -0
- package/templates/single/site/site.yml.hbs +7 -0
- package/templates/single/site/src/main.jsx +1 -0
- package/templates/single/site/vite.config.js +3 -0
- package/templates/single/template.json +10 -0
- package/templates/template/README.md.hbs +65 -0
- package/templates/template/package.json.hbs +33 -0
- package/templates/template/template/CLAUDE.md.hbs +70 -0
- package/templates/template/template/foundation/package.json.hbs +37 -0
- package/templates/template/template/foundation/src/components/Features/index.jsx +75 -0
- package/templates/template/template/foundation/src/components/Features/meta.js +41 -0
- package/templates/template/template/foundation/src/components/Hero/index.jsx +89 -0
- package/templates/template/template/foundation/src/components/Hero/meta.js +47 -0
- package/templates/template/template/foundation/src/entry-runtime.js +3 -0
- package/templates/template/template/foundation/src/index.js +36 -0
- package/templates/template/template/foundation/src/meta.js.hbs +6 -0
- package/templates/template/template/foundation/src/styles.css +12 -0
- package/templates/template/template/foundation/vite.config.js +3 -0
- package/templates/template/template/package.json.hbs +14 -0
- package/templates/template/template/pnpm-workspace.yaml +3 -0
- package/templates/template/template/site/index.html.hbs +13 -0
- package/templates/template/template/site/package.json.hbs +27 -0
- package/templates/template/template/site/pages/home/1-hero.md.hbs +16 -0
- package/templates/template/template/site/pages/home/2-features.md +21 -0
- package/templates/template/template/site/pages/home/page.yml +2 -0
- package/templates/template/template/site/site.yml.hbs +8 -0
- package/templates/template/template/site/src/main.jsx +1 -0
- package/templates/template/template/site/vite.config.js +3 -0
- package/templates/template/template/template.json.hbs +7 -0
- package/templates/template/template.json +4 -0
package/README.md
CHANGED
|
@@ -31,13 +31,19 @@ my-project/
|
|
|
31
31
|
│ │ └── 1-hero.md # Section content
|
|
32
32
|
│ ├── locales/ # i18n (mirrors pages/)
|
|
33
33
|
│ │ └── es/
|
|
34
|
+
│ ├── src/
|
|
35
|
+
│ │ └── main.jsx # 1-line entry point
|
|
36
|
+
│ ├── vite.config.js # 3-line config
|
|
34
37
|
│ └── public/ # Static assets
|
|
35
38
|
│
|
|
36
39
|
└── foundation/ # Your components
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
40
|
+
├── src/
|
|
41
|
+
│ └── components/
|
|
42
|
+
│ └── Hero/
|
|
43
|
+
│ ├── index.jsx
|
|
44
|
+
│ └── meta.js
|
|
45
|
+
├── vite.config.js # 3-line config
|
|
46
|
+
└── dist/ # Built output
|
|
41
47
|
```
|
|
42
48
|
|
|
43
49
|
**Pages are folders.** Create `pages/about/` with markdown files inside → visit `/about`. That's the whole routing model.
|
|
@@ -98,7 +104,24 @@ export function Hero({ content, params }) {
|
|
|
98
104
|
|
|
99
105
|
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.
|
|
100
106
|
|
|
101
|
-
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.
|
|
107
|
+
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. Configuration is minimal—both site and foundation use 3-line Vite configs:
|
|
108
|
+
|
|
109
|
+
```javascript
|
|
110
|
+
// site/vite.config.js
|
|
111
|
+
import { defineSiteConfig } from '@uniweb/build/site'
|
|
112
|
+
export default defineSiteConfig()
|
|
113
|
+
|
|
114
|
+
// foundation/vite.config.js
|
|
115
|
+
import { defineFoundationConfig } from '@uniweb/build'
|
|
116
|
+
export default defineFoundationConfig()
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
The site entry point is equally minimal:
|
|
120
|
+
|
|
121
|
+
```javascript
|
|
122
|
+
// site/src/main.jsx
|
|
123
|
+
import 'virtual:uniweb-site-entry'
|
|
124
|
+
```
|
|
102
125
|
|
|
103
126
|
## The Bigger Picture
|
|
104
127
|
|
|
@@ -122,6 +145,12 @@ npx uniweb@latest <command>
|
|
|
122
145
|
npm install -g uniweb
|
|
123
146
|
```
|
|
124
147
|
|
|
148
|
+
**Requirements:**
|
|
149
|
+
- Node.js 20.19 or later
|
|
150
|
+
- pnpm 9+ (recommended) or npm 10+
|
|
151
|
+
|
|
152
|
+
Projects use Vite 7 and Tailwind CSS v4 by default.
|
|
153
|
+
|
|
125
154
|
## Commands
|
|
126
155
|
|
|
127
156
|
### `create`
|
|
@@ -214,37 +243,35 @@ A minimal workspace with a site and foundation as sibling packages. This is the
|
|
|
214
243
|
|
|
215
244
|
```
|
|
216
245
|
my-project/
|
|
217
|
-
├── package.json # Workspace root (
|
|
246
|
+
├── package.json # Workspace root (npm + pnpm compatible)
|
|
218
247
|
├── pnpm-workspace.yaml # Pre-configured for evolution (see below)
|
|
248
|
+
├── CLAUDE.md # AI assistant instructions
|
|
219
249
|
│
|
|
220
250
|
├── site/ # Site package (content + entry)
|
|
221
251
|
│ ├── package.json
|
|
222
|
-
│ ├── vite.config.js
|
|
252
|
+
│ ├── vite.config.js # 3-line config using defineSiteConfig()
|
|
223
253
|
│ ├── index.html
|
|
224
|
-
│ ├── site.yml
|
|
254
|
+
│ ├── site.yml # Site configuration (foundation, title, i18n)
|
|
225
255
|
│ ├── src/
|
|
226
|
-
│ │ └── main.jsx #
|
|
227
|
-
│ ├── pages/ # Content
|
|
256
|
+
│ │ └── main.jsx # 1-line entry: import 'virtual:uniweb-site-entry'
|
|
257
|
+
│ ├── pages/ # Content pages (file-based routing)
|
|
228
258
|
│ │ └── home/
|
|
229
259
|
│ │ ├── page.yml
|
|
230
260
|
│ │ └── 1-hero.md
|
|
231
|
-
│ ├── locales/ # i18n (mirrors pages/)
|
|
232
|
-
│ │ └── es/
|
|
233
|
-
│ │ └── home/
|
|
234
261
|
│ └── public/ # Static assets
|
|
235
262
|
│
|
|
236
263
|
└── foundation/ # Foundation package (components)
|
|
237
264
|
├── package.json
|
|
238
|
-
├── vite.config.js
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
265
|
+
├── vite.config.js # 3-line config using defineFoundationConfig()
|
|
266
|
+
└── src/
|
|
267
|
+
├── index.js # Component exports
|
|
268
|
+
├── entry-runtime.js # Runtime entry (imports styles + index)
|
|
269
|
+
├── styles.css # Tailwind CSS v4
|
|
270
|
+
├── meta.js # Foundation metadata
|
|
271
|
+
└── components/
|
|
272
|
+
└── Section/
|
|
273
|
+
├── index.jsx
|
|
274
|
+
└── meta.js
|
|
248
275
|
```
|
|
249
276
|
|
|
250
277
|
**Key characteristics:**
|
|
@@ -260,23 +287,24 @@ A monorepo for multi-site or multi-foundation development.
|
|
|
260
287
|
|
|
261
288
|
```
|
|
262
289
|
my-workspace/
|
|
263
|
-
├── package.json # Workspace root (
|
|
290
|
+
├── package.json # Workspace root (npm + pnpm compatible)
|
|
264
291
|
├── pnpm-workspace.yaml # Same config as single template
|
|
292
|
+
├── CLAUDE.md # AI assistant instructions
|
|
265
293
|
│
|
|
266
294
|
├── sites/
|
|
267
295
|
│ ├── marketing/ # Main marketing site
|
|
268
296
|
│ │ ├── package.json
|
|
297
|
+
│ │ ├── vite.config.js # 3-line config
|
|
269
298
|
│ │ ├── site.yml
|
|
270
|
-
│ │ ├── src/
|
|
271
|
-
│ │
|
|
272
|
-
│ │ └── ...
|
|
299
|
+
│ │ ├── src/main.jsx # 1-line entry
|
|
300
|
+
│ │ └── pages/
|
|
273
301
|
│ └── docs/ # Documentation site
|
|
274
302
|
│
|
|
275
303
|
└── foundations/
|
|
276
304
|
├── marketing/ # Marketing foundation
|
|
277
305
|
│ ├── package.json
|
|
278
|
-
│ ├──
|
|
279
|
-
│ └──
|
|
306
|
+
│ ├── vite.config.js # 3-line config
|
|
307
|
+
│ └── src/components/
|
|
280
308
|
└── documentation/ # Documentation foundation
|
|
281
309
|
```
|
|
282
310
|
|
|
@@ -298,15 +326,39 @@ A complete marketing site with landing page components:
|
|
|
298
326
|
uniweb create my-site --template marketing
|
|
299
327
|
```
|
|
300
328
|
|
|
301
|
-
**Includes:**
|
|
302
|
-
- **Hero** — Gradient/light/dark banner with CTAs
|
|
303
|
-
- **Features** — Grid layout with icons
|
|
304
|
-
- **Pricing** — Tiered pricing tables
|
|
305
|
-
- **Testimonials** — Customer quotes
|
|
306
|
-
- **CTA** — Call-to-action sections
|
|
329
|
+
**Includes:** Hero, Features, Pricing, Testimonials, CTA, FAQ, Stats, LogoCloud, Video, Gallery, Team
|
|
307
330
|
|
|
308
331
|
Perfect for product launches, SaaS websites, and business landing pages.
|
|
309
332
|
|
|
333
|
+
**Tailwind v3 variant:**
|
|
334
|
+
```bash
|
|
335
|
+
uniweb create my-site --template marketing --variant tailwind3
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
#### Academic
|
|
339
|
+
|
|
340
|
+
A professional academic site for researchers, labs, and departments:
|
|
341
|
+
|
|
342
|
+
```bash
|
|
343
|
+
uniweb create my-site --template academic
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
**Includes:** ProfileHero, PublicationList, ResearchAreas, TeamGrid, Timeline, ContactCard, Navbar, Footer
|
|
347
|
+
|
|
348
|
+
Perfect for researcher portfolios, lab websites, and academic department sites.
|
|
349
|
+
|
|
350
|
+
#### Docs
|
|
351
|
+
|
|
352
|
+
A documentation site with navigation levels:
|
|
353
|
+
|
|
354
|
+
```bash
|
|
355
|
+
uniweb create my-site --template docs
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
**Includes:** Header, LeftPanel, DocSection, CodeBlock, Footer
|
|
359
|
+
|
|
360
|
+
Perfect for technical documentation, guides, and API references.
|
|
361
|
+
|
|
310
362
|
### External Templates
|
|
311
363
|
|
|
312
364
|
You can use templates from npm or GitHub:
|
|
@@ -347,6 +399,52 @@ cd foundation && pnpm add embla-carousel
|
|
|
347
399
|
# No path gymnastics needed
|
|
348
400
|
```
|
|
349
401
|
|
|
402
|
+
## Configuration
|
|
403
|
+
|
|
404
|
+
### Site Configuration
|
|
405
|
+
|
|
406
|
+
The `defineSiteConfig()` function handles all Vite configuration for sites:
|
|
407
|
+
|
|
408
|
+
```javascript
|
|
409
|
+
import { defineSiteConfig } from '@uniweb/build/site'
|
|
410
|
+
|
|
411
|
+
export default defineSiteConfig({
|
|
412
|
+
// All options are optional
|
|
413
|
+
tailwind: true, // Enable Tailwind CSS v4 (default: true)
|
|
414
|
+
plugins: [], // Additional Vite plugins
|
|
415
|
+
// ...any other Vite config options
|
|
416
|
+
})
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
### Foundation Configuration
|
|
420
|
+
|
|
421
|
+
The `defineFoundationConfig()` function handles all Vite configuration for foundations:
|
|
422
|
+
|
|
423
|
+
```javascript
|
|
424
|
+
import { defineFoundationConfig } from '@uniweb/build'
|
|
425
|
+
|
|
426
|
+
export default defineFoundationConfig({
|
|
427
|
+
// All options are optional
|
|
428
|
+
entry: 'src/entry-runtime.js', // Entry point path
|
|
429
|
+
fileName: 'foundation', // Output file name
|
|
430
|
+
externals: [], // Additional packages to externalize
|
|
431
|
+
includeDefaultExternals: true, // Include react, @uniweb/core, etc.
|
|
432
|
+
tailwind: true, // Enable Tailwind CSS v4 Vite plugin
|
|
433
|
+
sourcemap: true, // Generate sourcemaps
|
|
434
|
+
plugins: [], // Additional Vite plugins
|
|
435
|
+
build: {}, // Additional Vite build options
|
|
436
|
+
// ...any other Vite config options
|
|
437
|
+
})
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
For Tailwind CSS v3 projects, set `tailwind: false` and use PostCSS:
|
|
441
|
+
|
|
442
|
+
```javascript
|
|
443
|
+
export default defineFoundationConfig({
|
|
444
|
+
tailwind: false // Uses PostCSS instead of Vite plugin
|
|
445
|
+
})
|
|
446
|
+
```
|
|
447
|
+
|
|
350
448
|
## Foundation Build Process
|
|
351
449
|
|
|
352
450
|
When you run `uniweb build` on a foundation:
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "uniweb",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.19",
|
|
4
4
|
"description": "Create structured Vite + React sites with content/code separation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"uniweb": "src/index.js"
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
|
-
"src"
|
|
10
|
+
"src",
|
|
11
|
+
"templates"
|
|
11
12
|
],
|
|
12
13
|
"keywords": [
|
|
13
14
|
"uniweb",
|
|
@@ -35,6 +36,6 @@
|
|
|
35
36
|
"js-yaml": "^4.1.0",
|
|
36
37
|
"prompts": "^2.4.2",
|
|
37
38
|
"tar": "^7.0.0",
|
|
38
|
-
"@uniweb/build": "0.1.
|
|
39
|
+
"@uniweb/build": "0.1.9"
|
|
39
40
|
}
|
|
40
41
|
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
## Search Functionality
|
|
2
|
+
|
|
3
|
+
Uniweb sites support full-text search out of the box. Search indexes are generated automatically at build time.
|
|
4
|
+
|
|
5
|
+
### Enabling Search in Your Foundation
|
|
6
|
+
|
|
7
|
+
To add search to your site, your foundation needs the **Fuse.js** library:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
cd foundation
|
|
11
|
+
pnpm add fuse.js
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Then use the search helpers from `@uniweb/kit`:
|
|
15
|
+
|
|
16
|
+
```jsx
|
|
17
|
+
import { useSearch } from '@uniweb/kit/search'
|
|
18
|
+
|
|
19
|
+
function SearchComponent({ website }) {
|
|
20
|
+
const { query, results, isLoading, clear } = useSearch(website)
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<div>
|
|
24
|
+
<input
|
|
25
|
+
type="search"
|
|
26
|
+
placeholder="Search..."
|
|
27
|
+
onChange={e => query(e.target.value)}
|
|
28
|
+
/>
|
|
29
|
+
{isLoading && <span>Searching...</span>}
|
|
30
|
+
<ul>
|
|
31
|
+
{results.map(result => (
|
|
32
|
+
<li key={result.id}>
|
|
33
|
+
<a href={result.href}>{result.title}</a>
|
|
34
|
+
<p dangerouslySetInnerHTML=\{{ __html: result.snippetHtml }} />
|
|
35
|
+
</li>
|
|
36
|
+
))}
|
|
37
|
+
</ul>
|
|
38
|
+
</div>
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Search Configuration
|
|
44
|
+
|
|
45
|
+
Search is enabled by default. To customize, add to `site/site.yml`:
|
|
46
|
+
|
|
47
|
+
```yaml
|
|
48
|
+
search:
|
|
49
|
+
enabled: true
|
|
50
|
+
exclude:
|
|
51
|
+
routes: [/admin, /private] # Don't index these routes
|
|
52
|
+
components: [Debug] # Don't index these component types
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### How It Works
|
|
56
|
+
|
|
57
|
+
1. **Build time**: Uniweb extracts text from all pages and sections
|
|
58
|
+
2. **Output**: A `search-index.json` file is generated in `dist/`
|
|
59
|
+
3. **Runtime**: Components fetch and search the index client-side
|
|
60
|
+
4. **Multi-locale**: Each locale gets its own search index
|
|
61
|
+
|
|
62
|
+
### Search API
|
|
63
|
+
|
|
64
|
+
The `website` object provides these methods:
|
|
65
|
+
|
|
66
|
+
```javascript
|
|
67
|
+
// Check if search is available
|
|
68
|
+
website.isSearchEnabled() // Returns boolean
|
|
69
|
+
|
|
70
|
+
// Get the URL for the search index
|
|
71
|
+
website.getSearchIndexUrl() // "/search-index.json" or "/es/search-index.json"
|
|
72
|
+
|
|
73
|
+
// Get full search configuration
|
|
74
|
+
website.getSearchConfig() // { enabled, indexUrl, locale, include, exclude }
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Search Result Format
|
|
78
|
+
|
|
79
|
+
Results from `useSearch()` include:
|
|
80
|
+
|
|
81
|
+
| Field | Description |
|
|
82
|
+
|-------|-------------|
|
|
83
|
+
| `id` | Unique result identifier |
|
|
84
|
+
| `type` | `"page"` or `"section"` |
|
|
85
|
+
| `route` | Page route |
|
|
86
|
+
| `href` | Full link including anchor |
|
|
87
|
+
| `title` | Result title |
|
|
88
|
+
| `pageTitle` | Parent page title (for sections) |
|
|
89
|
+
| `snippetHtml` | Excerpt with `<mark>` highlighted matches |
|
|
90
|
+
| `snippetText` | Plain text excerpt |
|
|
91
|
+
|
|
92
|
+
### Without @uniweb/kit
|
|
93
|
+
|
|
94
|
+
If you prefer not to use the kit helpers, you can fetch the index directly:
|
|
95
|
+
|
|
96
|
+
```javascript
|
|
97
|
+
// Fetch the search index
|
|
98
|
+
const response = await fetch(website.getSearchIndexUrl())
|
|
99
|
+
const index = await response.json()
|
|
100
|
+
|
|
101
|
+
// Use with Fuse.js directly
|
|
102
|
+
import Fuse from 'fuse.js'
|
|
103
|
+
const fuse = new Fuse(index.entries, {
|
|
104
|
+
keys: ['title', 'content'],
|
|
105
|
+
threshold: 0.35,
|
|
106
|
+
includeMatches: true
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
const results = fuse.search('your query')
|
|
110
|
+
```
|
|
@@ -3,13 +3,19 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import fs from 'node:fs/promises'
|
|
6
|
-
import { existsSync } from 'node:fs'
|
|
6
|
+
import { existsSync, readdirSync, readFileSync } from 'node:fs'
|
|
7
7
|
import path from 'node:path'
|
|
8
|
+
import { fileURLToPath } from 'node:url'
|
|
8
9
|
import Handlebars from 'handlebars'
|
|
9
10
|
|
|
11
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
12
|
+
|
|
10
13
|
// Cache for compiled templates
|
|
11
14
|
const templateCache = new Map()
|
|
12
15
|
|
|
16
|
+
// Track if partials have been registered
|
|
17
|
+
let partialsRegistered = false
|
|
18
|
+
|
|
13
19
|
// Store for version data (set by registerVersions)
|
|
14
20
|
let versionData = {}
|
|
15
21
|
|
|
@@ -45,6 +51,40 @@ export function clearMissingVersions() {
|
|
|
45
51
|
missingVersions.clear()
|
|
46
52
|
}
|
|
47
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Register Handlebars partials from the partials directory
|
|
56
|
+
* Partials are available as {{> partial-name}} in templates
|
|
57
|
+
*/
|
|
58
|
+
function registerPartials() {
|
|
59
|
+
if (partialsRegistered) return
|
|
60
|
+
|
|
61
|
+
const partialsDir = path.join(__dirname, '..', 'partials')
|
|
62
|
+
|
|
63
|
+
if (!existsSync(partialsDir)) {
|
|
64
|
+
partialsRegistered = true
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
const files = readdirSync(partialsDir)
|
|
70
|
+
|
|
71
|
+
for (const file of files) {
|
|
72
|
+
if (file.endsWith('.hbs') || file.endsWith('.md')) {
|
|
73
|
+
const partialName = file.replace(/\.(hbs|md)$/, '')
|
|
74
|
+
const partialPath = path.join(partialsDir, file)
|
|
75
|
+
const partialContent = readFileSync(partialPath, 'utf8')
|
|
76
|
+
|
|
77
|
+
Handlebars.registerPartial(partialName, partialContent)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
partialsRegistered = true
|
|
82
|
+
} catch (err) {
|
|
83
|
+
console.warn('Warning: Could not register partials:', err.message)
|
|
84
|
+
partialsRegistered = true
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
48
88
|
/**
|
|
49
89
|
* Handlebars helper to get a package version
|
|
50
90
|
* Usage: {{version "@uniweb/build"}} or {{version "build"}}
|
|
@@ -99,6 +139,9 @@ function findUnresolvedPlaceholders(content) {
|
|
|
99
139
|
* Load and compile a Handlebars template with caching
|
|
100
140
|
*/
|
|
101
141
|
async function loadTemplate(templatePath) {
|
|
142
|
+
// Ensure partials are registered before first template load
|
|
143
|
+
registerPartials()
|
|
144
|
+
|
|
102
145
|
if (templateCache.has(templatePath)) {
|
|
103
146
|
return templateCache.get(templatePath)
|
|
104
147
|
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# CLAUDE.md - AI Assistant Instructions
|
|
2
|
+
|
|
3
|
+
This file provides guidance for AI assistants working with this Uniweb project.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
This is a **Uniweb** project - a component web platform that separates content from code.
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
{{projectName}}/
|
|
11
|
+
├── foundation/ # React component library (code)
|
|
12
|
+
├── site/ # Content and pages (markdown)
|
|
13
|
+
├── package.json # Root workspace config
|
|
14
|
+
└── pnpm-workspace.yaml
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Quick Commands
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# Install dependencies
|
|
21
|
+
pnpm install
|
|
22
|
+
|
|
23
|
+
# Start development server (site with foundation)
|
|
24
|
+
pnpm dev
|
|
25
|
+
|
|
26
|
+
# Build foundation (generates dist/foundation.js and schema.json)
|
|
27
|
+
cd foundation && pnpm build
|
|
28
|
+
|
|
29
|
+
# Build site for production (outputs to site/dist/)
|
|
30
|
+
cd site && pnpm build
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Key Files
|
|
34
|
+
|
|
35
|
+
**Site:**
|
|
36
|
+
- `site/vite.config.js` - 3-line config using `defineSiteConfig()` from `@uniweb/build/site`
|
|
37
|
+
- `site/src/main.jsx` - 1-line entry: `import 'virtual:uniweb-site-entry'`
|
|
38
|
+
- `site/site.yml` - Site configuration (foundation path, title, i18n)
|
|
39
|
+
- `site/pages/` - Content pages (file-based routing)
|
|
40
|
+
|
|
41
|
+
**Foundation:**
|
|
42
|
+
- `foundation/vite.config.js` - 3-line config using `defineFoundationConfig()` from `@uniweb/build`
|
|
43
|
+
- `foundation/src/entry-runtime.js` - Runtime entry point (imports styles + components)
|
|
44
|
+
- `foundation/src/index.js` - Component exports
|
|
45
|
+
- `foundation/src/styles.css` - Tailwind CSS v4 theme
|
|
46
|
+
- `foundation/src/components/*/meta.js` - Component metadata and schema
|
|
47
|
+
|
|
48
|
+
## Architecture
|
|
49
|
+
|
|
50
|
+
### Foundation Package (`/foundation`)
|
|
51
|
+
|
|
52
|
+
The **foundation** is a React component library. Each component:
|
|
53
|
+
|
|
54
|
+
1. Receives structured content parsed from markdown
|
|
55
|
+
2. Has configurable parameters (theme, layout, etc.)
|
|
56
|
+
3. Renders the content according to its design
|
|
57
|
+
|
|
58
|
+
**Key directories:**
|
|
59
|
+
- `src/components/*/` - Component implementations
|
|
60
|
+
- `src/components/*/meta.js` - Component metadata and schema
|
|
61
|
+
- `src/styles.css` - Global Tailwind styles
|
|
62
|
+
|
|
63
|
+
### Site Package (`/site`)
|
|
64
|
+
|
|
65
|
+
The **site** contains content written in markdown. The runtime loads the foundation and renders content based on component selections.
|
|
66
|
+
|
|
67
|
+
**Key directories:**
|
|
68
|
+
- `pages/` - Content pages organized in folders
|
|
69
|
+
- `pages/[page-name]/page.yml` - Page metadata
|
|
70
|
+
- `pages/[page-name]/*.md` - Content sections
|
|
71
|
+
|
|
72
|
+
## Content Authoring
|
|
73
|
+
|
|
74
|
+
### Section File Format
|
|
75
|
+
|
|
76
|
+
```markdown
|
|
77
|
+
---
|
|
78
|
+
type: ComponentName
|
|
79
|
+
theme: dark
|
|
80
|
+
layout: center
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
# Main Headline
|
|
84
|
+
|
|
85
|
+
Description paragraph.
|
|
86
|
+
|
|
87
|
+
[Call to Action](#link)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Content Structure
|
|
91
|
+
|
|
92
|
+
The semantic parser extracts content into:
|
|
93
|
+
|
|
94
|
+
- **`content.main.header`**: title, pretitle, subtitle
|
|
95
|
+
- **`content.main.body`**: paragraphs, links, imgs, lists
|
|
96
|
+
- **`content.items`**: Content groups from H3 headings
|
|
97
|
+
|
|
98
|
+
## Component Development
|
|
99
|
+
|
|
100
|
+
### Component Interface
|
|
101
|
+
|
|
102
|
+
```jsx
|
|
103
|
+
function MyComponent({ content, params, block, website }) {
|
|
104
|
+
const { title } = content.main?.header || {}
|
|
105
|
+
const { paragraphs = [] } = content.main?.body || {}
|
|
106
|
+
// ...
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Using @uniweb/kit
|
|
111
|
+
|
|
112
|
+
```jsx
|
|
113
|
+
import { H1, P, Link, cn } from '@uniweb/kit'
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Component Metadata (`meta.js`)
|
|
117
|
+
|
|
118
|
+
```javascript
|
|
119
|
+
export default {
|
|
120
|
+
title: 'Component Name',
|
|
121
|
+
description: 'What the component does',
|
|
122
|
+
category: 'Content',
|
|
123
|
+
elements: { /* content elements */ },
|
|
124
|
+
properties: { /* configurable params */ },
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Tailwind CSS v4
|
|
129
|
+
|
|
130
|
+
This project uses Tailwind CSS v4 with the Vite plugin.
|
|
131
|
+
|
|
132
|
+
**Theme customization** (`foundation/src/styles.css`):
|
|
133
|
+
|
|
134
|
+
```css
|
|
135
|
+
@import "tailwindcss";
|
|
136
|
+
@source "./components/**/*.{js,jsx}";
|
|
137
|
+
@theme {
|
|
138
|
+
--color-primary: #3b82f6;
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Resources
|
|
143
|
+
|
|
144
|
+
- [Uniweb Documentation](https://github.com/uniweb/uniweb)
|
|
145
|
+
- [@uniweb/kit Documentation](https://www.npmjs.com/package/@uniweb/kit)
|
|
146
|
+
- [Tailwind CSS v4](https://tailwindcss.com/docs)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{projectName}}",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "pnpm --filter site dev",
|
|
8
|
+
"build": "pnpm --filter foundation build && pnpm --filter site build",
|
|
9
|
+
"preview": "pnpm --filter site preview"
|
|
10
|
+
},
|
|
11
|
+
"devDependencies": {
|
|
12
|
+
"@types/node": "^22.0.0"
|
|
13
|
+
},
|
|
14
|
+
"workspaces": [
|
|
15
|
+
"site",
|
|
16
|
+
"foundation",
|
|
17
|
+
"sites/*",
|
|
18
|
+
"foundations/*"
|
|
19
|
+
],
|
|
20
|
+
"pnpm": {
|
|
21
|
+
"onlyBuiltDependencies": ["esbuild", "sharp"]
|
|
22
|
+
}
|
|
23
|
+
}
|