@simplysm/sd-cli 14.0.66 → 14.0.70
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/commands/init/generators/client-common.d.ts +3 -0
- package/dist/commands/init/generators/client-common.d.ts.map +1 -0
- package/dist/commands/init/generators/client-common.js +17 -0
- package/dist/commands/init/generators/client-common.js.map +1 -0
- package/dist/commands/init/generators/client.d.ts +3 -0
- package/dist/commands/init/generators/client.d.ts.map +1 -0
- package/dist/commands/init/generators/client.js +20 -0
- package/dist/commands/init/generators/client.js.map +1 -0
- package/dist/commands/init/generators/common.d.ts +3 -0
- package/dist/commands/init/generators/common.d.ts.map +1 -0
- package/dist/commands/init/generators/common.js +14 -0
- package/dist/commands/init/generators/common.js.map +1 -0
- package/dist/commands/init/generators/root.d.ts +3 -0
- package/dist/commands/init/generators/root.d.ts.map +1 -0
- package/dist/commands/init/generators/root.js +28 -0
- package/dist/commands/init/generators/root.js.map +1 -0
- package/dist/commands/init/generators/server.d.ts +3 -0
- package/dist/commands/init/generators/server.d.ts.map +1 -0
- package/dist/commands/init/generators/server.js +11 -0
- package/dist/commands/init/generators/server.js.map +1 -0
- package/dist/commands/init/init.d.ts +5 -0
- package/dist/commands/init/init.d.ts.map +1 -0
- package/dist/commands/init/init.js +44 -0
- package/dist/commands/init/init.js.map +1 -0
- package/dist/commands/init/normalize.d.ts +3 -0
- package/dist/commands/init/normalize.d.ts.map +1 -0
- package/dist/commands/init/normalize.js +42 -0
- package/dist/commands/init/normalize.js.map +1 -0
- package/dist/commands/init/prompts.d.ts +3 -0
- package/dist/commands/init/prompts.d.ts.map +1 -0
- package/dist/commands/init/prompts.js +89 -0
- package/dist/commands/init/prompts.js.map +1 -0
- package/dist/commands/init/render.d.ts +4 -0
- package/dist/commands/init/render.d.ts.map +1 -0
- package/dist/commands/init/render.js +20 -0
- package/dist/commands/init/render.js.map +1 -0
- package/dist/commands/init/template-paths.d.ts +2 -0
- package/dist/commands/init/template-paths.d.ts.map +1 -0
- package/dist/commands/init/template-paths.js +7 -0
- package/dist/commands/init/template-paths.js.map +1 -0
- package/dist/commands/init/types.d.ts +47 -0
- package/dist/commands/init/types.d.ts.map +1 -0
- package/dist/commands/init/types.js +2 -0
- package/dist/commands/init/types.js.map +1 -0
- package/dist/commands/init/validate.d.ts +4 -0
- package/dist/commands/init/validate.d.ts.map +1 -0
- package/dist/commands/init/validate.js +31 -0
- package/dist/commands/init/validate.js.map +1 -0
- package/dist/sd-cli-entry.d.ts.map +1 -1
- package/dist/sd-cli-entry.js +14 -1
- package/dist/sd-cli-entry.js.map +1 -1
- package/package.json +5 -4
- package/src/commands/init/generators/client-common.ts +29 -0
- package/src/commands/init/generators/client.ts +29 -0
- package/src/commands/init/generators/common.ts +22 -0
- package/src/commands/init/generators/root.ts +32 -0
- package/src/commands/init/generators/server.ts +15 -0
- package/src/commands/init/init.ts +54 -0
- package/src/commands/init/normalize.ts +46 -0
- package/src/commands/init/prompts.ts +101 -0
- package/src/commands/init/render.ts +25 -0
- package/src/commands/init/template-paths.ts +8 -0
- package/src/commands/init/templates/client/ngsw-config.json +27 -0
- package/src/commands/init/templates/client/package.json.hbs +29 -0
- package/src/commands/init/templates/client/src/AppPage.ts.hbs +18 -0
- package/src/commands/init/templates/client/src/index.html.hbs +12 -0
- package/src/commands/init/templates/client/src/main.ts.hbs +28 -0
- package/src/commands/init/templates/client/src/polyfills.ts +2 -0
- package/src/commands/init/templates/client/src/routes.ts +3 -0
- package/src/commands/init/templates/client/src/styles.scss +1 -0
- package/src/commands/init/templates/client/tsconfig.json +20 -0
- package/src/commands/init/templates/client-common/package.json.hbs +20 -0
- package/src/commands/init/templates/client-common/src/index.ts.hbs +11 -0
- package/src/commands/init/templates/client-common/src/providers/AppOrmProvider.ts.hbs +36 -0
- package/src/commands/init/templates/client-common/src/providers/AppServiceProvider.ts +27 -0
- package/src/commands/init/templates/client-common/tsconfig.json +20 -0
- package/src/commands/init/templates/common/package.json.hbs +10 -0
- package/src/commands/init/templates/common/src/MainDbContext.ts +4 -0
- package/src/commands/init/templates/common/src/index.ts.hbs +5 -0
- package/src/commands/init/templates/common/tsconfig.json +8 -0
- package/src/commands/init/templates/server/package.json.hbs +23 -0
- package/src/commands/init/templates/server/src/main.ts.hbs +25 -0
- package/src/commands/init/templates/server/tsconfig.json +8 -0
- package/src/commands/init/templates/workspace-root/.prettierignore +1 -0
- package/src/commands/init/templates/workspace-root/.prettierrc.yaml +12 -0
- package/src/commands/init/templates/workspace-root/eslint.config.ts +4 -0
- package/src/commands/init/templates/workspace-root/mise.toml.hbs +7 -0
- package/src/commands/init/templates/workspace-root/package.json.hbs +43 -0
- package/src/commands/init/templates/workspace-root/pnpm-workspace.yaml +17 -0
- package/src/commands/init/templates/workspace-root/sd.config.ts.hbs +63 -0
- package/src/commands/init/templates/workspace-root/tsconfig.json.hbs +30 -0
- package/src/commands/init/templates/workspace-root/vitest.config.ts.hbs +72 -0
- package/src/commands/init/types.ts +51 -0
- package/src/commands/init/validate.ts +41 -0
- package/src/sd-cli-entry.ts +19 -1
- package/tests/init/__snapshots__/render.spec.ts.snap +213 -0
- package/tests/init/normalize.spec.ts +114 -0
- package/tests/init/render.spec.ts +216 -0
- package/tests/init/validate.spec.ts +80 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { inject, Injectable } from "@angular/core";
|
|
2
|
+
import { createOrmClientConnector, type OrmClientConnector } from "@simplysm/service-client";
|
|
3
|
+
import { MainDbContext } from "@{{workspaceName}}/common";
|
|
4
|
+
import { AppServiceProvider } from "./AppServiceProvider";
|
|
5
|
+
|
|
6
|
+
@Injectable({ providedIn: "root" })
|
|
7
|
+
export class AppOrmProvider {
|
|
8
|
+
private readonly _appService = inject(AppServiceProvider);
|
|
9
|
+
|
|
10
|
+
private _orm?: OrmClientConnector;
|
|
11
|
+
private get orm(): OrmClientConnector {
|
|
12
|
+
return (this._orm ??= createOrmClientConnector(this._appService.client));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
connectAsync<R>(callback: (db: MainDbContext) => Promise<R>): Promise<R> {
|
|
16
|
+
return this.orm.connect(
|
|
17
|
+
{
|
|
18
|
+
DbClass: MainDbContext,
|
|
19
|
+
connOpt: { configName: "MAIN" },
|
|
20
|
+
dbContextOpt: { database: "{{workspaceNameUpper}}", schema: "dbo" },
|
|
21
|
+
},
|
|
22
|
+
callback,
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
connectWithoutTransAsync<R>(callback: (db: MainDbContext) => Promise<R>): Promise<R> {
|
|
27
|
+
return this.orm.connectWithoutTransaction(
|
|
28
|
+
{
|
|
29
|
+
DbClass: MainDbContext,
|
|
30
|
+
connOpt: { configName: "MAIN" },
|
|
31
|
+
dbContextOpt: { database: "{{workspaceNameUpper}}", schema: "dbo" },
|
|
32
|
+
},
|
|
33
|
+
callback,
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { inject, Injectable } from "@angular/core";
|
|
2
|
+
import { SdServiceClientFactoryProvider } from "@simplysm/angular";
|
|
3
|
+
import { env, num, parseBoolEnv } from "@simplysm/core-common";
|
|
4
|
+
|
|
5
|
+
export const APP_MAIN_SERVICE_KEY = "MAIN";
|
|
6
|
+
|
|
7
|
+
@Injectable({ providedIn: "root" })
|
|
8
|
+
export class AppServiceProvider {
|
|
9
|
+
private readonly _sdServiceClientFactory = inject(SdServiceClientFactoryProvider);
|
|
10
|
+
|
|
11
|
+
get client() {
|
|
12
|
+
return this._sdServiceClientFactory.get(APP_MAIN_SERVICE_KEY);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async connectAsync() {
|
|
16
|
+
await this._sdServiceClientFactory.connectAsync(
|
|
17
|
+
APP_MAIN_SERVICE_KEY,
|
|
18
|
+
Boolean(env("SERVER_HOST"))
|
|
19
|
+
? {
|
|
20
|
+
host: env("SERVER_HOST"),
|
|
21
|
+
port: num.parseInt(env("SERVER_PORT")),
|
|
22
|
+
ssl: parseBoolEnv(env("SERVER_SSL")),
|
|
23
|
+
}
|
|
24
|
+
: {},
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"lib": ["ESNext", "DOM", "DOM.Iterable"],
|
|
5
|
+
"outDir": "./dist",
|
|
6
|
+
"typeRoots": ["./node_modules/@types"],
|
|
7
|
+
"customConditions": ["browser"]
|
|
8
|
+
},
|
|
9
|
+
"angularCompilerOptions": {
|
|
10
|
+
"strictTemplates": true,
|
|
11
|
+
"strictInjectionParameters": true,
|
|
12
|
+
"strictInputAccessModifiers": true,
|
|
13
|
+
"strictStandalone": true,
|
|
14
|
+
"typeCheckHostBindings": true,
|
|
15
|
+
"enableI18nLegacyMessageIdFormat": false,
|
|
16
|
+
"extendedDiagnostics": {
|
|
17
|
+
"defaultCategory": "error"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@{{workspaceName}}/server",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "{{description}} - Server",
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"private": true,
|
|
8
|
+
"dependencies": {
|
|
9
|
+
"@{{workspaceName}}/common": "workspace:*",
|
|
10
|
+
"@simplysm/core-common": "^14.0.0",
|
|
11
|
+
"@simplysm/core-node": "^14.0.0",
|
|
12
|
+
"@simplysm/service-common": "^14.0.0",
|
|
13
|
+
"@simplysm/service-server": "^14.0.0"{{#if hasDb}},
|
|
14
|
+
"@simplysm/orm-common": "^14.0.0",
|
|
15
|
+
"@simplysm/orm-node": "^14.0.0"{{#if isMysql}},
|
|
16
|
+
"mysql2": "^3.22.0"{{/if}}{{#if isPostgres}},
|
|
17
|
+
"pg": "^8.13.0"{{/if}}{{#if isMssql}},
|
|
18
|
+
"mssql": "^11.0.0"{{/if}}{{/if}}
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@types/node": "^20.19.0"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { env, num, parseBoolEnv } from "@simplysm/core-common";
|
|
3
|
+
import { setupConsola } from "@simplysm/core-node";
|
|
4
|
+
import { createServiceServer, getConfig{{#if hasDb}}, OrmService{{/if}} } from "@simplysm/service-server";
|
|
5
|
+
|
|
6
|
+
setupConsola();
|
|
7
|
+
|
|
8
|
+
const rootConfig = await getConfig<Record<string, unknown>>(
|
|
9
|
+
path.resolve(import.meta.dirname, ".config.json"),
|
|
10
|
+
);
|
|
11
|
+
const authConfig = rootConfig?.["auth"] as { jwtSecret: string } | undefined;
|
|
12
|
+
if (authConfig?.jwtSecret == null) {
|
|
13
|
+
throw new Error("auth.jwtSecret 설정을 찾을 수 없습니다. .config.json을 확인하세요.");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const server = createServiceServer({
|
|
17
|
+
rootPath: import.meta.dirname,
|
|
18
|
+
services: [{{#if hasDb}}OrmService{{/if}}],
|
|
19
|
+
port: num.parseInt(env("SERVER_PORT"))!,
|
|
20
|
+
auth: { jwtSecret: authConfig.jwtSecret },
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
if (!parseBoolEnv(env("DEV"))) {
|
|
24
|
+
await server.listen();
|
|
25
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
*.hbs
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
printWidth: 100
|
|
2
|
+
tabWidth: 2
|
|
3
|
+
useTabs: false
|
|
4
|
+
semi: true
|
|
5
|
+
quoteProps: consistent
|
|
6
|
+
trailingComma: all
|
|
7
|
+
bracketSpacing: true
|
|
8
|
+
bracketSameLine: false
|
|
9
|
+
arrowParens: always
|
|
10
|
+
endOfLine: lf
|
|
11
|
+
htmlWhitespaceSensitivity: ignore
|
|
12
|
+
embeddedLanguageFormatting: auto
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{workspaceName}}",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "{{description}}",
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"private": true,
|
|
8
|
+
"workspaces": ["packages/*", "tests/*"],
|
|
9
|
+
"scripts": {
|
|
10
|
+
"dev": "sd-cli dev",
|
|
11
|
+
"build": "sd-cli build",
|
|
12
|
+
"pub": "sd-cli publish",
|
|
13
|
+
"pub:no-build": "sd-cli publish --no-build",
|
|
14
|
+
"---": "",
|
|
15
|
+
"check": "sd-cli check",
|
|
16
|
+
"typecheck": "sd-cli check --type typecheck",
|
|
17
|
+
"lint": "sd-cli check --type lint",
|
|
18
|
+
"test": "vitest run --reporter=dot --silent=passed-only",
|
|
19
|
+
"----": "",
|
|
20
|
+
{{#if hasMobile}}
|
|
21
|
+
"run-device": "sd-cli device -t {{firstMobileClientName}}",
|
|
22
|
+
"-----": "",
|
|
23
|
+
{{/if}}
|
|
24
|
+
"replace-deps": "sd-cli replace-deps",
|
|
25
|
+
"postinstall": "npx -y skills add angular/skills --skill angular-developer -a claude-code -y && playwright-cli install --skills && playwright install chromium"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@playwright/cli": "^0.1.11",
|
|
29
|
+
"@simplysm/lint": "^14.0.0",
|
|
30
|
+
"@simplysm/sd-claude": "^14.0.0",
|
|
31
|
+
"@simplysm/sd-cli": "^14.0.0",
|
|
32
|
+
"@types/node": "^20.19.0",
|
|
33
|
+
"@vitest/browser-playwright": "^4.1.0",
|
|
34
|
+
"@vitest/coverage-v8": "^4.1.0",
|
|
35
|
+
"eslint": "^9.39.0",
|
|
36
|
+
"jiti": "^2.7.0",
|
|
37
|
+
"prettier": "^3.8.0",
|
|
38
|
+
"typescript": "^5.9.0",
|
|
39
|
+
"vite": "^7.3.0",
|
|
40
|
+
"vite-tsconfig-paths": "^6.1.0",
|
|
41
|
+
"vitest": "^4.1.0"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
packages:
|
|
2
|
+
- packages/*
|
|
3
|
+
- tests/*
|
|
4
|
+
allowBuilds:
|
|
5
|
+
'@parcel/watcher': true
|
|
6
|
+
'@simplysm/sd-claude': true
|
|
7
|
+
'@simplysm/sd-codex': true
|
|
8
|
+
bufferutil: true
|
|
9
|
+
core-js: true
|
|
10
|
+
cpu-features: true
|
|
11
|
+
esbuild: true
|
|
12
|
+
lmdb: true
|
|
13
|
+
msgpackr-extract: true
|
|
14
|
+
sharp: true
|
|
15
|
+
ssh2: true
|
|
16
|
+
unrs-resolver: true
|
|
17
|
+
utf-8-validate: true
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { SdConfigFn } from "@simplysm/sd-cli";
|
|
2
|
+
|
|
3
|
+
const config: SdConfigFn = () => ({
|
|
4
|
+
packages: {
|
|
5
|
+
{{#if hasServer}}
|
|
6
|
+
"server": {
|
|
7
|
+
target: "server",
|
|
8
|
+
env: {
|
|
9
|
+
SERVER_PORT: "{{serverPort}}",
|
|
10
|
+
},
|
|
11
|
+
configs: {
|
|
12
|
+
{{#if hasDb}}
|
|
13
|
+
orm: {
|
|
14
|
+
MAIN: {
|
|
15
|
+
dialect: "{{dbDialect}}",
|
|
16
|
+
host: "localhost",
|
|
17
|
+
port: {{dbPort}},
|
|
18
|
+
username: "root",
|
|
19
|
+
password: "1234",
|
|
20
|
+
database: "{{workspaceNameUpper}}",
|
|
21
|
+
defaultIsolationLevel: "READ_UNCOMMITTED",
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
{{/if}}
|
|
25
|
+
auth: {
|
|
26
|
+
jwtSecret: "{{jwtSecret}}",
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
{{/if}}
|
|
31
|
+
{{#if hasCommon}}
|
|
32
|
+
"common": {
|
|
33
|
+
target: "neutral",
|
|
34
|
+
},
|
|
35
|
+
{{/if}}
|
|
36
|
+
{{#if hasClientCommon}}
|
|
37
|
+
"client-common": {
|
|
38
|
+
target: "browser",
|
|
39
|
+
},
|
|
40
|
+
{{/if}}
|
|
41
|
+
{{#each clients}}
|
|
42
|
+
"{{name}}": {
|
|
43
|
+
target: "client",
|
|
44
|
+
{{#if ../hasServer}}
|
|
45
|
+
server: "server",
|
|
46
|
+
{{/if}}
|
|
47
|
+
{{#if isMobile}}
|
|
48
|
+
capacitor: {
|
|
49
|
+
appId: "{{../mobileAppId}}",
|
|
50
|
+
appName: "{{../description}}",
|
|
51
|
+
plugins: {},
|
|
52
|
+
icon: "res/icon.png",
|
|
53
|
+
platform: {
|
|
54
|
+
android: {},
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
{{/if}}
|
|
58
|
+
},
|
|
59
|
+
{{/each}}
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
export default config;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ESNext",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"lib": ["ESNext"],
|
|
6
|
+
"moduleResolution": "bundler",
|
|
7
|
+
"typeRoots": ["./node_modules/@types"],
|
|
8
|
+
"strict": true,
|
|
9
|
+
"noImplicitOverride": true,
|
|
10
|
+
"noImplicitReturns": true,
|
|
11
|
+
"noImplicitAny": true,
|
|
12
|
+
"noFallthroughCasesInSwitch": true,
|
|
13
|
+
"useUnknownInCatchVariables": true,
|
|
14
|
+
"noPropertyAccessFromIndexSignature": true,
|
|
15
|
+
"forceConsistentCasingInFileNames": true,
|
|
16
|
+
"useDefineForClassFields": true,
|
|
17
|
+
"esModuleInterop": true,
|
|
18
|
+
"verbatimModuleSyntax": true,
|
|
19
|
+
"declaration": true,
|
|
20
|
+
"declarationMap": true,
|
|
21
|
+
"sourceMap": true,
|
|
22
|
+
"inlineSourceMap": false,
|
|
23
|
+
"skipLibCheck": true,
|
|
24
|
+
"importHelpers": true,
|
|
25
|
+
"baseUrl": ".",
|
|
26
|
+
"paths": {
|
|
27
|
+
"@{{workspaceName}}/*": ["packages/*/src/index.ts"]
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { defineConfig } from "vitest/config";
|
|
2
|
+
import { playwright } from "@vitest/browser-playwright";
|
|
3
|
+
import { sdAngularPlugin } from "@simplysm/sd-cli";
|
|
4
|
+
import tsconfigPaths from "vite-tsconfig-paths";
|
|
5
|
+
|
|
6
|
+
process.env["DEV"] = "true";
|
|
7
|
+
process.env["VER"] = "1.0.0-test";
|
|
8
|
+
|
|
9
|
+
export default defineConfig({
|
|
10
|
+
plugins: [tsconfigPaths()],
|
|
11
|
+
define: {
|
|
12
|
+
"import.meta.env.DEV": JSON.stringify("true"),
|
|
13
|
+
"import.meta.env.VER": JSON.stringify("1.0.0-test"),
|
|
14
|
+
},
|
|
15
|
+
test: {
|
|
16
|
+
testTimeout: 30000,
|
|
17
|
+
coverage: { provider: "v8", reportsDirectory: "./.coverage" },
|
|
18
|
+
projects: [
|
|
19
|
+
{{#if hasServer}}
|
|
20
|
+
{
|
|
21
|
+
extends: true,
|
|
22
|
+
test: {
|
|
23
|
+
name: "node",
|
|
24
|
+
environment: "node",
|
|
25
|
+
include: [
|
|
26
|
+
"packages/server/tests/**/*.spec.{ts,js,mjs,cjs}",
|
|
27
|
+
{{#if hasCommon}}
|
|
28
|
+
"packages/common/tests/**/*.spec.{ts,js,mjs,cjs}",
|
|
29
|
+
{{/if}}
|
|
30
|
+
],
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
{{/if}}
|
|
34
|
+
{{#if hasClientCommon}}
|
|
35
|
+
{
|
|
36
|
+
extends: true,
|
|
37
|
+
plugins: [sdAngularPlugin({ pkg: "client-common" })],
|
|
38
|
+
test: {
|
|
39
|
+
name: "client-common",
|
|
40
|
+
setupFiles: ["packages/client-common/tests/vitest.setup.ts"],
|
|
41
|
+
include: ["packages/client-common/tests/**/*.spec.{ts,js,mjs,cjs}"],
|
|
42
|
+
browser: {
|
|
43
|
+
provider: playwright(),
|
|
44
|
+
enabled: true,
|
|
45
|
+
headless: true,
|
|
46
|
+
screenshotFailures: false,
|
|
47
|
+
instances: [{ browser: "chromium", viewport: { width: 1920, height: 1080 } }],
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
{{/if}}
|
|
52
|
+
{{#each clients}}
|
|
53
|
+
{
|
|
54
|
+
extends: true,
|
|
55
|
+
plugins: [sdAngularPlugin({ pkg: "{{name}}" })],
|
|
56
|
+
test: {
|
|
57
|
+
name: "{{name}}",
|
|
58
|
+
setupFiles: ["packages/{{name}}/tests/vitest.setup.ts"],
|
|
59
|
+
include: ["packages/{{name}}/tests/**/*.spec.{ts,js,mjs,cjs}"],
|
|
60
|
+
browser: {
|
|
61
|
+
provider: playwright(),
|
|
62
|
+
enabled: true,
|
|
63
|
+
headless: true,
|
|
64
|
+
screenshotFailures: false,
|
|
65
|
+
instances: [{ browser: "chromium", viewport: { width: 1920, height: 1080 } }],
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
{{/each}}
|
|
70
|
+
],
|
|
71
|
+
},
|
|
72
|
+
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export type ClientType = "web" | "mobile";
|
|
2
|
+
export type DbDialect = "mysql" | "postgres" | "mssql";
|
|
3
|
+
|
|
4
|
+
export interface ClientInputSpec {
|
|
5
|
+
name: string;
|
|
6
|
+
type: ClientType;
|
|
7
|
+
hasRouter: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface InitInput {
|
|
11
|
+
workspaceName: string;
|
|
12
|
+
description: string;
|
|
13
|
+
hasServer: boolean;
|
|
14
|
+
clients: ClientInputSpec[];
|
|
15
|
+
hasDb: boolean;
|
|
16
|
+
dbDialect?: DbDialect;
|
|
17
|
+
mobileAppId?: string;
|
|
18
|
+
serverPort?: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface ClientSpec {
|
|
22
|
+
name: string;
|
|
23
|
+
type: ClientType;
|
|
24
|
+
hasRouter: boolean;
|
|
25
|
+
isMobile: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface NormalizedInput {
|
|
29
|
+
workspaceName: string;
|
|
30
|
+
workspaceNameUpper: string;
|
|
31
|
+
description: string;
|
|
32
|
+
hasServer: boolean;
|
|
33
|
+
hasDb: boolean;
|
|
34
|
+
dbDialect?: DbDialect;
|
|
35
|
+
dbPort: number;
|
|
36
|
+
isMysql: boolean;
|
|
37
|
+
isPostgres: boolean;
|
|
38
|
+
isMssql: boolean;
|
|
39
|
+
serverPort: number;
|
|
40
|
+
mobileAppId?: string;
|
|
41
|
+
firstMobileClientName?: string;
|
|
42
|
+
clients: ClientSpec[];
|
|
43
|
+
hasCommon: boolean;
|
|
44
|
+
hasClientCommon: boolean;
|
|
45
|
+
hasMobile: boolean;
|
|
46
|
+
hasAnyClient: boolean;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface RenderData extends NormalizedInput {
|
|
50
|
+
jwtSecret: string;
|
|
51
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { fsx } from "@simplysm/core-node";
|
|
2
|
+
import type { InitInput } from "./types";
|
|
3
|
+
|
|
4
|
+
const KEBAB_CASE_RE = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
|
|
5
|
+
|
|
6
|
+
export async function validateBeforePrompt(cwd: string): Promise<void> {
|
|
7
|
+
if (!(await fsx.exists(cwd))) return;
|
|
8
|
+
const children = await fsx.readdir(cwd);
|
|
9
|
+
const significant = children.filter((n) => n !== ".git");
|
|
10
|
+
if (significant.length > 0) {
|
|
11
|
+
throw new Error(
|
|
12
|
+
`작업 디렉토리가 비어있지 않습니다 (감지된 항목: ${significant.join(", ")}). 빈 디렉토리에서 다시 실행하세요.`,
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function validateInput(input: InitInput): void {
|
|
18
|
+
if (!input.hasServer && input.clients.length === 0) {
|
|
19
|
+
throw new Error("server 도 client 도 없는 워크스페이스는 만들 수 없습니다.");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (!KEBAB_CASE_RE.test(input.workspaceName)) {
|
|
23
|
+
throw new Error(
|
|
24
|
+
`워크스페이스 이름은 영문 kebab-case 여야 합니다 (예: my-workspace). 입력값: "${input.workspaceName}"`,
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const clientNames = new Set<string>();
|
|
29
|
+
for (const c of input.clients) {
|
|
30
|
+
if (!KEBAB_CASE_RE.test(c.name)) {
|
|
31
|
+
throw new Error(
|
|
32
|
+
`client 이름은 영문 kebab-case 여야 합니다. 입력값: "${c.name}"`,
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
const normalized = c.name.startsWith("client-") ? c.name : `client-${c.name}`;
|
|
36
|
+
if (clientNames.has(normalized)) {
|
|
37
|
+
throw new Error(`client 이름이 중복됩니다: "${normalized}"`);
|
|
38
|
+
}
|
|
39
|
+
clientNames.add(normalized);
|
|
40
|
+
}
|
|
41
|
+
}
|
package/src/sd-cli-entry.ts
CHANGED
|
@@ -9,6 +9,7 @@ import { type CheckType, runCheck } from "./commands/check";
|
|
|
9
9
|
import { runWatch } from "./commands/watch";
|
|
10
10
|
import { runDev } from "./commands/dev";
|
|
11
11
|
import { runBuild } from "./commands/build";
|
|
12
|
+
import { runInit } from "./commands/init/init";
|
|
12
13
|
import { runPublish } from "./commands/publish/publish-command";
|
|
13
14
|
import { runReplaceDeps } from "./commands/replace-deps";
|
|
14
15
|
import path from "path";
|
|
@@ -23,7 +24,16 @@ const logger = createLazyLogger("sd:cli:entry");
|
|
|
23
24
|
Error.stackTraceLimit = Infinity;
|
|
24
25
|
EventEmitter.defaultMaxListeners = 100;
|
|
25
26
|
|
|
26
|
-
const COMMAND_NAMES = [
|
|
27
|
+
const COMMAND_NAMES = [
|
|
28
|
+
"check",
|
|
29
|
+
"watch",
|
|
30
|
+
"dev",
|
|
31
|
+
"device",
|
|
32
|
+
"build",
|
|
33
|
+
"publish",
|
|
34
|
+
"replace-deps",
|
|
35
|
+
"init",
|
|
36
|
+
];
|
|
27
37
|
|
|
28
38
|
async function collectYargsHelp(argv: string[]): Promise<string> {
|
|
29
39
|
const lines: string[] = [];
|
|
@@ -322,6 +332,14 @@ export function createCliParser(argv: string[]): Argv {
|
|
|
322
332
|
});
|
|
323
333
|
},
|
|
324
334
|
)
|
|
335
|
+
.command(
|
|
336
|
+
"init",
|
|
337
|
+
"Bootstrap a new SI workspace via interactive prompts",
|
|
338
|
+
(cmd) => cmd.version(false).hide("help"),
|
|
339
|
+
async () => {
|
|
340
|
+
await runInit({ cwd: process.cwd() });
|
|
341
|
+
},
|
|
342
|
+
)
|
|
325
343
|
.demandCommand(1, "Please specify a command.")
|
|
326
344
|
.strict()
|
|
327
345
|
.fail((msg, err) => {
|