@uniweb/build 0.16.23 → 0.17.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/import-map-plugin.js +77 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uniweb/build",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"description": "Build tooling for the Uniweb Component Web Platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -61,14 +61,14 @@
|
|
|
61
61
|
"yaml": "^2.5.0",
|
|
62
62
|
"@uniweb/schemas": "0.2.10",
|
|
63
63
|
"@uniweb/theming": "0.1.15",
|
|
64
|
-
"@uniweb/
|
|
65
|
-
"@uniweb/
|
|
64
|
+
"@uniweb/content-writer": "0.3.3",
|
|
65
|
+
"@uniweb/projections": "0.2.5"
|
|
66
66
|
},
|
|
67
67
|
"optionalDependencies": {
|
|
68
|
+
"@uniweb/runtime": "0.10.0",
|
|
68
69
|
"@uniweb/schemas": "0.2.10",
|
|
69
|
-
"@uniweb/
|
|
70
|
-
"@uniweb/
|
|
71
|
-
"@uniweb/semantic-parser": "1.2.1"
|
|
70
|
+
"@uniweb/semantic-parser": "1.2.1",
|
|
71
|
+
"@uniweb/content-reader": "1.2.2"
|
|
72
72
|
},
|
|
73
73
|
"peerDependencies": {
|
|
74
74
|
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0",
|
package/src/import-map-plugin.js
CHANGED
|
@@ -31,11 +31,52 @@ const IMPORT_MAP_PREFIX = '\0importmap:'
|
|
|
31
31
|
/** Valid JS identifier — filters out non-identifier keys from CJS modules */
|
|
32
32
|
const isValidId = (k) => /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(k)
|
|
33
33
|
|
|
34
|
+
/**
|
|
35
|
+
* An external may be a bare specifier string, or an object declaring its
|
|
36
|
+
* surface explicitly.
|
|
37
|
+
*
|
|
38
|
+
* ⛔ WHY THE OBJECT FORM EXISTS — read before "simplifying" it away.
|
|
39
|
+
*
|
|
40
|
+
* The string form enumerates a module's exports by `await import(pkg)` in the
|
|
41
|
+
* Vite process, i.e. in NODE. That works for plain `.js`, and **throws for
|
|
42
|
+
* anything reached through a `.jsx` file** — Node cannot parse JSX. The catch
|
|
43
|
+
* below then falls back to `export * from pkg`, and `export *` **never
|
|
44
|
+
* re-exports `default`**. So a bridged module whose public surface IS its
|
|
45
|
+
* default export silently emits a bridge with that export missing.
|
|
46
|
+
*
|
|
47
|
+
* Measured 2026-08-08 on `@uniweb/runtime/provider` (whose only export is
|
|
48
|
+
* `export default function RuntimeProvider`): the fallback produced ZERO
|
|
49
|
+
* exports, so Rollup reused the emitted filename as a shared chunk and the
|
|
50
|
+
* "bridge" shipped containing `ErrorBoundary` instead. The file existed, the
|
|
51
|
+
* manifest listed it, the channel published it and its digests verified — and
|
|
52
|
+
* `import RuntimeProvider from '<bridge>'` throws "does not provide an export
|
|
53
|
+
* named 'default'". Nothing upstream of the browser could see it.
|
|
54
|
+
*
|
|
55
|
+
* `hasDefault` is what `export *` cannot express, so it is the one thing worth
|
|
56
|
+
* declaring. Named exports stay self-maintaining through `export *`.
|
|
57
|
+
*
|
|
58
|
+
* @typedef {string | { spec: string, hasDefault?: boolean, named?: string[] }} External
|
|
59
|
+
*/
|
|
60
|
+
const specOf = (e) => (typeof e === 'string' ? e : e.spec)
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* The bridge filename for a specifier: `@uniweb/runtime/provider` →
|
|
64
|
+
* `@uniweb-runtime-provider.js`.
|
|
65
|
+
*
|
|
66
|
+
* Exported because THREE producers must agree on it — the emitted chunk, the
|
|
67
|
+
* `<script type="importmap">`, and the runtime shell's `manifest.json`. A
|
|
68
|
+
* consumer resolves a bridge by the manifest's URL, so a manifest that names a
|
|
69
|
+
* file the build did not emit is a 404 at import time, not a build error.
|
|
70
|
+
*/
|
|
71
|
+
export const bridgeFileName = (spec) => `${spec.replace(/\//g, '-')}.js`
|
|
72
|
+
|
|
34
73
|
/**
|
|
35
74
|
* Create the import map Vite plugin.
|
|
36
75
|
*
|
|
37
76
|
* @param {Object} [options]
|
|
38
|
-
* @param {
|
|
77
|
+
* @param {External[]} [options.externals] - Specifiers to bridge (default: react, react-dom, @uniweb/core, etc.).
|
|
78
|
+
* Each entry is a bare specifier string, or `{ spec, hasDefault?, named? }` when the module's
|
|
79
|
+
* surface cannot be enumerated by importing it in Node — see the `External` typedef.
|
|
39
80
|
* @param {string} [options.name] - Plugin name (default: 'uniweb:import-map')
|
|
40
81
|
* @param {string} [options.basePath] - Base path prefix for import map URLs in HTML (default: '/')
|
|
41
82
|
* @param {string} [options.resolveFrom] - Absolute path to resolve bare specifiers from inside virtual modules.
|
|
@@ -68,7 +109,7 @@ export function importMapPlugin({
|
|
|
68
109
|
// from '\0importmap:@uniweb/core') can't be resolved by Rollup because virtual
|
|
69
110
|
// modules have no filesystem context. When a resolveFrom path is provided,
|
|
70
111
|
// resolve from there (e.g. the foundation directory under pnpm strict mode).
|
|
71
|
-
if (resolveFrom && importer?.startsWith(IMPORT_MAP_PREFIX) && externals.
|
|
112
|
+
if (resolveFrom && importer?.startsWith(IMPORT_MAP_PREFIX) && externals.some((e) => specOf(e) === id)) {
|
|
72
113
|
return this.resolve(id, resolveFrom, { skipSelf: true })
|
|
73
114
|
}
|
|
74
115
|
},
|
|
@@ -76,6 +117,24 @@ export function importMapPlugin({
|
|
|
76
117
|
async load(id) {
|
|
77
118
|
if (!id.startsWith(IMPORT_MAP_PREFIX)) return
|
|
78
119
|
const pkg = id.slice(IMPORT_MAP_PREFIX.length)
|
|
120
|
+
const declared = externals.find((e) => specOf(e) === pkg)
|
|
121
|
+
|
|
122
|
+
// A declared surface wins outright — no Node import is attempted, so a
|
|
123
|
+
// JSX-reaching module is bridged correctly instead of falling into the
|
|
124
|
+
// lossy catch below. See the `External` typedef for why this exists.
|
|
125
|
+
if (typeof declared === 'object' && (declared.named || declared.hasDefault !== undefined)) {
|
|
126
|
+
const lines = []
|
|
127
|
+
if (declared.named?.length) {
|
|
128
|
+
lines.push(`export { ${declared.named.join(', ')} } from '${pkg}'`)
|
|
129
|
+
} else {
|
|
130
|
+
// Named exports stay self-maintaining; only `default` is declared.
|
|
131
|
+
lines.push(`export * from '${pkg}'`)
|
|
132
|
+
}
|
|
133
|
+
if (declared.hasDefault) {
|
|
134
|
+
lines.push(`export { default } from '${pkg}'`)
|
|
135
|
+
}
|
|
136
|
+
return lines.join('\n')
|
|
137
|
+
}
|
|
79
138
|
|
|
80
139
|
// Generate explicit named re-exports (not `export *`) because CJS
|
|
81
140
|
// packages like React only expose a default via `export *`, losing
|
|
@@ -93,8 +152,17 @@ export function importMapPlugin({
|
|
|
93
152
|
lines.push(`export { default } from '${pkg}'`)
|
|
94
153
|
}
|
|
95
154
|
return lines.join('\n') || 'export {}'
|
|
96
|
-
} catch {
|
|
97
|
-
//
|
|
155
|
+
} catch (err) {
|
|
156
|
+
// Loud, because this fallback CANNOT carry a default export. Staying
|
|
157
|
+
// silent here is what let a bridge ship with its only export missing
|
|
158
|
+
// (see the `External` typedef). If the module has a default, declare
|
|
159
|
+
// `{ spec, hasDefault: true }`; if it genuinely has none, declare
|
|
160
|
+
// `{ spec, hasDefault: false }` to assert that and silence this.
|
|
161
|
+
this.warn(
|
|
162
|
+
`[import-map] could not enumerate "${pkg}" (${err.message.split('\n')[0]}). ` +
|
|
163
|
+
`Falling back to \`export *\`, which DROPS a default export. ` +
|
|
164
|
+
`Declare it as { spec: '${pkg}', hasDefault: true|false } to be explicit.`
|
|
165
|
+
)
|
|
98
166
|
return `export * from '${pkg}'`
|
|
99
167
|
}
|
|
100
168
|
},
|
|
@@ -105,10 +173,11 @@ export function importMapPlugin({
|
|
|
105
173
|
buildStart() {
|
|
106
174
|
if (!isBuild) return
|
|
107
175
|
for (const ext of externals) {
|
|
176
|
+
const spec = specOf(ext)
|
|
108
177
|
this.emitFile({
|
|
109
178
|
type: 'chunk',
|
|
110
|
-
id: `${IMPORT_MAP_PREFIX}${
|
|
111
|
-
fileName: `_importmap/${
|
|
179
|
+
id: `${IMPORT_MAP_PREFIX}${spec}`,
|
|
180
|
+
fileName: `_importmap/${bridgeFileName(spec)}`,
|
|
112
181
|
preserveSignature: 'exports-only',
|
|
113
182
|
})
|
|
114
183
|
}
|
|
@@ -125,7 +194,8 @@ export function importMapPlugin({
|
|
|
125
194
|
|
|
126
195
|
if (isBuild) {
|
|
127
196
|
for (const ext of externals) {
|
|
128
|
-
|
|
197
|
+
const spec = specOf(ext)
|
|
198
|
+
imports[spec] = `${basePath}_importmap/${bridgeFileName(spec)}`
|
|
129
199
|
}
|
|
130
200
|
} else if (devBridges) {
|
|
131
201
|
Object.assign(imports, devBridges)
|