alepha 0.14.4 → 0.15.1
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/README.md +44 -102
- package/dist/api/audits/index.d.ts +331 -443
- package/dist/api/audits/index.d.ts.map +1 -1
- package/dist/api/audits/index.js +2 -2
- package/dist/api/audits/index.js.map +1 -1
- package/dist/api/files/index.d.ts +0 -113
- package/dist/api/files/index.d.ts.map +1 -1
- package/dist/api/files/index.js +2 -3
- package/dist/api/files/index.js.map +1 -1
- package/dist/api/jobs/index.d.ts +151 -262
- package/dist/api/jobs/index.d.ts.map +1 -1
- package/dist/api/notifications/index.browser.js +4 -4
- package/dist/api/notifications/index.browser.js.map +1 -1
- package/dist/api/notifications/index.d.ts +164 -276
- package/dist/api/notifications/index.d.ts.map +1 -1
- package/dist/api/notifications/index.js +4 -4
- package/dist/api/notifications/index.js.map +1 -1
- package/dist/api/parameters/index.d.ts +265 -377
- package/dist/api/parameters/index.d.ts.map +1 -1
- package/dist/api/users/index.browser.js +1 -2
- package/dist/api/users/index.browser.js.map +1 -1
- package/dist/api/users/index.d.ts +195 -301
- package/dist/api/users/index.d.ts.map +1 -1
- package/dist/api/users/index.js +203 -184
- package/dist/api/users/index.js.map +1 -1
- package/dist/api/verifications/index.d.ts.map +1 -1
- package/dist/batch/index.d.ts.map +1 -1
- package/dist/batch/index.js +1 -2
- package/dist/batch/index.js.map +1 -1
- package/dist/bucket/index.d.ts.map +1 -1
- package/dist/cache/core/index.d.ts.map +1 -1
- package/dist/cache/redis/index.d.ts.map +1 -1
- package/dist/cache/redis/index.js +2 -2
- package/dist/cache/redis/index.js.map +1 -1
- package/dist/cli/index.d.ts +5900 -165
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +1481 -639
- package/dist/cli/index.js.map +1 -1
- package/dist/command/index.d.ts +8 -4
- package/dist/command/index.d.ts.map +1 -1
- package/dist/command/index.js +29 -25
- package/dist/command/index.js.map +1 -1
- package/dist/core/index.browser.js +563 -54
- package/dist/core/index.browser.js.map +1 -1
- package/dist/core/index.d.ts +175 -8
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +564 -54
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.native.js +563 -54
- package/dist/core/index.native.js.map +1 -1
- package/dist/datetime/index.d.ts.map +1 -1
- package/dist/datetime/index.js +4 -4
- package/dist/datetime/index.js.map +1 -1
- package/dist/email/index.d.ts +89 -42
- package/dist/email/index.d.ts.map +1 -1
- package/dist/email/index.js +129 -33
- package/dist/email/index.js.map +1 -1
- package/dist/fake/index.d.ts +7969 -2
- package/dist/fake/index.d.ts.map +1 -1
- package/dist/fake/index.js +22 -22
- package/dist/fake/index.js.map +1 -1
- package/dist/file/index.d.ts +134 -1
- package/dist/file/index.d.ts.map +1 -1
- package/dist/file/index.js +253 -1
- package/dist/file/index.js.map +1 -1
- package/dist/lock/core/index.d.ts.map +1 -1
- package/dist/lock/redis/index.d.ts.map +1 -1
- package/dist/logger/index.d.ts +1 -2
- package/dist/logger/index.d.ts.map +1 -1
- package/dist/logger/index.js +1 -5
- package/dist/logger/index.js.map +1 -1
- package/dist/mcp/index.d.ts +19 -1
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +28 -4
- package/dist/mcp/index.js.map +1 -1
- package/dist/orm/chunk-DH6iiROE.js +38 -0
- package/dist/orm/index.browser.js +9 -9
- package/dist/orm/index.browser.js.map +1 -1
- package/dist/orm/index.bun.js +2821 -0
- package/dist/orm/index.bun.js.map +1 -0
- package/dist/orm/index.d.ts +318 -169
- package/dist/orm/index.d.ts.map +1 -1
- package/dist/orm/index.js +2086 -1776
- package/dist/orm/index.js.map +1 -1
- package/dist/queue/core/index.d.ts +4 -4
- package/dist/queue/core/index.d.ts.map +1 -1
- package/dist/queue/redis/index.d.ts.map +1 -1
- package/dist/redis/index.bun.js +285 -0
- package/dist/redis/index.bun.js.map +1 -0
- package/dist/redis/index.d.ts +13 -31
- package/dist/redis/index.d.ts.map +1 -1
- package/dist/redis/index.js +18 -38
- package/dist/redis/index.js.map +1 -1
- package/dist/retry/index.d.ts.map +1 -1
- package/dist/router/index.d.ts.map +1 -1
- package/dist/scheduler/index.d.ts +83 -1
- package/dist/scheduler/index.d.ts.map +1 -1
- package/dist/scheduler/index.js +393 -1
- package/dist/scheduler/index.js.map +1 -1
- package/dist/security/index.browser.js +5 -1
- package/dist/security/index.browser.js.map +1 -1
- package/dist/security/index.d.ts +598 -112
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/index.js +1808 -97
- package/dist/security/index.js.map +1 -1
- package/dist/server/auth/index.d.ts +1200 -175
- package/dist/server/auth/index.d.ts.map +1 -1
- package/dist/server/auth/index.js +1268 -37
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/cache/index.d.ts +6 -3
- package/dist/server/cache/index.d.ts.map +1 -1
- package/dist/server/cache/index.js +1 -1
- package/dist/server/cache/index.js.map +1 -1
- package/dist/server/compress/index.d.ts.map +1 -1
- package/dist/server/cookies/index.d.ts.map +1 -1
- package/dist/server/cookies/index.js +3 -3
- package/dist/server/cookies/index.js.map +1 -1
- package/dist/server/core/index.d.ts +115 -13
- package/dist/server/core/index.d.ts.map +1 -1
- package/dist/server/core/index.js +321 -139
- package/dist/server/core/index.js.map +1 -1
- package/dist/server/cors/index.d.ts +0 -1
- package/dist/server/cors/index.d.ts.map +1 -1
- package/dist/server/health/index.d.ts +0 -1
- package/dist/server/health/index.d.ts.map +1 -1
- package/dist/server/helmet/index.d.ts.map +1 -1
- package/dist/server/links/index.browser.js +9 -1
- package/dist/server/links/index.browser.js.map +1 -1
- package/dist/server/links/index.d.ts +1 -2
- package/dist/server/links/index.d.ts.map +1 -1
- package/dist/server/links/index.js +14 -7
- package/dist/server/links/index.js.map +1 -1
- package/dist/server/metrics/index.d.ts +514 -1
- package/dist/server/metrics/index.d.ts.map +1 -1
- package/dist/server/metrics/index.js +4462 -4
- package/dist/server/metrics/index.js.map +1 -1
- package/dist/server/multipart/index.d.ts.map +1 -1
- package/dist/server/proxy/index.d.ts +0 -1
- package/dist/server/proxy/index.d.ts.map +1 -1
- package/dist/server/rate-limit/index.d.ts.map +1 -1
- package/dist/server/static/index.d.ts.map +1 -1
- package/dist/server/swagger/index.d.ts +1 -2
- package/dist/server/swagger/index.d.ts.map +1 -1
- package/dist/server/swagger/index.js +1 -2
- package/dist/server/swagger/index.js.map +1 -1
- package/dist/sms/index.d.ts +3 -1
- package/dist/sms/index.d.ts.map +1 -1
- package/dist/sms/index.js +10 -10
- package/dist/sms/index.js.map +1 -1
- package/dist/thread/index.d.ts +0 -1
- package/dist/thread/index.d.ts.map +1 -1
- package/dist/thread/index.js +2 -2
- package/dist/thread/index.js.map +1 -1
- package/dist/topic/core/index.d.ts.map +1 -1
- package/dist/topic/redis/index.d.ts.map +1 -1
- package/dist/vite/index.d.ts +6315 -149
- package/dist/vite/index.d.ts.map +1 -1
- package/dist/vite/index.js +140 -469
- package/dist/vite/index.js.map +1 -1
- package/dist/websocket/index.browser.js +9 -9
- package/dist/websocket/index.browser.js.map +1 -1
- package/dist/websocket/index.d.ts +28 -28
- package/dist/websocket/index.d.ts.map +1 -1
- package/dist/websocket/index.js +9 -9
- package/dist/websocket/index.js.map +1 -1
- package/package.json +13 -18
- package/src/api/files/controllers/AdminFileStatsController.ts +0 -1
- package/src/api/users/atoms/realmAuthSettingsAtom.ts +5 -0
- package/src/api/users/controllers/{UserRealmController.ts → RealmController.ts} +11 -11
- package/src/api/users/entities/users.ts +1 -1
- package/src/api/users/index.ts +8 -8
- package/src/api/users/primitives/{$userRealm.ts → $realm.ts} +17 -19
- package/src/api/users/providers/{UserRealmProvider.ts → RealmProvider.ts} +26 -30
- package/src/api/users/schemas/{userRealmConfigSchema.ts → realmConfigSchema.ts} +2 -2
- package/src/api/users/services/CredentialService.ts +7 -7
- package/src/api/users/services/IdentityService.ts +4 -4
- package/src/api/users/services/RegistrationService.spec.ts +25 -27
- package/src/api/users/services/RegistrationService.ts +38 -27
- package/src/api/users/services/SessionCrudService.ts +3 -3
- package/src/api/users/services/SessionService.spec.ts +3 -3
- package/src/api/users/services/SessionService.ts +27 -18
- package/src/api/users/services/UserService.ts +7 -7
- package/src/batch/providers/BatchProvider.ts +1 -2
- package/src/cli/apps/AlephaCli.ts +2 -2
- package/src/cli/apps/AlephaPackageBuilderCli.ts +47 -20
- package/src/cli/assets/apiHelloControllerTs.ts +19 -0
- package/src/cli/assets/apiIndexTs.ts +16 -0
- package/src/cli/assets/biomeJson.ts +2 -1
- package/src/cli/assets/claudeMd.ts +308 -0
- package/src/cli/assets/dummySpecTs.ts +2 -1
- package/src/cli/assets/editorconfig.ts +2 -1
- package/src/cli/assets/mainBrowserTs.ts +4 -3
- package/src/cli/assets/mainCss.ts +24 -0
- package/src/cli/assets/mainServerTs.ts +24 -0
- package/src/cli/assets/tsconfigJson.ts +2 -1
- package/src/cli/assets/webAppRouterTs.ts +16 -0
- package/src/cli/assets/webHelloComponentTsx.ts +20 -0
- package/src/cli/assets/webIndexTs.ts +16 -0
- package/src/cli/atoms/appEntryOptions.ts +13 -0
- package/src/cli/atoms/buildOptions.ts +1 -1
- package/src/cli/atoms/changelogOptions.ts +1 -1
- package/src/cli/commands/build.ts +97 -61
- package/src/cli/commands/db.ts +21 -18
- package/src/cli/commands/deploy.ts +17 -5
- package/src/cli/commands/dev.ts +26 -47
- package/src/cli/commands/gen/env.ts +1 -1
- package/src/cli/commands/init.ts +79 -25
- package/src/cli/commands/lint.ts +9 -3
- package/src/cli/commands/test.ts +8 -2
- package/src/cli/commands/typecheck.ts +5 -1
- package/src/cli/commands/verify.ts +4 -2
- package/src/cli/defineConfig.ts +9 -0
- package/src/cli/index.ts +2 -1
- package/src/cli/providers/AppEntryProvider.ts +131 -0
- package/src/cli/providers/ViteBuildProvider.ts +82 -0
- package/src/cli/providers/ViteDevServerProvider.ts +350 -0
- package/src/cli/providers/ViteTemplateProvider.ts +27 -0
- package/src/cli/services/AlephaCliUtils.ts +72 -602
- package/src/cli/services/PackageManagerUtils.ts +308 -0
- package/src/cli/services/ProjectScaffolder.ts +329 -0
- package/src/command/helpers/Runner.ts +15 -3
- package/src/core/Alepha.ts +2 -8
- package/src/core/__tests__/Alepha-graph.spec.ts +4 -0
- package/src/core/index.shared.ts +1 -0
- package/src/core/index.ts +2 -0
- package/src/core/primitives/$hook.ts +6 -2
- package/src/core/primitives/$module.spec.ts +4 -0
- package/src/core/primitives/$module.ts +12 -0
- package/src/core/providers/AlsProvider.ts +1 -1
- package/src/core/providers/CodecManager.spec.ts +12 -6
- package/src/core/providers/CodecManager.ts +26 -6
- package/src/core/providers/EventManager.ts +169 -13
- package/src/core/providers/KeylessJsonSchemaCodec.spec.ts +878 -0
- package/src/core/providers/KeylessJsonSchemaCodec.ts +789 -0
- package/src/core/providers/SchemaValidator.spec.ts +236 -0
- package/src/core/providers/StateManager.spec.ts +27 -16
- package/src/email/providers/LocalEmailProvider.spec.ts +111 -87
- package/src/email/providers/LocalEmailProvider.ts +52 -15
- package/src/email/providers/NodemailerEmailProvider.ts +167 -56
- package/src/file/errors/FileError.ts +7 -0
- package/src/file/index.ts +9 -1
- package/src/file/providers/MemoryFileSystemProvider.ts +393 -0
- package/src/logger/providers/PrettyFormatterProvider.ts +0 -9
- package/src/mcp/errors/McpError.ts +30 -0
- package/src/mcp/index.ts +3 -0
- package/src/mcp/transports/SseMcpTransport.ts +16 -6
- package/src/orm/index.browser.ts +1 -19
- package/src/orm/index.bun.ts +77 -0
- package/src/orm/index.shared-server.ts +22 -0
- package/src/orm/index.shared.ts +15 -0
- package/src/orm/index.ts +19 -39
- package/src/orm/providers/DrizzleKitProvider.ts +3 -5
- package/src/orm/providers/drivers/BunPostgresProvider.ts +3 -5
- package/src/orm/providers/drivers/BunSqliteProvider.ts +1 -1
- package/src/orm/providers/drivers/CloudflareD1Provider.ts +4 -0
- package/src/orm/providers/drivers/DatabaseProvider.ts +4 -0
- package/src/orm/providers/drivers/PglitePostgresProvider.ts +4 -0
- package/src/orm/services/Repository.ts +19 -0
- package/src/redis/index.bun.ts +35 -0
- package/src/redis/providers/BunRedisProvider.ts +12 -43
- package/src/redis/providers/BunRedisSubscriberProvider.ts +2 -3
- package/src/redis/providers/NodeRedisProvider.ts +16 -34
- package/src/{server/security → security}/__tests__/BasicAuth.spec.ts +11 -11
- package/src/{server/security → security}/__tests__/ServerSecurityProvider-realm.spec.ts +21 -16
- package/src/{server/security/providers → security/__tests__}/ServerSecurityProvider.spec.ts +5 -5
- package/src/security/index.browser.ts +5 -0
- package/src/security/index.ts +90 -7
- package/src/security/primitives/{$realm.spec.ts → $issuer.spec.ts} +11 -11
- package/src/security/primitives/{$realm.ts → $issuer.ts} +20 -17
- package/src/security/primitives/$role.ts +5 -5
- package/src/security/primitives/$serviceAccount.spec.ts +5 -5
- package/src/security/primitives/$serviceAccount.ts +3 -3
- package/src/{server/security → security}/providers/ServerSecurityProvider.ts +5 -7
- package/src/server/auth/primitives/$auth.ts +10 -10
- package/src/server/auth/primitives/$authCredentials.ts +3 -3
- package/src/server/auth/primitives/$authGithub.ts +3 -3
- package/src/server/auth/primitives/$authGoogle.ts +3 -3
- package/src/server/auth/providers/ServerAuthProvider.ts +13 -13
- package/src/server/cache/providers/ServerCacheProvider.ts +1 -1
- package/src/server/cookies/providers/ServerCookiesProvider.ts +3 -3
- package/src/server/core/index.ts +1 -1
- package/src/server/core/providers/BunHttpServerProvider.ts +1 -1
- package/src/server/core/providers/NodeHttpServerProvider.spec.ts +125 -0
- package/src/server/core/providers/NodeHttpServerProvider.ts +92 -24
- package/src/server/core/providers/ServerBodyParserProvider.ts +19 -23
- package/src/server/core/providers/ServerLoggerProvider.ts +23 -19
- package/src/server/core/providers/ServerProvider.ts +144 -24
- package/src/server/core/providers/ServerRouterProvider.ts +259 -115
- package/src/server/core/providers/ServerTimingProvider.ts +2 -2
- package/src/server/links/atoms/apiLinksAtom.ts +7 -0
- package/src/server/links/index.browser.ts +2 -0
- package/src/server/links/index.ts +3 -1
- package/src/server/links/providers/LinkProvider.ts +1 -1
- package/src/server/swagger/index.ts +1 -1
- package/src/sms/providers/LocalSmsProvider.spec.ts +153 -111
- package/src/sms/providers/LocalSmsProvider.ts +8 -7
- package/src/vite/index.ts +3 -2
- package/src/vite/tasks/buildClient.ts +0 -1
- package/src/vite/tasks/buildServer.ts +80 -22
- package/src/vite/tasks/copyAssets.ts +5 -4
- package/src/vite/tasks/generateCloudflare.ts +7 -0
- package/src/vite/tasks/generateSitemap.ts +64 -23
- package/src/vite/tasks/index.ts +0 -2
- package/src/vite/tasks/prerenderPages.ts +49 -24
- package/dist/server/security/index.browser.js +0 -13
- package/dist/server/security/index.browser.js.map +0 -1
- package/dist/server/security/index.d.ts +0 -173
- package/dist/server/security/index.d.ts.map +0 -1
- package/dist/server/security/index.js +0 -311
- package/dist/server/security/index.js.map +0 -1
- package/src/cli/assets/appRouterTs.ts +0 -9
- package/src/cli/assets/indexHtml.ts +0 -15
- package/src/cli/assets/mainTs.ts +0 -13
- package/src/cli/commands/format.ts +0 -17
- package/src/server/security/index.browser.ts +0 -10
- package/src/server/security/index.ts +0 -94
- package/src/vite/helpers/boot.ts +0 -106
- package/src/vite/plugins/viteAlephaDev.ts +0 -177
- package/src/vite/tasks/devServer.ts +0 -69
- package/src/vite/tasks/runAlepha.ts +0 -270
- /package/src/{server/security → security}/primitives/$basicAuth.ts +0 -0
- /package/src/{server/security → security}/providers/ServerBasicAuthProvider.ts +0 -0
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import { $inject, Alepha } from "alepha";
|
|
2
|
+
import type { RunnerMethod } from "alepha/command";
|
|
3
|
+
import { FileSystemProvider } from "alepha/file";
|
|
4
|
+
import { $logger } from "alepha/logger";
|
|
5
|
+
import { version } from "../version.ts";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Utility service for package manager operations.
|
|
9
|
+
*
|
|
10
|
+
* Handles detection, installation, and cleanup for:
|
|
11
|
+
* - Yarn
|
|
12
|
+
* - npm
|
|
13
|
+
* - pnpm
|
|
14
|
+
* - Bun
|
|
15
|
+
*/
|
|
16
|
+
export class PackageManagerUtils {
|
|
17
|
+
protected readonly log = $logger();
|
|
18
|
+
protected readonly fs = $inject(FileSystemProvider);
|
|
19
|
+
protected readonly alepha = $inject(Alepha);
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Detect the package manager used in the project.
|
|
23
|
+
*/
|
|
24
|
+
public async getPackageManager(
|
|
25
|
+
root: string,
|
|
26
|
+
flags?: { yarn?: boolean; pnpm?: boolean; npm?: boolean; bun?: boolean },
|
|
27
|
+
): Promise<"yarn" | "pnpm" | "npm" | "bun"> {
|
|
28
|
+
if (flags?.yarn) return "yarn";
|
|
29
|
+
if (flags?.pnpm) return "pnpm";
|
|
30
|
+
if (flags?.npm) return "npm";
|
|
31
|
+
if (flags?.bun) return "bun";
|
|
32
|
+
if (this.alepha.isBun()) return "bun";
|
|
33
|
+
if (await this.fs.exists(this.fs.join(root, "bun.lock"))) return "bun";
|
|
34
|
+
if (await this.fs.exists(this.fs.join(root, "yarn.lock"))) return "yarn";
|
|
35
|
+
if (await this.fs.exists(this.fs.join(root, "pnpm-lock.yaml")))
|
|
36
|
+
return "pnpm";
|
|
37
|
+
return "npm";
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Get the install command for a package.
|
|
42
|
+
*/
|
|
43
|
+
public async getInstallCommand(
|
|
44
|
+
root: string,
|
|
45
|
+
packageName: string,
|
|
46
|
+
dev = true,
|
|
47
|
+
): Promise<string> {
|
|
48
|
+
const pm = await this.getPackageManager(root);
|
|
49
|
+
let cmd: string;
|
|
50
|
+
|
|
51
|
+
switch (pm) {
|
|
52
|
+
case "yarn":
|
|
53
|
+
cmd = `yarn add ${dev ? "-D" : ""} ${packageName}`;
|
|
54
|
+
break;
|
|
55
|
+
case "pnpm":
|
|
56
|
+
cmd = `pnpm add ${dev ? "-D" : ""} ${packageName}`;
|
|
57
|
+
break;
|
|
58
|
+
case "bun":
|
|
59
|
+
cmd = `bun add ${dev ? "-d" : ""} ${packageName}`;
|
|
60
|
+
break;
|
|
61
|
+
default:
|
|
62
|
+
cmd = `npm install ${dev ? "--save-dev" : ""} ${packageName}`;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return cmd.replace(/\s+/g, " ").trim();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Check if a dependency is installed in the project.
|
|
70
|
+
*/
|
|
71
|
+
public async hasDependency(
|
|
72
|
+
root: string,
|
|
73
|
+
packageName: string,
|
|
74
|
+
): Promise<boolean> {
|
|
75
|
+
try {
|
|
76
|
+
const pkg = await this.readPackageJson(root);
|
|
77
|
+
return !!(
|
|
78
|
+
pkg.dependencies?.[packageName] || pkg.devDependencies?.[packageName]
|
|
79
|
+
);
|
|
80
|
+
} catch {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Check if Expo is present in the project.
|
|
87
|
+
*/
|
|
88
|
+
public async hasExpo(root: string): Promise<boolean> {
|
|
89
|
+
return this.hasDependency(root, "expo");
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Check if React is present in the project.
|
|
94
|
+
*/
|
|
95
|
+
public async hasReact(root: string): Promise<boolean> {
|
|
96
|
+
return this.hasDependency(root, "react");
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Install a dependency if it's missing from the project.
|
|
101
|
+
*/
|
|
102
|
+
public async ensureDependency(
|
|
103
|
+
root: string,
|
|
104
|
+
packageName: string,
|
|
105
|
+
options: {
|
|
106
|
+
dev?: boolean;
|
|
107
|
+
run?: RunnerMethod;
|
|
108
|
+
exec?: (
|
|
109
|
+
cmd: string,
|
|
110
|
+
opts?: { global?: boolean; root?: string },
|
|
111
|
+
) => Promise<void>;
|
|
112
|
+
} = {},
|
|
113
|
+
): Promise<void> {
|
|
114
|
+
const { dev = true } = options;
|
|
115
|
+
|
|
116
|
+
if (await this.hasDependency(root, packageName)) {
|
|
117
|
+
this.log.debug(`Dependency '${packageName}' is already installed`);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const cmd = await this.getInstallCommand(root, packageName, dev);
|
|
122
|
+
|
|
123
|
+
if (options.run) {
|
|
124
|
+
await options.run(cmd, { alias: `add ${packageName}`, root });
|
|
125
|
+
} else if (options.exec) {
|
|
126
|
+
this.log.debug(`Installing ${packageName}`);
|
|
127
|
+
await options.exec(cmd, { global: true, root });
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// ===========================================
|
|
132
|
+
// Package Manager Setup & Cleanup
|
|
133
|
+
// ===========================================
|
|
134
|
+
|
|
135
|
+
public async ensureYarn(root: string): Promise<void> {
|
|
136
|
+
const yarnrcPath = this.fs.join(root, ".yarnrc.yml");
|
|
137
|
+
if (!(await this.fs.exists(yarnrcPath))) {
|
|
138
|
+
await this.fs.writeFile(yarnrcPath, "nodeLinker: node-modules");
|
|
139
|
+
}
|
|
140
|
+
await this.removeAllPmFilesExcept(root, "yarn");
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
public async ensureBun(root: string): Promise<void> {
|
|
144
|
+
await this.removeAllPmFilesExcept(root, "bun");
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
public async ensurePnpm(root: string): Promise<void> {
|
|
148
|
+
await this.removeAllPmFilesExcept(root, "pnpm");
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
public async ensureNpm(root: string): Promise<void> {
|
|
152
|
+
await this.removeAllPmFilesExcept(root, "npm");
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
public async removeAllPmFilesExcept(
|
|
156
|
+
root: string,
|
|
157
|
+
except: string,
|
|
158
|
+
): Promise<void> {
|
|
159
|
+
if (except !== "yarn") await this.removeYarn(root);
|
|
160
|
+
if (except !== "pnpm") await this.removePnpm(root);
|
|
161
|
+
if (except !== "npm") await this.removeNpm(root);
|
|
162
|
+
if (except !== "bun") await this.removeBun(root);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
public async removeYarn(root: string): Promise<void> {
|
|
166
|
+
await this.removeFiles(root, [".yarn", ".yarnrc.yml", "yarn.lock"]);
|
|
167
|
+
await this.editPackageJson(root, (pkg) => {
|
|
168
|
+
delete pkg.packageManager;
|
|
169
|
+
return pkg;
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
public async removePnpm(root: string): Promise<void> {
|
|
174
|
+
await this.removeFiles(root, ["pnpm-lock.yaml", "pnpm-workspace.yaml"]);
|
|
175
|
+
await this.editPackageJson(root, (pkg) => {
|
|
176
|
+
delete pkg.packageManager;
|
|
177
|
+
return pkg;
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
public async removeNpm(root: string): Promise<void> {
|
|
182
|
+
await this.removeFiles(root, ["package-lock.json"]);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
public async removeBun(root: string): Promise<void> {
|
|
186
|
+
await this.removeFiles(root, ["bun.lockb", "bun.lock"]);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// ===========================================
|
|
190
|
+
// Package.json utilities
|
|
191
|
+
// ===========================================
|
|
192
|
+
|
|
193
|
+
public async readPackageJson(root: string): Promise<Record<string, any>> {
|
|
194
|
+
const content = await this.fs
|
|
195
|
+
.createFile({ path: this.fs.join(root, "package.json") })
|
|
196
|
+
.text();
|
|
197
|
+
return JSON.parse(content);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
public async writePackageJson(
|
|
201
|
+
root: string,
|
|
202
|
+
content: Record<string, any>,
|
|
203
|
+
): Promise<void> {
|
|
204
|
+
await this.fs.writeFile(
|
|
205
|
+
this.fs.join(root, "package.json"),
|
|
206
|
+
JSON.stringify(content, null, 2),
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
public async editPackageJson(
|
|
211
|
+
root: string,
|
|
212
|
+
editFn: (pkg: Record<string, any>) => Record<string, any>,
|
|
213
|
+
): Promise<void> {
|
|
214
|
+
try {
|
|
215
|
+
const pkg = await this.readPackageJson(root);
|
|
216
|
+
const updated = editFn(pkg);
|
|
217
|
+
await this.writePackageJson(root, updated);
|
|
218
|
+
} catch {
|
|
219
|
+
// package.json doesn't exist, skip
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
public async ensurePackageJson(
|
|
224
|
+
root: string,
|
|
225
|
+
modes: DependencyModes,
|
|
226
|
+
): Promise<Record<string, any>> {
|
|
227
|
+
const packageJsonPath = this.fs.join(root, "package.json");
|
|
228
|
+
|
|
229
|
+
if (!(await this.fs.exists(packageJsonPath))) {
|
|
230
|
+
const content = this.generatePackageJsonContent(modes);
|
|
231
|
+
await this.writePackageJson(root, content);
|
|
232
|
+
return content;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const packageJson = await this.readPackageJson(root);
|
|
236
|
+
const newContent = this.generatePackageJsonContent(modes);
|
|
237
|
+
|
|
238
|
+
packageJson.type = "module";
|
|
239
|
+
packageJson.dependencies ??= {};
|
|
240
|
+
packageJson.devDependencies ??= {};
|
|
241
|
+
packageJson.scripts ??= {};
|
|
242
|
+
|
|
243
|
+
Object.assign(packageJson.dependencies, newContent.dependencies);
|
|
244
|
+
Object.assign(packageJson.devDependencies, newContent.devDependencies);
|
|
245
|
+
Object.assign(packageJson.scripts, newContent.scripts);
|
|
246
|
+
|
|
247
|
+
await this.writePackageJson(root, packageJson);
|
|
248
|
+
return packageJson;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
public generatePackageJsonContent(modes: DependencyModes): {
|
|
252
|
+
dependencies: Record<string, string>;
|
|
253
|
+
devDependencies: Record<string, string>;
|
|
254
|
+
scripts: Record<string, string>;
|
|
255
|
+
type: "module";
|
|
256
|
+
} {
|
|
257
|
+
const dependencies: Record<string, string> = {
|
|
258
|
+
alepha: `^${version}`,
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
const devDependencies: Record<string, string> = {};
|
|
262
|
+
|
|
263
|
+
const scripts: Record<string, string> = {
|
|
264
|
+
dev: "alepha dev",
|
|
265
|
+
build: "alepha build",
|
|
266
|
+
lint: "alepha lint",
|
|
267
|
+
typecheck: "alepha typecheck",
|
|
268
|
+
verify: "alepha verify",
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
if (modes.ui) {
|
|
272
|
+
dependencies["@alepha/ui"] = `^${version}`;
|
|
273
|
+
modes.react = true;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (modes.react) {
|
|
277
|
+
dependencies["@alepha/react"] = `^${version}`;
|
|
278
|
+
dependencies.react = "^19.2.0";
|
|
279
|
+
dependencies["react-dom"] = "^19.2.0";
|
|
280
|
+
devDependencies["@types/react"] = "^19.2.0";
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return {
|
|
284
|
+
type: "module",
|
|
285
|
+
dependencies,
|
|
286
|
+
devDependencies,
|
|
287
|
+
scripts,
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// ===========================================
|
|
292
|
+
// Helper methods
|
|
293
|
+
// ===========================================
|
|
294
|
+
|
|
295
|
+
protected async removeFiles(root: string, files: string[]): Promise<void> {
|
|
296
|
+
await Promise.all(
|
|
297
|
+
files.map((file) =>
|
|
298
|
+
this.fs.rm(this.fs.join(root, file), { force: true, recursive: true }),
|
|
299
|
+
),
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
export interface DependencyModes {
|
|
305
|
+
react?: boolean;
|
|
306
|
+
ui?: boolean;
|
|
307
|
+
expo?: boolean;
|
|
308
|
+
}
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
import { basename, dirname } from "node:path";
|
|
2
|
+
import { $inject } from "alepha";
|
|
3
|
+
import { FileSystemProvider } from "alepha/file";
|
|
4
|
+
import { $logger } from "alepha/logger";
|
|
5
|
+
import { apiHelloControllerTs } from "../assets/apiHelloControllerTs.ts";
|
|
6
|
+
import { apiIndexTs } from "../assets/apiIndexTs.ts";
|
|
7
|
+
import { biomeJson } from "../assets/biomeJson.ts";
|
|
8
|
+
import { type ClaudeMdOptions, claudeMd } from "../assets/claudeMd.ts";
|
|
9
|
+
import { dummySpecTs } from "../assets/dummySpecTs.ts";
|
|
10
|
+
import { editorconfig } from "../assets/editorconfig.ts";
|
|
11
|
+
import { mainBrowserTs } from "../assets/mainBrowserTs.ts";
|
|
12
|
+
import { mainCss } from "../assets/mainCss.ts";
|
|
13
|
+
import { mainServerTs } from "../assets/mainServerTs.ts";
|
|
14
|
+
import { tsconfigJson } from "../assets/tsconfigJson.ts";
|
|
15
|
+
import { webAppRouterTs } from "../assets/webAppRouterTs.ts";
|
|
16
|
+
import { webHelloComponentTsx } from "../assets/webHelloComponentTsx.ts";
|
|
17
|
+
import { webIndexTs } from "../assets/webIndexTs.ts";
|
|
18
|
+
import {
|
|
19
|
+
type DependencyModes,
|
|
20
|
+
PackageManagerUtils,
|
|
21
|
+
} from "./PackageManagerUtils.ts";
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Service for scaffolding new Alepha projects.
|
|
25
|
+
*
|
|
26
|
+
* Handles creation of:
|
|
27
|
+
* - Project structure (src/api, src/web)
|
|
28
|
+
* - Configuration files (tsconfig, biome, editorconfig)
|
|
29
|
+
* - Entry points (main.server.ts, main.browser.ts)
|
|
30
|
+
* - Example code (HelloController, Hello component)
|
|
31
|
+
*/
|
|
32
|
+
export class ProjectScaffolder {
|
|
33
|
+
protected readonly log = $logger();
|
|
34
|
+
protected readonly fs = $inject(FileSystemProvider);
|
|
35
|
+
protected readonly pm = $inject(PackageManagerUtils);
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Get the app name from the directory name.
|
|
39
|
+
*
|
|
40
|
+
* Converts the directory name to a valid module name:
|
|
41
|
+
* - Converts to lowercase
|
|
42
|
+
* - Replaces spaces, dashes, underscores with nothing
|
|
43
|
+
* - Falls back to "app" if empty
|
|
44
|
+
*/
|
|
45
|
+
public getAppName(root: string): string {
|
|
46
|
+
const dirName = basename(root);
|
|
47
|
+
const appName = dirName.toLowerCase().replace(/[\s\-_]/g, "");
|
|
48
|
+
return appName || "app";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Ensure all configuration files exist.
|
|
53
|
+
*/
|
|
54
|
+
public async ensureConfig(
|
|
55
|
+
root: string,
|
|
56
|
+
opts: {
|
|
57
|
+
force?: boolean;
|
|
58
|
+
packageJson?: boolean | DependencyModes;
|
|
59
|
+
tsconfigJson?: boolean;
|
|
60
|
+
indexHtml?: boolean;
|
|
61
|
+
biomeJson?: boolean;
|
|
62
|
+
editorconfig?: boolean;
|
|
63
|
+
claudeMd?: boolean | ClaudeMdOptions;
|
|
64
|
+
},
|
|
65
|
+
): Promise<void> {
|
|
66
|
+
const tasks: Promise<void>[] = [];
|
|
67
|
+
const force = opts.force ?? false;
|
|
68
|
+
|
|
69
|
+
if (opts.packageJson) {
|
|
70
|
+
tasks.push(
|
|
71
|
+
this.pm
|
|
72
|
+
.ensurePackageJson(
|
|
73
|
+
root,
|
|
74
|
+
typeof opts.packageJson === "boolean" ? {} : opts.packageJson,
|
|
75
|
+
)
|
|
76
|
+
.then(() => {}),
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
if (opts.tsconfigJson) {
|
|
80
|
+
tasks.push(this.ensureTsConfig(root, { force }));
|
|
81
|
+
}
|
|
82
|
+
if (opts.indexHtml) {
|
|
83
|
+
tasks.push(this.ensureReactProject(root, { force }));
|
|
84
|
+
}
|
|
85
|
+
if (opts.biomeJson) {
|
|
86
|
+
tasks.push(this.ensureBiomeConfig(root, { force }));
|
|
87
|
+
}
|
|
88
|
+
if (opts.editorconfig) {
|
|
89
|
+
tasks.push(this.ensureEditorConfig(root, { force }));
|
|
90
|
+
}
|
|
91
|
+
if (opts.claudeMd) {
|
|
92
|
+
tasks.push(
|
|
93
|
+
this.ensureClaudeMd(
|
|
94
|
+
root,
|
|
95
|
+
typeof opts.claudeMd === "boolean"
|
|
96
|
+
? { force }
|
|
97
|
+
: { ...opts.claudeMd, force },
|
|
98
|
+
),
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
await Promise.all(tasks);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// ===========================================
|
|
106
|
+
// Config Files
|
|
107
|
+
// ===========================================
|
|
108
|
+
|
|
109
|
+
public async ensureTsConfig(
|
|
110
|
+
root: string,
|
|
111
|
+
opts: { force?: boolean } = {},
|
|
112
|
+
): Promise<void> {
|
|
113
|
+
// Check if tsconfig.json exists in current or parent directories
|
|
114
|
+
if (!opts.force && (await this.existsInParents(root, "tsconfig.json"))) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
await this.fs.writeFile(
|
|
118
|
+
this.fs.join(root, "tsconfig.json"),
|
|
119
|
+
tsconfigJson(),
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
public async ensureBiomeConfig(
|
|
124
|
+
root: string,
|
|
125
|
+
opts: { force?: boolean } = {},
|
|
126
|
+
): Promise<void> {
|
|
127
|
+
await this.ensureFile(root, "biome.json", biomeJson(), opts.force);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
public async ensureEditorConfig(
|
|
131
|
+
root: string,
|
|
132
|
+
opts: { force?: boolean } = {},
|
|
133
|
+
): Promise<void> {
|
|
134
|
+
await this.ensureFile(root, ".editorconfig", editorconfig(), opts.force);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
public async ensureClaudeMd(
|
|
138
|
+
root: string,
|
|
139
|
+
options: ClaudeMdOptions & { force?: boolean } = {},
|
|
140
|
+
): Promise<void> {
|
|
141
|
+
await this.ensureFile(root, "CLAUDE.md", claudeMd(options), options.force);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// ===========================================
|
|
145
|
+
// API Project Structure
|
|
146
|
+
// ===========================================
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Ensure src/main.server.ts exists with full API structure.
|
|
150
|
+
*
|
|
151
|
+
* Creates:
|
|
152
|
+
* - src/main.server.ts (entry point)
|
|
153
|
+
* - src/api/index.ts (API module)
|
|
154
|
+
* - src/api/controllers/HelloController.ts (example controller)
|
|
155
|
+
*/
|
|
156
|
+
public async ensureApiProject(
|
|
157
|
+
root: string,
|
|
158
|
+
opts: { force?: boolean } = {},
|
|
159
|
+
): Promise<void> {
|
|
160
|
+
const srcDir = this.fs.join(root, "src");
|
|
161
|
+
|
|
162
|
+
// Don't overwrite existing content unless force is set
|
|
163
|
+
if (!opts.force && (await this.fs.exists(srcDir))) {
|
|
164
|
+
const files = await this.fs.ls(srcDir);
|
|
165
|
+
if (files.length > 0) return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const appName = this.getAppName(root);
|
|
169
|
+
|
|
170
|
+
// Create directories
|
|
171
|
+
await this.fs.mkdir(this.fs.join(root, "src/api/controllers"), {
|
|
172
|
+
recursive: true,
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
// Create files
|
|
176
|
+
await this.ensureFile(srcDir, "main.server.ts", mainServerTs(), opts.force);
|
|
177
|
+
await this.ensureFile(
|
|
178
|
+
srcDir,
|
|
179
|
+
"api/index.ts",
|
|
180
|
+
apiIndexTs({ appName }),
|
|
181
|
+
opts.force,
|
|
182
|
+
);
|
|
183
|
+
await this.ensureFile(
|
|
184
|
+
srcDir,
|
|
185
|
+
"api/controllers/HelloController.ts",
|
|
186
|
+
apiHelloControllerTs(),
|
|
187
|
+
opts.force,
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// ===========================================
|
|
192
|
+
// React Project Structure
|
|
193
|
+
// ===========================================
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Ensure full React project structure exists.
|
|
197
|
+
*
|
|
198
|
+
* Creates:
|
|
199
|
+
* - src/main.server.ts, src/main.browser.ts
|
|
200
|
+
* - src/api/index.ts, src/api/controllers/HelloController.ts
|
|
201
|
+
* - src/web/index.ts, src/web/AppRouter.ts, src/web/components/Hello.tsx
|
|
202
|
+
*/
|
|
203
|
+
public async ensureReactProject(
|
|
204
|
+
root: string,
|
|
205
|
+
opts: { force?: boolean } = {},
|
|
206
|
+
): Promise<void> {
|
|
207
|
+
const appName = this.getAppName(root);
|
|
208
|
+
|
|
209
|
+
// Create directories
|
|
210
|
+
await this.fs.mkdir(this.fs.join(root, "src/api/controllers"), {
|
|
211
|
+
recursive: true,
|
|
212
|
+
});
|
|
213
|
+
await this.fs.mkdir(this.fs.join(root, "src/web/components"), {
|
|
214
|
+
recursive: true,
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
// src/main.css
|
|
218
|
+
await this.ensureFile(root, "src/main.css", mainCss(), opts.force);
|
|
219
|
+
|
|
220
|
+
// API structure
|
|
221
|
+
await this.ensureFile(
|
|
222
|
+
root,
|
|
223
|
+
"src/api/index.ts",
|
|
224
|
+
apiIndexTs({ appName }),
|
|
225
|
+
opts.force,
|
|
226
|
+
);
|
|
227
|
+
await this.ensureFile(
|
|
228
|
+
root,
|
|
229
|
+
"src/api/controllers/HelloController.ts",
|
|
230
|
+
apiHelloControllerTs(),
|
|
231
|
+
opts.force,
|
|
232
|
+
);
|
|
233
|
+
await this.ensureFile(
|
|
234
|
+
root,
|
|
235
|
+
"src/main.server.ts",
|
|
236
|
+
mainServerTs({ react: true }),
|
|
237
|
+
opts.force,
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
// Web structure
|
|
241
|
+
await this.ensureFile(
|
|
242
|
+
root,
|
|
243
|
+
"src/web/index.ts",
|
|
244
|
+
webIndexTs({ appName }),
|
|
245
|
+
opts.force,
|
|
246
|
+
);
|
|
247
|
+
await this.ensureFile(
|
|
248
|
+
root,
|
|
249
|
+
"src/web/AppRouter.ts",
|
|
250
|
+
webAppRouterTs(),
|
|
251
|
+
opts.force,
|
|
252
|
+
);
|
|
253
|
+
await this.ensureFile(
|
|
254
|
+
root,
|
|
255
|
+
"src/web/components/Hello.tsx",
|
|
256
|
+
webHelloComponentTsx(),
|
|
257
|
+
opts.force,
|
|
258
|
+
);
|
|
259
|
+
await this.ensureFile(
|
|
260
|
+
root,
|
|
261
|
+
"src/main.browser.ts",
|
|
262
|
+
mainBrowserTs(),
|
|
263
|
+
opts.force,
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// ===========================================
|
|
268
|
+
// Test Directory
|
|
269
|
+
// ===========================================
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Ensure test directory exists with a dummy test file.
|
|
273
|
+
*/
|
|
274
|
+
public async ensureTestDir(root: string): Promise<void> {
|
|
275
|
+
const testDir = this.fs.join(root, "test");
|
|
276
|
+
const dummyPath = this.fs.join(testDir, "dummy.spec.ts");
|
|
277
|
+
|
|
278
|
+
if (!(await this.fs.exists(testDir))) {
|
|
279
|
+
await this.fs.mkdir(testDir, { recursive: true });
|
|
280
|
+
await this.fs.writeFile(dummyPath, dummySpecTs());
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const files = await this.fs.ls(testDir);
|
|
285
|
+
if (files.length === 0) {
|
|
286
|
+
await this.fs.writeFile(dummyPath, dummySpecTs());
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// ===========================================
|
|
291
|
+
// Helpers
|
|
292
|
+
// ===========================================
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Write a file, optionally overriding if it exists.
|
|
296
|
+
*/
|
|
297
|
+
protected async ensureFile(
|
|
298
|
+
root: string,
|
|
299
|
+
relativePath: string,
|
|
300
|
+
content: string,
|
|
301
|
+
force?: boolean,
|
|
302
|
+
): Promise<void> {
|
|
303
|
+
const fullPath = this.fs.join(root, relativePath);
|
|
304
|
+
if (force || !(await this.fs.exists(fullPath))) {
|
|
305
|
+
await this.fs.writeFile(fullPath, content);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Check if a file exists in the given directory or any parent directory.
|
|
311
|
+
*/
|
|
312
|
+
protected async existsInParents(
|
|
313
|
+
root: string,
|
|
314
|
+
filename: string,
|
|
315
|
+
): Promise<boolean> {
|
|
316
|
+
let current = root;
|
|
317
|
+
while (true) {
|
|
318
|
+
if (await this.fs.exists(this.fs.join(current, filename))) {
|
|
319
|
+
return true;
|
|
320
|
+
}
|
|
321
|
+
const parent = dirname(current);
|
|
322
|
+
if (parent === current) {
|
|
323
|
+
// Reached filesystem root
|
|
324
|
+
return false;
|
|
325
|
+
}
|
|
326
|
+
current = parent;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
@@ -20,6 +20,11 @@ export interface RunOptions {
|
|
|
20
20
|
* Rename the command for logging purposes.
|
|
21
21
|
*/
|
|
22
22
|
alias?: string;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Root directory to execute the command in.
|
|
26
|
+
*/
|
|
27
|
+
root?: string;
|
|
23
28
|
}
|
|
24
29
|
|
|
25
30
|
export interface RunnerMethod {
|
|
@@ -73,11 +78,14 @@ export class Runner {
|
|
|
73
78
|
|
|
74
79
|
this.firstTaskStarted = true;
|
|
75
80
|
|
|
81
|
+
const root =
|
|
82
|
+
typeof options === "object" && options.root ? options.root : undefined;
|
|
83
|
+
|
|
76
84
|
if (Array.isArray(cmd)) {
|
|
77
85
|
return await this.execute(
|
|
78
86
|
cmd.map((it) =>
|
|
79
87
|
typeof it === "string"
|
|
80
|
-
? { name: it, handler: () => this.exec(it) }
|
|
88
|
+
? { name: it, handler: () => this.exec(it, { root }) }
|
|
81
89
|
: it,
|
|
82
90
|
),
|
|
83
91
|
);
|
|
@@ -89,7 +97,7 @@ export class Runner {
|
|
|
89
97
|
typeof options === "function"
|
|
90
98
|
? options
|
|
91
99
|
: typeof cmd === "string"
|
|
92
|
-
? () => this.exec(cmd)
|
|
100
|
+
? () => this.exec(cmd, { root })
|
|
93
101
|
: cmd.handler;
|
|
94
102
|
|
|
95
103
|
return await this.execute({
|
|
@@ -140,11 +148,15 @@ export class Runner {
|
|
|
140
148
|
return runFn;
|
|
141
149
|
}
|
|
142
150
|
|
|
143
|
-
protected async exec(
|
|
151
|
+
protected async exec(
|
|
152
|
+
cmd: string,
|
|
153
|
+
opts: { root?: string } = {},
|
|
154
|
+
): Promise<string> {
|
|
144
155
|
return await new Promise<string>((resolve, reject) => {
|
|
145
156
|
exec(
|
|
146
157
|
cmd,
|
|
147
158
|
{
|
|
159
|
+
cwd: opts.root,
|
|
148
160
|
env: {
|
|
149
161
|
...process.env,
|
|
150
162
|
LOG_FORMAT: "pretty",
|
package/src/core/Alepha.ts
CHANGED
|
@@ -165,13 +165,6 @@ export class Alepha {
|
|
|
165
165
|
...state.env,
|
|
166
166
|
...process.env,
|
|
167
167
|
};
|
|
168
|
-
|
|
169
|
-
// remove empty env variables
|
|
170
|
-
for (const key in state.env) {
|
|
171
|
-
if (state.env[key] === "") {
|
|
172
|
-
delete (state.env as any)[key];
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
168
|
}
|
|
176
169
|
|
|
177
170
|
// force production mode when building with vite
|
|
@@ -765,8 +758,9 @@ export class Alepha {
|
|
|
765
758
|
}
|
|
766
759
|
|
|
767
760
|
if (this.started) {
|
|
761
|
+
const mod = (service as WithModule)[MODULE]?.name;
|
|
768
762
|
throw new ContainerLockedError(
|
|
769
|
-
`Container is locked. No more services can be added. ${parent?.name}
|
|
763
|
+
`Container is locked. No more services can be added. Attempted to inject '${service.name}' from '${parent?.name}'. ${mod ? `Maybe register module '${mod}' in Alepha.` : ""}`,
|
|
770
764
|
);
|
|
771
765
|
}
|
|
772
766
|
}
|