-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapply-study-builder-java-tooling.mjs
More file actions
137 lines (125 loc) · 6.6 KB
/
Copy pathapply-study-builder-java-tooling.mjs
File metadata and controls
137 lines (125 loc) · 6.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
import { readFile, writeFile } from "node:fs/promises";
import { spawnSync } from "node:child_process";
import { resolve } from "node:path";
const root = process.cwd();
const prototypeRoot = resolve(root, "prototype");
async function read(relativePath) {
return readFile(resolve(root, relativePath), "utf8");
}
async function write(relativePath, content) {
await writeFile(resolve(root, relativePath), content, "utf8");
}
function replaceOnce(source, before, after, label) {
if (source.includes(after)) return source;
const first = source.indexOf(before);
if (first < 0) {
throw new Error(`${label} 적용 위치를 찾지 못했습니다. 현재 파일 변경 내용을 확인하세요.`);
}
if (source.indexOf(before, first + before.length) >= 0) {
throw new Error(`${label} 적용 위치가 두 곳 이상입니다. 자동 변경을 중단했습니다.`);
}
return `${source.slice(0, first)}${after}${source.slice(first + before.length)}`;
}
async function patchWorkspaceEditor() {
const path = "prototype/src/hooks/useWorkspaceEditor.js";
let source = await read(path);
source = replaceOnce(
source,
"const create = useCallback(async (kind, relativePath) => {",
"const create = useCallback(async (kind, relativePath, content = \"\") => {",
"새 파일 콘텐츠 전달",
);
source = replaceOnce(
source,
"await workspaceService.createFile(relativePath)",
"await workspaceService.createFile(relativePath, content)",
"새 파일 템플릿 저장",
);
await write(path, source);
}
async function patchCodeWorkspace() {
const path = "prototype/src/practice/CodeWorkspace.jsx";
let source = await read(path);
source = replaceOnce(
source,
`import {\n command,\n} from "./practiceData.js";\n`,
`import {\n command,\n} from "./practiceData.js";\nimport {\n editorOptionsForPath,\n editorStatusForPath,\n registerJavaSpringEditor,\n} from "./java-editor-setup.js";\n`,
"Java 편집기 모듈 import",
);
source = replaceOnce(
source,
"loader.config({ monaco })\n",
"loader.config({ monaco })\nregisterJavaSpringEditor(monaco)\n",
"Java/Spring completion 등록",
);
source = replaceOnce(
source,
` const saveRef = useRef(onSave)\n saveRef.current = onSave\n`,
` const saveRef = useRef(onSave)\n saveRef.current = onSave\n const editorStatus = editorStatusForPath(file?.relativePath)\n`,
"편집기 상태 계산",
);
source = replaceOnce(
source,
` onMount={(editor, monaco) => {\n editor.addCommand(\n`,
` onMount={(editor, monaco) => {\n registerJavaSpringEditor(monaco)\n editor.addCommand(\n`,
"Monaco Java 도구 초기화",
);
source = replaceOnce(
source,
` options={{\n ariaLabel:`,
` options={{\n ...editorOptionsForPath(file.relativePath),\n ariaLabel:`,
"파일별 편집기 설정",
);
source = source.replace(/^\s{10}tabSize: 2,\n/mu, "");
source = replaceOnce(
source,
` wordWrap: "off",\n }}\n />\n </div>\n`,
` wordWrap: "off",\n }}\n />\n <footer className="editor-statusbar" aria-label="편집기 상태">\n <span>{editorStatus.language}</span>\n {editorStatus.runtime ? <span>{editorStatus.runtime}</span> : null}\n <span>{editorStatus.indentation}</span>\n <span>UTF-8</span>\n <span>LF</span>\n </footer>\n </div>\n`,
"VS Code 형태 상태 표시줄",
);
await write(path, source);
}
async function patchEditorCss() {
const path = "prototype/src/practice/practice-editor.css";
let source = await read(path);
source = replaceOnce(
source,
"grid-template-rows: 38px minmax(0, 1fr);",
"grid-template-rows: 38px minmax(0, 1fr) 24px;",
"코드 편집기 상태 표시줄 레이아웃",
);
source = replaceOnce(
source,
"grid-template-rows: 38px 40px minmax(0, 1fr);",
"grid-template-rows: 38px 40px minmax(0, 1fr) 24px;",
"충돌 상태 편집기 레이아웃",
);
const start = "/* study-builder-java-tooling:start */";
const end = "/* study-builder-java-tooling:end */";
const styles = `${start}\n.editor-statusbar {\n min-width: 0;\n height: 24px;\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 14px;\n padding: 0 10px;\n border-top: 1px solid var(--line);\n background: #f3f5f3;\n color: #5f6963;\n font-size: 10.5px;\n line-height: 1;\n}\n\n.editor-statusbar span:first-child {\n margin-right: auto;\n color: var(--accent-dark);\n font-weight: 700;\n}\n\n.java-file-template-options {\n display: grid;\n gap: 12px;\n padding: 12px;\n border: 1px solid var(--line);\n border-radius: var(--radius-control);\n background: var(--soft);\n}\n\n.java-file-template-options label {\n display: grid;\n gap: 6px;\n color: var(--muted);\n font-size: var(--type-caption);\n font-weight: 650;\n}\n\n.java-file-template-options select {\n min-height: 36px;\n padding: 0 10px;\n border: 1px solid var(--line-strong);\n border-radius: var(--radius-small);\n background: var(--paper);\n color: var(--ink);\n font: inherit;\n}\n\n.java-file-preview {\n display: grid;\n gap: 6px;\n margin: 0;\n}\n\n.java-file-preview > div {\n display: grid;\n grid-template-columns: 86px minmax(0, 1fr);\n gap: 10px;\n align-items: baseline;\n}\n\n.java-file-preview dt {\n color: var(--tertiary);\n font-size: 11px;\n}\n\n.java-file-preview dd {\n min-width: 0;\n margin: 0;\n overflow: hidden;\n color: var(--ink);\n font-family: var(--font-mono);\n font-size: 11px;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n${end}`;
if (source.includes(start)) {
const startIndex = source.indexOf(start);
const endIndex = source.indexOf(end, startIndex);
if (endIndex < 0) throw new Error("Java tooling CSS 종료 표시를 찾지 못했습니다.");
source = `${source.slice(0, startIndex)}${styles}${source.slice(endIndex + end.length)}`;
} else {
source = `${source.trimEnd()}\n\n${styles}\n`;
}
await write(path, source);
}
function rebuildTemplate() {
const result = spawnSync(
process.execPath,
[resolve(prototypeRoot, "scripts/build-template-archives.mjs")],
{ cwd: prototypeRoot, stdio: "inherit" },
);
if (result.status !== 0) {
throw new Error("Spring Boot 실습 템플릿 ZIP을 다시 만들지 못했습니다.");
}
}
await patchWorkspaceEditor();
await patchCodeWorkspace();
await patchEditorCss();
rebuildTemplate();
console.log("Java/Spring 파일 생성과 Monaco 편집기 최적화를 적용했습니다.");
console.log("검증: cd prototype && npm run typecheck && npm test");