Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
725376d
Add first-class mobile scaffolding
Marve10s May 22, 2026
95ba8d6
Add Elixir Phoenix ecosystem
Marve10s May 22, 2026
3a7a076
Fix mobile defaults in template tests
Marve10s May 22, 2026
a63b791
Fix ecosystem count tests
Marve10s May 22, 2026
98c43bd
Avoid regex in Elixir app name normalization
Marve10s May 22, 2026
c72126e
Fix CI ecosystem assertions and Fresh Deno deps
Marve10s May 22, 2026
bdecc2e
Fix mobile defaults in CLI tests
Marve10s May 22, 2026
9b13822
Fix Fresh Deno 2.8 runtime tasks
Marve10s May 22, 2026
00546bd
Merge branch 'main' into ibrahim/elixir-phoenix-ecosystem
Marve10s May 22, 2026
447a2a4
Fix smoke test template regressions
Marve10s May 22, 2026
36ebded
Fix Fresh Deno workspace config
Marve10s May 22, 2026
dcc37e2
Address Elixir review feedback
Marve10s May 23, 2026
4f64670
Make React Native a standalone ecosystem
Marve10s May 23, 2026
5970272
Merge remote-tracking branch 'origin/main' into ibrahim/mobile-first-…
Marve10s May 23, 2026
3b1a753
Fix mobile deep linking defaults and CI Bun pin
Marve10s May 23, 2026
f347c9c
Fix TypeScript builder command default detection
Marve10s May 23, 2026
b4c7494
Fix React Native smoke verification
Marve10s May 23, 2026
a57f946
Preserve TypeScript native frontend compatibility
Marve10s May 23, 2026
f7b9fa4
Fix React Native builder ecosystem categories
Marve10s May 23, 2026
2fd9c9a
Fix mobile smoke template failures
Marve10s May 23, 2026
1d87ba5
Validate builder tech icons
Marve10s May 23, 2026
709b176
Redesign builder UI and unify lime/background theming
Marve10s May 23, 2026
47af194
Merge PR #201 into mobile PR
Marve10s May 23, 2026
05102e2
Merge mobile PR into Elixir PR
Marve10s May 23, 2026
82545ac
fix cli defaults and elixir auth scaffold
Marve10s May 23, 2026
a728e2a
clean up CI bun pins and ecosystem docs
Marve10s May 23, 2026
a8ab877
skip elixir auth migration without auth
Marve10s May 23, 2026
1a3a3c3
Fix ModelFusion fets AI smoke
Marve10s May 23, 2026
b09a014
Fix virtual native defaults
Marve10s May 23, 2026
6967d10
Add plain Elixir scaffold support
Marve10s May 23, 2026
faccaf9
Add Elixir builder presets
Marve10s May 23, 2026
8a9bf6d
Fix React Native Jest scaffold deps
Marve10s May 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .github/workflows/e2e-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ jobs:
with:
node-version: "22"

- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "21"

- uses: astral-sh/setup-uv@v5

- uses: actions/cache@v4
with:
path: |
Expand Down Expand Up @@ -90,6 +97,13 @@ jobs:
with:
node-version: "22"

- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "21"

- uses: astral-sh/setup-uv@v5

- uses: actions/cache@v4
with:
path: |
Expand Down Expand Up @@ -148,6 +162,13 @@ jobs:
with:
node-version: "22"

- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "21"

- uses: astral-sh/setup-uv@v5

- uses: actions/cache@v4
with:
path: |
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pr-size.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ jobs:
runs-on: ubuntu-24.04
permissions:
contents: read
issues: read
issues: write
pull-requests: write
concurrency:
group: pr-size-${{ github.event.pull_request.number }}
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ jobs:
- name: Validate Builder Tech Links
run: bun run --cwd apps/web validate:tech-links

- name: Validate Builder Tech Icons
run: bun run --cwd apps/web validate:tech-icons

- name: Run Lint
run: bun run lint

Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

<div align="center">

**Scaffold production-ready fullstack apps in seconds. Browse 450+ tools across five ecosystems — the CLI wires everything together.**
**Scaffold production-ready fullstack apps in seconds. Browse 450+ tools across seven ecosystems — the CLI wires everything together.**

