@gaonjs/cli 0.2.0 → 0.4.0
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/__fixtures__/db-minimal/domain/schema/widgets.d.ts +12 -0
- package/dist/__fixtures__/db-minimal/domain/schema/widgets.js +7 -0
- package/dist/__fixtures__/db-minimal/gaon.config.d.ts +2 -0
- package/dist/__fixtures__/db-minimal/gaon.config.js +11 -0
- package/dist/commands/db.d.ts +20 -0
- package/dist/commands/db.js +74 -0
- package/dist/commands/dev.d.ts +68 -0
- package/dist/commands/dev.js +287 -0
- package/dist/commands/g.d.ts +26 -0
- package/dist/commands/g.js +124 -0
- package/dist/db/diff.d.ts +17 -0
- package/dist/db/diff.js +57 -0
- package/dist/db/index.d.ts +4 -0
- package/dist/db/index.js +8 -0
- package/dist/db/migrate.d.ts +16 -0
- package/dist/db/migrate.js +173 -0
- package/dist/db/reset.d.ts +18 -0
- package/dist/db/reset.js +150 -0
- package/dist/db/resolve.d.ts +32 -0
- package/dist/db/resolve.js +130 -0
- package/dist/dev/console.d.ts +39 -0
- package/dist/dev/console.js +100 -0
- package/dist/dev/docker.d.ts +52 -0
- package/dist/dev/docker.js +163 -0
- package/dist/dev/index.d.ts +14 -0
- package/dist/dev/index.js +10 -0
- package/dist/dev/tsc.d.ts +41 -0
- package/dist/dev/tsc.js +127 -0
- package/dist/dev/watcher.d.ts +50 -0
- package/dist/dev/watcher.js +95 -0
- package/dist/dev.d.ts +1 -16
- package/dist/dev.js +10 -66
- package/dist/doctor/connections.d.ts +14 -0
- package/dist/doctor/connections.js +168 -0
- package/dist/doctor/dependency-direction.d.ts +11 -0
- package/dist/doctor/dependency-direction.js +186 -0
- package/dist/doctor/migration-diff.d.ts +11 -0
- package/dist/doctor/migration-diff.js +109 -0
- package/dist/doctor/n-plus-one.d.ts +5 -0
- package/dist/doctor/n-plus-one.js +242 -0
- package/dist/doctor/reporter.d.ts +5 -0
- package/dist/doctor/reporter.js +41 -0
- package/dist/doctor/response-mixing.d.ts +12 -0
- package/dist/doctor/response-mixing.js +158 -0
- package/dist/doctor/setup.d.ts +26 -0
- package/dist/doctor/setup.js +52 -0
- package/dist/doctor/types.d.ts +37 -0
- package/dist/doctor/types.js +34 -0
- package/dist/doctor.d.ts +40 -23
- package/dist/doctor.js +131 -202
- package/dist/index.d.ts +14 -2
- package/dist/index.js +171 -28
- package/dist/scaffold/controller.d.ts +12 -0
- package/dist/scaffold/controller.js +50 -0
- package/dist/scaffold/index.d.ts +20 -0
- package/dist/scaffold/index.js +41 -0
- package/dist/scaffold/inflect.d.ts +19 -0
- package/dist/scaffold/inflect.js +50 -0
- package/dist/scaffold/job.d.ts +3 -0
- package/dist/scaffold/job.js +46 -0
- package/dist/scaffold/model.d.ts +8 -0
- package/dist/scaffold/model.js +66 -0
- package/dist/scaffold/page.d.ts +7 -0
- package/dist/scaffold/page.js +46 -0
- package/dist/serve.d.ts +18 -0
- package/dist/serve.js +79 -0
- package/dist/templates/auth/auth.wiring.ts.tpl +1 -1
- package/dist/templates/auth/session.controller.ts.tpl +1 -1
- package/package.json +4 -3
package/dist/doctor.d.ts
CHANGED
|
@@ -1,36 +1,53 @@
|
|
|
1
|
-
|
|
2
|
-
export
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import { type DoctorCheck, type DoctorFatal, type DoctorFatalCode, type DoctorLevel, type DoctorResult, type DoctorRule, type RuleReport } from './doctor/types.js';
|
|
2
|
+
export type { DoctorCheck, DoctorFatal, DoctorFatalCode, DoctorLevel, DoctorResult, DoctorRule, RuleReport, };
|
|
3
|
+
export type { ResponseKind, ActionUsage } from './doctor/response-mixing.js';
|
|
4
|
+
export { inspectControllerSource, checkResponseMixing } from './doctor/response-mixing.js';
|
|
5
|
+
export { inspectControllerForNPlusOne, checkNPlusOne } from './doctor/n-plus-one.js';
|
|
6
|
+
export { extractRelativeImports, checkDependencyDirection } from './doctor/dependency-direction.js';
|
|
7
|
+
export { extractConfigDbKeys, extractKeyUses, checkConnections } from './doctor/connections.js';
|
|
8
|
+
export { scanSchema, checkMigrationDiff } from './doctor/migration-diff.js';
|
|
9
|
+
export { renderHuman, renderJson } from './doctor/reporter.js';
|
|
10
|
+
export { checkTypeScriptApi, detectProject, fatalNoProject, fatalTsApiMissing, } from './doctor/setup.js';
|
|
11
|
+
export interface DoctorCommandOptions {
|
|
12
|
+
readonly cwd?: string;
|
|
13
|
+
readonly json?: boolean;
|
|
14
|
+
readonly checks?: readonly DoctorRule[];
|
|
7
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* `gaon doctor` 진입점 (M9-E · 확장 · M9-E-Fix 하드닝).
|
|
18
|
+
*
|
|
19
|
+
* 반환: DoctorResult({passed, warnings, errors, fatal?}).
|
|
20
|
+
* exit code 는 진입점(runCli)이 결정한다:
|
|
21
|
+
* - result.fatal 이 있으면 → exit 2 (사용자 오류 · 크래시 아님)
|
|
22
|
+
* - result.errors.length > 0 → exit 1
|
|
23
|
+
* - 그 외 → exit 0
|
|
24
|
+
*
|
|
25
|
+
* 사전 검사 2 단계:
|
|
26
|
+
* 1) detectProject — gaon 프로젝트 마커가 하나도 없으면 fatal:no-project
|
|
27
|
+
* 로 종료(예: 사용자가 실수로 빈 폴더에서 실행).
|
|
28
|
+
* 2) checkTypeScriptApi — typescript 컴파일러 API 가 없으면(예: 사용자
|
|
29
|
+
* 프로젝트가 typescript@7 을 받았을 때 default export 가 스텁이라
|
|
30
|
+
* ts.ScriptTarget 이 undefined) fatal:ts-api-missing 로 종료 —
|
|
31
|
+
* "Cannot read properties of undefined (reading 'ES2022')" 크래시 대신.
|
|
32
|
+
* 규칙 실행 중 예외는 각 규칙 단위로 catch → 그 규칙만 error 로 리포트,
|
|
33
|
+
* 나머지 규칙은 계속 실행(부분 결과 확보).
|
|
34
|
+
*/
|
|
35
|
+
export declare function runDoctorCommand(opts?: DoctorCommandOptions): Promise<DoctorResult>;
|
|
8
36
|
export interface DoctorIssue {
|
|
9
37
|
readonly file: string;
|
|
10
38
|
readonly line: number;
|
|
11
39
|
readonly action: string;
|
|
12
|
-
readonly kinds: readonly
|
|
40
|
+
readonly kinds: readonly string[];
|
|
13
41
|
readonly message: string;
|
|
14
42
|
}
|
|
15
|
-
export interface
|
|
43
|
+
export interface LegacyDoctorResult {
|
|
16
44
|
readonly ok: boolean;
|
|
17
45
|
readonly checked: number;
|
|
18
46
|
readonly issues: readonly DoctorIssue[];
|
|
19
47
|
}
|
|
20
|
-
/** 컨트롤러 파일 하나를 검사한다. */
|
|
21
|
-
export declare function inspectControllerSource(file: string, source: string): ActionUsage[];
|
|
22
48
|
/**
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* 명시적 예외: 반환 없는 return(=undefined→204)만 있는 경우는 통과 — void 액션은
|
|
27
|
-
* 흔히 혼용 없이 정상적으로 쓰인다.
|
|
49
|
+
* 하위 호환: 응답 혼용만 검사해 legacy shape 을 낸다.
|
|
50
|
+
* appsDir 은 프로젝트의 apps/ 절대 경로. 상위(진입점)는 runDoctorCommand
|
|
51
|
+
* 를 쓴다.
|
|
28
52
|
*/
|
|
29
|
-
export declare function runDoctor(appsDir: string): Promise<
|
|
30
|
-
export declare function fileExists(file: string): Promise<boolean>;
|
|
31
|
-
export interface DoctorCommandOptions {
|
|
32
|
-
readonly cwd?: string;
|
|
33
|
-
readonly json?: boolean;
|
|
34
|
-
}
|
|
35
|
-
/** `gaon doctor` 진입점. */
|
|
36
|
-
export declare function runDoctorCommand(opts?: DoctorCommandOptions): Promise<number>;
|
|
53
|
+
export declare function runDoctor(appsDir: string): Promise<LegacyDoctorResult>;
|
package/dist/doctor.js
CHANGED
|
@@ -1,218 +1,147 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @gaonjs/cli · `gaon doctor` — 정적 검사 (
|
|
2
|
+
* @gaonjs/cli · `gaon doctor` — 정적 검사 (M9-E · CLI DX 완성)
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* 5 검사를 조립한다:
|
|
5
|
+
* 1) response-mixing (errata E-3 §C · 라이브)
|
|
6
|
+
* 2) n-plus-one (errata E-4 (e))
|
|
7
|
+
* 3) dependency-direction (CLAUDE.md §5 · 4 규칙)
|
|
8
|
+
* 4) connections (v0.15 §4.5)
|
|
9
|
+
* 5) migration-diff (최소 감지 · 상세는 M9-D)
|
|
8
10
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* 최종 응답 형태를 분류(render | redirect | json | plain).
|
|
13
|
-
* · 서로 다른 형태가 두 종류 이상이면 위반.
|
|
14
|
-
* · this.render/this.redirect/this.json/평범한 객체 반환의 4종을 구별한다.
|
|
11
|
+
* 각 검사는 순수 함수(cwd → RuleReport). 상위 runDoctorCommand 가 조립해
|
|
12
|
+
* DoctorResult 로 낸다. --json 은 자동화(CI)를 위해 반드시 파싱 가능한
|
|
13
|
+
* 구조를 유지한다(CLAUDE.md §4 · 모든 명령 --json).
|
|
15
14
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* 나머지 doctor 검사(의존 방향 §1-5, 커넥션 §4.5 등)는 후속 마일스톤에서
|
|
20
|
-
* 이 모듈에 규칙을 얹는다.
|
|
15
|
+
* 하위 호환: 기존 응답 혼용 API(`inspectControllerSource`·`runDoctor`)는
|
|
16
|
+
* 그대로 export — 기존 테스트가 계속 동작한다.
|
|
21
17
|
*/
|
|
22
|
-
import {
|
|
23
|
-
import { join, relative, resolve } from 'node:path';
|
|
18
|
+
import { resolve } from 'node:path';
|
|
24
19
|
import ts from 'typescript';
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
return false;
|
|
58
|
-
}
|
|
59
|
-
function actionName(prop) {
|
|
60
|
-
if (ts.isMethodDeclaration(prop) && ts.isIdentifier(prop.name))
|
|
61
|
-
return prop.name.text;
|
|
62
|
-
if (ts.isPropertyAssignment(prop) &&
|
|
63
|
-
ts.isIdentifier(prop.name) &&
|
|
64
|
-
(ts.isArrowFunction(prop.initializer) || ts.isFunctionExpression(prop.initializer))) {
|
|
65
|
-
return prop.name.text;
|
|
66
|
-
}
|
|
67
|
-
return undefined;
|
|
68
|
-
}
|
|
69
|
-
function actionBody(prop) {
|
|
70
|
-
if (ts.isMethodDeclaration(prop))
|
|
71
|
-
return prop.body;
|
|
72
|
-
if (ts.isPropertyAssignment(prop)) {
|
|
73
|
-
if (ts.isArrowFunction(prop.initializer))
|
|
74
|
-
return prop.initializer.body;
|
|
75
|
-
if (ts.isFunctionExpression(prop.initializer))
|
|
76
|
-
return prop.initializer.body;
|
|
77
|
-
}
|
|
78
|
-
return undefined;
|
|
79
|
-
}
|
|
80
|
-
/** 액션 본문의 모든 return 표현식을 응답 형태로 분류하고 중복을 제거해 반환한다. */
|
|
81
|
-
function classifyReturns(body) {
|
|
82
|
-
const kinds = new Set();
|
|
83
|
-
const classify = (expr) => {
|
|
84
|
-
// await x → x 로 되짚어 본다.
|
|
85
|
-
if (ts.isAwaitExpression(expr))
|
|
86
|
-
return classify(expr.expression);
|
|
87
|
-
// this.render / this.redirect / this.json
|
|
88
|
-
if (ts.isCallExpression(expr)) {
|
|
89
|
-
const c = expr.expression;
|
|
90
|
-
if (ts.isPropertyAccessExpression(c) && c.expression.kind === ts.SyntaxKind.ThisKeyword) {
|
|
91
|
-
if (c.name.text === 'render')
|
|
92
|
-
return 'render';
|
|
93
|
-
if (c.name.text === 'redirect')
|
|
94
|
-
return 'redirect';
|
|
95
|
-
if (c.name.text === 'json')
|
|
96
|
-
return 'json';
|
|
97
|
-
}
|
|
98
|
-
// 삼항·논리 연산 결과라도 호출식이 나오면 함수 반환값에 의존 — 보수적으로 plain.
|
|
99
|
-
return 'plain';
|
|
100
|
-
}
|
|
101
|
-
// 삼항 연산자 — 양쪽 각각 분류
|
|
102
|
-
if (ts.isConditionalExpression(expr)) {
|
|
103
|
-
kinds.add(classify(expr.whenTrue));
|
|
104
|
-
kinds.add(classify(expr.whenFalse));
|
|
105
|
-
return classify(expr.whenTrue); // 반환값(대표) — Set 에 이미 다 넣음
|
|
106
|
-
}
|
|
107
|
-
// 그 외(객체·배열·literal·식별자 등) → plain(JSON 액션)
|
|
108
|
-
return 'plain';
|
|
109
|
-
};
|
|
110
|
-
const visit = (node) => {
|
|
111
|
-
if (ts.isReturnStatement(node)) {
|
|
112
|
-
if (node.expression)
|
|
113
|
-
kinds.add(classify(node.expression));
|
|
114
|
-
else
|
|
115
|
-
kinds.add('plain'); // 빈 return 은 undefined → 204 (plain 으로 취급, 혼용 검사에서만 의미)
|
|
116
|
-
return; // return 안의 하위 함수는 다른 스코프 — 이 return 만 잡는다
|
|
117
|
-
}
|
|
118
|
-
// 중첩 함수/화살표는 다른 스코프 — 파고들지 않는다.
|
|
119
|
-
if (ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node) || ts.isArrowFunction(node))
|
|
120
|
-
return;
|
|
121
|
-
ts.forEachChild(node, visit);
|
|
122
|
-
};
|
|
123
|
-
// 표현식 본문 화살표(=> render(...))는 body 가 표현식.
|
|
124
|
-
if (ts.isBlock(body))
|
|
125
|
-
visit(body);
|
|
126
|
-
else
|
|
127
|
-
kinds.add(classify(body));
|
|
128
|
-
return [...kinds];
|
|
129
|
-
}
|
|
20
|
+
import { checkResponseMixing } from './doctor/response-mixing.js';
|
|
21
|
+
import { checkNPlusOne } from './doctor/n-plus-one.js';
|
|
22
|
+
import { checkDependencyDirection } from './doctor/dependency-direction.js';
|
|
23
|
+
import { checkConnections } from './doctor/connections.js';
|
|
24
|
+
import { checkMigrationDiff } from './doctor/migration-diff.js';
|
|
25
|
+
import { renderHuman, renderJson } from './doctor/reporter.js';
|
|
26
|
+
import { checkTypeScriptApi, detectProject, fatalNoProject, fatalTsApiMissing, } from './doctor/setup.js';
|
|
27
|
+
import { makeResult, } from './doctor/types.js';
|
|
28
|
+
export { inspectControllerSource, checkResponseMixing } from './doctor/response-mixing.js';
|
|
29
|
+
export { inspectControllerForNPlusOne, checkNPlusOne } from './doctor/n-plus-one.js';
|
|
30
|
+
export { extractRelativeImports, checkDependencyDirection } from './doctor/dependency-direction.js';
|
|
31
|
+
export { extractConfigDbKeys, extractKeyUses, checkConnections } from './doctor/connections.js';
|
|
32
|
+
export { scanSchema, checkMigrationDiff } from './doctor/migration-diff.js';
|
|
33
|
+
export { renderHuman, renderJson } from './doctor/reporter.js';
|
|
34
|
+
export { checkTypeScriptApi, detectProject, fatalNoProject, fatalTsApiMissing, } from './doctor/setup.js';
|
|
35
|
+
/**
|
|
36
|
+
* 실행할 검사 이름. 지정 없음(undefined) = 5개 모두.
|
|
37
|
+
*/
|
|
38
|
+
const ALL_RULES = [
|
|
39
|
+
'response-mixing',
|
|
40
|
+
'n-plus-one',
|
|
41
|
+
'dependency-direction',
|
|
42
|
+
'connections',
|
|
43
|
+
'migration-diff',
|
|
44
|
+
];
|
|
45
|
+
const CHECKERS = {
|
|
46
|
+
'response-mixing': checkResponseMixing,
|
|
47
|
+
'n-plus-one': checkNPlusOne,
|
|
48
|
+
'dependency-direction': checkDependencyDirection,
|
|
49
|
+
connections: checkConnections,
|
|
50
|
+
'migration-diff': checkMigrationDiff,
|
|
51
|
+
};
|
|
130
52
|
/**
|
|
131
|
-
*
|
|
132
|
-
*
|
|
53
|
+
* `gaon doctor` 진입점 (M9-E · 확장 · M9-E-Fix 하드닝).
|
|
54
|
+
*
|
|
55
|
+
* 반환: DoctorResult({passed, warnings, errors, fatal?}).
|
|
56
|
+
* exit code 는 진입점(runCli)이 결정한다:
|
|
57
|
+
* - result.fatal 이 있으면 → exit 2 (사용자 오류 · 크래시 아님)
|
|
58
|
+
* - result.errors.length > 0 → exit 1
|
|
59
|
+
* - 그 외 → exit 0
|
|
133
60
|
*
|
|
134
|
-
*
|
|
135
|
-
*
|
|
61
|
+
* 사전 검사 2 단계:
|
|
62
|
+
* 1) detectProject — gaon 프로젝트 마커가 하나도 없으면 fatal:no-project
|
|
63
|
+
* 로 종료(예: 사용자가 실수로 빈 폴더에서 실행).
|
|
64
|
+
* 2) checkTypeScriptApi — typescript 컴파일러 API 가 없으면(예: 사용자
|
|
65
|
+
* 프로젝트가 typescript@7 을 받았을 때 default export 가 스텁이라
|
|
66
|
+
* ts.ScriptTarget 이 undefined) fatal:ts-api-missing 로 종료 —
|
|
67
|
+
* "Cannot read properties of undefined (reading 'ES2022')" 크래시 대신.
|
|
68
|
+
* 규칙 실행 중 예외는 각 규칙 단위로 catch → 그 규칙만 error 로 리포트,
|
|
69
|
+
* 나머지 규칙은 계속 실행(부분 결과 확보).
|
|
136
70
|
*/
|
|
137
|
-
export async function runDoctor(appsDir) {
|
|
138
|
-
const issues = [];
|
|
139
|
-
let checked = 0;
|
|
140
|
-
const apps = await safeListDirs(appsDir);
|
|
141
|
-
for (const app of apps) {
|
|
142
|
-
const ctrlDir = join(appsDir, app, 'controllers');
|
|
143
|
-
for (const file of await safeListFiles(ctrlDir)) {
|
|
144
|
-
if (!file.endsWith('.ts') || file.endsWith('.d.ts') || file.endsWith('.test.ts'))
|
|
145
|
-
continue;
|
|
146
|
-
const full = join(ctrlDir, file);
|
|
147
|
-
const source = await readFile(full, 'utf8');
|
|
148
|
-
const usages = inspectControllerSource(full, source);
|
|
149
|
-
checked += usages.length;
|
|
150
|
-
for (const u of usages) {
|
|
151
|
-
if (u.kinds.length < 2)
|
|
152
|
-
continue;
|
|
153
|
-
// 'plain' 이 유일한 다른 형태이면서 실제 return 문에 undefined 만 있는 경우는 걸러야
|
|
154
|
-
// 하지만, 액션이 render|json 를 하다가 조건에 따라 undefined 반환하면 혼용이다. 유지.
|
|
155
|
-
const rel = relative(process.cwd(), full);
|
|
156
|
-
issues.push({
|
|
157
|
-
file: rel,
|
|
158
|
-
line: u.line,
|
|
159
|
-
action: u.action,
|
|
160
|
-
kinds: u.kinds,
|
|
161
|
-
message: `[doctor] 응답 혼용 감지: ${rel}:${u.line} · 액션 '${u.action}' 이 서로 다른 응답 형태를 섞습니다 (${u.kinds.join(', ')}).\n` +
|
|
162
|
-
`→ 한 액션은 한 응답 형태만 유지하세요 (render | JSON | redirect · errata E-3 §C).\n` +
|
|
163
|
-
`→ 조건 분기가 필요하면 액션을 둘로 나누거나, JSON 반환에는 예외를 던져 상태 코드를 표현하세요.`,
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
return { ok: issues.length === 0, checked, issues };
|
|
169
|
-
}
|
|
170
|
-
async function safeListDirs(dir) {
|
|
171
|
-
try {
|
|
172
|
-
const entries = await readdir(dir, { withFileTypes: true });
|
|
173
|
-
return entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
174
|
-
}
|
|
175
|
-
catch {
|
|
176
|
-
return [];
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
async function safeListFiles(dir) {
|
|
180
|
-
try {
|
|
181
|
-
const entries = await readdir(dir, { withFileTypes: true });
|
|
182
|
-
return entries.filter((e) => e.isFile()).map((e) => e.name);
|
|
183
|
-
}
|
|
184
|
-
catch {
|
|
185
|
-
return [];
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
// stat 은 API 표면 유지용 — 파일 존재 확인이 필요할 때 씀.
|
|
189
|
-
export async function fileExists(file) {
|
|
190
|
-
try {
|
|
191
|
-
await stat(file);
|
|
192
|
-
return true;
|
|
193
|
-
}
|
|
194
|
-
catch {
|
|
195
|
-
return false;
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
/** `gaon doctor` 진입점. */
|
|
199
71
|
export async function runDoctorCommand(opts = {}) {
|
|
200
72
|
const cwd = opts.cwd ?? process.cwd();
|
|
201
|
-
const
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
|
|
73
|
+
const root = resolve(cwd);
|
|
74
|
+
const json = opts.json ?? false;
|
|
75
|
+
// 1) 프로젝트 마커 검사
|
|
76
|
+
if (!detectProject(root)) {
|
|
77
|
+
const result = {
|
|
78
|
+
passed: [],
|
|
79
|
+
warnings: [],
|
|
80
|
+
errors: [],
|
|
81
|
+
fatal: fatalNoProject(root),
|
|
82
|
+
};
|
|
83
|
+
process.stdout.write((json ? renderJson(result) : renderHuman(result)) + '\n');
|
|
84
|
+
return result;
|
|
85
|
+
}
|
|
86
|
+
// 2) typescript compiler API 검사
|
|
87
|
+
if (!checkTypeScriptApi()) {
|
|
88
|
+
const installed = ts.version;
|
|
89
|
+
const result = {
|
|
90
|
+
passed: [],
|
|
91
|
+
warnings: [],
|
|
92
|
+
errors: [],
|
|
93
|
+
fatal: fatalTsApiMissing(installed),
|
|
94
|
+
};
|
|
95
|
+
process.stdout.write((json ? renderJson(result) : renderHuman(result)) + '\n');
|
|
96
|
+
return result;
|
|
205
97
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
98
|
+
const rules = opts.checks && opts.checks.length ? opts.checks : ALL_RULES;
|
|
99
|
+
const reports = [];
|
|
100
|
+
for (const rule of rules) {
|
|
101
|
+
const fn = CHECKERS[rule];
|
|
102
|
+
try {
|
|
103
|
+
reports.push(await fn(root));
|
|
210
104
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
105
|
+
catch (err) {
|
|
106
|
+
// 규칙 하나가 크래시해도 나머지 규칙 실행은 계속한다. 사용자는
|
|
107
|
+
// 크래시 대신 어느 규칙이 왜 실패했는지 안내받는다.
|
|
108
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
109
|
+
reports.push({
|
|
110
|
+
rule,
|
|
111
|
+
issues: [
|
|
112
|
+
{
|
|
113
|
+
rule,
|
|
114
|
+
level: 'error',
|
|
115
|
+
message: `${rule} 검사 중 예외 발생: ${msg}\n` +
|
|
116
|
+
`→ 나머지 규칙만 임시로 실행하려면 '--check=<다른 규칙>' 를 쓰세요.`,
|
|
117
|
+
detail: { thrown: msg },
|
|
118
|
+
},
|
|
119
|
+
],
|
|
120
|
+
});
|
|
215
121
|
}
|
|
216
122
|
}
|
|
217
|
-
|
|
123
|
+
const result = makeResult(reports);
|
|
124
|
+
const out = json ? renderJson(result) : renderHuman(result);
|
|
125
|
+
process.stdout.write(out + '\n');
|
|
126
|
+
return result;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* 하위 호환: 응답 혼용만 검사해 legacy shape 을 낸다.
|
|
130
|
+
* appsDir 은 프로젝트의 apps/ 절대 경로. 상위(진입점)는 runDoctorCommand
|
|
131
|
+
* 를 쓴다.
|
|
132
|
+
*/
|
|
133
|
+
export async function runDoctor(appsDir) {
|
|
134
|
+
// apps/ 의 부모를 cwd 로 간주(관례).
|
|
135
|
+
const cwd = resolve(appsDir, '..');
|
|
136
|
+
const report = await checkResponseMixing(cwd);
|
|
137
|
+
const issues = report.issues.map((i) => ({
|
|
138
|
+
file: i.file ?? '',
|
|
139
|
+
line: i.line ?? 0,
|
|
140
|
+
action: String(i.detail?.action ?? ''),
|
|
141
|
+
kinds: i.detail?.kinds ?? [],
|
|
142
|
+
message: i.message,
|
|
143
|
+
}));
|
|
144
|
+
// checked 는 컨트롤러 액션 수 — 응답 혼용 검사가 순회한 액션 수와 정합.
|
|
145
|
+
// 하위 호환 목적으로 issues 수로 대체(정확한 checked 는 신규 API 를 쓰라).
|
|
146
|
+
return { ok: issues.length === 0, checked: issues.length, issues };
|
|
218
147
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
|
-
|
|
1
|
+
import { type DoctorRule } from "./doctor.js";
|
|
2
|
+
export { startDev, resolveDevLayout, type DevDeps, type DevLayout, type DevApp, type DevEvent, type DevHandle, } from "./dev.js";
|
|
3
|
+
export { runDevCommand, type DevCommandOptions } from "./commands/dev.js";
|
|
4
|
+
export { createDevConsole, findComposeFile, isDockerAvailable, inspectCompose, composeUp, composeDown, ensureInfra, startTscWatchers, killChild, startRestartWatcher, isRestartChange, resolveWatchRoots, type DevConsole, type DevConsoleOptions, type DevSource, type DevLevel, type ComposeStatus, type ComposeUpOptions, type EnsureInfraResult, type DockerLocateOptions, type TscWatcherOptions, type TscWatcherHandle, type RestartWatcherOptions, type RestartWatcherHandle, } from "./dev/index.js";
|
|
2
5
|
export { runCheck, runCheckCommand, type CheckDeps, type CheckResult, type TypecheckResult, type CheckCommandOptions, } from "./check.js";
|
|
3
6
|
export { writeAuthScaffold, authScaffoldFiles, patchRoutes, runGenerateAuthCommand, type AuthScaffoldOptions, type ScaffoldFile, type ScaffoldResult, type GenerateAuthOptions, } from "./generate.js";
|
|
7
|
+
export { runGenerateCommand, planScaffold, parseGenerateArgs, type GenerateType, type GenerateOptions, type GenerateResult, } from "./commands/g.js";
|
|
4
8
|
export { runHubCommand, type HubCommandOptions } from "./hub.js";
|
|
9
|
+
export { runServeCommand, type ServeCommandOptions } from "./serve.js";
|
|
5
10
|
export { runWorkCommand, type WorkCommandOptions } from "./work.js";
|
|
6
11
|
export { runJobsCommand, type JobsCommandOptions } from "./jobs.js";
|
|
7
12
|
export { runDbSeedCommand, loadSeed, type DbSeedOptions, type DbSeedResult } from "./db.js";
|
|
8
|
-
export {
|
|
13
|
+
export { runDbCommand, type DbSubcommand, type DbCommandOptions, } from "./commands/db.js";
|
|
14
|
+
export { runDbDiff, runDbMigrate, runDbReset, resolveDbTarget, type DbDiffOptions, type DbDiffResult, type DbMigrateOptions, type DbMigrateResult, type DbResetOptions, type DbResetResult, type ResolveDbOptions, type ResolvedDbTarget, } from "./db/index.js";
|
|
15
|
+
export { runDoctorCommand, runDoctor, inspectControllerSource, checkResponseMixing, checkNPlusOne, checkDependencyDirection, checkConnections, checkMigrationDiff, renderHuman, renderJson, type DoctorResult, type DoctorCheck, type DoctorLevel, type DoctorRule, type RuleReport, type DoctorIssue, type LegacyDoctorResult, type ActionUsage, type ResponseKind, type DoctorCommandOptions, } from "./doctor.js";
|
|
9
16
|
export { loadDomain, type LoadedDomain } from "./domain.js";
|
|
10
17
|
export interface RoadmapReport {
|
|
11
18
|
readonly name: "gaon";
|
|
@@ -27,5 +34,10 @@ export declare function renderRoadmap(version?: string): string;
|
|
|
27
34
|
export interface RunOptions {
|
|
28
35
|
readonly version?: string;
|
|
29
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* `gaon doctor --check=<이름>[,<이름>...]` 파싱(M9-E).
|
|
39
|
+
* 지정 없음(undefined) = 5 검사 모두 실행. 알 수 없는 이름은 무시(안전).
|
|
40
|
+
*/
|
|
41
|
+
export declare function parseDoctorChecks(argv: readonly string[]): DoctorRule[] | undefined;
|
|
30
42
|
/** CLI 진입점. argv 는 실행 인자(process.argv.slice(2))를 받는다. */
|
|
31
43
|
export declare function runCli(argv: readonly string[], opts?: RunOptions): void;
|