Files
rfc/next.config.mjs
zeekayandHanzo Dev bc3b6b5b66 fix: resolve .web.js before the native sibling, unbreaking the gui build
The WIP commit added the gui runtime (GuiProvider + an @hanzo/ui Button) and the
build stopped compiling:

  Module parse failed: Unexpected token (13:12)
  ./…/react-native/Libraries/Utilities/codegenNativeComponent.js
  Import trace:
    @hanzo/ui/dist/backends/gui/checkbox.js
    → @hanzogui/lucide-icons-2 → react-native-svg/lib/module/fabric/…

A react-native package publishes `Foo.js` beside `Foo.web.js` and imports
`./Foo` extensionless, leaving the choice to the resolver. Webpack's default
extension order has no `.web.*` in it, so on web it silently picked the NATIVE
file — react-native-svg's fabric components, which import react-native Flow
source no bundler can parse. The `react-native$` alias never applied: the
specifier being resolved was `./ReactNativeSVG`, not `react-native`.

Prepending the web extensions is the whole fix; the same four lines carry the
same comment in app-std/next.config.mjs, which hit this first.

  $ pnpm build
  ✓ Compiled successfully in 44s
  ✓ Generating static pages (11/11)
  ✓ Exporting (2/2)

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-04 00:49:32 -07:00

54 lines
2.2 KiB
JavaScript

// Plain .mjs, not next.config.ts — deliberately.
//
// Next loads a TypeScript config through the TypeScript compiler API, so a .ts
// config would make config LOADING depend on which TypeScript is installed. As
// ESM there is nothing to resolve: Next reads this file natively and the type
// comes from the JSDoc annotation.
//
// That dependency is not hypothetical. Next also reads `compilerOptions.paths`
// through `ts.parseJsonConfigFileContent`, and TypeScript 7 — the native Go
// compiler — ships no JS API at all (`require('typescript')` is just
// `{version, versionMajorMinor}`). Under 7 every `@/…` import fails to resolve
// and the type-check step silently disappears, so this repo stays on 5.x until
// Next stops linking the API. See LLM.md.
/** @type {import('next').NextConfig} */
const config = {
reactStrictMode: true,
// The whole site is prerendered from markdown on disk; there is no runtime.
output: 'export',
images: { unoptimized: true },
trailingSlash: true,
// @hanzo/gui and the component layer on top of it ship untranspiled ESM that
// still names `react-native`. On web that name IS react-native-web, which is
// the alias below; `transpilePackages` is what makes Next compile the source
// rather than hand raw ESM to the server runtime.
transpilePackages: ['@hanzo/gui', '@hanzo/ui', '@hanzogui/config', 'react-native-web'],
webpack: (webpackConfig) => {
webpackConfig.resolve.alias = {
...webpackConfig.resolve.alias,
'react-native$': 'react-native-web',
};
// PREPEND the web extensions. A react-native package publishes `Foo.js`
// beside `Foo.web.js` and imports `./Foo` extensionless, leaving the choice
// to the resolver — so the default extension order silently picks the
// NATIVE file on web. That is how @hanzo/ui reaches react-native-svg's
// fabric `*NativeComponent.js`, which imports react-native Flow source
// webpack cannot parse ("Module parse failed: Unexpected token").
webpackConfig.resolve.extensions = [
'.web.tsx',
'.web.ts',
'.web.jsx',
'.web.js',
...webpackConfig.resolve.extensions,
];
return webpackConfig;
},
};
export default config;