@uniweb/build 0.24.5 → 0.25.1
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 +9 -9
- package/src/schema.js +89 -11
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uniweb/build",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.25.1",
|
|
4
4
|
"description": "Build tooling for the Uniweb Component Web Platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -60,16 +60,16 @@
|
|
|
60
60
|
"sharp": "^0.35.3",
|
|
61
61
|
"yaml": "^2.5.0",
|
|
62
62
|
"@uniweb/schemas": "^0.2.10",
|
|
63
|
-
"@uniweb/
|
|
64
|
-
"@uniweb/projections": "^0.3.
|
|
65
|
-
"@uniweb/
|
|
66
|
-
"@uniweb/
|
|
63
|
+
"@uniweb/theming": "^0.1.15",
|
|
64
|
+
"@uniweb/projections": "^0.3.4",
|
|
65
|
+
"@uniweb/content-writer": "^0.3.4",
|
|
66
|
+
"@uniweb/semantic-parser": "^1.3.0"
|
|
67
67
|
},
|
|
68
68
|
"optionalDependencies": {
|
|
69
|
-
"@uniweb/
|
|
69
|
+
"@uniweb/semantic-parser": "^1.3.0",
|
|
70
70
|
"@uniweb/schemas": "^0.2.10",
|
|
71
|
-
"@uniweb/runtime": "^0.12.
|
|
72
|
-
"@uniweb/
|
|
71
|
+
"@uniweb/runtime": "^0.12.9",
|
|
72
|
+
"@uniweb/content-reader": "^1.2.4"
|
|
73
73
|
},
|
|
74
74
|
"peerDependencies": {
|
|
75
75
|
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0",
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
"@tailwindcss/vite": "^4.0.0",
|
|
79
79
|
"@vitejs/plugin-react": "^4.0.0 || ^5.0.0",
|
|
80
80
|
"vite-plugin-svgr": "^4.0.0",
|
|
81
|
-
"@uniweb/core": "^0.
|
|
81
|
+
"@uniweb/core": "^0.11.2"
|
|
82
82
|
},
|
|
83
83
|
"peerDependenciesMeta": {
|
|
84
84
|
"vite": {
|
package/src/schema.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* - Additional paths (via config): meta.js required for addressability
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import { readdir, readFile } from 'node:fs/promises'
|
|
13
|
+
import { readdir, readFile, rm } from 'node:fs/promises'
|
|
14
14
|
import { existsSync } from 'node:fs'
|
|
15
15
|
import { isFontVar } from '@uniweb/theming'
|
|
16
16
|
import { join, dirname, extname, basename } from 'node:path'
|
|
@@ -177,6 +177,70 @@ function warnMisplacedCapabilities(module, filePath) {
|
|
|
177
177
|
)
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
+
/**
|
|
181
|
+
* Import a foundation config, transpiling it when Node alone cannot.
|
|
182
|
+
*
|
|
183
|
+
* ⛔ A foundation config routinely imports JSX, and Node cannot parse it.
|
|
184
|
+
* `defaultInsets` and `xref` take REACT COMPONENTS — that is what they are
|
|
185
|
+
* for — so `main.js` legitimately reads:
|
|
186
|
+
*
|
|
187
|
+
* import { buildXrefRegistry, Ref } from '@uniweb/kit/xref'
|
|
188
|
+
*
|
|
189
|
+
* kit ships source, so that resolves to `Ref.jsx` and a bare `import()`
|
|
190
|
+
* throws `Unknown file extension ".jsx"`. The config is fine; the loader was
|
|
191
|
+
* the problem.
|
|
192
|
+
*
|
|
193
|
+
* Fast path first: a plain `import()`, which is free and covers the common
|
|
194
|
+
* case of a config that is pure data. Only when Node rejects the SYNTAX do we
|
|
195
|
+
* pay for a bundle — the same fallback shape Vite uses for `vite.config.ts`.
|
|
196
|
+
*
|
|
197
|
+
* React stays external because Node imports it happily and it is the bulk of
|
|
198
|
+
* the graph; everything else is inlined so no `.jsx` survives to be resolved
|
|
199
|
+
* at run time. The temp file is written beside the config so bare specifiers
|
|
200
|
+
* still resolve from the project's own `node_modules`.
|
|
201
|
+
*/
|
|
202
|
+
async function importFoundationConfig(filePath) {
|
|
203
|
+
const href = pathToFileURL(filePath).href
|
|
204
|
+
try {
|
|
205
|
+
return await import(href)
|
|
206
|
+
} catch (error) {
|
|
207
|
+
if (!isUnparseableByNode(error)) throw error
|
|
208
|
+
|
|
209
|
+
const esbuild = (await import('esbuild')).default ?? (await import('esbuild'))
|
|
210
|
+
const outfile = join(
|
|
211
|
+
dirname(filePath),
|
|
212
|
+
`.${basename(filePath)}.uniweb-config.${process.pid}.mjs`,
|
|
213
|
+
)
|
|
214
|
+
try {
|
|
215
|
+
await esbuild.build({
|
|
216
|
+
entryPoints: [filePath],
|
|
217
|
+
outfile,
|
|
218
|
+
bundle: true,
|
|
219
|
+
format: 'esm',
|
|
220
|
+
platform: 'node',
|
|
221
|
+
jsx: 'automatic',
|
|
222
|
+
// Node can load these as-is, and they dominate the graph.
|
|
223
|
+
external: ['react', 'react/*', 'react-dom', 'react-dom/*'],
|
|
224
|
+
logLevel: 'silent',
|
|
225
|
+
})
|
|
226
|
+
return await import(pathToFileURL(outfile).href)
|
|
227
|
+
} finally {
|
|
228
|
+
await rm(outfile, { force: true }).catch(() => {})
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Does this error mean "Node cannot read this file", as opposed to "the
|
|
235
|
+
* config threw"? Only the former is worth re-trying through a bundler —
|
|
236
|
+
* re-running a config that threw on its own would just throw again.
|
|
237
|
+
*/
|
|
238
|
+
function isUnparseableByNode(error) {
|
|
239
|
+
if (error instanceof SyntaxError) return true
|
|
240
|
+
const code = error?.code
|
|
241
|
+
return code === 'ERR_UNKNOWN_FILE_EXTENSION' || code === 'ERR_UNSUPPORTED_DIR_IMPORT'
|
|
242
|
+
}
|
|
243
|
+
|
|
180
244
|
export async function loadFoundationConfig(srcDir) {
|
|
181
245
|
let filePath = null
|
|
182
246
|
for (const name of FOUNDATION_FILE_NAMES) {
|
|
@@ -188,18 +252,32 @@ export async function loadFoundationConfig(srcDir) {
|
|
|
188
252
|
}
|
|
189
253
|
if (!filePath) return {}
|
|
190
254
|
|
|
255
|
+
let module
|
|
191
256
|
try {
|
|
192
|
-
|
|
193
|
-
warnMisplacedCapabilities(module, filePath)
|
|
194
|
-
// Support both default export and named exports
|
|
195
|
-
return {
|
|
196
|
-
...module.default,
|
|
197
|
-
vars: inferFontVarTypes(module.vars || module.default?.vars),
|
|
198
|
-
defaultLayout: module.default?.defaultLayout,
|
|
199
|
-
}
|
|
257
|
+
module = await importFoundationConfig(filePath)
|
|
200
258
|
} catch (error) {
|
|
201
|
-
|
|
202
|
-
|
|
259
|
+
// ⛔ NEVER degrade to `{}` here. Everything a foundation declares — `vars`,
|
|
260
|
+
// `xref`, `defaultInsets`, `name` — arrives through this one call, so an
|
|
261
|
+
// empty return silently produces a foundation with NO THEME VARIABLES. The
|
|
262
|
+
// symptom is a site whose `px-[var(--section-padding-x)]` resolves to 0 and
|
|
263
|
+
// whose `max-w-[var(--width-content)]` resolves to `none`: content sprawls
|
|
264
|
+
// edge to edge and every layout token is gone, with nothing in the output
|
|
265
|
+
// naming a cause. Measured 2026-08-23 on a real site that had been shipping
|
|
266
|
+
// that way for three weeks behind a single `console.warn`.
|
|
267
|
+
throw new Error(
|
|
268
|
+
`Failed to load foundation config ${filePath}: ${error.message}\n` +
|
|
269
|
+
` Everything the foundation declares (vars, xref, defaultInsets, name) comes from this file,\n` +
|
|
270
|
+
` so the build cannot continue without it — a partial load would emit a site with no theme variables.`,
|
|
271
|
+
{ cause: error },
|
|
272
|
+
)
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
warnMisplacedCapabilities(module, filePath)
|
|
276
|
+
// Support both default export and named exports
|
|
277
|
+
return {
|
|
278
|
+
...module.default,
|
|
279
|
+
vars: inferFontVarTypes(module.vars || module.default?.vars),
|
|
280
|
+
defaultLayout: module.default?.defaultLayout,
|
|
203
281
|
}
|
|
204
282
|
}
|
|
205
283
|
|