@uniweb/build 0.11.8 → 0.12.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 +3 -3
- package/src/vite-foundation-plugin.js +166 -24
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uniweb/build",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "Build tooling for the Uniweb Component Web Platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -59,8 +59,8 @@
|
|
|
59
59
|
},
|
|
60
60
|
"optionalDependencies": {
|
|
61
61
|
"@uniweb/content-reader": "1.1.9",
|
|
62
|
-
"@uniweb/
|
|
63
|
-
"@uniweb/
|
|
62
|
+
"@uniweb/schemas": "0.2.1",
|
|
63
|
+
"@uniweb/runtime": "0.8.9"
|
|
64
64
|
},
|
|
65
65
|
"peerDependencies": {
|
|
66
66
|
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0",
|
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
* - Processing preview images for presets
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { writeFile, mkdir } from 'node:fs/promises'
|
|
10
|
+
import { writeFile, mkdir, readFile } from 'node:fs/promises'
|
|
11
|
+
import { existsSync } from 'node:fs'
|
|
11
12
|
import { join, resolve } from 'node:path'
|
|
12
13
|
import { buildSchema } from './schema.js'
|
|
13
14
|
import { generateEntryPoint, shouldRegenerateForFile } from './generate-entry.js'
|
|
@@ -42,30 +43,144 @@ async function buildSchemaWithPreviews(srcDir, outDir, isProduction, sectionPath
|
|
|
42
43
|
let _buildingSSRBundle = false
|
|
43
44
|
|
|
44
45
|
/**
|
|
45
|
-
*
|
|
46
|
+
* Emit dist/runtime-pin.json declaring the @uniweb/runtime version this
|
|
47
|
+
* foundation was built against. Read by the edge isolate (under the
|
|
48
|
+
* Strategy S split-bundle path) to decide which runtime/{ver}/ssr.js to
|
|
49
|
+
* side-load from R2. See kb/platform/plans/edge-ssr-bundling-strategy.md
|
|
50
|
+
* and kb/platform/operations/release-workflow.md.
|
|
51
|
+
*
|
|
52
|
+
* Reads the resolved version from the foundation's node_modules/@uniweb/
|
|
53
|
+
* runtime/package.json so the pin reflects what was actually linked at
|
|
54
|
+
* build time, not what the foundation's own package.json range happens
|
|
55
|
+
* to allow.
|
|
56
|
+
*
|
|
57
|
+
* Silently no-ops when @uniweb/runtime isn't resolvable (e.g., the
|
|
58
|
+
* foundation depends on the runtime via a workspace alias that puts it
|
|
59
|
+
* elsewhere). The edge resolver treats the absence of a pin as the
|
|
60
|
+
* legacy single-bundle path, so omitting the pin is harmless during the
|
|
61
|
+
* dual-mode window.
|
|
62
|
+
*
|
|
63
|
+
* Optional foundation-author override: a `uniweb.runtimePolicy` field
|
|
64
|
+
* in the foundation's own package.json gets recorded alongside the
|
|
65
|
+
* runtime version so the registry's semver resolver can apply it.
|
|
66
|
+
*
|
|
67
|
+
* @param {string} outDir - dist/ directory to write to.
|
|
68
|
+
* @param {string} projectRoot - foundation project root (where package.json lives).
|
|
69
|
+
*/
|
|
70
|
+
async function emitRuntimePin(outDir, projectRoot) {
|
|
71
|
+
// Resolve @uniweb/runtime via two strategies, in order:
|
|
72
|
+
// 1. createRequire from this plugin's location (catches the runtime
|
|
73
|
+
// pulled in transitively through @uniweb/build, @uniweb/core, etc.).
|
|
74
|
+
// 2. Walk up node_modules from the project root (catches the case
|
|
75
|
+
// where the foundation depends on runtime directly).
|
|
76
|
+
// The first covers the common case (foundations don't typically depend
|
|
77
|
+
// on runtime directly — it's the host environment, not a foundation
|
|
78
|
+
// import); the second is a safety net.
|
|
79
|
+
let runtimePkgPath = null
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
// Resolve via an exported subpath, not 'package.json' directly —
|
|
83
|
+
// @uniweb/runtime's `exports` map doesn't include package.json, so
|
|
84
|
+
// require.resolve on it throws ERR_PACKAGE_PATH_NOT_EXPORTED.
|
|
85
|
+
// Walking back from the resolved subpath finds the package root.
|
|
86
|
+
const { createRequire } = await import('node:module')
|
|
87
|
+
const { dirname: pathDirname } = await import('node:path')
|
|
88
|
+
const pluginRequire = createRequire(import.meta.url)
|
|
89
|
+
const ssrEntry = pluginRequire.resolve('@uniweb/runtime/ssr')
|
|
90
|
+
let dir = pathDirname(ssrEntry)
|
|
91
|
+
for (let i = 0; i < 5; i++) {
|
|
92
|
+
const candidate = join(dir, 'package.json')
|
|
93
|
+
if (existsSync(candidate)) {
|
|
94
|
+
const pkg = JSON.parse(await readFile(candidate, 'utf-8'))
|
|
95
|
+
if (pkg.name === '@uniweb/runtime') {
|
|
96
|
+
runtimePkgPath = candidate
|
|
97
|
+
break
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const parent = resolve(dir, '..')
|
|
101
|
+
if (parent === dir) break
|
|
102
|
+
dir = parent
|
|
103
|
+
}
|
|
104
|
+
} catch {
|
|
105
|
+
// Fall through to the walk-up search.
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (!runtimePkgPath) {
|
|
109
|
+
let dir = projectRoot
|
|
110
|
+
for (let i = 0; i < 10; i++) {
|
|
111
|
+
const candidate = join(dir, 'node_modules', '@uniweb', 'runtime', 'package.json')
|
|
112
|
+
if (existsSync(candidate)) {
|
|
113
|
+
runtimePkgPath = candidate
|
|
114
|
+
break
|
|
115
|
+
}
|
|
116
|
+
const parent = resolve(dir, '..')
|
|
117
|
+
if (parent === dir) break
|
|
118
|
+
dir = parent
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (!runtimePkgPath) {
|
|
123
|
+
// No runtime resolvable. Skip emission — edge will treat as legacy.
|
|
124
|
+
return
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
let runtimeVersion
|
|
128
|
+
try {
|
|
129
|
+
const pkg = JSON.parse(await readFile(runtimePkgPath, 'utf-8'))
|
|
130
|
+
runtimeVersion = pkg.version
|
|
131
|
+
} catch {
|
|
132
|
+
return
|
|
133
|
+
}
|
|
134
|
+
if (!runtimeVersion) return
|
|
135
|
+
|
|
136
|
+
// Read foundation's own package.json for an optional runtimePolicy
|
|
137
|
+
// field. Default policy (auto-patch) lives at the registry layer; we
|
|
138
|
+
// only record the foundation's override if explicitly set.
|
|
139
|
+
let policy = null
|
|
140
|
+
try {
|
|
141
|
+
const foundationPkgPath = join(projectRoot, 'package.json')
|
|
142
|
+
if (existsSync(foundationPkgPath)) {
|
|
143
|
+
const foundationPkg = JSON.parse(await readFile(foundationPkgPath, 'utf-8'))
|
|
144
|
+
policy = foundationPkg?.uniweb?.runtimePolicy ?? null
|
|
145
|
+
}
|
|
146
|
+
} catch {
|
|
147
|
+
// Foundation package.json malformed; skip policy. Pin still emits.
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const pin = { runtime: runtimeVersion }
|
|
151
|
+
if (policy) pin.policy = policy
|
|
152
|
+
|
|
153
|
+
const pinPath = join(outDir, 'runtime-pin.json')
|
|
154
|
+
await writeFile(pinPath, JSON.stringify(pin, null, 2) + '\n', 'utf-8')
|
|
155
|
+
console.log(`Generated runtime-pin.json (runtime ${runtimeVersion}${policy ? `, policy ${policy}` : ''})`)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* @deprecated 2026-04-27 — Strategy S Phase 2.
|
|
160
|
+
*
|
|
161
|
+
* Foundations no longer carry their own runtime bundle. Runtime + React +
|
|
162
|
+
* core + theming now live in R2 under `runtime/{version}/worker-runtime.js`,
|
|
163
|
+
* published by the platform's `/deploy-runtime` skill, and side-loaded
|
|
164
|
+
* by the Cloudflare isolate alongside `dist/foundation.js`.
|
|
46
165
|
*
|
|
166
|
+
* The invocation in `writeBundle()` is commented out; this function
|
|
167
|
+
* definition is kept for the rollout window so it can be flipped back
|
|
168
|
+
* on with one line if Phase 1's edge dispatcher misbehaves in production.
|
|
169
|
+
* Phase 3 cleanup deletes this function entirely once the new path is
|
|
170
|
+
* proven healthy.
|
|
171
|
+
*
|
|
172
|
+
* See `kb/platform/plans/edge-ssr-bundling-strategy.md`.
|
|
173
|
+
*
|
|
174
|
+
* Original purpose (preserved for context):
|
|
175
|
+
* Build a self-contained ESM bundle for edge SSR (Cloudflare Dynamic Workers).
|
|
47
176
|
* Produces `ssr-worker-bundle.js` — a single ESM file with React,
|
|
48
177
|
* ReactDOM/server, `@uniweb/core`, `@uniweb/runtime/ssr`,
|
|
49
178
|
* `@uniweb/theming`, and the foundation's components all inlined. No
|
|
50
|
-
* external imports.
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* (Node SSR). This file is something different: a self-contained SSR
|
|
56
|
-
* pipeline shaped for the Cloudflare Workers Dynamic Worker LOADER, with
|
|
57
|
-
* the foundation embedded as one of several inputs. The size ratio
|
|
58
|
-
* (typical ~12× larger than `foundation.js`) reflects what's actually
|
|
59
|
-
* inside it.
|
|
60
|
-
*
|
|
61
|
-
* The artifact is bundled this way because the Dynamic Worker LOADER
|
|
62
|
-
* accepts a closed `modules` map at isolate construction (no external
|
|
63
|
-
* resolver, no path back to npm or R2 for transitive imports). One
|
|
64
|
-
* file in, everything resolves. A future refactor can switch to side-
|
|
65
|
-
* loading runtime + React + core into the modules map separately so
|
|
66
|
-
* foundations stop carrying runtime in their bundles — see
|
|
67
|
-
* `kb/platform/plans/edge-runtime-side-loading.md`. Until then, this
|
|
68
|
-
* build pre-bundles for the isolate's contract.
|
|
179
|
+
* external imports. The artifact was bundled this way because the
|
|
180
|
+
* Dynamic Worker LOADER accepts a closed `modules` map at isolate
|
|
181
|
+
* construction. The Phase -1 prototype (2026-04-27) verified the LOADER
|
|
182
|
+
* actually deduplicates shared modules across multiple ESM bundles, so
|
|
183
|
+
* a multi-entry modules map became viable — that's what Strategy S uses.
|
|
69
184
|
*
|
|
70
185
|
* @param {string} outDir - Path to dist/ directory containing foundation.js
|
|
71
186
|
*/
|
|
@@ -118,8 +233,16 @@ async function buildSSRBundle(outDir) {
|
|
|
118
233
|
// - @uniweb/theming (buildSectionOverrides, used by runtime/ssr)
|
|
119
234
|
//
|
|
120
235
|
// All in a single file so the Dynamic Worker isolate has one React instance.
|
|
236
|
+
// L2/L3 helpers from @uniweb/runtime/ssr that worker SSR + framework SSG
|
|
237
|
+
// both depend on. Keep this list in sync with runtime/src/ssr-renderer.js
|
|
238
|
+
// exports — missing one here makes the foundation bundle fail to import
|
|
239
|
+
// it ("module does not provide an export named X") inside the SSR isolate.
|
|
121
240
|
const ssrExports = runtimeSSRPath
|
|
122
|
-
? `export {
|
|
241
|
+
? `export {
|
|
242
|
+
initPrerender, initPrerenderForLocale,
|
|
243
|
+
renderPage, injectPageContent, prefetchIcons,
|
|
244
|
+
sliceContentForLocale, hydrateDataStore,
|
|
245
|
+
} from "${runtimeSSRPath.replace(/\\/g, '/')}";`
|
|
123
246
|
: ''
|
|
124
247
|
|
|
125
248
|
// Resolve React to a single package directory to avoid duplicate instances
|
|
@@ -191,6 +314,7 @@ export function foundationBuildPlugin(options = {}) {
|
|
|
191
314
|
|
|
192
315
|
let resolvedSrcDir
|
|
193
316
|
let resolvedOutDir
|
|
317
|
+
let resolvedRoot
|
|
194
318
|
let isProduction
|
|
195
319
|
|
|
196
320
|
return {
|
|
@@ -209,6 +333,7 @@ export function foundationBuildPlugin(options = {}) {
|
|
|
209
333
|
async configResolved(config) {
|
|
210
334
|
resolvedSrcDir = resolve(config.root, srcDir)
|
|
211
335
|
resolvedOutDir = config.build.outDir
|
|
336
|
+
resolvedRoot = config.root
|
|
212
337
|
isProduction = config.mode === 'production'
|
|
213
338
|
},
|
|
214
339
|
|
|
@@ -235,8 +360,25 @@ export function foundationBuildPlugin(options = {}) {
|
|
|
235
360
|
|
|
236
361
|
console.log(`Generated meta/schema.json with ${Object.keys(schema).length - 1} components`)
|
|
237
362
|
|
|
238
|
-
//
|
|
239
|
-
|
|
363
|
+
// Emit runtime-pin.json so the edge isolate (under Strategy S) can
|
|
364
|
+
// side-load the matching runtime/{ver}/ssr.js. Lands silently before
|
|
365
|
+
// the dual-mode resolver ships; foundations published in the dual-mode
|
|
366
|
+
// window already have the pin and start using the split-bundle path
|
|
367
|
+
// automatically once the edge is updated.
|
|
368
|
+
await emitRuntimePin(outDir, resolvedRoot)
|
|
369
|
+
|
|
370
|
+
// Strategy S Phase 2: foundations no longer carry a self-contained
|
|
371
|
+
// SSR bundle. The runtime + React + core + theming live in R2 under
|
|
372
|
+
// runtime/{version}/worker-runtime.js (uploaded by the platform's
|
|
373
|
+
// /deploy-runtime skill); the Cloudflare isolate side-loads them
|
|
374
|
+
// alongside dist/foundation.js via the edge dual-mode dispatcher.
|
|
375
|
+
// See kb/platform/plans/edge-ssr-bundling-strategy.md.
|
|
376
|
+
//
|
|
377
|
+
// The buildSSRBundle() function is kept (just not invoked) so it
|
|
378
|
+
// can be flipped back on with one line if Phase 1's edge dispatcher
|
|
379
|
+
// misbehaves in production. Phase 3 cleanup deletes the function
|
|
380
|
+
// entirely once we're confident the new path is healthy.
|
|
381
|
+
// await buildSSRBundle(outDir)
|
|
240
382
|
},
|
|
241
383
|
|
|
242
384
|
async closeBundle() {
|