uniweb 0.7.1 → 0.7.2
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 +64 -17
- package/package.json +4 -3
- package/src/commands/add.js +563 -0
- package/src/commands/build.js +49 -6
- package/src/commands/doctor.js +181 -2
- package/src/index.js +273 -131
- package/src/templates/index.js +0 -94
- package/src/templates/processor.js +10 -87
- package/src/templates/resolver.js +3 -3
- package/src/templates/validator.js +59 -17
- package/src/utils/config.js +229 -0
- package/src/utils/scaffold.js +175 -0
- package/templates/{single/foundation → foundation}/package.json.hbs +2 -2
- package/templates/foundation/src/foundation.js.hbs +7 -0
- package/templates/foundation/src/sections/.gitkeep +0 -0
- package/templates/{multi/sites/main → site}/package.json.hbs +2 -2
- package/templates/site/site.yml.hbs +10 -0
- package/templates/site/theme.yml +1 -0
- package/templates/{_shared → workspace}/package.json.hbs +3 -9
- package/templates/workspace/pnpm-workspace.yaml.hbs +4 -0
- package/templates/_shared/pnpm-workspace.yaml +0 -5
- package/templates/multi/README.md.hbs +0 -85
- package/templates/multi/foundations/default/package.json.hbs +0 -38
- package/templates/multi/foundations/default/src/foundation.js +0 -41
- package/templates/multi/package.json.hbs +0 -26
- package/templates/multi/sites/main/pages/home/1-welcome.md.hbs +0 -14
- package/templates/multi/sites/main/site.yml.hbs +0 -12
- package/templates/multi/sites/main/vite.config.js +0 -7
- package/templates/multi/template/.vscode/settings.json +0 -6
- package/templates/multi/template.json +0 -5
- package/templates/single/foundation/src/sections/Section/index.jsx +0 -121
- package/templates/single/foundation/src/sections/Section/meta.js +0 -61
- package/templates/single/foundation/src/styles.css +0 -5
- package/templates/single/foundation/vite.config.js +0 -3
- package/templates/single/site/index.html.hbs +0 -13
- package/templates/single/site/main.js +0 -7
- package/templates/single/site/package.json.hbs +0 -27
- package/templates/single/site/pages/about/1-about.md.hbs +0 -13
- package/templates/single/site/pages/about/page.yml +0 -2
- package/templates/single/site/pages/home/page.yml +0 -2
- package/templates/single/site/public/favicon.svg +0 -7
- package/templates/single/site/site.yml.hbs +0 -10
- package/templates/single/template.json +0 -10
- /package/{templates/single → starter}/foundation/src/foundation.js +0 -0
- /package/{templates/multi/foundations/default → starter/foundation}/src/sections/Section/index.jsx +0 -0
- /package/{templates/multi/foundations/default → starter/foundation}/src/sections/Section/meta.js +0 -0
- /package/{templates/multi/sites/main → starter/site}/pages/about/1-about.md.hbs +0 -0
- /package/{templates/multi/sites/main → starter/site}/pages/about/page.yml +0 -0
- /package/{templates/single → starter}/site/pages/home/1-welcome.md.hbs +0 -0
- /package/{templates/multi/sites/main → starter/site}/pages/home/page.yml +0 -0
- /package/templates/{multi/foundations/default → foundation}/src/styles.css +0 -0
- /package/templates/{multi/foundations/default → foundation}/vite.config.js +0 -0
- /package/templates/{multi/sites/main → site}/index.html.hbs +0 -0
- /package/templates/{multi/sites/main → site}/main.js +0 -0
- /package/templates/{multi/sites/main → site}/public/favicon.svg +0 -0
- /package/templates/{single/site → site}/vite.config.js +0 -0
- /package/templates/{_shared → workspace}/AGENTS.md.hbs +0 -0
- /package/templates/{single → workspace}/README.md.hbs +0 -0
- /package/templates/{single/template/.vscode → workspace/_vscode}/settings.json +0 -0
package/src/commands/doctor.js
CHANGED
|
@@ -34,7 +34,52 @@ function isSite(dir) {
|
|
|
34
34
|
* Check if a directory is a foundation
|
|
35
35
|
*/
|
|
36
36
|
function isFoundation(dir) {
|
|
37
|
-
|
|
37
|
+
// Primary: has foundation.js config
|
|
38
|
+
if (existsSync(join(dir, 'src', 'foundation.js'))) return true
|
|
39
|
+
// Fallback: has src/sections/
|
|
40
|
+
if (existsSync(join(dir, 'src', 'sections'))) return true
|
|
41
|
+
// Legacy fallback: has src/components/
|
|
42
|
+
if (existsSync(join(dir, 'src', 'components'))) return true
|
|
43
|
+
return false
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Load foundation.js config from a directory
|
|
48
|
+
* Returns the default export, or null if not found/loadable
|
|
49
|
+
*/
|
|
50
|
+
function loadFoundationJs(dir) {
|
|
51
|
+
const filePath = join(dir, 'src', 'foundation.js')
|
|
52
|
+
if (!existsSync(filePath)) return null
|
|
53
|
+
try {
|
|
54
|
+
const content = readFileSync(filePath, 'utf8')
|
|
55
|
+
// Simple extraction: check for extension: true
|
|
56
|
+
return { extension: /extension\s*:\s*true/.test(content) }
|
|
57
|
+
} catch {
|
|
58
|
+
return null
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Load built schema.json from a directory
|
|
64
|
+
*/
|
|
65
|
+
function loadSchemaJson(dir) {
|
|
66
|
+
const schemaPath = join(dir, 'dist', 'schema.json')
|
|
67
|
+
if (!existsSync(schemaPath)) return null
|
|
68
|
+
try {
|
|
69
|
+
return JSON.parse(readFileSync(schemaPath, 'utf8'))
|
|
70
|
+
} catch {
|
|
71
|
+
return null
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Check if a foundation is an extension (via schema.json or foundation.js)
|
|
77
|
+
*/
|
|
78
|
+
function isExtensionPackage(dir) {
|
|
79
|
+
const schema = loadSchemaJson(dir)
|
|
80
|
+
if (schema?._self?.role === 'extension') return true
|
|
81
|
+
const config = loadFoundationJs(dir)
|
|
82
|
+
return config?.extension === true
|
|
38
83
|
}
|
|
39
84
|
|
|
40
85
|
/**
|
|
@@ -85,7 +130,8 @@ export async function doctor(args = []) {
|
|
|
85
130
|
}
|
|
86
131
|
} else if (isFoundation(projectDir)) {
|
|
87
132
|
workspaceDir = dirname(projectDir)
|
|
88
|
-
|
|
133
|
+
const parentName = basename(workspaceDir)
|
|
134
|
+
if (parentName === 'foundations' || parentName === 'extensions') {
|
|
89
135
|
workspaceDir = dirname(workspaceDir)
|
|
90
136
|
}
|
|
91
137
|
}
|
|
@@ -140,6 +186,31 @@ export async function doctor(args = []) {
|
|
|
140
186
|
}
|
|
141
187
|
}
|
|
142
188
|
|
|
189
|
+
// Discover extensions
|
|
190
|
+
const extensions = []
|
|
191
|
+
|
|
192
|
+
const extensionsDir = join(workspaceDir, 'extensions')
|
|
193
|
+
if (existsSync(extensionsDir)) {
|
|
194
|
+
try {
|
|
195
|
+
const entries = readdirSync(extensionsDir, { withFileTypes: true })
|
|
196
|
+
for (const entry of entries) {
|
|
197
|
+
if (entry.isDirectory()) {
|
|
198
|
+
const extensionPath = join(extensionsDir, entry.name)
|
|
199
|
+
if (isFoundation(extensionPath)) {
|
|
200
|
+
const pkg = loadPackageJson(extensionPath)
|
|
201
|
+
extensions.push({
|
|
202
|
+
path: extensionPath,
|
|
203
|
+
name: pkg?.name || entry.name,
|
|
204
|
+
folderName: entry.name
|
|
205
|
+
})
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
} catch {
|
|
210
|
+
// Ignore errors
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
143
214
|
if (foundations.length === 0) {
|
|
144
215
|
warn('No foundations found')
|
|
145
216
|
} else {
|
|
@@ -150,6 +221,15 @@ export async function doctor(args = []) {
|
|
|
150
221
|
}
|
|
151
222
|
}
|
|
152
223
|
|
|
224
|
+
if (extensions.length > 0) {
|
|
225
|
+
log('')
|
|
226
|
+
success(`Found ${extensions.length} extension(s):`)
|
|
227
|
+
for (const e of extensions) {
|
|
228
|
+
const nameMismatch = e.name !== e.folderName ? ` ${colors.dim}(folder: ${e.folderName}/)${colors.reset}` : ''
|
|
229
|
+
log(` • ${e.name}${nameMismatch}`)
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
153
233
|
// Find all sites
|
|
154
234
|
const sites = []
|
|
155
235
|
|
|
@@ -305,6 +385,105 @@ export async function doctor(args = []) {
|
|
|
305
385
|
}
|
|
306
386
|
}
|
|
307
387
|
|
|
388
|
+
// Check extensions
|
|
389
|
+
for (const ext of extensions) {
|
|
390
|
+
log('')
|
|
391
|
+
info(`Checking extension: ${ext.name}`)
|
|
392
|
+
|
|
393
|
+
// Check if it declares extension: true
|
|
394
|
+
if (!isExtensionPackage(ext.path)) {
|
|
395
|
+
issues.push({
|
|
396
|
+
type: 'warn',
|
|
397
|
+
message: `Extension "${ext.name}" in extensions/ doesn't declare extension: true in foundation.js`
|
|
398
|
+
})
|
|
399
|
+
warn(`Missing extension identity`)
|
|
400
|
+
log(` ${colors.dim}Add to src/foundation.js:${colors.reset}`)
|
|
401
|
+
log(` export default { extension: true }`)
|
|
402
|
+
} else {
|
|
403
|
+
success(`Extension identity: extension: true`)
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// Check for vars or layouts
|
|
407
|
+
const config = loadFoundationJs(ext.path)
|
|
408
|
+
const schema = loadSchemaJson(ext.path)
|
|
409
|
+
|
|
410
|
+
if (schema?._self?.vars && Object.keys(schema._self.vars).length > 0) {
|
|
411
|
+
issues.push({
|
|
412
|
+
type: 'warn',
|
|
413
|
+
message: `Extension "${ext.name}" declares theme variables (vars). Extensions don't define theme variables.`
|
|
414
|
+
})
|
|
415
|
+
warn(`Extension declares vars — these won't take effect`)
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
if (schema?._layouts && Object.keys(schema._layouts).length > 0) {
|
|
419
|
+
issues.push({
|
|
420
|
+
type: 'warn',
|
|
421
|
+
message: `Extension "${ext.name}" provides layouts. Extensions don't provide layouts.`
|
|
422
|
+
})
|
|
423
|
+
warn(`Extension provides layouts — these won't take effect`)
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// Check if built
|
|
427
|
+
const extensionDist = join(ext.path, 'dist', 'foundation.js')
|
|
428
|
+
if (!existsSync(extensionDist)) {
|
|
429
|
+
issues.push({
|
|
430
|
+
type: 'warn',
|
|
431
|
+
message: `Extension not built: ${ext.name}`
|
|
432
|
+
})
|
|
433
|
+
warn(`Extension not built yet`)
|
|
434
|
+
log(` ${colors.dim}Run: pnpm --filter ${ext.name} build${colors.reset}`)
|
|
435
|
+
} else {
|
|
436
|
+
success(`Extension built: dist/foundation.js exists`)
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// Check if any foundation with extension: true is wired as a primary foundation
|
|
441
|
+
for (const f of foundations) {
|
|
442
|
+
if (isExtensionPackage(f.path)) {
|
|
443
|
+
const sitesUsingAsPrimary = sites.filter(s => {
|
|
444
|
+
const siteYml = loadSiteYml(s.path)
|
|
445
|
+
return siteYml?.foundation === f.name
|
|
446
|
+
})
|
|
447
|
+
for (const site of sitesUsingAsPrimary) {
|
|
448
|
+
issues.push({
|
|
449
|
+
type: 'warn',
|
|
450
|
+
site: site.name,
|
|
451
|
+
message: `Foundation "${f.name}" declares extension: true but is wired as the primary foundation. It should be in extensions: instead.`
|
|
452
|
+
})
|
|
453
|
+
warn(`"${f.name}" is an extension but used as primary foundation in site "${site.name}"`)
|
|
454
|
+
log(` ${colors.dim}Move it to extensions: in site.yml instead of foundation:${colors.reset}`)
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// Validate extension URLs in site.yml
|
|
460
|
+
for (const site of sites) {
|
|
461
|
+
const siteYml = loadSiteYml(site.path)
|
|
462
|
+
if (!siteYml?.extensions || !Array.isArray(siteYml.extensions)) continue
|
|
463
|
+
|
|
464
|
+
for (const extUrl of siteYml.extensions) {
|
|
465
|
+
if (typeof extUrl !== 'string') continue
|
|
466
|
+
|
|
467
|
+
// Skip remote URLs — can't validate those
|
|
468
|
+
if (extUrl.startsWith('http://') || extUrl.startsWith('https://')) continue
|
|
469
|
+
|
|
470
|
+
// Local extension URL: check if it maps to a known extension
|
|
471
|
+
// URLs like /effects/foundation.js or /extensions/effects/foundation.js
|
|
472
|
+
const urlParts = extUrl.replace(/^\//, '').split('/')
|
|
473
|
+
const extName = urlParts.length >= 2 ? urlParts[urlParts.length - 2] : urlParts[0]
|
|
474
|
+
|
|
475
|
+
// Check if a matching extension exists and is built
|
|
476
|
+
const matchingExt = extensions.find(e => e.folderName === extName || e.name === extName)
|
|
477
|
+
if (matchingExt && !existsSync(join(matchingExt.path, 'dist', 'foundation.js'))) {
|
|
478
|
+
issues.push({
|
|
479
|
+
type: 'warn',
|
|
480
|
+
site: site.name,
|
|
481
|
+
message: `Extension "${extName}" referenced in site.yml is not built`
|
|
482
|
+
})
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
308
487
|
// Summary
|
|
309
488
|
log('')
|
|
310
489
|
log('─'.repeat(50))
|