mirror of
https://github.com/hanzoai/id.git
synced 2026-10-16 18:38:15 +00:00
fix(id): emit per-brand brand.json in build (ESM require) + harden loadBrand
Root cause of the post-cutover white-screen: vite.config.ts is loaded as a
native ESM module where `require` is undefined, so the brandJsonPlugin's
`require.resolve('<pkg>/brand.json')` threw and the silent catch skipped
emitting EVERY brand.json. The SPA then served index.html (HTTP 200,
text/html) for /brand/<pkg>/brand.json, loadBrand did res.json() on HTML,
threw "Unexpected token '<'", and App's catch rendered that error as the
whole page — no login form ever mounted.
- vite.config.ts: build a createRequire(import.meta.url) resolver so the
brand.json subpath resolves under ESM. Build now emits all four
dist/brand/<pkg>/brand.json (verified: @hanzo brand.name = "Hanzo").
- brand.ts: read the response as text and JSON.parse explicitly; on
non-JSON throw a precise diagnostic instead of a cryptic SyntaxError,
and require the "brand" key.
- apps/web 0.1.8 -> 0.1.9.
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "@hanzo/id-web",
|
||||
"private": true,
|
||||
"version": "0.1.8",
|
||||
"description": "Hanzo ID — white-label login / signup / IDV portal. Vite + React 19 + @hanzo/gui. Same image serves hanzo.id / lux.id / zoo.id / pars.id.",
|
||||
"version": "0.1.9",
|
||||
"description": "Hanzo ID \u2014 white-label login / signup / IDV portal. Vite + React 19 + @hanzo/gui. Same image serves hanzo.id / lux.id / zoo.id / pars.id.",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -2,6 +2,12 @@ import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
import { resolve } from 'path'
|
||||
import { readFileSync, existsSync } from 'fs'
|
||||
import { createRequire } from 'module'
|
||||
|
||||
// Vite loads this config as a native ESM module, where `require` is undefined.
|
||||
// Build a CJS-style resolver bound to this file so `<pkg>/brand.json` subpath
|
||||
// resolution works in both `configureServer` and `generateBundle`.
|
||||
const require = createRequire(import.meta.url)
|
||||
|
||||
/**
|
||||
* Per-brand brand.json copy plugin.
|
||||
|
||||
@@ -19,8 +19,19 @@ export async function loadBrand(brandPackage: string): Promise<BrandContract> {
|
||||
const url = `/brand/${encodeURIComponent(brandPackage)}/brand.json`
|
||||
const res = await fetch(url, { cache: 'no-store' })
|
||||
if (!res.ok) throw new Error(`brand.json fetch failed: ${res.status} for ${brandPackage}`)
|
||||
const raw = await res.json()
|
||||
return raw.brand as BrandContract
|
||||
// The SPA server returns index.html (HTTP 200, text/html) for unknown
|
||||
// paths, so a missing brand.json yields HTML, not JSON. Read as text and
|
||||
// parse explicitly so we throw a precise error instead of a cryptic
|
||||
// "Unexpected token '<'" that whites-out the whole portal.
|
||||
const text = await res.text()
|
||||
let raw: { brand?: BrandContract }
|
||||
try {
|
||||
raw = JSON.parse(text) as { brand?: BrandContract }
|
||||
} catch {
|
||||
throw new Error(`brand.json for ${brandPackage} is not JSON (got ${res.headers.get('content-type') ?? 'unknown'}); is /brand/<pkg>/brand.json served?`)
|
||||
}
|
||||
if (!raw.brand) throw new Error(`brand.json for ${brandPackage} has no "brand" key`)
|
||||
return raw.brand
|
||||
}
|
||||
// Node: dynamic import (build step + SSR fallback)
|
||||
const mod = (await import(/* @vite-ignore */ `${brandPackage}/brand.json`, {
|
||||
|
||||
Reference in New Issue
Block a user