RFC-0001: Frontend Initialization and Development Experience
- Status: Proposed
- Date: 2026-01-29
- Author: @anshpansuriya14
- Related: ADR-0005: Use Refine for Frontend
Summary
Define how third-party developers use Framework M with the Desk frontend without cloning the monorepo. Provide a "batteries included" experience similar to Frappe's bench start.
Problem Statement
Current State:
- Frontend (
/frontend) exists only in the monorepo - Developers must clone the repo to get the Desk UI
- No single command starts both backend and frontend
Desired State:
pip install framework-m
m new:project crm
cd crm
m start # Works immediately with bundled Desk
m start --with-frontend # For frontend customization
Proposed Solution
Option A: Bundled Static + npm Package (Recommended ✓)
┌─────────────────────────────────────────────────────────────────┐
│ Developer Experience │
├─────────────────────────────────────────────────────────────────┤
│ │
│ pip install framework-m │
│ │
│ ┌──────────────────────────┐ ┌──────────────────────────┐ │
│ │ m start │ │ m start --with-frontend │ │
│ │ │ │ │ │
│ │ Backend + Bundled Desk │ │ Backend + Vite Dev │ │
│ │ (Static files in pkg) │ │ (Uses @framework-m/desk)│ │
│ │ │ │ │ │
│ │ For: API users, │ │ For: Frontend devs, │ │
│ │ Quick start │ │ Customizers │ │
│ └──────────────────────────┘ └──────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
Components:
| Component | Location | Purpose | Registry |
|---|---|---|---|
| Bundled Desk | framework_m/static/ | Pre-built React app in PyPI package | PyPI |
| Frontend Template | framework_m/templates/frontend/ | Scaffold source for customization | PyPI (bundled) |
| npm Package | @framework-m/desk | Reusable Refine providers/components | GitLab npm |
GitLab npm Registry Benefits:
- Private by default (uses CI_JOB_TOKEN)
- No extra infrastructure or auth setup
- Same monorepo hosts both packages
- Can switch to public npmjs.com later via CLI
Option B: npm Package Only
- No bundled static, always require
--with-frontend - Simpler PyPI package, but worse DX for non-frontend developers
Option C: Docker-Only Distribution
- Ship Desk as a Docker container
- Good for production, bad for development
Recommended: Option A Implementation
Phase 1: m start with Bundled Desk
Pre-build Desk UI during release:
# .gitlab-ci.yml
build:frontend:
script:
- cd frontend && pnpm install && pnpm build
- cp -r dist libs/framework-m/src/framework_m/static/
Backend serves static:
# libs/framework-m-standard/src/adapters/web/app.py
from importlib.resources import files
STATIC_DIR = files("framework_m") / "static"
app.mount("/", StaticFilesMiddleware(directory=STATIC_DIR))
CLI command:
# libs/framework-m/src/framework_m/cli/serve.py
def start_command(
port: int = 8000,
reload: bool = False,
with_frontend: bool = False,
):
"""Start Framework M application."""
if with_frontend:
_start_with_frontend(port, reload)
else:
_start_backend_only(port, reload)
Phase 2: m start --with-frontend
Auto-scaffold frontend if not exists:
def _start_with_frontend(port: int, reload: bool):
frontend_dir = Path.cwd() / "frontend"
# Auto-scaffold if not exists
if not frontend_dir.exists():
_scaffold_frontend(frontend_dir)
# Start both processes
backend = _start_backend(port, reload)
frontend = _start_frontend_dev(frontend_dir)
# Wait for both
try:
backend.wait()
frontend.wait()
except KeyboardInterrupt:
backend.terminate()
frontend.terminate()
Scaffold from template:
def _scaffold_frontend(target: Path):
"""Copy frontend template from package."""
from importlib.resources import files, as_file
template = files("framework_m") / "templates/frontend"
with as_file(template) as template_dir:
shutil.copytree(template_dir, target)
print(f"✓ Scaffolded frontend to {target}")
print(" Installing dependencies...")
subprocess.run(["pnpm", "install"], cwd=target, check=True)
Phase 3: npm Package @framework-m/desk
Publish reusable components to GitLab npm registry:
// @framework-m/desk/src/index.ts
export { frameworkMDataProvider } from "./providers/data";
export { authProvider } from "./providers/auth";
export { liveProvider } from "./providers/live";
export { DocTypeList, DocTypeForm, DocTypeShow } from "./components";
export { useDocType, useMetadata, usePermissions } from "./hooks";
Template consumes npm package:
// templates/frontend/package.json
{
"dependencies": {
"@framework-m/desk": "^0.1.0",
"@refinedev/core": "^5.0.0",
"react": "^19.0.0"
}
}
GitLab npm Registry Setup:
# .npmrc in scaffolded frontend/
@framework-m:registry=https://gitlab.com/api/v4/projects/${CI_PROJECT_ID}/packages/npm/
# CI publishes with CI_JOB_TOKEN
# Users can optionally configure personal token for local dev
# Or switch to public npmjs.com later
Command Reference
| Command | Description |
|---|---|
m start | Start backend + serve bundled Desk |
m start --port 8000 | Custom port |
m start --reload | Dev mode with auto-reload |
m start --with-frontend | Start backend + frontend dev server |
m init:frontend | Scaffold frontend only (no start) |
m build | Build frontend for production |
Implementation Checklist
Phase 1: Bundled Desk (Required for v1)
- CI job to build frontend during release
- Include
static/inpyproject.tomlpackage data - Backend serves static files at
/ -
m startcommand in framework-m CLI
Phase 2: Frontend Scaffold (Required for customizers)
- Create
templates/frontend/in package -
m start --with-frontendcommand -
m init:frontendcommand - Process manager for backend + frontend
Phase 3: npm Package (Required for ecosystem)
- Extract Refine providers to
@framework-m/desk - Publish to npm public registry
- Template consumes npm package
- Documentation for frontend customization
Alternatives Considered
| Option | Pros | Cons |
|---|---|---|
| A: Bundled + npm Package (Chosen) | Best DX, reusable components, GitLab private registry | PyPI package ~10MB, users need GitLab token |
| B: npm Package Only | Smaller PyPI package | Poor DX for non-frontend devs |
| C: Docker Only | Clean separation | Bad for development |
| D: Clone Monorepo | Current state | Terrible DX |
Migration Path
For existing users:
# Before (current)
git clone https://gitlab.com/castlecraft/m.git
cd m/frontend && pnpm dev
# After (proposed)
pip install framework-m
m new:project myapp && cd myapp
m start # Just works
m start --with-frontend # If customizing
Files to Create/Modify
| File | Action | Description |
|---|---|---|
libs/framework-m/src/framework_m/cli/serve.py | NEW | m start command |
libs/framework-m/src/framework_m/cli/init.py | NEW | m init:frontend command |
libs/framework-m/src/framework_m/static/ | NEW | Pre-built Desk (from CI) |
libs/framework-m/src/framework_m/templates/frontend/ | NEW | Frontend scaffold template |
libs/framework-m-desk/ | NEW | npm package for Refine providers |
libs/framework-m/pyproject.toml | MODIFY | Include static + templates |
.gitlab-ci.yml | MODIFY | Add frontend + npm package build jobs |
Questions for Review
- Package Size: Is ~10MB increase in PyPI package acceptable for bundled Desk?
- npm Scope: Should we use
@framework-m/deskor@castlecraft/framework-m-desk? - Template Updates: How do users get template updates after scaffolding?