@uniweb/build 0.14.24 → 0.14.25
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/schema.js +6 -2
- package/src/vite-foundation-plugin.js +137 -10
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uniweb/build",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.25",
|
|
4
4
|
"description": "Build tooling for the Uniweb Component Web Platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -59,13 +59,13 @@
|
|
|
59
59
|
"js-yaml": "^4.1.0",
|
|
60
60
|
"sharp": "^0.33.2",
|
|
61
61
|
"yaml": "^2.5.0",
|
|
62
|
-
"@uniweb/theming": "0.1.
|
|
62
|
+
"@uniweb/theming": "0.1.6",
|
|
63
63
|
"@uniweb/content-writer": "0.2.6"
|
|
64
64
|
},
|
|
65
65
|
"optionalDependencies": {
|
|
66
|
-
"@uniweb/
|
|
67
|
-
"@uniweb/
|
|
68
|
-
"@uniweb/
|
|
66
|
+
"@uniweb/content-reader": "1.1.12",
|
|
67
|
+
"@uniweb/runtime": "0.8.23",
|
|
68
|
+
"@uniweb/schemas": "0.2.4"
|
|
69
69
|
},
|
|
70
70
|
"peerDependencies": {
|
|
71
71
|
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0",
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
"@tailwindcss/vite": "^4.0.0",
|
|
75
75
|
"@vitejs/plugin-react": "^4.0.0 || ^5.0.0",
|
|
76
76
|
"vite-plugin-svgr": "^4.0.0",
|
|
77
|
-
"@uniweb/core": "0.7.
|
|
77
|
+
"@uniweb/core": "0.7.17"
|
|
78
78
|
},
|
|
79
79
|
"peerDependenciesMeta": {
|
|
80
80
|
"vite": {
|
package/src/schema.js
CHANGED
|
@@ -92,9 +92,13 @@ export async function loadPackageJson(srcDir) {
|
|
|
92
92
|
const content = await readFile(packagePath, 'utf-8')
|
|
93
93
|
const pkg = JSON.parse(content)
|
|
94
94
|
|
|
95
|
-
// Extract only identity fields for schema
|
|
95
|
+
// Extract only identity fields for schema.
|
|
96
|
+
// `uniweb.id` is the REGISTERED name (registry identity), decoupled from the
|
|
97
|
+
// workspace package `name` (pnpm linking / file: deps / site.yml). It lets a
|
|
98
|
+
// foundation keep a scaffold-default package name like "src" while registering
|
|
99
|
+
// under a distinct id (e.g. "docs" → @org/docs). Falls back to `name`.
|
|
96
100
|
return {
|
|
97
|
-
name: pkg.name,
|
|
101
|
+
name: pkg.uniweb?.id || pkg.name,
|
|
98
102
|
version: pkg.version,
|
|
99
103
|
description: pkg.description,
|
|
100
104
|
}
|
|
@@ -340,6 +340,132 @@ async function emitFoundationVarsCss(outDir, schema) {
|
|
|
340
340
|
console.log(`Emitted ${Object.keys(flatVars).length} foundation theme-var default(s) to assets/style.css`)
|
|
341
341
|
}
|
|
342
342
|
|
|
343
|
+
/**
|
|
344
|
+
* Externals for the SSR bundle (`dist/entry-ssr.js`).
|
|
345
|
+
*
|
|
346
|
+
* Same set the browser foundation build externalizes (DEFAULT_EXTERNALS in
|
|
347
|
+
* foundation/config.js — react/react-dom/react-dom-server/jsx-runtime/core),
|
|
348
|
+
* which the Cloudflare edge isolate resolves to the SHARED runtime's React/core
|
|
349
|
+
* (worker-runtime.js) via its shims — so React stays deduped and runtime patches
|
|
350
|
+
* still propagate without a foundation rebuild.
|
|
351
|
+
*
|
|
352
|
+
* PLUS the client-only libraries kit code-splits via dynamic import:
|
|
353
|
+
* - shiki / shiki/bundle/full — syntax highlighting (kit Code renderer)
|
|
354
|
+
* - fuse.js — client search index
|
|
355
|
+
* Both hydrate in the browser and never run during renderToString (CLAUDE.md
|
|
356
|
+
* gotcha #12). Keeping them external drops the ~10 MB Shiki language graph from
|
|
357
|
+
* the SSR bundle and leaves them as DORMANT dynamic imports the isolate never
|
|
358
|
+
* awaits — so no extra modules-map entry is needed for them edge-side.
|
|
359
|
+
*/
|
|
360
|
+
const SSR_DEFAULT_EXTERNALS = [
|
|
361
|
+
'react',
|
|
362
|
+
'react-dom',
|
|
363
|
+
'react-dom/server',
|
|
364
|
+
'react/jsx-runtime',
|
|
365
|
+
'react/jsx-dev-runtime',
|
|
366
|
+
'@uniweb/core',
|
|
367
|
+
]
|
|
368
|
+
|
|
369
|
+
function isSSRExternal(id) {
|
|
370
|
+
if (SSR_DEFAULT_EXTERNALS.includes(id)) return true
|
|
371
|
+
if (id === 'shiki' || id.startsWith('shiki/')) return true
|
|
372
|
+
if (id === 'fuse.js' || id.startsWith('fuse.js/')) return true
|
|
373
|
+
return false
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Emit `dist/entry-ssr.js` — the single-file SSR twin of the (code-split)
|
|
378
|
+
* browser `dist/entry.js`.
|
|
379
|
+
*
|
|
380
|
+
* The modern browser `entry.js` is a facade that re-exports from
|
|
381
|
+
* `_entry.generated-*.js` and lazily code-splits kit's client-only features
|
|
382
|
+
* (Shiki, Fuse) into hundreds of chunks — a graph the Cloudflare Dynamic Worker
|
|
383
|
+
* isolate can't resolve (it loads a single `foundation` module). This builds the
|
|
384
|
+
* SAME source entry into ONE file, inlining the foundation's own graph and
|
|
385
|
+
* externalizing the runtime/React set (→ the isolate's shared worker-runtime)
|
|
386
|
+
* and the client-only Shiki/Fuse libs. Result: a ~foundation-sized ESM module
|
|
387
|
+
* (no React, no Shiki) the edge loads as `foundation` for request-time SSR.
|
|
388
|
+
*
|
|
389
|
+
* Built from source (not by re-bundling the built `entry.js`, whose Shiki
|
|
390
|
+
* specifier is already rewritten to a relative chunk path that couldn't be
|
|
391
|
+
* externalized) via a secondary Vite build into a temp dir; only the JS is
|
|
392
|
+
* copied out (the throwaway CSS is discarded — the SSR bundle needs no styles).
|
|
393
|
+
*
|
|
394
|
+
* Best-effort: a failure warns and emits nothing, so the edge simply serves
|
|
395
|
+
* the client-render shell for this foundation (existence-gated) — no regression.
|
|
396
|
+
*
|
|
397
|
+
* @param {string} foundationRoot - foundation project root (vite `root`).
|
|
398
|
+
* @param {string} entrySourcePath - absolute path to `_entry.generated.js`.
|
|
399
|
+
* @param {string} outDir - dist/ directory to write `entry-ssr.js` into.
|
|
400
|
+
*/
|
|
401
|
+
async function buildEntrySSR(foundationRoot, entrySourcePath, outDir) {
|
|
402
|
+
if (_buildingSSRBundle) return
|
|
403
|
+
_buildingSSRBundle = true
|
|
404
|
+
|
|
405
|
+
const { rm, cp, stat } = await import('node:fs/promises')
|
|
406
|
+
const tmpDir = join(outDir, '.entry-ssr-tmp')
|
|
407
|
+
|
|
408
|
+
try {
|
|
409
|
+
if (!existsSync(entrySourcePath)) {
|
|
410
|
+
console.warn(`Skipping entry-ssr.js: entry source not found at ${entrySourcePath}`)
|
|
411
|
+
return
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
const { build: viteBuild } = await import('vite')
|
|
415
|
+
|
|
416
|
+
// Same transform plugins as the browser foundation build (JSX, SVGR, and —
|
|
417
|
+
// best-effort — Tailwind), but WITHOUT foundationPlugin: no schema/entry
|
|
418
|
+
// regeneration and no writeBundle recursion. CSS output is discarded.
|
|
419
|
+
const plugins = []
|
|
420
|
+
try {
|
|
421
|
+
const tailwindcss = (await import('@tailwindcss/vite')).default
|
|
422
|
+
plugins.push(tailwindcss())
|
|
423
|
+
} catch {
|
|
424
|
+
// Tailwind optional / not installed — the SSR bundle discards CSS anyway.
|
|
425
|
+
}
|
|
426
|
+
const react = (await import('@vitejs/plugin-react')).default
|
|
427
|
+
const svgr = (await import('vite-plugin-svgr')).default
|
|
428
|
+
plugins.push(react(), svgr())
|
|
429
|
+
|
|
430
|
+
await viteBuild({
|
|
431
|
+
root: foundationRoot,
|
|
432
|
+
configFile: false,
|
|
433
|
+
logLevel: 'warn',
|
|
434
|
+
plugins,
|
|
435
|
+
build: {
|
|
436
|
+
outDir: tmpDir,
|
|
437
|
+
emptyOutDir: true,
|
|
438
|
+
sourcemap: false,
|
|
439
|
+
cssCodeSplit: false,
|
|
440
|
+
lib: {
|
|
441
|
+
entry: entrySourcePath,
|
|
442
|
+
formats: ['es'],
|
|
443
|
+
fileName: () => 'entry-ssr.js',
|
|
444
|
+
},
|
|
445
|
+
rollupOptions: {
|
|
446
|
+
external: isSSRExternal,
|
|
447
|
+
output: { inlineDynamicImports: true },
|
|
448
|
+
},
|
|
449
|
+
},
|
|
450
|
+
})
|
|
451
|
+
|
|
452
|
+
const built = join(tmpDir, 'entry-ssr.js')
|
|
453
|
+
if (!existsSync(built)) {
|
|
454
|
+
console.warn('Warning: entry-ssr.js build produced no JS output — skipped.')
|
|
455
|
+
return
|
|
456
|
+
}
|
|
457
|
+
const dest = join(outDir, 'entry-ssr.js')
|
|
458
|
+
await cp(built, dest)
|
|
459
|
+
const size = ((await stat(dest)).size / 1024).toFixed(1)
|
|
460
|
+
console.log(`Generated entry-ssr.js (${size} KB)`)
|
|
461
|
+
} catch (err) {
|
|
462
|
+
console.warn(`Warning: entry-ssr.js build failed: ${err.message}`)
|
|
463
|
+
} finally {
|
|
464
|
+
await rm(tmpDir, { recursive: true, force: true }).catch(() => {})
|
|
465
|
+
_buildingSSRBundle = false
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
343
469
|
/**
|
|
344
470
|
* Vite plugin for foundation builds
|
|
345
471
|
*/
|
|
@@ -418,17 +544,18 @@ export function foundationBuildPlugin(options = {}) {
|
|
|
418
544
|
// automatically once the edge is updated.
|
|
419
545
|
await emitRuntimePin(outDir, resolvedRoot)
|
|
420
546
|
|
|
421
|
-
//
|
|
422
|
-
//
|
|
423
|
-
// runtime
|
|
424
|
-
//
|
|
425
|
-
//
|
|
547
|
+
// Emit dist/entry-ssr.js — the single-file SSR twin of the (code-split)
|
|
548
|
+
// browser dist/entry.js — for the Cloudflare edge isolate. React + the
|
|
549
|
+
// runtime stay externalized (resolved to the isolate's SHARED
|
|
550
|
+
// worker-runtime, so runtime patches propagate without a rebuild — the
|
|
551
|
+
// Strategy S win); the client-only Shiki/Fuse libs are externalized so the
|
|
552
|
+
// ~10 MB Shiki graph stays out. The edge loads this as its single
|
|
553
|
+
// `foundation` module for request-time SSR, gated on its presence.
|
|
426
554
|
//
|
|
427
|
-
// The buildSSRBundle()
|
|
428
|
-
//
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
// await buildSSRBundle(outDir)
|
|
555
|
+
// (The legacy self-contained buildSSRBundle() — React + runtime INLINED,
|
|
556
|
+
// ~14 MB with Shiki — is retained below, unused, for reference only.)
|
|
557
|
+
const entrySourcePath = join(resolvedSrcDir, entryFileName)
|
|
558
|
+
await buildEntrySSR(resolvedRoot, entrySourcePath, outDir)
|
|
432
559
|
},
|
|
433
560
|
|
|
434
561
|
async closeBundle() {
|