diff --git a/README.md b/README.md
index 6464762..da46a7e 100644
--- a/README.md
+++ b/README.md
@@ -14,7 +14,7 @@
[](https://www.npmjs.com/package/create-polyglot)
Scaffold a modern polyglot microservices monorepo in seconds.
-Generate Node.js, Python (FastAPI), Go, Java (Spring Boot), and Next.js frontend services with optional Turborepo or Nx presets, Docker Compose, shared packages, plugin hooks, and a persisted configuration file.
+Generate Node.js, Python (FastAPI), Go, Java (Spring Boot), Next.js, plus optional modern frontend frameworks (Remix, Astro, SvelteKit) with Turborepo or Nx presets, Docker Compose, shared packages, plugin hooks, and a persisted configuration file.
@@ -54,7 +54,7 @@ Use it to prototype architectures, onboard teams faster, or spin up reproducible
## Features
-- 🚀 Rapid polyglot monorepo scaffolding (Node.js, Python/FastAPI, Go, Java Spring Boot, Next.js)
+- 🚀 Rapid polyglot monorepo scaffolding (Node.js, Python/FastAPI, Go, Java Spring Boot, Next.js, Remix, Astro, SvelteKit)
- 🧩 Optional presets: Turborepo, Nx, or Basic runner
- 🐳 Automatic Dockerfile + Docker Compose generation
- 🛠 Interactive wizard (or fully non-interactive via flags)
@@ -174,7 +174,7 @@ create-polyglot dev --docker
## Init Options
| Flag | Description |
|------|-------------|
-| `-s, --services ` | Comma separated services: `node,python,go,java,frontend` |
+| `-s, --services ` | Comma separated services: `node,python,go,java,frontend,remix,astro,sveltekit` |
| `--preset ` | `turborepo`, `nx`, or `basic` (default auto -> asks) |
| `--git` | Initialize git repo & initial commit |
| `--no-install` | Skip dependency installation step |
diff --git a/bin/index.js b/bin/index.js
index ea73f0a..3116051 100755
--- a/bin/index.js
+++ b/bin/index.js
@@ -55,7 +55,7 @@ program
.description('Add a new service or plugin')
.argument('', 'service | plugin')
.argument('', 'Name of the service or plugin')
- .option('--type ', 'Service type (node|python|go|java|frontend)')
+ .option('--type ', 'Service type (node|python|go|java|frontend|remix|astro|sveltekit)')
.option('--lang ', '(Deprecated) Alias of --type')
.option('--port ', 'Service port')
.option('--yes', 'Non-interactive defaults')
@@ -74,7 +74,10 @@ program
{ title: 'Python', value: 'python' },
{ title: 'Go', value: 'go' },
{ title: 'Java', value: 'java' },
- { title: 'Frontend (Next.js)', value: 'frontend' }
+ { title: 'Frontend (Next.js)', value: 'frontend' },
+ { title: 'Remix', value: 'remix' },
+ { title: 'Astro', value: 'astro' },
+ { title: 'SvelteKit', value: 'sveltekit' }
] });
type = ans.type;
}
@@ -83,7 +86,7 @@ program
if (ans.port) port = Number(ans.port);
}
}
- const defaultPorts = { frontend: 3000, node: 3001, go: 3002, java: 3003, python: 3004 };
+ const defaultPorts = { frontend: 3000, node: 3001, go: 3002, java: 3003, python: 3004, remix: 3005, astro: 3006, sveltekit: 3007 };
if (!type) throw new Error('Service type required');
if (!port) port = defaultPorts[type];
await addService(projectDir, { type, name, port }, opts);
diff --git a/bin/lib/scaffold.js b/bin/lib/scaffold.js
index e7a3542..39e3206 100644
--- a/bin/lib/scaffold.js
+++ b/bin/lib/scaffold.js
@@ -139,12 +139,15 @@ export async function scaffoldMonorepo(projectNameArg, options) {
{ title: 'Python (FastAPI)', value: 'python' },
{ title: 'Go (Fiber-like)', value: 'go' },
{ title: 'Java (Spring Boot)', value: 'java' },
- { title: 'Frontend (Next.js)', value: 'frontend' }
+ { title: 'Frontend (Next.js)', value: 'frontend' },
+ { title: 'Remix', value: 'remix' },
+ { title: 'Astro', value: 'astro' },
+ { title: 'SvelteKit', value: 'sveltekit' }
];
const templateMap = { java: 'spring-boot' };
let services = [];
const reservedNames = new Set(['scripts','packages','apps','node_modules','docker','compose','compose.yaml']);
- const defaultPorts = { frontend: 3000, node: 3001, go: 3002, java: 3003, python: 3004 };
+ const defaultPorts = { frontend: 3000, node: 3001, go: 3002, java: 3003, python: 3004, remix: 3005, astro: 3006, sveltekit: 3007 };
if (options.services) {
const validValues = allServiceChoices.map(c => c.value);
@@ -330,29 +333,58 @@ export async function scaffoldMonorepo(projectNameArg, options) {
console.log(chalk.yellow(`⚠️ create-next-app failed: ${e.message}. Using template.`));
}
}
+ // Strict external generators for new frameworks: abort on failure (no internal fallback yet)
+ if (svcType === 'remix') {
+ try {
+ console.log(chalk.cyan('⚙️ Running Remix generator (create-react-router with basic template)...'));
+ await execa('npx', ['--yes', 'create-react-router@latest', '.', '--template', 'remix-run/react-router/examples/basic', '--no-git-init', '--no-install'], { cwd: dest, stdio: 'inherit' });
+ usedGenerator = true;
+ } catch (e) {
+ console.error(chalk.red(`❌ create-react-router failed: ${e.message}. Aborting scaffold for this service.`));
+ continue; // skip creating this service
+ }
+ } else if (svcType === 'astro') {
+ try {
+ console.log(chalk.cyan('⚙️ Running Astro generator (create-astro)...'));
+ await execa('npx', ['--yes', 'create-astro@latest', '.', '--template', 'minimal', '--no-install', '--no-git'], { cwd: dest, stdio: 'inherit' });
+ usedGenerator = true;
+ } catch (e) {
+ console.error(chalk.red(`❌ create-astro failed: ${e.message}. Aborting scaffold for this service.`));
+ continue;
+ }
+ } else if (svcType === 'sveltekit') {
+ try {
+ console.log(chalk.cyan('⚙️ Running SvelteKit generator (sv create)...'));
+ await execa('npx', ['sv', 'create', '.', '--template', 'minimal', '--types', 'ts', '--no-install', '--no-add-ons'], { cwd: dest, stdio: 'inherit' });
+ usedGenerator = true;
+ } catch (e) {
+ console.error(chalk.red(`❌ sv create failed: ${e.message}. Aborting scaffold for this service.`));
+ continue;
+ }
+ }
if (!usedGenerator) {
- if (await fs.pathExists(src) && (await fs.readdir(src)).length > 0) {
- await fs.copy(src, dest, { overwrite: true });
-
- // Dynamically update the name field in package.json for Node.js services
- if (svcType === 'node') {
- const packageJsonPath = path.join(dest, 'package.json');
- if (await fs.pathExists(packageJsonPath)) {
- const packageJson = await fs.readJSON(packageJsonPath);
- packageJson.name = `@${projectNameArg || 'polyglot'}/${svcName}`; // Ensure unique name
- await fs.writeJSON(packageJsonPath, packageJson, { spaces: 2 });
+ // Non-framework services use internal templates
+ if (svcType !== 'remix' && svcType !== 'astro' && svcType !== 'sveltekit') {
+ if (await fs.pathExists(src) && (await fs.readdir(src)).length > 0) {
+ await fs.copy(src, dest, { overwrite: true });
+ if (svcType === 'node') {
+ const packageJsonPath = path.join(dest, 'package.json');
+ if (await fs.pathExists(packageJsonPath)) {
+ const packageJson = await fs.readJSON(packageJsonPath);
+ packageJson.name = `@${projectNameArg || 'polyglot'}/${svcName}`;
+ await fs.writeJSON(packageJsonPath, packageJson, { spaces: 2 });
+ }
}
- }
-
- if (templateFolder === 'spring-boot') {
- const propTxt = path.join(dest, 'src/main/resources/application.properties.txt');
- const prop = path.join(dest, 'src/main/resources/application.properties');
- if (await fs.pathExists(propTxt) && !(await fs.pathExists(prop))) {
- await fs.move(propTxt, prop);
+ if (templateFolder === 'spring-boot') {
+ const propTxt = path.join(dest, 'src/main/resources/application.properties.txt');
+ const prop = path.join(dest, 'src/main/resources/application.properties');
+ if (await fs.pathExists(propTxt) && !(await fs.pathExists(prop))) {
+ await fs.move(propTxt, prop);
+ }
}
+ } else {
+ await fs.writeFile(path.join(dest, 'README.md'), `# ${svcName} service\n\nScaffolded by create-polyglot.`);
}
- } else {
- await fs.writeFile(path.join(dest, 'README.md'), `# ${svcName} service\n\nScaffolded by create-polyglot.`);
}
}
@@ -444,7 +476,7 @@ export async function scaffoldMonorepo(projectNameArg, options) {
const dockerfile = path.join(svcDir, 'Dockerfile');
if (!(await fs.pathExists(dockerfile))) {
let dockerContent = '';
- if (svcObj.type === 'node' || svcObj.type === 'frontend') {
+ if (svcObj.type === 'node' || svcObj.type === 'frontend' || ['remix','astro','sveltekit'].includes(svcObj.type)) {
dockerContent = `# ${svcObj.name} (${svcObj.type}) service\nFROM node:20-alpine AS deps\nWORKDIR /app\nCOPY package*.json ./\nRUN npm install --omit=dev || true\nCOPY . .\nEXPOSE ${port}\nCMD [\"npm\", \"run\", \"dev\"]\n`;
} else if (svcObj.type === 'python') {
dockerContent = `FROM python:3.12-slim\nWORKDIR /app\nCOPY requirements.txt ./\nRUN pip install --no-cache-dir -r requirements.txt\nCOPY . .\nEXPOSE ${port}\nCMD [\"uvicorn\", \"app.main:app\", \"--host\", \"0.0.0.0\", \"--port\", \"${port}\"]\n`;
diff --git a/docs/.vitepress/config.mjs b/docs/.vitepress/config.mjs
index 55b2dd3..7903a19 100644
--- a/docs/.vitepress/config.mjs
+++ b/docs/.vitepress/config.mjs
@@ -6,7 +6,8 @@ export default defineConfig({
// sets VITEPRESS_BASE automatically; local dev stays '/'.
base: process.env.VITEPRESS_BASE || '/',
title: 'create-polyglot',
- description: 'Scaffold polyglot microservice monorepos (Node, Python, Go, Java, Next.js)',
+ // Updated to reflect newly supported frontend frameworks (Remix, Astro, SvelteKit)
+ description: 'Scaffold polyglot microservice monorepos (Node, Python, Go, Java, Next.js, Remix, Astro, SvelteKit)',
head: [
['meta', { name: 'theme-color', content: '#3e62ad' }],
['meta', { name: 'og:title', content: 'create-polyglot' }],
@@ -26,7 +27,8 @@ export default defineConfig({
{ text: 'Getting Started', link: '/guide/getting-started' },
{ text: 'Presets', link: '/guide/presets' },
{ text: 'Docker & Compose', link: '/guide/docker' },
- { text: 'Extending (New Service)', link: '/guide/extending-service' },
+ { text: 'Extending (New Service)', link: '/guide/extending-service' },
+ { text: 'Frontend Frameworks', link: '/guide/frontend-frameworks' },
{ text: 'Service Logs', link: '/logs-feature' },
{ text: 'Plugin System', link: '/plugin-system' },
{ text: 'Service Controls', link: '/service-controls-feature' },
@@ -46,7 +48,8 @@ export default defineConfig({
{ text: 'Python', link: '/templates/python' },
{ text: 'Go', link: '/templates/go' },
{ text: 'Java (Spring Boot)', link: '/templates/java' },
- { text: 'Frontend (Next.js)', link: '/templates/frontend' }
+ { text: 'Frontend (Next.js)', link: '/templates/frontend' },
+ { text: 'Frontend Frameworks (Remix/Astro/SvelteKit)', link: '/guide/frontend-frameworks' }
]
},
socialLinks: [
diff --git a/docs/guide/frontend-frameworks.md b/docs/guide/frontend-frameworks.md
new file mode 100644
index 0000000..290e6f6
--- /dev/null
+++ b/docs/guide/frontend-frameworks.md
@@ -0,0 +1,112 @@
+# Modern Frontend Framework Support
+
+create-polyglot can scaffold additional frontend frameworks beyond the default Next.js.
+
+## Supported Frameworks
+
+- **Next.js** (internal template or `create-next-app` when `--frontend-generator` is passed)
+- **Remix** (via `npx create-remix@latest . --template remix`)
+- **Astro** (via `npx create-astro@latest -- --template minimal`)
+- **SvelteKit** (via `npx sv create .`; falls back to `npx create-svelte@latest . --template skeleton` if the new command fails)
+
+## Selecting Frameworks
+
+Specify them in the `--services` list during init or with `create-polyglot add service`:
+
+```bash
+create-polyglot init my-stack -s remix,astro,sveltekit --yes
+create-polyglot add service docs --type astro --port 3030
+```
+
+## Ports
+
+Default ports:
+
+```text
+remix: 3005
+astro: 3006
+sveltekit: 3007
+```
+
+Override any port via `type:name:port` syntax:
+
+```bash
+create-polyglot init web-app -s remix:web:3100,astro:site:3200,sveltekit:kit:3300 --yes
+```
+
+## Generation Behavior
+
+| Framework | Generation Method | Fallback | Notes |
+|-----------|-------------------|----------|-------|
+| Remix | `create-remix` | None (skip on failure) | Skipped if generator errors. |
+| Astro | `create-astro` | None (skip on failure) | Uses `--template minimal`. |
+| SvelteKit | `sv create` | `create-svelte` | Deprecation handled gracefully. |
+
+Failed generators log an error and the service is skipped (not partially scaffolded) to avoid broken directories.
+
+## Docker
+
+All Node-based frameworks (Remix, Astro, SvelteKit) reuse the generic Node Dockerfile pattern:
+
+```Dockerfile
+FROM node:20-alpine AS deps
+WORKDIR /app
+COPY package*.json ./
+RUN npm install --omit=dev || true
+COPY . .
+EXPOSE
+CMD ["npm", "run", "dev"]
+```
+
+Adjust after generation if framework-specific build or preview commands are desired.
+
+## Service Manager
+
+Runtime start uses the detected package manager and `npm run dev` (or equivalent) for these frameworks. Ensure the generator produces a `dev` script. If not, add one manually.
+
+## Caveats & Future Plans
+
+- No internal fallback templates (kept lean). Potential future flag: `--allow-fallback`.
+- Post-generation customization (eslint, prettier) left to user.
+- May add automatic build scripts & production Docker variants later.
+
+## Example Full Init
+
+```bash
+npx create-polyglot init multi-web -s node,remix,astro,sveltekit --git --yes
+```
+
+After scaffold:
+
+```bash
+cd multi-web
+npm run list:services
+create-polyglot dev
+```
+
+## Troubleshooting
+
+| Issue | Cause | Fix |
+|-------|-------|-----|
+| Generator network failure | Offline or registry issue | Retry with stable connection; consider adding fallback templates. |
+| Missing dev script | Generator changed defaults | Add `"dev": ""` to `package.json`. |
+| Port collision | Duplicate specified port | Re-run with adjusted port list or edit `polyglot.json` then restart. |
+| SvelteKit deprecation warning | Using legacy command | Ensure `sv` is available; keep fallback until ecosystem fully migrates. |
+
+## Updating Existing Workspace
+
+Add a new framework to an existing project:
+
+```bash
+create-polyglot add service ui-new --type sveltekit --port 3400
+```
+
+Remove it later:
+
+```bash
+create-polyglot remove service ui-new --yes
+```
+
+---
+
+Need another framework (e.g., Nuxt, SolidStart)? Open an issue or PR with a proposed generator command.
diff --git a/docs/templates/index.md b/docs/templates/index.md
index c3f7104..02867bb 100644
--- a/docs/templates/index.md
+++ b/docs/templates/index.md
@@ -1,11 +1,10 @@
# Templates Overview
-All service templates live in `templates/` and are copied verbatim (with a Spring Boot properties rename step). Current templates:
+Service scaffolding comes from internal templates or official generators:
-- `node/` Express + `/health` endpoint
-- `python/` FastAPI + `/health`
-- `go/` net/http server + `/health`
-- `spring-boot/` Java with `/health` REST controller
-- `frontend/` Next.js minimal (or `create-next-app` output when generator used)
+- Internal templates (copied verbatim): `node/`, `python/`, `go/`, `spring-boot/`, `frontend/` (minimal Next.js when not using `--frontend-generator`).
+- External generators (no internal fallback unless added later): Remix (`create-remix`), Astro (`create-astro`), SvelteKit (`sv create` with legacy `create-svelte` fallback).
-Each template should stay minimal & dependency-light.
+Spring Boot still performs a post-copy rename of `application.properties.txt` → `application.properties`.
+
+Keep internal templates minimal & dependency-light; external generator outputs are left intact.
diff --git a/package-lock.json b/package-lock.json
index 490302b..5a1320f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "create-polyglot",
- "version": "1.17.0",
+ "version": "1.18.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "create-polyglot",
- "version": "1.17.0",
+ "version": "1.18.0",
"license": "MIT",
"dependencies": {
"chalk": "^5.6.2",
diff --git a/package.json b/package.json
index 9f20131..14315f0 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "create-polyglot",
- "version": "1.17.0",
+ "version": "1.18.0",
"description": "Scaffold polyglot microservice monorepos with built-in templates for Node, Python, Go, and more.",
"main": "bin/index.js",
"scripts": {