191 lines
6.3 KiB
Python
191 lines
6.3 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
Nuclear fix: make every Payload-touching page force-dynamic,
|
||
remove all generateStaticParams from those pages,
|
||
and reduce Next.js build workers to prevent DB pool exhaustion.
|
||
"""
|
||
import os, re, glob
|
||
|
||
def read(path):
|
||
with open(path, 'r') as f: return f.read()
|
||
|
||
def write(path, content):
|
||
with open(path, 'w') as f: f.write(content)
|
||
|
||
def remove_generate_static_params(content):
|
||
"""Remove the entire generateStaticParams function from a file."""
|
||
# Match: export async function generateStaticParams() { ... }
|
||
# Using a simple brace-counting approach
|
||
pattern = r'export async function generateStaticParams\(\)'
|
||
match = re.search(pattern, content)
|
||
if not match:
|
||
return content, False
|
||
|
||
start = match.start()
|
||
# Find the opening brace
|
||
brace_start = content.index('{', match.end())
|
||
depth = 0
|
||
i = brace_start
|
||
while i < len(content):
|
||
if content[i] == '{':
|
||
depth += 1
|
||
elif content[i] == '}':
|
||
depth -= 1
|
||
if depth == 0:
|
||
end = i + 1
|
||
break
|
||
i += 1
|
||
|
||
# Remove the function (and any leading newlines before it)
|
||
before = content[:start].rstrip('\n') + '\n'
|
||
after = content[end:].lstrip('\n')
|
||
return before + after, True
|
||
|
||
def ensure_force_dynamic(content):
|
||
"""Add export const dynamic = 'force-dynamic' if not present."""
|
||
if "dynamic = 'force-dynamic'" in content or 'dynamic = "force-dynamic"' in content:
|
||
return content, False
|
||
|
||
# Remove conflicting revalidate settings
|
||
content = re.sub(r"export const revalidate = \d+\n?", '', content)
|
||
|
||
# Add after the last import line
|
||
lines = content.split('\n')
|
||
last_import = 0
|
||
for i, line in enumerate(lines):
|
||
if line.startswith('import '):
|
||
last_import = i
|
||
|
||
lines.insert(last_import + 1, '')
|
||
lines.insert(last_import + 2, "export const dynamic = 'force-dynamic'")
|
||
lines.insert(last_import + 3, 'export const dynamicParams = true')
|
||
return '\n'.join(lines), True
|
||
|
||
# ── Find all page.tsx files that use Payload ──────────────────────────────
|
||
print("=== Nuclear Payload Build Fix ===\n")
|
||
|
||
page_files = glob.glob('src/app/**/page.tsx', recursive=True)
|
||
print(f"Found {len(page_files)} page files total\n")
|
||
|
||
payload_pages = []
|
||
for path in page_files:
|
||
content = read(path)
|
||
if 'payload-config' in content or 'getPayload' in content or 'getCachedGlobal' in content:
|
||
payload_pages.append(path)
|
||
|
||
print(f"→ {len(payload_pages)} pages use Payload:\n")
|
||
for p in payload_pages:
|
||
print(f" {p}")
|
||
|
||
print()
|
||
|
||
# ── Fix each Payload page ─────────────────────────────────────────────────
|
||
for path in payload_pages:
|
||
content = read(path)
|
||
changed = False
|
||
|
||
# Remove generateStaticParams
|
||
new_content, removed = remove_generate_static_params(content)
|
||
if removed:
|
||
print(f" ✓ Removed generateStaticParams: {path}")
|
||
changed = True
|
||
content = new_content
|
||
|
||
# Ensure force-dynamic
|
||
new_content, added = ensure_force_dynamic(content)
|
||
if added:
|
||
print(f" ✓ Added force-dynamic: {path}")
|
||
changed = True
|
||
content = new_content
|
||
|
||
if changed:
|
||
write(path, content)
|
||
else:
|
||
print(f" - Already OK: {path}")
|
||
|
||
# ── Update next.config to limit workers ──────────────────────────────────
|
||
print("\n→ Looking for next.config...")
|
||
next_configs = glob.glob('next.config.*')
|
||
if next_configs:
|
||
nc_path = next_configs[0]
|
||
nc = read(nc_path)
|
||
if 'workerThreads' not in nc and 'cpus' not in nc:
|
||
# Add experimental config to limit parallel workers
|
||
nc = nc.replace(
|
||
'const nextConfig',
|
||
'/** @type {import("next").NextConfig} */\nconst nextConfig'
|
||
) if '/** @type' not in nc else nc
|
||
|
||
# Insert experimental block before the closing of nextConfig object
|
||
if 'experimental:' not in nc:
|
||
nc = re.sub(
|
||
r'(const nextConfig\s*=\s*\{)',
|
||
r'\1\n experimental: {\n workerThreads: false,\n cpus: 1,\n },',
|
||
nc
|
||
)
|
||
write(nc_path, nc)
|
||
print(f" ✓ Limited build workers in {nc_path}")
|
||
else:
|
||
print(f" - experimental block already exists in {nc_path}, add cpus: 1 manually")
|
||
else:
|
||
print(f" - Worker limits already set in {nc_path}")
|
||
else:
|
||
# Create a basic next.config.js
|
||
print(" - No next.config found, creating next.config.js with worker limit...")
|
||
# Try to find and read existing config
|
||
existing = None
|
||
for name in ['next.config.js', 'next.config.mjs', 'next.config.ts']:
|
||
if os.path.exists(name):
|
||
existing = name
|
||
break
|
||
|
||
if not existing:
|
||
# Check if there's a withPayload wrapper we need to preserve
|
||
# Write a minimal config
|
||
config_content = '''import { withPayload } from '@payloadcms/next'
|
||
|
||
/** @type {import('next').NextConfig} */
|
||
const nextConfig = {
|
||
experimental: {
|
||
workerThreads: false,
|
||
cpus: 1,
|
||
},
|
||
}
|
||
|
||
export default withPayload(nextConfig)
|
||
'''
|
||
write('next.config.js', config_content)
|
||
print(" ✓ Created next.config.js")
|
||
|
||
# ── Verify generateStaticParams is gone from all payload pages ────────────
|
||
print("\n→ Verifying...")
|
||
remaining = []
|
||
for path in payload_pages:
|
||
content = read(path)
|
||
if 'generateStaticParams' in content:
|
||
remaining.append(path)
|
||
|
||
if remaining:
|
||
print("\n ⚠ generateStaticParams still present in:")
|
||
for p in remaining:
|
||
print(f" {p}")
|
||
print(" These need manual removal.")
|
||
else:
|
||
print(" ✓ No generateStaticParams remaining in Payload pages")
|
||
|
||
# ── Check for any other pages with generateStaticParams ───────────────────
|
||
all_with_gsp = []
|
||
for path in page_files:
|
||
content = read(path)
|
||
if 'generateStaticParams' in content and path not in payload_pages:
|
||
all_with_gsp.append(path)
|
||
|
||
if all_with_gsp:
|
||
print(f"\n ℹ Non-Payload pages with generateStaticParams (likely fine):")
|
||
for p in all_with_gsp:
|
||
print(f" {p}")
|
||
|
||
print("\n=== Done! ===")
|
||
print("\nNow run:")
|
||
print(" rm -rf .next && npm run build")
|