<br>

Expand All @@ -35,7 +35,7 @@
Most scaffolding tools lock you into one framework and one opinion. Better Fullstack doesn't.

- **450+ tools** — frontend, backend, database, auth, payments, AI, DevOps, and more
- **5 ecosystems** — TypeScript, Rust, Python, Go, Java — with more coming
- **7 ecosystems** — TypeScript, React Native, Rust, Python, Go, Java, Elixir — with more coming
- **Visual builder** — configure your stack in the browser, get a ready-to-run CLI command
- **Wired for you** — no manual glue code; every picked integration is preconfigured and working out of the box

Expand Down Expand Up @@ -113,8 +113,8 @@ Better Fullstack is organized around the decisions that matter: pick an ecosyste
Only the relevant options surface for the stack you pick.
</td>
<td width="33%">
<strong>5 ecosystems</strong><br>
TypeScript, Rust, Python, Go, Java.
<strong>7 ecosystems</strong><br>
TypeScript, React Native, Rust, Python, Go, Java, Elixir.
</td>
<td width="33%">
<strong>One command</strong><br>
Expand Down
4 changes: 2 additions & 2 deletions apps/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Configure your stack visually — pick every option from a UI, preview your choi
## Features

- **425 options** — frontend, backend, database, auth, payments, AI, DevOps, and more
- **5 ecosystems** — TypeScript, Rust, Python, Go, Java
- **7 ecosystems** — TypeScript, React Native, Rust, Python, Go, Java, Elixir
- **Visual builder** — configure your stack in the browser
- **Wired for you** — every picked integration is preconfigured and working out of the box

