uniweb 0.46.0 → 0.47.0
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/package.json +6 -6
- package/src/commands/doctor.js +205 -70
- package/src/framework-index.json +8 -8
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "uniweb",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.47.0",
|
|
4
4
|
"description": "Create structured Vite + React sites with content/code separation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -41,15 +41,15 @@
|
|
|
41
41
|
"js-yaml": "^4.1.0",
|
|
42
42
|
"prompts": "^2.4.2",
|
|
43
43
|
"tar": "^7.0.0",
|
|
44
|
-
"@uniweb/
|
|
45
|
-
"@uniweb/kit": "^0.17.1",
|
|
44
|
+
"@uniweb/runtime": "^0.19.0",
|
|
46
45
|
"@uniweb/semantic-parser": "^1.4.0",
|
|
47
|
-
"@uniweb/
|
|
46
|
+
"@uniweb/core": "^0.24.1",
|
|
47
|
+
"@uniweb/kit": "^0.18.0"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
50
|
-
"@uniweb/
|
|
50
|
+
"@uniweb/semantic-parser": "^1.4.0",
|
|
51
51
|
"@uniweb/content-reader": "^1.2.4",
|
|
52
|
-
"@uniweb/
|
|
52
|
+
"@uniweb/build": "^0.43.0"
|
|
53
53
|
},
|
|
54
54
|
"peerDependenciesMeta": {
|
|
55
55
|
"@uniweb/build": {
|
package/src/commands/doctor.js
CHANGED
|
@@ -195,7 +195,7 @@ function sourceMatches(dir, pattern) {
|
|
|
195
195
|
* question is always "did the developer write this".
|
|
196
196
|
*/
|
|
197
197
|
function forEachSourceFile(dir, fn) {
|
|
198
|
-
const roots = ['sections', 'layouts', 'components']
|
|
198
|
+
const roots = ['sections', 'layouts', 'components', 'utils']
|
|
199
199
|
const walk = (d, depth = 0) => {
|
|
200
200
|
if (depth > 6) return
|
|
201
201
|
let entries
|
|
@@ -211,7 +211,7 @@ function forEachSourceFile(dir, fn) {
|
|
|
211
211
|
walk(p, depth + 1)
|
|
212
212
|
} else if (/\.(jsx?|tsx?)$/.test(entry.name)) {
|
|
213
213
|
try {
|
|
214
|
-
fn(readFileSync(p, 'utf8'))
|
|
214
|
+
fn(readFileSync(p, 'utf8'), relative(dir, p))
|
|
215
215
|
} catch {
|
|
216
216
|
// unreadable file — not this check's problem to report
|
|
217
217
|
}
|
|
@@ -221,34 +221,6 @@ function forEachSourceFile(dir, fn) {
|
|
|
221
221
|
for (const r of roots) walk(join(dir, r))
|
|
222
222
|
}
|
|
223
223
|
|
|
224
|
-
/**
|
|
225
|
-
* The host services a foundation's own source actually reaches for.
|
|
226
|
-
*
|
|
227
|
-
* ⚠️ A HINT, NEVER THE ANSWER. This is a regex over source, so it sees only the
|
|
228
|
-
* literal spellings below: `resolveService(website, name)` with a variable name
|
|
229
|
-
* is invisible to it, and so is a service reached through a helper. That makes
|
|
230
|
-
* it usable for "you used this and did not declare it" (a false negative just
|
|
231
|
-
* means no warning) and useless as a source of truth (a false negative would
|
|
232
|
-
* mean a capability silently dropped from what we publish).
|
|
233
|
-
*
|
|
234
|
-
* ⛔ Which is why the declaration is authored and this only checks it. Deriving
|
|
235
|
-
* `uniweb.supports` from a scan would make a missed match into a service the
|
|
236
|
-
* operator cannot buy, with nothing anywhere naming the cause.
|
|
237
|
-
*/
|
|
238
|
-
function servicesUsedInSource(dir) {
|
|
239
|
-
const found = new Set()
|
|
240
|
-
forEachSourceFile(dir, (text) => {
|
|
241
|
-
for (const m of text.matchAll(/resolveService\s*\(\s*[^,()]+,\s*['"`]([\w-]+)['"`]/g)) {
|
|
242
|
-
found.add(m[1])
|
|
243
|
-
}
|
|
244
|
-
// The service-specific readers, which name no service string of their own.
|
|
245
|
-
if (/\bisSearchEnabled\s*\(/.test(text)) found.add('search')
|
|
246
|
-
if (/\buseTracker\s*\(/.test(text)) found.add('tracking')
|
|
247
|
-
if (/from\s+['"`]@uniweb\/api['"`]/.test(text)) found.add('api')
|
|
248
|
-
})
|
|
249
|
-
return found
|
|
250
|
-
}
|
|
251
|
-
|
|
252
224
|
// A fenced data block tagged `form`, in any of the serialization formats the
|
|
253
225
|
// parser accepts for one. Matched on the info string only — the body is the
|
|
254
226
|
// author's business.
|
|
@@ -426,68 +398,201 @@ const CONSENT_NEAR_MISSES = new Set(['require', 'requires', 'required.', 'true',
|
|
|
426
398
|
* options — there is nothing in it to be wrong.
|
|
427
399
|
*/
|
|
428
400
|
/**
|
|
429
|
-
* `uniweb doctor` —
|
|
401
|
+
* `uniweb doctor` — is `package.json` behind what the build derived?
|
|
430
402
|
*
|
|
431
403
|
* A host offers services (`config.services.<name>`); a foundation has to render
|
|
432
404
|
* something against one, or an operator who provisioned it gets nothing and no
|
|
433
|
-
* error. `
|
|
434
|
-
*
|
|
435
|
-
*
|
|
405
|
+
* error. `uniweb.supports` states which ones a foundation honours, and it rides
|
|
406
|
+
* to the registry as `info.supports` (`build/src/uwx/registry-package.js`).
|
|
407
|
+
*
|
|
408
|
+
* ## ⭐ THE BUILD ALREADY ANSWERED THIS — so read its answer, do not re-derive
|
|
436
409
|
*
|
|
437
|
-
*
|
|
410
|
+
* `@uniweb/build` derives the set from the post-tree-shake module graph and
|
|
411
|
+
* writes it to `dist/meta/schema.json`'s `_self.supports`
|
|
412
|
+
* (`build/src/foundation/derive-supports.js`). That value is what publishes,
|
|
413
|
+
* with or without this check, so the foundation is already correct on the wire.
|
|
438
414
|
*
|
|
439
|
-
*
|
|
440
|
-
*
|
|
441
|
-
*
|
|
442
|
-
*
|
|
443
|
-
*
|
|
444
|
-
*
|
|
445
|
-
*
|
|
415
|
+
* ⛔ **What was here before was a regex over `sections/`, `layouts/` and
|
|
416
|
+
* `components/`, and it did not work.** It knew four spellings and
|
|
417
|
+
* `useFormSubmit()` — the official submit gate — was not one, so it was silent
|
|
418
|
+
* on `templates/services`, the template written to demonstrate the feature. Its
|
|
419
|
+
* test could not find that, because the fixture was a source string written to
|
|
420
|
+
* match the regex. ⇒ **Do not reintroduce a second, weaker derivation here.**
|
|
421
|
+
* There is one, it runs in the build, and this reads it.
|
|
446
422
|
*
|
|
447
|
-
* ##
|
|
423
|
+
* ## So what is left for `doctor` to say
|
|
448
424
|
*
|
|
449
|
-
*
|
|
450
|
-
*
|
|
451
|
-
*
|
|
452
|
-
*
|
|
453
|
-
* their site will never render).
|
|
425
|
+
* Only that the FILE disagrees with the artifact. Nothing is broken when it
|
|
426
|
+
* does — but `uniweb.supports` is the one place a human can add a service the
|
|
427
|
+
* graph cannot see (a computed name), so a developer who never sees the derived
|
|
428
|
+
* set never learns the key exists, and never adds the one thing only they know.
|
|
454
429
|
*
|
|
455
|
-
*
|
|
456
|
-
*
|
|
430
|
+
* `--fix` writes the derived names in, where they land in a diff the developer
|
|
431
|
+
* reads. ⛔ The build never does this itself: rewriting source on every build is
|
|
432
|
+
* a surprise, and the artifact needs no help.
|
|
433
|
+
*
|
|
434
|
+
* Silent when the foundation has not been built — nothing has been measured, so
|
|
435
|
+
* there is nothing to compare and a guess would be worse than quiet.
|
|
457
436
|
*/
|
|
458
|
-
export function checkFoundationSupports({
|
|
459
|
-
|
|
460
|
-
|
|
437
|
+
export async function checkFoundationSupports({
|
|
438
|
+
foundationName,
|
|
439
|
+
folderName,
|
|
440
|
+
foundationDir,
|
|
441
|
+
pkg,
|
|
442
|
+
issues,
|
|
443
|
+
shouldFix,
|
|
444
|
+
fixed,
|
|
445
|
+
}) {
|
|
446
|
+
const schemaPath = join(foundationDir, 'dist', 'meta', 'schema.json')
|
|
447
|
+
if (!existsSync(schemaPath)) return
|
|
448
|
+
|
|
449
|
+
let derived
|
|
450
|
+
try {
|
|
451
|
+
derived = JSON.parse(readFileSync(schemaPath, 'utf8'))?._self?.supports
|
|
452
|
+
} catch {
|
|
453
|
+
return // unreadable or malformed build output — not this check's to report
|
|
454
|
+
}
|
|
455
|
+
// Absent means the build derived nothing and saw a computed name, so it
|
|
456
|
+
// reported UNKNOWN. There is no gap to name.
|
|
457
|
+
if (!Array.isArray(derived) || derived.length === 0) return
|
|
461
458
|
|
|
462
459
|
const declared = pkg?.uniweb?.supports
|
|
463
460
|
const hasDeclaration = Array.isArray(declared)
|
|
464
|
-
const missing =
|
|
465
|
-
.filter((name) => !hasDeclaration || !declared.includes(name))
|
|
466
|
-
.sort()
|
|
461
|
+
const missing = derived.filter((name) => !hasDeclaration || !declared.includes(name)).sort()
|
|
467
462
|
if (missing.length === 0) return
|
|
468
463
|
|
|
469
464
|
const id = 'foundation-supports-incomplete'
|
|
470
465
|
const list = missing.join(', ')
|
|
466
|
+
const merged = [...new Set([...(hasDeclaration ? declared : []), ...derived])].sort()
|
|
467
|
+
|
|
468
|
+
if (shouldFix?.(id)) {
|
|
469
|
+
const pkgPath = join(foundationDir, 'package.json')
|
|
470
|
+
try {
|
|
471
|
+
const src = readFileSync(pkgPath, 'utf8')
|
|
472
|
+
const parsed = JSON.parse(src)
|
|
473
|
+
parsed.uniweb = { ...(parsed.uniweb || {}), supports: merged }
|
|
474
|
+
writeJsonPreservingStyle(pkgPath, parsed, src)
|
|
475
|
+
fixed?.(`${folderName}/package.json now declares uniweb.supports: ${merged.join(', ')}`)
|
|
476
|
+
return
|
|
477
|
+
} catch (e) {
|
|
478
|
+
// Fall through to the warning — a failed write must not read as fixed.
|
|
479
|
+
warn(`[${id}] could not write ${folderName}/package.json: ${e.message}`)
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
471
483
|
issues.push({
|
|
472
484
|
id,
|
|
473
485
|
type: 'warning',
|
|
474
486
|
foundation: foundationName,
|
|
475
487
|
message: hasDeclaration
|
|
476
|
-
? `${foundationName}
|
|
477
|
-
: `${foundationName}
|
|
478
|
-
details: {
|
|
488
|
+
? `${foundationName} builds against ${list} but does not declare ${missing.length === 1 ? 'it' : 'them'} in uniweb.supports`
|
|
489
|
+
: `${foundationName} builds against ${list} but declares no uniweb.supports`,
|
|
490
|
+
details: { derived, declared: hasDeclaration ? declared : null },
|
|
491
|
+
fixable: true,
|
|
479
492
|
})
|
|
480
493
|
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
494
|
+
warn(`[${id}] ${foundationName} builds against ${list}, not in ${folderName}/package.json`)
|
|
495
|
+
log(` The build already publishes ${colors.dim}${derived.join(', ')}${colors.reset}, so nothing is missing on the wire.`)
|
|
496
|
+
log(` Declaring it matters when a service is named by a computed value — the`)
|
|
497
|
+
log(` build cannot see those, and yours is the only place they can be added.`)
|
|
498
|
+
log(` ${colors.dim}uniweb doctor --fix ${id}${colors.reset}`)
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* The gates kit exposes for a host service, and the field that IS the gate.
|
|
503
|
+
*
|
|
504
|
+
* Each hook returns a boolean saying whether the site actually has the service.
|
|
505
|
+
* A component that ignores it draws a control the visitor cannot use — a search
|
|
506
|
+
* box that searches nothing, a form whose answers have nowhere to go.
|
|
507
|
+
*/
|
|
508
|
+
const SERVICE_GATES = [
|
|
509
|
+
{ hook: 'useFormSubmit', gate: 'canSubmit', draws: 'a form' },
|
|
510
|
+
{ hook: 'useSearch', gate: 'isEnabled', draws: 'a search control' },
|
|
511
|
+
{ hook: 'useSearchIndex', gate: 'isEnabled', draws: 'a search control' },
|
|
512
|
+
// Returns everything `useSearch` does, `isEnabled` included, so it gates the
|
|
513
|
+
// same way. It was missing from this list until the hook was renamed out of
|
|
514
|
+
// `useSearchWithIntent` — the kind of gap a list of names grows quietly.
|
|
515
|
+
{ hook: 'useSearchPrefetch', gate: 'isEnabled', draws: 'a search control' },
|
|
516
|
+
]
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* `uniweb doctor` — a control drawn for a service the site may not have.
|
|
520
|
+
*
|
|
521
|
+
* `resolveService` answers with an address or nothing, and nothing is a normal
|
|
522
|
+
* state: the operator did not provision that service. ⭐ **The rule is that a
|
|
523
|
+
* control for a service the site does not have must not be drawn** — no error,
|
|
524
|
+
* no apology, no explanatory string (there is deliberately none to show, since
|
|
525
|
+
* any wording would be ours to invent, in one language, for a visitor with no
|
|
526
|
+
* stake in it). The hooks hand you a boolean for exactly this, and a component
|
|
527
|
+
* that never reads it renders something permanently dead.
|
|
528
|
+
*
|
|
529
|
+
* ## ⚖️ WHY A REGEX IS ACCEPTABLE HERE, HAVING JUST BEEN REJECTED NEXT DOOR
|
|
530
|
+
*
|
|
531
|
+
* `checkFoundationSupports` above no longer scans source, because deriving
|
|
532
|
+
* *which services a foundation reaches* requires following calls through
|
|
533
|
+
* `@uniweb/kit` into `@uniweb/core`, and a missed match there silently costs a
|
|
534
|
+
* capability on the wire. This question is a different shape:
|
|
535
|
+
*
|
|
536
|
+
* - it is **local to one file** — the hook call and its gate are in the same
|
|
537
|
+
* component, in the developer's own source, with no indirection to follow;
|
|
538
|
+
* - the gate names are **fixed by kit's API**, not inferred;
|
|
539
|
+
* - it only ever **warns**, and changes no artifact. A miss costs a missing
|
|
540
|
+
* warning, which is the safe direction.
|
|
541
|
+
*
|
|
542
|
+
* ⛔ Its limits, stated rather than discovered: a component that gates in its
|
|
543
|
+
* parent reads as ungated here, and one that destructures the gate without
|
|
544
|
+
* using it reads as gated. Both are warnings about a real smell either way, and
|
|
545
|
+
* neither can produce a wrong artifact.
|
|
546
|
+
*/
|
|
547
|
+
/**
|
|
548
|
+
* Source with its comments removed.
|
|
549
|
+
*
|
|
550
|
+
* ⚠️ MEASURED, NOT PRECAUTIONARY. The first cut of this check searched the raw
|
|
551
|
+
* text, and passed on `templates/services` with the gate deliberately deleted
|
|
552
|
+
* from the code — because the component's JSDoc still said `canSubmit`. Prose
|
|
553
|
+
* about a gate is not a gate.
|
|
554
|
+
*
|
|
555
|
+
* ⛔ Whole-line and block comments only, deliberately. A line that begins with
|
|
556
|
+
* `//` or `*` is unambiguously a comment, so stripping it can never remove real
|
|
557
|
+
* code; hunting trailing comments would mean deciding whether a `//` sits
|
|
558
|
+
* inside a string, and getting that wrong deletes code and invents a warning.
|
|
559
|
+
* The residue is that a gate named ONLY in a trailing comment still reads as
|
|
560
|
+
* used, which is a missed warning — the safe direction.
|
|
561
|
+
*/
|
|
562
|
+
function stripComments(src) {
|
|
563
|
+
return src.replace(/\/\*[\s\S]*?\*\//g, '').replace(/^[ \t]*\/\/.*$/gm, '')
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
export function checkUngatedServiceControls({ foundationName, folderName, srcDir, issues }) {
|
|
567
|
+
const found = []
|
|
568
|
+
|
|
569
|
+
forEachSourceFile(srcDir, (raw, file) => {
|
|
570
|
+
const text = stripComments(raw)
|
|
571
|
+
for (const { hook, gate, draws } of SERVICE_GATES) {
|
|
572
|
+
// A call, not merely an import: re-exporting a hook is not drawing with it.
|
|
573
|
+
if (!new RegExp(`\\b${hook}\\s*\\(`).test(text)) continue
|
|
574
|
+
if (new RegExp(`\\b${gate}\\b`).test(text)) continue
|
|
575
|
+
found.push({ file, hook, gate, draws })
|
|
576
|
+
}
|
|
577
|
+
})
|
|
578
|
+
|
|
579
|
+
if (found.length === 0) return
|
|
580
|
+
|
|
581
|
+
const id = 'ungated-service-control'
|
|
582
|
+
for (const f of found) {
|
|
583
|
+
issues.push({
|
|
584
|
+
id,
|
|
585
|
+
type: 'warning',
|
|
586
|
+
foundation: foundationName,
|
|
587
|
+
message: `${foundationName}/${f.file} calls ${f.hook}() without reading ${f.gate}`,
|
|
588
|
+
details: f,
|
|
589
|
+
})
|
|
590
|
+
warn(`[${id}] ${folderName}/${f.file} calls ${f.hook}() and never reads ${f.gate}`)
|
|
591
|
+
log(` It draws ${f.draws} even on a site with no such service, where the`)
|
|
592
|
+
log(` control cannot work. Gate on it instead:`)
|
|
593
|
+
log(` ${colors.dim}const { ${f.gate} } = ${f.hook}(…)${colors.reset}`)
|
|
594
|
+
log(` ${colors.dim}if (!${f.gate}) return null${colors.reset}`)
|
|
487
595
|
}
|
|
488
|
-
const merged = [...new Set([...(hasDeclaration ? declared : []), ...missing])].sort()
|
|
489
|
-
log(` Add to ${folderName}/package.json:`)
|
|
490
|
-
log(` ${colors.dim}"uniweb": { "supports": ${JSON.stringify(merged)} }${colors.reset}`)
|
|
491
596
|
}
|
|
492
597
|
|
|
493
598
|
export function checkTrackingBlock({ siteName, siteYml, issues }) {
|
|
@@ -941,11 +1046,41 @@ export async function doctor(args = []) {
|
|
|
941
1046
|
|
|
942
1047
|
// ── uniweb.supports — the services this foundation says it is built against ──
|
|
943
1048
|
for (const f of foundations) {
|
|
944
|
-
checkFoundationSupports({
|
|
1049
|
+
await checkFoundationSupports({
|
|
945
1050
|
foundationName: f.name,
|
|
946
1051
|
folderName: f.folderName,
|
|
947
|
-
|
|
1052
|
+
foundationDir: f.path,
|
|
948
1053
|
pkg: loadPackageJson(f.path),
|
|
1054
|
+
issues,
|
|
1055
|
+
shouldFix,
|
|
1056
|
+
fixed
|
|
1057
|
+
})
|
|
1058
|
+
checkUngatedServiceControls({
|
|
1059
|
+
foundationName: f.name,
|
|
1060
|
+
folderName: f.folderName,
|
|
1061
|
+
srcDir: resolveFoundationSrcPath(f.path),
|
|
1062
|
+
issues
|
|
1063
|
+
})
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
// Extensions carry `uniweb.supports` too — same key, same field, and a site's
|
|
1067
|
+
// answer is the union across its primary foundation and its extensions. The
|
|
1068
|
+
// regex-era check looked at foundations only, so an extension that drew a
|
|
1069
|
+
// search box was never asked about one.
|
|
1070
|
+
for (const e of extensions) {
|
|
1071
|
+
await checkFoundationSupports({
|
|
1072
|
+
foundationName: e.name,
|
|
1073
|
+
folderName: e.folderName,
|
|
1074
|
+
foundationDir: e.path,
|
|
1075
|
+
pkg: loadPackageJson(e.path),
|
|
1076
|
+
issues,
|
|
1077
|
+
shouldFix,
|
|
1078
|
+
fixed
|
|
1079
|
+
})
|
|
1080
|
+
checkUngatedServiceControls({
|
|
1081
|
+
foundationName: e.name,
|
|
1082
|
+
folderName: e.folderName,
|
|
1083
|
+
srcDir: resolveFoundationSrcPath(e.path),
|
|
949
1084
|
issues
|
|
950
1085
|
})
|
|
951
1086
|
}
|
package/src/framework-index.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"generatedAt": "2026-09-
|
|
3
|
+
"generatedAt": "2026-09-07T05:09:47.913Z",
|
|
4
4
|
"packages": {
|
|
5
5
|
"@uniweb/api": {
|
|
6
|
-
"version": "0.2.
|
|
6
|
+
"version": "0.2.11",
|
|
7
7
|
"path": "framework/api",
|
|
8
8
|
"deps": [
|
|
9
9
|
"@uniweb/core"
|
|
10
10
|
]
|
|
11
11
|
},
|
|
12
12
|
"@uniweb/build": {
|
|
13
|
-
"version": "0.
|
|
13
|
+
"version": "0.43.0",
|
|
14
14
|
"path": "framework/build",
|
|
15
15
|
"deps": [
|
|
16
16
|
"@uniweb/content-reader",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"deps": []
|
|
35
35
|
},
|
|
36
36
|
"@uniweb/core": {
|
|
37
|
-
"version": "0.24.
|
|
37
|
+
"version": "0.24.1",
|
|
38
38
|
"path": "framework/core",
|
|
39
39
|
"deps": [
|
|
40
40
|
"@uniweb/semantic-parser",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
]
|
|
55
55
|
},
|
|
56
56
|
"@uniweb/kit": {
|
|
57
|
-
"version": "0.
|
|
57
|
+
"version": "0.18.0",
|
|
58
58
|
"path": "framework/kit",
|
|
59
59
|
"deps": [
|
|
60
60
|
"@uniweb/core",
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
]
|
|
83
83
|
},
|
|
84
84
|
"@uniweb/runtime": {
|
|
85
|
-
"version": "0.
|
|
85
|
+
"version": "0.19.0",
|
|
86
86
|
"path": "framework/runtime",
|
|
87
87
|
"deps": [
|
|
88
88
|
"@uniweb/core",
|
|
@@ -110,7 +110,7 @@
|
|
|
110
110
|
"deps": []
|
|
111
111
|
},
|
|
112
112
|
"@uniweb/templates": {
|
|
113
|
-
"version": "0.12.
|
|
113
|
+
"version": "0.12.1",
|
|
114
114
|
"path": "framework/templates",
|
|
115
115
|
"deps": []
|
|
116
116
|
},
|
|
@@ -120,7 +120,7 @@
|
|
|
120
120
|
"deps": []
|
|
121
121
|
},
|
|
122
122
|
"@uniweb/unipress": {
|
|
123
|
-
"version": "0.9.
|
|
123
|
+
"version": "0.9.10",
|
|
124
124
|
"path": "framework/unipress",
|
|
125
125
|
"deps": [
|
|
126
126
|
"@uniweb/build",
|