Files
pack/core/providers/node/react_router.go
T
zeekay 7844a60aff refactor(pack): modernize to github.com/hanzoai/pack and remove upstream branding
- Migrate module path to github.com/hanzoai/pack across all Go packages
- Remove legacy upstream docs and competitor brand references
- Modernize LLM.md and mise.toml for pristine Hanzo Pack builder
2026-09-04 00:40:42 -07:00

80 lines
3.0 KiB
Go

package node
import (
"regexp"
"strings"
"github.com/hanzoai/pack/core/generate"
)
const (
DefaultReactRouterOutputDirectory = "build/client/"
DefaultReactRouterStartCommand = "react-router-serve ./build/server/index.js"
ReactRouterConfigJS = "react-router.config.js"
ReactRouterConfigTS = "react-router.config.ts"
)
var reactRouterSPAConfigRegex = regexp.MustCompile(`(?m)(?:^|[{,])\s*['"]?ssr['"]?\s*:\s*false\b`)
func (p *NodeProvider) getReactRouterConfigFileContents(ctx *generate.GenerateContext) string {
_, contents, _ := ctx.App.ReadFirstFileOf(ReactRouterConfigJS, ReactRouterConfigTS)
return contents
}
// SPA mode requires an explicit opt-out because React Router enables SSR by default.
func (p *NodeProvider) isReactRouterSPA(ctx *generate.GenerateContext) bool {
if !p.isReactRouter(ctx) {
return false
}
configContent := p.getReactRouterConfigFileContents(ctx)
return reactRouterSPAConfigRegex.MatchString(configContent)
}
// getReactRouterOutputDirectory attempts to read the output directory from react-router.config.{ts,js} by extracting
// the buildDirectory option in the config object.
func (p *NodeProvider) getReactRouterOutputDirectory(ctx *generate.GenerateContext) string {
configContent := p.getReactRouterConfigFileContents(ctx)
if configContent != "" {
// TODO this field can be an expression `buildDirectory: "build/" + process.env.NODE_ENV,` so we should tighten
// up the regex here (and in the vite provider, since vite config can have expressions too)
// `buildDirectory: "custom-directory/"`
buildDirRegex := regexp.MustCompile(`buildDirectory:\s*['"](.+?)['"]`)
if matches := buildDirRegex.FindStringSubmatch(configContent); len(matches) > 1 {
return matches[1]
}
}
return DefaultReactRouterOutputDirectory
}
func (p *NodeProvider) isReactRouter(ctx *generate.GenerateContext) bool {
return p.isReactRouterPackage(p.workspace.Root, ctx)
}
// isReactRouterPackage detects React Router packages in workspace environments by checking for:
// - presence of a react-router.config.{ts,js} file in the package directory
// - a build script containing "react-router build"
// - having a dependency on "@react-router/dev"
// - ensuring a build script exists so there is an actual output directory
func (p *NodeProvider) isReactRouterPackage(pkg *WorkspacePackage, ctx *generate.GenerateContext) bool {
if pkg == nil || pkg.PackageJson == nil {
return false
}
rrConfigJS := ReactRouterConfigJS
rrConfigTS := ReactRouterConfigTS
if pkg.Path != "" {
rrConfigJS = pkg.Path + "/" + ReactRouterConfigJS
rrConfigTS = pkg.Path + "/" + ReactRouterConfigTS
}
hasRRConfig := ctx.App.HasFile(rrConfigJS) || ctx.App.HasFile(rrConfigTS)
hasBuildCommand := pkg.PackageJson.HasScript("build")
hasRRBuildCommand := strings.Contains(strings.ToLower(pkg.PackageJson.GetScript("build")), "react-router build")
hasRRPackage := pkg.PackageJson.hasDependency("@react-router/dev")
return hasBuildCommand && (hasRRConfig || hasRRBuildCommand || hasRRPackage)
}