@dartix-software-solutions/create-fullstack-app 2.0.12 → 2.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/backends/fastapi/templates/app/core/config.py.hbs +13 -13
- package/dist/backends/fastapi/templates/app/main.py.hbs +31 -31
- package/dist/backends/fastapi/templates/app/routers/health.py.hbs +8 -8
- package/dist/backends/fastapi/templates/app/routers/users.py.hbs +30 -30
- package/dist/backends/fastapi/templates/app/schemas/user.py.hbs +12 -12
- package/dist/backends/fastapi/templates/requirements.txt.hbs +6 -6
- package/dist/bin/create-fullstack-app.js +1 -1
- package/dist/{chunk-36CSXSDC.js → chunk-Z7MIDMEN.js} +101 -40
- package/dist/databases/mongodb/templates/connection.ts.hbs +15 -15
- package/dist/databases/mongodb/templates/health-check.ts.hbs +11 -11
- package/dist/databases/mysql/templates/connection.ts.hbs +14 -14
- package/dist/databases/mysql/templates/health-check.ts.hbs +12 -12
- package/dist/databases/postgres/templates/connection.ts.hbs +14 -14
- package/dist/databases/postgres/templates/health-check.ts.hbs +12 -12
- package/dist/databases/redis/templates/connection.ts.hbs +14 -14
- package/dist/databases/redis/templates/health-check.ts.hbs +10 -10
- package/dist/databases/sqlite/templates/connection.ts.hbs +12 -12
- package/dist/databases/sqlite/templates/health-check.ts.hbs +10 -10
- package/dist/frontend-extras/react-table/templates/components/SampleTable.tsx.hbs +25 -25
- package/dist/frontend-extras/react-table/templates/data/sample-table-data.ts.hbs +4 -4
- package/dist/frontends/mobile/expo/templates/App.react-navigation.tsx.hbs +10 -10
- package/dist/frontends/mobile/expo/templates/app/(auth)/_layout.tsx.hbs +5 -5
- package/dist/frontends/mobile/expo/templates/app/(auth)/login.tsx.hbs +3 -3
- package/dist/frontends/mobile/expo/templates/app/(auth)/register.tsx.hbs +3 -3
- package/dist/frontends/mobile/expo/templates/app/(tabs)/_layout.tsx.hbs +11 -11
- package/dist/frontends/mobile/expo/templates/app/(tabs)/index.tsx.hbs +3 -3
- package/dist/frontends/mobile/expo/templates/app/(tabs)/profile.tsx.hbs +3 -3
- package/dist/frontends/mobile/expo/templates/app/(tabs)/settings.tsx.hbs +3 -3
- package/dist/frontends/mobile/expo/templates/app/+not-found.tsx.hbs +14 -14
- package/dist/frontends/mobile/expo/templates/app/_layout.tsx.hbs +11 -11
- package/dist/frontends/mobile/expo/templates/app/index.tsx.hbs +29 -29
- package/dist/frontends/mobile/expo/templates/types/router.ts.hbs +10 -10
- package/dist/frontends/mobile/react-native-cli/templates/app.json.hbs +4 -4
- package/dist/frontends/mobile/react-native-cli/templates/babel.config.js.hbs +14 -14
- package/dist/frontends/mobile/react-native-cli/templates/components/Avatar.tsx.hbs +29 -29
- package/dist/frontends/mobile/react-native-cli/templates/types/env.d.ts.hbs +3 -3
- package/dist/frontends/web/angular/templates/index.html.hbs +12 -12
- package/dist/frontends/web/angular/templates/main.ts.hbs +5 -5
- package/dist/frontends/web/angular/templates/pages/dashboard/dashboard.component.html.hbs +3 -3
- package/dist/frontends/web/angular/templates/pages/dashboard/dashboard.component.ts.hbs +10 -10
- package/dist/frontends/web/angular/templates/styles.css.hbs +11 -11
- package/dist/frontends/web/vue/templates/App.vue.hbs +9 -9
- package/dist/frontends/web/vue/templates/components/Layout.vue.hbs +17 -17
- package/dist/frontends/web/vue/templates/components/LoadingSpinner.vue.hbs +7 -7
- package/dist/frontends/web/vue/templates/components/Navbar.vue.hbs +24 -24
- package/dist/frontends/web/vue/templates/composables/useApi.ts.hbs +8 -8
- package/dist/frontends/web/vue/templates/composables/useAuth.ts.hbs +20 -20
- package/dist/frontends/web/vue/templates/env.d.ts.hbs +1 -1
- package/dist/frontends/web/vue/templates/index.html.hbs +12 -12
- package/dist/frontends/web/vue/templates/lib/config.ts.hbs +2 -2
- package/dist/frontends/web/vue/templates/main.ts.hbs +9 -9
- package/dist/frontends/web/vue/templates/pages/About.vue.hbs +6 -6
- package/dist/frontends/web/vue/templates/pages/Dashboard.vue.hbs +21 -21
- package/dist/frontends/web/vue/templates/pages/Home.vue.hbs +19 -19
- package/dist/frontends/web/vue/templates/pages/Login.vue.hbs +34 -34
- package/dist/frontends/web/vue/templates/pages/NotFound.vue.hbs +7 -7
- package/dist/frontends/web/vue/templates/router/index.ts.hbs +34 -34
- package/dist/frontends/web/vue/templates/stores/app.ts.hbs +8 -8
- package/dist/frontends/web/vue/templates/tsconfig.json.hbs +15 -15
- package/dist/frontends/web/vue/templates/vite.config.ts.hbs +6 -6
- package/dist/index.js +1 -1
- package/dist/orms/prisma/templates/injection-module-import.hbs +1 -1
- package/dist/orms/prisma/templates/injection-module-register.hbs +1 -1
- package/dist/orms/prisma/templates/models/user.ts.hbs +3 -3
- package/dist/orms/prisma/templates/prisma.module.ts.hbs +9 -9
- package/dist/orms/prisma/templates/prisma.service.ts.hbs +13 -13
- package/dist/orms/prisma/templates/services/user.service.nestjs.ts.hbs +16 -16
- package/dist/orms/prisma/templates/services/user.service.ts.hbs +10 -10
- package/package.json +1 -1
- package/dist/plugins/devops/docker/templates/.dockerignore.hbs +0 -5
- package/dist/plugins/devops/gitlab-ci/templates/.gitlab-ci.yml.hbs +0 -14
- package/dist/plugins/devtools/eslint/templates/.eslintignore.hbs +0 -3
- package/dist/plugins/devtools/lint-staged/templates/.lintstagedrc.hbs +0 -5
- package/dist/plugins/devtools/prettier/templates/.prettierignore.hbs +0 -4
- package/dist/plugins/devtools/prettier/templates/.prettierrc.hbs +0 -6
- package/dist/plugins/mobile-navigation/expo-router/templates/.gitkeep +0 -0
- package/dist/plugins/testing/detox/templates/.detoxrc.js.hbs +0 -7
- package/dist/plugins/testing/maestro/templates/.maestro/home.yaml.hbs +0 -4
- package/dist/plugins/testing/maestro/templates/.maestro/login.yaml.hbs +0 -5
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class Settings(BaseSettings):
|
|
5
|
-
model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8", extra="ignore")
|
|
6
|
-
|
|
7
|
-
app_name: str = "{{projectName}} API"
|
|
8
|
-
port: int = 8000
|
|
9
|
-
cors_origin: str = "http://localhost:5173"
|
|
10
|
-
debug: bool = True
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
settings = Settings()
|
|
1
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Settings(BaseSettings):
|
|
5
|
+
model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8", extra="ignore")
|
|
6
|
+
|
|
7
|
+
app_name: str = "{{projectName}} API"
|
|
8
|
+
port: int = 8000
|
|
9
|
+
cors_origin: str = "http://localhost:5173"
|
|
10
|
+
debug: bool = True
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
settings = Settings()
|
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
from contextlib import asynccontextmanager
|
|
2
|
-
|
|
3
|
-
from fastapi import FastAPI
|
|
4
|
-
from fastapi.middleware.cors import CORSMiddleware
|
|
5
|
-
|
|
6
|
-
from app.core.config import settings
|
|
7
|
-
from app.routers import health, users
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
@asynccontextmanager
|
|
11
|
-
async def lifespan(_app: FastAPI):
|
|
12
|
-
yield
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
app = FastAPI(
|
|
16
|
-
title=settings.app_name,
|
|
17
|
-
lifespan=lifespan,
|
|
18
|
-
docs_url="/docs" if settings.debug else None,
|
|
19
|
-
redoc_url="/redoc" if settings.debug else None,
|
|
20
|
-
)
|
|
21
|
-
|
|
22
|
-
app.add_middleware(
|
|
23
|
-
CORSMiddleware,
|
|
24
|
-
allow_origins=[settings.cors_origin],
|
|
25
|
-
allow_credentials=True,
|
|
26
|
-
allow_methods=["*"],
|
|
27
|
-
allow_headers=["*"],
|
|
28
|
-
)
|
|
29
|
-
|
|
30
|
-
app.include_router(health.router, tags=["health"])
|
|
31
|
-
app.include_router(users.router, prefix="/users", tags=["users"])
|
|
1
|
+
from contextlib import asynccontextmanager
|
|
2
|
+
|
|
3
|
+
from fastapi import FastAPI
|
|
4
|
+
from fastapi.middleware.cors import CORSMiddleware
|
|
5
|
+
|
|
6
|
+
from app.core.config import settings
|
|
7
|
+
from app.routers import health, users
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@asynccontextmanager
|
|
11
|
+
async def lifespan(_app: FastAPI):
|
|
12
|
+
yield
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
app = FastAPI(
|
|
16
|
+
title=settings.app_name,
|
|
17
|
+
lifespan=lifespan,
|
|
18
|
+
docs_url="/docs" if settings.debug else None,
|
|
19
|
+
redoc_url="/redoc" if settings.debug else None,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
app.add_middleware(
|
|
23
|
+
CORSMiddleware,
|
|
24
|
+
allow_origins=[settings.cors_origin],
|
|
25
|
+
allow_credentials=True,
|
|
26
|
+
allow_methods=["*"],
|
|
27
|
+
allow_headers=["*"],
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
app.include_router(health.router, tags=["health"])
|
|
31
|
+
app.include_router(users.router, prefix="/users", tags=["users"])
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
from fastapi import APIRouter
|
|
2
|
-
|
|
3
|
-
router = APIRouter()
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
@router.get("/health")
|
|
7
|
-
async def health():
|
|
8
|
-
return {"status": "ok"}
|
|
1
|
+
from fastapi import APIRouter
|
|
2
|
+
|
|
3
|
+
router = APIRouter()
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@router.get("/health")
|
|
7
|
+
async def health():
|
|
8
|
+
return {"status": "ok"}
|
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
from fastapi import APIRouter, HTTPException, status
|
|
2
|
-
|
|
3
|
-
from app.schemas.user import UserCreate, UserRead
|
|
4
|
-
|
|
5
|
-
router = APIRouter()
|
|
6
|
-
|
|
7
|
-
_db: list[UserRead] = [
|
|
8
|
-
UserRead(id=1, email="demo@example.com", name="Demo User"),
|
|
9
|
-
]
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
@router.get("/", response_model=list[UserRead])
|
|
13
|
-
async def list_users() -> list[UserRead]:
|
|
14
|
-
return list(_db)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
@router.get("/{user_id}", response_model=UserRead)
|
|
18
|
-
async def get_user(user_id: int) -> UserRead:
|
|
19
|
-
for user in _db:
|
|
20
|
-
if user.id == user_id:
|
|
21
|
-
return user
|
|
22
|
-
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="User not found")
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
@router.post("/", response_model=UserRead, status_code=status.HTTP_201_CREATED)
|
|
26
|
-
async def create_user(body: UserCreate) -> UserRead:
|
|
27
|
-
next_id = max((u.id for u in _db), default=0) + 1
|
|
28
|
-
user = UserRead(id=next_id, email=body.email, name=body.name)
|
|
29
|
-
_db.append(user)
|
|
30
|
-
return user
|
|
1
|
+
from fastapi import APIRouter, HTTPException, status
|
|
2
|
+
|
|
3
|
+
from app.schemas.user import UserCreate, UserRead
|
|
4
|
+
|
|
5
|
+
router = APIRouter()
|
|
6
|
+
|
|
7
|
+
_db: list[UserRead] = [
|
|
8
|
+
UserRead(id=1, email="demo@example.com", name="Demo User"),
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@router.get("/", response_model=list[UserRead])
|
|
13
|
+
async def list_users() -> list[UserRead]:
|
|
14
|
+
return list(_db)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@router.get("/{user_id}", response_model=UserRead)
|
|
18
|
+
async def get_user(user_id: int) -> UserRead:
|
|
19
|
+
for user in _db:
|
|
20
|
+
if user.id == user_id:
|
|
21
|
+
return user
|
|
22
|
+
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="User not found")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@router.post("/", response_model=UserRead, status_code=status.HTTP_201_CREATED)
|
|
26
|
+
async def create_user(body: UserCreate) -> UserRead:
|
|
27
|
+
next_id = max((u.id for u in _db), default=0) + 1
|
|
28
|
+
user = UserRead(id=next_id, email=body.email, name=body.name)
|
|
29
|
+
_db.append(user)
|
|
30
|
+
return user
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
from pydantic import BaseModel, EmailStr, Field
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class UserRead(BaseModel):
|
|
5
|
-
id: int = Field(..., ge=1)
|
|
6
|
-
email: EmailStr
|
|
7
|
-
name: str
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class UserCreate(BaseModel):
|
|
11
|
-
email: EmailStr
|
|
12
|
-
name: str
|
|
1
|
+
from pydantic import BaseModel, EmailStr, Field
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class UserRead(BaseModel):
|
|
5
|
+
id: int = Field(..., ge=1)
|
|
6
|
+
email: EmailStr
|
|
7
|
+
name: str
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class UserCreate(BaseModel):
|
|
11
|
+
email: EmailStr
|
|
12
|
+
name: str
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
fastapi>=0.115.0
|
|
2
|
-
uvicorn[standard]>=0.32.0
|
|
3
|
-
pydantic-settings>=2.6.0
|
|
4
|
-
python-dotenv>=1.0.0
|
|
5
|
-
gunicorn>=23.0.0
|
|
6
|
-
email-validator>=2.2.0
|
|
1
|
+
fastapi>=0.115.0
|
|
2
|
+
uvicorn[standard]>=0.32.0
|
|
3
|
+
pydantic-settings>=2.6.0
|
|
4
|
+
python-dotenv>=1.0.0
|
|
5
|
+
gunicorn>=23.0.0
|
|
6
|
+
email-validator>=2.2.0
|
|
@@ -1970,11 +1970,9 @@ var SingleAppLayout = class {
|
|
|
1970
1970
|
if (isFullstack) {
|
|
1971
1971
|
const feDir = context.hasMobile ? "mobile" : "client";
|
|
1972
1972
|
const scripts = {
|
|
1973
|
-
dev:
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
build: `cd ${feDir} && npm run build && cd ../server && npm run build`,
|
|
1977
|
-
lint: `cd ${feDir} && npm run lint && cd ../server && npm run lint`
|
|
1973
|
+
dev: "npm run dev --workspaces --if-present --parallel",
|
|
1974
|
+
build: "npm run build --workspaces --if-present",
|
|
1975
|
+
lint: "npm run lint --workspaces --if-present"
|
|
1978
1976
|
};
|
|
1979
1977
|
if (context.hasHusky) {
|
|
1980
1978
|
scripts.prepare = "husky";
|
|
@@ -1986,10 +1984,8 @@ var SingleAppLayout = class {
|
|
|
1986
1984
|
name: context.projectName,
|
|
1987
1985
|
version: "0.1.0",
|
|
1988
1986
|
private: true,
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
concurrently: "^9.1.0"
|
|
1992
|
-
}
|
|
1987
|
+
workspaces: [feDir, "server"],
|
|
1988
|
+
scripts
|
|
1993
1989
|
},
|
|
1994
1990
|
null,
|
|
1995
1991
|
2
|
|
@@ -2931,34 +2927,36 @@ function resolveDependencies(plugins, packageJsonTargets, context) {
|
|
|
2931
2927
|
});
|
|
2932
2928
|
}
|
|
2933
2929
|
for (const plugin of plugins) {
|
|
2934
|
-
const
|
|
2935
|
-
if (
|
|
2930
|
+
const targetKeys = resolveTargetsForPlugin(plugin, packageJsonTargets, context);
|
|
2931
|
+
if (targetKeys.length === 0) {
|
|
2936
2932
|
logger.warn(
|
|
2937
2933
|
`Could not resolve target package.json for plugin "${plugin.meta.id}" (category: ${plugin.meta.category})`
|
|
2938
2934
|
);
|
|
2939
2935
|
continue;
|
|
2940
2936
|
}
|
|
2941
|
-
const
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
if (merged.dependencies[dep.name]
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2937
|
+
for (const targetKey of targetKeys) {
|
|
2938
|
+
const merged = result.get(targetKey);
|
|
2939
|
+
if (!merged) continue;
|
|
2940
|
+
for (const dep of plugin.meta.deps) {
|
|
2941
|
+
if (merged.dependencies[dep.name]) {
|
|
2942
|
+
if (merged.dependencies[dep.name] !== dep.version) {
|
|
2943
|
+
logger.warn(
|
|
2944
|
+
`Dependency version conflict for "${dep.name}": "${merged.dependencies[dep.name]}" vs "${dep.version}" (from ${plugin.meta.id})`
|
|
2945
|
+
);
|
|
2946
|
+
}
|
|
2949
2947
|
}
|
|
2948
|
+
merged.dependencies[dep.name] = dep.version;
|
|
2950
2949
|
}
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
);
|
|
2950
|
+
for (const dep of plugin.meta.devDeps) {
|
|
2951
|
+
if (merged.devDependencies[dep.name]) {
|
|
2952
|
+
if (merged.devDependencies[dep.name] !== dep.version) {
|
|
2953
|
+
logger.warn(
|
|
2954
|
+
`Dev dependency version conflict for "${dep.name}": "${merged.devDependencies[dep.name]}" vs "${dep.version}" (from ${plugin.meta.id})`
|
|
2955
|
+
);
|
|
2956
|
+
}
|
|
2959
2957
|
}
|
|
2958
|
+
merged.devDependencies[dep.name] = dep.version;
|
|
2960
2959
|
}
|
|
2961
|
-
merged.devDependencies[dep.name] = dep.version;
|
|
2962
2960
|
}
|
|
2963
2961
|
}
|
|
2964
2962
|
for (const [key, merged] of result) {
|
|
@@ -2969,6 +2967,15 @@ function resolveDependencies(plugins, packageJsonTargets, context) {
|
|
|
2969
2967
|
}
|
|
2970
2968
|
return result;
|
|
2971
2969
|
}
|
|
2970
|
+
function resolveTargetsForPlugin(plugin, packageJsonTargets, context) {
|
|
2971
|
+
if (plugin.meta.category === "devtools" && context.isSingleApp && context.isFullstack) {
|
|
2972
|
+
const frontendTarget = packageJsonTargets.find((t) => t.target === TARGETS.FRONTEND)?.path;
|
|
2973
|
+
const backendTarget = packageJsonTargets.find((t) => t.target === TARGETS.BACKEND)?.path;
|
|
2974
|
+
return [frontendTarget, backendTarget].filter((p) => Boolean(p));
|
|
2975
|
+
}
|
|
2976
|
+
const single = resolveTargetForPlugin(plugin, packageJsonTargets, context);
|
|
2977
|
+
return single ? [single] : [];
|
|
2978
|
+
}
|
|
2972
2979
|
function resolveTargetForPlugin(plugin, packageJsonTargets, context) {
|
|
2973
2980
|
const inferredTarget = inferTargetFromPluginOutputs(plugin, context);
|
|
2974
2981
|
if (inferredTarget) {
|
|
@@ -3612,7 +3619,7 @@ function detectFileCollisions(activePlugins, context, pathResolver) {
|
|
|
3612
3619
|
|
|
3613
3620
|
// src/generator/post-generate.ts
|
|
3614
3621
|
import { execa } from "execa";
|
|
3615
|
-
import { readFile } from "fs/promises";
|
|
3622
|
+
import { readFile, readdir } from "fs/promises";
|
|
3616
3623
|
import { join } from "path";
|
|
3617
3624
|
|
|
3618
3625
|
// src/cli/ui/spinner.ts
|
|
@@ -3711,7 +3718,7 @@ async function runPostGenerate(outputDir, context, options) {
|
|
|
3711
3718
|
if (!options.skipInstall) {
|
|
3712
3719
|
const installCmd = getInstallCommand(context.packageManager);
|
|
3713
3720
|
const [cmd, ...args] = installCmd.split(" ");
|
|
3714
|
-
const installTargets = getInstallTargets(outputDir, context);
|
|
3721
|
+
const installTargets = await getInstallTargets(outputDir, context);
|
|
3715
3722
|
try {
|
|
3716
3723
|
for (const target of installTargets) {
|
|
3717
3724
|
await withSpinner(
|
|
@@ -3764,20 +3771,24 @@ function buildNextSteps(context, installed) {
|
|
|
3764
3771
|
}
|
|
3765
3772
|
return steps;
|
|
3766
3773
|
}
|
|
3767
|
-
function getInstallTargets(outputDir, context) {
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
|
|
3774
|
+
async function getInstallTargets(outputDir, context) {
|
|
3775
|
+
const packageDirs = await discoverPackageDirs(outputDir);
|
|
3776
|
+
const targets = [];
|
|
3777
|
+
for (const dir of packageDirs) {
|
|
3778
|
+
if (!await hasInstallableDependencies(dir)) continue;
|
|
3779
|
+
targets.push({
|
|
3780
|
+
label: labelForInstallTarget(dir, outputDir, context),
|
|
3781
|
+
cwd: dir
|
|
3782
|
+
});
|
|
3775
3783
|
}
|
|
3776
|
-
|
|
3784
|
+
if (targets.length === 0) {
|
|
3785
|
+
targets.push({ label: "project", cwd: outputDir });
|
|
3786
|
+
}
|
|
3787
|
+
return targets;
|
|
3777
3788
|
}
|
|
3778
3789
|
async function runBootstrapScripts(outputDir, context) {
|
|
3779
3790
|
const pm = context.packageManager;
|
|
3780
|
-
const installTargets = getInstallTargets(outputDir, context);
|
|
3791
|
+
const installTargets = await getInstallTargets(outputDir, context);
|
|
3781
3792
|
const safeBootstrapScripts = ["db:generate", "generate", "codegen", "graphql:codegen", "types:generate"];
|
|
3782
3793
|
for (const target of installTargets) {
|
|
3783
3794
|
try {
|
|
@@ -3804,6 +3815,56 @@ async function runBootstrapScripts(outputDir, context) {
|
|
|
3804
3815
|
}
|
|
3805
3816
|
}
|
|
3806
3817
|
}
|
|
3818
|
+
async function discoverPackageDirs(outputDir) {
|
|
3819
|
+
const dirs = [];
|
|
3820
|
+
await walkForPackageJson(outputDir, outputDir, dirs);
|
|
3821
|
+
const unique = [...new Set(dirs)];
|
|
3822
|
+
unique.sort((a, b) => {
|
|
3823
|
+
if (a === outputDir) return -1;
|
|
3824
|
+
if (b === outputDir) return 1;
|
|
3825
|
+
return a.localeCompare(b);
|
|
3826
|
+
});
|
|
3827
|
+
return unique;
|
|
3828
|
+
}
|
|
3829
|
+
async function walkForPackageJson(root, current, dirs) {
|
|
3830
|
+
const entries = await readdir(current, { withFileTypes: true });
|
|
3831
|
+
let hasPackageJson = false;
|
|
3832
|
+
for (const entry of entries) {
|
|
3833
|
+
if (entry.isFile() && entry.name === "package.json") {
|
|
3834
|
+
hasPackageJson = true;
|
|
3835
|
+
break;
|
|
3836
|
+
}
|
|
3837
|
+
}
|
|
3838
|
+
if (hasPackageJson) {
|
|
3839
|
+
dirs.push(current);
|
|
3840
|
+
}
|
|
3841
|
+
for (const entry of entries) {
|
|
3842
|
+
if (!entry.isDirectory()) continue;
|
|
3843
|
+
if (entry.name === "node_modules" || entry.name === ".git") continue;
|
|
3844
|
+
await walkForPackageJson(root, join(current, entry.name), dirs);
|
|
3845
|
+
}
|
|
3846
|
+
}
|
|
3847
|
+
function labelForInstallTarget(targetDir, outputDir, context) {
|
|
3848
|
+
if (targetDir === outputDir) return "root";
|
|
3849
|
+
if (targetDir === join(outputDir, "server")) return "server";
|
|
3850
|
+
if (targetDir === join(outputDir, "client")) return "client";
|
|
3851
|
+
if (targetDir === join(outputDir, "mobile")) return "mobile";
|
|
3852
|
+
const relative = targetDir.slice(outputDir.length + 1).replace(/\\/g, "/");
|
|
3853
|
+
if (relative) return relative;
|
|
3854
|
+
return context.projectName;
|
|
3855
|
+
}
|
|
3856
|
+
async function hasInstallableDependencies(cwd) {
|
|
3857
|
+
try {
|
|
3858
|
+
const packageJsonPath = join(cwd, "package.json");
|
|
3859
|
+
const raw = await readFile(packageJsonPath, "utf8");
|
|
3860
|
+
const pkg = JSON.parse(raw);
|
|
3861
|
+
const depsCount = Object.keys(pkg.dependencies ?? {}).length;
|
|
3862
|
+
const devDepsCount = Object.keys(pkg.devDependencies ?? {}).length;
|
|
3863
|
+
return depsCount > 0 || devDepsCount > 0;
|
|
3864
|
+
} catch {
|
|
3865
|
+
return false;
|
|
3866
|
+
}
|
|
3867
|
+
}
|
|
3807
3868
|
|
|
3808
3869
|
// src/generator/pipeline.ts
|
|
3809
3870
|
var TOTAL_STEPS = 16;
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { MongoClient } from 'mongodb';
|
|
2
|
-
|
|
3
|
-
let client: MongoClient | null = null;
|
|
4
|
-
|
|
5
|
-
export async function getMongoClient(): Promise<MongoClient> {
|
|
6
|
-
if (!client) {
|
|
7
|
-
const uri = process.env.MONGODB_URI;
|
|
8
|
-
if (!uri) {
|
|
9
|
-
throw new Error('MONGODB_URI is not set');
|
|
10
|
-
}
|
|
11
|
-
client = new MongoClient(uri);
|
|
12
|
-
await client.connect();
|
|
13
|
-
}
|
|
14
|
-
return client;
|
|
15
|
-
}
|
|
1
|
+
import { MongoClient } from 'mongodb';
|
|
2
|
+
|
|
3
|
+
let client: MongoClient | null = null;
|
|
4
|
+
|
|
5
|
+
export async function getMongoClient(): Promise<MongoClient> {
|
|
6
|
+
if (!client) {
|
|
7
|
+
const uri = process.env.MONGODB_URI;
|
|
8
|
+
if (!uri) {
|
|
9
|
+
throw new Error('MONGODB_URI is not set');
|
|
10
|
+
}
|
|
11
|
+
client = new MongoClient(uri);
|
|
12
|
+
await client.connect();
|
|
13
|
+
}
|
|
14
|
+
return client;
|
|
15
|
+
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { getMongoClient } from './db-connection.js';
|
|
2
|
-
|
|
3
|
-
export async function checkMongodbHealth(): Promise<boolean> {
|
|
4
|
-
try {
|
|
5
|
-
const c = await getMongoClient();
|
|
6
|
-
await c.db().command({ ping: 1 });
|
|
7
|
-
return true;
|
|
8
|
-
} catch {
|
|
9
|
-
return false;
|
|
10
|
-
}
|
|
11
|
-
}
|
|
1
|
+
import { getMongoClient } from './db-connection.js';
|
|
2
|
+
|
|
3
|
+
export async function checkMongodbHealth(): Promise<boolean> {
|
|
4
|
+
try {
|
|
5
|
+
const c = await getMongoClient();
|
|
6
|
+
await c.db().command({ ping: 1 });
|
|
7
|
+
return true;
|
|
8
|
+
} catch {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import mysql from 'mysql2/promise';
|
|
2
|
-
|
|
3
|
-
let pool: mysql.Pool | null = null;
|
|
4
|
-
|
|
5
|
-
export function getMysqlPool(): mysql.Pool {
|
|
6
|
-
if (!pool) {
|
|
7
|
-
const url = process.env.DATABASE_URL;
|
|
8
|
-
if (!url) {
|
|
9
|
-
throw new Error('DATABASE_URL is not set');
|
|
10
|
-
}
|
|
11
|
-
pool = mysql.createPool(url);
|
|
12
|
-
}
|
|
13
|
-
return pool;
|
|
14
|
-
}
|
|
1
|
+
import mysql from 'mysql2/promise';
|
|
2
|
+
|
|
3
|
+
let pool: mysql.Pool | null = null;
|
|
4
|
+
|
|
5
|
+
export function getMysqlPool(): mysql.Pool {
|
|
6
|
+
if (!pool) {
|
|
7
|
+
const url = process.env.DATABASE_URL;
|
|
8
|
+
if (!url) {
|
|
9
|
+
throw new Error('DATABASE_URL is not set');
|
|
10
|
+
}
|
|
11
|
+
pool = mysql.createPool(url);
|
|
12
|
+
}
|
|
13
|
+
return pool;
|
|
14
|
+
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { getMysqlPool } from './db-connection.js';
|
|
2
|
-
|
|
3
|
-
export async function checkMysqlHealth(): Promise<boolean> {
|
|
4
|
-
try {
|
|
5
|
-
const connection = await getMysqlPool().getConnection();
|
|
6
|
-
await connection.ping();
|
|
7
|
-
connection.release();
|
|
8
|
-
return true;
|
|
9
|
-
} catch {
|
|
10
|
-
return false;
|
|
11
|
-
}
|
|
12
|
-
}
|
|
1
|
+
import { getMysqlPool } from './db-connection.js';
|
|
2
|
+
|
|
3
|
+
export async function checkMysqlHealth(): Promise<boolean> {
|
|
4
|
+
try {
|
|
5
|
+
const connection = await getMysqlPool().getConnection();
|
|
6
|
+
await connection.ping();
|
|
7
|
+
connection.release();
|
|
8
|
+
return true;
|
|
9
|
+
} catch {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import pg from 'pg';
|
|
2
|
-
|
|
3
|
-
let pool: pg.Pool | null = null;
|
|
4
|
-
|
|
5
|
-
export function getPostgresPool(): pg.Pool {
|
|
6
|
-
if (!pool) {
|
|
7
|
-
const url = process.env.DATABASE_URL;
|
|
8
|
-
if (!url) {
|
|
9
|
-
throw new Error('DATABASE_URL is not set');
|
|
10
|
-
}
|
|
11
|
-
pool = new pg.Pool({ connectionString: url });
|
|
12
|
-
}
|
|
13
|
-
return pool;
|
|
14
|
-
}
|
|
1
|
+
import pg from 'pg';
|
|
2
|
+
|
|
3
|
+
let pool: pg.Pool | null = null;
|
|
4
|
+
|
|
5
|
+
export function getPostgresPool(): pg.Pool {
|
|
6
|
+
if (!pool) {
|
|
7
|
+
const url = process.env.DATABASE_URL;
|
|
8
|
+
if (!url) {
|
|
9
|
+
throw new Error('DATABASE_URL is not set');
|
|
10
|
+
}
|
|
11
|
+
pool = new pg.Pool({ connectionString: url });
|
|
12
|
+
}
|
|
13
|
+
return pool;
|
|
14
|
+
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { getPostgresPool } from './db-connection.js';
|
|
2
|
-
|
|
3
|
-
export async function checkPostgresHealth(): Promise<boolean> {
|
|
4
|
-
try {
|
|
5
|
-
const client = await getPostgresPool().connect();
|
|
6
|
-
await client.query('SELECT 1');
|
|
7
|
-
client.release();
|
|
8
|
-
return true;
|
|
9
|
-
} catch {
|
|
10
|
-
return false;
|
|
11
|
-
}
|
|
12
|
-
}
|
|
1
|
+
import { getPostgresPool } from './db-connection.js';
|
|
2
|
+
|
|
3
|
+
export async function checkPostgresHealth(): Promise<boolean> {
|
|
4
|
+
try {
|
|
5
|
+
const client = await getPostgresPool().connect();
|
|
6
|
+
await client.query('SELECT 1');
|
|
7
|
+
client.release();
|
|
8
|
+
return true;
|
|
9
|
+
} catch {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import Redis from 'ioredis';
|
|
2
|
-
|
|
3
|
-
let client: Redis | null = null;
|
|
4
|
-
|
|
5
|
-
export function getRedis(): Redis {
|
|
6
|
-
if (!client) {
|
|
7
|
-
const url = process.env.REDIS_URL;
|
|
8
|
-
if (!url) {
|
|
9
|
-
throw new Error('REDIS_URL is not set');
|
|
10
|
-
}
|
|
11
|
-
client = new Redis(url);
|
|
12
|
-
}
|
|
13
|
-
return client;
|
|
14
|
-
}
|
|
1
|
+
import Redis from 'ioredis';
|
|
2
|
+
|
|
3
|
+
let client: Redis | null = null;
|
|
4
|
+
|
|
5
|
+
export function getRedis(): Redis {
|
|
6
|
+
if (!client) {
|
|
7
|
+
const url = process.env.REDIS_URL;
|
|
8
|
+
if (!url) {
|
|
9
|
+
throw new Error('REDIS_URL is not set');
|
|
10
|
+
}
|
|
11
|
+
client = new Redis(url);
|
|
12
|
+
}
|
|
13
|
+
return client;
|
|
14
|
+
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { getRedis } from './redis-connection.js';
|
|
2
|
-
|
|
3
|
-
export async function checkRedisHealth(): Promise<boolean> {
|
|
4
|
-
try {
|
|
5
|
-
const pong = await getRedis().ping();
|
|
6
|
-
return pong === 'PONG';
|
|
7
|
-
} catch {
|
|
8
|
-
return false;
|
|
9
|
-
}
|
|
10
|
-
}
|
|
1
|
+
import { getRedis } from './redis-connection.js';
|
|
2
|
+
|
|
3
|
+
export async function checkRedisHealth(): Promise<boolean> {
|
|
4
|
+
try {
|
|
5
|
+
const pong = await getRedis().ping();
|
|
6
|
+
return pong === 'PONG';
|
|
7
|
+
} catch {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
}
|