Expand All @@ -42,7 +42,7 @@ Configure your stack visually — pick every option from a UI, preview your choi
--yes # Accept all defaults
--yolo # Scaffold a random stack — good for exploring
--template <name> # Use a preset (t3, mern, pern, uniwind)
--ecosystem <lang> # Start in typescript, rust, python, go, or java mode
--ecosystem <lang> # Start in typescript, react-native, rust, python, go, java, or elixir mode
--version-channel # Dependency channel: stable, latest, beta
--no-git # Skip git initialization
--no-install # Skip dependency installation
Expand Down
66 changes: 65 additions & 1 deletion apps/cli/src/create-command-input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,35 @@ import {
DatabaseSetupSchema,
DirectoryConflictSchema,
EcosystemSchema,
ElixirApiSchema,
ElixirAuthSchema,
ElixirCachingSchema,
ElixirDeploySchema,
ElixirEmailSchema,
ElixirHttpSchema,
ElixirJobsSchema,
ElixirJsonSchema,
ElixirObservabilitySchema,
ElixirOrmSchema,
ElixirQualitySchema,
ElixirRealtimeSchema,
ElixirTestingSchema,
ElixirValidationSchema,
ElixirWebFrameworkSchema,
EffectSchema,
EmailSchema,
ExamplesSchema,
FeatureFlagsSchema,
FileStorageSchema,
FileUploadSchema,
FormsSchema,
MobileDeepLinkingSchema,
MobileNavigationSchema,
MobileOTASchema,
MobilePushSchema,
MobileStorageSchema,
MobileTestingSchema,
MobileUISchema,
FrontendSchema,
GoApiSchema,
GoAuthSchema,
Expand Down Expand Up @@ -98,7 +120,9 @@ export const CreateCommandOptionsSchema = z.object({
.optional()
.default(false)
.describe("Preview generated file tree without writing to disk"),
ecosystem: EcosystemSchema.optional().describe("Language ecosystem (typescript, rust, python, go, or java)"),
ecosystem: EcosystemSchema.optional().describe(
"Language ecosystem (typescript, react-native, rust, python, go, java, or elixir)",
),
database: DatabaseSchema.optional(),
orm: ORMSchema.optional(),
auth: AuthSchema.optional(),
Expand All @@ -123,6 +147,13 @@ export const CreateCommandOptionsSchema = z.object({
i18n: I18nSchema.optional().describe("Internationalization (i18n) library"),
search: SearchSchema.optional().describe("Search engine solution"),
fileStorage: FileStorageSchema.optional().describe("File storage solution (S3, R2)"),
mobileNavigation: MobileNavigationSchema.optional().describe("Mobile navigation (expo-router, react-navigation)"),
mobileUI: MobileUISchema.optional().describe("Mobile UI (tamagui, gluestack-ui, uniwind, unistyles)"),
mobileStorage: MobileStorageSchema.optional().describe("Mobile storage (mmkv)"),
mobileTesting: MobileTestingSchema.optional().describe("Mobile testing (maestro, react-native-testing-library)"),
mobilePush: MobilePushSchema.optional().describe("Mobile push notifications (expo-notifications)"),
mobileOTA: MobileOTASchema.optional().describe("Mobile OTA updates (expo-updates)"),
mobileDeepLinking: MobileDeepLinkingSchema.optional().describe("Mobile deep linking (expo-linking)"),
frontend: z.array(FrontendSchema).optional(),
astroIntegration: AstroIntegrationSchema.optional().describe(
"Astro UI framework integration (react, vue, svelte, solid)",
Expand Down Expand Up @@ -219,6 +250,39 @@ export const CreateCommandOptionsSchema = z.object({
.array(JavaTestingLibrariesSchema)
.optional()
.describe("Java testing libraries"),
elixirWebFramework: ElixirWebFrameworkSchema.optional().describe(
"Elixir web framework (phoenix, phoenix-live-view, none)",
),
elixirOrm: ElixirOrmSchema.optional().describe("Elixir ORM/database (ecto, ecto-sql, none)"),
elixirAuth: ElixirAuthSchema.optional().describe(
"Elixir auth (phx-gen-auth, ueberauth, guardian, none)",
),
elixirApi: ElixirApiSchema.optional().describe("Elixir API layer (rest, absinthe, none)"),
elixirRealtime: ElixirRealtimeSchema.optional().describe(
"Elixir realtime (channels, presence, pubsub, live-view-streams, none)",
),
elixirJobs: ElixirJobsSchema.optional().describe("Elixir jobs (oban, quantum, none)"),
elixirValidation: ElixirValidationSchema.optional().describe(
"Elixir validation (ecto-changesets, nimble-options, none)",
),
elixirHttp: ElixirHttpSchema.optional().describe("Elixir HTTP client (req, finch, none)"),
elixirJson: ElixirJsonSchema.optional().describe("Elixir JSON library (jason, none)"),
elixirEmail: ElixirEmailSchema.optional().describe("Elixir email library (swoosh, none)"),
elixirCaching: ElixirCachingSchema.optional().describe(
"Elixir caching (cachex, nebulex, none)",
),
elixirObservability: ElixirObservabilitySchema.optional().describe(
"Elixir observability (telemetry, opentelemetry, prom_ex, none)",
),
elixirTesting: ElixirTestingSchema.optional().describe(
"Elixir testing (ex_unit, mox, bypass, wallaby, none)",
),
elixirQuality: ElixirQualitySchema.optional().describe(
"Elixir code quality (credo, dialyxir, sobelow, none)",
),
elixirDeploy: ElixirDeploySchema.optional().describe(
"Elixir deploy target (docker, fly, gigalixir, mix-release, none)",
),
aiDocs: z
.array(AiDocsSchema)
.optional()
Expand Down
74 changes: 73 additions & 1 deletion apps/cli/src/helpers/core/command-handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,56 @@ export interface CreateHandlerOptions {
silent?: boolean;
}

function getYesBaseConfig(flagConfig: Partial<ProjectConfig>): ProjectConfig {
const baseConfig = getDefaultConfig();

if (flagConfig.ecosystem !== "react-native") {
return baseConfig;
}

return {
...baseConfig,
backend: "none",
runtime: "none",
frontend: ["native-bare"],
addons: [],
examples: [],
auth: "none",
payments: "none",
email: "none",
fileUpload: "none",
effect: "none",
dbSetup: "none",
api: "none",
webDeploy: "none",
serverDeploy: "none",
cssFramework: "none",
uiLibrary: "none",
stateManagement: "none",
forms: "none",
testing: "none",
realtime: "none",
jobQueue: "none",
animation: "none",
logging: "none",
observability: "none",
featureFlags: "none",
analytics: "none",
cms: "none",
caching: "none",
i18n: "none",
search: "none",
fileStorage: "none",
mobileNavigation: "expo-router",
mobileUI: "none",
mobileStorage: "none",
mobileTesting: "none",
mobilePush: "none",
mobileOTA: "none",
mobileDeepLinking: "none",
};
}

function shouldPromptForVersionChannel(input: CreateInput & { projectName?: string }): boolean {
if (input.yes || input.versionChannel !== undefined || isSilent()) {
return false;
Expand Down Expand Up @@ -170,6 +220,13 @@ export async function createProjectHandler(
featureFlags: "none",
analytics: "none",
fileStorage: "none",
mobileNavigation: "none",
mobileUI: "none",
mobileStorage: "none",
mobileTesting: "none",
mobilePush: "none",
mobileOTA: "none",
mobileDeepLinking: "none",
pythonWebFramework: "none",
pythonOrm: "none",
pythonValidation: "none",
Expand All @@ -191,6 +248,21 @@ export async function createProjectHandler(
javaAuth: "none",
javaLibraries: [],
javaTestingLibraries: [],
elixirWebFramework: "none",
elixirOrm: "none",
elixirAuth: "none",
elixirApi: "none",
elixirRealtime: "none",
elixirJobs: "none",
elixirValidation: "none",
elixirHttp: "none",
elixirJson: "none",
elixirEmail: "none",
elixirCaching: "none",
elixirObservability: "none",
elixirTesting: "none",
elixirQuality: "none",
elixirDeploy: "none",
aiDocs: [],
} satisfies ProjectConfig,
reproducibleCommand: "",
Expand Down Expand Up @@ -246,7 +318,7 @@ export async function createProjectHandler(
const flagConfig = processProvidedFlagsWithoutValidation(cliInput, finalBaseName);

config = {
...getDefaultConfig(),
...getYesBaseConfig(flagConfig),
...flagConfig,
projectName: finalBaseName,
projectDir: finalResolvedPath,
Expand Down
12 changes: 10 additions & 2 deletions apps/cli/src/helpers/core/create-project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
runMavenTests,
runUvSync,
runGoModTidy,
runMixCompile,
} from "./install-dependencies";
import { displayPostInstallInstructions } from "./post-installation";

Expand Down Expand Up @@ -66,8 +67,11 @@ export async function createProject(options: ProjectConfig, cliInput: CreateProj

if (!isSilent()) log.success("Project template successfully scaffolded!");

// Skip npm/pnpm/bun install for Rust/Python projects (they use cargo/uv)
if (options.install && options.ecosystem === "typescript") {
// Skip npm/pnpm/bun install for Rust/Python/Go/Java projects (they use native toolchains)
if (
options.install &&
(options.ecosystem === "typescript" || options.ecosystem === "react-native")
) {
await installDependencies({
projectDir,
packageManager: options.packageManager,
Expand Down Expand Up @@ -102,6 +106,10 @@ export async function createProject(options: ProjectConfig, cliInput: CreateProj
}
}

if (options.install && options.ecosystem === "elixir") {
await runMixCompile({ projectDir });
}

await initializeGit(projectDir, options.git);

if (!isSilent()) {
Expand Down
25 changes: 25 additions & 0 deletions apps/cli/src/helpers/core/install-dependencies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,3 +163,28 @@ export async function runGradleTests({ projectDir }: { projectDir: string }) {
}
}
}

export async function runMixCompile({ projectDir }: { projectDir: string }) {
const s = spinner();

try {
s.start("Running mix deps.get and mix compile...");

await $({
cwd: projectDir,
stderr: "inherit",
})`mix deps.get`;

await $({
cwd: projectDir,
stderr: "inherit",
})`mix compile`;

s.stop("Elixir dependencies installed and project compiled");
} catch (error) {
s.stop(pc.red("mix compile failed"));
if (error instanceof Error) {
consola.error(pc.red(`Mix error: ${error.message}`));
}
}
}
Loading
Loading