@uniweb/templates 0.1.0 → 0.1.1
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
ADDED
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
# @uniweb/templates
|
|
2
|
+
|
|
3
|
+
Template processing engine and official templates for the Uniweb CLI.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This package provides:
|
|
8
|
+
1. **Template processing engine** - Handlebars-based file processing with variable substitution
|
|
9
|
+
2. **Official templates** - Showcase templates like `marketing`, `docs`, `learning`
|
|
10
|
+
3. **Validation utilities** - Version checking and template structure validation
|
|
11
|
+
|
|
12
|
+
## Usage
|
|
13
|
+
|
|
14
|
+
### From CLI
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# Use an official template
|
|
18
|
+
npx uniweb create my-project --template marketing
|
|
19
|
+
|
|
20
|
+
# Templates are resolved in order:
|
|
21
|
+
# 1. Built-in (single, multi) - in CLI
|
|
22
|
+
# 2. Official (@uniweb/templates) - marketing, docs, learning
|
|
23
|
+
# 3. npm packages - @scope/template-name
|
|
24
|
+
# 4. GitHub repos - github:user/repo
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Programmatic API
|
|
28
|
+
|
|
29
|
+
```javascript
|
|
30
|
+
import { applyBuiltinTemplate, hasTemplate, listBuiltinTemplates } from '@uniweb/templates'
|
|
31
|
+
|
|
32
|
+
// Check if a template exists
|
|
33
|
+
if (hasTemplate('marketing')) {
|
|
34
|
+
// Apply template to a directory
|
|
35
|
+
await applyBuiltinTemplate('marketing', './my-project', {
|
|
36
|
+
projectName: 'my-project',
|
|
37
|
+
year: new Date().getFullYear()
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// List available templates
|
|
42
|
+
const templates = await listBuiltinTemplates()
|
|
43
|
+
// [{ id: 'marketing', name: 'Marketing Starter', ... }]
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Creating Templates
|
|
47
|
+
|
|
48
|
+
### Directory Structure
|
|
49
|
+
|
|
50
|
+
Each template lives in `templates/<name>/` with this structure:
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
templates/
|
|
54
|
+
└── marketing/
|
|
55
|
+
├── template.json # Required: Template metadata
|
|
56
|
+
├── preview.png # Optional: Preview image
|
|
57
|
+
└── template/ # Required: Files to scaffold
|
|
58
|
+
├── package.json.hbs
|
|
59
|
+
├── pnpm-workspace.yaml
|
|
60
|
+
├── .gitignore
|
|
61
|
+
├── README.md.hbs
|
|
62
|
+
├── foundation/
|
|
63
|
+
│ ├── package.json.hbs
|
|
64
|
+
│ ├── src/
|
|
65
|
+
│ │ ├── index.js
|
|
66
|
+
│ │ ├── styles.css
|
|
67
|
+
│ │ └── components/
|
|
68
|
+
│ │ └── Hero/
|
|
69
|
+
│ │ ├── index.jsx
|
|
70
|
+
│ │ └── meta.js
|
|
71
|
+
│ └── ...
|
|
72
|
+
└── site/
|
|
73
|
+
├── package.json.hbs
|
|
74
|
+
├── site.yml.hbs
|
|
75
|
+
├── vite.config.js
|
|
76
|
+
├── src/
|
|
77
|
+
│ └── main.jsx
|
|
78
|
+
└── pages/
|
|
79
|
+
└── home/
|
|
80
|
+
├── page.yml
|
|
81
|
+
└── 1-hero.md
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### template.json
|
|
85
|
+
|
|
86
|
+
Required metadata file at the template root:
|
|
87
|
+
|
|
88
|
+
```json
|
|
89
|
+
{
|
|
90
|
+
"name": "Marketing Starter",
|
|
91
|
+
"description": "A complete marketing site with landing page components",
|
|
92
|
+
"uniweb": ">=0.2.0",
|
|
93
|
+
"preview": "preview.png",
|
|
94
|
+
"tags": ["marketing", "landing-page", "saas"],
|
|
95
|
+
"components": ["Hero", "Features", "Pricing", "Testimonials", "CTA"]
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
| Field | Required | Description |
|
|
100
|
+
|-------|----------|-------------|
|
|
101
|
+
| `name` | Yes | Human-readable template name |
|
|
102
|
+
| `description` | No | Longer description for discovery |
|
|
103
|
+
| `uniweb` | No | Semver range for Uniweb compatibility |
|
|
104
|
+
| `preview` | No | Preview image filename |
|
|
105
|
+
| `tags` | No | Keywords for discovery |
|
|
106
|
+
| `components` | No | List of included components |
|
|
107
|
+
|
|
108
|
+
### Handlebars Processing
|
|
109
|
+
|
|
110
|
+
Files ending in `.hbs` are processed through Handlebars. The `.hbs` extension is removed in the output.
|
|
111
|
+
|
|
112
|
+
**Available variables:**
|
|
113
|
+
- `{{projectName}}` - Project name from CLI
|
|
114
|
+
- `{{year}}` - Current year
|
|
115
|
+
- Custom variables can be passed via the API
|
|
116
|
+
|
|
117
|
+
**Example: package.json.hbs**
|
|
118
|
+
```json
|
|
119
|
+
{
|
|
120
|
+
"name": "{{projectName}}",
|
|
121
|
+
"version": "0.1.0",
|
|
122
|
+
"private": true
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**Supported file types for processing:**
|
|
127
|
+
- `.js`, `.jsx`, `.ts`, `.tsx`, `.mjs`, `.cjs`
|
|
128
|
+
- `.json`, `.yml`, `.yaml`
|
|
129
|
+
- `.md`, `.mdx`
|
|
130
|
+
- `.html`, `.htm`, `.css`, `.scss`
|
|
131
|
+
- `.txt`, `.xml`, `.svg`, `.vue`, `.astro`
|
|
132
|
+
|
|
133
|
+
Binary files (images, fonts, etc.) are copied as-is.
|
|
134
|
+
|
|
135
|
+
### Critical: Package Dependencies
|
|
136
|
+
|
|
137
|
+
**The site package MUST include the foundation as a workspace dependency:**
|
|
138
|
+
|
|
139
|
+
```json
|
|
140
|
+
// site/package.json.hbs
|
|
141
|
+
{
|
|
142
|
+
"name": "site",
|
|
143
|
+
"dependencies": {
|
|
144
|
+
"@uniweb/runtime": "^0.1.0",
|
|
145
|
+
"foundation": "workspace:*"
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
This `"foundation": "workspace:*"` entry is essential because:
|
|
151
|
+
1. pnpm creates a symlink at `site/node_modules/foundation` pointing to the workspace foundation
|
|
152
|
+
2. Vite's `#foundation` alias resolves to the `foundation` module
|
|
153
|
+
3. Without this, the site build will fail with "Could not load foundation"
|
|
154
|
+
|
|
155
|
+
**Use fixed package names:**
|
|
156
|
+
- Foundation: `"name": "foundation"` (not `{{projectName}}-foundation`)
|
|
157
|
+
- Site: `"name": "site"` (not `{{projectName}}-site`)
|
|
158
|
+
|
|
159
|
+
This matches how the CLI's built-in templates work and ensures the workspace linking functions correctly.
|
|
160
|
+
|
|
161
|
+
### Workspace Configuration
|
|
162
|
+
|
|
163
|
+
The root `pnpm-workspace.yaml` should include both packages:
|
|
164
|
+
|
|
165
|
+
```yaml
|
|
166
|
+
packages:
|
|
167
|
+
- 'site'
|
|
168
|
+
- 'foundation'
|
|
169
|
+
- 'sites/*'
|
|
170
|
+
- 'foundations/*'
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Variant Support
|
|
174
|
+
|
|
175
|
+
Templates can include variant-specific directories using the `.variant` suffix:
|
|
176
|
+
|
|
177
|
+
```
|
|
178
|
+
template/
|
|
179
|
+
├── foundation/
|
|
180
|
+
├── foundation.typescript/ # Used when variant='typescript'
|
|
181
|
+
└── site/
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
When applying with `variant: 'typescript'`, the `foundation.typescript/` directory contents will be used instead of `foundation/`.
|
|
185
|
+
|
|
186
|
+
## Validation
|
|
187
|
+
|
|
188
|
+
Templates are validated when applied:
|
|
189
|
+
|
|
190
|
+
1. **Structure check** - `template.json` and `template/` directory must exist
|
|
191
|
+
2. **Required fields** - `name` field is required in template.json
|
|
192
|
+
3. **Version compatibility** - If `uniweb` field is set, checks against current version
|
|
193
|
+
4. **Unresolved placeholders** - Warns if `{{variables}}` remain after processing
|
|
194
|
+
|
|
195
|
+
## Testing Templates
|
|
196
|
+
|
|
197
|
+
Add E2E tests in the main workspace at `tests/e2e/<template>-template.test.js`:
|
|
198
|
+
|
|
199
|
+
```javascript
|
|
200
|
+
import { describe, it, expect, beforeAll, afterAll } from 'vitest'
|
|
201
|
+
import { createTempDir, cleanupTempDir, runCreate, installDependencies, buildProject } from './helpers.js'
|
|
202
|
+
|
|
203
|
+
describe('Marketing Template Build', () => {
|
|
204
|
+
let tempDir, projectDir
|
|
205
|
+
|
|
206
|
+
beforeAll(async () => {
|
|
207
|
+
tempDir = await createTempDir()
|
|
208
|
+
projectDir = join(tempDir, 'test-project')
|
|
209
|
+
await runCreate('test-project', { cwd: tempDir, template: 'marketing' })
|
|
210
|
+
await patchForLocalPackages(projectDir)
|
|
211
|
+
installDependencies(projectDir)
|
|
212
|
+
}, 300000)
|
|
213
|
+
|
|
214
|
+
it('should build foundation successfully', () => {
|
|
215
|
+
buildProject(join(projectDir, 'foundation'))
|
|
216
|
+
expect(existsSync(join(projectDir, 'foundation/dist/foundation.js'))).toBe(true)
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
it('should build site successfully', () => {
|
|
220
|
+
buildProject(join(projectDir, 'site'))
|
|
221
|
+
expect(existsSync(join(projectDir, 'site/dist/index.html'))).toBe(true)
|
|
222
|
+
})
|
|
223
|
+
})
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Key testing points:
|
|
227
|
+
- Project scaffolds correctly with all expected files
|
|
228
|
+
- Foundation builds and produces `schema.json` with component metadata
|
|
229
|
+
- Site builds and produces `site-content.json` with parsed content
|
|
230
|
+
- Content sections are in correct order
|
|
231
|
+
- Handlebars variables are properly substituted
|
|
232
|
+
|
|
233
|
+
## Checklist for New Templates
|
|
234
|
+
|
|
235
|
+
- [ ] Create `templates/<name>/template.json` with required `name` field
|
|
236
|
+
- [ ] Create `templates/<name>/template/` directory structure
|
|
237
|
+
- [ ] Include both `foundation/` and `site/` packages
|
|
238
|
+
- [ ] Add `"foundation": "workspace:*"` to site's dependencies
|
|
239
|
+
- [ ] Use fixed names: `"name": "foundation"` and `"name": "site"`
|
|
240
|
+
- [ ] Include `pnpm-workspace.yaml` with workspace packages
|
|
241
|
+
- [ ] Add `.hbs` extension to files needing variable substitution
|
|
242
|
+
- [ ] Include sample content in `site/pages/`
|
|
243
|
+
- [ ] Create meaningful component metadata in `foundation/src/components/*/meta.js`
|
|
244
|
+
- [ ] Add E2E tests in the main workspace
|
|
245
|
+
- [ ] Test the full flow: create, install, build foundation, build site
|
|
246
|
+
|
|
247
|
+
## API Reference
|
|
248
|
+
|
|
249
|
+
### `applyTemplate(templatePath, targetPath, data, options)`
|
|
250
|
+
|
|
251
|
+
Apply a template from any path.
|
|
252
|
+
|
|
253
|
+
### `applyBuiltinTemplate(name, targetPath, data, options)`
|
|
254
|
+
|
|
255
|
+
Apply one of the official templates by name.
|
|
256
|
+
|
|
257
|
+
### `hasTemplate(name)`
|
|
258
|
+
|
|
259
|
+
Check if an official template exists.
|
|
260
|
+
|
|
261
|
+
### `listBuiltinTemplates()`
|
|
262
|
+
|
|
263
|
+
Get list of all official templates with metadata.
|
|
264
|
+
|
|
265
|
+
### `validateTemplate(templateRoot, options)`
|
|
266
|
+
|
|
267
|
+
Validate a template's structure and compatibility.
|
|
268
|
+
|
|
269
|
+
### `copyTemplateDirectory(sourcePath, targetPath, data, options)`
|
|
270
|
+
|
|
271
|
+
Low-level directory copying with Handlebars processing.
|
|
272
|
+
|
|
273
|
+
## License
|
|
274
|
+
|
|
275
|
+
MIT
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "
|
|
2
|
+
"name": "site",
|
|
3
3
|
"version": "0.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"private": true,
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
"preview": "vite preview"
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@uniweb/runtime": "^0.1.0"
|
|
13
|
+
"@uniweb/runtime": "^0.1.0",
|
|
14
|
+
"foundation": "workspace:*"
|
|
14
15
|
},
|
|
15
16
|
"devDependencies": {
|
|
16
17
|
"@vitejs/plugin-react": "^4.2.1",
|